0

I'm making a simple gui that has three buttons:

  1. select a file path to file
  2. select a file path to a separate file
  3. run a function, passing the file paths selected from buttons 1 and 2.

I have this so far:

import tkinter as tk
from tkinter.filedialog import askopenfilename
import pathlib

root = tk.Tk()

class App:

    def __init__(self, main):
        myFrame = tk.Frame(main)
        myFrame.pack()
        self.btn_1= tk.Button(main, text="Choose file", command=self.choose_data).pack()
        self.btn_2= tk.Button(main, text="Choose file", command=self.choose_data).pack()
        self.run_button = tk.Button(main, text="Run", command=lambda: self.run_analysis(self.btn_1, self.btn_2)).pack()

    def choose_data(self):
        global data_file
        data_file = pathlib.Path(askopenfilename(filetypes=[("Excel files", ".xls .xlsx")]))
        return data_file

    def run_analysis(self,a,b):
        do_something(a,b)

a = App(root)

root.mainloop()

The first buttons work as expected. And I can see the filepath is stored in a variable that I can print from the choose_data function

When I try to use these attributes from the run function I get this error

ValueError: Invalid file path or buffer object type: <class 'NoneType'>

When I print the attributes from a function within the init method, they are None. So from that and the error, I know thats where I'm going wrong.

I think its because the init method is beng called before the attributes are defined? And so when I call the run_analysis method it does not work?

I tried making data_file a global variable so that it would allow me to use it anywhere, but thats not working for me.

Any help would be appreciated

Thanks

3
  • You need to look up how functions return a value. Commented Dec 2, 2020 at 21:04
  • Thanks for response. I accidentally omitted the return statement in the choose data function. The same error persists. I have updated the question. Commented Dec 2, 2020 at 21:17
  • You haven't shown how you're actually trying to access that variable, or even the full error message. Without a minimal reproducible example it can be difficult to figure out what you're doing and what the solution would be. But it's safe to say that adding the return keyword isn't a magic fix; you need to actually use functions correctly. Also, your self.btn_1 and self.btn_2 are saved as just None as well. Commented Dec 2, 2020 at 21:28

1 Answer 1

2

Ok, the reason the attributes of your function are printing None is because they are None. When you define self.btn_1= tk.Button(main, text="Choose file", command=self.choose_data).pack() the variable self.btn_1 receive the return of the method pack. Since pack does not return anything, it is None. Then you pass self.btn_1 and self.btn_2 (both are None) to the method run_analysis.

First of all, if you want btn1 to be the button instead of Noneyou have to do it with two commands:

self.btn_1 = tk.Button(main, text="Choose file", command=self.choose_data)
self.btn_1.pack()

but be aware: it is the button and not the the result of the callback function it calls.

Second: if btn1 and btn2 are attributes of your object (they are saved in self) and the run_analysis is a non-static method (it has access to self) there are no necessity of passing the buttons as attributes of your method.

Third, I believe what you want to do is to read two data files and pass the data files to the do_something. In this case, the best thing to do is telling the choose_data which button is being pressed and saving the files in different variables according to it. You can do it like this:

class App:

    def __init__(self, main):
        myFrame = tk.Frame(main)
        myFrame.pack()
        self data_files = [None,None]
        self.btn_1= tk.Button(main, text="Choose file", command=lambda: self.choose_data(0)).pack()
        self.btn_2= tk.Button(main, text="Choose file", command=lambda: self.choose_data(1)).pack()
        self.run_button = tk.Button(main, text="Run", command=self.run_analysis).pack()

    def choose_data(self, index):
        self.data_files[index] = pathlib.Path(askopenfilename(filetypes=[("Excel files", ".xls .xlsx")]))

    def run_analysis(self):
        do_something(*self.data_files)
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, you have solved my problem! I will be researching into what you've written in detail. Thanks for sharing

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.