Skip to content

Commit 7aa187b

Browse files
committed
fix(param editor): Fix asking to save changes multiple times.
1 parent 52ee52c commit 7aa187b

5 files changed

Lines changed: 39 additions & 28 deletions

ardupilot_methodic_configurator/configuration_manager.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,10 +1418,11 @@ def export_current_file(self, annotate_doc: bool) -> None:
14181418
# Update the filesystem's file_parameters to match what was saved
14191419
self._local_filesystem.file_parameters[self.current_file] = export_params
14201420

1421-
# Note: We don't need to clear tracking sets or dirty flags on domain model parameters
1422-
# because after export, the user always navigates to a different file, which triggers
1423-
# repopulate_configuration_step_parameters() that clears tracking sets and rebuilds
1424-
# self.parameters fresh.
1421+
self._added_parameters.clear()
1422+
self._deleted_parameters.clear()
1423+
# copy parameters new values to their _values_on_file
1424+
for param in self.current_step_parameters.values():
1425+
param.copy_new_value_to_file()
14251426

14261427
def open_documentation_in_browser(self, filename: str) -> None:
14271428
_blog_text, blog_url = self.get_documentation_text_and_url("blog", filename)

ardupilot_methodic_configurator/data_model_ardupilot_parameter.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,11 @@ def set_forced_or_derived_change_reason(self, change_reason: str) -> None:
478478

479479
self._change_reason = change_reason
480480

481+
def copy_new_value_to_file(self) -> None:
482+
"""Copy the new value to the value on file, marking it as saved, resetting the is_dirty property."""
483+
self._value_on_file = self._new_value
484+
self._change_reason_on_file = self._change_reason
485+
481486

482487
class BitmaskHelper:
483488
"""Helper class for working with ArduPilot parameter bitmasks."""

ardupilot_methodic_configurator/frontend_tkinter_parameter_editor.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ def __create_parameter_area_widgets(self) -> None:
318318

319319
# Create a Scrollable parameter editor table
320320
self.parameter_editor_table = ParameterEditorTable(self.main_frame, self.configuration_manager, self)
321-
self.repopulate_parameter_table()
321+
self.repopulate_parameter_table(regenerate_from_disk=True)
322322
self.parameter_editor_table.pack(side="top", fill="both", expand=True)
323323

