RCAIDE-GUI 1.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.
Files changed (170) hide show
  1. common_widgets/__init__.py +6 -0
  2. common_widgets/animated_toggle.py +141 -0
  3. common_widgets/color.py +17 -0
  4. common_widgets/data_entry_widget.py +337 -0
  5. common_widgets/image_widget.py +21 -0
  6. common_widgets/unit_picker_widget.py +48 -0
  7. main.py +168 -0
  8. rcaide_gui-1.0.0.dist-info/METADATA +96 -0
  9. rcaide_gui-1.0.0.dist-info/RECORD +170 -0
  10. rcaide_gui-1.0.0.dist-info/WHEEL +5 -0
  11. rcaide_gui-1.0.0.dist-info/entry_points.txt +2 -0
  12. rcaide_gui-1.0.0.dist-info/top_level.txt +5 -0
  13. rcaide_io.py +818 -0
  14. tabs/__init__.py +10 -0
  15. tabs/aircraft_configs/__init__.py +3 -0
  16. tabs/aircraft_configs/aircraft_configs.py +301 -0
  17. tabs/analysis/__init__.py +4 -0
  18. tabs/analysis/analysis.py +92 -0
  19. tabs/analysis/widgets/__init__.py +13 -0
  20. tabs/analysis/widgets/aeroacoustics_widget.py +158 -0
  21. tabs/analysis/widgets/aerodynamics_widget.py +108 -0
  22. tabs/analysis/widgets/analysis_data_widget.py +29 -0
  23. tabs/analysis/widgets/atmosphere_widget.py +49 -0
  24. tabs/analysis/widgets/costs_widget.py +35 -0
  25. tabs/analysis/widgets/energy_widget.py +32 -0
  26. tabs/analysis/widgets/geometry_widget.py +55 -0
  27. tabs/analysis/widgets/planets_widget.py +34 -0
  28. tabs/analysis/widgets/propulsion_widget.py +53 -0
  29. tabs/analysis/widgets/stability_widget.py +34 -0
  30. tabs/analysis/widgets/weights_widget.py +94 -0
  31. tabs/geometry/__init__.py +6 -0
  32. tabs/geometry/aircraft_configs.py +232 -0
  33. tabs/geometry/frames/__init__.py +15 -0
  34. tabs/geometry/frames/booms/__init__.py +5 -0
  35. tabs/geometry/frames/booms/boom_frame.py +238 -0
  36. tabs/geometry/frames/cargo_bays/__init__.py +5 -0
  37. tabs/geometry/frames/cargo_bays/cargo_bay_frame.py +159 -0
  38. tabs/geometry/frames/default_frame.py +23 -0
  39. tabs/geometry/frames/energy_network/__init__.py +2 -0
  40. tabs/geometry/frames/energy_network/turbofan_network/__init__.py +3 -0
  41. tabs/geometry/frames/energy_network/turbofan_network/frames/__init__.py +1 -0
  42. tabs/geometry/frames/energy_network/turbofan_network/widgets/__init__.py +1 -0
  43. tabs/geometry/frames/fuselages/__init__.py +5 -0
  44. tabs/geometry/frames/fuselages/fuselage_frame.py +294 -0
  45. tabs/geometry/frames/geometry_frame.py +91 -0
  46. tabs/geometry/frames/landing_gears/__init__.py +5 -0
  47. tabs/geometry/frames/landing_gears/landing_gear_frame.py +189 -0
  48. tabs/geometry/frames/nacelles/__init__.py +1 -0
  49. tabs/geometry/frames/powertrain/__init__.py +6 -0
  50. tabs/geometry/frames/powertrain/converters/__init__.py +5 -0
  51. tabs/geometry/frames/powertrain/converters/converter_frame.py +137 -0
  52. tabs/geometry/frames/powertrain/distributors/__init__.py +5 -0
  53. tabs/geometry/frames/powertrain/distributors/distributor_frame.py +143 -0
  54. tabs/geometry/frames/powertrain/nacelles/__init__.py +5 -0
  55. tabs/geometry/frames/powertrain/nacelles/nacelle_frame.py +283 -0
  56. tabs/geometry/frames/powertrain/powertrain_frame.py +259 -0
  57. tabs/geometry/frames/powertrain/propulsors/__init__.py +5 -0
  58. tabs/geometry/frames/powertrain/propulsors/propulsor_frame.py +146 -0
  59. tabs/geometry/frames/powertrain/sources/__init__.py +6 -0
  60. tabs/geometry/frames/powertrain/sources/energy_source_frame.py +147 -0
  61. tabs/geometry/frames/vehicle_frame.py +171 -0
  62. tabs/geometry/frames/wings/__init__.py +5 -0
  63. tabs/geometry/frames/wings/wings_frame.py +457 -0
  64. tabs/geometry/geometry.py +513 -0
  65. tabs/geometry/widgets/__init__.py +12 -0
  66. tabs/geometry/widgets/booms/__init__.py +6 -0
  67. tabs/geometry/widgets/booms/boom_section_widget.py +109 -0
  68. tabs/geometry/widgets/cargo_bays/__init__.py +1 -0
  69. tabs/geometry/widgets/fuselages/__init__.py +8 -0
  70. tabs/geometry/widgets/fuselages/cabin_class_widget.py +188 -0
  71. tabs/geometry/widgets/fuselages/cabin_widget.py +217 -0
  72. tabs/geometry/widgets/fuselages/fuselage_section_widget.py +190 -0
  73. tabs/geometry/widgets/geometry_data_widget.py +25 -0
  74. tabs/geometry/widgets/landing_gears/__init__.py +6 -0
  75. tabs/geometry/widgets/nacelles/__init__.py +1 -0
  76. tabs/geometry/widgets/powertrain/__init__.py +9 -0
  77. tabs/geometry/widgets/powertrain/converters/__init__.py +5 -0
  78. tabs/geometry/widgets/powertrain/converters/turboelectric_generator_widget.py +74 -0
  79. tabs/geometry/widgets/powertrain/distributors/__init__.py +5 -0
  80. tabs/geometry/widgets/powertrain/distributors/fuel_line_widget.py +81 -0
  81. tabs/geometry/widgets/powertrain/modulators/__init__.py +5 -0
  82. tabs/geometry/widgets/powertrain/modulators/esc_widget.py +58 -0
  83. tabs/geometry/widgets/powertrain/nacelles/__init__.py +5 -0
  84. tabs/geometry/widgets/powertrain/nacelles/nacelle_section_widget.py +115 -0
  85. tabs/geometry/widgets/powertrain/powertrain_connector_widget.py +136 -0
  86. tabs/geometry/widgets/powertrain/powertrain_widget.py +206 -0
  87. tabs/geometry/widgets/powertrain/propulsors/__init__.py +5 -0
  88. tabs/geometry/widgets/powertrain/propulsors/turbofan_widget.py +462 -0
  89. tabs/geometry/widgets/powertrain/sources/__init__.py +6 -0
  90. tabs/geometry/widgets/powertrain/sources/fuel_tank_widget.py +103 -0
  91. tabs/geometry/widgets/powertrain/sources/source_selector_widget.py +35 -0
  92. tabs/geometry/widgets/wings/__init__.py +7 -0
  93. tabs/geometry/widgets/wings/wing_cs_widget.py +198 -0
  94. tabs/geometry/widgets/wings/wing_section_widget.py +298 -0
  95. tabs/home/__init__.py +3 -0
  96. tabs/home/home.py +585 -0
  97. tabs/mission/__init__.py +4 -0
  98. tabs/mission/mission.py +1124 -0
  99. tabs/mission/widgets/__init__.py +3 -0
  100. tabs/mission/widgets/flight_controls_widget.py +186 -0
  101. tabs/mission/widgets/mission_analysis_widget.py +166 -0
  102. tabs/mission/widgets/mission_segment_helper.py +358 -0
  103. tabs/mission/widgets/mission_segment_widget.py +358 -0
  104. tabs/solve/__init__.py +4 -0
  105. tabs/solve/plots/__init__.py +12 -0
  106. tabs/solve/plots/aeroacoustics/__init__.py +6 -0
  107. tabs/solve/plots/aeroacoustics/plot_2D_noise_contour.py +184 -0
  108. tabs/solve/plots/aeroacoustics/plot_3D_noise_contour.py +219 -0
  109. tabs/solve/plots/aeroacoustics/plot_noise_certification_contour.py +115 -0
  110. tabs/solve/plots/aeroacoustics/plot_noise_level.py +118 -0
  111. tabs/solve/plots/aerodynamics/__init__.py +10 -0
  112. tabs/solve/plots/aerodynamics/plot_aerodynamic_coefficients.py +156 -0
  113. tabs/solve/plots/aerodynamics/plot_aerodynamic_forces.py +155 -0
  114. tabs/solve/plots/aerodynamics/plot_aircraft_aerodynamics.py +136 -0
  115. tabs/solve/plots/aerodynamics/plot_drag_components.py +157 -0
  116. tabs/solve/plots/aerodynamics/plot_lift_distribution.py +107 -0
  117. tabs/solve/plots/aerodynamics/plot_rotor_conditions.py +255 -0
  118. tabs/solve/plots/aerodynamics/plot_rotor_disc_performance.py +144 -0
  119. tabs/solve/plots/aerodynamics/plot_rotor_performance.py +124 -0
  120. tabs/solve/plots/common/__init__.py +4 -0
  121. tabs/solve/plots/common/plot_style.py +71 -0
  122. tabs/solve/plots/common/set_axes.py +55 -0
  123. tabs/solve/plots/create_plot_widgets.py +34 -0
  124. tabs/solve/plots/emissions/__init__.py +3 -0
  125. tabs/solve/plots/emissions/plot_emissions.py +167 -0
  126. tabs/solve/plots/energy/__init__.py +12 -0
  127. tabs/solve/plots/energy/plot_altitude_sfc_weight.py +134 -0
  128. tabs/solve/plots/energy/plot_battery_cell_conditions.py +154 -0
  129. tabs/solve/plots/energy/plot_battery_degradation.py +157 -0
  130. tabs/solve/plots/energy/plot_battery_module_C_rates.py +139 -0
  131. tabs/solve/plots/energy/plot_battery_module_conditions.py +157 -0
  132. tabs/solve/plots/energy/plot_battery_pack_conditions.py +164 -0
  133. tabs/solve/plots/energy/plot_battery_ragone_diagram.py +97 -0
  134. tabs/solve/plots/energy/plot_battery_temperature.py +136 -0
  135. tabs/solve/plots/energy/plot_electric_propulsor_efficiencies.py +136 -0
  136. tabs/solve/plots/energy/plot_propulsor_throttles.py +119 -0
  137. tabs/solve/plots/mission/__init__.py +5 -0
  138. tabs/solve/plots/mission/plot_aircraft_velocities.py +145 -0
  139. tabs/solve/plots/mission/plot_flight_conditions.py +236 -0
  140. tabs/solve/plots/mission/plot_flight_trajectory.py +172 -0
  141. tabs/solve/plots/stability/__init__.py +5 -0
  142. tabs/solve/plots/stability/plot_flight_forces_and_moments.py +163 -0
  143. tabs/solve/plots/stability/plot_lateral_stability.py +151 -0
  144. tabs/solve/plots/stability/plot_longitudinal_stability.py +173 -0
  145. tabs/solve/plots/thermal_management/__init__.py +7 -0
  146. tabs/solve/plots/thermal_management/plot_air_cooled_conditions.py +152 -0
  147. tabs/solve/plots/thermal_management/plot_cross_flow_heat_exchanger_conditions.py +185 -0
  148. tabs/solve/plots/thermal_management/plot_reservoir_conditions.py +141 -0
  149. tabs/solve/plots/thermal_management/plot_thermal_management_performance.py +105 -0
  150. tabs/solve/plots/thermal_management/plot_wavy_channel_conditions.py +161 -0
  151. tabs/solve/plots/weights/__init__.py +4 -0
  152. tabs/solve/plots/weights/plot_load_diagram.py +163 -0
  153. tabs/solve/plots/weights/plot_weight_breakdown.py +164 -0
  154. tabs/solve/solve.py +1498 -0
  155. tabs/style_sheet.py +114 -0
  156. tabs/tab_widget.py +14 -0
  157. tabs/visualize_geometry/__init__.py +4 -0
  158. tabs/visualize_geometry/core_3d_viewer.py +253 -0
  159. tabs/visualize_geometry/features/__init__.py +10 -0
  160. tabs/visualize_geometry/features/axes_gizmo.py +173 -0
  161. tabs/visualize_geometry/features/background.py +161 -0
  162. tabs/visualize_geometry/features/blueprint.py +685 -0
  163. tabs/visualize_geometry/features/camera.py +364 -0
  164. tabs/visualize_geometry/features/drag_aircraft.py +219 -0
  165. tabs/visualize_geometry/features/grid.py +194 -0
  166. tabs/visualize_geometry/features/measurement.py +367 -0
  167. tabs/visualize_geometry/features/screenshot.py +91 -0
  168. tabs/visualize_geometry/geometry_helper_functions.py +3 -0
  169. tabs/visualize_geometry/visualize_geometry.py +731 -0
  170. utilities.py +257 -0
