Beispiel #1
0
class DiscoveryDialog(QDialog):
    def __init__(self, parent=None):
        super(DiscoveryDialog, self).__init__(parent)
        self.setModal(True)
        self.setMinimumSize(200,200) # To prevent Geometry error
        self.hosts = []
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.serverview = QTreeWidget()
        self.serverview.setHeaderLabels(['Server', 'Ports'])
        self.serverview.setIndentation(0)
        self.serverview.setStyleSheet('padding:0px')
        self.serverview.header().resizeSection(0, 180)
        layout.addWidget(self.serverview)
        btns = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        layout.addWidget(btns)
        btns.accepted.connect(self.on_accept)
        btns.rejected.connect(parent.closeEvent)

        bs.net.server_discovered.connect(self.add_srv)

    def add_srv(self, address, ports):
        for host in self.hosts:
            if address == host.address and ports == host.ports:
                # We already know this server, skip
                return
        host = QTreeWidgetItem(self.serverview)
        host.address = address
        host.ports = ports
        host.hostname = 'This computer' if address == get_ownip() else address
        host.setText(0, host.hostname)

        host.setText(1, '{},{}'.format(*ports))
        self.hosts.append(host)

    def on_accept(self):
        host = self.serverview.currentItem()
        if host:
            bs.net.stop_discovery()
            hostname = host.address
            eport, sport = host.ports
            bs.net.connect(hostname=hostname, event_port=eport, stream_port=sport)
            self.close()
Beispiel #2
0
class FontsColors(preferences.Page):
    def __init__(self, dialog):
        super(FontsColors, self).__init__(dialog)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)
        
        self.scheme = SchemeSelector(self)
        layout.addWidget(self.scheme)
        
        self.printScheme = QCheckBox()
        layout.addWidget(self.printScheme)
        
        hbox = QHBoxLayout()
        self.tree = QTreeWidget(self)
        self.tree.setHeaderHidden(True)
        self.tree.setAnimated(True)
        self.stack = QStackedWidget(self)
        hbox.addWidget(self.tree)
        hbox.addWidget(self.stack)
        layout.addLayout(hbox)
        
        hbox = QHBoxLayout()
        self.fontLabel = QLabel()
        self.fontChooser = QFontComboBox()
        self.fontSize = QDoubleSpinBox()
        self.fontSize.setRange(6.0, 32.0)
        self.fontSize.setSingleStep(0.5)
        self.fontSize.setDecimals(1)
        hbox.addWidget(self.fontLabel)
        hbox.addWidget(self.fontChooser, 1)
        hbox.addWidget(self.fontSize)
        layout.addLayout(hbox)
        
        # add the items to our list
        self.baseColorsItem = i = QTreeWidgetItem()
        self.tree.addTopLevelItem(i)
        self.defaultStylesItem = i = QTreeWidgetItem()
        self.tree.addTopLevelItem(i)
        
        self.defaultStyles = {}
        for name in textformats.defaultStyles:
            self.defaultStyles[name] = i = QTreeWidgetItem()
            self.defaultStylesItem.addChild(i)
            i.name = name
        self.defaultStylesItem.setExpanded(True)
        
        self.allStyles = {}
        for group, styles in ly.colorize.default_mapping():
            i = QTreeWidgetItem()
            children = {}
            self.allStyles[group] = (i, children)
            self.tree.addTopLevelItem(i)
            i.group = group
            for name, base, clss in styles:
                j = QTreeWidgetItem()
                j.name = name
                j.base = base
                i.addChild(j)
                children[name] = j
        
        self.baseColorsWidget = BaseColors(self)
        self.customAttributesWidget = CustomAttributes(self)
        self.emptyWidget = QWidget(self)
        self.stack.addWidget(self.baseColorsWidget)
        self.stack.addWidget(self.customAttributesWidget)
        self.stack.addWidget(self.emptyWidget)
        
        self.tree.currentItemChanged.connect(self.currentItemChanged)
        self.tree.setCurrentItem(self.baseColorsItem)
        self.scheme.currentChanged.connect(self.currentSchemeChanged)
        self.scheme.changed.connect(self.changed)
        self.baseColorsWidget.changed.connect(self.baseColorsChanged)
        self.customAttributesWidget.changed.connect(self.customAttributesChanged)
        self.fontChooser.currentFontChanged.connect(self.fontChanged)
        self.fontSize.valueChanged.connect(self.fontChanged)
        self.printScheme.clicked.connect(self.printSchemeChanged)
        
        app.translateUI(self)
        
    def translateUI(self):
        self.printScheme.setText(_("Use this scheme for printing"))
        self.fontLabel.setText(_("Font:"))
        self.baseColorsItem.setText(0, _("Base Colors"))
        self.defaultStylesItem.setText(0, _("Default Styles"))
        
        self.defaultStyleNames = defaultStyleNames()
        self.allStyleNames = allStyleNames()
        
        for name in textformats.defaultStyles:
            self.defaultStyles[name].setText(0, self.defaultStyleNames[name])
        for group, styles in ly.colorize.default_mapping():
            try:
                n = self.allStyleNames[group][0]
            except KeyError:
                n = group
            self.allStyles[group][0].setText(0, n)
            for name, base, clss in styles:
                try:
                    n = self.allStyleNames[group][1][name]
                except KeyError:
                    n = name
                self.allStyles[group][1][name].setText(0, n)
            
    def currentItemChanged(self, item, previous):
        if item is self.baseColorsItem:
            self.stack.setCurrentWidget(self.baseColorsWidget)
        elif not item.parent():
            self.stack.setCurrentWidget(self.emptyWidget)
        else:
            data = self.data[self.scheme.currentScheme()]
            w = self.customAttributesWidget
            self.stack.setCurrentWidget(w)
            toptext = None
            if item.parent() is self.defaultStylesItem:
                # default style
                w.setTitle(item.text(0))
                w.setTristate(False)
                w.setTextFormat(data.defaultStyles[item.name])
            else:
                # specific style of specific group
                group, name = item.parent().group, item.name
                w.setTitle("{0}: {1}".format(item.parent().text(0), item.text(0)))
                inherit = item.base
                if inherit:
                    toptext = _("(Inherits: {name})").format(name=self.defaultStyleNames[inherit])
                w.setTristate(bool(inherit))
                w.setTextFormat(data.allStyles[group][name])
            w.setTopText(toptext)
    
    def currentSchemeChanged(self):
        scheme = self.scheme.currentScheme()
        if scheme not in self.data:
            self.data[scheme] = textformats.TextFormatData(scheme)
        self.updateDisplay()
        if self.tree.currentItem():
            self.currentItemChanged(self.tree.currentItem(), None)
        with qutil.signalsBlocked(self.printScheme):
            self.printScheme.setChecked(scheme == self._printScheme)
    
    def fontChanged(self):
        data = self.data[self.scheme.currentScheme()]
        data.font = self.fontChooser.currentFont()
        data.font.setPointSizeF(self.fontSize.value())
        self.updateDisplay()
        self.changed.emit()
    
    def printSchemeChanged(self):
        if self.printScheme.isChecked():
            self._printScheme = self.scheme.currentScheme()
        else:
            self._printScheme = None
        self.changed.emit()
    
    def addSchemeData(self, scheme, tfd):
        self.data[scheme] = tfd
        
    def currentSchemeData(self):
        return self.data[self.scheme.currentScheme()]
        
    def updateDisplay(self):
        data = self.data[self.scheme.currentScheme()]
        
        with qutil.signalsBlocked(self.fontChooser, self.fontSize):
            self.fontChooser.setCurrentFont(data.font)
            self.fontSize.setValue(data.font.pointSizeF())
        
        with qutil.signalsBlocked(self):
            # update base colors
            for name in textformats.baseColors:
                self.baseColorsWidget.color[name].setColor(data.baseColors[name])
        
        # update base colors for whole treewidget
        p = QApplication.palette()
        p.setColor(QPalette.Base, data.baseColors['background'])
        p.setColor(QPalette.Text, data.baseColors['text'])
        p.setColor(QPalette.Highlight, data.baseColors['selectionbackground'])
        p.setColor(QPalette.HighlightedText, data.baseColors['selectiontext'])
        self.tree.setPalette(p)
        
        def setItemTextFormat(item, f):
            font = QFont(data.font)
            if f.hasProperty(QTextFormat.ForegroundBrush):
                item.setForeground(0, f.foreground().color())
            else:
                item.setForeground(0, data.baseColors['text'])
            if f.hasProperty(QTextFormat.BackgroundBrush):
                item.setBackground(0, f.background().color())
            else:
                item.setBackground(0, QBrush())
            font.setWeight(f.fontWeight())
            font.setItalic(f.fontItalic())
            font.setUnderline(f.fontUnderline())
            item.setFont(0, font)
            
        # update looks of default styles
        for name in textformats.defaultStyles:
            setItemTextFormat(self.defaultStyles[name], data.defaultStyles[name])
        
        # update looks of all the specific styles
        for group, styles in ly.colorize.default_mapping():
            children = self.allStyles[group][1]
            for name, inherit, clss in styles:
                f = QTextCharFormat(data.defaultStyles[inherit]) if inherit else QTextCharFormat()
                f.merge(data.allStyles[group][name])
                setItemTextFormat(children[name], f)
        
    def baseColorsChanged(self, name):
        # keep data up to date with base colors
        data = self.data[self.scheme.currentScheme()]
        data.baseColors[name] = self.baseColorsWidget.color[name].color()
        self.updateDisplay()
        self.changed.emit()
    
    def customAttributesChanged(self):
        item = self.tree.currentItem()
        if not item or not item.parent():
            return
        data = self.data[self.scheme.currentScheme()]
        if item.parent() is self.defaultStylesItem:
            # a default style has been changed
            data.defaultStyles[item.name] = self.customAttributesWidget.textFormat()
        else:
            # a specific style has been changed
            group, name = item.parent().group, item.name
            data.allStyles[group][name] = self.customAttributesWidget.textFormat()
        self.updateDisplay()
        self.changed.emit()
        
    def import_(self, filename):
        from . import import_export
        import_export.importTheme(filename, self, self.scheme)
        
    def export(self, name, filename):
        from . import import_export
        try:
            import_export.exportTheme(self, name, filename)
        except (IOError, OSError) as e:
            QMessageBox.critical(self, _("Error"), _(
                "Can't write to destination:\n\n{url}\n\n{error}").format(
                url=filename, error=e.strerror))
    
    def loadSettings(self):
        self.data = {} # holds all data with scheme as key
        self._printScheme = QSettings().value("printer_scheme", "default", str)
        self.scheme.loadSettings("editor_scheme", "editor_schemes")
        
    def saveSettings(self):
        self.scheme.saveSettings("editor_scheme", "editor_schemes", "fontscolors")
        for scheme in self.scheme.schemes():
            if scheme in self.data:
                self.data[scheme].save(scheme)
        if self._printScheme:
            QSettings().setValue("printer_scheme", self._printScheme)
        else:
            QSettings().remove("printer_scheme")
class ConfigurationWidget(QWidget):
    """
    Class implementing a dialog for the configuration of eric6.
    
    @signal preferencesChanged() emitted after settings have been changed
    @signal masterPasswordChanged(str, str) emitted after the master
        password has been changed with the old and the new password
    @signal accepted() emitted to indicate acceptance of the changes
    @signal rejected() emitted to indicate rejection of the changes
    """
    preferencesChanged = pyqtSignal()
    masterPasswordChanged = pyqtSignal(str, str)
    accepted = pyqtSignal()
    rejected = pyqtSignal()
    
    DefaultMode = 0
    HelpBrowserMode = 1
    TrayStarterMode = 2
    
    def __init__(self, parent=None, fromEric=True, displayMode=DefaultMode):
        """
        Constructor
        
        @param parent The parent widget of this dialog. (QWidget)
        @keyparam fromEric flag indicating a dialog generation from within the
            eric6 ide (boolean)
        @keyparam displayMode mode of the configuration dialog
            (DefaultMode, HelpBrowserMode, TrayStarterMode)
        @exception RuntimeError raised to indicate an invalid dialog mode
        """
        assert displayMode in (
            ConfigurationWidget.DefaultMode,
            ConfigurationWidget.HelpBrowserMode,
            ConfigurationWidget.TrayStarterMode
        )
        
        super(ConfigurationWidget, self).__init__(parent)
        self.fromEric = fromEric
        self.displayMode = displayMode
        
        self.__setupUi()
        
        self.itmDict = {}
        
        if not fromEric:
            from PluginManager.PluginManager import PluginManager
            try:
                self.pluginManager = e5App().getObject("PluginManager")
            except KeyError:
                self.pluginManager = PluginManager(self)
                e5App().registerObject("PluginManager", self.pluginManager)
        
        if displayMode == ConfigurationWidget.DefaultMode:
            self.configItems = {
                # key : [display string, pixmap name, dialog module name or
                #        page creation function, parent key,
                #        reference to configuration page (must always be last)]
                # The dialog module must have the module function create to
                # create the configuration page. This must have the method
                # save to save the settings.
                "applicationPage":
                [self.tr("Application"), "preferences-application.png",
                 "ApplicationPage", None, None],
                "cooperationPage":
                [self.tr("Cooperation"), "preferences-cooperation.png",
                 "CooperationPage", None, None],
                "corbaPage":
                [self.tr("CORBA"), "preferences-orbit.png",
                 "CorbaPage", None, None],
                "emailPage":
                [self.tr("Email"), "preferences-mail_generic.png",
                 "EmailPage", None, None],
                "graphicsPage":
                [self.tr("Graphics"), "preferences-graphics.png",
                 "GraphicsPage", None, None],
                "iconsPage":
                [self.tr("Icons"), "preferences-icons.png",
                 "IconsPage", None, None],
                "ircPage":
                [self.tr("IRC"), "irc.png",
                 "IrcPage", None, None],
                "networkPage":
                [self.tr("Network"), "preferences-network.png",
                 "NetworkPage", None, None],
                "notificationsPage":
                [self.tr("Notifications"),
                 "preferences-notifications.png",
                 "NotificationsPage", None, None],
                "pluginManagerPage":
                [self.tr("Plugin Manager"),
                 "preferences-pluginmanager.png",
                 "PluginManagerPage", None, None],
                "printerPage":
                [self.tr("Printer"), "preferences-printer.png",
                 "PrinterPage", None, None],
                "pythonPage":
                [self.tr("Python"), "preferences-python.png",
                 "PythonPage", None, None],
                "qtPage":
                [self.tr("Qt"), "preferences-qtlogo.png",
                 "QtPage", None, None],
                "securityPage":
                [self.tr("Security"), "preferences-security.png",
                 "SecurityPage", None, None],
                "shellPage":
                [self.tr("Shell"), "preferences-shell.png",
                 "ShellPage", None, None],
                "tasksPage":
                [self.tr("Tasks"), "task.png",
                 "TasksPage", None, None],
                "templatesPage":
                [self.tr("Templates"), "preferences-template.png",
                 "TemplatesPage", None, None],
                "trayStarterPage":
                [self.tr("Tray Starter"), "erict.png",
                 "TrayStarterPage", None, None],
                "vcsPage":
                [self.tr("Version Control Systems"),
                 "preferences-vcs.png",
                 "VcsPage", None, None],
                
                "0debuggerPage":
                [self.tr("Debugger"), "preferences-debugger.png",
                 None, None, None],
                "debuggerGeneralPage":
                [self.tr("General"), "preferences-debugger.png",
                 "DebuggerGeneralPage", "0debuggerPage", None],
                "debuggerPythonPage":
                [self.tr("Python"), "preferences-pyDebugger.png",
                 "DebuggerPythonPage", "0debuggerPage", None],
                "debuggerPython3Page":
                [self.tr("Python3"), "preferences-pyDebugger.png",
                 "DebuggerPython3Page", "0debuggerPage", None],
                "debuggerRubyPage":
                [self.tr("Ruby"), "preferences-rbDebugger.png",
                 "DebuggerRubyPage", "0debuggerPage", None],
                
                "0editorPage":
                [self.tr("Editor"), "preferences-editor.png",
                 None, None, None],
                "editorAPIsPage":
                [self.tr("APIs"), "preferences-api.png",
                 "EditorAPIsPage", "0editorPage", None],
                "editorAutocompletionPage":
                [self.tr("Autocompletion"),
                 "preferences-autocompletion.png",
                 "EditorAutocompletionPage", "0editorPage", None],
                "editorAutocompletionQScintillaPage":
                [self.tr("QScintilla"), "qscintilla.png",
                 "EditorAutocompletionQScintillaPage",
                 "editorAutocompletionPage", None],
                "editorCalltipsPage":
                [self.tr("Calltips"), "preferences-calltips.png",
                 "EditorCalltipsPage", "0editorPage", None],
                "editorCalltipsQScintillaPage":
                [self.tr("QScintilla"), "qscintilla.png",
                 "EditorCalltipsQScintillaPage", "editorCalltipsPage", None],
                "editorGeneralPage":
                [self.tr("General"), "preferences-general.png",
                 "EditorGeneralPage", "0editorPage", None],
                "editorFilePage":
                [self.tr("Filehandling"),
                 "preferences-filehandling.png",
                 "EditorFilePage", "0editorPage", None],
                "editorSearchPage":
                [self.tr("Searching"), "preferences-search.png",
                 "EditorSearchPage", "0editorPage", None],
                "editorSpellCheckingPage":
                [self.tr("Spell checking"),
                 "preferences-spellchecking.png",
                 "EditorSpellCheckingPage", "0editorPage", None],
                "editorStylesPage":
                [self.tr("Style"), "preferences-styles.png",
                 "EditorStylesPage", "0editorPage", None],
                "editorSyntaxPage":
                [self.tr("Code Checkers"), "preferences-debugger.png",
                 "EditorSyntaxPage", "0editorPage", None],
                "editorTypingPage":
                [self.tr("Typing"), "preferences-typing.png",
                 "EditorTypingPage", "0editorPage", None],
                "editorExportersPage":
                [self.tr("Exporters"), "preferences-exporters.png",
                 "EditorExportersPage", "0editorPage", None],
                
                "1editorLexerPage":
                [self.tr("Highlighters"),
                 "preferences-highlighting-styles.png",
                 None, "0editorPage", None],
                "editorHighlightersPage":
                [self.tr("Filetype Associations"),
                 "preferences-highlighter-association.png",
                 "EditorHighlightersPage", "1editorLexerPage", None],
                "editorHighlightingStylesPage":
                [self.tr("Styles"),
                 "preferences-highlighting-styles.png",
                 "EditorHighlightingStylesPage", "1editorLexerPage", None],
                "editorKeywordsPage":
                [self.tr("Keywords"), "preferences-keywords.png",
                 "EditorKeywordsPage", "1editorLexerPage", None],
                "editorPropertiesPage":
                [self.tr("Properties"), "preferences-properties.png",
                 "EditorPropertiesPage", "1editorLexerPage", None],
                
                "0helpPage":
                [self.tr("Help"), "preferences-help.png",
                 None, None, None],
                "helpAppearancePage":
                [self.tr("Appearance"), "preferences-styles.png",
                 "HelpAppearancePage", "0helpPage", None],
                "helpDocumentationPage":
                [self.tr("Help Documentation"),
                 "preferences-helpdocumentation.png",
                 "HelpDocumentationPage", "0helpPage", None],
                "helpViewersPage":
                [self.tr("Help Viewers"),
                 "preferences-helpviewers.png",
                 "HelpViewersPage", "0helpPage", None],
                "helpVirusTotalPage":
                [self.tr("VirusTotal Interface"), "virustotal.png",
                 "HelpVirusTotalPage", "0helpPage", None],
                "helpWebBrowserPage":
                [self.tr("eric6 Web Browser"), "ericWeb.png",
                 "HelpWebBrowserPage", "0helpPage", None],
                
                "0projectPage":
                [self.tr("Project"), "preferences-project.png",
                 None, None, None],
                "projectBrowserPage":
                [self.tr("Project Viewer"), "preferences-project.png",
                 "ProjectBrowserPage", "0projectPage", None],
                "projectPage":
                [self.tr("Project"), "preferences-project.png",
                 "ProjectPage", "0projectPage", None],
                "multiProjectPage":
                [self.tr("Multiproject"),
                 "preferences-multiproject.png",
                 "MultiProjectPage", "0projectPage", None],
                
                "0interfacePage":
                [self.tr("Interface"), "preferences-interface.png",
                 None, None, None],
                "interfacePage":
                [self.tr("Interface"), "preferences-interface.png",
                 "InterfacePage", "0interfacePage", None],
                "viewmanagerPage":
                [self.tr("Viewmanager"), "preferences-viewmanager.png",
                 "ViewmanagerPage", "0interfacePage", None],
            }
            
            self.configItems.update(
                e5App().getObject("PluginManager").getPluginConfigData())
        elif displayMode == ConfigurationWidget.HelpBrowserMode:
            self.configItems = {
                # key : [display string, pixmap name, dialog module name or
                #        page creation function, parent key,
                #        reference to configuration page (must always be last)]
                # The dialog module must have the module function create to
                # create the configuration page. This must have the method
                # save to save the settings.
                "interfacePage":
                [self.tr("Interface"), "preferences-interface.png",
                 "HelpInterfacePage", None, None],
                "networkPage":
                [self.tr("Network"), "preferences-network.png",
                 "NetworkPage", None, None],
                "printerPage":
                [self.tr("Printer"), "preferences-printer.png",
                 "PrinterPage", None, None],
                "securityPage":
                [self.tr("Security"), "preferences-security.png",
                 "SecurityPage", None, None],
                
                "0helpPage":
                [self.tr("Help"), "preferences-help.png",
                 None, None, None],
                "helpAppearancePage":
                [self.tr("Appearance"), "preferences-styles.png",
                 "HelpAppearancePage", "0helpPage", None],
                "helpDocumentationPage":
                [self.tr("Help Documentation"),
                 "preferences-helpdocumentation.png",
                 "HelpDocumentationPage", "0helpPage", None],
                "helpVirusTotalPage":
                [self.tr("VirusTotal Interface"), "virustotal.png",
                 "HelpVirusTotalPage", "0helpPage", None],
                "helpWebBrowserPage":
                [self.tr("eric6 Web Browser"), "ericWeb.png",
                 "HelpWebBrowserPage", "0helpPage", None],
            }
        elif displayMode == ConfigurationWidget.TrayStarterMode:
            self.configItems = {
                # key : [display string, pixmap name, dialog module name or
                #        page creation function, parent key,
                #        reference to configuration page (must always be last)]
                # The dialog module must have the module function create to
                # create the configuration page. This must have the method
                # save to save the settings.
                "trayStarterPage":
                [self.tr("Tray Starter"), "erict.png",
                 "TrayStarterPage", None, None],
            }
        else:
            raise RuntimeError("Illegal mode value: {0}".format(displayMode))
        
        # generate the list entries
        for key in sorted(self.configItems.keys()):
            pageData = self.configItems[key]
            if pageData[3]:
                pitm = self.itmDict[pageData[3]]  # get the parent item
            else:
                pitm = self.configList
            self.itmDict[key] = ConfigurationPageItem(pitm, pageData[0], key,
                                                      pageData[1])
            self.itmDict[key].setExpanded(True)
        self.configList.sortByColumn(0, Qt.AscendingOrder)
        
        # set the initial size of the splitter
        self.configSplitter.setSizes([200, 600])
        
        self.configList.itemActivated.connect(self.__showConfigurationPage)
        self.configList.itemClicked.connect(self.__showConfigurationPage)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.rejected)
        
        if displayMode != ConfigurationWidget.TrayStarterMode:
            self.__initLexers()
        
    def accept(self):
        """
        Public slot to accept the buttonBox accept signal.
        """
        if not isMacPlatform():
            wdg = self.focusWidget()
            if wdg == self.configList:
                return
        
        self.accepted.emit()
        
    def __setupUi(self):
        """
        Private method to perform the general setup of the configuration
        widget.
        """
        self.setObjectName("ConfigurationDialog")
        self.resize(900, 650)
        self.verticalLayout_2 = QVBoxLayout(self)
        self.verticalLayout_2.setSpacing(6)
        self.verticalLayout_2.setContentsMargins(6, 6, 6, 6)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        
        self.configSplitter = QSplitter(self)
        self.configSplitter.setOrientation(Qt.Horizontal)
        self.configSplitter.setObjectName("configSplitter")
        
        self.configListWidget = QWidget(self.configSplitter)
        self.leftVBoxLayout = QVBoxLayout(self.configListWidget)
        self.leftVBoxLayout.setContentsMargins(0, 0, 0, 0)
        self.leftVBoxLayout.setSpacing(0)
        self.leftVBoxLayout.setObjectName("leftVBoxLayout")
        self.configListFilter = E5ClearableLineEdit(
            self, self.tr("Enter filter text..."))
        self.configListFilter.setObjectName("configListFilter")
        self.leftVBoxLayout.addWidget(self.configListFilter)
        self.configList = QTreeWidget()
        self.configList.setObjectName("configList")
        self.leftVBoxLayout.addWidget(self.configList)
        self.configListFilter.textChanged.connect(self.__filterTextChanged)
        
        self.scrollArea = QScrollArea(self.configSplitter)
        self.scrollArea.setFrameShape(QFrame.NoFrame)
        self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scrollArea.setWidgetResizable(False)
        self.scrollArea.setObjectName("scrollArea")
        
        self.configStack = QStackedWidget()
        self.configStack.setFrameShape(QFrame.Box)
        self.configStack.setFrameShadow(QFrame.Sunken)
        self.configStack.setObjectName("configStack")
        self.scrollArea.setWidget(self.configStack)
        
        self.emptyPage = QWidget()
        self.emptyPage.setGeometry(QRect(0, 0, 372, 591))
        self.emptyPage.setObjectName("emptyPage")
        self.vboxlayout = QVBoxLayout(self.emptyPage)
        self.vboxlayout.setSpacing(6)
        self.vboxlayout.setContentsMargins(6, 6, 6, 6)
        self.vboxlayout.setObjectName("vboxlayout")
        spacerItem = QSpacerItem(
            20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.vboxlayout.addItem(spacerItem)
        self.emptyPagePixmap = QLabel(self.emptyPage)
        self.emptyPagePixmap.setAlignment(Qt.AlignCenter)
        self.emptyPagePixmap.setObjectName("emptyPagePixmap")
        self.emptyPagePixmap.setPixmap(
            QPixmap(os.path.join(getConfig('ericPixDir'), 'eric.png')))
        self.vboxlayout.addWidget(self.emptyPagePixmap)
        self.textLabel1 = QLabel(self.emptyPage)
        self.textLabel1.setAlignment(Qt.AlignCenter)
        self.textLabel1.setObjectName("textLabel1")
        self.vboxlayout.addWidget(self.textLabel1)
        spacerItem1 = QSpacerItem(
            20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.vboxlayout.addItem(spacerItem1)
        self.configStack.addWidget(self.emptyPage)
        
        self.verticalLayout_2.addWidget(self.configSplitter)
        
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Apply | QDialogButtonBox.Cancel |
            QDialogButtonBox.Ok | QDialogButtonBox.Reset)
        self.buttonBox.setObjectName("buttonBox")
        if not self.fromEric and \
                self.displayMode == ConfigurationWidget.DefaultMode:
            self.buttonBox.button(QDialogButtonBox.Apply).hide()
        self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False)
        self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(False)
        self.verticalLayout_2.addWidget(self.buttonBox)

        self.setWindowTitle(self.tr("Preferences"))
        
        self.configList.header().hide()
        self.configList.header().setSortIndicator(0, Qt.AscendingOrder)
        self.configList.setSortingEnabled(True)
        self.textLabel1.setText(
            self.tr("Please select an entry of the list \n"
                    "to display the configuration page."))
        
        QMetaObject.connectSlotsByName(self)
        self.setTabOrder(self.configList, self.configStack)
        
        self.configStack.setCurrentWidget(self.emptyPage)
        
        self.configList.setFocus()
    
    def __filterTextChanged(self, filter):
        """
        Private slot to handle a change of the filter.
        
        @param filter text of the filter line edit (string)
        """
        self.__filterChildItems(self.configList.invisibleRootItem(), filter)
    
    def __filterChildItems(self, parent, filter):
        """
        Private method to filter child items based on a filter string.
        
        @param parent reference to the parent item (QTreeWidgetItem)
        @param filter filter string (string)
        @return flag indicating a visible child item (boolean)
        """
        childVisible = False
        filter = filter.lower()
        for index in range(parent.childCount()):
            itm = parent.child(index)
            if itm.childCount() > 0:
                visible = self.__filterChildItems(itm, filter)
            else:
                visible = filter == "" or filter in itm.text(0).lower()
            if visible:
                childVisible = True
            itm.setHidden(not visible)
        
        return childVisible
    
    def __initLexers(self):
        """
        Private method to initialize the dictionary of preferences lexers.
        """
        import QScintilla.Lexers
        from .PreferencesLexer import PreferencesLexer, \
            PreferencesLexerLanguageError
        
        self.lexers = {}
        for language in QScintilla.Lexers.getSupportedLanguages():
            if language not in self.lexers:
                try:
                    self.lexers[language] = PreferencesLexer(language, self)
                except PreferencesLexerLanguageError:
                    pass
        
    def __importConfigurationPage(self, name):
        """
        Private method to import a configuration page module.
        
        @param name name of the configuration page module (string)
        @return reference to the configuration page module
        """
        modName = "Preferences.ConfigurationPages.{0}".format(name)
        try:
            mod = __import__(modName)
            components = modName.split('.')
            for comp in components[1:]:
                mod = getattr(mod, comp)
            return mod
        except ImportError:
            E5MessageBox.critical(
                self,
                self.tr("Configuration Page Error"),
                self.tr("""<p>The configuration page <b>{0}</b>"""
                        """ could not be loaded.</p>""").format(name))
            return None
        
    def __showConfigurationPage(self, itm, column):
        """
        Private slot to show a selected configuration page.
        
        @param itm reference to the selected item (QTreeWidgetItem)
        @param column column that was selected (integer) (ignored)
        """
        pageName = itm.getPageName()
        self.showConfigurationPageByName(pageName, setCurrent=False)
        
    def __initPage(self, pageData):
        """
        Private method to initialize a configuration page.
        
        @param pageData data structure for the page to initialize
        @return reference to the initialized page
        """
        page = None
        if isinstance(pageData[2], types.FunctionType):
            page = pageData[2](self)
        else:
            mod = self.__importConfigurationPage(pageData[2])
            if mod:
                page = mod.create(self)
        if page is not None:
            self.configStack.addWidget(page)
            pageData[-1] = page
            try:
                page.setMode(self.displayMode)
            except AttributeError:
                pass
        return page
        
    def showConfigurationPageByName(self, pageName, setCurrent=True):
        """
        Public slot to show a named configuration page.
        
        @param pageName name of the configuration page to show (string)
        @param setCurrent flag indicating to set the current item (boolean)
        """
        if pageName == "empty":
            page = self.emptyPage
        else:
            pageData = self.configItems[pageName]
            if pageData[-1] is None and pageData[2] is not None:
                # the page was not loaded yet, create it
                page = self.__initPage(pageData)
            else:
                page = pageData[-1]
            if page is None:
                page = self.emptyPage
            elif setCurrent:
                items = self.configList.findItems(
                    pageData[0],
                    Qt.MatchFixedString | Qt.MatchRecursive)
                if items:
                    self.configList.setCurrentItem(items[0])
        self.configStack.setCurrentWidget(page)
        ssize = self.scrollArea.size()
        if self.scrollArea.horizontalScrollBar():
            ssize.setHeight(
                ssize.height() -
                self.scrollArea.horizontalScrollBar().height() - 2)
        if self.scrollArea.verticalScrollBar():
            ssize.setWidth(
                ssize.width() -
                self.scrollArea.verticalScrollBar().width() - 2)
        psize = page.minimumSizeHint()
        self.configStack.resize(max(ssize.width(), psize.width()),
                                max(ssize.height(), psize.height()))
        
        if page != self.emptyPage:
            page.polishPage()
            self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(True)
            self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(True)
        else:
            self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False)
            self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(False)
        
        # reset scrollbars
        for sb in [self.scrollArea.horizontalScrollBar(),
                   self.scrollArea.verticalScrollBar()]:
            if sb:
                sb.setValue(0)
        
        self.__currentConfigurationPageName = pageName
        
    def getConfigurationPageName(self):
        """
        Public method to get the page name of the current page.
        
        @return page name of the current page (string)
        """
        return self.__currentConfigurationPageName
        
    def calledFromEric(self):
        """
        Public method to check, if invoked from within eric.
        
        @return flag indicating invocation from within eric (boolean)
        """
        return self.fromEric
        
    def getPage(self, pageName):
        """
        Public method to get a reference to the named page.
        
        @param pageName name of the configuration page (string)
        @return reference to the page or None, indicating page was
            not loaded yet
        """
        return self.configItems[pageName][-1]
        
    def getLexers(self):
        """
        Public method to get a reference to the lexers dictionary.
        
        @return reference to the lexers dictionary
        """
        return self.lexers
        
    def setPreferences(self):
        """
        Public method called to store the selected values into the preferences
        storage.
        """
        for key, pageData in list(self.configItems.items()):
            if pageData[-1]:
                pageData[-1].save()
                # page was loaded (and possibly modified)
                QApplication.processEvents()    # ensure HMI is responsive
        
    def on_buttonBox_clicked(self, button):
        """
        Private slot called by a button of the button box clicked.
        
        @param button button that was clicked (QAbstractButton)
        """
        if button == self.buttonBox.button(QDialogButtonBox.Apply):
            self.on_applyButton_clicked()
        elif button == self.buttonBox.button(QDialogButtonBox.Reset):
            self.on_resetButton_clicked()
        
    @pyqtSlot()
    def on_applyButton_clicked(self):
        """
        Private slot called to apply the settings of the current page.
        """
        if self.configStack.currentWidget() != self.emptyPage:
            page = self.configStack.currentWidget()
            savedState = page.saveState()
            page.save()
            self.preferencesChanged.emit()
            if savedState is not None:
                page.setState(savedState)
        
    @pyqtSlot()
    def on_resetButton_clicked(self):
        """
        Private slot called to reset the settings of the current page.
        """
        if self.configStack.currentWidget() != self.emptyPage:
            currentPage = self.configStack.currentWidget()
            savedState = currentPage.saveState()
            pageName = self.configList.currentItem().getPageName()
            self.configStack.removeWidget(currentPage)
            if pageName == "editorHighlightingStylesPage":
                self.__initLexers()
            self.configItems[pageName][-1] = None
            
            self.showConfigurationPageByName(pageName)
            if savedState is not None:
                self.configStack.currentWidget().setState(savedState)
