-
Notifications
You must be signed in to change notification settings - Fork 14
Quick Start
The window inherits off QMainWindow
, so the syntax is the exact same for the most part. Other than the way it's launched, it's been built to be fully compatible with existing QMainWindow
widgets.
This is the most basic example of how the class works. With no extra parameters, this will launched in Maya and Nuke as a docked widget.
class NewWindow(VFXWindow):
WindowID = 'my_new_window'
WindowName = 'My New Window'
WindowDockable = True
# Note: Never remove **kwargs, any extra required keyword arguments should come after parent=None
def __init__(self, parent=None, **kwargs):
super(VFXWindow, self).__init__(parent, **kwargs)
# Perform setup here
if __name__ == '__main__':
NewWindow.show()
To handle the automatic cleanup, each window can be given a unique WindowID
attribute. It deals with saving the previous position and dimensions of the window between sessions, and automatically closing any window that's launched a second time.
It's recommended to set the WindowName
attribute to give the window a title. While self.setWindowTitle()
can be used (and effort has been made to make sure it works correctly), there's a chance some interfaces may not be able modify the title once the GUI is running.
Determine if the dockable variants of a window should be used.
The currently loaded program can be accessed by an attribute. To run code within Maya for example, you'd check if self.maya
is True
. See the AbstractWindow.__init__
method for a list of these attributes.
Any callback is automatically unregistered if the window is closed, so it's definitely recommended to use the helper methods to create them. If a requirement is to remove or pause a selection of callbacks at a later time, an optional group
argument can be provided.
These are wrappers around the built in APIs, so the inputs are the same as what would be used normally. To manually remove callbacks, use self.removeCallbacks(group=None)
.
# Create callback for specific program version
if self.maya:
self.addCallbackScene('kAfterNew', self.newScene, group='sceneNew')
elif self.nuke:
self.addCallbackOnCreate(self.newScene, nodeClass='Root', group='sceneNew')
# Delete callback
self.removeCallback('sceneNew')
self.addCallbackEvent(callback, func, clientData=None, group=None)
self.addCallbackNode(callback, node, func, clientData=None, group=None)
self.addCallbackAttributeChange(node, func, clientData=None, group=None)
self.addCallbackAttributeAddOrRemove(node, func, clientData=None, group=None)
self.addCallbackNodeRename(node, func, clientData=None, group=None)
self.addCallbackNodeDirty(node, func, clientData=None, group=None)
self.addCallbackNodeDirtyPlug(node, func, clientData=None, group=None)
self.addCallbackUuidChange(node, func, clientData=None, group=None)
self.addCallbackKeyableChange(node, func, clientData=None, group=None)
self.addCallbackNodeRemove(node, func, clientData=None, group=None)
self.addCallbackScene(callback, func, clientData=None, group=None)
self.addCallbackJobEvent(callback, func, group=None, runOnce=False)
self.addCallbackJobCondition(callback, func, group=None, runOnce=False)
self.addCallbackNodeTypeAdd(func, nodeType='dependNode', clientData=None, group=None)
self.addCallbackNodeTypeRemove(func, nodeType='dependNode', clientData=None, group=None)
self.addCallbackTimeChange(func, clientData=None, group=None)
self.addCallbackForceUpdate(func, clientData=None, group=None)
self.addCallbackConnectionAfter(func, clientData=None, group=None)
self.addCallbackConnectionBefore(func, clientData=None, group=None)
self.addCallbackOnUserCreate(func, nodeClass=None, group=None)
self.addCallbackOnCreate(func, nodeClass=None, group=None)
self.addCallbackOnScriptLoad(func, nodeClass=None, group=None)
self.addCallbackOnScriptSave(func, nodeClass=None, group=None)
self.addCallbackOnScriptClose(func, nodeClass=None, group=None)
self.addCallbackOnDestroy(func, nodeClass=None, group=None)
self.addCallbackKnobChanged(func, nodeClass=None, group=None)
self.addCallbackUpdateUI(func, nodeClass=None, group=None)
self.addCallbackFrameChangeAfter(func, persistent=True, group=None)
self.addCallbackFrameChangeBefore(func, persistent=True, group=None)
self.addCallbackGameAfter(func, persistent=True, group=None)
self.addCallbackGameBefore(func, persistent=True, group=None)
self.addCallbackLoadSceneAfter(func, persistent=True, group=None)
self.addCallbackLoadSceneBefore(func, persistent=True, group=None)
self.addCallbackRenderCancel(func, persistent=True, group=None)
self.addCallbackRenderComplete(func, persistent=True, group=None)
self.addCallbackRenderInit(func, persistent=True, group=None)
self.addCallbackRenderAfter(func, persistent=True, group=None)
self.addCallbackRenderBefore(func, persistent=True, group=None)
self.addCallbackRenderStats(func, persistent=True, group=None)
self.addCallbackRenderWrite(func, persistent=True, group=None)
self.addCallbackSaveSceneAfter(func, persistent=True, group=None)
self.addCallbackSaveSceneBefore(func, persistent=True, group=None)
self.addCallbackSceneUpdateAfter(func, persistent=True, group=None)
self.addCallbackSceneUpdateBefore(func, persistent=True, group=None)
self.addCallbackVersionUpdate(func, persistent=True, group=None)
self.addCallbackDepsgraphUpdateAfter(func, persistent=True, group=None)
self.addCallbackDepsgraphUpdateBefore(func, persistent=True, group=None)
self.addCallbackUndoAfter(func, persistent=True, group=None)
self.addCallbackUndoBefore(func, persistent=True, group=None)
self.addCallbackRedoAfter(func, persistent=True, group=None)
self.addCallbackRedoBefore(func, persistent=True, group=None)
Signals can be handled in the same way as callbacks, with an option for pausing them.
# Add the signal
self.signalConnect(widget.textChanged, self.func, group='textChanged')
# Pause the signal
with self.signalPause('textChanged'):
inputWgt.setText('did not trigger')
inputWgt.setText('did trigger')
# Remove the signal
self.signalDisconnect('textChanged')
inputWgt.setText('did not trigger')
Each program has different ways to defer execution, so it is all wrapped under self.deferred(func)
. If the program does not support for deferring, then calling self.deferred
will just execute the function straight away.
It's possible to map the user interface colours to that of an application using self.setWindowPalette(program, version=None)
. If nothing happens, it's because I've found the current application will cause problems and have manually disabled it. While it might be fun having Maya look like Nuke, it'll start spitting out a ton of errors and will likely crash.
self.setWindowPalette('Maya', 2018)
As each window needs to clean its own callbacks up, a special way must be used to insert windows inside other windows.
Use ChildWindow.instance(self)
inside the parent window to get the properly set up window.
class ContainerWindow(VFXWindow):
def __init__(self, parent=None, **kwargs):
super(VFXWindow, self).__init__(parent, **kwargs)
self.setCentralWidget(ChildWindow.instance(self).centralWidget())
Make sure VFXWindow.clearWindowInstance(ChildWindow.WindowID)
is called when the widget closes.