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:
129
croppa/main.py
129
croppa/main.py
@@ -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
|
||||
|
Reference in New Issue
Block a user