0

I am still dabbling with the stacked frames set up for a tkinter app and fairly new to OOP and Tkinter. I have copied below code from another SO question and amended it slightly. What I do not get done: I want to update the label2 on the StartPage based on the click on Button2 on PageTwo from "Hello" to "5". But the update does not take place. What do I have to do differently to accomplish my task? Many thanks in advance

import tkinter as tk

TITLE_FONT = ("Helvetica", 18, "bold")

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.var = tk.StringVar()
        self.var.set('Hello')
        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 (StartPage, PageTwo):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("StartPage")

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


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is the start page", font=TITLE_FONT)
        label.pack(side="top", fill="x", pady=10)

        label2 = tk.Label(self, textvariable=self.controller.var, font=TITLE_FONT)
        label2.pack(side="top", fill="x", pady=10)
        label2.config(text=self.controller.var)

        button2 = tk.Button(self, text="Go to Page Two",
                            command=lambda: controller.show_frame("PageTwo"))
        button2.pack()

class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="This is page 2", font=TITLE_FONT)
        label.pack(side="top", fill="x", pady=10)
        button = tk.Button(self, text="Go to the start page",
                           command=lambda: controller.show_frame("StartPage"))
        button.pack()
        button2 = tk.Button(self, text="Change X",
                           command=lambda: self.calculate())
        button2.pack()

    def calculate(self):
        self.controller.var = 5
        return self.controller.var

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()
Al_Iskander
  • 971
  • 5
  • 13
  • 27

1 Answers1

1

There are many ways to accomplish what you want. Since you are using a StringVar for the label you want to change, the simplest solution is to update that variable with the new value:

def calculate(self):
    self.controller.var.set(5)

This solution tightly couples the controller and the other class. That is, your PageTwo must know that the controller associates a StringVar with that label. If you modify the controller to use some other mechanism, you will have to change every other class that tries to set that variable.

A way to provide loose coupling is to have the controller provide an API for changing the value. In a sense, this is why controllers exist -- to control the flow of information between pages. The details of exactly how that value is stored and displayed is hidden from the other classes.

For example:

class SampleApp(...):
    ...
    def set_label(self, value):
        self.var.set(value)

class PageTwo(...):
    ...
    def calculate(self):
        self.controller.set_label(5)

The advantage to the above is that it provides loose coupling between the two classes. The other pages don't need to know that the label is implemented with a StringVar or a Label widget. The controller simply provides and interface that says "when you need to change variable X, call this function". As long as the controller maintains that function, you can change the implementation without having to modify every other class that may need to change that label.

See What is the difference between loose coupling and tight coupling in the object oriented paradigm? for more information about the difference between loose coupling and tight coupling.

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • many thanks @Bryan Oakley, since you mention it, what would your preferred way to do it? Your suggestion is working for a simple label update. In real, the updated values come via a tuple of different words that has to be split up into different labels. The StringVar() solution I was trying on does not appear to be the most efficient solution for that. – Al_Iskander Dec 20 '16 at 21:23
  • I can't answer what my preferred way is. It depends on your actual data and what you're actually trying to accomplish. What I would do with a little demo program is quite different than what I would do with a real application. – Bryan Oakley Dec 20 '16 at 22:01
  • I was trying to do it with a global variable but got stuck because the page classes are kind of static and do not take over the global variable values. At least I have not yet figured out how to make them pick the values up. – Al_Iskander Dec 20 '16 at 22:32
  • @Al_Iskander: what do you mean "page classes are kind of static"? They aren't static -- there's nothing about them that is static. They are objects like anything else, and the data stored in them can change. These classes are common, normal, everyday python classes. There is nothing special about them. You use them exactly as you do any other classes and objects in python. – Bryan Oakley Dec 20 '16 at 22:35
  • By static I mean: when I create a global variable with values in PageTwo for example and then do `show_frame('StartPage')` the StartPage is shown but it does not operate tasks using the global variable. – Al_Iskander Dec 20 '16 at 23:01
  • @Al_Iskander: I don't understand your comment. If you create a global variable, it absolutely will be available globally. By "global variable" did you mean an instance variable (eg: `self.something`)? You can create global variables if you want, but the whole point of this particular architecture is for each page to be isolated, with each page using the controller if it needs access to resources in another page. If that's not what you want, you shouldn't be using this architecture. – Bryan Oakley Dec 20 '16 at 23:04
  • the architecture is perfect and exactly what I need. It's just very complex for the stage of my development in programming. – Al_Iskander Dec 21 '16 at 10:54