From e8d9eaca5172d7219dc5ffb0d8d8f689d630570a Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Thu, 27 Nov 2025 19:55:26 +0100 Subject: [PATCH] added additonal logging around file loading --- pyPhotoAlbum/models.py | 53 +++++++++++++++++++++++++-------- tests/test_element_ops_mixin.py | 1 + 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/pyPhotoAlbum/models.py b/pyPhotoAlbum/models.py index f3b0782..5dbf135 100644 --- a/pyPhotoAlbum/models.py +++ b/pyPhotoAlbum/models.py @@ -5,11 +5,14 @@ Data model classes for pyPhotoAlbum from abc import ABC, abstractmethod from typing import Tuple, Optional, Dict, Any, List import json +import logging import os import uuid from datetime import datetime, timezone from PIL import Image +logger = logging.getLogger(__name__) + # Global configuration for asset path resolution _asset_search_paths: List[str] = [] _primary_project_folder: Optional[str] = None @@ -349,9 +352,9 @@ class ImageData(BaseLayoutElement): Args: pil_image: Loaded PIL Image (already RGBA, already resized) """ - from PIL import Image - try: + logger.debug(f"ImageData: Async load completed for {self.image_path}, size: {pil_image.size}") + # Apply PIL-level rotation if needed if hasattr(self, 'pil_rotation_90') and self.pil_rotation_90 > 0: # Rotate counter-clockwise by 90° * pil_rotation_90 @@ -363,7 +366,7 @@ class ImageData(BaseLayoutElement): pil_image = pil_image.transpose(Image.ROTATE_180) elif angle == 270: pil_image = pil_image.transpose(Image.ROTATE_90) # CCW 270 = rotate left - print(f"ImageData: Applied PIL rotation {angle}° to {self.image_path}") + logger.debug(f"ImageData: Applied PIL rotation {angle}° to {self.image_path}") # Store the image for texture creation during next render() # This avoids GL context issues when callback runs on wrong thread/timing @@ -375,10 +378,10 @@ class ImageData(BaseLayoutElement): # Update metadata for future renders - always update to reflect rotated dimensions self.image_dimensions = (pil_image.width, pil_image.height) - # print(f"ImageData: Image ready for texture creation: {self.image_path}") + logger.debug(f"ImageData: Queued for texture creation: {self.image_path}") except Exception as e: - print(f"ImageData: Error processing async loaded image: {e}") + logger.error(f"ImageData: Error processing async loaded image {self.image_path}: {e}") self._pending_pil_image = None self._async_loading = False @@ -392,9 +395,19 @@ class ImageData(BaseLayoutElement): from OpenGL.GL import (glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D, glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, - GL_LINEAR, GL_RGBA, GL_UNSIGNED_BYTE, glDeleteTextures) + GL_LINEAR, GL_RGBA, GL_UNSIGNED_BYTE, glDeleteTextures, + glGetString, GL_VERSION) try: + # Verify GL context is actually current before creating textures + # glGetString returns None if no context is active + gl_version = glGetString(GL_VERSION) + if gl_version is None: + # No GL context - keep pending image and try again next render + logger.debug(f"ImageData: No GL context for texture creation, deferring: {self.image_path}") + return False + + logger.debug(f"ImageData: Creating texture for {self.image_path} (GL version: {gl_version})") pil_image = self._pending_pil_image # Delete old texture if it exists @@ -418,14 +431,30 @@ class ImageData(BaseLayoutElement): # Clear pending image to free memory self._pending_pil_image = None - # print(f"ImageData: Created texture for {self.image_path}") + # Clear the warning flag if we successfully created the texture + if hasattr(self, '_gl_context_warned'): + delattr(self, '_gl_context_warned') + + logger.info(f"ImageData: Successfully created texture for {self.image_path}") return True except Exception as e: - print(f"ImageData: Error creating texture: {e}") - self._texture_id = None - self._pending_pil_image = None - return False + error_str = str(e) + # Check if this is a GL context error (err 1282 = GL_INVALID_OPERATION) + # These are typically caused by no GL context being current + if 'GLError' in error_str and '1282' in error_str: + # GL context not ready - keep pending image and try again next render + # Don't spam the console with repeated messages + if not hasattr(self, '_gl_context_warned'): + logger.warning(f"ImageData: GL context error (1282) for {self.image_path}, will retry on next render") + self._gl_context_warned = True + return False + else: + # Other error - give up on this image + logger.error(f"ImageData: Error creating texture for {self.image_path}: {e}") + self._texture_id = None + self._pending_pil_image = None + return False def _on_async_image_load_failed(self, error_msg: str): """ @@ -434,7 +463,7 @@ class ImageData(BaseLayoutElement): Args: error_msg: Error message """ - print(f"ImageData: Async load failed for {self.image_path}: {error_msg}") + logger.error(f"ImageData: Async load failed for {self.image_path}: {error_msg}") self._async_loading = False self._async_load_requested = False diff --git a/tests/test_element_ops_mixin.py b/tests/test_element_ops_mixin.py index eb27b74..e89557b 100755 --- a/tests/test_element_ops_mixin.py +++ b/tests/test_element_ops_mixin.py @@ -28,6 +28,7 @@ class TestElementWindow(ElementOperationsMixin, QMainWindow): self.project = Mock() self.project.history = CommandHistory() self.project.asset_manager = Mock() + self.project.folder_path = "/tmp/test_project" # Track method calls self._update_view_called = False