コード例 #1
0
ファイル: proxyaction.py プロジェクト: nerdocs/MedUX
    def _update(self, action: QAction, initialize: bool):
        """updates the internal values from the given action."""
        if not self._action:
            return
        self._disconnectAction()
        self.changed.disconnect(self._updateToolTipWithKeySequence)
        if initialize:
            self.setSeparator(action.isSeparator())
            self.setMenuRole(action.menuRole())

        if self.hasAttribute(ProxyActionAttribute.UpdateIcon) or initialize:
            self.setIcon(action.icon())
            self.setIconText(action.iconText())
            self.setIconVisibleInMenu(action.isIconVisibleInMenu())

        if self.hasAttribute(ProxyActionAttribute.UpdateText) or initialize:
            self.setText(action.text())
            self._toolTip = action.toolTip()
            self._updateToolTipWithKeySequence()
            self.setStatusTip(action.statusTip())
            self.setWhatsThis(action.whatsThis())

        self.setCheckable(action.isCheckable())

        if not initialize:
            self.setChecked(action.isChecked())
            self.setEnabled(action.isEnabled())
            self.setVisible(action.isVisible())

        self._connectAction()
        self.changed.connect(self._updateToolTipWithKeySequence)
コード例 #2
0
    def __init__(self):
        super().__init__()
        #: widget's runtime state
        self.__state = State.NoState
        self._imageMeta = []
        self._imageCategories = {}

        self.__invalidated = False
        self.__pendingTask = None

        vbox = gui.vBox(self.controlArea)
        hbox = gui.hBox(vbox)
        self.recent_cb = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=16,
        )
        self.recent_cb.activated[int].connect(self.__onRecentActivated)
        icons = standard_icons(self)

        browseaction = QAction(
            "Open/Load Images", self,
            iconText="\N{HORIZONTAL ELLIPSIS}",
            icon=icons.dir_open_icon,
            toolTip="Select a directory from which to load the images"
        )
        browseaction.triggered.connect(self.__runOpenDialog)
        reloadaction = QAction(
            "Reload", self,
            icon=icons.reload_icon,
            toolTip="Reload current image set"
        )
        reloadaction.triggered.connect(self.reload)
        self.__actions = namespace(
            browse=browseaction,
            reload=reloadaction,
        )

        browsebutton = QPushButton(
            browseaction.iconText(),
            icon=browseaction.icon(),
            toolTip=browseaction.toolTip(),
            clicked=browseaction.trigger
        )
        reloadbutton = QPushButton(
            reloadaction.iconText(),
            icon=reloadaction.icon(),
            clicked=reloadaction.trigger,
            default=True,
        )

        hbox.layout().addWidget(self.recent_cb)
        hbox.layout().addWidget(browsebutton)
        hbox.layout().addWidget(reloadbutton)

        self.addActions([browseaction, reloadaction])

        reloadaction.changed.connect(
            lambda: reloadbutton.setEnabled(reloadaction.isEnabled())
        )
        box = gui.vBox(vbox, "Info")
        self.infostack = QStackedWidget()

        self.info_area = QLabel(
            text="No image set selected",
            wordWrap=True
        )
        self.progress_widget = QProgressBar(
            minimum=0, maximum=0
        )
        self.cancel_button = QPushButton(
            "Cancel", icon=icons.cancel_icon,
        )
        self.cancel_button.clicked.connect(self.cancel)

        w = QWidget()
        vlayout = QVBoxLayout()
        vlayout.setContentsMargins(0, 0, 0, 0)
        hlayout = QHBoxLayout()
        hlayout.setContentsMargins(0, 0, 0, 0)

        hlayout.addWidget(self.progress_widget)
        hlayout.addWidget(self.cancel_button)
        vlayout.addLayout(hlayout)

        self.pathlabel = TextLabel()
        self.pathlabel.setTextElideMode(Qt.ElideMiddle)
        self.pathlabel.setAttribute(Qt.WA_MacSmallSize)

        vlayout.addWidget(self.pathlabel)
        w.setLayout(vlayout)

        self.infostack.addWidget(self.info_area)
        self.infostack.addWidget(w)

        box.layout().addWidget(self.infostack)

        self.__initRecentItemsModel()
        self.__invalidated = True
        self.__executor = ThreadExecutor(self)

        QApplication.postEvent(self, QEvent(RuntimeEvent.Init))