Beispiel #4
0
class Shortcuts(preferences.Page):
    def __init__(self, dialog):
        super(Shortcuts, self).__init__(dialog)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.scheme = SchemeSelector(self)
        layout.addWidget(self.scheme)
        self.searchEntry = LineEdit()
        self.searchEntry.setPlaceholderText(_("Search..."))
        layout.addWidget(self.searchEntry)
        self.tree = QTreeWidget(self)
        self.tree.setHeaderLabels([_("Command"), _("Shortcut")])
        self.tree.setRootIsDecorated(False)
        self.tree.setColumnCount(2)
        self.tree.setAllColumnsShowFocus(True)
        self.tree.setAnimated(True)
        layout.addWidget(self.tree)

        self.edit = QPushButton(icons.get("preferences-desktop-keyboard-shortcuts"), '')
        layout.addWidget(self.edit)

        # signals
        self.searchEntry.textChanged.connect(self.updateFilter)
        self.scheme.currentChanged.connect(self.slotSchemeChanged)
        self.scheme.changed.connect(self.changed)
        self.tree.currentItemChanged.connect(self.slotCurrentItemChanged)
        self.tree.itemDoubleClicked.connect(self.editCurrentItem)
        self.edit.clicked.connect(self.editCurrentItem)

        # make a dict of all actions with the actions as key and the names as
        # value, with the collection prepended (for loading/saving)
        win = dialog.parent()
        allactions = {}
        for collection in actioncollectionmanager.manager(win).actionCollections():
            for name, action in collection.actions().items():
                allactions[action] = (collection, name)

        # keep a list of actions not in the menu structure
        left = list(allactions.keys())

        def add_actions(menuitem, actions):
            """Add actions to a QTreeWidgetItem."""
            for a in actions:
                if a.menu():
                    item = build_menu_item(a)
                    if item.childCount():
                        menuitem.addChild(item)
                elif a in left:
                    left.remove(a)
                    menuitem.addChild(ShortcutItem(a, *allactions[a]))
            menuitem.setFlags(Qt.ItemIsEnabled) # disable selection

        def build_menu_item(action):
            """Return a QTreeWidgetItem with children for all the actions in the submenu."""
            menuitem = QTreeWidgetItem()
            text = qutil.removeAccelerator(action.text())
            menuitem.setText(0, _("Menu {name}").format(name=text))
            add_actions(menuitem, action.menu().actions())
            return menuitem

        # present the actions nicely ordered as in the menus
        for a in win.menuBar().actions():
            menuitem = build_menu_item(a)
            if menuitem.childCount():
                self.tree.addTopLevelItem(menuitem)

        # sort leftover actions
        left.sort(key=lambda i: i.text())

        # show actions that are left, grouped by collection
        titlegroups = {}
        for a in left[:]: # copy
            collection, name = allactions[a]
            if collection.title():
                titlegroups.setdefault(collection.title(), []).append(a)
                left.remove(a)
        for title in sorted(titlegroups):
            item = QTreeWidgetItem(["{0}:".format(title)])
            for a in titlegroups[title]:
                item.addChild(ShortcutItem(a, *allactions[a]))
            self.tree.addTopLevelItem(item)
            item.setFlags(Qt.ItemIsEnabled) # disable selection

        # show other actions that were not in the menus
        item = QTreeWidgetItem([_("Other commands:")])
        for a in left:
            if a.text() and not a.menu():
                item.addChild(ShortcutItem(a, *allactions[a]))
        if item.childCount():
            self.tree.addTopLevelItem(item)
            item.setFlags(Qt.ItemIsEnabled) # disable selection

        self.tree.expandAll()

        item = self.tree.topLevelItem(0).child(0)
        if _lastaction:
            # find the previously selected item
            for i in self.items():
                if i.name == _lastaction:
                    item = i
                    break
        self.tree.setCurrentItem(item)
        self.tree.resizeColumnToContents(0)

    def items(self):
        """Yield all the items in the actions tree."""
        def children(item):
            for i in range(item.childCount()):
                c = item.child(i)
                if c.childCount():
                    for c1 in children(c):
                        yield c1
                else:
                    yield c
        for c in children(self.tree.invisibleRootItem()):
            yield c

    def item(self, collection, name):
        for item in self.items():
            if item.collection.name == collection and item.name == name:
                return item

    def saveSettings(self):
        self.scheme.saveSettings("shortcut_scheme", "shortcut_schemes", "shortcuts")
        for item in self.items():
            for scheme in self.scheme.schemes():
                item.save(scheme)
            item.clearSettings()
            item.switchScheme(self.scheme.currentScheme())

    def loadSettings(self):
        self.scheme.loadSettings("shortcut_scheme", "shortcut_schemes")
        # clear the settings in all the items
        for item in self.items():
            item.clearSettings()
            item.switchScheme(self.scheme.currentScheme())

    def slotSchemeChanged(self):
        """Called when the Scheme combobox is changed by the user."""
        for item in self.items():
            item.switchScheme(self.scheme.currentScheme())

    def slotCurrentItemChanged(self, item):
        if isinstance(item, ShortcutItem):
            self.edit.setText(
                _("&Edit Shortcut for \"{name}\"").format(name=item.text(0)))
            self.edit.setEnabled(True)
            global _lastaction
            _lastaction = item.name
        else:
            self.edit.setText(_("(no shortcut)"))
            self.edit.setEnabled(False)

    def import_(self, filename):
        from . import import_export
        import_export.importShortcut(filename, self, self.scheme)

    def export(self, name, filename):
        from . import import_export
        try:
            import_export.exportShortcut(self, self.scheme.currentScheme(), name, filename)
        except (IOError, OSError) as e:
            QMessageBox.critical(self, _("Error"), _(
                "Can't write to destination:\n\n{url}\n\n{error}").format(
                url=filename, error=e.strerror))

    def findShortcutConflict(self, shortcut):
        """Find the possible shortcut conflict and return the conflict name."""
        if shortcut:
            item = self.tree.currentItem()
            if not isinstance(item, ShortcutItem):
                return None
            scheme = self.scheme.currentScheme()
            for i in self.items():
                a = i.action(scheme)
                if i != item and a.shortcuts():
                    for s1 in a.shortcuts():
                        if s1.matches(shortcut) or shortcut.matches(s1):
                            return qutil.removeAccelerator(a.text())
        return None

    def editCurrentItem(self):
        item = self.tree.currentItem()
        if not isinstance(item, ShortcutItem):
            return

        dlg = ShortcutEditDialog(self, self.findShortcutConflict)
        scheme = self.scheme.currentScheme()
        action = item.action(scheme)
        default = item.defaultShortcuts() or None
        if dlg.editAction(action, default):
            shortcuts = action.shortcuts()
            # check for conflicts
            conflicting = []
            for i in self.items():
                if i is not item:
                    for s1, s2 in itertools.product(i.shortcuts(scheme), shortcuts):
                        if s1.matches(s2) or s2.matches(s1):
                            conflicting.append(i)
            if conflicting:
                for i in conflicting:
                    l = i.shortcuts(scheme)
                    for s1 in list(l): # copy
                        for s2 in shortcuts:
                            if s1.matches(s2) or s2.matches(s1):
                                l.remove(s1)
                    i.setShortcuts(l, scheme)

            # store the shortcut
            item.setShortcuts(shortcuts, scheme)
            self.changed.emit()

    def updateFilter(self):
        """Called when the search text changes."""
        search = self.searchEntry.text()
        scheme = self.scheme.currentScheme()
        def hidechildren(item):
            hideparent = True
            for n in range(item.childCount()):
                c = item.child(n)
                if c.childCount():
                    # submenu item
                    if hidechildren(c):
                        c.setHidden(True)
                    else:
                        c.setHidden(False)
                        c.setExpanded(True)
                        hideparent = False
                elif isinstance(c, ShortcutItem):
                    # shortcut item, should be the case
                    if c.matches(scheme, search):
                        c.setHidden(False)
                        hideparent = False
                    else:
                        c.setHidden(True)
            return hideparent
        hidechildren(self.tree.invisibleRootItem())
Beispiel #5
0
class SqlConnectionWidget(QWidget):
    """
    Class implementing a widget showing the SQL connections.
    
    @signal tableActivated(str) emitted after the entry for a table has been
        activated
    @signal schemaRequested(str) emitted when the schema display is requested
    @signal cleared() emitted after the connection tree has been cleared
    """
    tableActivated = pyqtSignal(str)
    schemaRequested = pyqtSignal(str)
    cleared = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(SqlConnectionWidget, self).__init__(parent)
        
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        
        self.__connectionTree = QTreeWidget(self)
        self.__connectionTree.setObjectName("connectionTree")
        self.__connectionTree.setHeaderLabels([self.tr("Database")])
        if qVersion() >= "5.0.0":
            self.__connectionTree.header().setSectionResizeMode(
                QHeaderView.Stretch)
        else:
            self.__connectionTree.header().setResizeMode(QHeaderView.Stretch)
        refreshAction = QAction(self.tr("Refresh"), self.__connectionTree)
        self.__schemaAction = QAction(
            self.tr("Show Schema"), self.__connectionTree)
        
        refreshAction.triggered.connect(self.refresh)
        self.__schemaAction.triggered.connect(self.showSchema)
        
        self.__connectionTree.addAction(refreshAction)
        self.__connectionTree.addAction(self.__schemaAction)
        self.__connectionTree.setContextMenuPolicy(Qt.ActionsContextMenu)
        
        layout.addWidget(self.__connectionTree)
        
        self.__activating = False
        
        self.__connectionTree.itemActivated.connect(self.__itemActivated)
        self.__connectionTree.currentItemChanged.connect(
            self.__currentItemChanged)
        
        self.__activeDb = ""
    
    def refresh(self):
        """
        Public slot to refresh the connection tree.
        """
        self.__connectionTree.clear()
        self.cleared.emit()
        
        connectionNames = QSqlDatabase.connectionNames()
        
        foundActiveDb = False
        for name in connectionNames:
            root = QTreeWidgetItem(self.__connectionTree)
            db = QSqlDatabase.database(name, False)
            root.setText(0, self.__dbCaption(db))
            if name == self.__activeDb:
                foundActiveDb = True
                self.__setActive(root)
            if db.isOpen():
                tables = db.tables()
                for table in tables:
                    itm = QTreeWidgetItem(root)
                    itm.setText(0, table)
        
        if not foundActiveDb and connectionNames:
            self.__activeDb = connectionNames[0]
            self.__setActive(self.__connectionTree.topLevelItem(0))
    
    def showSchema(self):
        """
        Public slot to show schema data of a database.
        """
        cItm = self.__connectionTree.currentItem()
        if cItm is None or cItm.parent() is None:
            return
        self.__setActive(cItm.parent())
        self.schemaRequested.emit(cItm.text(0))
    
    def __itemActivated(self, itm, column):
        """
        Private slot handling the activation of an item.
        
        @param itm reference to the item (QTreeWidgetItem)
        @param column column that was activated (integer)
        """
        if itm is None:
            return
        
        if not self.__activating:
            self.__activating = True
            if itm.parent() is None:
                self.__setActive(itm)
            else:
                self.__setActive(itm.parent())
                self.tableActivated.emit(itm.text(0))
            self.__activating = False
    
    def __currentItemChanged(self, current, previous):
        """
        Private slot handling a change of the current item.
        
        @param current reference to the new current item (QTreeWidgetItem)
        @param previous reference to the previous current item
            (QTreeWidgetItem)
        """
        self.__schemaAction.setEnabled(
            current is not None and current.parent() is not None)
    
    def __dbCaption(self, db):
        """
        Private method to assemble a string for the caption.
        
        @param db reference to the database object (QSqlDatabase)
        @return caption string (string)
        """
        nm = db.driverName()
        nm += ":"
        if db.userName():
            nm += db.userName()
            nm += "@"
        nm += db.databaseName()
        return nm
    
    def __setBold(self, itm, bold):
        """
        Private slot to set the font to bold.
        
        @param itm reference to the item to be changed (QTreeWidgetItem)
        @param bold flag indicating bold (boolean)
        """
        font = itm.font(0)
        font.setBold(bold)
        itm.setFont(0, font)
    
    def currentDatabase(self):
        """
        Public method to get the current database.
        
        @return reference to the current database (QSqlDatabase)
        """
        return QSqlDatabase.database(self.__activeDb)
    
    def __setActive(self, itm):
        """
        Private slot to set an item to active.
        
        @param itm reference to the item to set as the active item
            (QTreeWidgetItem)
        """
        for index in range(self.__connectionTree.topLevelItemCount()):
            if self.__connectionTree.topLevelItem(index).font(0).bold():
                self.__setBold(
                    self.__connectionTree.topLevelItem(index), False)
        
        if itm is None:
            return
        
        self.__setBold(itm, True)
        self.__activeDb = QSqlDatabase.connectionNames()[
            self.__connectionTree.indexOfTopLevelItem(itm)]
Beispiel #6
0
class Preferences(QDialog):

    configuration = {}
    weight = 0
    # Signals
    savePreferences = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent, Qt.Dialog)
        self.setWindowTitle(translations.TR_PREFERENCES_TITLE)
        self.setMinimumSize(900, 650)
        box = QVBoxLayout(self)
        box.setContentsMargins(3, 3, 3, 3)
        self.setAutoFillBackground(True)
        # Header
        self._header_label = QLabel("")
        header_font = self._header_label.font()
        header_font.setBold(True)
        header_font.setPointSize(header_font.pointSize() + 4)
        self._header_label.setFont(header_font)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)

        self.tree = QTreeWidget()
        self.tree.header().setHidden(True)
        self.tree.setSelectionMode(QTreeWidget.SingleSelection)
        self.tree.setAnimated(True)
        self.tree.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.tree.setFixedWidth(200)
        hbox.addWidget(self.tree)

        self.stacked = QStackedLayout()
        header_layout = QVBoxLayout()
        header_layout.setContentsMargins(0, 0, 0, 0)
        header_layout.addWidget(self._header_label)
        header_layout.addLayout(self.stacked)
        hbox.addLayout(header_layout)
        box.addLayout(hbox)

        # Footer buttons
        button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        box.addWidget(button_box)

        # Connections
        button_box.rejected.connect(self.close)
        button_box.accepted.connect(self._save_preferences)
        self.tree.selectionModel().currentRowChanged.connect(
            self._change_current)

        self.load_ui()

    @pyqtSlot()
    def _save_preferences(self):
        self.savePreferences.emit()
        self.close()

    def load_ui(self):
        sections = sorted(
            Preferences.configuration.keys(),
            key=lambda item: Preferences.configuration[item]['weight'])
        for section in sections:
            text = Preferences.configuration[section]['text']
            Widget = Preferences.configuration[section]['widget']
            widget = Widget(self)
            area = QScrollArea()
            area.setWidgetResizable(True)
            area.setWidget(widget)
            self.stacked.addWidget(area)
            index = self.stacked.indexOf(area)
            item = QTreeWidgetItem([text])
            item.setData(0, Qt.UserRole, index)
            self.tree.addTopLevelItem(item)

            # Sort Item Children
            subcontent = Preferences.configuration[section].get(
                'subsections', {})
            subsections = sorted(
                subcontent.keys(), key=lambda item: subcontent[item]['weight'])
            for sub in subsections:
                text = subcontent[sub]['text']
                Widget = subcontent[sub]['widget']
                widget = Widget(self)
                area = QScrollArea()
                area.setWidgetResizable(True)
                area.setWidget(widget)
                self.stacked.addWidget(area)
                index = self.stacked.indexOf(area)
                subitem = QTreeWidgetItem([text])
                subitem.setData(0, Qt.UserRole, index)
                item.addChild(subitem)

        self.tree.expandAll()
        self.tree.setCurrentIndex(self.tree.model().index(0, 0))

    def _change_current(self):
        item = self.tree.currentItem()
        index = item.data(0, Qt.UserRole)
        self.stacked.setCurrentIndex(index)
        self._header_label.setText(item.text(0))

    @classmethod
    def register_configuration(cls, section, widget, text,
                               weight=None, subsection=None):
        if weight is None:
            Preferences.weight += 1
            weight = Preferences.weight
        if subsection is None:
            Preferences.configuration[section] = {
                'widget': widget,
                'weight': weight,
                'text': text
            }
        else:
            config = Preferences.configuration.get(section, {})
            if not config:
                config[section] = {
                    'widget': None,
                    'weight': 100
                }
            subconfig = config.get('subsections', {})
            subconfig[subsection] = {
                'widget': widget,
                'weight': weight,
                'text': text
            }
            config['subsections'] = subconfig
            Preferences.configuration[section] = config
