Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Tkinter 如何从类中获取变量数据_Tkinter - Fatal编程技术网

Tkinter 如何从类中获取变量数据

Tkinter 如何从类中获取变量数据,tkinter,Tkinter,这是一个较长应用程序的简化示例,其中我有多页小部件收集用户输入的信息。MyApp将每个页面实例化为一个类。在本例中,PageTwo希望打印StringVar的值,该值在PageOne中存储来自条目小部件的数据。我该怎么做?我的每一次尝试都会以这样或那样的例外而告终 from tkinter import * from tkinter import ttk class MyApp(Tk): def __init__(self): Tk.__init__(self)

这是一个较长应用程序的简化示例,其中我有多页小部件收集用户输入的信息。MyApp将每个页面实例化为一个类。在本例中,PageTwo希望打印StringVar的值,该值在PageOne中存储来自条目小部件的数据。我该怎么做?我的每一次尝试都会以这样或那样的例外而告终

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

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

class PageTwo(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ...
接下来,向控制器添加一个方法,当给定类名或某些其他标识属性时,该方法将返回一个页面。在您的情况下,由于您的页面没有任何内部名称,您可以只使用类名:

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
注:上述实现基于问题中的代码。问题中的代码来源于stackoverflow的另一个答案。此代码与原始代码在如何管理控制器中的页面方面略有不同。这使用类引用作为键,原始答案使用类名

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

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)
在控制器中存储数据 从另一个页面直接访问一个页面并不是唯一的解决方案。缺点是页面紧密耦合。如果不在一个或多个其他类中进行相应的更改,则很难在一个页面中进行更改

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

例如,您可以在控制器中拥有一个字典(或数据库),每个页面都负责使用其数据子集更新该字典。然后,您可以随时向控制器索取数据。实际上,该页面正在签署一份合同,承诺使用GUI中的内容使其全局数据子集保持最新。只要维护合同,您就可以在页面的实现中做任何您想做的事情

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

class MyApp(Tk):
    def __init__(self):
        ...
        self.app_data = {"name":    StringVar(),
                         "address": StringVar(),
                         ...
                        }
接下来,在创建小部件时修改每个页面以引用控制器:

class PageOne(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller=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()
        ...
利用您的控制器 鉴于您已经有了控制器的概念(即使您没有使用它),您可以使用它在页面之间进行通信。第一步是在每个页面中保存对控制器的引用:

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

class PageTwo(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ...
接下来,向控制器添加一个方法,当给定类名或某些其他标识属性时,该方法将返回一个页面。在您的情况下,由于您的页面没有任何内部名称,您可以只使用类名:

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
注:上述实现基于问题中的代码。问题中的代码来源于stackoverflow的另一个答案。此代码与原始代码在如何管理控制器中的页面方面略有不同。这使用类引用作为键,原始答案使用类名

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

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)
在控制器中存储数据 从另一个页面直接访问一个页面并不是唯一的解决方案。缺点是页面紧密耦合。如果不在一个或多个其他类中进行相应的更改,则很难在一个页面中进行更改

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

例如,您可以在控制器中拥有一个字典(或数据库),每个页面都负责使用其数据子集更新该字典。然后,您可以随时向控制器索取数据。实际上,该页面正在签署一份合同,承诺使用GUI中的内容使其全局数据子集保持最新。只要维护合同,您就可以在页面的实现中做任何您想做的事情

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

class MyApp(Tk):
    def __init__(self):
        ...
        self.app_data = {"name":    StringVar(),
                         "address": StringVar(),
                         ...
                        }
接下来,在创建小部件时修改每个页面以引用控制器:

class PageOne(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller=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()
        ...

我没料到你在回答这个问题时会说得那么详细。我希望你能感受到我真正的感激。我花了一些时间尝试在控制器中存储数据,我喜欢它的工作方式。我没有预料到