I get the error _tkinter.TclError: bad window path name ".!button" and i'm not sure why
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.
James Malkin
Updated on June 17, 2022Comments
-
James Malkin almost 2 years
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
-
eyllanesc about 6 yearsIt is bad programming practice to call several variables with the same name.
-
James Malkin about 6 yearsdid not mean to delete your comment @eyllanesc but I forgot to mention this worked in past code before I switched to oop.
-
eyllanesc about 6 yearsthat works before does not imply that it works now
-
James Malkin about 6 years@eyllanesc and thank you for the tip, I'm in year 10 UK so I've only been coding like this for about 4 months and very basic stuff at that.
-
eyllanesc about 6 yearsThen to invest time in learning the good practices of programming, that you have 10 years is not a valid argument, do not use it.
-
James Malkin about 6 years@eyllanesc I will in my free time thank you.
-
eyllanesc about 6 yearsif you want help invest your time and provide a minimal reproducible example
-
abarnert about 6 yearsThis error usually happens when your tkinter widget objects get destroyed while the graphics are still live. It may be a matter of storing those
Gen_PButton
etc. as attributes onself
instead of locals, or some code that uses an instance of this class not keeping around a reference to it until aftermainloop()
ends, or… something else. It's hard to guess without seeing a complete example, but should be easy to debug if you do give us one. -
James Malkin about 6 years@abarnert I have put the entire code in the post if you'd like to look not sure if everything other than this is right anyway because this is the first time I've tried using oop
-
abarnert about 6 yearsThe entire code is way too much. Please read the help section that eyllanesc linked to
-
James Malkin about 6 yearsSorry @abarnert I misunderstood what was asked, I have added an example that recreates the issue and has everything needed to work once the issue is fixed.
-