|
24 | 24 |
|
25 | 25 | from ardupilot_methodic_configurator import _, __version__
|
26 | 26 | from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
|
| 27 | +from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings |
27 | 28 | from ardupilot_methodic_configurator.common_arguments import add_common_arguments
|
28 | 29 | from ardupilot_methodic_configurator.data_model_vehicle_components import ComponentDataModel
|
29 | 30 | from ardupilot_methodic_configurator.data_model_vehicle_components_base import (
|
@@ -108,6 +109,7 @@ def __init__(
|
108 | 109 | self.scroll_frame: ScrollFrame
|
109 | 110 | self.save_button: ttk.Button
|
110 | 111 | self.template_manager: ComponentTemplateManager
|
| 112 | + self.complexity_var = tk.StringVar() |
111 | 113 |
|
112 | 114 | # Initialize UI if there's data to work with
|
113 | 115 | if self._check_data():
|
@@ -175,11 +177,55 @@ def _add_explanation_text(self, parent: ttk.Frame) -> None:
|
175 | 177 | explanation_text += _("Labels for optional properties are displayed in gray text.\n")
|
176 | 178 | explanation_text += _("Scroll down to ensure that you do not overlook any properties.\n")
|
177 | 179 |
|
| 180 | + explanation_frame = ttk.Frame(parent) |
| 181 | + explanation_frame.pack(side=tk.TOP, fill=tk.X, padx=(10, 10), pady=(10, 0)) |
| 182 | + |
178 | 183 | explanation_label = ttk.Label(
|
179 |
| - parent, text=explanation_text, wraplength=WINDOW_WIDTH_PIX - VEICLE_IMAGE_WIDTH_PIX, justify=tk.LEFT |
| 184 | + explanation_frame, text=explanation_text, wraplength=WINDOW_WIDTH_PIX - VEICLE_IMAGE_WIDTH_PIX, justify=tk.LEFT |
180 | 185 | )
|
181 | 186 | explanation_label.configure(style="bigger.TLabel")
|
182 |
| - explanation_label.pack(side=tk.LEFT, padx=(10, 10), pady=(10, 0), anchor=tk.NW) |
| 187 | + explanation_label.pack(side=tk.LEFT, anchor=tk.NW) |
| 188 | + |
| 189 | + # Add UI complexity combobox |
| 190 | + complexity_frame = ttk.Frame(explanation_frame) |
| 191 | + complexity_frame.pack(side=tk.RIGHT, anchor=tk.NE, padx=(0, 10)) |
| 192 | + |
| 193 | + complexity_label = ttk.Label(complexity_frame, text=_("GUI Complexity:")) |
| 194 | + complexity_label.pack(side=tk.LEFT, padx=(0, 5)) |
| 195 | + |
| 196 | + complexity_setting = ProgramSettings.get_setting("gui_complexity") |
| 197 | + self.complexity_var.set(str(complexity_setting) if complexity_setting is not None else "simple") |
| 198 | + |
| 199 | + complexity_combobox = ttk.Combobox( |
| 200 | + complexity_frame, textvariable=self.complexity_var, values=["simple", "normal"], state="readonly", width=10 |
| 201 | + ) |
| 202 | + complexity_combobox.pack(side=tk.LEFT) |
| 203 | + complexity_combobox.bind("<<ComboboxSelected>>", self._on_complexity_changed) |
| 204 | + |
| 205 | + # Add tooltip |
| 206 | + show_tooltip( |
| 207 | + complexity_combobox, |
| 208 | + _( |
| 209 | + "Select the graphical user interface complexity level:\n" |
| 210 | + "simple for beginners, only minimal mandatory fields no optional params displayed\n" |
| 211 | + "normal for everybody else." |
| 212 | + ), |
| 213 | + ) |
| 214 | + |
| 215 | + def _on_complexity_changed(self, _event: Optional[tk.Event] = None) -> None: |
| 216 | + """Handle complexity combobox change.""" |
| 217 | + # Save the complexity setting |
| 218 | + ProgramSettings.set_setting("gui_complexity", self.complexity_var.get()) |
| 219 | + |
| 220 | + # Clear all widgets from the scroll frame |
| 221 | + for widget in self.scroll_frame.view_port.winfo_children(): |
| 222 | + widget.destroy() |
| 223 | + |
| 224 | + # Repopulate the frame with widgets according to the new complexity setting |
| 225 | + self.populate_frames() |
| 226 | + |
| 227 | + # Update the UI |
| 228 | + self.scroll_frame.view_port.update_idletasks() |
183 | 229 |
|
184 | 230 | def _add_vehicle_image(self, parent: ttk.Frame) -> None:
|
185 | 231 | """Add the vehicle image to the parent frame."""
|
@@ -293,6 +339,17 @@ def _add_widget(self, parent: tk.Widget, key: str, value: ComponentValue, path:
|
293 | 339 | path (list): The path to the current key in the JSON data.
|
294 | 340 |
|
295 | 341 | """
|
| 342 | + # Skip components that shouldn't be displayed in simple mode |
| 343 | + if isinstance(value, dict) and not self._should_display_in_simple_mode(key, value, path): |
| 344 | + return |
| 345 | + |
| 346 | + # For leaf nodes in simple mode, check if they are optional |
| 347 | + if not isinstance(value, dict) and self.complexity_var.get() == "simple": |
| 348 | + current_path = (*path, key) |
| 349 | + _, is_optional = self.local_filesystem.get_component_property_description(current_path) |
| 350 | + if is_optional: |
| 351 | + return |
| 352 | + |
296 | 353 | if isinstance(value, dict): # JSON non-leaf elements, add LabelFrame widget
|
297 | 354 | self._add_non_leaf_widget(parent, key, value, path)
|
298 | 355 | else: # JSON leaf elements, add Entry widget
|
@@ -399,7 +456,8 @@ def _create_leaf_widget_ui(self, parent: tk.Widget, config: dict) -> None:
|
399 | 456 |
|
400 | 457 | def _add_template_controls(self, parent_frame: ttk.LabelFrame, component_name: str) -> None:
|
401 | 458 | """Add template controls for a component."""
|
402 |
| - self.template_manager.add_template_controls(parent_frame, component_name) |
| 459 | + if self.complexity_var.get() != "simple": |
| 460 | + self.template_manager.add_template_controls(parent_frame, component_name) |
403 | 461 |
|
404 | 462 | def get_component_data_from_gui(self, component_name: str) -> ComponentData:
|
405 | 463 | """Extract component data from GUI elements."""
|
@@ -533,6 +591,66 @@ def create_for_testing(
|
533 | 591 |
|
534 | 592 | return instance
|
535 | 593 |
|
| 594 | + def _should_display_in_simple_mode(self, key: str, value: dict, path: list[str]) -> bool: # pylint: disable=too-many-branches |
| 595 | + """ |
| 596 | + Determine if a component should be displayed in simple mode. |
| 597 | +
|
| 598 | + In simple mode, only show components that have at least one non-optional parameter. |
| 599 | +
|
| 600 | + Args: |
| 601 | + key (str): The component key |
| 602 | + value (dict): The component value |
| 603 | + path (list): The path to the component |
| 604 | +
|
| 605 | + Returns: |
| 606 | + bool: True if the component should be displayed, False otherwise |
| 607 | +
|
| 608 | + """ |
| 609 | + # If not in simple mode, always display the component |
| 610 | + if self.complexity_var.get() != "simple": |
| 611 | + return True |
| 612 | + |
| 613 | + has_non_optional = False |
| 614 | + |
| 615 | + # Top-level components need special handling |
| 616 | + if not path and key in self.data_model.get_all_components(): |
| 617 | + # Check if this component has any non-optional parameters |
| 618 | + for sub_key, sub_value in value.items(): |
| 619 | + if isinstance(sub_value, dict): |
| 620 | + # For nested dictionaries, recursively check |
| 621 | + if self._should_display_in_simple_mode(sub_key, sub_value, [*path, key]): |
| 622 | + return True |
| 623 | + else: |
| 624 | + # For leaf nodes, check if they are optional |
| 625 | + current_path = (*path, key, sub_key) |
| 626 | + _, is_optional = self.local_filesystem.get_component_property_description(current_path) |
| 627 | + if not is_optional: |
| 628 | + return True |
| 629 | + return False |
| 630 | + |
| 631 | + # For non-top-level components or leaf nodes |
| 632 | + if isinstance(value, dict): |
| 633 | + # Check if this component has any non-optional parameters |
| 634 | + for sub_key, sub_value in value.items(): |
| 635 | + current_path = (*path, key, sub_key) |
| 636 | + if isinstance(sub_value, dict): |
| 637 | + if self._should_display_in_simple_mode(sub_key, sub_value, [*path, key]): |
| 638 | + has_non_optional = True |
| 639 | + break |
| 640 | + else: |
| 641 | + _, is_optional = self.local_filesystem.get_component_property_description(current_path) |
| 642 | + if not is_optional: |
| 643 | + has_non_optional = True |
| 644 | + break |
| 645 | + else: |
| 646 | + # Leaf node - check if it's optional |
| 647 | + current_path = (*path, key) |
| 648 | + _, is_optional = self.local_filesystem.get_component_property_description(current_path) |
| 649 | + if not is_optional: |
| 650 | + has_non_optional = True |
| 651 | + |
| 652 | + return has_non_optional |
| 653 | + |
536 | 654 |
|
537 | 655 | if __name__ == "__main__":
|
538 | 656 | args = argument_parser()
|
|
0 commit comments