How to get variable data from a class

This is a shorthand example of a longer application, where I have several pages of widgets that collect user input. MyApp creates each page as a class. In this example, PageTwo would like to print a StringVar value that stores data from the Entry widget into PageOne. How should I do it? Every attempt I tried ends with one exception.

from tkinter import * from tkinter import ttk class MyApp(Tk): def __init__(self): Tk.__init__(self) container = ttk.Frame(self) container.pack(side="top", fill="both", expand = True) 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(ttk.Frame): def __init__(self, parent, controller): ttk.Frame.__init__(self, parent) ttk.Label(self, text='PageOne').grid(padx=(20,20), pady=(20,20)) self.make_widget(controller) def make_widget(self, controller): self.some_input = StringVar self.some_entry = ttk.Entry(self, textvariable=self.some_input, width=8) self.some_entry.grid() button1 = ttk.Button(self, text='Next Page', command=lambda: controller.show_frame(PageTwo)) button1.grid() class PageTwo(ttk.Frame): def __init__(self, parent, controller): ttk.Frame.__init__(self, parent) ttk.Label(self, text='PageTwo').grid(padx=(20,20), pady=(20,20)) button1 = ttk.Button(self, text='Previous Page', command=lambda: controller.show_frame(PageOne)) button1.grid() button2 = ttk.Button(self, text='press to print', command=self.print_it) button2.grid() def print_it(self): print ('The value stored in StartPage some_entry = ')#What do I put here #to print the value of some_input from PageOne app = MyApp() app.title('Multi-Page Test App') app.mainloop() 
+7
tkinter
Aug 25 '15 at 19:27
source share
1 answer

Using your controller

Given that you already have the concept of a controller in place (even if you are not using it), you can use it to link between pages. The first step is to save the link to the controller on 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 ... 

Then add a method to the controller, which will return the page when the class name or some other identifying attribute is specified. In your case, since your pages do not have an internal name, you can simply use the class name:

 class MyApp(Tk): ... def get_page(self, classname): '''Returns an instance of a page given it 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 question. The code in the question takes place in another answer here on stackoverflow. This code is slightly different from the source code in the way it manages pages in the controller. This uses the class reference as a key, the original answer uses the class name.

Using this function, any page can get a link to any other page by calling this function. Then, with a link to the page, you can access the public members of this 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) 

Saving data in the controller

Direct access to one page from another is not the only solution. The disadvantage is that your pages are closely related. It would be difficult to make changes to one page without also making the corresponding changes to one or more other classes.

If your pages are designed to work together to define one data set, it may be wise to have this data stored in the controller so that any page does not need the internal design of other pages, Pages can freely implement widgets, but they don’t want to worry about what other pages can access these widgets.

You can have, for example, a dictionary (or database) in the controller, and each page is responsible for updating this dictionary with its subset of data. Then at any time you can simply request a controller for the data. In fact, the page signs the contract, promising to keep its subset of global data up to date with what is in the graphical interface. As long as you maintain the contract, you can do whatever you want when implementing the page.

To do this, the controller will create a data structure before creating the pages. Since we use tkinter, this data structure can consist of instances of StringVar or any other Var class. This is not necessary, but it is convenient and easy in this simple example:

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

Then you modify each page to refer to the controller when creating 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, not from the page. You can throw away get_page and print this value as follows:

  def print_it(self): value = self.controller.app_data["address"].get() ... 
+17
Aug 25 '15 at 20:07
source share



All Articles