<feed xmlns='http://www.w3.org/2005/Atom'>
<title>labUI.git/plugins/motion_capture, branch main</title>
<subtitle>Data acquisition program written in Python for use with serial communication devices.
</subtitle>
<id>https://git.kolset.xyz/labUI.git/atom?h=main</id>
<link rel='self' href='https://git.kolset.xyz/labUI.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/'/>
<updated>2026-09-19T23:38:07Z</updated>
<entry>
<title>Redesign Signal/Channel naming and fix pin-remap breaking references</title>
<updated>2026-09-19T23:38:07Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-09-19T23:38:07Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=f3327d56d48d63b21ffc03a55fa5e7db753d1707'/>
<id>urn:sha1:f3327d56d48d63b21ffc03a55fa5e7db753d1707</id>
<content type='text'>
Arduino analog Signal ids used to be the pin name itself, so fixing a
wrong pin silently changed the id and orphaned any plot trace, control,
or filter pipeline pointing at it. Analog Signal ids are now positional
(ai0, ai1, ...) and decoupled from the underlying pin, matching how
digital I/O already worked.

More broadly, formalizes a Device -&gt; Signal -&gt; Channel -&gt; Plot naming
model (SignalConfig for physical device channels, Channel for derived/
computed ones) and adds core/reference_registry.py to propagate any
Signal or Channel id rename through plot traces, controls, and filter
pipelines instead of leaving them dangling. Signal ids are now editable
in the Signals tab with the same propagation.

.labui profile schema bumped to v2.0 with a transparent migration for
old files, including remapping old pin-name Signal ids to the new
positional ones. Removed six confirmed-dead files (two legacy device
classes, four superseded top-level ui/ duplicates).
</content>
</entry>
<entry>
<title>Fix motion capture UI freezes: throttling, async camera open/scan, stale poll</title>
<updated>2026-08-17T16:02:16Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-08-17T16:02:16Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=abab8f7938dada9a2abff31e719186b99934601a'/>
<id>urn:sha1:abab8f7938dada9a2abff31e719186b99934601a</id>
<content type='text'>
Several compounding freeze sources in plugins/motion_capture/:
- tracker.py: _run_real had no frame-rate cap independent of the camera's
  configured fps (up to 120fps/2560x1440 was possible), and no bound on
  consecutive failed reads — a camera that "opens" (cv2 isOpened() can lie,
  seen with Windows MSMF/obsensor) but never actually delivers frames would
  get hammered forever. Added a 20Hz processing cap, a real test-read before
  committing to a camera, and a give-up-after-30-failures fallback to
  simulation.
- tracker.py/device.py: cv2.VideoCapture() opening a real camera is a slow,
  blocking call — CameraTracker.start() used to do this synchronously on
  the caller's thread, which froze the whole app when ProfileManager.apply()
  called it during profile load. Now runs on the background thread; status
  reporting (CameraDevice) moved to an on_result callback since the caller
  can no longer learn the outcome synchronously.
- window.py: the Camera View preview ran a 30Hz GUI-thread timer doing a
  full-frame copy + QImage/QPixmap rebuild every tick unconditionally (no
  visibility check) — dropped to 10Hz and skips entirely when hidden.
  Camera enumeration (list_cameras(), up to 8 sequential cv2.VideoCapture
  probes) also ran inline in the window constructor, blocking it open the
  same way camera_panel.py's CameraScanThread already avoided for the Add
  Device dialog — moved to the same async pattern here.

config_window.py: separately, ConfigWindow._dev_poll (device status label
refresh) was stopped in closeEvent and never restarted, since MainWindow
reuses the same ConfigWindow instance across opens — invisible before
because connect() was synchronous, but with the above change a device can
sit in CONNECTING for a while, and a stale poll made that look stuck
forever after reopening the window. Restart it in showEvent.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Rename LabDAQ → LabUI and overhaul plugin system</title>
<updated>2026-08-02T01:06:36Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-08-02T01:06:36Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=0aa59c13af65beeae104662593b21ba4d8a37789'/>
<id>urn:sha1:0aa59c13af65beeae104662593b21ba4d8a37789</id>
<content type='text'>
Branding:
- Rename app, window title, file extension (.labdaq → .labui), user data
  dirs (~/.labui/), spec file (labdaq.spec → labui.spec), and APP_NAME
  throughout all source, docs, and config files

