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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better-rtplot
3
- Version: 0.2.1
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 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.
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.** 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.
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
- Minimum installjust the client (send data only):
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
- Add the Qt/pyqtgraph server instead:
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 "better-rtplot[server]"
101
+ pip install better-rtplot
99
102
  ```
100
103
 
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.
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: 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/).
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 a plot window:**
114
+ **Terminal 1 — start the plot server:**
115
115
 
116
116
  ```bash
117
- python -m rtplot.server_browser # browser UI at http://localhost:8050
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. 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.
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
- *Browser server only.* Declare a control row inline in your plot layout:
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 (browser server only)
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
- **Browser server** (`python -m rtplot.server_browser`):
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
- ![Qt server example 1](https://github.com/jmontp/rtplot/blob/master/.images/rtplot_example1.png)
445
- ![Qt server example 2](https://github.com/jmontp/rtplot/blob/master/.images/rtplot_example2.png)
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 can be a traditional Qt application or a modern browser UI, and it
8
- also supports interactive controls (buttons, sliders, dials, text and
9
- numeric displays) that feed values back into the sending script in real time.
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.** 500+ fps on a single trace on a modern laptop. Binary WebSocket
40
- deltas on the browser server; raw Qt rendering on the desktop server.
41
- - **Two frontends.** A new browser-based server (aiohttp + uPlot) and the
42
- original pyqtgraph desktop server. Both speak the same ZMQ protocol, so
43
- client code is identical.
44
- - **Remote-friendly.** Either the sender or the plot host can bind — pick
45
- whichever fits your network. Works across LAN, WSL, and SSH tunnels.
46
- - **Plot config lives with the data.** The sender declares the plot layout,
47
- so a Pi running your experiment owns the look of its own dashboards.
48
- - **Interactive controls** *(browser server only)*. Declare buttons,
49
- sliders, dials, numeric/text displays in the same `initialize_plots`
50
- call. Poll from your tight loop; no threads, no callbacks.
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
- Minimum installjust the client (send data only):
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
- Add the Qt/pyqtgraph server instead:
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 "better-rtplot[server]"
76
+ pip install better-rtplot
74
77
  ```
75
78
 
76
- The `browser` extra pulls `aiohttp` + `pandas` + `pyarrow`; the `server`
77
- extra pulls `pyqtgraph` + `PySide6` + `pandas` + `pyarrow`. If you only
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: the browser server works out of the box open the URL it
82
- prints in your Windows browser. The Qt server needs an X server such as
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 a plot window:**
89
+ **Terminal 1 — start the plot server:**
90
90
 
91
91
  ```bash
92
- python -m rtplot.server_browser # browser UI at http://localhost:8050
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. Open http://localhost:8050 if you used the browser server; the
113
- Qt server will pop up its own window.
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
- *Browser server only.* Declare a control row inline in your plot layout:
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 (browser server only)
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
- **Browser server** (`python -m rtplot.server_browser`):
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
- ![Qt server example 1](https://github.com/jmontp/rtplot/blob/master/.images/rtplot_example1.png)
420
- ![Qt server example 2](https://github.com/jmontp/rtplot/blob/master/.images/rtplot_example2.png)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "better-rtplot"
3
- version = "0.2.1"
3
+ version = "0.2.5"
4
4
  description = ""
5
5
  authors = ["jmontp <jmontp@umich.edu>"]
6
6
  license = "GPL V3.0"