2.3. QeTrend - Time-Series Trend Widget

2.3.1. Overview

QeTrend is a time-series trend widget built on the QwtPlot library. It extends QePlotBase to provide date-time X-axis scaling and point-by-point data appending. Unlike QePlot which accepts bulk 1D arrays, QeTrend is designed for streaming scenarios where individual (value, timestamp) pairs are appended over time. The X axis uses a QwtDateScaleDraw with UTC timestamps, and each curve maintains a bounded queue of configurable size with automatic oldest-sample eviction.

The widget inherits the full Qwt plotting infrastructure from QePlotBase including zoom, pan, toolbar, line/scatter toggling, Y-axis scale selection, and PDF export.

../_images/qetrend_01.png

QeTrend widget

2.3.1.1. Constructor

The constructor signature is

QeTrend(parent: QWidget = None)

Parameters:

  • parent (QWidget, optional) – Parent widget.

Example:

# Any Qt Widget needs an Application running
from PySide6.QtWidgets import QApplication
app = QApplication([])
from cut.wdglib.widgets import QeTrend
trend = QeTrend()
trend.show()

2.3.1.2. Inheritance Hierarchy

QeTrend inherits from QePlotBase which encapsulates the shared Qwt plotting infrastructure:

hide empty members
hide empty attributes

class QeTrend
class QePlotBase
class QWidget <<PySide6.QtWidgets>>
class QObject <<PySide6.QtCore>>
class QPaintDevice <<PySide6.QtGui>>
class ShibokenObject <<Shiboken>>
class object

QeTrend --|> QePlotBase
QePlotBase --|> QWidget
QWidget --|> QObject
QObject --|> QPaintDevice
QPaintDevice --|> ShibokenObject
ShibokenObject --|> object

2.3.2. Methods

2.3.2.1. Data Loading

cut.wdglib.widgets.CreateOrAppendToTrend(value: float, timestamp: float, curveName: str = '') None

Append a single (value, timestamp) data point to a named curve. If the curve name is empty, one is auto-generated as "Curve N" based on the current curve count. If a curve with the given name already exists, the new point is appended to the existing curve. If it does not exist, a new curve is created.

Only the double overload is exposed in the Python bindings. Integer or other numeric types passed from Python are automatically converted to float by Python before being passed to this method.

The X axis displays human-readable date-time labels derived from the UTC epoch timestamp. If appending a point causes the curve to exceed the current queue size, the oldest samples are automatically dropped.

Parameters:
  • value – The sample value (plotted on the Y axis).

  • timestamp – POSIX UTC epoch timestamp (seconds since Jan 1, 1970).

  • curveName – Optional curve identifier. Auto-generated if empty.

Example:

import time
from cut.wdglib.widgets import QeTrend

trend = QeTrend()
now = time.time()

# Plot points on a curve named "sine"
for i in range(100):
    value = math.sin(i * 0.1)
    trend.CreateOrAppendToTrend(value, now + i, "sine")
cut.wdglib.widgets.CreateOrAppendToMemberVectors(value: float, timestamp: float, curveName: str = '') None

Exposed protected method that performs the same core logic as CreateOrAppendToTrend: appends a (timestamp, value) pair to the internal data vectors for a named curve, trims by queue size, and replots. This method is not intended for direct use; it is the internal implementation that the typed C++ overloads delegate to.

Parameters:
  • value – The sample value.

  • timestamp – POSIX UTC epoch timestamp.

  • curveName – Optional curve identifier.

cut.wdglib.widgets.ClearCurves() None

Remove all curves from the plot, clear the internal curve map, and release the timestamp/value data vectors for each curve. This overrides the base QePlotBase::ClearCurves() to ensure the trend-specific data structures are also cleaned up.

2.3.2.2. Queue Management

cut.wdglib.widgets.GetQueueSize() int

Return the maximum number of points kept per curve. When a new point is appended and this limit is exceeded, the oldest point is dropped.

