"""Main PySide6 window with custom title bar.""" from __future__ import annotations from pathlib import Path from PySide6 import QtCore, QtGui, QtWidgets class TitleBar(QtWidgets.QWidget): """Custom title bar mimicking modern Windows applications.""" HEIGHT = 40 def __init__(self, window: "MainWindow") -> None: super().__init__(window) self.window = window self.setFixedHeight(self.HEIGHT) self.setCursor(QtCore.Qt.ArrowCursor) self.setAutoFillBackground(True) palette = self.palette() palette.setColor(QtGui.QPalette.Window, QtGui.QColor("#16171d")) self.setPalette(palette) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(12, 8, 12, 8) layout.setSpacing(8) logo_path = Path(__file__).resolve().parents[1] / "assets" / "logo.png" if logo_path.exists(): pixmap = QtGui.QPixmap(str(logo_path)) logo_label = QtWidgets.QLabel() logo_label.setPixmap(pixmap.scaled(24, 24, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) layout.addWidget(logo_label) title_label = QtWidgets.QLabel("Interactive Color Range Analyzer") title_label.setStyleSheet("color: #f7f7fb; font-weight: 600;") layout.addWidget(title_label) layout.addStretch(1) self.min_btn = self._create_button("–", "Minimise window") self.min_btn.clicked.connect(window.showMinimized) layout.addWidget(self.min_btn) self.max_btn = self._create_button("❐", "Maximise / Restore") self.max_btn.clicked.connect(window.toggle_maximise) layout.addWidget(self.max_btn) close_btn = self._create_button("✕", "Close") close_btn.clicked.connect(window.close) close_btn.setStyleSheet( """ QPushButton { background-color: transparent; color: #f7f7fb; border: none; padding: 4px 10px; } QPushButton:hover { background-color: #d0342c; } """ ) layout.addWidget(close_btn) def _create_button(self, text: str, tooltip: str) -> QtWidgets.QPushButton: btn = QtWidgets.QPushButton(text) btn.setToolTip(tooltip) btn.setFixedSize(36, 24) btn.setCursor(QtCore.Qt.ArrowCursor) btn.setStyleSheet( """ QPushButton { background-color: transparent; color: #f7f7fb; border: none; padding: 4px 10px; } QPushButton:hover { background-color: rgba(255, 255, 255, 0.12); } """ ) return btn def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: if event.button() == QtCore.Qt.LeftButton: self.window.toggle_maximise() event.accept() def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: if event.button() == QtCore.Qt.LeftButton: self.window.start_system_move(event.globalPosition()) event.accept() super().mousePressEvent(event) class ImageView(QtWidgets.QLabel): """Simple image display widget that keeps aspect ratio.""" def __init__(self) -> None: super().__init__() self.setAlignment(QtCore.Qt.AlignCenter) self._pixmap: QtGui.QPixmap | None = None def set_image(self, pixmap: QtGui.QPixmap | None) -> None: self._pixmap = pixmap self._rescale() def resizeEvent(self, event: QtGui.QResizeEvent) -> None: super().resizeEvent(event) self._rescale() def _rescale(self) -> None: if self._pixmap is None: self.clear() self.setText("") self.setStyleSheet("color: rgba(255,255,255,0.5); font-size: 14px;") return target = self._pixmap.scaled( self.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation, ) self.setPixmap(target) self.setStyleSheet("") class MainWindow(QtWidgets.QMainWindow): """Main application window containing custom chrome and content area.""" def __init__(self) -> None: super().__init__() self.setWindowFlag(QtCore.Qt.FramelessWindowHint) self.setWindowFlag(QtCore.Qt.Window) self.setAttribute(QtCore.Qt.WA_TranslucentBackground, False) self.setMinimumSize(900, 600) container = QtWidgets.QWidget() container_layout = QtWidgets.QVBoxLayout(container) container_layout.setContentsMargins(0, 0, 0, 0) container_layout.setSpacing(0) self.title_bar = TitleBar(self) container_layout.addWidget(self.title_bar) self.content = QtWidgets.QWidget() self.content.setStyleSheet("background-color: #111216;") content_layout = QtWidgets.QVBoxLayout(self.content) content_layout.setContentsMargins(24, 24, 24, 24) content_layout.setSpacing(18) toolbar = QtWidgets.QHBoxLayout() toolbar.setSpacing(12) self.open_button = QtWidgets.QPushButton("Open Image…") self.open_button.setCursor(QtCore.Qt.PointingHandCursor) self.open_button.setStyleSheet( """ QPushButton { background: linear-gradient(135deg, #5168ff, #9a4dff); border: none; color: #ffffff; font-weight: 600; padding: 10px 16px; border-radius: 10px; } QPushButton:hover { filter: brightness(1.1); } """ ) self.open_button.clicked.connect(self.open_image) toolbar.addWidget(self.open_button) toolbar.addStretch(1) self.status_label = QtWidgets.QLabel("No image loaded") self.status_label.setStyleSheet("color: rgba(255,255,255,0.7); font-weight: 500;") toolbar.addWidget(self.status_label) content_layout.addLayout(toolbar) self.image_view = ImageView() self.image_view.setStyleSheet("border: 1px solid rgba(255,255,255,0.08); border-radius: 12px;") content_layout.addWidget(self.image_view, 1) container_layout.addWidget(self.content, 1) self.setCentralWidget(container) self._is_maximised = False self._current_image_path: Path | None = None # Window control helpers ------------------------------------------------- def toggle_maximise(self) -> None: handle = self.windowHandle() if handle is None: return if self._is_maximised: self.showNormal() self._is_maximised = False self.title_bar.max_btn.setText("❐") else: self.showMaximized() self._is_maximised = True self.title_bar.max_btn.setText("⧉") def start_system_move(self, _global_position: QtCore.QPointF) -> None: handle = self.windowHandle() if handle: handle.startSystemMove() # Image handling --------------------------------------------------------- def open_image(self) -> None: filters = "Images (*.png *.jpg *.jpeg *.bmp *.webp)" path_str, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select image", "", filters) if not path_str: return path = Path(path_str) pixmap = QtGui.QPixmap(str(path)) if pixmap.isNull(): QtWidgets.QMessageBox.warning(self, "ICRA", "Unable to open the selected image.") return self.image_view.set_image(pixmap) self._current_image_path = path self.status_label.setText(f"{path.name} · {pixmap.width()}×{pixmap.height()}")