better-rtplot 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- better_rtplot-0.2.1/PKG-INFO +446 -0
- better_rtplot-0.2.1/README.md +420 -0
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/pyproject.toml +1 -1
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/interactive_test.py +6 -1
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/server_browser.py +18 -1
- better_rtplot-0.2.0/PKG-INFO +0 -278
- better_rtplot-0.2.0/README.md +0 -252
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/LICENSE +0 -0
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/client.py +0 -0
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/example_code.py +0 -0
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/plot_log.py +0 -0
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/saved_plots/.gitignore +0 -0
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/server.py +0 -0
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/static/uPlot.iife.min.js +0 -0
- {better_rtplot-0.2.0 → better_rtplot-0.2.1}/rtplot/static/uPlot.min.css +0 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: better-rtplot
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary:
|
|
5
|
+
License: GPL V3.0
|
|
6
|
+
Author: jmontp
|
|
7
|
+
Author-email: jmontp@umich.edu
|
|
8
|
+
Requires-Python: >=3.9,<3.13
|
|
9
|
+
Classifier: License :: Other/Proprietary License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Provides-Extra: browser
|
|
16
|
+
Provides-Extra: server
|
|
17
|
+
Requires-Dist: aiohttp (>=3.9.0) ; extra == "browser"
|
|
18
|
+
Requires-Dist: numpy (>=1.23.5)
|
|
19
|
+
Requires-Dist: pandas (>=1.5.3) ; extra == "server" or extra == "browser"
|
|
20
|
+
Requires-Dist: pyarrow (>=11.0.0) ; extra == "server" or extra == "browser"
|
|
21
|
+
Requires-Dist: pyqtgraph (>=0.13.0) ; extra == "server"
|
|
22
|
+
Requires-Dist: pyside6 (>6.4.0) ; extra == "server"
|
|
23
|
+
Requires-Dist: pyzmq (>=25.0.0)
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
# rtplot — real-time plotting over ZMQ
|
|
29
|
+
|
|
30
|
+
**rtplot** lets a Python script push live data to a plot window — locally, or
|
|
31
|
+
across the network — with a few lines of code on the sender side. The plot
|
|
32
|
+
window can be a traditional Qt application or a modern browser UI, and it
|
|
33
|
+
also supports interactive controls (buttons, sliders, dials, text and
|
|
34
|
+
numeric displays) that feed values back into the sending script in real time.
|
|
35
|
+
|
|
36
|
+
Typical use: a robot or data-acquisition script runs on a Raspberry Pi or
|
|
37
|
+
microcontroller host, and you watch live signals and tweak gains from a
|
|
38
|
+
laptop on the same network.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Table of contents
|
|
43
|
+
|
|
44
|
+
- [Highlights](#highlights)
|
|
45
|
+
- [Install](#install)
|
|
46
|
+
- [60-second quickstart](#60-second-quickstart)
|
|
47
|
+
- [Choosing a server: browser vs. Qt](#choosing-a-server-browser-vs-qt)
|
|
48
|
+
- [Interactive controls](#interactive-controls)
|
|
49
|
+
- [Reading controls from Python](#reading-controls-from-python)
|
|
50
|
+
- [Pushing values into displays](#pushing-values-into-displays)
|
|
51
|
+
- [Element reference](#element-reference)
|
|
52
|
+
- [Plot configuration](#plot-configuration)
|
|
53
|
+
- [Sending data](#sending-data)
|
|
54
|
+
- [Saving data](#saving-data)
|
|
55
|
+
- [Networking modes](#networking-modes)
|
|
56
|
+
- [Performance tuning](#performance-tuning)
|
|
57
|
+
- [CLI reference](#cli-reference)
|
|
58
|
+
- [Examples](#examples)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Highlights
|
|
63
|
+
|
|
64
|
+
- **Fast.** 500+ fps on a single trace on a modern laptop. Binary WebSocket
|
|
65
|
+
deltas on the browser server; raw Qt rendering on the desktop server.
|
|
66
|
+
- **Two frontends.** A new browser-based server (aiohttp + uPlot) and the
|
|
67
|
+
original pyqtgraph desktop server. Both speak the same ZMQ protocol, so
|
|
68
|
+
client code is identical.
|
|
69
|
+
- **Remote-friendly.** Either the sender or the plot host can bind — pick
|
|
70
|
+
whichever fits your network. Works across LAN, WSL, and SSH tunnels.
|
|
71
|
+
- **Plot config lives with the data.** The sender declares the plot layout,
|
|
72
|
+
so a Pi running your experiment owns the look of its own dashboards.
|
|
73
|
+
- **Interactive controls** *(browser server only)*. Declare buttons,
|
|
74
|
+
sliders, dials, numeric/text displays in the same `initialize_plots`
|
|
75
|
+
call. Poll from your tight loop; no threads, no callbacks.
|
|
76
|
+
- **Save to Parquet** with a single button click or `client.save_plot()`
|
|
77
|
+
call.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Install
|
|
82
|
+
|
|
83
|
+
Minimum install — just the client (send data only):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install better-rtplot
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Add the browser server (recommended):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install "better-rtplot[browser]"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Add the Qt/pyqtgraph server instead:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install "better-rtplot[server]"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The `browser` extra pulls `aiohttp` + `pandas` + `pyarrow`; the `server`
|
|
102
|
+
extra pulls `pyqtgraph` + `PySide6` + `pandas` + `pyarrow`. If you only
|
|
103
|
+
`pip install better-rtplot` and try to launch a server, rtplot will print
|
|
104
|
+
a friendly message telling you which extra to add.
|
|
105
|
+
|
|
106
|
+
WSL users: the browser server works out of the box — open the URL it
|
|
107
|
+
prints in your Windows browser. The Qt server needs an X server such as
|
|
108
|
+
[VcXsrv](https://sourceforge.net/projects/vcxsrv/).
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 60-second quickstart
|
|
113
|
+
|
|
114
|
+
**Terminal 1 — start a plot window:**
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
python -m rtplot.server_browser # browser UI at http://localhost:8050
|
|
118
|
+
# or
|
|
119
|
+
python -m rtplot.server # desktop Qt window
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Terminal 2 — send data:**
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from rtplot import client
|
|
126
|
+
import numpy as np, time
|
|
127
|
+
|
|
128
|
+
client.local_plot() # send to the server on 127.0.0.1
|
|
129
|
+
client.initialize_plots(["sin", "cos"]) # one plot with two named traces
|
|
130
|
+
|
|
131
|
+
for i in range(10000):
|
|
132
|
+
t = i * 0.01
|
|
133
|
+
client.send_array([np.sin(t), np.cos(t)])
|
|
134
|
+
time.sleep(0.01)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
That's it. Open http://localhost:8050 if you used the browser server; the
|
|
138
|
+
Qt server will pop up its own window.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Choosing a server: browser vs. Qt
|
|
143
|
+
|
|
144
|
+
| | **Browser server** (`rtplot.server_browser`) | **Qt server** (`rtplot.server`) |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| Frontend | aiohttp + uPlot in any modern browser | pyqtgraph + PySide6 desktop window |
|
|
147
|
+
| Extra | `[browser]` | `[server]` |
|
|
148
|
+
| Works over SSH | Yes (just forward the HTTP port) | No (needs X forwarding) |
|
|
149
|
+
| Interactive controls | **Yes** — buttons, sliders, dials, displays | No |
|
|
150
|
+
| Typical frame rate | 60 Hz render, 1000 Hz data push cap | 500+ fps |
|
|
151
|
+
| Saves to Parquet | Yes | Yes |
|
|
152
|
+
|
|
153
|
+
If you're on WSL, running remotely, or you want interactive controls,
|
|
154
|
+
**use the browser server**. The Qt server is still available for local
|
|
155
|
+
desktop use and for legacy setups.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Interactive controls
|
|
160
|
+
|
|
161
|
+
*Browser server only.* Declare a control row inline in your plot layout:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from rtplot import client
|
|
165
|
+
import numpy as np, time
|
|
166
|
+
|
|
167
|
+
client.local_plot()
|
|
168
|
+
client.initialize_plots([
|
|
169
|
+
{"names": ["signal"], "yrange": [-6, 6]},
|
|
170
|
+
{"controls": [
|
|
171
|
+
{"type": "button", "id": "reset", "label": "Reset"},
|
|
172
|
+
{"type": "button", "id": "pause", "label": "Pause"},
|
|
173
|
+
{"type": "slider", "id": "gain", "label": "Gain",
|
|
174
|
+
"min": 0, "max": 5, "value": 1.0, "step": 0.1, "format": "{:.2f}"},
|
|
175
|
+
]},
|
|
176
|
+
{"controls": [
|
|
177
|
+
{"type": "dial", "id": "freq", "label": "Freq (Hz)",
|
|
178
|
+
"min": 0.1, "max": 5.0, "value": 1.0, "step": 0.05,
|
|
179
|
+
"sensitivity": 0.5, "format": "{:.2f}"},
|
|
180
|
+
{"type": "display", "id": "t", "label": "t (s)", "format": "{:.2f}"},
|
|
181
|
+
{"type": "text", "id": "msg", "label": "Status",
|
|
182
|
+
"value": "running"},
|
|
183
|
+
]},
|
|
184
|
+
])
|
|
185
|
+
|
|
186
|
+
running = True
|
|
187
|
+
t0 = time.time()
|
|
188
|
+
while True:
|
|
189
|
+
ctrl = client.poll_controls()
|
|
190
|
+
for btn in ctrl.buttons:
|
|
191
|
+
if btn == "reset": t0 = time.time()
|
|
192
|
+
if btn == "pause": running = not running
|
|
193
|
+
|
|
194
|
+
gain = ctrl.values.get("gain", 1.0)
|
|
195
|
+
freq = ctrl.values.get("freq", 1.0)
|
|
196
|
+
t = time.time() - t0
|
|
197
|
+
amp = gain * np.sin(2 * np.pi * freq * t) if running else 0.0
|
|
198
|
+
|
|
199
|
+
client.set_display("t", t)
|
|
200
|
+
client.set_display("msg", "paused" if not running else "running")
|
|
201
|
+
client.send_array(amp)
|
|
202
|
+
time.sleep(0.01)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Reading controls from Python
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
ctrl = client.poll_controls() # non-blocking, cheap to call every loop
|
|
209
|
+
gain = ctrl.values.get("gain", 1.0) # latest slider/dial value
|
|
210
|
+
for btn_id in ctrl.buttons: # list of buttons fired since last poll
|
|
211
|
+
handle(btn_id)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`poll_controls()` returns a `ControlState(values, buttons)` namedtuple:
|
|
215
|
+
|
|
216
|
+
- `values` — a `dict` of `{element_id: float}` for every slider and dial
|
|
217
|
+
the server has told the client about. Defaults declared in
|
|
218
|
+
`initialize_plots` are pre-seeded so the **first** call already sees
|
|
219
|
+
them.
|
|
220
|
+
- `buttons` — a `list` of button ids fired since the previous poll, in
|
|
221
|
+
order. The list is cleared on return, so each event is delivered
|
|
222
|
+
exactly once.
|
|
223
|
+
|
|
224
|
+
Call it from your tight loop before computing the next sample. No
|
|
225
|
+
threads, no callbacks, no missed events.
|
|
226
|
+
|
|
227
|
+
### Pushing values into displays
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
client.set_display("t", 12.34) # numeric display box
|
|
231
|
+
client.set_display("msg", "running") # text field
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`set_display()` accepts either a number (for `type: "display"` elements)
|
|
235
|
+
or a string (for `type: "text"` elements). Updates are coalesced on the
|
|
236
|
+
server and rebroadcast to every connected browser at ~30 Hz.
|
|
237
|
+
|
|
238
|
+
### Element reference
|
|
239
|
+
|
|
240
|
+
| Type | Purpose | Notable fields |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| `button` | Fires a discrete event when clicked | `id`, `label` |
|
|
243
|
+
| `slider` | Scalar input via horizontal range | `id`, `label`, `min`, `max`, `value`, `step`, `format` |
|
|
244
|
+
| `dial` | Scalar input via rotational drag | same as slider, plus `sensitivity` (full turns per range sweep; default `1.0`) |
|
|
245
|
+
| `display` | Read-only numeric readout | `id`, `label`, `format` |
|
|
246
|
+
| `text` | Read-only text field (prompts, status) | `id`, `label`, `value` |
|
|
247
|
+
|
|
248
|
+
Slider and dial widgets both render as **`[widget] [−] [number input] [+]`**,
|
|
249
|
+
so you can drag, type a value directly, or nudge by `step`. The dial
|
|
250
|
+
accepts "round and round" circular drag — each full rotation walks the
|
|
251
|
+
value through `(max − min) × sensitivity`, so `sensitivity: 0.25` gives
|
|
252
|
+
you four rotations per sweep for fine control.
|
|
253
|
+
|
|
254
|
+
The `format` field accepts Python-style `{:.Nf}` strings (e.g. `"{:.2f}"`).
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Plot configuration
|
|
259
|
+
|
|
260
|
+
Each entry in `initialize_plots` is one of:
|
|
261
|
+
|
|
262
|
+
- an **integer** — `client.initialize_plots(3)` → one plot with 3 anonymous
|
|
263
|
+
traces
|
|
264
|
+
- a **string** — `client.initialize_plots("torque")` → one plot with one
|
|
265
|
+
named trace
|
|
266
|
+
- a **list of strings** — one plot, one trace per name
|
|
267
|
+
- a **list of lists of strings** — one plot per sublist
|
|
268
|
+
- a **dict** — one plot, with full styling options (below)
|
|
269
|
+
- a **list of dicts** — multiple plots with full styling
|
|
270
|
+
|
|
271
|
+
A styled plot dict accepts any of:
|
|
272
|
+
|
|
273
|
+
| Key | Meaning |
|
|
274
|
+
|---|---|
|
|
275
|
+
| `names` | **Required.** List of trace names. |
|
|
276
|
+
| `colors` | List of per-trace colors. Single letter (`r g b c m y k w`) or any CSS color string. |
|
|
277
|
+
| `line_style` | `"-"` for dashed, `""` (or anything else) for solid, per trace. |
|
|
278
|
+
| `line_width` | Per-trace line width in pixels. |
|
|
279
|
+
| `title` | Plot title. |
|
|
280
|
+
| `xlabel` / `ylabel` | Axis labels. |
|
|
281
|
+
| `yrange` | `[ymin, ymax]` — pins the Y axis and significantly speeds up rendering. |
|
|
282
|
+
| `xrange` | Integer number of samples visible at once (default 200). |
|
|
283
|
+
|
|
284
|
+
Special row entries (not plots themselves):
|
|
285
|
+
|
|
286
|
+
- `{"controls": [...]}` — a row of interactive controls (browser server only)
|
|
287
|
+
- `{"non_plot_labels": ["name1", "name2"]}` — extra scalar names that ride
|
|
288
|
+
along with `send_array` and get saved into the output Parquet file, but
|
|
289
|
+
aren't rendered as traces
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Sending data
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
client.send_array(scalar) # float
|
|
297
|
+
client.send_array([a, b, c]) # 1-D list: one sample per trace
|
|
298
|
+
client.send_array(np.array([...])) # 1-D numpy array: one sample per trace
|
|
299
|
+
client.send_array(np.array([[...]]))# 2-D (num_traces, N): N samples at once
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Passing a 2-D array with `N > 1` lets you push a batch of samples per
|
|
303
|
+
`send_array` call, which is the fastest way to get many samples through
|
|
304
|
+
without dropping frames.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Saving data
|
|
309
|
+
|
|
310
|
+
The server saves every sample it has received since the latest
|
|
311
|
+
`initialize_plots` call to a Parquet file, including any
|
|
312
|
+
`non_plot_labels` data that rode along with your normal data.
|
|
313
|
+
|
|
314
|
+
Trigger a save from either side:
|
|
315
|
+
|
|
316
|
+
- **Browser UI:** click the **Save Plot** button.
|
|
317
|
+
- **Python:** `client.save_plot("my_run")`
|
|
318
|
+
|
|
319
|
+
Control where things get written:
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
python -m rtplot.server_browser -sd ./saved_plots -sn experiment1
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
- `-sd` / `--save-dir` — target directory
|
|
326
|
+
- `-sn` / `--save-name` — filename prefix (a timestamp is always appended)
|
|
327
|
+
|
|
328
|
+
### Save non-plot signals alongside the plotted ones
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
client.initialize_plots([
|
|
332
|
+
{"names": ["hip_angle", "knee_angle"]},
|
|
333
|
+
{"non_plot_labels": ["battery", "cpu_temp", "loop_latency"]},
|
|
334
|
+
])
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Send `battery`, `cpu_temp` and `loop_latency` as extra rows after the
|
|
338
|
+
plotted traces in each `send_array` call; they won't be drawn but they
|
|
339
|
+
will land in the Parquet file.
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Networking modes
|
|
344
|
+
|
|
345
|
+
rtplot uses ZMQ, so either the sender or the plot host can be the one
|
|
346
|
+
that *binds* a socket. Pick whichever works for your network and
|
|
347
|
+
firewalls.
|
|
348
|
+
|
|
349
|
+
**Mode A — plot host binds, sender connects** *(typical for lab laptops)*
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# on the plot host (e.g. your laptop)
|
|
353
|
+
python -m rtplot.server_browser
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
# on the sender (e.g. the Pi)
|
|
358
|
+
from rtplot import client
|
|
359
|
+
client.configure_ip("192.168.1.42") # the laptop's LAN IP
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**Mode B — sender binds, plot host connects** *(typical when the sender
|
|
363
|
+
has a static IP and the viewer roams around)*
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
# on the plot host
|
|
367
|
+
python -m rtplot.server_browser -p 192.168.1.50 # the sender's IP
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
```python
|
|
371
|
+
# on the sender
|
|
372
|
+
from rtplot import client
|
|
373
|
+
# no configure_ip call needed — the default behavior binds
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
If you pass `-p host:port` to the server, rtplot also derives the control
|
|
377
|
+
return-channel endpoint from that same host/port (it uses `port+1`). This
|
|
378
|
+
means sliders, buttons, and dials work transparently in both modes with
|
|
379
|
+
no extra config.
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Performance tuning
|
|
384
|
+
|
|
385
|
+
If you start running out of frames, try these, in roughly this order:
|
|
386
|
+
|
|
387
|
+
1. **Pin the Y range.** `{"yrange": [-2, 2]}` on each plot lets the
|
|
388
|
+
renderer skip autoscaling work and gives the single biggest win.
|
|
389
|
+
2. **Batch your samples.** Pass a 2-D numpy array to `send_array` so N
|
|
390
|
+
samples ship per call.
|
|
391
|
+
3. **Shrink the window.** Fewer pixels to redraw per frame.
|
|
392
|
+
4. **Reduce `line_width`.** Thicker lines cost more to rasterize.
|
|
393
|
+
5. **Use the `-s N` / `--skip N` server flag** to push every Nth sample
|
|
394
|
+
batch to the browser instead of every one. Add `-a` / `--adaptable`
|
|
395
|
+
to let the server tune `N` to your data rate automatically.
|
|
396
|
+
6. **Increase `xrange`.** Counterintuitively, a longer visible history
|
|
397
|
+
can be cheaper than a short one because the browser ring-buffers the
|
|
398
|
+
data and only replaces the tail on each push.
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## CLI reference
|
|
403
|
+
|
|
404
|
+
**Browser server** (`python -m rtplot.server_browser`):
|
|
405
|
+
|
|
406
|
+
| Flag | Default | Meaning |
|
|
407
|
+
|---|---|---|
|
|
408
|
+
| `-p HOST[:PORT]` | (bind) | Connect to a sender at this address instead of binding |
|
|
409
|
+
| `--host HOST` | `0.0.0.0` | HTTP bind interface |
|
|
410
|
+
| `--port N` | `8050` | HTTP port |
|
|
411
|
+
| `--no-browser` | off | Don't try to open a browser on startup |
|
|
412
|
+
| `--rate N` | `1000` | Max WebSocket push rate (Hz) |
|
|
413
|
+
| `-n N` / `--skip N` | `1` | Push every Nth sample batch |
|
|
414
|
+
| `-a` / `--adaptable` | off | Auto-tune skip rate to data rate |
|
|
415
|
+
| `-c` / `--column` | row | Lay plots out in columns instead of rows |
|
|
416
|
+
| `-d` / `--debug` | off | Extra debug logging |
|
|
417
|
+
| `-sd DIR` / `--save-dir DIR` | cwd | Where to write `.parquet` saves |
|
|
418
|
+
| `-sn NAME` / `--save-name NAME` | — | Prefix for saved filenames |
|
|
419
|
+
|
|
420
|
+
**Qt server** (`python -m rtplot.server`): same `-p`, `-n`, `-a`, `-c`,
|
|
421
|
+
`-d`, `-sd`, `-sn` flags as above, plus:
|
|
422
|
+
|
|
423
|
+
| Flag | Meaning |
|
|
424
|
+
|---|---|
|
|
425
|
+
| `-b` / `--bigscreen` | Pre-configure for the neurobionics lab big-screen display |
|
|
426
|
+
| `-t FILE` / `--plot_config FILE` | Load a plot configuration from a file on startup |
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Examples
|
|
431
|
+
|
|
432
|
+
- [`rtplot/example_code.py`](rtplot/example_code.py) — a walk through
|
|
433
|
+
every `initialize_plots` signature, plus a controls demo at the bottom.
|
|
434
|
+
- [`rtplot/interactive_test.py`](rtplot/interactive_test.py) — a guided
|
|
435
|
+
end-to-end test that walks you through clicking buttons, dragging
|
|
436
|
+
sliders, typing into the number input, using the ± nudge arrows, and
|
|
437
|
+
spinning the dial. Good for smoke-testing a fresh install.
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
python -m rtplot.server_browser &
|
|
441
|
+
python -m rtplot.interactive_test
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+

|
|
445
|
+

|
|
446
|
+
|