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,74 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/powertrain/converters/turboelectric_generator_widget.py
2
+
3
+ # Created: Dec 2025, M. Clarke
4
+ # ------------------------------------------------------------------------------
5
+ # Imports
6
+ # ------------------------------------------------------------------------------
7
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QTabWidget, QPushButton, QLineEdit
8
+ from RCAIDE.Library.Components.Powertrain.Converters import Turboelectric_Generator
9
+
10
+ from tabs.geometry.widgets import GeometryDataWidget
11
+ from common_widgets import DataEntryWidget
12
+
13
+
14
+ class TurboelectricGeneratorWidget(GeometryDataWidget):
15
+ def __init__(self, index, on_delete, data_values=None):
16
+ super(TurboelectricGeneratorWidget, self).__init__()
17
+
18
+ self.index = index
19
+ self.data_entry_widget: DataEntryWidget | None = None
20
+ self.on_delete = on_delete
21
+
22
+ self.main_section_layout = QVBoxLayout()
23
+
24
+ layout = self.create_scroll_layout()
25
+
26
+ # Segment Name layout
27
+ self.name_layout = QHBoxLayout()
28
+ self.section_name_edit = QLineEdit(self)
29
+ self.name_layout.addWidget(QLabel("Converter Name: "))
30
+ self.name_layout.addWidget(self.section_name_edit)
31
+ layout.addLayout(self.name_layout)
32
+
33
+ # delete_button = QPushButton("Delete Converter", self)
34
+ # delete_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
35
+ # delete_button.clicked.connect(self.delete_button_pressed)
36
+
37
+ # layout.addWidget(delete_button)
38
+ layout.setContentsMargins(0, 0, 0, 0)
39
+ self.setLayout(layout)
40
+
41
+ if data_values:
42
+ self.load_data_values(data_values)
43
+
44
+ def get_data_values(self):
45
+ title = self.section_name_edit.text()
46
+ # data = self.data_entry_widget.get_values()
47
+ data = {"Converter Name": title}
48
+ # data_si = self.data_entry_widget.get_values_si()
49
+ data_si = {}
50
+ return data, self.create_rcaide_structure(data_si)
51
+
52
+
53
+ def create_rcaide_structure(self, data):
54
+ te = Turboelectric_Generator()
55
+ te.tag = self.section_name_edit.text()
56
+ return te
57
+
58
+ def delete_button_pressed(self):
59
+ if self.on_delete is None:
60
+ print("on_delete is None")
61
+ return
62
+
63
+ self.on_delete(self.index)
64
+
65
+ def create_scroll_layout(self):
66
+ # Create a widget to contain the layoutd
67
+ scroll_content = QWidget()
68
+ # Set the main layout inside the scroll content
69
+ layout = QVBoxLayout(scroll_content)
70
+
71
+ # Set the main layout of the widget
72
+ self.setLayout(layout)
73
+
74
+ return layout
@@ -0,0 +1,5 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/powertrain/distributors/__init__.py
2
+ # ----------------------------------------------------------------------------------------------------------------------
3
+ # IMPORT
4
+ # ----------------------------------------------------------------------------------------------------------------------
5
+ from tabs.geometry.widgets.powertrain.distributors.fuel_line_widget import FuelLineWidget
@@ -0,0 +1,81 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/powertrain/distributors/fuel_line_widget.py
2
+
3
+ # Created: Dec 2025, M. Clarke
4
+ # ------------------------------------------------------------------------------
5
+ # Imports
6
+ # ------------------------------------------------------------------------------
7
+ import RCAIDE
8
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QTabWidget, QPushButton, QLineEdit
9
+
10
+ from tabs.geometry.widgets import GeometryDataWidget
11
+ from common_widgets import DataEntryWidget
12
+
13
+
14
+ class FuelLineWidget(GeometryDataWidget):
15
+ def __init__(self, index, on_delete, data_values=None):
16
+ super(FuelLineWidget, self).__init__()
17
+
18
+ self.index = index
19
+ self.data_entry_widget: DataEntryWidget | None = None
20
+ self.on_delete = on_delete
21
+
22
+ self.main_section_layout = QVBoxLayout()
23
+
24
+ layout = self.create_scroll_layout()
25
+
26
+ # Segment Name layout
27
+ self.name_layout = QHBoxLayout()
28
+ self.section_name_edit = QLineEdit(self)
29
+ self.name_layout.addWidget(QLabel("Fuel Line Name: "))
30
+ self.name_layout.addWidget(self.section_name_edit)
31
+ layout.addLayout(self.name_layout)
32
+
33
+ delete_button = QPushButton("Delete Fuel Line", self)
34
+ delete_button.setStyleSheet(
35
+ "color:#dbe7ff; font-weight:500; margin:0; padding:0;")
36
+ delete_button.clicked.connect(self.delete_button_pressed)
37
+
38
+ layout.addWidget(delete_button)
39
+ layout.setContentsMargins(0, 0, 0, 0)
40
+ self.setLayout(layout)
41
+
42
+ if data_values:
43
+ self.load_data_values(data_values)
44
+
45
+ def get_data_values(self):
46
+ data = {"distributor name": self.section_name_edit.text()}
47
+ return data, self.create_rcaide_structure(data)
48
+
49
+ def load_data_values(self, data):
50
+ # assert self.data_entry_widget is not None
51
+ # self.data_entry_widget.load_data(data)
52
+ # self.section_name_edit.setText(data["distributor name"])
53
+ if "distributor name" in data:
54
+ self.section_name_edit.setText(data["distributor name"])
55
+
56
+ # def load_data_values(self, section_data):
57
+ # self.data_entry_widget.load_data(section_data)
58
+ # self.section_name_edit.setText(section_data["Source Name"])
59
+
60
+ def create_rcaide_structure(self, data):
61
+ line = RCAIDE.Library.Components.Powertrain.Distributors.Fuel_Line()
62
+ line.tag = data["distributor name"]
63
+ return line
64
+
65
+ def delete_button_pressed(self):
66
+ if self.on_delete is None:
67
+ print("on_delete is None")
68
+ return
69
+
70
+ self.on_delete(self.index)
71
+
72
+ def create_scroll_layout(self):
73
+ # Create a widget to contain the layout
74
+ scroll_content = QWidget()
75
+ # Set the main layout inside the scroll content
76
+ layout = QVBoxLayout(scroll_content)
77
+
78
+ # Set the main layout of the widget
79
+ self.setLayout(layout)
80
+
81
+ return layout
@@ -0,0 +1,5 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/powertrain/modulators/__init__.py
2
+ # ----------------------------------------------------------------------------------------------------------------------
3
+ # IMPORT
4
+ # ----------------------------------------------------------------------------------------------------------------------
5
+ from tabs.geometry.widgets.powertrain.modulators.esc_widget import ESCWidget
@@ -0,0 +1,58 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/powertrain/modulators/esc_widget.py
2
+
3
+ # Created: Dec 2025, M. Clarke
4
+ # ------------------------------------------------------------------------------
5
+ # Imports
6
+ # ------------------------------------------------------------------------------
7
+ import RCAIDE
8
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QTabWidget, QPushButton, QLineEdit
9
+
10
+ from common_widgets import DataEntryWidget
11
+
12
+ class ESCWidget(QWidget):
13
+ def __init__(self, index, on_delete, data_values=None):
14
+ super(ESCWidget, self).__init__()
15
+
16
+ self.index = index
17
+ self.data_entry_widget: DataEntryWidget | None = None
18
+ self.on_delete = on_delete
19
+
20
+ self.main_section_layout = QVBoxLayout()
21
+
22
+ layout = self.create_scroll_layout()
23
+
24
+ # Segment Name layout
25
+ self.name_layout = QHBoxLayout()
26
+ self.section_name_edit = QLineEdit(self)
27
+ self.name_layout.addWidget(QLabel("Electronic Speed Controller Name: "))
28
+ self.name_layout.addWidget(self.section_name_edit)
29
+ layout.addLayout(self.name_layout)
30
+
31
+ #delete_button = QPushButton("Delete Electronic Speed Controller", self)
32
+ #delete_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
33
+ #delete_button.clicked.connect(self.delete_button_pressed)
34
+
35
+ #layout.addWidget(delete_button)
36
+ layout.setContentsMargins(0, 0, 0, 0)
37
+ self.setLayout(layout)
38
+
39
+ if data_values:
40
+ self.load_data_values(data_values, index)
41
+
42
+ def delete_button_pressed(self):
43
+ if self.on_delete is None:
44
+ print("on_delete is None")
45
+ return
46
+
47
+ self.on_delete(self.index)
48
+
49
+ def create_scroll_layout(self):
50
+ # Create a widget to contain the layoutd
51
+ scroll_content = QWidget()
52
+ # Set the main layout inside the scroll content
53
+ layout = QVBoxLayout(scroll_content)
54
+
55
+ # Set the main layout of the widget
56
+ self.setLayout(layout)
57
+
58
+ return layout
@@ -0,0 +1,5 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/nacelles/__init__.py
2
+ # ----------------------------------------------------------------------------------------------------------------------
3
+ # IMPORT
4
+ # ----------------------------------------------------------------------------------------------------------------------
5
+ from tabs.geometry.widgets.powertrain.nacelles.nacelle_section_widget import NacelleSectionWidget
@@ -0,0 +1,115 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/powertrain/nacelles/nacelle_section_widget.py
2
+
3
+ # Created: Dec 2025, M. Clarke
4
+ # ------------------------------------------------------------------------------
5
+ # Imports
6
+ # ------------------------------------------------------------------------------
7
+ # RCAIDE imports
8
+ import RCAIDE
9
+
10
+ from tabs.geometry.widgets import GeometryDataWidget
11
+ # RCAIDE GUI imports
12
+ from utilities import Units
13
+ from common_widgets import DataEntryWidget
14
+
15
+ # PyQT imports
16
+ from PyQt6.QtWidgets import QHBoxLayout, QLabel, QLineEdit, QPushButton, QSizePolicy, QSpacerItem, QVBoxLayout, QWidget, \
17
+ QFrame
18
+
19
+
20
+ # ----------------------------------------------------------------------------------------------------------------------
21
+ # Nacelle Section Widget
22
+ # ----------------------------------------------------------------------------------------------------------------------
23
+ class NacelleSectionWidget(GeometryDataWidget):
24
+ def __init__(self, index, on_delete, section_data=None):
25
+ super(NacelleSectionWidget, self).__init__()
26
+
27
+ # self.data_fields = {}
28
+ self.coordinate_filename = ""
29
+ self.index = index
30
+ self.on_delete = on_delete
31
+ self.data_entry_widget: DataEntryWidget | None = None
32
+
33
+ self.name_layout = QHBoxLayout()
34
+ self.init_ui(section_data)
35
+
36
+ # noinspection DuplicatedCode
37
+ def init_ui(self, section_data):
38
+ main_layout = QVBoxLayout()
39
+ # add spacing
40
+ spacer_left = QSpacerItem(
41
+ 80, 5, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
42
+ spacer_right = QSpacerItem(
43
+ 300, 5, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
44
+ self.name_layout.addItem(spacer_left)
45
+ self.name_layout.addWidget(QLabel("Segment Name: "))
46
+ self.name_layout.addWidget(QLineEdit(self))
47
+ self.name_layout.addItem(spacer_right)
48
+
49
+ main_layout.addLayout(self.name_layout)
50
+
51
+ # List of data labels
52
+ data_units_labels = [
53
+ ("Percent X Location", Units.Unitless),
54
+ ("Percent Z Location", Units.Unitless),
55
+ ("Height", Units.Length),
56
+ ("Width", Units.Length),
57
+ ]
58
+
59
+ self.data_entry_widget = DataEntryWidget(data_units_labels)
60
+ delete_button = QPushButton("Delete Section", self)
61
+ delete_button.setSizePolicy(
62
+ QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
63
+ delete_button.setFixedWidth(150)
64
+ delete_button.clicked.connect(self.delete_button_pressed)
65
+
66
+ # center delete button
67
+ delete_button_layout = QHBoxLayout()
68
+ delete_button_layout.addItem(QSpacerItem(
69
+ 50, 5, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
70
+ delete_button_layout.addWidget(delete_button)
71
+ delete_button_layout.addItem(QSpacerItem(
72
+ 50, 5, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
73
+
74
+ main_layout.addWidget(self.data_entry_widget)
75
+ main_layout.addLayout(delete_button_layout)
76
+
77
+ # Add horizontal bar
78
+ line_bar = QFrame()
79
+ line_bar.setFrameShape(QFrame.Shape.HLine)
80
+ line_bar.setFrameShadow(QFrame.Shadow.Sunken)
81
+ main_layout.addWidget(line_bar)
82
+
83
+ if section_data:
84
+ self.load_data_values(section_data)
85
+
86
+ self.setLayout(main_layout)
87
+
88
+ def create_rcaide_structure(self, data):
89
+ segment = RCAIDE.Library.Components.Nacelles.Segments.Segment()
90
+ segment.percent_x_location = data["Percent X Location"][0]
91
+ segment.percent_z_location = data["Percent Z Location"][0]
92
+ segment.height = data["Height"][0]
93
+ segment.width = data["Width"][0]
94
+ segment.tag = data["Segment Name"]
95
+
96
+ return segment
97
+
98
+ def get_data_values(self):
99
+ data = self.data_entry_widget.get_values()
100
+ data_si = self.data_entry_widget.get_values_si()
101
+ data["Segment Name"] = self.name_layout.itemAt(2).widget().text()
102
+ data_si["Segment Name"] = self.name_layout.itemAt(2).widget().text()
103
+
104
+ segment = self.create_rcaide_structure(data_si)
105
+ return data, segment
106
+
107
+ def load_data_values(self, data):
108
+ pass
109
+
110
+ def delete_button_pressed(self):
111
+ if self.on_delete is None:
112
+ print("on_delete is None")
113
+ return
114
+
115
+ self.on_delete(self.index)
@@ -0,0 +1,136 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/powertrain/powertrain_connector_widget.py
2
+
3
+ # Created: Dec 2025, M. Clarke
4
+ # ------------------------------------------------------------------------------
5
+ # Imports
6
+ # ------------------------------------------------------------------------------
7
+ from PyQt6.QtWidgets import QWidget, QTabWidget, QHBoxLayout, QVBoxLayout, QLabel, QCheckBox, QWidgetItem
8
+
9
+
10
+ class PowertrainConnectorWidget(QWidget):
11
+ def __init__(self):
12
+ super(PowertrainConnectorWidget, self).__init__()
13
+
14
+ self.main_layout = QHBoxLayout()
15
+ self.tab_widget = QTabWidget()
16
+ self.tab_widget.addTab(QWidget(), "Distributors")
17
+ self.tab_widget.addTab(QWidget(), "Converters")
18
+ self.tab_widget.addTab(QWidget(), "Sources")
19
+
20
+ self.main_layout.addWidget(self.tab_widget)
21
+ self.selector_layouts = []
22
+ self.names = []
23
+
24
+ self.setLayout(self.main_layout)
25
+
26
+ def clear(self):
27
+ pass
28
+
29
+ def update_selector(self, data):
30
+ distributor_names = [x["distributor name"] for x in data["distributor data"]]
31
+ propulsor_names = [x["Propulsor Tag"] for x in data["propulsor data"]]
32
+ converter_names = [x["Converter Name"] for x in data["converter data"]]
33
+ source_names = [x["Source Name"] for x in data["source data"]]
34
+
35
+ self.names = [distributor_names, propulsor_names, converter_names, source_names]
36
+
37
+ self.tab_widget.clear()
38
+
39
+ self.selector_layouts = []
40
+ temp = []
41
+ layout = QVBoxLayout()
42
+ for distributor in distributor_names:
43
+ sub_layout = QHBoxLayout()
44
+ sub_layout.addWidget(QLabel(distributor))
45
+ for propulsor in propulsor_names:
46
+ sub_layout.addWidget(QCheckBox(propulsor))
47
+
48
+ temp.append(sub_layout)
49
+ layout.addLayout(sub_layout)
50
+
51
+ self.selector_layouts.append(temp)
52
+ widget = QWidget()
53
+ widget.setLayout(layout)
54
+ self.tab_widget.addTab(widget, "Distributors")
55
+
56
+ temp = []
57
+ layout = QVBoxLayout()
58
+ for converter in converter_names:
59
+ sub_layout = QHBoxLayout()
60
+ sub_layout.addWidget(QLabel(converter))
61
+ for propulsor in propulsor_names:
62
+ sub_layout.addWidget(QCheckBox(propulsor))
63
+
64
+ temp.append(sub_layout)
65
+ layout.addLayout(sub_layout)
66
+
67
+ self.selector_layouts.append(temp)
68
+ widget = QWidget()
69
+ widget.setLayout(layout)
70
+ self.tab_widget.addTab(widget, "Converters")
71
+
72
+ temp = []
73
+ layout = QVBoxLayout()
74
+ for distributor in distributor_names:
75
+ sub_layout = QHBoxLayout()
76
+ sub_layout.addWidget(QLabel(distributor))
77
+ for source in source_names:
78
+ sub_layout.addWidget(QCheckBox(source))
79
+
80
+ temp.append(sub_layout)
81
+ layout.addLayout(sub_layout)
82
+
83
+ self.selector_layouts.append(temp)
84
+ widget = QWidget()
85
+ widget.setLayout(layout)
86
+ self.tab_widget.addTab(widget, "Sources")
87
+
88
+ def get_connections(self, data):
89
+ distributor_names = [x["distributor name"] for x in data["distributor data"]]
90
+ propulsor_names = [x["Propulsor Tag"] for x in data["propulsor data"]]
91
+ converter_names = [x["Converter Name"] for x in data["converter data"]]
92
+ source_names = [x["Source Name"] for x in data["source data"]]
93
+
94
+ current_names = [distributor_names, propulsor_names, converter_names, source_names]
95
+ if self.names != current_names:
96
+ self.update_selector(data)
97
+
98
+ connections = []
99
+ for tab in self.selector_layouts:
100
+ tab_connections = []
101
+ for layout in tab:
102
+ temp = []
103
+ assert isinstance(layout, QHBoxLayout)
104
+ for i in range(1, layout.count()):
105
+ item = layout.itemAt(i).widget()
106
+ assert item is not None and isinstance(item, QCheckBox)
107
+ temp.append(item.isChecked())
108
+
109
+ tab_connections.append(temp)
110
+
111
+ connections.append(tab_connections)
112
+ return connections
113
+
114
+ def load_connections(self, connections):
115
+ """Restores the checked state of boxes from saved data."""
116
+ if not connections or len(connections) != len(self.selector_layouts):
117
+ return
118
+
119
+ for tab_idx, tab_connections in enumerate(connections):
120
+
121
+ layout_rows = self.selector_layouts[tab_idx]
122
+
123
+ for row_idx, row_data in enumerate(tab_connections):
124
+ if row_idx >= len(layout_rows): break
125
+
126
+ layout = layout_rows[row_idx]
127
+ checkbox_index = 0
128
+
129
+ for i in range(1, layout.count()):
130
+ item = layout.itemAt(i).widget()
131
+ if isinstance(item, QCheckBox):
132
+ if checkbox_index < len(row_data):
133
+ item.setChecked(row_data[checkbox_index])
134
+ checkbox_index += 1
135
+
136
+
@@ -0,0 +1,206 @@
1
+ # RCAIDE_GUI/tabs/geometry/widgets/powertrain/powertrain_widget.py
2
+
3
+ # Created: Dec 2025, M. Clarke
4
+ # ------------------------------------------------------------------------------
5
+ # Imports
6
+ # ------------------------------------------------------------------------------
7
+ # RCAIDE imports
8
+ import RCAIDE
9
+
10
+ # PyQT imports
11
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFrame, QTabWidget
12
+
13
+ # RCAIDE GUI Imports
14
+ from tabs.geometry.widgets.powertrain.distributors import FuelLineWidget
15
+ from tabs.geometry.widgets.powertrain.sources import EnergySourceSelectorWidget
16
+
17
+ from tabs.geometry.frames.powertrain.sources import EnergySourceFrame
18
+ from tabs.geometry.frames.powertrain.distributors import DistributorFrame
19
+ from tabs.geometry.frames.powertrain.converters import ConverterFrame
20
+ from tabs.geometry.frames.powertrain.propulsors import PropulsorFrame
21
+ from tabs.geometry.widgets.powertrain.powertrain_connector_widget import PowertrainConnectorWidget
22
+ from common_widgets import DataEntryWidget
23
+
24
+
25
+ # ----------------------------------------------------------------------------------------------------------------------
26
+ # Powertrain Widget
27
+ # ---------------------------------------------------------------------------------------------------------------------
28
+ class PowertrainWidget(QWidget):
29
+ def __init__(self):
30
+ super(PowertrainWidget, self).__init__()
31
+
32
+ self.save_function = None
33
+ self.data_entry_widget: DataEntryWidget | None = None
34
+
35
+ self.fuellines_layout = QVBoxLayout() # Define main_layout here
36
+ self.fuellines_layout.setContentsMargins(0, 0, 0, 0)
37
+
38
+ # Header
39
+ # header_layout = QHBoxLayout()
40
+
41
+ layout = self.create_scroll_layout()
42
+
43
+ # add_section_button = QPushButton("Add Fuel Line Section", self)
44
+ # add_section_button.setStyleSheet("color:#dbe7ff; font-weight:500; margin:0; padding:0;")
45
+ # add_section_button.setMaximumWidth(200)
46
+ # add_section_button.clicked.connect(self.add_fuelline_section)
47
+ # header_layout.addWidget(add_section_button)
48
+
49
+ # layout.addLayout(header_layout)
50
+
51
+ name_layout = QHBoxLayout()
52
+
53
+ layout.addLayout(name_layout)
54
+
55
+ # Add line above buttons
56
+ line_bar = QFrame()
57
+ line_bar.setFrameShape(QFrame.Shape.HLine)
58
+ line_bar.setFrameShadow(QFrame.Shadow.Sunken)
59
+ line_bar.setStyleSheet("background-color: light grey;")
60
+
61
+ # Tab widget
62
+ self.tab_widget = QTabWidget()
63
+ layout.addWidget(self.tab_widget)
64
+
65
+ # Create Propulsors tab
66
+ self.distributor_frame = DistributorFrame()
67
+ self.tab_widget.addTab(self.distributor_frame, "Distributors")
68
+
69
+ self.energy_source_frame = EnergySourceFrame()
70
+ self.tab_widget.addTab(
71
+ self.energy_source_frame, "Energy Sources")
72
+
73
+ self.propulsor_frame = PropulsorFrame()
74
+ self.tab_widget.addTab(self.propulsor_frame, "Propulsors")
75
+
76
+ self.converter_frame = ConverterFrame()
77
+ self.tab_widget.addTab(self.converter_frame, "Converters")
78
+
79
+ layout.addWidget(line_bar)
80
+ layout.addLayout(self.fuellines_layout)
81
+
82
+ update_selector_button = QPushButton(
83
+ "Update Propulsor-Source Connectivity")
84
+ update_selector_button.setStyleSheet(
85
+ "color:#dbe7ff; font-weight:500; margin:0; padding:0;")
86
+ update_selector_button.clicked.connect(self.update_fuel_selector)
87
+ layout.addWidget(update_selector_button)
88
+
89
+ self.powertrain_connection_selector = PowertrainConnectorWidget()
90
+ # self.fuel_tank_selector.setTabPosition(QTabWidget.TabPosition.North)
91
+ layout.addWidget(self.powertrain_connection_selector)
92
+
93
+ def add_fuelline_section(self):
94
+ self.fuellines_layout.addWidget(
95
+ FuelLineWidget(self.fuellines_layout.count(), self.on_delete_button_pressed))
96
+
97
+ def update_fuel_selector(self):
98
+ self.powertrain_connection_selector.clear()
99
+ data = self.get_data_values(just_data=True)
100
+ self.powertrain_connection_selector.update_selector(data)
101
+
102
+ def on_delete_button_pressed(self, index):
103
+ widget_item = self.fuellines_layout.itemAt(index)
104
+ if widget_item is not None:
105
+ widget = widget_item.widget()
106
+ self.fuellines_layout.removeWidget(widget)
107
+ self.fuellines_layout.update()
108
+ print("Deleted Fuel Line at Index:", index)
109
+
110
+ for i in range(index, self.fuellines_layout.count()):
111
+ widget_item = self.fuellines_layout.itemAt(i)
112
+ assert widget_item is not None
113
+ widget = widget_item.widget()
114
+ assert widget is not None and isinstance(
115
+ widget, FuelLineWidget)
116
+
117
+ widget.index = i
118
+ print("Updated Index:", i)
119
+
120
+ def create_rcaide_structure(self, data):
121
+ data, connections, distributors, sources, propulsors, converters = data
122
+ net = RCAIDE.Framework.Networks.Fuel()
123
+
124
+ propulsor_connections = connections[0]
125
+ source_connections = connections[2]
126
+
127
+ for i, propulsor in enumerate(propulsors):
128
+ net.propulsors.append(propulsor)
129
+
130
+ for i, distributor in enumerate(distributors):
131
+ assigned_propulsors = [[]]
132
+ for j, propulsor in enumerate(propulsors):
133
+ if propulsor_connections[i][j]:
134
+ assigned_propulsors[0].append(propulsor.tag)
135
+
136
+ for j, source in enumerate(sources):
137
+ if source_connections[i][j]:
138
+ distributor.fuel_tanks.append(source)
139
+
140
+ distributor.assigned_propulsors = assigned_propulsors
141
+ net.fuel_lines.append(distributor)
142
+ # source_names = [x.tag for x in distributor.fuel_tanks]
143
+
144
+ return net
145
+
146
+ def get_data_values(self, just_data=False):
147
+ """Retrieve the entered data values from both SourceFrame and PropulsorFrame."""
148
+ data = {}
149
+
150
+ # Get the name of the fuel line
151
+ # fuel_line_name = self.section_name_edit.text()
152
+ # data["name"] = fuel_line_name
153
+
154
+ # Get data values from Distributor tab
155
+ distributor_data = self.distributor_frame.get_data_values()
156
+ data["distributor data"], distributors = distributor_data
157
+
158
+ # Get data values from Sources tab
159
+ source_data = self.energy_source_frame.get_data_values()
160
+ data["source data"], sources = source_data
161
+
162
+ # Get data values from Propulsors tab
163
+ propulsor_data = self.propulsor_frame.get_data_values()
164
+ data["propulsor data"], propulsors = propulsor_data
165
+
166
+ # Get data values from Converters tab
167
+ converter_data = self.converter_frame.get_data_values()
168
+ data["converter data"], converters = converter_data
169
+
170
+ if just_data:
171
+ return data
172
+
173
+ connections = self.powertrain_connection_selector.get_connections(data)
174
+ data["connections"] = connections
175
+
176
+ net = self.create_rcaide_structure(
177
+ (data, connections, distributors, sources, propulsors, converters))
178
+ return data, net
179
+
180
+ def load_data_values(self, data, index=0):
181
+ distributor_data = data["distributor data"]
182
+ self.distributor_frame.load_data(distributor_data)
183
+
184
+ source_data = data["source data"]
185
+ self.energy_source_frame.load_data(source_data)
186
+
187
+ propulsor_data = data["propulsor data"]
188
+ self.propulsor_frame.load_data(propulsor_data)
189
+
190
+ converter_data = data["converter data"]
191
+ self.converter_frame.load_data(converter_data)
192
+
193
+ self.powertrain_connection_selector.update_selector(data)
194
+ if "connections" in data:
195
+ self.powertrain_connection_selector.load_connections(
196
+ data["connections"])
197
+
198
+ def create_scroll_layout(self):
199
+ # Create a widget to contain the layout
200
+ scroll_content = QWidget()
201
+ # Set the main layout inside the scroll content
202
+ layout = QVBoxLayout(scroll_content)
203
+
204
+ # Set the main layout of the widget
205
+ self.setLayout(layout)
206
+ return layout