Tkinter 如何从类中获取变量数据
这是一个较长应用程序的简化示例,其中我有多页小部件收集用户输入的信息。MyApp将每个页面实例化为一个类。在本例中,PageTwo希望打印StringVar的值,该值在PageOne中存储来自条目小部件的数据。我该怎么做?我的每一次尝试都会以这样或那样的例外而告终Tkinter 如何从类中获取变量数据,tkinter,Tkinter,这是一个较长应用程序的简化示例,其中我有多页小部件收集用户输入的信息。MyApp将每个页面实例化为一个类。在本例中,PageTwo希望打印StringVar的值,该值在PageOne中存储来自条目小部件的数据。我该怎么做?我的每一次尝试都会以这样或那样的例外而告终 from tkinter import * from tkinter import ttk class MyApp(Tk): def __init__(self): Tk.__init__(self)
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()
...
我没料到你在回答这个问题时会说得那么详细。我希望你能感受到我真正的感激。我花了一些时间尝试在控制器中存储数据,我喜欢它的工作方式。我没有预料到