Returns:

Current queue size limit (default: 1000).

cut.wdglib.widgets.SetQueueSize(newQueueSize: int) None

Set the maximum number of points per curve. The value must be >= 1. If the new size is smaller than the current data size for any curve, the oldest samples are trimmed immediately. Emits the QueueSizeChanged signal.

Parameters:

newQueueSize – New queue size limit.

cut.wdglib.widgets.ResetQueueSize() None

Reset the queue size to the default value of 1000.

2.3.2.3. Toolbar Management

cut.wdglib.widgets.ToggleToolbar() None

Toggle the visibility of the plot toolbar.

cut.wdglib.widgets.SetMainWindowForToolbar(window: PySide6.QtWidgets.QMainWindow) None

Assign the owning QMainWindow so the toolbar can be docked into it.

Parameters:

window – The QMainWindow that will host the plot toolbar.

2.3.2.4. Zoom Controls

cut.wdglib.widgets.on_actionQePlotZoom_In_triggered() None

Zoom in on the plot canvas by a factor of 0.75.

cut.wdglib.widgets.on_actionQePlotZoom_Out_triggered() None

Zoom out on the plot canvas by a factor of 1/0.75.

cut.wdglib.widgets.on_actionQePlotZoom_Reset_triggered() None

Reset zoom level and re-enable auto-scaling on both axes.

2.3.2.5. Plot Style

cut.wdglib.widgets.on_actionLine_Plot_triggered(checked: bool) None

Toggle line plot mode. True connects points with lines; False switches to scatter mode.

cut.wdglib.widgets.on_actionScatter_Plot_triggered(checked: bool) None

Toggle scatter plot mode. True shows point markers only; False switches to line mode.

2.3.2.6. Y-Axis Scale

cut.wdglib.widgets.on_actionYAxis_Linear_triggered(checked: bool) None

Switch the Y-axis to linear scale.

cut.wdglib.widgets.on_actionYAxis_Log_triggered(checked: bool) None

Switch the Y-axis to logarithmic scale. Forces scatter mode and disables the line plot toggle.

cut.wdglib.widgets.on_actionYAxis_Square_Root_triggered(checked: bool) None

Stub for square-root Y-axis scale. Not yet implemented.

2.3.2.7. PDF Export

cut.wdglib.widgets.on_actionExport_triggered() None

Export the current plot to a PDF file in the user’s home directory with a UTC timestamp in the filename.

2.3.3. Protected Event Handlers

These methods are called by Qt during widget lifecycle events.

cut.wdglib.widgets.showEvent(event: PySide6.QtGui.QShowEvent) None

Called when the widget becomes visible. Attaches the toolbar to the owning QMainWindow.

cut.wdglib.widgets.hideEvent(event: PySide6.QtGui.QHideEvent) None

Called when the widget is hidden. Detaches the toolbar from the owning QMainWindow.

2.3.4. Exposed Member Variables

  • m_color_list (list[QColor]) – Read-only palette of 7 colors assigned to curves in rotation. Inherited from QePlotBase.

2.3.5. Signals

QeTrend defines one custom signal beyond the standard Qt signals inherited from QWidget and QObject:

cut.wdglib.widgets.QueueSizeChanged(value: int)

Emitted when the queue size is changed via SetQueueSize() or ResetQueueSize().

Parameters:

value – The new queue size (maximum points per curve).

Example:

trend.QueueSizeChanged.connect(lambda v: print(f"Queue size: {v}"))
trend.SetQueueSize(500)  # prints "Queue size: 500"

The standard Qt signals are also available:

  • customContextMenuRequested(pos: QPoint)

  • destroyed(obj: object = None)

  • objectNameChanged(name: str)

  • windowTitleChanged(title: str)

  • windowIconChanged(icon: QIcon)

  • windowIconTextChanged(text: str)

2.3.6. QeTypes Enum

