通过Tkinter类传递数据帧

sar*_*gof 17 python oop class tkinter python-2.7

我使用的是Python 2.7和Tkinter.我几乎是面向对象程序的新手.我有一个很长的程序,有许多Tkinter窗口,在某些时候我要求用户加载我用Pandas读取的Excel文件,并希望永久使用和更新该值(数据变量).我现在这样做的方式是使用全局变量,但我知道它是危险的,效率低下而且根本不优雅.

尽管我可以根据构建我的gui类的方式执行controller.show_frame(framename),但我最终自己构建了一些框架,因此数据变量会自行更新.

我在Stack Overflow中阅读并尝试了一些答案但可能已经错误地实现了它们:

  • 尝试在gui类中创建一个字典,类似于self.app_data = {data=[],filename=""}从其他窗口更新它,这里的事情是我认为类gui只实例化了一次,它创建了所有其他窗口类,所以这不起作用.也许我在那里做错了什么.(未在代码上显示).
  • 试图按照这里的建议去做一些事情,但我可能无法让它发挥作用.

主框架是我需要用于其他目的的某种中间步骤; 以下代码是我的程序的简化.

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

import Tkinter as tk
import pandas as pd 
import tkFileDialog
import tkMessageBox
global data, strat_columns, filename
data = pd.DataFrame([])
strat_columns = []
filename = ""

class gui(tk.Tk):

    data = pd.DataFrame([])
    filename = ""
    def __init__(self):
        tk.Tk.__init__(self)
        container = tk.Frame(self)
        container.pack(side="top",fill="both",expand=True)
        self.frames = {}

        for F in (main_frame, first_frame):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(main_frame)

    def show_frame(self,sel_frame):
        frame = self.frames[sel_frame]
        frame.tkraise()

    def get_page(self, page_class):
        return self.frames[page_class]


class main_frame(tk.Frame):

    def __init__(self,parent,controller):

        tk.Frame.__init__(self,parent)
        self.parent = parent 
        self.controller = controller
        button_new = tk.Button(self,
                           text="New window",
                           command=lambda: self.button_new_callback())
        button_new.pack()

    def button_new_callback(self,*args,**kwargs):
        self.controller.show_frame(first_frame)


class first_frame(tk.Frame):

    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent)
        self.controller = controller
        self.parent = parent
        self.show_frame = controller.show_frame
        statusText.set("Press Browse button and browse for file, then press the Go button")
        label = tk.Label(self, text="Please load a file: ")
        label.pack()
        entry = tk.Entry(self, width=50)
        entry.pack()
        button_go = tk.Button(self,
                           text="Go",
                           command=lambda: self.button_go_callback(entry,statusText,message))
        button_browse = tk.Button(self,
                               text="Browse",
                               command=lambda: self.button_browse_callback(entry))
        button_go.pack()
        button_browse.pack()
        message = tk.Label(self, textvariable=statusText)
        message.pack()

    def button_browse_callback(self,entry):
        global filename
        filename = tkFileDialog.askopenfilename()
        entry.delete(0, tk.END)
        entry.insert(0, filename)

    def button_go_callback(self,entry,statusText,message):
        global data
        input_file = entry.get()
        data = pd.read_excel(filename)
        sf = second_frame(self.parent, self)
        sf.grid(row=0, column=0, sticky="nsew")
        sf.tkraise()

class second_frame(tk.Frame):
     pass

if __name__ == "__main__":

    my_gui = gui()
    my_gui.mainloop()
    my_gui.title("TEST")
Run Code Online (Sandbox Code Playgroud)

Mik*_*SMT 3

有一些因素会导致程序无法正常运行。

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

对于该行下方的 2 个变量,class gui(tk.Tk):您需要将它们移动到该__init__部分,以便可以实例化这些变量。此外,您还需要将它们放入类属性中,以便其他方法甚至其他类可以与它们交互。我们可以通过self.在变量名称中添加前缀来做到这一点。

像下面这样:

self.data = pd.DataFrame([])
self.filename = ""
Run Code Online (Sandbox Code Playgroud)

要访问类的方法/属性,gui您需要将该gui类的对象传递给使用它的其他类。

statusText您的代码中未定义,因此set()在这里不起作用。只需添加self.statusText为类属性即可。

您的某些小部件不需要分配给变量名称,因为没有对它们进行编辑。例如:

label = tk.Label(self, text="Please load a file: ")
label.pack()
Run Code Online (Sandbox Code Playgroud)

这可以简单地更改为:

tk.Label(self, text="Please load a file: ").pack()
Run Code Online (Sandbox Code Playgroud)

这将有助于减少您编写的代码量并保持名称空间更干净。

有几种方法可以纠正所有这些问题,但最简单的方法是将这些代码移到一个类中。您所提供的代码没有充分的理由将多个框架与主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()
Run Code Online (Sandbox Code Playgroud)