Skip to content

Commit

Permalink
FEATURE: Remember if usage should be displayed
Browse files Browse the repository at this point in the history
  • Loading branch information
amilcarlucas committed Nov 4, 2024
1 parent 7d86e22 commit d372394
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 68 deletions.
1 change: 0 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"args": [
"--allow-editing-template-files",
"--device=test",
"--skip-welcome-popup",
"--skip-component-editor",
"--vehicle-dir=C:\\Users\\amilc\\Documents\\iav\\ardupilot_methodic_configurator\\vehicle_templates\\ArduCopter\\FETtec-5"
],
Expand Down
2 changes: 1 addition & 1 deletion MethodicConfigurator/ardupilot_methodic_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def main():
start_file = local_filesystem.get_start_file(args.n, imu_tcal_available)

# Call the GUI function with the starting intermediate parameter file
ParameterEditorWindow(start_file, flight_controller, local_filesystem, not args.skip_welcome_popup)
ParameterEditorWindow(start_file, flight_controller, local_filesystem)

# Close the connection to the flight controller
flight_controller.disconnect()
Expand Down
19 changes: 19 additions & 0 deletions MethodicConfigurator/backend_filesystem_program_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ def __get_settings_as_dict():

if "directory_selection" not in settings:
settings["directory_selection"] = {}

if "display_usage_popup" not in settings:
settings["display_usage_popup"] = {}
if "component_editor" not in settings["display_usage_popup"]:
settings["display_usage_popup"]["component_editor"] = True
if "parameter_editor" not in settings["display_usage_popup"]:
settings["display_usage_popup"]["parameter_editor"] = True

return settings

@staticmethod
Expand Down Expand Up @@ -220,3 +228,14 @@ def get_recently_used_dirs():
vehicle_dir = settings["directory_selection"].get("vehicle_dir", vehicles_default_dir)

return template_dir, new_base_dir, vehicle_dir

@staticmethod
def display_usage_popup(ptype: str):
return ProgramSettings.__get_settings_as_dict()["display_usage_popup"].get(ptype, True)

@staticmethod
def set_display_usage_popup(ptype: str, value: bool):
if ptype in ["component_editor", "parameter_editor"]:
settings, _, _ = ProgramSettings.__get_settings_config()
settings["display_usage_popup"][ptype] = value
ProgramSettings.__set_settings_from_dict(settings)
56 changes: 56 additions & 0 deletions MethodicConfigurator/frontend_tkinter_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from tkinter import font as tkFont
from tkinter import messagebox
from tkinter import ttk
from tkinter import BooleanVar

# from logging import debug as logging_debug
# from logging import info as logging_info
Expand All @@ -27,6 +28,8 @@

from MethodicConfigurator.backend_filesystem import LocalFilesystem

from MethodicConfigurator.backend_filesystem_program_settings import ProgramSettings

from MethodicConfigurator.internationalization import _


Expand Down Expand Up @@ -389,3 +392,56 @@ def put_image_in_label(parent: tk.Toplevel, filepath: str, image_height: int=40)
image_label = ttk.Label(parent, image=photo)
image_label.image = photo # Keep a reference to the image to prevent it from being garbage collected
return image_label


class UsagePopupWindow():
"""
A class for creating and managing usage popup windows in the application.
This class extends the BaseWindow class to provide functionality for displaying
usage popups with instructions and options to show them again or dismiss.
"""
def __init__(self):
pass

@staticmethod
def should_display(ptype: str) -> bool:
return ProgramSettings.display_usage_popup(ptype)

@staticmethod
def display(parent: tk.Tk, usage_popup_window: BaseWindow, title: str, # pylint: disable=too-many-arguments
ptype: str, geometry: str, instructions_text: RichText):

usage_popup_window.root.title(title)
usage_popup_window.root.geometry(geometry)

instructions_text.pack(padx=6, pady=10)

show_again_var = BooleanVar()
show_again_var.set(True)

def update_show_again():
ProgramSettings.set_display_usage_popup(ptype, show_again_var.get())

show_again_checkbox = ttk.Checkbutton(usage_popup_window.main_frame, text=_("Show this usage popup again"),
variable=show_again_var, command=update_show_again)
show_again_checkbox.pack(pady=(10, 5))

