src/eric7/SystemUtilities/QtUtilities.py

branch
eric7
changeset 9624
b47dfa7a137d
child 9625
2c760cdc6b64
diff -r 9c1f429cb56b -r b47dfa7a137d src/eric7/SystemUtilities/QtUtilities.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/SystemUtilities/QtUtilities.py	Sun Dec 18 19:33:46 2022 +0100
@@ -0,0 +1,357 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing Qt/PyQt/PySide related utility functions.
+"""
+
+import contextlib
+import functools
+import os
+import sys
+import sysconfig
+
+from PyQt6.QtCore import QT_VERSION, QDir, QLibraryInfo, QProcess
+
+from eric7.EricWidgets.EricApplication import ericApp
+from eric7.SystemUtilities import FileSystemUtilities, OSUtilities, PythonUtilities
+
+try:
+    from eric7.eric7config import getConfig
+except ImportError:
+    from eric7config import getConfig
+
+###############################################################################
+## Qt utility functions below
+###############################################################################
+
+
+def qVersionTuple():
+    """
+    Module function to get the Qt version as a tuple.
+
+    @return Qt version as a tuple
+    @rtype tuple of int
+    """
+    return (
+        (QT_VERSION & 0xFF0000) >> 16,
+        (QT_VERSION & 0xFF00) >> 8,
+        QT_VERSION & 0xFF,
+    )
+
+
+def generateQtToolName(toolname):
+    """
+    Module function to generate the executable name for a Qt tool like
+    designer.
+
+    @param toolname base name of the tool (string)
+    @return the Qt tool name without extension (string)
+    """
+    from eric7 import Preferences
+
+    return "{0}{1}{2}".format(
+        Preferences.getQt("QtToolsPrefix"),
+        toolname,
+        Preferences.getQt("QtToolsPostfix"),
+    )
+
+
+def getQtBinariesPath(libexec=False):
+    """
+    Module function to get the path of the Qt binaries.
+
+    @param libexec flag indicating to get the path of the executable library
+        (defaults to False)
+    @type bool (optional)
+    @return path of the Qt binaries
+    @rtype str
+    """
+    from eric7 import Preferences
+
+    binPath = ""
+
+    # step 1: check, if the user has configured a tools path
+    qtToolsDir = Preferences.getQt("QtToolsDir")
+    if qtToolsDir:
+        if libexec:
+            binPath = os.path.join(qtToolsDir, "..", "libexec")
+            if not os.path.exists(binPath):
+                binPath = qtToolsDir
+        else:
+            binPath = Preferences.getQt("QtToolsDir")
+        if not os.path.exists(binPath):
+            binPath = ""
+
+    # step 2: try the qt6_applications package
+    if not binPath:
+        with contextlib.suppress(ImportError):
+            # if qt6-applications is not installed just go to the next step
+            import qt6_applications  # __IGNORE_WARNING_I10__
+
+            if libexec:
+                binPath = os.path.join(
+                    os.path.dirname(qt6_applications.__file__), "Qt", "libexec"
+                )
+                if not os.path.exists(binPath):
+                    binPath = os.path.join(
+                        os.path.dirname(qt6_applications.__file__), "Qt", "bin"
+                    )
+            else:
+                binPath = os.path.join(
+                    os.path.dirname(qt6_applications.__file__), "Qt", "bin"
+                )
+            if not os.path.exists(binPath):
+                binPath = ""
+
+    # step3: determine via QLibraryInfo
+    if not binPath:
+        binPath = (
+            QLibraryInfo.path(QLibraryInfo.LibraryPath.LibraryExecutablesPath)
+            if libexec
+            else QLibraryInfo.path(QLibraryInfo.LibraryPath.BinariesPath)
+        )
+
+    # step 4: determine from used Python interpreter (designer is test object)
+    if not binPath:
+        program = "designer"
+        if OSUtilities.isWindowsPlatform():
+            program += ".exe"
+
+        progPath = os.path.join(PythonUtilities.getPythonScriptsDirectory(), program)
+        if os.path.exists(progPath):
+            binPath = PythonUtilities.getPythonScriptsDirectory()
+
+    return QDir.toNativeSeparators(binPath)
+
+
+def getQtMacBundle(toolname):
+    """
+    Module function to determine the correct Mac OS X bundle name for Qt tools.
+
+    @param toolname  plain name of the tool (e.g. "designer") (string)
+    @return bundle name of the Qt tool (string)
+    """
+    qtDir = getQtBinariesPath()
+    bundles = [
+        os.path.join(qtDir, "bin", generateQtToolName(toolname.capitalize())) + ".app",
+        os.path.join(qtDir, "bin", generateQtToolName(toolname)) + ".app",
+        os.path.join(qtDir, generateQtToolName(toolname.capitalize())) + ".app",
+        os.path.join(qtDir, generateQtToolName(toolname)) + ".app",
+    ]
+    if toolname == "designer":
+        # support the standalone Qt Designer installer from
+        # https://build-system.fman.io/qt-designer-download
+        designer = "Qt Designer.app"
+        bundles.extend(
+            [
+                os.path.join(qtDir, "bin", designer),
+                os.path.join(qtDir, designer),
+            ]
+        )
+    for bundle in bundles:
+        if os.path.exists(bundle):
+            return bundle
+    return ""
+
+
+def prepareQtMacBundle(toolname, args):
+    """
+    Module function for starting Qt tools that are Mac OS X bundles.
+
+    @param toolname  plain name of the tool (e.g. "designer")
+    @type str
+    @param args    name of input file for tool, if any
+    @type list of str
+    @return command-name and args for QProcess
+    @rtype tuple of (str, list of str)
+    """
+    fullBundle = getQtMacBundle(toolname)
+    if fullBundle == "":
+        return ("", [])
+
+    newArgs = []
+    newArgs.append("-a")
+    newArgs.append(fullBundle)
+    if args:
+        newArgs.append("--args")
+        newArgs += args
+
+    return ("open", newArgs)
+
+
+###############################################################################
+## PyQt utility functions below
+###############################################################################
+
+
+def getPyQt6ModulesDirectory():
+    """
+    Function to determine the path to PyQt6 modules directory.
+
+    @return path to the PyQt6 modules directory
+    @rtype str
+    """
+    pyqtPath = os.path.join(sysconfig.get_path("platlib"), "PyQt6")
+    if os.path.exists(pyqtPath):
+        return pyqtPath
+
+    return ""
+
+
+def getPyQtToolsPath(version=5):
+    """
+    Module function to get the path of the PyQt tools.
+
+    @param version PyQt major version
+    @type int
+    @return path to the PyQt tools
+    @rtype str
+    """
+    from eric7 import Preferences
+    from eric7.EricWidgets.EricApplication import ericApp
+
+    toolsPath = ""
+
+    # step 1: check, if the user has configured a tools path
+    if version == 5:
+        toolsPath = Preferences.getQt("PyQtToolsDir")
+        venvName = Preferences.getQt("PyQtVenvName")
+    elif version == 6:
+        toolsPath = Preferences.getQt("PyQt6ToolsDir")
+        venvName = Preferences.getQt("PyQt6VenvName")
+
+    # step 2: determine from used Python interpreter (pylupdate is test object)
+    if not toolsPath:
+        program = "pylupdate{0}".format(version)
+        if venvName:
+            venvManager = ericApp().getObject("VirtualEnvManager")
+            dirName = venvManager.getVirtualenvDirectory(venvName)
+        else:
+            dirName = os.path.dirname(sys.executable)
+
+        if OSUtilities.isWindowsPlatform():
+            program += ".exe"
+            if os.path.exists(os.path.join(dirName, program)):
+                toolsPath = dirName
+            elif os.path.exists(os.path.join(dirName, "Scripts", program)):
+                toolsPath = os.path.join(dirName, "Scripts")
+        else:
+            if os.path.exists(os.path.join(dirName, program)):
+                toolsPath = dirName
+            elif os.path.exists(os.path.join(dirName, "bin", program)):
+                toolsPath = os.path.join(dirName, "bin")
+
+    return toolsPath
+
+
+def generatePyQtToolPath(toolname, alternatives=None):
+    """
+    Module function to generate the executable path for a PyQt tool.
+
+    @param toolname base name of the tool
+    @type str
+    @param alternatives list of alternative tool names to try
+    @type list of str
+    @return executable path name of the tool
+    @rtype str
+    """
+    pyqtVariant = int(toolname[-1])
+    pyqtToolsPath = getPyQtToolsPath(pyqtVariant)
+    if pyqtToolsPath:
+        exe = os.path.join(pyqtToolsPath, toolname)
+        if OSUtilities.isWindowsPlatform():
+            exe += ".exe"
+    else:
+        if OSUtilities.isWindowsPlatform():
+            exe = OSUtilities.getWindowsExecutablePath(toolname)
+        else:
+            exe = toolname
+
+    if not FileSystemUtilities.isinpath(exe) and alternatives:
+        ex_ = generatePyQtToolPath(alternatives[0], alternatives[1:])
+        if FileSystemUtilities.isinpath(ex_):
+            exe = ex_
+
+    return exe
+
+
+###############################################################################
+## PySide2/PySide6 utility functions below
+###############################################################################
+
+
+def generatePySideToolPath(toolname, variant=2):
+    """
+    Module function to generate the executable path for a PySide2/PySide6 tool.
+
+    @param toolname base name of the tool
+    @type str
+    @param variant indicator for the PySide variant
+    @type int or str
+    @return the PySide2/PySide6 tool path with extension
+    @rtype str
+    """
+    from eric7 import Preferences
+
+    if OSUtilities.isWindowsPlatform():
+        hasPyside = checkPyside(variant)
+        if not hasPyside:
+            return ""
+
+        venvName = Preferences.getQt("PySide{0}VenvName".format(variant))
+        if not venvName:
+            venvName = Preferences.getDebugger("Python3VirtualEnv")
+        interpreter = (
+            ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(venvName)
+        )
+        if interpreter == "" or not FileSystemUtilities.isinpath(interpreter):
+            interpreter = PythonUtilities.getPythonExecutable()
+        prefix = os.path.dirname(interpreter)
+        if not prefix.endswith("Scripts"):
+            prefix = os.path.join(prefix, "Scripts")
+        return os.path.join(prefix, toolname + ".exe")
+    else:
+        # step 1: check, if the user has configured a tools path
+        path = Preferences.getQt("PySide{0}ToolsDir".format(variant))
+        if path:
+            return os.path.join(path, toolname)
+
+        # step 2: determine from used Python interpreter
+        dirName = os.path.dirname(sys.executable)
+        if os.path.exists(os.path.join(dirName, toolname)):
+            return os.path.join(dirName, toolname)
+
+        return toolname
+
+
+@functools.lru_cache()
+def checkPyside(variant=2):
+    """
+    Module function to check the presence of PySide2/PySide6.
+
+    @param variant indicator for the PySide variant
+    @type int or str
+    @return flags indicating the presence of PySide2/PySide6
+    @rtype bool
+    """
+    from eric7 import Preferences
+
+    venvName = Preferences.getQt("PySide{0}VenvName".format(variant))
+    if not venvName:
+        venvName = Preferences.getDebugger("Python3VirtualEnv")
+    interpreter = (
+        ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(venvName)
+    )
+    if interpreter == "" or not FileSystemUtilities.isinpath(interpreter):
+        interpreter = PythonUtilities.getPythonExecutable()
+
+    checker = os.path.join(getConfig("ericDir"), "SystemUtilities", "PySideImporter.py")
+    args = [checker, "--variant={0}".format(variant)]
+    proc = QProcess()
+    proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels)
+    proc.start(interpreter, args)
+    finished = proc.waitForFinished(30000)
+    return finished and proc.exitCode() == 0

eric ide

mercurial