imgui_bundle icon indicating copy to clipboard operation
imgui_bundle copied to clipboard

im_file_dialog will not work when using a pure python backend, or when using HelloImGui::Run (use portable-file-dialogs instead)

Open bgribble opened this issue 1 year ago • 4 comments

Hi, I am attempting to add im_file_dialog to my app but I am running in to an error when following the example code in demo_widgets.py. Once I have actually created the dialog, the next call to lfd.FileDialog.instance().is_done() errors out with a bad_function_call exception.

I am using an imgui_bundle wheel downloaded from the Github CI dated Dec 5 with Python 3.12.8 on Linux.

Here's some code to reproduce.

from imgui_bundle import imgui
from imgui_bundle.python_backends.sdl_backend import SDL2Renderer
from imgui_bundle import im_file_dialog as ifd

import OpenGL.GL as gl  # type: ignore
from sdl2 import *  # type: ignore
import ctypes
import sys


class AppState:
    text: str = """Hello, World\nLorem ipsum, etc.\netc."""
    text2: str = "Ahh"


app_state = AppState()


def main():
    window, gl_context = impl_pysdl2_init()
    imgui.create_context()
    impl = SDL2Renderer(window)

    running = True
    event = SDL_Event()
    while running:
        while SDL_PollEvent(ctypes.byref(event)) != 0:
            if event.type == SDL_QUIT:
                running = False
                break
            impl.process_event(event)
        impl.process_inputs()

        imgui.new_frame()

        imgui.begin("File selector problem", True)
        imgui.text("Button should open file selector dialog.")
        imgui.text("On my system, throws 'bad_function_call'")
        imgui.text("Code taken from demo_widgets.py")

        if imgui.button("File..."):
            ifd.FileDialog.instance().open(
                "##choose_file",
                "Select a file",
                "",
            )

        if ifd.FileDialog.instance().is_done("##choose_file"):
            if ifd.FileDialog.instance().has_result():
                result = ifd.FileDialog.instance().get_results()
                if result:
                    filename = result[0].path()
                    print(f"[file dialog] got path {filename} for result {result}")
            ifd.FileDialog.instance().close()


        imgui.end()

        gl.glClearColor(1.0, 1.0, 1.0, 1)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)

        imgui.render()
        impl.render(imgui.get_draw_data())
        SDL_GL_SwapWindow(window)

    impl.shutdown()
    SDL_GL_DeleteContext(gl_context)
    SDL_DestroyWindow(window)
    SDL_Quit()


def impl_pysdl2_init():
    width, height = 1280, 720
    window_name = "minimal ImGui/SDL2 example"

    if SDL_Init(SDL_INIT_EVERYTHING) < 0:
        print(
            "Error: SDL could not initialize! SDL Error: "
            + SDL_GetError().decode("utf-8")
        )
        sys.exit(1)

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24)
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8)
    SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1)
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1)
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1)
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE)

    SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, b"1")
    SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, b"1")
    SDL_SetHint(SDL_HINT_VIDEODRIVER, b"wayland,x11")
    SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, b"1")

    window = SDL_CreateWindow(
        window_name.encode("utf-8"),
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        width,
        height,
        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE,
    )

    if window is None:
        print(
            "Error: Window could not be created! SDL Error: "
            + SDL_GetError().decode("utf-8")
        )
        sys.exit(1)

    gl_context = SDL_GL_CreateContext(window)
    if gl_context is None:
        print(
            "Error: Cannot create OpenGL Context! SDL Error: "
            + SDL_GetError().decode("utf-8")
        )
        sys.exit(1)

    SDL_GL_MakeCurrent(window, gl_context)
    if SDL_GL_SetSwapInterval(1) < 0:
        print(
            "Warning: Unable to set VSync! SDL Error: " + SDL_GetError().decode("utf-8")
        )
        sys.exit(1)

    return window, gl_context


if __name__ == "__main__":
    main()

bgribble avatar Dec 11 '24 14:12 bgribble

Hi, I'll study this. In the meantime I really suggest you to use portable_file_dialogs: it is better in every way:

  • native dialogs
  • support high dPI
  • also offers notifications

pthom avatar Dec 11 '24 14:12 pthom

In the meantime I really suggest you to use portable_file_dialogs: it is better in every way:

ok, I had already done this as a placeholder, good to know it's a better solution and doesn't need replacing!

bgribble avatar Dec 11 '24 20:12 bgribble

The reason for the failure is:

The user of ImFileDialog (or im_file_dialog in Python) needs to provide an implementation for a function "CreateTexture" that transfers pixel data to the GPU.

ImmApp:Run() does this for you. However, when using HelloImGui::Run() or when using a pure python backend, this is not done.

Solution: use portable-file-dialogs instead

pthom avatar Dec 13 '24 08:12 pthom

could we maybe expose the CreateTexture/Delete texture functions in python so I can plug in my own opengl texture setup? Or perhaps expose ImFileDialogSetupTextureLoader?

portable-file-dialogs is okay but I kinda like the matching look of file dialog with the rest of my app.

biggysmith avatar Mar 21 '25 00:03 biggysmith