4
\$\begingroup\$

I have made a little Quiz Game in Python. This was my first experience in programming Gui's in Python with Tkinter. In general, I do not have much knowledge about graphic programming and have acquired the necessary knowledge through skimming internet articles and try and error. Accordingly, the code looks like sciolism and semi-professional structuring. That should be improved. I will not be able to avoid a good basic tutorial on graphic programming, I think. However, if you have tips for me how to improve the style I would be grateful to hear them.

from tkinter import Tk, Frame, Label, Button 
from time import sleep

class Question:
    def __init__(self, question, answers, correctLetter):
        self.question = question
        self.answers = answers
        self.correctLetter = correctLetter

    def check(self, letter, view):
        global right
        if(letter == self.correctLetter):
            label = Label(view, text="Right!")
            right += 1
        else:
            label = Label(view, text="Wrong!")
        label.pack()
        view.after(1000, lambda *args: self.unpackView(view))


    def getView(self, window):
        view = Frame(window)
        Label(view, text=self.question).pack()
        Button(view, text=self.answers[0], command=lambda *args: self.check("A", view)).pack()
        Button(view, text=self.answers[1], command=lambda *args: self.check("B", view)).pack()
        Button(view, text=self.answers[2], command=lambda *args: self.check("C", view)).pack()
        Button(view, text=self.answers[3], command=lambda *args: self.check("D", view)).pack()
        return view

    def unpackView(self, view):
        view.pack_forget()
        askQuestion()

def askQuestion():
    global questions, window, index, button, right, number_of_questions 
    if(len(questions) == index + 1):
        Label(window, text="Thank you for answering the questions. " + str(right) + " of " + str(number_of_questions) + " questions answered right").pack()
        return
    button.pack_forget()
    index += 1
    questions[index].getView(window).pack()

questions = []
file = open("questions.txt", "r")
line = file.readline()
while(line != ""):
    questionString = line
    answers = []
    for i in range (4):
        answers.append(file.readline())

    correctLetter = file.readline()
    correctLetter = correctLetter[:-1]
    questions.append(Question(questionString, answers, correctLetter))
    line = file.readline()
file.close()
index = -1
right = 0
number_of_questions = len(questions)

window = Tk()
button = Button(window, text="Start", command=askQuestion)
button.pack()
window.mainloop()

questions.txt (this file has to be in the same folder)

Where is Germany?
In Europe
In Great Britain
In Asia
In the USA
A
Where is England?
In Asia
On an island
In the USA
In China
B
What is the etymological meaning of antibiotics?
For Life
For Health
Against Live
Against Diseases
C
"Portability is for people who can not write new programs." From whom does the quote come from?
Bill Gates
Hillary Clinton
Richard Stallman
Linus Torvalds
D
Who is the richest man in the world?
Jeff Bezos
Bill Gates
Donald Trump
Me
AX
\$\endgroup\$
4
\$\begingroup\$

Use a single import

Instead of importing each individual class, I recommend importing the whole module with a short name. You can then use the short name as a prefix for all of your commands. It makes your code a bit more self-documenting, especially if you mix both tkinter and ttk in the same application.

For example:

import tkinter as tk
...
window = tk.Tk()
button = tk.Button(window, text="Start", command=askQuestion)

Separate widget creation from layout

In my experience, the layout of a GUI changes a lot more than the actual widgets, especially early in development. By separating widget creation from widget layout, it makes debugging much easier. It also makes it much easier to visualize the layout just by looking at the code.

Instead of this:

Label(view, text=self.question).pack()
Button(view, text=self.answers[0], command=lambda *args: self.check("A", view)).pack()
Button(view, text=self.answers[1], command=lambda *args: self.check("B", view)).pack()
Button(view, text=self.answers[2], command=lambda *args: self.check("C", view)).pack()
Button(view, text=self.answers[3], command=lambda *args: self.check("D", view)).pack()

... I recommend this:

label = Label(view, text=self.question)
button_a = Button(view, text=self.answers[0], command=lambda *args: self.check("A", view))
button_b = Button(view, text=self.answers[1], command=lambda *args: self.check("B", view))
button_c = Button(view, text=self.answers[2], command=lambda *args: self.check("C", view))
button_d = Button(view, text=self.answers[3], command=lambda *args: self.check("D", view))

label.pack()
button_a.pack()
button_b.pack()
button_c.pack()
button_d.pack()

Explicitly set side for pack

I recommend always explicitly setting the side for pack. Even though the default will likely never change, explicitly setting the side makes the code less ambiguous:

label.pack(side="top")
button_a.pack(side="top")
button_b.pack(side="top")
button_c.pack(side="top")
button_d.pack(side="top")
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.