From 0cbdf2ef360210c43f770087ea853440913cd6d2 Mon Sep 17 00:00:00 2001 From: JustinOros Date: Wed, 23 Jul 2025 11:37:40 -0700 Subject: [PATCH] Fixed a bug where window was not big enough to view text/labels on Windows OS. --- ham-test-gui.py | 157 +++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 82 deletions(-) diff --git a/ham-test-gui.py b/ham-test-gui.py index 2ae8036..b2c4fcd 100644 --- a/ham-test-gui.py +++ b/ham-test-gui.py @@ -1,5 +1,5 @@ #!/usr/bin/python3 -# Description: A Python-GUI interface for the HAM Operator Test. +# Description: A Python GUI interface for the HAM Operator Test. # Usage: python3 ham-test-gui.py # Author: Justin Oros # Source: https://github.com/JustinOros @@ -13,10 +13,10 @@ from datetime import datetime import tkinter as tk from tkinter import messagebox -# --- Setup logging --- +# Setup logging logging.basicConfig(level=logging.WARNING, format='[%(levelname)s] %(message)s') -# --- URLs and Local file paths --- +# URLs and Local file paths URLS = { "Technician": "https://raw.githubusercontent.com/russolsen/ham_radio_question_pool/master/technician-2022-2026/technician.json", "General": "https://raw.githubusercontent.com/russolsen/ham_radio_question_pool/master/general-2023-2027/general.json", @@ -29,7 +29,7 @@ LOCAL_FILES = { "Extra": "extra.json" } -# --- Helper functions to sync question files --- +# Helper functions to sync question files def get_remote_last_modified(url): try: @@ -81,15 +81,16 @@ def load_questions(test_name): with open(local_path, "r", encoding='utf-8') as f: return json.load(f) -# --- GUI Classes --- +# Main GUI Class -class QuizGUI: - def __init__(self, master, questions): +class HamTestGUI: + def __init__(self, master): self.master = master - self.master.title("HAM Radio Quiz") - self.questions = questions - random.shuffle(self.questions) + self.master.title("HAM Radio Test") + self.center_window(800, 300) + self.master.resizable(False, False) + self.questions = [] self.score = 0 self.total = 0 self.current_index = -1 @@ -97,36 +98,57 @@ class QuizGUI: self.next_pending = False self.correct_answer_text = "" - self.feedback_label = tk.Label(master, text="", font=("Arial", 14), wraplength=800, justify="left") - self.feedback_label.pack(pady=5) + # Container frame for dynamic content + self.content_frame = tk.Frame(self.master) + self.content_frame.pack(fill="both", expand=True) - self.question_label = tk.Label(master, text="", wraplength=600, justify="left", font=("Arial", 12)) - self.question_label.pack(padx=10, pady=10) + self.show_test_selection() - self.selected_answer = tk.StringVar() - self.answers_frame = tk.Frame(master) - self.answers_frame.pack(pady=5) + def center_window(self, width=800, height=600): + self.master.update_idletasks() + screen_width = self.master.winfo_screenwidth() + screen_height = self.master.winfo_screenheight() + x = (screen_width // 2) - (width // 2) + y = (screen_height // 2) - (height // 2) + self.master.geometry(f"{width}x{height}+{x}+{y}") - nav_frame = tk.Frame(master) - nav_frame.pack(pady=10) + def clear_content(self): + for widget in self.content_frame.winfo_children(): + widget.destroy() - self.quit_button = tk.Button(nav_frame, text="Quit", command=self.quit_quiz) - self.quit_button.pack(side=tk.LEFT, padx=5) + # Test selection UI + def show_test_selection(self): + self.clear_content() - self.next_button = tk.Button(nav_frame, text="Next", command=self.check_answer) - self.next_button.pack(side=tk.LEFT, padx=5) + label = tk.Label(self.content_frame, text="Select the test you want to take:", font=("Arial", 14)) + label.pack(padx=10, pady=10) - self.load_next_question() + for test_name in URLS.keys(): + btn = tk.Button(self.content_frame, text=test_name, width=20, font=("Arial", 12), + command=lambda tn=test_name: self.start_test(tn)) + btn.pack(pady=5) - def resize_to_fit_text(self, text): - avg_char_width = 7 - padding = 60 - max_width = 1000 - min_width = 400 - estimated_width = min(max(len(text) * avg_char_width + padding, min_width), max_width) - self.master.geometry(f"{estimated_width}x400") + def start_test(self, test_name): + print(f"Selected test: {test_name}") + print("Syncing question files...") + sync_files() - def load_next_question(self): + questions = load_questions(test_name) + if not questions: + messagebox.showerror("Error", f"No questions found for {test_name}.") + return + + self.questions = questions + random.shuffle(self.questions) + self.score = 0 + self.total = 0 + self.current_index = -1 + + self.show_question() + + # Quiz UI + def show_question(self): + self.clear_content() self.current_index += 1 if self.current_index >= len(self.questions): @@ -135,20 +157,20 @@ class QuizGUI: self.master.destroy() return - self.feedback_label.config(text="") - self.selected_answer.set(None) - - for widget in self.answers_frame.winfo_children(): - widget.destroy() + self.feedback_label = tk.Label(self.content_frame, text="", font=("Arial", 14), wraplength=780, justify="left") + self.feedback_label.pack(pady=5) q = self.questions[self.current_index] question_text = q.get("question") answers = q.get("answers", []) correct_index = q.get("correct") - width = min(max(400, len(question_text) * 7), 800) - self.master.geometry(f"{width}x400") - self.question_label.config(text=question_text) + self.question_label = tk.Label(self.content_frame, text=question_text, wraplength=780, justify="left", font=("Arial", 12)) + self.question_label.pack(padx=10, pady=10) + + self.selected_answer = tk.StringVar() + self.answers_frame = tk.Frame(self.content_frame) + self.answers_frame.pack(pady=5) options = list(enumerate(answers)) random.shuffle(options) @@ -161,7 +183,7 @@ class QuizGUI: text=ans_text, variable=self.selected_answer, value=ans_text, - wraplength=width - 50, + wraplength=760, justify="left", anchor="w", padx=10, @@ -172,6 +194,15 @@ class QuizGUI: self.correct_answer = ans_text self.correct_answer_text = ans_text + nav_frame = tk.Frame(self.content_frame) + nav_frame.pack(pady=10) + + self.quit_button = tk.Button(nav_frame, text="Quit", command=self.quit_quiz) + self.quit_button.pack(side=tk.LEFT, padx=5) + + self.next_button = tk.Button(nav_frame, text="Next", command=self.check_answer) + self.next_button.pack(side=tk.LEFT, padx=5) + self.next_pending = False def check_answer(self): @@ -191,57 +222,19 @@ class QuizGUI: else: feedback_text = f"Incorrect! The correct answer was: {self.correct_answer_text}" self.feedback_label.config(text=feedback_text, fg="red") - self.resize_to_fit_text(feedback_text) delay = 3500 # 3.5 sec self.next_pending = True - self.master.after(delay, self.load_next_question) + self.master.after(delay, self.show_question) def quit_quiz(self): pct = (self.score / self.total) * 100 if self.total > 0 else 0 - messagebox.showinfo("Quit Quiz", f"Questions answered: {self.total}\nCorrect: {self.score}\nPercentage: {pct:.2f}%") + messagebox.showinfo("Quit Quiz", f"Questions answered: {self.total}\nCorrect: {self.score}\nPercentage: {int(pct)}%") self.master.destroy() - -class TestSelectGUI: - def __init__(self, master): - self.master = master - self.master.title("Select Test") - self.selected_test = None - - label = tk.Label(master, text="Select the test you want to take:", font=("Arial", 14)) - label.pack(padx=10, pady=10) - - for test_name in URLS.keys(): - btn = tk.Button(master, text=test_name, width=20, command=lambda tn=test_name: self.select_test(tn)) - btn.pack(pady=5) - - def select_test(self, test_name): - self.selected_test = test_name - self.master.destroy() - - def main(): root = tk.Tk() - select_gui = TestSelectGUI(root) - root.mainloop() - - test_name = select_gui.selected_test - if test_name is None: - print("No test selected. Exiting.") - return - - print(f"Selected test: {test_name}") - print("Syncing question files...") - sync_files() - - questions = load_questions(test_name) - if not questions: - print("No questions found.") - return - - root = tk.Tk() - app = QuizGUI(root, questions) + app = HamTestGUI(root) root.mainloop()