Search code examples
python-3.xuser-interfacetkinteruser-controlstkinter-button

I want my code to wait unless either of two buttons is pressed


enter image description here

  • ui.py
import tkinter as tk
sc = 0
q_txt = 'Can you please verify the pythagorous theorem using similarities of triangle.'
class Window():
    var = tk.IntVar()
    def __init__(self):
        self.window = tk.Tk()
        self.window.title('Guess if you can')
        self.window.config(padx = 50, pady = 50, bg = '#130f4a')
        self.window.minsize(width = 400, height = 300)
        
        self.score = tk.Label(text = 'score: 0',fg = 'white' ,bg = "#130f4a", font = ('Ariel', 12, 'bold'))
        self.score.grid(row = 0, column = 1)
        
        self.true = tk.PhotoImage(file = '_tick.png')
        self.false = tk.PhotoImage(file = '_cross.png')
        
        self.cnvs = tk.Canvas(self.window, width = 300, height= 250, bg = 'white', highlightthickness = 0)
        self.cnvs_txt = self.cnvs.create_text(300//2, 250//2, text = q_txt, font = ('Times 20 italic bold', 15), width = 200)
        self.cnvs.grid(row = 1, column = 0, columnspan = 2, pady = 20)


        V = tk.IntVar(0)
        self.tick_btn = tk.Button(self.window, image = self.true, highlightthickness = 0, bg = 'green', command = lambda : V.set(1))
        self.tick_btn.grid(row = 2, column = 0, pady = 5)
        
        self.cross_btn = tk.Button(self.window, image = self.false, highlightthickness = 0, bg = 'red')
        self.cross_btn.grid(row = 2, column = 1, pady =5)

    def change_question(self, next_question):
        self.cnvs.itemconfig(self.cnvs_txt, text = next_question)

    def asktopress(self, V):
        self.tick_btn.wait_variable(V)


        self.window.mainloop()
  • main.py
import questions
import ui
QA = questions.que_and_ans
ob = ui.Window()
next_question = 'My name is anthony'
ob.asktopress()
ob.change_question(next_question)
  • questions.py
import json


with open('question_bank.txt') as file:
    f = file.read()
data = json.loads(f)
que_and_ans = [(x['question'], x['correct_answer']) for x in data['results']]
  • Stuck at

So, basically I want to keep changing the question after either one of two buttons is pressed

Let say, There is question#1 (boolean question True or False type only) and now the execution should have to wait unless and until tick_btn or cross_btn is pressed so depending on right or wrong it updates the score the next I think I'll be able to do but I'm stuck with bold sentence.

My search

  • Error I'm facing
Traceback (most recent call last):
  File "C:\Users\alphaowner\Desktop\#100_Days_of_Python\#34-Day\Project\main.py", line 2, in <module>
    import ui
  File "C:\Users\alphaowner\Desktop\#100_Days_of_Python\#34-Day\Project\ui.py", line 4, in <module>
    class Window():
  File "C:\Users\alphaowner\Desktop\#100_Days_of_Python\#34-Day\Project\ui.py", line 5, in Window
    var = tk.IntVar()
  File "C:\Users\alphaowner\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 539, in __init__
    Variable.__init__(self, master, value, name)
  File "C:\Users\alphaowner\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 346, in __init__
    master = _get_default_root('create variable')
  File "C:\Users\alphaowner\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 297, in _get_default_root
    raise RuntimeError(f"Too early to {what}: no default root window")
RuntimeError: Too early to create variable: no default root window

Solution

  • The error you are receiving is because in ui.py, you are initializing/creating a Tkinter object/variable of IntVar() type, var = tk.IntVar(), before the tk.Tk() root is initialized, so the solution is:

    class Window():
        def __init__(self):
            self.window = tk.Tk()
            var = tk.IntVar()
            self.window.title('Guess if you can')
        
    

    Now, the question is what way do you want the execution of the program to wait until tick_btn or cross_btn is pressed?

    You don't need to take care of the program to wait, because it will itself wait until the user makes any move (presses cross, or presses tick, or closes the program.) See, when the Tkinter window is in mainloop, its execution has started and now all you need to do is, change every question (as well as the score) depending upon what button user presses.

    So, just call a function for both the buttons and in that function, check the answer, update the score, ask new question, and also display the result.

            self.tick_btn = tk.Button(self.window, image = self.true, highlightthickness = 0, bg = 'white', command = lambda:self.check_answer(True))
            self.tick_btn.grid(row = 2, column = 0, pady = 5)
            
            self.cross_btn = tk.Button(self.window, image = self.false, highlightthickness = 0, bg = 'white', command = lambda:self.check_answer(False))
            self.cross_btn.grid(row = 2, column = 1, pady =5)
    
            self.window.mainloop()
    
        def check_answer(self, val):
            #all questions done!
            if self.questionNo >= len(self.QA):
                print("Done!")
            else:
                if (val == True and self.answer == "true") or (val == False and self.answer == "false"):
                    print("Correct")
                    self.var.set(self.var.get()+1) #works as self.var+=1
                    self.score["text"] = 'Score: '+str(self.var.get())
                else:
                    #negative marking or do nothing
                    print("Wrong")
    
                #Now move to next question
                self.questionNo+=1
                if self.questionNo < len(self.QA):
                    self.q_txt = self.QA[self.questionNo][0]
                    self.answer = self.QA[self.questionNo][1]
                    self.cnvs.itemconfig(self.cnvs_txt, text = self.q_txt)
                else:   #last question done
                    print("Your final score is "+str(self.var.get()))
    

    Here is a link to my GitHub repo, I made changes to your code to create a True-False QnA program using Tkinter.