pthom / imgui_bundle

Dear ImGui Bundle: an extensive set of Ready-to-use widgets and libraries, based on ImGui. Start your first app in 5 lines of code, or less. Whether you prefer Python or C++, this pack has your back!
https://pthom.github.io/imgui_bundle/
MIT License
588 stars 61 forks source link

Multiple separated instances/windows of immapp/hello_imgui #223

Open Johndeep opened 2 weeks ago

Johndeep commented 2 weeks ago

A simple question, since I tried to look for Python Code documentation of hello_imgui or immapp and found nothing yet.

Is it possible to tell hello_imgui or immapp (which is a thin extension to hello_imgui anyway, well according to the description) to create non blocking windows, so I can run two or more separated windows simultaneously?

It seems that even with threading library it is not possible. In that case, hello_imgui will just silently do nothing and exit the code.

My plan is to run one or more hello_imgui windows alongside with a general glfw window or, for example, pyvista or something.

Johndeep commented 2 weeks ago

Okay, I found a solution for this. It is rather simple actually, well, with some limitations. Instead of the threading or concurrent.future library, just use multiprocessing library.

Unlike threads, which shares context and data, processes do not do that. Thus it is possible to call upon multiple immapp/hello_imgui windows even easier than running the code multiple times with different parameters.

The limitation is obviously that the data is not shared but duplicated and then handled completely isolated from each other.

I still leave this thread open since I would like to know, if immapp/hello_imgui does have a not documented way to be started non blocking. Or if this feature is planned in a way.

pthom commented 2 weeks ago

Yes, apart from using the multi-processing library, I don't see any easy solution. GUI applications often require to be run on the main thread, which we can emulate with multiprocessing.

Even if you were to succeed in using a thread, you would encounter probably issues with some static variables which are present in Dear ImGui and Dear ImGui Bundle (Most of them are static not thread_local). The global ImGui Context Here is a probable example.

Johndeep commented 2 weeks ago

I tried to do threads again and found out that is does work, as long as I keep it at only one instance. As soon as I add another window per thread, everything just shuts down. Just like that.

For those who walk this path and want to have some more window to window interaction:

  1. If you want to signal every other windows to close which run besides hello_imgui with threads for example, then pass a function to the callback before_exit:
    
    def my_call_on_exit():
    # Close all other window or set a variable to signal closing.
    # Or, let other window know that hello_imgui just closed.
    # Beware that other libs might have issues with calls from other threads.
    #   SEGFAULT is the least you can expect in that case.

runner_params = hello_imgui.RunnerParams() runner_params.callbacks.before_exit = my_call_on_exit

