I decided to try using classes to recreate a game so that I do not have to use global variables but when I try to run the game I'm getting an error.
Traceback (most recent call last): File "D:\Users\James\Desktop\botmod OOP\index.py", line 203, in <module> Game.New_Game(Root) File "D:\Users\James\Desktop\botmod OOP\index.py", line 18, in New_Game Play_Button = self.Start_Canvas.create_window(300, 325, window = Play_Button) File "C:\Users\James\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py">, line 2501, in create_window return self._create('window', args, kw) File "C:\Users\James\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py">, line 2474, in _create *(args + self._options(cnf, kw))))
_tkinter.TclError: bad window path name ".!button"
I have looked through this to the best of my ability and cannot workout alone what the error is. Below is my code I can provide all the code if needed just comment. It should create a Tkinter button then apply it to the canvas but it gives me the error above.
def New_Game(self, Root):
self.Start_Canvas = Canvas(Root, width = 600, height = 500, bd=0, highlightthickness=0, bg = '#ffffff')
self.Start_Canvas.pack()
Title = self.Start_Canvas.create_text(300, 163, text = "BOTMOD", font = ('Cinema Gothic BTN Shadow', 75, 'normal'))
Gen_PButton = Button(text = "PLAY", width = 10, font=('Arial', 15), command = self.Play_Game)
Play_Button = self.Start_Canvas.create_window(300, 325, window = Gen_PButton)
Gen_EButton = Button(text = "EXIT", width = 10, font=('Arial', 15), command = lambda: self.Game_End("EXIT"))
Exit_Button = self.Start_Canvas.create_window(300, 375, window = Gen_EButton)
Before I used oop, this worked using Global variables and Functions so I cannot find what is causing the issue as the button is defined.
The Example Code is Below
from tkinter import *
from random import choice, shuffle
class Game:
def __init__(self):
self.Playing_Game = True
self.Game_Paused = False
self.Restart_Game = False
self.Already_Played = False
def New_Game(self, Root):
self.Start_Canvas = Canvas(Root, width = 600, height = 500, bd=0, highlightthickness=0, bg = '#ffffff')
self.Start_Canvas.pack()
Title = self.Start_Canvas.create_text(300, 163, text = "BOTMOD", font = ('Cinema Gothic BTN Shadow', 75, 'normal'))
Gen_PButton = Button(text = "PLAY", width = 10, font=('Arial', 15), command = self.Play_Game)
Play_Button = self.Start_Canvas.create_window(300, 325, window = Gen_PButton)
Gen_EButton = Button(text = "EXIT", width = 10, font=('Arial', 15), command = lambda: self.Game_End("EXIT"))
Exit_Button = self.Start_Canvas.create_window(300, 375, window = Gen_EButton)
def Play_Game(self):
if self.Already_Played == False:
self.Start_Canvas.destroy()
self.Menu_Canvas = Canvas(Root, width = 600, height = 500, bd=0, highlightthickness=0, bg = '#C0C0C0')
self.Menu_Canvas.pack()
def Game_End(self):
if self.End_Option == "EXIT":
self.Playing_Game = False
Root.destroy()
else:
self.Game_Finished = True
self.Game_Canvas.create_rectangle(120, 120, 480, 300, fill = "#ffffff")
self.Game_Canvas.create_text(300, 210, text = End_Option, font = ('Abadi', 35, "bold"))
#Continue_Button = Button(Root, text = 'Continue', command = self.Game_Restart)
Exit_Button = Button(Root, text = 'Exit', command = lambda: self.Game_End('EXIT'))
#Continue_Button.pack()
Exit_Button.pack()
Root = Tk()
Game = Game()
while True:
while Game.Restart_Game == False:
if Game.Playing_Game == False:
break
else:
Game_Finished = False
Root = Tk()
if Game.Already_Played == True:
Game.Play_Game()
Root.mainloop()
elif Game.Already_Played == False:
Game.New_Game(Root)
Root.mainloop()
break
The immediate problem is that you haven't given the buttons a parent. The tl;dr is that you just need to add Root
(or some other appropriate parent window) to the constructors for Gen_Pbutton
and Gen_EButton
, as you do for all of your other widgets.
The following two lines have an important difference:
Gen_PButton = Button(Root, text = "PLAY", width = 10, font=('Arial', 15), command = self.Play_Game)
Gen_PButton = Button(text = "PLAY", width = 10, font=('Arial', 15), command = self.Play_Game)
Both versions create a new name for a button widget (.!button
in this case), and create a new tkinter.Button
object associated with that name—but the first one also asks Root
to create the actual widget named .!button
, while the second one doesn't ask anyone to create the widget. So you end up with a Button
object that's attached to a non-existent widget, and whenever you try to use that Button
object, you get an error like this:
_tkinter.TclError: bad window path name ".!button"
The usual reason for an error like this is that you've destroyed the underlying button widget but keep trying to use the Button
. But in this case, you never created the widget in the first place, which obviously leads to the same problem.
To understand exactly what's going on under the covers, you have to understand how Tkinter works—the actual GUI widgets and windows are managed by code in a completely different language, Tcl/Tk, and tkinter is association Python objects with Tcl objects by name and proxying every one of your method calls to those Tcl objects.
You may wonder why tkinter lets you get away with this construction in the first place, instead of giving you a more comprehensible error, one line earlier, something like this:
_tkinter.TclError: trying to create ".!button" with null parent
Well, technically, it's perfectly legal. You might create that Tcl widget later through some lower-level method, or you might have already created that Tcl widget and now just want to wrap a tkinter controller around it. Both are very rare cases, but they're not nonsensical, so tkinter allows for them.
And, more to the point: would you really understand the "better" error message any more easily? Neither one makes sense when you're first learning tkinter, and both are things you can learn to understand and deal with.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With