コード例 #1
0
class DBServersWidget(QWidget):
    """Displays a list of servers"""
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.debug = False

        self.connections = {}

        self.setWindowTitle("Servers")
        #s#elf.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)

        self.mainLayout = QVBoxLayout()
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self.setLayout(self.mainLayout)

        #=============================================
        ## Top Toolbar
        topBar = QToolBar()
        topBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.mainLayout.addWidget(topBar)

        ## Add the action buttons
        topBar.addAction(Ico.icon(Ico.ServerAdd), "Add", self.on_server_add)
        self.actionServerEdit = topBar.addAction(Ico.icon(Ico.ServerEdit),
                                                 "Edit", self.on_server_edit)
        self.actionServerDelete = topBar.addAction(Ico.icon(Ico.ServerDelete),
                                                   "Delete",
                                                   self.on_server_delete)

        #=============================================
        ## Tree
        self.tree = QTreeWidget()
        self.mainLayout.addWidget(self.tree)
        self.tree.setUniformRowHeights(True)
        self.tree.setRootIsDecorated(True)

        self.tree.setHeaderLabels(["Server",
                                   "Butt"])  # set header, but hide anyway
        self.tree.header().hide()
        self.tree.header().setResizeMode(C.node, QHeaderView.Stretch)
        self.tree.setColumnWidth(C.butt, 20)

        self.connect(self.tree, SIGNAL('itemSelectionChanged()'),
                     self.on_tree_selection_changed)
        self.connect(self.tree,
                     SIGNAL('itemDoubleClicked (QTreeWidgetItem *,int)'),
                     self.on_tree_double_clicked)

        self.buttGroup = QButtonGroup(self)
        self.connect(self.buttGroup, SIGNAL("buttonClicked(QAbstractButton*)"),
                     self.on_open_server)

        self.on_tree_selection_changed()

        self.load_servers()

    #=======================================
    ##== Tree Events
    def on_tree_selection_changed(self):

        disabled = self.tree.selectionModel().hasSelection() == False
        self.actionServerEdit.setDisabled(disabled)
        self.actionServerDelete.setDisabled(disabled)

    def on_tree_double_clicked(self):
        self.actionServerEdit.trigger()

    #=======================================
    ## Server Actions
    def on_server_add(self):
        self.show_server_dialog(None)

    def on_server_edit(self):
        item = self.tree.currentItem()
        if item == None:
            return
        server = str(item.text(C.server))
        self.show_server_dialog(server)

    def show_server_dialog(self, server=None):
        d = DBServerDialog.DBServerDialog(self, server)
        if d.exec_():
            self.load_servers()

    def load_servers(self):
        """Load servers from :py:meth:`pyqtdb.XSettings.XSettings.get_servers` """

        self.tree.clear()

        for butt in self.buttGroup.buttons():
            self.buttGroup.removeButton(butt)

        for srv in G.settings.get_servers_list():

            item = QTreeWidgetItem()
            item.setText(C.node, srv['server'])
            #item.setText(C.user, srv['user'])
            self.tree.addTopLevelItem(item)

            butt = QToolButton()
            butt.setIcon(Ico.icon(Ico.Connect))
            butt.setProperty("server", srv['server'])
            self.tree.setItemWidget(item, C.butt, butt)
            self.buttGroup.addButton(butt)

    def on_server_delete(self):
        item = self.tree.currentItem()
        if item == None:
            return
        srv = str(item.text(C.server))
        G.settings.delete_server(srv)
        self.load_servers()

    def on_open_server(self, butt):

        # self.emit(SIGNAL("open_server"), butt.property("server").toString())
        srv_ki = str(butt.property("server").toString())
        server = G.settings.get_server(srv_ki)
        db = QSqlDatabase.addDatabase("QMYSQL", srv_ki)
        db.setHostName(server['server'])
        db.setUserName(server['user'])
        db.setPassword(server['passwd'])

        ok = db.open()
        if ok:
            #self.connections[srv_ki] =
            self.load_databases(srv_ki)
            print "open", ok

    def load_databases(self, srv_ki):
        """Load databases into tree node for server;  executes 'show databases;' or aslike """

        sql = "show databases;"
        query = QSqlQuery(QSqlDatabase.database(srv_ki))
        ok = query.exec_(sql)
        print ok, sql, query.result()

        # Get the parent node, ie the server node
        pItem = self.tree.findItems(srv_ki, Qt.MatchExactly, C.node)[0]

        ## Assumed value(0) is the table.. we need the defs (ie mysql case)
        while query.next():
            table_name = query.value(0).toString()
            nuItem = QTreeWidgetItem(pItem)
            nuItem.setText(C.node, table_name)
            #print table_name

        self.tree.setItemExpanded(pItem, True)