324324
# Create a frame for the buttons
@@ -654,7 +654,7 @@ def on_param_file_combobox_change(self, _event: Union[None, tk.Event], forced: b
654654
self.configuration_manager.current_file = selected_file
655655
self.documentation_frame.refresh_documentation_labels()
656656
self.documentation_frame.update_why_why_now_tooltip()
657-
self.repopulate_parameter_table()
657+
self.repopulate_parameter_table(regenerate_from_disk=True)
658658
self._update_skip_button_state()
659659

660660
def _update_progress_bar_from_file(self, selected_file: str) -> None:
@@ -676,14 +676,14 @@ def download_flight_controller_parameters(self, redownload: bool = False) -> Non
676676
if not redownload:
677677
self.on_param_file_combobox_change(None, forced=True) # the initial param read will trigger a table update
678678

679-
def repopulate_parameter_table(self) -> None:
679+
def repopulate_parameter_table(self, regenerate_from_disk: bool) -> None:
680680
if not self.configuration_manager.current_file:
681681
return # no file was yet selected, so skip it
682682
# Re-populate the table with the new parameters
683-
self.parameter_editor_table.repopulate(self.show_only_differences.get(), self.gui_complexity)
683+
self.parameter_editor_table.repopulate(self.show_only_differences.get(), self.gui_complexity, regenerate_from_disk)
684684

685685
def on_show_only_changed_checkbox_change(self) -> None:
686-
self.repopulate_parameter_table()
686+
self.repopulate_parameter_table(regenerate_from_disk=False)
687687

688688
def on_upload_selected_click(self) -> None:
689689
self.write_changes_to_intermediate_parameter_file()

ardupilot_methodic_configurator/frontend_tkinter_parameter_editor_table.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def _create_headers_and_tooltips(self, show_upload_column: bool) -> tuple[tuple[
127127
return tuple(base_headers), tuple(base_tooltips)
128128

129129
def repopulate( # pylint: disable=too-many-locals
130-
self, show_only_differences: bool, gui_complexity: str
130+
self, show_only_differences: bool, gui_complexity: str, regenerate_from_disk: bool
131131
) -> None:
132132
for widget in self.view_port.winfo_children():
133133
widget.destroy()
@@ -150,12 +150,13 @@ def repopulate( # pylint: disable=too-many-locals
150150
self.upload_checkbutton_var = {}
151151

152152
# Process configuration step and create domain model parameters
153-
(ui_errors, ui_infos) = self.configuration_manager.repopulate_configuration_step_parameters()
153+
if regenerate_from_disk:
154+
(ui_errors, ui_infos) = self.configuration_manager.repopulate_configuration_step_parameters()
154155

155-
for title, msg in ui_errors:
156-
messagebox.showerror(title, msg)
157-
for title, msg in ui_infos:
158-
messagebox.showinfo(title, msg)
156+
for title, msg in ui_errors:
157+
messagebox.showerror(title, msg)
158+
for title, msg in ui_infos:
159+
messagebox.showinfo(title, msg)
159160

160161
if show_only_differences:
161162
# Filter to show only different parameters
@@ -718,7 +719,7 @@ def _on_parameter_delete(self, param_name: str) -> None:
718719

719720
# Delete the parameter
720721
self.configuration_manager.delete_parameter_from_current_file(param_name)
721-
self.parameter_editor.repopulate_parameter_table()
722+
self.parameter_editor.repopulate_parameter_table(regenerate_from_disk=False)
722723

723724
# Restore the scroll position
724725
self.canvas.yview_moveto(current_scroll_position)
@@ -769,7 +770,7 @@ def _confirm_parameter_addition(self, param_name: str) -> bool:
769770
try:
770771
if self.configuration_manager.add_parameter_to_current_file(param_name):
771772
self._pending_scroll_to_bottom = True
772-
self.parameter_editor.repopulate_parameter_table()
773+
self.parameter_editor.repopulate_parameter_table(regenerate_from_disk=False)
773774
return True
774775
except InvalidParameterNameError as exc:
775776
messagebox.showerror(_("Invalid parameter name."), str(exc))

tests/test_frontend_tkinter_parameter_editor_table.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def test_repopulate_empty_parameters(parameter_editor_table: ParameterEditorTabl
270270
parameter_editor_table.configuration_manager.filesystem.file_parameters = ParDict({test_file: ParDict({})})
271271

272272
# Act: Repopulate the table
273-
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple")
273+
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple", regenerate_from_disk=False)
274274

275275
# Assert: No parameter rows were added
276276
parameter_editor_table.add_parameter_row.assert_not_called()
@@ -296,7 +296,7 @@ def test_repopulate_clears_existing_content(parameter_editor_table: ParameterEdi
296296
parameter_editor_table.configuration_manager.filesystem.param_default_dict = ParDict({"PARAM1": Par(0.0, "default")})
297297

298298
# Act: Repopulate the table
299-
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple")
299+
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple", regenerate_from_disk=False)
300300

301301
# Assert: Existing content was cleared
302302
assert not dummy_widget.winfo_exists()
@@ -318,7 +318,7 @@ def test_repopulate_handles_none_current_file(parameter_editor_table: ParameterE
318318
parameter_editor_table.configuration_manager.filesystem.param_default_dict = ParDict({})
319319

320320
# Act: Attempt to repopulate with no current file
321-
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple")
321+
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple", regenerate_from_disk=False)
322322

323323
# Assert: No parameter rows were added
324324
parameter_editor_table.add_parameter_row.assert_not_called()
@@ -344,7 +344,7 @@ def test_repopulate_single_parameter(parameter_editor_table: ParameterEditorTabl
344344

345345
# Act: Repopulate with single parameter
346346
with patch.object(parameter_editor_table, "grid_slaves", return_value=[]):
347-
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple")
347+
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple", regenerate_from_disk=False)
348348

349349
# Assert: Parameter row was added (implicitly tested through repopulate call)
350350

@@ -387,7 +387,7 @@ def test_repopulate_multiple_parameters(parameter_editor_table: ParameterEditorT
387387

388388
# Act: Repopulate with multiple parameters
389389
with patch.object(parameter_editor_table, "grid_slaves", return_value=[]):
390-
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple")
390+
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple", regenerate_from_disk=False)
391391

392392
# Assert: All parameters were processed (implicitly tested through repopulate call)
393393

@@ -418,7 +418,7 @@ def test_repopulate_preserves_checkbutton_states(parameter_editor_table: Paramet
418418
)
419419

420420
# Act: Repopulate the table
421-
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple")
421+
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple", regenerate_from_disk=False)
422422

423423
# Assert: Checkbutton states were preserved (implicitly tested through repopulate call)
424424

@@ -459,7 +459,7 @@ def test_repopulate_show_only_differences(parameter_editor_table: ParameterEdito
459459
)
460460

461461
# Act: Repopulate showing only differences
462-
parameter_editor_table.repopulate(show_only_differences=True, gui_complexity="simple")
462+
parameter_editor_table.repopulate(show_only_differences=True, gui_complexity="simple", regenerate_from_disk=False)
463463

464464
# Assert: Only differing parameters were processed (implicitly tested through repopulate call)
465465

@@ -485,7 +485,7 @@ def test_repopulate_uses_scroll_helper(parameter_editor_table: ParameterEditorTa
485485

486486
# Act: Repopulate and check scroll behavior
487487
with patch.object(parameter_editor_table, "_apply_scroll_position") as mock_scroll:
488-
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple")
488+
parameter_editor_table.repopulate(show_only_differences=False, gui_complexity="simple", regenerate_from_disk=False)
489489

490490
# Assert: Scroll position was applied correctly
491491
mock_scroll.assert_called_once_with(pending_scroll)
@@ -918,7 +918,9 @@ def test_on_parameter_delete_confirmed(self, parameter_editor_table: ParameterEd
918918

919919
# Assert: Parameter is deleted and table repopulated
920920
assert "TEST_PARAM" not in parameter_editor_table.configuration_manager.filesystem.file_parameters["test_file"]
921-
parameter_editor_table.parameter_editor.repopulate_parameter_table.assert_called_once_with()
921+
parameter_editor_table.parameter_editor.repopulate_parameter_table.assert_called_once_with(
922+
regenerate_from_disk=False
923+
)
922924

923925
def test_on_parameter_delete_cancelled(self, parameter_editor_table: ParameterEditorTable) -> None:
924926
"""
@@ -1650,7 +1652,7 @@ def test_user_can_add_parameter_to_configuration_file(self, parameter_editor_tab
16501652
# Assert: Parameter addition was successful
16511653
assert result is True
16521654
parameter_editor_table.configuration_manager.add_parameter_to_current_file.assert_called_once_with("NEW_PARAM")
1653-
parameter_editor_table.parameter_editor.repopulate_parameter_table.assert_called_once()
1655+
parameter_editor_table.parameter_editor.repopulate_parameter_table.assert_called_once_with(regenerate_from_disk=False)
16541656

16551657
def test_user_can_delete_parameter_from_configuration_file(self, parameter_editor_table: ParameterEditorTable) -> None:
16561658
"""
@@ -1674,7 +1676,9 @@ def test_user_can_delete_parameter_from_configuration_file(self, parameter_edito
16741676
parameter_editor_table.configuration_manager.delete_parameter_from_current_file.assert_called_once_with(
16751677
"TEST_PARAM"
16761678
)
1677-
parameter_editor_table.parameter_editor.repopulate_parameter_table.assert_called_once()
1679+
parameter_editor_table.parameter_editor.repopulate_parameter_table.assert_called_once_with(
1680+
regenerate_from_disk=False
1681+
)
16781682

16791683
def test_user_cannot_delete_parameter_when_cancelled(self, parameter_editor_table: ParameterEditorTable) -> None:
16801684
"""

0 commit comments

Comments
 (0)