newproject.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # Copyright (C) 2022 The Qt Company Ltd.
  2. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
  3. from __future__ import annotations
  4. import os
  5. import sys
  6. from dataclasses import dataclass
  7. from enum import Enum
  8. from pathlib import Path
  9. from .pyproject_toml import write_pyproject_toml
  10. from .pyproject_json import write_pyproject_json
  11. """New project generation code."""
  12. _WIDGET_MAIN = """if __name__ == '__main__':
  13. app = QApplication(sys.argv)
  14. window = MainWindow()
  15. window.show()
  16. sys.exit(app.exec())
  17. """
  18. _WIDGET_IMPORTS = """import sys
  19. from PySide6.QtWidgets import QApplication, QMainWindow
  20. """
  21. _WIDGET_CLASS_DEFINITION = """class MainWindow(QMainWindow):
  22. def __init__(self):
  23. super().__init__()
  24. """
  25. _WIDGET_SETUP_UI_CODE = """ self._ui = Ui_MainWindow()
  26. self._ui.setupUi(self)
  27. """
  28. _MAINWINDOW_FORM = """<?xml version="1.0" encoding="UTF-8"?>
  29. <ui version="4.0">
  30. <class>MainWindow</class>
  31. <widget class="QMainWindow" name="MainWindow">
  32. <property name="geometry">
  33. <rect>
  34. <x>0</x>
  35. <y>0</y>
  36. <width>800</width>
  37. <height>600</height>
  38. </rect>
  39. </property>
  40. <property name="windowTitle">
  41. <string>MainWindow</string>
  42. </property>
  43. <widget class="QWidget" name="centralwidget"/>
  44. <widget class="QMenuBar" name="menubar">
  45. <property name="geometry">
  46. <rect>
  47. <x>0</x>
  48. <y>0</y>
  49. <width>800</width>
  50. <height>22</height>
  51. </rect>
  52. </property>
  53. </widget>
  54. <widget class="QStatusBar" name="statusbar"/>
  55. </widget>
  56. </ui>
  57. """
  58. _QUICK_FORM = """import QtQuick
  59. import QtQuick.Controls
  60. ApplicationWindow {
  61. id: window
  62. width: 1024
  63. height: 600
  64. visible: true
  65. }
  66. """
  67. _QUICK_MAIN = """import sys
  68. from pathlib import Path
  69. from PySide6.QtGui import QGuiApplication
  70. from PySide6.QtCore import QUrl
  71. from PySide6.QtQml import QQmlApplicationEngine
  72. if __name__ == "__main__":
  73. app = QGuiApplication()
  74. engine = QQmlApplicationEngine()
  75. qml_file = Path(__file__).parent / 'main.qml'
  76. engine.load(QUrl.fromLocalFile(qml_file))
  77. if not engine.rootObjects():
  78. sys.exit(-1)
  79. exit_code = app.exec()
  80. del engine
  81. sys.exit(exit_code)
  82. """
  83. NewProjectFiles = list[tuple[str, str]] # tuple of (filename, contents).
  84. @dataclass(frozen=True)
  85. class NewProjectType:
  86. command: str
  87. description: str
  88. files: NewProjectFiles
  89. def _write_project(directory: Path, files: NewProjectFiles, legacy_pyproject: bool):
  90. """
  91. Create the project files in the specified directory.
  92. :param directory: The directory to create the project in.
  93. :param files: The files that belong to the project to create.
  94. """
  95. file_names = []
  96. for file_name, contents in files:
  97. (directory / file_name).write_text(contents)
  98. print(f"Wrote {directory.name}{os.sep}{file_name}.")
  99. file_names.append(file_name)
  100. if legacy_pyproject:
  101. pyproject_file = directory / f"{directory.name}.pyproject"
  102. write_pyproject_json(pyproject_file, file_names)
  103. else:
  104. pyproject_file = directory / "pyproject.toml"
  105. write_pyproject_toml(pyproject_file, directory.name, file_names)
  106. print(f"Wrote {pyproject_file}.")
  107. def _widget_project() -> NewProjectFiles:
  108. """Create a (form-less) widgets project."""
  109. main_py = (_WIDGET_IMPORTS + "\n\n" + _WIDGET_CLASS_DEFINITION + "\n\n"
  110. + _WIDGET_MAIN)
  111. return [("main.py", main_py)]
  112. def _ui_form_project() -> NewProjectFiles:
  113. """Create a Qt Designer .ui form based widgets project."""
  114. main_py = (_WIDGET_IMPORTS
  115. + "\nfrom ui_mainwindow import Ui_MainWindow\n\n\n"
  116. + _WIDGET_CLASS_DEFINITION + _WIDGET_SETUP_UI_CODE
  117. + "\n\n" + _WIDGET_MAIN)
  118. return [("main.py", main_py),
  119. ("mainwindow.ui", _MAINWINDOW_FORM)]
  120. def _qml_project() -> NewProjectFiles:
  121. """Create a QML project."""
  122. return [("main.py", _QUICK_MAIN),
  123. ("main.qml", _QUICK_FORM)]
  124. class NewProjectTypes(Enum):
  125. QUICK = NewProjectType("new-quick", "Create a new Qt Quick project", _qml_project())
  126. WIDGET_FORM = NewProjectType("new-ui", "Create a new Qt Widgets Form project",
  127. _ui_form_project())
  128. WIDGET = NewProjectType("new-widget", "Create a new Qt Widgets project", _widget_project())
  129. @staticmethod
  130. def find_by_command(command: str) -> NewProjectType | None:
  131. return next((pt.value for pt in NewProjectTypes if pt.value.command == command), None)
  132. def new_project(
  133. project_dir: Path, project_type: NewProjectType, legacy_pyproject: bool
  134. ) -> int:
  135. """
  136. Create a new project at the specified project_dir directory.
  137. :param project_dir: The directory path to create the project. If existing, must be empty.
  138. :param project_type: The Qt type of project to create (Qt Widgets, Qt Quick, etc.)
  139. :return: 0 if the project was created successfully, otherwise 1.
  140. """
  141. if any(project_dir.iterdir()):
  142. print(f"Can not create project at {project_dir}: directory is not empty.", file=sys.stderr)
  143. return 1
  144. project_dir.mkdir(parents=True, exist_ok=True)
  145. try:
  146. _write_project(project_dir, project_type.files, legacy_pyproject)
  147. except Exception as e:
  148. print(f"Error creating project file: {str(e)}", file=sys.stderr)
  149. return 1
  150. if project_type == NewProjectTypes.WIDGET_FORM:
  151. print(f'Run "pyside6-project build {project_dir}" to build the project')
  152. print(f'Run "pyside6-project run {project_dir / "main.py"}" to run the project')
  153. return 0