"""PyQtGraph real-time force chart window for examples.""" from __future__ import annotations import numpy as np def _require_pyqtgraph(): try: import pyqtgraph as pg except ImportError as exc: raise ImportError( "PyQtGraph is not installed. Install with: pip install pyqt5 pyqtgraph" ) from exc return pg class ForceChartWindow: """Rolling normal/shear force charts backed by PyQtGraph.""" def __init__( self, *, npoints: int = 100, sample_rate_hz: float = 30.0, title: str = "Orisys 实时力曲线", app_name: str = "Orisys Plot Example", ) -> None: pg = _require_pyqtgraph() pg.setConfigOption("background", "w") pg.setConfigOption("foreground", "k") self._pg = pg self.npoints = npoints self.sample_rate_hz = sample_rate_hz self.app = pg.mkQApp(app_name) self.win = pg.GraphicsLayoutWidget(show=True, title=title) self.win.setWindowTitle(title) self.data = np.zeros((npoints, 2, 3), dtype=np.float64) for channel in range(3): self.data[:, 0, channel] = np.linspace(-npoints, 0, npoints) / sample_rate_hz p1 = self.win.addPlot(title="法向力") p1.setLabel("bottom", "时间", units="s") self.win.nextRow() p2 = self.win.addPlot(title="切向力 X") p2.setLabel("bottom", "时间", units="s") self.win.nextRow() p3 = self.win.addPlot(title="切向力 Y") p3.setLabel("bottom", "时间", units="s") self._plots = (p1, p2, p3) self._curves = (p1.plot(pen="k"), p2.plot(pen="k"), p3.plot(pen="k")) def push(self, timestamp: float, fn: float, fx: float, fy: float) -> None: for channel in range(3): self.data[:-1, 0, channel] = self.data[1:, 0, channel] self.data[:-1, 1, channel] = self.data[1:, 1, channel] self.data[-1, 0, :] = timestamp self.data[-1, 1, 0] = fn self.data[-1, 1, 1] = fx self.data[-1, 1, 2] = fy def refresh(self, display_data: np.ndarray | None = None) -> None: plot_data = self.data if display_data is None else display_data for channel, (plot, curve) in enumerate(zip(self._plots, self._curves)): curve.setData(plot_data[:, :, channel]) plot.setYRange(float(plot_data[:, 1, channel].min()), float(plot_data[:, 1, channel].max()))