Reputation:
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
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
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