How to get variable data from a class?

Leveraging your controller

Given that you already have the concept of a controller in place (even though you aren’t using it), you can use it to communicate between pages. The first step is to save a reference to the controller in each page:

class PageOne(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ...

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

Next, add a method to the controller which will return a page when given the class name or some other identifying attribute. In your case, since your pages don’t have any internal name, you can just use the class name:

class MyApp(Tk):
    ...
    def get_page(self, classname):
        '''Returns an instance of a page given it's class name as a string'''
        for page in self.frames.values():
            if str(page.__class__.__name__) == classname:
                return page
        return None

note: the above implementation is based on the code in the question. The code in the question has it’s origin in another answer here on stackoverflow. This code differs from the original code slightly in how it manages the pages in the controller. This uses the class reference as a key, the original answer uses the class name.

With that in place, any page can get a reference to any other page by calling that function. Then, with a reference to the page, you can access the public members of that page:

class PageTwo(ttk.Frame):
    ...
    def print_it(self):
        page_one = self.controller.get_page("PageOne")
        value = page_one.some_entry.get()
        print ('The value stored in StartPage some_entry = %s' % value)

Storing data in the controller

Directly accessing one page from another is not the only solution. The downside is that your pages are tightly coupled. It would be hard to make a change in one page without having to also make a corresponding change in one or more other classes.

If your pages all are designed to work together to define a single set of data, it might be wise to have that data stored in the controller, so that any given page does not need to know the internal design of the other pages. The pages are free to implement the widgets however they want, without worrying about which other pages might access those widgets.

You could, for example, have a dictionary (or database) in the controller, and each page is responsible for updating that dictionary with it’s subset of data. Then, at any time you can just ask the controller for the data. In effect, the page is signing a contract, promising to keep it’s subset of the global data up to date with what is in the GUI. As long as you maintain the contract, you can do whatever you want in the implementation of the page.

To do that, the controller would create the data structure before creating the pages. Since we’re using tkinter, that data structure could be made up of instances of StringVar or any of the other *Var classes. It doesn’t have to be, but it’s convenient and easy in this simple example:

class MyApp(Tk):
    def __init__(self):
        ...
        self.app_data = {"name":    StringVar(),
                         "address": StringVar(),
                         ...
                        }

Next, you modify each page to reference the controller when creating the widgets:

class PageOne(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller=controller
        ...
        self.some_entry = ttk.Entry(self,
            textvariable=self.controller.app_data["name"], ...) 

Finally, you then access the data from the controller rather than from the page. You can throw away get_page, and print the value like this:

    def print_it(self):
        value = self.controller.app_data["address"].get()
        ...

Leave a Comment