2

I am trying to build a Tkinter UI with a button where the user chooses a main file and based on information in that file, the script would get some information about some secondary files. Upon choosing the main file, I am trying to use the file path of the main file to get the file path of the secondary files. However, because I am calling a function "open_main_file()" twice, I get two dialog boxes where the user will have to choose the main file twice instead of once. How can I circumvent that?

My code looks like this:

import pandas as pd
from tkinter import *
from os.path import dirname
from tkinter import filedialog

root = Tk()

def open_main_file():
    return filedialog.askopenfilename()

def parse_main_file():
    return pd.read_parquet(open_main_file(), engine='pyarrow')

def get_some_ids():
    return parse_main_file()['some_ids'].iloc[0]

def get_list_of_other_files():
    other_files_path = dirname(dirname(dirname(open_main_file())))
    list_of_other_files = []
    for f in get_some_ids():
        list_of_other_files.append(glob.glob(other_files_path + '/identifier=' + str(f) + '/*'))
    return map(''.join, list_of_other_files)

myButton = Button(root, text='Open File...', command=get_list_of_other_files).pack()
root.mainloop()
bloo
  • 306
  • 4
  • 13
  • 1
    @Wanja that is incorrect. You need to import Filedialog and some others like ttk seperate if you want them to use. – Thingamabobs Jul 21 '20 at 17:10
  • When I do that, I get, "NameError: name 'filedialog' is not defined", as if it's not getting imported – bloo Jul 21 '20 at 17:11
  • 1
    @Atlas435 Oh, sorry, you're right. My bad – Wanja Wischmeier Jul 21 '20 at 17:27
  • Just a heads up: `myButton = Button(root, text='Open File...', command=get_list_of_other_files).pack()` will set `myButton` to `None` as the method `pack` doesn't return anything. I'd suggest removing the method and call it afterwards with `myButton.pack()` to avoid future bugs – Ted Klein Bergman Jul 21 '20 at 18:02
  • Also, try to be consistent with your variable names and change `myButton` to `my_button` – Ted Klein Bergman Jul 21 '20 at 18:03
  • @TedKleinBergman Can you please provide a link that elaborates more on the .pack() None type? I am new to tkinter and I was only going along with online documentation and tutorials such as https://www.tutorialspoint.com/python/tk_pack.htm where nothing like that is mentioned. – bloo Jul 21 '20 at 18:08
  • @bloo As you see in the link you've provided, they don't define their buttons and call `pack` on the same line (as you do). That's because `pack` doesn't return anything at all. It a function that does stuff, not give you stuff. Instead, they first create a button (which they store in a variable) and **then** call `pack` on the next line. – Ted Klein Bergman Jul 21 '20 at 18:11
  • @TedKleinBergman You are right, I didn't realise I was doing that. I probably picked that habit up from an online video tutorial. It actually works in one line so I never understood the necessity of having it in separate lines. Thank you for the heads up. – bloo Jul 21 '20 at 18:18

1 Answers1

0

try not to use a function in other functions. In this case, you are calling a function one time in another one, and one time in the main code. That is why you get file dialog 2 times. Use the returned item and give it to the next function. You can code like this:

import pandas as pd
from tkinter import *
from os.path import dirname
from tkinter import filedialog

root = Tk()

def open_main_file(): # This function is not necessary
    return filedialog.askopenfilename()

def parse_main_file(main_file_path):
    return pd.read_parquet(main_file_path, engine='pyarrow')

def get_some_ids(main_file_path):
    return parse_main_file(main_file_path)['some_ids'].iloc[0]

def get_list_of_other_files():
    main_file_path = filedialog.askopenfilename()
    other_files_path = dirname(dirname(dirname(main_file_path)))
    list_of_other_files = []
    for f in get_some_ids(main_file_path):
        list_of_other_files.append(glob.glob(other_files_path + '/identifier=' + str(f) + '/*'))
    return map(''.join, list_of_constituent_files)

myButton = Button(root, text='Open File...', command=get_list_of_other_files).pack()
root.mainloop()
FarZad
  • 463
  • 3
  • 19
  • That worked. I understand your point in your answer but can you please guide me to a link where I can understand why it works like that? I think I am missing something basic in terms of how arguments in functions work that after hours of going through files and structures I cannot see clearly. – bloo Jul 21 '20 at 17:56
  • @bloo check this link: https://stackoverflow.com/questions/16501/what-is-a-lambda-function/62742314#62742314 – Thingamabobs Jul 21 '20 at 17:59
  • @bloo I just added some more explanations. see if it is helpful. and you can see here the user guide of tkinter.filedialog: https://docs.python.org/3.9/library/dialog.html#module-tkinter.filedialog – FarZad Jul 21 '20 at 17:59
  • 1
    @FarZadAsG Thank you. Atlas435 In your example code the mistake is "def get_list_of_other_files(): file = open_main_file()" while FarZadAsG has "def get_list_of_other_files():main_file_path = filedialog.askopenfilename()" which is pretty much the mistake I was making of using a function as a value for a variable. – bloo Jul 21 '20 at 18:01
  • 1
    I think I know now what the issue is. @FarZadAsG in your working example above, turns out that def open_main_file() is not even necessary. All it had to be done is just for filedialog.askopenfilename() to be assigned to a variable under the right function. – bloo Jul 21 '20 at 18:13
  • 1
    @bloo yes you are right. that is not necessary. I just wanted to keep the whole structure of your code. as you can see I even did not use that function later in the code. and just typed: `main_file_path = filedialog.askopenfilename()` – FarZad Jul 21 '20 at 18:24