コード例 #3
0
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
コード例 #4
0
    def __init__(self):
        super().__init__()
        #: widget's runtime state
        self.__state = State.NoState
        self._imageMeta = []
        self._imageCategories = {}

        self.__invalidated = False
        self.__pendingTask = None

        vbox = gui.vBox(self.controlArea)
        hbox = gui.hBox(vbox)
        self.recent_cb = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=16,
        )
        self.recent_cb.activated[int].connect(self.__onRecentActivated)
        icons = standard_icons(self)

        browseaction = QAction(
            "Open/Load Images",
            self,
            iconText="\N{HORIZONTAL ELLIPSIS}",
            icon=icons.dir_open_icon,
            toolTip="Select a directory from which to load the images")
        browseaction.triggered.connect(self.__runOpenDialog)
        reloadaction = QAction("Reload",
                               self,
                               icon=icons.reload_icon,
                               toolTip="Reload current image set")
        reloadaction.triggered.connect(self.reload)
        self.__actions = namespace(
            browse=browseaction,
            reload=reloadaction,
        )

        browsebutton = QPushButton(browseaction.iconText(),
                                   icon=browseaction.icon(),
                                   toolTip=browseaction.toolTip(),
                                   clicked=browseaction.trigger)
        reloadbutton = QPushButton(
            reloadaction.iconText(),
            icon=reloadaction.icon(),
            clicked=reloadaction.trigger,
            default=True,
        )

        hbox.layout().addWidget(self.recent_cb)
        hbox.layout().addWidget(browsebutton)
        hbox.layout().addWidget(reloadbutton)

        self.addActions([browseaction, reloadaction])

        reloadaction.changed.connect(
            lambda: reloadbutton.setEnabled(reloadaction.isEnabled()))
        box = gui.vBox(vbox, "Info")
        self.infostack = QStackedWidget()

        self.info_area = QLabel(text="No image set selected", wordWrap=True)
        self.progress_widget = QProgressBar(minimum=0, maximum=0)
        self.cancel_button = QPushButton(
            "Cancel",
            icon=icons.cancel_icon,
        )
        self.cancel_button.clicked.connect(self.cancel)

        w = QWidget()
        vlayout = QVBoxLayout()
        vlayout.setContentsMargins(0, 0, 0, 0)
        hlayout = QHBoxLayout()
        hlayout.setContentsMargins(0, 0, 0, 0)

        hlayout.addWidget(self.progress_widget)
        hlayout.addWidget(self.cancel_button)
        vlayout.addLayout(hlayout)

        self.pathlabel = TextLabel()
        self.pathlabel.setTextElideMode(Qt.ElideMiddle)
        self.pathlabel.setAttribute(Qt.WA_MacSmallSize)

        vlayout.addWidget(self.pathlabel)
        w.setLayout(vlayout)

        self.infostack.addWidget(self.info_area)
        self.infostack.addWidget(w)

        box.layout().addWidget(self.infostack)

        self.__initRecentItemsModel()
        self.__invalidated = True
        self.__executor = ThreadExecutor(self)

        QApplication.postEvent(self, QEvent(RuntimeEvent.Init))
