Files
MARTe-Integrated-Components/docs/superpowers/plans/2026-06-25-streamhub-qt-client.md
T
2026-06-26 09:11:10 +02:00

6.4 KiB

StreamHub Qt Client Implementation Plan

For agentic workers: implement task-by-task. Each task ends with a buildable deliverable. Build with cmake -B build && cmake --build build -j from Client/streamhub-qt/.

Goal: Native Qt5/6 oscilloscope client equivalent to the ImGui Client/streamhub/ client, same StreamHub WS protocol.

Architecture: Qt Widgets + custom QPainter plot widget + QtWebSockets. Reuse ../streamhub/Protocol.{h,cpp} and SignalBuffer.h verbatim. Single GUI thread; QWebSocket signals drive a model; 60 Hz QTimer repaints plots.

Tech Stack: C++17, Qt5 or Qt6 (autodetect), CMake ≥3.16.

Global Constraints

  • Must compile against Qt5 and Qt6 — use Qt${QT_VERSION_MAJOR}:: targets, no version-specific APIs without a guard.
  • Do not modify Client/streamhub/ (reference). Reuse its Protocol.cpp, Protocol.h, SignalBuffer.h by include path / direct source compile.
  • Domain model uses QColor (not ImVec4).
  • Reuse LTTBDecimate and SignalBuffer from SignalBuffer.h.

Task 1: Project scaffold + CMake autodetect + empty MainWindow

Files: Create CMakeLists.txt, main.cpp, MainWindow.{h,cpp}, Theme.{h,cpp}, Model.h.

  • CMake: find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets WebSockets), find_package(Qt${QT_VERSION_MAJOR} ...), CMAKE_AUTOMOC ON, C++17. Sources include ${CMAKE_CURRENT_SOURCE_DIR}/../streamhub/Protocol.cpp; include dir ../streamhub. Link Qt${QT_VERSION_MAJOR}::Widgets, Qt${QT_VERSION_MAJOR}::WebSockets.
  • Model.h: SignalView{SignalMeta meta; SignalBuffer buf; QColor color; double lineWidth; int marker; bool visible;}, Source, VScale, PlotAssignment, PlotLayout enum + layoutDims/layoutNames/layoutCount.
  • Theme: apply Catppuccin-Mocha QPalette + stylesheet; tracePalette(i).
  • main.cpp: parse -host/-port, build QApplication, apply theme, show MainWindow.
  • Deliverable: builds, opens an empty dark window.

Task 2: WsClient + Hub controller (data model + WS dispatch)

Files: Create WsClient.{h,cpp}, Hub.{h,cpp}.

  • WsClient: wraps QWebSocket; connectTo(host,port), reconnect, sendText; 3 s reconnect QTimer; signals connectedChanged(bool), textReceived(QString), binaryReceived(QByteArray).
  • Hub: owns WsClient + std::vector<Source> + TriggerCfgState + CaptureFrame + per-plot zoom caches + HistoryInfoMsg. On connect sends getSources/getStats/historyInfo. Routes text via ParseType to onSources/onConfig/onStats/onTriggerState/onZoom/onHistoryZoom/onHistoryInfo/ onMaxPointsUpdated; binary via first byte (v2 capture vs v1 push). Command senders mirror Build* in Protocol.h. Emits signals listed in the spec. Holds a findSource/findSignal/slotKey. Zoom caches keyed by plot index (requestZoom, requestHistoryZoom, nextZoomReqId).
  • Deliverable: builds; connecting to a hub logs received message types (temporary qDebug), model populates (verified via a debug dump).

Task 3: PlotWidget — static render + live scroll

Files: Create PlotWidget.{h,cpp}.

  • PlotWidget(Hub*, int plotIdx, QWidget*). paintEvent: draw frame, grid, ±4-div Y axis with 9 ticks, X axis with 10 ticks. Live mode: X = [wallNow-windowSec, wallNow]. For each assigned slot: read ring (buf.readLast(maxPoints)), clip to visible X, LTTBDecimate to 2400, normalize (normal/digital/mixed via ported resolveVScale/normalizeY/ bandNormalize), map to pixels, drawPolyline in trace color/width; optional markers. Badge row painted at top (trace name + V/div).
  • Deliverable: dropping handled later; for now a hard-coded assignment shows a live trace scrolling against wall clock.

Task 4: PlotWidget — interaction (zoom/pan/cursors/active/vmode) + drag-drop

Files: Modify PlotWidget.{h,cpp}; add small per-plot toolbar (Live/Back/ Fit, N/D/M, V-scale controls) as child widgets or painted buttons.

  • Mouse: wheel = Y zoom active trace (X zoom if none); Ctrl+wheel = X zoom/window; Shift+wheel = Y pan; right-drag = X pan (live→non-live); box-zoom optional. Click a badge = toggle active slot; context menu on badge = color/ width/marker/digital-in-mixed/remove. Zoom history Back/Fit/Live.
  • Global A/B cursors (drawn + draggable) with readouts; pause snapshot.
  • dragEnterEvent/dropEvent accept a signal-ref mime → append assignment.
  • Hi-res WS zoom + history zoom requests through Hub (live-narrow throttle, non-live debounce) and prefer cached hi-res/history data when it covers view.
  • Deliverable: full single-plot interaction parity.

Task 5: PlotGrid + SourceSidebar + MainWindow toolbar

Files: Create PlotGrid.{h,cpp}, SourceSidebar.{h,cpp}; expand MainWindow.{h,cpp}.

  • PlotGrid: nested QSplitters realizing each PlotLayout; rebuild on change.
  • SourceSidebar: QTreeWidget sources→signals; drag a signal (mime with src/sig index); "Add source" + remove; reflects Hub signals.
  • MainWindow toolbar: sidebar toggle, layout picker (menu of the 8 layouts), pause, cursors toggle, trigger-bar toggle, history-bar toggle, window-preset combo, stats button, connection (host/port + reconnect), status LED.
  • Add-source dialog (label/host/port/multicast/dataPort) → Hub::addSource.
  • Deliverable: drag-drop from sidebar to any layout cell works end to end.

Task 6: TriggerBar + StatsDialog + HistoryBar

Files: Create TriggerBar.{h,cpp}, StatsDialog.{h,cpp}, HistoryBar.{h,cpp}; wire into MainWindow.

  • TriggerBar: signal combo (full keys incl array index), edge combo, threshold spin, window combo (100 µs…10 s), pre% slider, normal/single → setTrigger; Arm/Disarm/Rearm/Stop; badge from triggerStateChanged. Capture frame routes to plots' trigger view.
  • StatsDialog: table of metrics per source + custom 20-bin histogram widget (QPainter bars), refreshed on statsChanged.
  • HistoryBar: Live, range readout, pan ←/→, jump presets, All; drives plots' non-live X range + history-zoom.
  • Deliverable: trigger capture, stats histogram, history browse all match the ImGui client.

Task 7: Build, run, verify, document

  • Clean Release build on Qt6 (and confirm Qt5 configure path).
  • Launch against a live hub; verify the spec's parity checklist.
  • Add a Client/streamhub-qt build note to CLAUDE.md and ARCHITECTURE.md.
  • Deliverable: working client + docs.