demo_widgets.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. # Part of ImGui Bundle - MIT License - Copyright (c) 2022-2026 Pascal Thomet - https://github.com/pthom/imgui_bundle
  2. from typing import List
  3. from imgui_bundle import (
  4. imgui,
  5. hello_imgui,
  6. imgui_md,
  7. imgui_toggle,
  8. ImVec2,
  9. immapp,
  10. ImVec4,
  11. im_cool_bar,
  12. icons_fontawesome_4,
  13. )
  14. from imgui_bundle import imgui_command_palette as imcmd
  15. from imgui_bundle import portable_file_dialogs as pfd
  16. @immapp.static(
  17. knob_float_value=0,
  18. knob_int_value=0,
  19. use_custom_colors=False,
  20. primary_col=[0.1, 0.45, 0.7, 1.0],
  21. secondary_col=[0.7, 0.7, 0.7, 1.0],
  22. track_col=[0.3, 0.3, 0.7, 1.0],
  23. )
  24. def demo_knobs():
  25. static = demo_knobs
  26. from imgui_bundle import imgui_knobs
  27. imgui_md.render(
  28. """
  29. # Knobs
  30. [imgui-knobs](https://github.com/altschuler/imgui-knobs) provides knobs for ImGui."""
  31. )
  32. knob_types = {
  33. "tick": imgui_knobs.ImGuiKnobVariant_.tick,
  34. "dot": imgui_knobs.ImGuiKnobVariant_.dot,
  35. "space": imgui_knobs.ImGuiKnobVariant_.space,
  36. "stepped": imgui_knobs.ImGuiKnobVariant_.stepped,
  37. "wiper": imgui_knobs.ImGuiKnobVariant_.wiper,
  38. "wiper_dot": imgui_knobs.ImGuiKnobVariant_.wiper_dot,
  39. "wiper_only": imgui_knobs.ImGuiKnobVariant_.wiper_only,
  40. }
  41. def show_float_knobs(knob_size: float):
  42. imgui.push_id(f"{knob_size}_float")
  43. for knob_typename, knob_type in knob_types.items():
  44. changed, static.knob_float_value = imgui_knobs.knob(
  45. knob_typename,
  46. p_value=static.knob_float_value,
  47. v_min=0.0,
  48. v_max=1.0,
  49. speed=0,
  50. format="%.2f",
  51. variant=knob_type.value,
  52. size=knob_size,
  53. flags=0,
  54. steps=100,
  55. )
  56. imgui.same_line()
  57. imgui.new_line()
  58. imgui.pop_id()
  59. def show_int_knobs(knob_size: float):
  60. imgui.push_id(f"{knob_size}_int")
  61. for knob_typename, knob_type in knob_types.items():
  62. changed, static.knob_int_value = imgui_knobs.knob_int(
  63. knob_typename,
  64. p_value=static.knob_int_value,
  65. v_min=0,
  66. v_max=15,
  67. speed=0,
  68. format="%02i",
  69. variant=knob_type.value,
  70. steps=10,
  71. size=knob_size,
  72. )
  73. imgui.same_line()
  74. imgui.new_line()
  75. imgui.pop_id()
  76. # Apply custom colors before drawing knobs
  77. if static.use_custom_colors:
  78. p, s, t = static.primary_col, static.secondary_col, static.track_col
  79. imgui_knobs.set_knob_colors(imgui_knobs.KnobColors(
  80. primary=imgui_knobs.color_set(imgui.ImColor(p[0], p[1], p[2], p[3])),
  81. secondary=imgui_knobs.color_set(imgui.ImColor(s[0], s[1], s[2], s[3])),
  82. track=imgui_knobs.color_set(imgui.ImColor(t[0], t[1], t[2], t[3])),
  83. ))
  84. else:
  85. imgui_knobs.unset_knob_colors()
  86. knobs_size_small = immapp.em_size() * 2.5
  87. knobs_size_big = knobs_size_small * 1.3
  88. imgui.begin_group()
  89. imgui.text("Some small knobs")
  90. show_float_knobs(knobs_size_small)
  91. imgui.end_group()
  92. imgui.same_line()
  93. imgui.begin_group()
  94. imgui.text("Some big knobs (int values)")
  95. show_int_knobs(knobs_size_big)
  96. imgui.end_group()
  97. # Customize colors button + popup (below the knobs)
  98. if imgui.button("Customize Colors"):
  99. imgui.open_popup("knob_colors_popup")
  100. if imgui.begin_popup("knob_colors_popup"):
  101. _, static.use_custom_colors = imgui.checkbox("Use custom colors", static.use_custom_colors)
  102. if static.use_custom_colors:
  103. _, static.primary_col = imgui.color_edit4("Primary (indicator)", static.primary_col)
  104. _, static.secondary_col = imgui.color_edit4("Secondary (circle)", static.secondary_col)
  105. _, static.track_col = imgui.color_edit4("Track (arc)", static.track_col)
  106. imgui.end_popup()
  107. @immapp.static(show_full_demo=False)
  108. def demo_spinner():
  109. static = demo_spinner
  110. from imgui_bundle import imspinner
  111. imgui_md.render(
  112. """
  113. # Spinners
  114. [imspinner](https://github.com/dalerank/imspinner) provides spinners for ImGui."""
  115. )
  116. color = imgui.ImColor(0.3, 0.5, 0.9, 1.0)
  117. imgui.text("spinner_moving_dots")
  118. imgui.same_line()
  119. imspinner.spinner_moving_dots("spinner_moving_dots", 20.0, 4.0, color, 20)
  120. imgui.same_line()
  121. radius = imgui.get_font_size() / 1.8
  122. imgui.text("spinner_arc_rotation")
  123. imgui.same_line()
  124. imspinner.spinner_arc_rotation("spinner_arc_rotation", radius, 4.0, color)
  125. imgui.same_line()
  126. radius1 = imgui.get_font_size() / 2.5
  127. imgui.text("spinner_ang_triple")
  128. imgui.same_line()
  129. imspinner.spinner_ang_triple(
  130. "spinner_ang_triple",
  131. radius1,
  132. radius1 * 1.5,
  133. radius1 * 2.0,
  134. 2.5,
  135. color,
  136. color,
  137. color,
  138. )
  139. imgui.same_line()
  140. _, static.show_full_demo = imgui.checkbox("Show full spinners demo", static.show_full_demo)
  141. if static.show_full_demo:
  142. imspinner.demo_spinners()
  143. @immapp.static(flag=True)
  144. def demo_toggle():
  145. static = demo_toggle
  146. imgui_md.render_unindented(
  147. """
  148. # Toggle Switch
  149. [imgui_toggle](https://github.com/cmdwtf/imgui_toggle) provides toggle switches for ImGui."""
  150. )
  151. _changed, static.flag = imgui_toggle.toggle("Default Toggle", static.flag)
  152. imgui.same_line()
  153. _changed, static.flag = imgui_toggle.toggle(
  154. "Animated Toggle", static.flag, imgui_toggle.ToggleFlags_.animated.value
  155. )
  156. imgui.same_line()
  157. toggle_config = imgui_toggle.material_style()
  158. toggle_config.animation_duration = 0.4
  159. _changed, static.flag = imgui_toggle.toggle(
  160. "Material Style (with slowed anim)", static.flag, config=toggle_config
  161. )
  162. imgui.same_line()
  163. _changed, static.flag = imgui_toggle.toggle(
  164. "iOS style", static.flag, config=imgui_toggle.ios_style(size_scale=0.2)
  165. )
  166. imgui.same_line()
  167. _changed, static.flag = imgui_toggle.toggle(
  168. "iOS style (light)",
  169. static.flag,
  170. config=imgui_toggle.ios_style(size_scale=0.2, light_mode=True),
  171. )
  172. @immapp.static(
  173. open_file_dialog=None,
  174. open_file_multiselect=None,
  175. save_file_dialog=None,
  176. select_folder_dialog=None,
  177. last_file_selection="",
  178. # Messages and Notifications
  179. icon_type=pfd.icon.info,
  180. message_dialog=None,
  181. message_choice_type=pfd.choice.ok,
  182. )
  183. def demo_portable_file_dialogs():
  184. # from imgui_bundle import portable_file_dialogs as pfd
  185. static = demo_portable_file_dialogs
  186. imgui.push_id("pfd")
  187. imgui_md.render_unindented(
  188. """
  189. # Portable File Dialogs
  190. [portable-file-dialogs](https://github.com/samhocevar/portable-file-dialogs) provides file dialogs
  191. as well as notifications and messages. They will use the native dialogs and notifications on each platform.
  192. """
  193. )
  194. def log_result(what: str):
  195. static.last_file_selection = what
  196. def log_result_list(whats: List[str]):
  197. static.last_file_selection = "\n".join(whats)
  198. imgui.text(" --- File dialogs ---")
  199. if imgui.button("Open file"):
  200. static.open_file_dialog = pfd.open_file("Select file")
  201. if static.open_file_dialog is not None and static.open_file_dialog.ready():
  202. log_result_list(static.open_file_dialog.result())
  203. static.open_file_dialog = None
  204. imgui.same_line()
  205. if imgui.button("Open file (multiselect)"):
  206. static.open_file_multiselect = pfd.open_file(
  207. "Select file", options=pfd.opt.multiselect
  208. )
  209. if (
  210. static.open_file_multiselect is not None
  211. and static.open_file_multiselect.ready()
  212. ):
  213. log_result_list(static.open_file_multiselect.result())
  214. static.open_file_multiselect = None
  215. imgui.same_line()
  216. if imgui.button("Save file"):
  217. static.save_file_dialog = pfd.save_file("Save file")
  218. if static.save_file_dialog is not None and static.save_file_dialog.ready():
  219. log_result(static.save_file_dialog.result())
  220. static.save_file_dialog = None
  221. imgui.same_line()
  222. if imgui.button("Select folder"):
  223. static.select_folder_dialog = pfd.select_folder("Select folder")
  224. if static.select_folder_dialog is not None and static.select_folder_dialog.ready():
  225. log_result(static.select_folder_dialog.result())
  226. static.select_folder_dialog = None
  227. if len(static.last_file_selection) > 0:
  228. imgui.text(static.last_file_selection)
  229. imgui.text(" --- Notifications and messages ---")
  230. # icon type
  231. imgui.text("Icon type")
  232. imgui.same_line()
  233. for notification_icon in (pfd.icon.info, pfd.icon.warning, pfd.icon.error):
  234. if imgui.radio_button(notification_icon.name, static.icon_type == notification_icon):
  235. static.icon_type = notification_icon
  236. imgui.same_line()
  237. imgui.new_line()
  238. if imgui.button("Add Notif"):
  239. pfd.notify("Notification title", "This is an example notification", static.icon_type)
  240. # messages
  241. imgui.same_line()
  242. # 1. Display the message
  243. if imgui.button("Add message"):
  244. static.message_dialog = pfd.message("Message title", "This is an example message", static.message_choice_type, static.icon_type)
  245. # 2. Handle the message result
  246. if static.message_dialog is not None and static.message_dialog.ready():
  247. print("msg ready: " + str(static.message_dialog.result()))
  248. static.message_dialog = None
  249. # Optional: Select the message type
  250. imgui.same_line()
  251. for choice_type in (pfd.choice.ok, pfd.choice.yes_no, pfd.choice.yes_no_cancel, pfd.choice.retry_cancel, pfd.choice.abort_retry_ignore):
  252. if imgui.radio_button(choice_type.name, static.message_choice_type == choice_type):
  253. static.message_choice_type = choice_type
  254. imgui.same_line()
  255. imgui.new_line()
  256. imgui.pop_id()
  257. @immapp.static(selected_filename="")
  258. def demo_imfile_dialog():
  259. static = demo_imfile_dialog # Access to static variable via static
  260. from imgui_bundle import has_submodule
  261. if not has_submodule("im_file_dialog"):
  262. return
  263. from imgui_bundle import im_file_dialog as ifd
  264. imgui_md.render_unindented(
  265. """
  266. # ImFileDialog
  267. [ImFileDialog](https://github.com/pthom/ImFileDialog.git) provides file dialogs for ImGui.
  268. """
  269. )
  270. # Warning / low support
  271. imgui.same_line()
  272. imgui.text(icons_fontawesome_4.ICON_FA_EXCLAMATION_TRIANGLE)
  273. imgui.set_item_tooltip("""
  274. It is advised to use Portable File Dialogs instead, which offer native dialogs on each platform,
  275. as well as notifications and messages.
  276. Known limitations of ImFileDialog:
  277. * Not adapted for High DPI resolution under windows
  278. * No support for multi-selection
  279. * Will not work under python with a pure python backend (requires to use `immapp.run()`)
  280. """)
  281. if imgui.button("Open file"):
  282. ifd.FileDialog.instance().open(
  283. "ShaderOpenDialog",
  284. "Open a shader",
  285. "Image file (*.png*.jpg*.jpeg*.bmp*.tga).png,.jpg,.jpeg,.bmp,.tga,.*",
  286. True,
  287. )
  288. imgui.same_line()
  289. if imgui.button("Open directory"):
  290. ifd.FileDialog.instance().open("DirectoryOpenDialog", "Open a directory", "")
  291. imgui.same_line()
  292. if imgui.button("Save file"):
  293. ifd.FileDialog.instance().save(
  294. "ShaderSaveDialog", "Save a shader", "*.sprj .sprj"
  295. )
  296. if len(static.selected_filename) > 0:
  297. imgui.text(f"Last file selection:\n {static.selected_filename}")
  298. # file dialogs
  299. if ifd.FileDialog.instance().is_done("ShaderOpenDialog"):
  300. if ifd.FileDialog.instance().has_result():
  301. # get_results: plural form - ShaderOpenDialog supports multi-selection
  302. res = ifd.FileDialog.instance().get_results()
  303. filenames = [f.path() for f in res]
  304. static.selected_filename = "\n ".join(filenames)
  305. ifd.FileDialog.instance().close()
  306. if ifd.FileDialog.instance().is_done("DirectoryOpenDialog"):
  307. if ifd.FileDialog.instance().has_result():
  308. static.selected_filename = ifd.FileDialog.instance().get_result().path()
  309. ifd.FileDialog.instance().close()
  310. if ifd.FileDialog.instance().is_done("ShaderSaveDialog"):
  311. if ifd.FileDialog.instance().has_result():
  312. static.selected_filename = ifd.FileDialog.instance().get_result().path()
  313. ifd.FileDialog.instance().close()
  314. @immapp.static(
  315. was_inited=False,
  316. show_command_palette=False,
  317. counter=0,
  318. command_palette_context=None,
  319. )
  320. def demo_command_palette():
  321. static = demo_command_palette
  322. def init_command_palette():
  323. static.command_palette_context = imcmd.ContextWrapper()
  324. highlight_font_color = ImVec4(1.0, 0.0, 0.0, 1.0)
  325. imcmd.set_style_color(
  326. imcmd.ImCmdTextType.highlight,
  327. imgui.color_convert_float4_to_u32(highlight_font_color),
  328. )
  329. # Add theme command: a two steps command, with initial callback + SubsequentCallback
  330. select_theme_cmd = imcmd.Command()
  331. select_theme_cmd.name = "Select theme"
  332. def select_theme_cmd_initial_cb():
  333. imcmd.prompt(["Classic", "Dark", "Light"])
  334. def select_theme_cmd_subsequent_cb(selected_option: int):
  335. if selected_option == 0:
  336. imgui.style_colors_classic()
  337. elif selected_option == 1:
  338. imgui.style_colors_dark()
  339. elif selected_option == 2:
  340. imgui.style_colors_light()
  341. select_theme_cmd.initial_callback = select_theme_cmd_initial_cb
  342. select_theme_cmd.subsequent_callback = select_theme_cmd_subsequent_cb
  343. imcmd.add_command(select_theme_cmd)
  344. # Simple command that increments a counter
  345. inc_cmd = imcmd.Command()
  346. inc_cmd.name = "increment counter"
  347. def inc_counter():
  348. static.counter += 1
  349. inc_cmd.initial_callback = inc_counter
  350. imcmd.add_command(inc_cmd)
  351. if not static.was_inited:
  352. init_command_palette()
  353. static.was_inited = True
  354. imgui_md.render_unindented(
  355. """
  356. # Command Palette
  357. [imgui-command-palette](https://github.com/hnOsmium0001/imgui-command-palette.git) provides a Sublime Text or VSCode style command palette in ImGui
  358. """
  359. )
  360. io = imgui.get_io()
  361. if io.key_ctrl and io.key_shift and imgui.is_key_pressed(imgui.Key.p):
  362. static.show_command_palette = not static.show_command_palette
  363. if static.show_command_palette:
  364. static.show_command_palette = imcmd.command_palette_window(
  365. "CommandPalette", True
  366. )
  367. imgui.new_line()
  368. imgui.text("Press Ctrl+Shift+P to bring up the command palette")
  369. imgui.new_line()
  370. imgui.text(f"{static.counter=}")
  371. def demo_cool_bar():
  372. # Function to show a CoolBar button
  373. def show_cool_bar_button(label):
  374. w = im_cool_bar.get_cool_bar_item_width()
  375. # Display transparent image and check if clicked
  376. hello_imgui.image_from_asset("images/world.png", ImVec2(w, w))
  377. clicked = imgui.is_item_hovered() and imgui.is_mouse_clicked(0)
  378. # Optional: add a label on the image
  379. top_left_corner = imgui.get_item_rect_min()
  380. text_pos = ImVec2(
  381. top_left_corner.x + immapp.em_size(1.0),
  382. top_left_corner.y + immapp.em_size(1.0),
  383. )
  384. imgui.get_window_draw_list().add_text(text_pos, 0xFFFFFFFF, label)
  385. return clicked
  386. button_labels = ["A", "B", "C", "D", "E", "F"]
  387. imgui_md.render_unindented(
  388. """
  389. # ImCoolBar
  390. ImCoolBar provides a dock-like Cool bar for Dear ImGui
  391. """
  392. )
  393. cool_bar_settings = im_cool_bar.ImCoolBarSettings()
  394. cool_bar_settings.anchor = ImVec2(
  395. 0.5, 0.07
  396. ) # position in the window (ratio of window size)
  397. cool_bar_settings.mode = im_cool_bar.ImCoolBarFlags_.horizontal
  398. if im_cool_bar.begin_cool_bar("##CoolBarMain", cool_bar_settings):
  399. for label in button_labels:
  400. if im_cool_bar.cool_bar_item():
  401. if show_cool_bar_button(label):
  402. print(f"Clicked {label}")
  403. im_cool_bar.end_cool_bar()
  404. imgui.new_line()
  405. imgui.new_line()
  406. def demo_gui():
  407. demo_cool_bar()
  408. demo_toggle()
  409. demo_spinner()
  410. demo_knobs()
  411. demo_command_palette()
  412. imgui.new_line()
  413. demo_portable_file_dialogs()
  414. imgui.new_line()
  415. demo_imfile_dialog()
  416. if __name__ == "__main__":
  417. immapp.run(demo_gui, with_markdown=True, window_size=(1000, 1000))