Tue, 25 Oct 2022 09:03:26 +0200
Adapted the import statements to the new structure.
# -*- coding: utf-8 -*- # Copyright (c) 2010 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a dialog to enter the parameters for cxfreeze. """ import sys import os import copy import contextlib from PyQt6.QtCore import pyqtSlot, QDir, QProcess from PyQt6.QtWidgets import QDialog, QListWidgetItem from eric7 import Utilities try: from eric7.EricGui import EricPixmapCache except ImportError: from UI import PixmapCache as EricPixmapCache from eric7.EricWidgets.EricDirFileDialog import EricDirFileDialog from eric7.EricWidgets.EricPathPicker import EricPathPickerModes from .Ui_CxfreezeConfigDialog import Ui_CxfreezeConfigDialog class CxfreezeConfigDialog(QDialog, Ui_CxfreezeConfigDialog): """ Class implementing a dialog to enter the parameters for cxfreeze. """ def __init__(self, project, exe, parms=None, parent=None): """ Constructor @param project reference to the project object @type Project @param exe name of the cxfreeze executable @type str @param parms parameters to set in the dialog @type dict @param parent parent widget of this dialog @type QWidget """ QDialog.__init__(self, parent) self.setupUi(self) self.selectFileOrFolderButton.setIcon(EricPixmapCache.getIcon("open.png")) self.targetDirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) self.targetDirPicker.setWindowTitle(self.tr("Select target directory")) iconsI18N = self.tr("Icons") allFilesI18N = self.tr("All files") if Utilities.isWindowsPlatform(): iconFilter = "{0} (*.ico);;{1} (*.*)".format(iconsI18N, allFilesI18N) elif Utilities.isMacPlatform(): iconFilter = "{0} (*.icns *.png);;{1} (*.*)".format(iconsI18N, allFilesI18N) else: iconFilter = "{0} (*.png);;{1} (*.*)".format(iconsI18N, allFilesI18N) self.applicationIconPicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) self.applicationIconPicker.setWindowTitle( self.tr("Select the application icon") ) self.applicationIconPicker.setFilters(iconFilter) self.extListFilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) self.extListFilePicker.setWindowTitle(self.tr("Select external list file")) self.__project = project 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 parms if parms is not None: self.__parameters.update(parms) self.cxfreezeExecCombo.addItems(exe) # try to set the saved script path with contextlib.suppress(ValueError): idx = exe.index(self.__parameters["script"]) self.cxfreezeExecCombo.setCurrentIndex(idx) # initialize general tab self.targetDirPicker.setText(self.__parameters["targetDirectory"]) self.targetNameEdit.setText(self.__parameters["targetName"]) self.basenameCombo.setEditText(self.__parameters["baseName"]) self.initscriptCombo.setEditText(self.__parameters["initScript"]) self.applicationIconPicker.setText(self.__parameters["applicationIcon"]) self.keeppathCheckBox.setChecked(self.__parameters["keepPath"]) self.compressCheckBox.setChecked(self.__parameters["compress"]) if self.__parameters["optimize"] == 0: self.nooptimizeRadioButton.setChecked(True) elif self.__parameters["optimize"] == 1: self.optimizeRadioButton.setChecked(True) else: self.optimizeDocRadioButton.setChecked(True) # initialize advanced tab self.defaultPathEdit.setText(os.pathsep.join(self.__parameters["defaultPath"])) self.includePathEdit.setText(os.pathsep.join(self.__parameters["includePath"])) self.replacePathsEdit.setText( os.pathsep.join(self.__parameters["replacePaths"]) ) self.includeModulesEdit.setText(",".join(self.__parameters["includeModules"])) self.excludeModulesEdit.setText(",".join(self.__parameters["excludeModules"])) self.extListFilePicker.setText(self.__parameters["extListFile"]) # initialize additional files tab self.fileOrFolderList.addItems(self.__parameters["additionalFiles"]) 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 "targetDirectory": "", "targetName": "", "baseName": "Console", "initScript": "Console", "applicationIcon": "", "script": "", "keepPath": False, "compress": False, "optimize": 0, # 0, 1 or 2 # advanced options "defaultPath": [], "includePath": [], "replacePaths": [], "includeModules": [], "excludeModules": [], "extListFile": "", # additional files tab "additionalFiles": [], } # overwrite 'baseName' if OS is Windows if sys.platform == "win32": self.defaults["baseName"] = "Win32GUI" # overwrite 'initScript' if version 3 interpreter if self.__project.getProjectLanguage() == "Python3": self.defaults["initScript"] = "Console3" 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 cxfreeze 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 and non default parameters @rtype tuple of (list of str, dict) """ parms = {} args = [] # 1. the program name args.append(self.cxfreezeExecCombo.currentText()) # 2. the commandline options # 2.1 general options if self.__parameters["targetDirectory"] != self.defaults["targetDirectory"]: parms["targetDirectory"] = self.__parameters["targetDirectory"] args.append("--target-dir={0}".format(self.__parameters["targetDirectory"])) if self.__parameters["targetName"] != self.defaults["targetName"]: parms["targetName"] = self.__parameters["targetName"][:] args.append("--target-name={0}".format(self.__parameters["targetName"])) parms["baseName"] = self.__parameters["baseName"][:] if self.__parameters["baseName"] != "": args.append("--base-name={0}".format(self.__parameters["baseName"])) parms["initScript"] = self.__parameters["initScript"][:] if self.__parameters["initScript"] != "": args.append("--init-script={0}".format(self.__parameters["initScript"])) parms["applicationIcon"] = self.__parameters["applicationIcon"][:] if self.__parameters["applicationIcon"] != self.defaults["applicationIcon"]: args.append("--icon={0}".format(self.__parameters["applicationIcon"])) parms["script"] = self.__parameters["script"][:] if self.__parameters["keepPath"] != self.defaults["keepPath"]: parms["keepPath"] = self.__parameters["keepPath"] args.append("--no-copy-deps") if self.__parameters["compress"] != self.defaults["compress"]: parms["compress"] = self.__parameters["compress"] args.append("--compress") if self.__parameters["optimize"] != self.defaults["optimize"]: parms["optimize"] = self.__parameters["optimize"] if self.__parameters["optimize"] == 1: args.append("-O") elif self.__parameters["optimize"] == 2: args.append("-OO") # 2.2 advanced options if self.__parameters["defaultPath"] != self.defaults["defaultPath"]: parms["defaultPath"] = self.__parameters["defaultPath"][:] args.append( "--default-path={0}".format( os.pathsep.join(self.__parameters["defaultPath"]) ) ) if self.__parameters["includePath"] != self.defaults["includePath"]: parms["includePath"] = self.__parameters["includePath"][:] args.append( "--include-path={0}".format( os.pathsep.join(self.__parameters["includePath"]) ) ) if self.__parameters["replacePaths"] != self.defaults["replacePaths"]: parms["replacePaths"] = self.__parameters["replacePaths"][:] args.append( "--replace-paths={0}".format( os.pathsep.join(self.__parameters["replacePaths"]) ) ) if self.__parameters["includeModules"] != self.defaults["includeModules"]: parms["includeModules"] = self.__parameters["includeModules"][:] args.append( "--include-modules={0}".format( ",".join(self.__parameters["includeModules"]) ) ) if self.__parameters["excludeModules"] != self.defaults["excludeModules"]: parms["excludeModules"] = self.__parameters["excludeModules"][:] args.append( "--exclude-modules={0}".format( ",".join(self.__parameters["excludeModules"]) ) ) if self.__parameters["extListFile"] != self.defaults["extListFile"]: parms["extListFile"] = self.__parameters["extListFile"] args.append("--ext-list-file={0}".format(self.__parameters["extListFile"])) # 2.3 additional files tab if self.__parameters["additionalFiles"] != []: parms["additionalFiles"] = self.__parameters["additionalFiles"][:] return (args, parms) @pyqtSlot(str) def on_cxfreezeExecCombo_currentIndexChanged(self, text): """ Private slot to handle the selection of a cxfreeze executable. @param text selected cxfreeze executable @type str """ # version specific setup if Utilities.isWindowsPlatform(): # remove "\Scripts\cx_Freeze.bat" from path dirname = os.path.dirname(text) dirname = os.path.dirname(dirname) # first try the fast way modpath = os.path.join(dirname, "Lib", "site-packages", "cx_Freeze") if not os.path.exists(modpath): # but if it failed search in the whole directory tree modpath = None for dirpath, dirnames, _ in os.walk(dirname): if "cx_Freeze" in dirnames: modpath = os.path.join(dirpath, "cx_Freeze") break else: with open(text, "r") as f: args = f.readline() if not args: return args = args.strip("!#\n").split(" ") program = args.pop(0) script = os.path.join( os.path.dirname(os.path.abspath(__file__)), "CxfreezeFindPath.py" ) if not os.path.exists(script): return args.append(script) process = QProcess() process.start(program, args) process.waitForFinished(5000) # get a QByteArray of the output cxPath = process.readAllStandardOutput() modpath = str(cxPath, encoding="utf-8").strip("\n\r") if not modpath.endswith("cx_Freeze"): return # populate combo boxes if modpath: d = QDir(os.path.join(modpath, "bases")) basesList = d.entryList(QDir.Filter.Files) if Utilities.isWindowsPlatform(): # strip the final '.exe' from the bases tmpBasesList = basesList[:] basesList = [] for b in tmpBasesList: base, ext = os.path.splitext(b) if ext == ".exe": basesList.append(base) else: basesList.append(b) basesList.insert(0, "") currentText = self.basenameCombo.currentText() self.basenameCombo.clear() self.basenameCombo.addItems(basesList) self.basenameCombo.setEditText(currentText) d = QDir(os.path.join(modpath, "initscripts")) initList = d.entryList(["*.py"]) initList.insert(0, "") currentText = self.initscriptCombo.currentText() self.initscriptCombo.clear() self.initscriptCombo.addItems([os.path.splitext(i)[0] for i in initList]) self.initscriptCombo.setEditText(currentText) @pyqtSlot(int) def on_fileOrFolderList_currentRowChanged(self, row): """ Private slot to handle the currentRowChanged signal of the fileOrFolderList. @param row the current row @type int """ self.deleteSelectedButton.setEnabled(row != -1) if row != -1: self.fileOrFolderList.setCurrentRow(row) @pyqtSlot(QListWidgetItem) def on_fileOrFolderList_itemDoubleClicked(self, itm): """ Private slot to handle the itemDoubleClicked signal of the fileOrFolderList. @param itm the selected row @type QListWidgetItem """ self.fileOrFolderEdit.setText(itm.text()) row = self.fileOrFolderList.currentRow() itm = self.fileOrFolderList.takeItem(row) del itm @pyqtSlot() def on_addFileOrFolderButton_clicked(self): """ Private slot to add the entered file or directory to the list view. """ txt = self.fileOrFolderEdit.text() if txt: self.fileOrFolderList.addItem(txt) self.fileOrFolderEdit.clear() row = self.fileOrFolderList.currentRow() self.on_fileOrFolderList_currentRowChanged(row) @pyqtSlot(str) def on_fileOrFolderEdit_textChanged(self, txt): """ Private slot to handle the textChanged signal of the directory edit. @param txt the text of the directory edit @type str """ self.addFileOrFolderButton.setEnabled(txt != "") @pyqtSlot() def on_deleteSelectedButton_clicked(self): """ Private slot to delete the selected entry from the list view. """ row = self.fileOrFolderList.currentRow() itm = self.fileOrFolderList.takeItem(row) del itm row = self.fileOrFolderList.currentRow() self.on_fileOrFolderList_currentRowChanged(row) @pyqtSlot() def on_selectFileOrFolderButton_clicked(self): """ Private slot to select files or folders. It displays a file and directory selection dialog to select the files and directories which should be copied into the distribution folder. """ items = EricDirFileDialog.getOpenFileAndDirNames( self, self.tr("Select files and folders"), self.__project.getProjectPath() ) for itm in items: itm = self.__project.getRelativePath(itm) self.fileOrFolderList.addItem(Utilities.toNativeSeparators(itm)) row = self.fileOrFolderList.currentRow() self.on_fileOrFolderList_currentRowChanged(row) def accept(self): """ Public method called by the Ok button. It saves the values in the parameters dictionary. """ # get data of general tab self.__parameters["targetDirectory"] = self.__project.getRelativePath( self.targetDirPicker.text() ) self.__parameters["targetName"] = self.targetNameEdit.text() self.__parameters["baseName"] = self.basenameCombo.currentText() self.__parameters["initScript"] = self.initscriptCombo.currentText() self.__parameters["applicationIcon"] = self.__project.getRelativePath( self.applicationIconPicker.text() ) self.__parameters["script"] = self.cxfreezeExecCombo.currentText() self.__parameters["keepPath"] = self.keeppathCheckBox.isChecked() self.__parameters["compress"] = self.compressCheckBox.isChecked() if self.nooptimizeRadioButton.isChecked(): self.__parameters["optimize"] = 0 elif self.optimizeRadioButton.isChecked(): self.__parameters["optimize"] = 1 else: self.__parameters["optimize"] = 2 # get data of advanced tab self.__parameters["defaultPath"] = self.__splitIt( self.defaultPathEdit.text(), os.pathsep ) self.__parameters["includePath"] = self.__splitIt( self.includePathEdit.text(), os.pathsep ) self.__parameters["replacePaths"] = self.__splitIt( self.replacePathsEdit.text(), os.pathsep ) self.__parameters["includeModules"] = self.__splitIt( self.includeModulesEdit.text(), "," ) self.__parameters["excludeModules"] = self.__splitIt( self.excludeModulesEdit.text(), "," ) self.__parameters["extListFile"] = self.__project.getRelativePath( self.extListFilePicker.text() ) # get data of the additional files tab additionalFiles = [ self.fileOrFolderList.item(x).text() for x in range(self.fileOrFolderList.count()) ] self.__parameters["additionalFiles"] = additionalFiles # call the accept slot of the base class QDialog.accept(self) def __splitIt(self, s, sep): """ Private method to split a string observing various conditions. @param s string to split @type str @param sep separator string @type str @return list of split values @rtype list of str """ if s == "" or s is None: return [] if s.endswith(sep): s = s[:-1] return s.split(sep)