update arrow rotate control

This commit is contained in:
2026-09-07 14:24:30 +08:00
parent e2d5e69229
commit a4766abee8
7 changed files with 95 additions and 98 deletions
@@ -49,6 +49,7 @@ from orisys_customer.ui.toolbar import ToolbarWidget
logger = logging.getLogger(__name__)
RUNNABLE_TYPES = frozenset({"box", "mini", "finger"})
ARROW_ROTATE_TYPES = frozenset({"box", "mini"})
def _bgr_to_qimage(frame: np.ndarray) -> QImage:
@@ -196,6 +197,7 @@ class CustomerMainWindow(QMainWindow):
self.toolbar.set_units(str(self.prefs.get("units") or "newton"))
self.arrow_panel.set_view_mode(str(self.prefs.get("image_view_mode") or "arrows_pure"))
self.arrow_panel.set_fps_visible(self._show_fps)
self.arrow_panel.set_rotation_deg(self.prefs.get("arrow_rotation_deg", 90))
self._apply_plot_prefs_to_panel()
sensor_type = str(self.prefs.get("sensor_type") or "box")
@@ -205,6 +207,7 @@ class CustomerMainWindow(QMainWindow):
self.sensor_picker.set_config_name(config_name)
else:
self.prefs["config_name"] = self.sensor_picker.config_name()
self._apply_arrow_rotation_for_sensor()
def _build_menus(self) -> None:
menu_bar = self.menuBar()
@@ -239,6 +242,10 @@ class CustomerMainWindow(QMainWindow):
self.act_plot_settings = QAction(self)
self.act_plot_settings.triggered.connect(self._on_plot_settings)
self.view_menu.addAction(self.act_plot_settings)
self.act_rotate_arrows = QAction(self)
self.act_rotate_arrows.triggered.connect(self._on_rotate_arrows)
self.view_menu.addAction(self.act_rotate_arrows)
self.view_menu.addSeparator()
self.theme_menu = self.view_menu.addMenu("")
@@ -289,6 +296,7 @@ class CustomerMainWindow(QMainWindow):
self.act_finger_3d.blockSignals(True)
self.act_finger_3d.setChecked(bool(self._show_finger_3d) if is_finger else False)
self.act_finger_3d.blockSignals(False)
self._apply_arrow_rotation_for_sensor()
def _apply_panel_visibility(self) -> None:
is_finger = self.sensor_picker.sensor_type() == "finger"
@@ -329,6 +337,7 @@ class CustomerMainWindow(QMainWindow):
self.act_finger_3d.setText(t("menu.finger_3d"))
self.act_show_fps.setText(t("menu.show_fps"))
self.act_plot_settings.setText(t("menu.plot_settings"))
self.act_rotate_arrows.setText(t("menu.rotate_arrows"))
self.theme_menu.setTitle(t("menu.theme"))
self.act_theme_dark.setText(t("menu.theme_dark"))
self.act_theme_light.setText(t("menu.theme_light"))
@@ -449,9 +458,22 @@ class CustomerMainWindow(QMainWindow):
config_ref=self._resolve_config_ref(),
)
self._apply_panel_visibility()
self._apply_arrow_rotation_for_sensor()
self._update_run_enabled()
self._persist_prefs()
def _apply_arrow_rotation_for_sensor(self) -> None:
enabled = self.sensor_picker.sensor_type() in ARROW_ROTATE_TYPES
self.arrow_panel.set_rotation_enabled(enabled)
self.act_rotate_arrows.setEnabled(enabled)
def _on_rotate_arrows(self) -> None:
if self.sensor_picker.sensor_type() not in ARROW_ROTATE_TYPES:
return
deg = self.arrow_panel.rotate_90_ccw()
self.prefs["arrow_rotation_deg"] = deg
self._persist_prefs()
def _on_config_changed(self, config_name: str) -> None:
self.prefs["config_name"] = str(config_name)
self._persist_prefs()
@@ -824,6 +846,7 @@ class CustomerMainWindow(QMainWindow):
self.prefs["video_path"] = self.toolbar.video_path()
self.prefs["units"] = self.toolbar.units()
self.prefs["image_view_mode"] = self.arrow_panel.view_mode()
self.prefs["arrow_rotation_deg"] = self.arrow_panel.rotation_deg()
self.prefs.setdefault("plot_window_sec", DEFAULT_PLOT_WINDOW_SEC)
self.prefs.setdefault("plot_fn_max", DEFAULT_PLOT_FN_MAX)
self.prefs.setdefault("plot_shear_max", DEFAULT_PLOT_SHEAR_MAX)
@@ -3,7 +3,7 @@
from __future__ import annotations
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtGui import QImage, QPixmap, QTransform
from PyQt5.QtWidgets import (
QComboBox,
QFrame,
@@ -16,6 +16,17 @@ from PyQt5.QtWidgets import (
from orisys_app_shared.i18n import t
VIEW_MODES = ("arrows_pure", "arrows_stitched", "depth")
_ROTATION_STEPS = (0, 90, 180, 270)
def _normalize_rotation_deg(value: object, default: int = 90) -> int:
try:
deg = int(value) % 360 # type: ignore[arg-type]
except (TypeError, ValueError):
return default
if deg not in _ROTATION_STEPS:
deg = int(round(deg / 90.0) * 90) % 360
return deg
class ArrowPanel(QFrame):
@@ -34,6 +45,9 @@ class ArrowPanel(QFrame):
self.fps_label.setVisible(False)
self._fps_visible = False
self._fps_value: float | None = None
self._rotation_deg = 90
self._rotation_enabled = True
self._source_image: QImage | None = None
self.view_combo = QComboBox()
self.view_combo.setMinimumWidth(140)
self.view_combo.currentIndexChanged.connect(self._on_view_index_changed)
@@ -68,6 +82,26 @@ class ArrowPanel(QFrame):
self.view_combo.setCurrentIndex(idx)
self.view_combo.blockSignals(False)
def rotation_deg(self) -> int:
return self._rotation_deg
def set_rotation_deg(self, deg: object) -> None:
self._rotation_deg = _normalize_rotation_deg(deg)
self._repaint_source()
def set_rotation_enabled(self, enabled: bool) -> None:
wanted = bool(enabled)
if wanted == self._rotation_enabled:
return
self._rotation_enabled = wanted
self._repaint_source()
def rotate_90_ccw(self) -> int:
if not self._rotation_enabled:
return self._rotation_deg
self.set_rotation_deg(self._rotation_deg + 90)
return self._rotation_deg
def set_fps_visible(self, visible: bool) -> None:
self._fps_visible = bool(visible)
self._refresh_fps_label()
@@ -113,10 +147,22 @@ class ArrowPanel(QFrame):
self._placeholder = t("app.scaffold_hint")
self.image_label.setText(self._placeholder)
def show_frame(self, image: QImage) -> None:
if image.isNull():
def _effective_rotation_deg(self) -> int:
if not self._rotation_enabled:
return 0
return self._rotation_deg
def _repaint_source(self) -> None:
if self._source_image is None or self._source_image.isNull():
return
self._paint_image(self._source_image)
def _paint_image(self, image: QImage) -> None:
pix = QPixmap.fromImage(image)
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)
scaled = pix.scaled(
self.image_label.size(),
Qt.KeepAspectRatio,
@@ -125,7 +171,14 @@ class ArrowPanel(QFrame):
self.image_label.setPixmap(scaled)
self.image_label.setText("")
def show_frame(self, image: QImage) -> None:
if image.isNull():
return
self._source_image = image
self._paint_image(image)
def clear_preview(self, message: str | None = None) -> None:
self._source_image = None
self.image_label.clear()
self.image_label.setPixmap(QPixmap())
text = message if message is not None else t("app.scaffold_hint")