Search code examples
pythontkinter

Python Tkinter - Run window before loop


Background Information

The below is the code I'm currently working on to create a program that essentially prints a word, and asks you to correctly spell the word. A word can only be printed once, as once used it is put into the usedWords dictionary so that it is not reused. When all the words have been used up, the while loop ends. This all works perfectly, however now I'm trying to integrate this with a GUI.

What specifically my problem is

The issue is that I would like to have the Tkinter window have a label, and have the label update every time a new word is printed out. However, I am currently getting an error AttributeError: 'NoneType' object has no attribute '_root' I believe this is happening because I have tried to add a Label before defining the main window is defined- but I have unable to do this any other way, if the window is defined before the loop the window does not open before the loop is finished, which is obviously counter-intuitive to what I want to actually do.

With all that said- my question

It's as simple as this- how do I make this worK? I want to create the window before the loop, and have the Label within the window update every time a new word is selected from the list

import random
import time
from tkinter import *
from tkinter.messagebox import *

words = ['reflection', 'attitude', 'replicate', 'accomodate', 'terrain',
         'argumental', 'stipulate', 'stimulation', 'latitude', 'marginal',
         'thedubester', 'security', 'documentation', 'ukulele',
         'terminal', 'exaggeration', 'declaration', 'apptitude', 'absence',
         'aggressive', 'acceptable', ' allegiance', 'embarass', 'hierarchy',
         'humorous', 'existence']

usedWords = []

while len(usedWords) < len(words):
    chooseWord = words[random.randrange(len(words))]

    var = StringVar()
    display = Label(textvariable = var)
    display.place(x=1,y=1)

    if chooseWord not in usedWords:

        
        var.set(chooseWord)
        userSpelt = input("Spelling: ")
        usedWords.append(chooseWord)

        if userSpelt in words:
            print("Correctly spelt!")
        else:
             print("Incorrectly spelt!")



master = Tk()
master.geometry("500x600")
master.title("Minesweeper V 1.0")
master.configure(background="#2c3e50")

Solution

  • I think that one of the issues that you're running into is trying to create a dialog box from console input. I'm sure there's a way to do it, but at the moment I can't think of a good way.

    Another issue, you're not actually creating the window (master, in your script) until after you've been through the number of words that are in your word list.

    Tkinter is a bit different than console scripting. It's event based, so if you write a while loop within the application, tkinter will "pause" until that while loop has completed.

    Instead, you may want to let the looping be done by tkinter and code around that. Here's a simple implementation that I came up with. It still needs work but it gets the job done (I'm assuming that you're using python 3 because you're using "tkinter" in all lowercase):

    import random
    from tkinter import *
    from tkinter.messagebox import *
    
    words = ['reflection', 'attitude', 'replicate', 'accomodate', 'terrain',
             'argumental', 'stipulate', 'stimulation', 'latitude', 'marginal',
             'thedubester', 'security', 'documentation', 'ukulele',
             'terminal', 'exaggeration', 'declaration', 'apptitude', 'absence',
             'aggressive', 'acceptable', ' allegiance', 'embarass', 'hierarchy',
             'humorous', 'existence']
    
    usedWords = []
    
    class App(Tk):
        def __init__(self):
            super().__init__()
            
            self._frame = Frame(self)
            self._frame.pack()
            
            self._label = Label(self, text="")
            self._label.pack()
            
            self._entry_var = StringVar()
            self._entry = Entry(self, textvariable=self._entry_var)
            self._entry.pack()
            
            self._button = Button(
                self,
                text="Check",
                command=self.check_callback
            )
            self._button.pack()
            
            self.get_new_word()
        
        def check_callback(self):
            if self._current_word == self._entry.get():
                showinfo("Correct", "Correctly spelled.")
                usedWords.append(self._current_word)
            else:
                showwarning("Incorrect", "Incorrectly spelled.")
                words.append(self._current_word)
            self.get_new_word()
        
        def get_new_word(self):
            self._current_word = words.pop(random.randrange(len(words)))
            self._label.configure(text=self._current_word)
            self._entry_var.set("")
            self.title(self._current_word)
    
    app = App()
    app.mainloop()
    

    This moves the tkinter portion into a class that inherits from the Tk class. In my opinion, it makes things easier once your program becomes more complex.