Graphics & Charts — generic and specialized painting infrastructure

Category chart

The Charting library

The examples in this menu and the other charting menus Scatter plot and Pie chart demonstrate some of the capabilities of the Wt charting widgets. These widgets are implemented using the Wt painting API WPaintedWidget which provides cross-browser native painting, using VML, SVG, or the HTML5 canvas tag.

The two main chart widgets are

These widgets are based on the MVC mechanism, and retrieve their data from a WAbstractItemModel.

A cartesian chart is either a CategoryChart or a ScatterPlot.

  • A CategoryChart shows data in categories.
  • A ScatterPlot shows the data according to a numerical X scale.
It can display one or multiple data series which may be rendered individually using bars, lines, areas, or points. To use a cartesian chart, the minimum you need to do is to set a model using setModel(), set the model column that holds the X data using setXSeriesColumn(int modelColumn), and add one or more series using addSeries(const WDataSeries&). Each series corresponds to one data column that holds Y data.

Many aspects of the charts may be customized. By default, style information for rendering data series are taken from a WChartPalette. It is straightforward to specialize this class to provide different styles from the standard styles provided by WStandardPalette.

The cartesian chart has support for dual Y axes. Each data series may be bound to one of the two Y axes. By default, only the first Y axis is displayed. To show the second Y axis you will need to call chart->axis(Y2Axis).setVisible(true) By default a chart has a horizontal X axis and a vertical Y axis, which corresponds to a Vertical orientation. The orientation may be changed to Horizontal using setOrientation(). The styling of the data series are defined by a palette which may be set using setPalette(WChartPalette *), but may be overridden by settings in each data series.

What is a category chart?

A category chart has different categories on the X axis, and displays values of one or more data series on the Y axes as a series of bars. The values corresponding to each category are plotted consecutively in model row order. Each data series corresponds to a column from the model and may be rendered differently (This is configured in the data series - See WDataSeries for more information).

As a cartesian chart it provides automatic configuration of the axes, and support for a second Y axis. In addition, you may use a simple built-in legend, or extend the class to provide a specialized legend. In the example below, we use a manual Y axis configuration, with a break as would be commonly used when your data has a few outliers.

The table view allows editing of the model, which is automatically reflected in the chart.

By the way, would you expect this example to work when Ajax/JavaScript are not available or disabled?

Example
Age
Rural Male
Rural Female
Urban Male
Urban Female
70-74
66
54
71
50
65-69
41
31
55
35
60-64
127
20
37
19
55-59
18
149
24
14
50-54
12
9
15
8
source
#include <Wt/Chart/WCartesianChart.h>
#include <Wt/Chart/WDataSeries.h>
#include <Wt/WAbstractItemView.h>
#include <Wt/WApplication.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WEnvironment.h>
#include <Wt/WItemDelegate.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WStandardItem.h>
#include <Wt/WTableView.h>

#include "../treeview-dragdrop/CsvUtil.h"


auto container = cpp14::make_unique<WContainerWidget>();

auto model
    = csvToModel(WApplication::appRoot() + "category.csv");

if (!model)
    return std::move(container);

/*
 * Configure all model items as selectable and editable.
 */
for (int row = 0; row < model->rowCount(); ++row)
    for (int col = 0; col < model->columnCount(); ++col)
        model->item(row, col)->setFlags(ItemFlag::Editable);

/*
 * Shows a table, allowing editing of the model
 */
auto table = container->addNew<WTableView>();
table->setModel(model);
table->setSortingEnabled(true);
table->setColumnResizeEnabled(true);
table->setAlternatingRowColors(true);
table->setHeaderAlignment(0, AlignmentFlag::Center);
table->setColumnAlignment(0, AlignmentFlag::Center);
table->setRowHeight(28);
table->setHeaderHeight(28);
table->setMargin(10, Side::Top | Side::Bottom);
table->setMargin(WLength::Auto, Side::Left | Side::Right);
table->setWidth(4*120 + 80 + 5*7 + 2);

/*
 * 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->setEditTriggers(EditTrigger::SingleClicked);
    table->setEditOptions(table->editOptions() | 
                          EditOption::SaveWhenClosed);
} else {
    table->setEditTriggers(EditTrigger::None);
}

/*
 * Use a delegate for the numeric data which rounds values sensibly.
 */
auto delegate = std::make_shared<WItemDelegate>();
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.
 */
auto chart = container->addNew<Chart::WCartesianChart>();
chart->setModel(model);
chart->setXSeriesColumn(0);
chart->setLegendEnabled(true);

/*
 * Provide ample space for the title, the X and Y axis and the legend.
 */
chart->setPlotAreaPadding(40, Side::Left | Side::Top | Side::Bottom);
chart->setPlotAreaPadding(120, Side::Right);

/*
 * Add all (but first) column as bar series.
 */
for (int column = 1; column < model->columnCount(); ++column) {
    auto series = cpp14::make_unique<Chart::WDataSeries>(column, Chart::SeriesType::Bar);
    series->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
    chart->addSeries(std::move(series));
}

chart->resize(600, 400);
chart->setMargin(WLength::Auto, Side::Left | Side::Right);

Top