Beispiel #7
0
class GroupTreeWidgetUI(object):
    def __init__(self, layout_widget: QWidget):
        """
        分组信息基类
        :param layout_widget:
        """
        self.layout_widget = layout_widget

        self.group_tree = QTreeWidget(self.layout_widget)
        # noinspection PyArgumentList
        self.headers_title = [
            _translate("DisplayInfoUI", "Id"),
            # _translate("DisplayInfoUI", "外网"),
            _translate("DisplayInfoUI", "主机信息"),
            _translate("DisplayInfoUI", "内网"),
            _translate("DisplayInfoUI", "计算机"),
            _translate("DisplayInfoUI", "操作系统"),
            _translate("DisplayInfoUI", "处理器"),
            _translate("DisplayInfoUI", "内存"),
            _translate("DisplayInfoUI", "硬盘容量"),
            _translate("DisplayInfoUI", "显卡"),
            _translate("DisplayInfoUI", "视频"),
            _translate("DisplayInfoUI", "语音"),
            _translate("DisplayInfoUI", "开机时间"),
            _translate("DisplayInfoUI", "服务版本"),
            _translate("DisplayInfoUI", "分组"),
            _translate("DisplayInfoUI", "区域"),
            _translate("DisplayInfoUI", "备注"),
        ]
        self.header_width = [90, 100]
        self.hide_column = [
            self.headers_title.index(item) for item in self.headers_title[2:-1]
        ]

    def options(self) -> None:
        """
        设置参数
        :return:
        """
        self.group_tree.setObjectName("group_tree")
        self.group_tree.setGeometry(QRect(0, 0, 100, 0))

        # 启用或禁用排序
        # __sortingEnabled = self.treeWidget.isSortingEnabled()
        # self.group_tree.setSortingEnabled(False)
        # 此处内容排序
        # self.group_tree.setSortingEnabled(__sortingEnabled)

        # 窗口小部件的背景将是透明的
        # self.group_tree.setAutoFillBackground(True)

        # 优化2 设置根节点的背景颜色
        # brush_red = QBrush(Qt.red)
        # root.setBackground(0, brush_red)
        # brush_blue = QBrush(Qt.blue)
        # root.setBackground(1, brush_blue)

        # 背景透明与表头冲突, 会呈现黑色
        # self.group_tree.setStyleSheet(
        #     # "QHeaderView::section{background-color: rgba(255, 0, 0, 0);};"  # 背景颜色
        #     "QHeaderView::section{background:transparent;};"  # 表头透明
        # )

        # 背景透明,标题头不透明
        # pll = self.group_tree.palette()
        # pll.setBrush(QPalette.Base, QBrush(QColor(255, 255, 255, 0)))
        # self.group_tree.setPalette(pll)

        # 节点全部展开
        # self.group_tree.expandAll()

    def set_headers(self) -> None:
        """
        设置标题
        :return:
        """
        # 设置列数
        # self.group_tree.setColumnCount(1)
        self.group_tree.setColumnCount(len(self.headers_title))

        # 设置树形控件头部的标题, 默认值为1
        # 单个设置标题,顺序
        # for title in self.headers_title:
        #     self.group_tree.setHeaderLabel(title)
        # 一次设置多个标题,顺序
        # self.group_tree.setHeaderLabels(self.headers_title)

        # 单个设置标题,精准
        for index, title in enumerate(self.headers_title):
            self.group_tree.headerItem().setText(index, title)

        # 隐藏隐藏表头
        # self.group_tree.header().setVisible(False)
        # self.group_tree.setHeaderHidden(True)

        # 设置树形控件的列的宽度
        for index, width in enumerate(self.header_width):
            self.group_tree.setColumnWidth(index, width)

        # 隐藏第一列
        # self.group_tree.hideColumn(0)
        for column in self.hide_column:
            self.group_tree.hideColumn(column)

        # 设置某一行的内容可编辑
        # QTreeWidgetItem *->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);

        # 可用来打开某一行的某一列的编辑状态
        # QTreeWidget::openPersistentEditor ( QTreeWidgetItem * item, int column = 0 )
        # self.group_tree.openPersistentEditor()
        # 则可以用来关闭某一行某一列的始终编辑状态
        #  QTreeWidget::closePersistentEditor ( QTreeWidgetItem * item, int column = 0 )

    def default_master_node(self) -> None:
        """
        设置默认主节点
        :return:
        """
        # noinspection PyArgumentList
        default_master_node = [
            _translate("DisplayInfoUI", "在线主机"),
            _translate("DisplayInfoUI", "下线主机"),
        ]
        for index, node in enumerate(default_master_node):
            master = QTreeWidgetItem(self.group_tree)
            # 节点信息设置
            # self.group_tree.topLevelItem(index).setText(0, node)
            master.setText(0, node)
            master.setText(1, "(0)")

    def setup_ui(self) -> None:
        self.options()
        self.set_headers()
        self.default_master_node()

        # 优化3 给节点添加响应事件
        # self.list_group.clicked.connect(self.on_clicked)
        # 项目进入触发 itemExpanded
        # 项目退出触发 itemCollapsed
        # 当用户在窗口小部件内单击时会发出此信号。 itemClicked
        # 内容改变时发生:itemChanged
        # self.group_tree.itemClicked.connect(self.on_clicked)

        # 鼠标点击事件
        self.group_tree.itemClicked.connect(self.master_item_clicked)

    def master_item_clicked(self, event1: QTreeWidgetItem,
                            event2: int) -> None:
        """
        鼠标单击事件
        获取根节点所有信息
        发送信号 -> 信息展示
        :param event1:
        :param event2:
        :return:
        """
        if event2:
            pass
        if event1.parent() is not None:
            # 不是根节点
            return None
        data = self.get_current_master_item_data()
        communicate.display_info.emit(data)

    def master_item_real_time_refresh(self):
        """
        数据实时刷新
        系统初始化发送数据 -> 信息展示
        :return:
        """
        master = self.group_tree.currentItem()
        if master is None:
            master: QTreeWidgetItem = self.group_tree.topLevelItem(0)
        if master.parent():
            master = master.parent()

        node_count = master.childCount()

        data_info = []
        for i in range(node_count):
            child = master.child(i)
            child_data_info = []
            for index in range(len(self.headers_title)):
                child_data_info.append(child.text(index))
            data_info.append(child_data_info)
        communicate.display_info.emit(data_info)

    def get_current_master_item_data(self) -> list:
        """
        获取当前根节点下的所有信息
        :return:
        """
        master: QTreeWidgetItem = self.group_tree.currentItem()
        node_count = master.childCount()

        data_info = []
        for i in range(node_count):
            child = master.child(i)
            child_data_info = []
            for index in range(len(self.headers_title)):
                child_data_info.append(child.text(index))
            data_info.append(child_data_info)
        return data_info

    def get_top_row_count(self) -> int:
        """
        获取根节个数
        :return:
        """
        # 获取根节个数
        count = self.group_tree.topLevelItemCount()
        # 根据索引获取根节点
        # master = self.group_tree.topLevelItem(1)
        return count

    def get_child_row_count(self, p_int: int) -> int:
        """
        获取子节个数
        :param p_int: 上一级节点索引
        :return:
        """
        # 获取子节个数
        count = self.group_tree.topLevelItem(p_int).childCount()
        # 根据上一级节点获取子节点对象
        # item = self.group_tree.topLevelItem(p_int).child(0)
        return count

    def add_master_item(self, title: str = "新根节点") -> QTreeWidgetItem:
        """
        添加根节点
        :param title:
        :return:
        """
        master = QTreeWidgetItem(self.group_tree)
        master.setText(0, title)
        # child.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable)
        return master

    def master_exists(self, _name: str) -> (bool, QTreeWidgetItem):
        """
        判断根节点名称是否存在
        :param _name: 名称
        :return: 返回存在的节点
        """
        if not _name:
            master: QTreeWidgetItem = self.group_tree.topLevelItem(0)
            return master
        # 获取根节个数
        master_count = self.group_tree.topLevelItemCount()
        for index in range(master_count - 1, -1, -1):
            # 获取根节点
            master: QTreeWidgetItem = self.group_tree.topLevelItem(index)
            name = master.text(0)
            if name == _name:
                return master
        return None

    def master_exists2(self, index: int) -> (bool, QTreeWidgetItem):
        """
        判断根节点索引是否存在
        :param index: 名称
        :return: 返回存在的节点
        """
        # 获取根节个数
        master_count = self.group_tree.topLevelItemCount()
        if index <= master_count:
            master = self.group_tree.topLevelItem(index)
            return master
        return None

    def add_child_item(self, name: str, item: list) -> None:
        """
        根据name获取根节点
        根节点不存在,创建根节点
        添加子节点
        :param name: 根节点 name
        :param item: 数据序列
        :return:
        """
        # 判断根节点是否存在返回根节点
        master: QTreeWidgetItem = self.master_exists(name)
        if not master:
            # 添加一个根节点
            master: QTreeWidgetItem = self.add_master_item(name)

        count = master.childCount()  # 获根节点的子节点个数
        item.pop(0)
        item.insert(0, count + 1)  # 插入ID
        child = QTreeWidgetItem()
        child.setCheckState(0, Qt.Unchecked)
        for index, value in enumerate(item):
            child.setText(index, str(value))
            # if index == 14:
            #     # 备注列
            #     child.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable)  # 设为可编辑,整行可编辑
        master.addChild(child)

        # 更新分组计数
        self.update_count()

        if settings.REAL_TIME_REFRESH:
            # 实时刷新上线数据
            self.master_item_real_time_refresh()

    def add_child_item2(self, index: int, item: list) -> None:
        """
        根据索引获取根节点
        索引不存在就创建一个根节点
        添加子节点

        bug:
        存在安全问题,如果检索的索引远远超出界限,将不能正确插入所需要的节点
        :param index: 根节点索引
        :param item: 数据序列
        :return:
        """
        # 获取根节点
        master = self.master_exists2(index)
        if not master:
            # 添加一个根节点
            master = self.add_master_item()

        count = master.childCount()
        item.pop(0)
        item.insert(0, count + 1)  # 插入ID
        child = QTreeWidgetItem()
        child.setCheckState(0, Qt.Unchecked)
        for index, value in enumerate(item):
            child.setText(index, str(value))
        master.addChild(child)

    def add_child_item3(self, item: list) -> None:
        """
        获取当前根节点
        添加子节点
        :param item: 数据序列
        :return:
        """
        # 获取当前根节点
        master: QTreeWidgetItem = self.group_tree.currentItem()
        if master.parent():
            print("当前不是根节点")
            return
        count = master.childCount()
        item.pop(0)
        item.insert(0, count + 1)  # 插入ID
        child = QTreeWidgetItem()
        child.setCheckState(0, Qt.Unchecked)
        for index, value in enumerate(item):
            child.setText(index, value)
        master.addChild(child)

    def child_exists(self, _name: str) -> (bool, QTreeWidgetItem):
        """
        判断子节点名称是否存在
        :param _name: 名称
        :return: 返回存在的节点
        """
        # 获取根节个数
        master_count = self.group_tree.topLevelItemCount()
        for index in range(master_count):
            # 获取根节点
            master: QTreeWidgetItem = self.group_tree.topLevelItem(index)
            child_count = master.childCount()
            for index_node in range(child_count):
                child = master.child(index_node)
                name = child.text(1)
                if name == _name:
                    return child
        return None

    def del_child_item(self, name: str, out_net: str) -> None:
        """
        根据name获取根节点
        根节点不存在,创建根节点
        添加子节点
        :param name: 根节点 分组
        :param out_net: 子节点 主机信息
        :return:
        """
        # 判断根节点是否存在返回根节点
        master: QTreeWidgetItem = self.master_exists(name)
        if not master:
            logger.error("主机下线失败 - 分组不存在: {}".format(name))
            return None
        child = self.child_exists(out_net)
        if not child:
            logger.error("主机下线失败 - 主机信息未找到: {}".format(out_net))
            return None
        # 删除子节点
        master.removeChild(child)

        # 更新分组计数
        self.update_count()
        # 更新索引
        self.update_index(master)

        if settings.REAL_TIME_REFRESH:
            # 实时刷新上线数据
            self.master_item_real_time_refresh()

    def update_count(self) -> None:
        """
        更新主节点 计数信息
        :return:
        """
        # QTreeWidgetItem(self.group_tree).text(0)
        # 获取根节个数
        count = self.group_tree.topLevelItemCount()
        # 根据索引获取根节点
        for index in range(count):
            master = self.group_tree.topLevelItem(index)
            child_count = master.childCount()
            # 在线主机 (0)
            text = "({})".format(child_count)
            master.setText(1, text)

    @staticmethod
    def update_index(master: QTreeWidgetItem) -> None:
        """
        更新子节点 索引信息
        :return:
        """
        # QTreeWidgetItem(self.group_tree).text(0)
        # 获取子节点个数
        count = master.childCount()
        # 根据索引获取子节点
        for index in range(count):
            child = master.child(index)
            # 在线主机 (0)
            child.setText(0, str(index + 1))

    # noinspection PyArgumentList
    def re_translate_ui(self) -> None:
        self.group_tree.headerItem().setText(0,
                                             _translate("GroupInfoUI", "Id"))
        self.group_tree.headerItem().setText(1,
                                             _translate("GroupInfoUI", "主机信息"))
        self.group_tree.headerItem().setText(2,
                                             _translate("GroupInfoUI", "备注"))

    def test_date(self) -> None:
        for i in range(10):
            self.add_child_item2(0, [str(i), str(i * 100), ""])
        for i in range(200):
            self.add_child_item2(1, [str(i), str(i * 100), ""])
        self.update_count()
Beispiel #8
0
class AddToProject(QDialog):

    def __init__(self, pathProjects, parent=None):
        #pathProjects must be a list
        super(AddToProject, self).__init__(parent)
        self.setWindowTitle(_translate("AddToProject", "Add File to Project"))
        self.pathSelected = ''
        vbox = QVBoxLayout(self)

        self._tree = QTreeWidget()
        self._tree.header().setHidden(True)
        self._tree.setSelectionMode(QTreeWidget.SingleSelection)
        self._tree.setAnimated(True)
        vbox.addWidget(self._tree)
        hbox = QHBoxLayout()
        btnAdd = QPushButton(_translate("AddToProject", "Add here!"))
        btnCancel = QPushButton(_translate("AddToProject", "Cancel"))
        hbox.addWidget(btnCancel)
        hbox.addWidget(btnAdd)
        vbox.addLayout(hbox)
        #load folders
        self._root = None
        self._loading_items = {}
        self.loading_projects(pathProjects)
        self._thread_execution = ThreadExecution(
            self._thread_load_projects, args=[pathProjects])
        self._thread_execution.finished.connect(self._callback_load_project)
        self._thread_execution.start()

        btnCancel.clicked['bool'].connect(self.close)
        btnAdd.clicked['bool'].connect(self._select_path)

    def loading_projects(self, projects):
        for project in projects:
            loadingItem = LoadingItem()
            item = loadingItem.add_item_to_tree(project, self._tree,
                                                parent=self)
            self._loading_items[project] = item

    def _thread_load_projects(self, projects):
        structures = []
        for pathProject in projects:
            folderStructure = file_manager.open_project(pathProject)
            structures.append((folderStructure, pathProject))
        self._thread_execution.storage_values = structures

    def _callback_load_project(self):
        structures = self._thread_execution.storage_values
        if structures:
            for structure, path in structures:
                item = self._loading_items.pop(path, None)
                if item is not None:
                    index = self._tree.indexOfTopLevelItem(item)
                    self._tree.takeTopLevelItem(index)
                self._load_project(structure, path)

    def _select_path(self):
        item = self._tree.currentItem()
        if item:
            self.pathSelected = item.toolTip(0)
            self.close()

    def _load_project(self, folderStructure, folder):
        if not folder:
            return

        name = file_manager.get_basename(folder)
        item = QTreeWidgetItem(self._tree)
        item.setText(0, name)
        item.setToolTip(0, folder)
        item.setIcon(0, QIcon(resources.IMAGES['tree-folder']))
        if folderStructure[folder][1] is not None:
            folderStructure[folder][1].sort()
        self._load_folder(folderStructure, folder, item)
        item.setExpanded(True)
        self._root = item

    def _load_folder(self, folderStructure, folder, parentItem):
        items = folderStructure[folder]

        if items[1] is not None:
            items[1].sort()
        for _file in items[1]:
            if _file.startswith('.'):
                continue
            subfolder = QTreeWidgetItem(parentItem)
            subfolder.setText(0, _file)
            subfolder.setToolTip(0, os.path.join(folder, _file))
            subfolder.setIcon(0, QIcon(resources.IMAGES['tree-folder']))
            self._load_folder(folderStructure,
                              os.path.join(folder, _file), subfolder)
class CreateReportPopup1(QDialog):
    def __init__(self, variety_info, *args, **kwargs):
        super(CreateReportPopup, self).__init__(*args, **kwargs)
        self.variety_info = variety_info
        # 总布局-左右
        layout = QHBoxLayout()
        # 左侧上下布局
        llayout = QVBoxLayout()
        # 左侧是品种树
        self.left_tree = QTreeWidget(clicked=self.variety_tree_clicked)
        self.left_tree.header().hide()
        self.left_tree.setMaximumWidth(160)
        llayout.addWidget(self.left_tree)
        layout.addLayout(llayout)
        # 右侧上下布局
        rlayout = QVBoxLayout()
        # 所属品种
        attach_varieties_layout = QHBoxLayout()
        attach_varieties_layout.addWidget(QLabel('所属品种:'))
        self.attach_varieties = QLabel()
        self.attach_varieties.variety_ids = list()  # id字符串
        attach_varieties_layout.addWidget(self.attach_varieties)
        attach_varieties_layout.addStretch()
        attach_varieties_layout.addWidget(QPushButton(
            '清空',
            objectName='deleteBtn',
            cursor=Qt.PointingHandCursor,
            clicked=self.clear_attach_varieties),
                                          alignment=Qt.AlignRight)
        rlayout.addLayout(attach_varieties_layout)
        rlayout.addWidget(QLabel(parent=self, objectName='varietyError'))
        # 所属分类
        attach_category_layout = QHBoxLayout()
        attach_category_layout.addWidget(QLabel('所属分类:'))
        self.category_combo = QComboBox()
        self.category_combo.setMinimumWidth(400)
        attach_category_layout.addWidget(self.category_combo)
        attach_category_layout.addStretch()
        # attach_category_layout.addWidget(QPushButton('新分类?', objectName='newCategoryBtn', cursor=Qt.PointingHandCursor, clicked=self.add_new_category), alignment=Qt.AlignRight)
        rlayout.addLayout(attach_category_layout)
        rlayout.addWidget(QLabel(parent=self, objectName='categoryError'))
        # 选择报告
        rlayout.addWidget(QPushButton('选择报告', clicked=self.select_reports),
                          alignment=Qt.AlignLeft)
        # 预览表格
        self.review_table = QTableWidget()
        self.review_table.verticalHeader().hide()
        rlayout.addWidget(self.review_table)
        # 提交按钮
        self.commit_button = QPushButton('提交',
                                         clicked=self.commit_upload_report)
        rlayout.addWidget(self.commit_button, alignment=Qt.AlignRight)
        layout.addLayout(rlayout)
        self.setLayout(layout)
        self.setMinimumWidth(800)
        self.setStyleSheet("""
        #deleteBtn{
            border: none;
            color:rgb(200,100,80)
        }
        #newCategoryBtn{
            border:none;
            color:rgb(80,100,200)
        }
        """)
        self.geTreeVarieties()
        for category_item in [("日报", 1), ("周报", 2), ("月报", 3), ("年报", 4),
                              ("专题报告", 5), ("其他", 0)]:
            self.category_combo.addItem(category_item[0], category_item[1])

    # 获取分类选框内容
    def getCategoryCombo(self):
        try:
            r = requests.get(url=settings.SERVER_ADDR +
                             'home/data-category/normal_report/?mc=' +
                             settings.app_dawn.value('machine'), )
            response = json.loads(r.content.decode('utf-8'))
            if r.status_code != 200:
                raise ValueError(response['message'])
        except Exception:
            pass
        else:
            self.category_combo.clear()
            for category_item in response['data']:
                self.category_combo.addItem(category_item['name'],
                                            category_item['id'])
            # 加入其它
            self.category_combo.addItem('其它', 0)

    # 清空所属品种
    def clear_attach_varieties(self):
        self.attach_varieties.setText('')
        self.attach_varieties.variety_ids.clear()  # id列表

    # 新增报告分类
    # def add_new_category(self):
    #     popup = QDialog(parent=self)
    #     def commit_new_category():
    #         print('提交新建分类')
    #         name = re.sub(r'\s+', '', popup.category_name_edit.text())
    #         if not name:
    #             popup.name_error_label.setText('请输入正确的分类名称!')
    #             return
    #         # 提交常规报告分类
    #         try:
    #             r = requests.post(
    #                 url=settings.SERVER_ADDR + 'home/data-category/normal_report/?mc=' + settings.app_dawn.value('machine'),
    #                 headers={'AUTHORIZATION': settings.app_dawn.value('AUTHORIZATION')},
    #                 data=json.dumps({'name': name})
    #             )
    #             response = json.loads(r.content.decode('utf-8'))
    #             if r.status_code != 201:
    #                 raise ValueError(response['message'])
    #         except Exception as e:
    #             popup.name_error_label.setText(str(e))
    #         else:
    #             # 重新获取填充分类选框
    #             self.getCategoryCombo()
    #             popup.close()
    #
    #     popup.setWindowTitle('新建分类')
    #     new_layout = QGridLayout()
    #     new_layout.addWidget(QLabel('名称:'), 0, 0)
    #     popup.category_name_edit = QLineEdit()
    #     new_layout.addWidget(popup.category_name_edit, 0, 1)
    #     popup.name_error_label = QLabel()
    #     new_layout.addWidget(popup.name_error_label, 1, 0, 1, 2)
    #     new_layout.addWidget(QPushButton('确定', clicked=commit_new_category), 2, 1)
    #     popup.setLayout(new_layout)
    #     if not popup.exec_():
    #         popup.deleteLater()
    #         del popup

    # 获取左侧品种树的品种内容
    def geTreeVarieties(self):
        # 填充品种树
        for group_item in self.variety_info:
            group = QTreeWidgetItem(self.left_tree)
            group.setText(0, group_item['name'])
            group.gid = group_item['id']
            # 添加子节点
            for variety_item in group_item['subs']:
                child = QTreeWidgetItem()
                child.setText(0, variety_item['name'])
                child.vid = variety_item['id']
                group.addChild(child)
        self.left_tree.expandAll()  # 展开所有

    # 点击左侧品种树
    def variety_tree_clicked(self):
        item = self.left_tree.currentItem()
        if item.childCount():  # has children open the root
            if item.isExpanded():
                item.setExpanded(False)
            else:
                item.setExpanded(True)
        text = item.text(0)
        if item.parent(
        ) and item.vid not in self.attach_varieties.variety_ids:  # 所属品种中增加当前品种
            self.attach_varieties.setText(self.attach_varieties.text() + text +
                                          '、')
            self.attach_varieties.variety_ids.append(item.vid)

    # 选择报告文件
    def select_reports(self):
        path_list, _ = QFileDialog.getOpenFileNames(self, '打开文件', '',
                                                    "PDF files(*.pdf)")
        # 遍历报告文件填充预览表格与设置状态
        self.review_table.setRowCount(len(path_list))
        self.review_table.setColumnCount(4)
        self.review_table.setHorizontalHeaderLabels(
            ['序号', '报告名', '报告日期', '状态'])
        self.review_table.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.review_table.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        for row, file_path in enumerate(path_list):
            item_1 = QTableWidgetItem(str(row + 1))
            item_1.file_path = file_path
            item_1.setTextAlignment(Qt.AlignCenter)
            self.review_table.setItem(row, 0, item_1)
            file_name = file_path.rsplit('/', 1)[1]
            item_2 = QTableWidgetItem(file_name)
            item_2.setTextAlignment(Qt.AlignCenter)
            self.review_table.setItem(row, 1, item_2)
            # 日期控件
            date_edit = QDateEdit(QDate.currentDate())
            date_edit.setCalendarPopup(True)
            date_edit.setDisplayFormat('yyyy-MM-dd')
            self.review_table.setCellWidget(row, 2, date_edit)
            # 装态
            item_4 = QTableWidgetItem('等待上传')
            item_4.setTextAlignment(Qt.AlignCenter)
            self.review_table.setItem(row, 3, item_4)

    # 确认上传报告
    def commit_upload_report(self):
        self.commit_button.setEnabled(False)
        # 获取所属品种列表id
        attach_variety_ids = self.attach_varieties.variety_ids
        if not attach_variety_ids:
            self.findChild(QLabel, 'varietyError').setText('请选择所属品种(支持多选)')
            return
        # 获取所属分类
        attach_category = self.category_combo.currentData()
        # 遍历表格打包文件信息(上传线程处理,每上传一个发个信号过来修改上传状态)
        file_message_list = list()
        for row in range(self.review_table.rowCount()):
            message_item = self.review_table.item(row, 3)  # 设置上传状态
            message_item.setText('正在上传...')
            message_item.setForeground(QBrush(QColor(20, 50, 200)))
            # 设置颜色
            file_message_list.append({
                'file_name':
                self.review_table.item(row, 1).text(),
                'file_path':
                self.review_table.item(row, 0).file_path,
                'category_id':
                attach_category,
                'variety_ids':
                attach_variety_ids,
                'file_date':
                self.review_table.cellWidget(row, 2).text(),
                'row_index':
                row
            })
        # 开启线程
        if hasattr(self, 'uploading_thread'):
            del self.uploading_thread
        self.uploading_thread = UploadReportThread(
            file_list=file_message_list,
            machine_code=settings.app_dawn.value('machine'),
            token=settings.app_dawn.value('AUTHORIZATION'),
        )
        self.uploading_thread.finished.connect(
            self.uploading_thread.deleteLater)
        self.uploading_thread.response_signal.connect(
            self.change_loading_state)
        self.uploading_thread.start()

    # 上传的线程返回消息
    def change_loading_state(self, row, succeed):
        item = self.review_table.item(row, 3)
        if succeed:
            item.setText('上传成功!')
            item.setForeground(QBrush(QColor(20, 200, 50)))
        else:
            item.setText('上传失败...')
            item.setForeground(QBrush(QColor(200, 20, 50)))
        if row == self.review_table.rowCount() - 1:
            self.commit_button.setEnabled(True)
Beispiel #10
0
class SessionsManager(QDialog):
    """Session Manager, to load different configurations of ninja."""
    def __init__(self, parent=None):
        super(SessionsManager, self).__init__(parent, Qt.Dialog)
        self._ninja = parent
        self.setModal(True)
        self.setWindowTitle(translations.TR_SESSIONS_TITLE)
        self.setMinimumWidth(550)
        self.setMinimumHeight(450)
        self._manager = _SessionManager(parent)
        self._load_ui()

    def install(self):
        self._manager.load_sessions()

    def _load_ui(self):
        main_layout = QVBoxLayout(self)
        main_layout.addWidget(QLabel(translations.TR_SESSIONS_DIALOG_BODY))
        main_hbox = QHBoxLayout()
        # Session list
        session_layout = QVBoxLayout()
        self._session_list = QTreeWidget()
        self._session_list.setHeaderLabels(["Session", "Last Modified"])
        session_layout.addWidget(self._session_list)
        # Content frame
        content_frame = QFrame()
        content_frame.hide()
        frame_layout = QVBoxLayout(content_frame)
        content_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        session_layout.addWidget(content_frame)
        frame_layout.setContentsMargins(0, 0, 0, 0)
        self._content_list = QTreeWidget()
        self._content_list.setHeaderHidden(True)
        frame_layout.addWidget(self._content_list)
        # Separator line
        line_frame = QFrame()
        line_frame.setFrameStyle(QFrame.VLine | QFrame.Sunken)
        # Buttons
        btn_layout = QVBoxLayout()
        btn_create = QPushButton(translations.TR_SESSIONS_BTN_CREATE)
        btn_activate = QPushButton(translations.TR_SESSIONS_BTN_ACTIVATE)
        btn_update = QPushButton(translations.TR_SESSIONS_BTN_UPDATE)
        btn_delete = QPushButton(translations.TR_SESSIONS_BTN_DELETE)
        btn_details = QPushButton(translations.TR_SESSIONS_BTN_DETAILS)
        btn_details.setCheckable(True)
        # Add buttons to layout
        btn_layout.addWidget(btn_create)
        btn_layout.addWidget(btn_activate)
        btn_layout.addWidget(btn_update)
        btn_layout.addWidget(btn_delete)
        btn_layout.addStretch()
        btn_layout.addWidget(btn_details)
        # Add widgets and layouts to the main layout
        main_layout.addLayout(main_hbox)
        main_hbox.addLayout(session_layout)
        main_hbox.addWidget(line_frame)
        main_hbox.addLayout(btn_layout)
        main_hbox.setSizeConstraint(QLayout.SetFixedSize)
        btn_details.toggled[bool].connect(content_frame.setVisible)
        # Connections
        self._session_list.itemSelectionChanged.connect(
            self.load_session_content)
        btn_activate.clicked.connect(self.open_session)
        btn_update.clicked.connect(self.save_session)
        btn_create.clicked.connect(self.create_session)
        btn_delete.clicked.connect(self.delete_session)

    def __load_sessions(self):
        for session_name in self._manager.sessions:
            item = QTreeWidgetItem()
            item.setText(0, session_name)
            item.setText(1, "FIXME: manage this!")
            self._session_list.addTopLevelItem(item)
        self._session_list.setCurrentItem(self._session_list.topLevelItem(0))

    def load_session_content(self):
        """Load the selected session, replacing the current session."""
        item = self._session_list.currentItem()
        self._content_list.clear()
        if item is not None:
            key = item.text(0)
            files, projects = self._manager.get_session(key)
            files_parent = QTreeWidgetItem(self._content_list,
                                           [translations.TR_FILES])
            for ffile in files:
                QTreeWidgetItem(files_parent, [ffile[0]])
            projects_parent = QTreeWidgetItem(self._content_list,
                                              [translations.TR_PROJECT])
            for project in projects:
                QTreeWidgetItem(projects_parent, [project])

            files_parent.setExpanded(True)
            projects_parent.setExpanded(True)

    def create_session(self):
        """Create a new Session."""
        session_info = QInputDialog.getText(
            None, translations.TR_SESSIONS_CREATE_TITLE,
            translations.TR_SESSIONS_CREATE_BODY)
        if session_info[1]:
            session_name = session_info[0]
            if not session_name or session_name in settings.SESSIONS:
                QMessageBox.information(self,
                                        translations.TR_SESSIONS_MESSAGE_TITLE,
                                        translations.TR_SESSIONS_MESSAGE_BODY)
                return
            self._manager.save_session_data(session_name)
        self.close()

    def save_session(self):
        """Save current session"""
        if self._session_list.currentItem():
            session_name = self._session_list.currentItem().text(0)
            self._manager.save_session_data(session_name)
            self._ninja.show_message(
                translations.TR_SESSIONS_UPDATED_NOTIF.format(session_name))
            self.load_session_content()

    def open_session(self):
        """Open a saved session"""
        if self._session_list.currentItem():
            session_name = self._session_list.currentItem().text(0)
            self._manager.load_session(session_name)
            self._manager.set_current_session(session_name)
            self.close()

    def delete_session(self):
        """Delete a session"""
        if self._session_list.currentItem():
            key = self._session_list.currentItem().text(0)
            self._manager.delete_session(key)
            self._session_list.takeTopLevelItem(
                self._session_list.currentIndex().row())

    @property
    def current_session(self):
        return self._manager.current_session()

    def showEvent(self, event):
        super().showEvent(event)
        self.__load_sessions()

    def hideEvent(self, event):
        super().hideEvent(event)
        self._session_list.clear()
