Wt examples  4.10.0
Loading...
Searching...
No Matches
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
30using namespace Wt;
31using namespace Wt::Chart;
32
33namespace {
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
115 WContainerWidget()
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
199 WContainerWidget()
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
295 WContainerWidget()
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:
331 chart->axis(Axis::X).setLocation(AxisValue::Zero);
332 chart->axis(Axis::Y).setLocation(AxisValue::Zero);
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));
349 config->setValueFill(FillRangeType::ZeroValue);
350}
351
353 WContainerWidget()
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
423 chart->setDisplayLabels(LabelOption::Outside | LabelOption::TextLabel | LabelOption::TextPercentage);
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)
ChartsExample()
Constructor.
virtual std::unique_ptr< WStandardItem > clone() const
Definition CsvUtil.C:17
virtual void setData(const cpp17::any &data, ItemDataRole role=ItemDataRole::User)
Definition CsvUtil.C:21
PieExample()
Creates the pie chart example.
ScatterPlotExample()
Creates the scatter plot example.
TimeSeriesExample()
Creates the time series scatter plot example.
void readFromCsv(std::istream &f, std::shared_ptr< WAbstractItemModel > model, int numRows, bool firstLineIsHeaders)
Definition CsvUtil.C:54