The Python client captures the screen on a configurable interval, decodes every visible QR code in each frame, and reassembles the chunks into the original file. It is fully compatible with the web sender — same wire format, same manifest, same fountain protocol.
# GUI (recommended)
python qrl_gui.py
# or
python -m qrl_client --gui
# Headless CLI
qrl-client --output /path/to/folder/
The destination folder for decoded files. Defaults to ~/Downloads/qrl_received if not set.
The filename itself is determined by the manifest QR in the stream — the client reads the filename from the first QR code of each cycle and saves to <output_folder>/<filename>. You only need to choose the folder.
Once the manifest is received, the detected filename appears below the picker in the format:
📄 report.pdf · 4.2 KB
Three options for what to capture:
Esc to cancel.Tighter regions are faster (fewer pixels to process per frame) and reduce false positives from unrelated QR codes on screen.
Screen dropdown — lists all physical monitors by name (e.g., DELL U2723QE · 3840×2160). Selects which monitor to capture when no custom region is set.
How often a frame is captured and passed to the QR decoder.
| Preset | FPS | Interval |
|---|---|---|
| Slow | 5 | 200 ms |
| Balanced | 10 | 100 ms |
| Fast | 20 | 50 ms |
| Max | 30 | 33 ms |
Match this to the server’s display speed or faster. If the client interval is longer than the server’s frame interval, some unique QR frames will be missed and recovered on the next display cycle.
Start capture begins the background capture thread. The live preview updates at 5 fps (independently of the capture rate) showing the raw captured frame.
Stop signals the capture thread to exit. Partial progress is written to a .progress.json sidecar file so a session can be resumed later.
Save log… dumps the activity log to a text file.
Updated after every batch of frames:
| Stat | Description |
|---|---|
| Frames | Total frames captured and processed |
| QR success | Percentage of frames that contained at least one readable QR |
| Chunks | decoded / total |
| ETA | Estimated remaining time based on current rate |
The progress bar fills as chunks arrive. It resets if the stream has not yet sent a manifest (total unknown).
Shows the most recently captured frame, scaled to fit the preview panel at 5 fps. Use it to confirm the capture region is correct and the QR codes are visible and in focus. The info line shows the preview dimensions.
The preview runs on the main thread via root.after at a fixed 200 ms interval and does not compete with the capture/decode loop for CPU.
mss (native Win32/X11/Cocoa screenshots, no Python copy).pyzbar.decode() on each frame.(stream_id, sequence).total_chunks chunks for a stream are present the stream is complete.compressed = true, the reassembled bytes are decompressed.is_directory = true, the bytes are unpacked into <output_folder>/<filename>/.<output_folder>/<filename>.The client handles both sequential (stream_id = 0) and fountain (stream_id = 2) packets transparently, routing by the fountain field in the manifest. See ARCHITECTURE.md for the wire format and fountain decoding algorithm.