Plugin system:
- Plugins no longer bundled in the PyInstaller build — installed at
  runtime by users via Settings → Plugins → Install Plugin (zip)
- PluginManager now takes user_dir + extra_scan_dirs; user plugins live
  in ~/.labui/plugins/, dev scan additionally covers project plugins/
- install_from_zip / uninstall / is_user_installed added to PluginManager
- vendor/ dir inside plugin zips: prepended to sys.path at load time so
  plugins can ship their own deps without requiring pip on end-user machine
- source_url field in manifest.json: shown as Download button in the
  missing-plugins dialog when a profile requires an absent plugin
- Frozen-app pip install now targets ~/.labui/plugin_packages/ using a
  real system Python (sys.executable is the exe in frozen builds)

Profile loading:
- Profile now stores plugins_manifest snapshot (id, name, version,
  source_url) alongside plugins_enabled
- On load, missing or dep-broken plugins trigger MissingPluginsDialog
  before the rest of the profile is applied; user can install from zip
  or download via source_url in-dialog, or cancel the load
- Plugin reconciliation only enables installed plugins — missing ones
  are not written to enabled.json

UI:
- Version label added to status bar (bottom-right, muted colour)
- Settings → Plugins tab: Install Plugin… button, per-plugin Remove
  button for user-installed plugins, live list refresh after install/remove

Docs:
- New docs/building.md covers the full release pipeline
- docs/plugin-development.md updated for new install flow, vendoring,
  source_url, profile behaviour, and distribution instructions

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Add editable device display name, persist it across profile save/load</title>
<updated>2026-07-29T19:03:24Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-07-29T19:03:24Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=d9ca58e48cf92438087ac24ee261c2c4911316b1'/>
<id>urn:sha1:d9ca58e48cf92438087ac24ee261c2c4911316b1</id>
<content type='text'>
DeviceInfo.name existed but was hardcoded per device type and never
operator-editable. Adds a Display Name field to Add Device and makes
the Info-tab Name field in the device config dialog editable, and
threads the name through get_save_config()/ProfileManager.apply() so
a custom name survives a .labdaq save/reload instead of reverting to
the type default. Name is applied post-construction rather than as a
constructor kwarg, since no device factory declares a "name" param.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Motion capture: profile support, FPS config, persistent tracking, window state</title>
<updated>2026-06-10T01:39:35Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-06-10T01:39:35Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=b776e70b4cd94c5d1f53dde39b2e46f6447cac3e'/>
<id>urn:sha1:b776e70b4cd94c5d1f53dde39b2e46f6447cac3e</id>
<content type='text'>
Profile integration:
- CameraDevice.get_save_config() serialises camera_index, simulate,
  resolution, fps — cameras now persist in .labdaq profiles
- ProfileManager.register/unregister_device_factory() lets plugins
  register their device classes for profile restoration without
  core → plugin dependency
- MotionCapturePlugin registers CameraDevice factory on load/unload

Camera configuration:
- Add Frame Rate combo to CameraPanel (Default/15/24/30/60/120 fps)
  defaults to 30 fps; flows through to CAP_PROP_FPS on connect
- tracker.start() accepts fps param alongside existing resolution

Tracking persistence:
- CameraTracker.is_running() — checks thread alive state
- MotionCaptureWindow initialises _connected from tracker.is_running()
  so reopening the window resumes the live feed without restarting
  the camera or losing the active tracking state

Window state:
- Plugin saves px_per_mm + camera_idx when window closes
- Reopened window restores saved values and syncs mode/shape UI
  from tracker state via restore_ui_state() (signals blocked to
  avoid resetting tracker)
