Create a new `settings.py` file to define and manage global application paths. Modify UI components (`split_view_widget.py`, `view_settings_dialog.py`) to use the centralized `ICONS_DIR` path constant instead of hardcoded relative paths for icons. This improves maintainability and makes the application independent of the working directory.
349 lines
11 KiB
Python
349 lines
11 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, 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.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.ref_pixmap = QPixmap(path_image)
|
|
if self.ref_image_rotate != 0:
|
|
self.ref_pixmap = self.ref_pixmap.transformed(QTransform().rotate(self.ref_image_rotate))
|
|
self.widget_ref.set_image(self.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.ref_pixmap:
|
|
return
|
|
self.ref_image_rotate = (self.ref_image_rotate - 90) % 360
|
|
self.ref_pixmap = self.ref_pixmap.transformed(QTransform().rotate(-90))
|
|
self.widget_ref.set_image(self.ref_pixmap)
|
|
|
|
def rotate_right(self):
|
|
if not self.ref_pixmap:
|
|
return
|
|
self.ref_image_rotate = (self.ref_image_rotate + 90) % 360
|
|
self.ref_pixmap = self.ref_pixmap.transformed(QTransform().rotate(90))
|
|
self.widget_ref.set_image(self.ref_pixmap) |