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

OS filedialog using Tkinter #2338

Open
Catalyst-42 opened this issue May 29, 2024 · 2 comments
Open

OS filedialog using Tkinter #2338

Catalyst-42 opened this issue May 29, 2024 · 2 comments
Labels
state: pending not addressed yet type: bug bug

Comments

@Catalyst-42
Copy link

Version of Dear PyGui

Version: 1.11.1 (on python 3.12.2, dpg installed via pip)
Operating System: macOS Sonoma 14.5

My Issue/Question

I wanted to create a system file dialog (instead of the file dialog provided by dpg.file_dialog), using Tkinter. When creating the dialog, various very strange errors occurred, which are impossible to handle and understand the reason for their occurrence. Over time I have found a working solution, but I don't think it's obvious or correct.

I also saw a similar problem in #1141, but by rewriting the solution I got a different error

To Reproduce

copy the code from the examples into the files first_example.py, example_1141.py and third_example.py and get_file.py

running python3 first_example.py gives zsh: trace trap python3 first_example.py
running python3 example_1141.py gives zsh: segmentation fault python3 example_1141.py
running python3 subprocess_demo.py works fine

Expected behavior

Filedialog, working in one file without need to call subprocess.

Screenshots/Video

Fine working example with subprocess:

subprocess_demo

Standalone, minimal, complete and verifiable example

The first obvious but unworkable example:

# first_example.py
import dearpygui.dearpygui as dpg
from tkinter import filedialog

dpg.create_context()

def open_file():
    file = filedialog.askopenfilename()  # Gives trace trap
    print(f"{file=}")

with dpg.window(tag="Demo"):
    dpg.add_button(label="Open file", callback=open_file)

dpg.create_viewport(title="Demo", width=600, height=412)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.set_primary_window("Demo", True)
dpg.start_dearpygui()
dpg.destroy_context()

Example from #1141, updated to last dpg version, unworkable, gives segfault:

# example_1141.py
from dearpygui.dearpygui import *

from tkinter import Tk
from tkinter import filedialog
import os

def new():
    Tk().withdraw()  # Gives segmentation fault
    file_path = filedialog.askdirectory(initialdir=os.getcwd())  
    print(file_path)
    DirString = "Chosen Directory: "+file_path
    set_value(dirpath,DirString)

with window(label="Tutorial") as main_window:
    add_button(label="Choose Directory",callback=new)
    dirpath = add_text(default_value="Chosen Directory: None Selected")

set_primary_window(main_window, True)
start_dearpygui()

Third, fine working (but weird looking) example:

# get_file.py
from tkinter import filedialog

file = filedialog.askopenfilename()
print(file, end="")
# third_example.py
import dearpygui.dearpygui as dpg
from tkinter import filedialog

from sys import executable  # To run subprocess
from subprocess import check_output

dpg.create_context()

def open_file():
    file = check_output([executable, "get_file.py"])  # Works fine, but looks weird
    print(f"{file=}")

with dpg.window(tag="Demo"):
    dpg.add_button(label="Open file", callback=open_file)

dpg.create_viewport(title="Demo", width=600, height=412)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.set_primary_window("Demo", True)
dpg.start_dearpygui()
dpg.destroy_context()
@Catalyst-42 Catalyst-42 added state: pending not addressed yet type: bug bug labels May 29, 2024
@v-ein
Copy link
Contributor

v-ein commented May 29, 2024

Disclaimer: I don't have any experience with Tkinter, and what's worse, I don't have tkinter itself (on Windows, it's optional), so can't really test your code. Nevertheless, here are some thoughts on the topic.

I believe that to successfully use Tkinter with DPG, one needs to understand events (and the event loop) and threads in both libraries. Here's a piece of doc describing threads in Tkinter:

https://docs.python.org/3/library/tkinter.html#threading-model

In DPG, you have two threads: the main thread runs the rendering loop, and a secondary thread (created by DPG internally) runs callbacks and handlers. When you click a button, its callback is run on that secondary "handlers thread".

Now, what happens in your 1st and 2nd example is Tk is run on that secondary thread, and somehow it doesn't like that. In 3rd example, you're running it in a separate process, and therefore in the main thread (whereas DPG's rendering loop runs in the main thread of another process).

Somehow Tkinter doc suggests that it can be used from any thread, but googling it up shows a number of discussions saying that it should be run on the main thread (albeit most of them dating back at 2010-2012). So it's probably a matter of properly initializing Tkinter and using it in the right context. Unfortunately, like I said, I can't play with it and therefore can't provide any ready-to-use solutions.

@tobyclh
Copy link
Sponsor

tobyclh commented Jun 2, 2024

Got the same problem and I think I figured out the solution albeit not being very clean either.
The gist is that you have to

  1. setup a root tk object before dpg.show_viewport
  2. call Tk from the main thread.

Alternative, the original code works perfectly on Windows for those who are interested.
Functional example:

import dearpygui.dearpygui as dpg
from tkinter import filedialog
from tkinter import Tk
root = Tk()
root.withdraw()

dpg.create_context()


def open_file():
    file = filedialog.askopenfilename()  # Gives trace trap
    print(f"{file}")

with dpg.window(tag="Demo"):
    dpg.add_checkbox(tag='should_open_file', default_value=False, show=False)
    dpg.add_button(label="Open file", callback=lambda: dpg.set_value('should_open_file', True))

dpg.create_viewport(title="Demo", width=600, height=412)

dpg.setup_dearpygui()
dpg.show_viewport()

dpg.set_primary_window("Demo", True)

while dpg.is_dearpygui_running():
    if dpg.get_value('should_open_file'):
        dpg.set_value('should_open_file', False)
        open_file()
    dpg.render_dearpygui_frame()
    pass
    
dpg.destroy_context()

and if you can afford another external library, xdialog works pretty well on my system

import xdialog
import dearpygui.dearpygui as dpg
dpg.create_context()


def open_file():
    # file = filedialog.askopenfilename()  # Gives trace trap
    file = xdialog.open_file("Title Here", filetypes=[("Text Files", "*.txt")], multiple=True)
    print(f"{file}")


with dpg.window(tag="Demo"):
    dpg.add_button(label="Open file", callback=open_file)
    dpg.add_loading_indicator()

dpg.create_viewport(title="Demo", width=600, height=412)

dpg.setup_dearpygui()
dpg.show_viewport()

dpg.set_primary_window("Demo", True)
dpg.start_dearpygui()
    
dpg.destroy_context()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state: pending not addressed yet type: bug bug
Projects
None yet
Development

No branches or pull requests

3 participants