Wed, 30 Dec 2020 11:02:06 +0100
Updated copyright for 2021.
# -*- coding: utf-8 -*- # Copyright (c) 2018 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing PyInstallerConfigDialog. """ import copy from PyQt5.QtCore import pyqtSlot from PyQt5.QtWidgets import QDialog, QDialogButtonBox from E5Gui.E5PathPicker import E5PathPickerModes 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(PyInstallerConfigDialog, self).__init__(parent) self.setupUi(self) self.__project = project self.__mode = mode self.inputFilePicker.setMode(E5PathPickerModes.OpenFileMode) 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(E5PathPickerModes.OpenFileMode) 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 if self.__parameters["mainscript"]: script = self.__project.getMainScript() else: script = 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(PyInstallerConfigDialog, self).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.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()