version checking nearly working
This commit is contained in:
5
assets/leatest-version.example.json
Normal file
5
assets/leatest-version.example.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"latestVersion": "1.4.0",
|
||||||
|
"downloadUrl": "https://www.scorchsoft.com/blog/text-to-mic-for-meetings/",
|
||||||
|
"notificationMessage": "A new version of Text to Mic is available. Please update to access the latest features."
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -61,7 +61,7 @@ class PresetsManager:
|
|||||||
compound=tk.LEFT,
|
compound=tk.LEFT,
|
||||||
text=" Presets",
|
text=" Presets",
|
||||||
command=self.toggle_presets,
|
command=self.toggle_presets,
|
||||||
style="Flat.TButton"
|
style="PresetsButton.TButton"
|
||||||
)
|
)
|
||||||
# Use grid instead of pack for the button to avoid mixing layout managers
|
# Use grid instead of pack for the button to avoid mixing layout managers
|
||||||
self.presets_button.grid(column=0, row=0, sticky=tk.W, padx=0, pady=2)
|
self.presets_button.grid(column=0, row=0, sticky=tk.W, padx=0, pady=2)
|
||||||
@@ -84,6 +84,14 @@ class PresetsManager:
|
|||||||
anchor="center",
|
anchor="center",
|
||||||
background=bg_color)
|
background=bg_color)
|
||||||
|
|
||||||
|
# Create specific style for the presets button with smaller font
|
||||||
|
self.parent.style.configure("PresetsButton.TButton",
|
||||||
|
borderwidth=0,
|
||||||
|
highlightthickness=0,
|
||||||
|
font=("Arial", 10), # Smaller font size
|
||||||
|
anchor="center",
|
||||||
|
background=bg_color)
|
||||||
|
|
||||||
# Create compact styles for arrow buttons
|
# Create compact styles for arrow buttons
|
||||||
self.parent.style.configure("Arrow.TButton",
|
self.parent.style.configure("Arrow.TButton",
|
||||||
borderwidth=0,
|
borderwidth=0,
|
||||||
@@ -578,6 +586,60 @@ class PresetsManager:
|
|||||||
else:
|
else:
|
||||||
messagebox.showinfo("Error", "Please enter text and select a category before saving.")
|
messagebox.showinfo("Error", "Please enter text and select a category before saving.")
|
||||||
|
|
||||||
|
def show_save_preset_dialog(self):
|
||||||
|
"""Show a dialog to save the current text as a preset."""
|
||||||
|
text = self.parent.text_input.get("1.0", tk.END).strip()
|
||||||
|
if not text:
|
||||||
|
messagebox.showinfo("Error", "Please enter some text before saving as a preset.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create dialog window
|
||||||
|
dialog = tk.Toplevel(self.parent)
|
||||||
|
dialog.title("Save As Preset")
|
||||||
|
dialog.geometry("300x200")
|
||||||
|
dialog.transient(self.parent)
|
||||||
|
dialog.grab_set()
|
||||||
|
dialog.focus_set()
|
||||||
|
|
||||||
|
# Add widgets to the dialog
|
||||||
|
ttk.Label(dialog, text="Enter preset category:").pack(pady=(15, 5))
|
||||||
|
|
||||||
|
# Add existing categories dropdown
|
||||||
|
categories = ["Select Category"] + list(set([cat["category"] for cat in self.presets]))
|
||||||
|
category_var = tk.StringVar(value="Select Category")
|
||||||
|
|
||||||
|
category_combo = ttk.Combobox(dialog, textvariable=category_var, values=categories)
|
||||||
|
category_combo.pack(pady=5, padx=20, fill=tk.X)
|
||||||
|
|
||||||
|
ttk.Label(dialog, text="Or enter a new category:").pack(pady=(10, 5))
|
||||||
|
|
||||||
|
new_category_entry = ttk.Entry(dialog)
|
||||||
|
new_category_entry.pack(pady=5, padx=20, fill=tk.X)
|
||||||
|
|
||||||
|
def save_preset():
|
||||||
|
new_category = new_category_entry.get().strip()
|
||||||
|
selected_category = category_var.get()
|
||||||
|
|
||||||
|
# Use new category if provided, otherwise use the dropdown selection
|
||||||
|
category = new_category if new_category else selected_category
|
||||||
|
|
||||||
|
if category and category != "Select Category":
|
||||||
|
self.add_preset(category, text, is_favourite=False)
|
||||||
|
messagebox.showinfo("Save Successful", f"The text has been successfully saved to the category: '{category}'.")
|
||||||
|
dialog.destroy()
|
||||||
|
else:
|
||||||
|
messagebox.showinfo("Error", "Please select an existing category or enter a new one.")
|
||||||
|
|
||||||
|
# Add save button
|
||||||
|
save_button = ttk.Button(dialog, text="Save", command=save_preset)
|
||||||
|
save_button.pack(pady=15)
|
||||||
|
|
||||||
|
# Center the dialog on the parent window
|
||||||
|
dialog.update_idletasks()
|
||||||
|
x = self.parent.winfo_x() + (self.parent.winfo_width() // 2) - (dialog.winfo_width() // 2)
|
||||||
|
y = self.parent.winfo_y() + (self.parent.winfo_height() // 2) - (dialog.winfo_height() // 2)
|
||||||
|
dialog.geometry(f"+{x}+{y}")
|
||||||
|
|
||||||
def load_presets(self):
|
def load_presets(self):
|
||||||
"""
|
"""
|
||||||
Load presets from the JSON file, copying from example if necessary.
|
Load presets from the JSON file, copying from example if necessary.
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ class SettingsManager:
|
|||||||
"model": "gpt-4o-mini",
|
"model": "gpt-4o-mini",
|
||||||
"prompt": "",
|
"prompt": "",
|
||||||
"auto_apply_ai_to_recording": False,
|
"auto_apply_ai_to_recording": False,
|
||||||
"current_tone": "None",
|
|
||||||
"hide_banner": False,
|
"hide_banner": False,
|
||||||
|
"auto_check_version": True,
|
||||||
|
"current_tone": "None",
|
||||||
"input_device": "Default",
|
"input_device": "Default",
|
||||||
"primary_device": "Select Device",
|
"primary_device": "Select Device",
|
||||||
"secondary_device": "None",
|
"secondary_device": "None",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import webbrowser
|
|||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import requests
|
||||||
|
|
||||||
from pystray import Icon as icon, MenuItem as item, Menu as menu
|
from pystray import Icon as icon, MenuItem as item, Menu as menu
|
||||||
from PIL import Image, ImageDraw, ImageTk
|
from PIL import Image, ImageDraw, ImageTk
|
||||||
@@ -27,6 +28,7 @@ from utils.presets_manager import PresetsManager
|
|||||||
from utils.ai_editor_manager import AIEditorManager
|
from utils.ai_editor_manager import AIEditorManager
|
||||||
from utils.settings_manager import SettingsManager
|
from utils.settings_manager import SettingsManager
|
||||||
from utils.app_text import AppText
|
from utils.app_text import AppText
|
||||||
|
from utils.version_checker import VersionChecker
|
||||||
|
|
||||||
# Modify the load environment variables to load from config/.env
|
# Modify the load environment variables to load from config/.env
|
||||||
def load_env_file():
|
def load_env_file():
|
||||||
@@ -125,14 +127,14 @@ class TextToMic(tk.Tk):
|
|||||||
self.tone_presets = TonePresetsManager.load_tone_presets(self)
|
self.tone_presets = TonePresetsManager.load_tone_presets(self)
|
||||||
self.current_tone_name = self.load_current_tone_from_settings()
|
self.current_tone_name = self.load_current_tone_from_settings()
|
||||||
|
|
||||||
# Create the category variable for the dropdown
|
# Initialize settings before creating menu
|
||||||
self.category_var = tk.StringVar(value="Select Category")
|
|
||||||
|
|
||||||
# Add toggle for banner visibility before presets manager initialization
|
|
||||||
self.banner_var = tk.BooleanVar()
|
|
||||||
settings = self.load_settings()
|
settings = self.load_settings()
|
||||||
|
self.banner_var = tk.BooleanVar()
|
||||||
self.banner_var.set(settings.get("hide_banner", False))
|
self.banner_var.set(settings.get("hide_banner", False))
|
||||||
|
|
||||||
|
# Initialize auto_check_version before creating menu
|
||||||
|
self.auto_check_version = tk.BooleanVar(value=settings.get("auto_check_version", True))
|
||||||
|
|
||||||
# Create the presets manager before initializing the GUI
|
# Create the presets manager before initializing the GUI
|
||||||
self.presets_manager = PresetsManager(self)
|
self.presets_manager = PresetsManager(self)
|
||||||
|
|
||||||
@@ -142,6 +144,9 @@ class TextToMic(tk.Tk):
|
|||||||
# Store reference to presets state
|
# Store reference to presets state
|
||||||
self.presets_collapsed = self.presets_manager.presets_collapsed
|
self.presets_collapsed = self.presets_manager.presets_collapsed
|
||||||
|
|
||||||
|
# Initialize the main frame as a class variable for version notification to work
|
||||||
|
self.main_frame = None
|
||||||
|
|
||||||
# Create menu and initialize GUI after presets manager is created
|
# Create menu and initialize GUI after presets manager is created
|
||||||
self.create_menu()
|
self.create_menu()
|
||||||
self.initialize_gui()
|
self.initialize_gui()
|
||||||
@@ -149,9 +154,18 @@ class TextToMic(tk.Tk):
|
|||||||
# Initialize our HotkeyManager
|
# Initialize our HotkeyManager
|
||||||
self.hotkey_manager = HotkeyManager(self)
|
self.hotkey_manager = HotkeyManager(self)
|
||||||
|
|
||||||
|
# Initialize version checker
|
||||||
|
self.version_checker = VersionChecker(self, self.version)
|
||||||
|
|
||||||
# If banner should be hidden based on settings, hide it now
|
# If banner should be hidden based on settings, hide it now
|
||||||
if self.banner_var.get():
|
if self.banner_var.get():
|
||||||
self.toggle_banner()
|
self.toggle_banner()
|
||||||
|
|
||||||
|
# Schedule version check after app is fully loaded
|
||||||
|
# Only check automatically if the setting is enabled
|
||||||
|
if self.auto_check_version.get():
|
||||||
|
# Delay the check to ensure UI is fully loaded
|
||||||
|
self.after(2000, self.version_checker.check_version, False)
|
||||||
|
|
||||||
def ensure_config_directory(self):
|
def ensure_config_directory(self):
|
||||||
"""Ensure the config directory exists."""
|
"""Ensure the config directory exists."""
|
||||||
@@ -198,9 +212,12 @@ class TextToMic(tk.Tk):
|
|||||||
self.menubar.add_cascade(label="Help", menu=help_menu)
|
self.menubar.add_cascade(label="Help", menu=help_menu)
|
||||||
help_menu.add_command(label="How to Use", command=self.show_instructions)
|
help_menu.add_command(label="How to Use", command=self.show_instructions)
|
||||||
help_menu.add_command(label="Terms of Use and Licence", command=self.show_terms_of_use)
|
help_menu.add_command(label="Terms of Use and Licence", command=self.show_terms_of_use)
|
||||||
help_menu.add_command(label="Version", command=self.show_version)
|
help_menu.add_command(label="Check Version", command=self.check_version)
|
||||||
help_menu.add_command(label="Hotkey Instructions", command=self.show_hotkey_instructions)
|
help_menu.add_command(label="Hotkey Instructions", command=self.show_hotkey_instructions)
|
||||||
|
|
||||||
|
# Add toggle for automatic version checking
|
||||||
|
help_menu.add_checkbutton(label="Auto Check for Updates", variable=self.auto_check_version, command=self.toggle_auto_version_check)
|
||||||
|
|
||||||
# Add toggle for banner visibility - use the existing banner_var from __init__
|
# Add toggle for banner visibility - use the existing banner_var from __init__
|
||||||
help_menu.add_checkbutton(label="Hide Banner", variable=self.banner_var, command=self.toggle_banner)
|
help_menu.add_checkbutton(label="Hide Banner", variable=self.banner_var, command=self.toggle_banner)
|
||||||
|
|
||||||
@@ -272,6 +289,9 @@ class TextToMic(tk.Tk):
|
|||||||
main_frame.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
main_frame.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1)
|
||||||
self.rowconfigure(0, weight=1)
|
self.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# Store reference to main_frame for version notification
|
||||||
|
self.main_frame = main_frame
|
||||||
|
|
||||||
# Use the background color from our style for the text widget
|
# Use the background color from our style for the text widget
|
||||||
bg_color = self.style.lookup('TFrame', 'background')
|
bg_color = self.style.lookup('TFrame', 'background')
|
||||||
@@ -373,18 +393,10 @@ class TextToMic(tk.Tk):
|
|||||||
save_frame = ttk.Frame(text_read_frame)
|
save_frame = ttk.Frame(text_read_frame)
|
||||||
save_frame.grid(column=1, row=0, sticky=tk.E)
|
save_frame.grid(column=1, row=0, sticky=tk.E)
|
||||||
|
|
||||||
# Preset Category dropdown
|
# Create a compact style for the button
|
||||||
categories = [cat["category"] for cat in self.presets_manager.presets]
|
|
||||||
category_menu = ttk.OptionMenu(save_frame, self.category_var, *categories)
|
|
||||||
category_menu.grid(column=0, row=0, sticky=tk.E, padx=(0, 5))
|
|
||||||
category_menu.config(style='Compact.TMenubutton')
|
|
||||||
|
|
||||||
# Create a compact style for the Save button to match dropdown height
|
|
||||||
self.style.configure('Compact.TButton', padding=(2, 1))
|
self.style.configure('Compact.TButton', padding=(2, 1))
|
||||||
|
save_as_preset_button = ttk.Button(save_frame, text="Save As Preset", width=15, style='Compact.TButton', command=self.show_save_preset_dialog)
|
||||||
# Save button with matching height
|
save_as_preset_button.grid(column=0, row=0, sticky=tk.E)
|
||||||
save_button = ttk.Button(save_frame, text="Save", width=8, style='Compact.TButton', command=self.save_current_text_as_preset)
|
|
||||||
save_button.grid(column=1, row=0, sticky=tk.E)
|
|
||||||
|
|
||||||
# Text input area with proper spacing
|
# Text input area with proper spacing
|
||||||
self.text_input = tk.Text(main_frame, height=5, width=68)
|
self.text_input = tk.Text(main_frame, height=5, width=68)
|
||||||
@@ -485,7 +497,7 @@ class TextToMic(tk.Tk):
|
|||||||
|
|
||||||
def save_current_text_as_preset(self):
|
def save_current_text_as_preset(self):
|
||||||
"""Forward the save request to the presets manager."""
|
"""Forward the save request to the presets manager."""
|
||||||
self.presets_manager.save_current_text_as_preset()
|
self.show_save_preset_dialog()
|
||||||
|
|
||||||
def show_instructions(self):
|
def show_instructions(self):
|
||||||
instruction_window = tk.Toplevel(self)
|
instruction_window = tk.Toplevel(self)
|
||||||
@@ -1227,6 +1239,11 @@ class TextToMic(tk.Tk):
|
|||||||
# Use grid (not pack) to ensure proper positioning
|
# Use grid (not pack) to ensure proper positioning
|
||||||
self.presets_manager.presets_button.grid_configure(column=0, row=0, sticky=tk.W, padx=0, pady=2)
|
self.presets_manager.presets_button.grid_configure(column=0, row=0, sticky=tk.W, padx=0, pady=2)
|
||||||
|
|
||||||
|
# If we have a version notification visible, ensure it remains at the top
|
||||||
|
if hasattr(self, 'version_checker') and self.version_checker.notification_visible:
|
||||||
|
self.version_checker.notification_frame.grid(row=0, column=0, sticky="ew")
|
||||||
|
self.main_frame.grid(row=1, column=0, sticky="nsew")
|
||||||
|
|
||||||
def toggle_presets(self):
|
def toggle_presets(self):
|
||||||
"""Toggle the visibility of the presets panel."""
|
"""Toggle the visibility of the presets panel."""
|
||||||
if hasattr(self, 'presets_manager'):
|
if hasattr(self, 'presets_manager'):
|
||||||
@@ -1265,6 +1282,11 @@ class TextToMic(tk.Tk):
|
|||||||
if not self.presets_collapsed:
|
if not self.presets_collapsed:
|
||||||
self.presets_manager.refresh_presets_display()
|
self.presets_manager.refresh_presets_display()
|
||||||
|
|
||||||
|
# If we have a version notification visible, ensure it remains at the top
|
||||||
|
if hasattr(self, 'version_checker') and self.version_checker.notification_visible:
|
||||||
|
self.version_checker.notification_frame.grid(row=0, column=0, sticky="ew")
|
||||||
|
self.main_frame.grid(row=1, column=0, sticky="nsew")
|
||||||
|
|
||||||
def update_buttons_for_playback(self, is_playing):
|
def update_buttons_for_playback(self, is_playing):
|
||||||
"""Update button text based on playback state."""
|
"""Update button text based on playback state."""
|
||||||
try:
|
try:
|
||||||
@@ -1326,4 +1348,18 @@ class TextToMic(tk.Tk):
|
|||||||
settings["secondary_device"] = device_name
|
settings["secondary_device"] = device_name
|
||||||
self.save_settings_to_JSON(settings)
|
self.save_settings_to_JSON(settings)
|
||||||
|
|
||||||
|
def show_save_preset_dialog(self):
|
||||||
|
"""Show the save preset dialog."""
|
||||||
|
self.presets_manager.show_save_preset_dialog()
|
||||||
|
|
||||||
|
def check_version(self):
|
||||||
|
"""Run the version checker and show the result"""
|
||||||
|
self.version_checker.check_version(True) # True means show result even if no update available
|
||||||
|
|
||||||
|
def toggle_auto_version_check(self):
|
||||||
|
"""Toggle automatic version checking and save the setting"""
|
||||||
|
settings = self.load_settings()
|
||||||
|
settings["auto_check_version"] = self.auto_check_version.get()
|
||||||
|
self.save_settings_to_JSON(settings)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
151
utils/version_checker.py
Normal file
151
utils/version_checker.py
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import threading
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import webbrowser
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk, messagebox
|
||||||
|
from packaging import version
|
||||||
|
import time
|
||||||
|
|
||||||
|
class VersionChecker:
|
||||||
|
def __init__(self, app, version):
|
||||||
|
self.app = app
|
||||||
|
self.current_version = version
|
||||||
|
self.version_url = "https://www.scorchsoft.com/public/blog/text-to-mic/leatest-version.json"
|
||||||
|
self.notification_visible = False
|
||||||
|
self.notification_frame = None
|
||||||
|
|
||||||
|
def check_version(self, show_result=True):
|
||||||
|
"""
|
||||||
|
Check if a new version is available.
|
||||||
|
If show_result is True, show a message even if no new version is found.
|
||||||
|
"""
|
||||||
|
thread = threading.Thread(target=self._check_version_thread, args=(show_result,))
|
||||||
|
thread.daemon = True # Make thread terminate when main program exits
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
def _check_version_thread(self, show_result):
|
||||||
|
"""Run the version check in a background thread to avoid blocking the UI"""
|
||||||
|
try:
|
||||||
|
# Add a small timeout to prevent hanging
|
||||||
|
response = requests.get(self.version_url, timeout=5)
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
latest_version = data.get("latestVersion")
|
||||||
|
download_url = data.get("downloadUrl")
|
||||||
|
message = data.get("notificationMessage")
|
||||||
|
|
||||||
|
# Ensure these values are present
|
||||||
|
if not latest_version or not download_url:
|
||||||
|
if show_result:
|
||||||
|
self.app.after(0, lambda: messagebox.showwarning(
|
||||||
|
"Version Check Failed",
|
||||||
|
"The update information is incomplete. Please try again later."
|
||||||
|
))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Use packaging.version for proper version comparison
|
||||||
|
try:
|
||||||
|
if version.parse(latest_version) > version.parse(self.current_version):
|
||||||
|
# New version available - show notification in UI thread
|
||||||
|
self.app.after(0, lambda: self.show_update_notification(latest_version, download_url, message))
|
||||||
|
elif show_result:
|
||||||
|
# No new version, but user requested check
|
||||||
|
self.app.after(0, lambda: messagebox.showinfo(
|
||||||
|
"Version Check",
|
||||||
|
f"You have the latest version ({self.current_version})."
|
||||||
|
))
|
||||||
|
except (version.InvalidVersion, TypeError) as e:
|
||||||
|
if show_result:
|
||||||
|
self.app.after(0, lambda: messagebox.showwarning(
|
||||||
|
"Version Check Failed",
|
||||||
|
f"Could not compare versions: {str(e)}"
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
if show_result:
|
||||||
|
self.app.after(0, lambda: messagebox.showwarning(
|
||||||
|
"Version Check Failed",
|
||||||
|
f"Could not check for updates. Server returned status code: {response.status_code}"
|
||||||
|
))
|
||||||
|
except requests.RequestException as e:
|
||||||
|
if show_result:
|
||||||
|
self.app.after(0, lambda: messagebox.showwarning(
|
||||||
|
"Version Check Failed",
|
||||||
|
f"Could not connect to update server: {str(e)}"
|
||||||
|
))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
if show_result:
|
||||||
|
self.app.after(0, lambda: messagebox.showwarning(
|
||||||
|
"Version Check Failed",
|
||||||
|
"Invalid update information received."
|
||||||
|
))
|
||||||
|
except Exception as e:
|
||||||
|
if show_result:
|
||||||
|
self.app.after(0, lambda: messagebox.showwarning(
|
||||||
|
"Version Check Failed",
|
||||||
|
f"Could not check for updates: {str(e)}"
|
||||||
|
))
|
||||||
|
|
||||||
|
def show_update_notification(self, latest_version, download_url, message):
|
||||||
|
"""Display an update notification banner in the app"""
|
||||||
|
if self.notification_visible:
|
||||||
|
return # Already showing notification
|
||||||
|
|
||||||
|
# Create notification frame
|
||||||
|
self.notification_frame = ttk.Frame(self.app, style='Notification.TFrame')
|
||||||
|
|
||||||
|
# Configure notification style (light yellow background)
|
||||||
|
self.app.style.configure('Notification.TFrame', background='#fff3cd')
|
||||||
|
self.app.style.configure('Notification.TLabel', background='#fff3cd', foreground='#856404')
|
||||||
|
self.app.style.configure('Notification.TButton', background='#fff3cd')
|
||||||
|
|
||||||
|
# Create notification content
|
||||||
|
notification_text = message or f"A new version ({latest_version}) is available. You're currently using version {self.current_version}."
|
||||||
|
|
||||||
|
label = ttk.Label(
|
||||||
|
self.notification_frame,
|
||||||
|
text=notification_text,
|
||||||
|
style='Notification.TLabel',
|
||||||
|
wraplength=400
|
||||||
|
)
|
||||||
|
label.grid(row=0, column=0, padx=(10, 5), pady=10, sticky="w")
|
||||||
|
|
||||||
|
# Create buttons
|
||||||
|
download_button = ttk.Button(
|
||||||
|
self.notification_frame,
|
||||||
|
text="Download",
|
||||||
|
command=lambda: self.open_download_page(download_url)
|
||||||
|
)
|
||||||
|
download_button.grid(row=0, column=1, padx=5, pady=10)
|
||||||
|
|
||||||
|
close_button = ttk.Button(
|
||||||
|
self.notification_frame,
|
||||||
|
text="×",
|
||||||
|
width=2,
|
||||||
|
command=self.dismiss_notification
|
||||||
|
)
|
||||||
|
close_button.grid(row=0, column=2, padx=(0, 5), pady=10)
|
||||||
|
|
||||||
|
# Insert at the top of the application, below menu
|
||||||
|
self.notification_frame.grid(row=0, column=0, sticky="ew", padx=0, pady=0)
|
||||||
|
|
||||||
|
# Move other content down
|
||||||
|
self.app.main_frame.grid(row=1, column=0, sticky="nsew")
|
||||||
|
|
||||||
|
self.notification_visible = True
|
||||||
|
|
||||||
|
def dismiss_notification(self):
|
||||||
|
"""Remove the notification banner"""
|
||||||
|
if self.notification_frame:
|
||||||
|
self.notification_frame.grid_forget()
|
||||||
|
self.notification_frame = None
|
||||||
|
|
||||||
|
# Move main frame back to top position
|
||||||
|
self.app.main_frame.grid(row=0, column=0, sticky="nsew")
|
||||||
|
|
||||||
|
self.notification_visible = False
|
||||||
|
|
||||||
|
def open_download_page(self, url):
|
||||||
|
"""Open the download URL in a web browser"""
|
||||||
|
webbrowser.open(url)
|
||||||
|
self.dismiss_notification()
|
||||||
Reference in New Issue
Block a user