class CreateReportPopup(QDialog):
    def __init__(self, variety_info, *args, **kwargs):
        super(CreateReportPopup, self).__init__(*args, **kwargs)
        self.setWindowTitle("新建报告")
        self.variety_info = variety_info
        # 总布局-左右
        layout = QHBoxLayout()
        # 左侧上下布局
        llayout = QVBoxLayout()
        # 左侧是品种树
        self.left_tree = QTreeWidget(clicked=self.variety_tree_clicked)
        self.left_tree.header().hide()
        self.left_tree.setMaximumWidth(160)
        llayout.addWidget(self.left_tree)
        layout.addLayout(llayout)
        # 右侧上下布局
        rlayout = QVBoxLayout(spacing=10)
        # 所属品种
        attach_varieties_layout = QHBoxLayout()
        attach_varieties_layout.addWidget(QLabel('所属品种:'))
        self.attach_varieties = QLabel()
        self.attach_varieties.variety_ids = list()  # id字符串
        attach_varieties_layout.addWidget(self.attach_varieties)
        attach_varieties_layout.addStretch()
        attach_varieties_layout.addWidget(QPushButton(
            '清空',
            objectName='deleteBtn',
            cursor=Qt.PointingHandCursor,
            clicked=self.clear_attach_varieties),
                                          alignment=Qt.AlignRight)
        rlayout.addLayout(attach_varieties_layout)
        # 所属分类
        attach_category_layout = QHBoxLayout()
        attach_category_layout.addWidget(QLabel('所属分类:'))
        self.category_combo = QComboBox()
        self.category_combo.setMinimumWidth(400)
        attach_category_layout.addWidget(self.category_combo)
        attach_category_layout.addStretch()
        rlayout.addLayout(attach_category_layout)
        date_layout = QHBoxLayout()
        date_layout.addWidget(QLabel("报告日期:", self))
        self.date_edit = QDateEdit(QDate.currentDate())
        self.date_edit.setCalendarPopup(True)
        self.date_edit.setDisplayFormat('yyyy-MM-dd')
        date_layout.addWidget(self.date_edit)
        date_layout.addStretch()
        rlayout.addLayout(date_layout)
        title_layout = QHBoxLayout()
        title_layout.addWidget(QLabel('报告标题:', self))
        self.title_edit = QLineEdit(self)
        title_layout.addWidget(self.title_edit)
        rlayout.addLayout(title_layout)
        # 选择报告
        select_report_layout = QHBoxLayout()
        select_report_layout.addWidget(QLabel('报告文件:', self))
        self.report_file_edit = FileLineEdit()
        self.report_file_edit.setParent(self)
        select_report_layout.addWidget(self.report_file_edit)
        rlayout.addLayout(select_report_layout)
        # 提交按钮
        self.commit_button = QPushButton('提交',
                                         clicked=self.commit_upload_report)
        rlayout.addWidget(self.commit_button, alignment=Qt.AlignRight)
        rlayout.addStretch()
        layout.addLayout(rlayout)
        self.setLayout(layout)
        self.setFixedSize(800, 500)
        self.setStyleSheet("""
        #deleteBtn{
            border: none;
            color:rgb(200,100,80)
        }
        #newCategoryBtn{
            border:none;
            color:rgb(80,100,200)
        }
        """)
        self.geTreeVarieties()
        for category_item in [("日报", 1), ("周报", 2), ("月报", 3), ("年报", 4),
                              ("专题报告", 5), ("其他", 0)]:
            self.category_combo.addItem(category_item[0], category_item[1])

    def geTreeVarieties(self):
        # 填充品种树
        for group_item in self.variety_info:
            group = QTreeWidgetItem(self.left_tree)
            group.setText(0, group_item['name'])
            group.gid = group_item['id']
            # 添加子节点
            for variety_item in group_item['subs']:
                child = QTreeWidgetItem()
                child.setText(0, variety_item['name'])
                child.vid = variety_item['id']
                group.addChild(child)
        self.left_tree.expandAll()  # 展开所有

    # 清空所属品种
    def clear_attach_varieties(self):
        self.attach_varieties.setText('')
        self.attach_varieties.variety_ids.clear()  # id列表

    def commit_upload_report(self):
        data = dict()
        title = self.title_edit.text()
        file_path = self.report_file_edit.text()
        if not all([title, file_path]):
            QMessageBox.information(self, "错误", "请填写完整信息")
            return
        data['utoken'] = settings.app_dawn.value('AUTHORIZATION')
        data['title'] = title
        data['link_varieties'] = ','.join(
            map(str, self.attach_varieties.variety_ids))
        data["custom_time"] = self.date_edit.text()
        data['category'] = self.category_combo.currentData()
        # 读取文件
        file = open(file_path, "rb")
        file_content = file.read()
        file.close()
        filename = file_path.rsplit('/', 1)[1]
        # 文件内容字段
        data["report_file"] = (filename, file_content)
        encode_data = encode_multipart_formdata(data)
        try:
            r = requests.post(url=settings.SERVER_ADDR + 'report/',
                              headers={"Content-Type": encode_data[1]},
                              data=encode_data[0])
            response = json.loads(r.content.decode('utf8'))
            if r.status_code != 201:
                raise ValueError(response['message'])
        except Exception as e:
            QMessageBox.information(self, "错误", str(e))
        else:
            QMessageBox.information(self, "成功", "添加报告成功")
            self.close()

    # 点击左侧品种树
    def variety_tree_clicked(self):
        item = self.left_tree.currentItem()
        if item.childCount():  # has children open the root
            if item.isExpanded():
                item.setExpanded(False)
            else:
                item.setExpanded(True)
        text = item.text(0)
        if item.parent(
        ) and item.vid not in self.attach_varieties.variety_ids:  # 所属品种中增加当前品种
            self.attach_varieties.setText(self.attach_varieties.text() + text +
                                          '、')
            self.attach_varieties.variety_ids.append(item.vid)
