|
|
|
@@ -28,20 +28,16 @@ from orisys_app_shared.force.filters import FilterSettings
|
|
|
|
|
from orisys_app_shared.force.session import SessionStore
|
|
|
|
|
from orisys_app_shared.i18n import set_locale, t
|
|
|
|
|
from orisys_app_shared.logging import report_error
|
|
|
|
|
from orisys_app_shared.paths import logo_path, recordings_dir, sensor_json_path
|
|
|
|
|
from orisys_app_shared.paths import recordings_dir, sensor_json_path
|
|
|
|
|
from orisys_app_shared.prefs import save_preferences
|
|
|
|
|
from orisys_app_shared.record import RecordConfig
|
|
|
|
|
from orisys_app_shared.theme import build_qss, clear_theme_cache
|
|
|
|
|
from PySide6.QtCore import Qt, QTimer
|
|
|
|
|
from PySide6.QtGui import QAction, QActionGroup, QIcon
|
|
|
|
|
from PySide6.QtCore import QTimer
|
|
|
|
|
from PySide6.QtWidgets import (
|
|
|
|
|
QApplication,
|
|
|
|
|
QMainWindow,
|
|
|
|
|
QMessageBox,
|
|
|
|
|
QSplitter,
|
|
|
|
|
QStatusBar,
|
|
|
|
|
QVBoxLayout,
|
|
|
|
|
QWidget,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
from orisys_studio import session_metadata
|
|
|
|
@@ -56,12 +52,7 @@ from orisys_studio.scene3d.prefs import settings_to_prefs
|
|
|
|
|
from orisys_studio.session_controller import SessionController
|
|
|
|
|
from orisys_studio.status_presenter import RailState, StatusPresenter
|
|
|
|
|
from orisys_studio.ui.about_dialog import AboutDialog
|
|
|
|
|
from orisys_studio.ui.analysis_progress import AnalysisProgress
|
|
|
|
|
from orisys_studio.ui.arrow_panel import ArrowPanel, bgr_to_qimage
|
|
|
|
|
from orisys_studio.ui.brand_bar import BrandBar
|
|
|
|
|
from orisys_studio.ui.cal_panel import CalPanel
|
|
|
|
|
from orisys_studio.ui.force_plots import ForcePlotsPanel
|
|
|
|
|
from orisys_studio.ui.mesh3d_panel import Mesh3dPanel
|
|
|
|
|
from orisys_studio.ui.arrow_panel import bgr_to_qimage
|
|
|
|
|
from orisys_studio.ui.finger_orientation_dialog import FingerOrientationDialog
|
|
|
|
|
from orisys_studio.ui.plot_settings_dialog import (
|
|
|
|
|
DEFAULT_PLOT_FN_MAX,
|
|
|
|
@@ -71,9 +62,9 @@ from orisys_studio.ui.plot_settings_dialog import (
|
|
|
|
|
DEFAULT_PLOT_WINDOW_SEC,
|
|
|
|
|
PlotSettingsDialog,
|
|
|
|
|
)
|
|
|
|
|
from orisys_studio.ui.sensor_picker import SensorPickerWidget
|
|
|
|
|
from orisys_studio.ui.toolbar import ToolbarWidget
|
|
|
|
|
from orisys_studio.ui.video_analysis_dialog import VideoAnalysisDialog
|
|
|
|
|
from orisys_studio.ui.window_menus import WindowMenus
|
|
|
|
|
from orisys_studio.ui.window_ui import WindowUi
|
|
|
|
|
from orisys_studio.view_layout import ViewLayoutController
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@@ -228,54 +219,25 @@ class StudioMainWindow(QMainWindow):
|
|
|
|
|
#: Built once the panels exist; 2D paint and shared depth span live there.
|
|
|
|
|
self.frame_view: FrameView | None = None
|
|
|
|
|
|
|
|
|
|
icon = QIcon(str(logo_path()))
|
|
|
|
|
if not icon.isNull():
|
|
|
|
|
self.setWindowIcon(icon)
|
|
|
|
|
|
|
|
|
|
central = QWidget()
|
|
|
|
|
self.setCentralWidget(central)
|
|
|
|
|
root = QVBoxLayout(central)
|
|
|
|
|
root.setContentsMargins(10, 10, 10, 10)
|
|
|
|
|
root.setSpacing(8)
|
|
|
|
|
|
|
|
|
|
self.brand_bar = BrandBar(theme=self._theme)
|
|
|
|
|
root.addWidget(self.brand_bar)
|
|
|
|
|
|
|
|
|
|
# The sensor picker itself lives in the sensor dialog; the window owns it
|
|
|
|
|
# (it is the state every other panel depends on) and the rail shows a
|
|
|
|
|
# summary button that opens it.
|
|
|
|
|
self.sensor_picker = SensorPickerWidget()
|
|
|
|
|
self.ui = WindowUi(self, theme=self._theme)
|
|
|
|
|
# Preserve the widget entry points used by controllers, tests and UI tools.
|
|
|
|
|
self.brand_bar = self.ui.brand_bar
|
|
|
|
|
self.sensor_picker = self.ui.sensor_picker
|
|
|
|
|
self.toolbar = self.ui.toolbar
|
|
|
|
|
self.status_chip = self.ui.status_chip
|
|
|
|
|
self.analysis_progress = self.ui.analysis_progress
|
|
|
|
|
self.content_splitter = self.ui.content_splitter
|
|
|
|
|
self.mesh3d_panel = self.ui.mesh3d_panel
|
|
|
|
|
self.arrow_panel = self.ui.arrow_panel
|
|
|
|
|
self.force_plots = self.ui.force_plots
|
|
|
|
|
self.cal_panel = self.ui.cal_panel
|
|
|
|
|
#: Built on first use so the pickers are parented where they belong.
|
|
|
|
|
self.sensor_dialog = None
|
|
|
|
|
self.toolbar = ToolbarWidget(theme=self._theme)
|
|
|
|
|
root.addWidget(self.toolbar)
|
|
|
|
|
self.status_chip = self.toolbar.status_chip
|
|
|
|
|
self.status_presenter = StatusPresenter(self.toolbar)
|
|
|
|
|
# Part B: the analysis pass a replay is served from. The surface sits directly
|
|
|
|
|
# under the rail, so "what is it doing and can I stop it" is answered where the
|
|
|
|
|
# transport is; it is hidden until a pass (or a cache hit) has something to say.
|
|
|
|
|
self.analysis_progress = AnalysisProgress(theme=self._theme)
|
|
|
|
|
root.addWidget(self.analysis_progress)
|
|
|
|
|
|
|
|
|
|
self.content_splitter = QSplitter(Qt.Orientation.Horizontal)
|
|
|
|
|
self.mesh3d_panel = Mesh3dPanel(theme=self._theme)
|
|
|
|
|
self.arrow_panel = ArrowPanel(theme=self._theme)
|
|
|
|
|
# The island latch is *upstream of both views*: the 2D canvas paints the
|
|
|
|
|
# latched boundary, and the 3D pane's validity texture is that same mask. Two
|
|
|
|
|
# views, one decision about where the measurement is — see
|
|
|
|
|
# ``image_backend.DomainMaskLatch`` and ``Scene3DView.set_domain_latch``.
|
|
|
|
|
# Bind the shared mask and chart history before controllers can deliver frames.
|
|
|
|
|
self.frame_view = FrameView(self.arrow_panel, self.mesh3d_panel)
|
|
|
|
|
self.mesh3d_panel.set_domain_latch(self.frame_view.domain_mask)
|
|
|
|
|
self.force_plots = ForcePlotsPanel(theme=self._theme)
|
|
|
|
|
# The charts browse the whole session, not just their rolling buffer.
|
|
|
|
|
self.force_plots.set_store(self.session_store)
|
|
|
|
|
self.cal_panel = CalPanel(theme=self._theme)
|
|
|
|
|
self.content_splitter.addWidget(self.mesh3d_panel)
|
|
|
|
|
self.content_splitter.addWidget(self.arrow_panel)
|
|
|
|
|
self.content_splitter.addWidget(self.force_plots)
|
|
|
|
|
self.content_splitter.addWidget(self.cal_panel)
|
|
|
|
|
self.content_splitter.setChildrenCollapsible(False)
|
|
|
|
|
root.addWidget(self.content_splitter, 1)
|
|
|
|
|
|
|
|
|
|
# Playback drives the *chart cursor* (the playhead) instead of a slider
|
|
|
|
|
# under the panels: the two views share one layout, and the transport is
|
|
|
|
@@ -1037,162 +999,72 @@ class StudioMainWindow(QMainWindow):
|
|
|
|
|
self.content_splitter.setSizes(sizes)
|
|
|
|
|
|
|
|
|
|
def _build_menus(self) -> None:
|
|
|
|
|
menu_bar = self.menuBar()
|
|
|
|
|
self.menus = WindowMenus(
|
|
|
|
|
self,
|
|
|
|
|
show_cal=self._show_cal,
|
|
|
|
|
show_finger_3d=self._show_finger_3d,
|
|
|
|
|
show_fps=self._show_fps,
|
|
|
|
|
show_diagnostics=self.display_ctl.show_diagnostics,
|
|
|
|
|
settings_expanded=self.toolbar.settings_expanded(),
|
|
|
|
|
)
|
|
|
|
|
# Existing controllers and tools use these names; each is the same Qt object.
|
|
|
|
|
self.file_menu = self.menus.file_menu
|
|
|
|
|
self.act_open_video = self.menus.act_open_video
|
|
|
|
|
self.act_reveal_config = self.menus.act_reveal_config
|
|
|
|
|
self.act_playback = self.menus.act_playback
|
|
|
|
|
self.act_playback_latest = self.menus.act_playback_latest
|
|
|
|
|
self.act_play = self.menus.act_play
|
|
|
|
|
self.session_menu = self.menus.session_menu
|
|
|
|
|
self.act_run = self.menus.act_run
|
|
|
|
|
self.act_stop = self.menus.act_stop
|
|
|
|
|
self.act_preview = self.menus.act_preview
|
|
|
|
|
self.act_reset = self.menus.act_reset
|
|
|
|
|
self.act_pause = self.menus.act_pause
|
|
|
|
|
self.analysis_menu = self.menus.analysis_menu
|
|
|
|
|
self.act_recompute = self.menus.act_recompute
|
|
|
|
|
self.act_analysis_setup = self.menus.act_analysis_setup
|
|
|
|
|
self.view_menu = self.menus.view_menu
|
|
|
|
|
self.act_cal_panel = self.menus.act_cal_panel
|
|
|
|
|
self.act_finger_3d = self.menus.act_finger_3d
|
|
|
|
|
self.act_scene3d_settings = self.menus.act_scene3d_settings
|
|
|
|
|
self.act_show_fps = self.menus.act_show_fps
|
|
|
|
|
self.act_show_diagnostics = self.menus.act_show_diagnostics
|
|
|
|
|
self.act_settings = self.menus.act_settings
|
|
|
|
|
self.act_plot_settings = self.menus.act_plot_settings
|
|
|
|
|
self.act_rotate_arrows = self.menus.act_rotate_arrows
|
|
|
|
|
self.act_reset_view = self.menus.act_reset_view
|
|
|
|
|
self.theme_menu = self.menus.theme_menu
|
|
|
|
|
self.theme_group = self.menus.theme_group
|
|
|
|
|
self.act_theme_dark = self.menus.act_theme_dark
|
|
|
|
|
self.act_theme_light = self.menus.act_theme_light
|
|
|
|
|
self.lang_menu = self.menus.lang_menu
|
|
|
|
|
self.lang_group = self.menus.lang_group
|
|
|
|
|
self.act_lang_zh = self.menus.act_lang_zh
|
|
|
|
|
self.act_lang_en = self.menus.act_lang_en
|
|
|
|
|
self.help_menu = self.menus.help_menu
|
|
|
|
|
self.act_about = self.menus.act_about
|
|
|
|
|
|
|
|
|
|
self.file_menu = menu_bar.addMenu("")
|
|
|
|
|
self.act_open_video = QAction(self)
|
|
|
|
|
self.file_menu.addAction(self.act_open_video)
|
|
|
|
|
self.act_reveal_config = QAction(self)
|
|
|
|
|
# Initial checks are set before any business handlers are connected.
|
|
|
|
|
self.act_reveal_config.triggered.connect(self._on_reveal_config)
|
|
|
|
|
self.file_menu.addAction(self.act_reveal_config)
|
|
|
|
|
self.file_menu.addSeparator()
|
|
|
|
|
self.act_playback = QAction(self)
|
|
|
|
|
self.file_menu.addAction(self.act_playback)
|
|
|
|
|
self.act_playback_latest = QAction(self)
|
|
|
|
|
self.file_menu.addAction(self.act_playback_latest)
|
|
|
|
|
|
|
|
|
|
# The transport is on the chart header; the space bar is the editing
|
|
|
|
|
# convention everyone's hands already know.
|
|
|
|
|
self.act_play = QAction(self)
|
|
|
|
|
self.act_play.setShortcut("Space")
|
|
|
|
|
self.act_play.setShortcutContext(Qt.ShortcutContext.ApplicationShortcut)
|
|
|
|
|
self.addAction(self.act_play)
|
|
|
|
|
|
|
|
|
|
# Acquisition session controls (run / preview / pause). Order on the bar:
|
|
|
|
|
# File | Session | Analysis | View | Help.
|
|
|
|
|
self.session_menu = menu_bar.addMenu("")
|
|
|
|
|
|
|
|
|
|
self.act_run = QAction(self)
|
|
|
|
|
self.act_run.setShortcut("F5")
|
|
|
|
|
self.act_run.triggered.connect(self._on_run_shortcut)
|
|
|
|
|
self.session_menu.addAction(self.act_run)
|
|
|
|
|
|
|
|
|
|
self.act_stop = QAction(self)
|
|
|
|
|
self.act_stop.setShortcut("Esc")
|
|
|
|
|
self.act_stop.triggered.connect(self._on_stop_shortcut)
|
|
|
|
|
self.session_menu.addAction(self.act_stop)
|
|
|
|
|
|
|
|
|
|
self.act_preview = QAction(self)
|
|
|
|
|
self.act_preview.setShortcut("F6")
|
|
|
|
|
self.act_preview.triggered.connect(self._on_preview_shortcut)
|
|
|
|
|
self.session_menu.addAction(self.act_preview)
|
|
|
|
|
|
|
|
|
|
self.act_reset = QAction(self)
|
|
|
|
|
self.act_reset.setShortcut("Ctrl+R")
|
|
|
|
|
self.act_reset.triggered.connect(self._on_reset)
|
|
|
|
|
self.session_menu.addAction(self.act_reset)
|
|
|
|
|
|
|
|
|
|
# Pause has a shortcut as well as a rail button: an operator who is holding a
|
|
|
|
|
# sample still wants to stop the numbers moving without reaching for the mouse.
|
|
|
|
|
self.act_pause = QAction(self)
|
|
|
|
|
self.act_pause.setShortcut("F7")
|
|
|
|
|
self.act_pause.setCheckable(True)
|
|
|
|
|
self.act_pause.toggled.connect(self._on_pause_action_toggled)
|
|
|
|
|
self.session_menu.addAction(self.act_pause)
|
|
|
|
|
|
|
|
|
|
# Offline analysis of a recording. Separate from Session: that menu starts
|
|
|
|
|
# and stops a live device session.
|
|
|
|
|
self.analysis_menu = menu_bar.addMenu("")
|
|
|
|
|
# Serve / leave a stored analysis result (or run the pass when there is none).
|
|
|
|
|
self.act_recompute = QAction(self)
|
|
|
|
|
self.act_recompute.setCheckable(True)
|
|
|
|
|
self.act_recompute.setChecked(False)
|
|
|
|
|
self.analysis_menu.addAction(self.act_recompute)
|
|
|
|
|
# Reopen the setup dialog after the first pass (profile / calibration / reference).
|
|
|
|
|
self.act_analysis_setup = QAction(self)
|
|
|
|
|
self.act_analysis_setup.triggered.connect(self.replay_ctl.on_analysis_setup)
|
|
|
|
|
self.analysis_menu.addAction(self.act_analysis_setup)
|
|
|
|
|
|
|
|
|
|
self.view_menu = menu_bar.addMenu("")
|
|
|
|
|
self.act_cal_panel = QAction(self)
|
|
|
|
|
self.act_cal_panel.setCheckable(True)
|
|
|
|
|
self.act_cal_panel.setChecked(self._show_cal)
|
|
|
|
|
self.view_menu.addAction(self.act_cal_panel)
|
|
|
|
|
|
|
|
|
|
self.act_finger_3d = QAction(self)
|
|
|
|
|
self.act_finger_3d.setCheckable(True)
|
|
|
|
|
self.act_finger_3d.setChecked(self._show_finger_3d)
|
|
|
|
|
self.view_menu.addAction(self.act_finger_3d)
|
|
|
|
|
|
|
|
|
|
# Everything the 3D pane can be told lives in one dialog: the pane itself is for
|
|
|
|
|
# looking at (see ``Mesh3dPanel``). This is the menu way in; the pane's header
|
|
|
|
|
# has the gear, which is the same call.
|
|
|
|
|
self.act_scene3d_settings = QAction(self)
|
|
|
|
|
self.act_scene3d_settings.triggered.connect(self._on_scene3d_settings)
|
|
|
|
|
self.view_menu.addAction(self.act_scene3d_settings)
|
|
|
|
|
|
|
|
|
|
self.act_show_fps = QAction(self)
|
|
|
|
|
self.act_show_fps.setCheckable(True)
|
|
|
|
|
self.act_show_fps.setChecked(self._show_fps)
|
|
|
|
|
self.act_show_fps.toggled.connect(self.display_ctl.on_show_fps_toggled)
|
|
|
|
|
self.view_menu.addAction(self.act_show_fps)
|
|
|
|
|
|
|
|
|
|
# How much the panels explain themselves around their pictures. Checked *before*
|
|
|
|
|
# the signal is connected, so restoring the preference cannot look like a click.
|
|
|
|
|
self.act_show_diagnostics = QAction(self)
|
|
|
|
|
self.act_show_diagnostics.setCheckable(True)
|
|
|
|
|
self.act_show_diagnostics.setChecked(self.display_ctl.show_diagnostics)
|
|
|
|
|
self.act_show_diagnostics.toggled.connect(
|
|
|
|
|
self.display_ctl.on_show_diagnostics_toggled
|
|
|
|
|
)
|
|
|
|
|
self.view_menu.addAction(self.act_show_diagnostics)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.act_settings = QAction(self)
|
|
|
|
|
self.act_settings.setCheckable(True)
|
|
|
|
|
self.act_settings.setChecked(self.toolbar.settings_expanded())
|
|
|
|
|
self.view_menu.addAction(self.act_settings)
|
|
|
|
|
|
|
|
|
|
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.display_ctl.on_rotate_arrows)
|
|
|
|
|
self.view_menu.addAction(self.act_rotate_arrows)
|
|
|
|
|
self.view_menu.addSeparator()
|
|
|
|
|
|
|
|
|
|
# The graphics half of the old 重置: curves, window, cursor and the 3D camera.
|
|
|
|
|
# Separate because resetting the device tracker is not the same intention as
|
|
|
|
|
# wanting the views back, and only the first one belongs to the device (session menu).
|
|
|
|
|
self.act_reset_view = QAction(self)
|
|
|
|
|
self.act_reset_view.setShortcut("F8")
|
|
|
|
|
self.act_reset_view.triggered.connect(self._on_reset_view)
|
|
|
|
|
self.view_menu.addAction(self.act_reset_view)
|
|
|
|
|
self.view_menu.addSeparator()
|
|
|
|
|
|
|
|
|
|
self.theme_menu = self.view_menu.addMenu("")
|
|
|
|
|
self.theme_group = QActionGroup(self)
|
|
|
|
|
self.theme_group.setExclusive(True)
|
|
|
|
|
self.act_theme_dark = QAction(self)
|
|
|
|
|
self.act_theme_dark.setCheckable(True)
|
|
|
|
|
self.act_theme_light = QAction(self)
|
|
|
|
|
self.act_theme_light.setCheckable(True)
|
|
|
|
|
self.theme_group.addAction(self.act_theme_dark)
|
|
|
|
|
self.theme_group.addAction(self.act_theme_light)
|
|
|
|
|
self.theme_menu.addAction(self.act_theme_dark)
|
|
|
|
|
self.theme_menu.addAction(self.act_theme_light)
|
|
|
|
|
self.act_theme_dark.triggered.connect(lambda: self._set_theme("dark"))
|
|
|
|
|
self.act_theme_light.triggered.connect(lambda: self._set_theme("light"))
|
|
|
|
|
|
|
|
|
|
self.lang_menu = self.view_menu.addMenu("")
|
|
|
|
|
self.lang_group = QActionGroup(self)
|
|
|
|
|
self.lang_group.setExclusive(True)
|
|
|
|
|
self.act_lang_zh = QAction(self)
|
|
|
|
|
self.act_lang_zh.setCheckable(True)
|
|
|
|
|
self.act_lang_en = QAction(self)
|
|
|
|
|
self.act_lang_en.setCheckable(True)
|
|
|
|
|
self.lang_group.addAction(self.act_lang_zh)
|
|
|
|
|
self.lang_group.addAction(self.act_lang_en)
|
|
|
|
|
self.lang_menu.addAction(self.act_lang_zh)
|
|
|
|
|
self.lang_menu.addAction(self.act_lang_en)
|
|
|
|
|
self.act_lang_zh.triggered.connect(lambda: self._set_language("zh_CN"))
|
|
|
|
|
self.act_lang_en.triggered.connect(lambda: self._set_language("en_US"))
|
|
|
|
|
|
|
|
|
|
self.help_menu = menu_bar.addMenu("")
|
|
|
|
|
self.act_about = QAction(self)
|
|
|
|
|
self.act_about.triggered.connect(self._show_about)
|
|
|
|
|
self.help_menu.addAction(self.act_about)
|
|
|
|
|
|
|
|
|
|
# The replay entries exist now: hand them over, and the controller connects
|
|
|
|
|
# them (it owns what they mean — see ``ReplayController.bind_actions``).
|
|
|
|
@@ -1216,10 +1088,7 @@ class StudioMainWindow(QMainWindow):
|
|
|
|
|
self._sync_menu_checks()
|
|
|
|
|
|
|
|
|
|
def _sync_menu_checks(self) -> None:
|
|
|
|
|
self.act_theme_dark.setChecked(self._theme != "light")
|
|
|
|
|
self.act_theme_light.setChecked(self._theme == "light")
|
|
|
|
|
self.act_lang_zh.setChecked(self._language.startswith("zh"))
|
|
|
|
|
self.act_lang_en.setChecked(not self._language.startswith("zh"))
|
|
|
|
|
self.menus.sync_appearance(theme=self._theme, language=self._language)
|
|
|
|
|
# The view entries are the layout's; the theme/language ones are the window's.
|
|
|
|
|
self.layout_ctl.sync_view_checks()
|
|
|
|
|
self.act_recompute.setEnabled(self.replay_ctl.can_recompute())
|
|
|
|
@@ -1372,51 +1241,11 @@ class StudioMainWindow(QMainWindow):
|
|
|
|
|
|
|
|
|
|
def retranslate(self) -> None:
|
|
|
|
|
self.setWindowTitle(t("app.title"))
|
|
|
|
|
self.file_menu.setTitle(t("menu.file"))
|
|
|
|
|
self.act_open_video.setText(t("menu.open_video"))
|
|
|
|
|
self.act_reveal_config.setText(t("menu.reveal_config"))
|
|
|
|
|
self.act_playback.setText(t("menu.playback"))
|
|
|
|
|
self.act_playback_latest.setText(t("menu.playback_latest"))
|
|
|
|
|
self.session_menu.setTitle(t("menu.session"))
|
|
|
|
|
self.act_run.setText(t("menu.run"))
|
|
|
|
|
self.act_stop.setText(t("menu.stop"))
|
|
|
|
|
self.act_preview.setText(t("menu.preview"))
|
|
|
|
|
# Ctrl+R is the rail's reset on a second surface, so its wording follows the
|
|
|
|
|
# view the same way the button's does (see ``_sync_reset_action``).
|
|
|
|
|
self.menus.retranslate()
|
|
|
|
|
# Reset wording and pause state depend on the active workflow.
|
|
|
|
|
self._sync_reset_action()
|
|
|
|
|
self.act_pause.setText(t("menu.pause"))
|
|
|
|
|
self._sync_pause_action()
|
|
|
|
|
self.analysis_menu.setTitle(t("menu.analysis"))
|
|
|
|
|
self.act_recompute.setText(t("menu.recompute"))
|
|
|
|
|
self.act_recompute.setToolTip(t("menu.recompute_tip"))
|
|
|
|
|
self.act_analysis_setup.setText(t("menu.analysis_setup"))
|
|
|
|
|
self.act_analysis_setup.setToolTip(t("menu.analysis_setup_tip"))
|
|
|
|
|
self.view_menu.setTitle(t("menu.view"))
|
|
|
|
|
self.act_cal_panel.setText(t("menu.calibration_panel"))
|
|
|
|
|
self.act_finger_3d.setText(t("menu.finger_3d"))
|
|
|
|
|
self.act_scene3d_settings.setText(t("menu.scene3d_settings"))
|
|
|
|
|
self.act_show_fps.setText(t("menu.show_fps"))
|
|
|
|
|
self.act_show_diagnostics.setText(t("menu.show_diagnostics"))
|
|
|
|
|
self.act_settings.setText(t("menu.settings"))
|
|
|
|
|
self.act_plot_settings.setText(t("menu.plot_settings"))
|
|
|
|
|
self.act_rotate_arrows.setText(t("menu.rotate_arrows"))
|
|
|
|
|
self.act_reset_view.setText(t("menu.reset_view"))
|
|
|
|
|
self.act_reset_view.setToolTip(t("menu.reset_view_tip"))
|
|
|
|
|
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"))
|
|
|
|
|
self.lang_menu.setTitle(t("menu.language"))
|
|
|
|
|
self.act_lang_zh.setText(t("menu.lang_zh"))
|
|
|
|
|
self.act_lang_en.setText(t("menu.lang_en"))
|
|
|
|
|
self.help_menu.setTitle(t("menu.help"))
|
|
|
|
|
self.act_about.setText(t("menu.about"))
|
|
|
|
|
self.toolbar.retranslate()
|
|
|
|
|
self.sensor_picker.retranslate()
|
|
|
|
|
self.mesh3d_panel.retranslate()
|
|
|
|
|
self.arrow_panel.retranslate()
|
|
|
|
|
self.force_plots.retranslate()
|
|
|
|
|
self.cal_panel.retranslate()
|
|
|
|
|
self.brand_bar.retranslate()
|
|
|
|
|
self.ui.retranslate()
|
|
|
|
|
self._refresh_status_chip()
|
|
|
|
|
self._refresh_status_rail()
|
|
|
|
|
self._refresh_record_state()
|
|
|
|
@@ -1490,12 +1319,7 @@ class StudioMainWindow(QMainWindow):
|
|
|
|
|
app = QApplication.instance()
|
|
|
|
|
if app is not None:
|
|
|
|
|
app.setStyleSheet(build_qss(theme))
|
|
|
|
|
self.force_plots.set_theme(theme)
|
|
|
|
|
self.cal_panel.set_theme(theme)
|
|
|
|
|
self.brand_bar.set_theme(theme)
|
|
|
|
|
self.toolbar.set_theme(theme)
|
|
|
|
|
self.arrow_panel.set_theme(theme)
|
|
|
|
|
self.mesh3d_panel.set_theme(theme)
|
|
|
|
|
self.ui.set_theme(theme)
|
|
|
|
|
self._refresh_status_chip()
|
|
|
|
|
self._refresh_status_rail()
|
|
|
|
|
self._refresh_record_state()
|
|
|
|
|