planformcreator2 4.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -0,0 +1,3 @@
1
+ """
2
+ PlanformCreator2 Package
3
+ """
@@ -0,0 +1,11 @@
1
+ """
2
+
3
+ Start app with 'python -m <package>' when installed as package
4
+
5
+ """
6
+
7
+ import sys
8
+ from . import app
9
+
10
+ if __name__ == '__main__':
11
+ sys.exit(app.start())
@@ -0,0 +1,282 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ The Planform Creator 2 App
5
+
6
+ Object model overview (a little simplified)
7
+
8
+ App - Main
9
+ |-- App_Mode - different UI modes like Modify,
10
+ |-- Data_Panel - UI lower data panel for mode
11
+ |-- Panel_Geometry - UI single panel with fields
12
+
13
+ |-- Planform_Diagram - UI upper diagram area
14
+ |-- Chord_Diagram_Item - UI a single plot item within diagram
15
+ |-- Chord_Artist - UI curvature plot artist
16
+
17
+ |-- App_Model - App shell around data model allowing signals
18
+ |-- Wing - wing object
19
+ |-- Planform - planform object of wing
20
+ |-- WingSections - wing sections of a planform
21
+ |-- Airfoil - airfoil with wing section
22
+
23
+ """
24
+
25
+ import os
26
+ import sys
27
+ import argparse
28
+ import logging
29
+
30
+ from PyQt6.QtCore import QMargins, Qt
31
+ from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget
32
+ from PyQt6.QtWidgets import QGridLayout
33
+ from PyQt6.QtGui import QCloseEvent, QGuiApplication, QIcon
34
+
35
+ # --- AE modules ---------------
36
+
37
+ from airfoileditor.resources import get_icons_path as ae_icons_path
38
+ from airfoileditor.base.common_utils import *
39
+ from airfoileditor.base.widgets import Icon, Widget
40
+ from airfoileditor.base.panels import Tab_Panel, Win_Util
41
+ from airfoileditor.base.app_utils import Settings, check_or_get_initial_file, Run_Checker, Update_Checker
42
+
43
+ # --- PC2 modules ---------------
44
+
45
+ PACKAGE_NAME = "planformcreator2"
46
+
47
+ # DEV: when running app.py as main, set package path and property to allow relative imports
48
+ if __name__ == "__main__":
49
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
50
+ __package__ = PACKAGE_NAME
51
+
52
+ from .resources import get_icon_path
53
+ from .app_model import App_Model, Mode_Id
54
+ from .app_modes import Modes_Manager, Mode_Modify
55
+
56
+ from .ui.pc2_diagrams import (Diagram_Wing, Diagram_Planform, Diagram_Airfoils, Diagram_Making_Of,
57
+ Diagram_Wing_Analysis)
58
+
59
+
60
+ logger = logging.getLogger(__name__)
61
+ # logger.setLevel(logging.DEBUG)
62
+
63
+
64
+ #-------------------------------------------------------------------------------
65
+ # The App
66
+ #-------------------------------------------------------------------------------
67
+
68
+ APP_NAME = "PlanformCreator2"
69
+ __version__ = "4.0.0" # hatch "version dynamic" reads this version for build
70
+
71
+
72
+ class Main (QMainWindow):
73
+ '''
74
+ App - Main Window
75
+ '''
76
+
77
+ def __init__(self, initial_file):
78
+ super().__init__()
79
+
80
+ logger.info (f"Init Main Window")
81
+
82
+ self._app_model = None # the app model
83
+ self._modes_manager = None # app modes manager
84
+ self._diagrams_panel = None # main diagrams panel
85
+
86
+ # --- init Settings, check for newer app version ---------------
87
+
88
+ Settings.set_file (APP_NAME, file_extension= '.settings')
89
+
90
+ is_first_run = Run_Checker.is_first_run (__version__) # to show Welcome message
91
+
92
+ Update_Checker (self, APP_NAME, PACKAGE_NAME, __version__)
93
+
94
+
95
+ # --- init App Model ---------------
96
+
97
+ self._app_model = App_Model (workingDir_default=Settings.user_data_dir (APP_NAME))
98
+ self._app_model.set_app_info (__version__, "No CHANGE_TEXT", is_first_run)
99
+
100
+ # either airfoil file or Xoptfoil2 input file
101
+
102
+ pc2_file = check_or_get_initial_file (initial_file)
103
+
104
+
105
+ # --- init UI ---------------
106
+
107
+ # main window style - dark or light mode
108
+
109
+ logger.info (f"Initialize UI")
110
+
111
+ self._set_win_style (ae_icons_path(), 'PC2.ico')
112
+ self._set_win_title ()
113
+ self._set_win_geometry ()
114
+
115
+ self._app_model.sig_new_wing.connect (self._set_win_title) # update title on new wing
116
+ self._app_model.sig_new_mode.connect (self._set_win_title) # update title on new case
117
+
118
+ # app Modes and manager ---------------
119
+
120
+ modes_manager = Modes_Manager (self._app_model)
121
+ modes_manager.add_mode (Mode_Modify (self._app_model))
122
+
123
+ modes_manager.set_mode (Mode_Id.MODIFY, pc2_file) # set initial object in app_model
124
+ modes_manager.sig_close_requested.connect (self.close) # app close requested from mode view
125
+
126
+ self._modes_manager = modes_manager # keep as it hosts slots
127
+
128
+ # main widgets and layout of app
129
+
130
+ diagram = self._create_diagrams_panel () # main diagrams tab panel
131
+ modes_panel = modes_manager.stacked_modes_panel() # stacked widget with mode data panels
132
+
133
+ l = QGridLayout ()
134
+ l.addWidget (diagram, 0,0)
135
+ l.addWidget (modes_panel, 1,0)
136
+ l.setRowStretch (0,1)
137
+ l.setRowMinimumHeight (0,400)
138
+ modes_manager.set_height (160, minimized=65)
139
+
140
+ l.setSpacing (5)
141
+ l.setContentsMargins (QMargins(5, 5, 5, 5))
142
+
143
+ main = QWidget()
144
+ main.setLayout (l)
145
+ self.setCentralWidget (main)
146
+
147
+ self._diagrams_panel = diagram
148
+
149
+ # --- Enter event loop ---------------
150
+
151
+ logger.info (f"{modes_manager.current_mode} ready")
152
+
153
+
154
+
155
+ # --- private ---------------------------------------------------------
156
+
157
+
158
+ def _set_win_title (self):
159
+ """ set window title with airfoil or case name """
160
+
161
+ pc2_file = self._app_model.wing.parm_fileName if self._app_model.wing is not None else "No Wing Loaded"
162
+ self.setWindowTitle (APP_NAME + " v" + str(__version__) + " [" + pc2_file + "]")
163
+
164
+
165
+ def _set_win_style (self, icons_path : Path = None, app_icon_name : str = None):
166
+ """
167
+ Set window style according to settings
168
+ """
169
+
170
+ # set resources dir for Icons
171
+ Icon.ICONS_PATH = icons_path
172
+
173
+ # get and set app icon
174
+ app_icon_path = get_icon_path(app_icon_name)
175
+ if app_icon_path:
176
+ self.setWindowIcon (QIcon (str(app_icon_path)))
177
+
178
+ # set dark or light mode
179
+ scheme_name = Settings().get('color_scheme', Qt.ColorScheme.Unknown.name) # either unknown (from System), Dark, Light
180
+ QGuiApplication.styleHints().setColorScheme(Qt.ColorScheme[scheme_name]) # set scheme of QT
181
+
182
+ # set mode for Widgets
183
+ Widget.light_mode = not (scheme_name == Qt.ColorScheme.Dark.name)
184
+
185
+
186
+
187
+ def _set_win_geometry (self):
188
+ """ set window geometry from settings """
189
+
190
+ app_settings = Settings() # load app settings
191
+
192
+ geometry = app_settings.get('window_geometry', [])
193
+ maximize = app_settings.get('window_maximize', False)
194
+ Win_Util.set_initialWindowSize (self, size_frac= (0.85, 0.80), pos_frac=(0.1, 0.1),
195
+ geometry=geometry, maximize=maximize)
196
+
197
+
198
+ def _save_app_settings (self):
199
+ """ save application settings to file """
200
+
201
+ s = Settings()
202
+
203
+ s.set ('window_maximize', self.isMaximized())
204
+ s.set ('window_geometry', self.normalGeometry().getRect())
205
+
206
+ wing = self._app_model.wing
207
+ if wing and not wing.is_new_wing:
208
+ s.set ('last_opened', wing.parm_pathFileName_abs)
209
+ else:
210
+ s.set ('last_opened', None)
211
+
212
+ s.set ('current_diagram', self._diagrams_panel.current_tab_name)
213
+
214
+ s.save()
215
+
216
+
217
+ @override
218
+ def closeEvent (self, event : QCloseEvent):
219
+ """ main window is closed """
220
+
221
+ # terminate polar watchdog thread, clean up working dir
222
+ self._app_model.close()
223
+
224
+ # save e.g. diagram options
225
+ self._save_app_settings ()
226
+
227
+ event.accept()
228
+
229
+
230
+ def _create_diagrams_panel (self) -> Tab_Panel:
231
+ """ upper UI main panel - Tab with diagrams"""
232
+
233
+ diagrams = []
234
+ diagrams.append (Diagram_Making_Of (self, self._app_model))
235
+ diagrams.append (Diagram_Wing (self, self._app_model))
236
+ diagrams.append (Diagram_Planform (self, self._app_model))
237
+ diagrams.append (Diagram_Airfoils (self, self._app_model))
238
+ diagrams.append (Diagram_Wing_Analysis (self, self._app_model))
239
+ tab_panel = Tab_Panel (parent=self)
240
+ for diagram in diagrams:
241
+ tab_panel.add_tab(diagram)
242
+ tab_panel.setMinimumHeight(500)
243
+
244
+ tab_panel.set_current_tab (Settings().get('current_diagram', None))
245
+
246
+ return tab_panel
247
+
248
+
249
+
250
+ #--------------------------------
251
+
252
+ def start ():
253
+ """ start the app """
254
+
255
+ # init logging - can be overwritten within a module
256
+
257
+ init_logging (level= logging.INFO) # INFO, DEBUG or WARNING
258
+
259
+ # command line arguments?
260
+
261
+ parser = argparse.ArgumentParser(prog=APP_NAME, description='Create a wing planform')
262
+ parser.add_argument("file", nargs='*', help="Project .pc2 file to show")
263
+ args = parser.parse_args()
264
+ if args.file:
265
+ initial_file = args.file[0]
266
+ else:
267
+ initial_file = None
268
+
269
+ # init Qt Application and style
270
+
271
+ app = QApplication(sys.argv)
272
+ app.setStyle('fusion')
273
+
274
+ main = Main (initial_file)
275
+ main.show()
276
+ rc = app.exec()
277
+ return rc
278
+
279
+
280
+ if __name__ == "__main__":
281
+
282
+ sys.exit (start())