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.
- planformcreator2/PC2_ico.ico +0 -0
- planformcreator2/__init__.py +3 -0
- planformcreator2/__main__.py +11 -0
- planformcreator2/app.py +282 -0
- planformcreator2/app_model.py +495 -0
- planformcreator2/app_modes.py +698 -0
- planformcreator2/icons/PC2.ico +0 -0
- planformcreator2/icons/PC2_ico.pdn +0 -0
- planformcreator2/model/VLM.py +214 -0
- planformcreator2/model/VLM_wing.py +1305 -0
- planformcreator2/model/__init__.py +2 -0
- planformcreator2/model/wing.py +4711 -0
- planformcreator2/model/wing_exports.py +1273 -0
- planformcreator2/resources.py +112 -0
- planformcreator2/templates/ASW_17.png +0 -0
- planformcreator2/templates/Amokka.pc2 +95 -0
- planformcreator2/templates/Bow.pc2 +100 -0
- planformcreator2/templates/Elliptical.pc2 +89 -0
- planformcreator2/templates/F3B_F3F.pc2 +93 -0
- planformcreator2/templates/Scale_Glider.pc2 +85 -0
- planformcreator2/templates/Spitfire.png +0 -0
- planformcreator2/templates/Trapezoid.pc2 +50 -0
- planformcreator2/ui/__init__.py +2 -0
- planformcreator2/ui/pc2_artists.py +2817 -0
- planformcreator2/ui/pc2_diagrams.py +2942 -0
- planformcreator2/ui/pc2_dialogs.py +1354 -0
- planformcreator2/ui/pc2_panels.py +754 -0
- planformcreator2-4.0.0.dist-info/METADATA +58 -0
- planformcreator2-4.0.0.dist-info/RECORD +32 -0
- planformcreator2-4.0.0.dist-info/WHEEL +4 -0
- planformcreator2-4.0.0.dist-info/entry_points.txt +2 -0
- planformcreator2-4.0.0.dist-info/licenses/LICENSE +21 -0
|
Binary file
|
planformcreator2/app.py
ADDED
|
@@ -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())
|