<feed xmlns='http://www.w3.org/2005/Atom'>
<title>labUI.git/ui/windows, 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-21T05:45:18Z</updated>
<entry>
<title>Persist dock layout to profiles; dedupe port names; Channels-tab Create button</title>
<updated>2026-09-21T05:45:18Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-09-21T05:45:18Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=a0473245f1f453bfc87ea1bc9bd479311e8a944b'/>
<id>urn:sha1:a0473245f1f453bfc87ea1bc9bd479311e8a944b</id>
<content type='text'>
- Fix duplicated device names ("FT231X USB UART - FT231X USB UART (...)")
  — pyserial on Linux often reports the same udev model string twice for
  cheap FTDI/CH340-style adapters. Collapse it at the list_ports() source
  so every consumer (device auto-name, port picker) benefits.

- Save Control Panel / Namespace dock area, floating geometry, visibility,
  size and order to .labui profiles via QMainWindow.saveState()/
  restoreState(), round-tripped through the existing Profile.settings
  passthrough (self._settings) — no profile schema change needed.

- Channels tab: replace the per-row Signal ⊕ button (and its pinned/
  frozen-column overlay) with a single "Create Channel" button below the
  Namespace panel. Select one or more Signal/Channel rows (multi-select
  now supported) and click it — selected rows become the new Channel's
  sources, or an empty Channel is created if nothing's selected. Fixes a
  bug in the process: the multi/zero-source path defaulted to
  kind="expression" with no expression text, which fails to compile and
  silently dropped the channel; use kind="none" instead, which the user
  then edits in the Channels pane.

  NamespacePanel itself (ui/namespace_panel.py) goes back to a plain
  read-only reference widget — the create-channel button and its logic
  live entirely in ConfigWindow (config_window.py), so the shared
  dockable Namespace panel on the main window is unaffected.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Make Channel the single source of truth for a reading's name</title>
<updated>2026-09-21T04:30:13Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-09-21T04:30:13Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=006366ef8099c8313ae596bf6267e9f98dfbeb5e'/>
<id>urn:sha1:006366ef8099c8313ae596bf6267e9f98dfbeb5e</id>
<content type='text'>
Device tab stays hardware-only (pins, wiring, may vary per workstation);
the Channels window owns the profile-facing identity. Concretely:

- Fix a real regression: SerialDevice generic-format Apply &amp; Reconnect
  nulled out custom channel_names/units every time (no fallback, unlike
  every other optional field in reconfigure()), silently reverting to
  sig0/sig1/... ids.
- Plot legend now live-tracks the current Signal/Channel name instead of
  freezing tr.label at add-time, so a rename shows up without re-adding
  the trace.
- Add an inline quick-reconnect (combo + button, pre-selects the actual
  current source) directly on each Channel block in the Channels window,
  for the common single-source case — rewiring hardware no longer means
  touching every Plot/pipeline that referenced it.
- Collapse the Channel ID field behind an "ID ▸" advanced toggle;
  channel_id is already auto-derived from the name (_slug_id) and rarely
  needs to be seen.
- Devices tab's Signal "Name" column relabeled "Label" with a tooltip —
  de-emphasized now that Channel name is the real identity.
- Move "create a Channel from this Signal" from a column on the Devices
  tab's signal table to a ⊕ button on each Signal row in the Channels
  tab's Namespace panel — the action now lives where the identity is
  created, not on the hardware tab. Only that instance opts in
  (on_create_channel); the read-only Namespace dock on the main window
  is unaffected.
- Floor the Namespace panel's width at its columns' natural size so
  dragging the Channels-tab splitter narrower can't push the ⊕ column
  behind the horizontal scrollbar.
- Breadcrumb log when a profile-saved Signal override can't be applied
  because the signal_id no longer exists.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Fix Signal name reset and orphaned references on device reconfigure</title>
<updated>2026-09-20T17:31:07Z</updated>
<author>
<name>Christian Kolset</name>
<email>christian.kolset@gmail.com</email>
</author>
<published>2026-09-20T17:31:07Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=bb8be051ffffa56a01093c9f3aaa80e84748568f'/>
<id>urn:sha1:bb8be051ffffa56a01093c9f3aaa80e84748568f</id>
<content type='text'>
Apply &amp; Reconnect rebuilt a device's whole Signal list from hardware
defaults on every call, silently discarding user/profile-set names,
units, and colors even when pins hadn't changed. Add
merge_signal_metadata() and call it from ArduinoDevice.remap_pins()
and SerialDevice.reconfigure() to reapply the same fields
ProfileManager already treats as user overrides, keyed by signal_id.

