src/eric7/SystemUtilities/QtUtilities.py

branch
eric7
changeset 9624
b47dfa7a137d
child 9625
2c760cdc6b64
equal deleted inserted replaced
9623:9c1f429cb56b 9624:b47dfa7a137d
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing Qt/PyQt/PySide related utility functions.
8 """
9
10 import contextlib
11 import functools
12 import os
13 import sys
14 import sysconfig
15
16 from PyQt6.QtCore import QT_VERSION, QDir, QLibraryInfo, QProcess
17
18 from eric7.EricWidgets.EricApplication import ericApp
19 from eric7.SystemUtilities import FileSystemUtilities, OSUtilities, PythonUtilities
20
21 try:
22 from eric7.eric7config import getConfig
23 except ImportError:
24 from eric7config import getConfig
25
26 ###############################################################################
27 ## Qt utility functions below
28 ###############################################################################
29
30
31 def qVersionTuple():
32 """
33 Module function to get the Qt version as a tuple.
34
35 @return Qt version as a tuple
36 @rtype tuple of int
37 """
38 return (
39 (QT_VERSION & 0xFF0000) >> 16,
40 (QT_VERSION & 0xFF00) >> 8,
41 QT_VERSION & 0xFF,
42 )
43
44
45 def generateQtToolName(toolname):
46 """
47 Module function to generate the executable name for a Qt tool like
48 designer.
49
50 @param toolname base name of the tool (string)
51 @return the Qt tool name without extension (string)
52 """
53 from eric7 import Preferences
54
55 return "{0}{1}{2}".format(
56 Preferences.getQt("QtToolsPrefix"),
57 toolname,
58 Preferences.getQt("QtToolsPostfix"),
59 )
60
61
62 def getQtBinariesPath(libexec=False):
63 """
64 Module function to get the path of the Qt binaries.
65
66 @param libexec flag indicating to get the path of the executable library
67 (defaults to False)
68 @type bool (optional)
69 @return path of the Qt binaries
70 @rtype str
71 """
72 from eric7 import Preferences
73
74 binPath = ""
75
76 # step 1: check, if the user has configured a tools path
77 qtToolsDir = Preferences.getQt("QtToolsDir")
78 if qtToolsDir:
79 if libexec:
80 binPath = os.path.join(qtToolsDir, "..", "libexec")
81 if not os.path.exists(binPath):
82 binPath = qtToolsDir
83 else:
84 binPath = Preferences.getQt("QtToolsDir")
85 if not os.path.exists(binPath):
86 binPath = ""
87
88 # step 2: try the qt6_applications package
89 if not binPath:
90 with contextlib.suppress(ImportError):
91 # if qt6-applications is not installed just go to the next step
92 import qt6_applications # __IGNORE_WARNING_I10__
93
94 if libexec:
95 binPath = os.path.join(
96 os.path.dirname(qt6_applications.__file__), "Qt", "libexec"
97 )
98 if not os.path.exists(binPath):
99 binPath = os.path.join(
100 os.path.dirname(qt6_applications.__file__), "Qt", "bin"
101 )
102 else:
103 binPath = os.path.join(
104 os.path.dirname(qt6_applications.__file__), "Qt", "bin"
105 )
106 if not os.path.exists(binPath):
107 binPath = ""
108
109 # step3: determine via QLibraryInfo
110 if not binPath:
111 binPath = (
112 QLibraryInfo.path(QLibraryInfo.LibraryPath.LibraryExecutablesPath)
113 if libexec
114 else QLibraryInfo.path(QLibraryInfo.LibraryPath.BinariesPath)
115 )
116
117 # step 4: determine from used Python interpreter (designer is test object)
118 if not binPath:
119 program = "designer"
120 if OSUtilities.isWindowsPlatform():
121 program += ".exe"
122
123 progPath = os.path.join(PythonUtilities.getPythonScriptsDirectory(), program)
124 if os.path.exists(progPath):
125 binPath = PythonUtilities.getPythonScriptsDirectory()
126
127 return QDir.toNativeSeparators(binPath)
128
129
130 def getQtMacBundle(toolname):
131 """
132 Module function to determine the correct Mac OS X bundle name for Qt tools.
133
134 @param toolname plain name of the tool (e.g. "designer") (string)
135 @return bundle name of the Qt tool (string)
136 """
137 qtDir = getQtBinariesPath()
138 bundles = [
139 os.path.join(qtDir, "bin", generateQtToolName(toolname.capitalize())) + ".app",
140 os.path.join(qtDir, "bin", generateQtToolName(toolname)) + ".app",
141 os.path.join(qtDir, generateQtToolName(toolname.capitalize())) + ".app",
142 os.path.join(qtDir, generateQtToolName(toolname)) + ".app",
143 ]
144 if toolname == "designer":
145 # support the standalone Qt Designer installer from
146 # https://build-system.fman.io/qt-designer-download
147 designer = "Qt Designer.app"
148 bundles.extend(
149 [
150 os.path.join(qtDir, "bin", designer),
151 os.path.join(qtDir, designer),
152 ]
153 )
154 for bundle in bundles:
155 if os.path.exists(bundle):
156 return bundle
157 return ""
158
159
160 def prepareQtMacBundle(toolname, args):
161 """
162 Module function for starting Qt tools that are Mac OS X bundles.
163
164 @param toolname plain name of the tool (e.g. "designer")
165 @type str
166 @param args name of input file for tool, if any
167 @type list of str
168 @return command-name and args for QProcess
169 @rtype tuple of (str, list of str)
170 """
171 fullBundle = getQtMacBundle(toolname)
172 if fullBundle == "":
173 return ("", [])
174
175 newArgs = []
176 newArgs.append("-a")
177 newArgs.append(fullBundle)
178 if args:
179 newArgs.append("--args")
180 newArgs += args
181
182 return ("open", newArgs)
183
184
185 ###############################################################################
186 ## PyQt utility functions below
187 ###############################################################################
188
189
190 def getPyQt6ModulesDirectory():
191 """
192 Function to determine the path to PyQt6 modules directory.
193
194 @return path to the PyQt6 modules directory
195 @rtype str
196 """
197 pyqtPath = os.path.join(sysconfig.get_path("platlib"), "PyQt6")
198 if os.path.exists(pyqtPath):
199 return pyqtPath
200
201 return ""
202
203
204 def getPyQtToolsPath(version=5):
205 """
206 Module function to get the path of the PyQt tools.
207
208 @param version PyQt major version
209 @type int
210 @return path to the PyQt tools
211 @rtype str
212 """
213 from eric7 import Preferences
214 from eric7.EricWidgets.EricApplication import ericApp
215
216 toolsPath = ""
217
218 # step 1: check, if the user has configured a tools path
219 if version == 5:
220 toolsPath = Preferences.getQt("PyQtToolsDir")
221 venvName = Preferences.getQt("PyQtVenvName")
222 elif version == 6:
223 toolsPath = Preferences.getQt("PyQt6ToolsDir")
224 venvName = Preferences.getQt("PyQt6VenvName")
225
226 # step 2: determine from used Python interpreter (pylupdate is test object)
227 if not toolsPath:
228 program = "pylupdate{0}".format(version)
229 if venvName:
230 venvManager = ericApp().getObject("VirtualEnvManager")
231 dirName = venvManager.getVirtualenvDirectory(venvName)
232 else:
233 dirName = os.path.dirname(sys.executable)
234
235 if OSUtilities.isWindowsPlatform():
236 program += ".exe"
237 if os.path.exists(os.path.join(dirName, program)):
238 toolsPath = dirName
239 elif os.path.exists(os.path.join(dirName, "Scripts", program)):
240 toolsPath = os.path.join(dirName, "Scripts")
241 else:
242 if os.path.exists(os.path.join(dirName, program)):
243 toolsPath = dirName
244 elif os.path.exists(os.path.join(dirName, "bin", program)):
245 toolsPath = os.path.join(dirName, "bin")
246
247 return toolsPath
248
249
250 def generatePyQtToolPath(toolname, alternatives=None):
251 """
252 Module function to generate the executable path for a PyQt tool.
253
254 @param toolname base name of the tool
255 @type str
256 @param alternatives list of alternative tool names to try
257 @type list of str
258 @return executable path name of the tool
259 @rtype str
260 """
261 pyqtVariant = int(toolname[-1])
262 pyqtToolsPath = getPyQtToolsPath(pyqtVariant)
263 if pyqtToolsPath:
264 exe = os.path.join(pyqtToolsPath, toolname)
265 if OSUtilities.isWindowsPlatform():
266 exe += ".exe"
267 else:
268 if OSUtilities.isWindowsPlatform():
269 exe = OSUtilities.getWindowsExecutablePath(toolname)
270 else:
271 exe = toolname
272
273 if not FileSystemUtilities.isinpath(exe) and alternatives:
274 ex_ = generatePyQtToolPath(alternatives[0], alternatives[1:])
275 if FileSystemUtilities.isinpath(ex_):
276 exe = ex_
277
278 return exe
279
280
281 ###############################################################################
282 ## PySide2/PySide6 utility functions below
283 ###############################################################################
284
285
286 def generatePySideToolPath(toolname, variant=2):
287 """
288 Module function to generate the executable path for a PySide2/PySide6 tool.
289
290 @param toolname base name of the tool
291 @type str
292 @param variant indicator for the PySide variant
293 @type int or str
294 @return the PySide2/PySide6 tool path with extension
295 @rtype str
296 """
297 from eric7 import Preferences
298
299 if OSUtilities.isWindowsPlatform():
300 hasPyside = checkPyside(variant)
301 if not hasPyside:
302 return ""
303
304 venvName = Preferences.getQt("PySide{0}VenvName".format(variant))
305 if not venvName:
306 venvName = Preferences.getDebugger("Python3VirtualEnv")
307 interpreter = (
308 ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(venvName)
309 )
310 if interpreter == "" or not FileSystemUtilities.isinpath(interpreter):
311 interpreter = PythonUtilities.getPythonExecutable()
312 prefix = os.path.dirname(interpreter)
313 if not prefix.endswith("Scripts"):
314 prefix = os.path.join(prefix, "Scripts")
315 return os.path.join(prefix, toolname + ".exe")
316 else:
317 # step 1: check, if the user has configured a tools path
318 path = Preferences.getQt("PySide{0}ToolsDir".format(variant))
319 if path:
320 return os.path.join(path, toolname)
321
322 # step 2: determine from used Python interpreter
323 dirName = os.path.dirname(sys.executable)
324 if os.path.exists(os.path.join(dirName, toolname)):
325 return os.path.join(dirName, toolname)
326
327 return toolname
328
329
330 @functools.lru_cache()
331 def checkPyside(variant=2):
332 """
333 Module function to check the presence of PySide2/PySide6.
334
335 @param variant indicator for the PySide variant
336 @type int or str
337 @return flags indicating the presence of PySide2/PySide6
338 @rtype bool
339 """
340 from eric7 import Preferences
341
342 venvName = Preferences.getQt("PySide{0}VenvName".format(variant))
343 if not venvName:
344 venvName = Preferences.getDebugger("Python3VirtualEnv")
345 interpreter = (
346 ericApp().getObject("VirtualEnvManager").getVirtualenvInterpreter(venvName)
347 )
348 if interpreter == "" or not FileSystemUtilities.isinpath(interpreter):
349 interpreter = PythonUtilities.getPythonExecutable()
350
351 checker = os.path.join(getConfig("ericDir"), "SystemUtilities", "PySideImporter.py")
352 args = [checker, "--variant={0}".format(variant)]
353 proc = QProcess()
354 proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels)
355 proc.start(interpreter, args)
356 finished = proc.waitForFinished(30000)
357 return finished and proc.exitCode() == 0

eric ide

mercurial