The creation of the chart is shown below. We create a line graph and set the vertical limits of the plot between -1 and 1, which is the range of the data values. In the horizontal direction we don't set any limits so that auto-scaling is used. We set resource XmNxAutoRangeMode to XintUSE_MIN_MAX so that the chart does not round-up the starting time and ending time limits for the horizontal axis.
chart_geometry.x1 = 0.0;
chart_geometry.y1 = 0.0;
chart_geometry.x2 = 100.0;
chart_geometry.y2 = 100.0;
chart = (Object)XtVaCreateWidget("chart",
(WidgetClass)xintChartObjectClass,
edit,
XmNchartTitle, "Real Time Example\nwith Data Series",
XmNgeometry, &chart_geometry,
XmNchartType, XintCHART_TYPE_LINE,
XmNfillStyle, XintFILL_NONE,
NULL);
y_limits.minimum = -1;
y_limits.maximum = 1;
XtVaSetValues((Widget)
XintChartGetComponent(chart, XintCHART_COMPONENT_PLOT),
XmNxAutoRangeMode, XintUSE_MIN_MAX,
XmNyLimits, &y_limits,
NULL);
For the data, we will use DataSeries objects because the data points can arrive at non constant intervals. We first create a DataGroup object since we want to display multiple curves. We also create a DataLabel object that will be used to display the time values on the horizontal axis. All the data objects are created with no data initially.
data_group = (Object)XtVaCreateWidget("data_group",
(WidgetClass)xintDataGroupObjectClass,
edit, NULL);
/*
* We use DataSeries to be able to position samples exactly at times
* where it is generated. Start with no data.
*/
for (i = 0; i < num_sets; i++)
XtVaCreateWidget("set", (WidgetClass)xintDataSeriesObjectClass,
edit,
XmNcount, 0,
XmNdataType, XintDATA_TYPE_FLOAT,
XmNdataGroup, data_group,
NULL);
/*
* Add empty Data Label object to handle the time labels
*/
XtVaCreateWidget("label", (WidgetClass)xintDataLabelObjectClass,
edit,
XmNlabelCount, 0,
XmNdataGroup, data_group,
NULL);
XintChartAssociateData(chart, data_group);
The update of the data is done as follows. We first update the data series using function XintDataSeriesExtend if the number of inserted points is less that MAX_POINT and XintDataSeriesShift otherwise. We use function XintDataListIterate to retrieve the ID of the data series from the data group. The update of the data label is done in a similar fashion. We only generate an annotation every 10 points so that labels don't overlap on the horizontal axis. Note that we pass the label position (time value) along with the label string to the data label update functions. Finally, the whole update sequence is surrounded by calls to XintDataBatchUpdate so that only one redraw gets generated from all the changes we have made to the data.
XintDataBatchUpdate(data_group, True);
for (i=0; i < nseries; i++) {
data_series = XintDataListIterate(&data_group, 1,
xintDataSeriesObjectClass, i);
if (points_inserted < MAX_POINT)
XintDataSeriesExtend(data_series, &float_time, &y[i], 1);
else
XintDataSeriesShift(data_series, &float_time, &y[i], 1);
}
/*
* Upate the labels (generate a label only every 10th value)
*/
if (points_inserted % 10 == 0) {
data_label = XintDataListIterate(&data_group, 1,
xintDataLabelObjectClass, 0);
sprintf(string_buffer, "%3.1f", float_time);
str_array[0] = string_buffer;
if (points_inserted < MAX_POINT)
XintDataLabelExtend(data_label, str_array, &float_time, 1);
else
XintDataLabelShift(data_label, str_array, &float_time, 1);
}
XintDataBatchUpdate(data_group, False);