dismiss_button = ttk.Button(usage_popup_window.main_frame, text=_("Dismiss"),
command=lambda: UsagePopupWindow.close(usage_popup_window, parent))
dismiss_button.pack(pady=10)

BaseWindow.center_window(usage_popup_window.root, parent)
usage_popup_window.root.attributes('-topmost', True)

if platform_system() == 'Windows':
parent.attributes('-disabled', True) # Disable parent window input

usage_popup_window.root.protocol("WM_DELETE_WINDOW", lambda: UsagePopupWindow.close(usage_popup_window, parent))

@staticmethod
def close(usage_popup_window, parent):
usage_popup_window.root.destroy()
if platform_system() == 'Windows':
parent.attributes('-disabled', False) # Re-enable the parent window
parent.focus_set()
30 changes: 5 additions & 25 deletions MethodicConfigurator/frontend_tkinter_component_editor_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
from tkinter import ttk
from tkinter import messagebox

from platform import system as platform_system

from MethodicConfigurator.common_arguments import add_common_arguments_and_parse

from MethodicConfigurator.backend_filesystem import LocalFilesystem
Expand All @@ -30,6 +28,7 @@
from MethodicConfigurator.frontend_tkinter_base import ScrollFrame
from MethodicConfigurator.frontend_tkinter_base import BaseWindow
from MethodicConfigurator.frontend_tkinter_base import RichText
from MethodicConfigurator.frontend_tkinter_base import UsagePopupWindow

from MethodicConfigurator.internationalization import _

Expand Down Expand Up @@ -111,19 +110,16 @@ def __init__(self, version, local_filesystem: LocalFilesystem=None):
self.save_button = ttk.Button(save_frame, text=_("Save data and start configuration"), command=self.save_data)
show_tooltip(self.save_button, _("Save component data and start parameter value configuration and tuning."))
self.save_button.pack(pady=7)
self.root.after(10, self.__display_component_editor_usage_instructions(self.root))
if UsagePopupWindow.should_display("component_editor"):
self.root.after(10, self.__display_component_editor_usage_instructions(self.root))

@staticmethod
def __display_component_editor_usage_instructions(parent: tk.Tk):
usage_popup_window = BaseWindow(parent)
usage_popup_window.root.title(_("How to use the component editor"))
usage_popup_window.root.geometry("690x160")

style = ttk.Style()

instructions_text = RichText(usage_popup_window.main_frame, wrap=tk.WORD, height=5, bd=0,
background=style.lookup("TLabel", "background"))
instructions_text.pack(padx=6, pady=10)
instructions_text.insert(tk.END, _("1. Describe "))
instructions_text.insert(tk.END, _("all"), "bold")
instructions_text.insert(tk.END, _(" vehicle component properties in the window below\n"))
Expand All @@ -136,26 +132,10 @@ def __display_component_editor_usage_instructions(parent: tk.Tk):
instructions_text.insert(tk.END, _("4. Press the "))
instructions_text.insert(tk.END, _("Save data and start configuration"), "italic")
instructions_text.insert(tk.END, _(" only after all information is correct"))

instructions_text.config(state=tk.DISABLED)

dismiss_button = ttk.Button(usage_popup_window.main_frame, text=_("Dismiss"),
command=lambda: ComponentEditorWindowBase.__close_instructions_window(usage_popup_window,
parent))
dismiss_button.pack(pady=10)

BaseWindow.center_window(usage_popup_window.root, parent)
usage_popup_window.root.attributes('-topmost', True)

if platform_system() == 'Windows':
parent.attributes('-disabled', True) # Disable parent window input

@staticmethod
def __close_instructions_window(instructions_window, parent):
instructions_window.root.destroy()
if platform_system() == 'Windows':
parent.attributes('-disabled', False) # Re-enable the parent window
parent.focus_set()
UsagePopupWindow.display(parent, usage_popup_window, _("How to use the component editor window"),
"component_editor", "690x200", instructions_text)

def update_json_data(self): # should be overwritten in child classes
if 'Format version' not in self.data:
Expand Down
80 changes: 39 additions & 41 deletions MethodicConfigurator/frontend_tkinter_parameter_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
from typing import List
from typing import Tuple

from platform import system as platform_system

