diff --git a/manimlib/config.py b/manimlib/config.py index 330cb658..c605340d 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -5,7 +5,6 @@ import colour import importlib import inspect import os -import screeninfo import sys import yaml @@ -311,48 +310,15 @@ def get_resolution(args: Optional[Namespace] = None, global_config: Optional[dic return int(width_str), int(height_str) -def get_window_position(monitor: screeninfo.Monitor, position_string: str, size: tuple[int, int]): - # Find position (Perhaps factor this out) - # Position might be specified with a string of the form - # x,y for integers x and y - if "," in position_string: - return tuple(map(int, position_string.split(","))) - elif len(position_string) == 2: - # Alternatively, it might be specified with a string like - # UR, OO, DL, etc. specifying what corner it should go to - char_to_n = {"L": 0, "U": 0, "O": 1, "R": 2, "D": 2} - width_diff = monitor.width - size[0] - height_diff = monitor.height - size[1] - x_step = char_to_n[position_string[1]] * width_diff // 2 - y_step = char_to_n[position_string[0]] * height_diff // 2 - return (monitor.x + x_step, -monitor.y + y_step) - else: - raise Exception("Window position string must be either a tuple of integers, or a pair of from \"ULORD\"") - - def get_window_config(args: Namespace, global_config: dict) -> dict: - # Default to making window half the screen size - # but make it full screen if -f is passed in - try: - monitors = screeninfo.get_monitors() - except screeninfo.ScreenInfoError: - # Default fallback - monitors = [screeninfo.Monitor(width=1920, height=1080)] - mon_index = global_config["window"]["monitor"] - monitor = monitors[min(mon_index, len(monitors) - 1)] - - width, height = get_resolution(args, global_config) - aspect_ratio = width / height - - window_width = monitor.width - if not (args.full_screen or global_config["window"]["full_screen"]): - window_width //= 2 - window_height = int(window_width / aspect_ratio) - size = (window_width, window_height) - - default_position = get_window_position(monitor, global_config["window"]["position"], size) - - return dict(size=size, default_position=default_position) + window_config = global_config["window"] + # Todo, this correction of configuration should maybe happen elsewhere + for key in "position", "size": + if window_config.get(key): + window_config[key] = eval(window_config[key]) + if args.full_screen: + window_config["full_screen"] = True + return window_config def get_camera_config(args: Optional[Namespace] = None, global_config: Optional[dict] = None) -> dict: diff --git a/manimlib/default_config.yml b/manimlib/default_config.yml index 96b2bcdf..b0169966 100644 --- a/manimlib/default_config.yml +++ b/manimlib/default_config.yml @@ -26,14 +26,16 @@ directories: # but here a user can specify a different cache location cache: "" window: - # Set the position of window on screen, you can use directions, e.g. UL/DR/OL/OO/... - # also, you can also specify the position(pixel) of the upper left corner of - # the window on the monitor, e.g. "960,540" - position: UR - # If using multiple monitors, which one should show the window? - monitor: 0 + # The position of window on screen. UR -> Upper Right, and likewise DL -> Down and Left, + # UO would be upper middle, etc. + position_string: UR + # If using multiple monitors, which one should show the window + monitor_index: 0 # If not full screen, the default to give it half the screen width full_screen: False + # Other optional specifications that override the above + # position: (500, 500) # Specific position, in pixel coordiantes, for upper right corner + # size: (1920, 1080) # Specific size, in pixels file_writer_config: # If break_into_partial_movies is set to True, then many small # files will be written corresponding to each Scene.play and diff --git a/manimlib/window.py b/manimlib/window.py index 70def312..da8a5792 100644 --- a/manimlib/window.py +++ b/manimlib/window.py @@ -6,8 +6,10 @@ import moderngl_window as mglw from moderngl_window.context.pyglet.window import Window as PygletWindow from moderngl_window.timers.clock import Timer from functools import wraps +import screeninfo from manimlib.config import get_global_config +from manimlib.constants import ASPECT_RATIO from manimlib.constants import FRAME_SHAPE from typing import TYPE_CHECKING @@ -29,20 +31,24 @@ class Window(PygletWindow): def __init__( self, scene: Optional[Scene] = None, - size: tuple[int, int] = (1280, 720), - default_position: tuple[int, int] = (0, 0), + position_string: str = "UR", + monitor_index: int = 1, + full_screen: bool = False, + size: Optional[tuple[int, int]] = None, + position: Optional[tuple[int, int]] = None, samples: int = 0 ): - super().__init__(size=size, samples=samples) - self.scene = scene - self.default_size = size - self.default_position = default_position - self.pressed_keys = set() - self.size = size + self.monitor = self.get_monitor(monitor_index) + self.default_size = size or self.get_default_size(full_screen) + self.default_position = position or self.position_from_string(position_string) + super().__init__(samples=samples) self.to_default_position() + self.pressed_keys = set() + + if self.scene: self.init_for_scene(scene) @@ -66,6 +72,30 @@ class Window(PygletWindow): mglw.activate_context(window=self, ctx=self.ctx) self.timer.start() + def get_monitor(self, index): + try: + monitors = screeninfo.get_monitors() + return monitors[min(index, len(monitors) - 1)] + except screeninfo.ScreenInfoError: + # Default fallback + return screeninfo.Monitor(width=1920, height=1080) + + def get_default_size(self, full_screen=False): + width = self.monitor.width // (1 if full_screen else 2) + height = int(width // ASPECT_RATIO) + return (width, height) + + def position_from_string(self, position_string): + # Alternatively, it might be specified with a string like + # UR, OO, DL, etc. specifying what corner it should go to + char_to_n = {"L": 0, "U": 0, "O": 1, "R": 2, "D": 2} + size = self.default_size + width_diff = self.monitor.width - size[0] + height_diff = self.monitor.height - size[1] + x_step = char_to_n[position_string[1]] * width_diff // 2 + y_step = char_to_n[position_string[0]] * height_diff // 2 + return (self.monitor.x + x_step, -self.monitor.y + y_step) + def focus(self): """ Puts focus on this window by hiding and showing it again.