Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tkinter appears not to pass file extension to file writing correctly #129534

Open
elkym opened this issue Jan 31, 2025 · 1 comment
Open

Tkinter appears not to pass file extension to file writing correctly #129534

elkym opened this issue Jan 31, 2025 · 1 comment
Labels
stdlib Python modules in the Lib dir topic-tkinter type-bug An unexpected behavior, bug, or error

Comments

@elkym
Copy link

elkym commented Jan 31, 2025

Bug description:

Requirements: pandas, pyarrow.parquet, openpyxl

Testing this on Windows 11, Python 3.13, pandas 2.2.3

File selection dialog custom class has added method _get_file_type for tkinter's asksaveasfilename for diagnostic prints.

The tkinter file dialog class has an internal method _fixresult-- this will call other methods and pass along the correct file ending, but somehow, my script will not append it.

If the user selects an existing file that has a file extension the ending is included in the file name, and the script correctly writes a file.
If the user manually adds a correct file extension from the list, then python treats that as the correct file extension and ignores the dropdown selection.

But if the user does not manually add a file ending by selection of an existing file, or entering data in the dialog box, no ending is passed to the function, and therefore the file fails to write.

Is this actually not an intended feature? My reading of the source code and documentation, along with some consulting of CoPilot, leads me to believe that the ending selected in the dropdown is intended to be appended to any filename that lacks an ending, but if I've misunderstood, then perhaps this should be reclassified as a new feature request. But my understanding is that it should do this. Help me out if you can. Thanks.

+-----------------------------+
| asksaveasfilename Function
|
| - Calls SaveAs.show()
+-------------+---------------+
|
v
+-------------+---------------+
| SaveAs Class
|
| - Inherits from _Dialog
| - Uses _Dialog.show()
+-------------+---------------+
|
v
+-------------+---------------+
| _Dialog Class
|
| - Defines show()
| - Calls tk.call()
| - Passes result to
| _fixresult()
+-------------+---------------+
|
v
+-------------+---------------+
| tk.call Method
|
| - Executes Tcl command
| - Returns selected file path
+-------------+---------------+
|
v
+-------------+---------------+
| _fixresult Method
|
| - Processes result
| - Ensures correct file path
| and extension
+-----------------------------+

CODE:

import tkinter as tk
from tkinter import filedialog
import os
import pandas as pd

class FileDialogTest:
    def __init__(self, file_extensions):
        self.file_extensions = file_extensions
        self.selected_file_type = None

    def _format_file_extensions(self):
        return [(f"{ext} files", ext) for ext in self.file_extensions]

    def _get_file_type(self, file_path):
        _, ext = os.path.splitext(file_path)
        return ext

    def select_file_to_save(self, default_name="testfile"):
        root = tk.Tk()
        root.withdraw()
        file_path = filedialog.asksaveasfilename(
            title="Select a file to save",
            filetypes=self._format_file_extensions(),
            initialfile=default_name
        )
        if not file_path:
            raise FileNotFoundError("No file selected.")
        print(f"[After Selection] Selected file path: {file_path}") # Print for identifying file extension issues
        self.selected_file_type = self._get_file_type(file_path)
        if not self.selected_file_type:
            # Append the default extension based on the selected file type
            self.selected_file_type = self.file_extensions[0]  # Default to the first extension if none is selected
            file_path += self.selected_file_type
        elif self.selected_file_type not in self.file_extensions:
            # Correct the file extension if it doesn't match the selected type
            file_path += self.file_extensions[self.file_extensions.index(self.selected_file_type)]
        print(f"[After Extension Check] Final file path: {file_path}") # Print for identifying file extension issues
        print(f"[After Extension Check] Selected file type: {self.selected_file_type}") # Print for identifying file extension issues
        return file_path, self.selected_file_type

# Define file extensions for testing
file_extensions = ['.txt', '.csv', '.parquet', '.xlsx']

# Initialize FileDialogTest with file extensions
file_dialog_test = FileDialogTest(file_extensions)

# Test select_file_to_save method
try:
    file_path, selected_file_type = file_dialog_test.select_file_to_save()
    print(f"[Main] Selected file path: {file_path}")  # Print for identifying file extension issues
    print(f"[Main] Selected file type: {selected_file_type}")  # Print for identifying file extension issues
    
    # Sample data to save as DataFrame
    sample_data = {
        "Column1": ["Value 1", "Value 2"],
        "Column2": [123, 456],
        "Column3": ["Another string", "Yet another string"],
        "Column4": [456.78, 789.01]
    }
    
    df = pd.DataFrame(sample_data)
    
    # Save the DataFrame to the selected file
    print(f"[Before Saving] Saving DataFrame to {file_path} as {selected_file_type}") # Print for identifying file extension issues
    if selected_file_type == '.csv':
        df.to_csv(file_path, index=False)
    elif selected_file_type == '.txt':
        df.to_csv(file_path, sep='\t', index=False)
    elif selected_file_type == '.parquet':
        df.to_parquet(file_path, index=False)
    elif selected_file_type == '.xlsx':
        df.to_excel(file_path, index=False)
    print(f"[After Saving] Data saved to {file_path}") # Print for identifying file extension issues
except FileNotFoundError as e:
    print(e)

CPython versions tested on:

3.13

Operating systems tested on:

Windows

@elkym elkym added the type-bug An unexpected behavior, bug, or error label Jan 31, 2025
@picnixz picnixz added stdlib Python modules in the Lib dir topic-tkinter labels Jan 31, 2025
@ericvsmith
Copy link
Member

This would be easier for someone to test and troubleshoot if you had a simpler example with no external dependencies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir topic-tkinter type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

3 participants