Beispiel #12
0
class DBogUI(object):
    def __init__(self):
        self.db_handler = MyDBHandler()
        self.sqlTranslator = SqlTranslator()
        self.page_size = 10
        self.former_table_data = []
        self.current_table_data = []
        self.changedTableData = []
        self.cell_editable_flg = 0
        self.mainWidget = None
        self.centerLayout = None
        self.stackedWidget = None
        self.statusbar = None
        self.data_page = None
        self.data_page_layout = None
        self.menubar = None
        self.dataPanel = None
        self.data_panel_layout = None
        self.sqlTextEdit = None
        self.table_widget = None
        self.btn_panel = None
        self.btn_panel_layout = None
        self.addLnBtn = None
        self.rmvLnBtn = None
        self.queryBtn = None
        self.editBtn = None
        self.saveBtn = None
        self.treePanel = None
        self.treePanelLayout = None
        self.tableTree = None
        self.translate_page = None
        self.translate_page_layout = None
        self.translate_up_panel = None
        self.translate_up_panel_layout = None
        self.src_sql_text_edit = None
        self.dest_sql_text_edit = None
        self.translate_down_panel = None
        self.translate_down_panel_layout = None
        self.from_sql_type_combobox = None
        self.to_sql_type_combobox = None
        self.translate_btn = None
        self.swap_btn = None
        self.clear_btn = None
        self.login_page = None
        self.usmLabel = None
        self.unmTextEdit = None
        self.pwdLabel = None
        self.pwdTextEdit = None
        self.hostLabel = None
        self.hostTextEdit = None
        self.dbNameLabel = None
        self.dbNameTextEdit = None
        self.cnntDbBtn = None
        self.load_cfg_btn = None
        self.error_widgets = []
        self.plain_color = None

    # setup UI
    def setup_ui(self, MainWindow):
        # UI MainWindow
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(900, 600)
        # center widgets
        self.mainWidget = QWidget(MainWindow)
        # 设置字体
        self.mainWidget.setFont(QFont("Roman times", 11))
        self.mainWidget.setObjectName("mainWidget")
        MainWindow.setCentralWidget(self.mainWidget)
        self.centerLayout = QVBoxLayout(self.mainWidget)
        # 设置stacked widget
        self.stackedWidget = QStackedWidget(self.mainWidget)
        self.centerLayout.addWidget(self.stackedWidget)
        # 设置data page
        self.build_data_page()
        # layout不同的比例区分大小
        self.data_page_layout.setStretchFactor(self.dataPanel, 4)
        self.data_page_layout.setStretchFactor(self.treePanel, 1)
        # 设置login面板
        self.build_login_page()
        # 设置translate面板
        self.build_translate_page()
        # 将三个面板,加入stackedWidget
        self.stackedWidget.addWidget(self.data_page)
        self.stackedWidget.addWidget(self.login_page)
        self.stackedWidget.addWidget(self.translate_page)
        # menu
        self.build_menu_bar(MainWindow)
        # status bar
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        # 刷新
        self.re_translate_ui(MainWindow)
        QMetaObject.connectSlotsByName(MainWindow)
        # refresh tree list
        self.db_handler.config(None, None, None, None)
        self.paint_tree()

    # data page
    def build_data_page(self):
        self.data_page = QWidget(self.mainWidget)
        self.centerLayout.addWidget(self.data_page)
        self.data_page_layout = QHBoxLayout(self.data_page)
        # 设置data面板
        self.build_tree_panel()
        # 设置data面板
        self.build_data_panel()
        # set event
        self.set_data_page_event()

    # menu
    def build_menu_bar(self, MainWindow):
        self.menubar = QMenuBar(MainWindow)
        self.menubar.setGeometry(QRect(0, 0, 400, 50))
        self.menubar.move(100, 100)
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        # 创建一个事件和一个特定图标
        show_data_action = QAction(QIcon('../resources/icon/database.png'), 'Operate', self)
        # show_data_action.setShortcut('Ctrl+Q')  # 设置事件的快捷方式
        show_data_action.setStatusTip('show data panel')  # 设置事件的状态提示
        show_data_action.triggered.connect(lambda: self.switch_page(0))  # 事件的触发
        login_menu = self.menubar.addMenu('&Data')  # 添加菜单file
        login_menu.addAction(show_data_action)  # 菜单添加事件
        # 创建一个事件和一个特定图标
        show_login_action = QAction(QIcon('../resources/icon/configure.png'), 'Config', self)
        # show_login_action.setShortcut('Ctrl+P')  # 设置事件的快捷方式
        show_login_action.setStatusTip('show login panel')  # 设置事件的状态提示
        show_login_action.triggered.connect(lambda: self.switch_page(1))  # 事件的触发
        login_menu = self.menubar.addMenu('&Login')  # 添加菜单file
        login_menu.addAction(show_login_action)  # 菜单添加事件
        # 创建一个事件和一个特定图标
        show_translate_action = QAction(QIcon('../resources/icon/translate.png'), 'Translate', self)
        # show_login_action.setShortcut('Ctrl+P')  # 设置事件的快捷方式
        show_translate_action.setStatusTip('show translate page')  # 设置事件的状态提示
        show_translate_action.triggered.connect(lambda: self.switch_page(2))  # 事件的触发
        translate_menu = self.menubar.addMenu('&Translate')  # 添加菜单file
        translate_menu.addAction(show_translate_action)  # 菜单添加事件

    # 设置data面板
    def build_data_panel(self):
        # data panel
        self.dataPanel = QWidget(self.data_page)
        self.data_page_layout.addWidget(self.dataPanel)
        self.data_panel_layout = QVBoxLayout(self.dataPanel)
        # SQL输入框
        self.sqlTextEdit = QTextEdit(self.dataPanel)
        self.sqlTextEdit.setObjectName("textEdit")
        # self.sqlTextEdit.setText("Enter your SQL here...")
        self.data_panel_layout.addWidget(self.sqlTextEdit)
        # 数据表格
        self.table_widget = TableWidget(self.dataPanel)
        self.table_widget.init_ui(0, 0, self.dataPanel)
        self.table_widget.set_page_controller(0)  # 表格设置页码控制
        self.table_widget.control_signal.connect(self.control_page)
        self.table_widget.setFixedSize(700, 400)
        self.data_panel_layout.addWidget(self.table_widget)
        # build inside panel
        self.build_btn_panel()
        self.data_panel_layout.setStretchFactor(self.sqlTextEdit, 2)
        self.data_panel_layout.setStretchFactor(self.table_widget, 6)
        self.data_panel_layout.setStretchFactor(self.btn_panel, 2)

    # button panel
    def build_btn_panel(self):
        # button panel
        self.btn_panel = QWidget(self.data_page)
        self.btn_panel.setFont(QFont("YaHei", 10))
        self.data_panel_layout.addWidget(self.btn_panel)
        self.btn_panel_layout = QHBoxLayout(self.btn_panel)
        # 表格操作:增加数据行按钮
        self.addLnBtn = QPushButton(self.btn_panel)
        self.addLnBtn.setMaximumSize(100, 80)
        self.addLnBtn.setObjectName("addLineButton")
        self.btn_panel_layout.addWidget(self.addLnBtn)
        # 表格操作:增加数据行按钮
        self.rmvLnBtn = QPushButton(self.btn_panel)
        self.rmvLnBtn.setObjectName("rmvLineButton")
        self.btn_panel_layout.addWidget(self.rmvLnBtn)
        # 查询按钮
        self.queryBtn = QPushButton(self.btn_panel)
        # self.queryBtn.setGeometry(QRect(150, 500, 100, 50))
        self.queryBtn.setObjectName("QueryButton")
        self.btn_panel_layout.addWidget(self.queryBtn)
        # 更新按钮
        self.editBtn = QPushButton(self.btn_panel)
        # self.editBtn.setGeometry(QRect(150, 500, 100, 50))
        self.editBtn.setObjectName("EditButton")
        self.btn_panel_layout.addWidget(self.editBtn)
        # 保存按钮
        self.saveBtn = QPushButton(self.btn_panel)
        # self.saveBtn.setGeometry(QRect(225, 500, 100, 50))
        self.saveBtn.setObjectName("SaveButton")
        self.btn_panel_layout.addWidget(self.saveBtn)
        # set partition / relative size
        self.btn_panel_layout.setStretchFactor(self.addLnBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.rmvLnBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.queryBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.editBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.saveBtn, 1)

    # left catalog tree panel
    def build_tree_panel(self):
        self.treePanel = QWidget(self.data_page)
        self.treePanel.setFont(QFont("SimHei", 11))
        self.treePanelLayout = QVBoxLayout(self.treePanel)
        # self.treePanel.setGeometry(QRect(0, 0, 100, 600))
        self.data_page_layout.addWidget(self.treePanel)
        # tableTree list
        self.tableTree = QTreeWidget(self.treePanel)
        self.treePanelLayout.addWidget(self.tableTree)
        # 设置列数
        self.tableTree.setColumnCount(1)
        self.tableTree.setColumnWidth(0, 100)
        # 设置头的标题
        self.tableTree.setHeaderLabel('Table List')
        # 绑定点击事件
        self.tableTree.clicked.connect(self.on_tree_clicked)

    # 加载树形菜单
    def paint_tree(self):
        if not CTools.isEmpty(self.db_handler.db_name):
            self.tableTree.clear()
            self.sqlTextEdit.setText("use %s;show tables;" % self.db_handler.db_name)
            table_lst = self.show_tables()
            # repaint tree nodes
            root = QTreeWidgetItem(self.tableTree)
            root.setText(0, self.db_handler.db_name)
            for i in range(len(table_lst)):
                table_name = table_lst[i]
                table_node = QTreeWidgetItem(root)
                table_node.setText(0, table_name)
            self.tableTree.addTopLevelItem(root)

    # data page
    def build_translate_page(self):
        self.translate_page = QWidget(self.mainWidget)
        self.centerLayout.addWidget(self.translate_page)
        self.translate_page_layout = QVBoxLayout(self.translate_page)
        self.build_translate_panel()

    # 设置data面板
    def build_translate_panel(self):
        # translate panel
        self.translate_up_panel = QWidget(self.translate_page)
        self.translate_page_layout.addWidget(self.translate_up_panel)
        # append widget
        self.translate_up_panel_layout = QHBoxLayout(self.translate_up_panel)
        # SQL输入框
        self.src_sql_text_edit = QTextEdit(self.translate_up_panel)
        self.src_sql_text_edit.setObjectName("src_sql_text_edit")
        self.src_sql_text_edit.setText("Enter source SQL to translate here...")
        self.translate_up_panel_layout.addWidget(self.src_sql_text_edit)
        # SQL输入框
        self.dest_sql_text_edit = QTextEdit(self.translate_up_panel)
        self.dest_sql_text_edit.setObjectName("dest_sql_text_edit")
        self.dest_sql_text_edit.setText("SQL translate result will be placed here...")
        self.dest_sql_text_edit.setReadOnly(True)
        self.translate_up_panel_layout.addWidget(self.dest_sql_text_edit)
        # translate panel
        self.translate_down_panel = QWidget(self.translate_page)
        self.translate_page_layout.addWidget(self.translate_down_panel)
        # append widget
        self.translate_down_panel_layout = QHBoxLayout(self.translate_down_panel)
        # select from sql type
        self.from_sql_type_combobox = QComboBox(self.translate_down_panel)
        self.from_sql_type_combobox.setMaximumSize(150, 120)
        self.from_sql_type_combobox.setObjectName("from_sql_type_combobox")
        self.from_sql_type_combobox.addItems(self.sqlTranslator.valid_from_dialect)
        self.translate_down_panel_layout.addWidget(self.from_sql_type_combobox)
        self.plain_color = self.from_sql_type_combobox.palette().color(QPalette.Background).toRgb()
        # select to sql type
        self.to_sql_type_combobox = QComboBox(self.translate_down_panel)
        self.to_sql_type_combobox.setMaximumSize(150, 120)
        self.to_sql_type_combobox.setObjectName("to_sql_type_combobox")
        # self.to_sql_type_combobox.addItems(['', 'mssql', 'sqlserver', 'mssqlserver', 'mysql', 'oracle', 'db2', 'db2udb'])
        self.to_sql_type_combobox.addItems(self.sqlTranslator.valid_to_dialect)
        self.translate_down_panel_layout.addWidget(self.to_sql_type_combobox)
        # translate按钮
        self.translate_btn = QPushButton(self.translate_down_panel)
        self.translate_btn.setMaximumSize(150, 120)
        self.translate_btn.setObjectName("Translate")
        self.translate_btn.setText("Translate")
        self.translate_down_panel_layout.addWidget(self.translate_btn)
        self.translate_btn.clicked.connect(self.on_translate)
        # 交换按钮
        self.swap_btn = QPushButton(self.translate_down_panel)
        self.swap_btn.setMaximumSize(150, 120)
        self.swap_btn.setObjectName("Swap")
        self.swap_btn.setText("Swap")
        self.translate_down_panel_layout.addWidget(self.swap_btn)
        self.swap_btn.clicked.connect(self.on_swap)
        # 清空按钮
        self.clear_btn = QPushButton(self.translate_down_panel)
        self.clear_btn.setMaximumSize(150, 120)
        self.clear_btn.setObjectName("Clear")
        self.clear_btn.setText("Clear")
        self.translate_down_panel_layout.addWidget(self.clear_btn)
        self.clear_btn.clicked.connect(self.on_clear)

    def on_translate(self):
        src_sql_str = self.src_sql_text_edit.toPlainText()
        from_type = self.from_sql_type_combobox.currentText()
        to_type = self.to_sql_type_combobox.currentText()
        error_msg = ""
        if not src_sql_str or src_sql_str == "Enter source SQL to translate here...":
            error_msg += "source SQL should not be empty\n"
            self.src_sql_text_edit.setTextBackgroundColor(QColor(255, 0, 0, 255))
            self.error_widgets.append(self.src_sql_text_edit)
        else:
            if self.src_sql_text_edit in self.error_widgets:
                self.error_widgets.remove(self.src_sql_text_edit)
        if not from_type:
            error_msg += "from SQL Type should not be empty\n"
            self.from_sql_type_combobox.setStyleSheet("QComboBox{color: rgb(255,0,0,255)}")
            self.error_widgets.append(self.from_sql_type_combobox)
        else:
            if self.from_sql_type_combobox in self.error_widgets:
                self.error_widgets.remove(self.from_sql_type_combobox)
        if not to_type:
            error_msg += "to SQL Type should not be empty\n"
            self.to_sql_type_combobox.setStyleSheet("QComboBox{color: rgb(255,0,0,255)}")
            self.error_widgets.append(self.to_sql_type_combobox)
        else:
            if self.to_sql_type_combobox in self.error_widgets:
                self.error_widgets.remove(self.to_sql_type_combobox)
        if self.error_widgets:
            QMessageBox.warning(None, "Warning", error_msg)
            return
        if self.src_sql_text_edit in self.error_widgets:
            self.src_sql_text_edit.setTextBackgroundColor(QColor(255, 255, 255, 255))
        if self.from_sql_type_combobox in self.error_widgets:
            self.from_sql_type_combobox.setStyleSheet("QComboBox{color: rgb%s}" % str(self.plain_color.getRgb()))
        if self.to_sql_type_combobox in self.error_widgets:
            self.to_sql_type_combobox.setStyleSheet("QComboBox{color: rgb%s}" % str(self.plain_color.getRgb()))
        dest_sql_str = self.sqlTranslator.web_translate(src_sql_str, from_type, to_type)
        self.dest_sql_text_edit.setText(dest_sql_str)

    def on_swap(self):
        # origin
        # from_sql_type_index = self.from_sql_type_combobox.currentIndex()
        # to_sql_type_index = self.to_sql_type_combobox.currentIndex()
        from_sql_type_text = self.from_sql_type_combobox.currentText()
        to_sql_type_text = self.to_sql_type_combobox.currentText()
        src_sql_str = self.src_sql_text_edit.toPlainText()
        dest_sql_str = self.dest_sql_text_edit.toPlainText()
        # change content
        self.from_sql_type_combobox.setCurrentIndex(
            self.sqlTranslator.get_opposite_sql_type(from_sql_type_text, 'from'))
        self.to_sql_type_combobox.setCurrentIndex(self.sqlTranslator.get_opposite_sql_type(to_sql_type_text, 'to'))
        self.dest_sql_text_edit.setText(src_sql_str)
        self.src_sql_text_edit.setText(dest_sql_str)

    def on_clear(self):
        self.from_sql_type_combobox.setCurrentIndex(0)
        self.to_sql_type_combobox.setCurrentIndex(0)
        self.dest_sql_text_edit.setText("")
        self.src_sql_text_edit.setText("")

    # show data after clicking the menu item in tree
    def on_tree_clicked(self):
        item = self.tableTree.currentItem()
        table_name = item.text(0)
        # no response when click database label
        if table_name != self.db_handler.db_name:
            cTool.logger.info("switch to data of table, name: %s" % table_name)
            self.sqlTextEdit.setText("select * from %s limit 0, %d;" % (table_name, self.page_size))
            self.do_query()

    # when press append record button
    def on_append_record(self, table_name, page_size):
        if self.is_cell_editable():
            self.sqlTextEdit.setText("select * from %s limit 0, %d;" % (table_name, page_size))
            self.do_query()
        else:
            self.warn_action("Table is not allowed to append record now")

    # when action is fobidden
    def warn_action(self, err_msg="illegal action"):
        cTool.logger.info("%s" % err_msg)
        QMessageBox.warning(None, "Warning", err_msg)

    # when press remove record button
    def on_remove_record(self):
        if self.is_cell_editable():
            cell_item_list = self.table_widget.table.selectedItems()
            count = len(cell_item_list)
            row_num_set = set()
            for x in range(0, count):
                table_item = cell_item_list[x]
                row_num_set.add(self.table_widget.table.row(table_item))
            selected_pk_set = set(
                self.former_table_data[row_num][self.db_handler.def_pk_index] for row_num in row_num_set)
            self.db_handler.delete_sql = self.db_handler.buildDeleteSQL(self.db_handler.def_table_name,
                                                                        self.db_handler.def_pk_name,
                                                                        selected_pk_set)
            # rmv_cnt = self.db_handler.modifyRecords(delete_sql)
            # print("updated count is %d for SQL %s" % (rmv_cnt, delete_sql))
            # self.do_query()
            self.former_table_data = [former_row_data for former_row_data in
                                      self.former_table_data if
                                      former_row_data[self.db_handler.def_pk_index] not in selected_pk_set]
            self.update_table_data(self.former_table_data, self.db_handler.def_field_name_lst, False)
        else:
            self.warn_action("Table is not allowed to delete record now")

    # 设置login面板
    def build_login_page(self):
        # login panel
        self.login_page = QWidget(self.stackedWidget)
        self.login_page.setGeometry(QRect(100, 100, 600, 600))
        # self.login_page_layout = QVBoxLayout(self.login_page)
        # username label
        self.usmLabel = QLabel(self.login_page)
        self.usmLabel.setGeometry(QRect(100, 100, 150, 50))
        self.usmLabel.setText("username ")
        # self.login_page_layout.addWidget(self.usmLabel)
        # username input
        self.unmTextEdit = QLineEdit(self.login_page)
        self.unmTextEdit.setText("")
        self.unmTextEdit.setGeometry(QRect(300, 100, 450, 50))
        # self.login_page_layout.addWidget(self.unmTextEdit)
        # password label
        self.pwdLabel = QLabel(self.login_page)
        self.pwdLabel.setGeometry(QRect(100, 150, 150, 50))
        self.pwdLabel.setText("password ")
        # self.login_page_layout.addWidget(self.pwdLabel)
        # password input
        self.pwdTextEdit = QLineEdit(self.login_page)
        self.pwdTextEdit.setText("")
        self.pwdTextEdit.setGeometry(QRect(300, 150, 450, 50))
        # self.login_page_layout.addWidget(self.pwdTextEdit)
        # host label
        self.hostLabel = QLabel(self.login_page)
        self.hostLabel.setGeometry(QRect(100, 200, 120, 50))
        self.hostLabel.setText("host address ")
        # self.login_page_layout.addWidget(self.hostLabel)
        # host input
        self.hostTextEdit = QLineEdit(self.login_page)
        self.hostTextEdit.setText("")
        self.hostTextEdit.setGeometry(QRect(300, 200, 450, 50))
        # self.login_page_layout.addWidget(self.hostTextEdit)
        # database name label
        self.dbNameLabel = QLabel(self.login_page)
        self.dbNameLabel.setGeometry(QRect(100, 250, 150, 50))
        self.dbNameLabel.setText("database name ")
        # self.login_page_layout.addWidget(self.dbNameLabel)
        # database name input
        self.dbNameTextEdit = QLineEdit(self.login_page)
        self.dbNameTextEdit.setText("")
        self.dbNameTextEdit.setGeometry(QRect(300, 250, 450, 50))
        # self.login_page_layout.addWidget(self.dbNameTextEdit)
        # confirm to connect database
        self.cnntDbBtn = QPushButton(self.login_page)
        self.cnntDbBtn.setObjectName("DbButton")
        self.cnntDbBtn.setGeometry(QRect(100, 350, 300, 50))
        # self.login_page_layout.addWidget(self.cnntDbBtn)
        # connect database with config file
        self.load_cfg_btn = QPushButton(self.login_page)
        self.load_cfg_btn.setObjectName("LoadConfigButton")
        self.load_cfg_btn.setGeometry(QRect(450, 350, 300, 50))
        # self.login_page_layout.addWidget(self.load_cfg_btn)
        # set event
        self.set_login_panel()

    # build login panel
    def set_login_panel(self):
        self.cnntDbBtn.clicked.connect(self.cnnt_db)
        self.load_cfg_btn.clicked.connect(self.re_config_db)

    # connect to database
    def cnnt_db(self):
        unmStr = self.unmTextEdit.text()
        pwdStr = self.pwdTextEdit.text()
        hostStr = self.hostTextEdit.text()
        dbNameStr = self.dbNameTextEdit.text()
        self.db_handler.config(hostStr, unmStr, pwdStr, dbNameStr)
        self.paint_tree()
        self.switch_page(0)

    # connect to database with configuration file
    def re_config_db(self):
        self.db_handler.config(mVars.host, mVars.username, mVars.password, mVars.database_name)

    # set label text of widget
    def re_translate_ui(self, MainWindow):
        _translate = QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "DBog"))
        self.setWindowIcon(QIcon("../resources/icon/ball.ico"))
        self.queryBtn.setText(_translate("MainWindow", "Query"))
        self.editBtn.setText(_translate("MainWindow", "Edit"))
        self.saveBtn.setText(_translate("MainWindow", "Save"))
        self.cnntDbBtn.setText(_translate("MainWindow", "ConnectByInputConfiguration"))
        self.load_cfg_btn.setText(_translate("MainWindow", "LoadConfigurationFile"))
        self.addLnBtn.setText(_translate("MainWindow", "AppendRecord"))
        self.rmvLnBtn.setText(_translate("MainWindow", "RemoveRecord"))

    # set event of data page
    def set_data_page_event(self):
        self.queryBtn.clicked.connect(self.do_query)
        self.editBtn.clicked.connect(self.do_edit)
        self.saveBtn.clicked.connect(self.do_save)
        self.addLnBtn.clicked.connect(lambda: self.on_append_record(self.db_handler.def_table_name, self.page_size - 1))
        self.rmvLnBtn.clicked.connect(self.on_remove_record)

    # switch to another page
    def switch_page(self, pnIndex):
        self.stackedWidget.setCurrentIndex(int(pnIndex))

    # execute query action
    def do_query(self):
        cmd_str = self.sqlTextEdit.toPlainText().replace(";", "")
        # 获取数据
        result_set = self.db_handler.get_all_result(cmd_str)
        # 获取表结构定义
        # db_filed_lst = self.db_handler.gen_alter(self.db_handler.def_table_name)
        # self.db_handler.field_name_lst = [db_filed["Field"] for db_filed in db_filed_lst]
        self.db_handler.gen_filed_name_list()
        # withdraw former table data / cast value None to empty string
        self.former_table_data = [[str(item) if item else "" for item in row] for row in result_set]
        # change button status to enable or not
        self.change_btn_status()
        # fill in table with empty line
        self.refresh_table(cmd_str)
        cTool.logger.info("result set for %s is %s" % (
            cmd_str, ','.join([str(row_data) for row_data in result_set])))

    # change button status to enable or not
    def change_btn_status(self):
        if self.is_cell_editable():
            self.addLnBtn.setEnabled(True)
            self.rmvLnBtn.setEnabled(True)
            self.saveBtn.setEnabled(True)
        else:
            self.addLnBtn.setEnabled(False)
            self.rmvLnBtn.setEnabled(False)
            self.saveBtn.setEnabled(False)

    # refresh table widget
    def refresh_table(self, cmd_str):
        row_cnt = len(self.former_table_data)
        if row_cnt > 0:
            col_cnt = len(self.former_table_data[0])
            while row_cnt < self.page_size:
                self.former_table_data.append(["" for i in range(col_cnt)])
                row_cnt = row_cnt + 1
        # count record
        page_cnt = self.count_record(cmd_str)
        # refresh table
        self.update_table_data(self.former_table_data, self.db_handler.def_field_name_lst, True)
        # refresh table page
        self.refresh_table_pager(page_cnt)

    # execute count of record
    def count_record(self, cmd_str):
        if "limit" in cmd_str:
            cmd_str = cmd_str.split("limit")[0]
        cnt_sql = "select count(1) from (%s) sub" % cmd_str
        ret_cnt = self.db_handler.get_single_result(cnt_sql)
        page_cnt = 0
        if ret_cnt and len(ret_cnt) > 0:
            ret_cnt = int(ret_cnt[0])
            page_cnt = int(ret_cnt / 10)
            if page_cnt % 10 != 0:
                page_cnt = page_cnt + 1
        return page_cnt

    # make new name of each field
    def rename_fields(self):
        ret_dict = self.count_list(self.db_handler.def_field_name_lst)
        for field_name in self.db_handler.def_field_name_lst:
            if ret_dict[field_name] > 1:
                field_name = "%s_%d" % (field_name, ret_dict[field_name])
                ret_dict[field_name] = ret_dict[field_name] - 1

    # statistic item count in a list
    def count_list(self, target_lst):
        ret_dict = {}
        for i in set(target_lst):
            ret_dict[i] = target_lst.count(i)
        return ret_dict

    # show table of current database
    def show_tables(self):
        tables_sql = "show tables;"
        self.sqlTextEdit.setText(tables_sql)
        return [tbl_name_tp[0] for tbl_name_tp in self.db_handler.get_all_result(tables_sql)]

    # execute update
    def do_edit(self):
        cmd_str = self.sqlTextEdit.toPlainText()
        row_count = self.db_handler.modifyRecords(cmd_str)
        info_msg = "Edit finish, effected row count: %d" % row_count
        cTool.logger.info(info_msg)
        QMessageBox.information(self, "Info", info_msg)

    # @decorator()
    # execute save action
    def do_save(self):
        # edit result set of multi tables is forbidden
        if self.is_cell_editable():
            # clear first
            self.current_table_data.clear()
            row_count = self.table_widget.table.rowCount()
            column_count = self.table_widget.table.columnCount()
            for i in range(0, row_count):
                curRowData = []
                for j in range(0, column_count):
                    cellValStr = self.table_widget.table.item(i, j).text()
                    curRowData.append(cellValStr)
                self.current_table_data.append(curRowData)
            # assemble SQL from data
            deleteSQL = self.db_handler.delete_sql
            if deleteSQL:
                # reset to empty string
                self.db_handler.delete_sql = ""
            else:
                dataLst = self.get_delete_data()
                deleteSQL = self.db_handler.buildDeleteSQL(self.db_handler.def_table_name, self.db_handler.def_pk_name,
                                                           dataLst) if dataLst else ""
            cTool.logger.info(deleteSQL)
            dataLst = self.get_insert_data()
            insertSQL = self.db_handler.buildInsertSQL(self.db_handler.def_table_name, dataLst) if dataLst else ""
            cTool.logger.info(insertSQL)
            rmCnt = self.db_handler.modifyRecords(deleteSQL) if deleteSQL else 0
            inCnt = self.db_handler.modifyRecords(insertSQL) if insertSQL else 0
            info_msg = "Save finish, effect record count removed: %d,inserted: %d" % (rmCnt, inCnt)
            cTool.logger.info(info_msg)
            QMessageBox.information(self, "Info", info_msg)
        else:
            self.warn_action("Table is not allowed to update now")

    # is table cell item editable
    def is_cell_editable(self):
        self.cell_editable_flg = QtCore.Qt.ItemIsEnabled if self.db_handler.is_combine_sql else QtCore.Qt.ItemIsEditable
        return self.cell_editable_flg == 2

    # withdraw delete data
    def get_delete_data(self):
        fmrSet = set(map(lambda x: ",".join(x), self.former_table_data))
        curSet = set(map(lambda x: ",".join(x), self.current_table_data))
        diffSet = fmrSet.difference(curSet)
        return list(map(lambda diffStr: diffStr.split(",")[self.db_handler.def_pk_index], diffSet))

    # withdraw insert data
    def get_insert_data(self):
        curSet = set(map(lambda x: ",".join(x), self.current_table_data))
        fmrSet = set(map(lambda x: ",".join(x), self.former_table_data))
        diffSet = curSet.difference(fmrSet)
        return list(map(lambda diffStr: diffStr.split(","), diffSet))

    # update/refresh table data
    def update_table_data(self, matrix_data, fd_name_list, update_header_flg):
        row_cnt = len(matrix_data)
        col_cnt = len(matrix_data[0]) if len(matrix_data) > 0 and len(matrix_data[0]) > 0 else 0
        # refresh table model struct
        self.clear_table_data()
        # if update_header_flg:
        self.refresh_table_header(fd_name_list, row_cnt, col_cnt)
        # refresh table data
        for row_num in range(0, row_cnt):
            for col_num in range(0, col_cnt):
                cell_data = str(matrix_data[row_num][col_num]) if matrix_data[row_num][col_num] else ""
                # 设置表格内容(行, 列) 文字
                self.table_widget.table.setItem(row_num, col_num, QTableWidgetItem(cell_data))

    # update/refresh table header
    def refresh_table_header(self, fd_name_list, row_cnt, col_cnt):
        self.table_widget.table.setRowCount(row_cnt)
        self.table_widget.table.setColumnCount(col_cnt)
        self.table_widget.table.setHorizontalHeaderLabels(fd_name_list)

    # update/refresh table pager
    def refresh_table_pager(self, page_count):
        self.table_widget.totalPageNum.setText(str(page_count))

    # clear table data
    def clear_table_data(self):
        self.table_widget.table.setRowCount(0)
        self.table_widget.table.setColumnCount(0)

    # config table item
    def touch_table_item(self):
        flag = QtCore.Qt.ItemIsEnabled if self.db_handler.is_combine_sql else QtCore.Qt.ItemIsEditable
        col_cnt = self.table_widget.table.colorCount()
        row_cnt = self.table_widget.table.rowCount()
        for col_num in range(0, col_cnt):
            for row_num in range(0, row_cnt):
                QTableWidgetItem(self.table_widget.table.item(row_cnt, col_cnt)).setFlags(flag)
                # 设置第二列不可编辑
                # self.table_widget.table.setItemDelegateForColumn(col_num, EmptyDelegate(self))
                # self.table_widget.table.setItemDelegateForColumn(col_num, QTableWidget.itemDelegate())

    # control page
    def control_page(self, signal):
        total_page = self.table_widget.show_total_page()
        if "home" == signal[0]:
            self.table_widget.curPage.setText("1")
        elif "pre" == signal[0]:
            if 1 == int(signal[1]):
                QMessageBox.information(self, "提示", "已经是第一页了", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(str(int(signal[1]) - 1))
        elif "next" == signal[0]:
            if total_page == int(signal[1]):
                QMessageBox.information(self, "提示", "已经是最后一页了", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(str(int(signal[1]) + 1))
        elif "final" == signal[0]:
            self.table_widget.curPage.setText(str(total_page))
        elif "confirm" == signal[0]:
            if total_page < int(signal[1]) or int(signal[1]) < 0:
                QMessageBox.information(self, "提示", "跳转页码超出范围", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(signal[1])
        self.change_table_content()  # 改变表格内容

    # change table pager and content
    def change_table_content(self):
        """根据当前页改变表格的内容"""
        cur_page = int(self.table_widget.curPage.text())
        # 每页默认十条数据
        cTool.logger.info("current page: %s" % cur_page)
        page_limit_sql = "limit %s, %s" % ((cur_page - 1) * 10, 10)
        query_sql = "select * from %s %s;" % (self.db_handler.def_table_name, page_limit_sql)
        self.sqlTextEdit.setText(query_sql)
        self.do_query()
Beispiel #13
0
class CenterWidget(QWidget):
    def __init__(self, udp_socket, own_nickname, parent=None):
        super(CenterWidget, self).__init__(parent)
        self.udp_socket = udp_socket
        self.own_nickname = own_nickname

        packSendData(self.udp_socket, server_addr,
                     {'event': 'get_all_users_info'})
        data, addr = self.udp_socket.recvfrom(1024)
        datafromserver = json.loads(data)

        hlay = QVBoxLayout()
        self.tree = QTreeWidget()  # 设置列数
        self.tree.setColumnCount(2)
        self.tree.setHeaderLabels(['昵称', '姓名', '状态'])  # 设置头的标题
        temp = self.loads(datafromserver.get('data'))
        str = '在线人数:' + repr(temp[0]) + '  离线人数:' + repr(
            temp[1]) + '  总人数:' + repr(temp[2])
        self.lab = QLabel(str)
        hlay.addWidget(self.lab)
        hlay.addWidget(self.tree)

        self.setLayout(hlay)

        self.tree.clicked.connect(self.onItemClick)
        self.tree.activated.connect(self.onItemClick)

    def onItemClick(self):
        '''
        功能:判断用户点击的是一个用户item、还是一个组item
            1. 是用户item就发送私密消息
            2. 是组item就发送组消息
        :return:
        '''
        item = self.tree.currentItem()
        item = self.tree.currentItem()
        isSecret = None
        if '部' in item.text(0) and item.text(1) == '':
            isSecret = False
        elif item.text(0) == '其他':
            isSecret = False
        else:
            isSecret = True
        self.comm = CommWidget(self.udp_socket, self.own_nickname,
                               item.text(0), isSecret)
        self.comm.show()

    def userMessage(self, peer_nickname, data):
        '''
        发送消息
        1. 构造数据
        2. 发送数据
        '''
        packSendData(
            self.udp_socket, server_addr, {
                'event': 'offline',
                'nickname': self.nickname,
                'peer_nickname': peer_nickname,
                'data': data
            })

    def groupMessage(self, peer_nickname):
        pass

    def loads(self, data):
        '''加载数据:
                1. 检测item所在部门是否创建
                2. 已经创建,item加入该部门成为子对象
                3. 没有创建,创建该部门,且让该item成为子对象
        '''
        '''换一种思路:
            1. 先建部分
            2. 在添加item到对于的部门下面
        '''
        '''获取部门信息'''
        parents = set()  # 集合,用于保存部门名称

        all_user = len(data)
        online_user = 0
        offline_user = 0

        for row_date in data:  # 遍历每一行数据(一个用户的数据)
            # print(row_date)
            parents.add(row_date[2])
        '''新建部门item'''
        for row_date in parents:  # 遍历每一行数据(一个用户的数据)
            item = QTreeWidgetItem(self.tree)  # root表示self.tree下下一级的一个item
            item.setText(0, row_date)
        '''按照用户所属部门新建用户item'''
        for row_date in data:  # 遍历每一行数据(一个用户的数据)
            for i in range(self.tree.topLevelItemCount()):
                item = self.tree.topLevelItem(i)
                if row_date[2] == item.text(0):
                    root = QTreeWidgetItem(item)  # root表示self.tree下下一级的一个item
                    root.setText(0, row_date[0])
                    root.setText(1, row_date[1])
                    root.setText(2, row_date[3])
                    if row_date[3] == '在线':
                        online_user = online_user + 1
                    elif row_date[3] == '离线':
                        offline_user = offline_user + 1
        return (online_user, offline_user, all_user)
class Preferences(QDialog):
#
    configuration = {}
    weight = 0
#
    savePreferences = pyqtSignal()
#
    def __init__(self, parent=None):
        super(Preferences, self).__init__(parent, Qt.Dialog)
        self.setWindowTitle(translations.TR_PREFERENCES_TITLE)
        self.setMinimumSize(QSize(900, 600))
        vbox = QVBoxLayout(self)
        hbox = QHBoxLayout()
        vbox.setContentsMargins(0, 0, 5, 5)
        hbox.setContentsMargins(0, 0, 0, 0)
#
        self.tree = QTreeWidget()
        self.tree.header().setHidden(True)
        self.tree.setSelectionMode(QTreeWidget.SingleSelection)
        self.tree.setAnimated(True)
        self.tree.header().setHorizontalScrollMode(
            QAbstractItemView.ScrollPerPixel)
        self.tree.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.tree.header().setStretchLastSection(False)
        self.tree.setFixedWidth(200)
        self.stacked = QStackedLayout()
        hbox.addWidget(self.tree)
        hbox.addLayout(self.stacked)
        vbox.addLayout(hbox)
#
        hbox_footer = QHBoxLayout()
        self._btnSave = QPushButton(translations.TR_SAVE)
        self._btnCancel = QPushButton(translations.TR_CANCEL)
        hbox_footer.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox_footer.addWidget(self._btnCancel)
        hbox_footer.addWidget(self._btnSave)
        vbox.addLayout(hbox_footer)
#
        self.tree.itemSelectionChanged.connect(self._change_current)
        self._btnCancel.clicked['bool'].connect(self.close)
        self._btnSave.clicked['bool'].connect(self._save_preferences)
#
        self.load_ui()
        self.tree.setCurrentItem(self.tree.topLevelItem(0))
#
    def _save_preferences(self):
        self.savePreferences.emit()
        self.close()
#
    def load_ui(self):
        sections = sorted(
            list(Preferences.configuration.keys()),
            key=lambda item: Preferences.configuration[item]['weight'])
        for section in sections:
            text = Preferences.configuration[section]['text']
            Widget = Preferences.configuration[section]['widget']
            widget = Widget(self)
            area = QScrollArea()
            area.setWidgetResizable(True)
            area.setWidget(widget)
            self.stacked.addWidget(area)
            index = self.stacked.indexOf(area)
            item = QTreeWidgetItem([text])
            item.setData(0, Qt.UserRole, index)
            self.tree.addTopLevelItem(item)
#
            #Sort Item Children
            subcontent = Preferences.configuration[section].get(
                'subsections', {})
            subsections = sorted(list(subcontent.keys()),
                                 key=lambda item: subcontent[item]['weight'])
            for sub in subsections:
                text = subcontent[sub]['text']
                Widget = subcontent[sub]['widget']
                widget = Widget(self)
                area = QScrollArea()
                area.setWidgetResizable(True)
                area.setWidget(widget)
                self.stacked.addWidget(area)
                index = self.stacked.indexOf(area)
                subitem = QTreeWidgetItem([text])
                subitem.setData(0, Qt.UserRole, index)
                item.addChild(subitem)
#
        self.tree.expandAll()
#
    def _change_current(self):
        item = self.tree.currentItem()
        index = item.data(0, Qt.UserRole)
        self.stacked.setCurrentIndex(index)
#
    @classmethod
    def register_configuration(cls, section, widget, text, weight=None,
                               subsection=None):
        if weight is None:
            Preferences.weight += 1
            weight = Preferences.weight
        if not subsection:
            Preferences.configuration[section] = {'widget': widget,
                                                  'weight': weight,
                                                  'text': text}
        else:
            config = Preferences.configuration.get(section, {})
            if not config:
                config[section] = {'widget': None, 'weight': 100}
            subconfig = config.get('subsections', {})
            subconfig[subsection] = {'widget': widget, 'weight': weight,
                                     'text': text}
            config['subsections'] = subconfig
            Preferences.configuration[section] = config
Beispiel #15
0
class Shortcuts(preferences.Page):
    def __init__(self, dialog):
        super(Shortcuts, self).__init__(dialog)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.scheme = SchemeSelector(self)
        layout.addWidget(self.scheme)
        self.searchEntry = LineEdit()
        self.searchEntry.setPlaceholderText(_("Search..."))
        layout.addWidget(self.searchEntry)
        self.tree = QTreeWidget(self)
        self.tree.setHeaderLabels([_("Command"), _("Shortcut")])
        self.tree.setRootIsDecorated(False)
        self.tree.setColumnCount(2)
        self.tree.setAllColumnsShowFocus(True)
        self.tree.setAnimated(True)
        layout.addWidget(self.tree)

        self.edit = QPushButton(
            icons.get("preferences-desktop-keyboard-shortcuts"), '')
        layout.addWidget(self.edit)

        # signals
        self.searchEntry.textChanged.connect(self.updateFilter)
        self.scheme.currentChanged.connect(self.slotSchemeChanged)
        self.scheme.changed.connect(self.changed)
        self.tree.currentItemChanged.connect(self.slotCurrentItemChanged)
        self.tree.itemDoubleClicked.connect(self.editCurrentItem)
        self.edit.clicked.connect(self.editCurrentItem)

        # make a dict of all actions with the actions as key and the names as
        # value, with the collection prepended (for loading/saving)
        win = dialog.parent()
        allactions = {}
        for collection in actioncollectionmanager.manager(
                win).actionCollections():
            for name, action in collection.actions().items():
                allactions[action] = (collection, name)

        # keep a list of actions not in the menu structure
        left = list(allactions.keys())

        def add_actions(menuitem, actions):
            """Add actions to a QTreeWidgetItem."""
            for a in actions:
                if a.menu():
                    item = build_menu_item(a)
                    if item.childCount():
                        menuitem.addChild(item)
                elif a in left:
                    left.remove(a)
                    menuitem.addChild(ShortcutItem(a, *allactions[a]))
            menuitem.setFlags(Qt.ItemIsEnabled)  # disable selection

        def build_menu_item(action):
            """Return a QTreeWidgetItem with children for all the actions in the submenu."""
            menuitem = QTreeWidgetItem()
            text = qutil.removeAccelerator(action.text())
            menuitem.setText(0, _("Menu {name}").format(name=text))
            add_actions(menuitem, action.menu().actions())
            return menuitem

        # present the actions nicely ordered as in the menus
        for a in win.menuBar().actions():
            menuitem = build_menu_item(a)
            if menuitem.childCount():
                self.tree.addTopLevelItem(menuitem)

        # sort leftover actions
        left.sort(key=lambda i: i.text())

        # show actions that are left, grouped by collection
        titlegroups = {}
        for a in left[:]:  # copy
            collection, name = allactions[a]
            if collection.title():
                titlegroups.setdefault(collection.title(), []).append(a)
                left.remove(a)
        for title in sorted(titlegroups):
            item = QTreeWidgetItem(["{0}:".format(title)])
            for a in titlegroups[title]:
                item.addChild(ShortcutItem(a, *allactions[a]))
            self.tree.addTopLevelItem(item)
            item.setFlags(Qt.ItemIsEnabled)  # disable selection

        # show other actions that were not in the menus
        item = QTreeWidgetItem([_("Other commands:")])
        for a in left:
            if a.text() and not a.menu():
                item.addChild(ShortcutItem(a, *allactions[a]))
        if item.childCount():
            self.tree.addTopLevelItem(item)
            item.setFlags(Qt.ItemIsEnabled)  # disable selection

        self.tree.expandAll()

        item = self.tree.topLevelItem(0).child(0)
        if _lastaction:
            # find the previously selected item
            for i in self.items():
                if i.name == _lastaction:
                    item = i
                    break
        self.tree.setCurrentItem(item)
        self.tree.resizeColumnToContents(0)

    def items(self):
        """Yield all the items in the actions tree."""
        def children(item):
            for i in range(item.childCount()):
                c = item.child(i)
                if c.childCount():
                    for c1 in children(c):
                        yield c1
                else:
                    yield c

        for c in children(self.tree.invisibleRootItem()):
            yield c

    def item(self, collection, name):
        for item in self.items():
            if item.collection.name == collection and item.name == name:
                return item

    def saveSettings(self):
        self.scheme.saveSettings("shortcut_scheme", "shortcut_schemes",
                                 "shortcuts")
        for item in self.items():
            for scheme in self.scheme.schemes():
                item.save(scheme)
            item.clearSettings()
            item.switchScheme(self.scheme.currentScheme())

    def loadSettings(self):
        self.scheme.loadSettings("shortcut_scheme", "shortcut_schemes")
        # clear the settings in all the items
        for item in self.items():
            item.clearSettings()
            item.switchScheme(self.scheme.currentScheme())

    def slotSchemeChanged(self):
        """Called when the Scheme combobox is changed by the user."""
        for item in self.items():
            item.switchScheme(self.scheme.currentScheme())

    def slotCurrentItemChanged(self, item):
        if isinstance(item, ShortcutItem):
            self.edit.setText(
                _("&Edit Shortcut for \"{name}\"").format(name=item.text(0)))
            self.edit.setEnabled(True)
            global _lastaction
            _lastaction = item.name
        else:
            self.edit.setText(_("(no shortcut)"))
            self.edit.setEnabled(False)

    def import_(self, filename):
        from . import import_export
        import_export.importShortcut(filename, self, self.scheme)

    def export(self, name, filename):
        from . import import_export
        try:
            import_export.exportShortcut(self, self.scheme.currentScheme(),
                                         name, filename)
        except (IOError, OSError) as e:
            QMessageBox.critical(
                self, _("Error"),
                _("Can't write to destination:\n\n{url}\n\n{error}").format(
                    url=filename, error=e.strerror))

    def findShortcutConflict(self, shortcut):
        """Find the possible shortcut conflict and return the conflict name."""
        if shortcut:
            item = self.tree.currentItem()
            if not isinstance(item, ShortcutItem):
                return None
            scheme = self.scheme.currentScheme()
            for i in self.items():
                a = i.action(scheme)
                if i != item and a.shortcuts():
                    for s1 in a.shortcuts():
                        if s1.matches(shortcut) or shortcut.matches(s1):
                            return qutil.removeAccelerator(a.text())
        return None

    def editCurrentItem(self):
        item = self.tree.currentItem()
        if not isinstance(item, ShortcutItem):
            return

        dlg = ShortcutEditDialog(self, self.findShortcutConflict)
        scheme = self.scheme.currentScheme()
        action = item.action(scheme)
        default = item.defaultShortcuts() or None
        if dlg.editAction(action, default):
            shortcuts = action.shortcuts()
            # check for conflicts
            conflicting = []
            for i in self.items():
                if i is not item:
                    for s1, s2 in itertools.product(i.shortcuts(scheme),
                                                    shortcuts):
                        if s1.matches(s2) or s2.matches(s1):
                            conflicting.append(i)
            if conflicting:
                for i in conflicting:
                    l = i.shortcuts(scheme)
                    for s1 in list(l):  # copy
                        for s2 in shortcuts:
                            if s1.matches(s2) or s2.matches(s1):
                                l.remove(s1)
                    i.setShortcuts(l, scheme)

            # store the shortcut
            item.setShortcuts(shortcuts, scheme)
            self.changed.emit()

    def updateFilter(self):
        """Called when the search text changes."""
        search = self.searchEntry.text()
        scheme = self.scheme.currentScheme()

        def hidechildren(item):
            hideparent = True
            for n in range(item.childCount()):
                c = item.child(n)
                if c.childCount():
                    # submenu item
                    if hidechildren(c):
                        c.setHidden(True)
                    else:
                        c.setHidden(False)
                        c.setExpanded(True)
                        hideparent = False
                elif isinstance(c, ShortcutItem):
                    # shortcut item, should be the case
                    if c.matches(scheme, search):
                        c.setHidden(False)
                        hideparent = False
                    else:
                        c.setHidden(True)
            return hideparent

        hidechildren(self.tree.invisibleRootItem())
class EditorConfiguration(QWidget):
    """EditorConfiguration widget class"""

    def __init__(self, parent):
        super(EditorConfiguration, self).__init__()
        self._preferences, vbox = parent, QVBoxLayout(self)

        # groups
        group1 = QGroupBox(translations.TR_PREFERENCES_EDITOR_CONFIG_INDENT)
        group2 = QGroupBox(translations.TR_PREFERENCES_EDITOR_CONFIG_MARGIN)
        group3 = QGroupBox(translations.TR_LINT_DIRTY_TEXT)
        group4 = QGroupBox(translations.TR_PEP8_DIRTY_TEXT)
        group5 = QGroupBox(translations.TR_HIGHLIGHTER_EXTRAS)
        group6 = QGroupBox(translations.TR_TYPING_ASSISTANCE)
        group7 = QGroupBox(translations.TR_DISPLAY)

        # groups container
        container_widget_with_all_preferences = QWidget()
        formFeatures = QGridLayout(container_widget_with_all_preferences)

        # Indentation
        hboxg1 = QHBoxLayout(group1)
        hboxg1.setContentsMargins(5, 15, 5, 5)
        self._spin, self._checkUseTabs = QSpinBox(), QComboBox()
        self._spin.setRange(1, 10)
        self._spin.setValue(settings.INDENT)
        hboxg1.addWidget(self._spin)
        self._checkUseTabs.addItems([
            translations.TR_PREFERENCES_EDITOR_CONFIG_SPACES.capitalize(),
            translations.TR_PREFERENCES_EDITOR_CONFIG_TABS.capitalize()])
        self._checkUseTabs.setCurrentIndex(int(settings.USE_TABS))
        hboxg1.addWidget(self._checkUseTabs)
        formFeatures.addWidget(group1, 0, 0)

        # Margin Line
        hboxg2 = QHBoxLayout(group2)
        hboxg2.setContentsMargins(5, 15, 5, 5)
        self._checkShowMargin = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_SHOW_MARGIN_LINE)
        self._checkShowMargin.setChecked(settings.SHOW_MARGIN_LINE)
        hboxg2.addWidget(self._checkShowMargin)
        self._spinMargin = QSpinBox()
        self._spinMargin.setRange(50, 100)
        self._spinMargin.setSingleStep(2)
        self._spinMargin.setValue(settings.MARGIN_LINE)
        hboxg2.addWidget(self._spinMargin)
        hboxg2.addWidget(QLabel(translations.TR_CHARACTERS))
        formFeatures.addWidget(group2, 0, 1)

        # Display Errors
        vboxDisplay = QVBoxLayout(group7)
        vboxDisplay.setContentsMargins(5, 15, 5, 5)
        self._checkHighlightLine = QComboBox()
        self._checkHighlightLine.addItems([
            translations.TR_PREFERENCES_EDITOR_CONFIG_ERROR_USE_BACKGROUND,
            translations.TR_PREFERENCES_EDITOR_CONFIG_ERROR_USE_UNDERLINE])
        self._checkHighlightLine.setCurrentIndex(
            int(settings.UNDERLINE_NOT_BACKGROUND))
        hboxDisplay1 = QHBoxLayout()
        hboxDisplay1.addWidget(QLabel(translations.TR_DISPLAY_ERRORS))
        hboxDisplay1.addWidget(self._checkHighlightLine)
        hboxDisplay2 = QHBoxLayout()
        self._checkDisplayLineNumbers = QCheckBox(
            translations.TR_DISPLAY_LINE_NUMBERS)
        self._checkDisplayLineNumbers.setChecked(settings.SHOW_LINE_NUMBERS)
        hboxDisplay2.addWidget(self._checkDisplayLineNumbers)
        vboxDisplay.addLayout(hboxDisplay1)
        vboxDisplay.addLayout(hboxDisplay2)
        formFeatures.addWidget(group7, 1, 0, 1, 0)

        # Find Lint Errors (highlighter)
        vboxg3 = QVBoxLayout(group3)
        self._checkErrors = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_FIND_ERRORS)
        self._checkErrors.setChecked(settings.FIND_ERRORS)
        self._checkErrors.stateChanged[int].connect(self._disable_show_errors)
        self._showErrorsOnLine = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_SHOW_TOOLTIP_ERRORS)
        self._showErrorsOnLine.setChecked(settings.ERRORS_HIGHLIGHT_LINE)
        self._showErrorsOnLine.stateChanged[int].connect(
            self._enable_errors_inline)
        vboxg3.addWidget(self._checkErrors)
        vboxg3.addWidget(self._showErrorsOnLine)
        vboxg3.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding))
        formFeatures.addWidget(group3, 2, 0)

        # Find PEP8 Errors (highlighter)
        vboxg4 = QHBoxLayout(group4)
        vboxg4.setContentsMargins(5, 15, 5, 5)
        vvbox = QVBoxLayout()
        self._checkStyle = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_SHOW_PEP8)
        self._checkStyle.setChecked(settings.CHECK_STYLE)
        self._checkStyle.stateChanged[int].connect(self._disable_check_style)
        vvbox.addWidget(self._checkStyle)
        self._checkStyleOnLine = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_SHOW_TOOLTIP_PEP8)
        self._checkStyleOnLine.setChecked(settings.CHECK_HIGHLIGHT_LINE)
        self._checkStyleOnLine.stateChanged[int].connect(
            self._enable_check_inline)
        vvbox.addWidget(self._checkStyleOnLine)
        vvbox.addItem(QSpacerItem(0, 0,
                      QSizePolicy.Expanding, QSizePolicy.Expanding))
        vboxg4.addLayout(vvbox)
        # Container for tree widget and buttons
        widget = QWidget()
        hhbox = QHBoxLayout(widget)
        hhbox.setContentsMargins(0, 0, 0, 0)
        # Tree Widget with custom item delegate
        # always adds uppercase text
        self._listIgnoreViolations = QTreeWidget()
        self._listIgnoreViolations.setObjectName("ignore_pep8")
        self._listIgnoreViolations.setItemDelegate(ui_tools.CustomDelegate())
        self._listIgnoreViolations.setMaximumHeight(80)
        self._listIgnoreViolations.setHeaderLabel(
            translations.TR_PREFERENCES_EDITOR_CONFIG_IGNORE_PEP8)
        for ic in settings.IGNORE_PEP8_LIST:
            self._listIgnoreViolations.addTopLevelItem(QTreeWidgetItem([ic]))
        hhbox.addWidget(self._listIgnoreViolations)
        box = QVBoxLayout()
        box.setContentsMargins(0, 0, 0, 0)
        btn_add = QPushButton(QIcon(":img/add_small"), '')
        btn_add.setMaximumSize(26, 24)
        btn_add.clicked.connect(self._add_code_pep8)
        box.addWidget(btn_add)
        btn_remove = QPushButton(QIcon(":img/delete_small"), '')
        btn_remove.setMaximumSize(26, 24)
        btn_remove.clicked.connect(self._remove_code_pep8)
        box.addWidget(btn_remove)
        box.addItem(QSpacerItem(0, 0,
                    QSizePolicy.Fixed, QSizePolicy.Expanding))
        hhbox.addLayout(box)
        vboxg4.addWidget(widget)
        formFeatures.addWidget(group4)

        # Show Python3 Migration, DocStrings and Spaces (highlighter)
        vboxg5 = QVBoxLayout(group5)
        vboxg5.setContentsMargins(5, 15, 5, 5)
        self._showMigrationTips = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_SHOW_MIGRATION)
        self._showMigrationTips.setChecked(settings.SHOW_MIGRATION_TIPS)
        vboxg5.addWidget(self._showMigrationTips)
        self._checkForDocstrings = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_CHECK_FOR_DOCSTRINGS)
        self._checkForDocstrings.setChecked(settings.CHECK_FOR_DOCSTRINGS)
        vboxg5.addWidget(self._checkForDocstrings)
        self._checkShowSpaces = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_SHOW_TABS_AND_SPACES)
        self._checkShowSpaces.setChecked(settings.SHOW_TABS_AND_SPACES)
        vboxg5.addWidget(self._checkShowSpaces)
        self._checkIndentationGuide = QCheckBox(
            translations.TR_SHOW_INDENTATION_GUIDE)
        self._checkIndentationGuide.setChecked(settings.SHOW_INDENTATION_GUIDE)
        vboxg5.addWidget(self._checkIndentationGuide)
        formFeatures.addWidget(group5, 3, 0)

        # End of line, Stop Scrolling At Last Line, Trailing space, Word wrap
        vboxg6 = QVBoxLayout(group6)
        vboxg6.setContentsMargins(5, 15, 5, 5)
        self._checkEndOfLine = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_END_OF_LINE)
        self._checkEndOfLine.setChecked(settings.USE_PLATFORM_END_OF_LINE)
        vboxg6.addWidget(self._checkEndOfLine)
        self._checkEndAtLastLine = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_END_AT_LAST_LINE)
        self._checkEndAtLastLine.setChecked(settings.END_AT_LAST_LINE)
        vboxg6.addWidget(self._checkEndAtLastLine)
        self._checkTrailing = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_REMOVE_TRAILING)
        self._checkTrailing.setChecked(settings.REMOVE_TRAILING_SPACES)
        vboxg6.addWidget(self._checkTrailing)
        self._allowWordWrap = QCheckBox(
            translations.TR_PREFERENCES_EDITOR_CONFIG_WORD_WRAP)
        self._allowWordWrap.setChecked(settings.ALLOW_WORD_WRAP)
        vboxg6.addWidget(self._allowWordWrap)
        formFeatures.addWidget(group6, 3, 1)

        # pack all the groups
        vbox.addWidget(container_widget_with_all_preferences)
        vbox.addItem(QSpacerItem(0, 10, QSizePolicy.Expanding,
                     QSizePolicy.Expanding))

        self._preferences.savePreferences.connect(self.save)

    def _add_code_pep8(self):
        item = QTreeWidgetItem()
        item.setFlags(item.flags() | Qt.ItemIsEditable)
        self._listIgnoreViolations.addTopLevelItem(item)
        self._listIgnoreViolations.setCurrentItem(item)
        self._listIgnoreViolations.editItem(item, 0)

    def _remove_code_pep8(self):
        index = self._listIgnoreViolations.indexOfTopLevelItem(
            self._listIgnoreViolations.currentItem())
        self._listIgnoreViolations.takeTopLevelItem(index)

    def _enable_check_inline(self, val):
        """Method that takes a value to enable the inline style checking"""
        if val == Qt.Checked:
            self._checkStyle.setChecked(True)

    def _enable_errors_inline(self, val):
        """Method that takes a value to enable the inline errors checking"""
        if val == Qt.Checked:
            self._checkErrors.setChecked(True)

    def _disable_check_style(self, val):
        """Method that takes a value to disable the inline style checking"""
        if val == Qt.Unchecked:
            self._checkStyleOnLine.setChecked(False)

    def _disable_show_errors(self, val):
        """Method that takes a value to disable the inline errors checking"""
        if val == Qt.Unchecked:
            self._showErrorsOnLine.setChecked(False)

    def save(self):
        """Method to save settings"""
        qsettings = IDE.ninja_settings()
        settings.USE_TABS = bool(self._checkUseTabs.currentIndex())
        qsettings.setValue('preferences/editor/useTabs',
                           settings.USE_TABS)
        margin_line = self._spinMargin.value()
        settings.MARGIN_LINE = margin_line
        settings.pycodestylemod_update_margin_line_length(margin_line)
        qsettings.setValue('preferences/editor/marginLine', margin_line)
        settings.SHOW_MARGIN_LINE = self._checkShowMargin.isChecked()
        qsettings.setValue('preferences/editor/showMarginLine',
                           settings.SHOW_MARGIN_LINE)
        settings.INDENT = self._spin.value()
        qsettings.setValue('preferences/editor/indent', settings.INDENT)
        endOfLine = self._checkEndOfLine.isChecked()
        settings.USE_PLATFORM_END_OF_LINE = endOfLine
        qsettings.setValue('preferences/editor/platformEndOfLine', endOfLine)
        settings.UNDERLINE_NOT_BACKGROUND = \
            bool(self._checkHighlightLine.currentIndex())
        qsettings.setValue('preferences/editor/errorsUnderlineBackground',
                           settings.UNDERLINE_NOT_BACKGROUND)
        settings.FIND_ERRORS = self._checkErrors.isChecked()
        qsettings.setValue('preferences/editor/errors', settings.FIND_ERRORS)
        settings.ERRORS_HIGHLIGHT_LINE = self._showErrorsOnLine.isChecked()
        qsettings.setValue('preferences/editor/errorsInLine',
                           settings.ERRORS_HIGHLIGHT_LINE)
        settings.CHECK_STYLE = self._checkStyle.isChecked()
        qsettings.setValue('preferences/editor/checkStyle',
                           settings.CHECK_STYLE)
        settings.SHOW_MIGRATION_TIPS = self._showMigrationTips.isChecked()
        qsettings.setValue('preferences/editor/showMigrationTips',
                           settings.SHOW_MIGRATION_TIPS)
        settings.CHECK_HIGHLIGHT_LINE = self._checkStyleOnLine.isChecked()
        qsettings.setValue('preferences/editor/checkStyleInline',
                           settings.CHECK_HIGHLIGHT_LINE)
        settings.END_AT_LAST_LINE = self._checkEndAtLastLine.isChecked()
        qsettings.setValue('preferences/editor/endAtLastLine',
                           settings.END_AT_LAST_LINE)
        settings.REMOVE_TRAILING_SPACES = self._checkTrailing.isChecked()
        qsettings.setValue('preferences/editor/removeTrailingSpaces',
                           settings.REMOVE_TRAILING_SPACES)
        settings.ALLOW_WORD_WRAP = self._allowWordWrap.isChecked()
        qsettings.setValue('preferences/editor/allowWordWrap',
                           settings.ALLOW_WORD_WRAP)
        settings.SHOW_TABS_AND_SPACES = self._checkShowSpaces.isChecked()
        qsettings.setValue('preferences/editor/showTabsAndSpaces',
                           settings.SHOW_TABS_AND_SPACES)
        settings.SHOW_INDENTATION_GUIDE = (
            self._checkIndentationGuide.isChecked())
        qsettings.setValue('preferences/editor/showIndentationGuide',
                           settings.SHOW_INDENTATION_GUIDE)
        settings.CHECK_FOR_DOCSTRINGS = self._checkForDocstrings.isChecked()
        qsettings.setValue('preferences/editor/checkForDocstrings',
                           settings.CHECK_FOR_DOCSTRINGS)
        settings.SHOW_LINE_NUMBERS = self._checkDisplayLineNumbers.isChecked()
        qsettings.setValue('preferences/editor/showLineNumbers',
                           settings.SHOW_LINE_NUMBERS)
        current_ignores = set(settings.IGNORE_PEP8_LIST)
        new_ignore_codes = []
        # Get pep8 from tree widget
        for index in range(self._listIgnoreViolations.topLevelItemCount()):
            ignore_code = self._listIgnoreViolations.topLevelItem(
                index).text(0)
            if ignore_code:
                new_ignore_codes.append(ignore_code.strip())
        # pep8 list that will be removed
        to_remove = [x for x in current_ignores
                     if x not in new_ignore_codes]
        # Update list
        settings.IGNORE_PEP8_LIST = new_ignore_codes
        qsettings.setValue('preferences/editor/defaultIgnorePep8',
                           settings.IGNORE_PEP8_LIST)
        # Add
        for ignore_code in settings.IGNORE_PEP8_LIST:
            settings.pycodestylemod_add_ignore(ignore_code)
        # Remove
        for ignore_code in to_remove:
            settings.pycodestylemod_remove_ignore(ignore_code)
        if settings.USE_TABS:
            settings.pycodestylemod_add_ignore("W191")
        else:
            settings.pycodestylemod_remove_ignore("W191")