コード例 #5
0
class FunctionsViewer( QWidget ):
    """ The free functions (including nested) viewer widget """

    def __init__( self, parent = None ):
        QWidget.__init__( self, parent )

        self.filterEdit = None
        self.definitionButton = None
        self.findButton = None
        self.copyPathButton = None
        self.funcViewer = None
        self.__createLayout()

        # create the context menu
        self.__menu = QMenu( self )
        self.__jumpMenuItem = self.__menu.addAction(
                                PixmapCache().getIcon( 'definition.png' ),
                                'Jump to definition', self.__goToDefinition )
        self.__menu.addSeparator()
        self.__findMenuItem = self.__menu.addAction(
                                PixmapCache().getIcon( 'findusage.png' ),
                                'Find occurences', self.__findWhereUsed )
        self.__menu.addSeparator()
        self.__disasmMenuItem = self.__menu.addAction(
                                PixmapCache().getIcon( 'disasmmenu.png' ),
                                'Disassemble',
                                self.__onDisassemble )
        self.__menu.addSeparator()
        self.__copyMenuItem = self.__menu.addAction(
                                PixmapCache().getIcon( 'copytoclipboard.png' ),
                                'Copy path to clipboard',
                                self.funcViewer.copyToClipboard )
        self.funcViewer.setContextMenuPolicy( Qt.CustomContextMenu )
        self.funcViewer.customContextMenuRequested.connect(
                                                self.__handleShowContextMenu )

        GlobalData().project.projectChanged.connect( self.__onProjectChanged )
        self.connect( self.funcViewer, SIGNAL( "selectionChanged" ),
                      self.__selectionChanged )
        self.funcViewer.openingItem.connect( self.itemActivated )
        self.connect( self.funcViewer, SIGNAL( "modelFilesChanged" ),
                      self.modelFilesChanged )

        self.filterEdit.lineEdit().setFocus()
        self.__contextItem = None
        return

    def setTooltips( self, switchOn ):
        " Triggers showing python objects tooltips "
        self.funcViewer.model().sourceModel().setTooltips( switchOn )
        return

    def __createLayout( self ):
        " Helper to create the viewer layout "

        self.funcViewer = FunctionsBrowser()

        # Toolbar part - buttons
        self.definitionButton = QAction(
                PixmapCache().getIcon( 'definition.png' ),
                'Jump to highlighted item definition', self )
        self.definitionButton.triggered.connect( self.__goToDefinition )
        self.findButton = QAction(
                PixmapCache().getIcon( 'findusage.png' ),
                'Find highlighted item occurences', self )
        self.findButton.triggered.connect( self.__findWhereUsed )
        self.copyPathButton = QAction(
                PixmapCache().getIcon( 'copytoclipboard.png' ),
                'Copy path to clipboard', self )
        self.copyPathButton.triggered.connect( self.funcViewer.copyToClipboard )

        self.findNotUsedButton = QAction(
                PixmapCache().getIcon( 'notused.png' ),
                'Unused function analysis', self )
        self.findNotUsedButton.triggered.connect( self.__findNotUsed )
        self.findNotUsedButton.setEnabled( False )

        self.toolbar = QToolBar( self )
        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.definitionButton )
        self.toolbar.addAction( self.findButton )
        self.toolbar.addAction( self.copyPathButton )

        filterLabel = QLabel( "  Filter " )
        self.toolbar.addWidget( filterLabel )
        self.filterEdit = CDMComboBox( True, self.toolbar )
        self.filterEdit.setSizePolicy( QSizePolicy.Expanding,
                                       QSizePolicy.Expanding )
        self.filterEdit.lineEdit().setToolTip( "Space separated regular expressions" )
        self.toolbar.addWidget( self.filterEdit )
        self.toolbar.addAction( self.findNotUsedButton )
        self.filterEdit.editTextChanged.connect( self.__filterChanged )
        self.filterEdit.itemAdded.connect( self.__filterItemAdded )
        self.filterEdit.enterClicked.connect( self.__enterInFilter )

        layout = QVBoxLayout()
        layout.setContentsMargins( 0, 0, 0, 0 )
        layout.setSpacing( 0 )
        layout.addWidget( self.toolbar )
        layout.addWidget( self.funcViewer )

        self.setLayout( layout )
        return

    def __filterChanged( self, text ):
        " Triggers when the filter text changed "
        self.funcViewer.setFilter( text )
        self.funcViewer.updateCounter()
        return

    def __selectionChanged( self, index ):
        " Handles the changed selection "
        if index is None:
            self.__contextItem = None
        else:
            self.__contextItem = self.funcViewer.model().item( index )

        self.__updateButtons()
        return

    def getItemCount( self ):
        " Provides the number of items in the model - total, not only visible "
        return self.funcViewer.model().sourceModel().rowCount()

    def itemActivated( self, path, line ):
        " Handles the item activation "
        self.filterEdit.addItem( self.filterEdit.lineEdit().text() )
        return

    def __filterItemAdded( self ):
        " The filter item has been added "
        project = GlobalData().project
        if project.fileName != "":
            project.setFindFuncHistory( self.filterEdit.getItems() )
        return

    def __enterInFilter( self ):
        " ENTER key has been clicked in the filter "

        # check if there any records displayed
        if self.funcViewer.model().rowCount() == 0:
            return

        # Move the focus to the list and select the first row
        self.funcViewer.clearSelection()
        flags = QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows
        self.funcViewer.setSelection( QRect( 0, 0, self.funcViewer.width(), 1 ),
                                      flags )
        self.funcViewer.setFocus()
        return

    def __onProjectChanged( self, what ):
        " Triggered when a project is changed "

        if what == CodimensionProject.CompleteProject:
            self.__contextItem = None
            self.__updateButtons()
            self.filterEdit.clear()

            project = GlobalData().project
            if project.isLoaded():
                self.filterEdit.editTextChanged.disconnect(
                                                        self.__filterChanged )
                self.filterEdit.addItems( project.findFuncHistory )
                self.filterEdit.editTextChanged.connect( self.__filterChanged )
                self.findNotUsedButton.setEnabled( self.getItemCount() > 0 )
            else:
                self.findNotUsedButton.setEnabled( False )
            self.filterEdit.clearEditText()
        return

    def __handleShowContextMenu( self, coord ):
        """ Show the context menu """

        index = self.funcViewer.indexAt( coord )
        if not index.isValid():
            return

        # This will update the __contextItem
        self.__selectionChanged( index )

        if self.__contextItem is None:
            return

        self.__jumpMenuItem.setEnabled( self.definitionButton.isEnabled() )
        self.__findMenuItem.setEnabled( self.findButton.isEnabled() )
        self.__copyMenuItem.setEnabled( self.copyPathButton.isEnabled() )

        canDisassemble = self.__contextItem.canGetDisassembler()
        self.__disasmMenuItem.setEnabled( canDisassemble )

        self.__menu.popup( QCursor.pos() )
        return

    def __goToDefinition( self ):
        " Jump to definition context menu handler "
        if self.__contextItem is not None:
            self.funcViewer.openItem( self.__contextItem )
        return

    def __findWhereUsed( self ):
        """ Find where used context menu handler """
        if self.__contextItem is not None:
            GlobalData().mainWindow.findWhereUsed( \
                    self.__contextItem.getPath(),
                    self.__contextItem.sourceObj )
        return

    def __findNotUsed( self ):
        " Runs the unused function analysis "
        GlobalData().mainWindow.onNotUsedFunctions()
        return

    def __updateButtons( self ):
        " Updates the toolbar buttons depending on what is selected "

        self.definitionButton.setEnabled( False )
        self.findButton.setEnabled( False )
        self.copyPathButton.setEnabled( False )
        if self.__contextItem is None:
            return

        if self.__contextItem.itemType == DecoratorItemType:
            self.definitionButton.setEnabled( True )
            self.copyPathButton.setEnabled( True )
            return

        if self.__contextItem.itemType in [ FunctionItemType, ClassItemType,
                                            AttributeItemType, GlobalItemType ]:
            self.definitionButton.setEnabled( True )
            self.findButton.setEnabled( True )
            self.copyPathButton.setEnabled( True )
        return

    def onFileUpdated( self, fileName, uuid ):
        " Triggered when the file is updated "
        self.funcViewer.onFileUpdated( fileName )
        self.findNotUsedButton.setEnabled( GlobalData().project.isLoaded() and \
                                           self.getItemCount() > 0 )
        return

    def modelFilesChanged( self ):
        " Triggered when the source model has a file or files added or deleted "
        self.findNotUsedButton.setEnabled( GlobalData().project.isLoaded() and \
                                           self.getItemCount() > 0 )
        return

    def __onDisassemble( self ):
        " Disassembling has been requested "
        if self.__contextItem is not None:
            self.funcViewer.getDisassembled( self.__contextItem )
