0

I am developing an EEL project, and I needed to create a file dialog on the python side in order to preprocess data before sending it to javascript.

I tried to use tk.filedialog.askopenfilename, but that somehow froze the javascript event loop.

I found an answer on StackOverflow that used wxpython to create a non-blocking file picker. However, when I run the code below, the file picker always starts minimized.

However, once you use the file picker once, it works perfectly the second time.

Any help appreciated.

import base64
import json
from tkinter import Tk
Tk().withdraw()
from tkinter.filedialog import askopenfilename

import PIL.Image
import eel
import numpy as np
import wx

# Reusable wxpython App instance for the creation of non-blocking popup dialogs
app=wx.App(None)

eel.init("public")

def encode(bts):
    return base64.b64encode(bts)

def array_to_json(array):
    return json.dumps({
        "shape": list(array.shape),
        "dtype": str(array.dtype),
        "data":list(np.ravel(array).astype(float)) # not efficient but quite clean
    })



@eel.expose
def load_image(path):
    return array_to_json(np.asarray(PIL.Image.open(path)))

@eel.expose
def pick_image():
    # return askopenfilename()
    """ --- Adapted from https://stackoverflow.com/a/59177064/5166365"""
    style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.STAY_ON_TOP | wx.DIALOG_NO_PARENT | wx.MAXIMIZE
    dialog = wx.FileDialog(None, "Open File", wildcard="*", style=style)
    dialog.Iconize(False)
    dialog.Maximize()
    dialog.Raise()
    path = ""
    if dialog.ShowModal() == wx.ID_OK:
        path = dialog.GetPath()
    else:
        path = ""
    return path
    """ --- """

eel.start("index.html")
Michael Sohnen
  • 953
  • 7
  • 15

1 Answers1

0

I was able to get it working with the following code. I used a regular window instead of a wx.Dialog or similar class.

class FancyFilePickerApplication:
    
    def __init__(self):
        self.app = wx.App()
        self.frame = wx.Frame(None,title="Fancy File Picker")
        self.build_ui()
    #@private
    def build_ui(self):

        self.vertical_sizer = wx.BoxSizer(wx.VERTICAL)
        self.control_panel = wx.Panel(self.frame,wx.ID_ANY)
        self.horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.control_panel.SetSizer(self.horizontal_sizer)
        self.frame.SetSizer(self.vertical_sizer) 
        self.dir_ctrl = wx.GenericDirCtrl(self.frame,wx.ID_ANY,wx.EmptyString,wx.DefaultPosition,wx.Size(400,300))
        self.vertical_sizer.Add(self.dir_ctrl,wx.SizerFlags().Expand())
        self.vertical_sizer.Add(self.control_panel,wx.SizerFlags().Expand())
        self.scale_factor_label = wx.StaticText(self.control_panel,wx.ID_ANY,"Scale Factor: ")
        self.scale_factor_textbox = wx.TextCtrl(self.control_panel,wx.ID_ANY)
        self.open_button = wx.Button(self.control_panel,wx.ID_ANY,"Open")
        self.horizontal_sizer.Add(self.scale_factor_label,wx.SizerFlags().Expand())
        self.horizontal_sizer.Add(self.scale_factor_textbox,wx.SizerFlags().Expand())
        self.horizontal_sizer.Add(self.open_button,wx.SizerFlags().Expand())
        self.open_button.Bind(wx.EVT_BUTTON,lambda evt:self.submit())

        self.frame.Bind(wx.EVT_CLOSE,self.cancel)

    def open(self, file_picked_callback):
        self.file_picked_callback = file_picked_callback
        self.frame.Fit()
        self.frame.Center()
        self.frame.Show()
        self.frame.Raise()
        self.frame.ToggleWindowStyle(wx.STAY_ON_TOP)
        self.app.MainLoop()

    #@private
    def submit(self):
        filepath =self.dir_ctrl.GetFilePath()
        scale_factor_text = self.scale_factor_textbox.GetValue()
        scale_factor = 1.0 if not scale_factor_text.strip() else float(scale_factor_text)
        self.file_picked_callback(filepath,scale_factor)
        self.frame.Destroy()

    #@private
    def cancel(self,evt):
        self.file_picked_callback("",0)
        self.frame.Destroy()
Michael Sohnen
  • 953
  • 7
  • 15