python-3.xtkinteropenfiledialog

How can i open multiple files using tkinter OpenFileDialog?


i'm struggling with my program. I'm using tkinter OpenFileDialog (AskOpenFile) to open .txt file and ploting it. I need open more than one file at once and work with these files. My function AskOpenFile works well, but i want to use AskOpenFiles function, so i can work with many files at once, resp. create one plot out of more .txt files. Can anybody please help me? I get following message: Exception in Tkinter callback Traceback (most recent call last):

File "C:\Users\m.salik\AppData\Local\Programs\Python\Python312\Lib\tkinter\__init__.py", line 1962, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\m.salik\PycharmProjects\pythonProject\Polycontact\GUI_main.py", line 95, in select_file
    Analysis_frame.analysis(self)
  File "C:\Users\m.salik\PycharmProjects\pythonProject\Polycontact\GUI_main.py", line 19, in analysis
    data = pd.read_csv(data_raw, sep=";", skiprows=[0], low_memory=False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\m.salik\PycharmProjects\pythonProject\.venv\Lib\site-packages\pandas\io\parsers\readers.py", line 948, in read_csv
    return _read(filepath_or_buffer, kwds)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\m.salik\PycharmProjects\pythonProject\.venv\Lib\site-packages\pandas\io\parsers\readers.py", line 611, in _read
    parser = TextFileReader(filepath_or_buffer, **kwds)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\m.salik\PycharmProjects\pythonProject\.venv\Lib\site-packages\pandas\io\parsers\readers.py", line 1448, in __init__
    self._engine = self._make_engine(f, self.engine)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\m.salik\PycharmProjects\pythonProject\.venv\Lib\site-packages\pandas\io\parsers\readers.py", line 1720, in _make_engine
    raise ValueError(msg)
ValueError: Invalid file path or buffer object type: <class 'list'>

My code:

````   import tkinter as tk
   from tkinter import *
   from tkinter import messagebox
   from tkinter import filedialog as fd
   import pandas as pd
   import matplotlib.pyplot as plt

class Analysis_frame(tk.LabelFrame):
    def __init__(self, container):
        super().__init__(container)
        self.configure(text="Analysis", relief="ridge", font=("Verdana", 10), borderwidth=1, border=3)

        self.__create_widgets()

    def analysis(self):
        global Test_Start, Test_End, Duration, Duration_End, Temperature, MinTemp, MaxTemp, Start_Time,  End_Time
        global Current_IN_max, Current_IN_min, MaxValue_I_IN_max, MinValue_I_IN_max
        global data
        data = pd.read_csv(data_raw, sep=";", skiprows=[0], low_memory=False)
        # print(data_raw.columns)
        Test_Start = data.iloc[:, 0].values[0]
        Test_End = data.iloc[:, 0].values[-1]
        Start_Time = data.iloc[:, 1].values[0]
        End_Time = data.iloc[:, 1].values[-1]
        Duration = data.iloc[:, 3]
        Duration_End = data.iloc[:, 3].values[-1]
        Temperature = data.iloc[:, 4]
        MinTemp = Temperature.min()
        MaxTemp = Temperature.max()
        Humidity = data.iloc[:, 5]
        Supply_IN_max = data.iloc[:, 8]
        Supply_IN_min = data.iloc[:, 9]
        Current_IN_max = data.iloc[:, 12]
        Current_IN_min = data.iloc[:, 13]
        Voltage_OUT_max = data.iloc[:, 20]
        pd.set_option("display.max_rows", 20000)

#####   Temperature diagram    #####
    def __create_plot_temp(self):
        fig, ax1 = plt.subplots(facecolor="white", figsize=(17,10), dpi=100)
        ax1.set_title("Temperature diagram", font="Verdana", fontsize=11)
        ax1.set_facecolor("whitesmoke")
        plt.subplots_adjust(left=0.05, right=0.95, top=0.98, bottom=0.15)


        ax1.set_ylim(bottom=0, top=1)
        ax1.set_xlim(0,Duration_End)
        ax2 = ax1.twinx()
        ax2.set_ylim(bottom=MinTemp-10, top=MaxTemp+15)
        ax1.minorticks_on()
        plt.minorticks_on()
        ax1.set_ylabel("Current [A]").set_fontsize(10)
        ax2.set_ylabel("Temperature [°C]").set_fontsize(10)
        ax1.set_xlabel("Test duration [h]").set_fontsize(10)

        sample1, = ax1.plot(Duration, Current_IN_max, label="IGSS1", color="green", marker=".", markersize=2,
                            mouseover=True, linewidth=0.2, visible=True, alpha=0.5)
        sample2, = ax1.plot(Duration, Current_IN_min, label="IGSS2", color="orange", marker=".", markersize=2,
                            mouseover=True, linewidth=0.2, visible=True, alpha=0.5)
        temp, = ax2.plot(Duration, Temperature, label="Temperature", color="red", linewidth=0.8, alpha=0.7)


        plt.figtext(0.05,0.10,f"Test start: {Test_Start +" "+ Start_Time}", fontfamily="verdana", fontsize=10)
        plt.figtext(0.85,0.10, f"Test end: {Test_End +" "+ End_Time}", fontfamily="verdana", fontsize=10)
        plt.show()

    def __create_widgets(self):
        tk.Button(self, text="Temperature" "\n" "diagram", font=("Verdana", 8), command=self.__create_plot_temp).grid(
            column=0, row=0, sticky=tk.S)


        for widget in self.winfo_children():        # spacings for all widgets within the frame
            widget.grid(padx=5, pady=5)

class Button_frame(tk.LabelFrame):                  # create frame
    def __init__(self, container):
        super().__init__(container)
        self.configure(text="Files", font=("Verdana", 10), relief="ridge", borderwidth=1, border=3)

        self.__create_widgets()

    def select_file(self):                          # function for opening the csv files, command for open_file button
        global data_raw
        filetypes = (
            ("textfiles", ".txt"),
        )
        file = fd.askopenfiles(
            filetypes=filetypes,
            mode="r+",
            title="Open file",
            initialdir="/",
        )
        if file is not None:
            data_raw = file
            Analysis_frame.analysis(self)
            messagebox.showinfo("Data", "Data loaded succesfull!")
        else:
            messagebox.showinfo("Data", "No data loaded!")

    def __create_widgets(self):                         # create buttons within the frame
        open_file = tk.Button(self, text="Open csv file", font=("Verdana", 8), command=self.select_file)
        open_file.grid(column=0, row=0, sticky=N)

        exit = tk.Button(self, text="Close", font=("Verdana", 8), command=quit)
        exit.grid(column=1, row=0, sticky=N)

        for widget in self.winfo_children():        # spacings for all widgets within the frame
            widget.grid(padx=5, pady=5)

class Main_window(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Analysis")
        self.geometry("600x500")
        #self.attributes("-toolwindow", True)

        self.__create_widgets()

    def __create_widgets(self):

        analysis = Analysis_frame(self)
        analysis.grid(column=0, row=2, sticky=NW)

        buttons = Button_frame(self)
        buttons.grid(column=0, row=1, sticky=NW)

        for widget in self.winfo_children():        # spacings for all widgets within the main window
            widget.grid(padx=5, pady=5)

if __name__ == "__main__":
    app = Main_window()
    app.mainloop()```

Solution

  • The issue is here:

    file = fd.askopenfiles(
                filetypes=filetypes,
                mode="r+",
                title="Open file",
                initialdir="/",
            )
    

    askopenfiles returns a list of files (even if you only select one file), so that's why you're getting the error

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

    (you're passing a list to your analysis method, but pd.read_csv expects a file path or buffer)

    If you want to open and process the selected files one after another, you need to iterate over the list returned by askopenfiles like so

    def select_file(self):
        global data_raw
        filetypes = (("textfiles", ".txt"),)
        files = fd.askopenfiles(  # changed this to 'files' since it's a list of multiple files
            filetypes=filetypes,
            mode="r+",
            title="Open file",
            initialdir="/",
        )
        for file in files:  # iterate over the list of files
             if file is not None:
                data_raw = file
                Analysis_frame.analysis(self)
                messagebox.showinfo("Data", "Data loaded succesfull!")
            else:
                messagebox.showinfo("Data", "No data loaded!")
    

    Also, as a recommendation, I'd be careful with all those globals. It's far more likely that you're better off using class attributes, e.g. self.data_raw if you need to access variable values from different methods within your classes or if you're passing values between classes.