Python 3 - Tkinter button commands
Question: I am new to Tkinter and Python as well. I have three buttons with commands in
my Tkinter frame. Button 1 calls open_csv_dialog(), opens a file dialog box to
select a .csv file and returns the path. Button 2 calls
save_destination_folder(), opens a file dialog box to open the preferred
directory and return the path.

My problem is with Button 3. It calls modify_word_doc() which needs the
filepaths returned from button 1 and button 2.

I have tried;

    
    
    button3 = ttk.Button(root, text="Run", command=lambda: modify_word_doc(open_csv_dialog, save_destination_folder)).pack()
    

but that obviously just prompts the file dialog box to open again for both the
open_csv_dialog() and save_destination_folder() function which is undesired. I
would like to just use the file path that was already returned from these two
functions and pass it into modify_word_doc without being prompted by another
file dialog box. I have also tried to use `partial` but I'm either using it
wrong or it still has the same undesired consequences.

I have read the Tkinter docs about commands and searched SO for a possible
answer, so apologies if this has been answered before and I failed to find it.

    
    
    import tkinter as tk
    from tkinter import filedialog
    from tkinter import ttk
    import os
    import csv
    import docx
    from functools import partial
    
    
    root = tk.Tk()
    
    
    def open_csv_dialog():
        file_path = filedialog.askopenfilename(filetypes=(("Database files",
            "*.csv"),("All files", "*.*")))
        return file_path
    
    
    def save_destination_folder():
        file_path = filedialog.askdirectory()
        return file_path
    
    
    def modify_word_doc(data, location):
        #data = open_csv_dialog()
        #location = save_destination_folder()
        #long code.  takes .csv file path opens, reads and modifies word doc with 
        #the contents of the .csv, then saves the new word doc to the requested 
        #file path returned from save_destination_folder().
    
    
    label = ttk.Label(root, text="Step 1 - Choose CSV File.",
        font=LARGE_FONT)
    label.pack(pady=10, padx=10)
    button = ttk.Button(root, text="Choose CSV",
        command= open_csv_dialog).pack()
    label = ttk.Label(root,
        text="Step 2 - Choose destination folder for your letters.",
        font=LARGE_FONT)
    label.pack(pady=10, padx=10)
    button2 = ttk.Button(root, text="Choose Folder",
        command=save_destination_folder).pack()
    label = ttk.Label(root, text="Step 3 - Select Run.", font=LARGE_FONT)
    label.pack(pady=10, padx=10)
    button3 = ttk.Button(root, text="Run", 
        command=lambda: modify_word_doc(open_csv_dialog, save_destination_folder)).pack()
    
    
    root.mainloop()
    


Answer: This was probably just an error typing the question.... but for completeness
on this line

    
    
    button3 = ttk.Button(root, text="Run", command=lambda: modify_word_doc(open_csv_dialog, save_destination_folder).pack()
    

You're missing the closing parenthesis for `ttk.Button(*)*.pack()`

It should be (syntactically):

    
    
     button3 = ttk.Button(root, text="Run", command=lambda: modify_word_doc(open_csv_dialog, save_destination_folder)).pack()
    

Also, using `.pack()` returns `None` so setting a variable to a widget +
geometry manager method just sets that variable to nothing, instead of a
reference to the widget object.

So, if you actually need a reference to this widget you should actually do:

    
    
     button3 = ttk.Button(*)
     button3.pack()
    

If you don't need a reference then just don't assign anything and save
yourself some typing, since it's redundant.

For the actual question:

If I understand your question, you have two buttons which set the file path of
the .csv and the destination folder. However, since both your functions use
the dialog you are being prompted again even though the may have already been
chosen.

You could use globals and various other ways to do this, I'll set an attribute
on the base root window since i think this is easiest here...

In the below code what I did was simply set an attribute on the `root` window
if the `file_path` has been selected. You can check this with an `if`
statement.

Then on either I call `check_state` to see if the root window has both of
these attributes `getattr(object, string, default)` will return the attribute
or the default if the attribute does not exists. So, by setting the file_path
to the string, or None if the location was re-selected the state will always
be updated correctly.

You can clean this up some more. You could actually make both of those one
function etc if you really wanted to.

    
    
    import tkinter as tk
    from tkinter import filedialog, ttk
    
    def check_state():
    
        if getattr(root, 'csv_path', False) and getattr(root, 'dest_path', False):
            button3['state'] = 'normal'
        else:
            button3['state'] = 'disabled'
    
    def open_csv_dialog():
    
        file_path = filedialog.askopenfilename(
            filetypes=(("Database files", "*.csv"), ("All files", "*.*")))
        if file_path:
            root.csv_path = file_path
        else:
            root.csv_path = None
        check_state()
    
    def save_destination_folder():
    
        file_path = filedialog.askdirectory()
        if file_path:
            root.dest_path = file_path
        else:
            root.dest_path = None
        check_state()
    
    def modify_word_doc():
       print(root.csv_path, root.dest_path)
    
    root = tk.Tk()
    ttk.Label(root, text="Step 1 - Choose CSV File.",).pack(pady=10, padx=10)
    ttk.Button(root, text="Choose CSV", command= open_csv_dialog).pack()
    ttk.Label(root, text="Step 2 - Choose destination folder for your letters.").pack(pady=10, padx=10)
    ttk.Button(root, text="Choose Folder", command=save_destination_folder).pack()
    ttk.Label(root, text="Step 3 - Select Run.").pack(pady=10, padx=10)
    
    #We need a reference to the widget here, for the state func...
    
    button3 = ttk.Button(root, text="Run", state='disabled', command=modify_word_doc)
    button3.pack()
    root.mainloop()
    