コード例 #2
0
class Main(plugin.Plugin):
    " Main Class "

    def initialize(self, *args, **kwargs):
        " Init Main Class "
        super(Main, self).initialize(*args, **kwargs)
        self.scriptPath, self.scriptArgs = "", []
        self.profilerPath, self.tempPath = profilerPath, tempPath
        self.output = " ERROR: FAIL: No output ! "

        self.process = QProcess()
        self.process.finished.connect(self.on_process_finished)
        self.process.error.connect(self.on_process_error)

        self.tabWidget, self.stat = QTabWidget(), QWidget()
        self.tabWidget.tabCloseRequested.connect(
            lambda: self.tabWidget.setTabPosition(1) if self.tabWidget.
            tabPosition() == 0 else self.tabWidget.setTabPosition(0))
        self.tabWidget.setStyleSheet('QTabBar{font-weight:bold;}')
        self.tabWidget.setMovable(True)
        self.tabWidget.setTabsClosable(True)
        self.vboxlayout1 = QVBoxLayout(self.stat)
        self.hboxlayout1 = QHBoxLayout()
        self.filterTableLabel = QLabel("<b>Type to Search : </b>", self.stat)
        self.hboxlayout1.addWidget(self.filterTableLabel)
        self.filterTableLineEdit = QLineEdit(self.stat)
        self.filterTableLineEdit.setPlaceholderText(' Type to Search . . . ')
        self.hboxlayout1.addWidget(self.filterTableLineEdit)
        self.filterHintTableLabel = QLabel(" ? ", self.stat)
        self.hboxlayout1.addWidget(self.filterHintTableLabel)
        self.vboxlayout1.addLayout(self.hboxlayout1)
        self.tableWidget = QTableWidget(self.stat)
        self.tableWidget.setAlternatingRowColors(True)
        self.tableWidget.setColumnCount(8)
        self.tableWidget.setRowCount(2)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(3, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(4, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(5, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(6, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(7, item)
        self.tableWidget.itemDoubleClicked.connect(
            self.on_tableWidget_itemDoubleClicked)
        self.vboxlayout1.addWidget(self.tableWidget)
        self.tabWidget.addTab(self.stat, " ? ")

        self.source = QWidget()
        self.gridlayout = QGridLayout(self.source)
        self.scintillaWarningLabel = QLabel(
            "QScintilla is not installed!. Falling back to basic text edit!.",
            self.source)
        self.gridlayout.addWidget(self.scintillaWarningLabel, 1, 0, 1, 2)
        self.sourceTreeWidget = QTreeWidget(self.source)
        self.sourceTreeWidget.setAlternatingRowColors(True)
        self.sourceTreeWidget.itemActivated.connect(
            self.on_sourceTreeWidget_itemActivated)
        self.sourceTreeWidget.itemClicked.connect(
            self.on_sourceTreeWidget_itemClicked)
        self.sourceTreeWidget.itemDoubleClicked.connect(
            self.on_sourceTreeWidget_itemClicked)

        self.gridlayout.addWidget(self.sourceTreeWidget, 0, 0, 1, 1)
        self.sourceTextEdit = QTextEdit(self.source)
        self.sourceTextEdit.setReadOnly(True)
        self.gridlayout.addWidget(self.sourceTextEdit, 0, 1, 1, 1)
        self.tabWidget.addTab(self.source, " ? ")

        self.result = QWidget()
        self.vlayout = QVBoxLayout(self.result)
        self.globalStatGroupBox = QGroupBox(self.result)
        self.hboxlayout = QHBoxLayout(self.globalStatGroupBox)
        self.totalTimeLcdNumber = QLCDNumber(self.globalStatGroupBox)
        self.totalTimeLcdNumber.setSegmentStyle(QLCDNumber.Filled)
        self.totalTimeLcdNumber.setNumDigits(7)
        self.totalTimeLcdNumber.display(1000000)
        self.totalTimeLcdNumber.setFrameShape(QFrame.StyledPanel)
        self.totalTimeLcdNumber.setSizePolicy(QSizePolicy.Expanding,
                                              QSizePolicy.Expanding)
        self.hboxlayout.addWidget(self.totalTimeLcdNumber)
        self.tTimeLabel = QLabel("<b>Total Time (Sec)</b>",
                                 self.globalStatGroupBox)
        self.tTimeLabel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.hboxlayout.addWidget(self.tTimeLabel)
        self.numCallLcdNumber = QLCDNumber(self.globalStatGroupBox)
        self.numCallLcdNumber.setNumDigits(7)
        self.numCallLcdNumber.display(1000000)
        self.numCallLcdNumber.setSegmentStyle(QLCDNumber.Filled)
        self.numCallLcdNumber.setFrameShape(QFrame.StyledPanel)
        self.numCallLcdNumber.setSizePolicy(QSizePolicy.Expanding,
                                            QSizePolicy.Expanding)
        self.hboxlayout.addWidget(self.numCallLcdNumber)
        self.numCallLabel = QLabel("<b>Number of calls</b>",
                                   self.globalStatGroupBox)
        self.numCallLabel.setSizePolicy(QSizePolicy.Minimum,
                                        QSizePolicy.Minimum)
        self.hboxlayout.addWidget(self.numCallLabel)
        self.primCallLcdNumber = QLCDNumber(self.globalStatGroupBox)
        self.primCallLcdNumber.setSegmentStyle(QLCDNumber.Filled)
        self.primCallLcdNumber.setFrameShape(QFrame.StyledPanel)
        self.primCallLcdNumber.setNumDigits(7)
        self.primCallLcdNumber.display(1000000)
        self.primCallLcdNumber.setSizePolicy(QSizePolicy.Expanding,
                                             QSizePolicy.Expanding)
        self.hboxlayout.addWidget(self.primCallLcdNumber)
        self.primCallLabel = QLabel("<b>Primitive calls (%)</b>",
                                    self.globalStatGroupBox)
        self.primCallLabel.setSizePolicy(QSizePolicy.Minimum,
                                         QSizePolicy.Minimum)
        self.hboxlayout.addWidget(self.primCallLabel)
        self.vlayout.addWidget(self.globalStatGroupBox)
        try:
            from PyKDE4.kdeui import KRatingWidget
            self.rating = KRatingWidget(self.globalStatGroupBox)
            self.rating.setToolTip('Profiling Performance Rating')
        except ImportError:
            pass
        self.tabWidget.addTab(self.result, " Get Results ! ")

        self.resgraph = QWidget()
        self.vlayout2 = QVBoxLayout(self.result)
        self.graphz = QGroupBox(self.resgraph)
        self.hboxlayout2 = QHBoxLayout(self.graphz)
        try:
            from PyKDE4.kdeui import KLed
            KLed(self.graphz)
        except ImportError:
            pass
        self.hboxlayout2.addWidget(
            QLabel('''
            Work in Progress  :)  Not Ready Yet'''))
        self.vlayout2.addWidget(self.graphz)
        self.tabWidget.addTab(self.resgraph, " Graphs and Charts ")

        self.pathz = QWidget()
        self.vlayout3 = QVBoxLayout(self.pathz)
        self.patz = QGroupBox(self.pathz)
        self.hboxlayout3 = QVBoxLayout(self.patz)
        self.profilepath = QLineEdit(profilerPath)
        self.getprofile = QPushButton(QIcon.fromTheme("document-open"), 'Open')
        self.getprofile.setToolTip(
            'Dont touch if you dont know what are doing')
        self.getprofile.clicked.connect(lambda: self.profilepath.setText(
            str(
                QFileDialog.getOpenFileName(
                    self.patz, ' Open the profile.py file ',
                    path.expanduser("~"), ';;(profile.py)'))))
        self.hboxlayout3.addWidget(
            QLabel(
                '<center><b>Profile.py Python Library Full Path:</b></center>')
        )
        self.hboxlayout3.addWidget(self.profilepath)
        self.hboxlayout3.addWidget(self.getprofile)

        self.argGroupBox = QGroupBox(self.pathz)
        self.hbxlayout = QHBoxLayout(self.argGroupBox)
        self.argLineEdit = QLineEdit(self.argGroupBox)
        self.argLineEdit.setToolTip(
            'Not touch if you dont know what are doing')
        self.argLineEdit.setPlaceholderText(
            'Dont touch if you dont know what are doing')
        self.hbxlayout.addWidget(
            QLabel('<b>Additional Profile Arguments:</b>'))
        self.hbxlayout.addWidget(self.argLineEdit)
        self.hboxlayout3.addWidget(self.argGroupBox)

        self.vlayout3.addWidget(self.patz)
        self.tabWidget.addTab(self.pathz, " Paths and Configs ")

        self.outp = QWidget()
        self.vlayout4 = QVBoxLayout(self.outp)
        self.outgro = QGroupBox(self.outp)
        self.outgro.setTitle(" MultiProcessing Output Logs ")
        self.hboxlayout4 = QVBoxLayout(self.outgro)
        self.outputlog = QTextEdit()
        self.outputlog.setText('''
        I do not fear computers, I fear the lack of them.   -Isaac Asimov ''')
        self.hboxlayout4.addWidget(self.outputlog)
        self.vlayout4.addWidget(self.outgro)
        self.tabWidget.addTab(self.outp, " Logs ")

        self.actionNew_profiling = QAction(QIcon.fromTheme("document-new"),
                                           'New Profiling', self)
        self.actionLoad_profile = QAction(QIcon.fromTheme("document-open"),
                                          'Open Profiling', self)
        self.actionClean = QAction(QIcon.fromTheme("edit-clear"), 'Clean',
                                   self)
        self.actionClean.triggered.connect(lambda: self.clearContent)
        self.actionAbout = QAction(QIcon.fromTheme("help-about"), 'About',
                                   self)
        self.actionAbout.triggered.connect(lambda: QMessageBox.about(
            self.dock, __doc__, ', '.join(
                (__doc__, __license__, __author__, __email__))))
        self.actionSave_profile = QAction(QIcon.fromTheme("document-save"),
                                          'Save Profiling', self)
        self.actionManual = QAction(QIcon.fromTheme("help-contents"), 'Help',
                                    self)
        self.actionManual.triggered.connect(lambda: open_new_tab(
            'http://docs.python.org/library/profile.html'))

        self.tabWidget.setCurrentIndex(2)

        self.globalStatGroupBox.setTitle("Global Statistics")
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText("Number of Calls")
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText("Total Time")
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText("Per Call")
        item = self.tableWidget.horizontalHeaderItem(3)
        item.setText("Cumulative Time")
        item = self.tableWidget.horizontalHeaderItem(4)
        item.setText("Per Call")
        item = self.tableWidget.horizontalHeaderItem(5)
        item.setText("Filename")
        item = self.tableWidget.horizontalHeaderItem(6)
        item.setText("Line")
        item = self.tableWidget.horizontalHeaderItem(7)
        item.setText("Function")
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.stat),
                                  "Statistics per Function")

        self.sourceTreeWidget.headerItem().setText(0, "Source files")
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.source),
                                  "Sources Navigator")
        #######################################################################

        self.scrollable, self.dock = QScrollArea(), QDockWidget()
        self.scrollable.setWidgetResizable(True)
        self.scrollable.setWidget(self.tabWidget)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.dock.setWidget(self.scrollable)
        QToolBar(self.dock).addActions(
            (self.actionNew_profiling, self.actionClean,
             self.actionSave_profile, self.actionLoad_profile,
             self.actionManual, self.actionAbout))

        self.actionNew_profiling.triggered.connect(
            self.on_actionNew_profiling_triggered)
        self.actionLoad_profile.triggered.connect(
            self.on_actionLoad_profile_triggered)
        self.actionSave_profile.triggered.connect(
            self.on_actionSave_profile_triggered)

        self.locator.get_service('misc').add_widget(
            self.dock, QIcon.fromTheme("document-open-recent"), __doc__)

        if QSCI:
            # Scintilla source editor management
            self.scintillaWarningLabel.setText(' QScintilla is Ready ! ')
            layout = self.source.layout()
            layout.removeWidget(self.sourceTextEdit)
            self.sourceTextEdit = Qsci.QsciScintilla(self.source)
            layout.addWidget(self.sourceTextEdit, 0, 1)
            doc = self.sourceTextEdit
            doc.setLexer(Qsci.QsciLexerPython(self.sourceTextEdit))
            doc.setReadOnly(True)
            doc.setEdgeMode(Qsci.QsciScintilla.EdgeLine)
            doc.setEdgeColumn(80)
            doc.setEdgeColor(QColor("#FF0000"))
            doc.setFolding(Qsci.QsciScintilla.BoxedTreeFoldStyle)
            doc.setBraceMatching(Qsci.QsciScintilla.SloppyBraceMatch)
            doc.setCaretLineVisible(True)
            doc.setMarginLineNumbers(1, True)
            doc.setMarginWidth(1, 25)
            doc.setTabWidth(4)
            doc.setEolMode(Qsci.QsciScintilla.EolUnix)
            self.marker = {}
            for color in COLORS:
                mnr = doc.markerDefine(Qsci.QsciScintilla.Background)
                doc.setMarkerBackgroundColor(color, mnr)
                self.marker[color] = mnr
        self.currentSourcePath = None

        # Connect table and tree filter edit signal to unique slot
        self.filterTableLineEdit.textEdited.connect(
            self.on_filterLineEdit_textEdited)

        # Timer to display filter hint message
        self.filterHintTimer = QTimer(self)
        self.filterHintTimer.setSingleShot(True)
        self.filterHintTimer.timeout.connect(self.on_filterHintTimer_timeout)

        # Timer to start search
        self.filterSearchTimer = QTimer(self)
        self.filterSearchTimer.setSingleShot(True)
        self.filterSearchTimer.timeout.connect(
            self.on_filterSearchTimer_timeout)

        self.tabLoaded = {}
        for i in range(10):
            self.tabLoaded[i] = False
        self.backgroundTreeMatchedItems = {}
        self.resizeWidgetToContent(self.tableWidget)

    def on_actionNew_profiling_triggered(self):
        self.clearContent()
        self.scriptPath = str(
            QFileDialog.getOpenFileName(self.dock,
                                        "Choose your script to profile",
                                        path.expanduser("~"),
                                        "Python (*.py *.pyw)"))
        commandLine = [
            self.profilerPath, "-o", self.tempPath, self.scriptPath
        ] + self.scriptArgs
        commandLine = " ".join(commandLine)
        ##if self.termCheckBox.checkState() == Qt.Checked:
        #termList = ["xterm", "aterm"]
        #for term in termList:
        #termPath = which(term)
        #if termPath:
        #break
        #commandLine = """%s -e "%s ; echo 'Press ENTER Exit' ; read" """ \
        #% (termPath, commandLine)
        self.process.start(commandLine)
        if not self.process.waitForStarted():
            print((" ERROR: {} failed!".format(commandLine)))
            return

    def on_process_finished(self, exitStatus):
        ' whan the process end '
        print((" INFO: OK: QProcess is %s" % self.process.exitCode()))
        self.output = self.process.readAll().data()
        if not self.output:
            self.output = " ERROR: FAIL: No output ! "
        self.outputlog.setText(self.output + str(self.process.exitCode()))
        if path.exists(self.tempPath):
            self.setStat(self.tempPath)
            remove(self.tempPath)
        else:
            self.outputlog.setText(" ERROR: QProcess FAIL: Profiling failed.")
        self.tabWidget.setCurrentIndex(2)

    def on_process_error(self, error):
        ' when the process fail, I hope you never see this '
        print(" ERROR: QProcess FAIL: Profiler Dead, wheres your God now ? ")
        if error == QProcess.FailedToStart:
            self.outputlog.setText(" ERROR: FAIL: Profiler execution failed ")
        elif error == QProcess.Crashed:
            self.outputlog.setText(" ERROR: FAIL: Profiler execution crashed ")
        else:
            self.outputlog.setText(" ERROR: FAIL: Profiler unknown error ")

    def on_actionLoad_profile_triggered(self):
        """Load a previous profile sessions"""
        statPath = str(
            QFileDialog.getOpenFileName(self.dock, "Open profile dump",
                                        path.expanduser("~"),
                                        "Profile file (*)"))
        if statPath:
            self.clearContent()
            print(' INFO: OK: Loading profiling from ' + statPath)
            self.setStat(statPath)

    def on_actionSave_profile_triggered(self):
        """Save a profile sessions"""
        statPath = str(
            QFileDialog.getSaveFileName(self.dock, "Save profile dump",
                                        path.expanduser("~"),
                                        "Profile file (*)"))
        if statPath:
            #TODO: handle error case and give feelback to user
            print(' INFO: OK: Saving profiling to ' + statPath)
            self.stat.save(statPath)

    #=======================================================================#
    # Common parts                                                          #
    #=======================================================================#

    def on_tabWidget_currentChanged(self, index):
        """slot for tab change"""
        # Kill search and hint timer if running to avoid cross effect
        for timer in (self.filterHintTimer, self.filterSearchTimer):
            if timer.isActive():
                timer.stop()
        if not self.stat:
            #No stat loaded, nothing to do
            return
        self.populateTable()
        self.populateSource()

    def on_filterLineEdit_textEdited(self, text):
        """slot for filter change (table or tree"""
        if self.filterSearchTimer.isActive():
            # Already runnning, stop it
            self.filterSearchTimer.stop()
        # Start timer
        self.filterSearchTimer.start(300)

    def on_filterHintTimer_timeout(self):
        """Timeout to warn user about text length"""
        print("timeout")
        tab = self.tabWidget.currentIndex()
        if tab == TAB_FUNCTIONSTAT:
            label = self.filterHintTableLabel
        label.setText("Type > 2 characters to search")

    def on_filterSearchTimer_timeout(self):
        """timeout to start search"""
        tab = self.tabWidget.currentIndex()
        if tab == TAB_FUNCTIONSTAT:
            text = self.filterTableLineEdit.text()
            label = self.filterHintTableLabel
            edit = self.filterTableLineEdit
            widget = self.tableWidget
        else:
            print("Unknow tab for filterSearch timeout !")

        print(("do search for %s" % text))
        if not len(text):
            # Empty keyword, just clean all
            if self.filterHintTimer.isActive():
                self.filterHintTimer.stop()
            label.setText(" ? ")
            self.warnUSer(True, edit)
            self.clearSearch()
            return
        if len(text) < 2:
            # Don't filter if text is too short and tell it to user
            self.filterHintTimer.start(600)
            return
        else:
            if self.filterHintTimer.isActive():
                self.filterHintTimer.stop()
            label.setText(" ? ")

        # Search
        self.clearSearch()
        matchedItems = []
        if tab == TAB_FUNCTIONSTAT:
            # Find items
            matchedItems = widget.findItems(text, Qt.MatchContains)
            widget.setSortingEnabled(False)
            matchedRows = [item.row() for item in matchedItems]
            # Hide matched items
            header = widget.verticalHeader()
            for row in range(widget.rowCount()):
                if row not in matchedRows:
                    header.hideSection(row)
            widget.setSortingEnabled(True)
        else:
            print(" Unknow tab for filterSearch timeout ! ")

        print(("got %s members" % len(matchedItems)))
        self.warnUSer(matchedItems, edit)
        self.resizeWidgetToContent(widget)

    def resizeWidgetToContent(self, widget):
        """Resize all columns according to content"""
        for i in range(widget.columnCount()):
            widget.resizeColumnToContents(i)

    def clearSearch(self):
        """Clean search result
        For table, show all items
        For tree, remove colored items"""
        tab = self.tabWidget.currentIndex()
        if tab == TAB_FUNCTIONSTAT:
            header = self.tableWidget.verticalHeader()
            if header.hiddenSectionCount():
                for i in range(header.count()):
                    if header.isSectionHidden(i):
                        header.showSection(i)

    def clearContent(self):
        # Clear tabs
        self.tableWidget.clearContents()
        self.sourceTreeWidget.clear()
        # Reset LCD numbers
        for lcdNumber in (self.totalTimeLcdNumber, self.numCallLcdNumber,
                          self.primCallLcdNumber):
            lcdNumber.display(1000000)
        # Reset stat
        self.pstat = None
        # Disable save as menu
        self.actionSave_profile.setEnabled(False)
        # Mark all tabs as unloaded
        for i in range(10):
            self.tabLoaded[i] = False

    def warnUSer(self, result, inputWidget):
        palette = inputWidget.palette()
        if result:
            palette.setColor(QPalette.Normal, QPalette.Base,
                             QColor(255, 255, 255))
        else:
            palette.setColor(QPalette.Normal, QPalette.Base,
                             QColor(255, 136, 138))
        inputWidget.setPalette(palette)
        inputWidget.update()

    def setStat(self, statPath):
        self.stat = Stat(path=statPath)
        # Global stat update
        self.totalTimeLcdNumber.display(self.stat.getTotalTime())
        self.numCallLcdNumber.display(self.stat.getCallNumber())
        self.primCallLcdNumber.display(self.stat.getPrimitiveCallRatio())
        # Refresh current tab
        self.on_tabWidget_currentChanged(self.tabWidget.currentIndex())
        # Activate save as menu
        self.actionSave_profile.setEnabled(True)
        try:
            self.rating.setMaxRating(10)
            self.rating.setRating(
                int(self.stat.getPrimitiveCallRatio()) / 10 - 1)
        except:
            pass

    #========================================================================#
    # Statistics table                                                      #
    #=======================================================================#

    def populateTable(self):
        row = 0
        rowCount = self.stat.getStatNumber()
        progress = QProgressDialog("Populating statistics table...", "Abort",
                                   0, 2 * rowCount)
        self.tableWidget.setSortingEnabled(False)
        self.tableWidget.setRowCount(rowCount)

        progress.setWindowModality(Qt.WindowModal)
        for (key, value) in self.stat.getStatItems():
            #ncalls
            item = StatTableWidgetItem(str(value[0]))
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_NCALLS, item)
            colorTableItem(item, self.stat.getCallNumber(), value[0])
            #total time
            item = StatTableWidgetItem(str(value[2]))
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_TTIME, item)
            colorTableItem(item, self.stat.getTotalTime(), value[2])
            #per call (total time)
            if value[0] != 0:
                tPerCall = str(value[2] / value[0])
                cPerCall = str(value[3] / value[0])
            else:
                tPerCall = ""
                cPerCall = ""
            item = StatTableWidgetItem(tPerCall)
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_TPERCALL, item)
            colorTableItem(
                item,
                100.0 * self.stat.getTotalTime() / self.stat.getCallNumber(),
                tPerCall)
            #per call (cumulative time)
            item = StatTableWidgetItem(cPerCall)
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_CPERCALL, item)
            colorTableItem(
                item,
                100.0 * self.stat.getTotalTime() / self.stat.getCallNumber(),
                cPerCall)
            #cumulative time
            item = StatTableWidgetItem(str(value[3]))
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_CTIME, item)
            colorTableItem(item, self.stat.getTotalTime(), value[3])
            #Filename
            self.tableWidget.setItem(row, STAT_FILENAME,
                                     StatTableWidgetItem(str(key[0])))
            #Line
            item = StatTableWidgetItem(str(key[1]))
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_LINE, item)
            #Function name
            self.tableWidget.setItem(row, STAT_FUNCTION,
                                     StatTableWidgetItem(str(key[2])))
            row += 1
            # Store it in stat hash array
            self.stat.setStatLink(item, key, TAB_FUNCTIONSTAT)
            progress.setValue(row)
            if progress.wasCanceled():
                return

        for i in range(self.tableWidget.rowCount()):
            progress.setValue(row + i)
            for j in range(self.tableWidget.columnCount()):
                item = self.tableWidget.item(i, j)
                if item:
                    item.setFlags(Qt.ItemIsEnabled)

        self.tableWidget.setSortingEnabled(True)
        self.resizeWidgetToContent(self.tableWidget)
        progress.setValue(2 * rowCount)

    def on_tableWidget_itemDoubleClicked(self, item):
        matchedItems = []
        filename = str(self.tableWidget.item(item.row(), STAT_FILENAME).text())
        if not filename or filename.startswith("<"):
            # No source code associated, return immediatly
            return
        function = self.tableWidget.item(item.row(), STAT_FUNCTION).text()
        line = self.tableWidget.item(item.row(), STAT_LINE).text()

        self.on_tabWidget_currentChanged(TAB_SOURCE)  # load source tab
        function = "%s (%s)" % (function, line)
        fathers = self.sourceTreeWidget.findItems(filename, Qt.MatchContains,
                                                  SOURCE_FILENAME)
        print(("find %s father" % len(fathers)))
        for father in fathers:
            findItems(father, function, SOURCE_FILENAME, matchedItems)
        print(("find %s items" % len(matchedItems)))

        if matchedItems:
            self.tabWidget.setCurrentIndex(TAB_SOURCE)
            self.sourceTreeWidget.scrollToItem(matchedItems[0])
            self.on_sourceTreeWidget_itemClicked(matchedItems[0],
                                                 SOURCE_FILENAME)
            matchedItems[0].setSelected(True)
        else:
            print("oups, item found but cannot scroll to it !")

    #=======================================================================#
    # Source explorer                                                      #
    #=====================================================================#

    def populateSource(self):
        items = {}
        for stat in self.stat.getStatKeys():
            source = stat[0]
            function = "%s (%s)" % (stat[2], stat[1])
            if source in ("", "profile") or source.startswith("<"):
                continue
            # Create the function child
            child = QTreeWidgetItem([function])
            # Store it in stat hash array
            self.stat.setStatLink(child, stat, TAB_SOURCE)
            if source in items:
                father = items[source]
            else:
                # Create the father
                father = QTreeWidgetItem([source])
                items[source] = father
            father.addChild(child)
        self.sourceTreeWidget.setSortingEnabled(False)
        for value in list(items.values()):
            self.sourceTreeWidget.addTopLevelItem(value)
        self.sourceTreeWidget.setSortingEnabled(True)

    def on_sourceTreeWidget_itemActivated(self, item, column):
        self.on_sourceTreeWidget_itemClicked(item, column)

    def on_sourceTreeWidget_itemClicked(self, item, column):
        line = 0
        parent = item.parent()
        if QSCI:
            doc = self.sourceTextEdit
        if parent:
            pathz = parent.text(column)
            result = match("(.*) \(([0-9]+)\)", item.text(column))
            if result:
                try:
                    function = str(result.group(1))
                    line = int(result.group(2))
                except ValueError:
                    # We got garbage... falling back to line 0
                    pass
        else:
            pathz = item.text(column)
        pathz = path.abspath(str(pathz))
        if self.currentSourcePath != pathz:
            # Need to load source
            self.currentSourcePath == pathz
            try:
                if QSCI:
                    doc.clear()
                    doc.insert(file(pathz).read())
                else:
                    self.sourceTextEdit.setPlainText(file(pathz).read())
            except IOError:
                QMessageBox.warning(self, "Error",
                                    "Source file could not be found",
                                    QMessageBox.Ok)
                return

            if QSCI:
                for function, line in [(i[2], i[1])
                                       for i in self.stat.getStatKeys()
                                       if i[0] == pathz]:
                    # expr, regexp, case sensitive, whole word, wrap, forward
                    doc.findFirst("def", False, True, True, False, True, line,
                                  0, True)
                    end, foo = doc.getCursorPosition()
                    time = self.stat.getStatTotalTime((pathz, line, function))
                    colorSource(doc, self.stat.getTotalTime(), time, line, end,
                                self.marker)
        if QSCI:
            doc.ensureLineVisible(line)
