def onRightClick(self): menu = qt.QMenu() position = qt.QCursor.pos() action = qt.QAction("Delete highlighted fiducial(s)", menu) menu.addAction(action) connectObject = qt.QObject() connectObject.connect(action, 'triggered()', self.onDeleteOneFiducialButton) action2 = qt.QAction("Cancel", menu) menu.addAction(action2) connectObject.connect(action2, 'triggered()', menu.hide) menu.exec_(position)
def createButtonRow(self, effects, rowLabel=""): """ create a row of the edit box given a list of effect names (items in _effects(list) """ rowFrame = qt.QFrame(self.mainFrame) self.mainFrame.layout().addWidget(rowFrame) self.rowFrames.append(rowFrame) hbox = qt.QHBoxLayout() rowFrame.setLayout(hbox) if rowLabel: label = qt.QLabel(rowLabel) hbox.addWidget(label) for effect in effects: # check that the effect belongs in our list of effects before including if (effect in self.effects): i = self.icons[effect] = qt.QIcon(self.effectIconFiles[effect]) a = self.actions[effect] = qt.QAction(i, '', rowFrame) self.effectButtons[effect] = b = self.buttons[ effect] = qt.QToolButton() b.setDefaultAction(a) b.setToolTip(effect) if EditBox.displayNames.has_key(effect): b.setToolTip(EditBox.displayNames[effect]) hbox.addWidget(b) # Setup the mapping between button and its associated effect name self.effectMapper.setMapping(self.buttons[effect], effect) # Connect button with signal mapper self.buttons[effect].connect('clicked()', self.effectMapper, 'map()') hbox.addStretch(1)
def createButtonRow(self, effects, rowLabel=""): f = qt.QFrame(self.parent) self.parent.layout().addWidget(f) self.rowFrames.append(f) hbox = qt.QHBoxLayout() f.setLayout(hbox) if rowLabel: label = qt.QLabel(rowLabel) hbox.addWidget(label) for effect in effects: # check that the effect belongs in our list of effects before including if (effect in self.effects): i = self.icons[effect] = qt.QIcon( self.effectIconFiles[effect, self.effectModes[effect]]) a = self.actions[effect] = qt.QAction(i, '', f) self.effectButtons[effect] = b = self.buttons[ effect] = qt.QToolButton() b.setDefaultAction(a) b.setToolTip(effect) if EditBox.displayNames.has_key(effect): b.setToolTip(EditBox.displayNames[effect]) hbox.addWidget(b) if self.disabled.__contains__(effect): b.setDisabled(1) # Setup the mapping between button and its associated effect name self.effectMapper.setMapping(self.buttons[effect], effect) # Connect button with signal mapper self.buttons[effect].connect('clicked()', self.effectMapper, 'map()') hbox.addStretch(1)
def Setup(self, featureName, descriptionLabel="Description:"): self.featureName = featureName self.descriptionLabel = descriptionLabel self.parameters = collections.OrderedDict() self.descriptionAction = qt.QWidgetAction(self) self.descriptionAction.setDefaultWidget(self.descriptionLabel) self.closeAction = qt.QAction("Close", self) self.reloadActions()
def addParameter(self, parameterName): self.parameters[parameterName] = {} self.parameters[parameterName]['Action'] = qt.QAction( ('Edit %s' % parameterName), self) self.parameters[parameterName][ 'Edit Window'] = FeatureWidgetHelperLib.ParameterEditWindow( self, self.featureName, parameterName) self.parameters[parameterName]['Action'].connect( 'triggered()', lambda parameterName=parameterName: self.parameters[parameterName][ 'Edit Window'].showWindow()) self.reloadActions()
def _makeAction(parent, text, icon=None, shortcut=None, slot=None): action = qt.QAction(text, parent) if icon is not None: action.setIcon(qt.QIcon.fromTheme(icon)) if shortcut is not None: action.shortcut = qt.QKeySequence.fromString(shortcut) action.shortcutContext = qt.Qt.WidgetWithChildrenShortcut if slot is not None: action.connect('triggered(bool)', slot) parent.addAction(action) return action
def createButtonRow(self, effects, rowLabel=""): """ create a row of the edit box given a list of effect names (items in _effects(list) """ rowFrame = qt.QFrame(self.mainFrame) self.mainFrame.layout().addWidget(rowFrame) self.rowFrames.append(rowFrame) rowFrame.objectName = "RowFrame%s" % len(self.rowFrames) hbox = qt.QHBoxLayout() rowFrame.setLayout( hbox ) if rowLabel: label = qt.QLabel(rowLabel) hbox.addWidget(label) for effect in effects: # check that the effect belongs in our list of effects before including if (effect in self.effects): i = self.icons[effect] = qt.QIcon(self.effectIconFiles[effect]) a = self.actions[effect] = qt.QAction(i, '', rowFrame) a.objectName = effect + 'Action' self.effectButtons[effect] = b = self.buttons[effect] = qt.QToolButton() b.objectName = effect + 'ToolButton' b.setDefaultAction(a) b.setToolTip(effect) if EditBox.displayNames.has_key(effect): b.setToolTip(EditBox.displayNames[effect]) hbox.addWidget(b) if effect not in ('EraseLabel', 'PreviousCheckPoint', 'NextCheckPoint'): # Mapping between action and its associated effect, is done # in function'_onEffectActionTriggered' by retrieving the 'effectName' # property. a.checkable = True a.setProperty('effectName', effect) self.effectActionGroup.addAction(a) elif effect == 'EraseLabel': a.checkable = True a.connect('triggered(bool)', self._onEraseLabelActionTriggered) elif effect == 'PreviousCheckPoint': a.connect('triggered(bool)', self.undoRedo.undo) elif effect == 'NextCheckPoint': a.connect('triggered(bool)', self.undoRedo.redo) hbox.addStretch(1)
def setup(self): # # servers # # testing server - not exposed (used for development) self.localFrame = ctk.ctkCollapsibleButton(self.parent) self.localFrame.setLayout(qt.QVBoxLayout()) self.localFrame.setText("Servers") self.layout.addWidget(self.localFrame) self.localFrame.collapsed = False self.toggleServer = qt.QPushButton("Start Testing Server") self.localFrame.layout().addWidget(self.toggleServer) self.toggleServer.connect('clicked()', self.onToggleServer) self.verboseServer = qt.QCheckBox("Verbose") self.localFrame.layout().addWidget(self.verboseServer) # advanced options - not exposed to end users # developers can uncomment these lines to access testing server self.toggleServer.hide() self.verboseServer.hide() # Listener settings = qt.QSettings() self.toggleListener = qt.QPushButton() if hasattr(slicer, 'dicomListener'): self.toggleListener.text = "Stop Listener" slicer.dicomListener.process.connect('stateChanged(int)', self.onListenerStateChanged) else: self.toggleListener.text = "Start Listener" self.localFrame.layout().addWidget(self.toggleListener) self.toggleListener.connect('clicked()', self.onToggleListener) self.runListenerAtStart = qt.QCheckBox( "Start Listener when Slicer Starts") self.localFrame.layout().addWidget(self.runListenerAtStart) if settings.contains('DICOM/RunListenerAtStart'): self.runListenerAtStart.checked = bool( settings.value('DICOM/RunListenerAtStart') == 'true') self.runListenerAtStart.connect('clicked()', self.onRunListenerAtStart) # the Database frame (home of the ctkDICOM widget) self.dicomFrame = ctk.ctkCollapsibleButton(self.parent) self.dicomFrame.setLayout(qt.QVBoxLayout()) self.dicomFrame.setText("DICOM Database and Networking") self.layout.addWidget(self.dicomFrame) # initialize the dicomDatabase # - pick a default and let the user know if not slicer.dicomDatabase: self.promptForDatabaseDirectory() # # create and configure the app widget - this involves # reaching inside and manipulating the widget hierarchy # - TODO: this configurability should be exposed more natively # in the CTK code to avoid the findChildren calls # self.dicomBrowser = ctk.ctkDICOMBrowser() DICOM.setDatabasePrecacheTags(self.dicomBrowser) self.detailsPopup = DICOMLib.DICOMDetailsPopup( self.dicomBrowser, setBrowserPersistence=self.setBrowserPersistence) self.tables = self.detailsPopup.tables self.showBrowser = qt.QPushButton('Show DICOM Browser') self.dicomFrame.layout().addWidget(self.showBrowser) self.showBrowser.connect('clicked()', self.detailsPopup.open) # connect to the main window's dicom button mw = slicer.util.mainWindow() try: action = slicer.util.findChildren(mw, name='actionLoadDICOM')[0] action.connect('triggered()', self.detailsPopup.open) except IndexError: print('Could not connect to the main window DICOM button') # connect to our menu file entry so it raises the browser fileMenu = slicer.util.lookupTopLevelWidget('FileMenu') if fileMenu: for action in fileMenu.actions(): if action.text == 'DICOM': action.connect('triggered()', self.detailsPopup.open) # make the tables view a bit bigger self.tables.setMinimumHeight(250) if hasattr(slicer, 'dicomListener'): slicer.dicomListener.fileToBeAddedCallback = self.onListenerToAddFile slicer.dicomListener.fileAddedCallback = self.onListenerAddedFile # TODO: populate context menu self.contextMenu = qt.QMenu(self.tables) self.exportAction = qt.QAction("Export to Study", self.contextMenu) self.contextMenu.addAction(self.exportAction) self.exportAction.enabled = False self.deleteAction = qt.QAction("Delete", self.contextMenu) self.contextMenu.addAction(self.deleteAction) self.deleteAction.enabled = False self.contextMenu.connect('triggered(QAction*)', self.onContextMenuTriggered) slicer.dicomDatabase.connect('databaseChanged()', self.onDatabaseChanged) self.dicomBrowser.connect('databaseDirectoryChanged(QString)', self.onDatabaseDirectoryChanged) self.tables.connect('seriesSelectionChanged(QStringList)', self.onSeriesSelected) self.tables.setContextMenuPolicy(3) self.tables.connect('customContextMenuRequested(QPoint)', self.onTreeContextMenuRequested) # enable to the Send button of the app widget and take it over # for our purposes - TODO: fix this to enable it at the ctkDICOM level self.sendButton = slicer.util.findChildren(self.dicomBrowser, text='Send')[0] self.sendButton.enabled = False self.sendButton.connect('triggered()', self.onSendClicked) # the recent activity frame self.activityFrame = ctk.ctkCollapsibleButton(self.parent) self.activityFrame.setLayout(qt.QVBoxLayout()) self.activityFrame.setText("Recent DICOM Activity") self.layout.addWidget(self.activityFrame) self.recentActivity = DICOMLib.DICOMRecentActivityWidget( self.activityFrame, detailsPopup=self.detailsPopup) self.activityFrame.layout().addWidget(self.recentActivity.widget) self.requestUpdateRecentActivity() # Add spacer to layout self.layout.addStretch(1)
def setup(self): # # servers # # testing server - not exposed (used for development) self.localFrame = ctk.ctkCollapsibleButton(self.parent) self.localFrame.setLayout(qt.QVBoxLayout()) self.localFrame.setText("Servers") self.layout.addWidget(self.localFrame) self.localFrame.collapsed = False self.toggleServer = qt.QPushButton("Start Testing Server") self.localFrame.layout().addWidget(self.toggleServer) self.toggleServer.connect('clicked()', self.onToggleServer) self.verboseServer = qt.QCheckBox("Verbose") self.localFrame.layout().addWidget(self.verboseServer) # advanced options - not exposed to end users # developers can uncomment these lines to access testing server self.toggleServer.hide() self.verboseServer.hide() # Listener settings = qt.QSettings() self.toggleListener = qt.QPushButton() if hasattr(slicer, 'dicomListener'): self.toggleListener.text = "Stop Listener" slicer.dicomListener.process.connect('stateChanged(int)', self.onListenerStateChanged) else: self.toggleListener.text = "Start Listener" self.localFrame.layout().addWidget(self.toggleListener) self.toggleListener.connect('clicked()', self.onToggleListener) self.runListenerAtStart = qt.QCheckBox( "Start Listener when Slicer Starts") self.localFrame.layout().addWidget(self.runListenerAtStart) if settings.contains('DICOM/RunListenerAtStart'): self.runListenerAtStart.checked = bool( settings.value('DICOM/RunListenerAtStart')) self.runListenerAtStart.connect('clicked()', self.onRunListenerAtStart) # the Database frame (home of the ctkDICOM widget) self.dicomFrame = ctk.ctkCollapsibleButton(self.parent) self.dicomFrame.setLayout(qt.QVBoxLayout()) self.dicomFrame.setText("DICOM Database and Networking") self.layout.addWidget(self.dicomFrame) # initialize the dicomDatabase # - don't let the user escape without # picking a valid database directory while not slicer.dicomDatabase: self.promptForDatabaseDirectory() # # create and configure the app widget - this involves # reaching inside and manipulating the widget hierarchy # - TODO: this configurability should be exposed more natively # in the CTK code to avoid the findChildren calls # self.dicomApp = ctk.ctkDICOMAppWidget() if self.hideSearch: # hide the search options - doesn't work yet and doesn't fit # well into the frame slicer.util.findChildren(self.dicomApp, 'SearchOption')[0].hide() self.detailsPopup = DICOMLib.DICOMDetailsPopup( self.dicomApp, setBrowserPersistence=self.setBrowserPersistence) self.tree = self.detailsPopup.tree self.showBrowser = qt.QPushButton('Show DICOM Browser') self.dicomFrame.layout().addWidget(self.showBrowser) self.showBrowser.connect('clicked()', self.detailsPopup.open) # make the tree view a bit bigger self.tree.setMinimumHeight(250) if hasattr(slicer, 'dicomListener'): slicer.dicomListener.fileToBeAddedCallback = self.onListenerToAddFile slicer.dicomListener.fileAddedCallback = self.onListenerAddedFile self.contextMenu = qt.QMenu(self.tree) self.exportAction = qt.QAction("Export to Study", self.contextMenu) self.contextMenu.addAction(self.exportAction) self.exportAction.enabled = False self.deleteAction = qt.QAction("Delete", self.contextMenu) self.contextMenu.addAction(self.deleteAction) self.contextMenu.connect('triggered(QAction*)', self.onContextMenuTriggered) slicer.dicomDatabase.connect('databaseChanged()', self.onDatabaseChanged) self.dicomApp.connect('databaseDirectoryChanged(QString)', self.onDatabaseDirectoryChanged) selectionModel = self.tree.selectionModel() # TODO: can't use this because QList<QModelIndex> is not visible in PythonQt #selectionModel.connect('selectionChanged(QItemSelection, QItemSelection)', self.onTreeSelectionChanged) self.tree.connect('clicked(QModelIndex)', self.onTreeClicked) self.tree.setContextMenuPolicy(3) self.tree.connect('customContextMenuRequested(QPoint)', self.onTreeContextMenuRequested) # enable to the Send button of the app widget and take it over # for our purposes - TODO: fix this to enable it at the ctkDICOM level self.sendButton = slicer.util.findChildren(self.dicomApp, text='Send')[0] self.sendButton.enabled = False self.sendButton.connect('clicked()', self.onSendClicked) # the recent activity frame self.activityFrame = ctk.ctkCollapsibleButton(self.parent) self.activityFrame.setLayout(qt.QVBoxLayout()) self.activityFrame.setText("Recent DICOM Activity") self.layout.addWidget(self.activityFrame) self.recentActivity = DICOMLib.DICOMRecentActivityWidget( self.activityFrame, detailsPopup=self.detailsPopup) self.activityFrame.layout().addWidget(self.recentActivity.widget) self.requestUpdateRecentActivity() # Add spacer to layout self.layout.addStretch(1)
def setup(self): # # servers # # testing server - not exposed (used for development) self.localFrame = ctk.ctkCollapsibleButton(self.parent) self.localFrame.setLayout(qt.QVBoxLayout()) self.localFrame.setText("Servers") self.layout.addWidget(self.localFrame) self.localFrame.collapsed = False self.toggleServer = qt.QPushButton("Start Testing Server") self.localFrame.layout().addWidget(self.toggleServer) self.toggleServer.connect('clicked()', self.onToggleServer) self.verboseServer = qt.QCheckBox("Verbose") self.localFrame.layout().addWidget(self.verboseServer) # advanced options - not exposed to end users # developers can uncomment these lines to access testing server self.toggleServer.hide() self.verboseServer.hide() # Listener settings = qt.QSettings() self.toggleListener = qt.QPushButton() if hasattr(slicer, 'dicomListener'): self.toggleListener.text = "Stop Listener" slicer.dicomListener.process.connect('stateChanged(int)', self.onListenerStateChanged) else: self.toggleListener.text = "Start Listener" self.localFrame.layout().addWidget(self.toggleListener) self.toggleListener.connect('clicked()', self.onToggleListener) self.runListenerAtStart = qt.QCheckBox( "Start Listener when Slicer Starts") self.localFrame.layout().addWidget(self.runListenerAtStart) if settings.contains('DICOM/RunListenerAtStart'): self.runListenerAtStart.checked = bool( settings.value('DICOM/RunListenerAtStart')) self.runListenerAtStart.connect('clicked()', self.onRunListenerAtStart) # the Database frame (home of the ctkDICOM widget) self.dicomFrame = ctk.ctkCollapsibleButton(self.parent) self.dicomFrame.setLayout(qt.QVBoxLayout()) self.dicomFrame.setText("DICOM Database and Networking") self.layout.addWidget(self.dicomFrame) self.dicomApp = ctk.ctkDICOMAppWidget() self.dicomFrame.layout().addWidget(self.dicomApp) # hide the search options - doesn't work yet and doesn't fit # well into the frame self.findChildren(self.dicomApp, 'SearchOption')[0].hide() # make the tree a little smaller to fit in slicer self.tree = self.findChildren(self.dicomApp, 'TreeView')[0] g = self.tree.geometry g.setHeight(150) if self.dicomApp.databaseDirectory: self.onDatabaseDirectoryChanged(self.dicomApp.databaseDirectory) else: self.promptForDatabaseDirectory() if hasattr(slicer, 'dicomListener'): slicer.dicomListener.fileAddedCallback = self.onListenerAddedFile self.contextMenu = qt.QMenu(self.tree) self.deleteAction = qt.QAction("Delete", self.contextMenu) self.contextMenu.addAction(self.deleteAction) self.contextMenu.connect('triggered(QAction*)', self.onContextMenuTriggered) self.dicomApp.connect('databaseDirectoryChanged(QString)', self.onDatabaseDirectoryChanged) self.tree.connect('clicked(const QModelIndex&)', self.onTreeClicked) self.tree.setContextMenuPolicy(3) self.tree.connect('customContextMenuRequested(QPoint)', self.onTreeContextMenuRequested) userFrame = self.findChildren(self.dicomApp, 'UserFrame')[0] userFrame.setLayout(qt.QVBoxLayout()) self.treeLabel = qt.QLabel('Selection: None') userFrame.layout().addWidget(self.treeLabel) self.loadButton = qt.QPushButton('Load to Slicer') self.loadButton.enabled = False userFrame.layout().addWidget(self.loadButton) self.loadButton.connect('clicked()', self.onLoadButton) self.exportButton = qt.QPushButton('Export Slicer Data to Study...') self.exportButton.enabled = False userFrame.layout().addWidget(self.exportButton) self.exportButton.connect('clicked()', self.onExportClicked) # enable to the Send button of the app widget and take it over # for our purposes - TODO: fix this to enable it at the ctkDICOM level self.sendButton = slicer.util.findChildren(self.dicomApp, text='Send')[0] self.sendButton.enabled = False self.sendButton.connect('clicked()', self.onSendClicked) # Add spacer to layout self.layout.addStretch(1)
def create(self): """create the segmentation helper box""" # # Master Frame # self.masterFrame = qt.QFrame(self.parent) self.masterFrame.setLayout(qt.QVBoxLayout()) self.parent.layout().addWidget(self.masterFrame) # # the master volume selector # self.masterSelectorFrame = qt.QFrame(self.parent) self.masterSelectorFrame.objectName = 'MasterVolumeFrame' self.masterSelectorFrame.setLayout(qt.QHBoxLayout()) self.masterFrame.layout().addWidget(self.masterSelectorFrame) self.masterSelectorLabel = qt.QLabel("Master Volume: ", self.masterSelectorFrame) self.masterSelectorLabel.setToolTip( "Select the master volume (background grayscale scalar volume node)" ) self.masterSelectorFrame.layout().addWidget(self.masterSelectorLabel) self.masterSelector = slicer.qMRMLNodeComboBox( self.masterSelectorFrame) self.masterSelector.objectName = 'MasterVolumeNodeSelector' # TODO self.masterSelector.nodeTypes = ["vtkMRMLScalarVolumeNode"] self.masterSelector.selectNodeUponCreation = False self.masterSelector.addEnabled = False self.masterSelector.removeEnabled = False self.masterSelector.noneEnabled = True self.masterSelector.showHidden = False self.masterSelector.showChildNodeTypes = False self.masterSelector.setMRMLScene(slicer.mrmlScene) # TODO: need to add a QLabel # self.masterSelector.SetLabelText( "Master Volume:" ) self.masterSelector.setToolTip( "Pick the master structural volume to define the segmentation. A label volume with the with \"%s\" appended to the name will be created if it doesn't already exist." % self.mergeVolumePostfix) self.masterSelectorFrame.layout().addWidget(self.masterSelector) # # merge label name and set button # self.mergeSelectorFrame = qt.QFrame(self.masterFrame) self.mergeSelectorFrame.objectName = 'MergeVolumeFrame' self.mergeSelectorFrame.setLayout(qt.QHBoxLayout()) self.masterFrame.layout().addWidget(self.mergeSelectorFrame) mergeNameToolTip = "Composite label map containing the merged structures (be aware that merge operations will overwrite any edits applied to this volume)" self.mergeNameLabel = qt.QLabel("Merge Volume: ", self.mergeSelectorFrame) self.mergeNameLabel.setToolTip(mergeNameToolTip) self.mergeSelectorFrame.layout().addWidget(self.mergeNameLabel) self.mergeSelector = slicer.qMRMLNodeComboBox(self.mergeSelectorFrame) self.mergeSelector.objectName = 'MergeVolumeNodeSelector' self.mergeSelector.setToolTip(mergeNameToolTip) self.mergeSelector.nodeTypes = ["vtkMRMLLabelMapVolumeNode"] self.mergeSelector.addEnabled = False self.mergeSelector.removeEnabled = False self.mergeSelector.noneEnabled = False self.mergeSelector.showHidden = False self.mergeSelector.showChildNodeTypes = False self.mergeSelector.setMRMLScene(slicer.mrmlScene) self.mergeSelectorFrame.layout().addWidget(self.mergeSelector) self.newMergeVolumeAction = qt.QAction("Create new LabelMapVolume", self.mergeSelector) self.newMergeVolumeAction.connect("triggered()", self.newMerge) self.mergeSelector.addMenuAction(self.newMergeVolumeAction) # # Structures Frame # self.structuresFrame = ctk.ctkCollapsibleGroupBox(self.masterFrame) self.structuresFrame.objectName = 'PerStructureVolumesFrame' self.structuresFrame.title = "Per-Structure Volumes" self.structuresFrame.collapsed = True self.structuresFrame.setLayout(qt.QVBoxLayout()) self.masterFrame.layout().addWidget(self.structuresFrame) self.structureListWidget = LabelStructureListWidget() self.structureListWidget.mergeVolumePostfix = self.mergeVolumePostfix self.structuresFrame.layout().addWidget(self.structureListWidget) # # signals, slots, and observers # # signals/slots on qt widgets are automatically when # this class destructs, but observers of the scene must be explicitly # removed in the destuctor # node selected self.masterSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect) self.mergeSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onMergeSelect) # so buttons will initially be disabled self.master = None self.structureListWidget.updateStructures()