_modules_info.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. # ----------------------------------------------------------------------------
  2. # Copyright (c) 2022-2023, PyInstaller Development Team.
  3. #
  4. # Distributed under the terms of the GNU General Public License (version 2
  5. # or later) with exception for distributing the bootloader.
  6. #
  7. # The full license is in the file COPYING.txt, distributed with this software.
  8. #
  9. # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
  10. #-----------------------------------------------------------------------------
  11. # Qt modules information - the core of our Qt collection approach
  12. # ----------------------------------------------------------------
  13. #
  14. # The python bindings for Qt (``PySide2``, ``PyQt5``, ``PySide6``, ``PyQt6``) consist of several python binary extension
  15. # modules that provide bindings for corresponding Qt modules. For example, the ``PySide2.QtNetwork`` python extension
  16. # module provides bindings for the ``QtNetwork`` Qt module from the ``qt/qtbase`` Qt repository.
  17. #
  18. # A Qt module can be considered as consisting of:
  19. # * a shared library (for example, on Linux, the shared library names for the ``QtNetwork`` Qt module in Qt5 and Qt6
  20. # are ``libQt5Network.so`` and ``libQt6Network.so``, respectively).
  21. # * plugins: a certain type (or class) of plugins is usually associated with a single Qt module (for example,
  22. # ``imageformats`` plugins are associated with the ``QtGui`` Qt module from the ``qt/qtbase`` Qt repository), but
  23. # additional plugins of that type may come from other Qt repositories. For example, ``imageformats/qsvg`` plugin
  24. # is provided by ``qtsvg/src/plugins/imageformats/svg`` from the ``qt/qtsvg`` repository, and ``imageformats/qpdf``
  25. # is provided by ``qtwebengine/src/pdf/plugins/imageformats/pdf`` from the ``qt/qtwebengine`` repository.
  26. # * translation files: names of translation files consist of a base name, which typically corresponds to the Qt
  27. # repository name, and language code. A single translation file usually covers all Qt modules contained within
  28. # the same repository. For example, translation files with base name ``qtbase`` contain translations for ``QtCore``,
  29. # ``QtGui``, ``QtWidgets``, ``QtNetwork``, and other Qt modules from the ``qt/qtbase`` Qt repository.
  30. #
  31. # The PyInstaller's built-in analysis of link-time dependencies ensures that when collecting a Qt python extension
  32. # module, we automatically pick up the linked Qt shared libraries. However, collection of linked Qt shared libraries
  33. # does not result in collection of plugins, nor translation files. In addition, the dependency of a Qt python extension
  34. # module on other Qt python extension modules (i.e., at the bindings level) cannot be automatically determined due to
  35. # PyInstaller's inability to scan imports in binary extensions.
  36. #
  37. # PyInstaller < 5.7 solved this problem using a dictionary that associated a Qt shared library name with python
  38. # extension name, plugins, and translation files. For each hooked Qt python extension module, the hook calls a helper
  39. # that analyzes the extension file for link-time dependencies, and matches those against the dictionary. Therefore,
  40. # based on linked shared libraries, we could recursively infer the list of files to collect in addition to the shared
  41. # libraries themselves:
  42. # - plugins and translation files belonging to Qt modules whose shared libraries we collect
  43. # - Qt python extension modules corresponding to the Qt modules that we collect
  44. #
  45. # The above approach ensures that even if analyzed python script contains only ``from PySide2 import QtWidgets``,
  46. # we would also collect ``PySide2.QtGui`` and ``PySide2.QtCore``, as well as all corresponding Qt module files
  47. # (the shared libraries, plugins, translation files). For this to work, a hook must be provided for the
  48. # ``PySide2.QtWidgets`` that performs the recursive analysis of the extension module file; so to ensure that each
  49. # Qt python extension module by itself ensures collection of all its dependencies, we need to hook all Qt python
  50. # extension modules provided by specific python Qt bindings package.
  51. #
  52. # The above approach with single dictionary, however, has several limitations:
  53. # - it cannot provide association for Qt python module that binds a Qt module without a shared library (i.e., a
  54. # headers-only module, or a statically-built module). In such cases, potential plugins and translations should
  55. # be associated directly with the Qt python extension file instead of the Qt module's (non-existent) shared library.
  56. # - it cannot (directly) handle differences between Qt5 and Qt6; we had to build a second dictionary
  57. # - it cannot handle differences between the bindings themselves; for example, PyQt5 binds some Qt modules that
  58. # PySide2 does not bind. Or, the binding's Qt python extension module is named differently in PyQt and PySide
  59. # bindings (or just differently in PyQt5, while PySide2, PySide6, and PyQt6 use the same name).
  60. #
  61. # In order address the above shortcomings, we now store all information a list of structures that contain information
  62. # for a particular Qt python extension and/or Qt module (shared library):
  63. # - python extension name (if applicable)
  64. # - Qt module name base (if applicable)
  65. # - plugins
  66. # - translation files base name
  67. # - applicable Qt version (if necessary)
  68. # - applicable Qt bindings (if necessary)
  69. #
  70. # This list is used to dynamically construct two dictionaries (based on the bindings name and Qt version):
  71. # - mapping python extension names to associated module information
  72. # - mapping Qt shared library names to associated module information
  73. # This allows us to associate plugins and translations with either Qt python extension or with the Qt module's shared
  74. # library (or both), whichever is applicable.
  75. #
  76. # The `qt_dynamic_dependencies_dict`_ from the original approach was constructed using several information sources, as
  77. # documented `here
  78. # <https://github.com/pyinstaller/pyinstaller/blob/fbf7948be85177dd44b41217e9f039e1d176de6b/PyInstaller/utils/hooks/qt.py#L266-L362>`_.
  79. #
  80. # In the current approach, the relations stored in the `QT_MODULES_INFO`_ list were determined directly, by inspecting
  81. # the Qt source code. This requires some prior knowledge of how the Qt code is organized (repositories and individual Qt
  82. # modules within them), as well as some searching based on guesswork. The procedure can be outlined as follows:
  83. # * check out the `main Qt repository <git://code.qt.io/qt/qt5.git>`_. This repository contains references to all other
  84. # Qt repositories in the form of git submodules.
  85. # * for Qt5:
  86. # * check out the latest release tag, e.g., v5.15.2, then check out the submodules.
  87. # * search the Qt modules' qmake .pro files; for example, ``qtbase/src/network/network.pro`` for QtNetwork module.
  88. # The plugin types associated with the module are listed in the ``MODULE_PLUGIN_TYPES`` variable (in this case,
  89. # ``bearer``).
  90. # * all translations are gathered in ``qttranslations`` sub-module/repository, and their association with
  91. # individual repositories can be seen in ``qttranslations/translations/translations.pro``.
  92. # * for Qt6:
  93. # * check out the latest release tag, e.g., v6.3.1, then check out the submodules.
  94. # * search the Qt modules' CMake files; for example, ``qtbase/src/network/CMakeLists.txt`` for QtNetwork module.
  95. # The plugin types associated with the module are listed under ``PLUGIN_TYPES`` argument of the
  96. # ``qt_internal_add_module()`` function that defines the Qt module.
  97. #
  98. # The idea is to make a list of all extension modules found in a Qt bindings package, as well as all available plugin
  99. # directories (which correspond to plugin types) and translation files. For each extension, identify the corresponding
  100. # Qt module (shared library name) and its associated plugins and translation files. Once this is done, most of available
  101. # plugins and translations in the python bindings package should have a corresponding python Qt extension module
  102. # available; this gives us associations based on the python extension module names as well as based on the Qt shared
  103. # library names. For any plugins and translation files remaining unassociated, identify the corresponding Qt module;
  104. # this gives us associations based only on Qt shared library names. While this second group of associations are never
  105. # processed directly (due to lack of corresponding python extension), they may end up being processed during the
  106. # recursive dependency analysis, if the corresponding Qt shared library is linked against by some Qt python extension
  107. # or another Qt shared library.
  108. # This structure is used to define Qt module information, such as python module/extension name, Qt module (shared
  109. # library) name, translation files' base names, plugins, as well as associated python bindings (which implicitly
  110. # also encode major Qt version).
  111. class _QtModuleDef:
  112. def __init__(self, module, shared_lib=None, translations=None, plugins=None, bindings=None):
  113. # Python module (extension) name without package namespace. For example, `QtCore`.
  114. # Can be None if python bindings do not bind the module, but we still need to establish relationship between
  115. # the Qt module (shared library) and its plugins and translations.
  116. self.module = module
  117. # Associated Qt module (shared library), if any. Used during recursive dependency analysis, where a python
  118. # module (extension) is analyzed for linked Qt modules (shared libraries), and then their corresponding
  119. # python modules (extensions) are added to hidden imports. For example, the Qt module name is `Qt5Core` or
  120. # `Qt6Core`, depending on the Qt version. Can be None for python modules that are not tied to a particular
  121. # Qt shared library (for example, the corresponding Qt module is headers-only) and hence they cannot be
  122. # inferred from recursive link-time dependency analysis.
  123. self.shared_lib = shared_lib
  124. # List of base names of translation files (if any) associated with the Qt module. Multiple base names may be
  125. # associated with a single module.
  126. # For example, `['qt', 'qtbase']` for `QtCore` or `['qtmultimedia']` for `QtMultimedia`.
  127. self.translations = translations or []
  128. # List of plugins associated with the Qt module.
  129. self.plugins = plugins or []
  130. # List of bindings (PySide2, PyQt5, PySide6, PyQt6) that provide the python module. This allows association of
  131. # plugins and translations with shared libraries even for bindings that do not provide python module binding
  132. # for the Qt module.
  133. self.bindings = set(bindings or [])
  134. # All Qt-based bindings.
  135. ALL_QT_BINDINGS = {"PySide2", "PyQt5", "PySide6", "PyQt6"}
  136. # Qt modules information - the core of our Qt collection approach.
  137. #
  138. # For every python module/extension (i.e., entry in the list below that has valid `module`), we need a corresponding
  139. # hook, ensuring that the extension file is analyzed, so that we collect the associated plugins and translation
  140. # files, as well as perform recursive analysis of link-time binary dependencies (so that plugins and translation files
  141. # belonging to those dependencies are collected as well).
  142. QT_MODULES_INFO = (
  143. # *** qt/qt3d ***
  144. _QtModuleDef("Qt3DAnimation", shared_lib="3DAnimation"),
  145. _QtModuleDef("Qt3DCore", shared_lib="3DCore"),
  146. _QtModuleDef("Qt3DExtras", shared_lib="3DExtras"),
  147. _QtModuleDef("Qt3DInput", shared_lib="3DInput", plugins=["3dinputdevices"]),
  148. _QtModuleDef("Qt3DLogic", shared_lib="3DLogic"),
  149. _QtModuleDef(
  150. "Qt3DRender", shared_lib="3DRender", plugins=["geometryloaders", "renderplugins", "renderers", "sceneparsers"]
  151. ),
  152. # *** qt/qtactiveqt ***
  153. # The python module is called QAxContainer in PyQt bindings, but QtAxContainer in PySide. The associated Qt module
  154. # is header-only, so there is no shared library.
  155. _QtModuleDef("QAxContainer", bindings=["PyQt*"]),
  156. _QtModuleDef("QtAxContainer", bindings=["PySide*"]),
  157. # *** qt/qtcharts ***
  158. # The python module is called QtChart in PyQt5, and QtCharts in PySide2, PySide6, and PyQt6 (which corresponds to
  159. # the associated Qt module name, QtCharts).
  160. _QtModuleDef("QtChart", shared_lib="Charts", bindings=["PyQt5"]),
  161. _QtModuleDef("QtCharts", shared_lib="Charts", bindings=["!PyQt5"]),
  162. # *** qt/qtbase ***
  163. # QtConcurrent python module is available only in PySide bindings.
  164. _QtModuleDef(None, shared_lib="Concurrent", bindings=["PyQt*"]),
  165. _QtModuleDef("QtConcurrent", shared_lib="Concurrent", bindings=["PySide*"]),
  166. _QtModuleDef("QtCore", shared_lib="Core", translations=["qt", "qtbase"]),
  167. # QtDBus python module is available in all bindings but PySide2.
  168. _QtModuleDef(None, shared_lib="DBus", bindings=["PySide2"]),
  169. _QtModuleDef("QtDBus", shared_lib="DBus", bindings=["!PySide2"]),
  170. # QtNetwork uses different plugins in Qt5 and Qt6.
  171. _QtModuleDef("QtNetwork", shared_lib="Network", plugins=["bearer"], bindings=["PySide2", "PyQt5"]),
  172. _QtModuleDef(
  173. "QtNetwork",
  174. shared_lib="Network",
  175. plugins=["networkaccess", "networkinformation", "tls"],
  176. bindings=["PySide6", "PyQt6"]
  177. ),
  178. _QtModuleDef(
  179. "QtGui",
  180. shared_lib="Gui",
  181. plugins=[
  182. "accessiblebridge",
  183. "egldeviceintegrations",
  184. "generic",
  185. "iconengines",
  186. "imageformats",
  187. "platforms",
  188. "platforms/darwin",
  189. "platforminputcontexts",
  190. "platformthemes",
  191. "xcbglintegrations",
  192. # The ``wayland-*`` plugins are part of QtWaylandClient Qt module, whose shared library
  193. # (e.g., libQt5WaylandClient.so) is linked by the wayland-related ``platforms`` plugins. Ideally, we would
  194. # collect these plugins based on the QtWaylandClient shared library entry, but as our Qt hook utilities do
  195. # not scan the plugins for dependencies, that would not work. So instead we list these plugins under QtGui
  196. # to achieve pretty much the same end result.
  197. "wayland-decoration-client",
  198. "wayland-graphics-integration-client",
  199. "wayland-shell-integration"
  200. ]
  201. ),
  202. _QtModuleDef("QtOpenGL", shared_lib="OpenGL"),
  203. # This python module is specific to PySide2 and has no associated Qt module.
  204. _QtModuleDef("QtOpenGLFunctions", bindings=["PySide2"]),
  205. # This Qt module was introduced with Qt6.
  206. _QtModuleDef("QtOpenGLWidgets", shared_lib="OpenGLWidgets", bindings=["PySide6", "PyQt6"]),
  207. _QtModuleDef("QtPrintSupport", shared_lib="PrintSupport", plugins=["printsupport"]),
  208. _QtModuleDef("QtSql", shared_lib="Sql", plugins=["sqldrivers"]),
  209. _QtModuleDef("QtTest", shared_lib="Test"),
  210. _QtModuleDef("QtWidgets", shared_lib="Widgets", plugins=["styles"]),
  211. _QtModuleDef("QtXml", shared_lib="Xml"),
  212. # *** qt/qtconnectivity ***
  213. _QtModuleDef("QtBluetooth", shared_lib="QtBluetooth", translations=["qtconnectivity"]),
  214. _QtModuleDef("QtNfc", shared_lib="Nfc", translations=["qtconnectivity"]),
  215. # *** qt/qtdatavis3d ***
  216. _QtModuleDef("QtDataVisualization", shared_lib="DataVisualization"),
  217. # *** qt/qtdeclarative ***
  218. _QtModuleDef("QtQml", shared_lib="Qml", translations=["qtdeclarative"], plugins=["qmltooling"]),
  219. # Have the Qt5 variant collect translations for qtquickcontrols (qt/qtquickcontrols provides only QtQuick plugins).
  220. _QtModuleDef(
  221. "QtQuick",
  222. shared_lib="Quick",
  223. translations=["qtquickcontrols"],
  224. plugins=["scenegraph"],
  225. bindings=["PySide2", "PyQt5"]
  226. ),
  227. _QtModuleDef("QtQuick", shared_lib="Quick", plugins=["scenegraph"], bindings=["PySide6", "PyQt6"]),
  228. # Qt6-only; in Qt5, this module is part of qt/qtquickcontrols2. Python module is available only in PySide6.
  229. _QtModuleDef(None, shared_lib="QuickControls2", bindings=["PyQt6"]),
  230. _QtModuleDef("QtQuickControls2", shared_lib="QuickControls2", bindings=["PySide6"]),
  231. _QtModuleDef("QtQuickWidgets", shared_lib="QuickWidgets"),
  232. # *** qt/qtgamepad ***
  233. # No python module; shared library -> plugins association entry.
  234. _QtModuleDef(None, shared_lib="Gamepad", plugins=["gamepads"]),
  235. # *** qt/qtgraphs ***
  236. # Qt6 >= 6.6.0; python module is available only in PySide6.
  237. _QtModuleDef("QtGraphs", shared_lib="Graphs", bindings=["PySide6"]),
  238. # *** qt/qthttpserver ***
  239. # Qt6 >= 6.4.0; python module is available only in PySide6.
  240. _QtModuleDef("QtHttpServer", shared_lib="HttpServer", bindings=["PySide6"]),
  241. # *** qt/qtlocation ***
  242. # QtLocation was reintroduced in Qt6 v6.5.0.
  243. _QtModuleDef(
  244. "QtLocation",
  245. shared_lib="Location",
  246. translations=["qtlocation"],
  247. plugins=["geoservices"],
  248. bindings=["PySide2", "PyQt5", "PySide6"]
  249. ),
  250. _QtModuleDef(
  251. "QtPositioning",
  252. shared_lib="Positioning",
  253. translations=["qtlocation"],
  254. plugins=["position"],
  255. ),
  256. # *** qt/qtmacextras ***
  257. # Qt5-only Qt module.
  258. _QtModuleDef("QtMacExtras", shared_lib="MacExtras", bindings=["PySide2", "PyQt5"]),
  259. # *** qt/qtmultimedia ***
  260. # QtMultimedia on Qt6 currently uses only a subset of plugin names from Qt5 counterpart.
  261. _QtModuleDef(
  262. "QtMultimedia",
  263. shared_lib="Multimedia",
  264. translations=["qtmultimedia"],
  265. plugins=[
  266. "mediaservice", "audio", "video/bufferpool", "video/gstvideorenderer", "video/videonode", "playlistformats",
  267. "resourcepolicy"
  268. ],
  269. bindings=["PySide2", "PyQt5"]
  270. ),
  271. _QtModuleDef(
  272. "QtMultimedia",
  273. shared_lib="Multimedia",
  274. translations=["qtmultimedia"],
  275. # `multimedia` plugins are available as of Qt6 >= 6.4.0; earlier versions had `video/gstvideorenderer` and
  276. # `video/videonode` plugins.
  277. plugins=["multimedia", "video/gstvideorenderer", "video/videonode"],
  278. bindings=["PySide6", "PyQt6"]
  279. ),
  280. _QtModuleDef("QtMultimediaWidgets", shared_lib="MultimediaWidgets"),
  281. # Qt6-only Qt module; python module is available in PySide6 >= 6.4.0 and PyQt6 >= 6.5.0
  282. _QtModuleDef("QtSpatialAudio", shared_lib="SpatialAudio", bindings=["PySide6", "PyQt6"]),
  283. # *** qt/qtnetworkauth ***
  284. # QtNetworkAuth python module is available in all bindings but PySide2.
  285. _QtModuleDef(None, shared_lib="NetworkAuth", bindings=["PySide2"]),
  286. _QtModuleDef("QtNetworkAuth", shared_lib="NetworkAuth", bindings=["!PySide2"]),
  287. # *** qt/qtpurchasing ***
  288. # Qt5-only Qt module, python module is available only in PyQt5.
  289. _QtModuleDef("QtPurchasing", shared_lib="Purchasing", bindings=["PyQt5"]),
  290. # *** qt/qtquick1 ***
  291. # This is an old, Qt 5.3-era module...
  292. _QtModuleDef(
  293. "QtDeclarative",
  294. shared_lib="Declarative",
  295. translations=["qtquick1"],
  296. plugins=["qml1tooling"],
  297. bindings=["PySide2", "PyQt5"]
  298. ),
  299. # *** qt/qtquick3d ***
  300. # QtQuick3D python module is available in all bindings but PySide2.
  301. _QtModuleDef(None, shared_lib="Quick3D", bindings=["PySide2"]),
  302. _QtModuleDef("QtQuick3D", shared_lib="Quick3D", bindings=["!PySide2"]),
  303. # No python module; shared library -> plugins association entry.
  304. _QtModuleDef(None, shared_lib="Quick3DAssetImport", plugins=["assetimporters"]),
  305. # *** qt/qtquickcontrols2 ***
  306. # Qt5-only module; in Qt6, this module is part of qt/declarative. Python module is available only in PySide2.
  307. _QtModuleDef(None, translations=["qtquickcontrols2"], shared_lib="QuickControls2", bindings=["PyQt5"]),
  308. _QtModuleDef(
  309. "QtQuickControls2", translations=["qtquickcontrols2"], shared_lib="QuickControls2", bindings=["PySide2"]
  310. ),
  311. # *** qt/qtremoteobjects ***
  312. _QtModuleDef("QtRemoteObjects", shared_lib="RemoteObjects"),
  313. # *** qt/qtscxml ***
  314. # Python module is available only in PySide bindings. Plugins are available only in Qt6.
  315. # PyQt wheels do not seem to ship the corresponding Qt modules (shared libs) at all.
  316. _QtModuleDef("QtScxml", shared_lib="Scxml", bindings=["PySide2"]),
  317. _QtModuleDef("QtScxml", shared_lib="Scxml", plugins=["scxmldatamodel"], bindings=["PySide6"]),
  318. # Qt6-only Qt module, python module is available only in PySide6.
  319. _QtModuleDef("QtStateMachine", shared_lib="StateMachine", bindings=["PySide6"]),
  320. # *** qt/qtsensors ***
  321. _QtModuleDef("QtSensors", shared_lib="Sensors", plugins=["sensors", "sensorgestures"]),
  322. # *** qt/qtserialport ***
  323. _QtModuleDef("QtSerialPort", shared_lib="SerialPort", translations=["qtserialport"]),
  324. # *** qt/qtscript ***
  325. # Qt5-only Qt module, python module is available only in PySide2. PyQt5 wheels do not seem to ship the corresponding
  326. # Qt modules (shared libs) at all.
  327. _QtModuleDef("QtScript", shared_lib="Script", translations=["qtscript"], plugins=["script"], bindings=["PySide2"]),
  328. _QtModuleDef("QtScriptTools", shared_lib="ScriptTools", bindings=["PySide2"]),
  329. # *** qt/qtserialbus ***
  330. # No python module; shared library -> plugins association entry.
  331. # PySide6 6.5.0 introduced python module.
  332. _QtModuleDef(None, shared_lib="SerialBus", plugins=["canbus"], bindings=["!PySide6"]),
  333. _QtModuleDef("QtSerialBus", shared_lib="SerialBus", plugins=["canbus"], bindings=["PySide6"]),
  334. # *** qt/qtsvg ***
  335. _QtModuleDef("QtSvg", shared_lib="Svg"),
  336. # Qt6-only Qt module.
  337. _QtModuleDef("QtSvgWidgets", shared_lib="SvgWidgets", bindings=["PySide6", "PyQt6"]),
  338. # *** qt/qtspeech ***
  339. _QtModuleDef("QtTextToSpeech", shared_lib="TextToSpeech", plugins=["texttospeech"]),
  340. # *** qt/qttools ***
  341. # QtDesigner python module is available in all bindings but PySide2.
  342. _QtModuleDef(None, shared_lib="Designer", plugins=["designer"], bindings=["PySide2"]),
  343. _QtModuleDef(
  344. "QtDesigner", shared_lib="Designer", translations=["designer"], plugins=["designer"], bindings=["!PySide2"]
  345. ),
  346. _QtModuleDef("QtHelp", shared_lib="Help", translations=["qt_help"]),
  347. # Python module is available only in PySide bindings.
  348. _QtModuleDef("QtUiTools", shared_lib="UiTools", bindings=["PySide*"]),
  349. # *** qt/qtvirtualkeyboard ***
  350. # No python module; shared library -> plugins association entry.
  351. _QtModuleDef(None, shared_lib="VirtualKeyboard", plugins=["virtualkeyboard"]),
  352. # *** qt/qtwebchannel ***
  353. _QtModuleDef("QtWebChannel", shared_lib="WebChannel"),
  354. # *** qt/qtwebengine ***
  355. # QtWebEngine is Qt5-only module (replaced by QtWebEngineQuick in Qt6).
  356. _QtModuleDef("QtWebEngine", shared_lib="WebEngine", bindings=["PySide2", "PyQt5"]),
  357. _QtModuleDef("QtWebEngineCore", shared_lib="WebEngineCore", translations=["qtwebengine"]),
  358. # QtWebEngineQuick is Qt6-only module (replacement for QtWebEngine in Qt5).
  359. _QtModuleDef("QtWebEngineQuick", shared_lib="WebEngineQuick", bindings=["PySide6", "PyQt6"]),
  360. _QtModuleDef("QtWebEngineWidgets", shared_lib="WebEngineWidgets"),
  361. # QtPdf and QtPdfWidgets have python module available in PySide6 and PyQt6 >= 6.4.0.
  362. _QtModuleDef("QtPdf", shared_lib="Pdf", bindings=["PySide6", "PyQt6"]),
  363. _QtModuleDef("QtPdfWidgets", shared_lib="PdfWidgets", bindings=["PySide6", "PyQt6"]),
  364. # *** qt/qtwebsockets ***
  365. _QtModuleDef("QtWebSockets", shared_lib="WebSockets", translations=["qtwebsockets"]),
  366. # *** qt/qtwebview ***
  367. # No python module; shared library -> plugins association entry.
  368. _QtModuleDef(None, shared_lib="WebView", plugins=["webview"]),
  369. # *** qt/qtwinextras ***
  370. # Qt5-only Qt module.
  371. _QtModuleDef("QtWinExtras", shared_lib="WinExtras", bindings=["PySide2", "PyQt5"]),
  372. # *** qt/qtx11extras ***
  373. # Qt5-only Qt module.
  374. _QtModuleDef("QtX11Extras", shared_lib="X11Extras", bindings=["PySide2", "PyQt5"]),
  375. # *** qt/qtxmlpatterns ***
  376. # Qt5-only Qt module.
  377. _QtModuleDef(
  378. "QtXmlPatterns", shared_lib="XmlPatterns", translations=["qtxmlpatterns"], bindings=["PySide2", "PyQt5"]
  379. ),
  380. # *** qscintilla ***
  381. # Python module is available only in PyQt bindings. No associated shared library.
  382. _QtModuleDef("Qsci", translations=["qscintilla"], bindings=["PyQt*"]),
  383. )
  384. # Helpers for turning Qt namespace specifiers, such as "!PySide2" or "PyQt*", into set of applicable
  385. # namespaces.
  386. def process_namespace_strings(namespaces):
  387. """"Process list of Qt namespace specifier strings into set of namespaces."""
  388. bindings = set()
  389. for namespace in namespaces:
  390. bindings |= _process_namespace_string(namespace)
  391. return bindings
  392. def _process_namespace_string(namespace):
  393. """Expand a Qt namespace specifier string into set of namespaces."""
  394. if namespace.startswith("!"):
  395. bindings = _process_namespace_string(namespace[1:])
  396. return ALL_QT_BINDINGS - bindings
  397. else:
  398. if namespace == "PySide*":
  399. return {"PySide2", "PySide6"}
  400. elif namespace == "PyQt*":
  401. return {"PyQt5", "PyQt6"}
  402. elif namespace in ALL_QT_BINDINGS:
  403. return {namespace}
  404. else:
  405. raise ValueError(f"Invalid Qt namespace specifier: {namespace}!")