- Window type changed to Qt.WindowType.Window for independent
  close button on all Linux WMs

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Upgrade motion capture: template matching, frame-freeze selection, resolution</title>
<updated>2026-06-10T00:47:49Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-06-10T00:47:49Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=898fde4dc286517cb27590b2f9cd7406d9922bd4'/>
<id>urn:sha1:898fde4dc286517cb27590b2f9cd7406d9922bd4</id>
<content type='text'>
Tracking:
- Add "template" mode (default) using snapshot + shape-masked matchTemplate
- Add "csrt" mode as alternative for texture-rich targets
- set_roi_rect(x, y, w, h, frame_rgb) initialises from exact selection rect
- set_roi() delegates to set_roi_rect (centred square, backwards-compat)
- start() accepts resolution=(w, h) to set camera capture resolution

Selection UX:
- Replace QLabel feed with FrameSelector widget (custom QWidget)
- "Click to Track" freezes frame; user scrolls to zoom (anchored to cursor),
  right-drags to pan, left-drags to draw rubber-band selection
- Shape preview: rectangle or inscribed ellipse depending on mode
- "Confirm Selection" extracts template from frozen frame and starts tracking
- "Clear" cancels selection or clears active tracking

Configuration:
- CameraPanel adds Resolution combo (Default / 640×480 / 720p / 1080p / 1440p)
- CameraDevice stores and forwards resolution to tracker.start()
- Template Match listed first in Mode combo; shape combo hidden for CSRT

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Restore Motion Capture toolbar button with multi-camera support</title>
<updated>2026-06-04T18:41:14Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-06-04T18:35:36Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=2815a90ffa7e734755884db675a9d5aa704c45a0'/>
<id>urn:sha1:2815a90ffa7e734755884db675a9d5aa704c45a0</id>
<content type='text'>
get_toolbar_actions() returns a checkable 🎥 button. On click:
- 0 camera devices → info message, button unchecks
- 1 camera device  → opens MotionCaptureWindow directly
- N camera devices → QInputDialog picker, opens selected device's window

Windows tracked per device_id; closing a window removes it from the
map and unchecks the button when none remain. on_unload closes all
open windows before deregistering the Camera panel.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Redesign motion capture plugin as Camera device type</title>
<updated>2026-06-04T18:41:14Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-06-04T18:29:24Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=864642a84d8e93cc4971c7915e39a60740cdd6fa'/>
<id>urn:sha1:864642a84d8e93cc4971c7915e39a60740cdd6fa</id>
<content type='text'>
Plugin now registers a "Camera" option in the Add Device dialog on load
(removes it on unload) instead of auto-creating a single device.

- camera_panel.py (new): CameraPanel with CameraScanThread that probes
  OpenCV indices 0–5, lists found cameras with resolution, shows
  "No cameras detected" when none available, simulation checkbox.
  build_device() returns CameraDevice for the selected index.

- device.py: rewrite MotionCaptureDevice → CameraDevice. Each instance
  owns its own CameraTracker created in connect(). Status is CONNECTED
  or SIMULATED based on tracker.simulated. get_config_widget() shows
  source/status and an "Open Camera View" button that launches the
  existing MotionCaptureWindow for that device's tracker. Backward-compat
  alias MotionCaptureDevice = CameraDevice kept for saved profiles.

- plugin.py: simplified to on_load/_PANELS registration + pixel_to_mm
  filter. No auto-devices, no toolbar button, no settings widget.

Camera data flows through the standard Device → Signal → Channel → Plot
pipeline like any other device.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Remove __pycache__ from tracking</title>
<updated>2026-06-03T22:48:47Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-06-03T22:47:50Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=7f80e836029dfd3d81ffe92593d6ccb7769298e3'/>
<id>urn:sha1:7f80e836029dfd3d81ffe92593d6ccb7769298e3</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Fix settings/plugin window bugs; remove example plugin</title>
<updated>2026-06-03T16:12:34Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-06-03T16:12:34Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=3610309b1e5d83049554afe287129c99389d231d'/>
<id>urn:sha1:3610309b1e5d83049554afe287129c99389d231d</id>
<content type='text'>
- Add missing QGroupBox import to settings_window (caused silent crash on open)
- Remove Controls tab and _controls_tab() method from settings
- Clamp child window positions to screen bounds so they don't go off-screen on Windows when main window is maximized
- Apply same screen-clamp fix to Motion Capture plugin window
- Delete example plugin; disable it in enabled.json

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
