PluginPyInstaller.py

changeset 3
eb2d30b4d34e
parent 2
5109c484bc49
child 4
52f0572b5908
--- a/PluginPyInstaller.py	Tue Jan 16 15:36:57 2018 +0100
+++ b/PluginPyInstaller.py	Wed Jan 17 16:25:59 2018 +0100
@@ -11,8 +11,14 @@
 
 import os
 import platform
+import shutil
 
-from PyQt5.QtCore import QObject, QCoreApplication
+from PyQt5.QtCore import pyqtSlot, QObject, QCoreApplication, QTranslator
+from PyQt5.QtWidgets import QDialog
+
+from E5Gui import E5MessageBox
+from E5Gui.E5Action import E5Action
+from E5Gui.E5Application import e5App
 
 import Utilities
 
@@ -154,10 +160,7 @@
         #
         # Linux, Unix ...
         pyinstallerScripts = ['pyinstaller', 'pyi-makespec']
-##        scriptSuffixes = [""]
-##        for minorVersion in minorVersions:
-##            scriptSuffixes.append(
-##                "-python{0}.{1}".format(majorVersion, minorVersion))
+        
         # There could be multiple pyinstaller executables in the path
         # e.g. for different python variants
         path = Utilities.getEnvironmentEntry('PATH')
@@ -232,30 +235,246 @@
 
 class PyInstallerPlugin(QObject):
     """
-    Class documentation goes here.
+    Class implementing the PyInstaller interface plug-in.
     """
     def __init__(self, ui):
         """
         Constructor
         
-        @param ui reference to the user interface object (UI.UserInterface)
+        @param ui reference to the user interface object
+        @type UI.UserInterface
         """
         super(PyInstallerPlugin, self).__init__(ui)
         self.__ui = ui
+        
+        self.__initialize()
+        _checkProgram()
+        
+        self.__translator = None
+        self.__loadTranslator()
+        
+    def __initialize(self):
+        """
+        Private slot to (re)initialize the plug-in.
+        """
+        self.__projectActs = []
     
     def activate(self):
         """
-        Public method to activate this plugin.
+        Public method to activate this plug-in.
         
         @return tuple of None and activation status (boolean)
         """
         global error
-        error = ""     # clear previous error
+        
+        # There is already an error, don't activate
+        if error:
+            return None, False
+        
+        # pyinstaller interface is only activated if it is available
+        if not _checkProgram():
+            return None, False
+        
+        # clear previous error
+        error = ""
+        
+        project = e5App().getObject("Project")
+        menu = project.getMenu("Packagers")
+        if menu:
+            # Execute PyInstaller
+            act = E5Action(
+                self.tr('Execute PyInstaller'),
+                self.tr('Execute Py&Installer'), 0, 0,
+                self, 'packagers_pyinstaller_run')
+            act.setStatusTip(
+                self.tr('Generate a distribution package using PyInstaller'))
+            act.setWhatsThis(self.tr(
+                """<b>Execute PyInstaller</b>"""
+                """<p>Generate a distribution package using PyInstaller."""
+                """ The command is executed in the project path. All"""
+                """ files and directories must be given absolute or"""
+                """ relative to the project directory.</p>"""
+            ))
+            act.triggered.connect(self.__pyinstaller)
+            menu.addAction(act)
+            self.__projectActs.append(act)
+            
+            # Execute pyi-makespec
+            act = E5Action(
+                self.tr('Make PyInstaller Spec File'),
+                self.tr('Make PyInstaller &Spec File'), 0, 0,
+                self, 'packagers_pyinstaller_spec')
+            act.setStatusTip(
+                self.tr('Generate a spec file to be used by PyInstaller'))
+            act.setWhatsThis(self.tr(
+                """<b>Make PyInstaller Spec File</b>"""
+                """<p>Generate a spec file to be used by PyInstaller"""
+                """ PyInstaller. The command is executed in the project"""
+                """ path. All files and directories must be given absolute"""
+                """ or relative to the project directory.</p>"""
+            ))
+            act.triggered.connect(self.__pyiMakeSpec)
+            menu.addAction(act)
+            self.__projectActs.append(act)
+            
+            # clean the pyinstaller created directories
+            act = E5Action(
+                self.tr('Clean PyInstaller'),
+                self.tr('&Clean PyInstaller'), 0, 0,
+                self, 'packagers_pyinstaller_clean')
+            act.setStatusTip(
+                self.tr('Remove the PyInstaller created directories'))
+            act.setWhatsThis(self.tr(
+                """<b>Clean PyInstaller</b>"""
+                """<p>Remove the PyInstaller created directories (dist and"""
+                """ build). These are subdirectories within the project"""
+                """ path."""
+            ))
+            act.triggered.connect(self.__pyinstallerCleanup)
+            menu.addAction(act)
+            self.__projectActs.append(act)
+            
+            project.addE5Actions(self.__projectActs)
+            project.showMenu.connect(self.__projectShowMenu)
         
         return None, True
     
     def deactivate(self):
         """
-        Public method to deactivate this plugin.
+        Public method to deactivate this plug-in.
+        """
+        menu = e5App().getObject("Project").getMenu("Packagers")
+        if menu:
+            for act in self.__projectActs:
+                menu.removeAction(act)
+            
+            e5App().getObject("Project").removeE5Actions(
+                self.__projectActs)
+        
+        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":
+            enable = e5App().getObject("Project").getProjectLanguage() in \
+                ["Python", "Python2", "Python3"]
+            for act in self.__projectActs:
+                act.setEnabled(enable)
+    
+    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__),
+                                 "PyInstaller", "i18n")
+                translation = "pyinstaller_{0}".format(loc)
+                translator = QTranslator(None)
+                loaded = translator.load(translation, locale_dir)
+                if loaded:
+                    self.__translator = translator
+                    e5App().installTranslator(self.__translator)
+                else:
+                    print("Warning: translation file '{0}' could not be"
+                          " loaded.".format(translation))
+                    print("Using default.")
+    
+    @pyqtSlot()
+    def __pyinstaller(self):
+        """
+        Private slot to execute the pyinstaller command for the current
+        project.
         """