コード例 #3
0
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
コード例 #4
0
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
コード例 #5
0
ファイル: DBServersWidget.py プロジェクト: pedromorgan/PyQtDb
class DBServersWidget(QWidget):
    """Displays a list of servers"""

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

        self.debug = False
        
        self.connections = {}
        
        self.setWindowTitle("Servers")
        #s#elf.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)

        
        self.mainLayout = QVBoxLayout()
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self.setLayout(self.mainLayout)

        #=============================================
        ## Top Toolbar
        topBar = QToolBar()
        topBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.mainLayout.addWidget(topBar)
        
        ## Add the action buttons
        topBar.addAction(Ico.icon(Ico.ServerAdd), "Add", self.on_server_add)
        self.actionServerEdit = topBar.addAction(Ico.icon(Ico.ServerEdit), "Edit", self.on_server_edit)
        self.actionServerDelete = topBar.addAction(Ico.icon(Ico.ServerDelete), "Delete", self.on_server_delete)
        
        #=============================================
        ## Tree
        self.tree = QTreeWidget()
        self.mainLayout.addWidget(self.tree)
        self.tree.setUniformRowHeights(True)
        self.tree.setRootIsDecorated(True)
        
        
        self.tree.setHeaderLabels(["Server", "Butt"]) # set header, but hide anyway
        self.tree.header().hide()
        self.tree.header().setResizeMode(C.node, QHeaderView.Stretch)
        self.tree.setColumnWidth(C.butt, 20)
        
        
        self.connect( self.tree, SIGNAL( 'itemSelectionChanged()' ), self.on_tree_selection_changed )
        self.connect( self.tree, SIGNAL( 'itemDoubleClicked (QTreeWidgetItem *,int)' ), self.on_tree_double_clicked )
    
        self.buttGroup = QButtonGroup(self)
        self.connect(self.buttGroup, SIGNAL("buttonClicked(QAbstractButton*)"), self.on_open_server)
    
        self.on_tree_selection_changed()
    
    
        self.load_servers()
        
    #=======================================
    ##== Tree Events
    def on_tree_selection_changed(self):
        
        disabled = self.tree.selectionModel().hasSelection() == False
        self.actionServerEdit.setDisabled(disabled)
        self.actionServerDelete.setDisabled(disabled)
        
    def on_tree_double_clicked(self):
        self.actionServerEdit.trigger()


    
    #=======================================
    ## Server Actions
    def on_server_add(self):
        self.show_server_dialog(None)
        
    def on_server_edit(self):
        item = self.tree.currentItem()
        if item == None:
            return
        server = str(item.text(C.server))
        self.show_server_dialog(server)
    
    def show_server_dialog(self, server=None):
        d = DBServerDialog.DBServerDialog(self, server)
        if d.exec_():
            self.load_servers()
    

            
    
    def load_servers(self):
        """Load servers from :py:meth:`pyqtdb.XSettings.XSettings.get_servers` """
        
        self.tree.clear()
        
        for butt in self.buttGroup.buttons():
            self.buttGroup.removeButton(butt)
        
        for srv in G.settings.get_servers_list():
            
            item = QTreeWidgetItem()
            item.setText(C.node, srv['server'])
            #item.setText(C.user, srv['user'])
            self.tree.addTopLevelItem(item)
            
            butt = QToolButton()
            butt.setIcon(Ico.icon(Ico.Connect))
            butt.setProperty("server", srv['server'])
            self.tree.setItemWidget(item, C.butt, butt)
            self.buttGroup.addButton(butt)
        
        
    def on_server_delete(self):
        item = self.tree.currentItem()
        if item == None:
            return
        srv = str(item.text(C.server))
        G.settings.delete_server(srv)
        self.load_servers()
        
        
    def on_open_server(self, butt):
        
        # self.emit(SIGNAL("open_server"), butt.property("server").toString())
        srv_ki = str(butt.property("server").toString())
        server = G.settings.get_server(srv_ki)
        db = QSqlDatabase.addDatabase("QMYSQL", srv_ki)
        db.setHostName(server['server'])
        db.setUserName(server['user'])
        db.setPassword(server['passwd'])
        
        ok = db.open()
        if ok:
            #self.connections[srv_ki] = 
            self.load_databases(srv_ki)
            print "open", ok
            
            
    def load_databases(self, srv_ki):
        """Load databases into tree node for server;  executes 'show databases;' or aslike """
        
        sql = "show databases;"
        query = QSqlQuery(QSqlDatabase.database(srv_ki))
        ok = query.exec_(sql)
        print ok, sql, query.result()
        
        # Get the parent node, ie the server node
        pItem = self.tree.findItems(srv_ki, Qt.MatchExactly, C.node)[0]
        
        ## Assumed value(0) is the table.. we need the defs (ie mysql case)
        while query.next():
            table_name =  query.value(0).toString()
            nuItem = QTreeWidgetItem(pItem)
            nuItem.setText(C.node, table_name)
            #print table_name
            
            
        self.tree.setItemExpanded(pItem, True)
        
        
        
            
