slinden
slinden

Reputation: 1025

How to reference correctly to frame names in tkinter

I am creating a card game in tkinter and need help with referencing to the frame names. My problem is that when I want to "refresh" the frame, I need to destroy and recreate it and this changes the progressive numbering of the frames.

Please take a look at the code below. The example shows that the third frame every time gets a new name as it gets recreated.

import tkinter as tk


class RootFrame(tk.Tk):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.main_window = tk.Frame(self)
        self.main_window.pack(side="top", fill="both", expand=True)
        self.main_label = tk.Label(self.main_window, text="Main Window")
        self.main_label.pack()

        self.second_frame = SecondFrame(self.main_window, self)
        self.second_frame.pack()


class SecondFrame(tk.Frame):

    def __init__(self, parent, controller, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.controller = controller

        label = tk.Label(self, text="Second Frame")
        label.pack()

        self.create_third_frame()

    def create_third_frame(self):
        self.third_frame = ThirdFrame(self, self.controller)
        self.third_frame.pack()

    def update_frame(self):
        self.third_frame.destroy()
        self.create_third_frame()


class ThirdFrame(tk.Frame):

    def __init__(self, parent, controller, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.controller = controller
        self.parent = parent

        label = tk.Label(self, text="Third Frame")
        label.pack()

        refresh_button = tk.Button(
            self, text="Resfresh", command=self.parent.update_frame)
        refresh_button.pack()

        print(self.winfo_name())


if __name__ == "__main__":
    app = RootFrame()
    app.mainloop()

The code above is used to illustrate the problem. Please run the code and you'll see the changing widget name in the terminal.

I use winfo_parent and winfo_name in my actual code to create different conditions for button bindings. For example, if the user clicks a widget1 in frame6 happens X and when I click a widget8 in frame2 happens Y. This works until I destroy() and recreate something and everything breaks.

I suppose that using winfo_name and winfo_parent for this kind of referencing is not the correct way to get around, but I really can't think of anything else.

Upvotes: 0

Views: 2861

Answers (1)

r.ook
r.ook

Reputation: 13888

I'm not sure exactly what you are asking, but you can assign a specific name to the widget:

def create_third_frame(self):
    self.third_frame = ThirdFrame(self, self.controller, name='testframe')
    self.third_frame.pack()

Then each time the name of the frame created will be consistent.

You can also reference the widget by name with Tk().nametowidget(), see this relevant answer here: Is it possible to search widget by name in Tkinter?

>>> from Tkinter import *
>>> win = Tk()
>>> button = Button( Frame( win, name = "myframe" ), name = "mybutton" )
>>> win.nametowidget("myframe.mybutton")
<Tkinter.Button instance at 0x2550c68>

I would recommend sticking with a OOP approach however, and just reference it with from your code like self.thirdframes where you might have a list or dict of ThirdFrame objects. This way your python code can easily reference the objects without going back to the tcl interpreter and parse the widget name. If you ever will only have one ThirdFrame, then just reference back to self.thirdframe whenever you need it.

Upvotes: 1

Related Questions