Files
Nexus/2023/scripts/rigging_tools/ngskintools2/api/session.py
2025-11-24 08:27:50 +08:00

163 lines
4.4 KiB
Python

"""
UI session is running as long as any of the ngSkinTools UI windows are open.
"""
import functools
from ngSkinTools2 import cleanup, signal
from ngSkinTools2.api import Layers, PaintTool, events, mirror, plugin
from ngSkinTools2.api.log import getLogger
from ngSkinTools2.api.python_compatibility import Object
from ngSkinTools2.observableValue import ObservableValue
from ngSkinTools2.signal import SignalHub
log = getLogger("events")
class CurrentLayerState(Object):
def __init__(self):
self.selectedSkinCluster = None
# will be None, when no current layer is available
self.layer = None # type: ngSkinTools2.api.Layer
class CurrentPaintTargetState(Object):
def __init__(self):
self.skinCluster = None
self.layerId = None
self.targets = None
class State(Object):
def __init__(self):
self.layersAvailable = False
self.selection = []
self.selectedSkinCluster = None
self.skin_cluster_dq_channel_used = False
self.all_layers = [] # type: List[ngSkinTools2.api.Layer]
self.currentLayer = CurrentLayerState()
self.currentInfluence = CurrentPaintTargetState()
def set_skin_cluster(self, cluster):
self.selectedSkinCluster = cluster
self.layers = None if cluster is None else Layers(cluster)
def mirror(self):
# type: () -> mirror.Mirror
return mirror.Mirror(self.selectedSkinCluster)
class Context(Object):
def __init__(self):
self.selected_layers = ObservableValue() # [] layerId
class Session(Object):
def __init__(self):
# reference objects that are keeping the session
self.references = set()
self.state = None # type: State
self.events = None # type: events.Events
self.signal_hub = None # type: SignalHub
self.context = None # type: Context
self.referenceId = 0
def active(self):
return len(self.references) > 0
def start(self):
log.info("STARTING SESSION")
plugin.load_plugin()
self.paint_tool = PaintTool()
self.state = State()
self.events = events.Events(self.state)
self.signal_hub = SignalHub()
self.signal_hub.activate()
cleanup.registerCleanupHandler(self.signal_hub.deactivate)
self.context = Context()
@signal.on(self.events.targetChanged)
def on_target_change():
log.info("target changed: clearing target context")
self.context.selected_layers.set([])
self.events.nodeSelectionChanged.emit()
def end(self):
log.info("ENDING SESSION")
cleanup.cleanup()
self.state = None
self.events = None
self.context = None
self.signal_hub = None
pass
def addReference(self):
"""
returns unique ID for this added reference; this value needs to be passed into removeReference();
this ensures that reference holder does not remove other references rather than his own.
:return:
"""
self.referenceId += 1
if not self.active():
self.start()
self.references.add(self.referenceId)
return self.referenceId
def removeReference(self, referenceId):
try:
self.references.remove(referenceId)
except KeyError:
pass
if not self.active():
self.end()
def addQtWidgetReference(self, widget):
ref = self.addReference()
widget.destroyed.connect(lambda: self.removeReference(ref))
def reference(self):
class Context(Object):
def __init__(self, session):
"""
:type session: Session
:type refObj: object
"""
self.session = session
def __enter__(self):
self.ref = self.session.addReference()
def __exit__(self, exc_type, exc_val, exc_tb):
self.session.removeReference(self.ref)
return Context(self)
session = Session()
def withSession(func):
"""
decorator makes sure that single session is running throughout function's lifetime
"""
@functools.wraps(func)
def result(*args, **kwargs):
ref = session.addReference()
try:
return func(*args, **kwargs)
finally:
session.removeReference(ref)
return result