首页 > 解决方案 > 使用面向对象的方法在 Tkinter 中的窗口之间传递数据

问题描述

使用 tkinter,我有一个主窗口,它从 TSV 文件读取并创建行及其值的列表,并根据每个项目创建按钮。当您单击一个按钮时,它会打开一个辅助 TopLevel 以编辑您单击的项目。完成后,我想将保存的数据传回主窗口,以便可以将其保存回列表,然后可以将其完全写回 TSV。

我已经搜索并阅读了有关此主题的几篇 SO 和博客文章,最接近的一篇是: How to pass data between top levels in tkinter

但是,在此解决方案中,在与根相同的类中为新(编辑)窗口创建小部件,我想将此工作分离到它自己的类中。问题是,当我将保存的值传递回主窗口时,出现错误(见下文代码)。

这是我正在使用的代码的简化版本(注意,这些类中的每一个都将在它们自己的单独文件中,但为了这个演示,它们被组合在一起。

#!/usr/bin/python3

from tkinter import *
from tkinter import ttk
import os
from tkinter import messagebox

class MainApp:
    def __init__(self, master):
        self.testVar = "none"
        self.master = master
        ttk.Button(self.master, text = "Open Window", command=lambda: NewWindow(self.master)).pack()

class NewWindow(Toplevel): 
    def __init__(self, master = None): 
          
        super().__init__(master = master) 
        self.title("New Window") 
        self.geometry("200x200") 
        label = Label(self, text ="This is a new Window") 
        label.pack() 
        ttk.Button(self, text = "Save", command=lambda: self.SaveData()).pack()
    
    def SaveData(self):
        messagebox.showinfo(title=None, message="Save complete: " + self.master.testVar)

def main():            
    os.system('cls')  # on windows
    root = Tk()
    MainApp(root)
    root.mainloop()
    
if __name__ == "__main__": main()

这是错误:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python39\lib\tkinter\__init__.py", line 1885, in __call__
    return self.func(*args)
  File "C:\repos\legend-bowl\test.py", line 22, in <lambda>
    ttk.Button(self, text = "Save", command=lambda: self.SaveData()).pack()
  File "C:\repos\legend-bowl\test.py", line 25, in SaveData
    messagebox.showinfo(title=None, message="Save complete: " + self.master.testVar)
  File "C:\Python39\lib\tkinter\__init__.py", line 2347, in __getattr__
    return getattr(self.tk, attr)
AttributeError: '_tkinter.tkapp' object has no attribute 'testVar'

我认为 NewWindow 的实例应该能够访问其父级的值,因为父级的实例被传递到其构造函数中。我觉得我错过了一些其他解决方案无法为我回答的简单问题。

  1. 我应该能够从其孩子的根目录访问吗?
  2. 显然,可能有更好的方法来处理这个我没有想到的,所以如果你提供它作为解决方案,我仍然想知道#1 的答案

标签: python-3.xtkinter

解决方案


问题是self.master它不是指的实例MainApp,它指的是根窗口。这是因为您没有传入MainAppto的实例NewWindow,而是self.master引用了 tkinter 定义的变量。

您需要将 to 的实例传递MainAppNewWindow,保存,然后在尝试访问时引用它testVar

一种方法是做这样的事情(虽然我个人认为一个函数会比lambda这里更好):

ttk.Button(self.master, text = "Open Window", command=lambda: NewWindow(self, self.master)).pack()

接下来,您必须定义NewWindow接受并保存参数,并使用它而不是self.master

class NewWindow(Toplevel):
    def __init__(self, mainapp, master = None):
        super().__init__(master = master)
        self.mainapp = mainapp
        ...
    def SaveData(self):
        messagebox.showinfo(title=None, message="Save complete: " + self.mainapp.testVar)

推荐阅读