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