Wt examples  3.7.1
ChartsExample.C
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 
7 #include <math.h>
8 #include <fstream>
9 
10 #include "ChartsExample.h"
11 #include "ChartConfig.h"
12 #include "CsvUtil.h"
13 
14 #include <Wt/WApplication>
15 #include <Wt/WDate>
16 #include <Wt/WEnvironment>
17 #include <Wt/WItemDelegate>
18 #include <Wt/WStandardItemModel>
19 #include <Wt/WText>
20 
21 #include <Wt/WBorderLayout>
22 #include <Wt/WFitLayout>
23 
24 #include <Wt/WStandardItem>
25 #include <Wt/WTableView>
26 
27 #include <Wt/Chart/WCartesianChart>
28 #include <Wt/Chart/WPieChart>
29 
30 using namespace Wt;
31 using namespace Wt::Chart;
32 
33 namespace {
34 
35  /*
36  * A standard item which converts text edits to numbers
37  */
38  class NumericItem : public WStandardItem {
39  public:
40  virtual NumericItem *clone() const {
41  return new NumericItem();
42  }
43 
44  virtual void setData(const boost::any &data, int role = UserRole) {
45  boost::any dt;
46 
47  if (role == EditRole) {
48  std::string s = Wt::asString(data).toUTF8();
49  char *endptr;
50  double d = strtod(s.c_str(), &endptr);
51  if (*endptr == 0)
52  dt = boost::any(d);
53  else
54  dt = data;
55  }
56 
57  WStandardItem::setData(data, role);
58  }
59  };
60 
61  /*
62  * Reads a CSV file as an (editable) standard item model.
63  */
64  WAbstractItemModel *readCsvFile(const std::string &fname,
65  WContainerWidget *parent)
66  {
67  WStandardItemModel *model = new WStandardItemModel(0, 0, parent);
68  model->setItemPrototype(new NumericItem());
69  std::ifstream f(fname.c_str());
70 
71  if (f) {
72  readFromCsv(f, model);
73 
74  for (int row = 0; row < model->rowCount(); ++row)
75  for (int col = 0; col < model->columnCount(); ++col) {
76  model->item(row, col)->setFlags(ItemIsSelectable | ItemIsEditable);
77 
78  /*
79  Example of tool tips (disabled here because they are not updated
80  when editing data)
81  */
82 
83  /*
84  WString toolTip = asString(model->headerData(col)) + ": "
85  + asString(model->item(row, col)->data(DisplayRole), "%.f");
86  model->item(row, col)->setToolTip(toolTip);
87  */
88  }
89 
90  return model;
91  } else {
92  WString error(WString::tr("error-missing-data"));
93  error.arg(fname, UTF8);
94  new WText(error, parent);
95  return 0;
96  }
97  }
98 }
99 
101  : WContainerWidget(root)
102 {
103  new WText(WString::tr("introduction"), this);
104 
105  new CategoryExample(this);
106  new TimeSeriesExample(this);
107  new ScatterPlotExample(this);
108  new PieExample(this);
109 }
110 
112  WContainerWidget(parent)
113 {
114  new WText(WString::tr("category chart"), this);
115 
116  WAbstractItemModel *model
117  = readCsvFile(WApplication::appRoot() + "category.csv", this);
118 
119  if (!model)
120  return;
121 
122  // Show a view that allows editing of the model.
123  WContainerWidget *w = new WContainerWidget(this);
124  WTableView *table = new WTableView(w);
125 
126  table->setMargin(10, Top | Bottom);
127  table->setMargin(WLength::Auto, Left | Right);
128 
129  table->setModel(model);
130  table->setSortingEnabled(true);
131  table->setColumnResizeEnabled(true);
132  // table->setSelectionMode(ExtendedSelection);
133  table->setAlternatingRowColors(true);
134  table->setColumnAlignment(0, AlignCenter);
135  table->setHeaderAlignment(0, AlignCenter);
136  table->setRowHeight(22);
137 
138  // Editing does not really work without Ajax, it would require an
139  // additional button somewhere to confirm the edited value.
140  if (WApplication::instance()->environment().ajax()) {
141  table->resize(600, 20 + 5*22);
142  table->setEditTriggers(WAbstractItemView::SingleClicked);
143  } else {
144  table->resize(600, WLength::Auto);
145  table->setEditTriggers(WAbstractItemView::NoEditTrigger);
146  }
147 
148  // We use a single delegate for all items which rounds values to
149  // the closest integer value.
150  WItemDelegate *delegate = new WItemDelegate(this);
151  delegate->setTextFormat("%.f");
152  table->setItemDelegate(delegate);
153 
154  table->setColumnWidth(0, 80);
155  for (int i = 1; i < model->columnCount(); ++i)
156  table->setColumnWidth(i, 120);
157 
158  /*
159  * Create the category chart.
160  */
161  WCartesianChart *chart = new WCartesianChart(this);
162  chart->setModel(model); // set the model
163  chart->setXSeriesColumn(0); // set the column that holds the categories
164  chart->setLegendEnabled(true); // enable the legend
165  chart->setZoomEnabled(true);
166  chart->setPanEnabled(true);
167 
168  // Automatically layout chart (space for axes, legend, ...)
169  chart->setAutoLayoutEnabled(true);
170 
171  chart->setBackground(WColor(200,200,200));
172 
173  /*
174  * Add all (but first) column as bar series
175  */
176  for (int i = 1; i < model->columnCount(); ++i) {
177  WDataSeries *s = new WDataSeries(i, BarSeries);
178  s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
179  chart->addSeries(s);
180  }
181 
182  chart->resize(800, 400);
183 
184  chart->setMargin(10, Top | Bottom);
185  chart->setMargin(WLength::Auto, Left | Right);
186 
187  /*
188  * Provide a widget to manipulate chart properties
189  */
190  new ChartConfig(chart, this);
191 }
192 
194  WContainerWidget(parent)
195 {
196  new WText(WString::tr("scatter plot"), this);
197 
198  WAbstractItemModel *model = readCsvFile(
199  WApplication::appRoot() + "timeseries.csv", this);
200 
201  if (!model)
202  return;
203 
204  /*
205  * Parses the first column as dates, to be able to use a date scale
206  */
207  for (int i = 0; i < model->rowCount(); ++i) {
208  WString s = asString(model->data(i, 0));
209  WDate d = WDate::fromString(s, "dd/MM/yy");
210  model->setData(i, 0, d);
211  }
212 
213  // Show a view that allows editing of the model.
214  WContainerWidget *w = new WContainerWidget(this);
215  WTableView *table = new WTableView(w);
216 
217  table->setMargin(10, Top | Bottom);
218  table->setMargin(WLength::Auto, Left | Right);
219 
220  table->setModel(model);
221  table->setSortingEnabled(false); // Does not make much sense for time series
222  table->setColumnResizeEnabled(true);
223  table->setSelectionMode(NoSelection);
224  table->setAlternatingRowColors(true);
225  table->setColumnAlignment(0, AlignCenter);
226  table->setHeaderAlignment(0, AlignCenter);
227  table->setRowHeight(22);
228 
229  // Editing does not really work without Ajax, it would require an
230  // additional button somewhere to confirm the edited value.
231  if (WApplication::instance()->environment().ajax()) {
232  table->resize(800, 20 + 5*22);
233  table->setEditTriggers(WAbstractItemView::SingleClicked);
234  } else {
235  table->resize(800, 20 + 5*22 + 25);
236  table->setEditTriggers(WAbstractItemView::NoEditTrigger);
237  }
238 
239  WItemDelegate *delegate = new WItemDelegate(this);
240  delegate->setTextFormat("%.1f");
241  table->setItemDelegate(delegate);
242  table->setItemDelegateForColumn(0, new WItemDelegate(this));
243 
244  table->setColumnWidth(0, 80);
245  for (int i = 1; i < model->columnCount(); ++i)
246  table->setColumnWidth(i, 90);
247 
248  /*
249  * Create the scatter plot.
250  */
251  WCartesianChart *chart = new WCartesianChart(this);
252  //chart->setPreferredMethod(WPaintedWidget::PngImage);
253  //chart->setBackground(gray);
254  chart->setModel(model); // set the model
255  chart->setXSeriesColumn(0); // set the column that holds the X data
256  chart->setLegendEnabled(true); // enable the legend
257  chart->setZoomEnabled(true);
258  chart->setPanEnabled(true);
259 
260  chart->setType(ScatterPlot); // set type to ScatterPlot
261  chart->axis(XAxis).setScale(DateScale); // set scale of X axis to DateScale
262 
263  // Automatically layout chart (space for axes, legend, ...)
264  chart->setAutoLayoutEnabled();
265 
266  chart->setBackground(WColor(200,200,200));
267  /*
268  * Add first two columns as line series
269  */
270  for (int i = 1; i < 3; ++i) {
271  WDataSeries *s = new WDataSeries(i, LineSeries);
272  s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
273  chart->addSeries(s);
274  }
275 
276  chart->resize(800, 400); // WPaintedWidget must be given explicit size
277 
278  chart->setMargin(10, Top | Bottom); // add margin vertically
279  chart->setMargin(WLength::Auto, Left | Right); // center horizontally
280 
281  new ChartConfig(chart, this);
282 }
283 
285  WContainerWidget(parent)
286 {
287  new WText(WString::tr("scatter plot 2"), this);
288 
289  WStandardItemModel *model = new WStandardItemModel(40, 2, this);
290  model->setItemPrototype(new NumericItem());
291  model->setHeaderData(0, WString("X"));
292  model->setHeaderData(1, WString("Y = sin(X)"));
293 
294  for (unsigned i = 0; i < 40; ++i) {
295  double x = (static_cast<double>(i) - 20) / 4;
296 
297  model->setData(i, 0, x);
298  model->setData(i, 1, sin(x));
299  }
300 
301  /*
302  * Create the scatter plot.
303  */
304  WCartesianChart *chart = new WCartesianChart(this);
305  chart->setModel(model); // set the model
306  chart->setXSeriesColumn(0); // set the column that holds the X data
307  chart->setLegendEnabled(true); // enable the legend
308  chart->setZoomEnabled(true);
309  chart->setPanEnabled(true);
310  chart->setCrosshairEnabled(true);
311 
312  chart->setBackground(WColor(200,200,200));
313 
314  chart->setType(ScatterPlot); // set type to ScatterPlot
315 
316  // Typically, for mathematical functions, you want the axes to cross
317  // at the 0 mark:
318  chart->axis(XAxis).setLocation(ZeroValue);
319  chart->axis(YAxis).setLocation(ZeroValue);
320 
321  // Automatically layout chart (space for axes, legend, ...)
322  chart->setAutoLayoutEnabled();
323 
324  // Add the curves
325  WDataSeries *s = new WDataSeries(1, CurveSeries);
326  s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
327  chart->addSeries(s);
328 
329  chart->resize(800, 300); // WPaintedWidget must be given explicit size
330 
331  chart->setMargin(10, Top | Bottom); // add margin vertically
332  chart->setMargin(WLength::Auto, Left | Right); // center horizontally
333 
334  ChartConfig *config = new ChartConfig(chart, this);
335  config->setValueFill(ZeroValueFill);
336 }
337 
339  WContainerWidget(parent)
340 {
341  new WText(WString::tr("pie chart"), this);
342 
343  WStandardItemModel *model = new WStandardItemModel(this);
344  model->setItemPrototype(new NumericItem());
345 
346  //headers
347  model->insertColumns(model->columnCount(), 2);
348  model->setHeaderData(0, WString("Item"));
349  model->setHeaderData(1, WString("Sales"));
350 
351  //data
352  model->insertRows(model->rowCount(), 6);
353  int row = 0;
354  model->setData(row, 0, WString("Blueberry"));
355  model->setData(row, 1, 120);
356  // model->setData(row, 1, WString("Blueberry"), ToolTipRole);
357  row++;
358  model->setData(row, 0, WString("Cherry"));
359  model->setData(row, 1, 30);
360  row++;
361  model->setData(row, 0, WString("Apple"));
362  model->setData(row, 1, 260);
363  row++;
364  model->setData(row, 0, WString("Boston Cream"));
365  model->setData(row, 1, 160);
366  row++;
367  model->setData(row, 0, WString("Other"));
368  model->setData(row, 1, 40);
369  row++;
370  model->setData(row, 0, WString("Vanilla Cream"));
371  model->setData(row, 1, 120);
372  row++;
373 
374  //set all items to be editable and selectable
375  for (int row = 0; row < model->rowCount(); ++row)
376  for (int col = 0; col < model->columnCount(); ++col)
377  model->item(row, col)->setFlags(ItemIsSelectable | ItemIsEditable);
378 
379  WContainerWidget *w = new WContainerWidget(this);
380  WTableView* table = new WTableView(w);
381 
382  table->setMargin(10, Top | Bottom);
383  table->setMargin(WLength::Auto, Left | Right);
384  table->setSortingEnabled(true);
385  table->setModel(model);
386  table->setColumnWidth(1, 100);
387  table->setRowHeight(22);
388 
389  if (WApplication::instance()->environment().ajax()) {
390  table->resize(150 + 100 + 14, 20 + 6 * 22);
391  table->setEditTriggers(WAbstractItemView::SingleClicked);
392  } else {
393  table->resize(150 + 100 + 14, WLength::Auto);
394  table->setEditTriggers(WAbstractItemView::NoEditTrigger);
395  }
396 
397  /*
398  * Create the pie chart.
399  */
400  WPieChart *chart = new WPieChart(this);
401  chart->setModel(model); // set the model
402  chart->setLabelsColumn(0); // set the column that holds the labels
403  chart->setDataColumn(1); // set the column that holds the data
404 
405  // configure location and type of labels
407 
408  // enable a 3D and shadow effect
409  chart->setPerspectiveEnabled(true, 0.2);
410  chart->setShadowEnabled(true);
411 
412  // explode the first item
413  chart->setExplode(0, 0.3);
414 
415  chart->resize(800, 300); // WPaintedWidget must be given an explicit size
416 
417  chart->setMargin(10, Top | Bottom); // add margin vertically
418  chart->setMargin(WLength::Auto, Left | Right); // center horizontally
419 }
420 
void setExplode(int modelRow, double factor)
A Widget that demonstrates a scatter plot.
Definition: ChartsExample.h:49
void setTextFormat(const WString &format)
WStandardItem * item(int row, int column=0) const
ChartsExample(Wt::WContainerWidget *root)
Constructor.
virtual void resize(const WLength &width, const WLength &height) override
virtual bool insertColumns(int column, int count, const WModelIndex &parent=WModelIndex())
void setPanEnabled(bool pan=true)
void setLabelsColumn(int column)
void addSeries(std::unique_ptr< WDataSeries > series)
void setItemDelegateForColumn(int column, const std::shared_ptr< WAbstractItemDelegate > &delegate)
A Widget that demonstrates a Pie chart.
Definition: ChartsExample.h:59
void setLocation(AxisValue value)
WAxis & axis(Axis axis)
virtual bool insertRows(int row, int count, const WModelIndex &parent=WModelIndex())
void setDisplayLabels(WFlags< LabelOption > options)
void setShadow(const WShadow &shadow)
void setItemDelegate(const std::shared_ptr< WAbstractItemDelegate > &delegate)
virtual void setModel(const std::shared_ptr< WAbstractItemModel > &model) override
void setType(ChartType type)
void setSelectionMode(SelectionMode mode)
CategoryExample(Wt::WContainerWidget *parent)
Creates the category chart example.
virtual bool setData(const WModelIndex &index, const cpp17::any &value, ItemDataRole role=ItemDataRole::Edit)
virtual int rowCount(const WModelIndex &parent=WModelIndex()) const=0
virtual void setData(const cpp17::any &data, ItemDataRole role=ItemDataRole::User)
void setPerspectiveEnabled(bool enabled, double height=1.0)
void setCrosshairEnabled(bool crosshair=true)
std::string toUTF8() const
virtual void setMargin(const WLength &margin, WFlags< Side > sides=AllSides) override
void setZoomEnabled(bool zoom=true)
A Widget that demonstrates a category chart.
Definition: ChartsExample.h:39
A widget that demonstrates a times series chart.
Definition: ChartsExample.h:29
void setBackground(const WBrush &background)
virtual void resize(const WLength &width, const WLength &height) override
void setScale(AxisScale scale)
static WString tr(const char *key)
void setXSeriesColumn(int modelColumn)
TimeSeriesExample(Wt::WContainerWidget *parent)
Creates the time series scatter plot example.
WString asString(const cpp17::any &v, const WString &formatString=WString())
void setModel(const std::shared_ptr< WAbstractItemModel > &model)
void setValueFill(Wt::Chart::FillRangeType fill)
Definition: ChartConfig.C:385
virtual cpp17::any data(const WModelIndex &index, ItemDataRole role=ItemDataRole::Display) const=0
void setItemPrototype(std::unique_ptr< WStandardItem > item)
virtual int columnCount(const WModelIndex &parent=WModelIndex()) const=0
virtual void setAlternatingRowColors(bool enable) override
void setEditTriggers(WFlags< EditTrigger > editTriggers)
void setShadowEnabled(bool enabled)
void setColumnResizeEnabled(bool enabled)
virtual void setColumnWidth(int column, const WLength &width) override
void setAutoLayoutEnabled(bool enabled=true)
void setDataColumn(int modelColumn)
virtual void setHeaderAlignment(int column, WFlags< AlignmentFlag > alignment)
void setLegendEnabled(bool enabled)
void setSortingEnabled(bool enabled)
virtual bool setHeaderData(int section, Orientation orientation, const cpp17::any &value, ItemDataRole role=ItemDataRole::Edit)
virtual void setMargin(const WLength &margin, WFlags< Side > sides=AllSides) override
void setFlags(WFlags< ItemFlag > flags)
PieExample(Wt::WContainerWidget *parent)
Creates the pie chart example.
A class that allows configuration of a cartesian chart.
Definition: ChartConfig.h:40
void readFromCsv(std::istream &f, Wt::WAbstractItemModel *model, int numRows, bool firstLineIsHeaders)
Utility function that reads a model from a CSV file.
Definition: CsvUtil.C:56
virtual void setRowHeight(const WLength &rowHeight) override
ScatterPlotExample(Wt::WContainerWidget *parent)
Creates the scatter plot example.
virtual void setColumnAlignment(int column, AlignmentFlag alignment)

Generated on Tue Dec 15 2020 for the C++ Web Toolkit (Wt) by doxygen 1.8.13