コード例 #6
0
ファイル: main.py プロジェクト: juancarlospaco/profiler
class Main(plugin.Plugin):
    " Main Class "
    def initialize(self, *args, **kwargs):
        " Init Main Class "
        super(Main, self).initialize(*args, **kwargs)
        self.scriptPath, self.scriptArgs = "", []
        self.profilerPath, self.tempPath = profilerPath, tempPath
        self.output = " ERROR: FAIL: No output ! "

        self.process = QProcess()
        self.process.finished.connect(self.on_process_finished)
        self.process.error.connect(self.on_process_error)

        self.tabWidget, self.stat = QTabWidget(), QWidget()
        self.tabWidget.tabCloseRequested.connect(lambda:
            self.tabWidget.setTabPosition(1)
            if self.tabWidget.tabPosition() == 0
            else self.tabWidget.setTabPosition(0))
        self.tabWidget.setStyleSheet('QTabBar{font-weight:bold;}')
        self.tabWidget.setMovable(True)
        self.tabWidget.setTabsClosable(True)
        self.vboxlayout1 = QVBoxLayout(self.stat)
        self.hboxlayout1 = QHBoxLayout()
        self.filterTableLabel = QLabel("<b>Type to Search : </b>", self.stat)
        self.hboxlayout1.addWidget(self.filterTableLabel)
        self.filterTableLineEdit = QLineEdit(self.stat)
        self.filterTableLineEdit.setPlaceholderText(' Type to Search . . . ')
        self.hboxlayout1.addWidget(self.filterTableLineEdit)
        self.filterHintTableLabel = QLabel(" ? ", self.stat)
        self.hboxlayout1.addWidget(self.filterHintTableLabel)
        self.vboxlayout1.addLayout(self.hboxlayout1)
        self.tableWidget = QTableWidget(self.stat)
        self.tableWidget.setAlternatingRowColors(True)
        self.tableWidget.setColumnCount(8)
        self.tableWidget.setRowCount(2)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(3, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(4, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(5, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(6, item)
        item = QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(7, item)
        self.tableWidget.itemDoubleClicked.connect(
                                        self.on_tableWidget_itemDoubleClicked)
        self.vboxlayout1.addWidget(self.tableWidget)
        self.tabWidget.addTab(self.stat, " ? ")

        self.source = QWidget()
        self.gridlayout = QGridLayout(self.source)
        self.scintillaWarningLabel = QLabel(
            "QScintilla is not installed!. Falling back to basic text edit!.",
            self.source)
        self.gridlayout.addWidget(self.scintillaWarningLabel, 1, 0, 1, 2)
        self.sourceTreeWidget = QTreeWidget(self.source)
        self.sourceTreeWidget.setAlternatingRowColors(True)
        self.sourceTreeWidget.itemActivated.connect(
                                        self.on_sourceTreeWidget_itemActivated)
        self.sourceTreeWidget.itemClicked.connect(
                                          self.on_sourceTreeWidget_itemClicked)
        self.sourceTreeWidget.itemDoubleClicked.connect(
                                          self.on_sourceTreeWidget_itemClicked)

        self.gridlayout.addWidget(self.sourceTreeWidget, 0, 0, 1, 1)
        self.sourceTextEdit = QTextEdit(self.source)
        self.sourceTextEdit.setReadOnly(True)
        self.gridlayout.addWidget(self.sourceTextEdit, 0, 1, 1, 1)
        self.tabWidget.addTab(self.source, " ? ")

        self.result = QWidget()
        self.vlayout = QVBoxLayout(self.result)
        self.globalStatGroupBox = QGroupBox(self.result)
        self.hboxlayout = QHBoxLayout(self.globalStatGroupBox)
        self.totalTimeLcdNumber = QLCDNumber(self.globalStatGroupBox)
        self.totalTimeLcdNumber.setSegmentStyle(QLCDNumber.Filled)
        self.totalTimeLcdNumber.setNumDigits(7)
        self.totalTimeLcdNumber.display(1000000)
        self.totalTimeLcdNumber.setFrameShape(QFrame.StyledPanel)
        self.totalTimeLcdNumber.setSizePolicy(QSizePolicy.Expanding,
                                              QSizePolicy.Expanding)
        self.hboxlayout.addWidget(self.totalTimeLcdNumber)
        self.tTimeLabel = QLabel("<b>Total Time (Sec)</b>",
                                 self.globalStatGroupBox)
        self.tTimeLabel.setSizePolicy(QSizePolicy.Minimum,
                                      QSizePolicy.Minimum)
        self.hboxlayout.addWidget(self.tTimeLabel)
        self.numCallLcdNumber = QLCDNumber(self.globalStatGroupBox)
        self.numCallLcdNumber.setNumDigits(7)
        self.numCallLcdNumber.display(1000000)
        self.numCallLcdNumber.setSegmentStyle(QLCDNumber.Filled)
        self.numCallLcdNumber.setFrameShape(QFrame.StyledPanel)
        self.numCallLcdNumber.setSizePolicy(QSizePolicy.Expanding,
                                            QSizePolicy.Expanding)
        self.hboxlayout.addWidget(self.numCallLcdNumber)
        self.numCallLabel = QLabel("<b>Number of calls</b>",
                                   self.globalStatGroupBox)
        self.numCallLabel.setSizePolicy(QSizePolicy.Minimum,
                                        QSizePolicy.Minimum)
        self.hboxlayout.addWidget(self.numCallLabel)
        self.primCallLcdNumber = QLCDNumber(self.globalStatGroupBox)
        self.primCallLcdNumber.setSegmentStyle(QLCDNumber.Filled)
        self.primCallLcdNumber.setFrameShape(QFrame.StyledPanel)
        self.primCallLcdNumber.setNumDigits(7)
        self.primCallLcdNumber.display(1000000)
        self.primCallLcdNumber.setSizePolicy(QSizePolicy.Expanding,
                                             QSizePolicy.Expanding)
        self.hboxlayout.addWidget(self.primCallLcdNumber)
        self.primCallLabel = QLabel("<b>Primitive calls (%)</b>",
                                    self.globalStatGroupBox)
        self.primCallLabel.setSizePolicy(QSizePolicy.Minimum,
                                         QSizePolicy.Minimum)
        self.hboxlayout.addWidget(self.primCallLabel)
        self.vlayout.addWidget(self.globalStatGroupBox)
        try:
            from PyKDE4.kdeui import KRatingWidget
            self.rating = KRatingWidget(self.globalStatGroupBox)
            self.rating.setToolTip('Profiling Performance Rating')
        except ImportError:
            pass
        self.tabWidget.addTab(self.result, " Get Results ! ")

        self.resgraph = QWidget()
        self.vlayout2 = QVBoxLayout(self.result)
        self.graphz = QGroupBox(self.resgraph)
        self.hboxlayout2 = QHBoxLayout(self.graphz)
        try:
            from PyKDE4.kdeui import KLed
            KLed(self.graphz)
        except ImportError:
            pass
        self.hboxlayout2.addWidget(QLabel('''
            Work in Progress  :)  Not Ready Yet'''))
        self.vlayout2.addWidget(self.graphz)
        self.tabWidget.addTab(self.resgraph, " Graphs and Charts ")

        self.pathz = QWidget()
        self.vlayout3 = QVBoxLayout(self.pathz)
        self.patz = QGroupBox(self.pathz)
        self.hboxlayout3 = QVBoxLayout(self.patz)
        self.profilepath = QLineEdit(profilerPath)
        self.getprofile = QPushButton(QIcon.fromTheme("document-open"), 'Open')
        self.getprofile.setToolTip('Dont touch if you dont know what are doing')
        self.getprofile.clicked.connect(lambda: self.profilepath.setText(str(
            QFileDialog.getOpenFileName(self.patz, ' Open the profile.py file ',
            path.expanduser("~"), ';;(profile.py)'))))
        self.hboxlayout3.addWidget(QLabel(
            '<center><b>Profile.py Python Library Full Path:</b></center>'))
        self.hboxlayout3.addWidget(self.profilepath)
        self.hboxlayout3.addWidget(self.getprofile)

        self.argGroupBox = QGroupBox(self.pathz)
        self.hbxlayout = QHBoxLayout(self.argGroupBox)
        self.argLineEdit = QLineEdit(self.argGroupBox)
        self.argLineEdit.setToolTip('Not touch if you dont know what are doing')
        self.argLineEdit.setPlaceholderText(
            'Dont touch if you dont know what are doing')
        self.hbxlayout.addWidget(QLabel('<b>Additional Profile Arguments:</b>'))
        self.hbxlayout.addWidget(self.argLineEdit)
        self.hboxlayout3.addWidget(self.argGroupBox)

        self.vlayout3.addWidget(self.patz)
        self.tabWidget.addTab(self.pathz, " Paths and Configs ")

        self.outp = QWidget()
        self.vlayout4 = QVBoxLayout(self.outp)
        self.outgro = QGroupBox(self.outp)
        self.outgro.setTitle(" MultiProcessing Output Logs ")
        self.hboxlayout4 = QVBoxLayout(self.outgro)
        self.outputlog = QTextEdit()
        self.outputlog.setText('''
        I do not fear computers, I fear the lack of them.   -Isaac Asimov ''')
        self.hboxlayout4.addWidget(self.outputlog)
        self.vlayout4.addWidget(self.outgro)
        self.tabWidget.addTab(self.outp, " Logs ")

        self.actionNew_profiling = QAction(QIcon.fromTheme("document-new"),
                                           'New Profiling', self)
        self.actionLoad_profile = QAction(QIcon.fromTheme("document-open"),
                                          'Open Profiling', self)
        self.actionClean = QAction(QIcon.fromTheme("edit-clear"), 'Clean', self)
        self.actionClean.triggered.connect(lambda: self.clearContent)
        self.actionAbout = QAction(QIcon.fromTheme("help-about"), 'About', self)
        self.actionAbout.triggered.connect(lambda: QMessageBox.about(self.dock,
            __doc__, ', '.join((__doc__, __license__, __author__, __email__))))
        self.actionSave_profile = QAction(QIcon.fromTheme("document-save"),
                                          'Save Profiling', self)
        self.actionManual = QAction(QIcon.fromTheme("help-contents"),
                                    'Help', self)
        self.actionManual.triggered.connect(lambda:
                    open_new_tab('http://docs.python.org/library/profile.html'))

        self.tabWidget.setCurrentIndex(2)

        self.globalStatGroupBox.setTitle("Global Statistics")
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText("Number of Calls")
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText("Total Time")
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText("Per Call")
        item = self.tableWidget.horizontalHeaderItem(3)
        item.setText("Cumulative Time")
        item = self.tableWidget.horizontalHeaderItem(4)
        item.setText("Per Call")
        item = self.tableWidget.horizontalHeaderItem(5)
        item.setText("Filename")
        item = self.tableWidget.horizontalHeaderItem(6)
        item.setText("Line")
        item = self.tableWidget.horizontalHeaderItem(7)
        item.setText("Function")
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.stat),
                                  "Statistics per Function")

        self.sourceTreeWidget.headerItem().setText(0, "Source files")
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.source),
                                  "Sources Navigator")
        #######################################################################

        self.scrollable, self.dock = QScrollArea(), QDockWidget()
        self.scrollable.setWidgetResizable(True)
        self.scrollable.setWidget(self.tabWidget)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.dock.setWidget(self.scrollable)
        QToolBar(self.dock).addActions((self.actionNew_profiling,
            self.actionClean, self.actionSave_profile, self.actionLoad_profile,
            self.actionManual, self.actionAbout))

        self.actionNew_profiling.triggered.connect(
                                        self.on_actionNew_profiling_triggered)
        self.actionLoad_profile.triggered.connect(
                                        self.on_actionLoad_profile_triggered)
        self.actionSave_profile.triggered.connect(
                                        self.on_actionSave_profile_triggered)

        self.locator.get_service('misc').add_widget(self.dock,
                            QIcon.fromTheme("document-open-recent"), __doc__)

        if QSCI:
            # Scintilla source editor management
            self.scintillaWarningLabel.setText(' QScintilla is Ready ! ')
            layout = self.source.layout()
            layout.removeWidget(self.sourceTextEdit)
            self.sourceTextEdit = Qsci.QsciScintilla(self.source)
            layout.addWidget(self.sourceTextEdit, 0, 1)
            doc = self.sourceTextEdit
            doc.setLexer(Qsci.QsciLexerPython(self.sourceTextEdit))
            doc.setReadOnly(True)
            doc.setEdgeMode(Qsci.QsciScintilla.EdgeLine)
            doc.setEdgeColumn(80)
            doc.setEdgeColor(QColor("#FF0000"))
            doc.setFolding(Qsci.QsciScintilla.BoxedTreeFoldStyle)
            doc.setBraceMatching(Qsci.QsciScintilla.SloppyBraceMatch)
            doc.setCaretLineVisible(True)
            doc.setMarginLineNumbers(1, True)
            doc.setMarginWidth(1, 25)
            doc.setTabWidth(4)
            doc.setEolMode(Qsci.QsciScintilla.EolUnix)
            self.marker = {}
            for color in COLORS:
                mnr = doc.markerDefine(Qsci.QsciScintilla.Background)
                doc.setMarkerBackgroundColor(color, mnr)
                self.marker[color] = mnr
        self.currentSourcePath = None

        # Connect table and tree filter edit signal to unique slot
        self.filterTableLineEdit.textEdited.connect(
                                            self.on_filterLineEdit_textEdited)

        # Timer to display filter hint message
        self.filterHintTimer = QTimer(self)
        self.filterHintTimer.setSingleShot(True)
        self.filterHintTimer.timeout.connect(self.on_filterHintTimer_timeout)

        # Timer to start search
        self.filterSearchTimer = QTimer(self)
        self.filterSearchTimer.setSingleShot(True)
        self.filterSearchTimer.timeout.connect(
                                            self.on_filterSearchTimer_timeout)

        self.tabLoaded = {}
        for i in range(10):
            self.tabLoaded[i] = False
        self.backgroundTreeMatchedItems = {}
        self.resizeWidgetToContent(self.tableWidget)

    def on_actionNew_profiling_triggered(self):
        self.clearContent()
        self.scriptPath = str(QFileDialog.getOpenFileName(self.dock,
            "Choose your script to profile", path.expanduser("~"),
            "Python (*.py *.pyw)"))
        commandLine = [self.profilerPath, "-o", self.tempPath,
                       self.scriptPath] + self.scriptArgs
        commandLine = " ".join(commandLine)
        ##if self.termCheckBox.checkState() == Qt.Checked:
        #termList = ["xterm", "aterm"]
        #for term in termList:
            #termPath = which(term)
            #if termPath:
                #break
        #commandLine = """%s -e "%s ; echo 'Press ENTER Exit' ; read" """ \
                      #% (termPath, commandLine)
        self.process.start(commandLine)
        if not self.process.waitForStarted():
            print((" ERROR: {} failed!".format(commandLine)))
            return

    def on_process_finished(self, exitStatus):
        ' whan the process end '
        print((" INFO: OK: QProcess is %s" % self.process.exitCode()))
        self.output = self.process.readAll().data()
        if not self.output:
            self.output = " ERROR: FAIL: No output ! "
        self.outputlog.setText(self.output + str(self.process.exitCode()))
        if path.exists(self.tempPath):
            self.setStat(self.tempPath)
            remove(self.tempPath)
        else:
            self.outputlog.setText(" ERROR: QProcess FAIL: Profiling failed.")
        self.tabWidget.setCurrentIndex(2)

    def on_process_error(self, error):
        ' when the process fail, I hope you never see this '
        print(" ERROR: QProcess FAIL: Profiler Dead, wheres your God now ? ")
        if error == QProcess.FailedToStart:
            self.outputlog.setText(" ERROR: FAIL: Profiler execution failed ")
        elif error == QProcess.Crashed:
            self.outputlog.setText(" ERROR: FAIL: Profiler execution crashed ")
        else:
            self.outputlog.setText(" ERROR: FAIL: Profiler unknown error ")

    def on_actionLoad_profile_triggered(self):
        """Load a previous profile sessions"""
        statPath = str(QFileDialog.getOpenFileName(self.dock,
            "Open profile dump", path.expanduser("~"), "Profile file (*)"))
        if statPath:
            self.clearContent()
            print(' INFO: OK: Loading profiling from ' + statPath)
            self.setStat(statPath)

    def on_actionSave_profile_triggered(self):
        """Save a profile sessions"""
        statPath = str(QFileDialog.getSaveFileName(self.dock,
                "Save profile dump", path.expanduser("~"), "Profile file (*)"))
        if statPath:
            #TODO: handle error case and give feelback to user
            print(' INFO: OK: Saving profiling to ' + statPath)
            self.stat.save(statPath)

    #=======================================================================#
    # Common parts                                                          #
    #=======================================================================#

    def on_tabWidget_currentChanged(self, index):
        """slot for tab change"""
        # Kill search and hint timer if running to avoid cross effect
        for timer in (self.filterHintTimer, self.filterSearchTimer):
            if timer.isActive():
                timer.stop()
        if not self.stat:
            #No stat loaded, nothing to do
            return
        self.populateTable()
        self.populateSource()

    def on_filterLineEdit_textEdited(self, text):
        """slot for filter change (table or tree"""
        if self.filterSearchTimer.isActive():
            # Already runnning, stop it
            self.filterSearchTimer.stop()
        # Start timer
        self.filterSearchTimer.start(300)

    def on_filterHintTimer_timeout(self):
        """Timeout to warn user about text length"""
        print("timeout")
        tab = self.tabWidget.currentIndex()
        if tab == TAB_FUNCTIONSTAT:
            label = self.filterHintTableLabel
        label.setText("Type > 2 characters to search")

    def on_filterSearchTimer_timeout(self):
        """timeout to start search"""
        tab = self.tabWidget.currentIndex()
        if tab == TAB_FUNCTIONSTAT:
            text = self.filterTableLineEdit.text()
            label = self.filterHintTableLabel
            edit = self.filterTableLineEdit
            widget = self.tableWidget
        else:
            print("Unknow tab for filterSearch timeout !")

        print(("do search for %s" % text))
        if not len(text):
            # Empty keyword, just clean all
            if self.filterHintTimer.isActive():
                self.filterHintTimer.stop()
            label.setText(" ? ")
            self.warnUSer(True, edit)
            self.clearSearch()
            return
        if len(text) < 2:
            # Don't filter if text is too short and tell it to user
            self.filterHintTimer.start(600)
            return
        else:
            if self.filterHintTimer.isActive():
                self.filterHintTimer.stop()
            label.setText(" ? ")

        # Search
        self.clearSearch()
        matchedItems = []
        if tab == TAB_FUNCTIONSTAT:
            # Find items
            matchedItems = widget.findItems(text, Qt.MatchContains)
            widget.setSortingEnabled(False)
            matchedRows = [item.row() for item in matchedItems]
            # Hide matched items
            header = widget.verticalHeader()
            for row in range(widget.rowCount()):
                if row not in matchedRows:
                    header.hideSection(row)
            widget.setSortingEnabled(True)
        else:
            print(" Unknow tab for filterSearch timeout ! ")

        print(("got %s members" % len(matchedItems)))
        self.warnUSer(matchedItems, edit)
        self.resizeWidgetToContent(widget)

    def resizeWidgetToContent(self, widget):
        """Resize all columns according to content"""
        for i in range(widget.columnCount()):
            widget.resizeColumnToContents(i)

    def clearSearch(self):
        """Clean search result
        For table, show all items
        For tree, remove colored items"""
        tab = self.tabWidget.currentIndex()
        if tab == TAB_FUNCTIONSTAT:
            header = self.tableWidget.verticalHeader()
            if header.hiddenSectionCount():
                for i in range(header.count()):
                    if header.isSectionHidden(i):
                        header.showSection(i)

    def clearContent(self):
        # Clear tabs
        self.tableWidget.clearContents()
        self.sourceTreeWidget.clear()
        # Reset LCD numbers
        for lcdNumber in (self.totalTimeLcdNumber, self.numCallLcdNumber,
                          self.primCallLcdNumber):
            lcdNumber.display(1000000)
        # Reset stat
        self.pstat = None
        # Disable save as menu
        self.actionSave_profile.setEnabled(False)
        # Mark all tabs as unloaded
        for i in range(10):
            self.tabLoaded[i] = False

    def warnUSer(self, result, inputWidget):
        palette = inputWidget.palette()
        if result:
            palette.setColor(QPalette.Normal, QPalette.Base,
                             QColor(255, 255, 255))
        else:
            palette.setColor(QPalette.Normal, QPalette.Base,
                             QColor(255, 136, 138))
        inputWidget.setPalette(palette)
        inputWidget.update()

    def setStat(self, statPath):
        self.stat = Stat(path=statPath)
        # Global stat update
        self.totalTimeLcdNumber.display(self.stat.getTotalTime())
        self.numCallLcdNumber.display(self.stat.getCallNumber())
        self.primCallLcdNumber.display(self.stat.getPrimitiveCallRatio())
        # Refresh current tab
        self.on_tabWidget_currentChanged(self.tabWidget.currentIndex())
        # Activate save as menu
        self.actionSave_profile.setEnabled(True)
        try:
            self.rating.setMaxRating(10)
            self.rating.setRating(
                                int(self.stat.getPrimitiveCallRatio()) / 10 - 1)
        except:
            pass

    #========================================================================#
    # Statistics table                                                      #
    #=======================================================================#

    def populateTable(self):
        row = 0
        rowCount = self.stat.getStatNumber()
        progress = QProgressDialog("Populating statistics table...",
                                         "Abort", 0, 2 * rowCount)
        self.tableWidget.setSortingEnabled(False)
        self.tableWidget.setRowCount(rowCount)

        progress.setWindowModality(Qt.WindowModal)
        for (key, value) in self.stat.getStatItems():
            #ncalls
            item = StatTableWidgetItem(str(value[0]))
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_NCALLS, item)
            colorTableItem(item, self.stat.getCallNumber(), value[0])
            #total time
            item = StatTableWidgetItem(str(value[2]))
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_TTIME, item)
            colorTableItem(item, self.stat.getTotalTime(), value[2])
            #per call (total time)
            if value[0] != 0:
                tPerCall = str(value[2] / value[0])
                cPerCall = str(value[3] / value[0])
            else:
                tPerCall = ""
                cPerCall = ""
            item = StatTableWidgetItem(tPerCall)
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_TPERCALL, item)
            colorTableItem(item, 100.0 * self.stat.getTotalTime() /
                           self.stat.getCallNumber(), tPerCall)
            #per call (cumulative time)
            item = StatTableWidgetItem(cPerCall)
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_CPERCALL, item)
            colorTableItem(item, 100.0 * self.stat.getTotalTime() /
                           self.stat.getCallNumber(), cPerCall)
            #cumulative time
            item = StatTableWidgetItem(str(value[3]))
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_CTIME, item)
            colorTableItem(item, self.stat.getTotalTime(), value[3])
            #Filename
            self.tableWidget.setItem(row, STAT_FILENAME,
                                        StatTableWidgetItem(str(key[0])))
            #Line
            item = StatTableWidgetItem(str(key[1]))
            item.setTextAlignment(Qt.AlignRight)
            self.tableWidget.setItem(row, STAT_LINE, item)
            #Function name
            self.tableWidget.setItem(row, STAT_FUNCTION,
                                        StatTableWidgetItem(str(key[2])))
            row += 1
            # Store it in stat hash array
            self.stat.setStatLink(item, key, TAB_FUNCTIONSTAT)
            progress.setValue(row)
            if progress.wasCanceled():
                return

        for i in range(self.tableWidget.rowCount()):
            progress.setValue(row + i)
            for j in range(self.tableWidget.columnCount()):
                item = self.tableWidget.item(i, j)
                if item:
                    item.setFlags(Qt.ItemIsEnabled)

        self.tableWidget.setSortingEnabled(True)
        self.resizeWidgetToContent(self.tableWidget)
        progress.setValue(2 * rowCount)

    def on_tableWidget_itemDoubleClicked(self, item):
        matchedItems = []
        filename = str(self.tableWidget.item(item.row(), STAT_FILENAME).text())
        if not filename or filename.startswith("<"):
            # No source code associated, return immediatly
            return
        function = self.tableWidget.item(item.row(), STAT_FUNCTION).text()
        line = self.tableWidget.item(item.row(), STAT_LINE).text()

        self.on_tabWidget_currentChanged(TAB_SOURCE)  # load source tab
        function = "%s (%s)" % (function, line)
        fathers = self.sourceTreeWidget.findItems(filename, Qt.MatchContains,
                                                  SOURCE_FILENAME)
        print(("find %s father" % len(fathers)))
        for father in fathers:
            findItems(father, function, SOURCE_FILENAME, matchedItems)
        print(("find %s items" % len(matchedItems)))

        if matchedItems:
            self.tabWidget.setCurrentIndex(TAB_SOURCE)
            self.sourceTreeWidget.scrollToItem(matchedItems[0])
            self.on_sourceTreeWidget_itemClicked(matchedItems[0],
                                                 SOURCE_FILENAME)
            matchedItems[0].setSelected(True)
        else:
            print("oups, item found but cannot scroll to it !")

    #=======================================================================#
    # Source explorer                                                      #
    #=====================================================================#

    def populateSource(self):
        items = {}
        for stat in self.stat.getStatKeys():
            source = stat[0]
            function = "%s (%s)" % (stat[2], stat[1])
            if source in ("", "profile") or source.startswith("<"):
                continue
            # Create the function child
            child = QTreeWidgetItem([function])
            # Store it in stat hash array
            self.stat.setStatLink(child, stat, TAB_SOURCE)
            if source in items:
                father = items[source]
            else:
                # Create the father
                father = QTreeWidgetItem([source])
                items[source] = father
            father.addChild(child)
        self.sourceTreeWidget.setSortingEnabled(False)
        for value in list(items.values()):
            self.sourceTreeWidget.addTopLevelItem(value)
        self.sourceTreeWidget.setSortingEnabled(True)

    def on_sourceTreeWidget_itemActivated(self, item, column):
        self.on_sourceTreeWidget_itemClicked(item, column)

    def on_sourceTreeWidget_itemClicked(self, item, column):
        line = 0
        parent = item.parent()
        if QSCI:
            doc = self.sourceTextEdit
        if parent:
            pathz = parent.text(column)
            result = match("(.*) \(([0-9]+)\)", item.text(column))
            if result:
                try:
                    function = str(result.group(1))
                    line = int(result.group(2))
                except ValueError:
                    # We got garbage... falling back to line 0
                    pass
        else:
            pathz = item.text(column)
        pathz = path.abspath(str(pathz))
        if self.currentSourcePath != pathz:
            # Need to load source
            self.currentSourcePath == pathz
            try:
                if QSCI:
                    doc.clear()
                    doc.insert(file(pathz).read())
                else:
                    self.sourceTextEdit.setPlainText(file(pathz).read())
            except IOError:
                QMessageBox.warning(self,
                                     "Error", "Source file could not be found",
                                     QMessageBox.Ok)
                return

            if QSCI:
                for function, line in [(i[2], i[1]
                           ) for i in self.stat.getStatKeys() if i[0] == pathz]:
                    # expr, regexp, case sensitive, whole word, wrap, forward
                    doc.findFirst("def", False, True, True, False, True, line,
                                  0, True)
                    end, foo = doc.getCursorPosition()
                    time = self.stat.getStatTotalTime((pathz, line, function))
                    colorSource(doc, self.stat.getTotalTime(), time, line, end,
                                self.marker)
        if QSCI:
            doc.ensureLineVisible(line)