0

I'm working on a GUI based hangman game in Python. The Tkinter main_window code is from a textbook but the ".Tk()" is causing problems. Upon running my code, I receive the error:

Traceback (most recent call last):
  File "E:\final.py", line 62, in <module>
    class MyGUI():
  File "E:\final.py", line 93, in MyGUI
    tkinter.mainloop()
  File "C:\Python34\lib\tkinter\__init__.py", line 403, in mainloop
    _default_root.tk.mainloop(n)
AttributeError: 'NoneType' object has no attribute 'tk'

I know I coded the GUI incorrectly but I'm not sure how to fix. Is this because I'm running the "setting" method before I run the GUI window? My code follows:

import tkinter
import tkinter.messagebox
import random

#fruit category
easyWords = ['apple', 'orange', 'mango', 'peach', 'guava']
#space category
mediumWords = ['atmosphere', 'jupiter', 'quasar', 'satellite', 'asteroid']
#science category
hardWords = ['biochemical', 'hemoglobin', 'emulsify', 'reactant', 'dynamo']

def setting():

    wordChoice = ''

    difficulty = input('''Welcome to hangman, select your difficulty.
Type, easy, medium, or hard to begin:''')

    if difficulty == 'easy':

        wordChoice = easyWords.random
        print('You have selected easy mode, start guessing your letters now in the game window. The category is: fruit')

    if difficulty == 'medium':

        wordChoice = mediumWords.random
        print('You have selected medium mode, start guessing your letters now in the game window. The category is: space')

    if difficulty == 'hard':

        wordChoice = hardWords.random
        print('You have selected hard mode, start guessing your letters now in the game window. The category is: science')

def game():

    missGuess = 0
    guesses = ''

    for char in wordChoice:
        label3.print(char),

        if char in guesses:
            print(char),

        else:
            label3.print("_"),
            missGuess += 1

        if missGuess == 1:
            label1.print('head')
        if missGuess == 2:
            label1.print('torso')
        if missGuess == 3:
            label1.print('left arm')
        if missGuess == 4:
            label1.print('right arm')
        if missGuess == 5:
            label1.print('left leg')
        if missGuess == 6:
            label1.print('right leg'),

class MyGUI():
    def __init__(self):
        self.main_window = tkinter.Tk()

        #create needed frames
        self.top_frame = tkinter.Frame(self.main_window)
        self.center_frame = tkinter.Frame(self.main_window)
        self.bottom_frame = tkinter.Frame(self.main_window)

        #create top frame labels
        self.label1 = tkinter.Label(self.top_frame, text='Hangman parts:')
        self.label2 = tkinter.Label(self.top_frame, text=' ')
        #center frame labels
        self.label3 = tkinter.Label(self.center_frame, text=' ')
        #bottom frame labels
        self.label4 = tkinter.Label(self.bottom_frame, text='Guess a letter:')
        self.entry1 = tkinter.Entry(self.bottom_frame, width=5)
        self.button1 = tkinter.Button(self.bottom_frame, text='Guess', command=self.game) #calls the game method
        self.button2 = tkinter.Button(self.bottom_frame, text='Exit', command=self.main_window.destroy)

        #pack top frame labels
        self.label1.pack(side='left')
        self.label2.pack(side='right')
        #pack center frame
        self.label3.pack(side='top')
        #bottom frame
        self.label4.pack(side='left')
        self.entry1.pack(side='left')
        self.button1.pack(side='left')
        self.button2.pack(side='left')

    tkinter.mainloop()



setting()
main()
2
  • Don't code too much and then test it. Always have a working code and program incrementally. For the start you could just show an empty window. Commented May 4, 2015 at 4:45
  • 'apple' and 'guava' are actually much harder hangman words than 'asteroid'. Commented May 4, 2015 at 6:23

2 Answers 2

2

You've put the tkinter.mainloop() inside the class body.

This means that it gets executed when the class itself is being defined. Which is before any instances of it are defined. Which means it's before your Tk() gets created. So, the default Tk instance doesn't exist yet, so it's None.

If you just indent it one notch further, to be at the end of the __init__ call, instead of at the class level, then it will work.

However, it's usually better to explicitly call mainloop on your own Tk instance (meaning self.main_window.mainloop() from inside a method of MyGUI, or some_instance.main_window.mainloop() from outside).

The reason to use self.main_window.mainloop() is that it's clearer and more debuggable. In particular, if you screw up and put it outside a method, you'll get an exception telling you there's no such thing as self, which makes it pretty obvious you meant to put the code in a method (just like a similar error on this in Java), instead of a mysterious error from inside the guts of Tkinter that you can only understand if you know its inner workings.

But if you want to understand the inner workings: Tk is an "almost-singleton"; while it's legal to create multiple instances, you rarely do. In Java, you might have a static class member storing the first-created instance as a "default" instance, and a static class method to access it, like Tk.get_default_root(). And then, you'd have another static class method, Tk.default_mainloop(), which just does Tk.get_default_root().mainloop(). In Python, you don't need static attributes and methods very often, because you can just make them module globals, so the default instance is stored in _default_root, and mainloop() calls _default_root.mainloop().


Meanwhile, you've got other problems you also need to fix. For example, you have multiple lines doing things like easyWords.random. First, if you want to call a function or method in Python, you need parentheses. Second, lists don't have a method named random. If you want to choose a random value out of a list, you do random.choice(easyWords). You also call a function named main that you haven't defined anywhere. And I'm sure there are other problems. Obviously, you have to fix all of them, not just the mainloop one, before it will work.

Sign up to request clarification or add additional context in comments.

2 Comments

The main method was created during coding but I forgot to erase it. I've replaced it with the class call. What exactly do you mean by "explicitly call mainloop on your own Tk instance"? Do you have a simple example? I'm pretty new to Python and the only other experience I have is a little Java.
@DustinWondrasek: The example is right there in the answer: self.main_window.mainloop(). I'll try to explain further by editing the answer.
1

A side note unrelated to your question. Your use of random may not work, e.g. as in the line:

wordChoice = easyWords.random.

List object does not have random attribute. I think what you intend to do is to randomly picking a word from the easyWords list. To do that, you can use:

wordChoice = random.choice(easyWords)

See if that works.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.