-        pass
+        project = e5App().getObject("Project")
+        majorVersionStr = project.getProjectLanguage()
+        if majorVersionStr == "Python3":
+            executables = [f for f in exePy3 if 
+                           f.endswith(("pyinstaller", "pyinstaller.exe"))]
+        else:
+            executables = [f for f in exePy2 if 
+                           f.endswith(("pyinstaller", "pyinstaller.exe"))]
+        if not executables:
+            E5MessageBox.critical(
+                self.__ui,
+                self.tr("pyinstaller"),
+                self.tr("""The pyinstaller executable could not be found."""))
+            return
+        
+        # check if all files saved and errorfree before continue
+        if not project.checkAllScriptsDirty(reportSyntaxErrors=True):
+            return
+        
+        from PyInstaller.PyInstallerConfigDialog import PyInstallerConfigDialog
+        params = project.getData('PACKAGERSPARMS', "PYINSTALLER")
+        dlg = PyInstallerConfigDialog(project, executables, params,
+                                      mode="installer")
+        if dlg.exec_() == QDialog.Accepted:
+            args, params = dlg.generateParameters()
+            project.setData('PACKAGERSPARMS', "PYINSTALLER", params)
+            
+            # now do the call
+            from PyInstaller.PyInstallerExecDialog import PyInstallerExecDialog
+            dia = PyInstallerExecDialog("pyinstaller")
+            dia.show()
+            res = dia.start(args, params, project.getProjectPath(),
+                            project.getMainScript())
+            if res:
+                dia.exec_()
+        # TODO: implement pyinstaller
+    
+    @pyqtSlot()
+    def __pyiMakeSpec(self):
+        """
+        Private slot to execute the pyi-makespec command for the current
+        project to generate a spec file to be used by pyinstaller.
+        """
+        project = e5App().getObject("Project")
+        majorVersionStr = project.getProjectLanguage()
+        if majorVersionStr == "Python3":
+            executables = [f for f in exePy3 if 
+                           f.endswith(("pyi-makespec", "pyi-makespec.exe"))]
+        else:
+            executables = [f for f in exePy2 if 
+                           f.endswith(("pyi-makespec", "pyi-makespec.exe"))]
+        if not executables:
+            E5MessageBox.critical(
+                self.__ui,
+                self.tr("pyi-makespec"),
+                self.tr("""The pyi-makespec executable could not be found."""))
+            return
+        
+        # check if all files saved and errorfree before continue
+        if not project.checkAllScriptsDirty(reportSyntaxErrors=True):
+            return
+        
+        # TODO: implement pyi-makespec
+    
+    @pyqtSlot()
+    def __pyinstallerCleanup(self):
+        """
+        Private slot to remove the directories created by pyinstaller.
+        """
+        project = e5App().getObject("Project")
+        
+        from PyInstaller.PyInstallerCleanupDialog import \
+            PyInstallerCleanupDialog
+        dlg = PyInstallerCleanupDialog()
+        if dlg.exec_() == QDialog.Accepted:
+            removeDirs = dlg.getDirectories()
+            for directory in removeDirs:
+                rd = os.path.join(project.getProjectPath(), directory)
+                shutil.rmtree(rd, ignore_errors=True)
+
+#
+# eflag: noqa = M801

eric ide

mercurial