Files
MayoStainHelper/ui/widgets/split_view_widget.py

331 lines
9.9 KiB
Python

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)
# Scena i element obrazu
self._scene = QGraphicsScene(self)
self.setScene(self._scene)
self._scene.setBackgroundBrush(
QBrush(QColor(20, 20, 20))) # ciemne tło
self._pixmap_item = QGraphicsPixmapItem()
self._scene.addItem(self._pixmap_item)
# Ustawienia widoku
# 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.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
# Parametry zoomu
self._zoom_factor = 1.25
self._current_scale = 1.0
def set_image(self, pixmap: QPixmap):
# pixmap = QPixmap(image_path)
self._pixmap_item.setPixmap(pixmap)
self._scene.setSceneRect(pixmap.rect())
# self.reset_transform()
def reset_transform(self):
"""Resetuje skalowanie i ustawia 1:1"""
self._current_scale = 1.0
self.setTransform(self.transform().fromScale(1, 1))
def wheelEvent(self, event: QWheelEvent):
"""Zoom kółkiem myszy"""
if event.modifiers() & Qt.KeyboardModifier.ControlModifier: # zoom tylko z CTRL
if event.angleDelta().y() > 0:
zoom = self._zoom_factor
else:
zoom = 1 / self._zoom_factor
self._current_scale *= zoom
self.scale(zoom, zoom)
else:
return
super().wheelEvent(event) # normalne przewijanie
class CameraPlaceholder(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setAutoFillBackground(True)
self.setStyleSheet("background-color: #141414;")
layout = QVBoxLayout(self)
layout.setSpacing(20)
self.camera_start_btn = QPushButton("Start Camera")
self.camera_start_btn.setFixedSize(200, 50)
style_sheet = """
QPushButton {
/* --- Styl podstawowy --- */
background-color: transparent;
border: 2px solid #CECECE; /* Grubość, styl i kolor obramowania */
border-radius: 25px; /* Kluczowa właściwość do zaokrąglenia rogów! */
color: #CECECE;
padding: 10px 20px; /* Wewnętrzny margines */
font-size: 16px;
}
QPushButton:hover {
/* --- Styl po najechaniu myszką --- */
color: #F0F0F0;
border: 2px solid #F0F0F0;
}
QPushButton:pressed {
/* --- Styl po naciśnięciu --- */
background-color: #e0e0e0; /* Ciemniejsze tło w momencie kliknięcia */
border: 2px solid #e0e0e0; /* Zmiana koloru ramki dla sygnalizacji akcji */
}
"""
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.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.error_label = QLabel()
self.error_label.setStyleSheet(
"background-color: transparent; color: #CECECE; font-size: 18px; font-style: italic;")
self.error_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addStretch()
layout.addWidget(self.camera_start_btn,
alignment=Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.info_label)
layout.addWidget(self.error_label)
layout.addStretch()
self.setLayout(layout)
def set_info_text(self, text: str):
self.info_label.setText(text)
def set_error_text(self, text: str):
self.error_label.setText(text)
def set_button_text(self, text:str):
self.camera_start_btn.setText(text)
class ViewWithOverlay(QWidget):
cameraConnection = Signal()
cameraSettings = Signal()
toggleOrientation = Signal()
swapViews = Signal()
rotateCW = Signal()
rotateCCW = Signal()
def __init__(self, live: bool = False):
super().__init__()
self.live = live
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
self.viewer = ZoomableImageView()
layout.addWidget(self.viewer)
self._create_top_right_buttons()
if self.live:
self._create_top_left_buttons()
self.resize(self.size())
# self.cw_btn.raise_()
# self.ccw_btn.raise_()
# self.flip_btn.raise_()
# self.orient_btn.raise_()
self.toggle_orientation(Qt.Orientation.Vertical)
def _create_tool_button(self, callback, icon_path: str | None):
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;
"""
btn = QToolButton(self)
if icon_path:
btn.setIcon(QIcon(icon_path))
btn.setIconSize(icon_size)
btn.setStyleSheet(btn_style)
btn.setFixedSize(*btn_size)
btn.clicked.connect(callback)
return btn
def _create_top_right_buttons(self):
self.cw_btn = self._create_tool_button(
icon_path="ui/icons/rotate-cw-svgrepo-com.svg",
callback=self.rotateCW,
)
self.ccw_btn = self._create_tool_button(
icon_path="ui/icons/rotate-ccw-svgrepo-com.svg",
callback=self.rotateCCW,
)
self.flip_btn = self._create_tool_button(
icon_path=None,
callback=self.swapViews,
)
self.orient_btn = self._create_tool_button(
icon_path=None,
callback=self.toggleOrientation,
)
def _create_top_left_buttons(self):
self.camera_btn = self._create_tool_button(
icon_path="ui/icons/settings-svgrepo-com.svg",
callback=self.cameraConnection
)
self.settings_btn = self._create_tool_button(
icon_path="ui/icons/error-16-svgrepo-com.svg",
callback=self.cameraSettings
)
def set_image(self, pixmap: QPixmap):
self.viewer.set_image(pixmap)
def resizeEvent(self, event):
super().resizeEvent(event)
# Aktualizacja pozycji przycisku przy zmianie rozmiaru
if self.live:
left_corner = 10
self.camera_btn.move(left_corner, 10)
left_corner += self.camera_btn.width() + 10
self.settings_btn.move(left_corner, 10)
right_corner = self.cw_btn.width() + 10
self.cw_btn.move(self.width() - right_corner, 10)
right_corner += self.ccw_btn.width() + 10
self.ccw_btn.move(self.width() - right_corner, 10)
right_corner += self.flip_btn.width() + 10
self.flip_btn.move(self.width() - right_corner, 10)
right_corner += self.orient_btn.width() + 10
self.orient_btn.move(self.width() - right_corner, 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:
if self.live:
self.camera_btn.show()
self.settings_btn.show()
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:
if self.live:
self.camera_btn.hide()
self.settings_btn.hide()
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)
print("Inicjalizacja SplitView2")
self.setOrientation(Qt.Orientation.Vertical)
self.widget_start = CameraPlaceholder()
# self.widget_live = ZoomableImageView()
self.widget_live = ViewWithOverlay()
# self.widget_live = PlaceholderWidget("Camera View", "#750466")
# self.widget_ref = ZoomableImageView()
self.widget_ref = ViewWithOverlay()
# self.widget_ref = PlaceholderWidget("Image View", "#007981")
self.stack = QStackedWidget()
self.stack.addWidget(self.widget_start)
self.stack.addWidget(self.widget_live)
self.stack.setCurrentWidget(self.widget_start)
self.addWidget(self.stack)
self.addWidget(self.widget_ref)
self.setSizes([self.height(), 0])
pixmap = QPixmap("media/empty_guitar_h.jpg")
# 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])
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"""
self.widget_live.set_image(pixmap)
if self.stack.currentWidget() != self.widget_live:
self.stack.setCurrentWidget(self.widget_live)
def set_reference_image(self, path_image: str):
"""Ustawienie obrazu referencyjnego"""
pixmap = QPixmap(path_image)
self.widget_ref.set_image(pixmap)
def toglle_live_view(self):
"""Przełączanie widoku na żywo"""
if self.stack.currentWidget() == self.widget_start:
self.stack.setCurrentWidget(self.widget_live)
else:
self.stack.setCurrentWidget(self.widget_start)