KBriggs
KBriggs

Reputation: 1412

Proper way to pass variables within a tkinter GUI

I am trying to write a simple GUI to plot data from a csv file, which I read into a pandas DataFrame. I am completely new to GUI programming, and I am having a hard time getting my head around event-driven setups like Tkinter.

As a simple exercise, I want to set up a couple of buttons, one to open a file and read in the DataFrame, and another to print out the resulting DataFrame. My first naive attempt didn't work:

import pandas as pd
import tkFileDialog
import Tkinter as tk

def open_events_db():
    file_path_string = tkFileDialog.askopenfilename()
    eventsdb = pd.read_csv(file_path_string,encoding='utf-8')
    return eventsdb

def print_events_db(eventsdb):
    print eventsdb

def main():
    root=tk.Tk()
    eventsdb = tk.Button(text='File Open', command=open_events_db).pack(fill=tk.X)
    tk.Button(text='Print DB', command=lambda:print_events_db(eventsdb)).pack(fill=tk.X)
    tk.mainloop()


if __name__=="__main__":
    main()

I can read in the file fine, and open it, but in hindsight obviously I can't return eventsdb from the file open button and have it as an argument to the print button.

I don't think it's unreasonable to have buttons that operate on that DB, though, so what is the proper way to pass variables around within the GUI?

Upvotes: 2

Views: 4916

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 385970

Functions called from buttons and event handers don't return their data. Instead, they must set global variables or class attributes.

def open_events_db():
    global eventsdb
    file_path_string = tkFileDialog.askopenfilename()
    eventsdb = pd.read_csv(file_path_string,encoding='utf-8')

def print_events_db():
    global eventsdb
    print eventsdb

...
tk.Button(text='Print DB', command=print_events_db).pack(fill=tk.X)

It's generally considered poor programming to rely on global variables. Since python is an object oriented language, it makes sense to write your app as a class. You would then use instance attributes rather than global variables.

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        open_button = tk.Button(text='File Open', command=self.open_events_db)
        print_button = tk.Button(text='Print DB', command=self.print_events_db)

        open_button.pack(fill=tk.X)
        print_button.pack(fill=tk.X)

    def open_events_db(self):
        file_path_string = tkFileDialog.askopenfilename()
        self.eventsdb = pd.read_csv(file_path_string,encoding='utf-8')

    def print_events_db():
        print self.eventsdb

def main():
    root=tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()

if __name__=="__main__":
    main()

Upvotes: 5

Related Questions