@@ -0,0 +1,3 @@
1
+ from tabs.mission.widgets.mission_segment_widget import MissionSegmentWidget
2
+ from tabs.mission.widgets.flight_controls_widget import FlightControlsWidget
3
+ from tabs.mission.widgets.mission_analysis_widget import MissionAnalysisWidget
@@ -0,0 +1,186 @@
1
+ # RCAIDE_GUI/tabs/mission/widgets/flight_controls_widget.py
2
+
3
+ # Created: May 2023, M. Clarke
4
+ # ------------------------------------------------------------------------------
5
+ # Imports
6
+ # ------------------------------------------------------------------------------
7
+ from PyQt6.QtWidgets import (
8
+ QHBoxLayout,
9
+ QLabel,
10
+ QVBoxLayout,
11
+ QWidget,
12
+ QCheckBox,
13
+ QSizePolicy,
14
+ )
15
+
16
+ from utilities import Units, create_line_bar, set_data
17
+ from common_widgets import DataEntryWidget
18
+ import rcaide_io
19
+
20
+ # ------------------------------------------------------------------------------
21
+ # Flight Controls Widget
22
+ # ------------------------------------------------------------------------------
23
+ class FlightControlsWidget(QWidget):
24
+ def __init__(self):
25
+ super().__init__()
26
+
27
+ base_layout = QVBoxLayout()
28
+ toggles_layout = QHBoxLayout()
29
+
30
+ self.data_entry_widgets = []
31
+
32
+ for key, toggles in self.fields.items():
33
+ sub_layout = QVBoxLayout()
34
+
35
+ header = QLabel("<b>" + key + "</b>")
36
+ header.setSizePolicy(
37
+ QSizePolicy.Policy.Minimum,
38
+ QSizePolicy.Policy.Minimum,
39
+ )
40
+ sub_layout.addWidget(header)
41
+ sub_layout.addWidget(create_line_bar())
42
+
43
+ self.data_entry_widgets.append(DataEntryWidget(toggles, 1))
44
+ sub_layout.addWidget(self.data_entry_widgets[-1])
45
+
46
+ toggles_layout.addLayout(sub_layout, 1)
47
+
48
+ base_layout.addLayout(toggles_layout)
49
+
50
+ # Propulsor assignment checkboxes
51
+ base_layout.addWidget(QLabel("<b>Assigned Propulsors</b>"))
52
+ base_layout.addWidget(create_line_bar())
53
+
54
+ self.propulsor_checkboxes = {}
55
+ self._prop_row = QHBoxLayout()
56
+ self._build_propulsor_checkboxes()
57
+ base_layout.addLayout(self._prop_row)
58
+
59
+ self.setLayout(base_layout)
60
+
61
+ # -------------------------------------------------------------------------
62
+ # Propulsor checkboxes
63
+ # -------------------------------------------------------------------------
64
+ def _build_propulsor_checkboxes(self):
65
+ while self._prop_row.count():
66
+ item = self._prop_row.takeAt(0)
67
+ if item.widget():
68
+ item.widget().deleteLater()
69
+ self.propulsor_checkboxes.clear()
70
+
71
+ names = []
72
+ try:
73
+ names = rcaide_io.propulsor_names[0] or []
74
+ except (AttributeError, IndexError, TypeError):
75
+ pass
76
+
77
+ for name in names:
78
+ cb = QCheckBox(name)
79
+ cb.setChecked(True)
80
+ self.propulsor_checkboxes[name] = cb
81
+ self._prop_row.addWidget(cb)
82
+
83
+ self._prop_row.addStretch(1)
84
+
85
+ def refresh_propulsors(self):
86
+ """Rebuild propulsor checkboxes from the current rcaide_io.propulsor_names."""
87
+ self._build_propulsor_checkboxes()
88
+
89
+ # -------------------------------------------------------------------------
90
+ # Data access
91
+ # -------------------------------------------------------------------------
92
+ def get_data(self):
93
+ data = {}
94
+ for widget in self.data_entry_widgets:
95
+ assert isinstance(widget, DataEntryWidget)
96
+ data.update(widget.get_values())
97
+ data["assigned_propulsors"] = [
98
+ name for name, cb in self.propulsor_checkboxes.items() if cb.isChecked()
99
+ ]
100
+ return data
101
+
102
+ def load_data(self, data):
103
+ for widget in self.data_entry_widgets:
104
+ assert isinstance(widget, DataEntryWidget)
105
+ widget.load_data(data)
106
+
107
+ selected = data.get("assigned_propulsors", [])
108
+ # Handle [value, 0] unit-arg format that may appear when loading from RCAIDE JSON
109
+ if (isinstance(selected, list) and len(selected) == 2
110
+ and isinstance(selected[1], int) and not isinstance(selected[1], bool)):
111
+ selected = selected[0] if isinstance(selected[0], list) else []
112
+
113
+ for name, cb in self.propulsor_checkboxes.items():
114
+ cb.setChecked(not selected or name in selected)
115
+
116
+ def set_control_variables(self, segment):
117
+ for widget in self.data_entry_widgets:
118
+ assert isinstance(widget, DataEntryWidget)
119
+ data_units_labels = widget.data_units_labels
120
+ data = widget.get_values()
121
+ for data_unit_label in data_units_labels:
122
+ rcaide_label = data_unit_label[-1]
123
+ user_label = data_unit_label[0]
124
+ set_data(segment, rcaide_label, data[user_label][0])
125
+
126
+ # Use the propulsors the user checked; fall back to all available propulsors
127
+ selected = [name for name, cb in self.propulsor_checkboxes.items() if cb.isChecked()]
128
+ if not selected:
129
+ selected = list(self.propulsor_checkboxes.keys())
130
+ if not selected:
131
+ # Last-resort fallback: derive names from vehicle networks
132
+ try:
133
+ for network in rcaide_io.rcaide_vehicle.networks:
134
+ for prop in network.propulsors:
135
+ if prop.tag not in selected:
136
+ selected.append(prop.tag)
137
+ except Exception:
138
+ pass
139
+
140
+ segment.assigned_control_variables.throttle.assigned_propulsors = [selected]
141
+
142
+ if getattr(segment.assigned_control_variables.throttle, "active", False):
143
+ if hasattr(segment.assigned_control_variables.throttle, "initial_guess_values"):
144
+ if not segment.assigned_control_variables.throttle.initial_guess_values:
145
+ segment.assigned_control_variables.throttle.initial_guess_values = [[0.7]]
146
+
147
+ if getattr(segment.assigned_control_variables.body_angle, "active", False):
148
+ if hasattr(segment.assigned_control_variables.body_angle, "initial_guess_values"):
149
+ if not segment.assigned_control_variables.body_angle.initial_guess_values:
150
+ segment.assigned_control_variables.body_angle.initial_guess_values = [[0.0]]
151
+
152
+ def set_defaults(self, throttle=False, body_angle=False):
153
+ for widget in self.data_entry_widgets:
154
+ assert isinstance(widget, DataEntryWidget)
155
+ defaults = {}
156
+ for label, _, rcaide_label in widget.data_units_labels:
157
+ label_lower = rcaide_label.lower()
158
+ value = False
159
+ if "throttle.active" in label_lower:
160
+ value = throttle
161
+ elif "body_angle.active" in label_lower:
162
+ value = body_angle
163
+ defaults[label] = (value, 0)
164
+ widget.load_data(defaults)
165
+
166
+ fields = {
167
+ "Kinematics": [
168
+ ("Body Angle", Units.Boolean, "assigned_control_variables.body_angle.active"),
169
+ ("Bank Angle", Units.Boolean, "assigned_control_variables.bank_angle.active"),
170
+ ("Wind Angle", Units.Boolean, "assigned_control_variables.wind_angle.active"),
171
+ ("Velocity", Units.Boolean, "assigned_control_variables.velocity.active"),
172
+ ("Acceleration", Units.Boolean, "assigned_control_variables.acceleration.active"),
173
+ ("Altitude", Units.Boolean, "assigned_control_variables.altitude.active"),
174
+ ],
175
+ "Control Surfaces": [
176
+ ("Elevator Deflection", Units.Boolean, "assigned_control_variables.elevator_deflection.active"),
177
+ ("Rudder Deflection", Units.Boolean, "assigned_control_variables.rudder_deflection.active"),
178
+ ("Flap Deflection", Units.Boolean, "assigned_control_variables.flap_deflection.active"),
179
+ ("Slat Deflection", Units.Boolean, "assigned_control_variables.slat_deflection.active"),
180
+ ("Aileron Deflection", Units.Boolean, "assigned_control_variables.aileron_deflection.active"),
181
+ ],
182
+ "Propulsion": [
183
+ ("Throttle", Units.Boolean, "assigned_control_variables.throttle.active"),
184
+ ("Thrust Vector Angle", Units.Boolean, "assigned_control_variables.thrust_vector_angle.active"),
185
+ ],
186
+ }
@@ -0,0 +1,166 @@
1
+ # RCAIDE_GUI/tabs/mission/widgets/mission_analysis_widget.py
2
+
3
+ # Created: May 2023, M. Clarke
4
+ # ------------------------------------------------------------------------------
5
+ # Imports
6
+ # ------------------------------------------------------------------------------
7
+ from PyQt6.QtCore import Qt
8
+ from PyQt6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QTreeWidget, \
9
+ QTreeWidgetItem, QHeaderView, QPushButton, QLabel
10
+
11
+ from tabs.analysis.widgets import *
12
+ from tabs import TabWidget
13
+ from utilities import create_scroll_area
14
+ import rcaide_io
15
+ import RCAIDE
16
+
17
+ # ============================================================
18
+ # Full Functional Analysis Widget (used by Mission tab)
19
+ # ============================================================
20
+ class MissionAnalysisWidget(TabWidget):
21
+ def __init__(self):
22
+ super(MissionAnalysisWidget, self).__init__()
23
+
24
+ options = [
25
+ "Aerodynamics",
26
+ "Atmospheric",
27
+ "Planets",
28
+ "Weights",
29
+ "Geometry",
30
+ "Energy",
31
+ "Propulsion",
32
+ "Costs",
33
+ "Aeroacoustics",
34
+ "Stability",
35
+ ]
36
+
37
+ self.tree_frame_layout = QVBoxLayout()
38
+ self.tree_widget = QTreeWidget()
39
+
40
+ save_analysis_button = QPushButton("Save Analyses")
41
+ save_analysis_button.clicked.connect(self.save_analyses)
42
+
43
+ self.tree_frame_layout.addWidget(save_analysis_button)
44
+ self.tree_frame_layout.addWidget(self.tree_widget)
45
+
46
+ self.tree_widget.setColumnCount(2)
47
+ self.tree_widget.setHeaderLabels(["Analysis", "Enabled"])
48
+ header = self.tree_widget.header()
49
+ assert header is not None
50
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
51
+
52
+ for index, option in enumerate(options):
53
+ item = QTreeWidgetItem([option])
54
+ if index > 5:
55
+ item.setCheckState(1, Qt.CheckState.Unchecked)
56
+ else:
57
+ item.setData(1, Qt.ItemDataRole.CheckStateRole, Qt.CheckState.Checked)
58
+ item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsUserCheckable & ~Qt.ItemFlag.ItemIsEnabled)
59
+
60
+ self.tree_widget.addTopLevelItem(item)
61
+
62
+ self.tree_widget.itemChanged.connect(self.handleItemChanged)
63
+
64
+ self.base_layout = QHBoxLayout()
65
+ self.base_layout.addLayout(self.tree_frame_layout, 1)
66
+
67
+ self.main_layout = None
68
+ layout_scroll = create_scroll_area(self, False)
69
+ self.base_layout.addLayout(layout_scroll, 4)
70
+
71
+ assert self.main_layout is not None and isinstance(self.main_layout, QVBoxLayout)
72
+
73
+ self.analysis_widgets = [
74
+ AerodynamicsWidget,
75
+ AtmosphereWidget,
76
+ PlanetsWidget,
77
+ WeightsWidget,
78
+ GeometryWidget,
79
+ EnergyWidget,
80
+ PropulsionWidget,
81
+ CostsWidget,
82
+ AeroacousticsWidget,
83
+ StabilityWidget,
84
+ ]
85
+ self.widgets = []
86
+
87
+ for index, analysis_widget in enumerate(self.analysis_widgets):
88
+ widget = analysis_widget()
89
+ assert isinstance(widget, AnalysisDataWidget)
90
+ if index >= 5:
91
+ widget.setVisible(False)
92
+ else:
93
+ widget.setVisible(True)
94
+
95
+ self.widgets.append(widget)
96
+ self.main_layout.addWidget(widget)
97
+
98
+ self.main_layout.addStretch()
99
+ self.main_layout.setSpacing(3)
100
+ self.base_layout.setSpacing(3)
101
+ self.setLayout(self.base_layout)
102
+
103
+ # ============================================================
104
+ # Logic for enabling/disabling sub-analyses
105
+ # ============================================================
106
+ def handleItemChanged(self, item, column):
107
+ if column != 1:
108
+ return
109
+
110
+ index = self.tree_widget.indexOfTopLevelItem(item)
111
+ layout_item = self.main_layout.itemAt(index)
112
+ assert layout_item is not None
113
+ widget = layout_item.widget()
114
+ assert widget is not None
115
+ widget.setVisible(item.checkState(1) == Qt.CheckState.Checked)
116
+
117
+ def load_from_values(self):
118
+ if not rcaide_io.analysis_data:
119
+ self.save_analyses()
120
+ return
121
+
122
+ for index, widget in enumerate(self.widgets):
123
+ assert isinstance(widget, AnalysisDataWidget)
124
+ widget.load_values(rcaide_io.analysis_data[index])
125
+
126
+ self.save_analyses()
127
+
128
+ def get_check_state(self, index) -> bool:
129
+ top_level_item = self.tree_widget.topLevelItem(index)
130
+ assert top_level_item is not None
131
+ return top_level_item.checkState(1) == Qt.CheckState.Checked
132
+
133
+ def save_analyses(self):
134
+ if (not getattr(values, "config_data", None) and
135
+ not getattr(values, "rcaide_configs", None)):
136
+ raise RuntimeError(
137
+ "No aircraft configuration data found. "
138
+ "Create a configuration in the Aircraft Configs tab."
139
+ )
140
+ if (not getattr(values, "rcaide_configs", None) or
141
+ not rcaide_io.rcaide_configs):
142
+ from tabs.aircraft_configs.aircraft_configs import build_rcaide_configs_from_geometry
143
+ try:
144
+ rcaide_io.rcaide_configs = build_rcaide_configs_from_geometry()
145
+ except Exception as exc:
146
+ raise RuntimeError(
147
+ "No RCAIDE configurations found. "
148
+ "Save a configuration in the Aircraft Configs tab."
149
+ ) from exc
150
+ rcaide_io.analysis_data = []
151
+ rcaide_io.rcaide_analyses = {}
152
+
153
+ # Build and save one RCAIDE analysis container per aircraft configuration.
154
+ for tag, config in rcaide_io.rcaide_configs.items():
155
+
156
+ analyses = RCAIDE.Framework.Analyses.Vehicle()
157
+ analyses.vehicle = config
158
+
159
+ for index, widget in enumerate(self.widgets):
160
+ assert isinstance(widget, AnalysisDataWidget)
161
+
162
+ if self.get_check_state(index):
163
+ analyses.append(widget.create_analysis(config))
164
+
165
+ rcaide_io.rcaide_analyses[tag] = analyses
166
+ rcaide_io.analysis_data.append(analyses)