PyInstallerInterface/PyInstallerConfigDialog.py

branch
eric7
changeset 38
fc9ef9dcd51a
parent 35
d9b3cadaf707
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PyInstallerInterface/PyInstallerConfigDialog.py	Thu May 27 20:28:55 2021 +0200
@@ -0,0 +1,378 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2018 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing PyInstallerConfigDialog.
+"""
+
+import copy
+
+from PyQt6.QtCore import pyqtSlot
+from PyQt6.QtWidgets import QDialog, QDialogButtonBox
+
+from EricWidgets.EricPathPicker import EricPathPickerModes
+
+from .Ui_PyInstallerConfigDialog import Ui_PyInstallerConfigDialog
+
+import Globals
+
+
+class PyInstallerConfigDialog(QDialog, Ui_PyInstallerConfigDialog):
+    """
+    Class implementing a dialog to enter the parameters for pyinstaller
+    and pyi-makespec.
+    """
+    def __init__(self, project, executables, params=None, mode="installer",
+                 parent=None):
+        """
+        Constructor
+        
+        @param project reference to the project object
+        @type Project.Project
+        @param executables names of the pyinstaller executables
+        @type list of str
+        @param params parameters to set in the dialog
+        @type dict
+        @param mode mode of the dialog
+        @type str (one of 'installer' or 'spec')
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+        
+        self.__project = project
+        self.__mode = mode
+        
+        self.inputFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
+        self.inputFilePicker.setDefaultDirectory(
+            self.__project.getProjectPath())
+        if self.__mode == "installer":
+            self.inputFilePicker.setFilters(self.tr(
+                "Python Files (*.py *.py3);;"
+                "Python GUI Files (*.pyw *.pyw3);;"
+                "Spec Files (*.spec);;"
+                "All Files (*)"
+            ))
+        elif self.__mode == "spec":
+            self.inputFilePicker.setFilters(self.tr(
+                "Python Files (*.py *.py3);;"
+                "Python GUI Files (*.pyw *.pyw3);;"
+                "All Files (*)"
+            ))
+        
+        self.executableCombo.addItems(executables)
+        
+        if not bool(project.getMainScript()):
+            # no main script defined
+            self.selectedScriptButton.setChecked(True)
+            self.mainScriptButton.setEnabled(False)
+        
+        self.iconFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
+        self.iconFilePicker.setDefaultDirectory(
+            self.__project.getProjectPath())
+        if Globals.isMacPlatform():
+            self.iconFilePicker.setFilters(self.tr(
+                "Icon Files (*.icns);;"
+                "All Files (*)"
+            ))
+        elif Globals.isWindowsPlatform():
+            self.iconFilePicker.setFilters(self.tr(
+                "Icon Files (*.ico);;"
+                "Executable Files (*.exe);;"
+                "All Files (*)"
+            ))
+        
+        # disable platform specific tabs
+        self.tabWidget.setTabEnabled(
+            self.tabWidget.indexOf(self.windowsMacTab),
+            Globals.isMacPlatform() or Globals.isWindowsPlatform())
+        self.tabWidget.setTabEnabled(
+            self.tabWidget.indexOf(self.macTab),
+            Globals.isMacPlatform())
+        
+        self.__initializeDefaults()
+        
+        # get a copy of the defaults to store the user settings
+        self.__parameters = copy.deepcopy(self.__defaults)
+        
+        # combine it with the values of params
+        if params is not None:
+            self.__parameters.update(params)
+        
+        # initialize general tab
+        if mode == "installer" and bool(self.__parameters["pyinstaller"]):
+            self.executableCombo.setCurrentIndex(
+                self.executableCombo.findText(
+                    self.__parameters["pyinstaller"]))
+        elif mode == "spec" and bool(self.__parameters["pyi-makespec"]):
+            self.executableCombo.setCurrentIndex(
+                self.executableCombo.findText(
+                    self.__parameters["pyi-makespec"]))
+        if self.__parameters["mainscript"]:
+            self.mainScriptButton.setChecked(True)
+        else:
+            self.selectedScriptButton.setChecked(True)
+        self.inputFilePicker.setText(self.__parameters["inputFile"])
+        if self.__parameters["oneDirectory"]:
+            self.oneDirButton.setChecked(True)
+        else:
+            self.oneFileButton.setChecked(True)
+        self.nameEdit.setText(self.__parameters["name"])
+        self.keyEdit.setText(self.__parameters["encryptionKey"])
+        self.cleanCheckBox.setChecked(self.__parameters["cleanBeforeBuilding"])
+        
+        # initialize Windows and macOS tab
+        if self.__parameters["consoleApplication"]:
+            self.consoleButton.setChecked(True)
+        else:
+            self.windowedButton.setChecked(True)
+        self.iconFilePicker.setText(self.__parameters["iconFile"])
+        self.iconIdEdit.setText(self.__parameters["iconId"])
+        
+        # initialize maxOS specific tab
+        self.bundleIdentifierEdit.setText(
+            self.__parameters["bundleIdentifier"])
+        
+        self.__updateOkButton()
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def __initializeDefaults(self):
+        """
+        Private method to set the default values.
+        
+        These are needed later on to generate the command line parameters.
+        """
+        self.__defaults = {
+            # general options
+            "pyinstaller": "",
+            "pyi-makespec": "",
+            "mainscript": bool(self.__project.getMainScript()),
+            "inputFile": "",
+            "oneDirectory": True,
+            "name": "",
+            "encryptionKey": "",
+            "cleanBeforeBuilding": False,
+            
+            # Windows and macOS options
+            "consoleApplication": True,
+            "iconFile": "",
+            "iconId": "",
+            
+            # macOS specific options
+            "bundleIdentifier": "",
+        }
+    
+    def generateParameters(self):
+        """
+        Public method that generates the command line parameters.
+        
+        It generates a list of strings to be used to set the QProcess arguments
+        for the pyinstaller/pyi-makespec call and a list containing the non
+        default parameters. The second list can be passed back upon object
+        generation to overwrite the default settings.
+        
+        @return a tuple of the command line parameters, non default parameters
+            and the script path
+        @rtype tuple of (list of str, dict, str)
+        """
+        parms = {}
+        args = []
+        
+        # 1. the program name
+        if self.__mode == "installer":
+            args.append(self.__parameters["pyinstaller"])
+            parms["pyinstaller"] = self.__parameters["pyinstaller"]
+        elif self.__mode == "spec":
+            args.append(self.__parameters["pyi-makespec"])
+            parms["pyi-makespec"] = self.__parameters["pyi-makespec"]
+        
+        # 2. the commandline options
+        # 2.1 general options, input
+        if not self.__parameters["mainscript"]:
+            parms["mainscript"] = False
+            parms["inputFile"] = self.__parameters["inputFile"]
+        
+        runWithSpec = self.__parameters["inputFile"].endswith(".spec")
+        if not runWithSpec:
+            # 2.2 general options, part 1
+            if not self.__parameters["oneDirectory"]:
+                parms["oneDirectory"] = self.__parameters["oneDirectory"]
+                args.append("--onefile")
+            if self.__parameters["name"] != self.__defaults["name"]:
+                parms["name"] = self.__parameters["name"]
+                args.append("--name")
+                args.append(self.__parameters["name"])
+            if (
+                self.__parameters["encryptionKey"] !=
+                self.__defaults["encryptionKey"]
+            ):
+                parms["encryptionKey"] = self.__parameters["encryptionKey"]
+                args.append("--key")
+                args.append(self.__parameters["encryptionKey"])
+        
+            # 2.3 Windows and macOS options
+            if (
+                self.__parameters["consoleApplication"] !=
+                self.__defaults["consoleApplication"]
+            ):
+                parms["consoleApplication"] = (
+                    self.__parameters["consoleApplication"]
+                )
+                args.append("--windowed")
+            if self.__parameters["iconFile"] != self.__defaults["iconFile"]:
+                parms["iconFile"] = self.__parameters["iconFile"]
+                parms["iconId"] = self.__parameters["iconId"]
+                args.append("--icon")
+                if self.__parameters["iconFile"].endswith(".exe"):
+                    if bool(self.__parameters["iconId"]):
+                        iconId = self.__parameters["iconId"]
+                    else:
+                        iconId = "0"
+                    args.append("{0},{1}".format(
+                        self.__parameters["iconFile"], iconId))
+                else:
+                    args.append(self.__parameters["iconFile"])
+        
+            # 2.4 macOS specific options
+            if (
+                self.__parameters["bundleIdentifier"] !=
+                self.__defaults["bundleIdentifier"]
+            ):
+                parms["bundleIdentifier"] = (
+                    self.__parameters["bundleIdentifier"]
+                )
+                args.append("--osx-bundle-identifier")
+                args.append(self.__parameters["bundleIdentifier"])
+        
+        # 2.5 general options, part 2
+        if (
+            self.__parameters["cleanBeforeBuilding"] !=
+            self.__defaults["cleanBeforeBuilding"]
+        ):
+            parms["cleanBeforeBuilding"] = (
+                self.__parameters["cleanBeforeBuilding"]
+            )
+            args.append("--clean")
+        
+        # 3. always add these arguments
+        if self.__mode == "installer":
+            args.append("--noconfirm")  # don't ask the user
+        
+        # determine the script to be processed
+        script = (
+            self.__project.getMainScript()
+            if self.__parameters["mainscript"] else
+            self.__parameters["inputFile"]
+        )
+        
+        return args, parms, script
+
+    def accept(self):
+        """
+        Public method called by the Ok button.
+        
+        It saves the values in the parameters dictionary.
+        """
+        # get data of general tab
+        if self.__mode == "installer":
+            self.__parameters["pyinstaller"] = (
+                self.executableCombo.currentText()
+            )
+        elif self.__mode == "spec":
+            self.__parameters["pyi-makespec"] = (
+                self.executableCombo.currentText()
+            )
+        self.__parameters["mainscript"] = self.mainScriptButton.isChecked()
+        self.__parameters["inputFile"] = self.inputFilePicker.text()
+        self.__parameters["oneDirectory"] = self.oneDirButton.isChecked()
+        self.__parameters["name"] = self.nameEdit.text()
+        self.__parameters["encryptionKey"] = self.keyEdit.text()
+        self.__parameters["cleanBeforeBuilding"] = (
+            self.cleanCheckBox.isChecked()
+        )
+        
+        # get data of Windows and macOS tab
+        self.__parameters["consoleApplication"] = (
+            self.consoleButton.isChecked()
+        )
+        self.__parameters["iconFile"] = self.iconFilePicker.text()
+        self.__parameters["iconId"] = self.iconIdEdit.text()
+        
+        # get data of macOS specific tab
+        self.__parameters["bundleIdentifier"] = (
+            self.bundleIdentifierEdit.text()
+        )
+
+        # call the accept slot of the base class
+        super().accept()
+    
+    def __updateOkButton(self):
+        """
+        Private method to update the enabled state of the OK button.
+        """
+        enable = True
+        
+        # If not to be run with the project main script, a script or
+        # spec file must be selected.
+        if (
+            self.selectedScriptButton.isChecked() and
+            not bool(self.inputFilePicker.text())
+        ):
+            enable = False
+        
+        # If the icon shall be picked from a .exe file, an icon ID
+        # must be entered (Windows only).
+        if (
+            self.iconFilePicker.text().endswith(".exe") and
+            not bool(self.iconIdEdit.text())
+        ):
+            enable = False
+        
+        self.buttonBox.button(
+            QDialogButtonBox.StandardButton.Ok).setEnabled(enable)
+    
+    @pyqtSlot(bool)
+    def on_selectedScriptButton_toggled(self, checked):
+        """
+        Private slot to handle changes of the radio button state.
+        
+        @param checked state of the radio button
+        @type bool
+        """
+        self.__updateOkButton()
+    
+    @pyqtSlot(str)
+    def on_inputFilePicker_textChanged(self, txt):
+        """
+        Private slot to handle changes of the input file.
+        
+        @param txt text of the file edit
+        @type str
+        """
+        self.__updateOkButton()
+    
+    @pyqtSlot(str)
+    def on_iconFilePicker_textChanged(self, txt):
+        """
+        Private slot to handle changes of the icon file.
+        
+        @param txt text of the file edit
+        @type str
+        """
+        self.iconIdEdit.setEnabled(txt.endswith(".exe"))
+        self.__updateOkButton()
+    
+    @pyqtSlot(str)
+    def on_iconIdEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the icon ID.
+        
+        @param txt iconID
+        @type str
+        """
+        self.__updateOkButton()

eric ide

mercurial