3

I'm trying to use Flask to build the GUI for a desktop application (i.e. a web app running on a server running locally bundled with an embedded browser). Things mostly seem to work for now, but I'd like to add a file chooser to allow users to select a directory on their computer. I need the full path of the directory so opening a dialog using HTML/JavaScript won't work (because of security restrictions).

What I've tried doing instead is to launch a Tkinter file dialog when a button is pressed on the page. The problem is that while it does appear to launch something, the process just freezes (without displaying a window) and I'm forced to kill it, after which the page redirects to '/view_1_actions' and I get an "Error code: ERR_EMPTY_RESPONSE" error (i.e. the Flask app only crashes after I kill what appears to be the dialog window).

Here's my code:

HTML:

<form action="/view_1_actions" method="post">
    <input type="submit" name="submit" value="Select"></input>
</form>

Python:

from flask import Flask, request, redirect
from Tkinter import Tk
from tkFileDialog import askdirectory

@app.route('/view_1_actions', methods = ['POST'])
def view_1_actions():
    if request.form['submit'] == 'Select':
        Tk().withdraw()
        dirname = askdirectory()
    return redirect('/')

Granted this a rather strange scenario since you wouldn't usually be launching a graphical interface on the "server side", but my question is whether there is a way to fix this or an alternative solution I could use to display a file chooser dialog (preferably the native one for each OS).

ダンボー
  • 409
  • 4
  • 13

2 Answers2

2

While I'm not sure about the process freezing (perhaps a conflict between Tk and the library you're using to draw the webview?) it is potentially a problem that the user can delay the response of the view function indefinitely (all the while in theory "using" the view).

What I would recommend would be to have the flask view function start/message a separate thread and then return immediately. (With whatever background you want behind the dialog.) The other thread is responsible for doing the dialog window in a native OS fashion, then when the user selects something and it finishes, send another flask request internally that will do whatever it needs to do with the selected file.

I've never done this in a desktop app before, but some variation of this (might be a separate process rather than a separate thread, but the theory is similar) is how I like to handle long running tasks on a server.

GrandOpener
  • 1,943
  • 1
  • 17
  • 25
  • Thanks. Indeed, starting the window in the main Flask thread locks up the rest of the application. I'm using threads in other parts of the application to run background processes, but I've just tried running the Tk window in a separate thread and now I get a white rectangle being displayed (which freezes, and I then have to kill). I'll go with your suggestion and maybe try a different GUI library. – ダンボー Jun 17 '14 at 18:08
1

I know that I answer 7 years later but if someone face the same issue, I found a not proper but tricky solution.

In a post request, i send the previous filename of the user.

I use tkinter to open the file dialog. Once the user select a file I destroy tkinter.

I can get the file path without having the problem "main thread is not in main loop".

import tkinter as tk
from tkinter.filedialog import askopenfilename

@equity.route('api/filedialog', methods = ['POST'])
def open_filedialog():
    """open file dialog to get a file name"""

    print(request.json)
    filename = 'not found'
    root = tk.Tk()
    
    filename = askopenfilename()
    
    
    root.withdraw()
    if filename == 'not found':
        root.mainloop()
    else:
        root.destroy()
    

    #if filename selected new file name
    
    if filename:
        return jsonify({'file' : filename}), 200
    #else old file name
    else:
        return jsonify(request.json), 200
Mael_Jourdain
  • 446
  • 4
  • 4