Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions ardupilot_methodic_configurator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from logging import info as logging_info
from sys import exit as sys_exit
from typing import Union
from webbrowser import open as webbrowser_open

import argcomplete

Expand All @@ -36,7 +35,7 @@
from ardupilot_methodic_configurator.backend_filesystem_freedesktop import FreeDesktop
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
from ardupilot_methodic_configurator.backend_internet import verify_and_open_url
from ardupilot_methodic_configurator.backend_internet import verify_and_open_url, webbrowser_open_url
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
from ardupilot_methodic_configurator.data_model_par_dict import ParDict
from ardupilot_methodic_configurator.data_model_parameter_editor import ParameterEditor
Expand Down Expand Up @@ -175,7 +174,7 @@ def display_first_use_documentation() -> None:
"https://ardupilot.github.io/MethodicConfigurator/USECASES.html"
"#use-the-ardupilot-methodic-configurator-software-for-the-first-time"
)
webbrowser_open(url=url, new=0, autoraise=True)
webbrowser_open_url(url=url, new=0, autoraise=True)


def connect_to_fc_and_set_vehicle_type(args: argparse.Namespace) -> tuple[FlightController, str]:
Expand Down
13 changes: 13 additions & 0 deletions ardupilot_methodic_configurator/backend_internet.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,16 @@ def verify_and_open_url(url: str) -> bool:
url_found = False

return url_found


def webbrowser_open_url(url: str, new: int = 0, autoraise: bool = True) -> bool:
"""
Open a URL in the default web browser.

Args:
url: The URL to open
new: 0 - same window, 1 - new window, 2 - new tab
autoraise: Whether to raise the window

"""
return webbrowser_open(url=url, new=new, autoraise=autoraise)
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@
from pathlib import Path
from time import time
from typing import Callable, Literal, Optional
from webbrowser import open as webbrowser_open # to open the web documentation

from ardupilot_methodic_configurator import _
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
from ardupilot_methodic_configurator.backend_filesystem_configuration_steps import PhaseData
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
from ardupilot_methodic_configurator.backend_internet import download_file_from_url
from ardupilot_methodic_configurator.backend_internet import download_file_from_url, webbrowser_open_url
from ardupilot_methodic_configurator.data_model_ardupilot_parameter import (
ArduPilotParameter,
ParameterOutOfRangeError,
Expand Down Expand Up @@ -1748,11 +1747,11 @@ def open_documentation_in_browser(self, filename: str) -> None:
_wiki_text, wiki_url = self.get_documentation_text_and_url("wiki", filename)
_external_tool_text, external_tool_url = self.get_documentation_text_and_url("external_tool", filename)
if wiki_url:
webbrowser_open(url=wiki_url, new=0, autoraise=False)
webbrowser_open_url(url=wiki_url, new=0, autoraise=False)
if external_tool_url:
webbrowser_open(url=external_tool_url, new=0, autoraise=False)
webbrowser_open_url(url=external_tool_url, new=0, autoraise=False)
if blog_url:
webbrowser_open(url=blog_url, new=0, autoraise=True)
webbrowser_open_url(url=blog_url, new=0, autoraise=True)

# frontend_tkinter_parameter_editor_table.py API end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from logging import info as logging_info
from logging import warning as logging_warning
from typing import Any, Optional
from webbrowser import open as webbrowser_open

from packaging import version
from requests import RequestException as requests_RequestException
Expand All @@ -31,6 +30,7 @@
download_and_install_on_windows,
download_and_install_pip_release,
get_release_info,
webbrowser_open_url,
)
from ardupilot_methodic_configurator.frontend_tkinter_software_update import UpdateDialog

Expand Down Expand Up @@ -111,7 +111,7 @@ def check_and_update(self, latest_release: dict[str, Any], current_version_str:
current_version_str, latest_version, latest_release.get("body", _("No changes listed"))
)
url = "https://github.com/ArduPilot/MethodicConfigurator/releases"
webbrowser_open(url=url, new=0, autoraise=True)
webbrowser_open_url(url=url, new=0, autoraise=True)

self.dialog = UpdateDialog(version_info, download_callback=lambda: self._perform_download(latest_release))
return self.dialog.show()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@
from tkinter import ttk

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

from ardupilot_methodic_configurator import _
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
from ardupilot_methodic_configurator.backend_internet import webbrowser_open_url
from ardupilot_methodic_configurator.frontend_tkinter_base_window import (
BaseWindow,
)


