hello_imgui_run_async.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. # Part of ImGui Bundle - MIT License - Copyright (c) 2022-2026 Pascal Thomet - https://github.com/pthom/imgui_bundle
  2. """Async support for HelloImGui - enables non-blocking GUI execution."""
  3. import asyncio
  4. from typing import Any, Optional, Callable, Tuple, overload
  5. from imgui_bundle.hello_imgui import RunnerParams, SimpleRunnerParams
  6. @overload
  7. async def run_async(
  8. runner_params: RunnerParams,
  9. ) -> None:
  10. """Run an application asynchronously using RunnerParams.
  11. This function will run until the application exits (user closes window or app_shall_exit is set).
  12. Use this when you need full control over the async lifecycle.
  13. Example:
  14. async def my_app():
  15. runner = hello_imgui.RunnerParams()
  16. runner.callbacks.show_gui = my_gui_function
  17. await hello_imgui.run_async(runner)
  18. print("GUI closed")
  19. asyncio.run(my_app())
  20. """
  21. ...
  22. @overload
  23. async def run_async(
  24. simple_params: SimpleRunnerParams,
  25. ) -> None:
  26. """Run an application asynchronously using SimpleRunnerParams."""
  27. ...
  28. @overload
  29. async def run_async(
  30. gui_function: Callable[[], None],
  31. window_title: str = "",
  32. window_size_auto: bool = False,
  33. window_restore_previous_geometry: bool = False,
  34. window_size: Optional[Tuple[int, int]] = None,
  35. fps_idle: float = 10.0,
  36. top_most: bool = False,
  37. ) -> None:
  38. """Run an application asynchronously using a simple GUI function and parameters."""
  39. ...
  40. async def run_async(*args: Any, **kwargs: Any) -> None:
  41. """Run a HelloImGui application asynchronously in a non-blocking way.
  42. This function provides async/await support for HelloImGui applications.
  43. It will run until the application exits (user closes window or app_shall_exit is set).
  44. Three signatures are supported:
  45. 1. run_async(runner_params: RunnerParams)
  46. 2. run_async(simple_params: SimpleRunnerParams)
  47. 3. run_async(gui_function, window_title="", window_size_auto=False, ...)
  48. For Jupyter notebook usage, see hello_imgui.nb.start() for a more convenient API.
  49. """
  50. # Import here to avoid issues at module load time
  51. from imgui_bundle import hello_imgui
  52. manual_render = hello_imgui.manual_render
  53. # Determine which setup method to use based on arguments
  54. if len(args) >= 1:
  55. first_arg = args[0]
  56. # Case 1: RunnerParams
  57. if isinstance(first_arg, RunnerParams):
  58. runner_params = first_arg
  59. manual_render.setup_from_runner_params(runner_params)
  60. # Case 2: SimpleRunnerParams
  61. elif isinstance(first_arg, SimpleRunnerParams):
  62. simple_params = first_arg
  63. manual_render.setup_from_simple_runner_params(simple_params)
  64. # Case 3: GUI function with keyword arguments
  65. elif callable(first_arg):
  66. gui_function = first_arg
  67. # Extract parameters, using defaults from hello_imgui.run signature
  68. window_title = kwargs.get("window_title", "")
  69. window_size_auto = kwargs.get("window_size_auto", False)
  70. window_restore_previous_geometry = kwargs.get("window_restore_previous_geometry", False)
  71. window_size = kwargs.get("window_size", None)
  72. fps_idle = kwargs.get("fps_idle", 10.0)
  73. top_most = kwargs.get("top_most", False)
  74. manual_render.setup_from_gui_function(
  75. gui_function,
  76. window_title=window_title,
  77. window_size_auto=window_size_auto,
  78. window_restore_previous_geometry=window_restore_previous_geometry,
  79. window_size=window_size,
  80. fps_idle=fps_idle,
  81. top_most=top_most,
  82. )
  83. else:
  84. raise TypeError(f"First argument must be RunnerParams, SimpleRunnerParams, or a callable GUI function, got {type(first_arg)}")
  85. else:
  86. raise TypeError("run_async() requires at least one argument")
  87. # Configure FPS settings for optimal async performance
  88. # EarlyReturn mode ensures C++ code never blocks: it returns immediately
  89. # and tells Python how long to wait (see _priv_idle_frame_wait_duration_for_python_async_io)
  90. params = hello_imgui.get_runner_params()
  91. params.fps_idling.fps_idling_mode = hello_imgui.FpsIdlingMode.early_return
  92. params.fps_idling.vsync_to_monitor = False # vsync is implemented via sleep in the backend
  93. if params.fps_idling.fps_max <= 0.0:
  94. params.fps_idling.fps_max = 60.0 # default cap to avoid 1000+ FPS spins
  95. # Async render loop
  96. try:
  97. while not hello_imgui.get_runner_params().app_shall_exit:
  98. manual_render.render()
  99. wait_time = hello_imgui._priv_idle_frame_wait_duration_for_python_async_io() # type: ignore
  100. if wait_time > 0.0:
  101. await asyncio.sleep(wait_time)
  102. else:
  103. await asyncio.sleep(0)
  104. finally:
  105. # Ensure cleanup happens even if an exception occurs
  106. manual_render.tear_down()