Home > Blockchain >  Python, Dash: Tkinter openfile dialog works fine only the first time but fails afterwards
Python, Dash: Tkinter openfile dialog works fine only the first time but fails afterwards

Time:02-18

I am struggling with the code below the whole day already.

import webbrowser
import dash
from dash import html, dcc
from dash.dependencies import Input, Output, State
from Open_Save_File import open_file_dialog

import tkinter
from tkinter import filedialog as fd

FILE_DIR = 'H:/Codes/'

webbrowser.get('windows-default').open('http://localhost:8050', new=2)

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    html.H4('Update my list',
            style={
                'textAlign': 'center'
            }),
    html.Br(),
    html.Hr(),
    
    html.Div([
        html.H5('Excel Directory:', 
                style = {'width': '20%', 'display': 'inline-block', \
                    'text-align': 'left'}),
        html.Div(id='selected_directory', children='No file selected!', \
            style={'width': '30%', 'display': 'inline-block'}),
        html.Button('Browse', id='open_excel_button', \
            n_clicks=0, style={'float': 'right', 'display': 'inline-block'})
    ]),
])

# 1. Callback for open_excel button
@app.callback(
    Output(component_id='selected_directory', component_property='children'),
    Input(component_id='open_excel_button', component_property='n_clicks'),
    prevent_initial_call=True
)
def open_excel_function(open_excel): 
    print ('*** 1A. Callback open_file_dialog')
    ctx = dash.callback_context
    trigger = ctx.triggered[0]['prop_id'].split('.')[0]
    print("***", trigger, "is triggered.")
    root = tkinter.Tk()
    root.withdraw()
    # root.iconbitmap(default='Extras/transparent.ico')
    if trigger == 'open_excel_button':
        file_directory = tkinter.filedialog.askopenfilename(initialdir=FILE_DIR) <-- Source of all evil....
        print('***', file_directory)
    else:
        file_directory = None
    return file_directory

if __name__ == '__main__':
    app.run_server(debug=True, use_reloader=False)  

It should open following UI in browser using Dash library and Tkinter:

enter image description here

If you click "browser" button, a open file dialog will open at the specified initial directory: enter image description here

It works fine the first time, the next times I will get the following error:

enter image description here

Can anyone help find out what is wrong with the line with tkinter.filedialog...?

I have tried other solutions, e.g. this. But I tried everything with this one, but don't know how to set initial directory. The InitialDir for this doesn't work.

With tkinkter, I could set the initial directory, but get the error (after works once) as can be seen in above screenshot. Basically I am stuck.

Thank you in advance for any pointer.

CodePudding user response:

In callback you

  • create tkinter main window root = tkinter.Tk()
  • hide it root.withdraw()

but you never destroy it when you exit file dialog.

When it runs again callback then it tries to create second main window and it makes conflict with previous (hidden) main window.

If I use root.destroy() then code works correctly.

app.callback(
    Output(component_id='selected_directory', component_property='children'),
    Input(component_id='open_excel_button', component_property='n_clicks'),
    prevent_initial_call=True
)
def open_excel_function(open_excel): 
    print ('*** 1A. Callback open_file_dialog')
    ctx = dash.callback_context
    trigger = ctx.triggered[0]['prop_id'].split('.')[0]
    print("***", trigger, "is triggered.")

    root = tkinter.Tk()
    root.withdraw()
    # root.iconbitmap(default='Extras/transparent.ico')

    if trigger == 'open_excel_button':
        file_directory = tkinter.filedialog.askopenfilename(initialdir=FILE_DIR)
        print('***', file_directory)
    else:
        file_directory = None

    root.destroy()  # <--- SOLUTION
    
    return file_directory

or even

def open_excel_function(open_excel): 
    print ('*** 1A. Callback open_file_dialog')
    ctx = dash.callback_context
    trigger = ctx.triggered[0]['prop_id'].split('.')[0]
    print("***", trigger, "is triggered.")

    if trigger == 'open_excel_button':
        root = tkinter.Tk()
        root.withdraw()
       # root.iconbitmap(default='Extras/transparent.ico')

        file_directory = tkinter.filedialog.askopenfilename(initialdir=FILE_DIR)
        print('***', file_directory)

        root.destroy()  # <--- SOLUTION
    else:
        file_directory = None
    
    return file_directory

EDIT:

If you want to get only dirname then maybe you should use tkinter.filedialog.askdirectory() instead of tkinter.filedialog.askopenfilename()

  • Related