python-eel / Eel

A little Python library for making simple Electron-like HTML/JS GUI apps
MIT License
6.44k stars 587 forks source link

Filedialog (tkinter) doesn't show if root ha been withdrawn #395

Closed ruggi99 closed 3 years ago

ruggi99 commented 4 years ago

Eel version 0.14.0

Describe the bug Cannot open filedialog in tkinter if root window has been withdrawn before

To Reproduce

import eel
from tkinter import filedialog, Tk

@eel.expose
def open_file():
    root2 = Tk()
    root2.title("your title")
    root2.withdraw()
    print("Before filedialog")
    filedialog.askopenfilename(filetypes=(
        ("All types", "*.*"),
    ), parent=root2)
    print("After filedialog")
    root2.destroy()

eel.init("web")

eel.start("main.html", size=(600, 600))

Expected behavior Filedialog opens without a root window behind

Current behavior filedialog doesn't show up, blocking the function (because cannot close dialog) The only printed string is "Before filedialog"

Screenshots Not applicable in my opinion.

Desktop (please complete the following information):

Additional context The js code:

onload = () =>
  document.getElementById("open_file").addEventListener( // #open_file is a button
    "click",
    () => eel.open_file()
  )

If the root isn't withdrawn then the root window show up and filedialog too. Obviously the goal is to open filedialog without the root visible The current code in Python works:

from gevent import spawn, sleep
from tkinter import filedialog, Tk

run_forever = True

def check_for_block():
    """ Simple visual indicator if mainloop is blocked """
    root2 = Tk()
    root2.title("Title here")
    root2.withdraw()
    filedialog.askopenfilename(filetypes=(
        ("All types", "*.*"),
    ), parent=root2)
    root2.destroy()
    global run_forever
    run_forever = False
    print("run_forever set to false")

spawn(check_for_block)

while run_forever:
    sleep(1)

That makes me think that gevent isn't involved in this bug. Also tried gevent.monkey.patch_all in eel/__init__.py but nothing changed. Python Script cannot be interrupted with keyboard (Ctrl+C). Isn't this strange?

thewingit commented 3 years ago

I'm actually seeing the same issue. I also tried to not withdraw the root window, but as soon as I select a file the root Tk window remains "frozen" and does not close.

My specs:

I actually solved this on Windows by enabling the win32 API (I installed with pip install pywin32): startPath = "C:\\Windows" fileDlgObj = win32ui.CreateFileDialog( 1 ) fileDlgObj.SetOFNInitialDir( startPath ) fileDlgObj.SetOFNTitle( "Please select a file" ) fileDlgObj.DoModal() usrSelectedFile = fileDlgObj.GetPathName()

This seems to work (at least on Windows)

ruggi99 commented 3 years ago

For me it works perfectly if I don't withdraw the root window. It doesn't freeze. I don't like too much the pywin32 package and I prefer to keep the root window visible instead.

This simple code works:

import eel
from tkinter import filedialog, Tk

run_forever = True

def check_for_block():
    """ Simple visual indicator if mainloop is blocked """
    root2 = Tk()
    root2.title("Title here")
    root2.withdraw()
    filedialog.askopenfilename(filetypes=(
        ("All types", "*.*"),
    ), parent=root2)
    root2.destroy()
    global run_forever
    run_forever = False
    print("run_forever set to false")

eel.spawn(check_for_block)

while run_forever:
    eel.sleep(1)

What I don't understand is why the code above works (root window is hidden) and in eel it doesn't. This is the main reason why I opened this issue

maximedrn commented 2 years ago

Hello, I think this problem does not concern the original author anymore but I think I found a solution that has the same behavior as the default browser dialog but with Tkinter.

The window opens in the foreground without the small Tkinter window appearing. Normally this is hidden using the .withdraw() method, which causes a freeze with eel.

Here is the solution:

from tkinter import Tk
from tkinter.filedialog import askdirectory

root = Tk()
root.attributes('-topmost', True)  # Display the dialog in the foreground.
root.iconify()  # Hide the little window.
folder = askdirectory(title='...', parent=root)
root.destroy()  # Destroy the root window when folder selected.

Tested on Windows 10/11 and it works perfectly 🙂

EDIT:

Newer versions of TKinter don't work well on macOS, it crashes or the pop appears in the bottom left corner of the screen for no reason. I advise to use PyQt5 instead:

For a directory:

from PyQt5.QtWidgets import QWidget, QFileDialog  # pip install PyQt5
from PyQt5.Qt import QApplication as QApp
_, file = QApp(['./']), QFileDialog.getOpenFileName(parent=QWidget(
    ), caption='Select a file', directory='../',
    filter='Text file (*.txt)', initialFilter='Text file (*.txt)')
print(file[0])

For a file:

from PyQt5.QtWidgets import QWidget, QFileDialog  # pip install PyQt5
from PyQt5.Qt import QApplication as QApp
_, folder = QApp(['./']), QFileDialog.getExistingDirectory(
    parent=QWidget(), caption='Select a folder')
print(folder)
ketsebaotgizachew commented 2 years ago

Hello, I think this problem does not concern the original author anymore but I think I found a solution that has the same behavior as the default browser dialog but with Tkinter.

The window opens in the foreground without the small Tkinter window appearing. Normally this is hidden using the .withdraw() method, which causes a freeze with eel.

Here is the solution:

from tkinter import Tk
from tkinter.filedialog import askdirectory

root = Tk()
root.attributes('-topmost', True)  # Display the dialog in the foreground.
root.iconify()  # Hide the little window.
folder = askdirectory(title='...', parent=root)
root.destroy()  # Destroy the root window when folder selected.

Tested on Windows 10/11 and it works perfectly 🙂

this one works fine good

chenbuer commented 9 months ago

Hello, I think this problem does not concern the original author anymore but I think I found a solution that has the same behavior as the default browser dialog but with Tkinter.

~The window opens in the foreground without the small Tkinter window appearing. Normally this is hidden using the .withdraw() method, which causes a freeze with eel.~

~Here is the solution:~

from tkinter import Tk
from tkinter.filedialog import askdirectory

root = Tk()
root.attributes('-topmost', True)  # Display the dialog in the foreground.
root.iconify()  # Hide the little window.
folder = askdirectory(title='...', parent=root)
root.destroy()  # Destroy the root window when folder selected.

~Tested on Windows 10/11 and it works perfectly 🙂~

EDIT:

Newer versions of TKinter don't work well on macOS, it crashes or the pop appears in the bottom left corner of the screen for no reason. I advise to use PyQt5 instead:

For a directory:

from PyQt5.QtWidgets import QWidget, QFileDialog  # pip install PyQt5
from PyQt5.Qt import QApplication as QApp
_, file = QApp(['./']), QFileDialog.getOpenFileName(parent=QWidget(
    ), caption='Select a file', directory='../',
    filter='Text file (*.txt)', initialFilter='Text file (*.txt)')
print(file[0])

For a file:

from PyQt5.QtWidgets import QWidget, QFileDialog  # pip install PyQt5
from PyQt5.Qt import QApplication as QApp
_, folder = QApp(['./']), QFileDialog.getExistingDirectory(
    parent=QWidget(), caption='Select a folder')
print(folder)

PyQt's method QFileDialog.getOpenFileName pops up the dialog at the back, how to Display the dialog in the foreground, Just like the function ---> Tk()attributes('-topmost', True)