class WProbes(QWidget, Logger.ClassLogger): """ Widget for probes """ def __init__(self, parent): """ Constructs WProbes widget @param parent: @type parent: """ QWidget.__init__(self, parent) self.parent = parent self.name = self.tr("Probes") self.itemCurrentRunning = None self.itemCurrentInstalled = None self.itemCurrentDefault = None self.probes = {} self.nbPrbs = 0 # self.prbsInstalled = None self.createWidgets() self.createConnections() self.createActions() self.createToolbar() self.deactivate() def createWidgets (self): """ QtWidgets creation QTreeWidget (Name, Running on address, Type, Sched at, Version, Description) _______________ | | |---------------| | | | | |_______________| """ layout = QHBoxLayout() self.deployBox = QGroupBox("Default probes") self.probesAvailable = QTreeWidget(self) self.probesAvailable.setIndentation(10) self.labelsAvail = [ self.tr("Installed") ] self.probesAvailable.setHeaderLabels(self.labelsAvail) self.runningBox = QGroupBox("Running") self.probesRegistered = QTreeWidget(self) self.probesRegistered.setIndentation(10) self.runningDockToolbar = QToolBar(self) self.runningDockToolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.availDockToolbar = QToolBar(self) self.availDockToolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.labels = [ self.tr("Name"), self.tr("Running on address"), self.tr("Started at"), self.tr("Type"), self.tr("Auto Startup"), self.tr("Description") ] self.probesRegistered.setHeaderLabels(self.labels) self.probesRegistered.setColumnWidth(0, 180) self.probesRegistered.setColumnWidth(1, 120) self.probesRegistered.setColumnWidth(2, 150) self.probesRegistered.setColumnWidth(3, 70) self.probesRegistered.setContextMenuPolicy(Qt.CustomContextMenu) self.probesDefault = QTreeWidget(self) self.probesDefault.setIndentation(10) self.labelsDefault = [ self.tr("Enabled"), self.tr("Name"), self.tr("Type"), self.tr("Description") ] self.probesDefault.setHeaderLabels(self.labelsDefault) self.probesDefault.setContextMenuPolicy(Qt.CustomContextMenu) self.probesDefault.setColumnWidth(1, 180) layoutRunning = QVBoxLayout() layoutRunning.addWidget(self.runningDockToolbar) layoutRunning.addWidget(self.probesRegistered) self.runningBox.setLayout(layoutRunning) self.probeNameEdit = QLineEdit('') self.probeNameEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed ) self.probeDescEdit = QLineEdit('') self.probeDescEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed ) self.probeTypeEdit = QLineEdit('') self.probeTypeEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed ) self.probeTypeEdit.setEnabled(False) self.probeDescrInstalledEdit = QTextEdit('') self.probeDescrInstalledEdit.setEnabled(False) self.checkAutoStartOption = QCheckBox() self.checkStartNowOption = QCheckBox() paramLayout = QGridLayout() paramLayout.addWidget(QLabel("Type:"), 0, 0, Qt.AlignRight) paramLayout.addWidget(self.probeTypeEdit, 0, 1) paramLayout.addWidget(QLabel("Name:"), 1, 0, Qt.AlignRight) paramLayout.addWidget(self.probeNameEdit, 1, 1) paramLayout.addWidget(QLabel("Description:"), 2, 0, Qt.AlignRight) paramLayout.addWidget(self.probeDescEdit, 2, 1) paramLayout.addWidget(QLabel("Startup on boot:"), 3, 0, Qt.AlignRight) paramLayout.addWidget(self.checkAutoStartOption, 3, 1) paramLayout.addWidget(QLabel("Start now:"), 4, 0, Qt.AlignRight) paramLayout.addWidget(self.checkStartNowOption, 4, 1) layoutLeft = QVBoxLayout() layoutAvail = QHBoxLayout() layoutAvail.addWidget(self.probesAvailable) layoutAvail.addWidget(self.probeDescrInstalledEdit) layoutLeft.addLayout(layoutAvail) layoutLeft.addWidget(self.runningBox) layoutDeploy = QVBoxLayout() layoutDeploy.addWidget(self.availDockToolbar) layoutDeploy.addLayout(paramLayout) layoutDeploy.addWidget(self.probesDefault) self.deployBox.setLayout(layoutDeploy) layoutRight = QVBoxLayout() layoutRight.addWidget(self.deployBox) layout.addLayout(layoutLeft) layout.addLayout(layoutRight) self.setLayout(layout) def createConnections (self): """ Create Qt Connections """ self.probesRegistered.customContextMenuRequested.connect(self.onPopupMenu) self.probesRegistered.currentItemChanged.connect(self.currentItemChanged) self.probesRegistered.itemClicked.connect(self.itemClicked) self.probesAvailable.currentItemChanged.connect(self.currentItemChanged) self.probesDefault.currentItemChanged.connect(self.currentItemChanged) self.probesDefault.customContextMenuRequested.connect(self.onPopupMenuDefault) def createActions (self): """ Actions defined: * stop probe * start probe * delete one probe * clear fields * refresh running probes * refresh default probes """ self.stopAction = QtHelper.createAction(self, "&Stop", self.stopProbe, tip = 'Stop probe', icon = QIcon(":/act-stop.png")) self.startAction = QtHelper.createAction(self, "&Add / Start", self.startProbe, tip = 'Add default probe', icon = QIcon(":/probe-add.png")) self.delProbeAction = QtHelper.createAction(self, "&Delete", self.delProbe, tip = 'Delete default probe', icon = QIcon(":/probe-del.png")) self.cancelAction = QtHelper.createAction(self, "&Clear", self.resetProbe, tip = 'Clear fields', icon = QIcon(":/clear.png") ) self.refreshRunningAction = QtHelper.createAction(self, "&Refresh", self.refreshRunningProbe, tip = 'Refresh running probes', icon = QIcon(":/act-refresh.png") ) self.refreshDefaultAction = QtHelper.createAction(self, "&Refresh", self.refreshDefaultProbe, tip = 'Refresh default probes', icon = QIcon(":/act-refresh.png") ) def createToolbar(self): """ Toolbar creation ||-------|| || Empty || ||-------|| """ self.runningDockToolbar.setObjectName("Registered Probe toolbar") self.runningDockToolbar.addAction(self.refreshRunningAction) self.runningDockToolbar.addSeparator() self.runningDockToolbar.addAction(self.stopAction) self.runningDockToolbar.addSeparator() self.runningDockToolbar.setIconSize(QSize(16, 16)) self.availDockToolbar.setObjectName("Installed Probe toolbar") self.availDockToolbar.addAction(self.refreshDefaultAction) self.availDockToolbar.addSeparator() self.availDockToolbar.addAction(self.startAction) self.availDockToolbar.addAction(self.delProbeAction) self.availDockToolbar.addSeparator() self.availDockToolbar.addAction(self.cancelAction) self.availDockToolbar.addSeparator() self.availDockToolbar.setIconSize(QSize(16, 16)) def itemClicked(self, currentItem): """ On item clicked @param currentItem: @type currentItem: """ if currentItem is not None: if isinstance( currentItem, ProbeItem ): self.itemCurrentRunning = currentItem self.stopAction.setEnabled(True) def currentItemChanged(self, currentItem, previousItem): """ On current item changed @param currentItem: @type currentItem: @param previousItem: @type previousItem: """ if currentItem is not None: if isinstance( currentItem, ProbeInstalledItem ): self.itemCurrentInstalled = currentItem if 'description' in currentItem.dataProbe: self.probeDescrInstalledEdit.setText( currentItem.dataProbe['description'] ) self.probeTypeEdit.setText( currentItem.dataProbe['type'] ) else: self.probeDescrInstalledEdit.setText( '' ) elif isinstance( currentItem, ProbeItem ): self.itemCurrentRunning = currentItem self.stopAction.setEnabled(True) elif isinstance( currentItem, ProbeDefaultItem ): self.itemCurrentDefault = currentItem self.delProbeAction.setEnabled(True) else: self.stopAction.setEnabled(False) self.delProbeAction.setEnabled(False) self.probeDescrInstalledEdit.setText( '' ) def onPopupMenu(self, pos): """ Display menu on right click @param pos: @type pos: """ self.menu = QMenu() item = self.probesRegistered.itemAt(pos) if item: self.itemCurrentRunning = item self.menu.addAction( "Stop...", self.stopProbe) self.menu.popup(self.probesRegistered.mapToGlobal(pos)) def onPopupMenuDefault(self, pos): """ Display menu on right click @param pos: @type pos: """ self.menu = QMenu() item = self.probesDefault.itemAt(pos) if item: self.itemCurrentDefault = item self.menu.addAction( "Delete...", self.delProbe) self.menu.popup(self.probesDefault.mapToGlobal(pos)) def refreshDefaultProbe(self): """ Refresh the default list of probes """ RCI.instance().defaultProbes() def refreshRunningProbe(self): """ Refresh the running list of probes """ RCI.instance().runningProbes() def delProbe(self): """ Delete probe """ if self.itemCurrentDefault is not None: reply = QMessageBox.question(self, "Stop probe", "Are you sure ?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: probeName = self.itemCurrentDefault.dataProbe['name'] self.delProbeAction.setEnabled(False) # rest call RCI.instance().removeProbe(probeName=probeName) def stopProbe(self): """ Stop the selected probe """ if self.itemCurrentRunning is not None: reply = QMessageBox.question(self, "Stop probe", "Are you sure ?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: probeName = self.itemCurrentRunning.dataProbe['id'] self.itemCurrentRunning = None self.stopAction.setEnabled(False) # rest call RCI.instance().disconnectProbe(probeName=probeName) def startProbe(self): """ Start a new probe """ # some checks before if self.probeTypeEdit.text() == '': QMessageBox.information(self, "Add Default Probe" , "Please select the probe type.") return if self.probeNameEdit.text() == '': QMessageBox.information(self, "Add Default Probe" , "Probe name is mandatory.") return if not self.checkAutoStartOption.isChecked() and not self.checkStartNowOption.isChecked(): QMessageBox.information(self, "Add Default Probe" , "Select startup option.") return # call web services probeType = str( self.probeTypeEdit.text() ) probeName = str( self.probeNameEdit.text() ) probeDescription = str( self.probeDescEdit.text() ) probeAutoStart = self.checkAutoStartOption.isChecked() if not self.checkStartNowOption.isChecked(): RCI.instance().addProbe(probeName=probeName, probeType=probeType, probeDescription=probeDescription) else: RCI.instance().connectProbe(probeName=probeName, probeType=probeType, probeDescription=probeDescription, probeBoot=probeAutoStart) def resetProbe(self): """ Clear probe field """ self.probeDescrInstalledEdit.setText( '' ) self.probeDescEdit.setText( '' ) self.probeNameEdit.setText( '' ) self.probeTypeEdit.setText( '' ) # clear selection itms = self.probesAvailable.selectedItems() for i in itms: if i.isSelected(): i.setSelected(False) self.itemCurrentInstalled = None def active (self): """ Enables QTreeWidget """ self.probesRegistered.setEnabled(True) self.probesAvailable.setEnabled(True) self.deployBox.setEnabled(True) self.runningBox.setEnabled(True) self.refreshRunningAction.setEnabled(True) def deactivate (self): """ Clears QTreeWidget and disables it """ self.checkAutoStartOption.setCheckState(Qt.Unchecked) self.checkStartNowOption.setCheckState(Qt.Unchecked) self.probesAvailable.clear() self.probeDescrInstalledEdit.setText('') self.probesRegistered.clear() self.probesDefault.clear() self.probes = {} self.probesRegistered.setEnabled(False) self.probesAvailable.setEnabled(False) self.deployBox.setEnabled(False) self.runningBox.setEnabled(False) # actions self.stopAction.setEnabled(False) self.delProbeAction.setEnabled(False) self.itemCurrentRunning = None self.itemCurrentInstalled = None self.probeDescEdit.setText( '' ) self.probeTypeEdit.setText( '' ) self.probeNameEdit.setText( '' ) self.resetNbProbes() self.refreshRunningAction.setEnabled(False) def getRunningProbes(self): """ Get running probes """ if sys.version_info > (3,): # python3 support return list(self.probes.keys()) else: return self.probes.keys() def getProbeTypeByName(self, name): """ Get probe type by name @param name: @type name: """ if name in self.probes: return self.probes[name].getProbeType() else: return '' def loadDefault (self, data): """ Loads default probes @param data: @type data: dict """ self.probesDefault.clear() for defProbe in data: defProbeItem = ProbeDefaultItem( probe = defProbe, parent= self.probesDefault) def loadData (self, data, dataInstalled=None): """ Loads probes @param data: @type data: dict @param dataInstalled: @type dataInstalled: """ if isinstance(data, dict): data = [ data ] self.probesRegistered.clear() for probe in data: probeItem = ProbeItem( probe = probe, parent= self.probesRegistered) self.probes[probe['id']] = probeItem # load tests stats if dataInstalled is not None: if len(dataInstalled) == 0: self.deployBox.setEnabled(False) self.probesAvailable.setEnabled(False) def resetNbProbes(self, data=None): """ Reset the number of probes """ pass def refreshData (self, data, action): """ Refresh probes @param data: @type data: dict @param action: expected values in 'del' and 'add' @type action: string """ if action == 'del': self.probesRegistered.clear() self.probes = {} self.resetNbProbes(data=data) self.loadData( data = data ) elif action == 'add': self.resetNbProbes(data=data) self.loadData( data = data ) else: self.error( 'action unknown: %s' % str(action) ) def refreshDataDefault(self, data, action): """ Refresh probes @param data: @type data: dict @param action: expected values in 'del' and 'add' @type action: string """ if action == 'del': self.probesDefault.clear() self.loadDefault( data = data ) elif action == 'add': self.probesDefault.clear() self.loadDefault( data = data ) else: self.error( 'action unknown: %s' % str(action) )
class IgnoredExceptionsViewer(QWidget): " Implements the client exceptions viewer for a debugger " def __init__(self, parent=None): QWidget.__init__(self, parent) self.__createPopupMenu() self.__createLayout() self.__ignored = [] self.__currentItem = None GlobalData().project.projectChanged.connect(self.__onProjectChanged) if Settings().showIgnoredExcViewer == False: self.__onShowHide(True) return def __createPopupMenu(self): " Creates the popup menu " self.__excptMenu = QMenu() self.__removeMenuItem = self.__excptMenu.addAction( PixmapCache().getIcon('ignexcptdel.png'), "Remove from ignore list", self.__onRemoveFromIgnore) return def __createLayout(self): " Creates the widget layout " verticalLayout = QVBoxLayout(self) verticalLayout.setContentsMargins(0, 0, 0, 0) verticalLayout.setSpacing(0) self.headerFrame = QFrame() self.headerFrame.setFrameStyle(QFrame.StyledPanel) self.headerFrame.setAutoFillBackground(True) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color(QPalette.Background) headerBackground.setRgb(min(headerBackground.red() + 30, 255), min(headerBackground.green() + 30, 255), min(headerBackground.blue() + 30, 255)) headerPalette.setColor(QPalette.Background, headerBackground) self.headerFrame.setPalette(headerPalette) self.headerFrame.setFixedHeight(24) self.__excptLabel = QLabel("Ignored exception types") expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding) fixedSpacer = QSpacerItem(3, 3) self.__showHideButton = QToolButton() self.__showHideButton.setAutoRaise(True) self.__showHideButton.setIcon(PixmapCache().getIcon('less.png')) self.__showHideButton.setFixedSize(20, 20) self.__showHideButton.setToolTip("Hide ignored exceptions list") self.__showHideButton.setFocusPolicy(Qt.NoFocus) self.__showHideButton.clicked.connect(self.__onShowHide) headerLayout = QHBoxLayout() headerLayout.setContentsMargins(1, 1, 1, 1) headerLayout.addSpacerItem(fixedSpacer) headerLayout.addWidget(self.__excptLabel) headerLayout.addSpacerItem(expandingSpacer) headerLayout.addWidget(self.__showHideButton) self.headerFrame.setLayout(headerLayout) self.exceptionsList = QTreeWidget(self) self.exceptionsList.setSortingEnabled(False) self.exceptionsList.setAlternatingRowColors(True) self.exceptionsList.setRootIsDecorated(False) self.exceptionsList.setItemsExpandable(True) self.exceptionsList.setUniformRowHeights(True) self.exceptionsList.setSelectionMode(QAbstractItemView.SingleSelection) self.exceptionsList.setSelectionBehavior(QAbstractItemView.SelectRows) self.exceptionsList.setItemDelegate(NoOutlineHeightDelegate(4)) self.exceptionsList.setContextMenuPolicy(Qt.CustomContextMenu) self.exceptionsList.customContextMenuRequested.connect( self.__showContextMenu) self.exceptionsList.itemSelectionChanged.connect( self.__onSelectionChanged) self.exceptionsList.setHeaderLabels(["Exception type"]) self.__excTypeEdit = QLineEdit() self.__excTypeEdit.setFixedHeight(26) self.__excTypeEdit.textChanged.connect(self.__onNewFilterChanged) self.__excTypeEdit.returnPressed.connect(self.__onAddExceptionFilter) self.__addButton = QPushButton("Add") # self.__addButton.setFocusPolicy( Qt.NoFocus ) self.__addButton.setEnabled(False) self.__addButton.clicked.connect(self.__onAddExceptionFilter) expandingSpacer2 = QWidget() expandingSpacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.__removeButton = QAction(PixmapCache().getIcon('delitem.png'), "Remove selected exception type", self) self.__removeButton.triggered.connect(self.__onRemoveFromIgnore) self.__removeButton.setEnabled(False) fixedSpacer1 = QWidget() fixedSpacer1.setFixedWidth(5) self.__removeAllButton = QAction( PixmapCache().getIcon('ignexcptdelall.png'), "Remove all the exception types", self) self.__removeAllButton.triggered.connect(self.__onRemoveAllFromIgnore) self.__removeAllButton.setEnabled(False) self.toolbar = QToolBar() self.toolbar.setOrientation(Qt.Horizontal) self.toolbar.setMovable(False) self.toolbar.setAllowedAreas(Qt.TopToolBarArea) self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.setFixedHeight(28) self.toolbar.setContentsMargins(0, 0, 0, 0) self.toolbar.addWidget(expandingSpacer2) self.toolbar.addAction(self.__removeButton) self.toolbar.addWidget(fixedSpacer1) self.toolbar.addAction(self.__removeAllButton) addLayout = QHBoxLayout() addLayout.setContentsMargins(1, 1, 1, 1) addLayout.setSpacing(1) addLayout.addWidget(self.__excTypeEdit) addLayout.addWidget(self.__addButton) verticalLayout.addWidget(self.headerFrame) verticalLayout.addWidget(self.toolbar) verticalLayout.addWidget(self.exceptionsList) verticalLayout.addLayout(addLayout) return def clear(self): " Clears the content " self.exceptionsList.clear() self.__excTypeEdit.clear() self.__addButton.setEnabled(False) self.__ignored = [] self.__currentItem = None self.__updateTitle() return def __onShowHide(self, startup=False): " Triggered when show/hide button is clicked " if startup or self.exceptionsList.isVisible(): self.exceptionsList.setVisible(False) self.__excTypeEdit.setVisible(False) self.__addButton.setVisible(False) self.__removeButton.setVisible(False) self.__removeAllButton.setVisible(False) self.__showHideButton.setIcon(PixmapCache().getIcon('more.png')) self.__showHideButton.setToolTip("Show ignored exceptions list") self.__minH = self.minimumHeight() self.__maxH = self.maximumHeight() self.setMinimumHeight(self.headerFrame.height()) self.setMaximumHeight(self.headerFrame.height()) Settings().showIgnoredExcViewer = False else: self.exceptionsList.setVisible(True) self.__excTypeEdit.setVisible(True) self.__addButton.setVisible(True) self.__removeButton.setVisible(True) self.__removeAllButton.setVisible(True) self.__showHideButton.setIcon(PixmapCache().getIcon('less.png')) self.__showHideButton.setToolTip("Hide ignored exceptions list") self.setMinimumHeight(self.__minH) self.setMaximumHeight(self.__maxH) Settings().showIgnoredExcViewer = True return def __onSelectionChanged(self): " Triggered when the current item is changed " selected = list(self.exceptionsList.selectedItems()) if selected: self.__currentItem = selected[0] self.__removeButton.setEnabled(True) else: self.__currentItem = None self.__removeButton.setEnabled(False) return def __showContextMenu(self, coord): " Shows the frames list context menu " contextItem = self.exceptionsList.itemAt(coord) if contextItem is not None: self.__currentItem = contextItem self.__excptMenu.popup(QCursor.pos()) return def __updateTitle(self): " Updates the section title " count = self.exceptionsList.topLevelItemCount() if count == 0: self.__excptLabel.setText("Ignored exception types") else: self.__excptLabel.setText("Ignored exception types (total: " + str(count) + ")") self.__removeAllButton.setEnabled(count != 0) return def __onProjectChanged(self, what): " Triggered when a project is changed " if what != CodimensionProject.CompleteProject: return self.clear() project = GlobalData().project if project.isLoaded(): self.__ignored = list(project.ignoredExcpt) else: self.__ignored = list(Settings().ignoredExceptions) for exceptionType in self.__ignored: item = QTreeWidgetItem(self.exceptionsList) item.setText(0, exceptionType) self.__updateTitle() return def __onNewFilterChanged(self, text): " Triggered when the text is changed " text = str(text).strip() if text == "": self.__addButton.setEnabled(False) return if " " in text: self.__addButton.setEnabled(False) return if text in self.__ignored: self.__addButton.setEnabled(False) return self.__addButton.setEnabled(True) return def __onAddExceptionFilter(self): " Adds an item into the ignored exceptions list " text = self.__excTypeEdit.text().strip() self.addExceptionFilter(text) def addExceptionFilter(self, excType): " Adds a new item into the ignored exceptions list " if excType == "": return if " " in excType: return if excType in self.__ignored: return item = QTreeWidgetItem(self.exceptionsList) item.setText(0, excType) project = GlobalData().project if project.isLoaded(): project.addExceptionFilter(excType) else: Settings().addExceptionFilter(excType) self.__ignored.append(excType) self.__updateTitle() return def __onRemoveFromIgnore(self): " Removes an item from the ignored exception types list " if self.__currentItem is None: return text = self.__currentItem.text(0) # Find the item index and remove it index = 0 while True: if self.exceptionsList.topLevelItem(index).text(0) == text: self.exceptionsList.takeTopLevelItem(index) break index += 1 project = GlobalData().project if project.isLoaded(): project.deleteExceptionFilter(text) else: Settings().deleteExceptionFilter(text) self.__ignored.remove(text) self.__updateTitle() return def __onRemoveAllFromIgnore(self): " Triggered when all the ignored exceptions should be deleted " self.clear() project = GlobalData().project if project.isLoaded(): project.setExceptionFilters([]) else: Settings().setExceptionFilters([]) return def isIgnored(self, exceptionType): " Returns True if this exception type should be ignored " return exceptionType in self.__ignored
class ClientExceptionsViewer( QWidget ): " Implements the client exceptions viewer for a debugger " def __init__( self, parent, ignoredExceptionsViewer ): QWidget.__init__( self, parent ) self.__ignoredExceptionsViewer = ignoredExceptionsViewer self.__currentItem = None self.__createPopupMenu() self.__createLayout() GlobalData().project.projectChanged.connect( self.__onProjectChanged ) return def setFocus( self ): " Sets the widget focus " self.exceptionsList.setFocus() return def __createPopupMenu( self ): " Creates the popup menu " self.__excptMenu = QMenu() self.__addToIgnoreMenuItem = self.__excptMenu.addAction( "Add to ignore list", self.__onAddToIgnore ) self.__jumpToCodeMenuItem = self.__excptMenu.addAction( "Jump to code", self.__onJumpToCode ) return def __createLayout( self ): " Creates the widget layout " verticalLayout = QVBoxLayout( self ) verticalLayout.setContentsMargins( 0, 0, 0, 0 ) verticalLayout.setSpacing( 0 ) self.headerFrame = QFrame() self.headerFrame.setFrameStyle( QFrame.StyledPanel ) self.headerFrame.setAutoFillBackground( True ) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color( QPalette.Background ) headerBackground.setRgb( min( headerBackground.red() + 30, 255 ), min( headerBackground.green() + 30, 255 ), min( headerBackground.blue() + 30, 255 ) ) headerPalette.setColor( QPalette.Background, headerBackground ) self.headerFrame.setPalette( headerPalette ) self.headerFrame.setFixedHeight( 24 ) self.__excptLabel = QLabel( "Exceptions" ) fixedSpacer = QSpacerItem( 3, 3 ) headerLayout = QHBoxLayout() headerLayout.setContentsMargins( 0, 0, 0, 0 ) headerLayout.addSpacerItem( fixedSpacer ) headerLayout.addWidget( self.__excptLabel ) self.headerFrame.setLayout( headerLayout ) self.exceptionsList = QTreeWidget( self ) self.exceptionsList.setSortingEnabled( False ) self.exceptionsList.setAlternatingRowColors( True ) self.exceptionsList.setRootIsDecorated( True ) self.exceptionsList.setItemsExpandable( True ) self.exceptionsList.setUniformRowHeights( True ) self.exceptionsList.setSelectionMode( QAbstractItemView.SingleSelection ) self.exceptionsList.setSelectionBehavior( QAbstractItemView.SelectRows ) self.exceptionsList.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.exceptionsList.setContextMenuPolicy( Qt.CustomContextMenu ) self.__addToIgnoreButton = QAction( PixmapCache().getIcon( 'add.png' ), "Add exception to the list of ignored", self ) self.__addToIgnoreButton.triggered.connect( self.__onAddToIgnore ) self.__addToIgnoreButton.setEnabled( False ) expandingSpacer = QWidget() expandingSpacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.__jumpToCodeButton = QAction( PixmapCache().getIcon( 'gotoline.png' ), "Jump to the code", self ) self.__jumpToCodeButton.triggered.connect( self.__onJumpToCode ) self.__jumpToCodeButton.setEnabled( False ) self.__delAllButton = QAction( PixmapCache().getIcon( 'trash.png' ), "Delete all the client exceptions", self ) self.__delAllButton.triggered.connect( self.__onDelAll ) self.__delAllButton.setEnabled( False ) self.toolbar = QToolBar() self.toolbar.setOrientation( Qt.Horizontal ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.TopToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedHeight( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addAction( self.__addToIgnoreButton ) self.toolbar.addAction( self.__jumpToCodeButton ) self.toolbar.addWidget( expandingSpacer ) self.toolbar.addAction( self.__delAllButton ) self.exceptionsList.itemDoubleClicked.connect( self.__onExceptionDoubleClicked ) self.exceptionsList.customContextMenuRequested.connect( self.__showContextMenu ) self.exceptionsList.itemSelectionChanged.connect( self.__onSelectionChanged ) self.exceptionsList.setHeaderLabels( [ "Exception" ] ) verticalLayout.addWidget( self.headerFrame ) verticalLayout.addWidget( self.toolbar ) verticalLayout.addWidget( self.exceptionsList ) return def clear( self ): " Clears the content " self.exceptionsList.clear() self.__updateExceptionsLabel() self.__addToIgnoreButton.setEnabled( False ) self.__jumpToCodeButton.setEnabled( False ) self.__delAllButton.setEnabled( False ) self.__currentItem = None self.emit( SIGNAL( 'ClientExceptionsCleared' ) ) return def __onExceptionDoubleClicked( self, item, column ): " Triggered when an exception is double clicked " if self.__currentItem is not None: if self.__currentItem.getType() == STACK_FRAME_ITEM: self.__onJumpToCode() return # This is an exception item itself. # Open a separate dialog window with th detailed info. return def __showContextMenu( self, coord ): " Shows the frames list context menu " self.__contextItem = self.exceptionsList.itemAt( coord ) self.__addToIgnoreMenuItem.setEnabled( self.__addToIgnoreButton.isEnabled() ) self.__jumpToCodeMenuItem.setEnabled( self.__jumpToCodeButton.isEnabled() ) if self.__contextItem is not None: self.__excptMenu.popup( QCursor.pos() ) return def __onAddToIgnore( self ): " Adds an exception into the ignore list " if self.__currentItem is not None: self.__ignoredExceptionsViewer.addExceptionFilter( str( self.__currentItem.getExceptionType() ) ) self.__addToIgnoreButton.setEnabled( False ) return def __onJumpToCode( self ): " Jumps to the corresponding source code line " if self.__currentItem is not None: if self.__currentItem.getType() == STACK_FRAME_ITEM: fileName = self.__currentItem.getFileName() if '<' not in fileName and '>' not in fileName: lineNumber = self.__currentItem.getLineNumber() editorsManager = GlobalData().mainWindow.editorsManager() editorsManager.openFile( fileName, lineNumber ) editor = editorsManager.currentWidget().getEditor() editor.gotoLine( lineNumber ) editorsManager.currentWidget().setFocus() return def __onDelAll( self ): " Triggered when all the exceptions should be deleted " self.clear() return def addException( self, exceptionType, exceptionMessage, stackTrace ): " Adds the exception to the view " for index in xrange( self.exceptionsList.topLevelItemCount() ): item = self.exceptionsList.topLevelItem( index ) if item.equal( exceptionType, exceptionMessage, stackTrace ): item.incrementCounter() self.exceptionsList.clearSelection() self.exceptionsList.setCurrentItem( item ) self.__updateExceptionsLabel() return item = ExceptionItem( self.exceptionsList, exceptionType, exceptionMessage, stackTrace ) self.exceptionsList.clearSelection() self.exceptionsList.setCurrentItem( item ) self.__updateExceptionsLabel() self.__delAllButton.setEnabled( True ) return def __updateExceptionsLabel( self ): " Updates the exceptions header label " total = self.getTotalCount() if total > 0: self.__excptLabel.setText( "Exceptions (total: " + str( total ) + ")" ) else: self.__excptLabel.setText( "Exceptions" ) return def getTotalCount( self ): " Provides the total number of exceptions " count = 0 for index in xrange( self.exceptionsList.topLevelItemCount() ): count += self.exceptionsList.topLevelItem( index ).getCount() return count def __onProjectChanged( self, what ): " Triggered when a project is changed " if what == CodimensionProject.CompleteProject: self.clear() return def __onSelectionChanged( self ): " Triggered when the current item is changed " selected = list( self.exceptionsList.selectedItems() ) if selected: self.__currentItem = selected[ 0 ] if self.__currentItem.getType() == STACK_FRAME_ITEM: fileName = self.__currentItem.getFileName() if '<' in fileName or '>' in fileName: self.__jumpToCodeButton.setEnabled( False ) else: self.__jumpToCodeButton.setEnabled( True ) self.__addToIgnoreButton.setEnabled( False ) else: self.__jumpToCodeButton.setEnabled( False ) excType = str( self.__currentItem.getExceptionType() ) if self.__ignoredExceptionsViewer.isIgnored( excType ) or \ " " in excType or excType.startswith( "unhandled" ): self.__addToIgnoreButton.setEnabled( False ) else: self.__addToIgnoreButton.setEnabled( True ) else: self.__currentItem = None self.__addToIgnoreButton.setEnabled( False ) self.__jumpToCodeButton.setEnabled( False ) return
class RecentProjectsViewer( QWidget ): " Recent projects viewer implementation " def __init__( self, parent = None ): QWidget.__init__( self, parent ) self.__projectContextItem = None self.__fileContextItem = None self.upper = self.__createRecentFilesLayout() self.lower = self.__createRecentProjectsLayout() self.__createProjectPopupMenu() self.__createFilePopupMenu() layout = QVBoxLayout() layout.setContentsMargins( 1, 1, 1, 1 ) splitter = QSplitter( Qt.Vertical ) splitter.addWidget( self.upper ) splitter.addWidget( self.lower ) splitter.setCollapsible( 0, False ) splitter.setCollapsible( 1, False ) layout.addWidget( splitter ) self.setLayout( layout ) self.__populateProjects() self.__populateFiles() self.__updateProjectToolbarButtons() self.__updateFileToolbarButtons() # Debugging mode support self.__debugMode = False parent.debugModeChanged.connect( self.__onDebugMode ) return def setTooltips( self, switchOn ): " Switches the tooltips mode " for index in xrange( 0, self.recentFilesView.topLevelItemCount() ): self.recentFilesView.topLevelItem( index ).updateIconAndTooltip() for index in xrange( 0, self.projectsView.topLevelItemCount() ): self.projectsView.topLevelItem( index ).updateTooltip() return def __createFilePopupMenu( self ): " create the recent files popup menu " self.__fileMenu = QMenu( self.recentFilesView ) self.__openMenuItem = self.__fileMenu.addAction( \ PixmapCache().getIcon( 'openitem.png' ), 'Open', self.__openFile ) self.__copyPathFileMenuItem = self.__fileMenu.addAction( \ PixmapCache().getIcon( 'copytoclipboard.png' ), 'Copy path to clipboard', self.__filePathToClipboard ) self.__fileMenu.addSeparator() self.__delFileMenuItem = self.__fileMenu.addAction( \ PixmapCache().getIcon( 'trash.png' ), 'Delete from recent', self.__deleteFile ) self.recentFilesView.setContextMenuPolicy( Qt.CustomContextMenu ) self.connect( self.recentFilesView, SIGNAL( "customContextMenuRequested(const QPoint &)" ), self.__handleShowFileContextMenu ) self.connect( GlobalData().project, SIGNAL( 'recentFilesChanged' ), self.__populateFiles ) return def __createProjectPopupMenu( self ): " Creates the recent project popup menu " self.__projectMenu = QMenu( self.projectsView ) self.__prjLoadMenuItem = self.__projectMenu.addAction( \ PixmapCache().getIcon( 'load.png' ), 'Load', self.__loadProject ) self.__projectMenu.addSeparator() self.__propsMenuItem = self.__projectMenu.addAction( \ PixmapCache().getIcon( 'smalli.png' ), 'Properties', self.__viewProperties ) self.__prjCopyPathMenuItem = self.__projectMenu.addAction( \ PixmapCache().getIcon( 'copytoclipboard.png' ), 'Copy path to clipboard', self.__prjPathToClipboard ) self.__projectMenu.addSeparator() self.__delPrjMenuItem = self.__projectMenu.addAction( \ PixmapCache().getIcon( 'trash.png' ), 'Delete from recent', self.__deleteProject ) self.projectsView.setContextMenuPolicy( Qt.CustomContextMenu ) self.connect( self.projectsView, SIGNAL( "customContextMenuRequested(const QPoint &)" ), self.__handleShowPrjContextMenu ) self.connect( Settings().iInstance, SIGNAL( 'recentListChanged' ), self.__populateProjects ) GlobalData().project.projectChanged.connect( self.__projectChanged ) return def __createRecentFilesLayout( self ): " Creates the upper part - recent files " headerFrame = QFrame() headerFrame.setFrameStyle( QFrame.StyledPanel ) headerFrame.setAutoFillBackground( True ) headerPalette = headerFrame.palette() headerBackground = headerPalette.color( QPalette.Background ) headerBackground.setRgb( min( headerBackground.red() + 30, 255 ), min( headerBackground.green() + 30, 255 ), min( headerBackground.blue() + 30, 255 ) ) headerPalette.setColor( QPalette.Background, headerBackground ) headerFrame.setPalette( headerPalette ) headerFrame.setFixedHeight( 24 ) recentFilesLabel = QLabel() recentFilesLabel.setText( "Recent files" ) headerLayout = QHBoxLayout() headerLayout.setContentsMargins( 3, 0, 0, 0 ) headerLayout.addWidget( recentFilesLabel ) headerFrame.setLayout( headerLayout ) self.recentFilesView = QTreeWidget() self.recentFilesView.setAlternatingRowColors( True ) self.recentFilesView.setRootIsDecorated( False ) self.recentFilesView.setItemsExpandable( False ) self.recentFilesView.setSortingEnabled( True ) self.recentFilesView.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.recentFilesView.setUniformRowHeights( True ) self.__filesHeaderItem = QTreeWidgetItem( [ "", "File", "Absolute path" ] ) self.recentFilesView.setHeaderItem( self.__filesHeaderItem ) self.recentFilesView.header().setSortIndicator( 1, Qt.AscendingOrder ) self.connect( self.recentFilesView, SIGNAL( "itemSelectionChanged()" ), self.__fileSelectionChanged ) self.connect( self.recentFilesView, SIGNAL( "itemActivated(QTreeWidgetItem *, int)" ), self.__fileActivated ) # Toolbar part - buttons self.openFileButton = QAction( PixmapCache().getIcon( 'openitem.png' ), 'Open the highlighted file', self ) self.connect( self.openFileButton, SIGNAL( "triggered()" ), self.__openFile ) self.copyFilePathButton = QAction( \ PixmapCache().getIcon( 'copytoclipboard.png' ), 'Copy path to clipboard', self ) self.connect( self.copyFilePathButton, SIGNAL( "triggered()" ), self.__filePathToClipboard ) spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.trashFileButton = QAction( PixmapCache().getIcon( 'delitem.png' ), 'Remove selected (not from the disk)', self ) self.connect( self.trashFileButton, SIGNAL( "triggered()" ), self.__deleteFile ) self.upperToolbar = QToolBar() self.upperToolbar.setMovable( False ) self.upperToolbar.setAllowedAreas( Qt.TopToolBarArea ) self.upperToolbar.setIconSize( QSize( 16, 16 ) ) self.upperToolbar.setFixedHeight( 28 ) self.upperToolbar.setContentsMargins( 0, 0, 0, 0 ) self.upperToolbar.addAction( self.openFileButton ) self.upperToolbar.addAction( self.copyFilePathButton ) self.upperToolbar.addWidget( spacer ) self.upperToolbar.addAction( self.trashFileButton ) recentFilesLayout = QVBoxLayout() recentFilesLayout.setContentsMargins( 0, 0, 0, 0 ) recentFilesLayout.setSpacing( 0 ) recentFilesLayout.addWidget( headerFrame ) recentFilesLayout.addWidget( self.upperToolbar ) recentFilesLayout.addWidget( self.recentFilesView ) upperContainer = QWidget() upperContainer.setContentsMargins( 0, 0, 0, 0 ) upperContainer.setLayout( recentFilesLayout ) return upperContainer def getRecentFilesToolbar( self ): " Provides a reference to the recent files toolbar " return self.upperToolbar def __createRecentProjectsLayout( self ): " Creates the bottom layout " self.headerFrame = QFrame() self.headerFrame.setFrameStyle( QFrame.StyledPanel ) self.headerFrame.setAutoFillBackground( True ) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color( QPalette.Background ) headerBackground.setRgb( min( headerBackground.red() + 30, 255 ), min( headerBackground.green() + 30, 255 ), min( headerBackground.blue() + 30, 255 ) ) headerPalette.setColor( QPalette.Background, headerBackground ) self.headerFrame.setPalette( headerPalette ) self.headerFrame.setFixedHeight( 24 ) recentProjectsLabel = QLabel() recentProjectsLabel.setText( "Recent projects" ) expandingSpacer = QSpacerItem( 10, 10, QSizePolicy.Expanding ) self.__showHideButton = QToolButton() self.__showHideButton.setAutoRaise( True ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'less.png' ) ) self.__showHideButton.setFixedSize( 20, 20 ) self.__showHideButton.setToolTip( "Hide recent projects list" ) self.__showHideButton.setFocusPolicy( Qt.NoFocus ) self.connect( self.__showHideButton, SIGNAL( 'clicked()' ), self.__onShowHide ) headerLayout = QHBoxLayout() headerLayout.setContentsMargins( 3, 0, 0, 0 ) headerLayout.addWidget( recentProjectsLabel ) headerLayout.addSpacerItem( expandingSpacer ) headerLayout.addWidget( self.__showHideButton ) self.headerFrame.setLayout( headerLayout ) # Toolbar part - buttons self.loadButton = QAction( PixmapCache().getIcon( 'load.png' ), 'Load the highlighted project', self ) self.connect( self.loadButton, SIGNAL( "triggered()" ), self.__loadProject ) self.propertiesButton = QAction( PixmapCache().getIcon( 'smalli.png' ), 'Show the highlighted project ' \ 'properties', self ) self.connect( self.propertiesButton, SIGNAL( "triggered()" ), self.__viewProperties ) self.copyPrjPathButton = QAction( \ PixmapCache().getIcon( 'copytoclipboard.png' ), 'Copy path to clipboard', self ) self.connect( self.copyPrjPathButton, SIGNAL( "triggered()" ), self.__prjPathToClipboard ) spacer = QWidget() spacer.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.trashButton = QAction( PixmapCache().getIcon( 'delitem.png' ), 'Remove selected (not from the disk)', self ) self.connect( self.trashButton, SIGNAL( "triggered()" ), self.__deleteProject ) self.lowerToolbar = QToolBar() self.lowerToolbar.setMovable( False ) self.lowerToolbar.setAllowedAreas( Qt.TopToolBarArea ) self.lowerToolbar.setIconSize( QSize( 16, 16 ) ) self.lowerToolbar.setFixedHeight( 28 ) self.lowerToolbar.setContentsMargins( 0, 0, 0, 0 ) self.lowerToolbar.addAction( self.loadButton ) self.lowerToolbar.addAction( self.propertiesButton ) self.lowerToolbar.addAction( self.copyPrjPathButton ) self.lowerToolbar.addWidget( spacer ) self.lowerToolbar.addAction( self.trashButton ) self.projectsView = QTreeWidget() self.projectsView.setAlternatingRowColors( True ) self.projectsView.setRootIsDecorated( False ) self.projectsView.setItemsExpandable( False ) self.projectsView.setSortingEnabled( True ) self.projectsView.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.projectsView.setUniformRowHeights( True ) self.__projectsHeaderItem = QTreeWidgetItem( [ "", "Project", "Absolute path" ] ) self.projectsView.setHeaderItem( self.__projectsHeaderItem ) self.projectsView.header().setSortIndicator( 1, Qt.AscendingOrder ) self.connect( self.projectsView, SIGNAL( "itemActivated(QTreeWidgetItem *, int)" ), self.__projectActivated ) self.connect( self.projectsView, SIGNAL( "itemSelectionChanged()" ), self.__projectSelectionChanged ) recentProjectsLayout = QVBoxLayout() recentProjectsLayout.setContentsMargins( 0, 0, 0, 0 ) recentProjectsLayout.setSpacing( 0 ) recentProjectsLayout.addWidget( self.headerFrame ) recentProjectsLayout.addWidget( self.lowerToolbar ) recentProjectsLayout.addWidget( self.projectsView ) lowerContainer = QWidget() lowerContainer.setContentsMargins( 0, 0, 0, 0 ) lowerContainer.setLayout( recentProjectsLayout ) return lowerContainer def getRecentProjectsToolbar( self ): " Provides a reference to the projects toolbar " return self.lowerToolbar def __projectSelectionChanged( self ): " Handles the projects changed selection " selected = list( self.projectsView.selectedItems() ) if selected: self.__projectContextItem = selected[ 0 ] else: self.__projectContextItem = None self.__updateProjectToolbarButtons() return def __fileSelectionChanged( self ): " Handles the files changed selection " selected = list( self.recentFilesView.selectedItems() ) if selected: self.__fileContextItem = selected[ 0 ] else: self.__fileContextItem = None self.__updateFileToolbarButtons() return def __updateProjectToolbarButtons( self ): " Updates the toolbar buttons depending on the __projectContextItem " if self.__projectContextItem is None: self.loadButton.setEnabled( False ) self.propertiesButton.setEnabled( False ) self.copyPrjPathButton.setEnabled( False ) self.trashButton.setEnabled( False ) else: enabled = self.__projectContextItem.isValid() isCurrentProject = self.__projectContextItem.isCurrent() self.propertiesButton.setEnabled( enabled ) self.copyPrjPathButton.setEnabled( True ) self.loadButton.setEnabled( enabled and not isCurrentProject and not self.__debugMode ) self.trashButton.setEnabled( not isCurrentProject ) return def __updateFileToolbarButtons( self ): " Updates the toolbar buttons depending on the __fileContextItem " enabled = self.__fileContextItem is not None self.openFileButton.setEnabled( enabled ) self.copyFilePathButton.setEnabled( enabled ) self.trashFileButton.setEnabled( enabled ) return def __handleShowPrjContextMenu( self, coord ): " Show the project item context menu " self.__projectContextItem = self.projectsView.itemAt( coord ) if self.__projectContextItem is None: return enabled = self.__projectContextItem.isValid() isCurrentProject = self.__projectContextItem.isCurrent() self.__propsMenuItem.setEnabled( enabled ) self.__delPrjMenuItem.setEnabled( not isCurrentProject ) # fName = self.__projectContextItem.getFilename() self.__prjLoadMenuItem.setEnabled( enabled and \ not isCurrentProject and \ not self.__debugMode ) self.__projectMenu.popup( QCursor.pos() ) return def __sortProjects( self ): " Sort the project items " self.projectsView.sortItems( \ self.projectsView.sortColumn(), self.projectsView.header().sortIndicatorOrder() ) return def __sortFiles( self ): " Sort the file items " self.recentFilesView.sortItems( \ self.recentFilesView.sortColumn(), self.recentFilesView.header().sortIndicatorOrder() ) return def __resizeProjectColumns( self ): """ Resize the projects list columns """ self.projectsView.header().setStretchLastSection( True ) self.projectsView.header().resizeSections( \ QHeaderView.ResizeToContents ) self.projectsView.header().resizeSection( 0, 22 ) self.projectsView.header().setResizeMode( 0, QHeaderView.Fixed ) return def __resizeFileColumns( self ): " Resize the files list columns " self.recentFilesView.header().setStretchLastSection( True ) self.recentFilesView.header().resizeSections( \ QHeaderView.ResizeToContents ) self.recentFilesView.header().resizeSection( 0, 22 ) self.recentFilesView.header().setResizeMode( 0, QHeaderView.Fixed ) return def __projectActivated( self, item, column ): " Handles the double click (or Enter) on the item " self.__projectContextItem = item self.__loadProject() return def __fileActivated( self, item, column ): " Handles the double click (or Enter) on a file item " self.__fileContextItem = item self.__openFile() return def __viewProperties( self ): " Handles the 'view properties' context menu item " if self.__projectContextItem is None: return if not self.__projectContextItem.isValid(): return if self.__projectContextItem.isCurrent(): # This is the current project - it can be edited project = GlobalData().project dialog = ProjectPropertiesDialog( project ) if dialog.exec_() == QDialog.Accepted: importDirs = [] for index in xrange( dialog.importDirList.count() ): importDirs.append( dialog.importDirList.item( index ).text() ) scriptName = dialog.scriptEdit.text().strip() relativePath = relpath( scriptName, project.getProjectDir() ) if not relativePath.startswith( '..' ): scriptName = relativePath project.updateProperties( scriptName, importDirs, dialog.creationDateEdit.text().strip(), dialog.authorEdit.text().strip(), dialog.licenseEdit.text().strip(), dialog.copyrightEdit.text().strip(), dialog.versionEdit.text().strip(), dialog.emailEdit.text().strip(), dialog.descriptionEdit.toPlainText().strip() ) else: # This is not the current project - it can be viewed fName = self.__projectContextItem.getFilename() dialog = ProjectPropertiesDialog( fName ) dialog.exec_() return def __deleteProject( self ): " Handles the 'delete from recent' context menu item " if self.__projectContextItem is None: return # Removal from the visible list is done via a signal which comes back # from settings fName = self.__projectContextItem.getFilename() Settings().deleteRecentProject( fName ) return def __loadProject( self ): " handles 'Load' context menu item " if self.__projectContextItem is None: return if not self.__projectContextItem.isValid(): return if self.__debugMode: return projectFileName = self.__projectContextItem.getFilename() if self.__projectContextItem.isCurrent(): GlobalData().mainWindow.openFile( projectFileName, -1 ) return # This is the current project, open for text editing QApplication.processEvents() QApplication.setOverrideCursor( QCursor( Qt.WaitCursor ) ) if os.path.exists( projectFileName ): mainWin = GlobalData().mainWindow editorsManager = mainWin.editorsManagerWidget.editorsManager if editorsManager.closeRequest(): prj = GlobalData().project prj.setTabsStatus( editorsManager.getTabsStatus() ) editorsManager.closeAll() prj.loadProject( projectFileName ) mainWin.activateProjectTab() else: logging.error( "The project " + \ os.path.basename( projectFileName ) + \ " disappeared from the file system." ) self.__populateProjects() QApplication.restoreOverrideCursor() return def __populateProjects( self ): " Populates the recent projects " self.projectsView.clear() for item in Settings().recentProjects: self.projectsView.addTopLevelItem( RecentProjectViewItem( item ) ) self.__sortProjects() self.__resizeProjectColumns() self.__updateProjectToolbarButtons() return def __populateFiles( self ): " Populates the recent files " self.recentFilesView.clear() for path in GlobalData().project.recentFiles: self.recentFilesView.addTopLevelItem( RecentFileViewItem( path ) ) self.__sortFiles() self.__resizeFileColumns() self.__updateFileToolbarButtons() return def __projectChanged( self, what ): " Triggered when the current project is changed " if what == CodimensionProject.CompleteProject: self.__populateProjects() self.__populateFiles() return if what == CodimensionProject.Properties: # Update the corresponding tooltip items = self.projectsView.findItems( GlobalData().project.fileName, Qt.MatchExactly, 2 ) if len( items ) != 1: logging.error( "Unexpected number of matched projects: " + \ str( len( items ) ) ) return items[ 0 ].updateTooltip() return def __openFile( self ): " Handles 'open' file menu item " self.__fileContextItem.updateIconAndTooltip() fName = self.__fileContextItem.getFilename() if not self.__fileContextItem.isValid(): logging.warning( "Cannot open " + fName ) return fileType = detectFileType( fName ) if fileType == PixmapFileType: GlobalData().mainWindow.openPixmapFile( fName ) return GlobalData().mainWindow.openFile( fName, -1 ) return def __deleteFile( self ): " Handles 'delete from recent' file menu item " self.removeRecentFile( self.__fileContextItem.getFilename() ) return def __handleShowFileContextMenu( self, coord ): " File context menu " self.__fileContextItem = self.recentFilesView.itemAt( coord ) if self.__fileContextItem is not None: self.__fileMenu.popup( QCursor.pos() ) return def __filePathToClipboard( self ): " Copies the file item path to the clipboard " if self.__fileContextItem is not None: QApplication.clipboard().setText( \ self.__fileContextItem.getFilename() ) return def __prjPathToClipboard( self ): " Copies the project item path to the clipboard " if self.__projectContextItem is not None: QApplication.clipboard().setText( \ self.__projectContextItem.getFilename() ) return def onFileUpdated( self, fileName, uuid ): " Triggered when the file is updated: python or project " realPath = os.path.realpath( fileName ) count = self.recentFilesView.topLevelItemCount() for index in xrange( 0, count ): item = self.recentFilesView.topLevelItem( index ) itemRealPath = os.path.realpath( item.getFilename() ) if realPath == itemRealPath: item.updateIconAndTooltip() break for index in xrange( 0, self.projectsView.topLevelItemCount() ): item = self.projectsView.topLevelItem( index ) itemRealPath = os.path.realpath( item.getFilename() ) if realPath == itemRealPath: item.updateTooltip() break return def __onShowHide( self ): " Triggered when show/hide button is clicked " if self.projectsView.isVisible(): self.projectsView.setVisible( False ) self.lowerToolbar.setVisible( False ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'more.png' ) ) self.__showHideButton.setToolTip( "Show recent projects list" ) self.__minH = self.lower.minimumHeight() self.__maxH = self.lower.maximumHeight() self.lower.setMinimumHeight( self.headerFrame.height() ) self.lower.setMaximumHeight( self.headerFrame.height() ) else: self.projectsView.setVisible( True ) self.lowerToolbar.setVisible( True ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'less.png' ) ) self.__showHideButton.setToolTip( "Hide recent projects list" ) self.lower.setMinimumHeight( self.__minH ) self.lower.setMaximumHeight( self.__maxH ) return def __onDebugMode( self, newState ): " Triggered when debug mode has changed " self.__debugMode = newState # Disable the load project button self.__updateProjectToolbarButtons() return def removeRecentFile( self, fName ): " Removes a single file from the recent files list " GlobalData().project.removeRecentFile( fName ) for index in xrange( self.recentFilesView.topLevelItemCount() ): candidate = self.recentFilesView.topLevelItem( index ) if candidate.getFilename() == fName: self.recentFilesView.takeTopLevelItem( index ) return return
class BookmarksWindow(QDialog): """ A simple UI for showing bookmarks and navigating to them. FIXME: For now, this window is tied to a particular lane. If your project has more than one lane, then each one will have it's own bookmark window, which is kinda dumb. """ def __init__(self, parent, topLevelOperatorView): super(BookmarksWindow, self).__init__(parent) self.setWindowTitle("Bookmarks") self.topLevelOperatorView = topLevelOperatorView self.bookmark_tree = QTreeWidget(self) self.bookmark_tree.setHeaderLabels(["Location", "Notes"]) self.bookmark_tree.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.bookmark_tree.setColumnWidth(0, 200) self.bookmark_tree.setColumnWidth(1, 300) self.note_edit = QLineEdit(self) self.add_bookmark_button = QPushButton("Add Bookmark", self, clicked=self.add_bookmark) geometry = self.geometry() geometry.setSize(QSize(520, 520)) self.setGeometry(geometry) layout = QVBoxLayout() layout.addWidget(self.bookmark_tree) layout.addWidget(self.note_edit) layout.addWidget(self.add_bookmark_button) self.setLayout(layout) self._load_bookmarks() self.bookmark_tree.setContextMenuPolicy(Qt.CustomContextMenu) self.bookmark_tree.customContextMenuRequested.connect( self.showContextMenu) self.bookmark_tree.itemDoubleClicked.connect(self._handle_doubleclick) def _handle_doubleclick(self, item, col): """ Navigate to the bookmark """ data = item.data(0, Qt.UserRole).toPyObject() if data is None: return (coord, notes) = data axes = self.topLevelOperatorView.InputImages.meta.getAxisKeys() axes = axes[:-1] # drop channel axes = sorted(axes) assert len(axes) == len(coord) tagged_coord = dict(zip(axes, coord)) tagged_location = OrderedDict(zip('txyzc', (0, 0, 0, 0, 0))) tagged_location.update(tagged_coord) t = tagged_location.values()[0] coord3d = tagged_location.values()[1:4] self.parent().editor.posModel.time = t self.parent().editor.navCtrl.panSlicingViews(coord3d, [0, 1, 2]) self.parent().editor.posModel.slicingPos = coord3d def showContextMenu(self, pos): item = self.bookmark_tree.itemAt(pos) data = item.data(0, Qt.UserRole).toPyObject() if data is None: return def delete_bookmark(): (coord, notes) = data bookmarks = list(self.topLevelOperatorView.Bookmarks.value) i = bookmarks.index((coord, notes)) bookmarks.pop(i) self.topLevelOperatorView.Bookmarks.setValue(bookmarks) self._load_bookmarks() menu = QMenu(parent=self) menu.addAction(QAction("Delete", menu, triggered=delete_bookmark)) globalPos = self.bookmark_tree.viewport().mapToGlobal(pos) menu.exec_(globalPos) #selection = menu.exec_( globalPos ) #if selection is removeLanesAction: # self.removeLanesRequested.emit( self._selectedLanes ) def add_bookmark(self): coord_txyzc = self.parent().editor.posModel.slicingPos5D tagged_coord_txyzc = dict(zip('txyzc', coord_txyzc)) axes = self.topLevelOperatorView.InputImages.meta.getAxisKeys() axes = axes[:-1] # drop channel axes = sorted(axes) coord = tuple(tagged_coord_txyzc[c] for c in axes) notes = str(self.note_edit.text()) bookmarks = list(self.topLevelOperatorView.Bookmarks.value) bookmarks.append((coord, notes)) self.topLevelOperatorView.Bookmarks.setValue(bookmarks) self._load_bookmarks() def _load_bookmarks(self): self.bookmark_tree.clear() lane_index = self.topLevelOperatorView.current_view_index() lane_nickname = self.topLevelOperatorView.InputImages.meta.nickname or "Lane {}".format( lane_index) bookmarks = self.topLevelOperatorView.Bookmarks.value group_item = QTreeWidgetItem(self.bookmark_tree, QStringList(lane_nickname)) for coord, notes in bookmarks: item = QTreeWidgetItem(group_item, QStringList()) item.setText(0, str(coord)) item.setData(0, Qt.UserRole, (coord, notes)) item.setText(1, notes) self.bookmark_tree.expandAll()
class StackViewer( QWidget ): " Implements the stack viewer for a debugger " def __init__( self, debugger, parent = None ): QWidget.__init__( self, parent ) self.__debugger = debugger self.currentStack = None self.currentFrame = 0 self.__createPopupMenu() self.__createLayout() if Settings().showStackViewer == False: self.__onShowHide( True ) return def __createPopupMenu( self ): " Creates the popup menu " self.__framesMenu = QMenu() self.__setCurrentMenuItem = self.__framesMenu.addAction( "Set current (single click)", self.__onSetCurrent ) self.__jumpMenuItem = self.__framesMenu.addAction( "Set current and jump to the source (double click)", self.__onSetCurrentAndJump ) return def __createLayout( self ): " Creates the widget layout " verticalLayout = QVBoxLayout( self ) verticalLayout.setContentsMargins( 0, 0, 0, 0 ) verticalLayout.setSpacing( 0 ) self.headerFrame = QFrame() self.headerFrame.setFrameStyle( QFrame.StyledPanel ) self.headerFrame.setAutoFillBackground( True ) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color( QPalette.Background ) headerBackground.setRgb( min( headerBackground.red() + 30, 255 ), min( headerBackground.green() + 30, 255 ), min( headerBackground.blue() + 30, 255 ) ) headerPalette.setColor( QPalette.Background, headerBackground ) self.headerFrame.setPalette( headerPalette ) self.headerFrame.setFixedHeight( 24 ) self.__stackLabel = QLabel( "Stack" ) expandingSpacer = QSpacerItem( 10, 10, QSizePolicy.Expanding ) fixedSpacer = QSpacerItem( 3, 3 ) self.__showHideButton = QToolButton() self.__showHideButton.setAutoRaise( True ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'less.png' ) ) self.__showHideButton.setFixedSize( 20, 20 ) self.__showHideButton.setToolTip( "Hide frames list" ) self.__showHideButton.setFocusPolicy( Qt.NoFocus ) self.__showHideButton.clicked.connect( self.__onShowHide ) headerLayout = QHBoxLayout() headerLayout.setContentsMargins( 0, 0, 0, 0 ) headerLayout.addSpacerItem( fixedSpacer ) headerLayout.addWidget( self.__stackLabel ) headerLayout.addSpacerItem( expandingSpacer ) headerLayout.addWidget( self.__showHideButton ) self.headerFrame.setLayout( headerLayout ) self.__framesList = QTreeWidget( self ) self.__framesList.setSortingEnabled( False ) # I might not need that because of two reasons: # - the window has no focus # - the window has custom current indicator # self.__framesList.setAlternatingRowColors( True ) self.__framesList.setRootIsDecorated( False ) self.__framesList.setItemsExpandable( False ) self.__framesList.setUniformRowHeights( True ) self.__framesList.setSelectionMode( QAbstractItemView.NoSelection ) self.__framesList.setSelectionBehavior( QAbstractItemView.SelectRows ) self.__framesList.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.__framesList.setFocusPolicy( Qt.NoFocus ) self.__framesList.setContextMenuPolicy( Qt.CustomContextMenu ) self.__framesList.itemClicked.connect( self.__onFrameClicked ) self.__framesList.itemDoubleClicked.connect( self.__onFrameDoubleClicked ) self.__framesList.customContextMenuRequested.connect( self.__showContextMenu ) self.__framesList.setHeaderLabels( [ "", "File:line", "Function", "Full path" ] ) verticalLayout.addWidget( self.headerFrame ) verticalLayout.addWidget( self.__framesList ) return def __onShowHide( self, startup = False ): " Triggered when show/hide button is clicked " if startup or self.__framesList.isVisible(): self.__framesList.setVisible( False ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'more.png' ) ) self.__showHideButton.setToolTip( "Show frames list" ) self.__minH = self.minimumHeight() self.__maxH = self.maximumHeight() self.setMinimumHeight( self.headerFrame.height() ) self.setMaximumHeight( self.headerFrame.height() ) Settings().showStackViewer = False else: self.__framesList.setVisible( True ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'less.png' ) ) self.__showHideButton.setToolTip( "Hide frames list" ) self.setMinimumHeight( self.__minH ) self.setMaximumHeight( self.__maxH ) Settings().showStackViewer = True return def clear( self ): " Clears the content " self.__framesList.clear() self.currentStack = None self.__stackLabel.setText( "Stack" ) return def __resizeColumns( self ): " Resize the files list columns " self.__framesList.header().setStretchLastSection( True ) self.__framesList.header().resizeSections( QHeaderView.ResizeToContents ) self.__framesList.header().resizeSection( 0, 22 ) self.__framesList.header().setResizeMode( 0, QHeaderView.Fixed ) return def populate( self, stack ): " Sets the new call stack and selects the first item in it " self.clear() self.currentStack = stack self.currentFrame = 0 frameNumber = 0 for s in stack: if len( s ) == 2: # This is when an exception comes funcName = "" else: funcName = s[ 2 ] item = StackFrameItem( s[ 0 ], s[ 1 ], funcName, frameNumber ) self.__framesList.addTopLevelItem( item ) frameNumber += 1 self.__resizeColumns() self.__framesList.topLevelItem( 0 ).setCurrent( True ) self.__stackLabel.setText( "Stack (total: " + str( len( stack ) ) + ")" ) return def getFrameNumber( self ): " Provides the current frame number " return self.currentFrame def __onFrameClicked( self, item, column ): " Triggered when a frame is clicked " if item.isCurrent(): return # Hide the current indicator self.__framesList.topLevelItem( self.currentFrame ).setCurrent( False ) # Show the new indicator self.currentFrame = item.getFrameNumber() for index in xrange( self.__framesList.topLevelItemCount() ): item = self.__framesList.topLevelItem( index ) if item.getFrameNumber() == self.currentFrame: item.setCurrent( True ) self.__debugger.remoteClientVariables( 1, self.currentFrame ) # globals self.__debugger.remoteClientVariables( 0, self.currentFrame ) # locals return def __onFrameDoubleClicked( self, item, column ): " Triggered when a frame is double clicked " # The frame has been switched already because the double click # signal always comes after the single click one fileName = item.getFilename() lineNumber = item.getLineNumber() editorsManager = GlobalData().mainWindow.editorsManager() editorsManager.openFile( fileName, lineNumber ) editor = editorsManager.currentWidget().getEditor() editor.gotoLine( lineNumber ) editorsManager.currentWidget().setFocus() return def __showContextMenu( self, coord ): " Shows the frames list context menu " self.__contextItem = self.__framesList.itemAt( coord ) if self.__contextItem is not None: self.__setCurrentMenuItem.setEnabled( not self.__contextItem.isCurrent() ) self.__framesMenu.popup( QCursor.pos() ) return def __onSetCurrent( self ): " Context menu item handler " self.__onFrameClicked( self.__contextItem, 0 ) return def __onSetCurrentAndJump( self ): " Context menu item handler " self.__onFrameClicked( self.__contextItem, 0 ) self.__onFrameDoubleClicked( self.__contextItem, 0 ) return def switchControl( self, isInIDE ): " Switches the UI depending where the control flow is " self.__framesList.setEnabled( isInIDE ) return
class RecentProjectsViewer(QWidget): " Recent projects viewer implementation " def __init__(self, parent=None): QWidget.__init__(self, parent) self.__projectContextItem = None self.__fileContextItem = None self.upper = self.__createRecentFilesLayout() self.lower = self.__createRecentProjectsLayout() self.__createProjectPopupMenu() self.__createFilePopupMenu() layout = QVBoxLayout() layout.setContentsMargins(1, 1, 1, 1) splitter = QSplitter(Qt.Vertical) splitter.addWidget(self.upper) splitter.addWidget(self.lower) splitter.setCollapsible(0, False) splitter.setCollapsible(1, False) layout.addWidget(splitter) self.setLayout(layout) self.__populateProjects() self.__populateFiles() self.__updateProjectToolbarButtons() self.__updateFileToolbarButtons() # Debugging mode support self.__debugMode = False parent.debugModeChanged.connect(self.__onDebugMode) return def setTooltips(self, switchOn): " Switches the tooltips mode " for index in xrange(0, self.recentFilesView.topLevelItemCount()): self.recentFilesView.topLevelItem(index).updateIconAndTooltip() for index in xrange(0, self.projectsView.topLevelItemCount()): self.projectsView.topLevelItem(index).updateTooltip() return def __createFilePopupMenu(self): " create the recent files popup menu " self.__fileMenu = QMenu(self.recentFilesView) self.__openMenuItem = self.__fileMenu.addAction( getIcon('openitem.png'), 'Open', self.__openFile) self.__copyPathFileMenuItem = self.__fileMenu.addAction( getIcon('copytoclipboard.png'), 'Copy path to clipboard', self.__filePathToClipboard) self.__fileMenu.addSeparator() self.__delFileMenuItem = self.__fileMenu.addAction( getIcon('trash.png'), 'Delete from recent', self.__deleteFile) self.recentFilesView.setContextMenuPolicy(Qt.CustomContextMenu) self.connect(self.recentFilesView, SIGNAL("customContextMenuRequested(const QPoint &)"), self.__handleShowFileContextMenu) self.connect(GlobalData().project, SIGNAL('recentFilesChanged'), self.__populateFiles) return def __createProjectPopupMenu(self): " Creates the recent project popup menu " self.__projectMenu = QMenu(self.projectsView) self.__prjLoadMenuItem = self.__projectMenu.addAction( getIcon('load.png'), 'Load', self.__loadProject) self.__projectMenu.addSeparator() self.__propsMenuItem = self.__projectMenu.addAction( getIcon('smalli.png'), 'Properties', self.__viewProperties) self.__prjCopyPathMenuItem = self.__projectMenu.addAction( getIcon('copytoclipboard.png'), 'Copy path to clipboard', self.__prjPathToClipboard) self.__projectMenu.addSeparator() self.__delPrjMenuItem = self.__projectMenu.addAction( getIcon('trash.png'), 'Delete from recent', self.__deleteProject) self.projectsView.setContextMenuPolicy(Qt.CustomContextMenu) self.connect(self.projectsView, SIGNAL("customContextMenuRequested(const QPoint &)"), self.__handleShowPrjContextMenu) Settings().recentListChanged.connect(self.__populateProjects) GlobalData().project.projectChanged.connect(self.__projectChanged) return def __createRecentFilesLayout(self): " Creates the upper part - recent files " headerFrame = QFrame() headerFrame.setFrameStyle(QFrame.StyledPanel) headerFrame.setAutoFillBackground(True) headerPalette = headerFrame.palette() headerBackground = headerPalette.color(QPalette.Background) headerBackground.setRgb(min(headerBackground.red() + 30, 255), min(headerBackground.green() + 30, 255), min(headerBackground.blue() + 30, 255)) headerPalette.setColor(QPalette.Background, headerBackground) headerFrame.setPalette(headerPalette) headerFrame.setFixedHeight(24) recentFilesLabel = QLabel() recentFilesLabel.setText("Recent files") headerLayout = QHBoxLayout() headerLayout.setContentsMargins(3, 0, 0, 0) headerLayout.addWidget(recentFilesLabel) headerFrame.setLayout(headerLayout) self.recentFilesView = QTreeWidget() self.recentFilesView.setAlternatingRowColors(True) self.recentFilesView.setRootIsDecorated(False) self.recentFilesView.setItemsExpandable(False) self.recentFilesView.setSortingEnabled(True) self.recentFilesView.setItemDelegate(NoOutlineHeightDelegate(4)) self.recentFilesView.setUniformRowHeights(True) self.__filesHeaderItem = QTreeWidgetItem(["", "File", "Absolute path"]) self.recentFilesView.setHeaderItem(self.__filesHeaderItem) self.recentFilesView.header().setSortIndicator(1, Qt.AscendingOrder) self.connect(self.recentFilesView, SIGNAL("itemSelectionChanged()"), self.__fileSelectionChanged) self.connect(self.recentFilesView, SIGNAL("itemActivated(QTreeWidgetItem *, int)"), self.__fileActivated) # Toolbar part - buttons self.openFileButton = QAction(getIcon('openitem.png'), 'Open the highlighted file', self) self.connect(self.openFileButton, SIGNAL("triggered()"), self.__openFile) self.copyFilePathButton = QAction(getIcon('copytoclipboard.png'), 'Copy path to clipboard', self) self.connect(self.copyFilePathButton, SIGNAL("triggered()"), self.__filePathToClipboard) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.trashFileButton = QAction(getIcon('delitem.png'), 'Remove selected (not from the disk)', self) self.connect(self.trashFileButton, SIGNAL("triggered()"), self.__deleteFile) self.upperToolbar = QToolBar() self.upperToolbar.setMovable(False) self.upperToolbar.setAllowedAreas(Qt.TopToolBarArea) self.upperToolbar.setIconSize(QSize(16, 16)) self.upperToolbar.setFixedHeight(28) self.upperToolbar.setContentsMargins(0, 0, 0, 0) self.upperToolbar.addAction(self.openFileButton) self.upperToolbar.addAction(self.copyFilePathButton) self.upperToolbar.addWidget(spacer) self.upperToolbar.addAction(self.trashFileButton) recentFilesLayout = QVBoxLayout() recentFilesLayout.setContentsMargins(0, 0, 0, 0) recentFilesLayout.setSpacing(0) recentFilesLayout.addWidget(headerFrame) recentFilesLayout.addWidget(self.upperToolbar) recentFilesLayout.addWidget(self.recentFilesView) upperContainer = QWidget() upperContainer.setContentsMargins(0, 0, 0, 0) upperContainer.setLayout(recentFilesLayout) return upperContainer def getRecentFilesToolbar(self): " Provides a reference to the recent files toolbar " return self.upperToolbar def __createRecentProjectsLayout(self): " Creates the bottom layout " self.headerFrame = QFrame() self.headerFrame.setFrameStyle(QFrame.StyledPanel) self.headerFrame.setAutoFillBackground(True) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color(QPalette.Background) headerBackground.setRgb(min(headerBackground.red() + 30, 255), min(headerBackground.green() + 30, 255), min(headerBackground.blue() + 30, 255)) headerPalette.setColor(QPalette.Background, headerBackground) self.headerFrame.setPalette(headerPalette) self.headerFrame.setFixedHeight(24) recentProjectsLabel = QLabel() recentProjectsLabel.setText("Recent projects") expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding) self.__showHideButton = QToolButton() self.__showHideButton.setAutoRaise(True) self.__showHideButton.setIcon(getIcon('less.png')) self.__showHideButton.setFixedSize(20, 20) self.__showHideButton.setToolTip("Hide recent projects list") self.__showHideButton.setFocusPolicy(Qt.NoFocus) self.connect(self.__showHideButton, SIGNAL('clicked()'), self.__onShowHide) headerLayout = QHBoxLayout() headerLayout.setContentsMargins(3, 0, 0, 0) headerLayout.addWidget(recentProjectsLabel) headerLayout.addSpacerItem(expandingSpacer) headerLayout.addWidget(self.__showHideButton) self.headerFrame.setLayout(headerLayout) # Toolbar part - buttons self.loadButton = QAction(getIcon('load.png'), 'Load the highlighted project', self) self.connect(self.loadButton, SIGNAL("triggered()"), self.__loadProject) self.propertiesButton = QAction( getIcon('smalli.png'), 'Show the highlighted project ' 'properties', self) self.connect(self.propertiesButton, SIGNAL("triggered()"), self.__viewProperties) self.copyPrjPathButton = QAction(getIcon('copytoclipboard.png'), 'Copy path to clipboard', self) self.connect(self.copyPrjPathButton, SIGNAL("triggered()"), self.__prjPathToClipboard) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.trashButton = QAction(getIcon('delitem.png'), 'Remove selected (not from the disk)', self) self.connect(self.trashButton, SIGNAL("triggered()"), self.__deleteProject) self.lowerToolbar = QToolBar() self.lowerToolbar.setMovable(False) self.lowerToolbar.setAllowedAreas(Qt.TopToolBarArea) self.lowerToolbar.setIconSize(QSize(16, 16)) self.lowerToolbar.setFixedHeight(28) self.lowerToolbar.setContentsMargins(0, 0, 0, 0) self.lowerToolbar.addAction(self.loadButton) self.lowerToolbar.addAction(self.propertiesButton) self.lowerToolbar.addAction(self.copyPrjPathButton) self.lowerToolbar.addWidget(spacer) self.lowerToolbar.addAction(self.trashButton) self.projectsView = QTreeWidget() self.projectsView.setAlternatingRowColors(True) self.projectsView.setRootIsDecorated(False) self.projectsView.setItemsExpandable(False) self.projectsView.setSortingEnabled(True) self.projectsView.setItemDelegate(NoOutlineHeightDelegate(4)) self.projectsView.setUniformRowHeights(True) self.__projectsHeaderItem = QTreeWidgetItem( ["", "Project", "Absolute path"]) self.projectsView.setHeaderItem(self.__projectsHeaderItem) self.projectsView.header().setSortIndicator(1, Qt.AscendingOrder) self.connect(self.projectsView, SIGNAL("itemActivated(QTreeWidgetItem *, int)"), self.__projectActivated) self.connect(self.projectsView, SIGNAL("itemSelectionChanged()"), self.__projectSelectionChanged) recentProjectsLayout = QVBoxLayout() recentProjectsLayout.setContentsMargins(0, 0, 0, 0) recentProjectsLayout.setSpacing(0) recentProjectsLayout.addWidget(self.headerFrame) recentProjectsLayout.addWidget(self.lowerToolbar) recentProjectsLayout.addWidget(self.projectsView) lowerContainer = QWidget() lowerContainer.setContentsMargins(0, 0, 0, 0) lowerContainer.setLayout(recentProjectsLayout) return lowerContainer def getRecentProjectsToolbar(self): " Provides a reference to the projects toolbar " return self.lowerToolbar def __projectSelectionChanged(self): " Handles the projects changed selection " selected = list(self.projectsView.selectedItems()) if selected: self.__projectContextItem = selected[0] else: self.__projectContextItem = None self.__updateProjectToolbarButtons() return def __fileSelectionChanged(self): " Handles the files changed selection " selected = list(self.recentFilesView.selectedItems()) if selected: self.__fileContextItem = selected[0] else: self.__fileContextItem = None self.__updateFileToolbarButtons() return def __updateProjectToolbarButtons(self): " Updates the toolbar buttons depending on the __projectContextItem " if self.__projectContextItem is None: self.loadButton.setEnabled(False) self.propertiesButton.setEnabled(False) self.copyPrjPathButton.setEnabled(False) self.trashButton.setEnabled(False) else: enabled = self.__projectContextItem.isValid() isCurrentProject = self.__projectContextItem.isCurrent() self.propertiesButton.setEnabled(enabled) self.copyPrjPathButton.setEnabled(True) self.loadButton.setEnabled(enabled and not isCurrentProject and not self.__debugMode) self.trashButton.setEnabled(not isCurrentProject) return def __updateFileToolbarButtons(self): " Updates the toolbar buttons depending on the __fileContextItem " enabled = self.__fileContextItem is not None self.openFileButton.setEnabled(enabled) self.copyFilePathButton.setEnabled(enabled) self.trashFileButton.setEnabled(enabled) return def __handleShowPrjContextMenu(self, coord): " Show the project item context menu " self.__projectContextItem = self.projectsView.itemAt(coord) if self.__projectContextItem is None: return enabled = self.__projectContextItem.isValid() isCurrentProject = self.__projectContextItem.isCurrent() self.__propsMenuItem.setEnabled(enabled) self.__delPrjMenuItem.setEnabled(not isCurrentProject) # fName = self.__projectContextItem.getFilename() self.__prjLoadMenuItem.setEnabled(enabled and not isCurrentProject and not self.__debugMode) self.__projectMenu.popup(QCursor.pos()) return def __sortProjects(self): " Sort the project items " self.projectsView.sortItems( \ self.projectsView.sortColumn(), self.projectsView.header().sortIndicatorOrder() ) return def __sortFiles(self): " Sort the file items " self.recentFilesView.sortItems( self.recentFilesView.sortColumn(), self.recentFilesView.header().sortIndicatorOrder()) return def __resizeProjectColumns(self): """ Resize the projects list columns """ self.projectsView.header().setStretchLastSection(True) self.projectsView.header().resizeSections(QHeaderView.ResizeToContents) self.projectsView.header().resizeSection(0, 22) self.projectsView.header().setResizeMode(0, QHeaderView.Fixed) return def __resizeFileColumns(self): " Resize the files list columns " self.recentFilesView.header().setStretchLastSection(True) self.recentFilesView.header().resizeSections( QHeaderView.ResizeToContents) self.recentFilesView.header().resizeSection(0, 22) self.recentFilesView.header().setResizeMode(0, QHeaderView.Fixed) return def __projectActivated(self, item, column): " Handles the double click (or Enter) on the item " self.__projectContextItem = item self.__loadProject() return def __fileActivated(self, item, column): " Handles the double click (or Enter) on a file item " self.__fileContextItem = item self.__openFile() return def __viewProperties(self): " Handles the 'view properties' context menu item " if self.__projectContextItem is None: return if not self.__projectContextItem.isValid(): return if self.__projectContextItem.isCurrent(): # This is the current project - it can be edited project = GlobalData().project dialog = ProjectPropertiesDialog(project, self) if dialog.exec_() == QDialog.Accepted: importDirs = [] for index in xrange(dialog.importDirList.count()): importDirs.append(dialog.importDirList.item(index).text()) scriptName = dialog.scriptEdit.text().strip() relativePath = relpath(scriptName, project.getProjectDir()) if not relativePath.startswith('..'): scriptName = relativePath project.updateProperties( scriptName, importDirs, dialog.creationDateEdit.text().strip(), dialog.authorEdit.text().strip(), dialog.licenseEdit.text().strip(), dialog.copyrightEdit.text().strip(), dialog.versionEdit.text().strip(), dialog.emailEdit.text().strip(), dialog.descriptionEdit.toPlainText().strip()) else: # This is not the current project - it can be viewed fName = self.__projectContextItem.getFilename() dialog = ProjectPropertiesDialog(fName, self) dialog.exec_() return def __deleteProject(self): " Handles the 'delete from recent' context menu item " if self.__projectContextItem is None: return # Removal from the visible list is done via a signal which comes back # from settings fName = self.__projectContextItem.getFilename() Settings().deleteRecentProject(fName) return def __loadProject(self): " handles 'Load' context menu item " if self.__projectContextItem is None: return if not self.__projectContextItem.isValid(): return if self.__debugMode: return projectFileName = self.__projectContextItem.getFilename() if self.__projectContextItem.isCurrent(): GlobalData().mainWindow.openFile(projectFileName, -1) return # This is the current project, open for text editing QApplication.processEvents() QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) if os.path.exists(projectFileName): mainWin = GlobalData().mainWindow editorsManager = mainWin.editorsManagerWidget.editorsManager if editorsManager.closeRequest(): prj = GlobalData().project prj.setTabsStatus(editorsManager.getTabsStatus()) editorsManager.closeAll() prj.loadProject(projectFileName) mainWin.activateProjectTab() else: logging.error("The project " + os.path.basename(projectFileName) + " disappeared from the file system.") self.__populateProjects() QApplication.restoreOverrideCursor() return def __populateProjects(self): " Populates the recent projects " self.projectsView.clear() for item in Settings().recentProjects: self.projectsView.addTopLevelItem(RecentProjectViewItem(item)) self.__sortProjects() self.__resizeProjectColumns() self.__updateProjectToolbarButtons() return def __populateFiles(self): " Populates the recent files " self.recentFilesView.clear() for path in GlobalData().project.recentFiles: self.recentFilesView.addTopLevelItem(RecentFileViewItem(path)) self.__sortFiles() self.__resizeFileColumns() self.__updateFileToolbarButtons() return def __projectChanged(self, what): " Triggered when the current project is changed " if what == CodimensionProject.CompleteProject: self.__populateProjects() self.__populateFiles() return if what == CodimensionProject.Properties: # Update the corresponding tooltip items = self.projectsView.findItems(GlobalData().project.fileName, Qt.MatchExactly, 2) if len(items) != 1: logging.error("Unexpected number of matched projects: " + str(len(items))) return items[0].updateTooltip() return def __openFile(self): " Handles 'open' file menu item " self.__fileContextItem.updateIconAndTooltip() fName = self.__fileContextItem.getFilename() if not self.__fileContextItem.isValid(): logging.warning("Cannot open " + fName) return fileType = detectFileType(fName) if fileType == PixmapFileType: GlobalData().mainWindow.openPixmapFile(fName) return GlobalData().mainWindow.openFile(fName, -1) return def __deleteFile(self): " Handles 'delete from recent' file menu item " self.removeRecentFile(self.__fileContextItem.getFilename()) return def __handleShowFileContextMenu(self, coord): " File context menu " self.__fileContextItem = self.recentFilesView.itemAt(coord) if self.__fileContextItem is not None: self.__fileMenu.popup(QCursor.pos()) return def __filePathToClipboard(self): " Copies the file item path to the clipboard " if self.__fileContextItem is not None: QApplication.clipboard().setText( self.__fileContextItem.getFilename()) return def __prjPathToClipboard(self): " Copies the project item path to the clipboard " if self.__projectContextItem is not None: QApplication.clipboard().setText( self.__projectContextItem.getFilename()) return def onFileUpdated(self, fileName, uuid): " Triggered when the file is updated: python or project " realPath = os.path.realpath(fileName) count = self.recentFilesView.topLevelItemCount() for index in xrange(0, count): item = self.recentFilesView.topLevelItem(index) itemRealPath = os.path.realpath(item.getFilename()) if realPath == itemRealPath: item.updateIconAndTooltip() break for index in xrange(0, self.projectsView.topLevelItemCount()): item = self.projectsView.topLevelItem(index) itemRealPath = os.path.realpath(item.getFilename()) if realPath == itemRealPath: item.updateTooltip() break return def __onShowHide(self): " Triggered when show/hide button is clicked " if self.projectsView.isVisible(): self.projectsView.setVisible(False) self.lowerToolbar.setVisible(False) self.__showHideButton.setIcon(getIcon('more.png')) self.__showHideButton.setToolTip("Show recent projects list") self.__minH = self.lower.minimumHeight() self.__maxH = self.lower.maximumHeight() self.lower.setMinimumHeight(self.headerFrame.height()) self.lower.setMaximumHeight(self.headerFrame.height()) else: self.projectsView.setVisible(True) self.lowerToolbar.setVisible(True) self.__showHideButton.setIcon(getIcon('less.png')) self.__showHideButton.setToolTip("Hide recent projects list") self.lower.setMinimumHeight(self.__minH) self.lower.setMaximumHeight(self.__maxH) return def __onDebugMode(self, newState): " Triggered when debug mode has changed " self.__debugMode = newState # Disable the load project button self.__updateProjectToolbarButtons() return def removeRecentFile(self, fName): " Removes a single file from the recent files list " GlobalData().project.removeRecentFile(fName) for index in xrange(self.recentFilesView.topLevelItemCount()): candidate = self.recentFilesView.topLevelItem(index) if candidate.getFilename() == fName: self.recentFilesView.takeTopLevelItem(index) return return
class WTestManager(QWidget, Logger.ClassLogger): """ Widget to display all tests in the scheduler """ def __init__(self, parent): """ Constructs WTestManager widget @param parent: @type parent: """ QWidget.__init__(self, parent) self.parent = parent self.name = self.tr("Task Manager") self.ascendingOrder = False self.ascendingOrderHistory = False self.ascendingOrderWaiting = False self.itemCurrent = None self.projects = [] self.createWidgets() self.createActions() self.createConnections() self.createActions() self.createToolbar() self.deactivate() def createWidgets (self): """ QtWidgets creation QTreeWidget (Id, Name, Init at, Sched at, Start at, Stop at, User, Duration) _______________ | QToolBar | |---------------| _______________ | | |---------------| | | | | |_______________| """ #self.mainTab = QTabWidget() layout = QVBoxLayout() topLayout = QHBoxLayout() # waiting tree widget self.tabWaiting = QTabWidget() #self.waitingBox = QGroupBox("Waiting") self.waitingBox = QFrame(self) self.testWaiting = QTreeWidget(self) self.testWaiting.setSelectionMode(QAbstractItemView.ExtendedSelection) self.waitingToolbar = QToolBar(self) self.labelsWaiting = [ self.tr("No.") , self.tr("Group"), self.tr("Schedulation Type"), self.tr("Project"), self.tr("Name") , self.tr("Next run"), self.tr("Repeat"), self.tr("Probes"), self.tr("Notifications") , self.tr("Tests result"), self.tr("Author") ] self.testWaiting.setHeaderLabels(self.labelsWaiting) self.testWaiting.setIndentation(10) self.testWaiting.setContextMenuPolicy(Qt.CustomContextMenu) layoutWaiting = QVBoxLayout() layoutWaiting.addWidget(self.waitingToolbar) layoutWaiting.addWidget(self.testWaiting) self.waitingBox.setLayout(layoutWaiting) self.tabWaiting.addTab( self.waitingBox, "Scheduled" ) self.enqueuedBox = QFrame(self) layoutEnqueued = QVBoxLayout() self.testEnqueued = QTreeWidget(self) self.labelsEnqueued = [ self.tr("Group of tests") ] self.testEnqueued.setHeaderLabels(self.labelsEnqueued) self.testEnqueued.setIndentation(10) layoutEnqueued.addWidget(self.testEnqueued) self.enqueuedBox.setLayout(layoutEnqueued) self.tabWaiting.addTab( self.enqueuedBox, "Waiting" ) # current tree widget self.currentBox = QGroupBox("Running") self.testManager = QTreeWidget(self) self.testManager.setSelectionMode(QAbstractItemView.ExtendedSelection) self.dockToolbar = QToolBar(self) self.labels = [ self.tr("No."), self.tr("Project"), self.tr("Name"), self.tr("Started at"), self.tr("Author"), self.tr("Recursive") ] self.testManager.setHeaderLabels(self.labels) self.testManager.setIndentation(10) self.testManager.setContextMenuPolicy(Qt.CustomContextMenu) layoutCurrent = QVBoxLayout() layoutCurrent.addWidget(self.dockToolbar) layoutCurrent.addWidget(self.testManager) self.currentBox.setLayout(layoutCurrent) v_splitter = QSplitter(self) v_splitter.addWidget( self.tabWaiting ) v_splitter.addWidget( self.currentBox ) topLayout.addWidget(v_splitter) # history tree widget self.historyBox = QGroupBox("History") self.testHistory = QTreeWidget(self) self.historyToolbar = QToolBar(self) self.labels2 = [ self.tr("Id"), self.tr("Schedulation Type"), self.tr("Project") , self.tr("Name"), self.tr("Sched at"), self.tr("Run start"), self.tr("Run end"), self.tr("Author"), self.tr("Duration (in sec.)"), self.tr("Run Result") ] self.testHistory.setHeaderLabels(self.labels2) self.statsBox = QGroupBox("Summary") self.nbRunningLabel = QLabel("0") self.nbWaitingLabel = QLabel("0") self.nbHistoryLabel = QLabel("0") self.nbCompleteHistoryLabel = QLabel("0") self.nbErrorHistoryLabel = QLabel("0") self.nbKilledHistoryLabel = QLabel("0") self.nbCancelledHistoryLabel = QLabel("0") layout2 = QFormLayout() layout2.addRow(QLabel("Running"), self.nbRunningLabel ) layout2.addRow(QLabel("Waiting"), self.nbWaitingLabel ) layout2.addRow( QLabel(""), QLabel("") ) layout2.addRow(QLabel("History"), self.nbHistoryLabel ) layout2.addRow(QLabel(" - COMPLETE"), self.nbCompleteHistoryLabel ) layout2.addRow(QLabel(" - ERROR"), self.nbErrorHistoryLabel ) layout2.addRow(QLabel(" - KILLED"), self.nbKilledHistoryLabel ) layout2.addRow(QLabel(" - CANCELLED"), self.nbCancelledHistoryLabel ) self.statsBox.setLayout(layout2) layoutHistory = QVBoxLayout() layoutHistory.addWidget(self.historyToolbar) layoutHistory.addWidget(self.testHistory) self.historyBox.setLayout(layoutHistory) subLayout = QHBoxLayout() subLayout.addWidget(self.historyBox) subLayout.addWidget(self.statsBox) frame_left = QFrame(self) frame_left.setFrameShape(QFrame.NoFrame) frame_left.setLayout(topLayout) frame_right = QFrame(self) frame_right.setFrameShape(QFrame.NoFrame) frame_right.setLayout(subLayout) topLayout.setContentsMargins(0,0,0,0) subLayout.setContentsMargins(0,0,0,0) splitter1 = QSplitter(Qt.Vertical) splitter1.addWidget(frame_left) splitter1.addWidget(frame_right) layout.addWidget(splitter1) self.setLayout(layout) def createConnections(self): """ Create Qt Connections """ self.testWaiting.customContextMenuRequested.connect(self.onPopupMenuWaiting) self.testWaiting.currentItemChanged.connect(self.onCurrentWaitingItemChanged) self.testManager.customContextMenuRequested.connect(self.onPopupMenu) self.testManager.currentItemChanged.connect(self.onCurrentItemChanged) self.testWaiting.itemDoubleClicked.connect(self.onItemDoubleClicked) def createActions (self): """ Actions defined: * sort running task * sort waiting task * sort history task * kill one task * kill all tasks * cancel one task * cancel all task * clear the history * edit a waiting task * refresh waiting tasks * refresh running tasks * partial refresh of the history * complete refresh of the history * disable a waiting task * enable a waiting task """ self.sortAction = QtHelper.createAction(self, "Ascending Order", self.toggleSort, toggled = True, icon = QIcon(":/ascending.png") ) self.sortHistoryAction = QtHelper.createAction(self, "Ascending Order", self.toggleSortHistory, toggled = True, icon = QIcon(":/ascending.png") ) self.sortWaitingAction = QtHelper.createAction(self, "Ascending Order", self.toggleSortWaiting, toggled = True, icon = QIcon(":/ascending.png") ) self.killAction = QtHelper.createAction(self, "&Kill", self.killTask, tip = 'Kill selected test', icon = QIcon(":/process-kill.png") ) self.killAllAction = QtHelper.createAction(self, "&Kill All", self.killAllTasks, tip = 'Kill all running tests', icon = QIcon(":/process-killall.png") ) self.cancelAction = QtHelper.createAction(self, "&Cancel", self.cancelTask, tip = 'Cancel selected test', icon = QIcon(":/process-cancel.png") ) self.cancelAllAction = QtHelper.createAction(self, "&Cancel All", self.cancelAllTasks, tip = 'Cancel all waiting tests', icon = QIcon(":/processes-cancelall.png") ) self.clearHistoryAction = QtHelper.createAction(self, "&Clear history", self.clearHistory, tip = 'Clear history', icon = QIcon(":/trash.png")) self.editWaitingAction = QtHelper.createAction(self, "&Edit", self.editWaiting, tip = 'Edit the selected task', icon = QIcon(":/reschedule.png")) self.refreshWaitingAction = QtHelper.createAction(self, "Waiting tasks", self.refreshWaitingList, icon = QIcon(":/act-refresh.png"), tip="Refresh waiting tasks" ) self.refreshRunningAction = QtHelper.createAction(self, "Running tasks", self.refreshRunningList, icon = QIcon(":/act-refresh.png"), tip="Refresh running tasks" ) self.partialRefreshHistoryAction = QtHelper.createAction(self, "Partial history", self.partialRefreshHistoryList, icon = QIcon(":/act-half-refresh.png"), tip="Refresh partial history" ) self.refreshHistoryAction = QtHelper.createAction(self, "Full history", self.refreshHistoryList, icon = QIcon(":/act-refresh.png"), tip="Refresh full history" ) self.disableAction = QtHelper.createAction(self, "&Disable", self.disableTask, tip = 'Disable task', icon = QIcon(":/process-pause-icon.png") ) self.enableAction = QtHelper.createAction(self, "&Enable", self.enableTask, tip = 'Enable task', icon = None) def createToolbar(self): """ Toolbar creation ||----------|| || Kill all || ||----------|| """ self.dockToolbar.setObjectName("Test Manager toolbar") self.dockToolbar.addAction(self.sortAction) self.dockToolbar.addAction(self.refreshRunningAction) self.dockToolbar.addSeparator() self.dockToolbar.addAction(self.killAction) self.dockToolbar.addAction(self.killAllAction) self.dockToolbar.addSeparator() self.dockToolbar.setIconSize(QSize(16, 16)) # self.historyToolbar.setObjectName("Test Manager History toolbar") self.historyToolbar.addAction(self.sortHistoryAction) self.historyToolbar.addAction(self.partialRefreshHistoryAction) self.historyToolbar.addAction(self.refreshHistoryAction) self.historyToolbar.addSeparator() self.historyToolbar.addAction(self.clearHistoryAction) self.historyToolbar.setIconSize(QSize(16, 16)) # self.waitingToolbar.setObjectName("Test Manager Waiting toolbar") self.waitingToolbar.addAction(self.sortWaitingAction) self.waitingToolbar.addAction(self.refreshWaitingAction) self.waitingToolbar.addSeparator() self.waitingToolbar.addAction(self.editWaitingAction) self.waitingToolbar.addAction(self.disableAction) self.waitingToolbar.addAction(self.cancelAction) self.waitingToolbar.addAction(self.cancelAllAction) self.waitingToolbar.addSeparator() self.waitingToolbar.setIconSize(QSize(16, 16)) def onItemDoubleClicked(self, item): """ On item double clicked in waiting task """ if item.taskEventType in [ UCI.SCHED_QUEUE_AT, UCI.SCHED_QUEUE ]: pass else: self.editWaiting() def getProjectName(self, prjId): """ Return the project name """ pname = "UNKNOWN" if prjId == 0: return "UNDEFINED" for p in self.projects: if int(p['project_id']) == int(prjId): pname = p['name'] break return pname def refreshWaitingList(self): """ Refresh the waiting task list """ RCI.instance().waitingTasks() def refreshRunningList(self): """ Refresh the running task list """ RCI.instance().runningTasks() def partialRefreshHistoryList(self): """ Partial refresh of the history task list """ RCI.instance().historyTasks() def refreshHistoryList(self): """ Refresh the history task list """ RCI.instance().historyTasksAll() def onCurrentItemChanged(self, witem1, witem2): """ On current item changed @param witem1: @type witem1: @param witem2: @type witem2: """ # kill task available just for admin and tester if UCI.RIGHTS_ADMIN in RCI.instance().userRights or UCI.RIGHTS_TESTER in RCI.instance().userRights : if witem1 is not None: if witem1.taskState == STATE_RUNNING : self.itemCurrent = witem1 self.killAction.setEnabled(True) else: self.killAction.setEnabled(False) def onCurrentWaitingItemChanged(self, witem1, witem2): """ On current waiting task item changed @param witem1: @type witem1: @param witem2: @type witem2: """ # kill task available just for admin and tester if UCI.RIGHTS_ADMIN in RCI.instance().userRights or UCI.RIGHTS_TESTER in RCI.instance().userRights : if witem1 is not None: self.itemCurrent = witem1 self.cancelAction.setEnabled(True) self.editWaitingAction.setEnabled(True) self.disableAction.setEnabled(True) else: self.cancelAction.setEnabled(False) self.editWaitingAction.setEnabled(False) self.disableAction.setEnabled(False) def disableTask(self): """ Disable a waiting task """ # if self.itemCurrent is not None: for currentItem in self.testWaiting.selectedItems(): RCI.instance().rescheduleTask(taskId=currentItem.taskId, taskEnabled=False, scheduleType=currentItem.taskEventType, scheduleAt=currentItem.taskEventArgs, scheduleRepeat=currentItem.taskEventNb, probesEnabled=currentItem.taskWithoutProbes, notificationsEnabled=currentItem.taskWithoutNotif, debugEnabled=False, logsEnabled=currentItem.taskNoKeepTr, fromTime=currentItem.taskEventFrom, toTime=currentItem.taskEventTo ) def enableTask(self): """ Enable a waiting task """ # if self.itemCurrent is not None: for currentItem in self.testWaiting.selectedItems(): RCI.instance().rescheduleTask(taskId=currentItem.taskId, taskEnabled=True, scheduleType=currentItem.taskEventType, scheduleAt=currentItem.taskEventArgs, scheduleRepeat=currentItem.taskEventNb, probesEnabled=currentItem.taskWithoutProbes, notificationsEnabled=currentItem.taskWithoutNotif, debugEnabled=False, logsEnabled=currentItem.taskNoKeepTr, fromTime=currentItem.taskEventFrom, toTime=currentItem.taskEventTo ) def editWaiting(self): """ Edit a waiting task """ if self.itemCurrent is not None: if self.itemCurrent.taskEventType in [ UCI.SCHED_QUEUE_AT, UCI.SCHED_QUEUE ]: pass else: dSched = ScheduleDialog.SchedDialog( self ) dSched.fillFields( schedType=self.itemCurrent.taskEventType, schedArgs=self.itemCurrent.taskEventArgs, taskName=self.itemCurrent.taskEventName, taskId=self.itemCurrent.taskId, schedNb=self.itemCurrent.taskEventNb, withoutProbes=self.itemCurrent.taskWithoutProbes, enabled=self.itemCurrent.taskEventEnabled, withoutNotifs=self.itemCurrent.taskWithoutNotif, noKeepTr=self.itemCurrent.taskNoKeepTr, schedFrom=self.itemCurrent.taskEventFrom, schedTo=self.itemCurrent.taskEventTo ) if dSched.exec_() == QDialog.Accepted: runAt, runType, runNb, withoutProbes, runEnabled, noTr, withoutNotifs, runFrom, runTo = dSched.getSchedtime() RCI.instance().rescheduleTask(taskId=self.itemCurrent.taskId, taskEnabled=runEnabled, scheduleType=runType, scheduleAt=runAt, scheduleRepeat=runNb, probesEnabled=withoutProbes, notificationsEnabled=withoutNotifs, debugEnabled=False, logsEnabled=noTr, fromTime=runFrom, toTime=runTo ) def clearHistory(self): """ Call the server to clear the history """ reply = QMessageBox.question(self, "Clear tasks history", "Are you sure ?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: RCI.instance().clearHistory() def testKilled(self): """ Test kiled """ self.itemCurrent = None self.killAction.setEnabled(False) def testCancelled(self): """ Test cancelled """ self.itemCurrent = None self.cancelAction.setEnabled(False) def active (self): """ Enables QTreeWidget """ self.waitingBox.setEnabled(True) self.currentBox.setEnabled(True) self.historyBox.setEnabled(True) self.statsBox.setEnabled(True) # self.testManager.setEnabled(True) self.testHistory.setEnabled(True) self.testWaiting.setEnabled(True) # self.sortAction.setEnabled(True) self.sortHistoryAction.setEnabled(True) self.sortWaitingAction.setEnabled(True) self.refreshWaitingAction.setEnabled(True) self.refreshRunningAction.setEnabled(True) self.refreshHistoryAction.setEnabled(True) self.partialRefreshHistoryAction.setEnabled(True) self.killAction.setEnabled(False) self.cancelAction.setEnabled(False) self.editWaitingAction.setEnabled(False) if UCI.RIGHTS_ADMIN in RCI.instance().userRights: self.killAllAction.setEnabled(True) self.cancelAllAction.setEnabled(True) self.clearHistoryAction.setEnabled(True) self.disableAction.setEnabled(False) self.enableAction.setEnabled(True) def deactivate (self): """ Clears QTreeWidget and disables it """ self.waitingBox.setEnabled(False) self.currentBox.setEnabled(False) self.historyBox.setEnabled(False) self.statsBox.setEnabled(False) self.testWaiting.clear() self.testWaiting.setEnabled(False) self.testManager.clear() self.testManager.setEnabled(False) self.testHistory.clear() self.testHistory.setEnabled(False) self.testEnqueued.clear() self.testHistory.setEnabled(False) self.nbRunningLabel.setText("0") self.nbWaitingLabel.setText("0") self.nbHistoryLabel.setText("0") self.nbCompleteHistoryLabel.setText("0") self.nbErrorHistoryLabel.setText("0") self.nbKilledHistoryLabel.setText("0") self.nbCancelledHistoryLabel.setText("0") self.itemCurrent = None self.setDefaultActionsValues() def setDefaultActionsValues (self): """ Set default values for qt actions """ self.sortAction.setEnabled(False) self.sortHistoryAction.setEnabled(False) self.sortWaitingAction.setEnabled(False) self.killAction.setEnabled(False) self.killAllAction.setEnabled(False) self.cancelAction.setEnabled(False) self.cancelAllAction.setEnabled(False) self.editWaitingAction.setEnabled(False) self.clearHistoryAction.setEnabled(False) self.refreshWaitingAction.setEnabled(False) self.refreshRunningAction.setEnabled(False) self.refreshHistoryAction.setEnabled(False) self.partialRefreshHistoryAction.setEnabled(False) self.disableAction.setEnabled(False) self.enableAction.setEnabled(False) def loadProjects(self, data): """ Load all projects """ self.projects = data def loadRunning (self, data): """ Loads running tasks @param data: @type data: dict """ self.nbRunningLabel.setText( str( len(data) ) ) for task in data: if self.getProjectName(task['project-id']) == "UNKNOWN": continue taskItem = TaskRunningItem( task = task, parent= self.testManager ) # resize cols for i in xrange(len(self.labels2) - 1): self.testManager.resizeColumnToContents(i) # change sort order if self.ascendingOrder: self.testWaiting.sortItems(2, Qt.AscendingOrder) # sort by sched time self.testManager.sortItems(2, Qt.DescendingOrder) def updateRunning(self, data): """ Update running tasks @param data: @type data: """ self.testManager.clear() self.loadRunning( data=data ) def loadWaiting (self, data): """ Loads waiting tasks @param data: @type data: dict """ self.nbWaitingLabel.setText( str(len(data)) ) for task in data: eventid, eventtype, eventargs, eventtime, eventname, author, realruntime, duration, result, eventnb, \ eventnbcur, eventenabled, withoutprobes, withoutnotif, nokeeptr, userid, projectid, eventfrom, eventto, groupid = task if self.getProjectName(projectid) == "UNKNOWN": continue taskItem = TaskWaitingItem( task = task, parent= self.testWaiting ) # resize cols for i in xrange(len(self.labels2) - 1): self.testWaiting.resizeColumnToContents(i) # change sort order if self.ascendingOrderHistory: self.testWaiting.sortItems(3, Qt.AscendingOrder) self.testWaiting.sortItems(3, Qt.DescendingOrder) def updateWaiting(self, data): """ Update waiting tasks @param data: @type data: """ self.testWaiting.clear() self.loadWaiting( data=data ) def loadEnqueued (self, data): """ Loads enqueued tasks @param data: @type data: dict """ for (groupId, groupData) in data.items(): groupName, groupsTests = groupData pluriel = '' if len(groupsTests) > 1: pluriel = '' groupTask = TaskEnqueuedItem( parent=self.testEnqueued, text="%s - %s - (%s test%s)" % (groupId, groupName, len(groupsTests), pluriel) ) groupTask.setExpanded(True) for gTest in groupsTests: tDict = eval(gTest) tst = "%s:%s.%s" % ( self.getProjectName(tDict['prj-id']), tDict['test-path'], tDict['test-extension']) # set the icon according to the type of test if tDict['test-extension'] == 'tux': testIcon = QIcon(":/tux.png") if tDict['test-extension'] == 'tax': testIcon = QIcon(":/tax.png") if tDict['test-extension'] == 'tsx': testIcon = QIcon(":/tsx.png") if tDict['test-extension'] == 'tpx': testIcon = QIcon(":/tpx.png") if tDict['test-extension'] == 'tgx': testIcon = QIcon(":/tgx.png") subTask = TaskEnqueuedItem(parent=groupTask, text=tst, icon=testIcon) def updateEnqueued(self, data): """ Update enqueued tasks @param data: @type data: """ self.testEnqueued.clear() self.loadEnqueued( data=data ) def loadHistory (self, data): """ Loads history tasks @param data: @type data: dict """ # Init counters nbComplete = int(self.nbCompleteHistoryLabel.text()) nbError = int(self.nbErrorHistoryLabel.text()) nbKilled = int(self.nbKilledHistoryLabel.text()) nbCancelled = int(self.nbCancelledHistoryLabel.text()) for task in data: dbid, eventtype, eventargs, eventtime, eventname, author, realruntime, duration, result, projectid = task if self.getProjectName(projectid) == "UNKNOWN": continue taskItem = TaskHistoryItem( task = task, parent= self.testHistory ) if taskItem.taskEventResult == STATE_COMPLETE: nbComplete += 1 if taskItem.taskEventResult == STATE_ERROR: nbError += 1 if taskItem.taskEventResult == STATE_KILLED: nbKilled += 1 if taskItem.taskEventResult == STATE_CANCELLED: nbCancelled += 1 # resize cols for i in xrange(len(self.labels2) - 1): self.testHistory.resizeColumnToContents(i) # change sort order by sched at if self.ascendingOrderHistory: self.testHistory.sortItems(0, Qt.AscendingOrder) self.testHistory.sortItems(0, Qt.DescendingOrder) # Update summary nbHistory = int(self.nbHistoryLabel.text()) self.nbHistoryLabel.setText( str( len(data) + nbHistory ) ) self.nbCompleteHistoryLabel.setText( str(nbComplete) ) self.nbErrorHistoryLabel.setText( str(nbError) ) self.nbKilledHistoryLabel.setText( str(nbKilled) ) self.nbCancelledHistoryLabel.setText( str(nbCancelled) ) def updateHistory(self, data): """ Update history tasks @param data: @type data: """ # reset counters self.nbHistoryLabel.setText("0") self.nbCompleteHistoryLabel.setText("0") self.nbErrorHistoryLabel.setText("0") self.nbKilledHistoryLabel.setText("0") self.nbCancelledHistoryLabel.setText("0") # self.testHistory.clear() self.loadHistory( data=data ) def refreshRunningTask(self, data, action): """ Refresh running task @param data: @type data: @param action: @type action: """ if action == "add": self.loadRunning(data=data) elif action == "update": self.updateRunning(data=data) else: pass # error def refreshWaitingTask(self, data, action): """ Refresh waiting task @param data: @type data: @param action: @type action: """ if action == "update": self.updateWaiting(data=data) else: pass # error def refreshEnqueuedTask(self, data, action): """ Refresh enqueued task @param data: @type data: @param action: @type action: """ if action == "update": self.updateEnqueued(data=data) else: pass # error def refreshHistoryTask(self, data, action): """ Refresh history task @param data: @type data: @param action: @type action: """ if action == "add": self.loadHistory(data=data) elif action == "update": self.updateHistory(data=data) else: pass # error def onPopupMenu(self, pos): """ Display menu on right click @param pos: @type pos: """ self.menu = QMenu() item = self.testManager.itemAt(pos) if item: self.itemCurrent = item # kill task available just for admin and tester if UCI.RIGHTS_ADMIN in RCI.instance().userRights or UCI.RIGHTS_TESTER in RCI.instance().userRights : if item.taskState == STATE_RUNNING : self.menu.addAction( self.killAction ) self.menu.popup(self.testManager.mapToGlobal(pos)) def onPopupMenuWaiting(self, pos): """ Display menu on right click @param pos: @type pos: """ self.menu = QMenu() item = self.testWaiting.itemAt(pos) if item: self.itemCurrent = item # kill task available just for admin and tester if UCI.RIGHTS_ADMIN in RCI.instance().userRights or UCI.RIGHTS_TESTER in RCI.instance().userRights : self.menu.addAction( self.editWaitingAction ) self.menu.addAction( self.cancelAction ) self.menu.addSeparator() self.menu.addAction( self.disableAction ) self.menu.addAction( self.enableAction ) self.menu.popup(self.testWaiting.mapToGlobal(pos)) def cancelTask(self): """ Cancel the selected task """ items = self.testWaiting.selectedItems() taskIds = [] for itm in items: taskIds.append( itm.taskId ) reply = QMessageBox.question(self, "Cancel test(s)", "Are you sure ?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: RCI.instance().cancelTasks(taskIds=taskIds) def cancelAllTasks(self): """ Cancel all tasks """ reply = QMessageBox.question(self, "Cancel all tests", "Are you sure ?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: RCI.instance().cancelTasksAll() def killTask(self): """ Kill the selected task """ items = self.testManager.selectedItems() taskIds = [] for itm in items: taskIds.append( itm.taskId ) reply = QMessageBox.question(self, "Kill test", "Are you sure ?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: RCI.instance().killTasks(taskIds=taskIds) def killAllTasks(self): """ Kill all tasks """ reply = QMessageBox.question(self, "Kill all tests", "Are you sure ?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: RCI.instance().killTasksAll() def toggleSort(self): """ Toggle sort of running task """ if not self.ascendingOrder: self.testManager.sortItems(2, Qt.AscendingOrder) self.ascendingOrder = True else: self.testManager.sortItems(2, Qt.DescendingOrder) self.ascendingOrder = False def toggleSortHistory(self): """ Toggle sort of the history """ if not self.ascendingOrderHistory: self.testHistory.sortItems(0, Qt.AscendingOrder) self.ascendingOrderHistory = True else: self.testHistory.sortItems(0, Qt.DescendingOrder) self.ascendingOrderHistory = False def toggleSortWaiting(self): """ Toggle sort of the waiting """ if not self.ascendingOrderWaiting: self.testWaiting.sortItems(3, Qt.AscendingOrder) self.ascendingOrderWaiting = True else: self.testWaiting.sortItems(3, Qt.DescendingOrder) self.ascendingOrderWaiting = False
class BookmarksWindow(QDialog): """ A simple UI for showing bookmarks and navigating to them. FIXME: For now, this window is tied to a particular lane. If your project has more than one lane, then each one will have it's own bookmark window, which is kinda dumb. """ def __init__(self, parent, topLevelOperatorView): super(BookmarksWindow, self).__init__(parent) self.setWindowTitle("Bookmarks") self.topLevelOperatorView = topLevelOperatorView self.bookmark_tree = QTreeWidget(self) self.bookmark_tree.setHeaderLabels( ["Location", "Notes"] ) self.bookmark_tree.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Preferred ) self.bookmark_tree.setColumnWidth(0, 200) self.bookmark_tree.setColumnWidth(1, 300) self.note_edit = QLineEdit(self) self.add_bookmark_button = QPushButton("Add Bookmark", self, clicked=self.add_bookmark) geometry = self.geometry() geometry.setSize( QSize(520, 520) ) self.setGeometry(geometry) layout = QVBoxLayout() layout.addWidget(self.bookmark_tree) layout.addWidget(self.note_edit) layout.addWidget(self.add_bookmark_button) self.setLayout(layout) self._load_bookmarks() self.bookmark_tree.setContextMenuPolicy( Qt.CustomContextMenu ) self.bookmark_tree.customContextMenuRequested.connect( self.showContextMenu ) self.bookmark_tree.itemDoubleClicked.connect(self._handle_doubleclick) def _handle_doubleclick(self, item, col): """ Navigate to the bookmark """ data = item.data(0, Qt.UserRole).toPyObject() if data is None: return (coord, notes) = data axes = self.topLevelOperatorView.InputImages.meta.getAxisKeys() axes = axes[:-1] # drop channel axes = sorted(axes) assert len(axes) == len(coord) tagged_coord = dict(zip(axes, coord)) tagged_location = OrderedDict(zip('txyzc', (0,0,0,0,0))) tagged_location.update(tagged_coord) t = tagged_location.values()[0] coord3d = tagged_location.values()[1:4] self.parent().editor.posModel.time = t self.parent().editor.navCtrl.panSlicingViews( coord3d, [0,1,2] ) self.parent().editor.posModel.slicingPos = coord3d def showContextMenu(self, pos): item = self.bookmark_tree.itemAt(pos) data = item.data(0, Qt.UserRole).toPyObject() if data is None: return def delete_bookmark(): (coord, notes) = data bookmarks = list(self.topLevelOperatorView.Bookmarks.value) i = bookmarks.index((coord, notes)) bookmarks.pop(i) self.topLevelOperatorView.Bookmarks.setValue(bookmarks) self._load_bookmarks() menu = QMenu(parent=self) menu.addAction( QAction("Delete", menu, triggered=delete_bookmark) ) globalPos = self.bookmark_tree.viewport().mapToGlobal( pos ) menu.exec_( globalPos ) #selection = menu.exec_( globalPos ) #if selection is removeLanesAction: # self.removeLanesRequested.emit( self._selectedLanes ) def add_bookmark(self): coord_txyzc = self.parent().editor.posModel.slicingPos5D tagged_coord_txyzc = dict( zip('txyzc', coord_txyzc) ) axes = self.topLevelOperatorView.InputImages.meta.getAxisKeys() axes = axes[:-1] # drop channel axes = sorted(axes) coord = tuple(tagged_coord_txyzc[c] for c in axes) notes = str(self.note_edit.text()) bookmarks = list(self.topLevelOperatorView.Bookmarks.value) bookmarks.append((coord, notes)) self.topLevelOperatorView.Bookmarks.setValue(bookmarks) self._load_bookmarks() def _load_bookmarks(self): self.bookmark_tree.clear() lane_index = self.topLevelOperatorView.current_view_index() lane_nickname = self.topLevelOperatorView.InputImages.meta.nickname or "Lane {}".format(lane_index) bookmarks = self.topLevelOperatorView.Bookmarks.value group_item = QTreeWidgetItem( self.bookmark_tree, QStringList(lane_nickname) ) for coord, notes in bookmarks: item = QTreeWidgetItem( group_item, QStringList() ) item.setText(0, str(coord)) item.setData(0, Qt.UserRole, (coord, notes)) item.setText(1, notes) self.bookmark_tree.expandAll()
class StackViewer(QWidget): " Implements the stack viewer for a debugger " def __init__(self, debugger, parent=None): QWidget.__init__(self, parent) self.__debugger = debugger self.currentStack = None self.currentFrame = 0 self.__createPopupMenu() self.__createLayout() if Settings().showStackViewer == False: self.__onShowHide(True) return def __createPopupMenu(self): " Creates the popup menu " self.__framesMenu = QMenu() self.__setCurrentMenuItem = self.__framesMenu.addAction( "Set current (single click)", self.__onSetCurrent) self.__jumpMenuItem = self.__framesMenu.addAction( "Set current and jump to the source (double click)", self.__onSetCurrentAndJump) return def __createLayout(self): " Creates the widget layout " verticalLayout = QVBoxLayout(self) verticalLayout.setContentsMargins(0, 0, 0, 0) verticalLayout.setSpacing(0) self.headerFrame = QFrame() self.headerFrame.setFrameStyle(QFrame.StyledPanel) self.headerFrame.setAutoFillBackground(True) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color(QPalette.Background) headerBackground.setRgb(min(headerBackground.red() + 30, 255), min(headerBackground.green() + 30, 255), min(headerBackground.blue() + 30, 255)) headerPalette.setColor(QPalette.Background, headerBackground) self.headerFrame.setPalette(headerPalette) self.headerFrame.setFixedHeight(24) self.__stackLabel = QLabel("Stack") expandingSpacer = QSpacerItem(10, 10, QSizePolicy.Expanding) fixedSpacer = QSpacerItem(3, 3) self.__showHideButton = QToolButton() self.__showHideButton.setAutoRaise(True) self.__showHideButton.setIcon(PixmapCache().getIcon('less.png')) self.__showHideButton.setFixedSize(20, 20) self.__showHideButton.setToolTip("Hide frames list") self.__showHideButton.setFocusPolicy(Qt.NoFocus) self.__showHideButton.clicked.connect(self.__onShowHide) headerLayout = QHBoxLayout() headerLayout.setContentsMargins(0, 0, 0, 0) headerLayout.addSpacerItem(fixedSpacer) headerLayout.addWidget(self.__stackLabel) headerLayout.addSpacerItem(expandingSpacer) headerLayout.addWidget(self.__showHideButton) self.headerFrame.setLayout(headerLayout) self.__framesList = QTreeWidget(self) self.__framesList.setSortingEnabled(False) # I might not need that because of two reasons: # - the window has no focus # - the window has custom current indicator # self.__framesList.setAlternatingRowColors( True ) self.__framesList.setRootIsDecorated(False) self.__framesList.setItemsExpandable(False) self.__framesList.setUniformRowHeights(True) self.__framesList.setSelectionMode(QAbstractItemView.NoSelection) self.__framesList.setSelectionBehavior(QAbstractItemView.SelectRows) self.__framesList.setItemDelegate(NoOutlineHeightDelegate(4)) self.__framesList.setFocusPolicy(Qt.NoFocus) self.__framesList.setContextMenuPolicy(Qt.CustomContextMenu) self.__framesList.itemClicked.connect(self.__onFrameClicked) self.__framesList.itemDoubleClicked.connect( self.__onFrameDoubleClicked) self.__framesList.customContextMenuRequested.connect( self.__showContextMenu) self.__framesList.setHeaderLabels( ["", "File:line", "Function", "Full path"]) verticalLayout.addWidget(self.headerFrame) verticalLayout.addWidget(self.__framesList) return def __onShowHide(self, startup=False): " Triggered when show/hide button is clicked " if startup or self.__framesList.isVisible(): self.__framesList.setVisible(False) self.__showHideButton.setIcon(PixmapCache().getIcon('more.png')) self.__showHideButton.setToolTip("Show frames list") self.__minH = self.minimumHeight() self.__maxH = self.maximumHeight() self.setMinimumHeight(self.headerFrame.height()) self.setMaximumHeight(self.headerFrame.height()) Settings().showStackViewer = False else: self.__framesList.setVisible(True) self.__showHideButton.setIcon(PixmapCache().getIcon('less.png')) self.__showHideButton.setToolTip("Hide frames list") self.setMinimumHeight(self.__minH) self.setMaximumHeight(self.__maxH) Settings().showStackViewer = True return def clear(self): " Clears the content " self.__framesList.clear() self.currentStack = None self.__stackLabel.setText("Stack") return def __resizeColumns(self): " Resize the files list columns " self.__framesList.header().setStretchLastSection(True) self.__framesList.header().resizeSections(QHeaderView.ResizeToContents) self.__framesList.header().resizeSection(0, 22) self.__framesList.header().setResizeMode(0, QHeaderView.Fixed) return def populate(self, stack): " Sets the new call stack and selects the first item in it " self.clear() self.currentStack = stack self.currentFrame = 0 frameNumber = 0 for s in stack: if len(s) == 2: # This is when an exception comes funcName = "" else: funcName = s[2] item = StackFrameItem(s[0], s[1], funcName, frameNumber) self.__framesList.addTopLevelItem(item) frameNumber += 1 self.__resizeColumns() self.__framesList.topLevelItem(0).setCurrent(True) self.__stackLabel.setText("Stack (total: " + str(len(stack)) + ")") return def getFrameNumber(self): " Provides the current frame number " return self.currentFrame def __onFrameClicked(self, item, column): " Triggered when a frame is clicked " if item.isCurrent(): return # Hide the current indicator self.__framesList.topLevelItem(self.currentFrame).setCurrent(False) # Show the new indicator self.currentFrame = item.getFrameNumber() for index in xrange(self.__framesList.topLevelItemCount()): item = self.__framesList.topLevelItem(index) if item.getFrameNumber() == self.currentFrame: item.setCurrent(True) self.__debugger.remoteClientVariables(1, self.currentFrame) # globals self.__debugger.remoteClientVariables(0, self.currentFrame) # locals return def __onFrameDoubleClicked(self, item, column): " Triggered when a frame is double clicked " # The frame has been switched already because the double click # signal always comes after the single click one fileName = item.getFilename() lineNumber = item.getLineNumber() editorsManager = GlobalData().mainWindow.editorsManager() editorsManager.openFile(fileName, lineNumber) editor = editorsManager.currentWidget().getEditor() editor.gotoLine(lineNumber) editorsManager.currentWidget().setFocus() return def __showContextMenu(self, coord): " Shows the frames list context menu " self.__contextItem = self.__framesList.itemAt(coord) if self.__contextItem is not None: self.__setCurrentMenuItem.setEnabled( not self.__contextItem.isCurrent()) self.__framesMenu.popup(QCursor.pos()) return def __onSetCurrent(self): " Context menu item handler " self.__onFrameClicked(self.__contextItem, 0) return def __onSetCurrentAndJump(self): " Context menu item handler " self.__onFrameClicked(self.__contextItem, 0) self.__onFrameDoubleClicked(self.__contextItem, 0) return def switchControl(self, isInIDE): " Switches the UI depending where the control flow is " self.__framesList.setEnabled(isInIDE) return
class IgnoredExceptionsViewer( QWidget ): " Implements the client exceptions viewer for a debugger " def __init__( self, parent = None ): QWidget.__init__( self, parent ) self.__createPopupMenu() self.__createLayout() self.__ignored = [] self.__currentItem = None GlobalData().project.projectChanged.connect( self.__onProjectChanged ) if Settings().showIgnoredExcViewer == False: self.__onShowHide( True ) return def __createPopupMenu( self ): " Creates the popup menu " self.__excptMenu = QMenu() self.__removeMenuItem = self.__excptMenu.addAction( PixmapCache().getIcon( 'ignexcptdel.png' ), "Remove from ignore list", self.__onRemoveFromIgnore ) return def __createLayout( self ): " Creates the widget layout " verticalLayout = QVBoxLayout( self ) verticalLayout.setContentsMargins( 0, 0, 0, 0 ) verticalLayout.setSpacing( 0 ) self.headerFrame = QFrame() self.headerFrame.setFrameStyle( QFrame.StyledPanel ) self.headerFrame.setAutoFillBackground( True ) headerPalette = self.headerFrame.palette() headerBackground = headerPalette.color( QPalette.Background ) headerBackground.setRgb( min( headerBackground.red() + 30, 255 ), min( headerBackground.green() + 30, 255 ), min( headerBackground.blue() + 30, 255 ) ) headerPalette.setColor( QPalette.Background, headerBackground ) self.headerFrame.setPalette( headerPalette ) self.headerFrame.setFixedHeight( 24 ) self.__excptLabel = QLabel( "Ignored exception types" ) expandingSpacer = QSpacerItem( 10, 10, QSizePolicy.Expanding ) fixedSpacer = QSpacerItem( 3, 3 ) self.__showHideButton = QToolButton() self.__showHideButton.setAutoRaise( True ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'less.png' ) ) self.__showHideButton.setFixedSize( 20, 20 ) self.__showHideButton.setToolTip( "Hide ignored exceptions list" ) self.__showHideButton.setFocusPolicy( Qt.NoFocus ) self.__showHideButton.clicked.connect( self.__onShowHide ) headerLayout = QHBoxLayout() headerLayout.setContentsMargins( 1, 1, 1, 1 ) headerLayout.addSpacerItem( fixedSpacer ) headerLayout.addWidget( self.__excptLabel ) headerLayout.addSpacerItem( expandingSpacer ) headerLayout.addWidget( self.__showHideButton ) self.headerFrame.setLayout( headerLayout ) self.exceptionsList = QTreeWidget( self ) self.exceptionsList.setSortingEnabled( False ) self.exceptionsList.setAlternatingRowColors( True ) self.exceptionsList.setRootIsDecorated( False ) self.exceptionsList.setItemsExpandable( True ) self.exceptionsList.setUniformRowHeights( True ) self.exceptionsList.setSelectionMode( QAbstractItemView.SingleSelection ) self.exceptionsList.setSelectionBehavior( QAbstractItemView.SelectRows ) self.exceptionsList.setItemDelegate( NoOutlineHeightDelegate( 4 ) ) self.exceptionsList.setContextMenuPolicy( Qt.CustomContextMenu ) self.exceptionsList.customContextMenuRequested.connect( self.__showContextMenu ) self.exceptionsList.itemSelectionChanged.connect( self.__onSelectionChanged ) self.exceptionsList.setHeaderLabels( [ "Exception type" ] ) self.__excTypeEdit = QLineEdit() self.__excTypeEdit.setFixedHeight( 26 ) self.__excTypeEdit.textChanged.connect( self.__onNewFilterChanged ) self.__excTypeEdit.returnPressed.connect( self.__onAddExceptionFilter ) self.__addButton = QPushButton( "Add" ) # self.__addButton.setFocusPolicy( Qt.NoFocus ) self.__addButton.setEnabled( False ) self.__addButton.clicked.connect( self.__onAddExceptionFilter ) expandingSpacer2 = QWidget() expandingSpacer2.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding ) self.__removeButton = QAction( PixmapCache().getIcon( 'delitem.png' ), "Remove selected exception type", self ) self.__removeButton.triggered.connect( self.__onRemoveFromIgnore ) self.__removeButton.setEnabled( False ) fixedSpacer1 = QWidget() fixedSpacer1.setFixedWidth( 5 ) self.__removeAllButton = QAction( PixmapCache().getIcon( 'ignexcptdelall.png' ), "Remove all the exception types", self ) self.__removeAllButton.triggered.connect( self.__onRemoveAllFromIgnore ) self.__removeAllButton.setEnabled( False ) self.toolbar = QToolBar() self.toolbar.setOrientation( Qt.Horizontal ) self.toolbar.setMovable( False ) self.toolbar.setAllowedAreas( Qt.TopToolBarArea ) self.toolbar.setIconSize( QSize( 16, 16 ) ) self.toolbar.setFixedHeight( 28 ) self.toolbar.setContentsMargins( 0, 0, 0, 0 ) self.toolbar.addWidget( expandingSpacer2 ) self.toolbar.addAction( self.__removeButton ) self.toolbar.addWidget( fixedSpacer1 ) self.toolbar.addAction( self.__removeAllButton ) addLayout = QHBoxLayout() addLayout.setContentsMargins( 1, 1, 1, 1 ) addLayout.setSpacing( 1 ) addLayout.addWidget( self.__excTypeEdit ) addLayout.addWidget( self.__addButton ) verticalLayout.addWidget( self.headerFrame ) verticalLayout.addWidget( self.toolbar ) verticalLayout.addWidget( self.exceptionsList ) verticalLayout.addLayout( addLayout ) return def clear( self ): " Clears the content " self.exceptionsList.clear() self.__excTypeEdit.clear() self.__addButton.setEnabled( False ) self.__ignored = [] self.__currentItem = None self.__updateTitle() return def __onShowHide( self, startup = False ): " Triggered when show/hide button is clicked " if startup or self.exceptionsList.isVisible(): self.exceptionsList.setVisible( False ) self.__excTypeEdit.setVisible( False ) self.__addButton.setVisible( False ) self.__removeButton.setVisible( False ) self.__removeAllButton.setVisible( False ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'more.png' ) ) self.__showHideButton.setToolTip( "Show ignored exceptions list" ) self.__minH = self.minimumHeight() self.__maxH = self.maximumHeight() self.setMinimumHeight( self.headerFrame.height() ) self.setMaximumHeight( self.headerFrame.height() ) Settings().showIgnoredExcViewer = False else: self.exceptionsList.setVisible( True ) self.__excTypeEdit.setVisible( True ) self.__addButton.setVisible( True ) self.__removeButton.setVisible( True ) self.__removeAllButton.setVisible( True ) self.__showHideButton.setIcon( PixmapCache().getIcon( 'less.png' ) ) self.__showHideButton.setToolTip( "Hide ignored exceptions list" ) self.setMinimumHeight( self.__minH ) self.setMaximumHeight( self.__maxH ) Settings().showIgnoredExcViewer = True return def __onSelectionChanged( self ): " Triggered when the current item is changed " selected = list( self.exceptionsList.selectedItems() ) if selected: self.__currentItem = selected[ 0 ] self.__removeButton.setEnabled( True ) else: self.__currentItem = None self.__removeButton.setEnabled( False ) return def __showContextMenu( self, coord ): " Shows the frames list context menu " contextItem = self.exceptionsList.itemAt( coord ) if contextItem is not None: self.__currentItem = contextItem self.__excptMenu.popup( QCursor.pos() ) return def __updateTitle( self ): " Updates the section title " count = self.exceptionsList.topLevelItemCount() if count == 0: self.__excptLabel.setText( "Ignored exception types" ) else: self.__excptLabel.setText( "Ignored exception types (total: " + str( count ) + ")" ) self.__removeAllButton.setEnabled( count != 0 ) return def __onProjectChanged( self, what ): " Triggered when a project is changed " if what != CodimensionProject.CompleteProject: return self.clear() project = GlobalData().project if project.isLoaded(): self.__ignored = list( project.ignoredExcpt ) else: self.__ignored = list( Settings().ignoredExceptions ) for exceptionType in self.__ignored: item = QTreeWidgetItem( self.exceptionsList ) item.setText( 0, exceptionType ) self.__updateTitle() return def __onNewFilterChanged( self, text ): " Triggered when the text is changed " text = str( text ).strip() if text == "": self.__addButton.setEnabled( False ) return if " " in text: self.__addButton.setEnabled( False ) return if text in self.__ignored: self.__addButton.setEnabled( False ) return self.__addButton.setEnabled( True ) return def __onAddExceptionFilter( self ): " Adds an item into the ignored exceptions list " text = self.__excTypeEdit.text().strip() self.addExceptionFilter( text ) def addExceptionFilter( self, excType ): " Adds a new item into the ignored exceptions list " if excType == "": return if " " in excType: return if excType in self.__ignored: return item = QTreeWidgetItem( self.exceptionsList ) item.setText( 0, excType ) project = GlobalData().project if project.isLoaded(): project.addExceptionFilter( excType ) else: Settings().addExceptionFilter( excType ) self.__ignored.append( excType ) self.__updateTitle() return def __onRemoveFromIgnore( self ): " Removes an item from the ignored exception types list " if self.__currentItem is None: return text = self.__currentItem.text( 0 ) # Find the item index and remove it index = 0 while True: if self.exceptionsList.topLevelItem( index ).text( 0 ) == text: self.exceptionsList.takeTopLevelItem( index ) break index += 1 project = GlobalData().project if project.isLoaded(): project.deleteExceptionFilter( text ) else: Settings().deleteExceptionFilter( text ) self.__ignored.remove( text ) self.__updateTitle() return def __onRemoveAllFromIgnore( self ): " Triggered when all the ignored exceptions should be deleted " self.clear() project = GlobalData().project if project.isLoaded(): project.setExceptionFilters( [] ) else: Settings().setExceptionFilters( [] ) return def isIgnored( self, exceptionType ): " Returns True if this exception type should be ignored " return exceptionType in self.__ignored