| Age | Commit message (Collapse) | Author |
|
|
|
Replaces layout radio buttons (Stacked/Side-by-Side/Grid) with a
drag-and-drop tiling canvas. Three drop zones per tile edge:
- squeeze (outer 1/6, amber): insert between two tiles
- split (1/6–1/3, blue): bisect tile 50/50
- center (green): swap tile positions
BSP tree stores layout; tree_to_grid() converts to rowspan/colspan for
pyqtgraph. _tree_equalize_ratios ensures all tiles share space equally
after any operation. Backward-compatible with existing .labdaq profiles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
_zone_at used absolute pixel distance to pick dominant axis, which always
favoured the short (vertical) axis for landscape tiles. Replaced with
normalized fraction (dist/dimension), so left/right zones are accessible
on wide tiles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Zone layout (outer to inner per edge):
0–1/6 squeeze (amber): insert dropped tile between two existing tiles
1/6–1/3 split (blue): bisect the target tile 50/50
1/3–2/3 center (green): swap/move
Squeeze behaviour: find the tile adjacent to the target in the drop
direction; insert dragged tile to the opposite side of that neighbor
so it lands visually between them. Falls back to split when no neighbor
exists (edge tile).
- _zone_at: returns "squeeze_*"/"split_*"/"center" with dominant-axis
selection (nearest edge wins)
- _adjacent_tile: finds geometrically adjacent tile by pixel proximity
- _do_drop: routes squeeze→insert-at-neighbor, split→bisect, center→swap
- paintEvent: amber/blue/green per zone type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Edge zones (left/right/top/bottom): split target tile equally
- Center zone: swap dragged and target tile positions in BSP tree
(tree structure preserved, only leaf pane values exchanged)
- Center overlay: green highlight (vs blue for split zones)
- _tree_swap: immutable swap of two leaf pane values
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
After each drag-drop, add, or remove, walk the tree and reset each split's
ratio to left_leaves/(left_leaves+right_leaves). This distributes space
equally per pane regardless of the prior tree structure.
- _tree_equalize_ratios: recomputes all ratios bottom-up by leaf count
- _tree_leaf_count: counts leaves in a subtree
- _tree_grid_size: uses LCM of split denominators (capped at 12) instead
of 2^depth, giving exact integer rowspans for equal distributions
- _tree_default: balanced binary tree + equalize (not uneven chain)
- Applied in _do_drop, _add_pane, _rm
Result: 3 side-by-side panes → equal widths; any mixed layout → equal area.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Dragging a tile to the edge (left/right/top/bottom) of another tile
splits that tile's space and inserts the dragged tile on that side.
Drop zones occupy the outer third of each tile edge; center has no zone.
Ghost overlay shows the split target during drag.
Data model: LayoutConfig.tree (BSP tree as nested dict). PaneSpec.row/col
removed. strip_chart uses tree_to_grid() → rowspan/colspan for pyqtgraph.
Backward compat: tree=None falls back to layout_mode stacking path.
Example: 3 stacked → drag Tile 3 to left of Tile 1 → Tile 3|Tile 1 on
top (side-by-side), Tile 2 spans full bottom.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Replace layout radio buttons (Stacked/Side-by-Side/Grid) with a visual
LayoutCanvas widget. Pane tiles snap to grid cells on drag-drop; dropping
on an occupied cell swaps positions. Ghost highlight shows snap target
during drag.
- PaneSpec gains row/col fields (None = unassigned; backward compat preserved)
- LayoutConfig gains "free" mode; strip_chart reads explicit positions
- PaneBlock loses ▲▼ move buttons; reordering is canvas-only
- QSS entries added for layoutTile, layoutTileDragging, layoutCanvasBar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Remove Qt.WindowType.Tool flag — it suppresses the maximize button.
Window now has standard chrome with minimize/maximize/close.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
virtual blocks
Physical channels panel: horizontal splitter (left/right), channel picker
dialog replaces dropdown, Filters and Math sections per block, Calibrate
button, pipeline-enabled checkbox removed. Virtual channels: collapsible
blocks, enable checkbox removed, kind dropdown removed (defaults to
expression). Window resized to 1400×820.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Loaded settings were never consumed — theme defaulted to dark and
poll interval stayed at 100 ms regardless of what was saved to disk.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- New core/app_settings.py: load/save settings.json to platform config dir
(Windows: %APPDATA%, Linux: ~/.config, macOS: ~/Library/Application Support)
- MainWindow loads saved settings on startup, saves on every Apply & Close
- Settings survive profile switches and app restarts independently of profiles
- Settings > General: Reset to Defaults button with QMessageBox confirmation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Always insert one source row for derivative/second_derivative on build
- Guard in _on_kind: add row when switching to derivative with no sources
- Code editor: monospace font (IBM Plex Mono/Consolas fallback)
- Code editor: tab stop = 4 character widths
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Reflects current design: split Physical/Virtual Channels layout,
no tabs, no legacy Signals list as primary UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Superseded by ui/windows/signals_window.py; no longer imported anywhere.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Default ID: sig_N → CH_VN
- Default name: Signal N → Virtual Channel N
- Rename "Kind" label to "Operation"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Split PipelineTab into Physical/Virtual sections via QSplitter
- Each section has independent scroll area, header, and add controls
- Add "With Respect To" control for derivative/second_derivative virtual channels: Time (dy/dt) or Channel (dy/dx)
- When Channel selected, x variable picker appears for any physical or derived channel
- signal_processor computes dy/dx using channel inputs when deriv_wrt=channel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Remove redundant Signals tab from Channels window; PipelineTab is now direct content
- PipelineTab removes stale blocks when devices are removed or reconfigured
- Devices>Signals tab refreshes automatically on device add/remove/configure
- Channel name and unit edits in Devices>Signals propagate live to Channels pipeline block headers; missing blocks auto-added if channel is enabled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- 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 <noreply@anthropic.com>
|
|
Replace camera index spinbox with a combo listing detected cameras by
name. On Linux enumerates /dev/video* and reads device names from sysfs
(fast, no probing). Fallback probes cv2.VideoCapture indices 0-7 on
other platforms. Simulation entry always appended as last option.
Also adds explicit simulation mode (index -1) to tracker.start() and a
rescan button (⟳) in both the main window and the Settings > Plugins
widget.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Tracks a user-selected point via webcam (OpenCV CSRT tracker) and
streams x_pos / y_pos pixel coordinates as live channels into the
acquisition pipeline. Falls back to a Lissajous figure simulation
when opencv-python is not installed, so the plugin works immediately
without hardware.
- tracker.py: background thread, real camera + sim modes, thread-safe API
- device.py: BaseDevice reading x/y from tracker, wires into engine
- filter.py: custom pixel_to_mm filter (registered in FILTER_CLASSES)
- window.py: floating tool window, live feed, click-to-track ROI selection
- plugin.py: LabPlugin wiring all pieces together, toolbar button,
settings widget, profile save/restore
Also: PluginAction.button_ref_callback lets plugins store a ref to their
toolbar button so they can uncheck it when their window closes.
opencv-python install note added to requirements.txt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Plugins live in plugins/<name>/ with a manifest.json and a LabPlugin
subclass. Enabled/disabled in Settings > Plugins tab; state persists in
plugins/enabled.json and in .labdaq profiles (loading a profile
enables/disables plugins to match). Plugins can contribute toolbar
buttons, BaseDevice instances (auto-wired into acquisition pipeline),
custom signal filter classes, a settings widget, and profile state via
get_save_state/apply_save_state. Load errors now report the exact
failing stage (file not found, class missing, on_load crash).
Includes example plugin skeleton and docs/plugin-development.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
|
|
|
|
Fixed derived signal saving profile. Renamed Signals>Pipeline to Signas>Channels
and adding/removing channels feature.
|
|
|
|
|
|
|
|
|
|
Including:
- Control panel
- Change status of simulation after devices creation.
- Arduino serial conflict with multiple channels.
- Claude instructions.
- Update to arduino on board code.
|
|
|
|
This version contains the profile feature. Allowins users to save the
channels, signals and plots for specific labs.
|
|
|
|
|
|
|
|
|
|
|
|
|