def show_about_window(root: ttk.Frame, _version: str) -> None: # pylint: disable=too-many-locals
def show_about_window(root: ttk.Frame, _version: str) -> None:
# Create a new window for the custom "About" message
about_window = tk.Toplevel(root)
about_window.title(_("About"))
Expand All @@ -48,58 +47,48 @@ def show_about_window(root: ttk.Frame, _version: str) -> None: # pylint: disabl
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 introduction"),
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)

component_editor_validation_var = tk.BooleanVar(value=ProgramSettings.display_usage_popup("component_editor_validation"))
component_editor_validation_checkbox = ttk.Checkbutton(
usage_popup_frame,
text=_("Component editor window data validation"),
variable=component_editor_validation_var,
command=lambda: ProgramSettings.set_display_usage_popup(
"component_editor_validation", component_editor_validation_var.get()
),
)
component_editor_validation_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)
def _create_usage_popup_checkbox(popup_type: str, text: str) -> None:
"""Create a usage popup checkbox for the given popup type."""
var = tk.BooleanVar(value=ProgramSettings.display_usage_popup(popup_type))
checkbox = ttk.Checkbutton(
usage_popup_frame,
text=text,
variable=var,
command=lambda: ProgramSettings.set_display_usage_popup(popup_type, var.get()),
)
checkbox.pack(side=tk.TOP, anchor=tk.W)

_create_usage_popup_checkbox("component_editor", _("Component editor window introduction"))
_create_usage_popup_checkbox("component_editor_validation", _("Component editor window data validation"))
_create_usage_popup_checkbox("parameter_editor", _("Parameter file editor and uploader window"))

# Create buttons for each action
user_manual_button = ttk.Button(
main_frame,
text=_("User Manual"),
command=lambda: webbrowser_open("https://github.com/ArduPilot/MethodicConfigurator/blob/master/USERMANUAL.md"),
command=lambda: webbrowser_open_url("https://github.com/ArduPilot/MethodicConfigurator/blob/master/USERMANUAL.md"),
)
support_forum_button = ttk.Button(
main_frame,
text=_("Support Forum"),
command=lambda: webbrowser_open("http://discuss.ardupilot.org/t/new-ardupilot-methodic-configurator-gui/115038/1"),
command=lambda: webbrowser_open_url("http://discuss.ardupilot.org/t/new-ardupilot-methodic-configurator-gui/115038/1"),
)
report_bug_button = ttk.Button(
main_frame,
text=_("Report a Bug"),
command=lambda: webbrowser_open("https://github.com/ArduPilot/MethodicConfigurator/issues/new/choose"),
command=lambda: webbrowser_open_url("https://github.com/ArduPilot/MethodicConfigurator/issues/new/choose"),
)
licenses_button = ttk.Button(
main_frame,
text=_("Licenses"),
command=lambda: webbrowser_open("https://github.com/ArduPilot/MethodicConfigurator/blob/master/credits/CREDITS.md"),
command=lambda: webbrowser_open_url(
"https://github.com/ArduPilot/MethodicConfigurator/blob/master/credits/CREDITS.md"
),
)
source_button = ttk.Button(
main_frame, text=_("Source Code"), command=lambda: webbrowser_open("https://github.com/ArduPilot/MethodicConfigurator")
main_frame,
text=_("Source Code"),
command=lambda: webbrowser_open_url("https://github.com/ArduPilot/MethodicConfigurator"),
)

# Place buttons using grid for equal spacing and better control over layout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
import tkinter as tk
from platform import system as platform_system
from tkinter import ttk
from webbrowser import open as webbrowser_open # to open the web documentation

