|
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