Beispiel #17
0
class Preferences(QDialog):

    configuration = {}
    weight = 0
    # Signals
    savePreferences = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent, Qt.Dialog)
        self.setWindowTitle(translations.TR_PREFERENCES_TITLE)
        self.setMinimumSize(900, 650)
        box = QVBoxLayout(self)
        box.setContentsMargins(3, 3, 3, 3)
        self.setAutoFillBackground(True)
        # Header
        self._header_label = QLabel("")
        header_font = self._header_label.font()
        header_font.setBold(True)
        header_font.setPointSize(header_font.pointSize() + 4)
        self._header_label.setFont(header_font)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)

        self.tree = QTreeWidget()
        self.tree.header().setHidden(True)
        self.tree.setSelectionMode(QTreeWidget.SingleSelection)
        self.tree.setAnimated(True)
        self.tree.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.tree.setFixedWidth(200)
        hbox.addWidget(self.tree)

        self.stacked = QStackedLayout()
        header_layout = QVBoxLayout()
        header_layout.setContentsMargins(0, 0, 0, 0)
        header_layout.addWidget(self._header_label)
        header_layout.addLayout(self.stacked)
        hbox.addLayout(header_layout)
        box.addLayout(hbox)

        # Footer buttons
        button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        box.addWidget(button_box)

        # Connections
        button_box.rejected.connect(self.close)
        button_box.accepted.connect(self._save_preferences)
        self.tree.selectionModel().currentRowChanged.connect(
            self._change_current)

        self.load_ui()

    @pyqtSlot()
    def _save_preferences(self):
        self.savePreferences.emit()
        self.close()

    def load_ui(self):
        sections = sorted(
            Preferences.configuration.keys(),
            key=lambda item: Preferences.configuration[item]['weight'])
        for section in sections:
            text = Preferences.configuration[section]['text']
            Widget = Preferences.configuration[section]['widget']
            widget = Widget(self)
            area = QScrollArea()
            area.setWidgetResizable(True)
            area.setWidget(widget)
            self.stacked.addWidget(area)
            index = self.stacked.indexOf(area)
            item = QTreeWidgetItem([text])
            item.setData(0, Qt.UserRole, index)
            self.tree.addTopLevelItem(item)

            # Sort Item Children
            subcontent = Preferences.configuration[section].get(
                'subsections', {})
            subsections = sorted(
                subcontent.keys(), key=lambda item: subcontent[item]['weight'])
            for sub in subsections:
                text = subcontent[sub]['text']
                Widget = subcontent[sub]['widget']
                widget = Widget(self)
                area = QScrollArea()
                area.setWidgetResizable(True)
                area.setWidget(widget)
                self.stacked.addWidget(area)
                index = self.stacked.indexOf(area)
                subitem = QTreeWidgetItem([text])
                subitem.setData(0, Qt.UserRole, index)
                item.addChild(subitem)

        self.tree.expandAll()
        self.tree.setCurrentIndex(self.tree.model().index(0, 0))

    def _change_current(self):
        item = self.tree.currentItem()
        index = item.data(0, Qt.UserRole)
        self.stacked.setCurrentIndex(index)
        self._header_label.setText(item.text(0))

    @classmethod
    def register_configuration(cls, section, widget, text,
                               weight=None, subsection=None):
        if weight is None:
            Preferences.weight += 1
            weight = Preferences.weight
        if subsection is None:
            Preferences.configuration[section] = {
                'widget': widget,
                'weight': weight,
                'text': text
            }
        else:
            config = Preferences.configuration.get(section, {})
            if not config:
                config[section] = {
                    'widget': None,
                    'weight': 100
                }
            subconfig = config.get('subsections', {})
            subconfig[subsection] = {
                'widget': widget,
                'weight': weight,
                'text': text
            }
            config['subsections'] = subconfig
            Preferences.configuration[section] = config
Beispiel #18
0
class SessionsManager(QDialog):
    """Session Manager, to load different configurations of ninja."""

    def __init__(self, parent=None):
        super(SessionsManager, self).__init__(parent, Qt.Dialog)
        self._ninja = parent
        self.setModal(True)
        self.setWindowTitle(translations.TR_SESSIONS_TITLE)
        self.setMinimumWidth(550)
        self.setMinimumHeight(450)
        self._manager = _SessionManager(parent)
        self._load_ui()

    def install(self):
        self._manager.load_sessions()

    def _load_ui(self):
        main_layout = QVBoxLayout(self)
        main_layout.addWidget(QLabel(translations.TR_SESSIONS_DIALOG_BODY))
        main_hbox = QHBoxLayout()
        # Session list
        session_layout = QVBoxLayout()
        self._session_list = QTreeWidget()
        self._session_list.setHeaderLabels(["Session", "Last Modified"])
        session_layout.addWidget(self._session_list)
        # Content frame
        content_frame = QFrame()
        content_frame.hide()
        frame_layout = QVBoxLayout(content_frame)
        content_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        session_layout.addWidget(content_frame)
        frame_layout.setContentsMargins(0, 0, 0, 0)
        self._content_list = QTreeWidget()
        self._content_list.setHeaderHidden(True)
        frame_layout.addWidget(self._content_list)
        # Separator line
        line_frame = QFrame()
        line_frame.setFrameStyle(QFrame.VLine | QFrame.Sunken)
        # Buttons
        btn_layout = QVBoxLayout()
        btn_create = QPushButton(translations.TR_SESSIONS_BTN_CREATE)
        btn_activate = QPushButton(translations.TR_SESSIONS_BTN_ACTIVATE)
        btn_update = QPushButton(translations.TR_SESSIONS_BTN_UPDATE)
        btn_delete = QPushButton(translations.TR_SESSIONS_BTN_DELETE)
        btn_details = QPushButton(translations.TR_SESSIONS_BTN_DETAILS)
        btn_details.setCheckable(True)
        # Add buttons to layout
        btn_layout.addWidget(btn_create)
        btn_layout.addWidget(btn_activate)
        btn_layout.addWidget(btn_update)
        btn_layout.addWidget(btn_delete)
        btn_layout.addStretch()
        btn_layout.addWidget(btn_details)
        # Add widgets and layouts to the main layout
        main_layout.addLayout(main_hbox)
        main_hbox.addLayout(session_layout)
        main_hbox.addWidget(line_frame)
        main_hbox.addLayout(btn_layout)
        main_hbox.setSizeConstraint(QLayout.SetFixedSize)
        btn_details.toggled[bool].connect(content_frame.setVisible)
        # Connections
        self._session_list.itemSelectionChanged.connect(
            self.load_session_content)
        btn_activate.clicked.connect(self.open_session)
        btn_update.clicked.connect(self.save_session)
        btn_create.clicked.connect(self.create_session)
        btn_delete.clicked.connect(self.delete_session)

    def __load_sessions(self):
        for session_name in self._manager.sessions:
            item = QTreeWidgetItem()
            item.setText(0, session_name)
            item.setText(1, "FIXME: manage this!")
            self._session_list.addTopLevelItem(item)
        self._session_list.setCurrentItem(
            self._session_list.topLevelItem(0))

    def load_session_content(self):
        """Load the selected session, replacing the current session."""
        item = self._session_list.currentItem()
        self._content_list.clear()
        if item is not None:
            key = item.text(0)
            files, projects = self._manager.get_session(key)
            files_parent = QTreeWidgetItem(
                self._content_list, [translations.TR_FILES])
            for ffile in files:
                QTreeWidgetItem(files_parent, [ffile[0]])
            projects_parent = QTreeWidgetItem(
                self._content_list, [translations.TR_PROJECT])
            for project in projects:
                QTreeWidgetItem(projects_parent, [project])

            files_parent.setExpanded(True)
            projects_parent.setExpanded(True)

    def create_session(self):
        """Create a new Session."""
        session_info = QInputDialog.getText(
            None, translations.TR_SESSIONS_CREATE_TITLE,
            translations.TR_SESSIONS_CREATE_BODY)
        if session_info[1]:
            session_name = session_info[0]
            if not session_name or session_name in settings.SESSIONS:
                QMessageBox.information(
                    self,
                    translations.TR_SESSIONS_MESSAGE_TITLE,
                    translations.TR_SESSIONS_MESSAGE_BODY)
                return
            self._manager.save_session_data(session_name)
        self.close()

    def save_session(self):
        """Save current session"""
        if self._session_list.currentItem():
            session_name = self._session_list.currentItem().text(0)
            self._manager.save_session_data(session_name)
            self._ninja.show_message(
                translations.TR_SESSIONS_UPDATED_NOTIF.format(session_name))
            self.load_session_content()

    def open_session(self):
        """Open a saved session"""
        if self._session_list.currentItem():
            session_name = self._session_list.currentItem().text(0)
            self._manager.load_session(session_name)
            self._manager.set_current_session(session_name)
            self.close()

    def delete_session(self):
        """Delete a session"""
        if self._session_list.currentItem():
            key = self._session_list.currentItem().text(0)
            self._manager.delete_session(key)
            self._session_list.takeTopLevelItem(
                self._session_list.currentIndex().row())

    @property
    def current_session(self):
        return self._manager.current_session()

    def showEvent(self, event):
        super().showEvent(event)
        self.__load_sessions()

    def hideEvent(self, event):
        super().hideEvent(event)
        self._session_list.clear()
