Tue, 10 Dec 2024 15:48:55 +0100
Updated copyright for 2025.
# -*- coding: utf-8 -*- # Copyright (c) 2010 - 2025 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the CxFreeze plugin. """ import os import platform from PyQt6.QtCore import QCoreApplication, QObject, QProcess, QTranslator from PyQt6.QtWidgets import QDialog from eric7.EricGui.EricAction import EricAction from eric7.EricWidgets import EricMessageBox from eric7.EricWidgets.EricApplication import ericApp try: from eric7.SystemUtilities.OSUtilities import getEnvironmentEntry, isWindowsPlatform except ImportError: # imports for eric < 23.1 from eric7.Globals import isWindowsPlatform from eric7.Utilities import getEnvironmentEntry # Start-of-Header __header__ = { "name": "CxFreeze Plugin", "author": "Detlev Offenbach <detlev@die-offenbachs.de>", "autoactivate": True, "deactivateable": True, "version": "10.3.0", "className": "CxFreezePlugin", "packageName": "CxFreeze", "shortDescription": "Show the CxFreeze dialogs.", "longDescription": ( "This plugin implements the CxFreeze dialogs. CxFreeze is used to" " generate a distribution package." ), "needsRestart": False, "hasCompiledForms": True, "pyqtApi": 2, } # End-of-Header error = "" exePy3 = [] def exeDisplayDataList(): """ Public method to support the display of some executable info. @return dictionary containing the data to query the presence of the executable @rtype dict """ dataList = [] data = { "programEntry": True, "header": QCoreApplication.translate("CxFreezePlugin", "Packagers - cx_freeze"), "exe": "dummyfreeze", "versionCommand": "--version", "versionStartsWith": "dummyfreeze", "versionPosition": -1, "version": "", "versionCleanup": None, } if _checkProgram(): for exePath in exePy3: data["exe"] = exePath data["versionStartsWith"] = "cxfreeze" dataList.append(data.copy()) else: dataList.append(data) return dataList def _findExecutable(majorVersion): """ Restricted function to determine the names of the executable. @param majorVersion major Python version of the executables @type int @return names of the executable @rtype list of str """ # Determine Python Version if majorVersion == 3: minorVersions = range(10) else: return [] executables = set() if isWindowsPlatform(): # # Windows # try: import winreg # noqa: I101, I103 except ImportError: import _winreg as winreg # __IGNORE_WARNING__ def getExePath(branch, access, versionStr): try: software = winreg.OpenKey(branch, "Software", 0, access) python = winreg.OpenKey(software, "Python", 0, access) pcore = winreg.OpenKey(python, "PythonCore", 0, access) version = winreg.OpenKey(pcore, versionStr, 0, access) installpath = winreg.QueryValue(version, "InstallPath") exe = os.path.join(installpath, "Scripts", "cxfreeze.bat") if os.access(exe, os.X_OK): return exe except WindowsError: # __IGNORE_WARNING__ return None return None versionSuffixes = ["", "-32", "-64"] for minorVersion in minorVersions: for versionSuffix in versionSuffixes: versionStr = "{0}.{1}{2}".format( majorVersion, minorVersion, versionSuffix ) exePath = getExePath( winreg.HKEY_CURRENT_USER, winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr, ) if exePath is not None: executables.add(exePath) exePath = getExePath( winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr, ) if exePath is not None: executables.add(exePath) # Even on Intel 64-bit machines it's 'AMD64' if platform.machine() == "AMD64": exePath = getExePath( winreg.HKEY_CURRENT_USER, winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr, ) if exePath is not None: executables.add(exePath) exePath = getExePath( winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr, ) if exePath is not None: executables.add(exePath) if not executables and majorVersion >= 3: # check the PATH environment variable if nothing was found # Python 3 only path = getEnvironmentEntry("PATH") if path: dirs = path.split(os.pathsep) for directory in dirs: for suffix in (".bat", ".exe"): exe = os.path.join(directory, "cxfreeze" + suffix) if os.access(exe, os.X_OK): executables.add(exe) else: # # Linux, Unix ... cxfreezeScript = "cxfreeze" scriptSuffixes = ["", "-python{0}".format(majorVersion)] for minorVersion in minorVersions: scriptSuffixes.append("-python{0}.{1}".format(majorVersion, minorVersion)) # There could be multiple cxfreeze executables in the path # e.g. for different python variants path = getEnvironmentEntry("PATH") # environment variable not defined if path is None: return [] # step 1: determine possible candidates exes = [] dirs = path.split(os.pathsep) for directory in dirs: for suffix in scriptSuffixes: exe = os.path.join(directory, cxfreezeScript + suffix) if os.access(exe, os.X_OK): exes.append(exe) # step 2: determine the Python variant _exePy3 = set() versionArgs = ["-c", "import sys; print(sys.version_info[0])"] for exe in exes: with open(exe, "r") as f: line0 = f.readline() program = line0.replace("#!", "").strip() process = QProcess() process.start(program, versionArgs) process.waitForFinished(5000) # get a QByteArray of the output versionBytes = process.readAllStandardOutput() versionStr = str(versionBytes, encoding="utf-8").strip() if versionStr == "3": _exePy3.add(exe) executables = _exePy3 # sort items, the probably newest topmost executables = list(executables) executables.sort(reverse=True) return executables def _checkProgram(): """ Restricted function to check the availability of cxfreeze. @return flag indicating availability @rtype bool """ global error, exePy3 exePy3 = _findExecutable(3) if exePy3 == []: if isWindowsPlatform(): error = QCoreApplication.translate( "CxFreezePlugin", "The cxfreeze.bat executable could not be found." "Did you run the cxfreeze-postinstall script?", ) else: error = QCoreApplication.translate( "CxFreezePlugin", "The cxfreeze executable could not be found." ) return False else: return True class CxFreezePlugin(QObject): """ Class implementing the CxFreeze plugin. """ def __init__(self, ui): """ Constructor @param ui reference to the user interface object @type UserInterface """ super().__init__(ui) self.__ui = ui self.__initialize() _checkProgram() self.__translator = None self.__loadTranslator() def __initialize(self): """ Private slot to (re)initialize the plugin. """ self.__projectAct = None self.__projectSeparator = None def activate(self): """ Public method to activate this plugin. @return tuple of None and activation status @rtype tuple of (None, bool) """ global error # There is already an error, don't activate if error: return None, False # cxfreeze is only activated if it is available if not _checkProgram(): return None, False project = ericApp().getObject("Project") menu = project.getMenu("Packagers") if menu: self.__projectAct = EricAction( self.tr("Use cx_freeze"), self.tr("Use cx_&freeze"), 0, 0, self, "packagers_cxfreeze", ) self.__projectAct.setStatusTip( self.tr("Generate a distribution package using cx_freeze") ) self.__projectAct.setWhatsThis( self.tr( """<b>Use cx_freeze</b>""" """<p>Generate a distribution package using cx_freeze.""" """ The command is executed in the project path. All""" """ files and directories must be given absolute or""" """ relative to the project directory.</p>""" ) ) self.__projectAct.triggered.connect(self.__cxfreeze) project.addEricActions([self.__projectAct]) self.__projectSeparator = menu.addSeparator() menu.addAction(self.__projectAct) project.showMenu.connect(self.__projectShowMenu) error = "" return None, True def deactivate(self): """ Public method to deactivate this plugin. """ menu = ericApp().getObject("Project").getMenu("Packagers") if menu: if self.__projectAct: menu.removeAction(self.__projectAct) ericApp().getObject("Project").removeEricActions([self.__projectAct]) if self.__projectSeparator: menu.removeAction(self.__projectSeparator) self.__initialize() def __projectShowMenu(self, menuName, menu): # noqa: U100 """ Private slot called, when the the project menu or a submenu is about to be shown. @param menuName name of the menu to be shown @type str @param menu reference to the menu @type QMenu """ if menuName == "Packagers" and self.__projectAct is not None: self.__projectAct.setEnabled( ericApp().getObject("Project").getProjectLanguage() == "Python3" ) def __loadTranslator(self): """ Private method to load the translation file. """ if self.__ui is not None: loc = self.__ui.getLocale() if loc and loc != "C": locale_dir = os.path.join(os.path.dirname(__file__), "CxFreeze", "i18n") translation = "cxfreeze_{0}".format(loc) translator = QTranslator(None) loaded = translator.load(translation, locale_dir) if loaded: self.__translator = translator ericApp().installTranslator(self.__translator) else: print( "Warning: translation file '{0}' could not be" " loaded.".format(translation) ) print("Using default.") def __cxfreeze(self): """ Private slot to handle the cxfreeze execution. """ from CxFreeze.CxfreezeConfigDialog import CxfreezeConfigDialog from CxFreeze.CxfreezeExecDialog import CxfreezeExecDialog project = ericApp().getObject("Project") if not project.getMainScript(): # no main script defined EricMessageBox.critical( self.__ui, self.tr("cxfreeze"), self.tr( """There is no main script defined for the current""" """ project.""" ), EricMessageBox.StandardButtons(EricMessageBox.Abort), ) return majorVersionStr = project.getProjectLanguage() exe = {"Python3": exePy3}.get(majorVersionStr) if exe == []: EricMessageBox.critical( self.__ui, self.tr("cxfreeze"), self.tr("""The cxfreeze executable could not be found."""), ) return # check if all files saved and errorfree before continue if not project.checkAllScriptsDirty(reportSyntaxErrors=True): return parms = project.getData("PACKAGERSPARMS", "CXFREEZE") dlg = CxfreezeConfigDialog(project, exe, parms, parent=self.__ui) if dlg.exec() == QDialog.DialogCode.Accepted: args, parms = dlg.generateParameters() project.setData("PACKAGERSPARMS", "CXFREEZE", parms) # now do the call dia = CxfreezeExecDialog("cxfreeze", parent=self.__ui) dia.show() res = dia.start(args, parms, project.ppath, project.getMainScript()) if res: dia.exec() def installDependencies(pipInstall): """ Function to install dependencies of this plug-in. @param pipInstall function to be called with a list of package names. @type function """ try: import cx_Freeze # __IGNORE_WARNING__ except ImportError: pipInstall(["cx-Freeze"]) # # eflag: noqa = M801, U200