如何从类中获取变量数据 [英] How to get variable data from a class

查看:64
本文介绍了如何从类中获取变量数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个较长的应用程序的缩短示例,其中我有多个小部件页面收集用户输入的信息.MyApp 将每个页面实例化为一个类.在该示例中,PageTwo 想要打印 StringVar 的值,该值存储来自 PageOne 中 Entry 小部件的数据.我怎么做?我尝试过的每一次尝试都以一个或另一个例外告终.

from tkinter import *从 tkinter 导入 ttk类 MyApp(Tk):def __init__(self):Tk.__init__(self)容器 = ttk.Frame(self)container.pack(side="top", fill="both", expand = True)self.frames = {}对于 F in (PageOne, PageTwo):框架 = F(容器,自身)self.frames[F] = 框架frame.grid(行=0,列=0,粘性= NSEW)self.show_frame(PageOne)def show_frame(self, cont):frame = self.frames[续]frame.tkraise()类 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(控制器)def make_widget(self, controller):self.some_input = StringVarself.some_entry = ttk.Entry(self, textvariable=self.some_input, width=8)self.some_entry.grid()button1 = ttk.Button(self, text='Next Page',命令= lambda:controller.show_frame(PageTwo))button1.grid()类 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='上一页',命令= 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('StartPage中存储的值 some_entry = ')#这里放什么#从PageOne打印some_input的值app = MyApp()app.title('多页测试应用')app.mainloop()

解决方案

利用你的控制器

鉴于您已经有了控制器的概念(即使您没有使用它),您可以使用它在页面之间进行通信.第一步是在每个页面中保存对控制器的引用:

class PageOne(ttk.Frame):def __init__(self, parent, controller):self.controller = 控制器...类 PageTwo(ttk.Frame):def __init__(self, parent, controller):self.controller = 控制器...

接下来,向控制器添加一个方法,当给定类名或其他一些识别属性时,该方法将返回一个页面.在您的情况下,由于您的页面没有任何内部名称,您可以只使用类名:

class MyApp(Tk):...def get_page(self, classname):'''返回一个页面的实例,给定它的类名作为字符串'''对于 self.frames.values() 中的页面:如果 str(page.__class__.__name__) == 类名:返回页返回无

<块引用>

注意:上述实现基于问题中的代码.问题中的代码起源于 stackoverflow 上的另一个答案.此代码与原始代码的不同之处在于它如何管理控制器中的页面.这使用类引用作为键,原始答案使用类名.

有了这个,任何页面都可以通过调用该函数获得对任何其他页面的引用.然后,通过对页面的引用,您可以访问该页面的公共成员:

class PageTwo(ttk.Frame):...def print_it(self):page_one = self.controller.get_page("PageOne")值 = page_one.some_entry.get()print('StartPage 中存储的值 some_entry = %s' % value)

在控制器中存储数据

从另一个页面直接访问一个页面并不是唯一的解决方案.缺点是您的页面紧密耦合.很难在一个页面中进行更改,而不必在一个或多个其他类中进行相应的更改.

如果您的所有页面都设计为一起工作以定义一组数据,那么将这些数据存储在控制器中可能是明智的,这样任何给定页面就不需要知道其他页面的内部设计.这些页面可以随意实现他们想要的小部件,而不必担心哪些其他页面可能会访问这些小部件.

例如,您可以在控制器中有一个字典(或数据库),每个页面都负责用它的数据子集更新该字典.然后,您可以随时向控制器询问数据.实际上,该页面正在签署合同,承诺将其全局数据的子集与 GUI 中的内容保持同步.只要维护契约,就可以在页面的实现中为所欲为.

为此,控制器将在创建页面之前创建数据结构.由于我们使用 tkinter,该数据结构可以由 StringVar 或任何其他 *Var 类的实例组成.不一定非得如此,但在这个简单的示例中它既方便又简单:

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

接下来,在创建小部件时修改每个页面以引用控制器:

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

最后,您可以从控制器而不是从页面访问数据.你可以扔掉 get_page,然后像这样打印值:

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

This is a shortened example of a longer application where I have multiple pages of widgets collecting information input by the user. The MyApp instantiates each page as a class. In the example, PageTwo would like to print the value of the StringVar which stores the data from an Entry widget in PageOne. How do I do that? Every attempt I've tried ends up with one exception or another.

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()

解决方案

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()
        ...

这篇关于如何从类中获取变量数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