ExtensionProtobuf/ProjectProtocolsBrowser.py

changeset 1
7157a39d4a0f
child 8
24fdd6e43cd7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ExtensionProtobuf/ProjectProtocolsBrowser.py	Sun Dec 04 14:32:07 2022 +0100
@@ -0,0 +1,838 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the a class used to display the protocols (protobuf) part
+of the project.
+"""
+
+import contextlib
+import functools
+import glob
+import os
+
+from PyQt6.QtCore import QProcess, QThread, pyqtSignal
+from PyQt6.QtWidgets import QApplication, QDialog, QMenu
+
+from eric7 import Globals, Preferences, Utilities
+from eric7.EricGui import EricPixmapCache
+from eric7.EricWidgets import EricMessageBox
+from eric7.EricWidgets.EricApplication import ericApp
+from eric7.EricWidgets.EricProgressDialog import EricProgressDialog
+from eric7.Project.FileCategoryRepositoryItem import FileCategoryRepositoryItem
+from eric7.Project.ProjectBaseBrowser import ProjectBaseBrowser
+from eric7.Project.ProjectBrowserModel import (
+    ProjectBrowserDirectoryItem,
+    ProjectBrowserFileItem,
+    ProjectBrowserSimpleDirectoryItem,
+)
+from eric7.Project.ProjectBrowserRepositoryItem import ProjectBrowserRepositoryItem
+from eric7.UI.BrowserModel import (
+    BrowserClassAttributeItem,
+    BrowserClassItem,
+    BrowserFileItem,
+    BrowserMethodItem,
+)
+from eric7.UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
+from eric7.UI.NotificationWidget import NotificationTypes
+
+
+class ProjectProtocolsBrowser(ProjectBaseBrowser):
+    """
+    A class used to display the protocols (protobuf) part of the project.
+
+    @signal appendStdout(str) emitted after something was received from
+        a QProcess on stdout
+    @signal appendStderr(str) emitted after something was received from
+        a QProcess on stderr
+    @signal showMenu(str, QMenu) emitted when a menu is about to be shown.
+        The name of the menu and a reference to the menu are given.
+    """
+
+    appendStdout = pyqtSignal(str)
+    appendStderr = pyqtSignal(str)
+    showMenu = pyqtSignal(str, QMenu)
+
+    FileFilter = "proto"
+
+    def __init__(self, plugin, parent=None):
+        """
+        Constructor
+
+        @param plugin reference to the plugin object
+        @type ProtobufExtensionPlugin
+        @param parent parent widget of this browser
+        @type QWidget
+        """
+        project = ericApp().getObject("Project")
+        projectBrowser = ericApp().getObject("ProjectBrowser")
+
+        ProjectBaseBrowser.__init__(self, project, self.FileFilter, parent)
+
+        self.selectedItemsFilter = [
+            ProjectBrowserFileItem,
+            ProjectBrowserSimpleDirectoryItem,
+        ]
+
+        self.setWindowTitle(self.tr("Protocols (protobuf)"))
+
+        self.setWhatsThis(
+            self.tr(
+                """<b>Project Protocols Browser</b>"""
+                """<p>This allows to easily see all protocols (protobuf files)"""
+                """ contained in the current project. Several actions can be"""
+                """ executed via the context menu.</p>"""
+            )
+        )
+
+        self.__plugin = plugin
+
+        # Add the file category handled by the browser.
+        project.addFileCategory(
+            "PROTOCOLS",
+            FileCategoryRepositoryItem(
+                fileCategoryFilterTemplate=self.tr("Protobuf Files ({0})"),
+                fileCategoryUserString=self.tr("Protobuf Files"),
+                fileCategoryTyeString=self.tr("Protobuf Files"),
+                fileCategoryExtensions=["*.proto"],
+            ),
+        )
+
+        # Add the project browser type to the browser type repository.
+        projectBrowser.addTypedProjectBrowser(
+            "protocols",
+            ProjectBrowserRepositoryItem(
+                projectBrowser=self,
+                projectBrowserUserString=self.tr("Protocols (protobuf) Browser"),
+                priority=50,
+                fileCategory="PROTOCOLS",
+                fileFilter=self.FileFilter,
+                getIcon=self.getIcon,
+            ),
+        )
+
+        # Connect signals of Project.
+        project.prepareRepopulateItem.connect(self._prepareRepopulateItem)
+        project.completeRepopulateItem.connect(self._completeRepopulateItem)
+        project.projectClosed.connect(self._projectClosed)
+        project.projectOpened.connect(self._projectOpened)
+        project.newProject.connect(self._newProject)
+        project.reinitVCS.connect(self._initMenusAndVcs)
+        project.projectPropertiesChanged.connect(self._initMenusAndVcs)
+
+        # Connect signals of ProjectBrowser.
+        projectBrowser.preferencesChanged.connect(self.handlePreferencesChanged)
+
+        # Connect some of our own signals.
+        self.appendStderr.connect(projectBrowser.appendStderr)
+        self.appendStdout.connect(projectBrowser.appendStdout)
+        self.closeSourceWindow.connect(projectBrowser.closeSourceWindow)
+        self.sourceFile[str].connect(projectBrowser.sourceFile[str])
+        self.sourceFile[str, int].connect(projectBrowser.sourceFile[str, int])
+
+    def deactivate(self):
+        """
+        Public method to deactivate the browser.
+        """
+        project = ericApp().getObject("Project")
+        projectBrowser = ericApp().getObject("ProjectBrowser")
+
+        # Disconnect some of our own signals.
+        self.appendStderr.disconnect(projectBrowser.appendStderr)
+        self.appendStdout.disconnect(projectBrowser.appendStdout)
+        self.closeSourceWindow.disconnect(projectBrowser.closeSourceWindow)
+        self.sourceFile[str].disconnect(projectBrowser.sourceFile[str])
+        self.sourceFile[str, int].disconnect(projectBrowser.sourceFile[str, int])
+
+        # Disconnect signals of ProjectBrowser.
+        projectBrowser.preferencesChanged.disconnect(self.handlePreferencesChanged)
+
+        # Disconnect signals of Project.
+        project.prepareRepopulateItem.disconnect(self._prepareRepopulateItem)
+        project.completeRepopulateItem.disconnect(self._completeRepopulateItem)
+        project.projectClosed.disconnect(self._projectClosed)
+        project.projectOpened.disconnect(self._projectOpened)
+        project.newProject.disconnect(self._newProject)
+        project.reinitVCS.disconnect(self._initMenusAndVcs)
+        project.projectPropertiesChanged.disconnect(self._initMenusAndVcs)
+
+        # Remove the project browser type from the browser type repository.
+        projectBrowser.removeTypedProjectBrowser("protocols")
+
+        # Remove the file category handled by the browser.
+        project.removeFileCategory("PROTOCOLS")
+
+    def getIcon(self):
+        """
+        Public method to get an icon for the project browser.
+
+        @return icon for the browser
+        @rtype QIcon
+        """
+        return EricPixmapCache.getIcon(
+            os.path.join(os.path.dirname(__file__), "icons", "protobuf")
+        )
+
+    def _createPopupMenus(self):
+        """
+        Protected overloaded method to generate the popup menu.
+        """
+        self.menuActions = []
+        self.multiMenuActions = []
+        self.dirMenuActions = []
+        self.dirMultiMenuActions = []
+
+        self.sourceMenu = QMenu(self)
+        self.sourceMenu.addAction(self.tr("Compile protocol"), self.__compileProtocol)
+        self.sourceMenu.addAction(
+            self.tr("Compile all protocols"), self.__compileAllProtocols
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr("Compile protocol as gRPC"),
+            functools.partial(self.__compileProtocol, grpc=True),
+        )
+        self.sourceMenu.addAction(
+            self.tr("Compile all protocols as gRPC"),
+            functools.partial(self.__compileAllProtocols, grpc=True),
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("Open"), self._openItem)
+        self.sourceMenu.addSeparator()
+        act = self.sourceMenu.addAction(self.tr("Rename file"), self._renameFile)
+        self.menuActions.append(act)
+        act = self.sourceMenu.addAction(
+            self.tr("Remove from project"), self._removeFile
+        )
+        self.menuActions.append(act)
+        act = self.sourceMenu.addAction(self.tr("Delete"), self.__deleteFile)
+        self.menuActions.append(act)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("Add protocols..."), self.__addProtocolFiles)
+        self.sourceMenu.addAction(
+            self.tr("Add protocols directory..."), self.__addProtocolsDirectory
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr("Copy Path to Clipboard"), self._copyToClipboard
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.sourceMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(self.tr("Configure..."), self._configure)
+        self.sourceMenu.addAction(
+            self.tr("Configure Protobuf..."), self.__configureProtobuf
+        )
+
+        self.menu = QMenu(self)
+        self.menu.addAction(self.tr("Compile protocol"), self.__compileProtocol)
+        self.menu.addAction(
+            self.tr("Compile all protocols"), self.__compileAllProtocols
+        )
+        self.menu.addSeparator()
+        self.menu.addAction(
+            self.tr("Compile protocol as gRPC"),
+            functools.partial(self.__compileProtocol, grpc=True),
+        )
+        self.menu.addAction(
+            self.tr("Compile all protocols as gRPC"),
+            functools.partial(self.__compileAllProtocols, grpc=True),
+        )
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Open"), self._openItem)
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Add protocols..."), self.__addProtocolFiles)
+        self.menu.addAction(
+            self.tr("Add protocols directory..."), self.__addProtocolsDirectory
+        )
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.menu.addAction(self.tr("Collapse all directories"), self._collapseAllDirs)
+        self.menu.addSeparator()
+        self.menu.addAction(self.tr("Configure..."), self._configure)
+        self.menu.addAction(self.tr("Configure Protobuf..."), self.__configureProtobuf)
+
+        self.backMenu = QMenu(self)
+        self.backMenu.addAction(
+            self.tr("Compile all protocols"), self.__compileAllProtocols
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(
+            self.tr("Compile all protocols as gRPC"),
+            functools.partial(self.__compileAllProtocols, grpc=True),
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(
+            self.tr("Add protocols..."), lambda: self.project.addFiles("PROTOCOLS")
+        )
+        self.backMenu.addAction(
+            self.tr("Add protocols directory..."),
+            lambda: self.project.addDirectory("PROTOCOLS"),
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.backMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(self.tr("Configure..."), self._configure)
+        self.backMenu.addAction(
+            self.tr("Configure Protobuf..."), self.__configureProtobuf
+        )
+        self.backMenu.setEnabled(False)
+
+        # create the menu for multiple selected files
+        self.multiMenu = QMenu(self)
+        self.multiMenu.addAction(
+            self.tr("Compile protocols"), self.__compileSelectedProtocols
+        )
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(
+            self.tr("Compile protocols as gRPC"),
+            functools.partial(self.__compileSelectedProtocols, grpc=True),
+        )
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Open"), self._openItem)
+        self.multiMenu.addSeparator()
+        act = self.multiMenu.addAction(self.tr("Remove from project"), self._removeFile)
+        self.multiMenuActions.append(act)
+        act = self.multiMenu.addAction(self.tr("Delete"), self.__deleteFile)
+        self.multiMenuActions.append(act)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.multiMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(self.tr("Configure..."), self._configure)
+        self.multiMenu.addAction(
+            self.tr("Configure Protobuf..."), self.__configureProtobuf
+        )
+
+        self.dirMenu = QMenu(self)
+        self.dirMenu.addAction(
+            self.tr("Compile all protocols"), self.__compileAllProtocols
+        )
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(
+            self.tr("Compile all protocols as gRPC"),
+            functools.partial(self.__compileAllProtocols, grpc=True),
+        )
+        act = self.dirMenu.addAction(self.tr("Remove from project"), self._removeFile)
+        self.dirMenuActions.append(act)
+        act = self.dirMenu.addAction(self.tr("Delete"), self._deleteDirectory)
+        self.dirMenuActions.append(act)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Add protocols..."), self.__addProtocolFiles)
+        self.dirMenu.addAction(
+            self.tr("Add protocols directory..."), self.__addProtocolsDirectory
+        )
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Copy Path to Clipboard"), self._copyToClipboard)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Expand all directories"), self._expandAllDirs)
+        self.dirMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(self.tr("Configure..."), self._configure)
+        self.dirMenu.addAction(
+            self.tr("Configure Protobuf..."), self.__configureProtobuf
+        )
+
+        self.dirMultiMenu = QMenu(self)
+        self.dirMultiMenu.addAction(
+            self.tr("Compile all protocols"), self.__compileAllProtocols
+        )
+        self.dirMultiMenu.addSeparator()
+        self.dirMultiMenu.addAction(
+            self.tr("Compile all protocols as gRPC"),
+            functools.partial(self.__compileAllProtocols, grpc=True),
+        )
+        self.dirMultiMenu.addAction(
+            self.tr("Add protocols..."), lambda: self.project.addFiles("PROTOCOLS")
+        )
+        self.dirMultiMenu.addAction(
+            self.tr("Add protocols directory..."),
+            lambda: self.project.addDirectory("PROTOCOLS"),
+        )
+        self.dirMultiMenu.addSeparator()
+        self.dirMultiMenu.addAction(
+            self.tr("Expand all directories"), self._expandAllDirs
+        )
+        self.dirMultiMenu.addAction(
+            self.tr("Collapse all directories"), self._collapseAllDirs
+        )
+        self.dirMultiMenu.addSeparator()
+        self.dirMultiMenu.addAction(self.tr("Configure..."), self._configure)
+        self.dirMultiMenu.addAction(
+            self.tr("Configure Protobuf..."), self.__configureProtobuf
+        )
+
+        self.sourceMenu.aboutToShow.connect(self.__showContextMenu)
+        self.multiMenu.aboutToShow.connect(self.__showContextMenuMulti)
+        self.dirMenu.aboutToShow.connect(self.__showContextMenuDir)
+        self.dirMultiMenu.aboutToShow.connect(self.__showContextMenuDirMulti)
+        self.backMenu.aboutToShow.connect(self.__showContextMenuBack)
+        self.mainMenu = self.sourceMenu
+
+    def _contextMenuRequested(self, coord):
+        """
+        Protected slot to show the context menu.
+
+        @param coord the position of the mouse pointer (QPoint)
+        """
+        if not self.project.isOpen():
+            return
+
+        with contextlib.suppress(Exception):  # secok
+            categories = self.getSelectedItemsCountCategorized(
+                [
+                    ProjectBrowserFileItem,
+                    BrowserClassItem,
+                    BrowserMethodItem,
+                    ProjectBrowserSimpleDirectoryItem,
+                ]
+            )
+            cnt = categories["sum"]
+            if cnt <= 1:
+                index = self.indexAt(coord)
+                if index.isValid():
+                    self._selectSingleItem(index)
+                    categories = self.getSelectedItemsCountCategorized(
+                        [
+                            ProjectBrowserFileItem,
+                            BrowserClassItem,
+                            BrowserMethodItem,
+                            ProjectBrowserSimpleDirectoryItem,
+                        ]
+                    )
+                    cnt = categories["sum"]
+
+            bfcnt = categories[str(ProjectBrowserFileItem)]
+            cmcnt = (
+                categories[str(BrowserClassItem)] + categories[str(BrowserMethodItem)]
+            )
+            sdcnt = categories[str(ProjectBrowserSimpleDirectoryItem)]
+            if cnt > 1 and cnt == bfcnt:
+                self.multiMenu.popup(self.mapToGlobal(coord))
+            elif cnt > 1 and cnt == sdcnt:
+                self.dirMultiMenu.popup(self.mapToGlobal(coord))
+            else:
+                index = self.indexAt(coord)
+                if cnt == 1 and index.isValid():
+                    if bfcnt == 1 or cmcnt == 1:
+                        itm = self.model().item(index)
+                        if isinstance(itm, ProjectBrowserFileItem):
+                            self.sourceMenu.popup(self.mapToGlobal(coord))
+                        elif isinstance(itm, (BrowserClassItem, BrowserMethodItem)):
+                            self.menu.popup(self.mapToGlobal(coord))
+                        else:
+                            self.backMenu.popup(self.mapToGlobal(coord))
+                    elif sdcnt == 1:
+                        self.dirMenu.popup(self.mapToGlobal(coord))
+                    else:
+                        self.backMenu.popup(self.mapToGlobal(coord))
+                else:
+                    self.backMenu.popup(self.mapToGlobal(coord))
+
+    def __showContextMenu(self):
+        """
+        Private slot called by the menu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenu(self, self.menu)
+
+        self.showMenu.emit("Main", self.menu)
+
+    def __showContextMenuMulti(self):
+        """
+        Private slot called by the multiMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenuMulti(self, self.multiMenu)
+
+        self.showMenu.emit("MainMulti", self.multiMenu)
+
+    def __showContextMenuDir(self):
+        """
+        Private slot called by the dirMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenuDir(self, self.dirMenu)
+
+        self.showMenu.emit("MainDir", self.dirMenu)
+
+    def __showContextMenuDirMulti(self):
+        """
+        Private slot called by the dirMultiMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenuDirMulti(self, self.dirMultiMenu)
+
+        self.showMenu.emit("MainDirMulti", self.dirMultiMenu)
+
+    def __showContextMenuBack(self):
+        """
+        Private slot called by the backMenu aboutToShow signal.
+        """
+        ProjectBaseBrowser._showContextMenuBack(self, self.backMenu)
+
+        self.showMenu.emit("MainBack", self.backMenu)
+
+    def _openItem(self):
+        """
+        Protected slot to handle the open popup menu entry.
+        """
+        itmList = self.getSelectedItems(
+            [
+                BrowserFileItem,
+                BrowserClassItem,
+                BrowserMethodItem,
+                BrowserClassAttributeItem,
+            ]
+        )
+
+        for itm in itmList:
+            if isinstance(itm, BrowserFileItem):
+                self.sourceFile[str].emit(itm.fileName())
+            elif isinstance(itm, BrowserClassItem):
+                self.sourceFile[str, int].emit(itm.fileName(), itm.classObject().lineno)
+            elif isinstance(itm, BrowserMethodItem):
+                self.sourceFile[str, int].emit(
+                    itm.fileName(), itm.functionObject().lineno
+                )
+            elif isinstance(itm, BrowserClassAttributeItem):
+                self.sourceFile[str, int].emit(
+                    itm.fileName(), itm.attributeObject().lineno
+                )
+
+    def __addProtocolFiles(self):
+        """
+        Private method to add protocol files to the project.
+        """
+        itm = self.model().item(self.currentIndex())
+        if isinstance(
+            itm, (ProjectBrowserFileItem, BrowserClassItem, BrowserMethodItem)
+        ):
+            dn = os.path.dirname(itm.fileName())
+        elif isinstance(
+            itm, (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
+        ):
+            dn = itm.dirName()
+        else:
+            dn = None
+        self.project.addFiles("protocol", dn)
+
+    def __addProtocolsDirectory(self):
+        """
+        Private method to add protocol files of a directory to the project.
+        """
+        itm = self.model().item(self.currentIndex())
+        if isinstance(
+            itm, (ProjectBrowserFileItem, BrowserClassItem, BrowserMethodItem)
+        ):
+            dn = os.path.dirname(itm.fileName())
+        elif isinstance(
+            itm, (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem)
+        ):
+            dn = itm.dirName()
+        else:
+            dn = None
+        self.project.addDirectory("protocol", dn)
+
+    def __deleteFile(self):
+        """
+        Private method to delete files from the project.
+        """
+        itmList = self.getSelectedItems()
+
+        files = []
+        fullNames = []
+        for itm in itmList:
+            fn2 = itm.fileName()
+            fullNames.append(fn2)
+            fn = self.project.getRelativePath(fn2)
+            files.append(fn)
+
+        dlg = DeleteFilesConfirmationDialog(
+            self.parent(),
+            self.tr("Delete Protocols"),
+            self.tr(
+                "Do you really want to delete these protocol files from" " the project?"
+            ),
+            files,
+        )
+
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            for fn2, fn in zip(fullNames, files):
+                self.closeSourceWindow.emit(fn2)
+                self.project.deleteFile(fn)
+
+    ###########################################################################
+    ##  Methods to handle the various compile commands
+    ###########################################################################
+
+    def __getCompilerCommand(self, grpc):
+        """
+        Private method to get the compiler command.
+
+        @param grpc flag indicating to get a gRPC command
+        @type bool
+        @return tuple giving the executable and its parameter list
+        @rtype tuple of (str, list of str)
+        """
+        exe = None
+        exeArgs = []
+
+        if grpc:
+            env = self.__plugin.getPreferences("grpcPythonEnv")
+            exe = ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(env)
+            if not exe:
+                exe = Globals.getPythonExecutable()
+            exeArgs = ["-m", "grpc_tools.protoc"]
+        else:
+            exe = self.__plugin.getPreferences("protoc")
+            if exe == "":
+                exe = "protoc.exe" if Utilities.isWindowsPlatform() else "protoc"
+            if not Utilities.isinpath(exe):
+                exe = None
+
+        return exe, exeArgs
+
+    def __readStdout(self):
+        """
+        Private slot to handle the readyReadStandardOutput signal of the
+        protoc process.
+        """
+        if self.compileProc is None:
+            return
+
+        ioEncoding = Preferences.getSystem("IOEncoding")
+
+        self.compileProc.setReadChannel(QProcess.ProcessChannel.StandardOutput)
+        while self.compileProc and self.compileProc.canReadLine():
+            s = "protoc: "
+            output = str(self.compileProc.readLine(), ioEncoding, "replace")
+            s += output
+            self.appendStdout.emit(s)
+
+    def __readStderr(self):
+        """
+        Private slot to handle the readyReadStandardError signal of the
+        protoc process.
+        """
+        if self.compileProc is None:
+            return
+
+        ioEncoding = Preferences.getSystem("IOEncoding")
+
+        self.compileProc.setReadChannel(QProcess.ProcessChannel.StandardError)
+        while self.compileProc and self.compileProc.canReadLine():
+            s = "protoc: "
+            error = str(self.compileProc.readLine(), ioEncoding, "replace")
+            s += error
+            self.appendStderr.emit(s)
+
+    def __compileProtoDone(self, exitCode, exitStatus, grpc):
+        """
+        Private slot to handle the finished signal of the protoc process.
+
+        @param exitCode exit code of the process
+        @type int
+        @param exitStatus exit status of the process
+        @type QProcess.ExitStatus
+        @param grpc flag indicating to compile as gRPC files
+        @type bool
+        """
+        self.__compileRunning = False
+        ui = ericApp().getObject("UserInterface")
+        if exitStatus == QProcess.ExitStatus.NormalExit and exitCode == 0:
+            path = os.path.dirname(self.__protoFile)
+            fileList = glob.glob(os.path.join(path, "*_pb2.py"))
+            if grpc:
+                fileList += glob.glob(os.path.join(path, "*_pb2_grpc.py"))
+            for file in fileList:
+                self.project.appendFile(file)
+            if grpc:
+                icon = EricPixmapCache.getPixmap("gRPC48")
+            else:
+                icon = EricPixmapCache.getPixmap("protobuf48")
+            ui.showNotification(
+                icon,
+                self.tr("Protocol Compilation"),
+                self.tr("The compilation of the protocol file was" " successful."),
+            )
+        else:
+            if grpc:
+                icon = EricPixmapCache.getPixmap(
+                    os.path.join(os.path.dirname(__file__), "icons", "gRPC48")
+                )
+            else:
+                icon = EricPixmapCache.getPixmap(
+                    os.path.join(os.path.dirname(__file__), "icons", "protobuf48")
+                )
+            ui.showNotification(
+                icon,
+                self.tr("Protocol Compilation"),
+                self.tr("The compilation of the protocol file failed."),
+                kind=NotificationTypes.CRITICAL,
+                timeout=0,
+            )
+        self.compileProc = None
+
+    def __compileProto(self, fn, noDialog=False, progress=None, grpc=False):
+        """
+        Private method to compile a .proto file to Python.
+
+        @param fn filename of the .proto file to be compiled
+        @type str
+        @param noDialog flag indicating silent operations
+        @type bool
+        @param progress reference to the progress dialog
+        @type EricProgressDialog
+        @param grpc flag indicating to compile as gRPC files
+        @type bool
+        @return reference to the compile process
+        @rtype QProcess
+        """
+        exe, exeArgs = self.__getCompilerCommand(grpc)
+        if exe:
+            self.compileProc = QProcess()
+            args = []
+
+            fn = os.path.join(self.project.ppath, fn)
+            self.__protoFile = fn
+
+            srcPath = os.path.dirname(fn)
+            args.append("--proto_path={0}".format(srcPath))
+            args.append("--python_out={0}".format(srcPath))
+            if grpc:
+                args.append("--grpc_python_out={0}".format(srcPath))
+            args.append(fn)
+
+            self.compileProc.finished.connect(
+                lambda c, s: self.__compileProtoDone(c, s, grpc)
+            )
+            self.compileProc.readyReadStandardOutput.connect(self.__readStdout)
+            self.compileProc.readyReadStandardError.connect(self.__readStderr)
+
+            self.noDialog = noDialog
+            self.compileProc.start(exe, exeArgs + args)
+            procStarted = self.compileProc.waitForStarted(5000)
+            if procStarted:
+                self.__compileRunning = True
+                return self.compileProc
+            else:
+                self.__compileRunning = False
+                if progress is not None:
+                    progress.cancel()
+                EricMessageBox.critical(
+                    self,
+                    self.tr("Process Generation Error"),
+                    self.tr(
+                        "<p>Could not start {0}.<br>"
+                        "Ensure that it is in the search path.</p>"
+                    ).format(exe),
+                )
+                return None
+        else:
+            EricMessageBox.critical(
+                self,
+                self.tr("Compiler Invalid"),
+                self.tr("The configured compiler is invalid."),
+            )
+            return None
+
+    def __compileProtocol(self, grpc=False):
+        """
+        Private method to compile a protocol to Python.
+
+        @param grpc flag indicating to compile as gRPC files
+        @type bool
+        """
+        if self.__getCompilerCommand(grpc)[0] is not None:
+            itm = self.model().item(self.currentIndex())
+            fn2 = itm.fileName()
+            fn = self.project.getRelativePath(fn2)
+            self.__compileProto(fn, grpc=grpc)
+
+    def __compileAllProtocols(self, grpc=False):
+        """
+        Private method to compile all protocols to Python.
+
+        @param grpc flag indicating to compile as gRPC files
+        @type bool
+        """
+        if self.__getCompilerCommand(grpc)[0] is not None:
+            numProtos = len(self.project.getProjectData(dataKey="PROTOCOLS"))
+            progress = EricProgressDialog(
+                self.tr("Compiling Protocols..."),
+                self.tr("Abort"),
+                0,
+                numProtos,
+                self.tr("%v/%m Protocols"),
+                self,
+            )
+            progress.setModal(True)
+            progress.setMinimumDuration(0)
+            progress.setWindowTitle(self.tr("Protocols"))
+
+            for prog, fn in enumerate(self.project.getProjectData(dataKey="PROTOCOLS")):
+                progress.setValue(prog)
+                if progress.wasCanceled():
+                    break
+                proc = self.__compileProto(fn, True, progress, grpc=grpc)
+                if proc is not None:
+                    while proc.state() == QProcess.ProcessState.Running:
+                        QThread.msleep(100)
+                        QApplication.processEvents()
+                else:
+                    break
+            progress.setValue(numProtos)
+
+    def __compileSelectedProtocols(self, grpc=False):
+        """
+        Private method to compile selected protocols to Python.
+
+        @param grpc flag indicating to compile as gRPC files
+        @type bool
+        """
+        if self.__getCompilerCommand(grpc)[0] is not None:
+            items = self.getSelectedItems()
+
+            files = [self.project.getRelativePath(itm.fileName()) for itm in items]
+            numProtos = len(files)
+            progress = EricProgressDialog(
+                self.tr("Compiling Protocols..."),
+                self.tr("Abort"),
+                0,
+                numProtos,
+                self.tr("%v/%m Protocols"),
+                self,
+            )
+            progress.setModal(True)
+            progress.setMinimumDuration(0)
+            progress.setWindowTitle(self.tr("Protocols"))
+
+            for prog, fn in enumerate(files):
+                progress.setValue(prog)
+                if progress.wasCanceled():
+                    break
+                proc = self.__compileProto(fn, True, progress, grpc=grpc)
+                if proc is not None:
+                    while proc.state() == QProcess.ProcessState.Running:
+                        QThread.msleep(100)
+                        QApplication.processEvents()
+                else:
+                    break
+            progress.setValue(numProtos)
+
+    def __configureProtobuf(self):
+        """
+        Private method to open the configuration dialog.
+        """
+        ericApp().getObject("UserInterface").showPreferences("protobufPage")

eric ide

mercurial