Beispiel #19
0
class NoteMain(QMainWindow):
    def __init__(self, parent=None):
        super(NoteMain, self).__init__(parent)
        self.setWindowTitle("CSDN 同步笔记(author:rechard)")
        #os.path.dirname(os.path.realpath(__file__) 获取到当前文件的路径
        #self.setWindowIcon(QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(__file__)), "icon.png" )))
        self.setWindowIcon(QIcon("./images/icon.png"))
        self.blogCache = BlogCache()
        self.browser = BrowserWidget()
        self.browser.cookieAdd.connect(self.on_browser_cookieAdd)
        self.split = QSplitter()
        self.articleList = ArticleList()
        self.articleList.itemClicked.connect(self.articleItemClick)
        self.initCategory()
        self.split.addWidget(self.articleList)
        self.split.addWidget(self.browser)
        self.toolbar = MDToolBarWidget(self.browser)
        self.split.addWidget(self.toolbar)
        self.split.setSizes([0, 0, 900, 0])
        self.setCentralWidget(self.split)
        self.showMaximized()
        #self.setGeometry(200, 200, 800, 500)
        self.cookies = {}  # 存放domain的key-value
        self.loginDone = False
        #self.loginSuccessInit()

    #点击article list里的选项
    def articleItemClick(self, clickItem: ArticleItem):
        self.showArticleInLeftFrame(clickItem.getBlog())

    def on_browser_cookieAdd(self, cookie):  # 处理cookie添加的事件
        name = cookie.name().data().decode('utf-8')  # 先获取cookie的名字,再把编码处理一下
        value = cookie.value().data().decode('utf-8')  # 先获取cookie值,再把编码处理一下
        self.cookies[name] = value
        if self.cookies.get('UserToken') and not self.loginDone:
            self.loginSuccessInit()

    def onLoadFinished(self, finished):
        if finished and self.cookies.get('UserToken') and not self.loginDone:
            self.loginSuccessInit()

    def loginSuccessInit(self):
        self.initMenu()
        self.loadAllBlogsCategories()
        self.loadAllDraftBlogCategories()
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.loginDone = True
        self.browser.setHtml('welcome!')
        self.split.setSizes([250, 250, 900, 50])

    # 显示文章到左边的显示框里
    def showArticleInLeftFrame(self, blog: Blog = None):
        if blog != None:
            self.browser.setBlog(blog)
            self.browser.setTitle(blog.title)
            if blog.url == 'draft':
                self.showHtml(blog)
            else:
                self.backend = LoadArticleThread(blog,
                                                 CSDNService(self.cookies))
                self.backend.trigger.connect(self.showHtml)
                self.backend.start()

    def showHtml(self, data: Blog):
        searchTxt = self.searchTxt.text()
        html = data.content
        if len(searchTxt.strip()) > 0:
            html = html.replace(
                searchTxt, '<font style=\'background-color:yellow\'>' +
                searchTxt + '</font>')
        self.browser.setHtml(html)
        self.browser.setMD(data.mdText)

    # 加载loading 条
    def startloading(self):
        self.imageLabel = QLabel()
        self.movie = QMovie("./images/loading.gif")
        self.imageLabel.setMovie(self.movie)
        self.movie.start()
        self.setCentralWidget(self.imageLabel)

    def stopLoading(self):
        self.setCentralWidget(self.browser)

    def initMenu(self):
        tb: QToolBar = self.addToolBar("toolbar")
        add = QAction(QIcon('./images/add.png'), '&新建草稿', self)
        add.setStatusTip('新建文章')
        tb.addAction(add)

        refreshAct = QAction(QIcon('./images/refresh.png'), '&同步文章', self)
        refreshAct.setShortcut('Ctrl+R')
        refreshAct.setStatusTip('从csdn同步文章...')
        tb.addAction(refreshAct)

        personal = QAction(QIcon('./images/personal.png'), '&个人信息', self)
        personal.setStatusTip('个人信息')
        tb.addAction(personal)

        help = QAction(QIcon('./images/help.png'), '&帮助', self)
        help.setStatusTip('帮助说明')
        tb.addAction(help)

        tb.actionTriggered[QAction].connect(self.windowaction)
        tb.setFloatable(False)
        logging.info("menu ini success")

    def windowaction(self, q):
        if q.text() == "&新建草稿":
            self.createDraft()
        elif q.text() == "&同步文章":
            self.reloadArticles()
        elif q.text() == "&个人信息":
            QMessageBox.about(self, "个人信息", "用户名:%s" % self.cookies['UN'])
        elif q.text() == "&帮助":
            QMessageBox.about(
                self, "帮助说明", '''
             同步csdn文章的一个笔记工具,模仿有道笔记的界面,使用到了python,pyqt5, sqllite等
            ''')

    def createDraft(self):
        blog = Blog()
        blog.title = '无标题笔记'
        blog.category = 'draft'
        blog.categoryUrl = 'none'
        blog.content = ''
        blog.mdText = ''
        blog.url = 'draft'
        c = TreeWidgetItem(blog=blog)
        c.setText(0, blog.title)
        c.setIcon(0, QIcon("./images/draft.png"))
        self.draftArticles.addChild(c)
        # todo: how to focus on new created item
        BlogCache().saveArticlies([blog])
        item = ListWidgetItem(blog=blog)
        item.setSizeHint(QSize(100, 80))
        item.setIcon(QIcon("./images/draft.png"))
        item.setText(blog.title)
        self.articleList.addItem(item)

    def reloadArticles(self):
        service = CSDNService(self.cookies)
        #self.reloadPublish=ReloadBlogThread(url='https://blog.csdn.net/guo_xl',service=service,parent=self)
        self.reloadPublish = ReloadBlogThread(url='https://blog.csdn.net/' +
                                              self.cookies['UN'],
                                              service=service,
                                              parent=self)
        self.reloadPublish.start()
        self.reloadPublish.trigger.connect(self.handleReloadMessage)
        self.reloadDraft = ReloadDraftBlogThread(service=service)
        self.reloadDraft.start()
        self.reloadDraft.trigger.connect(self.handleDraftReloadMessage)
        self.statusBar.showMessage("博客加载中...")
        MessageTip("开始同步").show()

    #返回的data是个[],里面装了Blog对象
    def handleDraftReloadMessage(self, data):
        if isinstance(data, str):
            self.statusBar.showMessage(data)
            if data == "加载完毕":
                self.loadAllDraftBlogCategories()
        elif isinstance(data, List):
            self.draftArticles.takeChildren()
            for blog in data:
                c = TreeWidgetItem(blog=blog)
                c.setText(0, blog.title)
                c.setIcon(0, QIcon("./images/draft.png"))
                self.draftArticles.addChild(c)
        elif isinstance(data, Blog):
            self.blogCache.saveArticlies([data])

    def handleReloadMessage(self, data):
        if isinstance(data, str):
            self.statusBar.showMessage(data)
            if data == "加载完毕":
                self.loadAllBlogsCategories()
        elif isinstance(data, dict):
            list = data["data"]
            type = data["type"]
            if type == BlogLoadType.categories:
                self.handleDisplayCategories(list)
            elif type == BlogLoadType.subCategories:
                categoryName = data["categoryName"]
                logging.info("处理%s" % categoryName)
                #找到categoryName对应的menu item
                #坑爹,不要用publishArticlesCategory.takeChildren()移除所有的item
                count = self.publishArticlesCategory.childCount()
                i = 0
                while i < count:
                    c = self.publishArticlesCategory.child(i)
                    i += 1
                    if categoryName == c.text(0):
                        self.handleDisplaySubCategories(list, c)
                        logging.info("处理完%s" % categoryName)
                        return
        elif isinstance(data, Blog):
            self.blogCache.saveArticlies([data])

    #记载草稿
    def loadAllDraftBlogCategories(self):
        data = self.blogCache.searchAllDraft()
        self.draftArticles.takeChildren()
        for blog in data:
            c = TreeWidgetItem(blog=blog)
            c.setText(0, blog.title)
            c.setIcon(0, QIcon("./images/draft.png"))
            self.draftArticles.addChild(c)
            item = ListWidgetItem(blog=blog)
            item.setSizeHint(QSize(100, 80))
            item.setIcon(QIcon("./images/draft.png"))
            item.setText(blog.title)
            self.articleList.addItem(item)

    #加载blog's all categories
    def loadAllBlogsCategories(self):
        # 注意这里写法
        # 这样是错误的,为什么?
        # backend=BackendThread()
        # backend.start()
        # backend.articleCategory.connect(self.handleDisplay)
        data = self.blogCache.searchAllPublished()
        publicCategory = self.publishArticlesCategory
        publicCategory.takeChildren()
        for k, v in data.items():
            child = QTreeWidgetItem()
            child.setText(0, v.get("category"))  # title
            child.setText(1, v.get("categoryUrl"))  # url
            child.setText(2, 'category')
            child.setIcon(0, QIcon("./images/folder_close.png"))
            publicCategory.addChild(child)
            for blog in v.get("blogs"):
                c = TreeWidgetItem(blog=blog)
                c.setText(0, blog.title)
                c.setIcon(0, QIcon("./images/blog_normal.png"))
                child.addChild(c)
                item = ListWidgetItem(blog=blog)
                item.setSizeHint(QSize(100, 80))
                item.setIcon(QIcon("./images/blog_normal.png"))
                item.setText(blog.title)
                self.articleList.addItem(item)
        self.publishArticlesCategory.setDisabled(False)

    def handleDisplayCategories(self, data):
        publicCategory = self.publishArticlesCategory
        for item in data:
            if self.findCategoryByUrl(item["url"], publicCategory) == None:
                child = QTreeWidgetItem()
                child.setText(0, item["title"])  # title
                child.setText(1, item["url"])  # url
                child.setText(2, 'category')
                child.setIcon(0, QIcon("./images/folder_close.png"))
                publicCategory.addChild(child)

    def findCategoryByUrl(self, url, widget):
        total = widget.childCount()
        i = 0
        while i < total:
            if widget.child(i).text(1) == url:
                return widget.child(i)
            i = i + 1
        return None

    def initCategory(self):
        self.container = QWidget(self)
        layout = QVBoxLayout(self.container)
        self.tree = QTreeWidget()
        # 设置列数
        self.tree.setColumnCount(3)
        self.tree.setColumnHidden(1, True)
        self.tree.setColumnHidden(2, True)
        # 设置头的标题
        self.tree.setHeaderLabels(['title', 'url', 'category'])
        self.tree.setHeaderHidden(True)
        # 设置列宽
        self.tree.setColumnWidth(0, 320)
        self.searchTxt = QLineEdit()
        self.searchTxt.setPlaceholderText("关键字查询")
        self.searchTxt.textChanged.connect(self.search)
        layout.addWidget(self.searchTxt)
        layout.addWidget(self.tree)
        self.initPublishArticle()
        self.initDraftArticle()
        self.tree.itemClicked.connect(self.onClicked)
        self.tree.itemCollapsed.connect(self.onCollapsed)
        self.tree.itemExpanded.connect(self.onExpended)
        self.split.addWidget(self.container)

        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(self.onCustomContextMenu)

    def onCustomContextMenu(self, point):
        i = self.tree.indexAt(point)
        menu = QMenu()
        upload = QAction("上传到CSDN", self)
        menu.addAction(upload)
        t = time.time()
        k = menu.exec_(self.tree.mapToGlobal(point))
        #If the menu hasn't been open for more than 0.6s,
        #assume user did not have time to properly react to it opening
        if time.time() - t < 0.6:
            return
        if k == upload:
            msg = QMessageBox()
            msg.setText("同步到csdn")
            msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
            r = msg.exec_()
            if r == QMessageBox.Yes:
                MessageTip("开始同步到csdn").show()
                item = self.tree.currentItem()
                ut = UpdateThread(item.getBlog(), CSDNService(self.cookies))
                ut.update_proess_signal.connect(self.onUploaded)
                ut.start()

    def onUploaded(self, code: int):
        if (code == 200):
            win = MessageTip("发布到csdn成功")
            win.show()

    def onCollapsed(self, index: QModelIndex):
        index.setIcon(0, QIcon("./images/folder_open.png")
                      ) if index.isExpanded() else index.setIcon(
                          0, QIcon("./images/folder_close.png"))

    def onExpended(self, index: QModelIndex):
        index.setIcon(0, QIcon("./images/folder_open.png")
                      ) if index.isExpanded() else index.setIcon(
                          0, QIcon("./images/folder_close.png"))

    def search(self, data):
        result = self.blogCache.fuzzySearch(data)
        #public:QTreeWidgetItem=self.publishArticlesCategory # public
        # total=public.childCount()
        # i=0
        # while i<total:
        #   c=public.child(i)
        #   ctotal=c.childCount()
        #   j=0
        #   while j<ctotal:
        #       if len(data)>0 and (not result or c.child(j).text(0) not in result):
        #           c.child(j).setHidden(True)
        #       else:
        #           c.child(j).setHidden(False)
        #       j=j+1
        #   i=i+1
        i = 0
        articleList: QListWidget = self.articleList
        total = articleList.count()
        while i < total:
            if len(data) > 0 and (not result
                                  or articleList.item(i).text() not in result):
                articleList.item(i).setHidden(True)
            else:
                articleList.item(i).setHidden(False)
            i = i + 1

    def onClicked(self):
        try:
            item = self.tree.currentItem()
            if item != None:
                #None是root节点
                if isinstance(item, TreeWidgetItem):
                    self.showArticleInLeftFrame(item.getBlog())
        except Exception as ex:
            logging.exception(ex)

    #加载category下的子category
    # def loadSubCategories(self, item:QTreeWidgetItem):
    #     list=item.takeChildren()
    #     list.clear()
    #     #改成异步加载
    #     logging.info("loading sub category start")
    #     self.backend=BackendThread(url=item.text(1),type=BlogLoadType.subCategories)
    #     self.backend.trigger.connect(lambda data:self.handleDisplaySubCategories(data, item))
    #     self.backend.start()

    def handleDisplaySubCategories(self, articles, item):
        logging.info("loading %s subcategory" % item.text(0))
        for i in articles:
            if self.findCategoryByUrl(i["url"], item) == None:
                c = QTreeWidgetItem()
                c.setText(0, i['title'])  # title
                c.setText(1, i['url'])  # url
                c.setText(2, 'article')
                c.setText(3, i['updateTime'])
                item.addChild(c)

    def initDraftArticle(self):
        self.draftArticles = QTreeWidgetItem(self.tree)
        self.draftArticles.setText(0, '草稿箱')
        self.draftArticles.setIcon(0, QIcon("./images/folder_close.png"))
        self.tree.addTopLevelItem(self.draftArticles)

    def initPublishArticle(self):
        self.publishArticlesCategory = QTreeWidgetItem(self.tree)
        self.publishArticlesCategory.setText(0, '公开文章')
        self.publishArticlesCategory.setIcon(
            0, QIcon("./images/folder_close.png"))
        self.publishArticlesCategory.setDisabled(True)
        # self.label =  QLabel('', self)
        # self.movie =  QMovie("./images/loading.gif")
        # self.label.setMovie(self.movie)
        # self.movie.start()

        ### 设置节点的背景颜色
        #brush_red = QBrush(Qt.red)
        #self.publishArticles.setBackground(0, brush_red)
        #brush_green = QBrush(Qt.green)
        #self.publishArticlesCategories.setBackground(1, brush_green)
        self.tree.addTopLevelItem(self.publishArticlesCategory)
        #异步加载文章
        #self.statusBar.showMessage("博客加载中...")

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
Beispiel #20
0
class BolumlemePencere(QWidget):
    def __init__(self, ebeveyn=None):
        super(BolumlemePencere, self).__init__(ebeveyn)
        self.ebeveyn = ebeveyn

        self.sistemDiski = ["", ""]
        self.takasDiski = ["", ""]
        self.seciliDisk = None
        self.diskler = parted.getAllDevices()
        disklerWidget = QWidget()
        disklerLayout = QHBoxLayout()
        self.disklerAcilirKutu = QComboBox()
        self.yenileButon = QPushButton(self.tr("Yenile"))
        self.yenileButon.pressed.connect(self.diskYenile)

        self.bolumListeKutu = QTreeWidget()
        self.bolumListeKutu.setColumnCount(4)
        self.bolumListeKutu.headerItem().setText(0, self.tr("bölüm"))
        self.bolumListeKutu.headerItem().setText(1, self.tr("kullanım"))
        self.bolumListeKutu.headerItem().setText(2, self.tr("boyut"))
        self.bolumListeKutu.headerItem().setText(3, self.tr("format"))
        # self.bolumListeKutu.headerItem().setText(4, self.tr("bayraklar"))
        self.bolumListeKutu.headerItem().setText(4, self.tr("bölüm numarası"))

        self.disklerAcilirKutu.currentIndexChanged.connect(self.diskDegisti)

        disklerLayout.addWidget(self.disklerAcilirKutu)
        disklerLayout.addWidget(self.yenileButon)
        disklerWidget.setLayout(disklerLayout)
        layout = QVBoxLayout()
        layout.addWidget(disklerWidget)
        layout.addWidget(self.bolumListeKutu)
        lejant = QLabel()
        lejant.setPixmap(QPixmap(":/gorseller/lejant.png"))
        lejant.setAlignment(Qt.AlignCenter)
        layout.addWidget(lejant)
        self.bolumListeKutu.itemClicked.connect(self.bolumSecildiFonk)
        self.bolumListeKutu.itemDoubleClicked.connect(self.bolumFormatSecFonk)

        opWidget = QWidget()
        opButonlar = QHBoxLayout()
        self.yeniBolumBtn = QPushButton(self.tr("Yeni Bölüm Ekle"))
        self.yeniBolumBtn.pressed.connect(self.bolumEkleFonk)
        self.bolumSilBtn = QPushButton(self.tr("Bölümü Sil"))
        self.bolumSilBtn.pressed.connect(self.bolumSilFonk)
        opButonlar.addWidget(self.yeniBolumBtn)
        opButonlar.addWidget(self.bolumSilBtn)
        opWidget.setLayout(opButonlar)
        layout.addWidget(opWidget)

        self.bolumSilBtn.setEnabled(False)
        self.setLayout(layout)
        self.diskYenile()

    def diskYenile(self):
        self.disklerAcilirKutu.clear()
        self.diskler = parted.getAllDevices()
        for disk in self.diskler:
            try:
                if parted.Disk(disk).type == "msdos":
                    self.disklerAcilirKutu.addItem("{} {} GB ({})".format(
                        disk.model, format(disk.getSize(unit="GB"), '.2f'),
                        disk.path),
                                                   userData=disk.path)
            except parted.DiskLabelException:
                disk = parted.freshDisk(disk, "msdos")
                # CDROM Aygıtları için
                try:
                    disk.commit()
                except parted.IOException:
                    pass
                else:
                    disk = disk.device
                    self.disklerAcilirKutu.addItem("{} {} GB ({})".format(
                        disk.model, format(disk.getSize(unit="GB"), '.2f'),
                        disk.path),
                                                   userData=disk.path)

    def diskDegisti(self):
        if self.disklerAcilirKutu.currentData():
            self.aygit = parted.getDevice(self.disklerAcilirKutu.currentData())
            self.ebeveyn.disk = parted.Disk(self.aygit)
            self.bolumListeYenile()

    def bolumListeYenile(self):
        self.extended = None
        self.bolumListeKutu.clear()
        for bolum in self.ebeveyn.disk.partitions:
            _bolum = self.bolumBilgi(bolum, "GB")
            if self.sistemDiski and bolum.path == self.sistemDiski[0]:
                if self.sistemDiski[1] == "evet":
                    item = self.treeWidgetItemOlustur(_bolum["yol"],
                                                      self.tr("Sistem Diski"),
                                                      _bolum["boyut"] + " GB",
                                                      "ext4",
                                                      _bolum["bayraklar"],
                                                      _bolum["no"])
                else:
                    item = self.treeWidgetItemOlustur(_bolum["yol"],
                                                      self.tr("Sistem Diski"),
                                                      _bolum["boyut"],
                                                      _bolum["dosyaSis"],
                                                      _bolum["bayraklar"],
                                                      _bolum["no"])
            elif self.takasDiski and bolum.path == self.takasDiski[0]:
                item = self.treeWidgetItemOlustur(_bolum["yol"],
                                                  self.tr("Takas Alanı"),
                                                  _bolum["boyut"],
                                                  self.tr("takas"),
                                                  _bolum["bayraklar"],
                                                  _bolum["no"])
            else:
                item = self.treeWidgetItemOlustur(_bolum["yol"], "",
                                                  _bolum["boyut"],
                                                  _bolum["dosyaSis"],
                                                  _bolum["bayraklar"],
                                                  _bolum["no"])

            if _bolum["tur"] == parted.PARTITION_NORMAL:
                item.setIcon(0, QIcon("gorseller/primary.xpm"))
            elif _bolum["tur"] == parted.PARTITION_EXTENDED:
                item.setIcon(0, QIcon("gorseller/extended.xpm"))
                self.extended = item
            elif _bolum["tur"] == parted.PARTITION_LOGICAL:
                item.setIcon(0, QIcon("gorseller/logical.xpm"))
                self.extended.addChild(item)
                self.extended.setExpanded(True)
            self.bolumListeKutu.addTopLevelItem(item)

        for bosBolum in self.ebeveyn.disk.getFreeSpacePartitions():
            _toplam = 0
            _bolum = self.bolumBilgi(bosBolum, "GB")
            if float(_bolum["boyut"]) > 1:
                if _bolum["tur"] == 5:
                    uzatilmisKalan = self.treeWidgetItemOlustur(
                        "", self.tr("Uzatılmış Bölüm Kalan"), _bolum["boyut"],
                        "", "", "ayrilmamis")
                    uzatilmisKalan.setIcon(0, QIcon(":/gorseller/blank.xpm"))
                    self.extended.addChild(uzatilmisKalan)
                    self.extended.setExpanded(True)
                if _bolum["tur"] == parted.PARTITION_FREESPACE:
                    _toplam = _toplam + float(_bolum["boyut"])
                ayrilmamis = self.treeWidgetItemOlustur(
                    "", self.tr("Ayrılmamış Bölüm"), _toplam, "", "",
                    "ayrilmamis")
                ayrilmamis.setIcon(0, QIcon(":/gorseller/blank.xpm"))
                self.bolumListeKutu.addTopLevelItem(ayrilmamis)
        self.bolumListeKutu.resizeColumnToContents(0)
        self.bolumListeKutu.resizeColumnToContents(1)
        self.bolumListeKutu.resizeColumnToContents(2)
        self.bolumListeKutu.resizeColumnToContents(3)

    def treeWidgetItemOlustur(self, bolum, kullanim, boyut, format, islev,
                              bolumno):
        item = QTreeWidgetItem()
        item.setText(0, str(bolum))
        item.setText(1, str(kullanim))
        item.setText(2, str(boyut) + " GB ")
        item.setText(3, str(format))
        # item.setText(4, str(islev))
        item.setText(4, str(bolumno))
        return item

    def bolumSecildiFonk(self, tiklanan):
        if tiklanan.text(4) != "ayrilmamis":
            self.bolumSilBtn.setEnabled(True)
        else:
            self.bolumSilBtn.setEnabled(False)

    def bolumFormatSecFonk(self, tiklanan):
        if tiklanan.text(4) != "ayrilmamis":
            self.seciliDisk = tiklanan
            diskOzellikPencere = diskOzellikleriSinif(self)
            diskOzellikPencere.exec_()
            if self.sistemDiski[0] != "":
                self.ebeveyn.kurparam["disk"]["bolum"] = self.sistemDiski[0]
                self.ebeveyn.kurparam["disk"]["format"] = self.sistemDiski[1]
            if self.takasDiski[0] != "":
                self.ebeveyn.kurparam["disk"]["takasbolum"] = self.takasDiski[
                    0]
            else:
                self.ebeveyn.kurparam["disk"]["takasbolum"] = ""

            if self.sistemDiski[0] == "":
                pass
            elif self.sistemDiski[0] != "" and self.takasDiski[0] == "":
                QMessageBox.information(
                    self, self.tr("Bilgi"),
                    self.
                    tr("Takas Alanı Belirtmediniz\nTakas alanı ram miktarınızın düşük olduğu durumlarda\nram yerine disk kullanarak işlemlerin devam etmesini sağlar."
                       ))
                self.ebeveyn.ileriDugme.setDisabled(False)
                self.bolumListeYenile()
            elif self.sistemDiski[0] != "" and self.takasDiski[0] != "":
                if self.sistemDiski[0] == self.takasDiski[0]:
                    QMessageBox.warning(
                        self, self.tr("Hata"), self.takasDiski[0] + self.
                        tr(" diskini hem sistem hem takas için seçtiniz\nAynı diski hem sistem hem takas olarak kullanmazsınız"
                           ))
                    self.ebeveyn.ileriDugme.setDisabled(True)
                else:
                    self.ebeveyn.ileriDugme.setDisabled(False)
                    self.bolumListeYenile()

    def bolumEkleFonk(self):
        if self._en_buyuk_bos_alan():
            alan = self._en_buyuk_bos_alan()
            birincilSayi = len(self.ebeveyn.disk.getPrimaryPartitions())
            uzatilmisSayi = ext_count = 1 if self.ebeveyn.disk.getExtendedPartition(
            ) else 0
            parts_avail = self.ebeveyn.disk.maxPrimaryPartitionCount - (
                birincilSayi + uzatilmisSayi)
            if not parts_avail and not ext_count:
                QMessageBox.warning(
                    self, self.tr("Uyarı"),
                    self.
                    tr("""Eğer dörtten fazla disk bölümü oluşturmak istiyorsanız birincil bölümlerden birini silip uzatılmış bölüm oluşturun. 
                                    Bu durumda oluşturduğunuz uzatılmış bölümleri işletim sistemi kurmak için kullanamayacağınızı aklınızda bulundurun."""
                       ))
            else:
                if parts_avail:
                    if not uzatilmisSayi and parts_avail > 1:
                        self.bolumOlustur(alan, parted.PARTITION_NORMAL)
                        self.bolumListeYenile()
                    elif parts_avail == 1:
                        self.bolumOlustur(alan, parted.PARTITION_EXTENDED)
                        self.bolumListeYenile()
                if uzatilmisSayi:
                    ext_part = self.ebeveyn.disk.getExtendedPartition()
                    try:
                        alan = ext_part.geometry.intersect(alan)
                    except ArithmeticError:
                        QMessageBox.critical(
                            self, self.tr("Hata"),
                            self.
                            tr("Yeni disk bölümü oluşturmak için yeterli alan yok ! Uzatılmış bölümün boyutunu arttırmayı deneyiniz."
                               ))
                    else:
                        self.bolumOlustur(alan, parted.PARTITION_LOGICAL)
                        self.bolumListeYenile()

    def bolumSilFonk(self):
        if self.bolumListeKutu.currentItem().text(4) != "ayrilmamis":
            bolumNo = int(self.bolumListeKutu.currentItem().text(4))
            for bolum in self.ebeveyn.disk.partitions:
                if bolum.number == bolumNo:
                    try:
                        self.ebeveyn.disk.deletePartition(bolum)
                        self.bolumListeYenile()
                    except parted.PartitionException:
                        QMessageBox.warning(
                            self, self.tr("Uyarı"),
                            self.
                            tr("Lütfen uzatılmış bölümleri silmeden önce mantıksal bölümleri siliniz."
                               ))
            self.bolumSilBtn.setDisabled(True)

    def bolumBilgi(self, bolum, birim):
        _bolum = {}
        _bolum["yol"] = bolum.path
        if birim == "GB":
            _bolum["boyut"] = format(bolum.getSize(unit=birim), '.2f')
        else:
            _bolum["boyut"] = bolum.getSize(unit=birim)
        _bolum["dosyaSis"] = "Bilinmeyen"

        if bolum.fileSystem:
            if bolum.fileSystem.type.startswith('linux-swap'):
                _bolum["dosyaSis"] = "takas"
            else:
                _bolum["dosyaSis"] = bolum.fileSystem.type
        try:
            _bolum["bayraklar"] = bolum.getFlagsAsString()
        except:
            pass
        _bolum["no"] = bolum.number
        _bolum["tur"] = bolum.type
        return _bolum

    def _en_buyuk_bos_alan(self):
        maks_boyut = -1
        alan = None
        alignment = self.aygit.optimumAlignment

        for _alan in self.ebeveyn.disk.getFreeSpaceRegions():
            if _alan.length > maks_boyut and _alan.length > alignment.grainSize:
                alan = _alan
                maks_boyut = _alan.length
        return alan

    def bolumOlustur(self, alan, bolumTur):
        if bolumTur == parted.PARTITION_NORMAL or bolumTur == parted.PARTITION_EXTENDED:
            for bosBolum in self.ebeveyn.disk.getFreeSpacePartitions():
                _bolum = self.bolumBilgi(bosBolum, "GB")
                if _bolum["tur"] == parted.PARTITION_FREESPACE:
                    maksBoyut = float(_bolum["boyut"])
        elif bolumTur == bolumTur == parted.PARTITION_LOGICAL:
            for bosBolum in self.ebeveyn.disk.getFreeSpacePartitions():
                _bolum = self.bolumBilgi(bosBolum, "GB")
                if _bolum["tur"] == 5:
                    maksBoyut = float(_bolum["boyut"])

        alignment = self.aygit.optimalAlignedConstraint
        constraint = self.aygit.getConstraint()
        data = {
            'start': constraint.startAlign.alignUp(alan, alan.start),
            'end': constraint.endAlign.alignDown(alan, alan.end),
        }

        boyut, ok = QInputDialog().getDouble(self,
                                             self.tr('Bölüm oluştur'),
                                             self.tr('GB cinsinden boyut:'),
                                             min=0.001,
                                             value=1,
                                             max=maksBoyut,
                                             decimals=3)

        if ok:
            data["end"] = int(data["start"]) + int(
                parted.sizeToSectors(float(boyut), "GiB",
                                     self.aygit.sectorSize))
            try:
                geometry = parted.Geometry(device=self.aygit,
                                           start=int(data["start"]),
                                           end=int(data["end"]))
                partition = parted.Partition(
                    disk=self.ebeveyn.disk,
                    type=bolumTur,
                    geometry=geometry,
                )

                self.ebeveyn.disk.addPartition(partition=partition,
                                               constraint=constraint)
            except (parted.PartitionException, parted.GeometryException,
                    parted.CreateException) as e:
                # GeometryException accounts for incorrect start/end values (e.g. start < end),
                # CreateException is raised e.g. when the partition doesn't fit on the disk.
                # PartedException is a generic error (e.g. start/end values out of range)
                raise RuntimeError(e.message)
