eric6/Plugins/WizardPlugins/SetupWizard/SetupWizardDialog.py

changeset 6942
2602857055c5
parent 6940
12ed1a714527
child 7005
342819f05839
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/WizardPlugins/SetupWizard/SetupWizardDialog.py	Sun Apr 14 15:09:21 2019 +0200
@@ -0,0 +1,749 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2013 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the setup.py wizard dialog.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode    # __IGNORE_WARNING__
+except NameError:
+    pass
+
+import os
+import sys
+import datetime
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QTreeWidgetItem, \
+    QListWidgetItem, QApplication
+
+from E5Gui.E5Application import e5App
+from E5Gui import E5MessageBox, E5FileDialog
+from E5Gui.E5Completers import E5DirCompleter
+
+from .Ui_SetupWizardDialog import Ui_SetupWizardDialog
+
+import UI.PixmapCache
+import Utilities
+import Preferences
+
+
+class SetupWizardDialog(QDialog, Ui_SetupWizardDialog):
+    """
+    Class implementing the setup.py wizard dialog.
+    
+    It displays a dialog for entering the parameters
+    for the E5MessageBox code generator.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SetupWizardDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.dataTabWidget.setCurrentIndex(0)
+        
+        self.__packageDirCompleter = E5DirCompleter(self.packageEdit)
+        self.__packageRootDirCompleter = E5DirCompleter(self.packageRootEdit)
+        self.__sourceDirCompleter = E5DirCompleter(self.sourceDirectoryEdit)
+        
+        self.packageRootDirButton.setIcon(UI.PixmapCache.getIcon("open.png"))
+        self.packageDirButton.setIcon(UI.PixmapCache.getIcon("open.png"))
+        self.sourceDirectoryButton.setIcon(UI.PixmapCache.getIcon("open.png"))
+        
+        self.variantComboBox.addItem(self.tr("distutils"), "distutils.core")
+        self.variantComboBox.addItem(self.tr("setuptools"), "setuptools")
+        self.variantComboBox.setCurrentIndex(1)
+        
+        self.__mandatoryStyleSheet = "QLineEdit {border: 2px solid;}"
+        for lineEdit in [self.nameEdit, self.versionEdit,
+                         self.homePageUrlEdit, self.authorEdit,
+                         self.authorEmailEdit, self.maintainerEdit,
+                         self.maintainerEmailEdit]:
+            lineEdit.setStyleSheet(self.__mandatoryStyleSheet)
+        
+        self.developmentStatusComboBox.addItem("", "")
+        
+        self.__populateFromTroveLists()
+        
+        self.licenseClassifierComboBox.setCurrentIndex(
+            self.licenseClassifierComboBox.findText(
+                "(GPLv3)", Qt.MatchContains | Qt.MatchCaseSensitive))
+        
+        self.__okButton = self.buttonBox.button(QDialogButtonBox.Ok)
+        self.__okButton.setEnabled(False)
+        
+        projectOpen = e5App().getObject("Project").isOpen()
+        self.projectButton.setEnabled(projectOpen)
+        self.autodiscoverPackagesButton.setEnabled(projectOpen)
+        
+        self.homePageUrlEdit.textChanged.connect(self.__enableOkButton)
+        self.nameEdit.textChanged.connect(self.__enableOkButton)
+        self.versionEdit.textChanged.connect(self.__enableOkButton)
+        self.authorEdit.textChanged.connect(self.__enableOkButton)
+        self.authorEmailEdit.textChanged.connect(self.__enableOkButton)
+        self.maintainerEdit.textChanged.connect(self.__enableOkButton)
+        self.maintainerEmailEdit.textChanged.connect(self.__enableOkButton)
+    
+    def __enableOkButton(self):
+        """
+        Private slot to set the state of the OK button.
+        """
+        enable = (
+            bool(self.nameEdit.text()) and
+            bool(self.versionEdit.text()) and
+            bool(self.homePageUrlEdit.text()) and
+            ((bool(self.authorEdit.text()) and
+                bool(self.authorEmailEdit.text())) or
+             (bool(self.maintainerEdit.text()) and
+              bool(self.maintainerEmailEdit.text()))) and
+            self.homePageUrlEdit.text().startswith(("http://", "https://"))
+        )
+        
+        self.__okButton.setEnabled(enable)
+    
+    def __populateFromTroveLists(self):
+        """
+        Private method to populate lists from the Trove list file.
+        """
+        filename = os.path.join(os.path.dirname(__file__),
+                                "data", "trove_classifiers.txt")
+        try:
+            f = open(filename, "r")
+            lines = f.readlines()
+            f.close()
+        except (IOError, OSError) as err:
+            E5MessageBox.warning(
+                self,
+                self.tr("Reading Trove Classifiers"),
+                self.tr("""<p>The Trove Classifiers file <b>{0}</b>"""
+                        """ could not be read.</p><p>Reason: {1}</p>""")
+                .format(filename, str(err)))
+            return
+        
+        self.__classifiersDict = {}
+        for line in lines:
+            line = line.strip()
+            if line.startswith("License "):
+                self.licenseClassifierComboBox.addItem(
+                    "/".join(line.split(" :: ")[1:]),
+                    line
+                )
+            elif line.startswith("Development Status "):
+                self.developmentStatusComboBox.addItem(
+                    line.split(" :: ")[1], line)
+            else:
+                self.__addClassifierEntry(line)
+        self.__classifiersDict = {}
+    
+    def __addClassifierEntry(self, line):
+        """
+        Private method to add a new entry to the list of trove classifiers.
+        
+        @param line line containing the data for the entry (string)
+        """
+        itm = None
+        pitm = None
+        dataList = line.split(" :: ")
+        for index in range(len(dataList)):
+            key = " :: ".join(dataList[:index + 1])
+            if key not in self.__classifiersDict:
+                if pitm is None:
+                    itm = QTreeWidgetItem(
+                        self.classifiersList, [dataList[index]])
+                    pitm = itm
+                else:
+                    itm = QTreeWidgetItem(pitm, [dataList[index]])
+                itm.setExpanded(True)
+                self.__classifiersDict[key] = itm
+            else:
+                pitm = self.__classifiersDict[key]
+        itm.setCheckState(0, Qt.Unchecked)
+        itm.setData(0, Qt.UserRole, line)
+    
+    def __getLicenseText(self):
+        """
+        Private method to get the license text.
+        
+        @return license text (string)
+        """
+        if not self.licenseClassifierCheckBox.isChecked():
+            return self.licenseEdit.text()
+        else:
+            lic = self.licenseClassifierComboBox.currentText()
+            if "(" in lic:
+                lic = lic.rsplit("(", 1)[1].split(")", 1)[0]
+            return lic
+    
+    def getCode(self, indLevel, indString):
+        """
+        Public method to get the source code.
+        
+        @param indLevel indentation level (int)
+        @param indString string used for indentation (space or tab) (string)
+        @return generated code (string)
+        """
+        # Note: all paths are created with '/'; setup will do the right thing
+        
+        # calculate our indentation level and the indentation string
+        il = indLevel + 1
+        istring = il * indString
+        i1string = (il + 1) * indString
+        i2string = (il + 2) * indString
+        estring = os.linesep + indLevel * indString
+        
+        # now generate the code
+        if self.introCheckBox.isChecked():
+            code = "#!/usr/bin/env python{0}{1}".format(
+                sys.version_info[0], os.linesep)
+            code += "# -*- coding: utf-8 -*-{0}{0}".format(os.linesep)
+        else:
+            code = ""
+        
+        if self.metaDataCheckBox.isChecked():
+            code += '# metadata{0}'.format(os.linesep)
+            code += '"{0}"{1}'.format(
+                self.summaryEdit.text() or "Setup routine",
+                os.linesep
+            )
+            code += '__version__ = "{0}"{1}'.format(
+                self.versionEdit.text(), os.linesep)
+            code += '__license__ = "{0}"{1}'.format(
+                self.__getLicenseText(), os.linesep)
+            code += '__author__ = "{0}"{1}'.format(
+                self.authorEdit.text() or self.maintainerEdit.text(),
+                os.linesep)
+            code += '__email__ = "{0}"{1}'.format(
+                self.authorEmailEdit.text() or self.maintainerEmailEdit.text(),
+                os.linesep)
+            code += '__url__ = "{0}"{1}'.format(
+                self.homePageUrlEdit.text(), os.linesep)
+            code += '__date__ = "{0}"{1}'.format(
+                datetime.datetime.now().isoformat().split('.')[0], os.linesep)
+            code += '__prj__ = "{0}"{1}'.format(
+                self.nameEdit.text(), os.linesep)
+            code += os.linesep
+        
+        if self.descriptionFromFilesCheckBox.isChecked():
+            code += "from __future__ import with_statement{0}".format(
+                os.linesep)
+        
+        if self.importCheckBox.isChecked():
+            variant = self.variantComboBox.itemData(
+                self.variantComboBox.currentIndex())
+            if variant == "setuptools":
+                additionalImport = ", find_packages"
+            else:
+                additionalImport = ""
+            code += "from {0} import setup{1}{2}".format(
+                variant, additionalImport, os.linesep)
+        if code:
+            code += "{0}{0}".format(os.linesep)
+        
+        if self.descriptionFromFilesCheckBox.isChecked():
+            code += 'def get_long_description():{0}'.format(os.linesep)
+            code += '{0}descr = []{1}'.format(istring, os.linesep)
+            code += '{0}for fname in "{1}":{2}'.format(
+                istring,
+                '", "'.join(self.descriptionEdit.toPlainText().splitlines()),
+                os.linesep)
+            code += '{0}{0}with open(fname) as f:{1}'.format(
+                istring, os.linesep)
+            code += '{0}{0}{0}descr.append(f.read()){1}'.format(
+                istring, os.linesep)
+            code += '{0}return "\\n\\n".join(descr){1}'.format(
+                istring, os.linesep)
+            code += "{0}{0}".format(os.linesep)
+        
+        code += 'setup({0}'.format(os.linesep)
+        code += '{0}name="{1}",{2}'.format(
+            istring, self.nameEdit.text(), os.linesep)
+        code += '{0}version="{1}",{2}'.format(
+            istring, self.versionEdit.text(), os.linesep)
+        
+        if self.summaryEdit.text():
+            code += '{0}description="{1}",{2}'.format(
+                istring, self.summaryEdit.text(), os.linesep)
+        
+        if self.descriptionFromFilesCheckBox.isChecked():
+            code += '{0}long_description=get_long_description(),{1}'.format(
+                istring, os.linesep)
+        elif self.descriptionEdit.toPlainText():
+            code += '{0}long_description="""{1}""",{2}'.format(
+                istring, self.descriptionEdit.toPlainText(), os.linesep)
+        
+        if self.authorEdit.text():
+            code += '{0}author="{1}",{2}'.format(
+                istring, self.authorEdit.text(), os.linesep)
+            code += '{0}author_email="{1}",{2}'.format(
+                istring, self.authorEmailEdit.text(), os.linesep)
+        
+        if self.maintainerEdit.text():
+            code += '{0}maintainer="{1}",{2}'.format(
+                istring, self.maintainerEdit.text(), os.linesep)
+            code += '{0}maintainer_email="{1}",{2}'.format(
+                istring, self.maintainerEmailEdit.text(), os.linesep)
+        
+        code += '{0}url="{1}",{2}'.format(
+            istring, self.homePageUrlEdit.text(), os.linesep)
+        if self.downloadUrlEdit.text():
+            code += '{0}download_url="{1}",{2}'.format(
+                istring, self.downloadUrlEdit.text(), os.linesep)
+        
+        classifiers = []
+        if not self.licenseClassifierCheckBox.isChecked():
+            code += '{0}license="{1}",{2}'.format(
+                istring, self.licenseEdit.text(), os.linesep)
+        else:
+            classifiers.append(
+                self.licenseClassifierComboBox.itemData(
+                    self.licenseClassifierComboBox.currentIndex()))
+        
+        platforms = self.platformsEdit.toPlainText().splitlines()
+        if platforms:
+            code += '{0}platforms=[{1}'.format(istring, os.linesep)
+            code += '{0}"{1}"{2}'.format(
+                i1string,
+                '",{0}{1}"'.format(os.linesep, i1string).join(platforms),
+                os.linesep)
+            code += '{0}],{1}'.format(istring, os.linesep)
+        
+        if self.developmentStatusComboBox.currentIndex() != 0:
+            classifiers.append(
+                self.developmentStatusComboBox.itemData(
+                    self.developmentStatusComboBox.currentIndex()))
+        
+        itm = self.classifiersList.topLevelItem(0)
+        while itm:
+            itm.setExpanded(True)
+            if itm.checkState(0) == Qt.Checked:
+                classifiers.append(itm.data(0, Qt.UserRole))
+            itm = self.classifiersList.itemBelow(itm)
+        
+        # cleanup classifiers list - remove all invalid entries
+        classifiers = [c for c in classifiers if bool(c)]
+        if classifiers:
+            code += '{0}classifiers=[{1}'.format(istring, os.linesep)
+            code += '{0}"{1}"{2}'.format(
+                i1string,
+                '",{0}{1}"'.format(os.linesep, i1string).join(classifiers),
+                os.linesep)
+            code += '{0}],{1}'.format(istring, os.linesep)
+        del classifiers
+        
+        if self.keywordsEdit.text():
+            code += '{0}keywords="{1}",{2}'.format(
+                istring, self.keywordsEdit.text(), os.linesep)
+        
+        if self.variantComboBox.currentIndex() == 0:
+            # distutils
+            packages = []
+            for row in range(self.packagesList.count()):
+                packages.append(self.packagesList.item(row).text())
+            if packages:
+                code += '{0}packages=[{1}'.format(istring, os.linesep)
+                code += '{0}"{1}"{2}'.format(
+                    i1string,
+                    '",{0}{1}"'.format(os.linesep, i1string).join(packages),
+                    os.linesep)
+                code += '{0}],{1}'.format(istring, os.linesep)
+            del packages
+        elif self.variantComboBox.currentIndex() == 1:
+            # setuptools
+            code += '{0}packages=find_packages('.format(istring)
+            src = Utilities.fromNativeSeparators(
+                self.sourceDirectoryEdit.text())
+            excludePatterns = []
+            for row in range(self.excludePatternList.count()):
+                excludePatterns.append(
+                    self.excludePatternList.item(row).text())
+            if src:
+                code += '{0}{1}"{2}"'.format(os.linesep, i1string, src)
+                if excludePatterns:
+                    code += ','
+                else:
+                    code += '{0}{1}'.format(os.linesep, istring)
+            if excludePatterns:
+                code += '{0}{1}exclude=[{0}'.format(os.linesep, i1string)
+                code += '{0}"{1}"{2}'.format(
+                    i2string,
+                    '",{0}{1}"'.format(os.linesep, i2string)
+                    .join(excludePatterns),
+                    os.linesep)
+                code += '{0}]{1}{2}'.format(i1string, os.linesep, istring)
+            code += '),{0}'.format(os.linesep)
+            
+            if self.includePackageDataCheckBox.isChecked():
+                code += '{0}include_package_data = True,{1}'.format(
+                    istring, os.linesep)
+        
+        modules = []
+        for row in range(self.modulesList.count()):
+            modules.append(self.modulesList.item(row).text())
+        if modules:
+            code += '{0}py_modules=[{1}'.format(istring, os.linesep)
+            code += '{0}"{1}"{2}'.format(
+                i1string,
+                '",{0}{1}"'.format(os.linesep, i1string).join(modules),
+                os.linesep)
+            code += '{0}],{1}'.format(istring, os.linesep)
+        del modules
+        
+        scripts = []
+        for row in range(self.scriptsList.count()):
+            scripts.append(self.scriptsList.item(row).text())
+        if scripts:
+            code += '{0}scripts=[{1}'.format(istring, os.linesep)
+            code += '{0}"{1}"{2}'.format(
+                i1string,
+                '",{0}{1}"'.format(os.linesep, i1string).join(scripts),
+                os.linesep)
+            code += '{0}],{1}'.format(istring, os.linesep)
+        del scripts
+        
+        code += "){0}".format(estring)
+        return code
+    
+    @pyqtSlot()
+    def on_projectButton_clicked(self):
+        """
+        Private slot to populate some fields with data retrieved from the
+        current project.
+        """
+        project = e5App().getObject("Project")
+        
+        self.nameEdit.setText(project.getProjectName())
+        try:
+            self.versionEdit.setText(project.getProjectVersion())
+            self.authorEdit.setText(project.getProjectAuthor())
+            self.authorEmailEdit.setText(project.getProjectAuthorEmail())
+            description = project.getProjectDescription()
+        except AttributeError:
+            self.versionEdit.setText(project.pdata["VERSION"][0])
+            self.authorEdit.setText(project.pdata["AUTHOR"][0])
+            self.authorEmailEdit.setText(project.pdata["EMAIL"][0])
+            description = project.pdata["DESCRIPTION"][0]
+        
+        summary = description.split(".", 1)[0]\
+            .replace("\r", "").replace("\n", "") + "."
+        self.summaryEdit.setText(summary)
+        self.descriptionEdit.setPlainText(description)
+        
+        self.packageRootEdit.setText(project.getProjectPath())
+        
+        # prevent overwriting of entries by disabling the button
+        self.projectButton.setEnabled(False)
+    
+    @pyqtSlot()
+    def on_packagesList_itemSelectionChanged(self):
+        """
+        Private slot to handle a change of selected items of the
+        packages list.
+        """
+        self.deletePackageButton.setEnabled(
+            len(self.packagesList.selectedItems()) > 0)
+    
+    @pyqtSlot()
+    def on_deletePackageButton_clicked(self):
+        """
+        Private slot to delete the selected package items.
+        """
+        for itm in self.packagesList.selectedItems():
+            self.packagesList.takeItem(
+                self.packagesList.row(itm))
+            del itm
+    
+    @pyqtSlot()
+    def on_addPackageButton_clicked(self):
+        """
+        Private slot to add a package to the list.
+        """
+        pkg = Utilities.toNativeSeparators(self.packageEdit.text())
+        self.__addPackage(pkg)
+    
+    @pyqtSlot()
+    def on_packageEdit_returnPressed(self):
+        """
+        Private slot handling a press of the return button of the
+        package edit.
+        """
+        self.on_addPackageButton_clicked()
+    
+    @pyqtSlot(str)
+    def on_packageEdit_textChanged(self, txt):
+        """
+        Private slot to handle a change of the package text.
+        
+        @param txt text of the line edit (string)
+        """
+        self.addPackageButton.setEnabled(bool(txt))
+    
+    @pyqtSlot()
+    def on_packageDirButton_clicked(self):
+        """
+        Private slot to select a package directory via a directory
+        selection dialog.
+        """
+        startDir = self.packageEdit.text()
+        if not startDir:
+            startDir = self.packageRootEdit.text() or self.__getStartDir()
+        packageDir = E5FileDialog.getExistingDirectory(
+            self,
+            self.tr("Package Directory"),
+            Utilities.fromNativeSeparators(startDir))
+        if packageDir:
+            self.packageEdit.setText(
+                Utilities.toNativeSeparators(packageDir))
+    
+    @pyqtSlot()
+    def on_autodiscoverPackagesButton_clicked(self):
+        """
+        Private slot to discover packages automatically.
+        """
+        self.autodiscoverPackagesButton.setEnabled(False)
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+        startDir = self.packageRootEdit.text() or self.__getStartDir()
+        if startDir:
+            self.packagesList.clear()
+            for dirpath, _dirnames, filenames in os.walk(startDir):
+                if "__init__.py" in filenames:
+                    self.__addPackage(dirpath)
+        self.autodiscoverPackagesButton.setEnabled(True)
+        QApplication.restoreOverrideCursor()
+    
+    @pyqtSlot()
+    def on_packageRootDirButton_clicked(self):
+        """
+        Private slot to select the packages root directory via a
+        directory selection dialog.
+        """
+        startDir = self.packageRootEdit.text()
+        if not startDir:
+            startDir = self.__getStartDir()
+        packagesRootDir = E5FileDialog.getExistingDirectory(
+            self,
+            self.tr("Packages Root Directory"),
+            Utilities.fromNativeSeparators(startDir),
+            E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
+        if packagesRootDir:
+            self.packageRootEdit.setText(
+                Utilities.toNativeSeparators(packagesRootDir))
+    
+    @pyqtSlot(str)
+    def on_packageRootEdit_textChanged(self, txt):
+        """
+        Private slot handling the entering of a packages root.
+        
+        @param txt text of the line edit (string)
+        """
+        projectOpen = e5App().getObject("Project").isOpen()
+        validPackagesRoot = bool(txt) and os.path.exists(txt)
+        self.autodiscoverPackagesButton.setEnabled(
+            projectOpen or validPackagesRoot)
+    
+    def __addPackage(self, pkgDir):
+        """
+        Private method to add a package to the list.
+        
+        @param pkgDir name of the package directory (string)
+        """
+        if pkgDir:
+            if "\\" in pkgDir or "/" in pkgDir:
+                # It is a directory. Check for an __init__.py file.
+                if os.path.isabs(pkgDir):
+                    prefix = ""
+                else:
+                    prefix = self.packageRootEdit.text()
+                initName = os.path.join(
+                    prefix,
+                    Utilities.toNativeSeparators(pkgDir),
+                    "__init__.py")
+                if not os.path.exists(initName):
+                    res = E5MessageBox.information(
+                        self,
+                        self.tr("Add Package"),
+                        self.tr("""<p>The directory <b>{0}</b> is not"""
+                                """ a Python package.</p>""")
+                        .format(pkgDir),
+                        E5MessageBox.StandardButtons(
+                            E5MessageBox.Ignore |
+                            E5MessageBox.Ok))
+                    if res == E5MessageBox.Ok:
+                        return
+            
+            pkg = pkgDir.replace(
+                Utilities.toNativeSeparators(self.packageRootEdit.text()), "")
+            if pkg.startswith(("\\", "/")):
+                pkg = pkg[1:]
+            if pkg:
+                QListWidgetItem(
+                    pkg.replace("\\", ".").replace("/", "."),
+                    self.packagesList)
+                self.packageEdit.clear()
+    
+    def __getStartDir(self):
+        """
+        Private method to get the start directory for selection dialogs.
+        
+        @return start directory (string)
+        """
+        return (Preferences.getMultiProject("Workspace") or
+                Utilities.getHomeDir())
+    
+    @pyqtSlot()
+    def on_scriptsList_itemSelectionChanged(self):
+        """
+        Private slot to handle a change of selected items of the
+        scripts list.
+        """
+        self.deleteScriptButton.setEnabled(
+            len(self.scriptsList.selectedItems()) > 0)
+    
+    @pyqtSlot()
+    def on_deleteScriptButton_clicked(self):
+        """
+        Private slot to delete the selected script items.
+        """
+        for itm in self.scriptsList.selectedItems():
+            self.scriptsList.takeItem(
+                self.scriptsList.row(itm))
+            del itm
+    
+    @pyqtSlot()
+    def on_addScriptButton_clicked(self):
+        """
+        Private slot to add scripts to the list.
+        """
+        startDir = self.packageRootEdit.text() or self.__getStartDir()
+        scriptsList = E5FileDialog.getOpenFileNames(
+            self,
+            self.tr("Add Scripts"),
+            startDir,
+            self.tr("Python Files (*.py);;All Files(*)"))
+        for script in scriptsList:
+            script = script.replace(
+                Utilities.toNativeSeparators(startDir), "")
+            if script.startswith(("\\", "/")):
+                script = script[1:]
+            if script:
+                QListWidgetItem(Utilities.fromNativeSeparators(script),
+                                self.scriptsList)
+    
+    @pyqtSlot()
+    def on_modulesList_itemSelectionChanged(self):
+        """
+        Private slot to handle a change of selected items of the
+        modules list.
+        """
+        self.deleteModuleButton.setEnabled(
+            len(self.modulesList.selectedItems()) > 0)
+    
+    @pyqtSlot()
+    def on_deleteModuleButton_clicked(self):
+        """
+        Private slot to delete the selected script items.
+        """
+        for itm in self.modulesList.selectedItems():
+            self.modulesList.takeItem(
+                self.modulesList.row(itm))
+            del itm
+    
+    @pyqtSlot()
+    def on_addModuleButton_clicked(self):
+        """
+        Private slot to add Python modules to the list.
+        """
+        startDir = self.packageRootEdit.text() or self.__getStartDir()
+        modulesList = E5FileDialog.getOpenFileNames(
+            self,
+            self.tr("Add Python Modules"),
+            startDir,
+            self.tr("Python Files (*.py)"))
+        for module in modulesList:
+            module = module.replace(
+                Utilities.toNativeSeparators(startDir), "")
+            if module.startswith(("\\", "/")):
+                module = module[1:]
+            if module:
+                QListWidgetItem(os.path.splitext(module)[0]
+                                .replace("\\", ".").replace("/", "."),
+                                self.modulesList)
+    
+    @pyqtSlot(int)
+    def on_variantComboBox_currentIndexChanged(self, index):
+        """
+        Private slot handling a change of the setup variant.
+        
+        @param index index of the selected entry (integer)
+        """
+        self.packagesStackedWidget.setCurrentIndex(index)
+    
+    @pyqtSlot()
+    def on_excludePatternList_itemSelectionChanged(self):
+        """
+        Private slot to handle a change of selected items of the
+        exclude pattern list.
+        """
+        self.deleteExcludePatternButton.setEnabled(
+            len(self.excludePatternList.selectedItems()) > 0)
+    
+    @pyqtSlot()
+    def on_deleteExcludePatternButton_clicked(self):
+        """
+        Private slot to delete the selected exclude pattern items.
+        """
+        for itm in self.excludePatternList.selectedItems():
+            self.excludePatternList.takeItem(
+                self.excludePatternList.row(itm))
+            del itm
+    
+    @pyqtSlot()
+    def on_addExludePatternButton_clicked(self):
+        """
+        Private slot to add an exclude pattern to the list.
+        """
+        pattern = self.excludePatternEdit.text()\
+            .replace("\\", ".").replace("/", ".")
+        if not self.excludePatternList.findItems(
+                pattern, Qt.MatchExactly | Qt.MatchCaseSensitive):
+            QListWidgetItem(pattern, self.excludePatternList)
+    
+    @pyqtSlot(str)
+    def on_excludePatternEdit_textChanged(self, txt):
+        """
+        Private slot to handle a change of the exclude pattern text.
+        
+        @param txt text of the line edit (string)
+        """
+        self.addExludePatternButton.setEnabled(bool(txt))
+    
+    @pyqtSlot()
+    def on_excludePatternEdit_returnPressed(self):
+        """
+        Private slot handling a press of the return button of the
+        exclude pattern edit.
+        """
+        self.on_addExludePatternButton_clicked()
+    
+    @pyqtSlot()
+    def on_sourceDirectoryButton_clicked(self):
+        """
+        Private slot to select the packages root directory via a
+        directory selection dialog.
+        """
+        startDir = self.sourceDirectoryEdit.text() or self.__getStartDir()
+        sourceDirectory = E5FileDialog.getExistingDirectory(
+            self,
+            self.tr("Source Directory"),
+            Utilities.fromNativeSeparators(startDir),
+            E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
+        if sourceDirectory:
+            self.sourceDirectoryEdit.setText(
+                Utilities.toNativeSeparators(sourceDirectory))

eric ide

mercurial