fix stop button disabled during connection
This commit is contained in:
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -42,7 +43,9 @@ from orisys_studio.ui.force_plots import ForcePlotsPanel
|
||||
from orisys_studio.ui.mesh3d_panel import Mesh3dPanel
|
||||
from orisys_studio.ui.plot_settings_dialog import (
|
||||
DEFAULT_PLOT_FN_MAX,
|
||||
DEFAULT_PLOT_FN_RAW_MAX,
|
||||
DEFAULT_PLOT_SHEAR_MAX,
|
||||
DEFAULT_PLOT_SHEAR_RAW_MAX,
|
||||
DEFAULT_PLOT_WINDOW_SEC,
|
||||
PlotSettingsDialog,
|
||||
)
|
||||
@@ -61,6 +64,8 @@ MIN_PANEL_WIDTHS = {
|
||||
"cal": 300,
|
||||
}
|
||||
SESSION_PHASES = ("idle", "connecting", "running", "error")
|
||||
#: After this long in ``connecting``, tell the user that Stop is the way out.
|
||||
CONNECT_SLOW_HINT_S = 8.0
|
||||
|
||||
|
||||
def _bgr_to_qimage(frame: np.ndarray) -> QImage:
|
||||
@@ -93,6 +98,8 @@ class StudioMainWindow(QMainWindow):
|
||||
self._show_fps = bool(self.prefs.get("show_fps", False))
|
||||
self._session_phase = "idle"
|
||||
self._running = False
|
||||
self._connect_started_at = 0.0
|
||||
self._connect_slow_notified = False
|
||||
self._preview = None
|
||||
self._preview_timer = QTimer(self)
|
||||
self._preview_timer.setInterval(33)
|
||||
@@ -309,6 +316,11 @@ class StudioMainWindow(QMainWindow):
|
||||
self.act_run.triggered.connect(self._on_run_shortcut)
|
||||
self.view_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.view_menu.addAction(self.act_stop)
|
||||
|
||||
self.act_preview = QAction(self)
|
||||
self.act_preview.setShortcut("F6")
|
||||
self.act_preview.triggered.connect(self._on_preview_shortcut)
|
||||
@@ -421,6 +433,7 @@ class StudioMainWindow(QMainWindow):
|
||||
self.act_plot_settings.setText(t("menu.plot_settings"))
|
||||
self.act_rotate_arrows.setText(t("menu.rotate_arrows"))
|
||||
self.act_run.setText(t("menu.run"))
|
||||
self.act_stop.setText(t("menu.stop"))
|
||||
self.act_preview.setText(t("menu.preview"))
|
||||
self.act_reset.setText(t("menu.reset"))
|
||||
self.theme_menu.setTitle(t("menu.theme"))
|
||||
@@ -446,6 +459,7 @@ class StudioMainWindow(QMainWindow):
|
||||
def _set_phase(self, phase: str) -> None:
|
||||
"""Track the acquisition phase and reflect it in the status chip."""
|
||||
self._session_phase = phase if phase in SESSION_PHASES else "idle"
|
||||
self.toolbar.set_run_phase(self._session_phase)
|
||||
self._refresh_status_chip()
|
||||
|
||||
def _refresh_status_chip(self) -> None:
|
||||
@@ -513,16 +527,24 @@ class StudioMainWindow(QMainWindow):
|
||||
window_sec=float(self.prefs.get("plot_window_sec") or DEFAULT_PLOT_WINDOW_SEC),
|
||||
fn_max=float(self.prefs.get("plot_fn_max") or DEFAULT_PLOT_FN_MAX),
|
||||
shear_max=float(self.prefs.get("plot_shear_max") or DEFAULT_PLOT_SHEAR_MAX),
|
||||
fn_raw_max=float(
|
||||
self.prefs.get("plot_fn_raw_max") or DEFAULT_PLOT_FN_RAW_MAX
|
||||
),
|
||||
shear_raw_max=float(
|
||||
self.prefs.get("plot_shear_raw_max") or DEFAULT_PLOT_SHEAR_RAW_MAX
|
||||
),
|
||||
units=self.toolbar.units(),
|
||||
)
|
||||
if dialog.exec_() != PlotSettingsDialog.Accepted:
|
||||
return
|
||||
window_sec, fn_max, shear_max = dialog.values()
|
||||
window_sec, fn_max, shear_max, fn_raw_max, shear_raw_max = dialog.values()
|
||||
self.prefs["plot_window_sec"] = window_sec
|
||||
self.prefs["plot_fn_max"] = fn_max
|
||||
self.prefs["plot_shear_max"] = shear_max
|
||||
self.prefs["plot_fn_raw_max"] = fn_raw_max
|
||||
self.prefs["plot_shear_raw_max"] = shear_raw_max
|
||||
self.force_plots.set_window_sec(window_sec)
|
||||
self.force_plots.set_axis_limits(fn_max, shear_max)
|
||||
self.force_plots.set_axis_limits(fn_max, shear_max, fn_raw_max, shear_raw_max)
|
||||
self._last_plot_packet_key = None
|
||||
self._persist_prefs()
|
||||
|
||||
@@ -533,6 +555,8 @@ class StudioMainWindow(QMainWindow):
|
||||
self.force_plots.set_axis_limits(
|
||||
float(self.prefs.get("plot_fn_max") or DEFAULT_PLOT_FN_MAX),
|
||||
float(self.prefs.get("plot_shear_max") or DEFAULT_PLOT_SHEAR_MAX),
|
||||
float(self.prefs.get("plot_fn_raw_max") or DEFAULT_PLOT_FN_RAW_MAX),
|
||||
float(self.prefs.get("plot_shear_raw_max") or DEFAULT_PLOT_SHEAR_RAW_MAX),
|
||||
)
|
||||
self.force_plots.set_fixed_newton(self.toolbar.units() == "newton")
|
||||
|
||||
@@ -590,10 +614,19 @@ class StudioMainWindow(QMainWindow):
|
||||
self._persist_prefs()
|
||||
|
||||
# ----------------------------------------------------------- shortcuts
|
||||
@property
|
||||
def _session_active(self) -> bool:
|
||||
"""True until the worker has fully settled, not just until Stop was hit."""
|
||||
if self._running:
|
||||
return True
|
||||
worker = self._active_worker
|
||||
return worker is not None and worker.is_running
|
||||
|
||||
def _sync_shortcut_actions(self) -> None:
|
||||
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)
|
||||
self.act_preview.setEnabled(self.toolbar.btn_preview.isEnabled())
|
||||
self.act_reset.setEnabled(self.toolbar.btn_reset.isEnabled())
|
||||
|
||||
@@ -602,6 +635,12 @@ class StudioMainWindow(QMainWindow):
|
||||
return
|
||||
self.toolbar.btn_run.setChecked(not self.toolbar.btn_run.isChecked())
|
||||
|
||||
def _on_stop_shortcut(self) -> None:
|
||||
"""Esc must always be able to cancel a session, even a stuck connect."""
|
||||
if not self._session_active:
|
||||
return
|
||||
self._stop_run(reason="user_stop")
|
||||
|
||||
def _on_preview_shortcut(self) -> None:
|
||||
if not self.toolbar.btn_preview.isEnabled():
|
||||
return
|
||||
@@ -781,12 +820,16 @@ class StudioMainWindow(QMainWindow):
|
||||
|
||||
self._running = True
|
||||
self._set_phase("connecting")
|
||||
self._connect_started_at = time.perf_counter()
|
||||
self._connect_slow_notified = False
|
||||
self.cal_panel.set_meta_context(sensor_type=sensor_type, config_ref=config_ref)
|
||||
self.cal_panel.set_running(True)
|
||||
self.toolbar.set_source_controls_enabled(False)
|
||||
self.sensor_picker.set_picker_enabled(False)
|
||||
self.toolbar.set_reset_enabled(False)
|
||||
self._run_timer.start()
|
||||
# Keep the stop affordance consistent with the phase we just entered.
|
||||
self._update_run_enabled()
|
||||
self.statusBar().showMessage(t("status.connecting"))
|
||||
logger.info(
|
||||
"Connecting type=%s source=%s config=%s",
|
||||
@@ -795,10 +838,13 @@ class StudioMainWindow(QMainWindow):
|
||||
config_ref,
|
||||
)
|
||||
|
||||
def _stop_run(self, *, reason: str = "user_stop") -> None:
|
||||
def _stop_run(self, *, reason: str = "user_stop", wait: bool = False) -> None:
|
||||
self._run_timer.stop()
|
||||
if self._active_worker is not None:
|
||||
self._active_worker.stop()
|
||||
# Never wait here: a device open that hangs must not freeze the GUI.
|
||||
# ``wait=True`` is reserved for shutdown paths.
|
||||
self._active_worker.stop(wait=wait)
|
||||
was_connecting = self._session_phase == "connecting"
|
||||
self._running = False
|
||||
self._set_phase("error" if reason == "failed" else "idle")
|
||||
self.cal_panel.set_running(False)
|
||||
@@ -812,14 +858,18 @@ class StudioMainWindow(QMainWindow):
|
||||
self.arrow_panel.clear_preview(t("panels.arrow_empty"))
|
||||
else:
|
||||
self.arrow_panel.set_fps(None)
|
||||
winding_down = self._session_active
|
||||
if reason == "ended":
|
||||
self.statusBar().showMessage(t("status.video_ended"))
|
||||
elif reason == "disconnected":
|
||||
self.statusBar().showMessage(t("status.camera_disconnected"))
|
||||
elif reason == "failed":
|
||||
pass # caller sets status
|
||||
elif reason == "user_stop":
|
||||
self.statusBar().showMessage(t("status.run_stopped"))
|
||||
elif winding_down and was_connecting:
|
||||
# The worker thread is still releasing the device in the background.
|
||||
self.statusBar().showMessage(t("status.run_stopping_cancelled"))
|
||||
elif winding_down:
|
||||
self.statusBar().showMessage(t("status.run_stopping"))
|
||||
else:
|
||||
self.statusBar().showMessage(t("status.run_stopped"))
|
||||
|
||||
@@ -874,6 +924,13 @@ class StudioMainWindow(QMainWindow):
|
||||
self._stop_run(reason="user_stop")
|
||||
return
|
||||
|
||||
if not self._connect_slow_notified and self._session_phase == "connecting":
|
||||
# The worker may be stuck opening a device; make sure the user knows
|
||||
# the (non-blocking) Stop button is the way out.
|
||||
if time.perf_counter() - self._connect_started_at >= CONNECT_SLOW_HINT_S:
|
||||
self._connect_slow_notified = True
|
||||
self.statusBar().showMessage(t("status.connect_slow"))
|
||||
|
||||
packet = self._active_worker.latest()
|
||||
if packet is None:
|
||||
return
|
||||
@@ -882,6 +939,7 @@ class StudioMainWindow(QMainWindow):
|
||||
|
||||
if self._session_phase == "connecting" and is_new:
|
||||
self._set_phase("running")
|
||||
self._connect_slow_notified = True
|
||||
self.toolbar.set_reset_enabled(True)
|
||||
self._sync_shortcut_actions()
|
||||
self.statusBar().showMessage(
|
||||
@@ -987,6 +1045,8 @@ class StudioMainWindow(QMainWindow):
|
||||
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)
|
||||
self.prefs.setdefault("plot_fn_raw_max", DEFAULT_PLOT_FN_RAW_MAX)
|
||||
self.prefs.setdefault("plot_shear_raw_max", DEFAULT_PLOT_SHEAR_RAW_MAX)
|
||||
self.prefs["show_cal_panel"] = bool(self._show_cal)
|
||||
self.prefs["show_finger_3d"] = bool(self._show_finger_3d)
|
||||
self.prefs["show_fps"] = bool(self._show_fps)
|
||||
@@ -996,7 +1056,8 @@ class StudioMainWindow(QMainWindow):
|
||||
|
||||
def closeEvent(self, event) -> None: # noqa: N802
|
||||
self._stop_preview(announce=False)
|
||||
self._stop_run(reason="user_stop")
|
||||
# Blocking: on the way out we must not leave a device handle behind.
|
||||
self._stop_run(reason="user_stop", wait=True)
|
||||
self._persist_prefs()
|
||||
logger.info("Studio shell closed; preferences saved")
|
||||
super().closeEvent(event)
|
||||
|
||||
@@ -63,6 +63,8 @@ class ForcePlotsPanel(QFrame):
|
||||
self._theme = theme
|
||||
self._fn_max = 20.0
|
||||
self._shear_max = 20.0
|
||||
self._fn_raw_max = 1000000.0
|
||||
self._shear_raw_max = 500000.0
|
||||
self._fixed_newton = True
|
||||
self._unit = "raw"
|
||||
self._last_values: tuple[float, float, float] = (0.0, 0.0, 0.0)
|
||||
@@ -197,9 +199,19 @@ class ForcePlotsPanel(QFrame):
|
||||
self._clear_curves()
|
||||
self._apply_axis_mode()
|
||||
|
||||
def set_axis_limits(self, fn_max: float, shear_max: float) -> None:
|
||||
def set_axis_limits(
|
||||
self,
|
||||
fn_max: float,
|
||||
shear_max: float,
|
||||
fn_raw_max: float | None = None,
|
||||
shear_raw_max: float | None = None,
|
||||
) -> None:
|
||||
self._fn_max = max(0.1, float(fn_max))
|
||||
self._shear_max = max(0.1, float(shear_max))
|
||||
if fn_raw_max is not None:
|
||||
self._fn_raw_max = max(0.0, float(fn_raw_max))
|
||||
if shear_raw_max is not None:
|
||||
self._shear_raw_max = max(0.0, float(shear_raw_max))
|
||||
self._apply_axis_mode()
|
||||
|
||||
def set_fixed_newton(self, enabled: bool) -> None:
|
||||
@@ -247,17 +259,36 @@ class ForcePlotsPanel(QFrame):
|
||||
self.plots[i].setYRange(*state.y_ranges[i], padding=0.0)
|
||||
|
||||
def _uses_fixed_y(self) -> bool:
|
||||
return bool(self._fixed_newton) and self._unit == "N"
|
||||
"""Fixed Y whenever we have limits for the active unit.
|
||||
|
||||
Raw mode used to auto-range, which collapsed the axis onto the idle
|
||||
jitter and then rescaled on every contact. A fixed raw range keeps the
|
||||
baseline visually flat: the raw maxima are sized so the idle jitter is
|
||||
well under one pixel.
|
||||
"""
|
||||
if self._unit == "N":
|
||||
return bool(self._fixed_newton)
|
||||
return self._fn_raw_max > 0.0 and self._shear_raw_max > 0.0
|
||||
|
||||
def _fixed_y_range(self, channel_index: int) -> tuple[float, float]:
|
||||
if self._unit == "N":
|
||||
fn_max, shear_max = self._fn_max, self._shear_max
|
||||
else:
|
||||
fn_max, shear_max = self._fn_raw_max, self._shear_raw_max
|
||||
if channel_index == 0:
|
||||
return 0.0, self._fn_max
|
||||
return -self._shear_max, self._shear_max
|
||||
return 0.0, fn_max
|
||||
return -shear_max, shear_max
|
||||
|
||||
def _preview_fixed_y(self) -> bool:
|
||||
"""Whether to paint fixed axes before any sample has arrived."""
|
||||
if self.history.sample_count > 0:
|
||||
return self._uses_fixed_y()
|
||||
if self._unit == "N":
|
||||
return bool(self._fixed_newton)
|
||||
return True
|
||||
|
||||
def _apply_axis_mode(self) -> None:
|
||||
# Preview fixed newton axes when newton mode is selected, even before samples.
|
||||
preview_fixed = bool(self._fixed_newton)
|
||||
use_fixed = self._uses_fixed_y() or (preview_fixed and self.history.sample_count == 0)
|
||||
use_fixed = self._preview_fixed_y()
|
||||
for i, plot in enumerate(self.plots):
|
||||
if use_fixed:
|
||||
ymin, ymax = self._fixed_y_range(i)
|
||||
|
||||
@@ -18,6 +18,10 @@ from orisys_app_shared.i18n import t
|
||||
DEFAULT_PLOT_WINDOW_SEC = 4.0
|
||||
DEFAULT_PLOT_FN_MAX = 20.0
|
||||
DEFAULT_PLOT_SHEAR_MAX = 20.0
|
||||
# Raw (uncalibrated) HHD units: sized from measured peaks so the idle jitter
|
||||
# stays sub-pixel while a full contact still fits (see prefs store).
|
||||
DEFAULT_PLOT_FN_RAW_MAX = 1000000.0
|
||||
DEFAULT_PLOT_SHEAR_RAW_MAX = 500000.0
|
||||
|
||||
|
||||
class PlotSettingsDialog(QDialog):
|
||||
@@ -28,11 +32,13 @@ class PlotSettingsDialog(QDialog):
|
||||
window_sec: float = DEFAULT_PLOT_WINDOW_SEC,
|
||||
fn_max: float = DEFAULT_PLOT_FN_MAX,
|
||||
shear_max: float = DEFAULT_PLOT_SHEAR_MAX,
|
||||
fn_raw_max: float = DEFAULT_PLOT_FN_RAW_MAX,
|
||||
shear_raw_max: float = DEFAULT_PLOT_SHEAR_RAW_MAX,
|
||||
units: str = "newton",
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
self.setModal(True)
|
||||
self.resize(360, 200)
|
||||
self.resize(380, 260)
|
||||
|
||||
root = QVBoxLayout(self)
|
||||
form = QFormLayout()
|
||||
@@ -61,16 +67,40 @@ class PlotSettingsDialog(QDialog):
|
||||
self.shear_max_spin.setSuffix(" N")
|
||||
self.shear_max_spin.setValue(float(shear_max))
|
||||
|
||||
# Raw limits: 0 means "auto range on the data".
|
||||
self.fn_raw_max_label = QLabel()
|
||||
self.fn_raw_max_spin = QDoubleSpinBox()
|
||||
self.fn_raw_max_spin.setRange(0.0, 1e9)
|
||||
self.fn_raw_max_spin.setSingleStep(50000.0)
|
||||
self.fn_raw_max_spin.setDecimals(0)
|
||||
self.fn_raw_max_spin.setValue(float(fn_raw_max))
|
||||
|
||||
self.shear_raw_max_label = QLabel()
|
||||
self.shear_raw_max_spin = QDoubleSpinBox()
|
||||
self.shear_raw_max_spin.setRange(0.0, 1e9)
|
||||
self.shear_raw_max_spin.setSingleStep(50000.0)
|
||||
self.shear_raw_max_spin.setDecimals(0)
|
||||
self.shear_raw_max_spin.setValue(float(shear_raw_max))
|
||||
|
||||
form.addRow(self.window_label, self.window_spin)
|
||||
form.addRow(self.fn_max_label, self.fn_max_spin)
|
||||
form.addRow(self.shear_max_label, self.shear_max_spin)
|
||||
form.addRow(self.fn_raw_max_label, self.fn_raw_max_spin)
|
||||
form.addRow(self.shear_raw_max_label, self.shear_raw_max_spin)
|
||||
root.addLayout(form)
|
||||
|
||||
newton = str(units or "newton").lower().startswith("n")
|
||||
self.fn_max_spin.setEnabled(newton)
|
||||
self.shear_max_spin.setEnabled(newton)
|
||||
self.fn_max_label.setEnabled(newton)
|
||||
self.shear_max_label.setEnabled(newton)
|
||||
raw_widgets = (
|
||||
self.fn_raw_max_spin,
|
||||
self.shear_raw_max_spin,
|
||||
self.fn_raw_max_label,
|
||||
self.shear_raw_max_label,
|
||||
)
|
||||
for widget in (self.fn_max_spin, self.shear_max_spin, self.fn_max_label,
|
||||
self.shear_max_label):
|
||||
widget.setEnabled(newton)
|
||||
for widget in raw_widgets:
|
||||
widget.setEnabled(not newton)
|
||||
|
||||
buttons_row = QHBoxLayout()
|
||||
self.btn_reset = QPushButton()
|
||||
@@ -85,17 +115,21 @@ class PlotSettingsDialog(QDialog):
|
||||
|
||||
self.retranslate()
|
||||
|
||||
def values(self) -> tuple[float, float, float]:
|
||||
def values(self) -> tuple[float, float, float, float, float]:
|
||||
return (
|
||||
float(self.window_spin.value()),
|
||||
float(self.fn_max_spin.value()),
|
||||
float(self.shear_max_spin.value()),
|
||||
float(self.fn_raw_max_spin.value()),
|
||||
float(self.shear_raw_max_spin.value()),
|
||||
)
|
||||
|
||||
def _reset_defaults(self) -> None:
|
||||
self.window_spin.setValue(DEFAULT_PLOT_WINDOW_SEC)
|
||||
self.fn_max_spin.setValue(DEFAULT_PLOT_FN_MAX)
|
||||
self.shear_max_spin.setValue(DEFAULT_PLOT_SHEAR_MAX)
|
||||
self.fn_raw_max_spin.setValue(DEFAULT_PLOT_FN_RAW_MAX)
|
||||
self.shear_raw_max_spin.setValue(DEFAULT_PLOT_SHEAR_RAW_MAX)
|
||||
|
||||
def retranslate(self) -> None:
|
||||
self.setWindowTitle(t("dialog.plot_settings_title"))
|
||||
@@ -105,6 +139,10 @@ class PlotSettingsDialog(QDialog):
|
||||
self.fn_max_spin.setToolTip(t("dialog.plot_fn_max_tip"))
|
||||
self.shear_max_label.setText(t("dialog.plot_shear_max"))
|
||||
self.shear_max_spin.setToolTip(t("dialog.plot_shear_max_tip"))
|
||||
self.fn_raw_max_label.setText(t("dialog.plot_fn_raw_max"))
|
||||
self.fn_raw_max_spin.setToolTip(t("dialog.plot_fn_raw_max_tip"))
|
||||
self.shear_raw_max_label.setText(t("dialog.plot_shear_raw_max"))
|
||||
self.shear_raw_max_spin.setToolTip(t("dialog.plot_shear_raw_max_tip"))
|
||||
self.btn_reset.setText(t("dialog.reset_defaults"))
|
||||
ok_btn = self.button_box.button(QDialogButtonBox.Ok)
|
||||
cancel_btn = self.button_box.button(QDialogButtonBox.Cancel)
|
||||
|
||||
@@ -59,6 +59,7 @@ class ToolbarWidget(QWidget):
|
||||
super().__init__(parent)
|
||||
self._run_tooltip_reason = ""
|
||||
self._run_available = True
|
||||
self._run_phase = "idle"
|
||||
outer = QVBoxLayout(self)
|
||||
outer.setContentsMargins(0, 0, 0, 0)
|
||||
outer.setSpacing(0)
|
||||
@@ -244,6 +245,11 @@ class ToolbarWidget(QWidget):
|
||||
self._run_tooltip_reason = str(reason or "")
|
||||
self._apply_run_tooltip()
|
||||
|
||||
def set_run_phase(self, phase: str) -> None:
|
||||
"""Track the session phase so Stop can advertise itself while connecting."""
|
||||
self._run_phase = str(phase or "idle")
|
||||
self.refresh_labels()
|
||||
|
||||
def active_source(self) -> str | int | None:
|
||||
if self.source_mode() == "video":
|
||||
path = self.video_path()
|
||||
@@ -307,7 +313,12 @@ class ToolbarWidget(QWidget):
|
||||
|
||||
def _refresh_run_label(self) -> None:
|
||||
if self.btn_run.isChecked():
|
||||
self.btn_run.setText(t("toolbar.stop_label"))
|
||||
# While connecting, cancel is the action the user actually needs —
|
||||
# a device open can hang, and this is the way out of it.
|
||||
if self._run_phase == "connecting":
|
||||
self.btn_run.setText(t("toolbar.cancel_label"))
|
||||
else:
|
||||
self.btn_run.setText(t("toolbar.stop_label"))
|
||||
else:
|
||||
self.btn_run.setText(t("toolbar.run_label"))
|
||||
self.btn_run.setObjectName(
|
||||
@@ -319,7 +330,10 @@ class ToolbarWidget(QWidget):
|
||||
|
||||
def _apply_run_tooltip(self) -> None:
|
||||
if self.btn_run.isChecked():
|
||||
self.btn_run.setToolTip(t("toolbar.stop"))
|
||||
if self._run_phase == "connecting":
|
||||
self.btn_run.setToolTip(t("toolbar.cancel_tip"))
|
||||
else:
|
||||
self.btn_run.setToolTip(t("toolbar.stop"))
|
||||
elif not self._run_available and self._run_tooltip_reason:
|
||||
self.btn_run.setToolTip(self._run_tooltip_reason)
|
||||
elif not self._run_available:
|
||||
|
||||
Reference in New Issue
Block a user