<feed xmlns='http://www.w3.org/2005/Atom'>
<title>labUI.git/plugins, 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-08-02T01:06:36Z</updated>
<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>Merge branch 'feat/plugin-pip-install-prompt'</title>
<updated>2026-07-29T20:21:40Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-07-29T20:21:40Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=c98885f503a8a2eb4b691b41404231da8d2aeda5'/>
<id>urn:sha1:c98885f503a8a2eb4b691b41404231da8d2aeda5</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Check plugin dependencies before loading, warn instead of silent console error</title>
<updated>2026-07-29T19:21:05Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-07-29T19:21:05Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=45ff7227fabb97273d4645601733f90f5f744eb1'/>
<id>urn:sha1:45ff7227fabb97273d4645601733f90f5f744eb1</id>
<content type='text'>
manifest.json already had an unused "requires" field (motion_capture's
manifest lists opencv-python&gt;=4.8.0) — PluginManifest never parsed it,
so a missing dependency just crashed the import inside _load_plugin(),
caught by the broad except and only ever printed to the console.

Adds PluginManifest.requires, PluginManager.missing_requirements()/
get_missing_dependencies(), and an early check in _load_plugin() that
skips the risky import entirely when a requirement is missing.
main_window.plugin_enable() now checks this before calling
PluginManager.enable() and shows a QMessageBox with the missing
packages and a pip install command instead of failing silently.

Checks by distribution name via importlib.metadata (what pip installed
it as), not import name — those differ for packages like opencv-python
(imports as cv2) or pyserial (imports as serial), so importlib.util.
find_spec() would give false negatives.

Also fixes a button-state bug this surfaces: SettingsWindow's plugin
toggle optimistically flipped to "Disable" the instant Enable was
clicked, before knowing whether enabling actually succeeds — pre-existing,
but now trivially reproducible (any plugin missing a dependency). Enable
no longer flips the button immediately; main_window calls the new
sync_plugin_button() once the real outcome is known.

Co-Authored-By: Claude Sonnet 5 &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>Update CML protocol documentation and improve command handling; adjust motion_capture setting to false</title>
<updated>2026-07-28T23:01:38Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-07-28T23:01:38Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=b2dc94bb2e7c7e57cba202697ed4d58bbb1f01f5'/>
<id>urn:sha1:b2dc94bb2e7c7e57cba202697ed4d58bbb1f01f5</id>
<content type='text'>
</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>
</feed>
