migrate pyqt5 to pyside6

This commit is contained in:
ykcheungab
2026-09-21 09:34:56 +08:00
parent e276b5de0f
commit ccedb9ddd9
66 changed files with 2184 additions and 395 deletions
+34 -19
View File
@@ -9,11 +9,9 @@ from pathlib import Path
from typing import Any
import numpy as np
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QIcon, QImage
from PyQt5.QtWidgets import (
QAction,
QActionGroup,
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QAction, QActionGroup, QIcon, QImage
from PySide6.QtWidgets import (
QApplication,
QFrame,
QHBoxLayout,
@@ -240,7 +238,7 @@ class StudioMainWindow(QMainWindow):
root.addWidget(self.toolbar)
self.status_chip = self.toolbar.status_chip
self.content_splitter = QSplitter(Qt.Horizontal)
self.content_splitter = QSplitter(Qt.Orientation.Horizontal)
self.mesh3d_panel = Mesh3dPanel(theme=self._theme)
self.arrow_panel = ArrowPanel(theme=self._theme)
self.force_plots = ForcePlotsPanel(theme=self._theme)
@@ -888,7 +886,7 @@ class StudioMainWindow(QMainWindow):
# convention everyone's hands already know.
self.act_play = QAction(self)
self.act_play.setShortcut("Space")
self.act_play.setShortcutContext(Qt.ApplicationShortcut)
self.act_play.setShortcutContext(Qt.ShortcutContext.ApplicationShortcut)
self.addAction(self.act_play)
self.view_menu = menu_bar.addMenu("")
@@ -1402,7 +1400,7 @@ class StudioMainWindow(QMainWindow):
self._persist_prefs()
def _show_about(self) -> None:
AboutDialog(self).exec_()
AboutDialog(self).exec()
def _on_camera_id_changed(self, value: int) -> None:
self.prefs["camera_id"] = str(int(value))
@@ -1507,7 +1505,7 @@ class StudioMainWindow(QMainWindow):
filters_locked=bool(self._running or self.recorder.is_recording),
sample_rate_hz=self.display_ctl.device_sample_rate(),
)
if dialog.exec_() != PlotSettingsDialog.Accepted:
if dialog.exec() != PlotSettingsDialog.Accepted:
return
window_sec, fn_max, shear_max, fn_raw_max, shear_raw_max = dialog.values()
self.display_ctl.apply_plot_settings(
@@ -1589,10 +1587,10 @@ class StudioMainWindow(QMainWindow):
sensor_type=sensor_type,
config=config_ref,
),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if answer != QMessageBox.Yes:
if answer != QMessageBox.StandardButton.Yes:
self.recorder.log_event(
"calibration_apply_blocked", "; ".join(reasons)
)
@@ -1602,10 +1600,10 @@ class StudioMainWindow(QMainWindow):
self,
t("cal.apply_title"),
t("cal.apply_confirm", sensor_type=sensor_type, config=config_ref),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if answer != QMessageBox.Yes:
if answer != QMessageBox.StandardButton.Yes:
self.statusBar().showMessage(t("cal.apply_skipped"))
return
worker = self._worker_for(sensor_type)
@@ -1720,7 +1718,18 @@ class StudioMainWindow(QMainWindow):
if getattr(self, "act_run", None) is None:
return
self.act_run.setEnabled(self.toolbar.btn_run.isEnabled())
self.act_stop.setEnabled(self._session_active)
# The menu's Stop mirrors the toolbar's stop affordance, not the acquisition
# session alone: a replay or a recompute pass is something to stop.
#
# This is not cosmetic. Qt6 honours ``enabled`` in ``QAction::trigger()`` (Qt5
# did not) and never delivers the shortcut of a disabled action, so a Stop that
# is disabled while a replay is playing is a keyboard escape hatch that does not
# work — the one thing ``_on_stop_shortcut`` exists to guarantee.
self.act_stop.setEnabled(
self._session_active
or self.replay_ctl.is_playing()
or self._recompute_running
)
self.act_preview.setEnabled(self.toolbar.btn_preview.isEnabled())
self.act_reset.setEnabled(self.toolbar.btn_reset.isEnabled())
@@ -1740,10 +1749,16 @@ class StudioMainWindow(QMainWindow):
In the replay view it is the same escape hatch as the rail's 停止 button: a
replay or a recompute pass that will not stop by itself has to be stoppable
from the keyboard.
from the keyboard. The replay branch accepts the same three states
``_sync_shortcut_actions`` enables the action for, so the action can never be
enabled with nothing behind it.
"""
if self.toolbar.run_role() == "playback":
if self.replay_ctl.is_playing() or self._session_active:
if (
self.replay_ctl.is_playing()
or self._session_active
or self._recompute_running
):
self.replay_ctl.stop_activity()
return
if not self._session_active:
@@ -2348,7 +2363,7 @@ class StudioMainWindow(QMainWindow):
geo = self.prefs.get("window_geometry")
if isinstance(geo, str) and geo:
try:
from PyQt5.QtCore import QByteArray
from PySide6.QtCore import QByteArray
self.restoreGeometry(QByteArray.fromHex(geo.encode("ascii")))
except Exception:
@@ -21,7 +21,7 @@ from __future__ import annotations
import logging
from typing import Any, Optional, Protocol
from PyQt5.QtCore import QObject
from PySide6.QtCore import QObject
logger = logging.getLogger(__name__)
+7 -2
View File
@@ -6,7 +6,12 @@ import logging
import os
import sys
from PyQt5.QtWidgets import QApplication
# pyqtgraph picks its toolkit from whichever binding is already imported, and in a
# development environment that also has PyQt5 installed (``orisys[gui]``) it can pick the
# wrong one. The application is PySide6-only, so say so before anything imports pyqtgraph.
os.environ.setdefault("PYQTGRAPH_QT_LIB", "PySide6")
from PySide6.QtWidgets import QApplication # noqa: E402
from orisys_app_shared import API_VERSION, __version__ as shared_version
from orisys_app_shared.compat import check_shared_compat
@@ -85,7 +90,7 @@ def main() -> int:
language,
gl_profile,
)
code = int(app.exec_())
code = int(app.exec())
# Leave the process immediately.
#
+5 -5
View File
@@ -24,7 +24,7 @@ from pathlib import Path
from typing import Optional
import numpy as np
from PyQt5.QtCore import QObject, pyqtSignal
from PySide6.QtCore import QObject, Signal
from orisys_app_shared.i18n import t
from orisys_app_shared.record import RecordingReader, VideoOnlySource
@@ -47,13 +47,13 @@ class ReplayEngine(QObject):
"""Open one recording and answer "what is at t?" for every view."""
#: The cursor moved; the chart cursor and the 2D panel follow.
position_changed = pyqtSignal(float)
position_changed = Signal(float)
#: The frame under the cursor changed.
frames_changed = pyqtSignal()
frames_changed = Signal()
#: The notes about this recording, or the loaded recording, changed.
status_changed = pyqtSignal()
status_changed = Signal()
#: A recording was opened (True) or the previous one was dropped (False).
recording_changed = pyqtSignal(bool)
recording_changed = Signal(bool)
def __init__(self, parent=None) -> None:
super().__init__(parent)
@@ -28,8 +28,8 @@ from pathlib import Path
from typing import Any, Optional, Protocol
import numpy as np
from PyQt5.QtCore import QObject, QTimer
from PyQt5.QtWidgets import QFileDialog
from PySide6.QtCore import QObject, QTimer
from PySide6.QtWidgets import QFileDialog
from orisys_app_shared.acquisition.image_backend import SOURCE_REPLAY, ImageFrame
from orisys_app_shared.i18n import t
@@ -33,7 +33,7 @@ from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable, Optional
from PyQt5.QtCore import QObject, pyqtSignal
from PySide6.QtCore import QObject, Signal
from orisys_app_shared.paths import config_dir
from orisys_app_shared.scene3d.contracts import (
@@ -180,8 +180,8 @@ class SceneAssetLoader(QObject):
selection the operator has already moved away from.
"""
loaded = pyqtSignal(object, str, int, object) # SceneAssets, description, id, device
failed = pyqtSignal(object, int) # Diagnostic, request id
loaded = Signal(object, str, int, object) # SceneAssets, description, id, device
failed = Signal(object, int) # Diagnostic, request id
def __init__(self, parent: Optional[QObject] = None) -> None:
super().__init__(parent)
@@ -72,21 +72,21 @@ def configure_surface_format(profile: Profile = "desktop33") -> None:
component must not slow down (the acquisition loop feeds the scene through a
capacity-one mailbox), and the design document defers anti-aliasing work.
"""
from PyQt5.QtGui import QSurfaceFormat
from PySide6.QtGui import QSurfaceFormat
fmt = QSurfaceFormat()
if profile == "gles30":
fmt.setRenderableType(QSurfaceFormat.OpenGLES)
fmt.setRenderableType(QSurfaceFormat.RenderableType.OpenGLES)
fmt.setVersion(3, 0)
elif profile == "desktop33":
fmt.setRenderableType(QSurfaceFormat.OpenGL)
fmt.setRenderableType(QSurfaceFormat.RenderableType.OpenGL)
fmt.setVersion(3, 3)
fmt.setProfile(QSurfaceFormat.CoreProfile)
fmt.setProfile(QSurfaceFormat.OpenGLContextProfile.CoreProfile)
else: # pragma: no cover - a typo must not reach a board silently
raise ValueError(f"unknown GL profile: {profile!r}")
fmt.setDepthBufferSize(24)
fmt.setStencilBufferSize(8)
fmt.setSwapBehavior(QSurfaceFormat.DoubleBuffer)
fmt.setSwapBehavior(QSurfaceFormat.SwapBehavior.DoubleBuffer)
fmt.setSamples(0)
QSurfaceFormat.setDefaultFormat(fmt)
@@ -442,7 +442,10 @@ class GLDispatch:
if context is None or not context.isValid():
raise error("gl_unsupported", "no current OpenGL context")
fmt = context.format()
profile = profile_from_renderable_type(int(fmt.renderableType()))
# PySide6 enums are not ints: `int(QSurfaceFormat.OpenGLES)` raises TypeError, so read
# `.value`. (PyQt5 exposed them as ints. Getting this wrong made every GL probe raise,
# the 3D pane report "unsupported", and the GL smoke tests skip instead of running.)
profile = profile_from_renderable_type(int(fmt.renderableType().value))
try:
vendor = _gl_string(GL, GL.GL_VENDOR)
renderer = _gl_string(GL, GL.GL_RENDERER)
@@ -454,7 +457,7 @@ class GLDispatch:
"gl_unsupported",
f"the context answered no queries: {type(exc).__name__}: {exc}",
) from exc
from PyQt5.QtGui import QGuiApplication
from PySide6.QtGui import QGuiApplication
platform_name = ""
try:
+13 -13
View File
@@ -22,9 +22,9 @@ from typing import TYPE_CHECKING, Optional
import numpy as np
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QImage
from PyQt5.QtWidgets import (
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QImage
from PySide6.QtWidgets import (
QCheckBox,
QComboBox,
QDoubleSpinBox,
@@ -87,7 +87,7 @@ class ColorScaleBar(QLabel):
self.setObjectName("colorScaleBar")
self.setFixedHeight(12)
self.setMinimumWidth(120)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.setVisible(False)
self._lut_id = "sequential"
@@ -99,14 +99,14 @@ class ColorScaleBar(QLabel):
self.update()
def paintEvent(self, event) -> None: # noqa: N802 - Qt naming
from PyQt5.QtGui import QPainter
from PySide6.QtGui import QPainter
from orisys_studio.scene3d.gl_backend import lut_rgba
# (256, 1, 4) RGBA -> one scanline; the bytes object stays alive for the call,
# which is what QImage's borrowed buffer needs.
rgba = np.ascontiguousarray(lut_rgba(self._lut_id)[:, 0, :]).tobytes()
image = QImage(rgba, 256, 1, 256 * 4, QImage.Format_RGBA8888)
image = QImage(rgba, 256, 1, 256 * 4, QImage.Format.Format_RGBA8888)
painter = QPainter(self)
painter.drawImage(self.rect(), image)
painter.end()
@@ -115,10 +115,10 @@ class ColorScaleBar(QLabel):
class ThreeDPanel(QFrame):
"""Toggle, presets, reset, housing visibility, gain, colour scale, status."""
enabledChanged = pyqtSignal(bool)
settingsChanged = pyqtSignal(object) # DisplaySettings
resetRequested = pyqtSignal()
presetRequested = pyqtSignal(str)
enabledChanged = Signal(bool)
settingsChanged = Signal(object) # DisplaySettings
resetRequested = Signal()
presetRequested = Signal(str)
def __init__(
self,
@@ -299,13 +299,13 @@ class ThreeDPanel(QFrame):
scale_row.setSpacing(6)
self.scale_lower_label = QLabel()
self.scale_lower_label.setObjectName("panelMeta")
self.scale_lower_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.scale_lower_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
scale_row.addWidget(self.scale_lower_label)
self.scale_bar = ColorScaleBar()
scale_row.addWidget(self.scale_bar, 1)
self.scale_upper_label = QLabel()
self.scale_upper_label.setObjectName("panelMeta")
self.scale_upper_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.scale_upper_label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
scale_row.addWidget(self.scale_upper_label)
root.addLayout(scale_row)
@@ -327,7 +327,7 @@ class ThreeDPanel(QFrame):
self.source_label = ShrinkableLabel()
self.source_label.setObjectName("panelMeta")
self.source_label.setWordWrap(False)
self.source_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.source_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
legend_row.addWidget(self.source_label, 2)
root.addLayout(legend_row)
@@ -16,8 +16,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Optional
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QDialog,
QDialogButtonBox,
QLabel,
@@ -60,7 +60,7 @@ class Scene3dSettingsDialog(QDialog):
root.addWidget(panel.controls_widget)
panel.controls_widget.setVisible(True)
self.buttons = QDialogButtonBox(QDialogButtonBox.Close)
self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
self.buttons.rejected.connect(self.close)
self.buttons.accepted.connect(self.close)
root.addWidget(self.buttons)
@@ -72,7 +72,7 @@ class Scene3dSettingsDialog(QDialog):
self.hint.setText(t("scene3d.settings_hint"))
# Qt ships its own translations for a button box, and the application installs
# none: without this the dialog ends in an English "Close" among Chinese labels.
close_button = self.buttons.button(QDialogButtonBox.Close)
close_button = self.buttons.button(QDialogButtonBox.StandardButton.Close)
if close_button is not None:
close_button.setText(t("dialog.close"))
@@ -84,7 +84,7 @@ class Scene3dSettingsDialog(QDialog):
def keyPressEvent(self, event) -> None: # noqa: N802 - Qt naming
"""Esc closes the dialog, as it does everywhere else in this application."""
if event.key() == Qt.Key_Escape:
if event.key() == Qt.Key.Key_Escape:
self.hide()
return
super().keyPressEvent(event)
@@ -28,8 +28,8 @@ import logging
from concurrent.futures import ThreadPoolExecutor
from typing import Any, Callable, Optional
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QVBoxLayout, QWidget
from PySide6.QtCore import Signal
from PySide6.QtWidgets import QVBoxLayout, QWidget
from orisys_app_shared.scene3d.composer import SceneComposer
from orisys_app_shared.scene3d.contracts import (
@@ -71,9 +71,9 @@ def gl_available() -> tuple[bool, str]:
pane on a machine where PyOpenGL is not installed.
"""
try:
from PyQt5.QtWidgets import QOpenGLWidget # noqa: F401
from PySide6.QtOpenGLWidgets import QOpenGLWidget # noqa: F401
except Exception as exc: # noqa: BLE001
return False, f"PyQt5 OpenGL widgets are unavailable: {exc}"
return False, f"PySide6 OpenGL widgets are unavailable: {exc}"
try:
import OpenGL # noqa: F401
except Exception as exc: # noqa: BLE001
@@ -84,11 +84,11 @@ def gl_available() -> tuple[bool, str]:
class Scene3DView(QWidget):
"""One 3D pane: the GL widget on top, its controls underneath."""
stateChanged = pyqtSignal(object) # SceneState
diagnosticRaised = pyqtSignal(object) # Diagnostic
renderStatsChanged = pyqtSignal(object) # RenderStats
descriptorChanged = pyqtSignal(object) # Optional[FieldDescriptor]
enabledChanged = pyqtSignal(bool)
stateChanged = Signal(object) # SceneState
diagnosticRaised = Signal(object) # Diagnostic
renderStatsChanged = Signal(object) # RenderStats
descriptorChanged = Signal(object) # Optional[FieldDescriptor]
enabledChanged = Signal(bool)
def __init__(self, parent: Optional[QWidget] = None) -> None:
super().__init__(parent)
+23 -16
View File
@@ -19,9 +19,11 @@ from __future__ import annotations
import time
from typing import Optional
from PyQt5.QtCore import QEvent, Qt, pyqtSignal
from PyQt5.QtGui import QMouseEvent, QWheelEvent
from PyQt5.QtWidgets import QOpenGLWidget, QWidget
from PySide6.QtCore import QEvent, Qt, Signal
from PySide6.QtGui import QMouseEvent, QWheelEvent
# Qt6 moved QOpenGLWidget out of QtWidgets into its own module; PySide6 follows.
from PySide6.QtOpenGLWidgets import QOpenGLWidget
from PySide6.QtWidgets import QWidget
from orisys_app_shared.scene3d.contracts import (
Diagnostic,
@@ -48,13 +50,13 @@ STATS_INTERVAL_S = 0.5
class Sensor3DWidget(QOpenGLWidget):
"""Renders one scene. Signals carry state, never per-frame arrays."""
capabilitiesChanged = pyqtSignal(object) # GLCapabilities
diagnosticRaised = pyqtSignal(object) # Diagnostic
renderStatsChanged = pyqtSignal(object) # RenderStats, rate-limited
capabilitiesChanged = Signal(object) # GLCapabilities
diagnosticRaised = Signal(object) # Diagnostic
renderStatsChanged = Signal(object) # RenderStats, rate-limited
#: What the frame on screen claims to be — quantity, unit, quality. The panel
#: labels the colour bar from this and nothing else, so an unmeasured surface
#: cannot inherit the previous frame's range.
descriptorChanged = pyqtSignal(object) # Optional[FieldDescriptor]
descriptorChanged = Signal(object) # Optional[FieldDescriptor]
def __init__(
self,
@@ -259,14 +261,19 @@ class Sensor3DWidget(QOpenGLWidget):
When the platform cannot create a GL context (the offscreen plugin, a broken
driver, a board without EGL) ``initializeGL`` is simply never called: there is
no exception to catch, and the widget would sit there looking like a working
view that has no data yet. Qt does answer the question, though — ``context()``
stays ``None`` — so the first paint asks it and turns "no context" into the
state the rest of the application already knows how to show.
view that has no data yet. Qt does answer the question, though, so the first
paint asks it and turns "no context" into the state the rest of the application
already knows how to show.
Under Qt5 that answer was ``context() is None``. Qt6 returns an *invalid*
context object instead, so the check has to be ``isValid()`` as well — comparing
against ``None`` alone silently stops reporting a broken GL stack.
"""
super().paintEvent(event)
if self._unavailable or self._capabilities is not None:
return
if self.context() is None:
context = self.context()
if context is None or not context.isValid():
self._fail(
diagnostic(
"gl_unsupported",
@@ -305,7 +312,7 @@ class Sensor3DWidget(QOpenGLWidget):
# --------------------------------------------------------------- interaction
def mousePressEvent(self, event: QMouseEvent) -> None: # pragma: no cover
self._last_press = (float(event.x()), float(event.y()))
self._last_press = (float(event.position().x()), float(event.position().y()))
super().mousePressEvent(event)
def mouseReleaseEvent(self, event: QMouseEvent) -> None: # pragma: no cover
@@ -316,17 +323,17 @@ class Sensor3DWidget(QOpenGLWidget):
if self._last_press is None:
super().mouseMoveEvent(event)
return
x, y = float(event.x()), float(event.y())
x, y = float(event.position().x()), float(event.position().y())
delta = (x - self._last_press[0], y - self._last_press[1])
self._last_press = (x, y)
viewport = (max(self.width(), 1), max(self.height(), 1))
buttons = event.buttons()
panning = bool(buttons & Qt.MiddleButton) or bool(
buttons & Qt.LeftButton and event.modifiers() & Qt.ShiftModifier
panning = bool(buttons & Qt.MouseButton.MiddleButton) or bool(
buttons & Qt.MouseButton.LeftButton and event.modifiers() & Qt.KeyboardModifier.ShiftModifier
)
if panning:
self._camera.pan(delta, viewport)
elif buttons & Qt.LeftButton:
elif buttons & Qt.MouseButton.LeftButton:
self._camera.orbit(delta, viewport)
self.update()
@@ -26,7 +26,7 @@ import logging
import time
from typing import Any, Callable, Optional
from PyQt5.QtCore import QObject, QTimer, pyqtSignal
from PySide6.QtCore import QObject, QTimer, Signal
from orisys_app_shared.i18n import t
from orisys_studio import session_metadata
@@ -47,11 +47,11 @@ class SessionController(QObject):
"""Owns the acquisition session's state and its UI-free mechanics."""
#: The run timer fired: the window's presenter slot does the painting.
tick = pyqtSignal()
tick = Signal()
#: The phase was (re)applied, not only changed: the status chip repaints from it,
#: and an idempotent repaint is cheaper than a second source of truth.
phaseChanged = pyqtSignal(str)
runningChanged = pyqtSignal(bool)
phaseChanged = Signal(str)
runningChanged = Signal(bool)
def __init__(
self,
@@ -2,9 +2,9 @@
from __future__ import annotations
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QDialog, QHBoxLayout, QLabel, QPushButton, QVBoxLayout
from PySide6.QtCore import Qt
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import QDialog, QHBoxLayout, QLabel, QPushButton, QVBoxLayout
from orisys_app_shared.i18n import t
from orisys_app_shared.paths import logo_topbar_path
@@ -24,19 +24,19 @@ class AboutDialog(QDialog):
logo = QLabel()
pix = QPixmap(str(logo_topbar_path()))
if not pix.isNull():
logo.setPixmap(pix.scaledToHeight(48, Qt.SmoothTransformation))
logo.setPixmap(pix.scaledToHeight(48, Qt.TransformationMode.SmoothTransformation))
row.addWidget(logo)
row.addStretch(1)
root.addLayout(row)
self.body = QLabel()
self.body.setWordWrap(True)
self.body.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.body.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
root.addWidget(self.body)
close_btn = QPushButton("OK")
close_btn.clicked.connect(self.accept)
root.addWidget(close_btn, alignment=Qt.AlignRight)
root.addWidget(close_btn, alignment=Qt.AlignmentFlag.AlignRight)
self.retranslate()
def retranslate(self) -> None:
+12 -12
View File
@@ -12,9 +12,9 @@ from __future__ import annotations
from typing import Optional
from PyQt5.QtCore import QSize, Qt, pyqtSignal
from PyQt5.QtGui import QImage, QPainter, QPixmap, QTransform
from PyQt5.QtWidgets import (
from PySide6.QtCore import QSize, Qt, Signal
from PySide6.QtGui import QImage, QPainter, QPixmap, QTransform
from PySide6.QtWidgets import (
QComboBox,
QFrame,
QHBoxLayout,
@@ -48,7 +48,7 @@ def bgr_to_qimage(frame) -> QImage:
"""
rgb = frame[:, :, ::-1].copy()
height, width, channels = rgb.shape
return QImage(rgb.data, width, height, channels * width, QImage.Format_RGB888).copy()
return QImage(rgb.data, width, height, channels * width, QImage.Format.Format_RGB888).copy()
_VIEW_LABELS = {
@@ -92,7 +92,7 @@ class AspectImageLabel(QLabel):
def __init__(self, parent=None) -> None:
super().__init__(parent)
self._aspect = DEFAULT_FRAME_ASPECT
policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
policy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
policy.setHeightForWidth(True)
self.setSizePolicy(policy)
self.setMinimumSize(200, _IMAGE_MIN_HEIGHT)
@@ -132,7 +132,7 @@ class ColorBarLabel(QLabel):
self.setObjectName("colorBar")
self.setFixedHeight(_COLORBAR_HEIGHT)
self.setMinimumWidth(_COLORBAR_WIDTH)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
self.setVisible(False)
def set_theme(self, theme: str) -> None:
@@ -145,14 +145,14 @@ class ColorBarLabel(QLabel):
# (256, 1, 3) BGR -> one RGB scanline. The bytes object stays alive for
# the whole call, which is what QImage's borrowed buffer needs.
rgb = flow_colormap(self._theme)[:, 0, ::-1].copy().tobytes()
image = QImage(rgb, 256, 1, 256 * 3, QImage.Format_RGB888)
image = QImage(rgb, 256, 1, 256 * 3, QImage.Format.Format_RGB888)
painter = QPainter(self)
painter.drawImage(self.rect(), image)
painter.end()
class ArrowPanel(QFrame):
view_mode_changed = pyqtSignal(str)
view_mode_changed = Signal(str)
def __init__(self, parent=None, *, theme: str = "dark") -> None:
super().__init__(parent)
@@ -188,7 +188,7 @@ class ArrowPanel(QFrame):
layout.addLayout(header)
self.image_label = AspectImageLabel()
self.image_label.setAlignment(Qt.AlignCenter)
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image_label.setScaledContents(False)
self.image_label.setWordWrap(True)
# The image keeps the frame's shape and is centred in whatever height is
@@ -515,11 +515,11 @@ class ArrowPanel(QFrame):
deg = self._effective_rotation_deg()
if deg:
# QTransform.rotate is clockwise on screen pixmaps; negate for CCW.
pix = pix.transformed(QTransform().rotate(-deg), Qt.FastTransformation)
pix = pix.transformed(QTransform().rotate(-deg), Qt.TransformationMode.FastTransformation)
scaled = pix.scaled(
self.image_label.size(),
Qt.KeepAspectRatio,
Qt.SmoothTransformation,
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation,
)
self.image_label.setPixmap(scaled)
self.image_label.setText("")
+12 -12
View File
@@ -14,9 +14,9 @@ from __future__ import annotations
import logging
from pathlib import Path
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QColor, QPainter, QPixmap
from PyQt5.QtWidgets import QFrame, QHBoxLayout, QLabel, QPushButton, QVBoxLayout
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QColor, QPainter, QPixmap
from PySide6.QtWidgets import QFrame, QHBoxLayout, QLabel, QPushButton, QVBoxLayout
from orisys_app_shared.i18n import t
from orisys_app_shared.paths import logo_topbar_path
@@ -39,13 +39,13 @@ def tinted_pixmap(path: Path | str, tint: str, size: int = LOGO_SIZE) -> QPixmap
if source.isNull():
return QPixmap()
scaled = source.scaled(
size, size, Qt.KeepAspectRatio, Qt.SmoothTransformation
size, size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation
)
out = QPixmap(scaled.size())
out.fill(Qt.transparent)
out.fill(Qt.GlobalColor.transparent)
painter = QPainter(out)
painter.drawPixmap(0, 0, scaled)
painter.setCompositionMode(QPainter.CompositionMode_SourceIn)
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceIn)
painter.fillRect(out.rect(), QColor(tint))
painter.end()
return out
@@ -55,11 +55,11 @@ class BrandBar(QFrame):
"""Thin brand rail: lockup and top-level view switcher, nothing else."""
#: The user asked for the live view.
live_requested = pyqtSignal()
live_requested = Signal()
#: The user asked for the history/replay view.
playback_requested = pyqtSignal()
playback_requested = Signal()
#: The user asked for the calibration view.
calibration_requested = pyqtSignal()
calibration_requested = Signal()
def __init__(self, parent=None, *, theme: str = "dark") -> None:
super().__init__(parent)
@@ -74,7 +74,7 @@ class BrandBar(QFrame):
self.logo = QLabel()
self.logo.setObjectName("brandLogo")
self.logo.setFixedSize(LOGO_SIZE, LOGO_SIZE)
self.logo.setAlignment(Qt.AlignCenter)
self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.logo)
# The lockup keeps its two labels (the product's tagline stays available
@@ -113,7 +113,7 @@ class BrandBar(QFrame):
self.product = QLabel()
self.product.setObjectName("brandProduct")
self.product.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.product.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
layout.addWidget(self.product)
self.set_theme(theme)
@@ -123,7 +123,7 @@ class BrandBar(QFrame):
def _nav_button(self) -> QPushButton:
button = QPushButton()
button.setObjectName("navButton")
button.setCursor(Qt.PointingHandCursor)
button.setCursor(Qt.CursorShape.PointingHandCursor)
button.setFlat(True)
button.setCheckable(True)
return button
+19 -19
View File
@@ -3,9 +3,9 @@
from __future__ import annotations
import numpy as np
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QColor, QFont, QFontDatabase
from PyQt5.QtWidgets import (
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QColor, QFont, QFontDatabase
from PySide6.QtWidgets import (
QAbstractItemView,
QButtonGroup,
QDoubleSpinBox,
@@ -60,11 +60,11 @@ def _mono_font() -> str:
class CalPanel(QFrame):
status_message = pyqtSignal(str)
status_message = Signal(str)
#: Coefficients were written to disk. It does **not** mean they were applied.
calibration_exported = pyqtSignal(str)
calibration_exported = Signal(str)
#: The operator asked to push the last export into the running device.
apply_requested = pyqtSignal(str)
apply_requested = Signal(str)
def __init__(self, parent=None, *, theme: str = "dark") -> None:
super().__init__(parent)
@@ -97,9 +97,9 @@ class CalPanel(QFrame):
outer.setSpacing(0)
self.scroll = QScrollArea()
self.scroll.setWidgetResizable(True)
self.scroll.setFrameShape(QFrame.NoFrame)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll.setFrameShape(QFrame.Shape.NoFrame)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
content = QWidget()
self.scroll.setWidget(content)
outer.addWidget(self.scroll)
@@ -135,7 +135,7 @@ class CalPanel(QFrame):
form = QFormLayout()
form.setHorizontalSpacing(10)
form.setVerticalSpacing(6)
form.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
form.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
self._weight_label = QLabel()
self._weight_label.setObjectName("fieldLabel")
self.weight_spin = QDoubleSpinBox()
@@ -182,16 +182,16 @@ class CalPanel(QFrame):
# Diagnostic table: the loaded axis plus the two crosstalk axes, a
# stability measure and the point's state.
self.table = QTableWidget(0, len(DIAGNOSTIC_COLUMNS))
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.setSelectionMode(QAbstractItemView.SingleSelection)
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
self.table.setAlternatingRowColors(True)
self.table.setShowGrid(False)
header = self.table.horizontalHeader()
# Every column takes the width its own header and cells need: eliding a
# diagnostic column ("FN 均值" -> "FN 均") hides the meaning, so a narrow
# panel scrolls instead.
header.setSectionResizeMode(QHeaderView.ResizeToContents)
header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
header.setMinimumSectionSize(64)
self.table.verticalHeader().setVisible(False)
self.table.verticalHeader().setDefaultSectionSize(26)
@@ -235,7 +235,7 @@ class CalPanel(QFrame):
):
# The action row must survive a narrow three-panel layout.
button.setProperty("compact", "true")
button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
button.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
self.btn_toggle_excluded.clicked.connect(self._on_toggle_excluded)
self.btn_toggle_validation.clicked.connect(self._on_toggle_validation)
self.btn_delete.clicked.connect(self._on_delete_row)
@@ -523,11 +523,11 @@ class CalPanel(QFrame):
)
for column, text in enumerate(values):
item = QTableWidgetItem(text)
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEditable)
if column in (0, 1, 2, 3):
item.setTextAlignment(int(Qt.AlignRight | Qt.AlignVCenter))
item.setTextAlignment(int(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter))
else:
item.setTextAlignment(int(Qt.AlignCenter))
item.setTextAlignment(int(Qt.AlignmentFlag.AlignCenter))
if point.excluded:
item.setForeground(QColor(color(self._theme, "text_muted")))
elif point.is_validation:
@@ -611,7 +611,7 @@ class CalPanel(QFrame):
"""Double click a row to inspect that point's three-axis curve."""
dialog = self.point_dialog(self._current_point())
if dialog is not None:
dialog.exec_()
dialog.exec()
def _on_toggle_excluded(self) -> None:
point = self._current_point()
@@ -7,9 +7,9 @@ the three-panel layout, while a single point can still be examined in full.
from __future__ import annotations
import numpy as np
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QFontDatabase
from PyQt5.QtWidgets import (
from PySide6.QtCore import Qt
from PySide6.QtGui import QFont, QFontDatabase
from PySide6.QtWidgets import (
QDialog,
QDialogButtonBox,
QFormLayout,
@@ -103,7 +103,7 @@ class CalibrationPointDialog(QDialog):
root.addWidget(self.plot_widget, 1)
self._build_traces()
buttons = QDialogButtonBox(QDialogButtonBox.Close)
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
buttons.rejected.connect(self.reject)
buttons.accepted.connect(self.accept)
root.addWidget(buttons)
+3 -3
View File
@@ -9,8 +9,8 @@ clips the text and the tooltip remains the way to read it.
from __future__ import annotations
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QLabel, QSizePolicy
from PySide6.QtCore import QSize
from PySide6.QtWidgets import QLabel, QSizePolicy
class ShrinkableLabel(QLabel):
@@ -18,7 +18,7 @@ class ShrinkableLabel(QLabel):
def __init__(self, text: str = "", parent=None) -> None:
super().__init__(text, parent)
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
self.setMinimumWidth(0)
def minimumSizeHint(self) -> QSize: # noqa: N802 - Qt naming
+13 -13
View File
@@ -5,9 +5,9 @@ from __future__ import annotations
import time
import numpy as np
from PyQt5.QtCore import Qt, QSize, pyqtSignal
from PyQt5.QtGui import QColor, QFont, QFontDatabase, QFontMetrics
from PyQt5.QtWidgets import (
from PySide6.QtCore import Qt, QSize, Signal
from PySide6.QtGui import QColor, QFont, QFontDatabase, QFontMetrics
from PySide6.QtWidgets import (
QFrame,
QHBoxLayout,
QLabel,
@@ -116,18 +116,18 @@ class _Sync:
class ForcePlotsPanel(QFrame):
#: The visible time window changed while browsing history.
window_changed = pyqtSignal(float, float)
window_changed = Signal(float, float)
#: The shared cursor moved; other views sync their playback position to it.
cursor_moved = pyqtSignal(float)
cursor_moved = Signal(float)
#: "follow" (live) or "browse" (user-controlled window).
mode_changed = pyqtSignal(str)
mode_changed = Signal(str)
#: The unit chip was clicked: switch between newton and raw.
unit_toggle_requested = pyqtSignal()
unit_toggle_requested = Signal()
#: The gear was clicked: open the plot settings (window, range, filters).
plot_settings_requested = pyqtSignal()
plot_settings_requested = Signal()
#: The operator took the timeline over (dragged the cursor, box-zoomed, panned
#: or moved the overview marker): playback stops where it is.
timeline_taken_over = pyqtSignal()
timeline_taken_over = Signal()
def __init__(self, parent=None, *, theme: str = "dark") -> None:
super().__init__(parent)
@@ -217,7 +217,7 @@ class ForcePlotsPanel(QFrame):
self.unit_badge = QPushButton()
self.unit_badge.setObjectName("unitBadge")
self.unit_badge.setProperty("compact", "true")
self.unit_badge.setCursor(Qt.PointingHandCursor)
self.unit_badge.setCursor(Qt.CursorShape.PointingHandCursor)
self.unit_badge.clicked.connect(self.unit_toggle_requested.emit)
header.addWidget(self.unit_badge)
root.addLayout(header)
@@ -329,7 +329,7 @@ class ForcePlotsPanel(QFrame):
self.window_label.setObjectName("panelMeta")
self.stats_label = QLabel()
self.stats_label.setObjectName("panelMeta")
self.stats_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.stats_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
browse_row.addWidget(self.window_caption)
browse_row.addWidget(self.window_label)
browse_row.addStretch(1)
@@ -348,7 +348,7 @@ class ForcePlotsPanel(QFrame):
button.setProperty("iconOnly", "true")
button.setIcon(icons.icon(icon_name, self._theme))
button.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
button.setCursor(Qt.PointingHandCursor)
button.setCursor(Qt.CursorShape.PointingHandCursor)
button._icon_name = icon_name # noqa: SLF001 - read back by set_theme
return button
@@ -389,7 +389,7 @@ class ForcePlotsPanel(QFrame):
value_row.setSpacing(5)
value_label = QLabel()
value_label.setObjectName("valueCard")
value_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
value_label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
value_label.setFont(_mono_font(16, bold=True))
# Never a fixed height: the assertion that a value is not clipped is
# derived from the font it is actually drawn with.
+8 -8
View File
@@ -14,8 +14,8 @@ from __future__ import annotations
from functools import lru_cache
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtGui import QColor, QIcon, QPainter, QPen, QPixmap
from PySide6.QtCore import QRectF, Qt
from PySide6.QtGui import QColor, QIcon, QPainter, QPen, QPixmap
from orisys_app_shared.theme import color
@@ -50,7 +50,7 @@ def _paint_zoom_fit(painter: QPainter, size: int) -> None:
def _paint_box_zoom(painter: QPainter, size: int) -> None:
"""A dashed frame: drag a region to zoom into it."""
pen = painter.pen()
pen.setStyle(Qt.DashLine)
pen.setStyle(Qt.PenStyle.DashLine)
pen.setDashPattern([2.2, 1.6])
painter.setPen(pen)
painter.drawRect(QRectF(2.0, 2.0, size - 4.0, size - 4.0))
@@ -143,19 +143,19 @@ _PAINTERS = {
def pixmap(name: str, tint: str, size: int = ICON_SIZE) -> QPixmap:
"""One monochrome icon, drawn in ``tint`` (a ``#RRGGBB`` token value)."""
canvas = QPixmap(size, size)
canvas.fill(Qt.transparent)
canvas.fill(Qt.GlobalColor.transparent)
paint = _PAINTERS.get(str(name))
if paint is None:
return canvas
painter = QPainter(canvas)
painter.setRenderHint(QPainter.Antialiasing, True)
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
pen = QPen()
pen.setWidthF(_PEN_WIDTH)
pen.setCapStyle(Qt.RoundCap)
pen.setJoinStyle(Qt.RoundJoin)
pen.setCapStyle(Qt.PenCapStyle.RoundCap)
pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
pen.setColor(QColor(tint))
painter.setPen(pen)
painter.setBrush(Qt.NoBrush)
painter.setBrush(Qt.BrushStyle.NoBrush)
try:
paint(painter, size)
finally:
@@ -16,8 +16,8 @@ from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING, Callable, Optional
from PyQt5.QtCore import QSize, Qt, pyqtSignal
from PyQt5.QtWidgets import (
from PySide6.QtCore import QSize, Qt, Signal
from PySide6.QtWidgets import (
QFrame,
QHBoxLayout,
QLabel,
@@ -78,7 +78,7 @@ class Mesh3dPanel(QFrame):
#: Emitted when the operator turns the pane on. The window wires the packet sink
#: from this, so no packet is adapted while the pane is off.
enabledChanged = pyqtSignal(bool)
enabledChanged = Signal(bool)
def __init__(
self, parent: Optional[QWidget] = None, *, theme: str = "dark"
@@ -128,14 +128,14 @@ class Mesh3dPanel(QFrame):
self.btn_reset_view = QPushButton()
self.btn_reset_view.setProperty("compact", "true")
self.btn_reset_view.setProperty("iconOnly", "true")
self.btn_reset_view.setCursor(Qt.PointingHandCursor)
self.btn_reset_view.setCursor(Qt.CursorShape.PointingHandCursor)
self.btn_reset_view.clicked.connect(self.reset_view)
header.addWidget(self.btn_reset_view)
self.btn_settings = QPushButton()
self.btn_settings.setProperty("compact", "true")
self.btn_settings.setProperty("iconOnly", "true")
self.btn_settings.setCursor(Qt.PointingHandCursor)
self.btn_settings.setCursor(Qt.CursorShape.PointingHandCursor)
self.btn_settings.clicked.connect(self.open_settings)
header.addWidget(self.btn_settings)
root.addLayout(header)
@@ -145,7 +145,7 @@ class Mesh3dPanel(QFrame):
self.message = QLabel()
self.message.setObjectName("hintText")
self.message.setAlignment(Qt.AlignCenter)
self.message.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.message.setWordWrap(True)
self._stack.addWidget(self.message)
@@ -9,7 +9,7 @@ reports as done is what the record will say (see ``force/filters.py``).
from __future__ import annotations
from PyQt5.QtWidgets import (
from PySide6.QtWidgets import (
QCheckBox,
QComboBox,
QDialog,
@@ -133,7 +133,7 @@ class PlotSettingsDialog(QDialog):
self.btn_reset.clicked.connect(self._reset_defaults)
buttons_row.addWidget(self.btn_reset)
buttons_row.addStretch(1)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
buttons_row.addWidget(self.button_box)
@@ -384,8 +384,8 @@ class PlotSettingsDialog(QDialog):
self.rate_label.setText(t("dialog.filter_rate"))
self.rate_spin.setToolTip(t("dialog.filter_rate_tip"))
self._refresh_filter_note()
ok_btn = self.button_box.button(QDialogButtonBox.Ok)
cancel_btn = self.button_box.button(QDialogButtonBox.Cancel)
ok_btn = self.button_box.button(QDialogButtonBox.StandardButton.Ok)
cancel_btn = self.button_box.button(QDialogButtonBox.StandardButton.Cancel)
if ok_btn is not None:
ok_btn.setText(t("dialog.ok"))
if cancel_btn is not None:
@@ -14,7 +14,7 @@ timer without touching the writer thread.
from __future__ import annotations
from PyQt5.QtWidgets import QLabel
from PySide6.QtWidgets import QLabel
from orisys_app_shared.i18n import t
from orisys_app_shared.theme import color
@@ -17,7 +17,7 @@ from __future__ import annotations
from datetime import datetime
from pathlib import Path
from PyQt5.QtWidgets import (
from PySide6.QtWidgets import (
QDialog,
QFrame,
QHBoxLayout,
@@ -111,7 +111,7 @@ class SensorDialog(QDialog):
def _section_rule() -> QFrame:
rule = QFrame()
rule.setObjectName("sectionSeparator")
rule.setFrameShape(QFrame.HLine)
rule.setFrameShape(QFrame.Shape.HLine)
rule.setFixedHeight(1)
return rule
@@ -9,8 +9,8 @@ own labels and tooltips instead of a bare "配置" combo.
from __future__ import annotations
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import (
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import (
QComboBox,
QHBoxLayout,
QLabel,
@@ -29,8 +29,8 @@ BADGE_WIDTH = 46
class SensorPickerWidget(QWidget):
sensor_type_changed = pyqtSignal(str)
config_changed = pyqtSignal(str)
sensor_type_changed = Signal(str)
config_changed = Signal(str)
def __init__(self, parent=None) -> None:
super().__init__(parent)
@@ -42,7 +42,7 @@ class SensorPickerWidget(QWidget):
#: attribute: it states which sensor the whole window is configured for.
self.thumb_label = QLabel()
self.thumb_label.setObjectName("unitBadge")
self.thumb_label.setAlignment(Qt.AlignCenter)
self.thumb_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
# A minimum, not a fixed width: "FINGER" must not be truncated to "INGE".
self.thumb_label.setMinimumWidth(BADGE_WIDTH)
self.thumb_label.setProperty("rail", "true")
@@ -55,7 +55,7 @@ class SensorPickerWidget(QWidget):
self.type_combo = QComboBox()
self.type_combo.setMinimumWidth(TYPE_WIDTH)
self.type_combo.setProperty("rail", "true")
self.type_combo.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
self.type_combo.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
for name in list_sensor_types():
self.type_combo.addItem(name, name)
self.type_combo.currentIndexChanged.connect(self._on_type_index_changed)
@@ -65,7 +65,7 @@ class SensorPickerWidget(QWidget):
self.config_combo = QComboBox()
self.config_combo.setMinimumWidth(CONFIG_WIDTH)
self.config_combo.setProperty("rail", "true")
self.config_combo.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
self.config_combo.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
self.config_combo.currentIndexChanged.connect(self._on_config_index_changed)
layout.addWidget(self.thumb_label)
+20 -20
View File
@@ -12,8 +12,8 @@ scrolls sideways instead of squeezing the controls into an unreadable row.
from __future__ import annotations
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import (
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import (
QComboBox,
QFrame,
QHBoxLayout,
@@ -42,9 +42,9 @@ RECORD_MODES = (("video_data", "record.mode_video_data"), ("data_only", "record.
def _separator() -> QFrame:
line = QFrame()
line.setObjectName("sectionSeparator")
line.setFrameShape(QFrame.VLine)
line.setFrameShape(QFrame.Shape.VLine)
line.setFixedWidth(1)
line.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
line.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
return line
@@ -70,23 +70,23 @@ def _group(*widgets: QWidget) -> QWidget:
class ToolbarWidget(QWidget):
search_clicked = pyqtSignal()
preview_toggled = pyqtSignal(bool)
camera_id_changed = pyqtSignal(int)
search_clicked = Signal()
preview_toggled = Signal(bool)
camera_id_changed = Signal(int)
#: The sensor summary button: opens the sensor and calibration dialog.
sensor_clicked = pyqtSignal()
sensor_clicked = Signal()
#: "Back to the camera" while a video file is the run source.
use_camera_clicked = pyqtSignal()
use_camera_clicked = Signal()
#: Replay view: pick the recorded video to play.
replay_video_clicked = pyqtSignal()
replay_video_clicked = Signal()
#: Replay view: re-run the pipeline over that video (the only processed picture).
recompute_clicked = pyqtSignal()
run_toggled = pyqtSignal(bool)
reset_clicked = pyqtSignal()
units_changed = pyqtSignal(str)
record_toggled = pyqtSignal(bool)
recompute_clicked = Signal()
run_toggled = Signal(bool)
reset_clicked = Signal()
units_changed = Signal(str)
record_toggled = Signal(bool)
#: The settings area was expanded or collapsed by the user.
settings_toggled = pyqtSignal(bool)
settings_toggled = Signal(bool)
def __init__(
self,
@@ -124,9 +124,9 @@ class ToolbarWidget(QWidget):
# narrow window.
self._settings_scroll = QScrollArea()
self._settings_scroll.setWidgetResizable(True)
self._settings_scroll.setFrameShape(QFrame.NoFrame)
self._settings_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self._settings_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self._settings_scroll.setFrameShape(QFrame.Shape.NoFrame)
self._settings_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self._settings_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.settings_page = self._build_settings()
self._settings_scroll.setWidget(self.settings_page)
outer.addWidget(self._settings_scroll)
@@ -157,7 +157,7 @@ class ToolbarWidget(QWidget):
self.status_chip = QLabel()
self.status_chip.setObjectName("statusChip")
self.status_chip.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
self.status_chip.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
self.source_summary = ShrinkableLabel()
self.source_summary.setObjectName("panelMeta")
self.calibration_label = ShrinkableLabel()
+1 -1
View File
@@ -24,7 +24,7 @@ import logging
from dataclasses import dataclass
from typing import Any, Optional, Protocol
from PyQt5.QtCore import QObject
from PySide6.QtCore import QObject
logger = logging.getLogger(__name__)