python python-2.7 class oop tkinter

python - Pase el marco de datos a través de las clases Tkinter



tkinter python 2.7 install (2)

Estoy usando Python 2.7 y Tkinter. Soy casi nuevo en los programas orientados a objetos. Tengo un programa largo con muchas ventanas Tkinter y en algún momento le pido al usuario que cargue un archivo de Excel que leí con Pandas, y quiero usar y actualizar de forma permanente ese valor (de una variable de datos ). La forma en que lo estoy haciendo ahora es con variables globales, pero sé que es peligroso, ineficiente y nada elegante.

A pesar de que podría hacer controller.show_frame (nombre de cuadro) dada la forma en que se construyó mi clase de gui, terminé construyendo algunos de los marcos solo para que la variable de datos se actualice.

Leí e intenté algunas respuestas en Stack Overflow, pero es posible que las haya implementado mal:

  • Intenté crear un diccionario dentro de la clase gui, algo así como self.app_data = {data=[],filename=""} y actualizarlo desde otras ventanas, la cosa aquí es que creo que la clase gui solo se crea una instancia una vez y tipo de crea todas las otras clases de ventana por lo que esto no funcionó. Tal vez hice algo mal allí. (No se muestra en el código).
  • Intenté hacer algo como se sugirió here pero simplemente no pude hacer que funcionara.

El marco principal es una especie de paso intermedio que necesito para otros propósitos; El siguiente código es una simplificación de mi programa.

Sé que este es un código de pesadilla horrible! Gracias :)

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")


Hay algunas cosas que están causando problemas con su programa para que no se ejecute correctamente.

Lo primero que noté es el uso de variables globales. Esto se puede evitar con el uso de atributos de clase.

Para las 2 variables que tiene justo debajo de la línea de la línea class gui(tk.Tk): necesita moverlas a la sección __init__ para que esas variables puedan ser instanciadas. También debe convertirlos en atributos de clase para que otros métodos o incluso otras clases puedan interactuar con ellos. Podemos hacer esto agregando el self. prefijo a los nombres de las variables.

Algo así como el siguiente:

self.data = pd.DataFrame([]) self.filename = ""

Para acceder a los métodos / atributos de la clase gui , debe pasar el objeto de la clase gui a las otras clases que trabajan con ella.

statusText en su código no está definido, por lo que set() no funcionará aquí. Solo agrega self.statusText como un atributo de clase.

Algunos de sus widgets no necesitan ser asignados a un nombre de variable ya que no se les realiza ninguna edición. Por ejemplo:

label = tk.Label(self, text="Please load a file: ") label.pack()

Esto se puede cambiar simplemente a:

tk.Label(self, text="Please load a file: ").pack()

Esto ayudará a reducir la cantidad de código que está escribiendo y a mantener el espacio de nombres más limpio.

Hay algunas maneras de corregir todo esto, pero la forma más fácil es mover este código a una clase. No hay una buena razón para que el código que ha presentado tenga varios marcos separados de la clase de gui principal.

El código a continuación es una versión reescrita de su código que utiliza una clase para cumplir con lo que parece ser la tarea que su código está tratando de lograr y reducir la cantidad de código necesario en alrededor de 30 líneas. Estoy seguro de que puede ser refinado aún más, pero este ejemplo debería ser útil.

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()


en mi opinión, están atando demasiado los datos y la GUI. ¿Qué pasa si en el futuro quieres mostrar algo más? Usaría un enfoque más genérico: crearía una clase DataProvider que leería y devolvería los datos por usted:

Proveedor de datos

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

Utilizando esta clase, puede obtener los datos que puede presentar en su 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()

Si todo funciona bien, debe tener una pequeña GUI que muestre el contenido del archivo Excel y los primeros bytes de la imagen.

Déjame saber si todo funciona bien o si necesitas más información.

Gracias