PluginCxFreeze.py

Tue, 25 Oct 2022 09:03:26 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 25 Oct 2022 09:03:26 +0200
branch
eric7
changeset 140
9e20ee9c7ca2
parent 139
4df5e67b084b
child 142
9ceff8a10455
permissions
-rw-r--r--

Adapted the import statements to the new structure.

# -*- coding: utf-8 -*-

# Copyright (c) 2010 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the CxFreeze plugin.
"""

import os
import platform

from PyQt6.QtCore import QObject, QTranslator, QCoreApplication, QProcess
from PyQt6.QtWidgets import QDialog

from eric7 import Utilities
from eric7.EricGui.EricAction import EricAction
from eric7.EricWidgets import EricMessageBox
from eric7.EricWidgets.EricApplication import ericApp

# Start-of-Header
name = "CxFreeze Plugin"
author = "Detlev Offenbach <detlev@die-offenbachs.de>"
autoactivate = True
deactivateable = True
version = "10.1.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
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 Utilities.isWindowsPlatform():
        #
        # Windows
        #
        try:
            import winreg
        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 = Utilities.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 = Utilities.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 Utilities.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):
        """
        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.
        """
        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

        from CxFreeze.CxfreezeConfigDialog import CxfreezeConfigDialog

        parms = project.getData("PACKAGERSPARMS", "CXFREEZE")
        dlg = CxfreezeConfigDialog(project, exe, parms)
        if dlg.exec() == QDialog.DialogCode.Accepted:
            args, parms = dlg.generateParameters()
            project.setData("PACKAGERSPARMS", "CXFREEZE", parms)

            # now do the call
            from CxFreeze.CxfreezeExecDialog import CxfreezeExecDialog

            dia = CxfreezeExecDialog("cxfreeze")
            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

eric ide

mercurial