I get the error _tkinter.TclError: bad window path name ".!button" and i'm not sure why

16,947

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.

Share:
16,947
James Malkin
Author by

James Malkin

Updated on June 17, 2022

Comments

  • James Malkin
    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
      eyllanesc about 6 years
      It is bad programming practice to call several variables with the same name.
    • James Malkin
      James Malkin about 6 years
      did not mean to delete your comment @eyllanesc but I forgot to mention this worked in past code before I switched to oop.
    • eyllanesc
      eyllanesc about 6 years
      that works before does not imply that it works now
    • James Malkin
      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
      eyllanesc about 6 years
      Then 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
      James Malkin about 6 years
      @eyllanesc I will in my free time thank you.
    • eyllanesc
      eyllanesc about 6 years
      if you want help invest your time and provide a minimal reproducible example
    • abarnert
      abarnert about 6 years
      This 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 on self instead of locals, or some code that uses an instance of this class not keeping around a reference to it until after mainloop() 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
      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
      abarnert about 6 years
      The entire code is way too much. Please read the help section that eyllanesc linked to
    • James Malkin
      James Malkin about 6 years
      Sorry @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.