The QeTypes enum is importable from cut.common.pyb. While CreateOrAppendToTrend accepts only double values in Python, the internal C++ overloads for other types use this enum for type identification.

QeTypes.Int8
QeTypes.Int16
QeTypes.Int32
QeTypes.Int64
QeTypes.UInt8
QeTypes.UInt16
QeTypes.UInt32
QeTypes.UInt64
QeTypes.Float
QeTypes.Double
QeTypes.String
QeTypes.Boolean
QeTypes.NotSet

2.3.7. Example Usage

Basic time-series plotting:

import time, math
from cut.wdglib.widgets import QeTrend
from PySide6.QtWidgets import QApplication

app = QApplication([])
trend = QeTrend()

now = time.time()
for i in range(200):
    value = math.sin(i * 0.05) * math.exp(-i / 200.0)
    trend.CreateOrAppendToTrend(value, now + i * 0.1, "damped_sine")

trend.ToggleToolbar()
trend.show()
app.exec()

Multiple curves:

import time, math
from cut.wdglib.widgets import QeTrend

trend = QeTrend()
now = time.time()

for i in range(100):
    t = now + i * 0.1
    trend.CreateOrAppendToTrend(math.sin(i * 0.1), t, "sine")
    trend.CreateOrAppendToTrend(math.cos(i * 0.1), t, "cosine")

# Configure display
trend.on_actionLine_Plot_triggered(True)
trend.QueueSizeChanged.connect(lambda v: print(f"Queue size: {v}"))

Queue size management:

from cut.wdglib.widgets import QeTrend

trend = QeTrend()
print(f"Default queue size: {trend.GetQueueSize()}")  # 1000

trend.SetQueueSize(500)
# Now each curve will hold at most 500 points

trend.ResetQueueSize()
# Back to 1000

Stream simulation:

import time, math
from cut.wdglib.widgets import QeTrend

trend = QeTrend()
trend.SetQueueSize(100)  # Keep only last 100 points

for i in range(500):
    trend.CreateOrAppendToTrend(
        value=math.sin(i * 0.2) + (i * 0.001),
        timestamp=time.time() + i * 0.01,
        curveName="sensor_1"
    )
# After 500 appends, only the last 100 points are retained

2.3.8. Notes

  • Only the double overload of CreateOrAppendToTrend is exposed in Python. The C++ header declares 11 overloads (one for each numeric type), but all except double are removed from the Python bindings. The comment in the typesystem XML explains: “Internally, QwtPlot accepts only doubles so a conversion would have been done later.” Integer values passed from Python are automatically promoted to float by Python.

  • The X axis uses UTC epoch timestamps with QwtDateScaleDraw, displaying human-readable date labels at millisecond/second/minute/hour granularity.

  • No setNumpyNdArray() convenience method is provided for QeTrend, unlike QePlot, QeMatrixView, and QeVectorView. This is intentional: the trending API uses discrete point-by-point appending with timestamps rather than bulk array loading.

  • When SetQueueSize(n) is called with n smaller than the current data size, the oldest samples are immediately dropped from all curves.

  • Curves are auto-named as "Curve 0" , "Curve 1", etc. if no name is provided. Providing the same name on subsequent calls appends to the existing curve rather than creating a new one.

  • The widget does not support multi-curve bulk plotting from Python. Each call to CreateOrAppendToTrend appends exactly one data point.

  • The toolbar is hidden by default. Call ToggleToolbar() to show it.

  • PDF export writes to ~/qeplot-<timestamp>.pdf. There is no way to override the output path from Python.

  • Mouse interaction is built-in: left-click drag for panning, mouse wheel for zooming, with coordinate tracking shown as a tracker tooltip.

  • The Y-axis logarithmic scale forces scatter mode and disables line plot.

  • The square-root Y-axis scale (on_actionYAxis_Square_Root_triggered) is a stub with no implementation.