better-rtplot 0.2.1__tar.gz → 0.2.5__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 → better_rtplot-0.2.5}/PKG-INFO +162 -70
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/README.md +161 -69
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/pyproject.toml +1 -1
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/interactive_test.py +3 -2
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/server_browser.py +481 -95
- better_rtplot-0.2.5/rtplot/server_browser_gui.py +1157 -0
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/LICENSE +0 -0
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/client.py +0 -0
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/example_code.py +0 -0
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/plot_log.py +0 -0
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/saved_plots/.gitignore +0 -0
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/server.py +0 -0
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/static/uPlot.iife.min.js +0 -0
- {better_rtplot-0.2.1 → better_rtplot-0.2.5}/rtplot/static/uPlot.min.css +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: better-rtplot
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary:
|
|
5
5
|
License: GPL V3.0
|
|
6
6
|
Author: jmontp
|
|
@@ -29,9 +29,9 @@ Description-Content-Type: text/markdown
|
|
|
29
29
|
|
|
30
30
|
**rtplot** lets a Python script push live data to a plot window — locally, or
|
|
31
31
|
across the network — with a few lines of code on the sender side. The plot
|
|
32
|
-
window
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
window runs in any modern browser and supports interactive controls
|
|
33
|
+
(buttons, sliders, dials, text and numeric displays) that feed values
|
|
34
|
+
back into the sending script in real time.
|
|
35
35
|
|
|
36
36
|
Typical use: a robot or data-acquisition script runs on a Raspberry Pi or
|
|
37
37
|
microcontroller host, and you watch live signals and tweak gains from a
|
|
@@ -44,7 +44,6 @@ laptop on the same network.
|
|
|
44
44
|
- [Highlights](#highlights)
|
|
45
45
|
- [Install](#install)
|
|
46
46
|
- [60-second quickstart](#60-second-quickstart)
|
|
47
|
-
- [Choosing a server: browser vs. Qt](#choosing-a-server-browser-vs-qt)
|
|
48
47
|
- [Interactive controls](#interactive-controls)
|
|
49
48
|
- [Reading controls from Python](#reading-controls-from-python)
|
|
50
49
|
- [Pushing values into displays](#pushing-values-into-displays)
|
|
@@ -53,6 +52,7 @@ laptop on the same network.
|
|
|
53
52
|
- [Sending data](#sending-data)
|
|
54
53
|
- [Saving data](#saving-data)
|
|
55
54
|
- [Networking modes](#networking-modes)
|
|
55
|
+
- [Viewing the plot from another device](#viewing-the-plot-from-another-device)
|
|
56
56
|
- [Performance tuning](#performance-tuning)
|
|
57
57
|
- [CLI reference](#cli-reference)
|
|
58
58
|
- [Examples](#examples)
|
|
@@ -61,18 +61,23 @@ laptop on the same network.
|
|
|
61
61
|
|
|
62
62
|
## Highlights
|
|
63
63
|
|
|
64
|
-
- **Fast.**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- **
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
- **Fast.** Binary WebSocket deltas push data at up to 1 kHz. The
|
|
65
|
+
browser coalesces incoming samples into a single repaint per
|
|
66
|
+
`requestAnimationFrame`, so rendering runs at your monitor's refresh
|
|
67
|
+
rate (typically 60 Hz, 120 Hz on higher-refresh displays) regardless
|
|
68
|
+
of how fast samples arrive.
|
|
69
|
+
- **Browser-based.** The plot window is served by aiohttp and rendered
|
|
70
|
+
by uPlot in any modern browser. No desktop GUI toolkit to install,
|
|
71
|
+
works over SSH port forwarding out of the box.
|
|
72
|
+
- **Remote-friendly.** Either the sender or the plot host can bind —
|
|
73
|
+
pick whichever fits your network. Works across LAN, WSL, and SSH
|
|
74
|
+
tunnels.
|
|
75
|
+
- **Plot config lives with the data.** The sender declares the plot
|
|
76
|
+
layout, so a Pi running your experiment owns the look of its own
|
|
77
|
+
dashboards.
|
|
78
|
+
- **Interactive controls.** Declare buttons, sliders, dials,
|
|
79
|
+
numeric/text displays in the same `initialize_plots` call. Poll from
|
|
80
|
+
your tight loop; no threads, no callbacks.
|
|
76
81
|
- **Save to Parquet** with a single button click or `client.save_plot()`
|
|
77
82
|
call.
|
|
78
83
|
|
|
@@ -80,45 +85,41 @@ laptop on the same network.
|
|
|
80
85
|
|
|
81
86
|
## Install
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
pip install better-rtplot
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Add the browser server (recommended):
|
|
88
|
+
Install rtplot with the server bundle — this is the normal path and
|
|
89
|
+
gets you everything:
|
|
90
90
|
|
|
91
91
|
```bash
|
|
92
92
|
pip install "better-rtplot[browser]"
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
This pulls `aiohttp` (for serving the plot UI) plus `pandas` + `pyarrow`
|
|
96
|
+
(for saving runs to Parquet). If you only need the sender side — your
|
|
97
|
+
script pushes data to someone else's plot host and you don't run a
|
|
98
|
+
server locally — you can install the client-only minimum instead:
|
|
96
99
|
|
|
97
100
|
```bash
|
|
98
|
-
pip install
|
|
101
|
+
pip install better-rtplot
|
|
99
102
|
```
|
|
100
103
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
`pip install better-rtplot` and try to launch a server, rtplot will print
|
|
104
|
-
a friendly message telling you which extra to add.
|
|
104
|
+
In that case, if you later try to launch a server locally you'll get a
|
|
105
|
+
clear error telling you to add the `[browser]` extra.
|
|
105
106
|
|
|
106
|
-
WSL users:
|
|
107
|
-
prints in your Windows browser.
|
|
108
|
-
[VcXsrv](https://sourceforge.net/projects/vcxsrv/).
|
|
107
|
+
WSL users: nothing extra needed. The plot window is served by HTTP, so
|
|
108
|
+
just open the URL rtplot prints in your Windows browser.
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
112
112
|
## 60-second quickstart
|
|
113
113
|
|
|
114
|
-
**Terminal 1 — start
|
|
114
|
+
**Terminal 1 — start the plot server:**
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
|
-
python -m rtplot.server_browser
|
|
118
|
-
# or
|
|
119
|
-
python -m rtplot.server # desktop Qt window
|
|
117
|
+
python -m rtplot.server_browser
|
|
120
118
|
```
|
|
121
119
|
|
|
120
|
+
It prints a URL like `http://localhost:8050` — open that in your
|
|
121
|
+
browser. The page stays blank until a client sends a plot config.
|
|
122
|
+
|
|
122
123
|
**Terminal 2 — send data:**
|
|
123
124
|
|
|
124
125
|
```python
|
|
@@ -134,31 +135,14 @@ for i in range(10000):
|
|
|
134
135
|
time.sleep(0.01)
|
|
135
136
|
```
|
|
136
137
|
|
|
137
|
-
That's it.
|
|
138
|
-
|
|
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.
|
|
138
|
+
That's it. The browser tab you opened will start drawing the two
|
|
139
|
+
traces in real time.
|
|
156
140
|
|
|
157
141
|
---
|
|
158
142
|
|
|
159
143
|
## Interactive controls
|
|
160
144
|
|
|
161
|
-
|
|
145
|
+
Declare a control row inline in your plot layout:
|
|
162
146
|
|
|
163
147
|
```python
|
|
164
148
|
from rtplot import client
|
|
@@ -283,7 +267,8 @@ A styled plot dict accepts any of:
|
|
|
283
267
|
|
|
284
268
|
Special row entries (not plots themselves):
|
|
285
269
|
|
|
286
|
-
- `{"controls": [...]}` — a row of interactive controls (
|
|
270
|
+
- `{"controls": [...]}` — a row of interactive controls (see
|
|
271
|
+
[Interactive controls](#interactive-controls))
|
|
287
272
|
- `{"non_plot_labels": ["name1", "name2"]}` — extra scalar names that ride
|
|
288
273
|
along with `send_array` and get saved into the output Parquet file, but
|
|
289
274
|
aren't rendered as traces
|
|
@@ -380,6 +365,124 @@ no extra config.
|
|
|
380
365
|
|
|
381
366
|
---
|
|
382
367
|
|
|
368
|
+
## Viewing the plot from another device
|
|
369
|
+
|
|
370
|
+
The section above is about the link between your *sender script* and the
|
|
371
|
+
*plot host* (the machine running `rtplot.server_browser`). This section
|
|
372
|
+
is about the other relationship: the link between the plot host and a
|
|
373
|
+
separate *viewer device* — a phone, tablet, or another laptop that just
|
|
374
|
+
wants to open the browser UI.
|
|
375
|
+
|
|
376
|
+
**You don't need SSH for this.** The plot host already runs a plain HTTP
|
|
377
|
+
server on port `8050`, bound to every interface, and the viewer device
|
|
378
|
+
is only a web browser. All you need to do is get traffic from the
|
|
379
|
+
viewer to port `8050` on the plot host.
|
|
380
|
+
|
|
381
|
+
### On the same LAN (phone, tablet, another laptop on the same Wi-Fi)
|
|
382
|
+
|
|
383
|
+
1. Find the plot host's LAN IP:
|
|
384
|
+
|
|
385
|
+
```powershell
|
|
386
|
+
ipconfig | findstr IPv4 # Windows
|
|
387
|
+
```
|
|
388
|
+
```bash
|
|
389
|
+
ip -4 addr | grep inet # Linux/WSL
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
2. Open `http://<lan_ip>:8050` in the browser on the viewer device.
|
|
393
|
+
|
|
394
|
+
3. If Windows, allow inbound connections on port `8050` through Windows
|
|
395
|
+
Defender Firewall. The very first time you run
|
|
396
|
+
`python -m rtplot.server_browser`, Windows pops up an "Allow Python to
|
|
397
|
+
receive connections" dialog — tick **Private networks** and click
|
|
398
|
+
**Allow**. If you missed the dialog, add the rule manually from an
|
|
399
|
+
elevated PowerShell:
|
|
400
|
+
|
|
401
|
+
```powershell
|
|
402
|
+
# PowerShell as Administrator
|
|
403
|
+
New-NetFirewallRule -DisplayName "rtplot" `
|
|
404
|
+
-Direction Inbound -LocalPort 8050 -Protocol TCP `
|
|
405
|
+
-Action Allow -Profile Private
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Only allow on **Private** (home / trusted Wi-Fi), not **Public**,
|
|
409
|
+
unless you know what you're doing. To remove the rule later:
|
|
410
|
+
|
|
411
|
+
```powershell
|
|
412
|
+
Remove-NetFirewallRule -DisplayName "rtplot"
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
No router configuration, no SSH tunneling, no external accounts. Just a
|
|
416
|
+
firewall exception.
|
|
417
|
+
|
|
418
|
+
### WSL2 wrinkle
|
|
419
|
+
|
|
420
|
+
If you run the server inside WSL2 instead of native Windows, WSL2's
|
|
421
|
+
`localhost` auto-forward lets **you** reach it from your Windows browser,
|
|
422
|
+
but does **not** forward traffic from the LAN. To expose a WSL2-hosted
|
|
423
|
+
server to other devices you need one extra hop — a Windows-side port
|
|
424
|
+
proxy that forwards incoming LAN traffic into WSL2:
|
|
425
|
+
|
|
426
|
+
```powershell
|
|
427
|
+
# PowerShell as Administrator
|
|
428
|
+
$wslIp = (wsl hostname -I).Trim().Split()[0]
|
|
429
|
+
netsh interface portproxy add v4tov4 `
|
|
430
|
+
listenport=8050 listenaddress=0.0.0.0 `
|
|
431
|
+
connectport=8050 connectaddress=$wslIp
|
|
432
|
+
New-NetFirewallRule -DisplayName "rtplot wsl" `
|
|
433
|
+
-Direction Inbound -LocalPort 8050 -Protocol TCP `
|
|
434
|
+
-Action Allow -Profile Private
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
WSL2's IP changes on every reboot, so rerun the `netsh` line after a
|
|
438
|
+
restart (or just run `rtplot.server_browser` from native Windows and
|
|
439
|
+
skip this whole step).
|
|
440
|
+
|
|
441
|
+
To undo:
|
|
442
|
+
```powershell
|
|
443
|
+
netsh interface portproxy delete v4tov4 listenport=8050 listenaddress=0.0.0.0
|
|
444
|
+
Remove-NetFirewallRule -DisplayName "rtplot wsl"
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Across the internet (viewer on cellular, another network, etc.)
|
|
448
|
+
|
|
449
|
+
Two easy options, neither of which requires touching your router:
|
|
450
|
+
|
|
451
|
+
**Cloudflare Tunnel** (free, one-shot URL):
|
|
452
|
+
|
|
453
|
+
```powershell
|
|
454
|
+
winget install --id Cloudflare.cloudflared
|
|
455
|
+
cloudflared tunnel --url http://localhost:8050
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
Prints an `https://<random>.trycloudflare.com` URL valid for the
|
|
459
|
+
lifetime of the command — paste it into the viewer's browser. Kill the
|
|
460
|
+
command when you're done.
|
|
461
|
+
|
|
462
|
+
**Tailscale** (private mesh VPN, best for recurring setups):
|
|
463
|
+
|
|
464
|
+
Install [Tailscale](https://tailscale.com) on both the plot host and
|
|
465
|
+
every viewer device. Each device gets a stable `100.x.y.z` IP that
|
|
466
|
+
works from any network. Open `http://100.x.y.z:8050` on the viewer.
|
|
467
|
+
|
|
468
|
+
Both tunnel paths forward the HTTP + WebSocket traffic that the browser
|
|
469
|
+
needs; neither involves ZMQ, since the viewer is browser-only. Your
|
|
470
|
+
sender script keeps talking to the plot host locally as usual.
|
|
471
|
+
|
|
472
|
+
### Ports at a glance
|
|
473
|
+
|
|
474
|
+
| Port | What it's for | Who actually needs it open |
|
|
475
|
+
|---|---|---|
|
|
476
|
+
| `8050` (TCP) | HTTP + WebSocket to the browser UI | the plot host, inbound from viewers |
|
|
477
|
+
| `5555` (TCP) | ZMQ data (sender → server) | only the sender and the plot host |
|
|
478
|
+
| `5556` (TCP) | ZMQ control return channel (server → sender) | only the sender and the plot host |
|
|
479
|
+
|
|
480
|
+
For the "other device is a viewer" case, you only need to expose `8050`.
|
|
481
|
+
`5555` / `5556` are between the sender script and the plot host — they
|
|
482
|
+
do not need to be reachable from the viewer device at all.
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
383
486
|
## Performance tuning
|
|
384
487
|
|
|
385
488
|
If you start running out of frames, try these, in roughly this order:
|
|
@@ -401,7 +504,7 @@ If you start running out of frames, try these, in roughly this order:
|
|
|
401
504
|
|
|
402
505
|
## CLI reference
|
|
403
506
|
|
|
404
|
-
|
|
507
|
+
`python -m rtplot.server_browser` accepts:
|
|
405
508
|
|
|
406
509
|
| Flag | Default | Meaning |
|
|
407
510
|
|---|---|---|
|
|
@@ -417,14 +520,6 @@ If you start running out of frames, try these, in roughly this order:
|
|
|
417
520
|
| `-sd DIR` / `--save-dir DIR` | cwd | Where to write `.parquet` saves |
|
|
418
521
|
| `-sn NAME` / `--save-name NAME` | — | Prefix for saved filenames |
|
|
419
522
|
|
|
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
523
|
---
|
|
429
524
|
|
|
430
525
|
## Examples
|
|
@@ -441,6 +536,3 @@ If you start running out of frames, try these, in roughly this order:
|
|
|
441
536
|
python -m rtplot.interactive_test
|
|
442
537
|
```
|
|
443
538
|
|
|
444
|
-

|
|
445
|
-

|
|
446
|
-
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
**rtplot** lets a Python script push live data to a plot window — locally, or
|
|
6
6
|
across the network — with a few lines of code on the sender side. The plot
|
|
7
|
-
window
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
window runs in any modern browser and supports interactive controls
|
|
8
|
+
(buttons, sliders, dials, text and numeric displays) that feed values
|
|
9
|
+
back into the sending script in real time.
|
|
10
10
|
|
|
11
11
|
Typical use: a robot or data-acquisition script runs on a Raspberry Pi or
|
|
12
12
|
microcontroller host, and you watch live signals and tweak gains from a
|
|
@@ -19,7 +19,6 @@ laptop on the same network.
|
|
|
19
19
|
- [Highlights](#highlights)
|
|
20
20
|
- [Install](#install)
|
|
21
21
|
- [60-second quickstart](#60-second-quickstart)
|
|
22
|
-
- [Choosing a server: browser vs. Qt](#choosing-a-server-browser-vs-qt)
|
|
23
22
|
- [Interactive controls](#interactive-controls)
|
|
24
23
|
- [Reading controls from Python](#reading-controls-from-python)
|
|
25
24
|
- [Pushing values into displays](#pushing-values-into-displays)
|
|
@@ -28,6 +27,7 @@ laptop on the same network.
|
|
|
28
27
|
- [Sending data](#sending-data)
|
|
29
28
|
- [Saving data](#saving-data)
|
|
30
29
|
- [Networking modes](#networking-modes)
|
|
30
|
+
- [Viewing the plot from another device](#viewing-the-plot-from-another-device)
|
|
31
31
|
- [Performance tuning](#performance-tuning)
|
|
32
32
|
- [CLI reference](#cli-reference)
|
|
33
33
|
- [Examples](#examples)
|
|
@@ -36,18 +36,23 @@ laptop on the same network.
|
|
|
36
36
|
|
|
37
37
|
## Highlights
|
|
38
38
|
|
|
39
|
-
- **Fast.**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
- **
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
- **Fast.** Binary WebSocket deltas push data at up to 1 kHz. The
|
|
40
|
+
browser coalesces incoming samples into a single repaint per
|
|
41
|
+
`requestAnimationFrame`, so rendering runs at your monitor's refresh
|
|
42
|
+
rate (typically 60 Hz, 120 Hz on higher-refresh displays) regardless
|
|
43
|
+
of how fast samples arrive.
|
|
44
|
+
- **Browser-based.** The plot window is served by aiohttp and rendered
|
|
45
|
+
by uPlot in any modern browser. No desktop GUI toolkit to install,
|
|
46
|
+
works over SSH port forwarding out of the box.
|
|
47
|
+
- **Remote-friendly.** Either the sender or the plot host can bind —
|
|
48
|
+
pick whichever fits your network. Works across LAN, WSL, and SSH
|
|
49
|
+
tunnels.
|
|
50
|
+
- **Plot config lives with the data.** The sender declares the plot
|
|
51
|
+
layout, so a Pi running your experiment owns the look of its own
|
|
52
|
+
dashboards.
|
|
53
|
+
- **Interactive controls.** Declare buttons, sliders, dials,
|
|
54
|
+
numeric/text displays in the same `initialize_plots` call. Poll from
|
|
55
|
+
your tight loop; no threads, no callbacks.
|
|
51
56
|
- **Save to Parquet** with a single button click or `client.save_plot()`
|
|
52
57
|
call.
|
|
53
58
|
|
|
@@ -55,45 +60,41 @@ laptop on the same network.
|
|
|
55
60
|
|
|
56
61
|
## Install
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
pip install better-rtplot
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Add the browser server (recommended):
|
|
63
|
+
Install rtplot with the server bundle — this is the normal path and
|
|
64
|
+
gets you everything:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
pip install "better-rtplot[browser]"
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
This pulls `aiohttp` (for serving the plot UI) plus `pandas` + `pyarrow`
|
|
71
|
+
(for saving runs to Parquet). If you only need the sender side — your
|
|
72
|
+
script pushes data to someone else's plot host and you don't run a
|
|
73
|
+
server locally — you can install the client-only minimum instead:
|
|
71
74
|
|
|
72
75
|
```bash
|
|
73
|
-
pip install
|
|
76
|
+
pip install better-rtplot
|
|
74
77
|
```
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
`pip install better-rtplot` and try to launch a server, rtplot will print
|
|
79
|
-
a friendly message telling you which extra to add.
|
|
79
|
+
In that case, if you later try to launch a server locally you'll get a
|
|
80
|
+
clear error telling you to add the `[browser]` extra.
|
|
80
81
|
|
|
81
|
-
WSL users:
|
|
82
|
-
prints in your Windows browser.
|
|
83
|
-
[VcXsrv](https://sourceforge.net/projects/vcxsrv/).
|
|
82
|
+
WSL users: nothing extra needed. The plot window is served by HTTP, so
|
|
83
|
+
just open the URL rtplot prints in your Windows browser.
|
|
84
84
|
|
|
85
85
|
---
|
|
86
86
|
|
|
87
87
|
## 60-second quickstart
|
|
88
88
|
|
|
89
|
-
**Terminal 1 — start
|
|
89
|
+
**Terminal 1 — start the plot server:**
|
|
90
90
|
|
|
91
91
|
```bash
|
|
92
|
-
python -m rtplot.server_browser
|
|
93
|
-
# or
|
|
94
|
-
python -m rtplot.server # desktop Qt window
|
|
92
|
+
python -m rtplot.server_browser
|
|
95
93
|
```
|
|
96
94
|
|
|
95
|
+
It prints a URL like `http://localhost:8050` — open that in your
|
|
96
|
+
browser. The page stays blank until a client sends a plot config.
|
|
97
|
+
|
|
97
98
|
**Terminal 2 — send data:**
|
|
98
99
|
|
|
99
100
|
```python
|
|
@@ -109,31 +110,14 @@ for i in range(10000):
|
|
|
109
110
|
time.sleep(0.01)
|
|
110
111
|
```
|
|
111
112
|
|
|
112
|
-
That's it.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## Choosing a server: browser vs. Qt
|
|
118
|
-
|
|
119
|
-
| | **Browser server** (`rtplot.server_browser`) | **Qt server** (`rtplot.server`) |
|
|
120
|
-
|---|---|---|
|
|
121
|
-
| Frontend | aiohttp + uPlot in any modern browser | pyqtgraph + PySide6 desktop window |
|
|
122
|
-
| Extra | `[browser]` | `[server]` |
|
|
123
|
-
| Works over SSH | Yes (just forward the HTTP port) | No (needs X forwarding) |
|
|
124
|
-
| Interactive controls | **Yes** — buttons, sliders, dials, displays | No |
|
|
125
|
-
| Typical frame rate | 60 Hz render, 1000 Hz data push cap | 500+ fps |
|
|
126
|
-
| Saves to Parquet | Yes | Yes |
|
|
127
|
-
|
|
128
|
-
If you're on WSL, running remotely, or you want interactive controls,
|
|
129
|
-
**use the browser server**. The Qt server is still available for local
|
|
130
|
-
desktop use and for legacy setups.
|
|
113
|
+
That's it. The browser tab you opened will start drawing the two
|
|
114
|
+
traces in real time.
|
|
131
115
|
|
|
132
116
|
---
|
|
133
117
|
|
|
134
118
|
## Interactive controls
|
|
135
119
|
|
|
136
|
-
|
|
120
|
+
Declare a control row inline in your plot layout:
|
|
137
121
|
|
|
138
122
|
```python
|
|
139
123
|
from rtplot import client
|
|
@@ -258,7 +242,8 @@ A styled plot dict accepts any of:
|
|
|
258
242
|
|
|
259
243
|
Special row entries (not plots themselves):
|
|
260
244
|
|
|
261
|
-
- `{"controls": [...]}` — a row of interactive controls (
|
|
245
|
+
- `{"controls": [...]}` — a row of interactive controls (see
|
|
246
|
+
[Interactive controls](#interactive-controls))
|
|
262
247
|
- `{"non_plot_labels": ["name1", "name2"]}` — extra scalar names that ride
|
|
263
248
|
along with `send_array` and get saved into the output Parquet file, but
|
|
264
249
|
aren't rendered as traces
|
|
@@ -355,6 +340,124 @@ no extra config.
|
|
|
355
340
|
|
|
356
341
|
---
|
|
357
342
|
|
|
343
|
+
## Viewing the plot from another device
|
|
344
|
+
|
|
345
|
+
The section above is about the link between your *sender script* and the
|
|
346
|
+
*plot host* (the machine running `rtplot.server_browser`). This section
|
|
347
|
+
is about the other relationship: the link between the plot host and a
|
|
348
|
+
separate *viewer device* — a phone, tablet, or another laptop that just
|
|
349
|
+
wants to open the browser UI.
|
|
350
|
+
|
|
351
|
+
**You don't need SSH for this.** The plot host already runs a plain HTTP
|
|
352
|
+
server on port `8050`, bound to every interface, and the viewer device
|
|
353
|
+
is only a web browser. All you need to do is get traffic from the
|
|
354
|
+
viewer to port `8050` on the plot host.
|
|
355
|
+
|
|
356
|
+
### On the same LAN (phone, tablet, another laptop on the same Wi-Fi)
|
|
357
|
+
|
|
358
|
+
1. Find the plot host's LAN IP:
|
|
359
|
+
|
|
360
|
+
```powershell
|
|
361
|
+
ipconfig | findstr IPv4 # Windows
|
|
362
|
+
```
|
|
363
|
+
```bash
|
|
364
|
+
ip -4 addr | grep inet # Linux/WSL
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
2. Open `http://<lan_ip>:8050` in the browser on the viewer device.
|
|
368
|
+
|
|
369
|
+
3. If Windows, allow inbound connections on port `8050` through Windows
|
|
370
|
+
Defender Firewall. The very first time you run
|
|
371
|
+
`python -m rtplot.server_browser`, Windows pops up an "Allow Python to
|
|
372
|
+
receive connections" dialog — tick **Private networks** and click
|
|
373
|
+
**Allow**. If you missed the dialog, add the rule manually from an
|
|
374
|
+
elevated PowerShell:
|
|
375
|
+
|
|
376
|
+
```powershell
|
|
377
|
+
# PowerShell as Administrator
|
|
378
|
+
New-NetFirewallRule -DisplayName "rtplot" `
|
|
379
|
+
-Direction Inbound -LocalPort 8050 -Protocol TCP `
|
|
380
|
+
-Action Allow -Profile Private
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Only allow on **Private** (home / trusted Wi-Fi), not **Public**,
|
|
384
|
+
unless you know what you're doing. To remove the rule later:
|
|
385
|
+
|
|
386
|
+
```powershell
|
|
387
|
+
Remove-NetFirewallRule -DisplayName "rtplot"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
No router configuration, no SSH tunneling, no external accounts. Just a
|
|
391
|
+
firewall exception.
|
|
392
|
+
|
|
393
|
+
### WSL2 wrinkle
|
|
394
|
+
|
|
395
|
+
If you run the server inside WSL2 instead of native Windows, WSL2's
|
|
396
|
+
`localhost` auto-forward lets **you** reach it from your Windows browser,
|
|
397
|
+
but does **not** forward traffic from the LAN. To expose a WSL2-hosted
|
|
398
|
+
server to other devices you need one extra hop — a Windows-side port
|
|
399
|
+
proxy that forwards incoming LAN traffic into WSL2:
|
|
400
|
+
|
|
401
|
+
```powershell
|
|
402
|
+
# PowerShell as Administrator
|
|
403
|
+
$wslIp = (wsl hostname -I).Trim().Split()[0]
|
|
404
|
+
netsh interface portproxy add v4tov4 `
|
|
405
|
+
listenport=8050 listenaddress=0.0.0.0 `
|
|
406
|
+
connectport=8050 connectaddress=$wslIp
|
|
407
|
+
New-NetFirewallRule -DisplayName "rtplot wsl" `
|
|
408
|
+
-Direction Inbound -LocalPort 8050 -Protocol TCP `
|
|
409
|
+
-Action Allow -Profile Private
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
WSL2's IP changes on every reboot, so rerun the `netsh` line after a
|
|
413
|
+
restart (or just run `rtplot.server_browser` from native Windows and
|
|
414
|
+
skip this whole step).
|
|
415
|
+
|
|
416
|
+
To undo:
|
|
417
|
+
```powershell
|
|
418
|
+
netsh interface portproxy delete v4tov4 listenport=8050 listenaddress=0.0.0.0
|
|
419
|
+
Remove-NetFirewallRule -DisplayName "rtplot wsl"
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Across the internet (viewer on cellular, another network, etc.)
|
|
423
|
+
|
|
424
|
+
Two easy options, neither of which requires touching your router:
|
|
425
|
+
|
|
426
|
+
**Cloudflare Tunnel** (free, one-shot URL):
|
|
427
|
+
|
|
428
|
+
```powershell
|
|
429
|
+
winget install --id Cloudflare.cloudflared
|
|
430
|
+
cloudflared tunnel --url http://localhost:8050
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Prints an `https://<random>.trycloudflare.com` URL valid for the
|
|
434
|
+
lifetime of the command — paste it into the viewer's browser. Kill the
|
|
435
|
+
command when you're done.
|
|
436
|
+
|
|
437
|
+
**Tailscale** (private mesh VPN, best for recurring setups):
|
|
438
|
+
|
|
439
|
+
Install [Tailscale](https://tailscale.com) on both the plot host and
|
|
440
|
+
every viewer device. Each device gets a stable `100.x.y.z` IP that
|
|
441
|
+
works from any network. Open `http://100.x.y.z:8050` on the viewer.
|
|
442
|
+
|
|
443
|
+
Both tunnel paths forward the HTTP + WebSocket traffic that the browser
|
|
444
|
+
needs; neither involves ZMQ, since the viewer is browser-only. Your
|
|
445
|
+
sender script keeps talking to the plot host locally as usual.
|
|
446
|
+
|
|
447
|
+
### Ports at a glance
|
|
448
|
+
|
|
449
|
+
| Port | What it's for | Who actually needs it open |
|
|
450
|
+
|---|---|---|
|
|
451
|
+
| `8050` (TCP) | HTTP + WebSocket to the browser UI | the plot host, inbound from viewers |
|
|
452
|
+
| `5555` (TCP) | ZMQ data (sender → server) | only the sender and the plot host |
|
|
453
|
+
| `5556` (TCP) | ZMQ control return channel (server → sender) | only the sender and the plot host |
|
|
454
|
+
|
|
455
|
+
For the "other device is a viewer" case, you only need to expose `8050`.
|
|
456
|
+
`5555` / `5556` are between the sender script and the plot host — they
|
|
457
|
+
do not need to be reachable from the viewer device at all.
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
358
461
|
## Performance tuning
|
|
359
462
|
|
|
360
463
|
If you start running out of frames, try these, in roughly this order:
|
|
@@ -376,7 +479,7 @@ If you start running out of frames, try these, in roughly this order:
|
|
|
376
479
|
|
|
377
480
|
## CLI reference
|
|
378
481
|
|
|
379
|
-
|
|
482
|
+
`python -m rtplot.server_browser` accepts:
|
|
380
483
|
|
|
381
484
|
| Flag | Default | Meaning |
|
|
382
485
|
|---|---|---|
|
|
@@ -392,14 +495,6 @@ If you start running out of frames, try these, in roughly this order:
|
|
|
392
495
|
| `-sd DIR` / `--save-dir DIR` | cwd | Where to write `.parquet` saves |
|
|
393
496
|
| `-sn NAME` / `--save-name NAME` | — | Prefix for saved filenames |
|
|
394
497
|
|
|
395
|
-
**Qt server** (`python -m rtplot.server`): same `-p`, `-n`, `-a`, `-c`,
|
|
396
|
-
`-d`, `-sd`, `-sn` flags as above, plus:
|
|
397
|
-
|
|
398
|
-
| Flag | Meaning |
|
|
399
|
-
|---|---|
|
|
400
|
-
| `-b` / `--bigscreen` | Pre-configure for the neurobionics lab big-screen display |
|
|
401
|
-
| `-t FILE` / `--plot_config FILE` | Load a plot configuration from a file on startup |
|
|
402
|
-
|
|
403
498
|
---
|
|
404
499
|
|
|
405
500
|
## Examples
|
|
@@ -415,6 +510,3 @@ If you start running out of frames, try these, in roughly this order:
|
|
|
415
510
|
python -m rtplot.server_browser &
|
|
416
511
|
python -m rtplot.interactive_test
|
|
417
512
|
```
|
|
418
|
-
|
|
419
|
-

|
|
420
|
-

|