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,232 @@
1
+ # RCAIDE_GUI/tabs/geometry/aircraft_configs.py
2
+
3
+ # RCAIDE imports
4
+ import RCAIDE
5
+
6
+ # RCAIDE-GUI imports
7
+ from tabs.geometry.frames import *
8
+ from tabs import TabWidget
9
+ from utilities import set_data
10
+ import rcaide_io
11
+ from common_widgets import Color
12
+
13
+ # PyQt imports
14
+ from PyQt6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QComboBox, QStackedLayout, QTreeWidget, QTreeWidgetItem, QLabel, QLineEdit
15
+
16
+ # Python imports
17
+ import json
18
+ from typing import Type
19
+
20
+ # WARNING: This file is incomplete and is not intended to be used yet.
21
+
22
+ # ------------------------------------------------------------------------------
23
+ # Aircraft Configs Widget
24
+ # ------------------------------------------------------------------------------
25
+ class AircraftConfigsWidget(TabWidget):
26
+ def __init__(self):
27
+ """Create a widget for entering vehicle geometry."""
28
+ super(AircraftConfigsWidget, self).__init__()
29
+
30
+ # Define actions based on the selected index
31
+ self.frames: list[Type[GeometryFrame]] = [DefaultFrame, FuselageFrame, WingsFrame,
32
+ LandingGearFrame, PowertrainFrame]
33
+
34
+ self.tabs = ["", "Booms", "Cargo Bars", "Fuselages","Landing Gear","Wings", "Powertrain"]
35
+
36
+ options = ["Add Vehicle Component", "Add Boom", "Add Cargo Bay", "Add Fuselage",
37
+ "Add Landing Gear" , "Add Powertrain", "Add Wing"]
38
+
39
+ self.selected_config_index = 0
40
+
41
+ rcaide_io.config_data = [[]]
42
+
43
+ for _ in range(len(self.tabs)):
44
+ rcaide_io.config_data[0].append([])
45
+
46
+ base_layout = QHBoxLayout()
47
+ self.tree_frame_layout = QVBoxLayout()
48
+ self.right_layout = QVBoxLayout()
49
+ self.main_layout = QStackedLayout()
50
+
51
+ for index, frame in enumerate(self.frames):
52
+ frame_widget = frame()
53
+ frame_widget.set_save_function(self.save_data)
54
+ frame_widget.set_tab_index(index)
55
+ self.main_layout.addWidget(frame_widget) # type: ignore
56
+
57
+ vehicle_name_layout = QHBoxLayout()
58
+ vehicle_name_layout.addWidget(QLabel("Configuration Name:"))
59
+ rcaide_io.vehicle_name_input = QLineEdit()
60
+ vehicle_name_layout.addWidget(rcaide_io.vehicle_name_input)
61
+ self.tree_frame_layout.addLayout(vehicle_name_layout)
62
+
63
+ self.dropdown = QComboBox()
64
+ self.dropdown.addItems(options)
65
+ self.dropdown.currentIndexChanged.connect(self.on_dropdown_change)
66
+ self.tree_frame_layout.addWidget(self.dropdown)
67
+
68
+ # Create a QComboBox and add options
69
+ self.tree = QTreeWidget()
70
+ self.tree.setColumnCount(1)
71
+ self.tree.setHeaderLabels(["Vehicle Components"])
72
+ self.tree.itemClicked.connect(self.on_tree_item_clicked)
73
+
74
+ config_items = [QTreeWidgetItem(["Base Configuration"])]
75
+ self.tree.addTopLevelItems(config_items)
76
+ self.tree_frame_layout.addWidget(self.tree)
77
+
78
+ self.right_layout.addWidget(Color("blue"), 3)
79
+ self.right_layout.addLayout(self.main_layout, 7)
80
+ base_layout.addLayout(self.tree_frame_layout, 1)
81
+ base_layout.addLayout(self.right_layout, 4)
82
+
83
+ self.main_layout.setSpacing(3)
84
+ base_layout.setSpacing(3)
85
+
86
+ # Initially display the DefaultFrame
87
+ self.main_layout.setCurrentIndex(0)
88
+ self.setLayout(base_layout)
89
+
90
+ def on_dropdown_change(self, index):
91
+ """Change the index of the main layout based on the selected index of the dropdown.
92
+
93
+ Args:
94
+ index: The index of the selected item in the dropdown.
95
+ """
96
+ layout = self.layout()
97
+ if layout:
98
+ self.main_layout.setCurrentIndex(index)
99
+
100
+ def on_tree_item_clicked(self, item: QTreeWidgetItem, _col):
101
+ """Change the index of the main layout based on the selected item in the tree.
102
+
103
+ Args:
104
+ item: The selected item in the tree.
105
+ _col: The column index of the selected item. (Not used)
106
+
107
+ """
108
+ assert item is not None
109
+ # get item depth
110
+ depth = 0
111
+
112
+ item2: QTreeWidgetItem = item
113
+ while item2.parent():
114
+ parent = item2.parent()
115
+ assert parent is not None
116
+
117
+ item2 = parent
118
+ depth += 1
119
+
120
+ if depth == 0:
121
+ self.main_layout.setCurrentIndex(0)
122
+ return
123
+ if depth == 1:
124
+ top_item = item.parent()
125
+
126
+ assert top_item is not None
127
+ tree_index = top_item.indexOfChild(item)
128
+ tab_index = self.find_tab_index(tree_index)
129
+
130
+ self.main_layout.setCurrentIndex(tab_index)
131
+ if depth == 2:
132
+ component_item = item.parent()
133
+
134
+ assert component_item is not None
135
+ top_item = component_item.parent()
136
+ assert top_item is not None
137
+
138
+ tree_index = top_item.indexOfChild(component_item)
139
+ tab_index = self.find_tab_index(tree_index)
140
+ self.main_layout.setCurrentIndex(tab_index)
141
+
142
+ index = component_item.indexOfChild(item)
143
+ frame = self.main_layout.currentWidget()
144
+ assert isinstance(frame, GeometryFrame)
145
+ frame.load_data(rcaide_io.config_data[tab_index][index], index)
146
+
147
+
148
+ def save_data(self, tab_index, vehicle_component=None, index=0, data=None, new=False):
149
+ """Save the entered data in a frame to the list.
150
+
151
+ Args:
152
+ tab_index: The index of the tab.
153
+ index: The index of the vehicle element in the list. (Within its type, eg fuselage #0, #1, etc.)
154
+ vehicle_component: The vehicle component to be appended to the vehicle.
155
+ data: The data to be saved.
156
+ new: A flag to indicate if the data is of a new element.
157
+ """
158
+ if data is None:
159
+ return
160
+
161
+ top_item = self.tree.topLevelItem(self.selected_config_index)
162
+ tree_index = self.find_tree_index(tab_index)
163
+ assert top_item is not None
164
+
165
+ if not rcaide_io.config_data[tab_index]:
166
+ component_item = QTreeWidgetItem([self.tabs[tab_index]])
167
+ top_item.insertChild(tree_index, component_item)
168
+
169
+ if new:
170
+ rcaide_io.config_data[tab_index].append(data)
171
+ child = QTreeWidgetItem([data["name"]])
172
+ item = top_item.child(tree_index)
173
+ assert item is not None
174
+ item.addChild(child)
175
+ index = item.indexOfChild(child)
176
+ else:
177
+ rcaide_io.config_data[tab_index][index] = data
178
+ child = top_item.child(tree_index)
179
+ assert child is not None
180
+ child = child.child(index)
181
+ assert child is not None
182
+ child.setText(0, data["name"])
183
+
184
+ # with open("app_data/geometry.json", "w") as f:
185
+ # f.write(json.dumps(rcaide_io.config_data, indent=2))
186
+
187
+ if vehicle_component:
188
+ # Check if it is an energy network being added
189
+ if tab_index == 5:
190
+ rcaide_io.vehicle.append_energy_network(vehicle_component)
191
+ else:
192
+ rcaide_io.vehicle.append_component(vehicle_component)
193
+
194
+ return index
195
+
196
+ def get_vehicle(self):
197
+ return rcaide_io.vehicle
198
+
199
+ def get_data(self):
200
+ return rcaide_io.config_data
201
+
202
+ def find_tree_index(self, tab_index):
203
+ tree_index = tab_index
204
+ for i in range(tree_index):
205
+ if not rcaide_io.config_data[i]:
206
+ tree_index -= 1
207
+
208
+ tree_index = max(0, tree_index)
209
+ return tree_index
210
+
211
+ def find_tab_index(self, tree_index):
212
+ print(tree_index)
213
+ tab_index = 0
214
+ count = 0
215
+
216
+ for i in range(1, len(rcaide_io.config_data)):
217
+ if not rcaide_io.config_data[i]:
218
+ continue
219
+ count += 1
220
+ if count == tree_index + 1:
221
+ tab_index = i
222
+ break
223
+ return tab_index
224
+
225
+
226
+ def get_widget() -> QWidget:
227
+ """Return the geometry widget.
228
+
229
+ Returns:
230
+ The geometry widget.
231
+ """
232
+ return AircraftConfigsWidget()
@@ -0,0 +1,15 @@
1
+ # RCAIDE_GUI/tabs/geometry/frames/__init__.py
2
+ # ----------------------------------------------------------------------------------------------------------------------
3
+ # IMPORT
4
+ # ----------------------------------------------------------------------------------------------------------------------
5
+
6
+ from tabs.geometry.frames.geometry_frame import GeometryFrame
7
+ from tabs.geometry.frames.default_frame import DefaultFrame
8
+ from tabs.geometry.frames.vehicle_frame import VehicleFrame
9
+
10
+ from tabs.geometry.frames.booms import *
11
+ from tabs.geometry.frames.cargo_bays import *
12
+ from tabs.geometry.frames.fuselages import *
13
+ from tabs.geometry.frames.landing_gears import *
14
+ from tabs.geometry.frames.powertrain import *
15
+ from tabs.geometry.frames.wings import *
@@ -0,0 +1,5 @@
1
+ # RCAIDE_GUI/tabs/geometry/frames/booms/__init__.py
2
+ # ----------------------------------------------------------------------------------------------------------------------
3
+ # IMPORT
4
+ # ----------------------------------------------------------------------------------------------------------------------
5
+ from tabs.geometry.frames.booms.boom_frame import BoomFrame
@@ -0,0 +1,238 @@
1
+ # RCAIDE_GUI/tabs/geometry/frames/booms/boom_frame.py
2
+ #
3
+ # Created: Dec 2025, M. Clarke
4
+
5
+ # ----------------------------------------------------------------------------------------------------------------------
6
+ # IMPORT
7
+ # ----------------------------------------------------------------------------------------------------------------------
8
+ # RCAIDE imports
9
+ import RCAIDE
10
+
11
+ # RCAIDE GUI imports
12
+ from tabs.geometry.frames import GeometryFrame
13
+ from tabs.geometry.widgets.booms import BoomSectionWidget
14
+ from utilities import show_popup, create_line_bar, set_data, Units, create_scroll_area, clear_layout
15
+ from common_widgets import DataEntryWidget
16
+
17
+ # PyQT imports
18
+ from PyQt6.QtWidgets import QWidget, QLabel, QVBoxLayout, QPushButton, QLineEdit, QHBoxLayout, \
19
+ QSpacerItem, QSizePolicy, QScrollArea
20
+
21
+ # ----------------------------------------------------------------------------------------------------------------------
22
+ # Boom Frame
23
+ # ----------------------------------------------------------------------------------------------------------------------
24
+ class BoomFrame(GeometryFrame):
25
+ # List of data labels
26
+ data_units_labels = [
27
+ ("Fineness Nose", Units.Unitless, "fineness.nose"),
28
+ ("Fineness Tail", Units.Unitless, "fineness.tail"),
29
+ ("Lengths Nose", Units.Length, "lengths.nose"),
30
+ ("Lengths Tail", Units.Length, "lengths.tail"),
31
+ ("Lengths Total", Units.Length, "lengths.total"),
32
+ ("Width", Units.Length, "width"),
33
+ ("Heights Maximum", Units.Length, "heights.maximum"),
34
+ ("Height at Quarter", Units.Length, "heights.at_quarter_length"),
35
+ ("Height at Three Quarters", Units.Length, "heights.at_three_quarters_length"),
36
+ ("Height at Wing Root Quarter Chord", Units.Length, "heights.at_wing_root_quarter_chord"),
37
+ ("Areas Side Projected", Units.Area, "areas.side_projected"),
38
+ ("Area Wetted", Units.Area, "areas.wetted"),
39
+ ("Area Front Projected", Units.Area, "areas.front_projected"),
40
+ ("Differential Pressure", Units.Pressure, "differential_pressure"),
41
+ ("Effective Diameter", Units.Length, "effective_diameter"),
42
+ ]
43
+
44
+ def __init__(self):
45
+ """Create a frame for entering boom data."""
46
+ super(BoomFrame, self).__init__()
47
+ self.data_entry_widget: DataEntryWidget | None = None
48
+
49
+ create_scroll_area(self)
50
+
51
+ assert self.main_layout is not None
52
+ self.main_layout.addWidget(QLabel("<b>Booms</b>"))
53
+ self.main_layout.addWidget(create_line_bar())
54
+ self.index = -1
55
+
56
+ self.add_name_layout()
57
+
58
+ # Add the data entry widget to the main layout
59
+ self.data_entry_widget = DataEntryWidget(self.data_units_labels)
60
+ self.wire_auto_save(self.data_entry_widget)
61
+ self.main_layout.addWidget(self.data_entry_widget)
62
+ self.main_layout.addWidget(create_line_bar())
63
+
64
+ # Add the sections layout to the main layout
65
+ self.boom_sections_layout = QVBoxLayout()
66
+ self.main_layout.addLayout(self.boom_sections_layout)
67
+
68
+ self.add_buttons_layout()
69
+
70
+ # Adds scroll function
71
+ self.main_layout.addItem(QSpacerItem(
72
+ 20, 40, QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Expanding))
73
+
74
+ def add_name_layout(self):
75
+ name_layout = QHBoxLayout()
76
+ spacer_left = QSpacerItem(
77
+ 50, 5, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
78
+ spacer_right = QSpacerItem(
79
+ 200, 5, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
80
+ self.name_line_edit = QLineEdit(self)
81
+ name_layout.addItem(spacer_left)
82
+ name_layout.addWidget(QLabel("Name: "))
83
+ name_layout.addWidget(self.name_line_edit)
84
+ name_layout.addItem(spacer_right)
85
+
86
+ assert self.main_layout is not None
87
+ self.main_layout.addLayout(name_layout)
88
+
89
+ # noinspection PyUnresolvedReferences
90
+ def add_buttons_layout(self):
91
+ """Add the save, delete, and new buttons to the layout."""
92
+ new_section_button = QPushButton("New Boom Section", self)
93
+ new_section_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
94
+ save_button = QPushButton("Save Data", self)
95
+ save_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
96
+ delete_button = QPushButton("Delete Data", self)
97
+ delete_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
98
+ new_button = QPushButton("New Boom Structure", self)
99
+ new_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
100
+
101
+ new_section_button.clicked.connect(self.add_boom_section)
102
+ save_button.clicked.connect(self.save_data)
103
+ delete_button.clicked.connect(self.delete_data)
104
+ new_button.clicked.connect(self.create_new_structure)
105
+
106
+ buttons_layout = QHBoxLayout()
107
+ buttons_layout.addWidget(new_section_button)
108
+ buttons_layout.addWidget(save_button)
109
+ buttons_layout.addWidget(delete_button)
110
+ buttons_layout.addWidget(new_button)
111
+
112
+ assert self.main_layout is not None
113
+ self.main_layout.addLayout(buttons_layout)
114
+
115
+ # noinspection DuplicatedCode
116
+ def save_data(self):
117
+ """Call the save function and pass the entered data to it."""
118
+ entered_data, boom = self.get_data_values()
119
+ if self.save_function:
120
+ if self.index >= 0:
121
+ self.index = self.save_function(
122
+ tab_index=self.tab_index, index=self.index, data=entered_data, persist=True)
123
+ return
124
+ else:
125
+ self.index = self.save_function(
126
+ tab_index=self.tab_index, vehicle_component=boom, data=entered_data, new=True, persist=True)
127
+
128
+ show_popup("Data Saved!", self)
129
+
130
+ def add_boom_section(self):
131
+ self.boom_sections_layout.addWidget(BoomSectionWidget(
132
+ self.boom_sections_layout.count(), self.delete_boom_section))
133
+
134
+ def delete_boom_section(self, index):
135
+ item = self.boom_sections_layout.itemAt(index)
136
+ assert item is not None
137
+ widget = item.widget()
138
+ assert widget is not None
139
+
140
+ widget.deleteLater()
141
+ self.boom_sections_layout.removeWidget(widget)
142
+ self.boom_sections_layout.update()
143
+
144
+ for i in range(index, self.boom_sections_layout.count()):
145
+ item = self.boom_sections_layout.itemAt(i)
146
+ if item is None:
147
+ continue
148
+
149
+ widget = item.widget()
150
+ if widget is not None and isinstance(widget, BoomSectionWidget):
151
+ widget.index = i
152
+
153
+ # TODO: Implement proper deletion of data
154
+ def delete_data(self):
155
+ pass
156
+
157
+ def create_new_structure(self):
158
+ """Create a new boom structure."""
159
+ # Clear the main data values
160
+ assert self.data_entry_widget is not None and self.name_line_edit is not None
161
+ self.data_entry_widget.clear_values()
162
+ self.name_line_edit.clear()
163
+
164
+ # Clear the boom sections
165
+ for i in range(self.boom_sections_layout.count()):
166
+ item = self.boom_sections_layout.itemAt(i)
167
+ assert item is not None
168
+
169
+ widget = item.widget()
170
+ assert widget is not None
171
+
172
+ widget.deleteLater()
173
+
174
+ self.boom_sections_layout.update()
175
+ self.index = -1
176
+
177
+ def create_rcaide_structure(self):
178
+ assert self.data_entry_widget is not None
179
+ data = self.data_entry_widget.get_values_si()
180
+ boom = RCAIDE.Library.Components.Booms.Boom()
181
+ for data_unit_label in self.data_units_labels:
182
+ rcaide_label = data_unit_label[-1]
183
+ user_label = data_unit_label[0]
184
+ set_data(boom, rcaide_label, data[user_label][0])
185
+
186
+ for i in range(self.boom_sections_layout.count()):
187
+ item = self.boom_sections_layout.itemAt(i)
188
+ if item is None:
189
+ continue
190
+
191
+ boom_section = item.widget()
192
+ if boom_section is not None and isinstance(boom_section, BoomSectionWidget):
193
+ _, segment = boom_section.get_data_values()
194
+ boom.segments.append(segment)
195
+
196
+ return boom
197
+
198
+ def get_data_values(self):
199
+ """Retrieve the entered data values from the text fields."""
200
+ assert self.data_entry_widget is not None
201
+ data = self.data_entry_widget.get_values()
202
+ boom = self.create_rcaide_structure()
203
+
204
+ data["sections"] = []
205
+ for i in range(self.boom_sections_layout.count()):
206
+ item = self.boom_sections_layout.itemAt(i)
207
+ if item is None:
208
+ continue
209
+
210
+ boom_section = item.widget()
211
+ if boom_section is not None and isinstance(boom_section, BoomSectionWidget):
212
+ segment_data, segment = boom_section.get_data_values()
213
+ data["sections"].append(segment_data)
214
+ boom.append_segment(segment)
215
+
216
+ assert self.name_line_edit is not None
217
+ data["name"] = self.name_line_edit.text()
218
+ return data, boom
219
+
220
+ def load_data(self, data, index):
221
+ """Load the data into the widgets.
222
+
223
+ Args:
224
+ data: The data to be loaded into the widgets.
225
+ index: The index of the data in the list.
226
+ """
227
+ assert self.data_entry_widget is not None
228
+ self.data_entry_widget.load_data(data)
229
+
230
+ clear_layout(self.boom_sections_layout)
231
+
232
+ for section in data["sections"]:
233
+ self.boom_sections_layout.addWidget(BoomSectionWidget(
234
+ self.boom_sections_layout.count(), self.delete_boom_section, section))
235
+
236
+ assert self.name_line_edit is not None
237
+ self.name_line_edit.setText(data["name"])
238
+ self.index = index
@@ -0,0 +1,5 @@
1
+ # RCAIDE_GUI/tabs/geometry/frames/cargo_bays/__init__.py
2
+ # ----------------------------------------------------------------------------------------------------------------------
3
+ # IMPORT
4
+ # ----------------------------------------------------------------------------------------------------------------------
5
+ from tabs.geometry.frames.cargo_bays.cargo_bay_frame import CargoBayFrame
@@ -0,0 +1,159 @@
1
+ # RCAIDE_GUI/tabs/geometry/frames/cargo_bays/cargo_bay_frame.py
2
+ #
3
+ # Created: Dec 2025, M. Clarke
4
+
5
+ # ----------------------------------------------------------------------------------------------------------------------
6
+ # IMPORT
7
+ # ----------------------------------------------------------------------------------------------------------------------
8
+ # RCAIDE imports
9
+ import RCAIDE
10
+
11
+ # PyQT imports
12
+ from PyQt6.QtWidgets import QWidget, QLabel, QVBoxLayout, QPushButton, QLineEdit, QHBoxLayout, \
13
+ QSpacerItem, QSizePolicy, QScrollArea
14
+
15
+ # RCAIDE GUI imports
16
+ from tabs.geometry.frames import GeometryFrame
17
+ from utilities import show_popup, create_line_bar, set_data, Units, create_scroll_area, clear_layout
18
+ from common_widgets import DataEntryWidget
19
+
20
+ # ----------------------------------------------------------------------------------------------------------------------
21
+ # Cargo Bay Frame
22
+ # ----------------------------------------------------------------------------------------------------------------------
23
+ class CargoBayFrame(GeometryFrame):
24
+ # List of data labels
25
+ data_units_labels = [
26
+ ("Length", Units.Unitless, "length"),
27
+ ("Width", Units.Unitless, "width"),
28
+ ("Height", Units.Length, "height"),
29
+ # RCAIDE Cargo_Bay stores cargo mass directly; it has no baggage/container subobjects.
30
+ ("Cargo Mass", Units.Mass, "mass_properties.mass"),
31
+ ("Center of Gravity", Units.Position, "mass_properties.center_of_gravity"),
32
+ ("Origin", Units.Position, "origin"),
33
+ ]
34
+
35
+ def __init__(self):
36
+ """Create a frame for entering cargo bay data."""
37
+ super(CargoBayFrame, self).__init__()
38
+ self.data_entry_widget: DataEntryWidget | None = None
39
+
40
+ create_scroll_area(self)
41
+
42
+ assert self.main_layout is not None
43
+ self.main_layout.addWidget(QLabel("<b>Cargo Bays</b>"))
44
+ self.main_layout.addWidget(create_line_bar())
45
+ self.index = -1
46
+
47
+ self.add_name_layout()
48
+
49
+ # Add the data entry widget to the main layout
50
+ self.data_entry_widget = DataEntryWidget(self.data_units_labels)
51
+ self.wire_auto_save(self.data_entry_widget)
52
+ self.main_layout.addWidget(self.data_entry_widget)
53
+ self.main_layout.addWidget(create_line_bar())
54
+
55
+ self.add_buttons_layout()
56
+
57
+ # Adds scroll function
58
+ self.main_layout.addItem(QSpacerItem(
59
+ 20, 40, QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Expanding))
60
+
61
+ def add_name_layout(self):
62
+ name_layout = QHBoxLayout()
63
+ spacer_left = QSpacerItem(
64
+ 50, 5, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
65
+ spacer_right = QSpacerItem(
66
+ 200, 5, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
67
+ self.name_line_edit = QLineEdit(self)
68
+ name_layout.addItem(spacer_left)
69
+ name_layout.addWidget(QLabel("Name: "))
70
+ name_layout.addWidget(self.name_line_edit)
71
+ name_layout.addItem(spacer_right)
72
+
73
+ assert self.main_layout is not None
74
+ self.main_layout.addLayout(name_layout)
75
+
76
+ # noinspection PyUnresolvedReferences
77
+ def add_buttons_layout(self):
78
+ """Add the save, delete, and new buttons to the layout."""
79
+ save_button = QPushButton("Save Data", self)
80
+ save_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
81
+ delete_button = QPushButton("Delete Data", self)
82
+ delete_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
83
+ new_button = QPushButton("New Cargo Bay", self)
84
+ new_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
85
+
86
+ save_button.clicked.connect(self.save_data)
87
+ delete_button.clicked.connect(self.delete_data)
88
+ new_button.clicked.connect(self.create_new_structure)
89
+
90
+ buttons_layout = QHBoxLayout()
91
+ buttons_layout.addWidget(save_button)
92
+ buttons_layout.addWidget(delete_button)
93
+ buttons_layout.addWidget(new_button)
94
+
95
+ assert self.main_layout is not None
96
+ self.main_layout.addLayout(buttons_layout)
97
+
98
+ # noinspection DuplicatedCode
99
+ def save_data(self):
100
+ """Call the save function and pass the entered data to it."""
101
+ entered_data, cargo_bay = self.get_data_values()
102
+ if self.save_function:
103
+ if self.index >= 0:
104
+ self.index = self.save_function(
105
+ tab_index=self.tab_index, index=self.index, data=entered_data, persist=True)
106
+ return
107
+ else:
108
+ self.index = self.save_function(
109
+ tab_index=self.tab_index, vehicle_component=cargo_bay, data=entered_data, new=True, persist=True)
110
+
111
+ show_popup("Data Saved!", self)
112
+ else:
113
+ print("No save function set.")
114
+
115
+ # TODO: Implement proper deletion of data
116
+ def delete_data(self):
117
+ pass
118
+
119
+ def create_new_structure(self):
120
+ """Create a new cargo bay structure."""
121
+ # Clear the main data values
122
+ assert self.data_entry_widget is not None and self.name_line_edit is not None
123
+ self.data_entry_widget.clear_values()
124
+ self.name_line_edit.clear()
125
+
126
+ def create_rcaide_structure(self):
127
+ assert self.data_entry_widget is not None
128
+ data = self.data_entry_widget.get_values_si()
129
+ cargo_bay = RCAIDE.Library.Components.Cargo_Bays.Cargo_Bay()
130
+ for data_unit_label in self.data_units_labels:
131
+ rcaide_label = data_unit_label[-1]
132
+ user_label = data_unit_label[0]
133
+ set_data(cargo_bay, rcaide_label, data[user_label][0])
134
+
135
+ return cargo_bay
136
+
137
+ def get_data_values(self):
138
+ """Retrieve the entered data values from the text fields."""
139
+ assert self.data_entry_widget is not None
140
+ data = self.data_entry_widget.get_values()
141
+ cargo_bay = self.create_rcaide_structure()
142
+
143
+ assert self.name_line_edit is not None
144
+ data["name"] = self.name_line_edit.text()
145
+ return data, cargo_bay
146
+
147
+ def load_data(self, data, index):
148
+ """Load the data into the widgets.
149
+
150
+ Args:
151
+ data: The data to be loaded into the widgets.
152
+ index: The index of the data in the list.
153
+ """
154
+ assert self.data_entry_widget is not None
155
+ self.data_entry_widget.load_data(data)
156
+
157
+ assert self.name_line_edit is not None
158
+ self.name_line_edit.setText(data["name"])
159
+ self.index = index
@@ -0,0 +1,23 @@
1
+ # RCAIDE_GUI/tabs/geometry/frames/default_frame.py
2
+ #
3
+ # Created: Dec 2025, M. Clarke
4
+
5
+ # ----------------------------------------------------------------------------------------------------------------------
6
+ # IMPORT
7
+ # ----------------------------------------------------------------------------------------------------------------------
8
+ from PyQt6.QtCore import Qt
9
+ from PyQt6.QtWidgets import QWidget, QLabel, QVBoxLayout
10
+
11
+ from tabs.geometry.frames import GeometryFrame
12
+
13
+
14
+ class DefaultFrame(GeometryFrame):
15
+ def __init__(self):
16
+ super(DefaultFrame, self).__init__()
17
+
18
+ # Create a QVBoxLayout for the DefaultFrame
19
+ layout = QVBoxLayout(self)
20
+
21
+ # Add a QLabel with the text to the layout
22
+ label = QLabel("Please select or create a configuration to begin!")
23
+ layout.addWidget(label, alignment=Qt.AlignmentFlag.AlignCenter)
@@ -0,0 +1,2 @@
1
+ # RCAIDE_GUI/tabs/geometry/frames/energy_network/__init__.py
2
+ from . import turbofan_network
@@ -0,0 +1,3 @@
1
+ # RCAIDE_GUI/tabs/geometry/frames/energy_network/turbofan_network/__init__.py
2
+ from . import frames
3
+ from . import widgets