1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.xycharts
.linecharts
;
15 import java
.util
.LinkedHashMap
;
17 import java
.util
.Map
.Entry
;
19 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
20 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
21 import org
.eclipse
.swt
.SWT
;
22 import org
.eclipse
.swt
.widgets
.Composite
;
23 import org
.eclipse
.swt
.widgets
.Display
;
24 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
25 import org
.eclipse
.tracecompass
.tmf
.ui
.TmfUiRefreshHandler
;
26 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.xycharts
.TmfChartTimeStampFormat
;
27 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.xycharts
.TmfXYChartViewer
;
28 import org
.swtchart
.IAxisTick
;
29 import org
.swtchart
.ILineSeries
;
30 import org
.swtchart
.ILineSeries
.PlotSymbolType
;
31 import org
.swtchart
.ISeries
;
32 import org
.swtchart
.ISeries
.SeriesType
;
33 import org
.swtchart
.ISeriesSet
;
34 import org
.swtchart
.LineStyle
;
35 import org
.swtchart
.Range
;
38 * Abstract line chart viewer class implementation. All series in this viewer
39 * use the same X axis values. They are automatically created as values are
40 * provided for a key. Series by default will be displayed as a line. Each
41 * series appearance can be overridden when creating it.
43 * @author - Geneviève Bastien
45 public abstract class TmfCommonXLineChartViewer
extends TmfXYChartViewer
{
47 private static final double DEFAULT_MAXY
= Double
.MIN_VALUE
;
48 private static final double DEFAULT_MINY
= Double
.MAX_VALUE
;
50 /* The desired number of points per pixel */
51 private static final double RESOLUTION
= 1.0;
53 private static final int[] LINE_COLORS
= { SWT
.COLOR_BLUE
, SWT
.COLOR_RED
, SWT
.COLOR_GREEN
,
54 SWT
.COLOR_MAGENTA
, SWT
.COLOR_CYAN
,
55 SWT
.COLOR_DARK_BLUE
, SWT
.COLOR_DARK_RED
, SWT
.COLOR_DARK_GREEN
,
56 SWT
.COLOR_DARK_MAGENTA
, SWT
.COLOR_DARK_CYAN
, SWT
.COLOR_DARK_YELLOW
,
57 SWT
.COLOR_BLACK
, SWT
.COLOR_GRAY
};
58 private static final LineStyle
[] LINE_STYLES
= { LineStyle
.SOLID
, LineStyle
.DASH
, LineStyle
.DOT
, LineStyle
.DASHDOT
};
60 private final Map
<String
, double[]> fSeriesValues
= new LinkedHashMap
<>();
61 private double[] fXValues
;
62 private double fResolution
;
64 private UpdateThread fUpdateThread
;
70 * The parent composite
72 * The title of the viewer
74 * The label of the xAxis
76 * The label of the yAXIS
78 public TmfCommonXLineChartViewer(Composite parent
, String title
, String xLabel
, String yLabel
) {
79 super(parent
, title
, xLabel
, yLabel
);
81 setResolution(RESOLUTION
);
82 setTooltipProvider(new TmfCommonXLineChartTooltipProvider(this));
86 * Set the number of requests per pixel that should be done on this chart
89 * The number of points per pixels
91 protected void setResolution(double resolution
) {
92 fResolution
= resolution
;
96 public void loadTrace(ITmfTrace trace
) {
97 super.loadTrace(trace
);
102 * Forces a reinitialization of the data sources, even if it has already
103 * been initialized for this trace before
105 protected void reinitialize() {
106 fSeriesValues
.clear();
107 Thread thread
= new Thread() {
110 initializeDataSource();
111 TmfUiRefreshHandler
.getInstance().queueUpdate(TmfCommonXLineChartViewer
.this,
115 if (!getSwtChart().isDisposed()) {
116 /* Delete the old series */
128 * Initialize the source of the data for this viewer. This method is run in
129 * a separate thread, so this is where for example one can execute an
130 * analysis module and wait for its completion to initialize the series
132 protected void initializeDataSource() {
136 private class UpdateThread
extends Thread
{
137 private final IProgressMonitor fMonitor
;
138 private final int fNumRequests
;
140 public UpdateThread(int numRequests
) {
141 super("Line chart update"); //$NON-NLS-1$
142 fNumRequests
= numRequests
;
143 fMonitor
= new NullProgressMonitor();
148 updateData(getWindowStartTime(), getWindowEndTime(), fNumRequests
, fMonitor
);
149 updateThreadFinished(this);
152 public void cancel() {
153 fMonitor
.setCanceled(true);
157 private synchronized void newUpdateThread() {
159 final int numRequests
= (int) (getSwtChart().getPlotArea().getBounds().width
* fResolution
);
160 fUpdateThread
= new UpdateThread(numRequests
);
161 fUpdateThread
.start();
164 private synchronized void updateThreadFinished(UpdateThread thread
) {
165 if (thread
== fUpdateThread
) {
166 fUpdateThread
= null;
171 * Cancels the currently running update thread. It is automatically called
172 * when the content is updated, but child viewers may want to call it
173 * manually to do some operations before calling
174 * {@link TmfCommonXLineChartViewer#updateContent}
176 protected synchronized void cancelUpdate() {
177 if (fUpdateThread
!= null) {
178 fUpdateThread
.cancel();
183 protected void updateContent() {
184 getDisplay().asyncExec(new Runnable() {
193 * Convenience method to compute the values of the X axis for a given time
194 * range. This method will return nb values depending, equally separated
197 * The returned time values are in internal time, ie to get trace time, the
198 * time offset needs to be added to those values.
201 * The start time of the time range
203 * End time of the range
205 * The number of steps in the x axis.
206 * @return The time values (converted to double) to match every step.
208 protected final double[] getXAxis(long start
, long end
, int nb
) {
209 setTimeOffset(start
- 1);
211 double timestamps
[] = new double[nb
];
212 long steps
= (end
- start
);
213 double step
= steps
/ (double) nb
;
216 for (int i
= 0; i
< nb
; i
++) {
217 timestamps
[i
] = curTime
;
224 * Set the values of the x axis. There is only one array of values for the x
225 * axis for all series of a line chart so it needs to be set once here.
228 * The values for the x axis. The values must be in internal
229 * time, ie time offset have been subtracted from trace time
232 protected final void setXAxis(double[] xaxis
) {
237 * Update the series data because the time range has changed. The x axis
238 * values for this data update can be computed using the
239 * {@link TmfCommonXLineChartViewer#getXAxis(long, long, int)} method which
240 * will return a list of uniformely separated time values.
242 * Each series values should be set by calling the
243 * {@link TmfCommonXLineChartViewer#setSeries(String, double[])}.
245 * This method is responsible for calling the
246 * {@link TmfCommonXLineChartViewer#updateDisplay()} when needed for the new
247 * values to be displayed.
250 * The start time of the range for which the get the data
252 * The end time of the range
254 * The number of 'points' in the chart.
256 * The progress monitor object
258 protected abstract void updateData(long start
, long end
, int nb
, IProgressMonitor monitor
);
261 * Set the data for a given series of the graph. The series does not need to
262 * be created before calling this, but it needs to have at least as many
263 * values as the x axis.
265 * If the series does not exist, it will automatically be created at display
266 * time, with the default values.
269 * The name of the series for which to set the values
270 * @param seriesValues
271 * The array of values for the series
273 protected void setSeries(String seriesName
, double[] seriesValues
) {
274 if (fXValues
.length
> seriesValues
.length
) {
275 throw new IllegalStateException();
277 fSeriesValues
.put(seriesName
, seriesValues
);
281 * Add a new series to the XY line chart. By default, it is a simple solid
285 * The name of the series to create
286 * @return The series so that the concrete viewer can modify its properties
289 protected ILineSeries
addSeries(String seriesName
) {
290 ISeriesSet seriesSet
= getSwtChart().getSeriesSet();
291 int seriesCount
= seriesSet
.getSeries().length
;
292 ILineSeries series
= (ILineSeries
) seriesSet
.createSeries(SeriesType
.LINE
, seriesName
);
293 series
.setVisible(true);
294 series
.enableArea(false);
295 series
.setLineStyle(LINE_STYLES
[(seriesCount
/ (LINE_COLORS
.length
)) % LINE_STYLES
.length
]);
296 series
.setSymbolType(PlotSymbolType
.NONE
);
297 series
.setLineColor(Display
.getDefault().getSystemColor(LINE_COLORS
[seriesCount
% LINE_COLORS
.length
]));
302 * Delete a series from the chart and its values from the viewer.
305 * Name of the series to delete
307 protected void deleteSeries(String seriesName
) {
308 ISeries series
= getSwtChart().getSeriesSet().getSeries(seriesName
);
309 if (series
!= null) {
310 getSwtChart().getSeriesSet().deleteSeries(series
.getId());
312 fSeriesValues
.remove(seriesName
);
316 * Update the chart's values before refreshing the viewer
318 protected void updateDisplay() {
319 Display
.getDefault().asyncExec(new Runnable() {
320 final TmfChartTimeStampFormat tmfChartTimeStampFormat
= new TmfChartTimeStampFormat(getTimeOffset());
324 if (!getSwtChart().isDisposed()) {
325 double maxy
= DEFAULT_MAXY
;
326 double miny
= DEFAULT_MINY
;
327 for (Entry
<String
, double[]> entry
: fSeriesValues
.entrySet()) {
328 ILineSeries series
= (ILineSeries
) getSwtChart().getSeriesSet().getSeries(entry
.getKey());
329 if (series
== null) {
330 series
= addSeries(entry
.getKey());
332 series
.setXSeries(fXValues
);
333 /* Find the minimal and maximum values in this series */
334 for (double value
: entry
.getValue()) {
335 maxy
= Math
.max(maxy
, value
);
336 miny
= Math
.min(miny
, value
);
338 series
.setYSeries(entry
.getValue());
340 if (maxy
== DEFAULT_MAXY
) {
344 IAxisTick xTick
= getSwtChart().getAxisSet().getXAxis(0).getTick();
345 xTick
.setFormat(tmfChartTimeStampFormat
);
347 final double start
= fXValues
[0];
348 int lastX
= fXValues
.length
- 1;
349 double end
= (start
== fXValues
[lastX
]) ? start
+ 1 : fXValues
[lastX
];
350 getSwtChart().getAxisSet().getXAxis(0).setRange(new Range(start
, end
));
351 getSwtChart().getAxisSet().getXAxis(0).adjustRange();
353 getSwtChart().getAxisSet().getYAxis(0).setRange(new Range(miny
, maxy
));
355 getSwtChart().redraw();
362 * Create the series once the initialization of the viewer's data source is
363 * done. Series do not need to be created before setting their values, but
364 * if their appearance needs to be customized, this method is a good place
365 * to do so. It is called only once per trace.
367 protected void createSeries() {