aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeneviève Bastien2013-04-24 08:35:01 (EDT)
committerGenevieve Bastien2014-01-16 09:22:33 (EST)
commitf8db3aba7ad8903dde2a522445e586b35b66d875 (patch)
tree2293fd281306be3cd704f96c8881492dc611bb4e
parentc37c12a9a72f04cef68eb8a61ce1a255a218cf9a (diff)
downloadorg.eclipse.linuxtools-f8db3aba7ad8903dde2a522445e586b35b66d875.zip
org.eclipse.linuxtools-f8db3aba7ad8903dde2a522445e586b35b66d875.tar.gz
org.eclipse.linuxtools-f8db3aba7ad8903dde2a522445e586b35b66d875.tar.bz2
TMF: Add abstract XY line chart viewer on top of SWT chartrefs/changes/42/20342/5
* Provides a viewer for line charts. With this viewer, all series will use the same X axis data. * It automatically creates series as values are provided for a key. * A tooltip shows the values for each series at the selected x position. * Series can also be manually added and the returned series can be modified, so while "line chart" is the default representation, it can be used in other ways. Change-Id: I90b63e1dc87a1dbd49d37ff99fb22e04eecad2be Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net> Reviewed-on: https://git.eclipse.org/r/20342 Reviewed-by: Alexandre Montplaisir <alexmonthy@voxpopuli.im> Tested-by: Hudson CI Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com> IP-Clean: Bernd Hufmann <bernd.hufmann@ericsson.com> Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
-rw-r--r--lttng/org.eclipse.linuxtools.tmf.ui/META-INF/MANIFEST.MF1
-rw-r--r--lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/xycharts/linecharts/TmfCommonXLineChartTooltipProvider.java128
-rw-r--r--lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/xycharts/linecharts/TmfCommonXLineChartViewer.java294
3 files changed, 423 insertions, 0 deletions
diff --git a/lttng/org.eclipse.linuxtools.tmf.ui/META-INF/MANIFEST.MF b/lttng/org.eclipse.linuxtools.tmf.ui/META-INF/MANIFEST.MF
index 124fb87..600c51f 100644
--- a/lttng/org.eclipse.linuxtools.tmf.ui/META-INF/MANIFEST.MF
+++ b/lttng/org.eclipse.linuxtools.tmf.ui/META-INF/MANIFEST.MF
@@ -41,6 +41,7 @@ Export-Package: org.eclipse.linuxtools.internal.tmf.ui;x-friends:="org.eclipse.l
org.eclipse.linuxtools.tmf.ui.viewers.statistics.model,
org.eclipse.linuxtools.tmf.ui.viewers.xycharts;x-internal:=true,
org.eclipse.linuxtools.tmf.ui.viewers.xycharts.barcharts;x-internal:=true,
+ org.eclipse.linuxtools.tmf.ui.viewers.xycharts.linecharts;x-internal:=true,
org.eclipse.linuxtools.tmf.ui.views,
org.eclipse.linuxtools.tmf.ui.views.callstack,
org.eclipse.linuxtools.tmf.ui.views.colors,
diff --git a/lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/xycharts/linecharts/TmfCommonXLineChartTooltipProvider.java b/lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/xycharts/linecharts/TmfCommonXLineChartTooltipProvider.java
new file mode 100644
index 0000000..fe44c5d
--- /dev/null
+++ b/lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/xycharts/linecharts/TmfCommonXLineChartTooltipProvider.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2014 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Geneviève Bastien - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.viewers.xycharts.linecharts;
+
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
+import org.eclipse.linuxtools.tmf.ui.viewers.xycharts.ITmfChartTimeProvider;
+import org.eclipse.linuxtools.tmf.ui.viewers.xycharts.TmfBaseProvider;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.swtchart.IAxis;
+import org.swtchart.ISeries;
+
+/**
+ * Displays a tooltip on line charts. For each series, it shows the y value at
+ * the selected x value. This tooltip assumes that all series share a common set
+ * of X axis values. If the X series is not common, the tooltip text may not be
+ * accurate.
+ *
+ * @author Geneviève Bastien
+ */
+public class TmfCommonXLineChartTooltipProvider extends TmfBaseProvider implements MouseTrackListener {
+
+ /**
+ * Constructor for the tooltip provider
+ *
+ * @param tmfChartViewer
+ * The parent chart viewer
+ */
+ public TmfCommonXLineChartTooltipProvider(ITmfChartTimeProvider tmfChartViewer) {
+ super(tmfChartViewer);
+ register();
+ }
+
+ // ------------------------------------------------------------------------
+ // TmfBaseProvider
+ // ------------------------------------------------------------------------
+
+ @Override
+ public void register() {
+ getChart().getPlotArea().addMouseTrackListener(this);
+ }
+
+ @Override
+ public void deregister() {
+ if ((getChartViewer().getControl() != null) && !getChartViewer().getControl().isDisposed()) {
+ getChart().getPlotArea().removeMouseTrackListener(this);
+ }
+ }
+
+ @Override
+ public void refresh() {
+ // nothing to do
+ }
+
+ // ------------------------------------------------------------------------
+ // MouseTrackListener
+ // ------------------------------------------------------------------------
+
+ @Override
+ public void mouseEnter(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExit(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseHover(MouseEvent e) {
+ if (getChartViewer().getWindowDuration() != 0) {
+ IAxis xAxis = getChart().getAxisSet().getXAxis(0);
+
+ double xCoordinate = xAxis.getDataCoordinate(e.x);
+
+ ISeries[] series = getChart().getSeriesSet().getSeries();
+
+ if ((xCoordinate < 0) || (series.length == 0)) {
+ return;
+ }
+
+ /* Find the index of the value we want */
+ double[] xS = series[0].getXSeries();
+ if (xS == null) {
+ return;
+ }
+ int index = 0;
+ for (int i = 0; i < xS.length; i++) {
+ if (xS[i] > xCoordinate) {
+ break;
+ }
+ index = i;
+ }
+
+ /* set tooltip of closest data point */
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("time="); //$NON-NLS-1$
+ buffer.append(new TmfTimestamp((long) xCoordinate + getChartViewer().getTimeOffset(), ITmfTimestamp.NANOSECOND_SCALE).toString());
+ buffer.append('\n');
+
+ /* For each series, get the value at the index */
+ for (ISeries serie : series) {
+ double[] yS = serie.getYSeries();
+ /* Make sure the series values and the value at index exist */
+ if (yS == null || yS.length <= index) {
+ continue;
+ }
+ buffer.append(serie.getId());
+ buffer.append('=');
+ buffer.append(yS[index]);
+ buffer.append('\n');
+ }
+
+ getChart().getPlotArea().setToolTipText(buffer.toString());
+ getChart().redraw();
+ }
+ }
+
+}
diff --git a/lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/xycharts/linecharts/TmfCommonXLineChartViewer.java b/lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/xycharts/linecharts/TmfCommonXLineChartViewer.java
new file mode 100644
index 0000000..c9545c8
--- /dev/null
+++ b/lttng/org.eclipse.linuxtools.tmf.ui/src/org/eclipse/linuxtools/tmf/ui/viewers/xycharts/linecharts/TmfCommonXLineChartViewer.java
@@ -0,0 +1,294 @@
+/*******************************************************************************
+ * Copyright (c) 2014 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Geneviève Bastien - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.viewers.xycharts.linecharts;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+import org.eclipse.linuxtools.tmf.ui.viewers.xycharts.TmfChartTimeStampFormat;
+import org.eclipse.linuxtools.tmf.ui.viewers.xycharts.TmfXYChartViewer;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.swtchart.IAxisTick;
+import org.swtchart.ILineSeries;
+import org.swtchart.ILineSeries.PlotSymbolType;
+import org.swtchart.ISeries;
+import org.swtchart.ISeries.SeriesType;
+import org.swtchart.LineStyle;
+import org.swtchart.Range;
+
+/**
+ * Abstract line chart viewer class implementation. All series in this viewer
+ * use the same X axis values. They are automatically created as values are
+ * provided for a key. Series by default will be displayed as a line. Each
+ * series appearance can be overridden when creating it.
+ *
+ * @author - Geneviève Bastien
+ * @since 3.0
+ */
+public abstract class TmfCommonXLineChartViewer extends TmfXYChartViewer {
+
+ private static final double DEFAULT_MAXY = Double.MIN_VALUE;
+ private static final double DEFAULT_MINY = Double.MAX_VALUE;
+
+ /* The desired number of points per pixel */
+ private static final double RESOLUTION = 1.0;
+
+ private final Map<String, double[]> fSeriesValues = new LinkedHashMap<>();
+ private double[] fXValues;
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * The parent composite
+ * @param title
+ * The title of the viewer
+ * @param xLabel
+ * The label of the xAxis
+ * @param yLabel
+ * The label of the yAXIS
+ */
+ public TmfCommonXLineChartViewer(Composite parent, String title, String xLabel, String yLabel) {
+ super(parent, title, xLabel, yLabel);
+
+ setTooltipProvider(new TmfCommonXLineChartTooltipProvider(this));
+ }
+
+ @Override
+ public void loadTrace(ITmfTrace trace) {
+ super.loadTrace(trace);
+ fSeriesValues.clear();
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ initializeDataSource();
+ Display.getDefault().asyncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ if (!getSwtChart().isDisposed()) {
+ /* Delete the old series */
+ clearContent();
+ createSeries();
+ }
+ }
+ });
+ }
+ };
+ thread.start();
+ }
+
+ /**
+ * Initialize the source of the data for this viewer. This method is run in
+ * a separate thread, so this is where for example one can execute an
+ * analysis module and wait for its completion to initialize the series
+ */
+ protected void initializeDataSource() {
+
+ }
+
+ @Override
+ protected void updateContent() {
+ getDisplay().asyncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ final int numRequests = (int) (getSwtChart().getPlotArea().getBounds().width * RESOLUTION);
+ Thread thread = new Thread("Line chart update") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ updateData(getWindowStartTime(), getWindowEndTime(), numRequests);
+ }
+ };
+ thread.start();
+ }
+ });
+ }
+
+ /**
+ * Convenience method to compute the values of the X axis for a given time
+ * range. This method will return nb values depending, equally separated
+ * from start to end.
+ *
+ * The returned time values are in internal time, ie to get trace time, the
+ * time offset needs to be added to those values.
+ *
+ * @param start
+ * The start time of the time range
+ * @param end
+ * End time of the range
+ * @param nb
+ * The number of steps in the x axis.
+ * @return The time values (converted to double) to match every step.
+ */
+ protected final double[] getXAxis(long start, long end, int nb) {
+ setTimeOffset(start - 1);
+
+ double timestamps[] = new double[nb];
+ long steps = (end - start);
+ double step = steps / (double) nb;
+
+ double curTime = 1;
+ for (int i = 0; i < nb; i++) {
+ timestamps[i] = curTime;
+ curTime += step;
+ }
+ return timestamps;
+ }
+
+ /**
+ * Set the values of the x axis. There is only one array of values for the x
+ * axis for all series of a line chart so it needs to be set once here.
+ *
+ * @param xaxis
+ * The values for the x axis. The values must be in internal
+ * time, ie time offset have been subtracted from trace time
+ * values.
+ */
+ protected final void setXAxis(double[] xaxis) {
+ fXValues = xaxis;
+ }
+
+ /**
+ * Update the series data because the time range has changed. The x axis
+ * values for this data update can be computed using the
+ * {@link TmfCommonXLineChartViewer#getXAxis(long, long, int)} method which will
+ * return a list of uniformely separated time values.
+ *
+ * Each series values should be set by calling the
+ * {@link TmfCommonXLineChartViewer#setSeries(String, double[])}.
+ *
+ * This method is responsible for calling the
+ * {@link TmfCommonXLineChartViewer#updateDisplay()} when needed for the new values
+ * to be displayed.
+ *
+ * @param start
+ * The start time of the range for which the get the data
+ * @param end
+ * The end time of the range
+ * @param nb
+ * The number of 'points' in the chart.
+ */
+ protected abstract void updateData(long start, long end, int nb);
+
+ /**
+ * Set the data for a given series of the graph. The series does not need to
+ * be created before calling this, but it needs to have at least as many
+ * values as the x axis.
+ *
+ * If the series does not exist, it will automatically be created at display
+ * time, with the default values.
+ *
+ * @param seriesName
+ * The name of the series for which to set the values
+ * @param seriesValues
+ * The array of values for the series
+ */
+ protected void setSeries(String seriesName, double[] seriesValues) {
+ if (fXValues.length > seriesValues.length) {
+ throw new IllegalStateException();
+ }
+ fSeriesValues.put(seriesName, seriesValues);
+ }
+
+ /**
+ * Add a new series to the XY line chart. By default, it is a simple solid
+ * line.
+ *
+ * TODO: This is where the color alternance and other defaults should be set
+ *
+ * @param seriesName
+ * The name of the series to create
+ * @return The series so that the concrete viewer can modify its properties
+ * if required
+ */
+ protected ILineSeries addSeries(String seriesName) {
+ ILineSeries series = (ILineSeries) getSwtChart().getSeriesSet().createSeries(SeriesType.LINE, seriesName);
+ series.setVisible(true);
+ series.enableArea(false);
+ series.setLineStyle(LineStyle.SOLID);
+ series.setSymbolType(PlotSymbolType.NONE);
+ return series;
+ }
+
+ /**
+ * Delete a series from the chart and its values from the viewer.
+ *
+ * @param seriesName
+ * Name of the series to delete
+ */
+ protected void deleteSeries(String seriesName) {
+ ISeries series = getSwtChart().getSeriesSet().getSeries(seriesName);
+ if (series != null) {
+ getSwtChart().getSeriesSet().deleteSeries(series.getId());
+ }
+ fSeriesValues.remove(seriesName);
+ }
+
+ /**
+ * Update the chart's values before refreshing the viewer
+ */
+ protected void updateDisplay() {
+ Display.getDefault().asyncExec(new Runnable() {
+ final TmfChartTimeStampFormat tmfChartTimeStampFormat = new TmfChartTimeStampFormat(getTimeOffset());
+
+ @Override
+ public void run() {
+ if (!getSwtChart().isDisposed()) {
+ double maxy = DEFAULT_MAXY;
+ double miny = DEFAULT_MINY;
+ for (Entry<String, double[]> entry : fSeriesValues.entrySet()) {
+ ILineSeries series = (ILineSeries) getSwtChart().getSeriesSet().getSeries(entry.getKey());
+ if (series == null) {
+ series = addSeries(entry.getKey());
+ }
+ series.setXSeries(fXValues);
+ /* Find the minimal and maximum values in this series */
+ for (double value : entry.getValue()) {
+ maxy = Math.max(maxy, value);
+ miny = Math.min(miny, value);
+ }
+ series.setYSeries(entry.getValue());
+ }
+
+ IAxisTick xTick = getSwtChart().getAxisSet().getXAxis(0).getTick();
+ xTick.setFormat(tmfChartTimeStampFormat);
+
+ final double start = fXValues[0];
+ int lastX = fXValues.length - 1;
+ double end = (start == fXValues[lastX]) ? start + 1 : fXValues[lastX];
+ getSwtChart().getAxisSet().getXAxis(0).setRange(new Range(start, end));
+ getSwtChart().getAxisSet().getXAxis(0).adjustRange();
+ if (maxy > miny) {
+ getSwtChart().getAxisSet().getYAxis(0).setRange(new Range(miny, maxy));
+ }
+ getSwtChart().redraw();
+ }
+ }
+ });
+ }
+
+ /**
+ * Create the series once the initialization of the viewer's data source is
+ * done. Series do not need to be created before setting their values, but
+ * if their appearance needs to be customized, this method is a good place
+ * to do so. It is called only once per trace.
+ */
+ protected void createSeries() {
+
+ }
+
+}