您的问题非常非常广泛,并不适合 Stackoverflow。不过,我将尝试为您提供一些一般性指导,因为我了解尝试学习 GUI 编程有多么困难。
概述
基于事件的编程与过程代码非常不同。您需要初始化 UI,然后设置函数来响应事件,而不是编写自上而下的代码。然后可以使用这些函数来更改显示。
我见过的一位最有才华的程序员给了我一个建议,即你应该始终从考虑你的数据结构开始。如果你已经很好地设计了你的数据,那么实现几乎是不言而喻的。如果你有糟糕的数据结构(例如:只是一堆随机变量),它会让程序非常难以编写和理解。
使用对象提问
那么,让我们从查看您的问题开始。它们是必须解析的一行文本。如果所有问题都是可以按主题存储在字典中的对象,那么编写 GUI 会更容易。
不必花哨,如果我们可以假设您的所有数据都已正确格式化,则定义可能看起来像这样:
class Question():
def __init__(self, question_string):
subject, question,answer1,answer2,answer3,answer4 = question_string.split(":")
self.subject = subject
self.question = question
self.correct_answer = answer1
self.answers = (answer1, answer2, answer3, answer4)
要创建一个问题,您可以从文件中传递一行。一旦你有了对象,就很容易得到主题、问题文本、正确答案和可能的答案。
>>> q = Question("English:What is a proper noun?:A person or a place:An object:A describing word:A doing word")
>>> print(q.question)
What is a proper noun?
>>> print(q.correct_answer)
A person or a place
>>> print(q.answers)
('A person or a place', 'An object', 'A describing word', 'A doing word')
这样,您可以遍历文件的行,为每一行创建一个对象。您可以将问题存储在列表或按主题组织的字典中。既然您说您希望用户选择一个主题,那么为每个主题创建一个带有键的字典是最有意义的。
可能看起来像这样:
questions = {"English": [], "Maths": []}
for row in f.readlines():
question = Question(row)
questions[question.subject].append(question)
创建图形用户界面
一般的想法是预先创建小部件,然后在选择问题时更新小部件。为此,我们将创建一个框架来容纳一个问题,并且在该框架中将有一个问题标签和四个单选按钮用于答案。单选按钮是正确的 UI 选择,因为它们代表了一个独特的选择。还会有一个标签显示答案是否正确。
首先,创建小部件以显示单个问题及其答案。
此代码假定您之前创建了一个名为 question_frame 的 Frame。将小部件放在一个单独的框架中可以让您在将来更轻松地向您的 GUI 添加其他小部件。
单选按钮需要共享 tkinter 变量之一(StringVar、IntVar 等)。这就是 tkinter 知道一次只能选择一个的方式,它提供了一种获取所选值的机制。我们将使用IntVar 并将其命名为answer_var。
answer_var = tk.StringVar()
question_label = tk.Label(question_frame, width=80, anchor="w")
answer_radiobuttons = (
tk.Radiobutton(question_frame, anchor="w", variable=answer_var, value=0),
tk.Radiobutton(question_frame, anchor="w", variable=answer_var, value=1),
tk.Radiobutton(question_frame, anchor="w", variable=answer_var, value=2),
tk.Radiobutton(question_frame, anchor="w", variable=answer_var, value=3),
)
这会创建一个标签来显示“正确”或“不正确”
result_label = tk.Label(root)
最后,我们需要在框架中排列小部件
pack 在这里使用是因为它简单。如果您愿意,可以使用grid。
question_label.pack(side="top", fill="x", anchor="w")
result_label.pack(side="bottom", fill="x")
for button in answer_radiobuttons:
button.pack(side="top", fill="x", expand=True, anchor="w")
创建一个显示问题的函数
现在有了显示问题及其答案的小部件,并且现在我们有了一些问题对象,我们需要编写一个可以显示单个问题及其答案的函数。我们可以通过使用小部件的configure 方法来做到这一点,该方法允许在创建小部件后对其进行修改。
在以下示例中,答案是随机的,因此用户无法仅通过选择每个问题的第一个单选按钮来获得正确答案。此外,我们将使用一个全局变量来跟踪当前问题。
def show_question(question):
global current_question
current_question = question
# configure the question label to display the question
question_label.configure(text=question.question)
# configure the radiobuttons to hold the answers
answers = list(question.answers)
random.shuffle(answers)
for i in range(4):
answer_radiobuttons[i].configure(text=answers[i])
answer_var.set(-1) # causes buttons to show as unselected
这样,您可以通过将任何问题传递给show_question 来显示任何问题。例如,您可以使用“下一步”按钮从问题列表中选择一个问题,然后将其显示在屏幕上。
创建一个函数来检查答案
现在我们有了一个保存问题的数据结构,以及一种显示问题的方法。现在我们需要一种方法来验证结果。 Radiobutton 小部件接受可用于此目的的 command 属性,或者您可以有一个“检查答案”按钮,或者您可以在用户尝试单击“下一个问题”按钮时调用该函数。无论如何,重点是创建一个进行检查的函数。如何触发取决于您。
我们知道单选按钮绑定到一个名为answer_var 的变量,并且该变量有一个返回值的get 方法。我们在列表中也有单选按钮,因此我们可以使用 cget 方法获取所选单选按钮的文本。这样,我们就可以将其与当前问题对象的正确答案进行比较。
看起来像这样:
def check_answer():
choice = answer_var.get()
answer = answer_radiobuttons[choice].cget("text")
if answer == current_question.correct_answer:
result_label.configure(text="Correct")
else:
result_label.configure(text="Incorrect")
作为如何使用它的示例,我们可以要求单选按钮在配置时调用此函数。我们可以通过添加command 属性来更改单选按钮的创建方式:
answer_radiobuttons = (
tk.Radiobutton(main_frame, anchor="w", variable=answer_var, value=0, command=check_answer),
tk.Radiobutton(main_frame, anchor="w", variable=answer_var, value=1, command=check_answer),
tk.Radiobutton(main_frame, anchor="w", variable=answer_var, value=2, command=check_answer),
tk.Radiobutton(main_frame, anchor="w", variable=answer_var, value=3, command=check_answer),
)
总结
我遗漏了很多细节,但那是因为没有一种最好的方法可以做到这一点,而且您的问题非常广泛。但是,一般模式适用于大多数 GUI 程序:
- 初始化您的数据
- 初始化显示
- 通过对用户的操作做出反应来更新显示