from ardupilot_methodic_configurator import _
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
from ardupilot_methodic_configurator.backend_internet import webbrowser_open_url
from ardupilot_methodic_configurator.data_model_parameter_editor import ParameterEditor
from ardupilot_methodic_configurator.frontend_tkinter_rich_text import get_widget_font_family_and_size
from ardupilot_methodic_configurator.frontend_tkinter_show import show_tooltip
Expand Down Expand Up @@ -141,7 +141,7 @@ def _refresh_documentation_label(self, label_key: str, text: str, url: str, url_
# Create a font with underline attribute
underlined_font = (font_family, font_size, "underline")
label.config(text=text, foreground="blue", cursor="hand2", font=underlined_font)
label.bind("<Button-1>", lambda event: webbrowser_open(url)) # noqa: ARG005
label.bind("<Button-1>", lambda event: webbrowser_open_url(url)) # noqa: ARG005
show_tooltip(label, url)
else:
# Use regular font without underline
Expand Down
20 changes: 20 additions & 0 deletions ardupilot_methodic_configurator/frontend_tkinter_rich_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
from tkinter import font as tkFont # noqa: N812
from tkinter import ttk

from ardupilot_methodic_configurator.backend_internet import webbrowser_open_url
from ardupilot_methodic_configurator.frontend_tkinter_font import get_safe_font_config, safe_font_nametofont
from ardupilot_methodic_configurator.frontend_tkinter_show import show_tooltip_on_richtext_tag


class RichText(tk.Text): # pylint: disable=too-many-ancestors
Expand Down Expand Up @@ -69,6 +71,24 @@ def __init__(self, *args, **kwargs) -> None:
self.tag_configure("italic", font=italic_font)
self.tag_configure("h1", font=h1_font, spacing3=default_size)

def insert_clickable_link(self, text: str, unique_name: str, url: str, index: str = tk.END) -> None:
"""
Insert a clickable link into the RichText widget.

Args:
text: The display text for the link.
unique_name: A unique name for the link tag to avoid conflicts.
url: The URL that the link points to.
index: The index at which to insert the link.

"""
self.insert(index, text, (unique_name,))
self.tag_configure(unique_name, foreground="blue", underline=True)
self.tag_bind(unique_name, "<Button-1>", lambda _: webbrowser_open_url(url))
self.tag_bind(unique_name, "<Enter>", lambda _: self.config(cursor="hand2"))
self.tag_bind(unique_name, "<Leave>", lambda _: self.config(cursor=""))
show_tooltip_on_richtext_tag(self, url, unique_name)


def get_widget_font_family_and_size(widget: tk.Widget) -> tuple[str, int]:
"""
Expand Down
24 changes: 18 additions & 6 deletions ardupilot_methodic_configurator/frontend_tkinter_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class Tooltip:
Creates a tooltip that appears when the mouse hovers over a widget and disappears when the mouse leaves the widget.
"""

def __init__(self, widget: tk.Widget, text: str, position_below: bool = True) -> None:
def __init__(self, widget: tk.Widget, text: str, position_below: bool = True, tag_name: str = "") -> None:
self.widget: tk.Widget = widget
self.text: str = text
self.tooltip: Optional[tk.Toplevel] = None
Expand All @@ -68,11 +68,19 @@ def __init__(self, widget: tk.Widget, text: str, position_below: bool = True) ->
# Bind the <Enter> and <Leave> events to show and hide the tooltip
if platform_system() == "Darwin":
# On macOS, only create the tooltip when the mouse enters the widget
self.widget.bind("<Enter>", self.create_show)
self.widget.bind("<Leave>", self.destroy_hide)
if tag_name and isinstance(self.widget, tk.Text):
self.widget.tag_bind(tag_name, "<Enter>", self.create_show, "+")
self.widget.tag_bind(tag_name, "<Leave>", self.destroy_hide, "+")
else:
self.widget.bind("<Enter>", self.create_show, "+")
self.widget.bind("<Leave>", self.destroy_hide, "+")
else:
self.widget.bind("<Enter>", self.show)
self.widget.bind("<Leave>", self.hide)
if tag_name and isinstance(self.widget, tk.Text):
self.widget.tag_bind(tag_name, "<Enter>", self.show, "+")
self.widget.tag_bind(tag_name, "<Leave>", self.hide, "+")
else:
self.widget.bind("<Enter>", self.show, "+")
self.widget.bind("<Leave>", self.hide, "+")
# On non-macOS, create the tooltip immediately and show/hide it on events
self.tooltip = tk.Toplevel(widget)
self.tooltip.wm_overrideredirect(boolean=True)
Expand Down Expand Up @@ -134,4 +142,8 @@ def destroy_hide(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG00


def show_tooltip(widget: tk.Widget, text: str, position_below: bool = True) -> Tooltip:
return Tooltip(widget, text, position_below=position_below)
return Tooltip(widget, text, position_below=position_below, tag_name="")


def show_tooltip_on_richtext_tag(widget: tk.Text, text: str, tag_name: str, position_below: bool = True) -> Tooltip:
return Tooltip(widget, text, position_below=position_below, tag_name=tag_name)
Loading
Loading