ardupilot-methodic-configurator 2.6.1__py3-none-any.whl → 2.7.1__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.
Potentially problematic release.
This version of ardupilot-methodic-configurator might be problematic. Click here for more details.
- ardupilot_methodic_configurator/__init__.py +2 -2
- ardupilot_methodic_configurator/__main__.py +34 -1
- ardupilot_methodic_configurator/annotate_params.py +49 -14
- ardupilot_methodic_configurator/argparse_check_range.py +1 -1
- ardupilot_methodic_configurator/backend_filesystem.py +7 -3
- ardupilot_methodic_configurator/backend_filesystem_configuration_steps.py +53 -6
- ardupilot_methodic_configurator/backend_filesystem_freedesktop.py +275 -0
- ardupilot_methodic_configurator/backend_filesystem_json_with_schema.py +3 -3
- ardupilot_methodic_configurator/backend_filesystem_program_settings.py +26 -8
- ardupilot_methodic_configurator/backend_filesystem_vehicle_components.py +3 -3
- ardupilot_methodic_configurator/backend_flightcontroller.py +37 -20
- ardupilot_methodic_configurator/backend_flightcontroller_info.py +1 -1
- ardupilot_methodic_configurator/backend_internet.py +1 -1
- ardupilot_methodic_configurator/backend_mavftp.py +1 -1
- ardupilot_methodic_configurator/battery_cell_voltages.py +1 -1
- ardupilot_methodic_configurator/common_arguments.py +1 -1
- ardupilot_methodic_configurator/configuration_manager.py +561 -121
- ardupilot_methodic_configurator/configuration_steps_ArduCopter.json +7 -5
- ardupilot_methodic_configurator/configuration_steps_ArduPlane.json +3 -1
- ardupilot_methodic_configurator/configuration_steps_Heli.json +3 -1
- ardupilot_methodic_configurator/configuration_steps_Rover.json +3 -1
- ardupilot_methodic_configurator/configuration_steps_strings.py +5 -3
- ardupilot_methodic_configurator/data_model_ardupilot_parameter.py +55 -2
- ardupilot_methodic_configurator/data_model_configuration_step.py +96 -46
- ardupilot_methodic_configurator/data_model_fc_ids.py +16 -7
- ardupilot_methodic_configurator/data_model_motor_test.py +1 -1
- ardupilot_methodic_configurator/data_model_par_dict.py +25 -11
- ardupilot_methodic_configurator/data_model_software_updates.py +1 -1
- ardupilot_methodic_configurator/data_model_template_overview.py +1 -1
- ardupilot_methodic_configurator/data_model_vehicle_components.py +1 -1
- ardupilot_methodic_configurator/data_model_vehicle_components_base.py +3 -2
- ardupilot_methodic_configurator/data_model_vehicle_components_display.py +1 -1
- ardupilot_methodic_configurator/data_model_vehicle_components_import.py +2 -1
- ardupilot_methodic_configurator/data_model_vehicle_components_json_schema.py +1 -1
- ardupilot_methodic_configurator/data_model_vehicle_components_templates.py +1 -1
- ardupilot_methodic_configurator/data_model_vehicle_components_validation.py +41 -1
- ardupilot_methodic_configurator/data_model_vehicle_project.py +1 -1
- ardupilot_methodic_configurator/data_model_vehicle_project_creator.py +1 -1
- ardupilot_methodic_configurator/data_model_vehicle_project_opener.py +1 -1
- ardupilot_methodic_configurator/extract_param_defaults.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_autoresize_combobox.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_base_window.py +6 -2
- ardupilot_methodic_configurator/frontend_tkinter_component_editor.py +56 -29
- ardupilot_methodic_configurator/frontend_tkinter_component_editor_base.py +18 -13
- ardupilot_methodic_configurator/frontend_tkinter_component_template_manager.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_connection_selection.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_directory_selection.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_entry_dynamic.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_flightcontroller_info.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_font.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_motor_test.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_pair_tuple_combobox.py +7 -1
- ardupilot_methodic_configurator/frontend_tkinter_parameter_editor.py +107 -156
- ardupilot_methodic_configurator/frontend_tkinter_parameter_editor_documentation_frame.py +24 -58
- ardupilot_methodic_configurator/frontend_tkinter_parameter_editor_table.py +24 -56
- ardupilot_methodic_configurator/frontend_tkinter_progress_window.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_project_creator.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_project_opener.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_rich_text.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_scroll_frame.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_show.py +3 -3
- ardupilot_methodic_configurator/frontend_tkinter_software_update.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_stage_progress.py +19 -29
- ardupilot_methodic_configurator/frontend_tkinter_template_overview.py +1 -1
- ardupilot_methodic_configurator/frontend_tkinter_usage_popup_window.py +1 -1
- ardupilot_methodic_configurator/internationalization.py +1 -1
- ardupilot_methodic_configurator/param_pid_adjustment_update.py +43 -39
- ardupilot_methodic_configurator/tempcal_imu.py +1 -1
- ardupilot_methodic_configurator/vehicle_components.py +1 -1
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/AirCar_v1/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Big_Owl/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Chimera7/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/FETtec-5/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/GazeboIrisWithTargetFollow/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Holybro_X500/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Holybro_X500_V2/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Holybro_X650_LTE/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Hoverit_X11+/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Hoverit_X13/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Marmotte5v2/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/ReadyToSkyZD550/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/TarotFY680Hexacopter/05_remote_controller.param +1 -1
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/TarotFY680Hexacopter/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/TarotFY680Hexacopter/47_position_controller.param +2 -2
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/Tarot_X4/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/X11_plus/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/diatone_taycan_mxc/4.3.8-params/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/diatone_taycan_mxc/4.4.4-params/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/diatone_taycan_mxc/4.5.x-params/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/diatone_taycan_mxc/4.6.x-params/14_logging.param +3 -3
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/empty_4.5.x/10_gnss.param +1 -1
- ardupilot_methodic_configurator/vehicle_templates/ArduCopter/empty_4.6.x/10_gnss.param +1 -1
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/METADATA +12 -7
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/RECORD +107 -106
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/credits/CREDITS.md +1 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/WHEEL +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/entry_points.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSE.md +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSES/Apache-2.0.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSES/BSD-3-Clause.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSES/GPL-3.0-or-later.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSES/LGPL-3.0-or-later.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSES/MIT-CMU.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSES/MIT.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSES/MPL-2.0.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/licenses/LICENSES/PSF-2.0.txt +0 -0
- {ardupilot_methodic_configurator-2.6.1.dist-info → ardupilot_methodic_configurator-2.7.1.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"""
|
|
4
4
|
A combobox GUI with support for complex lists.
|
|
5
5
|
|
|
6
|
-
This file is part of
|
|
6
|
+
This file is part of ArduPilot Methodic Configurator. https://github.com/ArduPilot/MethodicConfigurator
|
|
7
7
|
|
|
8
8
|
SPDX-FileCopyrightText: 2024-2025 Amilcar do Carmo Lucas <amilcar.lucas@iav.de>
|
|
9
9
|
|
|
@@ -111,6 +111,12 @@ class PairTupleCombobox(ttk.Combobox): # pylint: disable=too-many-ancestors
|
|
|
111
111
|
setup_combobox_mousewheel_handling(self)
|
|
112
112
|
|
|
113
113
|
def set_entries_tuple(self, list_pair_tuple: list[tuple[str, str]], selected_element: Union[None, str]) -> None:
|
|
114
|
+
# Clear existing entries before setting new ones
|
|
115
|
+
self.list_keys.clear()
|
|
116
|
+
self.list_shows.clear()
|
|
117
|
+
self.append_entries_tuple(list_pair_tuple, selected_element)
|
|
118
|
+
|
|
119
|
+
def append_entries_tuple(self, list_pair_tuple: list[tuple[str, str]], selected_element: Union[None, str]) -> None:
|
|
114
120
|
if isinstance(list_pair_tuple, list):
|
|
115
121
|
for tpl in list_pair_tuple:
|
|
116
122
|
self.list_keys.append(tpl[0])
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"""
|
|
4
4
|
Parameter editor GUI.
|
|
5
5
|
|
|
6
|
-
This file is part of
|
|
6
|
+
This file is part of ArduPilot Methodic Configurator. https://github.com/ArduPilot/MethodicConfigurator
|
|
7
7
|
|
|
8
8
|
SPDX-FileCopyrightText: 2024-2025 Amilcar do Carmo Lucas <amilcar.lucas@iav.de>
|
|
9
9
|
|
|
@@ -11,7 +11,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
import sys
|
|
14
|
-
import time
|
|
15
14
|
import tkinter as tk
|
|
16
15
|
from argparse import ArgumentParser, Namespace
|
|
17
16
|
|
|
@@ -19,23 +18,24 @@ from argparse import ArgumentParser, Namespace
|
|
|
19
18
|
from logging import basicConfig as logging_basicConfig
|
|
20
19
|
from logging import error as logging_error
|
|
21
20
|
from logging import getLevelName as logging_getLevelName
|
|
22
|
-
from logging import info as logging_info
|
|
23
21
|
from logging import warning as logging_warning
|
|
24
22
|
from tkinter import filedialog, messagebox, ttk
|
|
25
|
-
from typing import
|
|
23
|
+
from typing import Optional, Union
|
|
26
24
|
|
|
27
25
|
# from logging import critical as logging_critical
|
|
28
26
|
from webbrowser import open as webbrowser_open # to open the blog post documentation
|
|
29
27
|
|
|
30
28
|
from ardupilot_methodic_configurator import _, __version__
|
|
31
29
|
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
|
|
30
|
+
from ardupilot_methodic_configurator.backend_filesystem_freedesktop import FreeDesktop
|
|
32
31
|
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
|
|
33
32
|
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
|
|
34
33
|
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
|
|
35
|
-
from ardupilot_methodic_configurator.configuration_manager import ConfigurationManager
|
|
34
|
+
from ardupilot_methodic_configurator.configuration_manager import ConfigurationManager, ExperimentChoice
|
|
36
35
|
from ardupilot_methodic_configurator.frontend_tkinter_autoresize_combobox import AutoResizeCombobox
|
|
37
36
|
from ardupilot_methodic_configurator.frontend_tkinter_base_window import (
|
|
38
37
|
BaseWindow,
|
|
38
|
+
ask_retry_cancel_popup,
|
|
39
39
|
ask_yesno_popup,
|
|
40
40
|
show_error_popup,
|
|
41
41
|
show_info_popup,
|
|
@@ -156,10 +156,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
156
156
|
def __init__(self, configuration_manager: ConfigurationManager) -> None:
|
|
157
157
|
super().__init__()
|
|
158
158
|
self.configuration_manager = configuration_manager
|
|
159
|
-
# Maintain backward compatibility with existing code
|
|
160
|
-
self.local_filesystem = configuration_manager.filesystem
|
|
161
159
|
|
|
162
|
-
self.at_least_one_changed_parameter_written = False
|
|
163
160
|
self.file_selection_combobox: AutoResizeCombobox
|
|
164
161
|
self.show_only_differences: tk.BooleanVar
|
|
165
162
|
self.annotate_params_into_files: tk.BooleanVar
|
|
@@ -169,7 +166,6 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
169
166
|
self.tempcal_imu_progress_window: ProgressWindow
|
|
170
167
|
self.file_upload_progress_window: ProgressWindow
|
|
171
168
|
self.skip_button: ttk.Button
|
|
172
|
-
self.last_time_asked_to_save: float = 0
|
|
173
169
|
self.gui_complexity = str(ProgramSettings.get_setting("gui_complexity"))
|
|
174
170
|
|
|
175
171
|
self.root.title(
|
|
@@ -193,20 +189,15 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
193
189
|
|
|
194
190
|
self.__create_conf_widgets(__version__)
|
|
195
191
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
last_step_nr = int(last_step_filename[:2]) + 1 if len(last_step_filename) >= 2 else 1
|
|
192
|
+
last_step_nr = self.configuration_manager.get_last_configuration_step_number()
|
|
193
|
+
if last_step_nr is not None:
|
|
194
|
+
phases = self.configuration_manager.get_sorted_phases_with_end_and_weight(last_step_nr)
|
|
200
195
|
|
|
201
|
-
self.stage_progress_bar = StageProgressBar(
|
|
202
|
-
self.main_frame, self.local_filesystem.configuration_phases, last_step_nr, self.gui_complexity
|
|
203
|
-
)
|
|
196
|
+
self.stage_progress_bar = StageProgressBar(self.main_frame, phases, last_step_nr, self.gui_complexity)
|
|
204
197
|
self.stage_progress_bar.pack(side=tk.TOP, fill="x", expand=False, pady=(2, 2), padx=(4, 4))
|
|
205
198
|
|
|
206
199
|
# Create a DocumentationFrame object for the Documentation Content
|
|
207
|
-
self.documentation_frame = DocumentationFrame(
|
|
208
|
-
self.main_frame, self.local_filesystem, self.configuration_manager.current_file
|
|
209
|
-
)
|
|
200
|
+
self.documentation_frame = DocumentationFrame(self.main_frame, self.configuration_manager)
|
|
210
201
|
self.documentation_frame.documentation_frame.pack(side=tk.TOP, fill="x", expand=False, pady=(2, 2), padx=(4, 4))
|
|
211
202
|
|
|
212
203
|
self.__create_parameter_area_widgets()
|
|
@@ -217,6 +208,10 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
217
208
|
# this one should be on top of the previous one hence the longer time
|
|
218
209
|
if UsagePopupWindow.should_display("parameter_editor"):
|
|
219
210
|
self.root.after(100, self.__display_usage_popup_window(self.root)) # type: ignore[arg-type]
|
|
211
|
+
|
|
212
|
+
# Set up startup notification for the main application window
|
|
213
|
+
FreeDesktop.setup_startup_notification(self.root) # type: ignore[arg-type]
|
|
214
|
+
|
|
220
215
|
self.root.mainloop()
|
|
221
216
|
|
|
222
217
|
def __create_conf_widgets(self, version: str) -> None:
|
|
@@ -231,7 +226,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
231
226
|
directory_selection_frame = VehicleDirectorySelectionWidgets(
|
|
232
227
|
self,
|
|
233
228
|
config_subframe,
|
|
234
|
-
self.
|
|
229
|
+
self.configuration_manager.get_vehicle_directory(),
|
|
235
230
|
destroy_parent_on_open=False,
|
|
236
231
|
)
|
|
237
232
|
if self.gui_complexity != "simple":
|
|
@@ -250,7 +245,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
250
245
|
# Create Combobox for intermediate parameter file selection
|
|
251
246
|
self.file_selection_combobox = AutoResizeCombobox(
|
|
252
247
|
file_selection_frame,
|
|
253
|
-
|
|
248
|
+
self.configuration_manager.parameter_files(),
|
|
254
249
|
self.configuration_manager.current_file,
|
|
255
250
|
_(
|
|
256
251
|
"Select the intermediate parameter file from the list of available"
|
|
@@ -326,7 +321,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
326
321
|
|
|
327
322
|
# Create a Scrollable parameter editor table
|
|
328
323
|
self.parameter_editor_table = ParameterEditorTable(self.main_frame, self.configuration_manager, self)
|
|
329
|
-
self.repopulate_parameter_table()
|
|
324
|
+
self.repopulate_parameter_table(regenerate_from_disk=True)
|
|
330
325
|
self.parameter_editor_table.pack(side="top", fill="both", expand=True)
|
|
331
326
|
|
|
332
327
|
# Create a frame for the buttons
|
|
@@ -354,7 +349,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
354
349
|
annotate_params_checkbox = ttk.Checkbutton(
|
|
355
350
|
checkboxes_frame,
|
|
356
351
|
text=_("Annotate docs into .param files"),
|
|
357
|
-
state="normal" if self.
|
|
352
|
+
state="normal" if self.configuration_manager.parameter_documentation_available() else "disabled",
|
|
358
353
|
variable=self.annotate_params_into_files,
|
|
359
354
|
command=lambda: ProgramSettings.set_setting(
|
|
360
355
|
"annotate_docs_into_param_files", self.annotate_params_into_files.get()
|
|
@@ -419,7 +414,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
419
414
|
state=(
|
|
420
415
|
"normal"
|
|
421
416
|
if self.gui_complexity != "simple"
|
|
422
|
-
or self.configuration_manager.is_configuration_step_optional(
|
|
417
|
+
or self.configuration_manager.is_configuration_step_optional()
|
|
423
418
|
or not self.configuration_manager.is_fc_connected
|
|
424
419
|
else "disabled"
|
|
425
420
|
)
|
|
@@ -505,7 +500,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
505
500
|
|
|
506
501
|
try:
|
|
507
502
|
# Inject GUI callbacks into business logic workflow
|
|
508
|
-
|
|
503
|
+
_success = self.configuration_manager.handle_imu_temperature_calibration_workflow(
|
|
509
504
|
selected_file,
|
|
510
505
|
ask_user_confirmation=ask_yesno_popup,
|
|
511
506
|
select_file=select_file,
|
|
@@ -514,47 +509,43 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
514
509
|
progress_callback=self.tempcal_imu_progress_window.update_progress_bar_300_pct,
|
|
515
510
|
)
|
|
516
511
|
|
|
517
|
-
if success:
|
|
518
|
-
# Force writing doc annotations to file
|
|
519
|
-
self.parameter_editor_table.set_at_least_one_param_edited(True)
|
|
520
|
-
|
|
521
512
|
finally:
|
|
522
513
|
self.tempcal_imu_progress_window.destroy()
|
|
523
514
|
|
|
524
|
-
def __handle_dialog_choice(self, result: list, dialog: tk.Toplevel, choice:
|
|
515
|
+
def __handle_dialog_choice(self, result: list, dialog: tk.Toplevel, choice: ExperimentChoice) -> None:
|
|
525
516
|
result.append(choice)
|
|
526
517
|
dialog.destroy()
|
|
527
518
|
|
|
528
|
-
def __should_copy_fc_values_to_file(self, selected_file: str) -> None:
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
)
|
|
532
|
-
if should_copy and relevant_fc_params and auto_changed_by:
|
|
533
|
-
msg = _(
|
|
534
|
-
"This configuration step requires external changes by: {auto_changed_by}\n\n"
|
|
535
|
-
"The external tool experiment procedure is described in the tuning guide.\n\n"
|
|
536
|
-
"Choose an option:\n"
|
|
537
|
-
"* CLOSE - Close the application and go perform the experiment\n"
|
|
538
|
-
"* YES - Copy current FC values to {selected_file} (if you've already completed the experiment)\n"
|
|
539
|
-
"* NO - Continue without copying values (if you haven't performed the experiment yet,"
|
|
540
|
-
" but know what you are doing)"
|
|
541
|
-
).format(auto_changed_by=auto_changed_by, selected_file=selected_file)
|
|
542
|
-
|
|
519
|
+
def __should_copy_fc_values_to_file(self, selected_file: str) -> None:
|
|
520
|
+
def ask_user_choice(title: str, message: str, options: list[str]) -> ExperimentChoice: # pylint: disable=too-many-locals
|
|
521
|
+
"""GUI callback for asking user choice with custom dialog."""
|
|
543
522
|
# Create custom dialog with Close, Yes, No buttons
|
|
544
523
|
dialog = tk.Toplevel(self.root)
|
|
545
524
|
# Hide dialog initially to prevent flickering
|
|
546
525
|
dialog.withdraw()
|
|
547
526
|
dialog.transient(self.root)
|
|
548
|
-
dialog.title(
|
|
527
|
+
dialog.title(title)
|
|
549
528
|
dialog.resizable(width=False, height=False)
|
|
550
529
|
dialog.protocol("WM_DELETE_WINDOW", dialog.destroy)
|
|
551
530
|
|
|
552
531
|
# Message text
|
|
553
|
-
message_label = tk.Label(dialog, text=
|
|
532
|
+
message_label = tk.Label(dialog, text=message, justify=tk.LEFT, padx=20, pady=10)
|
|
554
533
|
message_label.pack(padx=10, pady=10)
|
|
555
534
|
|
|
535
|
+
# Clickable link to tuning guide
|
|
536
|
+
safe_font_config = get_safe_font_config()
|
|
537
|
+
link_label = tk.Label(
|
|
538
|
+
dialog,
|
|
539
|
+
text=_("Click here to open the Tuning Guide relevant Section"),
|
|
540
|
+
fg="blue",
|
|
541
|
+
cursor="hand2",
|
|
542
|
+
font=(str(safe_font_config["family"]), int(safe_font_config["size"]), "underline"),
|
|
543
|
+
)
|
|
544
|
+
link_label.pack(pady=(0, 10))
|
|
545
|
+
link_label.bind("<Button-1>", lambda _e: self.configuration_manager.open_documentation_in_browser(selected_file))
|
|
546
|
+
|
|
556
547
|
# Result variable
|
|
557
|
-
result: list[
|
|
548
|
+
result: list[ExperimentChoice] = []
|
|
558
549
|
|
|
559
550
|
# Button frame
|
|
560
551
|
button_frame = tk.Frame(dialog)
|
|
@@ -563,25 +554,31 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
563
554
|
# Close button (default)
|
|
564
555
|
close_button = tk.Button(
|
|
565
556
|
button_frame,
|
|
566
|
-
text=
|
|
557
|
+
text=options[0], # "Close"
|
|
567
558
|
width=10,
|
|
568
|
-
command=lambda: self.__handle_dialog_choice(result, dialog, choice=
|
|
559
|
+
command=lambda: self.__handle_dialog_choice(result, dialog, choice="close"),
|
|
569
560
|
)
|
|
570
561
|
close_button.pack(side=tk.LEFT, padx=5)
|
|
571
562
|
|
|
572
563
|
# Yes button
|
|
573
564
|
yes_button = tk.Button(
|
|
574
|
-
button_frame,
|
|
565
|
+
button_frame,
|
|
566
|
+
text=options[1],
|
|
567
|
+
width=10, # "Yes"
|
|
568
|
+
command=lambda: self.__handle_dialog_choice(result, dialog, choice=True),
|
|
575
569
|
)
|
|
576
570
|
yes_button.pack(side=tk.LEFT, padx=5)
|
|
577
571
|
|
|
578
572
|
# No button
|
|
579
573
|
no_button = tk.Button(
|
|
580
|
-
button_frame,
|
|
574
|
+
button_frame,
|
|
575
|
+
text=options[2],
|
|
576
|
+
width=10, # "No"
|
|
577
|
+
command=lambda: self.__handle_dialog_choice(result, dialog, choice=False),
|
|
581
578
|
)
|
|
582
579
|
no_button.pack(side=tk.LEFT, padx=5)
|
|
583
580
|
|
|
584
|
-
dialog.bind("<Return>", lambda _event: self.__handle_dialog_choice(result, dialog,
|
|
581
|
+
dialog.bind("<Return>", lambda _event: self.__handle_dialog_choice(result, dialog, choice="close"))
|
|
585
582
|
|
|
586
583
|
# Center the dialog on the parent window
|
|
587
584
|
dialog.deiconify()
|
|
@@ -604,25 +601,27 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
604
601
|
|
|
605
602
|
# Wait until dialog is closed
|
|
606
603
|
self.root.wait_window(dialog)
|
|
607
|
-
|
|
604
|
+
return result[-1] if result else "close"
|
|
608
605
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
606
|
+
result = self.configuration_manager.handle_copy_fc_values_workflow(
|
|
607
|
+
selected_file,
|
|
608
|
+
ask_user_choice,
|
|
609
|
+
show_info_popup,
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
if result == "close":
|
|
613
|
+
# User chose to close the application
|
|
614
|
+
sys.exit(0)
|
|
616
615
|
|
|
617
616
|
def __should_jump_to_file(self, selected_file: str) -> str:
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return
|
|
617
|
+
dest_file = self.configuration_manager.handle_file_jump_workflow(
|
|
618
|
+
selected_file,
|
|
619
|
+
self.gui_complexity,
|
|
620
|
+
ask_yesno_popup,
|
|
621
|
+
)
|
|
622
|
+
if dest_file != selected_file:
|
|
623
|
+
self.file_selection_combobox.set(dest_file)
|
|
624
|
+
return dest_file
|
|
626
625
|
|
|
627
626
|
def __should_download_file_from_url(self, selected_file: str) -> None:
|
|
628
627
|
self.configuration_manager.should_download_file_from_url_workflow(
|
|
@@ -657,22 +656,22 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
657
656
|
self.__do_tempcal_imu(selected_file)
|
|
658
657
|
# open the documentation of the next step in the browser,
|
|
659
658
|
# before giving the user the option to close the SW in the __should_copy_fc_values_to_file method
|
|
660
|
-
self.documentation_frame.
|
|
659
|
+
if self.documentation_frame.get_auto_open_documentation_in_browser() or self.gui_complexity == "simple":
|
|
660
|
+
self.configuration_manager.open_documentation_in_browser(selected_file)
|
|
661
661
|
self.__should_copy_fc_values_to_file(selected_file)
|
|
662
662
|
selected_file = self.__should_jump_to_file(selected_file)
|
|
663
663
|
self.__should_download_file_from_url(selected_file)
|
|
664
664
|
self.__should_upload_file_to_fc(selected_file)
|
|
665
665
|
|
|
666
|
-
#
|
|
666
|
+
# current_file might have been changed by jump, so update again
|
|
667
667
|
self.configuration_manager.current_file = selected_file
|
|
668
|
-
self.
|
|
669
|
-
self.documentation_frame.
|
|
670
|
-
self.
|
|
671
|
-
self.repopulate_parameter_table()
|
|
668
|
+
self.documentation_frame.refresh_documentation_labels()
|
|
669
|
+
self.documentation_frame.update_why_why_now_tooltip()
|
|
670
|
+
self.repopulate_parameter_table(regenerate_from_disk=True)
|
|
672
671
|
self._update_skip_button_state()
|
|
673
672
|
|
|
674
673
|
def _update_progress_bar_from_file(self, selected_file: str) -> None:
|
|
675
|
-
if self.
|
|
674
|
+
if self.configuration_manager.configuration_phases():
|
|
676
675
|
try:
|
|
677
676
|
step_nr = int(selected_file[:2])
|
|
678
677
|
self.stage_progress_bar.update_progress(step_nr)
|
|
@@ -690,41 +689,18 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
690
689
|
if not redownload:
|
|
691
690
|
self.on_param_file_combobox_change(None, forced=True) # the initial param read will trigger a table update
|
|
692
691
|
|
|
693
|
-
def repopulate_parameter_table(self) -> None:
|
|
692
|
+
def repopulate_parameter_table(self, regenerate_from_disk: bool) -> None:
|
|
694
693
|
if not self.configuration_manager.current_file:
|
|
695
694
|
return # no file was yet selected, so skip it
|
|
696
695
|
# Re-populate the table with the new parameters
|
|
697
|
-
self.parameter_editor_table.repopulate(self.show_only_differences.get(), self.gui_complexity)
|
|
696
|
+
self.parameter_editor_table.repopulate(self.show_only_differences.get(), self.gui_complexity, regenerate_from_disk)
|
|
698
697
|
|
|
699
698
|
def on_show_only_changed_checkbox_change(self) -> None:
|
|
700
|
-
self.repopulate_parameter_table()
|
|
701
|
-
|
|
702
|
-
def upload_params_that_require_reset(self, selected_params: dict) -> None:
|
|
703
|
-
"""
|
|
704
|
-
Write only the selected parameters to the flight controller that require a reset.
|
|
705
|
-
|
|
706
|
-
After the reset, the other parameters that do not require a reset must still be written to the flight controller.
|
|
707
|
-
"""
|
|
708
|
-
self.reset_progress_window = ProgressWindow(
|
|
709
|
-
self.root,
|
|
710
|
-
_("Resetting Flight Controller"),
|
|
711
|
-
_("Waiting for {} of {} seconds"),
|
|
712
|
-
only_show_when_update_progress_called=True,
|
|
713
|
-
)
|
|
714
|
-
|
|
715
|
-
if self.configuration_manager.upload_parameters_that_require_reset_workflow(
|
|
716
|
-
selected_params,
|
|
717
|
-
ask_confirmation=ask_yesno_popup,
|
|
718
|
-
show_error=show_error_popup,
|
|
719
|
-
progress_callback=self.reset_progress_window.update_progress_bar,
|
|
720
|
-
):
|
|
721
|
-
self.at_least_one_changed_parameter_written = True
|
|
722
|
-
|
|
723
|
-
self.reset_progress_window.destroy() # for the case that we are doing a test and there is no real FC connected
|
|
699
|
+
self.repopulate_parameter_table(regenerate_from_disk=False)
|
|
724
700
|
|
|
725
701
|
def on_upload_selected_click(self) -> None:
|
|
726
702
|
self.write_changes_to_intermediate_parameter_file()
|
|
727
|
-
selected_params = self.parameter_editor_table.get_upload_selected_params(
|
|
703
|
+
selected_params = self.parameter_editor_table.get_upload_selected_params(self.gui_complexity)
|
|
728
704
|
if selected_params:
|
|
729
705
|
if self.configuration_manager.fc_parameters:
|
|
730
706
|
self.upload_selected_params(selected_params)
|
|
@@ -741,41 +717,31 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
741
717
|
|
|
742
718
|
# This function can recurse multiple times if there is an upload error
|
|
743
719
|
def upload_selected_params(self, selected_params: dict) -> None:
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
720
|
+
# Create progress windows
|
|
721
|
+
self.reset_progress_window = ProgressWindow(
|
|
722
|
+
self.root,
|
|
723
|
+
_("Resetting Flight Controller"),
|
|
724
|
+
_("Waiting for {} of {} seconds"),
|
|
725
|
+
only_show_when_update_progress_called=True,
|
|
748
726
|
)
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
nr_changed = self.configuration_manager.upload_selected_parameters_workflow(
|
|
754
|
-
selected_params, show_error=show_error_popup
|
|
727
|
+
self.param_download_progress_window = ProgressWindow(
|
|
728
|
+
self.root,
|
|
729
|
+
_("Re-downloading FC parameters"),
|
|
730
|
+
_("Downloaded {} of {} parameters"),
|
|
755
731
|
)
|
|
756
732
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
_("Failed to upload the following parameters to the flight controller:\n")
|
|
770
|
-
+ f"{(', ').join(param_upload_error)}",
|
|
771
|
-
):
|
|
772
|
-
self.upload_selected_params(selected_params)
|
|
773
|
-
else:
|
|
774
|
-
logging_info(_("All parameters uploaded to the flight controller successfully"))
|
|
775
|
-
|
|
776
|
-
self.configuration_manager.export_fc_params_missing_or_different()
|
|
777
|
-
|
|
778
|
-
self.local_filesystem.write_last_uploaded_filename(self.configuration_manager.current_file)
|
|
733
|
+
try:
|
|
734
|
+
self.configuration_manager.upload_selected_params_workflow(
|
|
735
|
+
selected_params,
|
|
736
|
+
ask_confirmation=ask_yesno_popup,
|
|
737
|
+
ask_retry_cancel=ask_retry_cancel_popup,
|
|
738
|
+
show_error=show_error_popup,
|
|
739
|
+
progress_callback_for_reset=self.reset_progress_window.update_progress_bar,
|
|
740
|
+
progress_callback_for_download=self.param_download_progress_window.update_progress_bar,
|
|
741
|
+
)
|
|
742
|
+
finally:
|
|
743
|
+
self.reset_progress_window.destroy()
|
|
744
|
+
self.param_download_progress_window.destroy()
|
|
779
745
|
|
|
780
746
|
def on_download_last_flight_log_click(self) -> None:
|
|
781
747
|
"""Handle the download last flight log button click."""
|
|
@@ -811,7 +777,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
811
777
|
skip_button_state = (
|
|
812
778
|
"normal"
|
|
813
779
|
if self.gui_complexity != "simple"
|
|
814
|
-
or self.configuration_manager.is_configuration_step_optional(
|
|
780
|
+
or self.configuration_manager.is_configuration_step_optional()
|
|
815
781
|
or not self.configuration_manager.is_fc_connected
|
|
816
782
|
else "disabled"
|
|
817
783
|
)
|
|
@@ -821,7 +787,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
821
787
|
self.write_changes_to_intermediate_parameter_file()
|
|
822
788
|
|
|
823
789
|
# Use ConfigurationManager to get the next non-optional file
|
|
824
|
-
next_file = self.configuration_manager.get_next_non_optional_file(
|
|
790
|
+
next_file = self.configuration_manager.get_next_non_optional_file()
|
|
825
791
|
|
|
826
792
|
if next_file is None:
|
|
827
793
|
# No more files to process, write summary and close
|
|
@@ -839,25 +805,10 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
|
|
|
839
805
|
self.on_param_file_combobox_change(None)
|
|
840
806
|
|
|
841
807
|
def write_changes_to_intermediate_parameter_file(self) -> None:
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
# But only if self.annotate_params_into_files.get()
|
|
847
|
-
if self.parameter_editor_table.get_at_least_one_param_edited() or (
|
|
848
|
-
self.annotate_params_into_files.get() and elapsed_since_last_ask > 1.0
|
|
849
|
-
):
|
|
850
|
-
msg = _("Do you want to write the changes to the {current_filename} file?").format(
|
|
851
|
-
current_filename=self.configuration_manager.current_file
|
|
852
|
-
)
|
|
853
|
-
if messagebox.askyesno(_("One or more parameters have been edited"), msg.format(**locals())):
|
|
854
|
-
self.local_filesystem.export_to_param(
|
|
855
|
-
self.local_filesystem.file_parameters[self.configuration_manager.current_file],
|
|
856
|
-
self.configuration_manager.current_file,
|
|
857
|
-
annotate_doc=self.annotate_params_into_files.get(),
|
|
858
|
-
)
|
|
859
|
-
self.parameter_editor_table.set_at_least_one_param_edited(False)
|
|
860
|
-
self.last_time_asked_to_save = time.time()
|
|
808
|
+
self.configuration_manager.handle_write_changes_workflow(
|
|
809
|
+
self.annotate_params_into_files.get(),
|
|
810
|
+
ask_yesno_popup,
|
|
811
|
+
)
|
|
861
812
|
|
|
862
813
|
def close_connection_and_quit(self) -> None:
|
|
863
814
|
focused_widget = self.parameter_editor_table.view_port.focus_get()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
The documentation frame containing the documentation for the current configuration step.
|
|
3
3
|
|
|
4
|
-
This file is part of
|
|
4
|
+
This file is part of ArduPilot Methodic Configurator. https://github.com/ArduPilot/MethodicConfigurator
|
|
5
5
|
|
|
6
6
|
SPDX-FileCopyrightText: 2024-2025 Amilcar do Carmo Lucas <amilcar.lucas@iav.de>
|
|
7
7
|
|
|
@@ -11,11 +11,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
11
11
|
import tkinter as tk
|
|
12
12
|
from platform import system as platform_system
|
|
13
13
|
from tkinter import ttk
|
|
14
|
-
from webbrowser import open as webbrowser_open # to open the
|
|
14
|
+
from webbrowser import open as webbrowser_open # to open the web documentation
|
|
15
15
|
|
|
16
16
|
from ardupilot_methodic_configurator import _
|
|
17
|
-
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
|
|
18
17
|
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
|
|
18
|
+
from ardupilot_methodic_configurator.configuration_manager import ConfigurationManager
|
|
19
19
|
from ardupilot_methodic_configurator.frontend_tkinter_rich_text import get_widget_font_family_and_size
|
|
20
20
|
from ardupilot_methodic_configurator.frontend_tkinter_show import show_tooltip
|
|
21
21
|
|
|
@@ -49,16 +49,16 @@ class DocumentationFrame:
|
|
|
49
49
|
),
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
def __init__(self, root: tk.Widget,
|
|
52
|
+
def __init__(self, root: tk.Widget, configuration_manager: ConfigurationManager) -> None:
|
|
53
53
|
self.root = root
|
|
54
|
-
self.
|
|
54
|
+
self.configuration_manager = configuration_manager
|
|
55
55
|
self.documentation_frame: ttk.LabelFrame
|
|
56
56
|
self.documentation_labels: dict[str, ttk.Label] = {}
|
|
57
57
|
self.mandatory_level: ttk.Progressbar
|
|
58
58
|
self.auto_open_var = tk.BooleanVar(value=bool(ProgramSettings.get_setting("auto_open_doc_in_browser")))
|
|
59
|
-
self._create_documentation_frame(
|
|
59
|
+
self._create_documentation_frame()
|
|
60
60
|
|
|
61
|
-
def _create_documentation_frame(self
|
|
61
|
+
def _create_documentation_frame(self) -> None:
|
|
62
62
|
self.documentation_frame = ttk.LabelFrame(self.root, text=_("Documentation"))
|
|
63
63
|
|
|
64
64
|
# Create a grid structure within the documentation_frame
|
|
@@ -86,8 +86,8 @@ class DocumentationFrame:
|
|
|
86
86
|
documentation_grid.columnconfigure(1, weight=1)
|
|
87
87
|
|
|
88
88
|
# Dynamically update the documentation text and URL links
|
|
89
|
-
self.refresh_documentation_labels(
|
|
90
|
-
self.update_why_why_now_tooltip(
|
|
89
|
+
self.refresh_documentation_labels()
|
|
90
|
+
self.update_why_why_now_tooltip()
|
|
91
91
|
|
|
92
92
|
def _create_bottom_row(self, documentation_grid: ttk.Frame, row: int) -> None:
|
|
93
93
|
bottom_frame = ttk.Frame(documentation_grid)
|
|
@@ -111,52 +111,28 @@ class DocumentationFrame:
|
|
|
111
111
|
)
|
|
112
112
|
auto_open_checkbox.pack(side=tk.LEFT, expand=False)
|
|
113
113
|
|
|
114
|
-
def update_why_why_now_tooltip(self
|
|
115
|
-
|
|
116
|
-
why_now_tooltip_text = self.local_filesystem.get_seq_tooltip_text(current_file, "why_now")
|
|
117
|
-
tooltip_text = ""
|
|
118
|
-
if why_tooltip_text:
|
|
119
|
-
tooltip_text += _("Why: ") + _(why_tooltip_text) + "\n"
|
|
120
|
-
if why_now_tooltip_text:
|
|
121
|
-
tooltip_text += _("Why now: ") + _(why_now_tooltip_text)
|
|
114
|
+
def update_why_why_now_tooltip(self) -> None:
|
|
115
|
+
tooltip_text = self.configuration_manager.get_why_why_now_tooltip()
|
|
122
116
|
if tooltip_text:
|
|
123
117
|
show_tooltip(self.documentation_frame, tooltip_text, position_below=False)
|
|
124
118
|
|
|
125
|
-
def
|
|
126
|
-
|
|
127
|
-
_wiki_text, wiki_url = self.local_filesystem.get_documentation_text_and_url(current_file, "wiki")
|
|
128
|
-
_external_tool_text, external_tool_url = self.local_filesystem.get_documentation_text_and_url(
|
|
129
|
-
current_file, "external_tool"
|
|
130
|
-
)
|
|
119
|
+
def get_auto_open_documentation_in_browser(self) -> bool:
|
|
120
|
+
return self.auto_open_var.get()
|
|
131
121
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
webbrowser_open(url=wiki_url, new=0, autoraise=False)
|
|
135
|
-
if external_tool_url:
|
|
136
|
-
webbrowser_open(url=external_tool_url, new=0, autoraise=False)
|
|
137
|
-
if blog_url:
|
|
138
|
-
webbrowser_open(url=blog_url, new=0, autoraise=True)
|
|
139
|
-
|
|
140
|
-
def refresh_documentation_labels(self, current_file: str) -> None:
|
|
141
|
-
if current_file:
|
|
142
|
-
title = _("{current_file} Documentation")
|
|
143
|
-
frame_title = title.format(**locals())
|
|
144
|
-
else:
|
|
145
|
-
frame_title = _("Documentation")
|
|
122
|
+
def refresh_documentation_labels(self) -> None:
|
|
123
|
+
frame_title = self.configuration_manager.get_documentation_frame_title()
|
|
146
124
|
self.documentation_frame.config(text=frame_title)
|
|
147
125
|
|
|
148
|
-
blog_text, blog_url = self.
|
|
126
|
+
blog_text, blog_url = self.configuration_manager.get_documentation_text_and_url("blog")
|
|
149
127
|
self._refresh_documentation_label(self.BLOG_LABEL, _(blog_text) if blog_text else "", blog_url)
|
|
150
|
-
wiki_text, wiki_url = self.
|
|
128
|
+
wiki_text, wiki_url = self.configuration_manager.get_documentation_text_and_url("wiki")
|
|
151
129
|
self._refresh_documentation_label(self.WIKI_LABEL, _(wiki_text) if wiki_text else "", wiki_url)
|
|
152
|
-
external_tool_text, external_tool_url = self.
|
|
153
|
-
current_file, "external_tool"
|
|
154
|
-
)
|
|
130
|
+
external_tool_text, external_tool_url = self.configuration_manager.get_documentation_text_and_url("external_tool")
|
|
155
131
|
self._refresh_documentation_label(
|
|
156
132
|
self.EXTERNAL_TOOL_LABEL, _(external_tool_text) if external_tool_text else "", external_tool_url
|
|
157
133
|
)
|
|
158
|
-
mandatory_text, _mandatory_url = self.
|
|
159
|
-
self._refresh_mandatory_level(
|
|
134
|
+
mandatory_text, _mandatory_url = self.configuration_manager.get_documentation_text_and_url("mandatory")
|
|
135
|
+
self._refresh_mandatory_level(mandatory_text)
|
|
160
136
|
|
|
161
137
|
def _refresh_documentation_label(self, label_key: str, text: str, url: str, url_expected: bool = True) -> None:
|
|
162
138
|
label = self.documentation_labels[label_key]
|
|
@@ -175,17 +151,7 @@ class DocumentationFrame:
|
|
|
175
151
|
if url_expected:
|
|
176
152
|
show_tooltip(label, _("Documentation URL not available"))
|
|
177
153
|
|
|
178
|
-
def _refresh_mandatory_level(self,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
percentage = int("".join([c for c in text[:3] if c.isdigit()]))
|
|
183
|
-
if 0 <= percentage <= 100:
|
|
184
|
-
self.mandatory_level.config(value=percentage)
|
|
185
|
-
tooltip = _("This configuration step ({current_file} intermediate parameter file) is {percentage}% mandatory")
|
|
186
|
-
else:
|
|
187
|
-
raise ValueError
|
|
188
|
-
except ValueError:
|
|
189
|
-
self.mandatory_level.config(value=0)
|
|
190
|
-
tooltip = _("Mandatory level not available for this configuration step ({current_file})")
|
|
191
|
-
show_tooltip(self.mandatory_level, tooltip.format(**locals()))
|
|
154
|
+
def _refresh_mandatory_level(self, text: str) -> None:
|
|
155
|
+
percentage, tooltip = self.configuration_manager.parse_mandatory_level_percentage(text)
|
|
156
|
+
self.mandatory_level.config(value=percentage)
|
|
157
|
+
show_tooltip(self.mandatory_level, tooltip)
|