Stable with arrow nav
everything working with new keyboard arrow keys nav for next/prev image. Will add png chunk metadata next.
This commit is contained in:
Binary file not shown.
Binary file not shown.
-25
@@ -1,25 +0,0 @@
|
||||
# helpers.py
|
||||
from PySide6.QtGui import QPixmap
|
||||
|
||||
def load_image(path: str) -> QPixmap:
|
||||
"""
|
||||
Load an image from disk and return a QPixmap.
|
||||
In the future, this can also handle scaling, caching, etc.
|
||||
"""
|
||||
pixmap = QPixmap(path)
|
||||
if pixmap.isNull():
|
||||
raise FileNotFoundError(f"Could not load image: {path}")
|
||||
return pixmap
|
||||
|
||||
|
||||
def get_fake_metadata(path: str) -> dict:
|
||||
"""
|
||||
Temporary stub: returns fake metadata.
|
||||
Replace later with real EXIF/PNG chunk parsing.
|
||||
"""
|
||||
return {
|
||||
"Filename": path,
|
||||
"Width": 1920,
|
||||
"Height": 1080,
|
||||
"Format": "PNG"
|
||||
}
|
||||
+15
-2
@@ -1,9 +1,10 @@
|
||||
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
|
||||
from PySide6.QtGui import QPixmap, QDragEnterEvent, QDropEvent, QPainter
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtOpenGLWidgets import QOpenGLWidget # ← ADD THIS IMPORT
|
||||
from PySide6.QtOpenGLWidgets import QOpenGLWidget
|
||||
import os
|
||||
|
||||
"""this defines the 'canvas' class, meaning the area where images are dropped and displayed"""
|
||||
|
||||
class ImageCanvas(QGraphicsView):
|
||||
# Signal emitted when a valid file is dropped
|
||||
@@ -33,9 +34,21 @@ class ImageCanvas(QGraphicsView):
|
||||
self.setRenderHint(QPainter.Antialiasing, True)
|
||||
self.setRenderHint(QPainter.SmoothPixmapTransform, True)
|
||||
|
||||
# ← ADD OPENGL ACCELERATION HERE
|
||||
self.setViewport(QOpenGLWidget()) # Hardware-accelerated rendering
|
||||
|
||||
# Enable keyboard focus for the canvas
|
||||
self.setFocusPolicy(Qt.StrongFocus)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""Forward arrow keys to main window for navigation"""
|
||||
if event.key() in (Qt.Key_Right, Qt.Key_Left):
|
||||
main_window = self.window() # Get the actual main window, not just the immediate parent
|
||||
if main_window:
|
||||
main_window.keyPressEvent(event)
|
||||
event.accept()
|
||||
else:
|
||||
super().keyPressEvent(event)
|
||||
|
||||
# --- Drag & Drop Events ---
|
||||
def dragEnterEvent(self, event: QDragEnterEvent):
|
||||
if event.mimeData().hasUrls():
|
||||
|
||||
+89
-192
@@ -5,35 +5,36 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
from image_canvas import ImageCanvas
|
||||
from image_folder import ImageFolder
|
||||
from PySide6.QtWidgets import QLabel
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtCore import QTimer
|
||||
import os
|
||||
from PySide6.QtCore import Qt, QTimer, QEvent
|
||||
|
||||
import sys
|
||||
|
||||
import helpers
|
||||
|
||||
|
||||
class MetaViewApp(QMainWindow):
|
||||
from metaview_logic import MetaViewLogicMixin
|
||||
|
||||
class MetaViewApp(MetaViewLogicMixin, QMainWindow):
|
||||
# Now inherits from both QMainWindow and our logic mixin
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Image Viewer Layout")
|
||||
self.resize(1400, 900)
|
||||
self.folder_model = None
|
||||
self.setWindowTitle("Image Viewer GL Layout")
|
||||
|
||||
# Image cache
|
||||
self.image_cache = {} # Dictionary to cache loaded images
|
||||
self.cache_size = 5 # Number of images to keep cached
|
||||
# --- 4:3 Optimized Window Size ---
|
||||
# Calculate for 1024x768 image + UI elements
|
||||
self.resize(1400, 900) # Wider window to accommodate right sidebar
|
||||
|
||||
self.folder_model = None
|
||||
self.image_cache = {}
|
||||
self.cache_size = 5
|
||||
|
||||
# --- Design reference sizes (for proportional scaling) ---
|
||||
self.design_canvas_w = 672
|
||||
self.design_canvas_h = 504
|
||||
self.design_thumb_w = self.design_canvas_w // 5 # 134
|
||||
self.design_thumb_h = self.design_thumb_w * 3 // 4 # 100
|
||||
self.design_total_h = (
|
||||
self.design_canvas_h + self.design_thumb_h + 40 + 20 + 20
|
||||
)
|
||||
# --- Design reference sizes for 4:3 ---
|
||||
self.design_canvas_w = 1024 # Target image width
|
||||
self.design_canvas_h = 768 # Target image height
|
||||
self.design_thumb_w = 120 # Thumbnail width
|
||||
self.design_thumb_h = 90 # Thumbnail height (4:3)
|
||||
|
||||
self.scale_factor = 1.0
|
||||
|
||||
# ---- Central Widget ----
|
||||
@@ -42,34 +43,49 @@ class MetaViewApp(QMainWindow):
|
||||
|
||||
# ---- Master Grid Layout ----
|
||||
self.main_layout = QGridLayout(central)
|
||||
self.main_layout.setContentsMargins(10, 20, 30, 20)
|
||||
self.main_layout.setContentsMargins(10, 20, 10, 20)
|
||||
self.main_layout.setSpacing(10)
|
||||
|
||||
# Column weights like Tkinter's columnconfigure
|
||||
self.main_layout.setColumnStretch(0, 0) # left sidebar
|
||||
self.main_layout.setColumnStretch(1, 5) # center area
|
||||
self.main_layout.setColumnStretch(2, 3) # right panel future
|
||||
# Column weights - center area for image, right for metadata
|
||||
self.main_layout.setColumnStretch(0, 0) # left aesthetic spacer (fixed)
|
||||
self.main_layout.setColumnStretch(1, 3) # center image area (main content)
|
||||
self.main_layout.setColumnStretch(2, 1) # right metadata panel (~1/4 width)
|
||||
self.main_layout.setRowStretch(0, 1) # main row expands
|
||||
|
||||
# --- Left Sidebar ---
|
||||
self.left_margin_frame = QFrame()
|
||||
left_layout = QVBoxLayout(self.left_margin_frame)
|
||||
left_layout.addWidget(QLabel("Sidebar"))
|
||||
self.main_layout.addWidget(self.left_margin_frame, 0, 0)
|
||||
# --- Left Spacer (Aesthetic) ---
|
||||
self.left_spacer = QFrame()
|
||||
self.left_spacer.setFixedWidth(40) # Small fixed width spacer
|
||||
self.left_spacer.setStyleSheet("background: transparent;")
|
||||
self.main_layout.addWidget(self.left_spacer, 0, 0)
|
||||
|
||||
# --- Center Image Area ---
|
||||
self.center_group = QGroupBox("Image View and Controls")
|
||||
self.center_group = QGroupBox("Image View")
|
||||
self.center_group.setStyleSheet("""
|
||||
QGroupBox {
|
||||
border: 2px solid #333333;
|
||||
border-radius: 4px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top left;
|
||||
left: 10px;
|
||||
padding: 0 5px 0 5px;
|
||||
background: palette(window);
|
||||
}
|
||||
""")
|
||||
|
||||
self.main_layout.addWidget(self.center_group, 0, 1)
|
||||
center_layout = QGridLayout(self.center_group)
|
||||
center_layout.setRowStretch(0, 5) # image view
|
||||
center_layout.setRowStretch(0, 5) # image view (most space)
|
||||
center_layout.setRowStretch(1, 1) # filmstrip
|
||||
center_layout.setRowStretch(2, 0) # buttons
|
||||
center_layout.setRowStretch(3, 0) # path display later
|
||||
center_layout.setColumnStretch(0, 1)
|
||||
|
||||
# ---- Image Canvas Equivalent ----
|
||||
# ---- Image Canvas ----
|
||||
self.image_view = ImageCanvas()
|
||||
self.image_view.fileDropped.connect(self.load_image_from_path)
|
||||
self.image_view.setMinimumSize(800, 600) # 4:3 minimum
|
||||
center_layout.addWidget(self.image_view, 0, 0)
|
||||
|
||||
# ---- Filmstrip Frame ----
|
||||
@@ -85,11 +101,12 @@ class MetaViewApp(QMainWindow):
|
||||
# Thumbnail scroller
|
||||
self.thumb_scroll_area = QScrollArea()
|
||||
self.thumb_scroll_area.setWidgetResizable(True)
|
||||
self.thumb_scroll_area.setMinimumHeight(100)
|
||||
self.thumb_container = QWidget()
|
||||
self.thumb_layout = QHBoxLayout(self.thumb_container)
|
||||
self.thumb_scroll_area.setWidget(self.thumb_container)
|
||||
|
||||
filmstrip_layout.addWidget(self.thumb_scroll_area)
|
||||
|
||||
center_layout.addWidget(self.filmstrip_frame, 1, 0)
|
||||
|
||||
# ---- Button Frame ----
|
||||
@@ -106,176 +123,56 @@ class MetaViewApp(QMainWindow):
|
||||
|
||||
center_layout.addWidget(self.button_frame, 2, 0)
|
||||
|
||||
open_btn.clicked.connect(self.open_test_image)
|
||||
# --- Right Metadata Panel ---
|
||||
self.right_metadata = QGroupBox("Metadata")
|
||||
self.right_metadata.setMinimumWidth(300) # ~1/4 of window width
|
||||
metadata_layout = QVBoxLayout(self.right_metadata)
|
||||
|
||||
# Placeholder metadata content
|
||||
metadata_layout.addWidget(QLabel("File Information:"))
|
||||
self.file_info_label = QLabel("No image loaded")
|
||||
metadata_layout.addWidget(self.file_info_label)
|
||||
|
||||
metadata_layout.addWidget(QLabel("AI Parameters:"))
|
||||
self.ai_params_label = QLabel("Waiting for image...")
|
||||
metadata_layout.addWidget(self.ai_params_label)
|
||||
|
||||
metadata_layout.addStretch() # Push content to top
|
||||
|
||||
self.main_layout.addWidget(self.right_metadata, 0, 2)
|
||||
|
||||
# ---- Connect Signals ----
|
||||
open_btn.clicked.connect(self.open_image_file)
|
||||
next_btn.clicked.connect(self.show_next_image)
|
||||
prev_btn.clicked.connect(self.show_prev_image)
|
||||
|
||||
# REMOVED THE PROBLEMATIC LINE: self.resizeEvent(None)
|
||||
# Instead, use a timer to initialize after UI is fully built
|
||||
# Initialize after UI is built
|
||||
QTimer.singleShot(100, self.initialize_scale)
|
||||
|
||||
def initialize_scale(self):
|
||||
"""Initialize scale factor after UI is fully built"""
|
||||
self.update_scale_factor()
|
||||
if self.folder_model:
|
||||
self.populate_filmstrip()
|
||||
|
||||
|
||||
def update_scale_factor(self):
|
||||
"""Calculate scale factor based on current sizes"""
|
||||
if not hasattr(self, "image_view") or not hasattr(self, "filmstrip_frame"):
|
||||
return
|
||||
# Install event filter on THIS window only
|
||||
self.installEventFilter(self)
|
||||
|
||||
# Current available width and height
|
||||
avail_w = self.image_view.width()
|
||||
avail_h = self.image_view.height()
|
||||
|
||||
print(f"Available space for image: {avail_w}x{avail_h}")
|
||||
|
||||
# Compute scale factors relative to design sizes
|
||||
scale_w = avail_w / self.design_canvas_w
|
||||
scale_h = avail_h / self.design_total_h
|
||||
self.scale_factor = min(scale_w, scale_h)
|
||||
print(f"Scale factor updated: {self.scale_factor}")
|
||||
def eventFilter(self, obj, event):
|
||||
if event.type() == QEvent.KeyPress:
|
||||
print(f"🎹 EVENT FILTER: Key {event.key()} in {type(obj).__name__}")
|
||||
if event.key() in (Qt.Key_Right, Qt.Key_Left):
|
||||
print("🎹 EVENT FILTER: Calling keyPressEvent directly")
|
||||
result = self.keyPressEvent(event)
|
||||
print(f"🎹 EVENT FILTER: keyPressEvent returned {result}")
|
||||
return True # Event handled, stop propagation
|
||||
return super().eventFilter(obj, event)
|
||||
|
||||
|
||||
# --- Example method using helpers ---
|
||||
def open_test_image(self):
|
||||
"""
|
||||
Demo function: load a test image and print metadata.
|
||||
"""
|
||||
path = "test.png" # placeholder
|
||||
try:
|
||||
pixmap = helpers.load_image(path)
|
||||
meta = helpers.get_fake_metadata(path)
|
||||
print("Metadata:", meta)
|
||||
except FileNotFoundError as e:
|
||||
print(e)
|
||||
|
||||
#load image with path
|
||||
def load_image_from_path(self, file_path):
|
||||
folder = os.path.dirname(file_path)
|
||||
self.folder_model = ImageFolder(folder, start_file=file_path)
|
||||
|
||||
# Clear cache when folder changes
|
||||
self.image_cache.clear()
|
||||
|
||||
current = self.folder_model.current()
|
||||
if current:
|
||||
print("Displaying:", current["name"])
|
||||
self.display_image(current["path"])
|
||||
self.populate_filmstrip()
|
||||
self.preload_adjacent_images()
|
||||
|
||||
# New method for displaying images
|
||||
def display_image(self, path):
|
||||
"""Display image from cache or load it"""
|
||||
if path in self.image_cache:
|
||||
# Use cached pixmap
|
||||
print(f"Using cached image: {os.path.basename(path)}")
|
||||
self.image_view.load_image_from_pixmap(self.image_cache[path])
|
||||
else:
|
||||
# Load and cache
|
||||
pixmap = QPixmap(path)
|
||||
if not pixmap.isNull():
|
||||
self.image_cache[path] = pixmap
|
||||
self.image_view.load_image(path)
|
||||
|
||||
# New method for preloading
|
||||
def preload_adjacent_images(self):
|
||||
"""Pre-load images around current index"""
|
||||
if not self.folder_model or not self.folder_model.files:
|
||||
return
|
||||
|
||||
files = self.folder_model.files
|
||||
current_idx = self.folder_model.index
|
||||
|
||||
# Pre-load 2 images on each side
|
||||
for offset in range(-2, 3):
|
||||
if offset == 0:
|
||||
continue # Skip current (already loaded)
|
||||
|
||||
idx = (current_idx + offset) % len(files)
|
||||
path = files[idx]["path"]
|
||||
|
||||
if path not in self.image_cache:
|
||||
# Load into cache but don't display
|
||||
print(f"Pre-loading: {os.path.basename(path)}")
|
||||
pixmap = QPixmap(path)
|
||||
if not pixmap.isNull():
|
||||
self.image_cache[path] = pixmap
|
||||
|
||||
def show_next_image(self):
|
||||
if self.folder_model:
|
||||
current = self.folder_model.next()
|
||||
if current:
|
||||
self.display_image(current["path"])
|
||||
self.populate_filmstrip()
|
||||
self.preload_adjacent_images()
|
||||
|
||||
def show_prev_image(self):
|
||||
if self.folder_model:
|
||||
current = self.folder_model.prev()
|
||||
if current:
|
||||
self.display_image(current["path"])
|
||||
self.populate_filmstrip()
|
||||
self.preload_adjacent_images()
|
||||
|
||||
# Mouse wheel handler
|
||||
def wheelEvent(self, event):
|
||||
if event.angleDelta().y() > 0:
|
||||
# wheel scrolled up
|
||||
self.show_prev_image()
|
||||
else:
|
||||
# wheel scrolled down
|
||||
self.show_next_image()
|
||||
|
||||
event.accept()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
self.update_scale_factor()
|
||||
# Rebuild thumbs at new scale
|
||||
if self.folder_model:
|
||||
self.populate_filmstrip()
|
||||
|
||||
def populate_filmstrip(self):
|
||||
# Clear old thumbnails
|
||||
for i in reversed(range(self.thumb_layout.count())):
|
||||
item = self.thumb_layout.takeAt(i)
|
||||
if widget := item.widget():
|
||||
widget.deleteLater()
|
||||
|
||||
if not self.folder_model:
|
||||
return
|
||||
|
||||
# Scale design size by current factor
|
||||
thumb_w = max(64, int(self.design_thumb_w * self.scale_factor))
|
||||
thumb_h = max(48, int(self.design_thumb_h * self.scale_factor))
|
||||
|
||||
# Show 5 thumbnails centered on current index
|
||||
center_index = self.folder_model.index
|
||||
files = self.folder_model.files
|
||||
if not files:
|
||||
return
|
||||
|
||||
for offset in range(-2, 3):
|
||||
idx = (center_index + offset) % len(files)
|
||||
file_path = files[idx]["path"]
|
||||
|
||||
pix = QPixmap(file_path).scaled(
|
||||
thumb_w, thumb_h,
|
||||
Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation
|
||||
)
|
||||
lbl = QLabel()
|
||||
lbl.setPixmap(pix)
|
||||
lbl.setAlignment(Qt.AlignCenter)
|
||||
|
||||
self.thumb_layout.addWidget(lbl)
|
||||
def check_focus(self):
|
||||
"""Check what has focus"""
|
||||
print(f"🎯 Current focus widget: {self.focusWidget()}")
|
||||
print(f"🎯 Main window has focus: {self.hasFocus()}")
|
||||
print(f"🎯 Image view has focus: {self.image_view.hasFocus()}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
window = MetaViewApp()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
+83
-195
@@ -1,169 +1,55 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication, QMainWindow, QWidget, QFrame, QGroupBox,
|
||||
QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QPushButton,
|
||||
QScrollArea
|
||||
)
|
||||
|
||||
|
||||
# metaview_logic.py
|
||||
from PySide6.QtCore import Qt
|
||||
from image_canvas import ImageCanvas
|
||||
from image_folder import ImageFolder
|
||||
from PySide6.QtWidgets import QLabel
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtCore import QTimer
|
||||
from PySide6.QtGui import QPainter
|
||||
from ui_styles import create_styled_groupbox
|
||||
from PySide6.QtWidgets import QFileDialog
|
||||
from pathlib import Path
|
||||
|
||||
import struct
|
||||
import zlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
import helpers
|
||||
class MetaViewLogicMixin:
|
||||
"""All the behavior logic goes here - no UI creation"""
|
||||
|
||||
# Mouse wheel handler
|
||||
def wheelEvent(self, event):
|
||||
if event.angleDelta().y() > 0:
|
||||
# wheel scrolled up
|
||||
self.show_prev_image()
|
||||
else:
|
||||
# wheel scrolled down
|
||||
self.show_next_image()
|
||||
|
||||
class MetaViewApp(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Image Viewer GL Layout")
|
||||
|
||||
# --- 4:3 Optimized Window Size ---
|
||||
# Calculate for 1024x768 image + UI elements
|
||||
self.resize(1400, 900) # Wider window to accommodate right sidebar
|
||||
|
||||
self.folder_model = None
|
||||
self.image_cache = {}
|
||||
self.cache_size = 5
|
||||
|
||||
# --- Design reference sizes for 4:3 ---
|
||||
self.design_canvas_w = 1024 # Target image width
|
||||
self.design_canvas_h = 768 # Target image height
|
||||
self.design_thumb_w = 120 # Thumbnail width
|
||||
self.design_thumb_h = 90 # Thumbnail height (4:3)
|
||||
|
||||
self.scale_factor = 1.0
|
||||
|
||||
# ---- Central Widget ----
|
||||
central = QWidget()
|
||||
self.setCentralWidget(central)
|
||||
|
||||
# ---- Master Grid Layout ----
|
||||
self.main_layout = QGridLayout(central)
|
||||
self.main_layout.setContentsMargins(10, 20, 10, 20)
|
||||
self.main_layout.setSpacing(10)
|
||||
|
||||
# Column weights - center area for image, right for metadata
|
||||
self.main_layout.setColumnStretch(0, 0) # left aesthetic spacer (fixed)
|
||||
self.main_layout.setColumnStretch(1, 3) # center image area (main content)
|
||||
self.main_layout.setColumnStretch(2, 1) # right metadata panel (~1/4 width)
|
||||
self.main_layout.setRowStretch(0, 1) # main row expands
|
||||
|
||||
# --- Left Spacer (Aesthetic) ---
|
||||
self.left_spacer = QFrame()
|
||||
self.left_spacer.setFixedWidth(40) # Small fixed width spacer
|
||||
self.left_spacer.setStyleSheet("background: transparent;")
|
||||
self.main_layout.addWidget(self.left_spacer, 0, 0)
|
||||
|
||||
# --- Center Image Area ---
|
||||
self.center_group = QGroupBox("Image View")
|
||||
# Keep everything default Qt style, just make borders darker
|
||||
#self.center_group.setStyleSheet("QGroupBox { border: 2px solid red; }")
|
||||
self.center_group.setStyleSheet("""
|
||||
QGroupBox {
|
||||
border: 2px solid #333333;
|
||||
border-radius: 4px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top left;
|
||||
left: 10px;
|
||||
padding: 0 5px 0 5px;
|
||||
background: palette(window);
|
||||
}
|
||||
""")
|
||||
#self.right_metadata.setStyleSheet("QGroupBox { border-color: #222222; }")
|
||||
self.main_layout.addWidget(self.center_group, 0, 1)
|
||||
center_layout = QGridLayout(self.center_group)
|
||||
center_layout.setRowStretch(0, 5) # image view (most space)
|
||||
center_layout.setRowStretch(1, 1) # filmstrip
|
||||
center_layout.setRowStretch(2, 0) # buttons
|
||||
center_layout.setColumnStretch(0, 1)
|
||||
|
||||
# ---- Image Canvas ----
|
||||
self.image_view = ImageCanvas()
|
||||
self.image_view.fileDropped.connect(self.load_image_from_path)
|
||||
self.image_view.setMinimumSize(800, 600) # 4:3 minimum
|
||||
center_layout.addWidget(self.image_view, 0, 0)
|
||||
|
||||
# ---- Filmstrip Frame ----
|
||||
self.filmstrip_frame = QFrame()
|
||||
filmstrip_layout = QVBoxLayout(self.filmstrip_frame)
|
||||
|
||||
# Top separator
|
||||
sep = QFrame()
|
||||
sep.setFrameShape(QFrame.HLine)
|
||||
sep.setStyleSheet("color: #252222;")
|
||||
filmstrip_layout.addWidget(sep)
|
||||
|
||||
# Thumbnail scroller
|
||||
self.thumb_scroll_area = QScrollArea()
|
||||
self.thumb_scroll_area.setWidgetResizable(True)
|
||||
self.thumb_scroll_area.setMinimumHeight(100)
|
||||
self.thumb_container = QWidget()
|
||||
self.thumb_layout = QHBoxLayout(self.thumb_container)
|
||||
self.thumb_scroll_area.setWidget(self.thumb_container)
|
||||
filmstrip_layout.addWidget(self.thumb_scroll_area)
|
||||
|
||||
center_layout.addWidget(self.filmstrip_frame, 1, 0)
|
||||
|
||||
# ---- Button Frame ----
|
||||
self.button_frame = QFrame()
|
||||
btn_layout = QHBoxLayout(self.button_frame)
|
||||
|
||||
next_btn = QPushButton("Next Image")
|
||||
prev_btn = QPushButton("Previous Image")
|
||||
open_btn = QPushButton("Open Image")
|
||||
|
||||
btn_layout.addWidget(next_btn)
|
||||
btn_layout.addWidget(prev_btn)
|
||||
btn_layout.addWidget(open_btn)
|
||||
|
||||
center_layout.addWidget(self.button_frame, 2, 0)
|
||||
|
||||
# --- Right Metadata Panel ---
|
||||
self.right_metadata = QGroupBox("Metadata")
|
||||
self.right_metadata.setMinimumWidth(300) # ~1/4 of window width
|
||||
metadata_layout = QVBoxLayout(self.right_metadata)
|
||||
|
||||
# Placeholder metadata content
|
||||
metadata_layout.addWidget(QLabel("File Information:"))
|
||||
self.file_info_label = QLabel("No image loaded")
|
||||
metadata_layout.addWidget(self.file_info_label)
|
||||
|
||||
metadata_layout.addWidget(QLabel("AI Parameters:"))
|
||||
self.ai_params_label = QLabel("Waiting for image...")
|
||||
metadata_layout.addWidget(self.ai_params_label)
|
||||
|
||||
metadata_layout.addStretch() # Push content to top
|
||||
|
||||
self.main_layout.addWidget(self.right_metadata, 0, 2)
|
||||
|
||||
# ---- Connect Signals ----
|
||||
open_btn.clicked.connect(self.open_test_image)
|
||||
next_btn.clicked.connect(self.show_next_image)
|
||||
prev_btn.clicked.connect(self.show_prev_image)
|
||||
|
||||
# Initialize after UI is built
|
||||
QTimer.singleShot(100, self.initialize_scale)
|
||||
event.accept()
|
||||
return super().wheelEvent(event) # ← FIXED: Only call super() once
|
||||
|
||||
# arrow / key nav
|
||||
def keyPressEvent(self, event):
|
||||
if event.key() == Qt.Key_Right:
|
||||
self.show_next_image()
|
||||
event.accept()
|
||||
elif event.key() == Qt.Key_Left:
|
||||
self.show_prev_image()
|
||||
event.accept()
|
||||
else:
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def initialize_scale(self):
|
||||
"""Initialize after UI is fully built"""
|
||||
self.update_scale_factor()
|
||||
if self.folder_model:
|
||||
self.populate_filmstrip()
|
||||
|
||||
|
||||
def update_scale_factor(self):
|
||||
"""Calculate scale factor based on current image view size"""
|
||||
if not hasattr(self, "image_view"):
|
||||
return
|
||||
|
||||
# Use only the image view's available space
|
||||
avail_w = self.image_view.width()
|
||||
avail_h = self.image_view.height()
|
||||
|
||||
@@ -172,20 +58,15 @@ class MetaViewApp(QMainWindow):
|
||||
scale_h = avail_h / self.design_canvas_h
|
||||
self.scale_factor = min(scale_w, scale_h)
|
||||
|
||||
# --- Image Handling ---
|
||||
def load_image_from_path(self, file_path):
|
||||
folder = os.path.dirname(file_path)
|
||||
self.folder_model = ImageFolder(folder, start_file=file_path)
|
||||
self.image_cache.clear()
|
||||
|
||||
current = self.folder_model.current()
|
||||
if current:
|
||||
print("Displaying:", current["name"])
|
||||
self.display_image(current["path"])
|
||||
self.update_metadata(current) # Update metadata panel
|
||||
self.populate_filmstrip()
|
||||
self.preload_adjacent_images()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
self.update_scale_factor()
|
||||
if self.folder_model:
|
||||
self.populate_filmstrip()
|
||||
|
||||
|
||||
#kind of a placeholder here for png chunks later
|
||||
def update_metadata(self, file_info):
|
||||
"""Update the right metadata panel with file information"""
|
||||
self.file_info_label.setText(
|
||||
@@ -202,15 +83,18 @@ class MetaViewApp(QMainWindow):
|
||||
"CFG: 7.5"
|
||||
)
|
||||
|
||||
def display_image(self, path):
|
||||
"""Display image from cache or load it"""
|
||||
if path in self.image_cache:
|
||||
self.image_view.load_image_from_pixmap(self.image_cache[path])
|
||||
else:
|
||||
pixmap = QPixmap(path)
|
||||
if not pixmap.isNull():
|
||||
self.image_cache[path] = pixmap
|
||||
self.image_view.load_image(path)
|
||||
def load_image_from_path(self, file_path):
|
||||
folder = os.path.dirname(file_path)
|
||||
self.folder_model = ImageFolder(folder, start_file=file_path)
|
||||
self.image_cache.clear()
|
||||
|
||||
current = self.folder_model.current()
|
||||
if current:
|
||||
print("Displaying:", current["name"])
|
||||
self.display_image(current["path"])
|
||||
self.update_metadata(current) # Update metadata panel
|
||||
self.populate_filmstrip()
|
||||
self.preload_adjacent_images()
|
||||
|
||||
def preload_adjacent_images(self):
|
||||
"""Pre-load images around current index"""
|
||||
@@ -230,6 +114,16 @@ class MetaViewApp(QMainWindow):
|
||||
if not pixmap.isNull():
|
||||
self.image_cache[path] = pixmap
|
||||
|
||||
def display_image(self, path):
|
||||
"""Display image from cache or load it"""
|
||||
if path in self.image_cache:
|
||||
self.image_view.load_image_from_pixmap(self.image_cache[path])
|
||||
else:
|
||||
pixmap = QPixmap(path)
|
||||
if not pixmap.isNull():
|
||||
self.image_cache[path] = pixmap
|
||||
self.image_view.load_image(path)
|
||||
|
||||
def show_next_image(self):
|
||||
if self.folder_model:
|
||||
current = self.folder_model.next()
|
||||
@@ -247,20 +141,8 @@ class MetaViewApp(QMainWindow):
|
||||
self.update_metadata(current)
|
||||
self.populate_filmstrip()
|
||||
self.preload_adjacent_images()
|
||||
|
||||
def wheelEvent(self, event):
|
||||
if event.angleDelta().y() > 0:
|
||||
self.show_prev_image()
|
||||
else:
|
||||
self.show_next_image()
|
||||
event.accept()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
self.update_scale_factor()
|
||||
if self.folder_model:
|
||||
self.populate_filmstrip()
|
||||
|
||||
|
||||
|
||||
def populate_filmstrip(self):
|
||||
# Clear old thumbnails
|
||||
for i in reversed(range(self.thumb_layout.count())):
|
||||
@@ -324,18 +206,24 @@ class MetaViewApp(QMainWindow):
|
||||
|
||||
self.thumb_layout.addWidget(lbl)
|
||||
|
||||
def open_test_image(self):
|
||||
path = "test.png"
|
||||
try:
|
||||
pixmap = helpers.load_image(path)
|
||||
meta = helpers.get_fake_metadata(path)
|
||||
print("Metadata:", meta)
|
||||
except FileNotFoundError as e:
|
||||
print(e)
|
||||
from PySide6.QtWidgets import QFileDialog
|
||||
|
||||
def open_image_file(self):
|
||||
"""Open file dialog to select an image"""
|
||||
file_dialog = QFileDialog(self)
|
||||
file_dialog.setWindowTitle("Open Image File")
|
||||
file_dialog.setNameFilter("Images (*.png *.jpg *.jpeg *.bmp *.gif *.tiff *.webp)")
|
||||
file_dialog.setFileMode(QFileDialog.ExistingFile)
|
||||
|
||||
if file_dialog.exec():
|
||||
selected_files = file_dialog.selectedFiles()
|
||||
if selected_files:
|
||||
file_path = selected_files[0]
|
||||
self.load_image_from_path(file_path)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
window = MetaViewApp()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
Reference in New Issue
Block a user