Python / Tkinter:在类内部创建可调用的框架

时间:2018-12-24 13:17:57

标签: python pandas tkinter

我正在尝试使用Tkinter和另一个名为pandastable的模块来创建用户界面。我通过类来调用UI,因为在UI处于活动状态时,它可使我的所有子功能保持活动状态。

这是我被困的地方。在编写此代码的过程中,有必要根据用户输入重新创建熊猫数据框,然后刷新UI以在屏幕上显示新数据框。因此,我需要做的是使用该类刷新它,但是我不知道如何使它工作。这是完整的代码。刷新在函数“ refresh_df”下进行。

import pandas as pd
import numpy as np
from tkinter import *
from tkinter import ttk
from pandastable import Table, TableModel

#Create the frame class and call my functions from inside the class

class UserInterface(Table):
    # Launch the df in a pandastable frame

    def handleCellEntry(self, row, col):
        super().handleCellEntry(row, col)
        print('changed:', row, col, "(TODO: update database)")
        return

    def refresh_df(self, df):
        Frame.__init__(self)
        f = Frame(mainframe)
        f.grid(column=0, row=1, sticky=(E, W))
        screen_width = f.winfo_screenwidth() * 0.8
        screen_height = f.winfo_screenheight() * 0.7
        self.table = pt = Table(f, dataframe=df, height = screen_height, width = screen_width)
        pt.show()
        return

    def change_df(self, input_val):
        #Responds to button
        ui_df['Test col'] = input_val
        self.refresh_df(df=ui_df)

    def change_df_combo(self, event):
        #Responds to combobox, supposed to filter by 'Sec_type'
        combo_selection = str(combo_box.get())
        ui_df = pos_df[pos_df['Sec_type'] == combo_selection]
        ui_df['Test col combo'] = combo_selection
        self.refresh_df(df=ui_df)

pos_data = {'Location' : ['Denver', 'Boulder', 'Phoenix', 'Reno', 'Portland',
    'Eugene', 'San Francisco'], 'Sec_type' : ['mbus', 'mbus', 'vmus', 'caus',
    'vmus', 'mbus', 'mbus'], 'Rando_num': [18, 5, 34, 11, 72, 42, 9]}
pos_df = pd.DataFrame(data = pos_data)

ui_df = pos_df

#Launch Tkinter basics
root = Tk()
root.title("S test...")

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

f = Frame(mainframe)
f.grid(column=0, row=1, sticky=(E, W))
screen_width = f.winfo_screenwidth() * 0.8
screen_height = f.winfo_screenheight() * 0.7

ui = UserInterface(f, dataframe=pos_df, height = screen_height, width = screen_width)

#'Test' button, adds a column
col_val_input = 'It worked!'
test_button = ttk.Button(mainframe, text = 'Test', command= lambda: ui.change_df(input_val=col_val_input))
test_button.grid(column=0, row=0, sticky=(W))

#Combobox to filter df
combo_choices = ['mbus', 'vmus', 'caus']
choice = StringVar()
combo_box = ttk.Combobox(mainframe, textvariable=choice)
combo_box['values'] = combo_choices
combo_box.grid(column=1, row=0, sticky=(W))
combo_box.bind('<<ComboboxSelected>>', ui.change_df_combo)

ui.show()

root.mainloop()

关键是这两行:

        self.table = pt = Table(f, dataframe=df, height = screen_height, width = screen_width)
        pt.show()

这些调用数据框很好,但是他们不是从类内部完成的,所以我没有获得所有功能。在我看来,这样的事情应该起作用:

self.table = Table(f, dataframe=df, height = screen_height, width = screen_width)
self.show()

但事实并非如此。同样,我在这里使用pandastable,但是我认为实际的关键问题是我没有在类中使用正确的术语来使所有这些工作。有什么想法吗?

1 个答案:

答案 0 :(得分:0)

好的熊猫书呆子...我认为这是处理此问题的正确方法。 (仅显示课程)。关键是要使用updateModel / TableModel功能。

我通过更改DataFrame然后更新TableModel来使用它,但是IIUC TableModel实际上是它自己的df,因此如果对您有用,您可以直接更新它而不会影响原始df。无论如何,我对原始代码的问题是,一旦触发了ComboBox事件,我就有效地创建了一个新的df,因此pandastable不知道该怎么做。您可以操纵DataFrame并只调用“ redraw()”,效果很好,但是如果重构df,它就会崩溃。

代码如下:

import pandas as pd
import numpy as np
from tkinter import *
from tkinter import ttk
from pandastable import Table, TableModel

#Create the frame class and call my functions from inside the class

class UserInterface(Table):
    # Launch the df in a pandastable frame

    def handleCellEntry(self, row, col):
        super().handleCellEntry(row, col)
        print('changed:', row, col, "(TODO: update database)")
        return    

    def change_df(self, input_val):
        #Responds to button
        ui_df['Test col'] = input_val
        self.updateModel(TableModel(ui_df))
        self.redraw()

    def change_df_combo(self, event):
        #Responds to combobox, filter by 'Sec_type'
        combo_selection = str(combo_box.get())
        ui_df = pos_df[pos_df['Sec_type'] == combo_selection]
        ui_df['Test col combo'] = combo_selection
        self.updateModel(TableModel(ui_df))
        self.redraw()

pos_data = {'Location' : ['Denver', 'Boulder', 'Phoenix', 'Reno', 'Portland',
    'Eugene', 'San Francisco'], 'Sec_type' : ['mbus', 'mbus', 'vmus', 'caus',
    'vmus', 'mbus', 'mbus'], 'Rando_num': [18, 5, 34, 11, 72, 42, 9]}
pos_df = pd.DataFrame(data = pos_data)

ui_df = pos_df

#Launch Tkinter basics
root = Tk()
root.title("S test...")

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

f = Frame(mainframe)
f.grid(column=0, row=1, sticky=(E, W))
screen_width = f.winfo_screenwidth() * 0.8
screen_height = f.winfo_screenheight() * 0.7

ui = UserInterface(f, dataframe=pos_df, height = screen_height, width = screen_width)

#'Test' button, adds a column
col_val_input = 'It worked!'
test_button = ttk.Button(mainframe, text = 'Test', command= lambda: ui.change_df(input_val=col_val_input))
test_button.grid(column=0, row=0, sticky=(W))

#Combobox to filter df
combo_choices = ['mbus', 'vmus', 'caus']
choice = StringVar()
combo_box = ttk.Combobox(mainframe, textvariable=choice)
combo_box['values'] = combo_choices
combo_box.grid(column=1, row=0, sticky=(W))
combo_box.bind('<<ComboboxSelected>>', ui.change_df_combo)

ui.show()

root.mainloop()