def __init__( self, path, columns = defaultFileSystemColumns, allowMultipleSelection = False, displayMode = DisplayMode.List, **kw ) : GafferUI.Widget.__init__( self, _TreeView(), **kw ) self._qtWidget().setAlternatingRowColors( True ) self._qtWidget().setUniformRowHeights( True ) self._qtWidget().setEditTriggers( QtWidgets.QTreeView.NoEditTriggers ) self._qtWidget().activated.connect( Gaffer.WeakMethod( self.__activated ) ) if Qt.__binding__ in ( "PySide2", "PyQt5" ) : self._qtWidget().header().setSectionsMovable( False ) else : self._qtWidget().header().setMovable( False ) self._qtWidget().header().setSortIndicator( 0, QtCore.Qt.AscendingOrder ) self._qtWidget().setSortingEnabled( True ) self._qtWidget().expansionChanged.connect( Gaffer.WeakMethod( self.__expansionChanged ) ) # install an empty model, so we an construct our selection model # around it. we'll update the model contents shortly in setPath(). _GafferUI._pathListingWidgetUpdateModel( GafferUI._qtAddress( self._qtWidget() ), None ) _GafferUI._pathListingWidgetSetColumns( GafferUI._qtAddress( self._qtWidget() ), columns ) self.__selectionModel = QtCore.QItemSelectionModel( self._qtWidget().model() ) self._qtWidget().setSelectionModel( self.__selectionModel ) self.__selectionChangedSlot = Gaffer.WeakMethod( self.__selectionChanged ) self._qtWidget().selectionModel().selectionChanged.connect( self.__selectionChangedSlot ) if allowMultipleSelection : self._qtWidget().setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection ) self.__pathSelectedSignal = GafferUI.WidgetSignal() self.__selectionChangedSignal = GafferUI.WidgetSignal() self.__displayModeChangedSignal = GafferUI.WidgetSignal() self.__expansionChangedSignal = GafferUI.WidgetSignal() # members for implementing drag and drop self.__emittingButtonPress = False self.__borrowedButtonPress = None self.__buttonPressConnection = self.buttonPressSignal().connect( Gaffer.WeakMethod( self.__buttonPress ) ) self.__buttonReleaseConnection = self.buttonReleaseSignal().connect( Gaffer.WeakMethod( self.__buttonRelease ) ) self.__mouseMoveConnection = self.mouseMoveSignal().connect( Gaffer.WeakMethod( self.__mouseMove ) ) self.__dragBeginConnection = self.dragBeginSignal().connect( Gaffer.WeakMethod( self.__dragBegin ) ) self.__dragEndConnection = self.dragEndSignal().connect( Gaffer.WeakMethod( self.__dragEnd ) ) self.__dragPointer = "paths" self.__path = None self.setDisplayMode( displayMode ) self.setPath( path )
def _updateFromSet( self ) : GafferUI.NodeSetEditor._updateFromSet( self ) del self.__column[:] self.__nodeUI = None self.__nameWidget = None node = self._lastAddedNode() if not node : return with self.__column : with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, borderWidth=8, spacing=4 ) : GafferUI.Label( "<h4>Node Name</h4>" ) self.__nameWidget = GafferUI.NameWidget( node ) self.__nameWidget.setEditable( not self.getReadOnly() ) with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing=4 ) as infoSection : GafferUI.Label( "<h4>" + node.typeName().rpartition( ":" )[-1] + "</h4>" ) button = GafferUI.Button( image = "info.png", hasFrame = False ) url = Gaffer.Metadata.nodeValue( node, "documentation:url" ) if url : button.clickedSignal().connect( lambda button : GafferUI.showURL( url ), scoped = False ) toolTip = "<h3>" + node.typeName().rpartition( ":" )[2] + "</h3>" description = Gaffer.Metadata.nodeDescription( node ) if description : toolTip += "\n\n" + description infoSection.setToolTip( toolTip ) GafferUI.MenuButton( image = "gear.png", hasFrame = False, menu = GafferUI.Menu( Gaffer.WeakMethod( self.__menuDefinition ) ) ) frame = GafferUI.Frame( borderStyle=GafferUI.Frame.BorderStyle.None, borderWidth=0 ) self.__column.append( frame, expand=True ) self.__nodeUI = GafferUI.NodeUI.create( node ) self.__nodeUI.setReadOnly( self.getReadOnly() ) frame.setChild( self.__nodeUI )
def __setupClipboardSync( self ) : ## This function sets up two way syncing between the clipboard held in the Gaffer::ApplicationRoot # and the global QtGui.QClipboard which is shared with external applications, and used by the cut and paste # operations in GafferUI's underlying QWidgets. This is very useful, as it allows nodes to be copied from # the graph and pasted into emails/chats etc, and then copied out of emails/chats and pasted into the node graph. # ## \todo I don't think this is the ideal place for this functionality. Firstly, we need it in all apps # rather than just the gui app. Secondly, we want a way of using the global clipboard using GafferUI # public functions without needing an ApplicationRoot. Thirdly, it's questionable that ApplicationRoot should # have a clipboard anyway - it seems like a violation of separation between the gui and non-gui libraries. # Perhaps we should abolish the ApplicationRoot clipboard and the ScriptNode cut/copy/paste routines, relegating # them all to GafferUI functionality? QtGui = GafferUI._qtImport( "QtGui" ) self.__clipboardContentsChangedConnection = self.root().clipboardContentsChangedSignal().connect( Gaffer.WeakMethod( self.__clipboardContentsChanged ) ) QtGui.QApplication.clipboard().dataChanged.connect( Gaffer.WeakMethod( self.__qtClipboardContentsChanged ) ) self.__ignoreQtClipboardContentsChanged = False
def __createHoudiniQGLWidget( cls, format ) : try : import hou except ImportError : # we're not in houdini - createQGLWidget() will just make a # normal widget. return None import IECoreHoudini # Prior to Houdini 14 we are running embedded on the hou.ui idle loop, # so we needed to force the Houdini GL context to be current, and share # it, similar to how we do this in Maya. if hou.applicationVersion()[0] < 14 : return cls.__createHostedQGLWidget( format, IECoreHoudini.makeMainGLContextCurrent ) # In Houdini 14 and beyond, Qt is the native UI, and we can access # Houdini's shared QGLWidget directly, provided we are using a recent # Cortex version. return QtOpenGL.QGLWidget( format, shareWidget = GafferUI._qtObject( IECoreHoudini.sharedGLWidget(), QtOpenGL.QGLWidget ) )
import IECore import GafferUI import GafferScene import GafferSceneUI import os scriptNode = script scriptWindow = GafferUI.ScriptWindow.acquire( script ) ea = GafferUI.NodeGraph( scriptNode ) eb = GafferUI.NodeGraph( scriptNode ) layout = eval( "GafferUI.CompoundEditor( scriptNode, children = ( GafferUI.SplitContainer.Orientation.Horizontal, 0.65, ( {'tabs': (ea,), 'tabsVisible': True, 'currentTab': 0, 'pinned': [True]}, {'tabs': (eb,), 'tabsVisible': True, 'currentTab': 0, 'pinned': [True]} ) ) )" ) script.selection().add( script["ThisIsABox"] ) eb.graphGadget().setRoot( script["ThisIsABox"] ) scriptWindow.setLayout( layout ) scriptWindow._Widget__qtWidget.resize(925,450) ea.frame([script["Backdrop"],script["ThisIsABox"]]) eb.frame([ script["ThisIsABox"]["Cube3"], script["ThisIsABox"]["Cube2"], script["ThisIsABox"]["Sphere5"], script["ThisIsABox"]["PathFilter1"] ])
class bundleListWidget(GafferUI.EditorWidget): __pathSelectedSignal = GafferUI.WidgetSignal() __selectionChangedSignal = GafferUI.WidgetSignal() __displayModeChangedSignal = GafferUI.WidgetSignal() __expansionChangedSignal = GafferUI.WidgetSignal() _selectionChangedSignal = GafferUI.WidgetSignal() def __init__(self, scriptNode=None, **kw): from GafferUI.PathListingWidget import _TreeView __toolTip__ = '''Light Icons: off\t= asset not present in the scene green\t= asset present in the scene, and up-to-date red\t= asset present in the scene, but its an older/not current version edit\t= EDIT MODE - the scene has the source nodes for publishing a new version, and NOT the actual asset. \t If this is a render scene, please make sure to remove the original nodes and import the asset! \t (Unless renderSettings assets, since they are allways available for publishing) ''' __toolTip__ += '''\nBackground: blue\t= shot specific asset yellow\t= same as edit icon ''' self._script = scriptNode super(bundleListWidget, self).__init__(_TreeView(), scriptNode, toolTip=__toolTip__, **kw) # GafferUI.EditorWidget.__init__( self, QtGui.QTreeView(), scriptNode, **kw ) self._lastLS = None # self._model = TreeModel(populateAssets()[0]) # self._qtWidget().setModel(self._model) # self._qtWidget().expandToDepth(1) self.setTreeModel() self._mayaNodeDeleted() self._qtWidget().resizeColumnToContents(0) self._qtWidget().setColumnWidth(0, 100) self._qtWidget().setIndentation(10) self._qtWidget().setAlternatingRowColors(True) self._qtWidget().setUniformRowHeights(True) self._qtWidget().setEditTriggers(QtGui.QTreeView.NoEditTriggers) self._qtWidget().activated.connect(Gaffer.WeakMethod(self.__activated)) self._qtWidget().header().setMovable(False) self._qtWidget().header().setSortIndicator(0, QtCore.Qt.AscendingOrder) self._qtWidget().setSortingEnabled(True) # members for implementing drag and drop self.__emittingButtonPress = False self.__borrowedButtonPress = None self.__buttonPressConnection = self.buttonPressSignal().connect( Gaffer.WeakMethod(self.__buttonPress)) self.__buttonReleaseConnection = self.buttonReleaseSignal().connect( Gaffer.WeakMethod(self.__buttonRelease)) self.__mouseMoveConnection = self.mouseMoveSignal().connect( Gaffer.WeakMethod(self.__mouseMove)) self.__dragBeginConnection = self.dragBeginSignal().connect( Gaffer.WeakMethod(self.__dragBegin)) self.__dragEndConnection = self.dragEndSignal().connect( Gaffer.WeakMethod(self.__dragEnd)) self.__dragPointer = "paths" self.__contextMenuConnection = self.contextMenuSignal().connect( Gaffer.WeakMethod(self.__contextMenu)) self.__hostApp = None # self.__dragEnterConnection = self.dragEnterSignal().connect( Gaffer.WeakMethod( self.__dragEnter ) ) self.__path = None self._nodeGraph = None self._script = scriptNode self.timer = QtCore.QTimer() def forceRefresh(): # import threading # threading.Thread( target = ).start() self._mayaNodeDeleted(force=True) self.timer.singleShot(60 * 1000, forceRefresh) # self.timer.singleShot( 60*1000,forceRefresh) def _mayaNodeDeleted(self, force=False): ls = [] if assetUtils.m: ls = assetUtils.m.ls("|SAM*") # print ls if ls != self._lastLS or force: self.refresh(ls) def __SAM_assetList_mayaNodeDeleted__(): def __SAM_assetList_mayaNodeDeleted_IDLE__(): import genericAsset genericAsset.maya.cleanUnusedShadingNodes() genericAsset.maya.attachShadersLazy() # genericAsset.maya.setRenderSettings() self._mayaNodeDeleted(True) assetUtils.mayaLazyScriptJob( runOnce=True, idleEvent=__SAM_assetList_mayaNodeDeleted_IDLE__) assetUtils.mayaLazyScriptJob( runOnce=False, deleteEvent=__SAM_assetList_mayaNodeDeleted__) assetUtils.mayaLazyScriptJob( runOnce=False, event=['deleteAll', __SAM_assetList_mayaNodeDeleted__]) assetUtils.mayaLazyScriptJob( runOnce=False, event=['NewSceneOpened', __SAM_assetList_mayaNodeDeleted__]) assetUtils.mayaLazyScriptJob( runOnce=False, event=['SceneOpened', __SAM_assetList_mayaNodeDeleted__]) # def __SAM_assetList_mayaSelectSam__(): # print m.ls(sl=1) # assetUtils.mayaLazyScriptJob( runOnce=False, event=['SomethingSelected',__SAM_assetList_mayaSelectSam__] ) def hostApp(self, hostAppName): self.__hostApp = hostAppName def __contextMenu(self, pathListing): return self._menu(pathListing) def _menu(self, pathListing): # asset=0 # assetData=1 # assetFullPath=2 # assetOP=3 # assetOPSourceExistsInHost=4 menuDefinition = IECore.MenuDefinition() selectedData = pathListing.getSelectedColumns() selectedPaths = selectedData['assetFullPath'] selectedPathsOP = selectedData['assetOP'] selectedPathsExistInHost = selectedData['assetOPSourceExistsInHost'] # for path in selectedPaths: selected_source_exists_in_host = False if [ x for x in selectedPathsExistInHost if x ] else True isAssetMenu = [x for x in selectedPaths if len(x.split('/')) > 2] isPublishMenu = [x for x in selectedPaths if len(x.split('/')) <= 2] if isAssetMenu: def checkout(paths): def __SAM_assetList_doImport__(): import genericAsset pb = genericAsset.progressBar( len(paths) + 1, "Importing assets...") for path in paths: pb.step() print path op = assetUtils.assetOP(path) op.doImport() pb.step() pb.close() self._mayaNodeDeleted() # print op, path __SAM_assetList_doImport__() # assetUtils.mayaLazyScriptJob( runOnce=True, idleEvent=__SAM_assetList_doImport__) canImport = selected_source_exists_in_host if len(selectedPaths) == 1: if 'renderSettings/' in selectedPaths[0]: canImport = True menuDefinition.append( "/Import selected", { "command": IECore.curry(checkout, selectedPaths), "active": canImport }) if len(selectedPaths) == 1: menuDefinition.append("/ ", {}) menuDefinition.append("/ ", {}) def updateAsset(asset): op = assetUtils.assetOP(asset[0]) op.updatePublish() menuDefinition.append( "/Publish an UPDATE to the selected asset", { "command": IECore.curry(updateAsset, selectedPaths), "active": not selected_source_exists_in_host }) menuDefinition.append("/", {}) def mayaImportDependency(paths): def __SAM_assetList_mayaImportDependency__(): # print paths for path in paths: # print path op = assetUtils.assetOP(path) op.mayaImportDependency() if assetUtils.m: assetUtils.mayaLazyScriptJob( runOnce=True, idleEvent=__SAM_assetList_mayaImportDependency__) else: __SAM_assetList_mayaImportDependency__() def mayaOpenDependency(paths): def __SAM_assetList_mayaOpenDependency__(): # print paths for path in paths: # print path op = assetUtils.assetOP(path) op.mayaOpenDependency() if assetUtils.m: assetUtils.mayaLazyScriptJob( runOnce=True, idleEvent=__SAM_assetList_mayaOpenDependency__) else: __SAM_assetList_mayaOpenDependency__() if assetUtils.m: menuDefinition.append( "/open original scene in this maya session ", { "command": IECore.curry(mayaImportDependency, selectedPaths) }) menuDefinition.append( "/open original scene in a new maya session ", { "command": IECore.curry(mayaOpenDependency, selectedPaths) }) elif isPublishMenu and len(selectedPaths) == 1: from pprint import pprint types = assetUtils.Asset.types(refresh=True) atype = selectedPaths[0].split('/')[0] def createNewAsset(paths, assetType): op = assetUtils.assetOP(assetType) op.newPublish() for t in [x for x in types if atype in x.split('/')]: if 'render/' in t: if assetUtils.m and 'render/maya' not in t: continue menuDefinition.append( "/publish new asset of type %s" % t.replace('/', '_'), { "command": IECore.curry(createNewAsset, selectedPaths, t) }) self.__menu = GafferUI.Menu(menuDefinition) if len(menuDefinition.items()): self.__menu.popup(parent=pathListing.ancestor(GafferUI.Window)) return True def refresh(self, lastLS=None): self.setTreeModel() if self._lastLS != lastLS: self._lastLS = lastLS self._model.refresh(self._lastLS) def setTreeModel(self): self._model = TreeModel(populateAssets()[0]) self._qtWidget().setModel(self._model) self._qtWidget().expandToDepth(1) self._qtWidget().setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection) self.__selectionChangedSlot = Gaffer.WeakMethod( self.__selectionChanged) self._qtWidget().selectionModel().selectionChanged.connect( self.__selectionChangedSlot) self._qtWidget().setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection) def getTitle(self): return 'SAM Bundle List' def __repr__(self): return "GafferUI.SAMbundleListWidget( scriptNode )" def setNodeGraph(self, nodeGraph): self._nodeGraph = nodeGraph def setScript(self, script): self._script = script def getSelectedPaths(self): selectedRows = self._qtWidget().selectionModel().selectedRows() # print [ index.internalPointer().data(TreeItem.assetFullPath) for index in selectedRows ] return [ index.internalPointer().data(TreeItem.assetFullPath) for index in selectedRows ] def getSelectedColumns(self): selectedRows = self._qtWidget().selectionModel().selectedRows() # print [ index.internalPointer().data(TreeItem.assetFullPath) for index in selectedRows ] ret = {} for column in [x for x in dir(TreeItem) if 'asset' in x]: ret[column] = [] for index in selectedRows: ret[column] += [ index.internalPointer().data(eval('TreeItem.%s' % column)) ] return ret # def setSelectedPaths( self, paths, scrollToFirst=True, expandNonLeaf=True ) : # # # If there are pending changes to our path model, we must perform # # them now, so that the model is valid with respect to the paths # # we're trying to select. # self.__updateLazily.flush( self ) # selectionModel = self._qtWidget().selectionModel() # selectionModel.selectionChanged.disconnect( self.__selectionChangedSlot ) # # selectionModel.clear() # # for path in paths : # # indexToSelect = self.__indexForPath( path ) # if indexToSelect.isValid() : # selectionModel.select( indexToSelect, selectionModel.Select | selectionModel.Rows ) # if scrollToFirst : # self._qtWidget().scrollTo( indexToSelect, self._qtWidget().EnsureVisible ) # selectionModel.setCurrentIndex( indexToSelect, selectionModel.Current ) # scrollToFirst = False # if expandNonLeaf and not path.isLeaf() : # self._qtWidget().setExpanded( indexToSelect, True ) # # selectionModel.selectionChanged.connect( self.__selectionChangedSlot ) # # self.selectionChangedSignal()( self ) def __selectionChanged(self, selected, deselected): self.selectionChangedSignal()(self) return True def selectionChangedSignal(self): if assetUtils.m: assetUtils.m.select(cl=1) selectedData = self.getSelectedColumns() # print selectedData for n in range(len(selectedData['assetFullPath'])): path, op = selectedData['assetFullPath'][n], selectedData[ 'assetOP'][n] # print path # op = assetUtils.assetOP( path ) if op: nodes = op.assetSourceExistsInHost() if nodes and 'renderSettings' not in path: if assetUtils.m: assetUtils.m.select(nodes) else: op.mayaSelectNodes() # self.refresh(op.mayaLastLs()) self.parent()._qtWidget().emit( QtCore.SIGNAL("selectionChangedSignal(PyQt_PyObject)"), {"self": self}) return self.__selectionChangedSignal def __pathForIndex(self, modelIndex): if type(modelIndex) == list: return [ Gaffer.Path(index.internalPointer().data( TreeItem.assetFullPath)) for index in modelIndex ] else: return Gaffer.Path(modelIndex.internalPointer().data( TreeItem.assetFullPath)) def __activated(self, modelIndex): activatedPath = self.__pathForIndex(modelIndex) self.__path = activatedPath if type(activatedPath) == list: self.__path[:] = activatedPath[:] if activatedPath.isLeaf(): self.pathSelectedSignal()(self) return True return False ## This signal is emitted when the user double clicks on a leaf path. def pathSelectedSignal(self): return self.__pathSelectedSignal def __buttonPress(self, widget, event): if self.__emittingButtonPress: return False self.__borrowedButtonPress = None if event.buttons == event.Buttons.Left and event.modifiers == event.Modifiers.None: # We want to implement drag and drop of the selected items, which means borrowing # mouse press events that the QTreeView needs to perform selection and expansion. # This makes things a little tricky. There are are two cases : # # 1) There is an existing selection, and it's been clicked on. We borrow the event # so we can get a dragBeginSignal(), and to prevent the QTreeView reducing a current # multi-selection down to the single clicked item. If a drag doesn't materialise we'll # re-emit the event straight to the QTreeView in __buttonRelease so the QTreeView can # do its thing. # # 2) There is no existing selection. We pass the event to the QTreeView # to see if it will select something which we can subsequently drag. # # This is further complicated by the fact that the button presses we simulate for Qt # will end up back in this function, so we have to be careful to ignore those. index = self._qtWidget().indexAt( QtCore.QPoint(event.line.p0.x, event.line.p0.y)) if self._qtWidget().selectionModel().isSelected(index): # case 1 : existing selection. self.__borrowedButtonPress = event return True else: # case 2 : no existing selection. # allow qt to update the selection first. self.__emitButtonPress(event) # we must always return True to prevent the event getting passed # to the QTreeView again, and so we get a chance to start a drag. return True return False def __buttonRelease(self, widget, event): if self.__borrowedButtonPress is not None: self.__emitButtonPress(self.__borrowedButtonPress) self.__borrowedButtonPress = None return False def __mouseMove(self, widget, event): if event.buttons: # take the event so that the underlying QTreeView doesn't # try to do drag-selection, which would ruin our own upcoming drag. return True return False def __dragBegin(self, widget, event): self.__borrowedButtonPress = None selectedPaths = self.getSelectedPaths() if len(selectedPaths): GafferUI.Pointer.setCurrent(self.__dragPointer) each = str(selectedPaths[0]) print each node = Gaffer.Node('_'.join(each.split('/')[:-1])) node['in'] = Gaffer.Plug() node['out'] = Gaffer.Plug(direction=Gaffer.Plug.Direction.Out) return node return IECore.StringVectorData([str(p) for p in selectedPaths], ) return None def __dragEnter(self, widget, event): print widget print event.destinationGadget print event.destinationWidget sys.stdout.flush() def __dragEnd(self, widget, event): import sys for each in event.data: # node = Gaffer.Node( '_'.join(each.split('/')[:-1]) ) # node['in']= Gaffer.Plug( ) # node['out']= Gaffer.Plug( direction = Gaffer.Plug.Direction.Out ) if event.destinationGadget: event.destinationGadget.ScriptNode.addChild(node) # print dir(event) print widget print event.destinationGadget print event.destinationWidget sys.stdout.flush() # self.__viewportGadget.dragEndSignal()( self.__viewportGadget, event ) # if self._nodeGraph: # graph = self._nodeGraph.graphGadget() # graph.getLayout().layoutNodes( graph ) GafferUI.Pointer.setCurrent(None) def __emitButtonPress(self, event): qEvent = QtGui.QMouseEvent( QtCore.QEvent.MouseButtonPress, QtCore.QPoint(event.line.p0.x, event.line.p0.y), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier) try: self.__emittingButtonPress = True # really i think we should be using QApplication::sendEvent() # here, but it doesn't seem to be working. it works with the qObject # in the Widget event filter, but for some reason that differs from # Widget._owner( qObject )._qtWidget() which is what we have here. self._qtWidget().mousePressEvent(qEvent) finally: self.__emittingButtonPress = False
def __propagateExpanded( self, index, expanded ) : numLevels = 0 if self.__currentEventModifiers & QtCore.Qt.ShiftModifier : numLevels = 10000 elif self.__currentEventModifiers & QtCore.Qt.ControlModifier : numLevels = 1 if numLevels : self.collapsed.disconnect( self.__collapsed ) self.expanded.disconnect( self.__expanded ) # This call is critical for performance. Without it, # QTreeView will start doing relayout for every single # call to setExpanded() that we make inside # _pathListingWidgetPropagateExpanded(). With it, it # waits nicely till the end and does it all at once. self.scheduleDelayedItemsLayout() # Defer to C++ to do the heavy lifting. _GafferUI._pathListingWidgetPropagateExpanded( GafferUI._qtAddress( self ), GafferUI._qtAddress( index ), expanded, numLevels ) self.collapsed.connect( self.__collapsed ) self.expanded.connect( self.__expanded )
def testAddressAndObject( self ) : button = GafferUI.Button() address = GafferUI._qtAddress( button._qtWidget() ) self.assertTrue( isinstance( address, int ) ) widget = GafferUI._qtObject( address, QtGui.QPushButton ) self.assertTrue( isinstance( widget, QtGui.QPushButton ) )
def __indexForPath( self, path ) : result = QtCore.QModelIndex() _GafferUI._pathListingWidgetIndexForPath( GafferUI._qtAddress( self._qtWidget() ), path, GafferUI._qtAddress( result ), ) return result
def __createHostedQGLWidget( cls, format ) : # When running Gaffer embedded in a host application such as Maya # or Houdini, we want to be able to share OpenGL resources between # gaffer uis and host viewport uis, because IECoreGL will be used # in both. So we implement our own QGLContext class which creates a # context which shares with the host. The custom QGLContext is # implemented in GLWidgetBinding.cpp, and automatically shares with # the context which is current at the time of its creation. The host # context should therefore be made current before calling this # method. result = QtOpenGL.QGLWidget() _GafferUI._glWidgetSetHostedContext( GafferUI._qtAddress( result ), GafferUI._qtAddress( format ) ) return result
def _updateFromSet( self ) : GafferUI.NodeSetEditor._updateFromSet( self ) del self.__column[:] self.__nodeUI = None self.__nameWidget = None node = self._lastAddedNode() if not node : with self.__column : GafferUI.Spacer( imath.V2i( 0 ) ) return with self.__column : with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, borderWidth=8, spacing=4 ) : GafferUI.Label( "<h4>Node Name</h4>" ) self.__nameWidget = GafferUI.NameWidget( node ) ## \todo Make NameWidget support the readOnly metadata internally itself. # We can't do that easily right now, because it would need to be managing # the exact same `setEditable()` call that we're using here to propagate # our Widget readonlyness. Really our Widget readonlyness mechanism is a # bit lacking, and it should really be inherited automatically so we don't # have to propagate it like this. self.__nameWidget.setEditable( not self.getReadOnly() and not Gaffer.MetadataAlgo.readOnly( node ) ) with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing=4, parenting = { "horizontalAlignment" : GafferUI.HorizontalAlignment.Right }, ) as infoSection : GafferUI.Label( "<h4>" + node.typeName().rpartition( ":" )[-1] + "</h4>" ) button = GafferUI.Button( image = "info.png", hasFrame = False ) url = Gaffer.Metadata.value( node, "documentation:url" ) if url : button.clickedSignal().connect( lambda button : GafferUI.showURL( url ), scoped = False ) toolTip = "<h3>" + node.typeName().rpartition( ":" )[2] + "</h3>" description = Gaffer.Metadata.nodeDescription( node ) if description : toolTip += "\n\n" + description infoSection.setToolTip( toolTip ) GafferUI.MenuButton( image = "gear.png", hasFrame = False, menu = GafferUI.Menu( Gaffer.WeakMethod( self.__menuDefinition ) ) ) frame = GafferUI.Frame( borderStyle=GafferUI.Frame.BorderStyle.None, borderWidth=0 ) self.__column.append( frame, expand=True ) self.__nodeUI = GafferUI.NodeUI.create( node ) self.__nodeUI.setReadOnly( self.getReadOnly() ) frame.setChild( self.__nodeUI )
def __update( self ) : # update the listing if necessary. when the path itself changes, we only # want to update if the directory being viewed has changed. if the path # hasn't changed at all then we assume that the filter has changed and # we therefore have to update the listing anyway. # \todo Add an argument to Path.pathChangedSignal() to specify whether it # is the path or the filtering that has changed, and remove self.__currentPath. # Also consider whether it might be easier for the C++ PathModel to be # doing the signal handling at that point. dirPath = self.__dirPath() if self.__currentDir!=dirPath or str( self.__path )==self.__currentPath : selectedPaths = self.getSelectedPaths() expandedPaths = None if str( self.__path ) == self.__currentPath : # the path location itself hasn't changed so we are assuming that just the filter has. # if we're in the tree view mode, the user would probably be very happy # if we didn't forget what was expanded. if self.getDisplayMode() == self.DisplayMode.Tree : expandedPaths = self.getExpandedPaths() _GafferUI._pathListingWidgetUpdateModel( GafferUI._qtAddress( self._qtWidget() ), dirPath.copy() ) if expandedPaths is not None : self.setExpandedPaths( expandedPaths ) self.setSelectedPaths( selectedPaths, scrollToFirst = False, expandNonLeaf = False ) self.__currentDir = dirPath self.__currentPath = str( self.__path )
def __qtClipboardContentsChanged( self ) : if self.__ignoreQtClipboardContentsChanged : return QtGui = GafferUI._qtImport( "QtGui" ) text = str( QtGui.QApplication.clipboard().text() ) if text : with Gaffer.BlockedConnection( self.__clipboardContentsChangedConnection ) : self.root().setClipboardContents( IECore.StringData( text ) )
def __clipboardContentsChanged( self, applicationRoot ) : assert( applicationRoot.isSame( self.root() ) ) data = applicationRoot.getClipboardContents() QtGui = GafferUI._qtImport( "QtGui" ) clipboard = QtGui.QApplication.clipboard() try : self.__ignoreQtClipboardContentsChanged = True # avoid triggering an unecessary copy back in __qtClipboardContentsChanged clipboard.setText( str( data ) ) finally : self.__ignoreQtClipboardContentsChanged = False
def __clipboardContentsChanged(self, applicationRoot): assert applicationRoot.isSame(self.root()) data = applicationRoot.getClipboardContents() if isinstance(data, IECore.StringData): QtGui = GafferUI._qtImport("QtGui") clipboard = QtGui.QApplication.clipboard() try: self.__ignoreQtClipboardContentsChanged = ( True ) # avoid triggering an unecessary copy back in __qtClipboardContentsChanged clipboard.setText(data.value) finally: self.__ignoreQtClipboardContentsChanged = False
def setExpansion( self, paths ) : self.collapsed.disconnect( self.__collapsed ) self.expanded.disconnect( self.__expanded ) self.collapseAll() # This call is critical to performance - without # it an update is triggered for every call to # setExpanded(). self.scheduleDelayedItemsLayout() _GafferUI._pathListingWidgetSetExpansion( GafferUI._qtAddress( self ), paths ) self.collapsed.connect( self.__collapsed ) self.expanded.connect( self.__expanded ) self.__recalculateColumnSizes() self.expansionChanged.emit()
def __createHoudiniQGLWidget( cls, format ) : try : import hou except ImportError : # we're not in houdini - createQGLWidget() will just make a # normal widget. return None import IECoreHoudini if hasattr( IECoreHoudini, "sharedGLWidget" ) : # In Houdini 14 and 15, Qt is the native UI, and we can access # Houdini's shared QGLWidget directly. return QtOpenGL.QGLWidget( format, shareWidget = GafferUI._qtObject( IECoreHoudini.sharedGLWidget(), QtOpenGL.QGLWidget ) ) # While Qt is the native UI in Houdini 16.0, they have moved away # from QGLWidgets for their Qt5 builds, so we need to force the # Houdini GL context to be current, and share it. IECoreHoudini.makeMainGLContextCurrent() return cls.__createHostedQGLWidget( format )
def setSelection( self, paths, scrollToFirst=True, expandNonLeaf=True ) : assert( isinstance( paths, IECore.PathMatcher ) ) # If there are pending changes to our path model, we must perform # them now, so that the model is valid with respect to the paths # we're trying to select. self.__updateLazily.flush( self ) assert( isinstance( paths, IECore.PathMatcher ) ) selectionModel = self._qtWidget().selectionModel() selectionModel.selectionChanged.disconnect( self.__selectionChangedSlot ) selectionModel.clear() _GafferUI._pathListingWidgetSetSelection( GafferUI._qtAddress( self._qtWidget() ), paths, scrollToFirst, expandNonLeaf ) selectionModel.selectionChanged.connect( self.__selectionChangedSlot ) self.selectionChangedSignal()( self )
def getExpandedPaths( self ) : return _GafferUI._pathListingWidgetGetExpandedPaths( GafferUI._qtAddress( self._qtWidget() ) )
# the OpenGL module loves spewing things into logs, and for some reason # when running in maya 2012 the default log level allows info messages through. # so we set a specific log level on the OpenGL logger to keep it quiet. logging.getLogger("OpenGL").setLevel(logging.WARNING) import IECore import Gaffer import GafferUI # import lazily to improve startup of apps which don't use GL functionality GL = Gaffer.lazyImport("OpenGL.GL") IECoreGL = Gaffer.lazyImport("IECoreGL") QtCore = GafferUI._qtImport("QtCore") QtGui = GafferUI._qtImport("QtGui") QtOpenGL = GafferUI._qtImport("QtOpenGL", lazy=True) ## The GLWidget is a base class for all widgets which wish to draw using OpenGL. # Derived classes override the _draw() method to achieve this. class GLWidget(GafferUI.Widget): ## This enum defines the optional elements of the GL buffer used # for display. BufferOptions = IECore.Enum.create("Alpha", "Depth", "Double") ## Note that you won't always get the buffer options you ask for - a best fit is found # among the available formats. In particular it appears that a depth buffer is often present # even when not requested. def __init__(self, bufferOptions=set(), **kw):
def __init__(self, scriptNode, **kw): self.__row = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, borderWidth=4, spacing=2) GafferUI.Editor.__init__(self, self.__row, scriptNode, **kw) with self.__row: self.__visibilityButton = GafferUI.Button(image="timeline3.png", hasFrame=False) self.__visibilityButtonClickedConnection = self.__visibilityButton.clickedSignal( ).connect(Gaffer.WeakMethod(self.__visibilityButtonClicked)) self.__scriptRangeStart = GafferUI.NumericPlugValueWidget( scriptNode["frameRange"]["start"]) self.__scriptRangeStart.numericWidget().setFixedCharacterWidth(4) self.__scriptRangeStart.setToolTip( self.__scriptRangeStart.getPlug().fullName()) self.__sliderRangeStart = GafferUI.NumericWidget( scriptNode["frameRange"]["start"].getValue()) self.__sliderRangeStart.setFixedCharacterWidth(4) self.__sliderRangeStart.setToolTip("Slider minimum") self.__sliderRangeStartChangedConnection = self.__sliderRangeStart.editingFinishedSignal( ).connect(Gaffer.WeakMethod(self.__sliderRangeChanged)) self.__slider = GafferUI.NumericSlider( value=self.getContext().getFrame(), min=float(scriptNode["frameRange"]["start"].getValue()), max=float(scriptNode["frameRange"]["end"].getValue()), parenting={"expand": True}, ) self.__slider.setPositionIncrement( 0 ) # disable so the slider doesn't mask our global frame increment shortcut self.__sliderValueChangedConnection = self.__slider.valueChangedSignal( ).connect(Gaffer.WeakMethod(self.__valueChanged)) self.__startButton = GafferUI.Button(image="timelineStart.png", hasFrame=False) self.__startButtonClickedConnection = self.__startButton.clickedSignal( ).connect(Gaffer.WeakMethod(self.__startOrEndButtonClicked)) self.__playPause = GafferUI.Button(image="timelinePlay.png", hasFrame=False) self.__playPauseClickedConnection = self.__playPause.clickedSignal( ).connect(Gaffer.WeakMethod(self.__playPauseClicked)) self.__endButton = GafferUI.Button(image="timelineEnd.png", hasFrame=False) self.__endButtonClickedConnection = self.__endButton.clickedSignal( ).connect(Gaffer.WeakMethod(self.__startOrEndButtonClicked)) self.__frame = GafferUI.NumericWidget(self.getContext().getFrame()) self.__frame.setFixedCharacterWidth(5) self.__frame.setToolTip("Current frame") self.__frameChangedConnection = self.__frame.valueChangedSignal( ).connect(Gaffer.WeakMethod(self.__valueChanged)) self.__sliderRangeEnd = GafferUI.NumericWidget( scriptNode["frameRange"]["end"].getValue()) self.__sliderRangeEnd.setFixedCharacterWidth(4) self.__sliderRangeEnd.setToolTip("Slider maximum") self.__sliderRangeEndChangedConnection = self.__sliderRangeEnd.editingFinishedSignal( ).connect(Gaffer.WeakMethod(self.__sliderRangeChanged)) self.__scriptRangeEnd = GafferUI.NumericPlugValueWidget( scriptNode["frameRange"]["end"]) self.__scriptRangeEnd.numericWidget().setFixedCharacterWidth(4) self.__scriptRangeEnd.setToolTip( self.__scriptRangeEnd.getPlug().fullName()) self.__scriptNodePlugSetConnection = scriptNode.plugSetSignal( ).connect(Gaffer.WeakMethod(self.__scriptNodePlugSet)) frameIncrementShortcut = QtWidgets.QShortcut( QtGui.QKeySequence("Right"), self._qtWidget()) frameIncrementShortcut.activated.connect( Gaffer.WeakMethod(self.__incrementFrame)) frameDecrementShortcut = QtWidgets.QShortcut( QtGui.QKeySequence("Left"), self._qtWidget()) frameDecrementShortcut.activated.connect( functools.partial(Gaffer.WeakMethod(self.__incrementFrame), -1)) self.__playback = None self._updateFromContext(set())
def testObjectAtLine(self): cubes = [] names = ("left", "center", "right") for i in range(3): cube = GafferScene.Cube() cube["transform"]["translate"].setValue( imath.V3f((i - 1) * 2.0, 0.0, -2.5)) cube["name"].setValue(names[i]) cubes.append(cube) group = GafferScene.Group() for i, cube in enumerate(cubes): group["in"][i].setInput(cube["out"]) sg = GafferSceneUI.SceneGadget() sg.setScene(group["out"]) sg.setMinimumExpansionDepth(100) with GafferUI.Window() as w: gw = GafferUI.GadgetWidget(sg) w.setVisible(True) self.waitForIdle(10000) vp = gw.getViewportGadget() # This is the single most important line in this test. If you don't set # this to false, you get an orthographic camera, even if you set a # perspective projection. vp.setPlanarMovement(False) c = IECoreScene.Camera() c.setProjection("perspective") c.setFocalLength(35) c.setAperture(imath.V2f(36, 24)) vp.setCamera(c) cameraTransform = imath.M44f() cameraTransform.translate(imath.V3f(0, 0, 2)) vp.setCameraTransform(cameraTransform) self.waitForIdle(10000) # We assume in this case, that gadget space is world space leftCubeDir = IECore.LineSegment3f(imath.V3f(0, 0, 2), imath.V3f(-2, 0, -2)) pathA = sg.objectAt(leftCubeDir) pathB, hitPoint = sg.objectAndIntersectionAt(leftCubeDir) self.assertIsNotNone(pathA) self.assertEqual(pathA, IECore.InternedStringVectorData(["group", "left"])) self.assertEqual(pathA, pathB) self.assertAlmostEqual(hitPoint.x, -2, delta=0.01) self.assertAlmostEqual(hitPoint.y, 0, delta=0.01) self.assertAlmostEqual(hitPoint.z, -2, delta=0.01) centerCubeDir = IECore.LineSegment3f(imath.V3f(0, 0, 1), imath.V3f(0, 0, -1)) pathA = sg.objectAt(centerCubeDir) pathB, hitPoint = sg.objectAndIntersectionAt(centerCubeDir) self.assertIsNotNone(pathA) self.assertEqual(pathA, IECore.InternedStringVectorData(["group", "center"])) self.assertEqual(pathA, pathB) self.assertAlmostEqual(hitPoint.x, 0, delta=0.01) self.assertAlmostEqual(hitPoint.y, 0, delta=0.01) self.assertAlmostEqual(hitPoint.z, -2, delta=0.01) rightCubeDir = IECore.LineSegment3f(imath.V3f(0, 0, 2), imath.V3f(2, 0, -2)) pathA = sg.objectAt(rightCubeDir) pathB, hitPoint = sg.objectAndIntersectionAt(rightCubeDir) self.assertIsNotNone(pathA) self.assertEqual(pathA, IECore.InternedStringVectorData(["group", "right"])) self.assertEqual(pathA, pathB) self.assertAlmostEqual(hitPoint.x, 2, delta=0.01) self.assertAlmostEqual(hitPoint.y, 0, delta=0.01) self.assertAlmostEqual(hitPoint.z, -2, delta=0.01) missDir = IECore.LineSegment3f(imath.V3f(0, 0, 2), imath.V3f(0, 10, -2)) pathA = sg.objectAt(missDir) pathB, hitPoint = sg.objectAndIntersectionAt(missDir) self.assertIsNone(pathA) self.assertIsNone(pathB)
def __init__(self, plug, **kw): self.__column = GafferUI.ListContainer(spacing=4) GafferUI.PlugValueWidget.__init__(self, self.__column, plug, **kw) with self.__column: columns = self.__listingColumns() self.__pathListing = GafferUI.PathListingWidget( _ImagesPath(self.__images(), []), columns=columns, allowMultipleSelection=True, sortable=False, horizontalScrollMode=GafferUI.ScrollMode.Automatic) self.__pathListing.setDragPointer("") self.__pathListing.setHeaderVisible(True) self.__pathListing.selectionChangedSignal().connect( Gaffer.WeakMethod(self.__pathListingSelectionChanged), scoped=False) self.__pathListing.dragEnterSignal().connect(Gaffer.WeakMethod( self.__pathListingDragEnter), scoped=False) self.__pathListing.dragLeaveSignal().connect(Gaffer.WeakMethod( self.__pathListingDragLeave), scoped=False) self.__pathListing.dragMoveSignal().connect(Gaffer.WeakMethod( self.__pathListingDragMove), scoped=False) self.__pathListing.dropSignal().connect(Gaffer.WeakMethod( self.__pathListingDrop), scoped=False) self.keyPressSignal().connect(Gaffer.WeakMethod(self.__keyPress), scoped=False) with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing=4): addButton = GafferUI.Button(image="pathChooser.png", hasFrame=False, toolTip="Load image") addButton.clickedSignal().connect(Gaffer.WeakMethod( self.__addClicked), scoped=False) self.__duplicateButton = GafferUI.Button( image="duplicate.png", hasFrame=False, toolTip= "Duplicate selected image, hold <kbd>alt</kbd> to view copy. [<kbd>Ctrl-D</kbd>]" ) self.__duplicateButton.setEnabled(False) self.__duplicateButton.clickedSignal().connect( Gaffer.WeakMethod(self.__duplicateClicked), scoped=False) self.__exportButton = GafferUI.Button( image="export.png", hasFrame=False, toolTip="Export selected image") self.__exportButton.setEnabled(False) self.__exportButton.clickedSignal().connect(Gaffer.WeakMethod( self.__exportClicked), scoped=False) self.__extractButton = GafferUI.Button( image="extract.png", hasFrame=False, toolTip="Create CatalogueSelect node for selected image") self.__extractButton.setEnabled(False) self.__extractButton.clickedSignal().connect(Gaffer.WeakMethod( self.__extractClicked), scoped=False) GafferUI.Spacer(imath.V2i(0), parenting={"expand": True}) self.__removeButton = GafferUI.Button( image="delete.png", hasFrame=False, toolTip="Remove selected image [<kbd>Delete</kbd>]") self.__removeButton.setEnabled(False) self.__removeButton.clickedSignal().connect(Gaffer.WeakMethod( self.__removeClicked), scoped=False) GafferUI.Divider() with GafferUI.Collapsible(label="Image Properties", collapsed=False): with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=4): with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing=4): GafferUI.Label("Name") self.__nameWidget = GafferUI.NameWidget( graphComponent=None) GafferUI.Label("Description") self.__descriptionWidget = GafferUI.MultiLineStringPlugValueWidget( plug=None) Gaffer.Metadata.plugValueChangedSignal().connect(Gaffer.WeakMethod( self.__plugMetadataValueChanged), scoped=False) self.contextMenuSignal().connect(Gaffer.WeakMethod(self.__contextMenu), scoped=False) self._updateFromPlug()
def testConnectedNodeGadgets(self): script = Gaffer.ScriptNode() # a -> b -> c -> e -> f # | # v # d script["a"] = GafferTest.AddNode() script["b"] = GafferTest.AddNode() script["c"] = GafferTest.AddNode() script["d"] = GafferTest.AddNode() script["e"] = GafferTest.AddNode() script["f"] = GafferTest.AddNode() script["b"]["op1"].setInput(script["a"]["sum"]) script["c"]["op1"].setInput(script["b"]["sum"]) script["d"]["op1"].setInput(script["c"]["sum"]) script["e"]["op1"].setInput(script["c"]["sum"]) script["f"]["op1"].setInput(script["e"]["sum"]) g = GafferUI.GraphGadget(script) # test traversing in both directions u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets(script["b"]) ] self.assertEqual(set(u), set(["a", "c", "d", "e", "f"])) u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets(script["e"]) ] self.assertEqual(set(u), set(["a", "b", "c", "d", "f"])) u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets(script["c"], degreesOfSeparation=1) ] self.assertEqual(set(u), set(["b", "d", "e"])) # test traversing upstream u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets(script["c"], direction=Gaffer.Plug.Direction.In) ] self.assertEqual(set(u), set(["a", "b"])) u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets(script["c"], direction=Gaffer.Plug.Direction.In, degreesOfSeparation=1) ] self.assertEqual(set(u), set(["b"])) # test traversing downstream u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets( script["c"], direction=Gaffer.Plug.Direction.Out) ] self.assertEqual(set(u), set(["d", "e", "f"])) u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets( script["c"], direction=Gaffer.Plug.Direction.Out, degreesOfSeparation=1) ] self.assertEqual(set(u), set(["d", "e"])) # test that invisible nodes are ignored g.setFilter(Gaffer.StandardSet([script["f"], script["e"], script["c"]])) u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets(script["e"]) ] self.assertEqual(set(u), set(["f", "c"])) u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets(script["e"], direction=Gaffer.Plug.Direction.In) ] self.assertEqual(set(u), set(["c"])) u = [ x.node().relativeName(script) for x in g.connectedNodeGadgets( script["e"], direction=Gaffer.Plug.Direction.Out) ] self.assertEqual(set(u), set(["f"]))
def __fixedWidthNumericPlugValueWidget(plug): result = GafferUI.NumericPlugValueWidget(plug) result.numericWidget().setFixedCharacterWidth(5) return result
def _menu(self, pathListing): # asset=0 # assetData=1 # assetFullPath=2 # assetOP=3 # assetOPSourceExistsInHost=4 menuDefinition = IECore.MenuDefinition() selectedData = pathListing.getSelectedColumns() selectedPaths = selectedData['assetFullPath'] selectedPathsOP = selectedData['assetOP'] selectedPathsExistInHost = selectedData['assetOPSourceExistsInHost'] # for path in selectedPaths: selected_source_exists_in_host = False if [ x for x in selectedPathsExistInHost if x ] else True isAssetMenu = [x for x in selectedPaths if len(x.split('/')) > 2] isPublishMenu = [x for x in selectedPaths if len(x.split('/')) <= 2] if isAssetMenu: def checkout(paths): def __SAM_assetList_doImport__(): import genericAsset pb = genericAsset.progressBar( len(paths) + 1, "Importing assets...") for path in paths: pb.step() print path op = assetUtils.assetOP(path) op.doImport() pb.step() pb.close() self._mayaNodeDeleted() # print op, path __SAM_assetList_doImport__() # assetUtils.mayaLazyScriptJob( runOnce=True, idleEvent=__SAM_assetList_doImport__) canImport = selected_source_exists_in_host if len(selectedPaths) == 1: if 'renderSettings/' in selectedPaths[0]: canImport = True menuDefinition.append( "/Import selected", { "command": IECore.curry(checkout, selectedPaths), "active": canImport }) if len(selectedPaths) == 1: menuDefinition.append("/ ", {}) menuDefinition.append("/ ", {}) def updateAsset(asset): op = assetUtils.assetOP(asset[0]) op.updatePublish() menuDefinition.append( "/Publish an UPDATE to the selected asset", { "command": IECore.curry(updateAsset, selectedPaths), "active": not selected_source_exists_in_host }) menuDefinition.append("/", {}) def mayaImportDependency(paths): def __SAM_assetList_mayaImportDependency__(): # print paths for path in paths: # print path op = assetUtils.assetOP(path) op.mayaImportDependency() if assetUtils.m: assetUtils.mayaLazyScriptJob( runOnce=True, idleEvent=__SAM_assetList_mayaImportDependency__) else: __SAM_assetList_mayaImportDependency__() def mayaOpenDependency(paths): def __SAM_assetList_mayaOpenDependency__(): # print paths for path in paths: # print path op = assetUtils.assetOP(path) op.mayaOpenDependency() if assetUtils.m: assetUtils.mayaLazyScriptJob( runOnce=True, idleEvent=__SAM_assetList_mayaOpenDependency__) else: __SAM_assetList_mayaOpenDependency__() if assetUtils.m: menuDefinition.append( "/open original scene in this maya session ", { "command": IECore.curry(mayaImportDependency, selectedPaths) }) menuDefinition.append( "/open original scene in a new maya session ", { "command": IECore.curry(mayaOpenDependency, selectedPaths) }) elif isPublishMenu and len(selectedPaths) == 1: from pprint import pprint types = assetUtils.Asset.types(refresh=True) atype = selectedPaths[0].split('/')[0] def createNewAsset(paths, assetType): op = assetUtils.assetOP(assetType) op.newPublish() for t in [x for x in types if atype in x.split('/')]: if 'render/' in t: if assetUtils.m and 'render/maya' not in t: continue menuDefinition.append( "/publish new asset of type %s" % t.replace('/', '_'), { "command": IECore.curry(createNewAsset, selectedPaths, t) }) self.__menu = GafferUI.Menu(menuDefinition) if len(menuDefinition.items()): self.__menu.popup(parent=pathListing.ancestor(GafferUI.Window)) return True
def __nodeGadget(pathFilter): nodeGadget = GafferUI.StandardNodeGadget(pathFilter) addObjectDropTarget(nodeGadget) return nodeGadget
which the `paths` are relative to. This can be useful when working on a single asset in isolation, and then placing it into multiple locations within a layout. When no filter is connected, all `paths` are treated as being relative to `/`, the true scene root. """, "plugValueWidget:type", "", ], }) ########################################################################## # NodeGadget drop handler ########################################################################## GafferUI.Pointer.registerPointer( "addObjects", GafferUI.Pointer("addObjects.png", imath.V2i(36, 18))) GafferUI.Pointer.registerPointer( "removeObjects", GafferUI.Pointer("removeObjects.png", imath.V2i(36, 18))) GafferUI.Pointer.registerPointer( "replaceObjects", GafferUI.Pointer("replaceObjects.png", imath.V2i(36, 18))) __DropMode = IECore.Enum.create("None", "Add", "Remove", "Replace") __originalDragPointer = None def __pathsPlug(node): for plug in node.children(Gaffer.Plug): if Gaffer.Metadata.value(plug, "ui:scene:acceptsPaths"):
def __init__( self, plug, **kw ) : column = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing = 4 ) GafferUI.PlugValueWidget.__init__( self, column, plug, **kw ) with column : with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing = 4 ) : GafferUI.Label( "Display Mode" ) drawModeWidget = GafferUI.MultiSelectionMenu( allowMultipleSelection = False, allowEmptySelection = False ) drawModeWidget.append( "Ramp" ) drawModeWidget.append( "Curves" ) drawModeWidget.setSelection( "Ramp" ) drawModeWidget.selectionChangedSignal().connect( Gaffer.WeakMethod( self.__drawModeChanged ), scoped = False ) GafferUI.Spacer( imath.V2i( 0 ), parenting = { "expand" : True } ) # TODO: Since we don't have a good way to register metadata on child plugs, we just write the # metadata on this child plug right before constructing a widget for it. There should probably # be some way to do this genericly during initialization Gaffer.Metadata.registerValue( plug['interpolation'], "plugValueWidget:type", "GafferUI.PresetsPlugValueWidget", persistent=False ) for name, value in sorted( Gaffer.SplineDefinitionInterpolation.names.items() ): Gaffer.Metadata.registerValue( plug['interpolation'], "preset:" + name, value, persistent=False ) GafferUI.PlugWidget( GafferUI.PlugValueWidget.create( plug["interpolation"] ) ) self.__splineWidget = GafferUI.SplineWidget() if isinstance( plug, ( Gaffer.SplinefColor3fPlug, Gaffer.SplinefColor4fPlug ) ) : self.__splineWidget.setDrawMode( self.__splineWidget.DrawMode.Ramp ) else: self.__splineWidget.setDrawMode( self.__splineWidget.DrawMode.Splines ) self.__splineWidget._qtWidget().setMinimumHeight( 50 ) self.__slider = GafferUI.Slider() self.__slider.setMinimumSize( 2 ) self.__positionsChangedConnection = self.__slider.positionChangedSignal().connect( Gaffer.WeakMethod( self.__positionsChanged ), scoped = False ) self.__slider.indexRemovedSignal().connect( Gaffer.WeakMethod( self.__indexRemoved ), scoped = False ) self.__slider.selectedIndexChangedSignal().connect( Gaffer.WeakMethod( self.__selectedIndexChanged ), scoped = False ) self.__lastPositionChangedReason = None self.__positionsMergeGroupId = 0 with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing = 4 ) : self.__positionLabel = GafferUI.LabelPlugValueWidget( plug.pointXPlug( 0 ) ) self.__positionField = GafferUI.NumericPlugValueWidget( plug.pointXPlug( 0 ) ) self.__valueLabel = GafferUI.LabelPlugValueWidget( plug.pointYPlug( 0 ) ) if isinstance( plug.pointYPlug( 0 ), Gaffer.FloatPlug ): self.__valueField = GafferUI.NumericPlugValueWidget( plug.pointYPlug( 0 ) ) else: self.__valueField = GafferUI.ColorPlugValueWidget( plug.pointYPlug( 0 ) ) self.setPlug( plug )
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ########################################################################## import warnings import Gaffer import GafferUI QtGui = GafferUI._qtImport("QtGui") ## The PlugWidget combines a LabelPlugValueWidget with a second PlugValueWidget ## suitable for editing the plug. ## \todo This could provide functionality for arbitrary Widgets to be placed ## on the right, which combined with the ability to find a ## PlugWidget given a Plug could be quite useful for many things. ## \todo Remove deprecated label and description capabilities. class PlugWidget(GafferUI.Widget): def __init__(self, plugOrWidget, label=None, description=None, **kw): GafferUI.Widget.__init__(self, QtGui.QWidget(), **kw) layout = QtGui.QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0)
def getColumns( self ) : return _GafferUI._pathListingWidgetGetColumns( GafferUI._qtAddress( self._qtWidget() ) )
class GraphEditor( GafferUI.Editor ) : def __init__( self, scriptNode, **kw ) : # We want to disable precise navigation motions as they interfere # with our keyboard shortcuts and aren't that useful in the graph viewportGadget = GafferUI.ViewportGadget() viewportGadget.setPreciseMotionAllowed( False ) viewportGadget.setMaxPlanarZoom( imath.V2f( 25 ) ) self.__gadgetWidget = GafferUI.GadgetWidget( gadget = viewportGadget, bufferOptions = set( [ GafferUI.GLWidget.BufferOptions.Double, ] ), ) GafferUI.Editor.__init__( self, self.__gadgetWidget, scriptNode, **kw ) graphGadget = GafferUI.GraphGadget( self.scriptNode() ) self.__rootChangedConnection = graphGadget.rootChangedSignal().connect( Gaffer.WeakMethod( self.__rootChanged ) ) self.__gadgetWidget.getViewportGadget().setPrimaryChild( graphGadget ) self.__gadgetWidget.getViewportGadget().setDragTracking( GafferUI.ViewportGadget.DragTracking.XDragTracking | GafferUI.ViewportGadget.DragTracking.YDragTracking ) self.__frame( scriptNode.selection() ) self.__gadgetWidget.buttonPressSignal().connect( Gaffer.WeakMethod( self.__buttonPress ), scoped = False ) self.__gadgetWidget.keyPressSignal().connect( Gaffer.WeakMethod( self.__keyPress ), scoped = False ) self.__gadgetWidget.buttonDoubleClickSignal().connect( Gaffer.WeakMethod( self.__buttonDoubleClick ), scoped = False ) self.dragEnterSignal().connect( Gaffer.WeakMethod( self.__dragEnter ), scoped = False ) self.dragLeaveSignal().connect( Gaffer.WeakMethod( self.__dragLeave ), scoped = False ) self.dropSignal().connect( Gaffer.WeakMethod( self.__drop ), scoped = False ) self.__gadgetWidget.getViewportGadget().preRenderSignal().connect( Gaffer.WeakMethod( self.__preRender ), scoped = False ) with GafferUI.ListContainer( borderWidth = 8, spacing = 0 ) as overlay : with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, parenting = { "verticalAlignment" : GafferUI.VerticalAlignment.Top, } ) : GafferUI.Spacer( imath.V2i( 1 ) ) GafferUI.MenuButton( image = "annotations.png", hasFrame = False, menu = GafferUI.Menu( Gaffer.WeakMethod( self.__annotationsMenu ), title = "Annotations" ) ) self.__gadgetWidget.addOverlay( overlay ) self.__nodeMenu = None ## Returns the internal GadgetWidget holding the GraphGadget. def graphGadgetWidget( self ) : return self.__gadgetWidget ## Returns the internal Gadget used to draw the graph. This may be # modified directly to set up appropriate filters etc. This is just # a convenience method returning graphGadgetWidget().getViewportGadget().getPrimaryChild(). def graphGadget( self ) : return self.graphGadgetWidget().getViewportGadget().getPrimaryChild() ## Frames the specified nodes in the viewport. If extend is True # then the current framing will be extended to include the specified # nodes, if False then the framing will be reset to frame only the # nodes specified. def frame( self, nodes, extend=False ) : self.__frame( nodes, extend ) def getTitle( self ) : title = super( GraphEditor, self ).getTitle() if title: return title result = IECore.CamelCase.toSpaced( self.__class__.__name__ ) root = self.graphGadget().getRoot() if not root.isSame( self.scriptNode() ) : result += " : " + root.relativeName( self.scriptNode() ).replace( ".", " / " ) return result __plugContextMenuSignal = Gaffer.Signal3() ## Returns a signal which is emitted to create a context menu for a # plug in the graph. Slots may connect to this signal to edit the # menu definition on the fly - the signature for the signal is # ( graphEditor, plug, menuDefinition ) and the menu definition should just be # edited in place. @classmethod def plugContextMenuSignal( cls ) : return cls.__plugContextMenuSignal __connectionContextMenuSignal = Gaffer.Signal3() ## Returns a signal which is emitted to create a context menu for a # connection in the graph. Slots may connect to this signal to edit the # menu definition on the fly - the signature for the signal is # ( graphEditor, destinationPlug, menuDefinition ) and the menu definition # should just be edited in place. @classmethod def connectionContextMenuSignal( cls ) : return cls.__connectionContextMenuSignal @classmethod def appendConnectionNavigationMenuDefinitions( cls, graphEditor, destinationPlug, menuDefinition ) : def __append( plug, label ) : node = plug.node() nodeGadget = graphEditor.graphGadget().nodeGadget( node ) if node is not None else None menuDefinition.append( "/Navigate/Go to " + label, { "active" : nodeGadget is not None, "command" : functools.partial( Gaffer.WeakMethod( graphEditor.frame ), [ node ] ) } ) menuDefinition.append( "/Navigate/Select " + label, { "active" : node is not None, "command" : functools.partial( cls.__select, node ) } ) __append( destinationPlug.getInput(), "Source Node" ) __append( destinationPlug, "Destination Node" ) __nodeContextMenuSignal = Gaffer.Signal3() ## Returns a signal which is emitted to create a context menu for a # node in the graph. Slots may connect to this signal to edit the # menu definition on the fly - the signature for the signal is # ( graphEditor, node, menuDefinition ) and the menu definition should just be # edited in place. Typically you would add slots to this signal # as part of a startup script. @classmethod def nodeContextMenuSignal( cls ) : return cls.__nodeContextMenuSignal ## May be used from a slot attached to nodeContextMenuSignal() to install some # standard menu items for modifying the connection visibility for a node. @classmethod def appendConnectionVisibilityMenuDefinitions( cls, graphEditor, node, menuDefinition ) : def plugDirectionsWalk( gadget ) : result = set() if isinstance( gadget, GafferUI.Nodule ) : result.add( gadget.plug().direction() ) for c in gadget.children() : result |= plugDirectionsWalk( c ) return result plugDirections = plugDirectionsWalk( graphEditor.graphGadget().nodeGadget( node ) ) if not plugDirections : return readOnly = Gaffer.MetadataAlgo.readOnly( node ) menuDefinition.append( "/ConnectionVisibilityDivider", { "divider" : True } ) if Gaffer.Plug.Direction.In in plugDirections : menuDefinition.append( "/Show Input Connections", { "checkBox" : functools.partial( cls.__getNodeInputConnectionsVisible, graphEditor.graphGadget(), node ), "command" : functools.partial( cls.__setNodeInputConnectionsVisible, graphEditor.graphGadget(), node ), "active" : not readOnly, } ) if Gaffer.Plug.Direction.Out in plugDirections : menuDefinition.append( "/Show Output Connections", { "checkBox" : functools.partial( cls.__getNodeOutputConnectionsVisible, graphEditor.graphGadget(), node ), "command" : functools.partial( cls.__setNodeOutputConnectionsVisible, graphEditor.graphGadget(), node ), "active" : not readOnly } ) ## May be used from a slot attached to nodeContextMenuSignal() to install a # standard menu item for modifying the enabled state of a node. @classmethod def appendEnabledPlugMenuDefinitions( cls, graphEditor, node, menuDefinition ) : enabledPlug = node.enabledPlug() if isinstance( node, Gaffer.DependencyNode ) else None if enabledPlug is not None : menuDefinition.append( "/EnabledDivider", { "divider" : True } ) menuDefinition.append( "/Enabled", { "command" : functools.partial( cls.__setEnabled, node ), "checkBox" : enabledPlug.getValue(), "active" : enabledPlug.settable() and not Gaffer.MetadataAlgo.readOnly( enabledPlug ) } ) @classmethod def appendContentsMenuDefinitions( cls, graphEditor, node, menuDefinition ) : menuDefinition.append( "/FocusDivider", { "divider" : True } ) menuDefinition.append( "/Focus", { "command" : functools.partial( graphEditor.scriptNode().setFocus, node ), "active" : not node.isSame( graphEditor.scriptNode().getFocus() ), "shortCut" : "Ctrl+`" } ) if not GraphEditor.__childrenViewable( node ) : return menuDefinition.append( "/ContentsDivider", { "divider" : True } ) menuDefinition.append( "/Show Contents...", { "command" : functools.partial( cls.acquire, node ) } ) __nodeDoubleClickSignal = GafferUI.WidgetEventSignal() ## Returns a signal which is emitted whenever a node is double clicked. # Slots should have the signature ( graphEditor, node ). @classmethod def nodeDoubleClickSignal( cls ) : return cls.__nodeDoubleClickSignal ## Ensures that the specified node has a visible GraphEditor viewing # it, and returns that editor. ## \todo Consider how this relates to the todo items in NodeSetEditor.acquire(). @classmethod def acquire( cls, rootNode ) : if isinstance( rootNode, Gaffer.ScriptNode ) : script = rootNode else : script = rootNode.scriptNode() scriptWindow = GafferUI.ScriptWindow.acquire( script ) tabbedContainer = None for editor in scriptWindow.getLayout().editors( type = GafferUI.GraphEditor ) : if rootNode.isSame( editor.graphGadget().getRoot() ) : editor.parent().setCurrent( editor ) return editor editor = GraphEditor( script ) editor.graphGadget().setRoot( rootNode ) scriptWindow.getLayout().addEditor( editor ) return editor def __repr__( self ) : return "GafferUI.GraphEditor( scriptNode )" def _nodeMenu( self ) : if self.__nodeMenu is None : self.__nodeMenu = GafferUI.Menu( GafferUI.NodeMenu.acquire( self.scriptNode().applicationRoot() ).definition(), searchable=True ) self.__nodeMenu.visibilityChangedSignal().connect( Gaffer.WeakMethod( self.__nodeMenuVisibilityChanged ), scoped = False ) return self.__nodeMenu def __nodeMenuVisibilityChanged( self, widget ) : assert( widget is self.__nodeMenu ) if not self.__nodeMenu.visible() : # generally we steal focus on mouse enter (implemented in GadgetWidget), # but when the node menu closes we may not get an enter event, so we have to steal # the focus back here. self.__gadgetWidget._qtWidget().setFocus() def __buttonPress( self, widget, event ) : if event.buttons & GafferUI.ButtonEvent.Buttons.Right : # right click - display either the node creation popup menu # or a menu specific to the node/plug/connection under the # mouse if possible. viewport = self.__gadgetWidget.getViewportGadget() gadgets = viewport.gadgetsAt( imath.V2f( event.line.p1.x, event.line.p1.y ) ) if len( gadgets ) : overrideMenuDefinition = IECore.MenuDefinition() overrideMenuTitle = None if isinstance( gadgets[0], GafferUI.Nodule ) : self.plugContextMenuSignal()( self, gadgets[0].plug(), overrideMenuDefinition ) overrideMenuTitle = gadgets[0].plug().relativeName( self.graphGadget().getRoot() ) elif isinstance( gadgets[0], GafferUI.ConnectionGadget ) : self.connectionContextMenuSignal()( self, gadgets[0].dstNodule().plug(), overrideMenuDefinition ) overrideMenuTitle = "-> " + gadgets[0].dstNodule().plug().relativeName( self.graphGadget().getRoot() ) else : nodeGadget = gadgets[0] if not isinstance( nodeGadget, GafferUI.NodeGadget ) : nodeGadget = nodeGadget.ancestor( GafferUI.NodeGadget ) if nodeGadget is not None : self.nodeContextMenuSignal()( self, nodeGadget.node(), overrideMenuDefinition ) overrideMenuTitle = nodeGadget.node().getName() if len( overrideMenuDefinition.items() ) : menuDefinition = overrideMenuDefinition self._m = GafferUI.Menu( menuDefinition, title=overrideMenuTitle ) self._m.popup( self ) return True if not ( Gaffer.MetadataAlgo.getChildNodesAreReadOnly( self.graphGadget().getRoot() ) or Gaffer.MetadataAlgo.readOnly( self.graphGadget().getRoot() ) ): self._nodeMenu().popup( self ) return True return False def __nodeGadgetAt( self, position ) : viewport = self.__gadgetWidget.getViewportGadget() line = viewport.rasterToGadgetSpace( imath.V2f( position.x, position.y ), gadget = self.graphGadget() ) return self.graphGadget().nodeGadgetAt( line ) def __keyPress( self, widget, event ) : if event.key == "F" and not event.modifiers : self.__frame( self.scriptNode().selection() ) return True elif event.key == "QuoteLeft" and not event.modifiers : self.__frame( [ self.scriptNode().getFocus() ] ) return True elif event.key == "QuoteLeft" and event.modifiers == event.modifiers.Control : selection = self.scriptNode().selection() if len( selection ) > 0 : self.scriptNode().setFocus( selection[0] ) elif event.key == "Down" : selection = self.scriptNode().selection() if selection.size() == 1 and selection[0].parent() == self.graphGadget().getRoot() : needsModifiers = not GraphEditor.__childrenViewable( selection[0] ) if ( ( needsModifiers and event.modifiers == event.modifiers.Shift | event.modifiers.Control ) or ( not needsModifiers and event.modifiers == event.modifiers.None_ ) ) : self.graphGadget().setRoot( selection[0] ) return True elif event.key == "Up" : root = self.graphGadget().getRoot() if not isinstance( root, Gaffer.ScriptNode ) : self.graphGadget().setRoot( root.parent() ) return True elif event.key == "Tab" : if not ( Gaffer.MetadataAlgo.getChildNodesAreReadOnly( self.graphGadget().getRoot() ) or Gaffer.MetadataAlgo.readOnly( self.graphGadget().getRoot() ) ): self._nodeMenu().popup( self ) return True return False def __frame( self, nodes, extend = False, at = None ) : graphGadget = self.graphGadget() # get the bounds of the nodes bound = imath.Box3f() for node in nodes : nodeGadget = graphGadget.nodeGadget( node ) if nodeGadget : bound.extendBy( nodeGadget.transformedBound( graphGadget ) ) # if there were no nodes then use the bound of the whole # graph. if bound.isEmpty() : bound = graphGadget.bound() # if there's still nothing then an arbitrary area in the centre of the world if bound.isEmpty() : bound = imath.Box3f( imath.V3f( -10, -10, 0 ), imath.V3f( 10, 10, 0 ) ) # pad it a little bit so # it sits nicer in the frame bound.setMin( bound.min() - imath.V3f( 1, 1, 0 ) ) bound.setMax( bound.max() + imath.V3f( 1, 1, 0 ) ) if extend : # we're extending the existing framing, which we assume the # user was happy with other than it not showing the nodes in question. # so we just take the union of the existing frame and the one for the nodes. cb = self.__currentFrame() bound.extendBy( imath.Box3f( imath.V3f( cb.min().x, cb.min().y, 0 ), imath.V3f( cb.max().x, cb.max().y, 0 ) ) ) else : # we're reframing from scratch, so the frame for the nodes is all we need. # we do however want to make sure that we don't zoom in too far if the node # bounds are small, as having a single node filling the screen is of little use - # it's better to see some context around it. boundSize = bound.size() widgetSize = imath.V3f( self._qtWidget().width(), self._qtWidget().height(), 0 ) pixelsPerUnit = min( widgetSize.x / boundSize.x, widgetSize.y / boundSize.y ) adjustedPixelsPerUnit = min( pixelsPerUnit, 10 ) newBoundSize = widgetSize / adjustedPixelsPerUnit boundCenter = bound.center() bound.setMin( boundCenter - newBoundSize / 2.0 ) bound.setMax( boundCenter + newBoundSize / 2.0 ) self.__gadgetWidget.getViewportGadget().frame( bound ) if at is not None : # Offset the bound and reframe so that the centre of the bound moves # to `at`, which is specified in raster space. We have to do this # _after_ the initial call to `frame()` because # `rasterToGadgetSpace()` is affected by the zoom calculated by # `frame()`. Because we are not changing the size of the bound, the # second call to `frame()` will use the same zoom. viewport = self.graphGadgetWidget().getViewportGadget() offset = viewport.rasterToGadgetSpace( imath.V2f( self.bound().size() / 2 ), graphGadget ).p0 - viewport.rasterToGadgetSpace( at, graphGadget ).p0 bound.setMin( bound.min() + offset ) bound.setMax( bound.max() + offset ) self.__gadgetWidget.getViewportGadget().frame( bound ) def __buttonDoubleClick( self, widget, event ) : nodeGadget = self.__nodeGadgetAt( event.line.p1 ) if nodeGadget is not None : return self.nodeDoubleClickSignal()( self, nodeGadget.node() ) def __dragEnter( self, widget, event ) : if event.sourceWidget is self.__gadgetWidget : return False if self.__dropNodes( event.data ) : self.__dragEnterPointer = GafferUI.Pointer.getCurrent() GafferUI.Pointer.setCurrent( "target" ) return True return False def __dragLeave( self, widget, event ) : GafferUI.Pointer.setCurrent( self.__dragEnterPointer ) return True def __drop( self, widget, event ) : if event.sourceWidget is self.__gadgetWidget : return False dropNodes = self.__dropNodes( event.data ) if dropNodes : self.graphGadget().setRoot( dropNodes[0].parent() ) self.__frame( dropNodes, at = imath.V2f( event.line.p0.x, event.line.p0.y ) ) return True return False def __dropNodes( self, dragData ) : if isinstance( dragData, Gaffer.Node ) : return [ dragData ] elif isinstance( dragData, Gaffer.Set ) : nodes = [ x for x in dragData if isinstance( x, Gaffer.Node ) ] if len( set( n.parent() for n in nodes ) ) == 1 : # Can only frame nodes if they all share the same parent. return nodes return [] def __currentFrame( self ) : viewportGadget = self.graphGadgetWidget().getViewportGadget() rasterMin = viewportGadget.rasterToWorldSpace( imath.V2f( 0 ) ).p0 rasterMax = viewportGadget.rasterToWorldSpace( imath.V2f( viewportGadget.getViewport() ) ).p0 frame = imath.Box2f() frame.extendBy( imath.V2f( rasterMin[0], rasterMin[1] ) ) frame.extendBy( imath.V2f( rasterMax[0], rasterMax[1] ) ) return frame def __rootChanged( self, graphGadget, previousRoot ) : # save/restore the current framing so jumping in # and out of Boxes isn't a confusing experience. Gaffer.Metadata.registerValue( previousRoot, "ui:graphEditor:framing", self.__currentFrame(), persistent = False ) frame = Gaffer.Metadata.value( self.graphGadget().getRoot(), "ui:graphEditor:framing" ) if frame is not None : self.graphGadgetWidget().getViewportGadget().frame( imath.Box3f( imath.V3f( frame.min().x, frame.min().y, 0 ), imath.V3f( frame.max().x, frame.max().y, 0 ) ) ) else : self.__frame( self.graphGadget().getRoot().children( Gaffer.Node ) ) # do what we need to do to keep our title up to date. self.__changeConnections = [] if not graphGadget.getRoot().isSame( self.scriptNode() ) : # We have to track the root _and_ its ancestors node = graphGadget.getRoot() while node and not isinstance( node, Gaffer.ScriptNode ) : self.__changeConnections.append( node.nameChangedSignal().connect( Gaffer.WeakMethod( self.__rootNameChanged ) ) ) self.__changeConnections.append( node.parentChangedSignal().connect( Gaffer.WeakMethod( self.__rootParentChanged ) ) ) node = node.parent() self.titleChangedSignal()( self ) def __rootNameChanged( self, root ) : self.titleChangedSignal()( self ) def __rootParentChanged( self, node, oldParent ) : # This may be called for our root, or any of its parents if node.parent() == None : self.graphGadget().setRoot( self.scriptNode() ) def __preRender( self, viewportGadget ) : # Find all unpositioned nodes. graphGadget = self.graphGadget() nodes = [ g.node() for g in graphGadget.unpositionedNodeGadgets() ] if not nodes : return nodes = Gaffer.StandardSet( nodes ) # Lay them out somewhere near the centre of frame. gadgetWidget = self.graphGadgetWidget() fallbackPosition = gadgetWidget.getViewportGadget().rasterToGadgetSpace( imath.V2f( gadgetWidget.size() ) / 2.0, gadget = graphGadget ).p0 fallbackPosition = imath.V2f( fallbackPosition.x, fallbackPosition.y ) graphGadget.getLayout().positionNodes( graphGadget, nodes, fallbackPosition ) graphGadget.getLayout().layoutNodes( graphGadget, nodes ) # And then extend the frame to include them, in case the # layout has gone off screen. self.frame( nodes, extend = True ) def __annotationsMenu( self ) : graphGadget = self.graphGadget() annotationsGadget = graphGadget["__annotations"] annotations = Gaffer.MetadataAlgo.annotationTemplates() + [ "user", annotationsGadget.untemplatedAnnotations ] visiblePattern = annotationsGadget.getVisibleAnnotations() visibleAnnotations = { a for a in annotations if IECore.StringAlgo.matchMultiple( a, visiblePattern ) } result = IECore.MenuDefinition() result.append( "All", { "checkBox" : len( visibleAnnotations ) == len( annotations ), "command" : functools.partial( Gaffer.WeakMethod( self.__setVisibleAnnotations ), annotations = { "*" } ) } ) result.append( "None", { "checkBox" : len( visibleAnnotations ) == 0, "command" : functools.partial( Gaffer.WeakMethod( self.__setVisibleAnnotations ), annotations = set() ) } ) result.append( "__annotationsDivider__", { "divider" : True } ) def appendMenuItem( annotation, label = None ) : if label is None : # Support snake_case and CamelCase for conversion of name to label, # since not all extensions use the Gaffer convention. labelParts = annotation.split( ":" ) label = "/".join( ( " ".join( x.title() for x in p.split( "_" ) ) ) if "_" in p else IECore.CamelCase.toSpaced( p ) for p in labelParts ) if annotation in visibleAnnotations : toggled = visibleAnnotations - { annotation } else : toggled = visibleAnnotations | { annotation } result.append( "/" + label, { "checkBox" : annotation in visibleAnnotations, "command" : functools.partial( Gaffer.WeakMethod( self.__setVisibleAnnotations ), annotations = toggled ) } ) userAnnotations = set( Gaffer.MetadataAlgo.annotationTemplates( userOnly = True ) ) for annotation in sorted( userAnnotations ) : appendMenuItem( annotation ) if len( userAnnotations ) : result.append( "__userDivider__", { "divider" : True } ) appendMenuItem( "user" ) result.append( "__nonUserDivider__", { "divider" : True } ) for annotation in sorted( Gaffer.MetadataAlgo.annotationTemplates() ) : if annotation not in userAnnotations : appendMenuItem( annotation ) result.append( "__otherDivider__", { "divider" : True } ) appendMenuItem( annotationsGadget.untemplatedAnnotations, label = "Other" ) return result def __setVisibleAnnotations( self, unused, annotations ) : annotationsGadget = self.graphGadget()["__annotations"] pattern = " ".join( a.replace( " ", r"\ " ) for a in annotations ) annotationsGadget.setVisibleAnnotations( pattern ) @classmethod def __getNodeInputConnectionsVisible( cls, graphGadget, node ) : return not graphGadget.getNodeInputConnectionsMinimised( node ) @classmethod def __setNodeInputConnectionsVisible( cls, graphGadget, node, value ) : with Gaffer.UndoScope( node.ancestor( Gaffer.ScriptNode ) ) : graphGadget.setNodeInputConnectionsMinimised( node, not value ) @classmethod def __getNodeOutputConnectionsVisible( cls, graphGadget, node ) : return not graphGadget.getNodeOutputConnectionsMinimised( node ) @classmethod def __setNodeOutputConnectionsVisible( cls, graphGadget, node, value ) : with Gaffer.UndoScope( node.ancestor( Gaffer.ScriptNode ) ) : graphGadget.setNodeOutputConnectionsMinimised( node, not value ) @classmethod def __setEnabled( cls, node, value ) : with Gaffer.UndoScope( node.ancestor( Gaffer.ScriptNode ) ) : node.enabledPlug().setValue( value ) @staticmethod def __childrenViewable( node ) : viewable = Gaffer.Metadata.value( node, "graphEditor:childrenViewable" ) if viewable is not None : return viewable ## \todo: Remove nodeGraph fallback when all client code has been updated return Gaffer.Metadata.value( node, "nodeGraph:childrenViewable" ) @staticmethod def __select( node ) : node.scriptNode().selection().clear() node.scriptNode().selection().add( node )
def testOwner(self): t = GafferUI.TabbedContainer() self.failUnless(GafferUI.Widget._owner(t._qtWidget()) is t)
IECore.registerRunTimeTyped(_Camera) GafferUI.NodeToolbar.registerCreator(_Camera.staticTypeId(), GafferUI.StandardNodeToolbar) GafferUI.PlugValueWidget.registerCreator(_Camera.staticTypeId(), "in", None) GafferUI.PlugValueWidget.registerCreator(_Camera.staticTypeId(), "out", None) GafferUI.PlugValueWidget.registerCreator(_Camera.staticTypeId(), "user", None) GafferUI.PlugValueWidget.registerCreator( _Camera.staticTypeId(), "lookAt", lambda plug: GafferUI.PathPlugValueWidget( plug, path=GafferScene.ScenePath(plug.node()["in"], plug.node().scriptNode().context(), "/"), ), ) def __fixedWidthNumericPlugValueWidget(plug): result = GafferUI.NumericPlugValueWidget(plug) result.numericWidget().setFixedCharacterWidth(5) return result GafferUI.PlugValueWidget.registerCreator(_Camera.staticTypeId(), "depth", __fixedWidthNumericPlugValueWidget)
def testExceptionsDuringCompute(self): # Make this scene # # - bigSphere # - littleSphere (with exception in attributes expression) s = Gaffer.ScriptNode() s["s1"] = GafferScene.Sphere() s["s1"]["name"].setValue("bigSphere") s["s2"] = GafferScene.Sphere() s["s2"]["name"].setValue("littleSphere") s["s2"]["radius"].setValue(0.1) s["p"] = GafferScene.Parent() s["p"]["in"].setInput(s["s1"]["out"]) s["p"]["children"][0].setInput(s["s2"]["out"]) s["p"]["parent"].setValue("/bigSphere") s["a"] = GafferScene.StandardAttributes() s["a"]["in"].setInput(s["p"]["out"]) s["a"]["attributes"]["doubleSided"]["enabled"].setValue(True) s["e"] = Gaffer.Expression() s["e"].setExpression( 'parent["a"]["attributes"]["doubleSided"]["value"] = context["nonexistent"]' ) s["f"] = GafferScene.PathFilter() s["f"]["paths"].setValue( IECore.StringVectorData(["/bigSphere/littleSphere"])) s["a"]["filter"].setInput(s["f"]["out"]) # Try to view it sg = GafferSceneUI.SceneGadget() sg.setScene(s["a"]["out"]) sg.setMinimumExpansionDepth(4) with GafferUI.Window() as w: gw = GafferUI.GadgetWidget(sg) gw.getViewportGadget().setPlanarMovement(False) gw.getViewportGadget().setCamera( IECoreScene.Camera(parameters={ "projection": "perspective", })) originalMessageHandler = IECore.MessageHandler.getDefaultHandler() mh = IECore.CapturingMessageHandler() IECore.MessageHandler.setDefaultHandler( IECore.LevelFilteredMessageHandler( mh, IECore.LevelFilteredMessageHandler.defaultLevel())) try: w.setVisible(True) self.waitForIdle(1000) sg.waitForCompletion() # Check we were told about the problem self.assertEqual(len(mh.messages), 1) self.assertEqual(mh.messages[0].level, mh.Level.Error) self.assertTrue("nonexistent" in mh.messages[0].message) # And that there isn't some half-assed partial scene # being displayed. self.assertTrue(sg.bound().isEmpty()) gw.getViewportGadget().frame( imath.Box3f(imath.V3f(-1), imath.V3f(1))) self.assertObjectAt(sg, imath.V2f(0.5), None) # And that redraws don't cause more fruitless attempts # to compute the scene. gw.getViewportGadget().frame( imath.Box3f(imath.V3f(-1.1), imath.V3f(1.1))) self.waitForIdle(1000) self.assertEqual(len(mh.messages), 1) self.assertObjectAt(sg, imath.V2f(0.5), None) self.assertTrue(sg.bound().isEmpty()) # Fix the problem with the scene, and check that we can see something now s["f"]["enabled"].setValue(False) sg.waitForCompletion() self.assertEqual(len(mh.messages), 1) self.assertFalse(sg.bound().isEmpty()) self.assertObjectAt(sg, imath.V2f(0.5), IECore.InternedStringVectorData(["bigSphere"])) finally: IECore.MessageHandler.setDefaultHandler(originalMessageHandler)
import IECore import Gaffer import GafferUI import IECore import gtk window = GafferUI.Window(title="Frame test") window.gtkWidget().connect("delete_event", gtk.main_quit) t = GafferUI.TextGadget(IECore.Font("/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf"), "hello") f = GafferUI.Frame(t) window.setChild(GafferUI.GadgetWidget(f)) window.show() GafferUI.EventLoop.start()
), }, { "label" : "Alpha Map", "summary" : __alphaMapSummary, "namesAndLabels" : ( ( "as:alpha_map", "Alpha Map" ), ), }, { "label" : "Photons", "summary" : __photonsSummary, "namesAndLabels" : ( ( "as:photon_target", "Photon Target" ), ), }, ), ) GafferUI.PlugValueWidget.registerCreator( GafferAppleseed.AppleseedAttributes, "attributes.alphaMap.value", lambda plug : GafferUI.PathPlugValueWidget( plug, path = Gaffer.FileSystemPath( "/", filter = Gaffer.FileSystemPath.createStandardFilter() ), pathChooserDialogueKeywords = { "bookmarks" : GafferUI.Bookmarks.acquire( plug, category = "appleseed" ), "leaf" : True, }, ), )
def _addButton(self, label): button = GafferUI.Button(label=label) self.__buttonRow.append(button) return button
def __createParameterWidget(plug): return GafferUI.CompoundParameterValueWidget( plug.node().parameterHandler(), collapsible=False)
def __init__(self, childPlug): column = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=4) GafferUI.PlugValueWidget.__init__(self, column, childPlug) with column: with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing=4) as header: collapseButton = GafferUI.Button( image="collapsibleArrowRight.png", hasFrame=False) collapseButton.__clickedConnection = collapseButton.clickedSignal( ).connect(Gaffer.WeakMethod(self.__collapseButtonClicked)) GafferUI.PlugValueWidget.create(childPlug["active"]) self.__label = GafferUI.Label(childPlug["label"].getValue()) GafferUI.Spacer(IECore.V2i(1), maximumSize=IECore.V2i(100000, 1), parenting={"expand": True}) self.__deleteButton = GafferUI.Button(image="delete.png", hasFrame=False) self.__deleteButton.__clickedConnection = self.__deleteButton.clickedSignal( ).connect(Gaffer.WeakMethod(self.__deleteButtonClicked)) self.__deleteButton.setVisible(False) with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical, spacing=4) as self.__detailsColumn: GafferUI.PlugWidget(childPlug["label"]) GafferUI.PlugWidget(childPlug["name"]) GafferUI.PlugWidget(childPlug["type"]) GafferUI.PlugWidget(childPlug["data"]) GafferUI.CompoundDataPlugValueWidget(childPlug["parameters"], collapsed=None) GafferUI.Divider(GafferUI.Divider.Orientation.Horizontal) self.__detailsColumn.setVisible(False) self.__enterConnection = header.enterSignal().connect( Gaffer.WeakMethod(self.__enter)) self.__leaveConnection = header.leaveSignal().connect( Gaffer.WeakMethod(self.__leave))
def getSelectedPaths( self ) : return _GafferUI._pathListingWidgetPathsForPathMatcher( GafferUI._qtAddress( self._qtWidget() ), self.getSelection() )
( "Midpoint", "midpoint" ), ), ) GafferUI.PlugValueWidget.registerCreator( GafferRenderMan.RenderManOptions.staticTypeId(), "options.statisticsLevel.value", GafferUI.EnumPlugValueWidget, labelsAndValues = ( ( "0 (Off)", 0 ), ( "1", 1 ), ( "2", 2 ), ( "3 (Most Verbose)", 3 ), ), ) GafferUI.PlugValueWidget.registerCreator( GafferRenderMan.RenderManOptions.staticTypeId(), "options.statisticsFileName.value", lambda plug : GafferUI.PathPlugValueWidget( plug, path = Gaffer.FileSystemPath( "/", filter = Gaffer.FileSystemPath.createStandardFilter( extensions = ( "htm", "html", "txt", "stats" ) ) ), pathChooserDialogueKeywords = { "bookmarks" : GafferUI.Bookmarks.acquire( plug.ancestor( Gaffer.ApplicationRoot.staticTypeId() ), category = "statistics", ), "leaf" : True, }, ) )
def getSelection( self ) : return _GafferUI._pathListingWidgetGetSelection( GafferUI._qtAddress( self._qtWidget() ) )
def testLookThrough(self): script = Gaffer.ScriptNode() script["sphere"] = GafferScene.Sphere() script["camera"] = GafferScene.Camera() script["camera"]["transform"]["translate"].setValue(imath.V3f(1, 0, 0)) script["group"] = GafferScene.Group() script["group"]["in"][0].setInput(script["sphere"]["out"]) script["group"]["in"][1].setInput(script["camera"]["out"]) with GafferUI.Window() as window: viewer = GafferUI.Viewer(script) window.setVisible(True) viewer.setNodeSet(Gaffer.StandardSet([script["group"]])) view = viewer.view() self.assertTrue(isinstance(view, GafferSceneUI.SceneView)) def setViewCameraTransform(matrix): view.viewportGadget().setCameraTransform(matrix) def getViewCameraTransform(): return view.viewportGadget().getCameraTransform() # Simulate the user translating the camera. setViewCameraTransform(imath.M44f().translate(imath.V3f(100, 0, 0))) self.assertEqual(getViewCameraTransform(), imath.M44f().translate(imath.V3f(100, 0, 0))) # Set the path for the look-through camera, but don't activate it - nothing should have changed. view["camera"]["lookThroughCamera"].setValue("/group/camera") self.assertEqual(getViewCameraTransform(), imath.M44f().translate(imath.V3f(100, 0, 0))) # Enable the look-through - the camera should update. view["camera"]["lookThroughEnabled"].setValue(True) self.waitForIdle(100) self.assertEqual(getViewCameraTransform(), script["group"]["out"].transform("/group/camera")) # Disable the look-through - the camera should revert to its previous position. view["camera"]["lookThroughEnabled"].setValue(False) self.waitForIdle(100) self.assertEqual(getViewCameraTransform(), imath.M44f().translate(imath.V3f(100, 0, 0))) # Simulate the user moving the viewport camera, and then move the (now disabled) look-through # camera. The user movement should win out. setViewCameraTransform(imath.M44f().translate(imath.V3f(200, 0, 0))) self.assertEqual(getViewCameraTransform(), imath.M44f().translate(imath.V3f(200, 0, 0))) script["camera"]["transform"]["translate"].setValue(imath.V3f(2, 0, 0)) self.waitForIdle(100) self.assertEqual(getViewCameraTransform(), imath.M44f().translate(imath.V3f(200, 0, 0))) # Change the viewer context - since look-through is disabled the user camera should not move. viewer.getContext().setFrame(10) self.waitForIdle(100) self.assertEqual(getViewCameraTransform(), imath.M44f().translate(imath.V3f(200, 0, 0))) # Work around "Internal C++ object (PySide.QtWidgets.QWidget) already deleted" error. In an # ideal world we'll fix this, but it's unrelated to what we're testing here. window.removeChild(viewer)
def getDisplayMode( self ) : if _GafferUI._pathListingWidgetGetFlat( GafferUI._qtAddress( self._qtWidget() ) ) : return self.DisplayMode.List else : return self.DisplayMode.Tree
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ########################################################################## from __future__ import with_statement import os import warnings import IECore import Gaffer import GafferUI QtCore = GafferUI._qtImport( "QtCore" ) class PathWidget( GafferUI.TextWidget ) : def __init__( self, path, **kw ) : GafferUI.TextWidget.__init__( self, str( path ), **kw ) # we can be fairly sure that the average path requires a bit more space # than the other things that go in TextWidgets. self.setPreferredCharacterWidth( 60 ) self.__keyPressConnection = self.keyPressSignal().connect( Gaffer.WeakMethod( self.__keyPress ) ) self.__selectingFinishedConnection = self.selectingFinishedSignal().connect( Gaffer.WeakMethod( self.__selectingFinished ) ) self.__textChangedConnection = self.textChangedSignal().connect( Gaffer.WeakMethod( self.__textChanged ) )
def setColumns( self, columns ) : if columns == self.getColumns() : return _GafferUI._pathListingWidgetSetColumns( GafferUI._qtAddress( self._qtWidget() ), columns )
def testReveal(self): with GafferUI.TabbedContainer() as t1: with GafferUI.TabbedContainer() as t2: with GafferUI.ListContainer() as c1: l1 = GafferUI.Label("l1") with GafferUI.ListContainer() as c2: l2 = GafferUI.Label("l2") with GafferUI.TabbedContainer() as t3: with GafferUI.ListContainer() as c3: l3 = GafferUI.Label("l3") with GafferUI.ListContainer() as c4: l4 = GafferUI.Label("l4") l1.reveal() self.assertTrue(t1.getCurrent() is t2) self.assertTrue(t2.getCurrent() is c1) l3.reveal() self.assertTrue(t1.getCurrent() is t3) self.assertTrue(t3.getCurrent() is c3)
"sets", "Specifies a list of tags to be loaded and converted into gaffer sets.", ) ########################################################################## # Widgets ########################################################################## GafferUI.PlugValueWidget.registerCreator( GafferScene.SceneReader, "fileName", lambda plug: GafferUI.PathPlugValueWidget( plug, path=Gaffer.FileSystemPath( "/", filter=Gaffer.FileSystemPath.createStandardFilter( extensions=IECore.SceneInterface.supportedExtensions())), pathChooserDialogueKeywords={ "bookmarks": GafferUI.Bookmarks.acquire(plug, category="sceneCache"), "leaf": True, }, )) ########################################################################## # Right click menu for tags ########################################################################## def __toggleTag(plug, tag, checked): tags = plug.getValue().split() if checked:
def _updateFromSet(self): GafferUI.NodeSetEditor._updateFromSet(self) del self.__column[:] self.__nodeUI = None self.__nameWidget = None node = self._lastAddedNode() if not node: return with self.__column: with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, borderWidth=8, spacing=4): GafferUI.Label("<h4>Node Name</h4>") self.__nameWidget = GafferUI.NameWidget(node) ## \todo Make NameWidget support the readOnly metadata internally itself. # We can't do that easily right now, because it would need to be managing # the exact same `setEditable()` call that we're using here to propagate # our Widget readonlyness. Really our Widget readonlyness mechanism is a # bit lacking, and it should really be inherited automatically so we don't # have to propagate it like this. self.__nameWidget.setEditable( not self.getReadOnly() and not Gaffer.MetadataAlgo.readOnly(node)) with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing=4) as infoSection: GafferUI.Label("<h4>" + node.typeName().rpartition(":")[-1] + "</h4>") button = GafferUI.Button(image="info.png", hasFrame=False) url = Gaffer.Metadata.value(node, "documentation:url") if url: button.clickedSignal().connect( lambda button: GafferUI.showURL(url), scoped=False) toolTip = "<h3>" + node.typeName().rpartition(":")[2] + "</h3>" description = Gaffer.Metadata.nodeDescription(node) if description: toolTip += "\n\n" + description infoSection.setToolTip(toolTip) GafferUI.MenuButton(image="gear.png", hasFrame=False, menu=GafferUI.Menu( Gaffer.WeakMethod( self.__menuDefinition))) frame = GafferUI.Frame(borderStyle=GafferUI.Frame.BorderStyle.None, borderWidth=0) self.__column.append(frame, expand=True) self.__nodeUI = GafferUI.NodeUI.create(node) self.__nodeUI.setReadOnly(self.getReadOnly()) frame.setChild(self.__nodeUI)
def __pathForIndex( self, modelIndex ) : return _GafferUI._pathListingWidgetPathForIndex( GafferUI._qtAddress( self._qtWidget() ), GafferUI._qtAddress( modelIndex ), )
import Gaffer import GafferUI GafferUI.Nodule.registerNodule(Gaffer.ObjectReader.staticTypeId(), fnmatch.translate("*"), lambda plug: None) GafferUI.Nodule.registerNodule(Gaffer.ObjectReader.staticTypeId(), "out", GafferUI.StandardNodule) GafferUI.PlugValueWidget.registerCreator( Gaffer.ObjectReader.staticTypeId(), "fileName", lambda plug: GafferUI.PathPlugValueWidget( plug, path=Gaffer.FileSystemPath( "/", filter=Gaffer.FileSystemPath.createStandardFilter( extensions=IECore.Reader.supportedExtensions(), extensionsLabel="Show only supported files", ), ), ), ) def __createParameterWidget(plug): return GafferUI.CompoundParameterValueWidget( plug.node().parameterHandler(), collapsible=False) GafferUI.PlugValueWidget.registerCreator(Gaffer.ObjectReader.staticTypeId(), "parameters", __createParameterWidget)
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ########################################################################## from __future__ import with_statement import IECore import Gaffer import GafferUI QtGui = GafferUI._qtImport( "QtGui" ) class CompoundPlugValueWidget( GafferUI.PlugValueWidget ) : ## Possible values for collapsed are : # # True : use Collapsible container which starts off collapsed # False : use Collapsible container which starts off opened # None : don't use Collapsible container # # Note that the True/False values for collapsible just set the initial state - # after this the current state is stored for the session on a per-node basis # for user convenience. # # If summary is specified it will be called each time a child plug changes value, # and the result used to provide a summary in the collapsible header.
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ########################################################################## import IECore import Gaffer import GafferUI QtCore = GafferUI._qtImport( "QtCore" ) QtGui = GafferUI._qtImport( "QtGui" ) ## The ColorSwatch simply displays a flat patch of colour. By default, the colour # is specified in linear space and GafferUI.DisplayTransform is used to ensure it # is correctly corrected when displayed. To specify the colour directly in display # space, pass `useDisplayTransform = False` to the constructor. class ColorSwatch( GafferUI.Widget ) : __linearBackgroundColor0 = IECore.Color3f( 0.1 ) __linearBackgroundColor1 = IECore.Color3f( 0.2 ) def __init__( self, color=IECore.Color4f( 1 ), useDisplayTransform = True, **kw ) : GafferUI.Widget.__init__( self, _Checker(), **kw )
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ########################################################################## import fnmatch import weakref import IECore import Gaffer import GafferUI QtCore = GafferUI._qtImport( "QtCore" ) ########################################################################## # Public functions ########################################################################## def appendMenuDefinitions( menuDefinition, prefix="" ) : menuDefinition.append( prefix + "/Execute Selected", { "command" : executeSelected, "shortCut" : "Ctrl+E", "active" : selectionAvailable } ) menuDefinition.append( prefix + "/Repeat Previous", { "command" : repeatPrevious, "shortCut" : "Ctrl+R", "active" : previousAvailable } ) def appendNodeContextMenuDefinitions( nodeGraph, node, menuDefinition ) : if not hasattr( node, "execute" ) : return
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ########################################################################## import weakref import IECore import Gaffer import GafferUI QtCore = GafferUI._qtImport( "QtCore" ) QtGui = GafferUI._qtImport( "QtGui" ) Gaffer.Metadata.registerNode( Gaffer.LocalDispatcher, "description", """ Schedules execution of task graphs on the local machine. Tasks may be dispatched in the background to keep the UI responsive. """, plugs = { "executeInBackground" : (
def setDisplayMode( self, displayMode ) : if displayMode == self.getDisplayMode() : return # It is possible to implement list mode as follows : # # ``` # self._qtWidget().setItemsExpandable( False ) # self._qtWidget().setRootIsDecorated( False ) # ``` # # However, even when doing this QTreeView will call # QModel::hasChildren() anyway, causing our model to # recurse one level deeper than strictly necessary. # This can be costly, so instead we implement list # view by making the model flat. _GafferUI._pathListingWidgetSetFlat( GafferUI._qtAddress( self._qtWidget() ), displayMode == self.DisplayMode.List ) self.__displayModeChangedSignal( self )
def __init__(self): GafferUI.TabbedContainer.__init__(self) self.signal = GafferUI.WidgetSignal()
def __linkActivated( self, label, url ) : GafferUI.showURL( url )