Also close a related gap: removing a pin that a Channel/Plot/Control
still referenced left it silently dangling, and SignalProcessor never
pruned its latest-value cache, so a derived Channel kept computing off
a permanently frozen stale reading. Add SignalProcessor.
on_device_reconfigured() to purge that cache, reference_registry.
find_references() for a console breadcrumb, and live "missing source"
indicators on Plot traces and Channel blocks reusing ControlWidget's
existing stale/QSS pattern. Also fixes ControlWidget._write() still
enqueuing writes to a channel that no longer exists.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<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>Add .labui file association — open by double-click, cross-platform</title>
<updated>2026-08-17T16:01:59Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-08-17T16:01:59Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=e749cca30da2d02839aca736873e7e5ad45e191c'/>
<id>urn:sha1:e749cca30da2d02839aca736873e7e5ad45e191c</id>
<content type='text'>
Windows/Linux self-register via core/file_association.py (winreg /
XDG .desktop+mime), triggered from a new Settings button since there's
no installer to do it at install time. macOS gets it at build time via
labui.spec's new BUNDLE() step (CFBundleDocumentTypes in Info.plist) —
that step also makes mac builds a real .app bundle for the first time,
not just a bare onedir folder. main.py gains a LabUIApplication
subclass to catch the macOS QFileOpenEvent (buffered if it arrives
before MainWindow exists) plus argv[1] handling for Windows/Linux.
ProfileButton.load_path() extracted so both paths share the same
load-and-apply logic as the existing file-dialog flow.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Channels tab drag-to-reorder, Namespace absolute references, fix stale Channel ID renames</title>
<updated>2026-08-13T19:39:25Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-08-13T19:39:25Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=34c12b2b5b280206890cd03153b1c95116acc85e'/>
<id>urn:sha1:34c12b2b5b280206890cd03153b1c95116acc85e</id>
<content type='text'>
Generalized the Plot pane drag-to-reorder (_PaneDragHandle/_PlotDropContainer
-&gt; _DragHandle/_DragReorderContainer) and reused it for the Channels tab's
derived-channel list; reordering also updates SignalProcessor's evaluation
order (list order matters for chained Channels) via new reorder_derived().

Namespace panel: added an ID column (bare channel_id, matching the actual
channels.get(...) script convention) and fixed the Source column for
Channels to show resolved absolute references (dev_id/ch_id for a Signal,
bare id for a chained Channel) instead of the operation kind, so a chain
is traceable by eye.

Fixed a real bug this surfaced: renaming a Channel's ID never propagated
to other Channels' sources, leaving dangling ("derived", old_id)
references that silently stopped updating. Added
SignalProcessor.rename_derived() to fix up sources plus carry the ring
buffer/latest-value cache to the new key, wired to DerivedBlock's Apply.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Configuration window UI reorg + Plot Builder polish</title>
<updated>2026-08-13T17:45:50Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-08-13T17:45:50Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=a6e812dcbf3f2fc27565e52b6c0c6f9b7e4363ee'/>
<id>urn:sha1:a6e812dcbf3f2fc27565e52b6c0c6f9b7e4363ee</id>
<content type='text'>
Devices tab split into Physical Devices (left) / Signals (right); Channels
tab now hosts the Namespace browser (left) alongside Channels (right,
larger by default). Plot Builder panes gain drag-to-reorder (same mechanics
as ControlPanel's widget list, adapted for the editable pane title via a
dedicated grip icon), "+ Add Channel" moved beside Size for a more compact
row, and Min/Max/Size/line-width spin boxes widened so values no longer
overlap the up/down arrows.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Updated configuration window design.</title>
<updated>2026-08-13T17:22:24Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-08-13T17:22:24Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=23802b2131fc0098243bab73df7be104ef9891d1'/>
<id>urn:sha1:23802b2131fc0098243bab73df7be104ef9891d1</id>
<content type='text'>
- Split device tab into pysical devices and signals.
- Replaced old Signals panel (under Channels tab) with namespace.
  to make Channel creation easier.
</content>
</entry>
<entry>
<title>Fixed loading profiles data retention and display.</title>
<updated>2026-08-13T17:04:34Z</updated>
<author>
<name>Christian Kolset</name>
<email>ckolset@colostate.edu</email>
</author>
<published>2026-08-13T16:59:25Z</published>
<link rel='alternate' type='text/html' href='https://git.kolset.xyz/labUI.git/commit/?id=012a1093e3cb9a1b376fc3beee4df859ccfb8da5'/>
<id>urn:sha1:012a1093e3cb9a1b376fc3beee4df859ccfb8da5</id>
<content type='text'>
</content>
</entry>
</feed>
