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, QImage, QTransform from PySide6.QtCore import Qt, QSize, Signal, QEvent import sys from ui.widgets.placeholder_widget import PlaceholderWidget from settings import ICONS_DIR 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): if pixmap.isNull(): return self._pixmap_item.setPixmap(pixmap) self._scene.setSceneRect(pixmap.rect()) if self._current_scale == 1.0: self.fitInView(self._pixmap_item, Qt.AspectRatioMode.KeepAspectRatio) self._first_image = False 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=str(ICONS_DIR / "rotate-cw-svgrepo-com.svg"), callback=self.rotateCW, ) self.ccw_btn = self._create_tool_button( icon_path=str(ICONS_DIR / "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=str(ICONS_DIR / "settings-svgrepo-com.svg"), callback=self.cameraConnection ) self.settings_btn = self._create_tool_button( icon_path=str(ICONS_DIR / "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(str(ICONS_DIR / "flip-vertical-svgrepo-com.svg"))) self.orient_btn.setIcon(QIcon(str(ICONS_DIR / "horizontal-stacks-svgrepo-com.svg"))) else: self.flip_btn.setIcon(QIcon(str(ICONS_DIR / "flip-horizontal-svgrepo-com.svg"))) self.orient_btn.setIcon(QIcon(str(ICONS_DIR / "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 = ViewWithOverlay(live=True) self.widget_ref = ViewWithOverlay() 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.ref_image_rotate = 0 self.original_ref_pixmap = None 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) self.widget_ref.rotateCCW.connect(self.rotate_left) self.widget_ref.rotateCW.connect(self.rotate_right) 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""" self.original_ref_pixmap = QPixmap(path_image) self.ref_image_rotate = 0 self.widget_ref.set_image(self.original_ref_pixmap) def toggle_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) def rotate_left(self): if not self.original_ref_pixmap: return self.ref_image_rotate = (self.ref_image_rotate - 90) % 360 transform = QTransform().rotate(self.ref_image_rotate) rotated_pixmap = self.original_ref_pixmap.transformed(transform) self.widget_ref.set_image(rotated_pixmap) def rotate_right(self): if not self.original_ref_pixmap: return self.ref_image_rotate = (self.ref_image_rotate + 90) % 360 transform = QTransform().rotate(self.ref_image_rotate) rotated_pixmap = self.original_ref_pixmap.transformed(transform) self.widget_ref.set_image(rotated_pixmap)