SXXXXXXX_Radalyze/radalyze/gui/help_window.py
2025-06-13 14:13:29 +02:00

165 lines
5.9 KiB
Python

import tkinter as tk
from tkinter import scrolledtext, messagebox, filedialog, ttk
import markdown
from fpdf import FPDF
from typing import Optional
# It's better to manage this path from the main application later,
# but for now, we keep it simple.
HELP_MARKDOWN_FILE = "help.md"
class HelpWindow(tk.Toplevel):
"""
A Toplevel window that displays help content from a Markdown file.
The window provides options to export the content to HTML or PDF.
"""
def __init__(self, master: tk.Tk):
"""
Initializes the help window.
Args:
master: The parent tk.Tk window.
"""
super().__init__(master)
self.title("Radalyze - Help")
self.geometry("800x600")
self._help_content: Optional[str] = None
self._create_widgets()
self._load_and_display_help()
self._center_window()
def _create_widgets(self) -> None:
"""Creates and arranges the widgets in the window."""
# --- Toolbar ---
toolbar_frame = ttk.Frame(self)
toolbar_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
export_html_button = ttk.Button(
toolbar_frame, text="Export to HTML", command=self._export_to_html
)
export_html_button.pack(side=tk.LEFT, padx=5)
export_pdf_button = ttk.Button(
toolbar_frame, text="Export to PDF", command=self._export_to_pdf
)
export_pdf_button.pack(side=tk.LEFT, padx=5)
# --- Text Area ---
self._text_widget = scrolledtext.ScrolledText(self, wrap=tk.WORD, padx=5)
self._text_widget.pack(expand=True, fill="both")
self._text_widget.config(state=tk.DISABLED) # Make it read-only
def _load_and_display_help(self) -> None:
"""Loads the content from the Markdown file and displays it."""
try:
with open(HELP_MARKDOWN_FILE, "r", encoding="utf-8") as f:
self._help_content = f.read()
self._text_widget.config(state=tk.NORMAL)
self._text_widget.delete("1.0", tk.END)
self._text_widget.insert(tk.INSERT, self._help_content)
self._text_widget.config(state=tk.DISABLED)
except FileNotFoundError:
self._show_error(f"Help file not found: '{HELP_MARKDOWN_FILE}'")
except Exception as e:
self._show_error(f"An error occurred while reading the help file: {e}")
def _export_to_html(self) -> None:
"""Exports the help content to an HTML file."""
if not self._help_content:
self._show_error("Help content is not loaded. Cannot export.")
return
save_path = filedialog.asksaveasfilename(
title="Save Help as HTML",
defaultextension=".html",
filetypes=[("HTML files", "*.html"), ("All files", "*.*")]
)
if not save_path:
return # User cancelled
try:
html_content = markdown.markdown(self._help_content)
with open(save_path, "w", encoding="utf-8") as f:
f.write(html_content)
messagebox.showinfo(
"Export Successful", f"Help content successfully saved to:\n{save_path}", parent=self
)
except Exception as e:
self._show_error(f"Failed to export to HTML: {e}")
def _export_to_pdf(self) -> None:
"""Exports the help content to a PDF file."""
if not self._help_content:
self._show_error("Help content is not loaded. Cannot export.")
return
save_path = filedialog.asksaveasfilename(
title="Save Help as PDF",
defaultextension=".pdf",
filetypes=[("PDF files", "*.pdf"), ("All files", "*.*")]
)
if not save_path:
return # User cancelled
try:
pdf = self._create_pdf_from_markdown(self._help_content)
pdf.output(save_path)
messagebox.showinfo(
"Export Successful", f"Help content successfully saved to:\n{save_path}", parent=self
)
except Exception as e:
self._show_error(f"Failed to export to PDF: {e}")
@staticmethod
def _create_pdf_from_markdown(markdown_text: str) -> FPDF:
"""
Creates an FPDF object from a Markdown string.
This is a basic parser and handles headings (#) and list items (*).
"""
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", size=12)
for line in markdown_text.split('\n'):
line = line.strip()
if line.startswith('#'):
level = line.find(' ')
text = line[level:].strip()
size = max(12, 20 - 2 * level)
pdf.set_font("Arial", 'B', size=size)
pdf.multi_cell(0, 10, text, 0, 'L')
pdf.set_font("Arial", size=12)
elif line.startswith('*'):
text = line[1:].strip()
pdf.set_x(15)
pdf.multi_cell(0, 5, f"{text}")
pdf.set_x(10)
else:
pdf.multi_cell(0, 5, line)
if not line: # Add a small space for empty lines
pdf.ln(5)
return pdf
def _center_window(self) -> None:
"""Centers the window on the screen."""
self.update_idletasks()
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
window_width = self.winfo_width()
window_height = self.winfo_height()
pos_x = (screen_width // 2) - (window_width // 2)
pos_y = (screen_height // 2) - (window_height // 2)
self.geometry(f"+{pos_x}+{pos_y}")
def _show_error(self, message: str) -> None:
"""Convenience method to show an error message box."""
messagebox.showerror("Help Window Error", message, parent=self)