user4860528
user4860528

Reputation:

Python Using tkinter

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()

Upvotes: 0

Views: 946

Answers (2)

Nick He
Nick He

Reputation: 11

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.

Upvotes: 1

abarnert
abarnert

Reputation: 366133

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.

Upvotes: 2

Related Questions