From 39738b84e9164b0f2d01f22440548c4393160013 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Sat, 6 Dec 2025 17:36:41 -0600 Subject: port to pygame --- manager.py | 24 +++++---------- renderer.py | 7 ++--- requirements.txt | 1 + window.py | 89 +++++++++++++++++++++++--------------------------------- 4 files changed, 49 insertions(+), 72 deletions(-) diff --git a/manager.py b/manager.py index 275e6b2..363aa2f 100644 --- a/manager.py +++ b/manager.py @@ -1,27 +1,14 @@ import os import sys -import signal import copy from threading import Thread from pathlib import Path -from OpenGL.GLUT import glutLeaveMainLoop class PixMan: _instance = None _initialized = False - @staticmethod - def handle_sigint(sig, frame): - try: - # Threads are started as daemons so will automatically shut down - glutLeaveMainLoop() - sys.exit(0) - except: - pass - finally: - print("Exiting...") - def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls) @@ -48,8 +35,6 @@ class PixMan: self.t_flask = None self.t_idle_download = None - signal.signal(signal.SIGINT, PixMan.handle_sigint) - self.configfile = configfile self.config = Config.load(self.configfile) if os.path.exists(self.configfile) else Config() @@ -76,7 +61,8 @@ class PixMan: # Create display self.display = PixDisplay(self.texture_list) - self.display.main({}) # TODO glut args + self.display.main() + self.die() def update_textures(self): if self.texture_list: @@ -110,6 +96,12 @@ class PixMan: return True + def die(self): + # Join threads and exit + self.t_flask.join() + self.t_idle_download.join() + sys.exit(0) + @property def frozen(self): # For pyinstaller diff --git a/renderer.py b/renderer.py index 3d157cb..52572b1 100644 --- a/renderer.py +++ b/renderer.py @@ -1,7 +1,6 @@ +import pygame import numpy as np from OpenGL.GL import * -from OpenGL.GLUT import * -from OpenGL.GLU import * from typing import Protocol class ImageRenderer: @@ -139,7 +138,7 @@ class ImageRenderer: self.draw_image(tex, win_w, win_h, alpha) - glutSwapBuffers() + pygame.display.flip() def draw_transition(self, tex_start, tex_end, win_w, win_h, delta_time, transition_time, transition_duration, reversed): assert self.transition, "No transition has been set" @@ -150,7 +149,7 @@ class ImageRenderer: self.transition.draw(tex_start, tex_end, win_w, win_h, delta_time, transition_time, transition_duration, reversed) - glutSwapBuffers() + pygame.display.flip() class Transition(Protocol): diff --git a/requirements.txt b/requirements.txt index 991c1ce..506973a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,6 @@ flask-cors # DEBUG flask-socketio numpy pillow +pygame pyopengl requests diff --git a/window.py b/window.py index 5387930..bba0213 100644 --- a/window.py +++ b/window.py @@ -1,7 +1,6 @@ +import pygame +from pygame.locals import * from OpenGL.GL import * -from OpenGL.GLUT import * -from OpenGL.GLU import * -from time import time from queue import Queue from renderer import ImageRenderer, TransitionMix @@ -9,6 +8,7 @@ from manager import PixMan class PixDisplay: def __init__(self, textures): + self.screen = None self.last_time = 0 self.start_time = 0 self.image_time = 0 @@ -39,16 +39,6 @@ class PixDisplay: self.textures = textures self.current_texture_index = 0 - @property - def max_framerate(self): - #return int(1000/int(1000/self.frame_time)) - return self.frame_time - - @max_framerate.setter - def max_framerate(self, max_fps): - self._max_fps = max_fps # This is just for the getter since otherwise e.g. int(1000/int(1000/60)) would round to 62 - self.frame_time = int(1000 / max_fps) # In ms - def increment_texture_index(self, increment): self.transition_reverse = increment < 0 @@ -66,10 +56,9 @@ class PixDisplay: # Main display function def display(self): # Calculate timings - current_time = time() - alive_time = current_time - self.start_time - delta_time = current_time - self.last_time - self.last_time = current_time + alive_time = pygame.time.get_ticks() / 1000 + delta_time = alive_time - self.last_time + self.last_time = alive_time if not self.tex or not self.tex.initialized or not self.tex.id: if self.textures.asset_count > 0: @@ -78,7 +67,7 @@ class PixDisplay: if not self.tex or not self.tex.id: glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glutSwapBuffers() + pygame.display.flip() return # Run queue events @@ -92,7 +81,7 @@ class PixDisplay: # Get window size old_win_w, old_win_h = self.win_w, self.win_h - self.win_w, self.win_h = glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT) + self.win_w, self.win_h = self.screen.get_size() # Draw static image except during a transition if self.image_time < self.image_duration: @@ -115,42 +104,20 @@ class PixDisplay: self.image_time = 0 self.auto_transition = True - # Limit framerate - def timer(self, value): - glutPostRedisplay() - glutTimerFunc(self.frame_time, self.timer, 0) # Schedule next frame - def seek(self, increment): self.auto_transition = False self.increment_texture_index(increment) self.image_time = self.image_duration - def handle_special_key(self, key, x, y): - if key == GLUT_KEY_LEFT: - self.seek(-1) - elif key == GLUT_KEY_RIGHT: - self.seek(1) - - def handle_visibility_change(self, state): - if state == GLUT_VISIBLE: - self._force_redraw = True - glutPostRedisplay() - # Initialization and main loop - def main(self, glut_args): + def main(self): # Initialize the window - glutInit(glut_args) - glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB) - glutCreateWindow("Image Viewer with Fade Transition") - glEnable(GL_TEXTURE_2D) - - self.renderer = ImageRenderer() - self.renderer.set_transition(TransitionMix) - self.image_time = 0 - self.start_time = time() - self.last_time = time() + pygame.init() + self.screen = pygame.display.set_mode((0, 0), DOUBLEBUF | OPENGL | FULLSCREEN) # TODO different display mode options + pygame.mouse.set_visible(False) # Set up the OpenGL viewport and projection + glEnable(GL_TEXTURE_2D) glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(-1, 1, -1, 1, -1, 1) @@ -160,10 +127,28 @@ class PixDisplay: glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - # Run display - glutDisplayFunc(self.display) - glutTimerFunc(self.frame_time, self.timer, 0) - glutVisibilityFunc(self.handle_visibility_change) # Redraw in case framebuffer gets destroyed when window is obscured - glutSpecialFunc(self.handle_special_key) - glutMainLoop() + # Setup renderer and slide timing + self.renderer = ImageRenderer() + self.renderer.set_transition(TransitionMix) + self.image_time = 0 + self.last_time = 0 + # Run display + clock = pygame.time.Clock() + while True: + for event in pygame.event.get(): + if event.type == QUIT: + pygame.quit() + return + elif event.type == KEYDOWN: + # Quit with escape or ctrl+q + if event.key == pygame.K_ESCAPE or event.key == pygame.K_q and event.mod & pygame.KMOD_CTRL: + pygame.quit() + return + # Seek with left/right arrow + elif event.key == pygame.K_LEFT: + self.seek(-1) + elif event.key == pygame.K_RIGHT: + self.seek(+1) + self.display() + clock.tick(self.max_framerate) -- cgit v1.2.3