|
QtIndustrialWidgets 2.3.0
Modern, High-Performance Industrial Instruments & SCADA Widgets for Qt
|
QtIndustrialWidgets::AnalysisChart shows recorded multi-channel data for offline review: zoom, pan, two measurement cursors and a readout band. It complements StripChart, which stays the live, streaming display. Both charts draw the same data with the same rules (dropouts, gaps, decimation), so the same record never looks different in the two.
addChannel() returns an id that is never reused and is not affected by removing other channels. Ids increase in the order the channels are added.setChannelData(id, t, values, count)** copies count absolute std::chrono::nanoseconds timestamps and double values; the caller's buffers can be freed after the call. Each channel has its own timestamps (different or irregular rates are allowed).false, issues a warning and keeps the previous data. count == 0 clears the channel.clear()** empties the data of every channel and keeps the channels; **removeAllChannels()** removes the channels.setXRange(), setYRange() and resetView() set the view from code; viewChanged() reports every change.resetView() shows the full extent, with Y covering the finite values of the visible channels plus a 10 % margin (StripChart's autoscale margin). The first data loaded sets the view this way.TimeLabelFormat::Relative shows the time elapsed since the data start; Absolute shows timeOrigin() + t as wall-clock time (HH:mm:ss.zzz), or t itself when timeOrigin() is invalid.The widget takes keyboard focus (Qt::StrongFocus).
| Input | Action |
|---|---|
| Wheel | Zoom X around the pointer, ×1.25 per notch |
| Ctrl + wheel | Zoom Y around the pointer |
| Left drag | Pan X and Y; a drag that starts within 4 logical px of a visible cursor moves that cursor |
| Right drag, or Shift + left drag | Box zoom (a box under 4 logical px is ignored) |
| Double-click, Home | resetView() |
+ / - | Zoom X around the centre |
| ← / → | Pan X by 10 % of the view |
| ↑ / ↓ | Pan Y by 10 % of the view |
Cursor::A and Cursor::B, are hidden until setCursorVisible(). When the first data is loaded they are placed at 1/3 and 2/3 of the view. setCursorPosition() moves them from code; they can also be dragged.cursorReadout(channel, cursor)** returns the sample nearest in time to the cursor (no interpolation; on a tie, the earlier one). It returns no value when the cursor is hidden or lies outside that channel's data.cursorDeltaTime()** is t_B − t_A of the cursor positions; **cursorDeltaValue(channel)** is y_B − y_A, with no value when either readout is missing or non-finite.cursorsChanged() reports a cursor moved, shown or hidden, or new data under it.The readout band below the time axis shows the cursor times and Δt, then for each listed channel y_A, y_B and Δy. --- marks a missing or non-finite readout.
minimumSizeHint() always holds it whole; on a widget much narrower or wider than that nominal axis, the band's unit or decimals can differ from those of the axis labels (for example the axis in ms, the band in s).+N more. Their readouts are available through cursorReadout() and cursorDeltaValue(), and setChannelVisible() chooses which channels the band lists.minimumSizeHint(): from that size up no text of the band is clipped.readoutVisible hides the band; cursorColor sets the colour of the cursor lines.StripChart (#11).gapThreshold() breaks the trace. With the default 0 the threshold is 4 × an exponential moving average of the sampling interval of the channel (weights 0.9 / 0.1, in sample order), as in StripChart.decimationMode, same meaning as in StripChart): with Auto (the default) a channel with more samples in the view than device columns of the plot is drawn M4-decimated, one bucket per device column: each column shows the first, minimum, maximum and last finite sample of its bucket, with a cosmetic 1-device-pixel pen and without antialiasing. Off always draws the raw samples, Always always decimates. Buckets are floor((t − xStart) · W_dev / span), so they follow the view; the plot's edges are snapped to device pixels, and traces, ticks and cursors share one device mapping at every device pixel ratio.setChannelData() builds a pyramid over the values (blocks of 64 samples, then levels that each combine two nodes of the level below), so a redraw combines whole blocks instead of reading every sample in the view. Its buckets equal those of M4Decimator::decimateToBuckets() on the same samples.Per sample, a channel stores:
| Part | Bytes per sample |
|---|---|
Timestamp and value (copied by setChannelData()) | 16 |
| Pyramid: one 64 B node per block of 64 samples, levels above it adding at most as much again, and the first timestamp of each block (8 B per 64 samples) | ≤ 2.125 |
The pyramid adds at most 2.125 / 16 ≈ 13.3 % to the stored samples (the node size is 64 B on 64-bit platforms). For 4 channels × 10 M samples that is 640 MB of samples plus at most 85 MB of pyramid. The measured figures, with the time of setChannelData(), are in the Benchmark Report.
Target of #25: a redraw after a zoom or pan step takes at most 16.7 ms for 4 channels × 10 M samples at a 1920 device px plot. It is not met on the machine of the Benchmark Report: 6 of its 7 cases FAIL. The full view of the four 10 M-sample channels redraws in 42.07 ms (median, p95 65.22 ms) with the SpikesAndNaN signal; panning at 1/100 of the record (100 k samples per channel in the view) passes with a p95 of 10.81 ms. The time goes mostly into drawing the decimated envelope, then into reading the buckets from the pyramid; the report has the full tables, the method and the profile.
Getting data out of a StripChart, loading files, several Y axes, horizontal cursors, statistics between the cursors, FFT, export and printing.