0

Good afternoon all,

Rather than using the command line or hard coding a file name in Python, I'd like to have a separate class that I can use in any other Python program that allows a file to be chosen with the Tkinter file dialog box. Here is what I have so far:

import Tkinter as tk
import tkFileDialog
import tkFont

###################################################################################################
class MyFileDialog(tk.Frame):

    # class level variables #######################################################################
    my_form = tk.Tk()                   # declare form
    my_button = tk.Button(my_form)      # declare button
    chosen_file = ""

    # constructor #################################################################################
    def __init__(self):
                                            # create button event
        self.my_button.configure(text = "press here", command = self.on_my_button_click)
        self.my_button.pack()               # add button to form

        # make the button font bigger so I don't have to squint
        updated_font = tkFont.Font(font = self.my_button['font'])
        updated_font['size'] = int(float(updated_font['size']) * 1.6)
        self.my_button.configure(font = updated_font)

    ###############################################################################################
    def on_my_button_click(self):
        self.chosen_file = tkFileDialog.askopenfilename()       # get chosen file name
        return

    ###############################################################################################
    def get_chosen_file(self):
        return self.chosen_file                 # return chosen file name

###################################################################################################
if __name__ == "__main__":
    my_file_dialog = MyFileDialog()

    my_file_dialog.my_form.mainloop()

    # I need to get the chosen file here, when the file is chosen, NOT when the window is closed !!
    # please help !!

    print my_file_dialog.get_chosen_file()    # this does not print until the window is closed !!!

The current problem with this code is (as the comment towards the end indicates) that the test code does not receive the chosen file name until the window is closed, however I need the test code to receive the file name when the file is chosen.

I should mention that in production use the test code would be in a different file altogether that would import and instantiate this class, therefore the modularity of the class should be maintained. Anybody out there have a solution? Please help !

cdahms
  • 3,402
  • 10
  • 49
  • 75

1 Answers1

0

In a nutshell, mainloop() blocks all other calls until the window is destroyed (more on this: Tkinter understanding mainloop). This is why your print statement waits until the window is closed.

if __name__ == "__main__":

    my_file_dialog = MyFileDialog()
    my_file_dialog.my_form.mainloop()

    # this line will not run until the mainloop() is complete.
    print my_file_dialog.get_chosen_file()

One simple solution might be to change your chosen_file variable to a tk.StringVar(), which you could then attach a callback method to.

import Tkinter as tk
import tkFileDialog
import tkFont

class MyFileDialog(tk.Frame):

    def __init__(self, master):

        tk.Frame.__init__(self, master)

        self.master = master

        # class variables
        self.chosen_file = tk.StringVar()

        # ui elements
        self.my_button = tk.Button(self)

        # configure button
        self.my_button.configure(\
            text = "press here", command = self.on_my_button_click)

        # make button font bigger
        # ...

        # add button to frame
        self.my_button.pack()

        # attach a callback to chosen_file with trace
        self.chosen_file.trace("w", self.callback)

    # execute this method whenever chosen_file gets written
    def callback(self, *args):

        print("MyFileDialog: chosen_file is %s" % self.chosen_file.get())

    def on_my_button_click(self):

        f = tkFileDialog.askopenfilename()

        # set chosen_file here to trigger callback (wherever it is)
        self.chosen_file.set(f)


if __name__ == "__main__":

    root = tk.Tk()
    app = MyFileDialog(root).pack()
    root.mainloop()

Note that you don't have to place the callback method in your MyFileDialog class: you could just as easily create the callback in another class once you have instantiated a MyFileDialog object.

class MyApp(tk.Frame):

    def __init__(self, master):

        tk.Frame.__init__(self, master)

        self.master = master

        self.mfd = MyFileDialog(master)
        self.mfd.pack()

        # attach callback to chosen_file from MyFileDialog
        self.mfd.chosen_file.trace("w", self.callback)

    def callback(self, *args):

        print("MyApp: chosen_file is %s" % self.mfd.chosen_file.get())
Community
  • 1
  • 1
chdorr
  • 288
  • 3
  • 8