Skip to content

Releases: paolosereno/QtIndustrialWidgets

Release v2.3.0

Choose a tag to compare

@paolosereno paolosereno released this 18 Oct 14:54
7fa34b2

Added

  • AnalysisChart widget, phase 1 part 1 (#25): offline review of recorded multi-channel data (qiw::AnalysisChart). Channels with stable ids; setChannelData() copies absolute std::chrono::nanoseconds timestamps and double values per channel (different or irregular rates allowed), rejects decreasing timestamps (the previous data is kept, false and a warning), and 0 samples clears the channel; clear() empties the data and removeAllChannels() removes the channels. One shared Y axis; programmatic view (setXRange() clamped between a few samples of the densest visible channel and the data extent, at most one view width outside the data; setYRange(); resetView() with StripChart's 10 % autoscale margin); nearestSample(). Traces follow StripChart's rules, so the same data looks the same in both: a non-finite value or a time gap above gapThreshold() (default: 4 × the EMA of the sampling interval) breaks the trace. Time axis Relative (elapsed since the data start) or Absolute (timeOrigin() + t), value axis, legend, Designer plugin entry and a gallery showcase. Decimation: see part 3 below.
  • AnalysisChart phase 1 part 2: interaction, cursors and readout band (#25). Mouse and keyboard navigation (the widget takes focus, Qt::StrongFocus): left drag pans X and Y; Shift + left drag or right drag zooms to a box (a box under 4 logical px is ignored); the wheel zooms X by ×1.25 per notch around the pointer, Ctrl + wheel zooms Y; double-click and Home reset the view; + / - zoom X around the centre; ← / → pan X and ↑ / ↓ pan Y by 10 % of the view. Two vertical cursors Cursor::A and Cursor::B, hidden until setCursorVisible(), placed at 1/3 and 2/3 of the view when the first data is loaded; a left drag that starts within 4 logical px of a visible cursor moves it instead of panning. cursorPosition() / setCursorPosition(), cursorReadout() (the nearest sample, no interpolation; none when the cursor is hidden or outside the channel's data), cursorDeltaTime() (t_B − t_A of the cursor positions), cursorDeltaValue(), cursorColor, readoutVisible and the cursorsChanged() signal. A readout band below the time axis shows the cursor times, Δt and, per visible channel, y_A, y_B and Δy (--- when missing or non-finite), in the time unit of a nominal 10-tick axis over the view and with one decimal more than a 10-tick value axis, so its texts do not depend on the widget size. It lists at most 6 channels; further visible channels are summarised in a last row (+N more). The band is part of minimumSizeHint(): from that size up all of it is inside the widget.
  • AnalysisChart phase 1 part 3: decimation, performance, docs and gallery (#25). decimationMode (Off / Auto / Always, as StripChart::DecimationMode; default Auto): a channel with more samples in the view than device columns of the plot is drawn M4-decimated with one bucket per device column, as StripChart draws it (first, minimum, maximum and last finite sample of each bucket; cosmetic 1-device-pixel pen without antialiasing; the column of a bucket holding a non-finite sample left blank; the same gap threshold). Buckets follow the view (floor((t − xStart) · W_dev / span)); 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 min/max pyramid (blocks of 64 samples, at most 2.125 bytes per sample on top of the 16 of the stored data) from which a redraw takes the buckets instead of reading every sample in the view. New bench_AnalysisChart; the #25 target (redraw after a zoom or pan step ≤ 16.7 ms for 4 channels × 10 M samples at a 1920 device px plot) is not met: 6 of 7 cases fail, e.g. the full view redraws in 42.07 ms (median) — tables, method and the profile of the cause in docs/benchmarks.md. New guide docs/analysis_chart.md (API, bindings, readout band, dropout rule, memory, performance), README and Designer list entries, and an "Analysis Chart" gallery tab with about 1 M generated samples (two channels at different rates, spikes, NaN dropouts, a recording gap) and controls for the cursors, the view, the decimation mode and the time label format.
  • AnalysisChart measured performance (#25): the target of #25, a redraw after a zoom or pan step in at most 16.7 ms for 4 channels × 10 M samples at a 1920 device px plot, is not met. On the benchmark machine the full view of 4 × 10 M samples redraws in 42 ms (median, 42.07 ms; 6 of the 7 measured cases fail). Most of the time goes into drawing the decimated M4 columns, each stroked twice; drawing one vertical stroke per column, in StripChart and AnalysisChart, is being measured in #73. Tables and method: docs/benchmarks.md.

Fixed

  • vcpkg port (#66): portfile.cmake carries the SHA512 of the v2.2.0 tag archive instead of an all-zero placeholder (never set since 1.0.x), and vcpkg.json declares the host dependencies vcpkg-cmake and vcpkg-cmake-config that the portfile needs. Not verified with vcpkg install; the port remains experimental. An in-repo port can carry the hash of a tag only on main after the tag, not in the tagged commit.
  • Standalone Doxygen configuration: docs/Doxyfile no longer states version 1.0.0; the version comes from CMakeLists.txt through QIW_VERSION (README). The README command now creates build/docs first (Doxygen creates only the last folder of the output path, so it failed on a fresh clone), and the obsolete DOT_TRANSPARENT tag is gone (#69).
  • Source archives (#69): .gitattributes marks .claude/ (Claude Code settings) export-ignore, so git archive and the GitHub release archives no longer contain it.
  • Designer plugin description of StripChart (#69): the "60+ FPS performance" claim, not backed by a measurement, is replaced by a pointer to the measured paint times in docs/benchmarks.md.

What's Changed

  • fix(vcpkg): SHA512 and host dependencies for v2.2.0; docs follow-ups by @paolosereno in #68
  • feat(analysischart): AnalysisChart phase 1, part 1: data model, view, raw rendering (#25) by @paolosereno in #70
  • feat(analysischart): interaction, cursors A/B and readout band (#25 part 2) by @paolosereno in #71
  • feat(analysischart): M4 decimation with a min/max pyramid, benchmark, docs and gallery (#25 part 3) by @paolosereno in #72
  • chore(release): v2.3.0 by @paolosereno in #75

Full Changelog: v2.2.0...v2.3.0

Release v2.2.0

Choose a tag to compare

@paolosereno paolosereno released this 17 Oct 07:50
5d43c41

Added

  • CircularBargraph widget: High-contrast circular arc indicator composed of discrete curved LED segments (qiw::CircularBargraph). Supports progressive Bar mode and single-cursor Dot mode, configurable angular sweep (e.g. 180°, 270°, 360°) and segment resolution (16, 24, 32, 48 segments), 3-stage threshold color zones by segment position (Normal, Warning, Critical; each segment takes the zone containing its centre, unlit segments show a dimmed tint of their zone) with warningThreshold <= errorThreshold kept by the setters (a setter that would break the order moves the other threshold too), value state (ValueState: Valid, Underrange, Overrange, Invalid; valueState property and valueStateChanged signal; out-of-range shown at the bound with a blinking end segment and < / > readout, NaN shown as Invalid with readout ---) and read-only rawValue, ballistic peak-hold with decaying falloff starting at the end of the hold, centered digital readout pod, full Qt property bindings, and Qt Designer plugin integration (#17).
  • LedIndicator::textColor property (#45): textColor() / setTextColor() set the label colour like the textColor of the other widgets. The default (invalid colour) keeps following QPalette::WindowText, so existing code renders as before.

Changed

  • Supported Qt versions in the documentation (#53): README, docs/getting_started.md and the CMake comment now state Qt 6.2+ only; Qt 5.15 is not supported at the moment, since it is not built or tested in CI. The build configuration is unchanged.
  • Sensor dropouts in decimated traces: A decimation bucket (pixel column) that contains any non-finite sample is no longer drawn: its column stays blank and the trace is broken on both sides, in both axis modes, in batch decimation and in the incremental stream. Previously the bucket's finite samples were drawn as an isolated stroke and the dropout could be invisible. As a consequence, finite samples in such a column, including peaks, are not drawn; autoscale still uses them.
  • Decimated traces aligned to device-pixel columns: Every decimation bucket now covers exactly one device column, in both axis modes and at any device pixel ratio (including 1.25 and 1.5): buckets are assigned with exact integer arithmetic (k = floor(t · W_dev / span), k = floor(i · W_dev / capacity)), the window holds exactly W_dev buckets, the plot's left edge is snapped to a device pixel boundary, and decimated vertices are drawn at the centre of their column with the end pixels of every segment drawn explicitly. Raw traces and absolute time ticks use the same window mapping. Rendered output of decimated and raw traces differs from previous versions by fractions of a pixel. In SampleIndex mode the window holds the W_dev newest buckets, so with a wrapped buffer the oldest partial bucket can fall out on the left, and raw samples are spaced by capacity rather than capacity − 1 (the newest sample of a full buffer lies within one device pixel of the right edge).
  • Glow dot next to a dropout: In decimated traces the glow dot on the latest point is no longer drawn when the newest bucket is blank (the legend still shows the latest value), and it is clipped so it never draws inside a blank dropout column.
  • RadialGauge digital display layout (#43): the readout pod is no longer a fixed 0.68 r × 0.28 r box. It is narrowed so that it and the unit text keep max(2 px, 0.02 r) from every scale label; except in the case below, it is never wider than before. The value font is the largest size up to the previous 0.15 r at which the widest value of the range (minimum or maximum at the current precision) fits, down to max(8 px, scale label font). The pod stays at its previous height or moves below the end-of-arc labels, whichever allows the larger value font (the previous height on a tie). The value has precedence: if it does not fit at that size, the pod grows to hold it whole, even over a label. The scale radius and label font are unchanged. With the default 270° arc the pod is narrower on most gauges (e.g. 0–8000 at 300 px: 99.3 → 82.3 px), and the pod moves down where that keeps a larger value font (e.g. −1000…1000 and 0–100000 at 200 px).
  • SevenSegmentDisplay::text() for values that round to zero (#46): with decimalPlaces ≥ 0, a negative value that rounds to zero now gives the same text as zero (e.g. −0.04 with 1 decimal: "0.0", previously "-0.0").
  • IndustrialSwitch layout of labels, LED and safety guard (#44):
    • the position labels are placed around the safety guard (its shadow, lift tab and hinge brackets included), so the closed cover never covers them; the closed cover is drawn at 0.9 opacity;
    • the status LED sits left of the ON label (vertical) or above it (horizontal), with its glow clear of every text;
    • the open guard is drawn edge-on in free plate space (left of the switch, or below it and below the centre label) instead of rotated over the labels and out of the widget; a click on it closes the guard (previously a click in the top 15 px of the guard area);
    • where a label, the LED or the open guard would not fit, the switch is drawn smaller about its centre.
  • IndustrialSwitch::minimumSizeHint() from the real font metrics (#44): the smallest size (aspect ratio of the previous 50×80 / 80×50, not smaller) at which every label is drawn whole inside the plate and nothing covers it, computed from the widget font (glyph ink included), the safety guard, the number of positions and the texts. From this size up no label is clipped or covered; below it the LED and the guard still never cover a label, but labels may be clipped. Layouts no longer squeeze a switch below it (e.g. the gallery's Manual Controls tab).
  • IndustrialSwitch::sizeHint() (#44): never smaller than minimumSizeHint(); with a safety guard and 3 positions, the smallest size with the aspect ratio of the previous hint (75×125 / 125×75) at which the switch keeps its natural size and every text fits.
  • LevelMeter scale layout from the real label widths (#40): the room for the scale labels is computed from the ink width of the widest label in the scale font (widget font × 0.7, at least 6 pt) instead of fixed sizes. Vertical meters reserve max(36 px, room for the widest label), so meters whose labels fit the previous 36 px keep their layout (e.g. the default dB scale). Horizontal meters inset the bars at both ends so the first and last labels are whole inside the chassis; the bars are shorter by that amount.
  • LevelMeter::minimumSizeHint() from the real font metrics (#40): from this size up every scale label is drawn whole and, on a horizontal linear scale, neighbouring labels do not overlap. The previous fixed 45 px width of a vertical meter was narrower than the chassis margins, the bars and the 36 px scale together (the bars were drawn partly outside the widget), so the vertical minimum is now larger (e.g. 74 × 100 instead of 45 × 100 for 2 channels, −60…+6 dB, default font). Horizontal linear meters get a wider minimum when their labels are wide (e.g. 0–100000). Size hints are updated when the range, the unit or the channel labels change.
  • LevelMeter::sizeHint() (#40): never smaller than minimumSizeHint(); a vertical meter's scale term grows with the widest label.
  • RadialGauge draws only every k-th scale label when labels would be too close (#50): previously a label was drawn at every major tick, so wide values overlapped near the top of the arc (e.g. "4000000" and "5000000" at 200 px). Now k is the smallest divisor of majorTicks for which every pair of drawn labels keeps max(4 px, 0.5 × label font height) between their boxes (the LinearGauge rule of #31); if none fits, only the first and last labels are drawn. On a span of 360° or more, where the last label lies on the first, the last one is not drawn. All ticks are still drawn and the label font is unchanged; with wide values fewer labels are shown (e.g. 0 / 2000000 / … / 10000000 at 200 px). The digital display is laid out against the drawn labels only, so on such gauges it can be larger or placed lower than before.
  • LevelMeter draws only the scale labels that keep a minimum gap (#57): every tick is still drawn, but a label only if it keeps max(4 px, 0.5 × scale font height) from every other drawn label (the rule of #31 and #50); the first and last labels are always drawn (only the first when they clash). Linear scale: every k-th label, k the smallest divisor of 6 that keeps the gap. dB ladder: labels taken in priority order (the ends, 0 dB, −20, −6, −40, −12, −30, −3, +3), each only if it keeps the gap from those already taken. Small meters show fewer labels; e.g. the gallery's stereo dB meter no longer shows −3 and +3.
  • LevelMeter unit placement (#57): on a vertical meter with a unit, a band above the bars holds the unit above the top label (the bars are shorter by that band; meters without a unit are unchanged); on a horizontal meter the unit moves right of the last label when the bars are too thin for it to clear the ticks, and the bars leave room for it inside the chassis.
  • LevelMeter::minimumSizeHint() of a horizontal meter (#57): the height covers the margins, the title, the bars at 6 px each and the scale (from the scale font, 36 px at least) instead of a fixed 45 px (e.g. 64 px for 1 channel, 74 px for 2, 112 px for 4 with a title); the width keeps max(4 px, 0.5 × scale font height) between linear labels instead of 4 px, so all linear labels are drawn at the minimum (e.g. 0–100000: 257 → 261 px at 9 pt, 354 → 372 px at 13 pt), and on a dB scale it keeps the same gap between the two end labels, so both are drawn at the minimum (e.g. −60…+6 dB with unit "dB": 100 → 129 px at 20 pt; unchanged with the default font). Horizontal scale labels are centred in a rectangle as tall as the scale font line when it exceeds 14 px, so large fonts no longer d...
Read more

Release v2.1.1

Choose a tag to compare

@github-actions github-actions released this 10 Oct 18:30

Full Changelog: v2.1.0...v2.1.1

Release v2.1.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 13:40

Full Changelog: v2.0.1...v2.1.0

Release v2.0.1

Choose a tag to compare

@github-actions github-actions released this 10 Oct 09:48

Full Changelog: v2.0.0...v2.0.1

Release v2.0.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 08:21

Full Changelog: v1.0.1...v2.0.0

Release v1.0.1

Choose a tag to compare

@github-actions github-actions released this 09 Oct 15:24

Full Changelog: v1.0.0...v1.0.1

Release v1.0.0

Choose a tag to compare

@github-actions github-actions released this 09 Oct 14:03