Refactor ProjectView to improve thumbnail layout and item display: set default items per row to 2, implement dynamic thumbnail size calculation, and update keyboard shortcuts for adjusting items per row. Enhance thumbnail caching mechanism to optimize performance and maintain aspect ratio during resizing.

This commit is contained in:
2025-09-16 10:02:53 +02:00
parent 8c4663c4ef
commit b8899004f3

View File

@@ -57,13 +57,21 @@ class ProjectView:
self.progress_data = {}
self.selected_index = 0
self.scroll_offset = 0
self.items_per_row = 4
self.items_per_row = 2 # Default to 2 items per row
self.window_width = 1200
self.window_height = 800
self._load_video_files()
self._load_progress_data()
def _calculate_thumbnail_size(self, window_width: int) -> tuple:
"""Calculate thumbnail size based on items per row and window width"""
available_width = window_width - self.THUMBNAIL_MARGIN
item_width = (available_width - (self.items_per_row - 1) * self.THUMBNAIL_MARGIN) // self.items_per_row
thumbnail_width = max(50, item_width) # Minimum 50px width
thumbnail_height = int(thumbnail_width * self.THUMBNAIL_SIZE[1] / self.THUMBNAIL_SIZE[0]) # Maintain aspect ratio
return (thumbnail_width, thumbnail_height)
def _load_video_files(self):
"""Load all video files from directory"""
self.video_files = []
@@ -106,10 +114,15 @@ class ProjectView:
return self.progress_data[video_path]['progress']
return 0.0
def get_thumbnail_for_video(self, video_path: Path) -> np.ndarray:
def get_thumbnail_for_video(self, video_path: Path, size: tuple = None) -> np.ndarray:
"""Get thumbnail for a video, generating it if needed"""
if video_path in self.thumbnails:
return self.thumbnails[video_path]
if size is None:
size = self.THUMBNAIL_SIZE
# Use a cache key that includes the size
cache_key = (video_path, size)
if cache_key in self.thumbnails:
return self.thumbnails[cache_key]
# Generate thumbnail on demand
try:
@@ -121,8 +134,8 @@ class ProjectView:
cap.set(cv2.CAP_PROP_POS_FRAMES, middle_frame)
ret, frame = cap.read()
if ret:
thumbnail = cv2.resize(frame, self.THUMBNAIL_SIZE)
self.thumbnails[video_path] = thumbnail
thumbnail = cv2.resize(frame, size)
self.thumbnails[cache_key] = thumbnail
cap.release()
return thumbnail
cap.release()
@@ -130,7 +143,7 @@ class ProjectView:
print(f"Error generating thumbnail for {video_path.name}: {e}")
# Return a placeholder if thumbnail generation failed
placeholder = np.full((self.THUMBNAIL_SIZE[1], self.THUMBNAIL_SIZE[0], 3),
placeholder = np.full((size[1], size[0], 3),
self.THUMBNAIL_BG_COLOR, dtype=np.uint8)
return placeholder
@@ -164,15 +177,16 @@ class ProjectView:
cv2.putText(canvas, text, (text_x, text_y), font, 1.0, self.TEXT_COLOR, 2)
return canvas
# Calculate layout - dynamically determine items per row based on thumbnail size
# Calculate how many thumbnails can fit in the window width
available_width = actual_width - self.THUMBNAIL_MARGIN # Account for left margin
items_per_row = max(1, (available_width + self.THUMBNAIL_MARGIN) // (self.THUMBNAIL_SIZE[0] + self.THUMBNAIL_MARGIN))
items_per_row = min(items_per_row, len(self.video_files)) # Don't exceed number of videos
# Calculate layout - use fixed items_per_row and calculate thumbnail size to fit
items_per_row = min(self.items_per_row, len(self.video_files)) # Don't exceed number of videos
# Calculate thumbnail size to fit the desired number of items per row
thumbnail_width, thumbnail_height = self._calculate_thumbnail_size(actual_width)
# Calculate item height dynamically based on thumbnail size
item_height = thumbnail_height + self.PROGRESS_BAR_HEIGHT + self.TEXT_HEIGHT + self.THUMBNAIL_MARGIN
item_width = (actual_width - (items_per_row + 1) * self.THUMBNAIL_MARGIN) // items_per_row
thumbnail_width = min(item_width, self.THUMBNAIL_SIZE[0])
thumbnail_height = int(thumbnail_width * self.THUMBNAIL_SIZE[1] / self.THUMBNAIL_SIZE[0])
# Draw videos in grid
for i, video_path in enumerate(self.video_files):
@@ -182,12 +196,12 @@ class ProjectView:
# Skip if scrolled out of view
if row < self.scroll_offset:
continue
if row > self.scroll_offset + (actual_height // self.ITEM_HEIGHT):
if row > self.scroll_offset + (actual_height // item_height):
break
# Calculate position
x = self.THUMBNAIL_MARGIN + col * (item_width + self.THUMBNAIL_MARGIN)
y = self.THUMBNAIL_MARGIN + (row - self.scroll_offset) * self.ITEM_HEIGHT
y = self.THUMBNAIL_MARGIN + (row - self.scroll_offset) * item_height
# Draw thumbnail background
cv2.rectangle(canvas,
@@ -203,9 +217,9 @@ class ProjectView:
self.SELECTED_COLOR, 3)
# Draw thumbnail
thumbnail = self.get_thumbnail_for_video(video_path)
# Resize thumbnail to fit
resized_thumbnail = cv2.resize(thumbnail, (thumbnail_width, thumbnail_height))
thumbnail = self.get_thumbnail_for_video(video_path, (thumbnail_width, thumbnail_height))
# Thumbnail is already the correct size, no need to resize
resized_thumbnail = thumbnail
# Ensure thumbnail doesn't exceed canvas bounds
end_y = min(y + thumbnail_height, actual_height)
@@ -257,16 +271,11 @@ class ProjectView:
cv2.putText(canvas, progress_text, (progress_text_x, text_y),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, self.TEXT_COLOR, 1)
# Calculate current items per row for display
available_width = actual_width - self.THUMBNAIL_MARGIN
current_items_per_row = max(1, (available_width + self.THUMBNAIL_MARGIN) // (self.THUMBNAIL_SIZE[0] + self.THUMBNAIL_MARGIN))
current_items_per_row = min(current_items_per_row, len(self.video_files))
# Draw instructions
instructions = [
"Project View - Videos in current directory",
"WASD: Navigate | E: Open video | Q: Larger thumbnails | Y: Smaller thumbnails | q: Quit | ESC: Back to editor",
f"Showing {len(self.video_files)} videos | {current_items_per_row} per row | Thumbnail: {self.THUMBNAIL_SIZE[0]}x{self.THUMBNAIL_SIZE[1]}"
"WASD: Navigate | E: Open video | Q: More items per row | Y: Fewer items per row | q: Quit | ESC: Back to editor",
f"Showing {len(self.video_files)} videos | {items_per_row} per row | Thumbnail: {thumbnail_width}x{thumbnail_height}"
]
for i, instruction in enumerate(instructions):
@@ -286,38 +295,14 @@ class ProjectView:
if self.video_files and 0 <= self.selected_index < len(self.video_files):
return f"open_video:{self.video_files[self.selected_index]}"
elif key == ord('w') or key == ord('W'): # W - Up
# Calculate current items per row dynamically
try:
window_rect = cv2.getWindowImageRect("Project View")
if window_rect[2] > 0:
available_width = window_rect[2] - self.THUMBNAIL_MARGIN
else:
available_width = self.window_width - self.THUMBNAIL_MARGIN
except:
available_width = self.window_width - self.THUMBNAIL_MARGIN
current_items_per_row = max(1, (available_width + self.THUMBNAIL_MARGIN) // (self.THUMBNAIL_SIZE[0] + self.THUMBNAIL_MARGIN))
current_items_per_row = min(current_items_per_row, len(self.video_files))
current_items_per_row = min(self.items_per_row, len(self.video_files))
if self.selected_index >= current_items_per_row:
self.selected_index -= current_items_per_row
else:
self.selected_index = 0
self._update_scroll()
elif key == ord('s') or key == ord('S'): # S - Down
# Calculate current items per row dynamically
try:
window_rect = cv2.getWindowImageRect("Project View")
if window_rect[2] > 0:
available_width = window_rect[2] - self.THUMBNAIL_MARGIN
else:
available_width = self.window_width - self.THUMBNAIL_MARGIN
except:
available_width = self.window_width - self.THUMBNAIL_MARGIN
current_items_per_row = max(1, (available_width + self.THUMBNAIL_MARGIN) // (self.THUMBNAIL_SIZE[0] + self.THUMBNAIL_MARGIN))
current_items_per_row = min(current_items_per_row, len(self.video_files))
current_items_per_row = min(self.items_per_row, len(self.video_files))
if self.selected_index + current_items_per_row < len(self.video_files):
self.selected_index += current_items_per_row
else:
@@ -331,20 +316,14 @@ class ProjectView:
if self.selected_index < len(self.video_files) - 1:
self.selected_index += 1
self._update_scroll()
elif key == ord('Q'): # uppercase Q - Make thumbnails LARGER
# Increase thumbnail size (no maximum limit)
new_width = self.THUMBNAIL_SIZE[0] + 20
new_height = int(new_width * self.THUMBNAIL_SIZE[1] / self.THUMBNAIL_SIZE[0])
self.THUMBNAIL_SIZE = (new_width, new_height)
self.ITEM_HEIGHT = self.THUMBNAIL_SIZE[1] + self.PROGRESS_BAR_HEIGHT + self.TEXT_HEIGHT + self.THUMBNAIL_MARGIN
print(f"Thumbnail size: {self.THUMBNAIL_SIZE}")
elif key == ord('y') or key == ord('Y'): # Y - Make thumbnails SMALLER
# Decrease thumbnail size (minimum 20x15 to prevent division by zero)
new_width = max(20, self.THUMBNAIL_SIZE[0] - 20)
new_height = int(new_width * self.THUMBNAIL_SIZE[1] / self.THUMBNAIL_SIZE[0])
self.THUMBNAIL_SIZE = (new_width, new_height)
self.ITEM_HEIGHT = self.THUMBNAIL_SIZE[1] + self.PROGRESS_BAR_HEIGHT + self.TEXT_HEIGHT + self.THUMBNAIL_MARGIN
print(f"Thumbnail size: {self.THUMBNAIL_SIZE}")
elif key == ord('Q'): # uppercase Q - More items per row (smaller thumbnails)
if self.items_per_row < len(self.video_files):
self.items_per_row += 1
print(f"Items per row: {self.items_per_row}")
elif key == ord('y') or key == ord('Y'): # Y - Fewer items per row (larger thumbnails)
if self.items_per_row > 1:
self.items_per_row -= 1
print(f"Items per row: {self.items_per_row}")
return "none"
@@ -353,24 +332,28 @@ class ProjectView:
if not self.video_files:
return
# Calculate current items per row dynamically
# Use fixed items per row
items_per_row = min(self.items_per_row, len(self.video_files))
# Get window dimensions for calculations
try:
window_rect = cv2.getWindowImageRect("Project View")
if window_rect[2] > 0 and window_rect[3] > 0:
available_width = window_rect[2] - self.THUMBNAIL_MARGIN
window_width = window_rect[2]
window_height = window_rect[3]
else:
available_width = self.window_width - self.THUMBNAIL_MARGIN
window_width = self.window_width
window_height = self.window_height
except:
available_width = self.window_width - self.THUMBNAIL_MARGIN
window_width = self.window_width
window_height = self.window_height
items_per_row = max(1, (available_width + self.THUMBNAIL_MARGIN) // (self.THUMBNAIL_SIZE[0] + self.THUMBNAIL_MARGIN))
items_per_row = min(items_per_row, len(self.video_files))
# Calculate thumbnail size and item height dynamically
thumbnail_width, thumbnail_height = self._calculate_thumbnail_size(window_width)
item_height = thumbnail_height + self.PROGRESS_BAR_HEIGHT + self.TEXT_HEIGHT + self.THUMBNAIL_MARGIN
selected_row = self.selected_index // items_per_row
visible_rows = max(1, window_height // self.ITEM_HEIGHT)
visible_rows = max(1, window_height // item_height)
# Calculate how many rows we can actually show
total_rows = (len(self.video_files) + items_per_row - 1) // items_per_row