Python 通过Tkinter类传递数据帧

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中

我正在使用Python2.7和Tkinter。我几乎不熟悉面向对象程序。我有一个包含许多Tkinter窗口的长程序,在某个时刻,我要求用户加载一个Excel文件,我用Pandas读取该文件,并希望永久使用和更新(数据变量的)该值。我现在的做法是使用全局变量,但我知道这是危险的、低效的,而且一点也不优雅

尽管我可以按照gui类的构建方式来构建controller.show_frame(framename),但我最终还是自己构建了一些框架,以便数据变量能够自我更新

我在Stack Overflow中阅读并尝试了一些答案,但可能实现错误:

  • 尝试在gui类中创建一个字典,比如
    self.app_data={data=[],filename=“”}
    并从其他窗口更新它,这里的问题是,我认为类gui只实例化一次,它创建了所有其他窗口类,所以这不起作用。也许我做错了什么。(代码上未显示)
  • 我试着按照建议做些什么,但就是做不到
主框架是某种中间步骤,我需要用于其他目的;下面的代码是我的程序的简化

我知道这是一个可怕的噩梦代码!谢谢:)


有一些事情导致程序无法正常运行

我注意到的第一件事是全局变量的使用。这可以通过使用类属性来避免

对于
类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()