problem solved. thank you AI.
OK, digging into the vibe debugging, this seemed pretty good. I was going to summarize it, but the AI generated description seems pretty legit, so here is what it did:
Getting pyuscope's GUI running on the labsmore scope host (jaguar)
Host: videoteam@ps1.fpgas.online:4031 (hostname jaguar, Debian 13, Python 3.13)
pyuscope: ~/pyuscope, run as ./app/argus.py inside venv ~/venv/labsmore
Scope: lip-vm1 — a Hayear HY-800B v4l2 camera (on /dev/video4) + GRBL motion
Date: 2026-06-15
Carl's GUI wouldn't start. There turned out to be three separate problems,
one stacked behind the other. All three are now fixed and the GUI runs.
Problem 1 — Qt "xcb" plugin crash (the one Carl reported)
Symptom
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in
".../cv2/qt/plugins" even though it was found.
Cause (plain English)
pyuscope draws its window with Qt (PyQt5). The venv also had the deluxe
opencv-python, which secretly ships its own private copy of Qt. The moment
pyuscope does import cv2, opencv quietly repoints an environment variable
(QT_QPA_PLATFORM_PLUGIN_PATH) at its own Qt plugins. Now two different copies
of Qt fight inside one process: Qt finds an xcb plugin file (so it isn't
"missing") but it's the wrong, mismatched one — "found" but "could not load".
Proven: QT_QPA_PLATFORM_PLUGIN_PATH is empty before import cv2 and becomes
.../cv2/qt/plugins immediately after.
Fix — use the headless opencv build (same API, no bundled Qt). pyuscope uses
no opencv GUI functions, so nothing is lost:
~/venv/labsmore/bin/pip uninstall -y opencv-python
~/venv/labsmore/bin/pip install opencv-python-headless==4.13.0.92
Problem 2 — GStreamer "could not link" crash (GST_IS_CAPS null)
Symptom (after Problem 1 was fixed)
gst_caps_intersect_full: assertion 'GST_IS_CAPS (caps1)' failed
...
assert self.raw_element.link(element) AssertionError (gstwidget.py:530)
Cause
This host has a newer PyGObject (3.56) in which the old shortcut of passing
a string straight into the Gst.Caps() constructor was removed — it now raises
SystemError / silently ignores the string, producing an empty/invalid caps.
pyuscope built its video pipeline's capsfilter with Gst.Caps("video/x-raw,..."),
so every microscope config got a null caps and the pipeline wouldn't link.
Fix — use the supported API, Gst.Caps.from_string(...), at the 3 call
sites (uscope/gui/gstwidget.py ×2, uscope/imager/gst.py ×1). Applied
in-place; see git diff in ~/pyuscope. (Worth upstreaming to Labsmore.)
Problem 3 — the real root cause: the venv couldn't see GStreamer's Python support
Symptom (after Problem 2 was fixed)
gst_base_sink_init: assertion 'pad_template != NULL' failed
...
assert queue.link(dst) AssertionError (gstwidget.py:634)
Cause
pyuscope's frame-grabber (CaptureSink in uscope/gst_util.py) is a GStreamer
element written in Python. The machinery that turns its __gsttemplates__ /
__gstmetadata__ declarations into a real element (with a "sink" pad) lives in
the gst-python overrides — Debian's python3-gst-1.0 package, not in pip's
PyGObject.
The venv was created with python3 -m venv (so include-system-site-packages = false) and had a pip-installed PyGObject. That pip PyGObject has no
gst-python overrides, so the sink never got a pad and the pipeline couldn't link.
This same missing-overrides problem is also what broke Gst.Caps("string") in
Problem 2 — one root cause, two symptoms.
Proven: with the venv's own python but the system overrides on PYTHONPATH, the
sink registers correctly; without them, it doesn't.
Fix — let the venv use the apt-installed python3-gi + python3-gst-1.0
(matched to GStreamer 1.26.2):
# 1) allow the venv to fall back to system packages
# edit ~/venv/labsmore/pyvenv.cfg: include-system-site-packages = true
# 2) remove the override-less pip PyGObject so the system one is used
~/venv/labsmore/bin/pip uninstall -y PyGObject
(The venv's own pip packages — PyQt5, numpy, opencv-headless — still take
precedence; only gi/Gst now come from the system, which is how GStreamer
Python apps are meant to run on Debian.)
Result — the GUI runs
--microscope mock (test pattern): full GUI comes up with live video — see
argus_mock_screenshot.png.
--microscope lip-vm1 (the real scope): launches cleanly with zero console
errors, connects to the HY-800B camera (/dev/video4) and the GRBL motion
controller, and reaches pyuscope's normal "System is not homed" dialog.
A direct GStreamer grab from /dev/video4 produces valid frames (dark only
because the scope light was off / no sample) — scope_camera_frame.jpg.
How Carl launches it
ssh -p 4031 videoteam@ps1.fpgas.online
tmux a # tmux already has DISPLAY=:0
cd ~/pyuscope
. ~/venv/labsmore/bin/activate
./app/argus.py --microscope lip-vm1
Then click OK on the "System is not homed" dialog once the stage is clear
(this homes the CNC — it physically moves the stage), or Cancel to use the
GUI without homing.
Durability of the fixes
opencv: nothing depends on opencv-python; setup.py has install_requires=[]
and setup_ubuntu_20.04.sh only installs apt python3-opencv (--user), so
the headless swap won't be undone in the venv.
venv config: a backup of the original was saved as
~/venv/labsmore/pyvenv.cfg.bak-claude.
source patch: the Gst.Caps.from_string change lives in the ~/pyuscope git
checkout (visible in git diff) — review and upstream when convenient.