如何在Tkinter按钮点击事件中从GUI类调用测验逻辑类的方法
核心实现逻辑
你当前的代码存在类实例隔离、属性引用错误、基础容器未创建三类核心问题,可通过「主GUI类组合调用测验逻辑类」的方式实现需求,不需要用继承,耦合度更低。
- 主GUI类提前创建所有页面容器,给触发测验开始的按钮绑定页面切换方法
- 页面切换方法中完成容器切换后,直接将当前主GUI实例作为参数传给测验类,完成测验逻辑初始化
- 测验类内部通过传入的主GUI实例操作窗口、容器等UI元素,不需要单独调用全局实例
修正后可运行代码
import tkinter as tk from tkinter import tix from tkinter import ttk import json import tkinter.messagebox import sqlite3 # 主GUI类 class Main: def __init__(self): self.root = tix.Tk() self.root.title("Quiz App") self.root.geometry("850x600+500+200") self.root.resizable(False,False) # 提前创建两个页面容器 self.frame2 = tk.Frame(self.root) self.frame3 = tk.Frame(self.root) # frame2示例内容:登录页,你可以替换成自己的原有逻辑 tk.Label(self.frame2, text="用户名:").grid(row=0, column=0, padx=10, pady=10) self.entry1 = tk.Entry(self.frame2) self.entry1.grid(row=0, column=1, padx=10, pady=10) tk.Label(self.frame2, text="密码:").grid(row=1, column=0, padx=10, pady=10) self.entry2 = tk.Entry(self.frame2, show="*") self.entry2.grid(row=1, column=1, padx=10, pady=10) # 登录按钮点击触发切换到测验页 tk.Button(self.frame2, text="开始测验", command=self.frame2to3).grid(row=2, column=0, columnspan=2, pady=20) self.frame2.pack(fill="both", expand=True) # 切换到测验页并初始化测验逻辑 def frame2to3(self): # 这里可以加用户名密码存入SQLite的逻辑,你可以自己补充 self.frame2.pack_forget() self.frame3.pack(fill="both", expand=True) # 传入当前主GUI实例初始化测验类 self.quiz = Quiz(main_gui=self) def clearfunc1(self): self.entry1.delete(0, 'end') self.entry2.delete(0, 'end') def clearfunc2(self): self.entry3.delete(0, 'end') self.entry4.delete(0, 'end') def close(self): if tkinter.messagebox.askyesno("Quiz App","Are you sure you want to exit?") > 0: self.root.destroy() return # 测验逻辑类 class Quiz: # 接收主GUI实例参数 def __init__(self, main_gui): self.main_gui = main_gui self.q_no = 0 self.opt_selected = tk.IntVar() self.opts = self.radio_buttons() self.display_question() self.display_options() self.buttons() self.data_size = len(question) self.correct = 0 def display_result(self): wrong_count = self.data_size - self.correct correct_text = f"Correct: {self.correct}" wrong_text = f"Wrong: {wrong_count}" score = int(self.correct/self.data_size*100) result = f"Score: {score}%" show_result = tk.Label(self.main_gui.frame3, text=f"{result}\n{correct_text}\n{wrong_text}") show_result.place(x=30, y=300) def check_ans(self, q_no): if self.opt_selected.get() == answer[q_no]: return True def next_btn(self): if self.check_ans(self.q_no): self.correct += 1 self.q_no += 1 if self.q_no == self.data_size: self.display_result() # 答题结束后禁用下一题按钮 self.next_button.config(state="disabled") else: self.display_question() self.display_options() def buttons(self): self.next_button = tk.Button(self.main_gui.frame3, text = "下一题" , command=self.next_btn) self.next_button.place(x=780,y=550) self.exitButton3 = tk.Button(self.main_gui.frame3,text="Exit", command = self.main_gui.close) self.exitButton3.place(x=250,y=140) def display_options(self): val = 0 self.opt_selected.set(0) for option in options[self.q_no]: self.opts[val]['text'] = option val += 1 def display_question(self): # 先清空旧的问题标签 for widget in self.main_gui.frame3.winfo_children(): if isinstance(widget, tk.Label) and widget.winfo_y() == 90: widget.destroy() ques_no = tk.Label(self.main_gui.frame3, text=question[self.q_no]) ques_no.place(x=30,y=90) def radio_buttons(self): q_list =[] y_pos = 150 while len(q_list) < 4: radio_btn = tk.Radiobutton(self.main_gui.frame3, text = "", variable=self.opt_selected,value = len(q_list)+1) q_list.append(radio_btn) radio_btn.place(x=100,y=y_pos) y_pos += 30 return q_list if __name__ == "__main__": # 读取JSON题目数据 with open('data1.json') as f: data1 = json.load(f) question = data1['question'] options = data1['options'] answer = data1['answer'] # 启动主GUI app = Main() app.root.mainloop()
补充说明
- 你可以在
frame2to3方法中补充用户名密码写入SQLite的逻辑,直接用sqlite3库操作即可 - 示例代码中给frame2加了基础的登录表单,你可以替换成自己原有UI逻辑
- 原有代码中存在的变量名冲突、属性引用错误、页面元素残留问题都已经修复
内容的提问来源于stack exchange,提问作者akash_s93
相关产品推荐
相关产品推荐

