pyside_tool.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #!/usr/bin/env python
  2. # Copyright (C) 2022 The Qt Company Ltd.
  3. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
  4. from __future__ import annotations
  5. import importlib
  6. import os
  7. import subprocess
  8. import sys
  9. import sysconfig
  10. from pathlib import Path
  11. import PySide6 as ref_mod
  12. VIRTUAL_ENV = "VIRTUAL_ENV"
  13. def is_pyenv_python():
  14. pyenv_root = os.environ.get("PYENV_ROOT")
  15. if pyenv_root:
  16. resolved_exe = Path(sys.executable).resolve()
  17. if str(resolved_exe).startswith(pyenv_root):
  18. return True
  19. return False
  20. def is_virtual_env():
  21. return sys.prefix != sys.base_prefix
  22. def init_virtual_env():
  23. """PYSIDE-2251: Enable running from a non-activated virtual environment
  24. as is the case for Visual Studio Code by setting the VIRTUAL_ENV
  25. variable which is used by the Qt Designer plugin."""
  26. if is_virtual_env() and not os.environ.get(VIRTUAL_ENV):
  27. os.environ[VIRTUAL_ENV] = sys.prefix
  28. def main():
  29. # This will take care of "pyside6-lupdate" listed as an entrypoint
  30. # in setup.py are copied to 'scripts/..'
  31. cmd = os.path.join("..", os.path.basename(sys.argv[0]))
  32. command = [os.path.join(os.path.dirname(os.path.realpath(__file__)), cmd)]
  33. command.extend(sys.argv[1:])
  34. sys.exit(subprocess.call(command))
  35. def qt_tool_wrapper(qt_tool, args, libexec=False):
  36. # Taking care of pyside6-uic, pyside6-rcc, and pyside6-designer
  37. # listed as an entrypoint in setup.py
  38. pyside_dir = Path(ref_mod.__file__).resolve().parent
  39. if libexec and sys.platform != "win32":
  40. exe = pyside_dir / 'Qt' / 'libexec' / qt_tool
  41. else:
  42. exe = pyside_dir / qt_tool
  43. cmd = [os.fspath(exe)] + args
  44. returncode = subprocess.call(cmd)
  45. if returncode != 0:
  46. command = ' '.join(cmd)
  47. print(f"'{command}' returned {returncode}", file=sys.stderr)
  48. sys.exit(returncode)
  49. def pyside_script_wrapper(script_name):
  50. """Launch a script shipped with PySide."""
  51. script = Path(__file__).resolve().parent / script_name
  52. command = [sys.executable, os.fspath(script)] + sys.argv[1:]
  53. sys.exit(subprocess.call(command))
  54. def ui_tool_binary(binary):
  55. """Return the binary of a UI tool (App bundle on macOS)."""
  56. if sys.platform != "darwin":
  57. return binary
  58. name = binary[0:1].upper() + binary[1:]
  59. return f"{name}.app/Contents/MacOS/{name}"
  60. def lrelease():
  61. qt_tool_wrapper("lrelease", sys.argv[1:])
  62. def lupdate():
  63. qt_tool_wrapper("lupdate", sys.argv[1:])
  64. def uic():
  65. qt_tool_wrapper("uic", ['-g', 'python'] + sys.argv[1:], True)
  66. def rcc():
  67. args = []
  68. user_args = sys.argv[1:]
  69. if "--binary" not in user_args:
  70. args.extend(['-g', 'python'])
  71. args.extend(user_args)
  72. qt_tool_wrapper("rcc", args, True)
  73. def qmltyperegistrar():
  74. qt_tool_wrapper("qmltyperegistrar", sys.argv[1:], True)
  75. def qmlimportscanner():
  76. qt_tool_wrapper("qmlimportscanner", sys.argv[1:], True)
  77. def qmlcachegen():
  78. qt_tool_wrapper("qmlcachegen", sys.argv[1:], True)
  79. def qmllint():
  80. qt_tool_wrapper("qmllint", sys.argv[1:])
  81. def qmlformat():
  82. qt_tool_wrapper("qmlformat", sys.argv[1:])
  83. def qmlls():
  84. qt_tool_wrapper("qmlls", sys.argv[1:])
  85. def assistant():
  86. qt_tool_wrapper(ui_tool_binary("assistant"), sys.argv[1:])
  87. def _extend_path_var(var, value, prepend=False):
  88. env_value = os.environ.get(var)
  89. if env_value:
  90. env_value = (f'{value}{os.pathsep}{env_value}'
  91. if prepend else f'{env_value}{os.pathsep}{value}')
  92. else:
  93. env_value = value
  94. os.environ[var] = env_value
  95. def designer():
  96. init_virtual_env()
  97. # https://www.python.org/dev/peps/pep-0384/#linkage :
  98. # "On Unix systems, the ABI is typically provided by the python executable
  99. # itself", that is, libshiboken does not link against any Python library
  100. # and expects to get these symbols from a python executable. Since no
  101. # python executable is involved when loading this plugin, pre-load python.so
  102. # This should also help to work around a numpy issue, see
  103. # https://stackoverflow.com/questions/49784583/numpy-import-fails-on-multiarray-extension-library-when-called-from-embedded-pyt
  104. major_version = sys.version_info[0]
  105. minor_version = sys.version_info[1]
  106. os.environ['PY_MAJOR_VERSION'] = str(major_version)
  107. os.environ['PY_MINOR_VERSION'] = str(minor_version)
  108. if sys.platform == 'linux':
  109. # Determine library name (examples/utils/pyside_config.py)
  110. version = f'{major_version}.{minor_version}'
  111. library_name = f'libpython{version}{sys.abiflags}.so'
  112. if is_pyenv_python():
  113. library_name = str(Path(sysconfig.get_config_var('LIBDIR')) / library_name)
  114. os.environ['LD_PRELOAD'] = library_name
  115. elif sys.platform == 'darwin':
  116. library_name = sysconfig.get_config_var("LDLIBRARY")
  117. framework_prefix = sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")
  118. lib_path = None
  119. if framework_prefix:
  120. lib_path = os.fspath(Path(framework_prefix) / library_name)
  121. elif is_pyenv_python():
  122. lib_path = str(Path(sysconfig.get_config_var('LIBDIR')) / library_name)
  123. else:
  124. # ideally this should never be reached because the system Python and Python installed
  125. # from python.org are all framework builds
  126. print("Unable to find Python library directory. Use a framework build of Python.",
  127. file=sys.stderr)
  128. sys.exit(0)
  129. os.environ['DYLD_INSERT_LIBRARIES'] = lib_path
  130. elif sys.platform == 'win32':
  131. # Find Python DLLs from the base installation
  132. if is_virtual_env():
  133. _extend_path_var("PATH", os.fspath(Path(sys._base_executable).parent), True)
  134. qt_tool_wrapper(ui_tool_binary("designer"), sys.argv[1:])
  135. def linguist():
  136. qt_tool_wrapper(ui_tool_binary("linguist"), sys.argv[1:])
  137. def genpyi():
  138. pyside_dir = Path(__file__).resolve().parents[1]
  139. support = pyside_dir / "support"
  140. cmd = support / "generate_pyi.py"
  141. command = [sys.executable, os.fspath(cmd)] + sys.argv[1:]
  142. sys.exit(subprocess.call(command))
  143. def metaobjectdump():
  144. pyside_script_wrapper("metaobjectdump.py")
  145. def _check_requirements(requirements_file):
  146. """Check if all required packages are installed."""
  147. missing_packages = []
  148. with open(requirements_file, 'r', encoding='UTF-8') as file:
  149. for line in file:
  150. # versions
  151. package = line.strip().split('==')[0]
  152. if not importlib.util.find_spec(package):
  153. missing_packages.append(line.strip())
  154. return missing_packages
  155. def project():
  156. pyside_script_wrapper("project.py")
  157. def qml():
  158. pyside_script_wrapper("qml.py")
  159. def qtpy2cpp():
  160. pyside_script_wrapper("qtpy2cpp.py")
  161. def deploy():
  162. pyside_script_wrapper("deploy.py")
  163. def android_deploy():
  164. if sys.platform == "win32":
  165. print("pyside6-android-deploy only works from a Unix host and not a Windows host",
  166. file=sys.stderr)
  167. else:
  168. android_requirements_file = Path(__file__).parent / "requirements-android.txt"
  169. if android_requirements_file.exists():
  170. missing_packages = _check_requirements(android_requirements_file)
  171. if missing_packages:
  172. print("The following packages are required but not installed:")
  173. for package in missing_packages:
  174. print(f" - {package}")
  175. print("Please install them using:")
  176. print(f" pip install -r {android_requirements_file}")
  177. sys.exit(1)
  178. pyside_script_wrapper("android_deploy.py")
  179. def qsb():
  180. qt_tool_wrapper("qsb", sys.argv[1:])
  181. def balsam():
  182. qt_tool_wrapper("balsam", sys.argv[1:])
  183. def balsamui():
  184. qt_tool_wrapper("balsamui", sys.argv[1:])
  185. def svgtoqml():
  186. qt_tool_wrapper("svgtoqml", sys.argv[1:])
  187. if __name__ == "__main__":
  188. main()