Beispiel #21
-1
class ConfigurationWidget(QWidget):
    """
    Class implementing a dialog for the configuration of eric6.
    
    @signal preferencesChanged() emitted after settings have been changed
    @signal masterPasswordChanged(str, str) emitted after the master
        password has been changed with the old and the new password
    @signal accepted() emitted to indicate acceptance of the changes
    @signal rejected() emitted to indicate rejection of the changes
    """
    preferencesChanged = pyqtSignal()
    masterPasswordChanged = pyqtSignal(str, str)
    accepted = pyqtSignal()
    rejected = pyqtSignal()
    
    DefaultMode = 0
    HelpBrowserMode = 1
    TrayStarterMode = 2
    HexEditorMode = 3
    
    def __init__(self, parent=None, fromEric=True, displayMode=DefaultMode,
                 expandedEntries=[]):
        """
        Constructor
        
        @param parent The parent widget of this dialog. (QWidget)
        @keyparam fromEric flag indicating a dialog generation from within the
            eric6 ide (boolean)
        @keyparam displayMode mode of the configuration dialog
            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode)
        @exception RuntimeError raised to indicate an invalid dialog mode
        @keyparam expandedEntries list of entries to be shown expanded
            (list of strings)
        """
        assert displayMode in (
            ConfigurationWidget.DefaultMode,
            ConfigurationWidget.HelpBrowserMode,
            ConfigurationWidget.TrayStarterMode,
            ConfigurationWidget.HexEditorMode,
        )
        
        super(ConfigurationWidget, self).__init__(parent)
        self.fromEric = fromEric
        self.displayMode = displayMode
        
        self.__setupUi()
        
        self.itmDict = {}
        
        if not fromEric:
            from PluginManager.PluginManager import PluginManager
            try:
                self.pluginManager = e5App().getObject("PluginManager")
            except KeyError:
                self.pluginManager = PluginManager(self)
                e5App().registerObject("PluginManager", self.pluginManager)
        
        if displayMode == ConfigurationWidget.DefaultMode:
            self.configItems = {
                # key : [display string, pixmap name, dialog module name or
                #        page creation function, parent key,
                #        reference to configuration page (must always be last)]
                # The dialog module must have the module function 'create' to
                # create the configuration page. This must have the method
                # 'save' to save the settings.
                "applicationPage":
                [self.tr("Application"), "preferences-application.png",
                 "ApplicationPage", None, None],
                "cooperationPage":
                [self.tr("Cooperation"), "preferences-cooperation.png",
                 "CooperationPage", None, None],
                "corbaPage":
                [self.tr("CORBA"), "preferences-orbit.png",
                 "CorbaPage", None, None],
                "emailPage":
                [self.tr("Email"), "preferences-mail_generic.png",
                 "EmailPage", None, None],
                "graphicsPage":
                [self.tr("Graphics"), "preferences-graphics.png",
                 "GraphicsPage", None, None],
                "hexEditorPage":
                [self.tr("Hex Editor"), "hexEditor.png",
                 "HexEditorPage", None, None],
                "iconsPage":
                [self.tr("Icons"), "preferences-icons.png",
                 "IconsPage", None, None],
                "ircPage":
                [self.tr("IRC"), "irc.png",
                 "IrcPage", None, None],
                "logViewerPage":
                [self.tr("Log-Viewer"), "preferences-logviewer.png",
                 "LogViewerPage", None, None],
                "mimeTypesPage":
                [self.tr("Mimetypes"), "preferences-mimetypes.png",
                 "MimeTypesPage", None, None],
                "networkPage":
                [self.tr("Network"), "preferences-network.png",
                 "NetworkPage", None, None],
                "notificationsPage":
                [self.tr("Notifications"),
                 "preferences-notifications.png",
                 "NotificationsPage", None, None],
                "pluginManagerPage":
                [self.tr("Plugin Manager"),
                 "preferences-pluginmanager.png",
                 "PluginManagerPage", None, None],
                "printerPage":
                [self.tr("Printer"), "preferences-printer.png",
                 "PrinterPage", None, None],
                "pythonPage":
                [self.tr("Python"), "preferences-python.png",
                 "PythonPage", None, None],
                "qtPage":
                [self.tr("Qt"), "preferences-qtlogo.png",
                 "QtPage", None, None],
                "securityPage":
                [self.tr("Security"), "preferences-security.png",
                 "SecurityPage", None, None],
                "shellPage":
                [self.tr("Shell"), "preferences-shell.png",
                 "ShellPage", None, None],
                "tasksPage":
                [self.tr("Tasks"), "task.png",
                 "TasksPage", None, None],
                "templatesPage":
                [self.tr("Templates"), "preferences-template.png",
                 "TemplatesPage", None, None],
                "trayStarterPage":
                [self.tr("Tray Starter"), "erict.png",
                 "TrayStarterPage", None, None],
                "vcsPage":
                [self.tr("Version Control Systems"),
                 "preferences-vcs.png",
                 "VcsPage", None, None],
                
                "0debuggerPage":
                [self.tr("Debugger"), "preferences-debugger.png",
                 None, None, None],
                "debuggerGeneralPage":
                [self.tr("General"), "preferences-debugger.png",
                 "DebuggerGeneralPage", "0debuggerPage", None],
                "debuggerPythonPage":
                [self.tr("Python"), "preferences-pyDebugger.png",
                 "DebuggerPythonPage", "0debuggerPage", None],
                "debuggerPython3Page":
                [self.tr("Python3"), "preferences-pyDebugger.png",
                 "DebuggerPython3Page", "0debuggerPage", None],
                
                "0editorPage":
                [self.tr("Editor"), "preferences-editor.png",
                 None, None, None],
                "editorAPIsPage":
                [self.tr("APIs"), "preferences-api.png",
                 "EditorAPIsPage", "0editorPage", None],
                "editorAutocompletionPage":
                [self.tr("Autocompletion"),
                 "preferences-autocompletion.png",
                 "EditorAutocompletionPage", "0editorPage", None],
                "editorAutocompletionQScintillaPage":
                [self.tr("QScintilla"), "qscintilla.png",
                 "EditorAutocompletionQScintillaPage",
                 "editorAutocompletionPage", None],
                "editorCalltipsPage":
                [self.tr("Calltips"), "preferences-calltips.png",
                 "EditorCalltipsPage", "0editorPage", None],
                "editorCalltipsQScintillaPage":
                [self.tr("QScintilla"), "qscintilla.png",
                 "EditorCalltipsQScintillaPage", "editorCalltipsPage", None],
                "editorGeneralPage":
                [self.tr("General"), "preferences-general.png",
                 "EditorGeneralPage", "0editorPage", None],
                "editorFilePage":
                [self.tr("Filehandling"),
                 "preferences-filehandling.png",
                 "EditorFilePage", "0editorPage", None],
                "editorSearchPage":
                [self.tr("Searching"), "preferences-search.png",
                 "EditorSearchPage", "0editorPage", None],
                "editorSpellCheckingPage":
                [self.tr("Spell checking"),
                 "preferences-spellchecking.png",
                 "EditorSpellCheckingPage", "0editorPage", None],
                "editorStylesPage":
                [self.tr("Style"), "preferences-styles.png",
                 "EditorStylesPage", "0editorPage", None],
                "editorSyntaxPage":
                [self.tr("Code Checkers"), "preferences-debugger.png",
                 "EditorSyntaxPage", "0editorPage", None],
                "editorTypingPage":
                [self.tr("Typing"), "preferences-typing.png",
                 "EditorTypingPage", "0editorPage", None],
                "editorExportersPage":
                [self.tr("Exporters"), "preferences-exporters.png",
                 "EditorExportersPage", "0editorPage", None],
                
                "1editorLexerPage":
                [self.tr("Highlighters"),
                 "preferences-highlighting-styles.png",
                 None, "0editorPage", None],
                "editorHighlightersPage":
                [self.tr("Filetype Associations"),
                 "preferences-highlighter-association.png",
                 "EditorHighlightersPage", "1editorLexerPage", None],
                "editorHighlightingStylesPage":
                [self.tr("Styles"),
                 "preferences-highlighting-styles.png",
                 "EditorHighlightingStylesPage", "1editorLexerPage", None],
                "editorKeywordsPage":
                [self.tr("Keywords"), "preferences-keywords.png",
                 "EditorKeywordsPage", "1editorLexerPage", None],
                "editorPropertiesPage":
                [self.tr("Properties"), "preferences-properties.png",
                 "EditorPropertiesPage", "1editorLexerPage", None],
                
                "1editorMouseClickHandlers":
                [self.tr("Mouse Click Handlers"),
                 "preferences-mouse-click-handler.png",
                 "EditorMouseClickHandlerPage", "0editorPage", None],
                
                "0helpPage":
                [self.tr("Help"), "preferences-help.png",
                 None, None, None],
                "helpDocumentationPage":
                [self.tr("Help Documentation"),
                 "preferences-helpdocumentation.png",
                 "HelpDocumentationPage", "0helpPage", None],
                "helpViewersPage":
                [self.tr("Help Viewers"),
                 "preferences-helpviewers.png",
                 "HelpViewersPage", "0helpPage", None],
                
                "0projectPage":
                [self.tr("Project"), "preferences-project.png",
                 None, None, None],
                "projectBrowserPage":
                [self.tr("Project Viewer"), "preferences-project.png",
                 "ProjectBrowserPage", "0projectPage", None],
                "projectPage":
                [self.tr("Project"), "preferences-project.png",
                 "ProjectPage", "0projectPage", None],
                "multiProjectPage":
                [self.tr("Multiproject"),
                 "preferences-multiproject.png",
                 "MultiProjectPage", "0projectPage", None],
                
                "0interfacePage":
                [self.tr("Interface"), "preferences-interface.png",
                 None, None, None],
                "interfacePage":
                [self.tr("Interface"), "preferences-interface.png",
                 "InterfacePage", "0interfacePage", None],
                "viewmanagerPage":
                [self.tr("Viewmanager"), "preferences-viewmanager.png",
                 "ViewmanagerPage", "0interfacePage", None],
            }
            try:
                from PyQt5 import QtWebKit      # __IGNORE_WARNING__
                self.configItems.update({
                    "helpAppearancePage":
                    [self.tr("Appearance"), "preferences-styles.png",
                     "HelpAppearancePage", "0helpPage", None],
                    "helpFlashCookieManagerPage":
                    [self.tr("Flash Cookie Manager"),
                     "flashCookie16.png",
                     "HelpFlashCookieManagerPage", "0helpPage", None],
                    "helpVirusTotalPage":
                    [self.tr("VirusTotal Interface"), "virustotal.png",
                     "HelpVirusTotalPage", "0helpPage", None],
                    "helpWebBrowserPage":
                    [self.tr("eric6 Web Browser"), "ericWeb.png",
                     "HelpWebBrowserPage", "0helpPage", None],
                })
            except ImportError:
                pass
            
            self.configItems.update(
                e5App().getObject("PluginManager").getPluginConfigData())
        
        elif displayMode == ConfigurationWidget.HelpBrowserMode:
            self.configItems = {
                # key : [display string, pixmap name, dialog module name or
                #        page creation function, parent key,
                #        reference to configuration page (must always be last)]
                # The dialog module must have the module function 'create' to
                # create the configuration page. This must have the method
                # 'save' to save the settings.
                "interfacePage":
                [self.tr("Interface"), "preferences-interface.png",
                 "HelpInterfacePage", None, None],
                "networkPage":
                [self.tr("Network"), "preferences-network.png",
                 "NetworkPage", None, None],
                "printerPage":
                [self.tr("Printer"), "preferences-printer.png",
                 "PrinterPage", None, None],
                "securityPage":
                [self.tr("Security"), "preferences-security.png",
                 "SecurityPage", None, None],
                
                "0helpPage":
                [self.tr("Help"), "preferences-help.png",
                 None, None, None],
                "helpDocumentationPage":
                [self.tr("Help Documentation"),
                 "preferences-helpdocumentation.png",
                 "HelpDocumentationPage", "0helpPage", None],
            }
            try:
                from PyQt5 import QtWebKit      # __IGNORE_WARNING__
                self.configItems.update({
                    "helpAppearancePage":
                    [self.tr("Appearance"), "preferences-styles.png",
                     "HelpAppearancePage", "0helpPage", None],
                    "helpFlashCookieManagerPage":
                    [self.tr("Flash Cookie Manager"),
                     "flashCookie16.png",
                     "HelpFlashCookieManagerPage", "0helpPage", None],
                    "helpVirusTotalPage":
                    [self.tr("VirusTotal Interface"), "virustotal.png",
                     "HelpVirusTotalPage", "0helpPage", None],
                    "helpWebBrowserPage":
                    [self.tr("eric6 Web Browser"), "ericWeb.png",
                     "HelpWebBrowserPage", "0helpPage", None],
                })
            except ImportError:
                pass
        
        elif displayMode == ConfigurationWidget.TrayStarterMode:
            self.configItems = {
                # key : [display string, pixmap name, dialog module name or
                #        page creation function, parent key,
                #        reference to configuration page (must always be last)]
                # The dialog module must have the module function 'create' to
                # create the configuration page. This must have the method
                # 'save' to save the settings.
                "trayStarterPage":
                [self.tr("Tray Starter"), "erict.png",
                 "TrayStarterPage", None, None],
            }
        
        elif displayMode == ConfigurationWidget.HexEditorMode:
            self.configItems = {
                # key : [display string, pixmap name, dialog module name or
                #        page creation function, parent key,
                #        reference to configuration page (must always be last)]
                # The dialog module must have the module function 'create' to
                # create the configuration page. This must have the method
                # 'save' to save the settings.
                "hexEditorPage":
                [self.tr("Hex Editor"), "hexEditor.png",
                 "HexEditorPage", None, None],
            }
        
        else:
            raise RuntimeError("Illegal mode value: {0}".format(displayMode))
        
        # generate the list entries
        self.__expandedEntries = []
        for key in sorted(self.configItems.keys()):
            pageData = self.configItems[key]
            if pageData[3]:
                if pageData[3] in self.itmDict:
                    pitm = self.itmDict[pageData[3]]  # get the parent item
                else:
                    continue
            else:
                pitm = self.configList
            self.itmDict[key] = ConfigurationPageItem(pitm, pageData[0], key,
                                                      pageData[1])
            self.itmDict[key].setData(0, Qt.UserRole, key)
            if (not self.fromEric or
                displayMode != ConfigurationWidget.DefaultMode or
                    key in expandedEntries):
                self.itmDict[key].setExpanded(True)
        self.configList.sortByColumn(0, Qt.AscendingOrder)
        
        # set the initial size of the splitter
        self.configSplitter.setSizes([200, 600])
        
        self.configList.itemActivated.connect(self.__showConfigurationPage)
        self.configList.itemClicked.connect(self.__showConfigurationPage)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.rejected)
        
        if displayMode in [ConfigurationWidget.HelpBrowserMode,
                           ConfigurationWidget.TrayStarterMode,
                           ConfigurationWidget.HexEditorMode]:
            self.configListSearch.hide()
        
        if displayMode not in [ConfigurationWidget.TrayStarterMode,
                               ConfigurationWidget.HexEditorMode]:
            self.__initLexers()
        
    def accept(self):
        """
        Public slot to accept the buttonBox accept signal.
        """
        if not isMacPlatform():
            wdg = self.focusWidget()
            if wdg == self.configList:
                return
        
        self.accepted.emit()
        
    def __setupUi(self):
        """
        Private method to perform the general setup of the configuration
        widget.
        """
        self.setObjectName("ConfigurationDialog")
        self.resize(900, 650)
        self.verticalLayout_2 = QVBoxLayout(self)
        self.verticalLayout_2.setSpacing(6)
        self.verticalLayout_2.setContentsMargins(6, 6, 6, 6)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        
        self.configSplitter = QSplitter(self)
        self.configSplitter.setOrientation(Qt.Horizontal)
        self.configSplitter.setObjectName("configSplitter")
        
        self.configListWidget = QWidget(self.configSplitter)
        self.leftVBoxLayout = QVBoxLayout(self.configListWidget)
        self.leftVBoxLayout.setContentsMargins(0, 0, 0, 0)
        self.leftVBoxLayout.setSpacing(0)
        self.leftVBoxLayout.setObjectName("leftVBoxLayout")
        self.configListSearch = E5ClearableLineEdit(
            self, self.tr("Enter search text..."))
        self.configListSearch.setObjectName("configListSearch")
        self.leftVBoxLayout.addWidget(self.configListSearch)
        self.configList = QTreeWidget()
        self.configList.setObjectName("configList")
        self.leftVBoxLayout.addWidget(self.configList)
        self.configListSearch.textChanged.connect(self.__searchTextChanged)
        
        self.scrollArea = QScrollArea(self.configSplitter)
        self.scrollArea.setFrameShape(QFrame.NoFrame)
        self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scrollArea.setWidgetResizable(False)
        self.scrollArea.setObjectName("scrollArea")
        
        self.configStack = QStackedWidget()
        self.configStack.setFrameShape(QFrame.Box)
        self.configStack.setFrameShadow(QFrame.Sunken)
        self.configStack.setObjectName("configStack")
        self.scrollArea.setWidget(self.configStack)
        
        self.emptyPage = QWidget()
        self.emptyPage.setGeometry(QRect(0, 0, 372, 591))
        self.emptyPage.setObjectName("emptyPage")
        self.vboxlayout = QVBoxLayout(self.emptyPage)
        self.vboxlayout.setSpacing(6)
        self.vboxlayout.setContentsMargins(6, 6, 6, 6)
        self.vboxlayout.setObjectName("vboxlayout")
        spacerItem = QSpacerItem(
            20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.vboxlayout.addItem(spacerItem)
        self.emptyPagePixmap = QLabel(self.emptyPage)
        self.emptyPagePixmap.setAlignment(Qt.AlignCenter)
        self.emptyPagePixmap.setObjectName("emptyPagePixmap")
        self.emptyPagePixmap.setPixmap(
            QPixmap(os.path.join(getConfig('ericPixDir'), 'eric.png')))
        self.vboxlayout.addWidget(self.emptyPagePixmap)
        self.textLabel1 = QLabel(self.emptyPage)
        self.textLabel1.setAlignment(Qt.AlignCenter)
        self.textLabel1.setObjectName("textLabel1")
        self.vboxlayout.addWidget(self.textLabel1)
        spacerItem1 = QSpacerItem(
            20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.vboxlayout.addItem(spacerItem1)
        self.configStack.addWidget(self.emptyPage)
        
        self.verticalLayout_2.addWidget(self.configSplitter)
        
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Apply | QDialogButtonBox.Cancel |
            QDialogButtonBox.Ok | QDialogButtonBox.Reset)
        self.buttonBox.setObjectName("buttonBox")
        if not self.fromEric and \
                self.displayMode == ConfigurationWidget.DefaultMode:
            self.buttonBox.button(QDialogButtonBox.Apply).hide()
        self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False)
        self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(False)
        self.verticalLayout_2.addWidget(self.buttonBox)

        self.setWindowTitle(self.tr("Preferences"))
        
        self.configList.header().hide()
        self.configList.header().setSortIndicator(0, Qt.AscendingOrder)
        self.configList.setSortingEnabled(True)
        self.textLabel1.setText(
            self.tr("Please select an entry of the list \n"
                    "to display the configuration page."))
        
        QMetaObject.connectSlotsByName(self)
        self.setTabOrder(self.configList, self.configStack)
        
        self.configStack.setCurrentWidget(self.emptyPage)
        
        self.configList.setFocus()
    
    def __searchTextChanged(self, text):
        """
        Private slot to handle a change of the search text.
        
        @param text text to search for (string)
        """
        self.__searchChildItems(self.configList.invisibleRootItem(), text)
    
    def __searchChildItems(self, parent, text):
        """
        Private method to enable child items based on a search string.
        
        @param parent reference to the parent item (QTreeWidgetItem)
        @param text text to search for (string)
        @return flag indicating an enabled child item (boolean)
        """
        childEnabled = False
        text = text.lower()
        for index in range(parent.childCount()):
            itm = parent.child(index)
            if itm.childCount() > 0:
                enable = self.__searchChildItems(itm, text) or \
                    text == "" or text in itm.text(0).lower()
            else:
                enable = text == "" or text in itm.text(0).lower()
            if enable:
                childEnabled = True
            itm.setDisabled(not enable)
        
        return childEnabled
    
    def __initLexers(self):
        """
        Private method to initialize the dictionary of preferences lexers.
        """
        import QScintilla.Lexers
        from .PreferencesLexer import PreferencesLexer, \
            PreferencesLexerLanguageError
        
        self.lexers = {}
        for language in QScintilla.Lexers.getSupportedLanguages():
            if language not in self.lexers:
                try:
                    self.lexers[language] = PreferencesLexer(language, self)
                except PreferencesLexerLanguageError:
                    pass
        
    def __importConfigurationPage(self, name):
        """
        Private method to import a configuration page module.
        
        @param name name of the configuration page module (string)
        @return reference to the configuration page module
        """
        modName = "Preferences.ConfigurationPages.{0}".format(name)
        try:
            mod = __import__(modName)
            components = modName.split('.')
            for comp in components[1:]:
                mod = getattr(mod, comp)
            return mod
        except ImportError:
            E5MessageBox.critical(
                self,
                self.tr("Configuration Page Error"),
                self.tr("""<p>The configuration page <b>{0}</b>"""
                        """ could not be loaded.</p>""").format(name))
            return None
        
    def __showConfigurationPage(self, itm, column):
        """
        Private slot to show a selected configuration page.
        
        @param itm reference to the selected item (QTreeWidgetItem)
        @param column column that was selected (integer) (ignored)
        """
        pageName = itm.getPageName()
        self.showConfigurationPageByName(pageName, setCurrent=False)
        
    def __initPage(self, pageData):
        """
        Private method to initialize a configuration page.
        
        @param pageData data structure for the page to initialize
        @return reference to the initialized page
        """
        page = None
        if isinstance(pageData[2], types.FunctionType):
            page = pageData[2](self)
        else:
            mod = self.__importConfigurationPage(pageData[2])
            if mod:
                page = mod.create(self)
        if page is not None:
            self.configStack.addWidget(page)
            pageData[-1] = page
            try:
                page.setMode(self.displayMode)
            except AttributeError:
                pass
        return page
        
    def showConfigurationPageByName(self, pageName, setCurrent=True):
        """
        Public slot to show a named configuration page.
        
        @param pageName name of the configuration page to show (string)
        @param setCurrent flag indicating to set the current item (boolean)
        """
        if pageName == "empty" or pageName not in self.configItems:
            page = self.emptyPage
        else:
            pageData = self.configItems[pageName]
            if pageData[-1] is None and pageData[2] is not None:
                # the page was not loaded yet, create it
                page = self.__initPage(pageData)
            else:
                page = pageData[-1]
            if page is None:
                page = self.emptyPage
            elif setCurrent:
                items = self.configList.findItems(
                    pageData[0],
                    Qt.MatchFixedString | Qt.MatchRecursive)
                for item in items:
                    if item.data(0, Qt.UserRole) == pageName:
                        self.configList.setCurrentItem(item)
        self.configStack.setCurrentWidget(page)
        ssize = self.scrollArea.size()
        if self.scrollArea.horizontalScrollBar():
            ssize.setHeight(
                ssize.height() -
                self.scrollArea.horizontalScrollBar().height() - 2)
        if self.scrollArea.verticalScrollBar():
            ssize.setWidth(
                ssize.width() -
                self.scrollArea.verticalScrollBar().width() - 2)
        psize = page.minimumSizeHint()
        self.configStack.resize(max(ssize.width(), psize.width()),
                                max(ssize.height(), psize.height()))
        
        if page != self.emptyPage:
            page.polishPage()
            self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(True)
            self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(True)
        else:
            self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False)
            self.buttonBox.button(QDialogButtonBox.Reset).setEnabled(False)
        
        # reset scrollbars
        for sb in [self.scrollArea.horizontalScrollBar(),
                   self.scrollArea.verticalScrollBar()]:
            if sb:
                sb.setValue(0)
        
        self.__currentConfigurationPageName = pageName
        
    def getConfigurationPageName(self):
        """
        Public method to get the page name of the current page.
        
        @return page name of the current page (string)
        """
        return self.__currentConfigurationPageName
        
    def calledFromEric(self):
        """
        Public method to check, if invoked from within eric.
        
        @return flag indicating invocation from within eric (boolean)
        """
        return self.fromEric
        
    def getPage(self, pageName):
        """
        Public method to get a reference to the named page.
        
        @param pageName name of the configuration page (string)
        @return reference to the page or None, indicating page was
            not loaded yet
        """
        return self.configItems[pageName][-1]
        
    def getLexers(self):
        """
        Public method to get a reference to the lexers dictionary.
        
        @return reference to the lexers dictionary
        """
        return self.lexers
        
    def setPreferences(self):
        """
        Public method called to store the selected values into the preferences
        storage.
        """
        for key, pageData in list(self.configItems.items()):
            if pageData[-1]:
                pageData[-1].save()
                # page was loaded (and possibly modified)
                QApplication.processEvents()    # ensure HMI is responsive
        
    def on_buttonBox_clicked(self, button):
        """
        Private slot called by a button of the button box clicked.
        
        @param button button that was clicked (QAbstractButton)
        """
        if button == self.buttonBox.button(QDialogButtonBox.Apply):
            self.on_applyButton_clicked()
        elif button == self.buttonBox.button(QDialogButtonBox.Reset):
            self.on_resetButton_clicked()
        
    @pyqtSlot()
    def on_applyButton_clicked(self):
        """
        Private slot called to apply the settings of the current page.
        """
        if self.configStack.currentWidget() != self.emptyPage:
            page = self.configStack.currentWidget()
            savedState = page.saveState()
            page.save()
            self.preferencesChanged.emit()
            if savedState is not None:
                page.setState(savedState)
            page.polishPage()
        
    @pyqtSlot()
    def on_resetButton_clicked(self):
        """
        Private slot called to reset the settings of the current page.
        """
        if self.configStack.currentWidget() != self.emptyPage:
            currentPage = self.configStack.currentWidget()
            savedState = currentPage.saveState()
            pageName = self.configList.currentItem().getPageName()
            self.configStack.removeWidget(currentPage)
            if pageName == "editorHighlightingStylesPage":
                self.__initLexers()
            self.configItems[pageName][-1] = None
            
            self.showConfigurationPageByName(pageName)
            if savedState is not None:
                self.configStack.currentWidget().setState(savedState)
        
    def getExpandedEntries(self):
        """
        Public method to get a list of expanded entries.
        
        @return list of expanded entries (list of string)
        """
        return self.__expandedEntries
    
    @pyqtSlot(QTreeWidgetItem)
    def on_configList_itemCollapsed(self, item):
        """
        Private slot handling a list entry being collapsed.
        
        @param item reference to the collapsed item (QTreeWidgetItem)
        """
        pageName = item.data(0, Qt.UserRole)
        if pageName in self.__expandedEntries:
            self.__expandedEntries.remove(pageName)
    
    @pyqtSlot(QTreeWidgetItem)
    def on_configList_itemExpanded(self, item):
        """
        Private slot handling a list entry being expanded.
        
        @param item reference to the expanded item (QTreeWidgetItem)
        """
        pageName = item.data(0, Qt.UserRole)
        if pageName not in self.__expandedEntries:
            self.__expandedEntries.append(pageName)