Fixed a bug where window was not big enough to view text/labels on Windows OS.

This commit is contained in:
JustinOros
2025-07-23 11:37:40 -07:00
parent 46d3855d96
commit 0cbdf2ef36
+75 -82
View File
@@ -1,5 +1,5 @@
#!/usr/bin/python3 #!/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 # Usage: python3 ham-test-gui.py
# Author: Justin Oros # Author: Justin Oros
# Source: https://github.com/JustinOros # Source: https://github.com/JustinOros
@@ -13,10 +13,10 @@ from datetime import datetime
import tkinter as tk import tkinter as tk
from tkinter import messagebox from tkinter import messagebox
# --- Setup logging --- # Setup logging
logging.basicConfig(level=logging.WARNING, format='[%(levelname)s] %(message)s') logging.basicConfig(level=logging.WARNING, format='[%(levelname)s] %(message)s')
# --- URLs and Local file paths --- # URLs and Local file paths
URLS = { URLS = {
"Technician": "https://raw.githubusercontent.com/russolsen/ham_radio_question_pool/master/technician-2022-2026/technician.json", "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", "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" "Extra": "extra.json"
} }
# --- Helper functions to sync question files --- # Helper functions to sync question files
def get_remote_last_modified(url): def get_remote_last_modified(url):
try: try:
@@ -81,15 +81,16 @@ def load_questions(test_name):
with open(local_path, "r", encoding='utf-8') as f: with open(local_path, "r", encoding='utf-8') as f:
return json.load(f) return json.load(f)
# --- GUI Classes --- # Main GUI Class
class QuizGUI: class HamTestGUI:
def __init__(self, master, questions): def __init__(self, master):
self.master = master self.master = master
self.master.title("HAM Radio Quiz") self.master.title("HAM Radio Test")
self.questions = questions self.center_window(800, 300)
random.shuffle(self.questions) self.master.resizable(False, False)
self.questions = []
self.score = 0 self.score = 0
self.total = 0 self.total = 0
self.current_index = -1 self.current_index = -1
@@ -97,36 +98,57 @@ class QuizGUI:
self.next_pending = False self.next_pending = False
self.correct_answer_text = "" self.correct_answer_text = ""
self.feedback_label = tk.Label(master, text="", font=("Arial", 14), wraplength=800, justify="left") # Container frame for dynamic content
self.feedback_label.pack(pady=5) 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.show_test_selection()
self.question_label.pack(padx=10, pady=10)
self.selected_answer = tk.StringVar() def center_window(self, width=800, height=600):
self.answers_frame = tk.Frame(master) self.master.update_idletasks()
self.answers_frame.pack(pady=5) 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) def clear_content(self):
nav_frame.pack(pady=10) for widget in self.content_frame.winfo_children():
widget.destroy()
self.quit_button = tk.Button(nav_frame, text="Quit", command=self.quit_quiz) # Test selection UI
self.quit_button.pack(side=tk.LEFT, padx=5) def show_test_selection(self):
self.clear_content()
self.next_button = tk.Button(nav_frame, text="Next", command=self.check_answer) label = tk.Label(self.content_frame, text="Select the test you want to take:", font=("Arial", 14))
self.next_button.pack(side=tk.LEFT, padx=5) 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): def start_test(self, test_name):
avg_char_width = 7 print(f"Selected test: {test_name}")
padding = 60 print("Syncing question files...")
max_width = 1000 sync_files()
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 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 self.current_index += 1
if self.current_index >= len(self.questions): if self.current_index >= len(self.questions):
@@ -135,20 +157,20 @@ class QuizGUI:
self.master.destroy() self.master.destroy()
return return
self.feedback_label.config(text="") self.feedback_label = tk.Label(self.content_frame, text="", font=("Arial", 14), wraplength=780, justify="left")
self.selected_answer.set(None) self.feedback_label.pack(pady=5)
for widget in self.answers_frame.winfo_children():
widget.destroy()
q = self.questions[self.current_index] q = self.questions[self.current_index]
question_text = q.get("question") question_text = q.get("question")
answers = q.get("answers", []) answers = q.get("answers", [])
correct_index = q.get("correct") correct_index = q.get("correct")
width = min(max(400, len(question_text) * 7), 800) self.question_label = tk.Label(self.content_frame, text=question_text, wraplength=780, justify="left", font=("Arial", 12))
self.master.geometry(f"{width}x400") self.question_label.pack(padx=10, pady=10)
self.question_label.config(text=question_text)
self.selected_answer = tk.StringVar()
self.answers_frame = tk.Frame(self.content_frame)
self.answers_frame.pack(pady=5)
options = list(enumerate(answers)) options = list(enumerate(answers))
random.shuffle(options) random.shuffle(options)
@@ -161,7 +183,7 @@ class QuizGUI:
text=ans_text, text=ans_text,
variable=self.selected_answer, variable=self.selected_answer,
value=ans_text, value=ans_text,
wraplength=width - 50, wraplength=760,
justify="left", justify="left",
anchor="w", anchor="w",
padx=10, padx=10,
@@ -172,6 +194,15 @@ class QuizGUI:
self.correct_answer = ans_text self.correct_answer = ans_text
self.correct_answer_text = 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 self.next_pending = False
def check_answer(self): def check_answer(self):
@@ -191,57 +222,19 @@ class QuizGUI:
else: else:
feedback_text = f"Incorrect! The correct answer was: {self.correct_answer_text}" feedback_text = f"Incorrect! The correct answer was: {self.correct_answer_text}"
self.feedback_label.config(text=feedback_text, fg="red") self.feedback_label.config(text=feedback_text, fg="red")
self.resize_to_fit_text(feedback_text)
delay = 3500 # 3.5 sec delay = 3500 # 3.5 sec
self.next_pending = True self.next_pending = True
self.master.after(delay, self.load_next_question) self.master.after(delay, self.show_question)
def quit_quiz(self): def quit_quiz(self):
pct = (self.score / self.total) * 100 if self.total > 0 else 0 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() 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(): def main():
root = tk.Tk() root = tk.Tk()
select_gui = TestSelectGUI(root) app = HamTestGUI(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)
root.mainloop() root.mainloop()