Python 通过Tkinter类传递数据帧
我正在使用Python2.7和Tkinter。我几乎不熟悉面向对象程序。我有一个包含许多Tkinter窗口的长程序,在某个时刻,我要求用户加载一个Excel文件,我用Pandas读取该文件,并希望永久使用和更新(数据变量的)该值。我现在的做法是使用全局变量,但我知道这是危险的、低效的,而且一点也不优雅 尽管我可以按照gui类的构建方式来构建controller.show_frame(framename),但我最终还是自己构建了一些框架,以便数据变量能够自我更新 我在Stack Overflow中阅读并尝试了一些答案,但可能实现错误:Python 通过Tkinter类传递数据帧,python,python-2.7,class,oop,tkinter,Python,Python 2.7,Class,Oop,Tkinter,我正在使用Python2.7和Tkinter。我几乎不熟悉面向对象程序。我有一个包含许多Tkinter窗口的长程序,在某个时刻,我要求用户加载一个Excel文件,我用Pandas读取该文件,并希望永久使用和更新(数据变量的)该值。我现在的做法是使用全局变量,但我知道这是危险的、低效的,而且一点也不优雅 尽管我可以按照gui类的构建方式来构建controller.show_frame(framename),但我最终还是自己构建了一些框架,以便数据变量能够自我更新 我在Stack Overflow中
- 尝试在gui类中创建一个字典,比如
并从其他窗口更新它,这里的问题是,我认为类gui只实例化一次,它创建了所有其他窗口类,所以这不起作用。也许我做错了什么。(代码上未显示)self.app_data={data=[],filename=“”}
- 我试着按照建议做些什么,但就是做不到李>
有一些事情导致程序无法正常运行 我注意到的第一件事是全局变量的使用。这可以通过使用类属性来避免 对于
类gui(tk.tk)下面的两个变量:
您需要将它们移动到\uuuu init\uuuu
部分,以便可以实例化这些变量。您还需要将它们转换为类属性,以便其他方法甚至其他类可以与它们交互。我们可以通过在变量名中添加self.
前缀来实现这一点
如下所示:
self.data = pd.DataFrame([])
self.filename = ""
要访问gui
类的方法/属性,需要将gui
类的对象传递给使用它的其他类
代码中的statusText
未定义,因此set()
在此处不起作用。只需添加self.statusText
作为类属性
您的一些小部件不需要分配给变量名,因为没有对它们进行编辑。例如:
label = tk.Label(self, text="Please load a file: ")
label.pack()
这可以简单地更改为:
tk.Label(self, text="Please load a file: ").pack()
这将有助于减少您正在编写的代码量,并保持名称空间更干净
有几种方法可以纠正所有这些问题,但最简单的方法是将这些代码移到一个类中。您提供的代码没有充分的理由将几个帧与主gui
类分开
下面的代码是代码的重写版本,使用一个类来完成代码试图完成的任务,并将所需的代码量减少了30多行。我相信它可以进一步完善,但这个例子应该会有所帮助
import Tkinter as tk
import pandas as pd
import tkFileDialog
class gui(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.data = pd.DataFrame([])
self.filename = ""
self.strat_columns = []
self.main_frame()
self.first_frame()
self.mainframe.tkraise()
def get_page(self, page_class):
return self.frames[page_class]
def main_frame(self):
self.mainframe = tk.Frame(self.master)
self.mainframe.grid(row=0, column=0, sticky="nsew")
tk.Button(self.mainframe, text="New window",
command=lambda: self.firstframe.tkraise()).pack()
def first_frame(self):
self.firstframe = tk.Frame(self.master)
self.firstframe.grid(row=0, column=0, sticky="nsew")
self.statusText = tk.StringVar()
self.statusText.set("Press Browse button and browse for file, then press the Go button")
label = tk.Label(self.firstframe, text="Please load a file: ").pack()
self.first_frame_entry = tk.Entry(self.firstframe, width=50)
self.first_frame_entry.pack()
tk.Button(self.firstframe, text="Go", command=self.button_go_callback).pack()
tk.Button(self.firstframe, text="Browse", command=self.button_browse_callback).pack()
self.message = tk.Label(self.firstframe, textvariable=self.statusText)
self.message.pack()
def button_browse_callback(self):
self.filename = tkFileDialog.askopenfilename()
self.first_frame_entry.delete(0, tk.END)
self.first_frame_entry.insert(0, self.filename)
def button_go_callback(self):
self.data = pd.read_excel(self.filename)
if __name__ == "__main__":
root = tk.Tk()
root.title("TEST")
my_gui = gui(root)
root.mainloop()
在我看来,您将数据和GUI捆绑得太多了。如果将来你想展示其他东西呢?我将使用更通用的方法:我将创建一个
DataProvider
类,该类将为您读取并返回数据:
数据提供程序
import pandas as pd
class DataProvider(object):
def __init__(self):
self._excel = {}
self._binary = {}
def readExcel(self, filename):
self._excel[filename] = pd.read_excel(filename)
def excel(self):
return self._excel
def readBinary(self, filename):
self._binary[filename] = open(filename,"rb").read()
def binary(self):
return self._binary
使用该类,您可以获得可以在GUI中显示的数据:
gui.py
from Tkinter import *
from DataProvider import *
import binascii
#example data
dp = DataProvider()
dp.readExcel("file.xlsx")
dp.readBinary("img.png")
root = Tk()
frame = Frame(root)
frame.pack()
bottomframe = Frame(root)
bottomframe.pack( side = BOTTOM )
w = Label(bottomframe, text=dp.excel()["file.xlsx"])
w.pack()
w = Label(bottomframe, text=binascii.hexlify(dp.binary()["img.png"][:5]))
w.pack()
root.mainloop()
如果一切正常,您应该有一个小GUI,显示Excel文件的内容和图像的前几个字节
让我知道,如果一切顺利或如果你需要更多的信息
谢谢您可以在
gui中启动数据
、列
和文件名
,并使用主框架
和第一框架
中的控制器
对象访问它们,例如:self.controller.filename='/some/file'
。或者,您可以构建一个小类,例如:my\u data
,其中包含3个类变量:data
、strat\u列
和filename
。然后您可以直接访问它们(无需创建新实例),例如:my_data.filename='/some/file'
,正如t.m.adam所提到的,您应该将两个变量data
和filename
移动到\u init\u
部分中。不要在类中使用全局。相反,将变量放入带有self.
前缀的类属性中。这将消除全球合作的必要性。类属性可以从类或其方法中的任何位置访问。谢谢!我按照@t.m.adam的建议创建了my_数据类,只要我将它们创建为sf=second_frame(self.parent,self),它就可以成功地在窗口之间传递信息;sf.网格(行=0,列=0,sticky=“nsew”);sf.tkraise()
。如果我尝试以self.controller.show\u frame(second\u frame)
的方式执行此操作,窗口将呈现变量的初始值,并且它们相应的类将不会再次“实例化”(不确定这是否是正确的单词)。关于为什么会发生这种情况有什么线索吗?谢谢为什么不尝试使用实例变量呢?我只提到了类变量方法作为globals的替代方法。我只是想指出,self.app_data={data=[],filename=“”}
将不起作用,因为它不是dict的有效格式。它需要类似于self.app_data={data:[],filename:“}
字典使用冒号,左边是键,右边是数据。谢谢!到目前为止,它正在发挥作用。当我展开程序时,在一个回调
方法中,我想在对数据进行转换后打开一个窗口第二帧
<代码>定义第二帧(self):self.secondframe=tk.frame(self.master)self.secondframe.grid(行=0,列=0,sticky=“nsew”)
from Tkinter import *
from DataProvider import *
import binascii
#example data
dp = DataProvider()
dp.readExcel("file.xlsx")
dp.readBinary("img.png")
root = Tk()
frame = Frame(root)
frame.pack()
bottomframe = Frame(root)
bottomframe.pack( side = BOTTOM )
w = Label(bottomframe, text=dp.excel()["file.xlsx"])
w.pack()
w = Label(bottomframe, text=binascii.hexlify(dp.binary()["img.png"][:5]))
w.pack()
root.mainloop()