pythonpython-3.xtkintertkinter-label

In tkinter, how can I insert the value of an inherited class variable into a Label widget?


The issue I am having is specifically with the Label widget. In the linked code, the tests for Text widgets and Button widgets both grab the inherited value and display it correctly.

You can find the Label widget in question in class PageTwo

The variable I am trying to inherit and display within the Label is the "num" variable set in the first class.

The goal is to take that variable, set the value in another class, and then display the newly set value later in a Label widget.

I have tried to set the Label to display the variable directly, as a str value, within an f-string, as well as setting a local variable within PageTwo to take the value of TestClass.num

Example of the code in question is:

import tkinter as tk


class TestClass(tk.Tk):
    num = None

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        tk.Tk.wm_title(self, "Game")

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for F in (PageOne, PageTwo):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(PageOne)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()


class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self,
                         text="Make a selection",
                         wraplength=450, justify='center')
        label.pack(padx=10, pady=10)

        TestClass.num = tk.StringVar()
        tk.Radiobutton(self, text="1", variable=TestClass.num, value="1", ).pack()
        tk.Radiobutton(self, text="2", variable=TestClass.num, value="2", ).pack()
        tk.Radiobutton(self, text="3", variable=TestClass.num, value="3", ).pack()

        view_selection = tk.Button(self, text="test selection", command=lambda: print(TestClass.num.get()))
        view_selection.pack()

        next_page = tk.Button(self, text="Next Page",
                              command=lambda: controller.show_frame(PageTwo))
        next_page.pack(pady=10, padx=10)


class PageTwo(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        # label = tk.Label(self, text=TestClass.num.get()
        label = tk.Label(self, text=f"The number should show up here -> {TestClass.num.get()}  <- ")
        label.pack()

        text1 = tk.Text(self)
        text1.pack()

        see_num = tk.Button(self, text="View Number",
                            command=lambda: text1.insert('1.0', TestClass.num.get()))
        see_num.pack(pady=10, padx=10)


app = TestClass()
app.mainloop()

Solution

  • I have come across such uses of controller for a while now but never really got to know where the idea arose from. Anyway over here I see the problem might actually be in the program flow(when you instantiate with F(container, self) you are executing the __init__ of that class, hence the values are already being set.

    When you select each item in the radio button, you want the value of the label to appropriately edit itself. So for that I think static variables are more appropriate(to access the widgets inside another class) and you can use command to fire a callback.

    Also you need to fix the tristate issue of your Radiobutton, by giving different initial value to the StringVar to not be equal to the tristatevalue of Radiobutton or giving different tristatevalue to each radiobutton or to use ttk.Radiobutton that does not use this tristatevalue.

    So the changes to be made for PageTwo are to create some static variables:

    class PageTwo(tk.Frame):
        label = ''
        text = ''
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
    
            PageTwo.text = "The number should show up here ->  {}  <- " # {} so we can `format` it to be whatever text we want, later
            PageTwo.label = tk.Label(self, text=self.text.format(''))
            PageTwo.label.pack()
            ...
            ...
    

    And in PageOne, you need to set a command for Radiobutton that will be called each time an option is changed.

    class PageOne(tk.Frame):
        def __init__(self, parent, controller):
            ...
            ...
            TestClass.num = tk.StringVar(value=' ')
            command = lambda *args: PageTwo.label.config(text=PageTwo.text.format(TestClass.num.get()))
            tk.Radiobutton(self, text="1", variable=TestClass.num, value="1",command=command).pack()
            tk.Radiobutton(self, text="2", variable=TestClass.num, value="2",command=command).pack()
            tk.Radiobutton(self, text="3", variable=TestClass.num, value="3",command=command).pack()
            ...
            ...
    

    Another method I think possible is to create a function to create widgets, and load that up later when the page is supposed to be showed.