/* * Copyright (C) 2008 Emweb bv, Herent, Belgium. * * See the LICENSE file for terms of use. */ #include #include #include "ChartsExample.h" #include "ChartConfig.h" #include "CsvUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Wt; using namespace Wt::Chart; namespace { /* * A standard item which converts text edits to numbers */ class NumericItem : public WStandardItem { public: virtual std::unique_ptr clone() const override { return std::make_unique(); } virtual void setData(const cpp17::any &data, ItemDataRole role = ItemDataRole::User) override { cpp17::any dt; if (role == ItemDataRole::Edit) { std::string s = asString(data).toUTF8(); char *endptr; double d = strtod(s.c_str(), &endptr); if (*endptr == 0) dt = cpp17::any(d); else dt = data; } WStandardItem::setData(data, role); } }; /* * Reads a CSV file as an (editable) standard item model. */ std::shared_ptr readCsvFile(const std::string &fname, WContainerWidget *parent) { std::shared_ptr model = std::make_shared(0, 0); std::unique_ptr prototype = std::make_unique(); model->setItemPrototype(std::move(prototype)); std::ifstream f(fname.c_str()); if (f) { readFromCsv(f, model.get()); for (int row = 0; row < model->rowCount(); ++row) for (int col = 0; col < model->columnCount(); ++col) { model->item(row, col)->setFlags(ItemFlag::Selectable | ItemFlag::Editable); /* Example of tool tips (disabled here because they are not updated when editing data) */ /* WString toolTip = asString(model->headerData(col)) + ": " + asString(model->item(row, col)->data(DisplayRole), "%.f"); model->item(row, col)->setToolTip(toolTip); */ } return model; } else { WString error(WString::tr("error-missing-data")); error.arg(fname, CharEncoding::UTF8); parent->addWidget(std::make_unique(error)); return 0; } } } ChartsExample::ChartsExample() : WContainerWidget() { this->addWidget(std::make_unique(WString::tr("introduction"))); this->addWidget(std::make_unique()); this->addWidget(std::make_unique()); this->addWidget(std::make_unique()); this->addWidget(std::make_unique()); } CategoryExample::CategoryExample(): WContainerWidget() { this->addWidget(std::make_unique(WString::tr("category chart"))); std::shared_ptr model = readCsvFile(WApplication::appRoot() + "category.csv", this); if (!model) return; // Show a view that allows editing of the model. auto *w = this->addWidget(std::make_unique()); auto *table = w->addWidget(std::make_unique()); table->setMargin(10, Side::Top | Side::Bottom); table->setMargin(WLength::Auto, Side::Left | Side::Right); table->setModel(model); table->setSortingEnabled(true); table->setColumnResizeEnabled(true); // table->setSelectionMode(SelectionMode::Extended); table->setAlternatingRowColors(true); table->setColumnAlignment(0, AlignmentFlag::Center); table->setHeaderAlignment(0, AlignmentFlag::Center); table->setRowHeight(22); // Editing does not really work without Ajax, it would require an // additional button somewhere to confirm the edited value. if (WApplication::instance()->environment().ajax()) { table->resize(600, 20 + 5*22); table->setEditTriggers(EditTrigger::SingleClicked); } else { table->resize(600, WLength::Auto); table->setEditTriggers(EditTrigger::None); } // We use a single delegate for all items which rounds values to // the closest integer value. std::shared_ptr delegate = std::make_shared(); delegate->setTextFormat("%.f"); table->setItemDelegate(delegate); table->setColumnWidth(0, 80); for (int i = 1; i < model->columnCount(); ++i) table->setColumnWidth(i, 120); /* * Create the category chart. */ WCartesianChart *chart = this->addWidget(std::make_unique()); chart->setModel(model); // set the model chart->setXSeriesColumn(0); // set the column that holds the categories chart->setLegendEnabled(true); // enable the legend chart->setZoomEnabled(true); chart->setPanEnabled(true); // Automatically layout chart (space for axes, legend, ...) chart->setAutoLayoutEnabled(true); chart->setBackground(WColor(200,200,200)); /* * Add all (but first) column as bar series */ for (int i = 1; i < model->columnCount(); ++i) { std::unique_ptr s = std::make_unique(i, SeriesType::Bar); s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3)); chart->addSeries(std::move(s)); } chart->resize(800, 400); chart->setMargin(10, Side::Top | Side::Bottom); chart->setMargin(WLength::Auto, Side::Left | Side::Right); /* * Provide a widget to manipulate chart properties */ this->addWidget(std::make_unique(chart)); } TimeSeriesExample::TimeSeriesExample(): WContainerWidget() { this->addWidget(std::make_unique(WString::tr("scatter plot"))); std::shared_ptr model = readCsvFile(WApplication::appRoot() + "timeseries.csv", this); if (!model) return; /* * Parses the first column as dates, to be able to use a date scale */ for (int i = 0; i < model->rowCount(); ++i) { WString s = asString(model->data(i, 0)); WDate d = WDate::fromString(s, "dd/MM/yy"); model->setData(i, 0, d); } // Show a view that allows editing of the model. auto *w = this->addWidget(std::make_unique()); auto *table = w->addWidget(std::make_unique()); table->setMargin(10, Side::Top | Side::Bottom); table->setMargin(WLength::Auto, Side::Left | Side::Right); table->setModel(model); table->setSortingEnabled(false); // Does not make much sense for time series table->setColumnResizeEnabled(true); table->setSelectionMode(SelectionMode::None); table->setAlternatingRowColors(true); table->setColumnAlignment(0, AlignmentFlag::Center); table->setHeaderAlignment(0, AlignmentFlag::Center); table->setRowHeight(22); // Editing does not really work without Ajax, it would require an // additional button somewhere to confirm the edited value. if (WApplication::instance()->environment().ajax()) { table->resize(800, 20 + 5*22); table->setEditTriggers(EditTrigger::SingleClicked); } else { table->resize(800, 20 + 5*22 + 25); table->setEditTriggers(EditTrigger::None); } std::shared_ptr delegate = std::make_shared(); delegate->setTextFormat("%.1f"); table->setItemDelegate(delegate); std::shared_ptr delegateColumn = std::make_shared(); table->setItemDelegateForColumn(0, delegateColumn); table->setColumnWidth(0, 80); for (int i = 1; i < model->columnCount(); ++i) table->setColumnWidth(i, 90); /* * Create the scatter plot. */ WCartesianChart *chart = this->addWidget(std::make_unique()); //chart->setPreferredMethod(WPaintedWidget::PngImage); //chart->setBackground(gray); chart->setModel(model); // set the model chart->setXSeriesColumn(0); // set the column that holds the X data chart->setLegendEnabled(true); // enable the legend chart->setZoomEnabled(true); chart->setPanEnabled(true); chart->setType(ChartType::Scatter); // set type to ScatterPlot chart->axis(Axis::X).setScale(AxisScale::Date); // set scale of X axis to DateScale // Automatically layout chart (space for axes, legend, ...) chart->setAutoLayoutEnabled(); chart->setBackground(WColor(200,200,200)); /* * Add first two columns as line series */ for (int i = 1; i < 3; ++i) { std::unique_ptr s = std::make_unique(i, SeriesType::Line); s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3)); chart->addSeries(std::move(s)); } chart->resize(800, 400); // WPaintedWidget must be given explicit size chart->setMargin(10, Side::Top | Side::Bottom); // add margin vertically chart->setMargin(WLength::Auto, Side::Left | Side::Right); // center horizontally this->addWidget(std::make_unique(chart)); } ScatterPlotExample::ScatterPlotExample(): WContainerWidget() { this->addWidget(std::make_unique(WString::tr("scatter plot 2"))); std::shared_ptr model = std::make_shared(40, 2); std::unique_ptr prototype = std::make_unique(); model->setItemPrototype(std::move(prototype)); model->setHeaderData(0, WString("X")); model->setHeaderData(1, WString("Y = sin(X)")); for (unsigned i = 0; i < 40; ++i) { double x = (static_cast(i) - 20) / 4; model->setData(i, 0, x); model->setData(i, 1, sin(x)); } /* * Create the scatter plot. */ WCartesianChart *chart = this->addWidget(std::make_unique()); chart->setModel(model); // set the model chart->setXSeriesColumn(0); // set the column that holds the X data chart->setLegendEnabled(true); // enable the legend chart->setZoomEnabled(true); chart->setPanEnabled(true); chart->setCrosshairEnabled(true); chart->setBackground(WColor(200,200,200)); chart->setType(ChartType::Scatter); // set type to ScatterPlot // Typically, for mathematical functions, you want the axes to cross // at the 0 mark: //chart->axis(Axis::X).setLocation(AxisValue::Zero); //chart->axis(Axis::Y).setLocation(AxisValue::Zero); // Automatically layout chart (space for axes, legend, ...) chart->setAutoLayoutEnabled(); // Add the curves std::unique_ptr s = std::make_unique(1, SeriesType::Curve); s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3)); chart->addSeries(std::move(s)); chart->resize(800, 300); // WPaintedWidget must be given explicit size chart->setMargin(10, Side::Top | Side::Bottom); // add margin vertically chart->setMargin(WLength::Auto, Side::Left | Side::Right); // center horizontally ChartConfig *config = this->addWidget(std::make_unique(chart)); config->setValueFill(FillRangeType::ZeroValue); chart = this->addWidget(std::make_unique()); chart->setModel(model); // set the model chart->setXSeriesColumn(0); // set the column that holds the X data chart->setLegendEnabled(true); // enable the legend chart->setZoomEnabled(true); chart->setPanEnabled(true); chart->setCrosshairEnabled(true); chart->setBackground(WColor(200, 200, 200)); chart->setType(ChartType::Scatter); // set type to ScatterPlot // Typically, for mathematical functions, you want the axes to cross // at the 0 mark: //chart->axis(Axis::X).setLocation(AxisValue::Zero); //chart->axis(Axis::Y).setLocation(AxisValue::Zero); // Automatically layout chart (space for axes, legend, ...) chart->setAutoLayoutEnabled(); // Add the curves s = std::make_unique(1, SeriesType::Curve); s->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3)); chart->addSeries(std::move(s)); chart->resize(500, 600); // WPaintedWidget must be given explicit size chart->setMargin(10, Side::Top | Side::Bottom); // add margin vertically chart->setMargin(WLength::Auto, Side::Left | Side::Right); // center horizontally config = this->addWidget(std::make_unique(chart)); config->setValueFill(FillRangeType::ZeroValue); } PieExample::PieExample(): WContainerWidget() { this->addWidget(std::make_unique(WString::tr("pie chart"))); std::shared_ptr model = std::make_shared(); std::unique_ptr prototype = std::make_unique(); model->setItemPrototype(std::move(prototype)); //headers model->insertColumns(model->columnCount(), 2); model->setHeaderData(0, WString("Item")); model->setHeaderData(1, WString("Sales")); //data model->insertRows(model->rowCount(), 6); int row = 0; model->setData(row, 0, WString("Blueberry")); model->setData(row, 1, 120); // model->setData(row, 1, WString("Blueberry"), ToolTipRole); row++; model->setData(row, 0, WString("Cherry")); model->setData(row, 1, 30); row++; model->setData(row, 0, WString("Apple")); model->setData(row, 1, 260); row++; model->setData(row, 0, WString("Boston Cream")); model->setData(row, 1, 160); row++; model->setData(row, 0, WString("Other")); model->setData(row, 1, 40); row++; model->setData(row, 0, WString("Vanilla Cream")); model->setData(row, 1, 120); row++; //set all items to be editable and selectable for (int row = 0; row < model->rowCount(); ++row) for (int col = 0; col < model->columnCount(); ++col) model->item(row, col)->setFlags(ItemFlag::Selectable | ItemFlag::Editable); WContainerWidget *w = this->addWidget(std::make_unique()); WTableView* table = w->addWidget(std::make_unique()); table->setMargin(10, Side::Top | Side::Bottom); table->setMargin(WLength::Auto, Side::Left | Side::Right); table->setSortingEnabled(true); table->setModel(model); table->setColumnWidth(1, 100); table->setRowHeight(22); if (WApplication::instance()->environment().ajax()) { table->resize(150 + 100 + 14, 20 + 6 * 22); table->setEditTriggers(EditTrigger::SingleClicked); } else { table->resize(150 + 100 + 14, WLength::Auto); table->setEditTriggers(EditTrigger::None); } /* * Create the pie chart. */ WPieChart *chart = this->addWidget(std::make_unique()); chart->setModel(model); // set the model chart->setLabelsColumn(0); // set the column that holds the labels chart->setDataColumn(1); // set the column that holds the data // configure location and type of labels chart->setDisplayLabels(LabelOption::Outside | LabelOption::TextLabel | LabelOption::TextPercentage); // enable a 3D and shadow effect chart->setPerspectiveEnabled(true, 0.2); chart->setShadowEnabled(true); // explode the first item chart->setExplode(0, 0.3); chart->resize(800, 300); // WPaintedWidget must be given an explicit size chart->setMargin(10, Side::Top | Side::Bottom); // add margin vertically chart->setMargin(WLength::Auto, Side::Left | Side::Right); // center horizontally }