2. If you want to close hello_imgui along closing other windows, you would need to signal hello_imgui to do just that:
```Python
runner_params.app_shall_exit = True

As soon as you've set that variable, thankfully regardless from whichever thread, hello_imgui will close.

I appreciate that immapp simplifies many things, but due to the lack of documentation, I find pure hello_imgui a bit more adjustable for my purpose.

pthom commented 2 weeks ago

Thanks for the info.

Immapp is very small and its main api is « immapp.run(addons , runnerparams) » where runnerparams are from hello imgui.

Everything’s that is possible with hello imgui should be possible within immapp (you have access to the same params)

could you tell me what you found confusing so that I can try to improve the documentation?

Johndeep commented 1 week ago

Well, I mean what I said. Besides this, which is C/C++ only, there seems to be a lack of documentation dedicated for Python. Of course, if you're already familiar of wrappings of C++ for Python, you merely need to know that CamelCase written functions like VoidFunction BeforeExit becomes before_exit in Python.

I found out about this via Intellisense and source code digging that those function actually exists.

It would be helpful to have short and executable examples without too much comments and enough explanation for specific parts of functions, or for what it can be used. But I can understand that it is quite hard to write a good documentation, since there are many documentations out there for many types of applications and libraries, many of them aren't that great either.

May I allow myself a rather different question regarding the performance of one such hello_imgui window: Besides the fps_idling.enable_idling for framerate control, is it somewhere possible to disable vsync? Usually I do this glfw.swap_interval(0) to improve responsiveness of the interface.

pthom commented 1 week ago

May I allow myself a rather different question regarding the performance of one such hello_imgui window: Besides the fps_idling.enable_idling for framerate control, is it somewhere possible to disable vsync? Usually I do this glfw.swap_interval(0) to improve responsiveness of the interface.

Not yet, but this is interesting. I'll study that.

Johndeep commented 1 week ago

Just to explain my reasoning behind this:

Because I sync hello_imgui with pyvista, it seems that some elements starts to sync with the framerate with pyvista, which is a bit annoying, since it seems to be lagging then. But in truth, because I use Threads to have sharing memories, Python GIL starts to become a very annoying companion in making everything single threaded. And thus lagging. At least that is my assumption.

So if you want to sync two windows with different content in general with Threads, be careful to either separate the variables from each other or Python GIL will be the party pooper in being very strictly non-parallel. Or use Processes with either Manager or Value as sharing points. Annoyingly, it becomes difficult to share Classes or custom Objects. I'll investigate other possible methods or workarounds.

pthom commented 1 week ago

It would be helpful to have short and executable examples without too much comments and enough explanation for specific parts of functions, or for what it can be used. But I can understand that it is quite hard to write a good documentation, since there are many documentations out there for many types of applications and libraries, many of them aren't that great either.

There are a lot of examples in the documentation and in the interactive manual. Did you find them? Please look at the video below, which shows you how to access them.

https://github.com/pthom/imgui_bundle/assets/7694091/be6244d9-9f38-46b9-b80f-29e97de31fc2

Johndeep commented 1 week ago

Thank you for pointing out that page again. To be honest, I've made heavy use of that documentation page plus Code finder and how to write it in Python for quite a while already (I've found it by google though). And I really appreciate that documentation/example page! ^^ At least for general implementation and normal use cases.

Then again, I might currently have particular special cases not or barely covered in the documentation though. It is quite recent that I switched over from glfw backend to hello_imgui for simplification reasons and for being more robust, if something like Ubuntu + Wayland does not work again with the GlfwRenderer(window) from imgui_bundle for example. (Unfortunately, even with the latest build 1.5.0, I still get the context error by calling GlfwRenderer. In Wayland. X11 works fine.)

In my case, I wanted to push the possibilities how to handle two windows with different context (one of them being hello_imgui) as smooth as possible. What I've found out by looking into the docs and tinkering:

  1. In your demos, it seems that you're not consistent with using either hello_imgui.RunnerParams() or immapp.RunnerParams(). Well, you're using both across the demos and examples. Some times this, then that, or mixed.
  2. It seems to me that it might be wiser just to extend hello_imgui instead of having both hello_imgui and immapp, since, if you make use use of the RunnerParams for maximum control, it seems the same to me. While I favor hello_imgui, it might be confusing for new people to see both being used alternatively. One might ask, "so what's the difference besides immapp being able to make a window in a single line?".
  3. The example run "demo_python_context_manager" ends into a "Not found" page unfortunately.
  4. It seems that setting fps_idle has a big impact on the startup time of the app. For example, I set it to 1 second, it will take around 1-2 seconds to start up. (at 0.5 it takes 4-5 seconds) The lower the value, the longer the boot up. One might want to set that parameter after the hello_imgui window is actually shown. But that's minor.. unless you set it to 0.001, which would take 16 minutes to actually show a window...
  5. Oh yeah, dunno if it is because being in the web, but the fps demo on the website always tops at, in my case, 60 fps, regardless of fps_idle value. CORRECTION: Okay, I need to make the window inactive by actually clicking outside of the page for it to get into idle state. In an app it would suffice if you just don't move the mouse. I understand.

That's it so far.