Move logic for window size and position into Window class

This commit is contained in:
Grant Sanderson 2024-12-10 11:07:54 -06:00
parent 178cca0ca5
commit d4c5c4736a
3 changed files with 54 additions and 56 deletions

View file

@ -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:

View file

@ -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

View file

@ -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.