Refactor: Update MenuBar for new GiteaUpdater and flexible tooltips

- Updated MenuBar to use the new GiteaUpdater class for update checks.
- Modified __init__ to accept gitea_api_url and a dictionary of tooltips,
  making the MenuBar more project-independent and translatable.
- Refactored check_for_updates and update_ui_for_update to handle the
  new GiteaUpdater's return values (new version string or None) and
  new states (DISABLED, ERROR).
- Improved updater logic to prevent execution when no updates are available
  or an error occurred.
- Corrected about dialog icon parameter.
This commit is contained in:
2025-08-13 17:18:28 +02:00
parent 8b4068fdc7
commit 6fe090e9e5

View File

@@ -13,7 +13,7 @@ if typing.TYPE_CHECKING:
from .logger import app_logger from .logger import app_logger
from .common_tools import ConfigManager, Tooltip, message_box_animation from .common_tools import ConfigManager, Tooltip, message_box_animation
from .gitea import GiteaUpdate from .gitea import GiteaUpdater
from .animated_icon import AnimatedIcon, PIL_AVAILABLE from .animated_icon import AnimatedIcon, PIL_AVAILABLE
from .message import MessageDialog from .message import MessageDialog
@@ -24,7 +24,8 @@ class MenuBar(ttk.Frame):
def __init__( def __init__(
self, self,
container: ttk.Frame, container: ttk.Frame,
image_manager: 'Any', # Should have a .get_icon(str) -> PhotoImage method # Should have a .get_icon(str) -> PhotoImage method
image_manager: 'Any',
tooltip_state: 'BooleanVar', tooltip_state: 'BooleanVar',
on_theme_toggle: 'Callable[[], None]', on_theme_toggle: 'Callable[[], None]',
toggle_log_window: 'Callable[[], None]', toggle_log_window: 'Callable[[], None]',
@@ -32,6 +33,8 @@ class MenuBar(ttk.Frame):
msg_config: 'Any', # Should have .STR and .TTIP dictionaries msg_config: 'Any', # Should have .STR and .TTIP dictionaries
about_icon_path: str, about_icon_path: str,
about_url: str, about_url: str,
gitea_api_url: str, # New: Gitea API URL for update checks
tooltips: dict[str, str], # New: Dictionary of tooltips
**kwargs: 'Any', **kwargs: 'Any',
) -> None: ) -> None:
""" """
@@ -47,6 +50,8 @@ class MenuBar(ttk.Frame):
msg_config: Project-specific messages. Must have STR and TTIP dicts. msg_config: Project-specific messages. Must have STR and TTIP dicts.
about_icon_path: Filesystem path to the icon for the 'About' dialog. about_icon_path: Filesystem path to the icon for the 'About' dialog.
about_url: URL for the project's repository or website. about_url: URL for the project's repository or website.
gitea_api_url: The Gitea API URL for update checks.
tooltips: A dictionary containing tooltip messages.
**kwargs: Additional keyword arguments for the ttk.Frame. **kwargs: Additional keyword arguments for the ttk.Frame.
""" """
super().__init__(container, **kwargs) super().__init__(container, **kwargs)
@@ -57,6 +62,9 @@ class MenuBar(ttk.Frame):
self.msg_config = msg_config self.msg_config = msg_config
self.about_icon_path = about_icon_path self.about_icon_path = about_icon_path
self.about_url = about_url self.about_url = about_url
self.gitea_api_url = gitea_api_url # Store the Gitea API URL
self.tooltips = tooltips # Store the tooltips dictionary
self.update_status: str = ""
# --- Horizontal button frame for settings --- # --- Horizontal button frame for settings ---
actions_frame = ttk.Frame(self) actions_frame = ttk.Frame(self)
@@ -68,7 +76,8 @@ class MenuBar(ttk.Frame):
) )
self.theme_btn.grid(column=0, row=0, padx=(0, 2)) self.theme_btn.grid(column=0, row=0, padx=(0, 2))
self.update_theme_icon() self.update_theme_icon()
Tooltip(self.theme_btn, "Thema wechseln (Hell/Dunkel)", state_var=self.tooltip_state) Tooltip(self.theme_btn, self.tooltips["theme_toggle"],
state_var=self.tooltip_state)
# --- Tooltip Button --- # --- Tooltip Button ---
self.tooltip_btn = ttk.Button( self.tooltip_btn = ttk.Button(
@@ -76,7 +85,8 @@ class MenuBar(ttk.Frame):
) )
self.tooltip_btn.grid(column=1, row=0, padx=(0, 2)) self.tooltip_btn.grid(column=1, row=0, padx=(0, 2))
self.update_tooltip_icon() self.update_tooltip_icon()
Tooltip(self.tooltip_btn, "Tooltips an/aus", state_var=self.tooltip_state) Tooltip(self.tooltip_btn, self.tooltips["tooltips_toggle"],
state_var=self.tooltip_state)
# --- Update Button --- # --- Update Button ---
self.update_btn = ttk.Button( self.update_btn = ttk.Button(
@@ -86,7 +96,8 @@ class MenuBar(ttk.Frame):
) )
self.update_btn.grid(column=2, row=0) self.update_btn.grid(column=2, row=0)
self.update_update_icon() self.update_update_icon()
Tooltip(self.update_btn, "Updates an/aus", state_var=self.tooltip_state) Tooltip(self.update_btn, self.tooltips["updates_toggle"],
state_var=self.tooltip_state)
# --- Animated Icon for Updates --- # --- Animated Icon for Updates ---
self.animated_icon_frame = ttk.Frame(actions_frame) self.animated_icon_frame = ttk.Frame(actions_frame)
@@ -127,10 +138,12 @@ class MenuBar(ttk.Frame):
command=self.about, command=self.about,
) )
self.about_btn.grid(column=3, row=0) self.about_btn.grid(column=3, row=0)
Tooltip(self.about_btn, self.msg_config.STR["about"], state_var=self.tooltip_state) Tooltip(self.about_btn,
self.msg_config.STR["about"], state_var=self.tooltip_state)
# --- Start background update check --- # --- Start background update check ---
self.update_thread = threading.Thread(target=self.check_for_updates, daemon=True) self.update_thread = threading.Thread(
target=self.check_for_updates, daemon=True)
self.update_thread.start() self.update_thread.start()
def update_theme_icon(self) -> None: def update_theme_icon(self) -> None:
@@ -142,7 +155,8 @@ class MenuBar(ttk.Frame):
def update_tooltip_icon(self) -> None: def update_tooltip_icon(self) -> None:
"""Sets the tooltip button icon based on the tooltip state.""" """Sets the tooltip button icon based on the tooltip state."""
icon_name = "tooltip_small" if self.tooltip_state.get() else "no_tooltip_small" icon_name = "tooltip_small" if self.tooltip_state.get() else "no_tooltip_small"
self.tooltip_btn.configure(image=self.image_manager.get_icon(icon_name)) self.tooltip_btn.configure(
image=self.image_manager.get_icon(icon_name))
def update_update_icon(self) -> None: def update_update_icon(self) -> None:
"""Sets the update button icon based on the update setting.""" """Sets the update button icon based on the update setting."""
@@ -178,46 +192,62 @@ class MenuBar(ttk.Frame):
def check_for_updates(self) -> None: def check_for_updates(self) -> None:
"""Checks for updates via the Gitea API in a background thread.""" """Checks for updates via the Gitea API in a background thread."""
# If updates are turned off in settings, do not perform check
if ConfigManager.get("updates") == "off":
self.after(0, self.update_ui_for_update, "DISABLED")
return
try: try:
res = GiteaUpdate.api_down( # Use the GiteaUpdater to check for updates
self.app_config.UPDATE_URL, new_version = GiteaUpdater.check_for_update(
self.gitea_api_url,
self.app_config.VERSION, self.app_config.VERSION,
ConfigManager.get("updates"),
) )
self.after(0, self.update_ui_for_update, res) # Pass the result (new_version string or None) to update_ui_for_update
self.after(0, self.update_ui_for_update, new_version)
except Exception as e: except Exception as e:
app_logger.log(f"Error during update check: {e}") app_logger.log(f"Error during update check: {e}")
self.after(0, self.update_ui_for_update, "No Internet Connection!") # Indicate a network error or other exception
self.after(0, self.update_ui_for_update, "ERROR")
def update_ui_for_update(self, res: str) -> None: def update_ui_for_update(self, new_version: Optional[str]) -> None:
""" """
Updates the UI based on the result of the update check. Updates the UI based on the result of the update check.
Args: Args:
res: The result string from the update check. new_version: The new version string if an update is available,
"DISABLED" if updates are off, "ERROR" if an error occurred,
or None if no update is available.
""" """
self.update_status = new_version
self.animated_icon_frame.grid_remove() self.animated_icon_frame.grid_remove()
self.animated_icon.hide() self.animated_icon.hide()
self.animated_icon_frame.grid()
tooltip_msg = "" tooltip_msg = ""
if res == "False":
tooltip_msg = self.msg_config.TTIP["updates_disabled"] if new_version == "DISABLED":
elif res == "No Internet Connection!": tooltip_msg = self.tooltips["updates_disabled"]
tooltip_msg = self.msg_config.TTIP["no_server_conn_tt"]
elif res == "No Updates":
tooltip_msg = self.msg_config.TTIP["up_to_date"]
self.animated_icon_frame.grid()
self.animated_icon.stop() self.animated_icon.stop()
else: elif new_version == "ERROR":
tooltip_msg = self.msg_config.TTIP["install_new_version"] tooltip_msg = self.tooltips["no_server_conn_tt"]
self.animated_icon_frame.grid() self.animated_icon.stop(status="DISABLE")
elif new_version is None:
tooltip_msg = self.tooltips["up_to_date"]
self.animated_icon.stop()
else: # A new version string is returned, meaning an update is available
tooltip_msg = self.tooltips["install_new_version"].format(version=new_version)
self.animated_icon.start() self.animated_icon.start()
Tooltip(self.update_btn, tooltip_msg, state_var=self.tooltip_state) Tooltip(self.update_btn, tooltip_msg, state_var=self.tooltip_state)
Tooltip(self.animated_icon_frame, tooltip_msg, state_var=self.tooltip_state) Tooltip(self.animated_icon_frame, tooltip_msg,
state_var=self.tooltip_state)
def updater(self) -> None: def updater(self) -> None:
"""Runs the external installer script for updating the application.""" """Runs the external installer script for updating the application."""
if self.update_status in ["False", "No Internet Connection!", "No Updates"]:
return
tmp_dir = Path("/tmp/lxtools") tmp_dir = Path("/tmp/lxtools")
Path.mkdir(tmp_dir, exist_ok=True) Path.mkdir(tmp_dir, exist_ok=True)
os.chdir(tmp_dir) os.chdir(tmp_dir)
@@ -240,6 +270,6 @@ class MenuBar(ttk.Frame):
None, None,
partial(webbrowser.open, self.about_url), partial(webbrowser.open, self.about_url),
], ],
icon=self.about_icon_path, icon=self.about_icon,
wraplength=420, wraplength=420,
).show() ).show()