feat: add split view functionality with image rotation and flipping controls

This commit is contained in:
2025-09-27 19:14:42 +02:00
parent a1c608f279
commit d86a6429f7
7 changed files with 202 additions and 18 deletions

View File

@@ -1,9 +1,10 @@
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QApplication, QMainWindow, QWidget, QVBoxLayout, QSplitter, QStackedWidget, QPushButton, QLabel
from PySide6.QtGui import QPixmap, QWheelEvent, QPainter, QBrush, QColor
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QApplication, QMainWindow, QWidget, QVBoxLayout, QSplitter, QStackedWidget, QPushButton, QLabel, QToolButton
from PySide6.QtGui import QEnterEvent, QPixmap, QWheelEvent, QPainter, QBrush, QColor, QIcon
from PySide6.QtCore import Qt, QSize, Signal, QEvent
import sys
from ui.widgets.placeholder_widget import PlaceholderWidget
class ZoomableImageView(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
@@ -11,18 +12,21 @@ class ZoomableImageView(QGraphicsView):
# Scena i element obrazu
self._scene = QGraphicsScene(self)
self.setScene(self._scene)
self._scene.setBackgroundBrush(QBrush(QColor(20, 20, 20))) # ciemne tło
self._scene.setBackgroundBrush(
QBrush(QColor(20, 20, 20))) # ciemne tło
self._pixmap_item = QGraphicsPixmapItem()
self._scene.addItem(self._pixmap_item)
# Ustawienia widoku
self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) # przesuwanie myszą
# przesuwanie myszą
self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
self.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
# Wyłączenie suwaków
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
# Parametry zoomu
self._zoom_factor = 1.25
@@ -53,6 +57,7 @@ class ZoomableImageView(QGraphicsView):
return
super().wheelEvent(event) # normalne przewijanie
class CameraPlaceholder(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
@@ -91,11 +96,13 @@ class CameraPlaceholder(QWidget):
self.camera_start_btn.setStyleSheet(style_sheet)
self.info_label = QLabel("Kliknij, aby uruchomić kamerę")
self.info_label.setStyleSheet("background-color: transparent; color: #CECECE; font-size: 18px;")
self.info_label.setStyleSheet(
"background-color: transparent; color: #CECECE; font-size: 18px;")
self.info_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addStretch()
layout.addWidget(self.camera_start_btn, alignment=Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.camera_start_btn,
alignment=Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.info_label)
layout.addStretch()
self.setLayout(layout)
@@ -103,6 +110,112 @@ class CameraPlaceholder(QWidget):
def set_info_text(self, text: str):
self.info_label.setText(text)
class ViewWithOverlay(QWidget):
toggleOrientation = Signal()
swapViews = Signal()
rotateCW = Signal()
rotateCCW = Signal()
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
self.viewer = ZoomableImageView()
layout.addWidget(self.viewer)
icon_size = QSize(32, 32)
btn_size = (48, 48)
btn_style = """
background-color: rgba(255, 255, 255, 0.5);
border-radius: 8px;
border: 2px solid #1f1f1f;
"""
self.cw_btn = QToolButton(self)
self.cw_btn.setIcon(QIcon("ui/icons/rotate-cw-svgrepo-com.svg"))
self.cw_btn.setIconSize(icon_size)
self.cw_btn.setStyleSheet(btn_style)
self.cw_btn.setFixedSize(*btn_size)
move_x = self.cw_btn.width() + 10
self.cw_btn.move(self.width() - move_x, 10)
self.cw_btn.clicked.connect(self.rotateCW)
self.ccw_btn = QToolButton(self)
self.ccw_btn.setIcon(QIcon("ui/icons/rotate-ccw-svgrepo-com.svg"))
self.ccw_btn.setIconSize(icon_size)
self.ccw_btn.setStyleSheet(btn_style)
self.ccw_btn.setFixedSize(*btn_size)
move_x += self.ccw_btn.width() + 10
self.ccw_btn.move(self.width() - move_x, 10)
self.ccw_btn.clicked.connect(self.rotateCCW)
self.flip_btn = QToolButton(self)
# self.flip_btn.setIcon(QIcon("ui/icons/flip-vertical-svgrepo-com.svg"))
self.flip_btn.setIconSize(icon_size)
self.flip_btn.setStyleSheet(btn_style)
self.flip_btn.setFixedSize(*btn_size)
move_x += self.flip_btn.width() + 10
self.flip_btn.move(self.width() - move_x, 10)
self.flip_btn.clicked.connect(self.swapViews)
self.orient_btn = QToolButton(self)
# self.orient_btn.setIcon(QIcon("ui/icons/horizontal-stacks-svgrepo-com.svg"))
self.orient_btn.setIconSize(icon_size)
self.orient_btn.setStyleSheet(btn_style)
self.orient_btn.setFixedSize(*btn_size)
move_x += self.orient_btn.width() + 10
self.orient_btn.move(self.width() - move_x, 10)
self.orient_btn.clicked.connect(self.toggleOrientation)
self.cw_btn.raise_()
self.ccw_btn.raise_()
self.flip_btn.raise_()
self.orient_btn.raise_()
self.toggle_orientation(Qt.Orientation.Vertical)
def set_image(self, pixmap: QPixmap):
self.viewer.set_image(pixmap)
def resizeEvent(self, event):
super().resizeEvent(event)
# Aktualizacja pozycji przycisku przy zmianie rozmiaru
move_x = self.cw_btn.width() + 10
self.cw_btn.move(self.width() - move_x, 10)
move_x += self.ccw_btn.width() + 10
self.ccw_btn.move(self.width() - move_x, 10)
move_x += self.flip_btn.width() + 10
self.flip_btn.move(self.width() - move_x, 10)
move_x += self.orient_btn.width() + 10
self.orient_btn.move(self.width() - move_x, 10)
def toggle_orientation(self, orientation):
if orientation == Qt.Orientation.Vertical:
self.flip_btn.setIcon(QIcon("ui/icons/flip-vertical-svgrepo-com.svg"))
self.orient_btn.setIcon(QIcon("ui/icons/horizontal-stacks-svgrepo-com.svg"))
else:
self.flip_btn.setIcon(QIcon("ui/icons/flip-horizontal-svgrepo-com.svg"))
self.orient_btn.setIcon(QIcon("ui/icons/vertical-stacks-svgrepo-com.svg"))
def enterEvent(self, event: QEnterEvent) -> None:
self.orient_btn.show()
self.flip_btn.show()
self.ccw_btn.show()
self.cw_btn.show()
return super().enterEvent(event)
def leaveEvent(self, event: QEvent) -> None:
self.orient_btn.hide()
self.flip_btn.hide()
self.ccw_btn.hide()
self.cw_btn.hide()
return super().leaveEvent(event)
class SplitView(QSplitter):
def __init__(self, parent=None):
super().__init__(parent)
@@ -110,9 +223,11 @@ class SplitView(QSplitter):
self.setOrientation(Qt.Orientation.Vertical)
self.widget_start = CameraPlaceholder()
self.widget_live = ZoomableImageView()
# self.widget_live = ZoomableImageView()
self.widget_live = ViewWithOverlay()
# self.widget_live = PlaceholderWidget("Camera View", "#750466")
self.widget_ref = ZoomableImageView()
# self.widget_ref = ZoomableImageView()
self.widget_ref = ViewWithOverlay()
# self.widget_ref = PlaceholderWidget("Image View", "#007981")
self.stack = QStackedWidget()
@@ -129,18 +244,31 @@ class SplitView(QSplitter):
# pixmap.fill(Qt.GlobalColor.lightGray)
self.widget_live.set_image(pixmap)
self.widget_live.toggleOrientation.connect(self.toggle_orientation)
self.widget_ref.toggleOrientation.connect(self.toggle_orientation)
self.widget_live.swapViews.connect(self.swap_views)
self.widget_ref.swapViews.connect(self.swap_views)
def toggle_orientation(self):
if self.orientation() == Qt.Orientation.Vertical:
self.setOrientation(Qt.Orientation.Horizontal)
self.setSizes([self.width()//2, self.width()//2])
self.widget_live.toggle_orientation(Qt.Orientation.Horizontal)
self.widget_ref.toggle_orientation(Qt.Orientation.Horizontal)
else:
self.setOrientation(Qt.Orientation.Vertical)
self.setSizes([self.height()//2, self.height()//2])
# def set_live_image(self, path_image: str):
# """Ustawienie obrazu na żywo"""
# pixmap = QPixmap(path_image)
# self.widget_live.set_image(pixmap)
self.widget_live.toggle_orientation(Qt.Orientation.Vertical)
self.widget_ref.toggle_orientation(Qt.Orientation.Vertical)
def swap_views(self):
"""Zamiana widoków miejscami"""
index_live = self.indexOf(self.stack)
index_ref = self.indexOf(self.widget_ref)
sizes = self.sizes()
self.insertWidget(index_live, self.widget_ref)
self.insertWidget(index_ref, self.stack)
self.setSizes(sizes)
def set_live_image(self, pixmap: QPixmap):
"""Ustawienie obrazu na żywo"""
@@ -159,6 +287,3 @@ class SplitView(QSplitter):
self.stack.setCurrentWidget(self.widget_live)
else:
self.stack.setCurrentWidget(self.widget_start)