|
15 | 15 | import random |
16 | 16 | import threading |
17 | 17 | import time |
| 18 | +from pathlib import Path |
18 | 19 | from queue import Queue |
19 | 20 |
|
20 | 21 | import srt |
|
25 | 26 | import dearpygui.dearpygui as dpg |
26 | 27 |
|
27 | 28 | dearpygui_imported = True |
| 29 | + dpg.create_context() |
| 30 | + window = dpg.generate_uuid() |
28 | 31 | except ImportError: |
29 | 32 | dearpygui_imported = False |
30 | 33 | from typing import TYPE_CHECKING |
|
34 | 37 | from watchdog.events import DirModifiedEvent, FileModifiedEvent, FileSystemEventHandler |
35 | 38 | from watchdog.observers import Observer |
36 | 39 |
|
| 40 | +from manim import __version__ |
37 | 41 | from manim.mobject.mobject import Mobject |
38 | 42 | from manim.mobject.opengl.opengl_mobject import OpenGLPoint |
39 | 43 |
|
40 | 44 | from .. import config, logger |
41 | 45 | from ..animation.animation import Animation, Wait, prepare_animation |
42 | 46 | from ..camera.camera import Camera |
43 | 47 | from ..constants import * |
44 | | -from ..gui.gui import configure_pygui |
45 | 48 | from ..renderer.cairo_renderer import CairoRenderer |
46 | 49 | from ..renderer.opengl_renderer import OpenGLCamera, OpenGLMobject, OpenGLRenderer |
47 | 50 | from ..renderer.shader import Object3D |
|
51 | 54 | from ..utils.family_ops import restructure_list_to_exclude_certain_family_members |
52 | 55 | from ..utils.file_ops import open_media_file |
53 | 56 | from ..utils.iterables import list_difference_update, list_update |
| 57 | +from ..utils.module_ops import scene_classes_from_file |
54 | 58 |
|
55 | 59 | if TYPE_CHECKING: |
56 | 60 | from collections.abc import Iterable, Sequence |
@@ -144,7 +148,7 @@ def __init__( |
144 | 148 | self.skip_animation_preview = False |
145 | 149 | self.meshes: list[Object3D] = [] |
146 | 150 | self.camera_target = ORIGIN |
147 | | - self.widgets: list[Any] = [] |
| 151 | + self.widgets: list[dict[str, Any]] = [] |
148 | 152 | self.dearpygui_imported = dearpygui_imported |
149 | 153 | self.updaters: list[Callable[[float], None]] = [] |
150 | 154 | self.key_to_function_map: dict[str, Callable[[], None]] = {} |
@@ -1406,13 +1410,12 @@ def embedded_method(*args: Any, **kwargs: Any) -> None: |
1406 | 1410 | if self.dearpygui_imported and config["enable_gui"]: |
1407 | 1411 | if not dpg.is_dearpygui_running(): |
1408 | 1412 | gui_thread = threading.Thread( |
1409 | | - target=configure_pygui, |
1410 | | - args=(self.renderer, self.widgets), |
| 1413 | + target=self._configure_pygui, |
1411 | 1414 | kwargs={"update": False}, |
1412 | 1415 | ) |
1413 | 1416 | gui_thread.start() |
1414 | 1417 | else: |
1415 | | - configure_pygui(self.renderer, self.widgets, update=True) |
| 1418 | + self._configure_pygui(update=True) |
1416 | 1419 |
|
1417 | 1420 | self.camera.model_matrix = self.camera.default_model_matrix |
1418 | 1421 |
|
@@ -1543,6 +1546,73 @@ def embed(self) -> None: |
1543 | 1546 | # End scene when exiting an embed. |
1544 | 1547 | raise Exception("Exiting scene.") |
1545 | 1548 |
|
| 1549 | + def _configure_pygui(self, update: bool = True) -> None: |
| 1550 | + if not self.dearpygui_imported: |
| 1551 | + raise RuntimeError("Attempted to use DearPyGUI when it isn't imported.") |
| 1552 | + if update: |
| 1553 | + dpg.delete_item(window) |
| 1554 | + else: |
| 1555 | + dpg.create_viewport() |
| 1556 | + dpg.setup_dearpygui() |
| 1557 | + dpg.show_viewport() |
| 1558 | + |
| 1559 | + dpg.set_viewport_title(title=f"Manim Community v{__version__}") |
| 1560 | + dpg.set_viewport_width(1015) |
| 1561 | + dpg.set_viewport_height(540) |
| 1562 | + |
| 1563 | + def rerun_callback(sender: Any, data: Any) -> None: |
| 1564 | + self.queue.put(("rerun_gui", [], {})) |
| 1565 | + |
| 1566 | + def continue_callback(sender: Any, data: Any) -> None: |
| 1567 | + self.queue.put(("exit_gui", [], {})) |
| 1568 | + |
| 1569 | + def scene_selection_callback(sender: Any, data: Any) -> None: |
| 1570 | + config["scene_names"] = (dpg.get_value(sender),) |
| 1571 | + self.queue.put(("rerun_gui", [], {})) |
| 1572 | + |
| 1573 | + scene_classes = scene_classes_from_file( |
| 1574 | + Path(config["input_file"]), full_list=True |
| 1575 | + ) # type: ignore[call-overload] |
| 1576 | + scene_names = [scene_class.__name__ for scene_class in scene_classes] |
| 1577 | + |
| 1578 | + with dpg.window( |
| 1579 | + id=window, |
| 1580 | + label="Manim GUI", |
| 1581 | + pos=[config["gui_location"][0], config["gui_location"][1]], |
| 1582 | + width=1000, |
| 1583 | + height=500, |
| 1584 | + ): |
| 1585 | + dpg.set_global_font_scale(2) |
| 1586 | + dpg.add_button(label="Rerun", callback=rerun_callback) |
| 1587 | + dpg.add_button(label="Continue", callback=continue_callback) |
| 1588 | + dpg.add_combo( |
| 1589 | + label="Selected scene", |
| 1590 | + items=scene_names, |
| 1591 | + callback=scene_selection_callback, |
| 1592 | + default_value=config["scene_names"][0], |
| 1593 | + ) |
| 1594 | + dpg.add_separator() |
| 1595 | + if len(self.widgets) != 0: |
| 1596 | + with dpg.collapsing_header( |
| 1597 | + label=f"{config['scene_names'][0]} widgets", |
| 1598 | + default_open=True, |
| 1599 | + ): |
| 1600 | + for widget_config in self.widgets: |
| 1601 | + widget_config_copy = widget_config.copy() |
| 1602 | + name = widget_config_copy["name"] |
| 1603 | + widget = widget_config_copy["widget"] |
| 1604 | + if widget != "separator": |
| 1605 | + del widget_config_copy["name"] |
| 1606 | + del widget_config_copy["widget"] |
| 1607 | + getattr(dpg, f"add_{widget}")( |
| 1608 | + label=name, **widget_config_copy |
| 1609 | + ) |
| 1610 | + else: |
| 1611 | + dpg.add_separator() |
| 1612 | + |
| 1613 | + if not update: |
| 1614 | + dpg.start_dearpygui() |
| 1615 | + |
1546 | 1616 | def update_to_time(self, t: float) -> None: |
1547 | 1617 | dt = t - self.last_t |
1548 | 1618 | self.last_t = t |
|
0 commit comments