from webbrowser import open as webbrowser_open # to open the blog post documentation

from MethodicConfigurator.annotate_params import Par
Expand All @@ -37,6 +35,8 @@
from MethodicConfigurator.backend_filesystem import LocalFilesystem
from MethodicConfigurator.backend_filesystem import is_within_tolerance

from MethodicConfigurator.backend_filesystem_program_settings import ProgramSettings

from MethodicConfigurator.backend_flightcontroller import FlightController

from MethodicConfigurator.frontend_tkinter_base import show_tooltip
Expand All @@ -45,6 +45,7 @@
from MethodicConfigurator.frontend_tkinter_base import BaseWindow
from MethodicConfigurator.frontend_tkinter_base import RichText
from MethodicConfigurator.frontend_tkinter_base import get_widget_font
from MethodicConfigurator.frontend_tkinter_base import UsagePopupWindow

from MethodicConfigurator.frontend_tkinter_directory_selection import VehicleDirectorySelectionWidgets

Expand Down Expand Up @@ -134,11 +135,11 @@ def __update_documentation_label(self, label_key, text, url, url_expected=True):
show_tooltip(label, _("Documentation URL not available"))


def show_about_window(root, _version: str):
def show_about_window(root, _version: str): # pylint: disable=too-many-locals
# Create a new window for the custom "About" message
about_window = tk.Toplevel(root)
about_window.title(_("About"))
about_window.geometry("650x220")
about_window.geometry("650x320")

main_frame = ttk.Frame(about_window)
main_frame.pack(expand=True, fill=tk.BOTH)
Expand All @@ -151,6 +152,26 @@ def show_about_window(root, _version: str):
about_label = ttk.Label(main_frame, text=about_message.format(**locals()), wraplength=450)
about_label.grid(column=0, row=0, padx=10, pady=10, columnspan=5) # Span across all columns

usage_popup_frame = ttk.Frame(main_frame)
usage_popup_frame.grid(column=0, row=1, columnspan=5, padx=10, pady=10)

usage_popup_label = ttk.Label(usage_popup_frame, text=_("Display usage popup"))
usage_popup_label.pack(side=tk.TOP, anchor=tk.W)

component_editor_var = tk.BooleanVar(value=ProgramSettings.display_usage_popup("component_editor"))
component_editor_checkbox = ttk.Checkbutton(usage_popup_frame, text=_("Component editor window"),
variable=component_editor_var,
command=lambda: ProgramSettings.set_display_usage_popup("component_editor",
component_editor_var.get()))
component_editor_checkbox.pack(side=tk.TOP, anchor=tk.W)

parameter_editor_var = tk.BooleanVar(value=ProgramSettings.display_usage_popup("parameter_editor"))
parameter_editor_checkbox = ttk.Checkbutton(usage_popup_frame, text=_("Parameter file editor and uploader window"),
variable=parameter_editor_var,
command=lambda: ProgramSettings.set_display_usage_popup("parameter_editor",
parameter_editor_var.get()))
parameter_editor_checkbox.pack(side=tk.TOP, anchor=tk.W)

# Create buttons for each action
user_manual_button = ttk.Button(main_frame, text=_("User Manual"),
command=lambda: webbrowser_open(
Expand All @@ -169,11 +190,11 @@ def show_about_window(root, _version: str):
"https://github.com/ArduPilot/MethodicConfigurator"))

# Place buttons using grid for equal spacing and better control over layout
user_manual_button.grid(column=0, row=1, padx=10, pady=10)
support_forum_button.grid(column=1, row=1, padx=10, pady=10)
report_bug_button.grid(column=2, row=1, padx=10, pady=10)
licenses_button.grid(column=3, row=1, padx=10, pady=10)
source_button.grid(column=4, row=1, padx=10, pady=10)
user_manual_button.grid(column=0, row=2, padx=10, pady=10)
support_forum_button.grid(column=1, row=2, padx=10, pady=10)
report_bug_button.grid(column=2, row=2, padx=10, pady=10)
licenses_button.grid(column=3, row=2, padx=10, pady=10)
source_button.grid(column=4, row=2, padx=10, pady=10)

