migrate pyqt5 to pyside6
This commit is contained in:
@@ -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__)
|
||||
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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("")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user