コード例 #6
0
class FileOutlineViewer( QWidget ):
    """ The file outline viewer widget """

    def __init__( self, editorsManager, parent = None ):
        QWidget.__init__( self, parent )

        self.__editorsManager = editorsManager
        self.__mainWindow = parent
        self.__editorsManager.currentChanged.connect( self.__onTabChanged )
        self.connect( self.__editorsManager, SIGNAL( "tabClosed" ),
                      self.__onTabClosed )
        self.connect( self.__editorsManager, SIGNAL( 'bufferSavedAs' ),
                      self.__onSavedBufferAs )
        self.connect( self.__editorsManager, SIGNAL( 'fileTypeChanged' ),
                      self.__onFileTypeChanged )

        self.__outlineBrowsers = {}  # UUID -> OutlineAttributes
        self.__currentUUID = None
        self.__updateTimer = QTimer( self )
        self.__updateTimer.setSingleShot( True )
        self.__updateTimer.timeout.connect( self.__updateView )

        self.findButton = None
        self.outlineViewer = None
        self.toolbar = None
        self.__createLayout()

        self.__modifiedFormat = Settings().modifiedFormat

        # create the context menu
        self.__menu = QMenu( self )
        self.__findMenuItem = self.__menu.addAction(
                                PixmapCache().getIcon( 'findusage.png' ),
                                'Find where used', self.__findWhereUsed )
        return

    def setTooltips( self, switchOn ):
        " Sets the tooltips mode "
        for key in self.__outlineBrowsers:
            self.__outlineBrowsers[ key ].browser.setTooltips( switchOn )
        return

    def __connectOutlineBrowser( self, browser ):
        " Connects a new buffer signals "
        browser.setContextMenuPolicy( Qt.CustomContextMenu )
        browser.customContextMenuRequested.connect( self.__handleShowContextMenu )

        self.connect( browser,
                      SIGNAL( "selectionChanged" ),
                      self.__selectionChanged )
        return

    def __createLayout( self ):
        " Helper to create the viewer layout "

        # Toolbar part - buttons
        self.findButton = QAction(
                PixmapCache().getIcon( 'findusage.png' ),
                'Find where highlighted item is used', self )
        self.findButton.setVisible( False )
        self.findButton.triggered.connect( self.__findWhereUsed )
        self.showParsingErrorsButton = QAction(
                PixmapCache().getIcon( 'showparsingerrors.png' ),
                'Show lexer/parser errors', self )
        self.showParsingErrorsButton.triggered.connect( self.__showParserError )
        self.showParsingErrorsButton.setEnabled( False )

        self.toolbar = QToolBar( self )
        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.findButton )
        self.toolbar.addAction( self.showParsingErrorsButton )

        # Prepare members for reuse
        self.__noneLabel = QLabel( "\nNot a python file" )
        self.__noneLabel.setFrameShape( QFrame.StyledPanel )
        self.__noneLabel.setAlignment( Qt.AlignHCenter )
        headerFont = self.__noneLabel.font()
        headerFont.setPointSize( headerFont.pointSize() + 2 )
        self.__noneLabel.setFont( headerFont )
        self.__noneLabel.setAutoFillBackground( True )
        noneLabelPalette = self.__noneLabel.palette()
        noneLabelPalette.setColor( QPalette.Background,
                                   GlobalData().skin.nolexerPaper )
        self.__noneLabel.setPalette( noneLabelPalette )

        self.__layout = QVBoxLayout()
        self.__layout.setContentsMargins( 0, 0, 0, 0 )
        self.__layout.setSpacing( 0 )
        self.__layout.addWidget( self.toolbar )
        self.__layout.addWidget( self.__noneLabel )

        self.setLayout( self.__layout )
        return

    def __selectionChanged( self, index ):
        " Handles the changed selection "
        if index is None:
            self.__outlineBrowsers[ self.__currentUUID ].contentItem = None
        else:
            self.__outlineBrowsers[ self.__currentUUID ].contentItem = \
                self.__outlineBrowsers[
                        self.__currentUUID ].browser.model().item( index )

        self.__updateButtons()
        return

    def __handleShowContextMenu( self, coord ):
        """ Show the context menu """

        browser = self.__outlineBrowsers[ self.__currentUUID ].browser
        index = browser.indexAt( coord )
        if not index.isValid():
            return

        # This will update the contextItem
        self.__selectionChanged( index )

        contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem
        if contextItem is None:
            return

        self.__findMenuItem.setEnabled( self.findButton.isEnabled() )

        self.__menu.popup( QCursor.pos() )
        return

    def __goToDefinition( self ):
        " Jump to definition context menu handler "
        contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem
        if contextItem is not None:
            self.__outlineBrowsers[
                        self.__currentUUID ].browser.openItem( contextItem )
        return

    def __findWhereUsed( self ):
        """ Find where used context menu handler """
        contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem
        if contextItem is not None:
            GlobalData().mainWindow.findWhereUsed(
                    contextItem.getPath(),
                    contextItem.sourceObj )
        return

    def __updateButtons( self ):
        " Updates the toolbar buttons depending on what is selected "

        self.findButton.setEnabled( False )

        contextItem = self.__outlineBrowsers[ self.__currentUUID ].contentItem
        if contextItem is None:
            return

        if contextItem.itemType in [ FunctionItemType, ClassItemType,
                                     AttributeItemType, GlobalItemType ]:
            self.findButton.setEnabled( True )
        return

    def __onTabChanged( self, index ):
        " Triggered when another tab becomes active "

        # If the timer is still active that means the tab was switched before
        # the handler had a chance to work. Therefore update the previous tab
        # first if so.
        if self.__updateTimer.isActive():
            self.__updateTimer.stop()
            self.__updateView()

        # Now, switch the outline browser to the new tab
        if index == -1:
            widget = self.__editorsManager.currentWidget()
        else:
            widget = self.__editorsManager.getWidgetByIndex( index )
        if widget is None:
            if self.__currentUUID is not None:
                self.__outlineBrowsers[ self.__currentUUID ].browser.hide()
                self.__currentUUID = None
            self.__noneLabel.show()
            self.showParsingErrorsButton.setEnabled( False )
            return
        if widget.getType() not in [ MainWindowTabWidgetBase.PlainTextEditor,
                                     MainWindowTabWidgetBase.VCSAnnotateViewer ]:
            if self.__currentUUID is not None:
                self.__outlineBrowsers[ self.__currentUUID ].browser.hide()
                self.__currentUUID = None
            self.__noneLabel.show()
            self.showParsingErrorsButton.setEnabled( False )
            return

        # This is text editor, detect the file type
        if widget.getFileType() not in [ PythonFileType, Python3FileType ]:
            if self.__currentUUID is not None:
                self.__outlineBrowsers[ self.__currentUUID ].browser.hide()
                self.__currentUUID = None
            self.__noneLabel.show()
            self.showParsingErrorsButton.setEnabled( False )
            return


        # This is a python file, check if we already have the parsed info in
        # the cache
        uuid = widget.getUUID()
        if uuid in self.__outlineBrowsers:
            # We have it, hide the current and show the existed
            if self.__currentUUID is not None:
                self.__outlineBrowsers[ self.__currentUUID ].browser.hide()
                self.__currentUUID = None
            else:
                self.__noneLabel.hide()
            self.__currentUUID = uuid
            self.__outlineBrowsers[ self.__currentUUID ].browser.show()

            info = self.__outlineBrowsers[ self.__currentUUID ].info
            self.showParsingErrorsButton.setEnabled( info.isOK != True )
            return

        # It is first time we are here, create a new
        editor = widget.getEditor()
        editor.SCEN_CHANGE.connect( self.__onBufferChanged )
        editor.cursorPositionChanged.connect( self.__cursorPositionChanged )
        info = getBriefModuleInfoFromMemory( editor.text() )

        self.showParsingErrorsButton.setEnabled( info.isOK != True )

        shortFileName = widget.getShortName()
        browser = OutlineBrowser( uuid, shortFileName, info, self )
        browser.setHeaderHighlight( info.isOK != True )
        self.__connectOutlineBrowser( browser )
        self.__layout.addWidget( browser )
        if self.__currentUUID is not None:
            self.__outlineBrowsers[ self.__currentUUID ].browser.hide()
            self.__currentUUID = None
        else:
            self.__noneLabel.hide()

        self.__currentUUID = uuid
        attributes = OutlineAttributes()
        attributes.browser = browser
        attributes.contextItem = None
        attributes.info = info
        attributes.shortFileName = shortFileName
        attributes.changed = False
        self.__outlineBrowsers[ self.__currentUUID ] = attributes
        self.__outlineBrowsers[ self.__currentUUID ].browser.show()
        return

    def getCurrentUsedInfo( self ):
        " Provides the info used to show the current outline window "
        if self.__currentUUID in self.__outlineBrowsers:
            return self.__outlineBrowsers[ self.__currentUUID ].info
        return None

    def __cursorPositionChanged( self, xpos, ypos ):
        " Triggered when a cursor position is changed "
        if self.__updateTimer.isActive():
            # If a file is very large and the cursor is moved
            # straight after changes this will delay the update till
            # the real pause.
            self.__updateTimer.stop()
            self.__updateTimer.start( 1500 )
        return

    def __onBufferChanged( self ):
        " Triggered when a change in the buffer is identified "
        if self.__currentUUID is None:
            return
        widget = self.__editorsManager.getWidgetByUUID(
                                        self.__currentUUID )
        if widget is None:
            return
        if widget.getEditor().ignoreBufferChangedSignal:
            return
        if self.__mainWindow.debugMode:
            return

        self.__updateTimer.stop()
        if self.__currentUUID in self.__outlineBrowsers:
            if self.__outlineBrowsers[ self.__currentUUID ].changed == False:
                self.__outlineBrowsers[ self.__currentUUID ].changed = True
                browser = self.__outlineBrowsers[ self.__currentUUID ].browser
                fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName
                title = self.__modifiedFormat % fName
                browser.model().sourceModel().updateRootData( 0, title )
        self.__updateTimer.start( 1500 )
        return

    def __updateView( self ):
        " Updates the view when a file is changed "
        self.__updateTimer.stop()
        info = self.getCurrentBufferInfo()
        if info is None:
            return

        self.showParsingErrorsButton.setEnabled( info.isOK != True )
        browser = self.__outlineBrowsers[ self.__currentUUID ].browser
        fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName
        browser.setHeaderHighlight( info.isOK != True )

        if not info.isOK:
            title = self.__modifiedFormat % fName
            browser.model().sourceModel().updateRootData( 0, title )
            return

        browser.model().sourceModel().updateRootData( 0, fName )
        self.__outlineBrowsers[ self.__currentUUID ].changed = False

        browser.updateFileItem( browser.model().sourceModel().rootItem, info )
        self.__outlineBrowsers[ self.__currentUUID ].info = info

        return

    def getCurrentBufferInfo( self ):
        " Provides the current buffer parsed info "
        if self.__currentUUID is None:
            return None
        widget = self.__editorsManager.getWidgetByUUID( self.__currentUUID )
        if widget is None:
            return None

        editor = widget.getEditor()
        info = getBriefModuleInfoFromMemory( editor.text() )
        return info

    def __onTabClosed( self, uuid ):
        " Triggered when a tab is closed "

        if uuid in self.__outlineBrowsers:
            del self.__outlineBrowsers[ uuid ]
        return

    def __onSavedBufferAs( self, fileName, uuid ):
        " Triggered when a file is saved with a new name "

        if uuid in self.__outlineBrowsers:

            baseName = os.path.basename( fileName )
            if detectFileType( fileName ) not in [ PythonFileType,
                                                   Python3FileType ]:
                # It's not a python file anymore
                if uuid == self.__currentUUID:
                    self.__outlineBrowsers[ uuid ].browser.hide()
                    self.__noneLabel.show()
                    self.__currentUUID = None

                del self.__outlineBrowsers[ uuid ]
                self.showParsingErrorsButton.setEnabled( False )
                self.findButton.setEnabled( False )
                return

            # Still python file with a different name
            browser = self.__outlineBrowsers[ uuid ].browser
            self.__outlineBrowsers[ uuid ].shortFileName = baseName
            if self.__outlineBrowsers[ uuid ].changed:
                title = self.__modifiedFormat % baseName
            else:
                title = baseName
            browser.model().sourceModel().updateRootData( 0, title )
        return

    def __onFileTypeChanged( self, fileName, uuid, newFileType ):
        " Triggered when the current buffer file type is changed, e.g. .cgi "
        if newFileType in [ PythonFileType, Python3FileType ]:
            # The file became a python one
            if uuid not in self.__outlineBrowsers:
                self.__onTabChanged( -1 )
        else:
            if uuid in self.__outlineBrowsers:
                # It's not a python file any more
                if uuid == self.__currentUUID:
                    self.__outlineBrowsers[ uuid ].browser.hide()
                    self.__noneLabel.show()
                    self.__currentUUID = None

                del self.__outlineBrowsers[ uuid ]
                self.showParsingErrorsButton.setEnabled( False )
                self.findButton.setEnabled( False )
        return

    def __showParserError( self ):
        " Shows the parser errors window "
        if self.__currentUUID is None:
            return

        try:
            fName = self.__outlineBrowsers[ self.__currentUUID ].shortFileName

            widget = self.__editorsManager.getWidgetByUUID( self.__currentUUID )
            if widget is None:
                return

            editor = widget.getEditor()
            info = getBriefModuleInfoFromMemory( editor.text() )
            dialog = ParserErrorsDialog( fName, info )
            dialog.exec_()
        except Exception, ex:
            logging.error( str( ex ) )
        return