# Configure the grid to ensure equal spacing and expansion
main_frame.columnconfigure([0, 1, 2, 3, 4], weight=1)
Expand All @@ -187,7 +208,7 @@ class ParameterEditorWindow(BaseWindow): # pylint: disable=too-many-instance-at
parameters, documentation, and flight controller connection settings.
"""
def __init__(self, current_file: str, flight_controller: FlightController,
local_filesystem: LocalFilesystem, display_welcome_popup: bool=True):
local_filesystem: LocalFilesystem):
super().__init__()
self.current_file = current_file
self.flight_controller = flight_controller
Expand Down Expand Up @@ -230,8 +251,8 @@ def __init__(self, current_file: str, flight_controller: FlightController,
self.root.after(10, self.on_param_file_combobox_change(None, True))

# this one should be on top of the previous one hence the longer time
if display_welcome_popup:
self.root.after(100, self.__please_read_the_docs(self.root))
if UsagePopupWindow.should_display("parameter_editor"):
self.root.after(100, self.__display_usage_popup_window(self.root))
self.root.mainloop()

def __create_conf_widgets(self, version: str):
Expand Down Expand Up @@ -352,16 +373,12 @@ def __create_parameter_area_widgets(self):
"controller\nIf changes have been made to the current file it will ask if you want to save them"))

@staticmethod
def __please_read_the_docs(parent: tk.Tk):
welcome_window = BaseWindow(parent)
welcome_window.root.title(_("Welcome to the ArduPilot Methodic Configurator"))
welcome_window.root.geometry("690x170")

def __display_usage_popup_window(parent: tk.Tk):
usage_popup_window = BaseWindow(parent)
style = ttk.Style()

instructions_text = RichText(welcome_window.main_frame, wrap=tk.WORD, height=5, bd=0,
instructions_text = RichText(usage_popup_window.main_frame, wrap=tk.WORD, height=5, bd=0,
background=style.lookup("TLabel", "background"))
instructions_text.pack(padx=10, pady=10)
instructions_text.insert(tk.END, _("1. Read "))
instructions_text.insert(tk.END, _("all"), "bold")
instructions_text.insert(tk.END, _(" the documentation on top of the parameter table\n"))
Expand All @@ -379,25 +396,10 @@ def __please_read_the_docs(parent: tk.Tk):
instructions_text.insert(tk.END, _("Upload selected params to FC, and advance to next param file"), "italic")
instructions_text.insert(tk.END, _(" button\n"))
instructions_text.insert(tk.END, _("5. Repeat from the top until the program automatically closes"))

instructions_text.config(state=tk.DISABLED)

dismiss_button = ttk.Button(welcome_window.main_frame, text=_("Dismiss"),
command=lambda: ParameterEditorWindow.__close_instructions_window(welcome_window, parent))
dismiss_button.pack(pady=10)

BaseWindow.center_window(welcome_window.root, parent)
welcome_window.root.attributes('-topmost', True)

if platform_system() == 'Windows':
parent.attributes('-disabled', True) # Disable parent window input

@staticmethod
def __close_instructions_window(welcome_window, parent):
welcome_window.root.destroy()
if platform_system() == 'Windows':
parent.attributes('-disabled', False) # Re-enable the parent window
parent.focus_set()
UsagePopupWindow.display(parent, usage_popup_window, _("How to use the parameter file editor and uploader window"),
"parameter_editor", "690x200", instructions_text)

def __do_tempcal_imu(self, selected_file:str):
tempcal_imu_result_param_filename, tempcal_imu_result_param_fullpath = \
Expand Down Expand Up @@ -759,10 +761,6 @@ def close_connection_and_quit(self):

@staticmethod
def add_argparse_arguments(parser):
parser.add_argument('--skip-welcome-popup',
action='store_true',
help=_('Skip the welcome popup window. Only use this if you already know how to use the software. '
'Default to false'))
return parser


Expand Down Expand Up @@ -790,4 +788,4 @@ def argument_parser():

fc = FlightController(args.reboot_time)
filesystem = LocalFilesystem(args.vehicle_dir, args.vehicle_type, None, args.allow_editing_template_files)
ParameterEditorWindow('04_board_orientation.param', fc, filesystem, not args.skip_welcome_popup)
ParameterEditorWindow('04_board_orientation.param', fc, filesystem)

0 comments on commit d372394

Please sign in to comment.