imgui_fig.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. """imgui_fig.fig: Display Matplotlib figures in an ImGui window.
  2. Important:
  3. in order to use imgui_fig, you need to change matplotlib renderer to Agg.
  4. so that the figure is not displayed on the screen before we can capture it:
  5. add the following lines at the start of your script (and before importing pyplot):
  6. import matplotlib
  7. matplotlib.use('Agg')
  8. """
  9. from imgui_bundle.immapp import static # noqa: E402
  10. from imgui_bundle import immvision, ImVec2, imgui # noqa: E402
  11. from typing import TYPE_CHECKING
  12. if TYPE_CHECKING:
  13. import numpy as np
  14. import matplotlib.figure
  15. from numpy.typing import NDArray
  16. @static(fig_image_cache=dict())
  17. def _fig_to_image(label_id: str, figure: "matplotlib.figure.Figure", refresh_image: bool = False) -> "NDArray[np.uint8]":
  18. """
  19. Convert a Matplotlib figure to an RGB image.
  20. Parameters:
  21. - figure (matplotlib.figure.Figure): The Matplotlib figure to convert.
  22. Returns:
  23. - numpy.ndarray: An RGB image as a NumPy array with uint8 datatype.
  24. """
  25. import numpy # noqa: E402
  26. import matplotlib # noqa: E402
  27. backend_message = """
  28. imgui_fig.fig failed: in order to use imgui_fig, you need to change matplotlib renderer to Agg.
  29. Add the following lines at the start of your script (and before importing pyplot):
  30. import matplotlib
  31. matplotlib.use('Agg')
  32. """
  33. matplotlib_backend = matplotlib.rcParams['backend']
  34. if matplotlib_backend.lower() != 'agg':
  35. raise RuntimeError(backend_message)
  36. statics = _fig_to_image
  37. fig_id = imgui.get_id(label_id)
  38. if refresh_image and fig_id in statics.fig_image_cache:
  39. del statics.fig_image_cache[fig_id]
  40. if fig_id not in statics.fig_image_cache:
  41. # draw the renderer
  42. figure.canvas.draw()
  43. # Get the RGBA buffer from the figure
  44. w, h = figure.canvas.get_width_height()
  45. buf = numpy.frombuffer(figure.canvas.buffer_rgba(), dtype=numpy.uint8)
  46. try:
  47. buf.shape = (h, w, 4)
  48. img = buf
  49. matplotlib.pyplot.close(figure)
  50. statics.fig_image_cache[fig_id] = img
  51. except ValueError as e:
  52. raise RuntimeError(backend_message) from e
  53. except Exception as e:
  54. print(f"Error: {e}")
  55. return statics.fig_image_cache[fig_id] # type: ignore
  56. def fig(label_id: str,
  57. figure: "matplotlib.figure.Figure",
  58. size: ImVec2 | None = None,
  59. refresh_image: bool = False,
  60. resizable: bool = True,
  61. show_options_button: bool = False) -> ImVec2:
  62. """
  63. Display a Matplotlib figure in an ImGui window.
  64. Parameters:
  65. - label_id (str): An identifier for the ImGui image widget.
  66. - figure (matplotlib.figure.Figure): The Matplotlib figure to display.
  67. - size (Size): Size of the displayed fig
  68. Will be updated if resizable is True
  69. - refresh_image (bool): Flag to refresh the image.
  70. - show_options_button (bool): Flag to show additional options.
  71. Returns:
  72. - The position of the mouse in the figure
  73. Important:
  74. before importing pyplot, set the renderer to Tk,
  75. so that the figure is not displayed on the screen before we can capture it.
  76. ```python
  77. import matplotlib
  78. matplotlib.use('Agg')
  79. import matplotlib.pyplot as plt
  80. ```
  81. """
  82. image_rgb = _fig_to_image(label_id, figure, refresh_image)
  83. immvision.push_color_order_rgb()
  84. mouse_position_tuple = immvision.image_display_resizable(
  85. label_id, image_rgb, size, refresh_image, resizable, show_options_button)
  86. immvision.pop_color_order()
  87. mouse_position = ImVec2(mouse_position_tuple[0], mouse_position_tuple[1])
  88. return mouse_position