Wt examples
3.2.3
|
A class that allows configuration of a cartesian chart. More...
#include <ChartConfig.h>
Classes | |
struct | AxisControl |
Struct that holds the controls for one axis. More... | |
struct | SeriesControl |
Struct that holds the controls for one series. More... | |
Public Member Functions | |
ChartConfig (Wt::Chart::WCartesianChart *chart, Wt::WContainerWidget *parent) | |
Constructor. | |
void | setValueFill (Wt::Chart::FillRangeType fill) |
Private Member Functions | |
void | connectSignals (Wt::WFormWidget *w) |
void | update () |
Static Private Member Functions | |
static bool | validate (Wt::WFormWidget *w) |
Private Attributes | |
Wt::Chart::WCartesianChart * | chart_ |
Wt::Chart::FillRangeType | fill_ |
std::vector< SeriesControl > | seriesControls_ |
Controls for series. | |
std::vector< AxisControl > | axisControls_ |
Controls for axes. | |
Wt::WLineEdit * | titleEdit_ |
Wt::WLineEdit * | chartWidthEdit_ |
Wt::WLineEdit * | chartHeightEdit_ |
Wt::WComboBox * | chartOrientationEdit_ |
Wt::WComboBox * | legendLocationEdit_ |
Wt::WComboBox * | legendSideEdit_ |
Wt::WComboBox * | legendAlignmentEdit_ |
A class that allows configuration of a cartesian chart.
This widget provides forms for configuring chart, series, and axis properties and manipulates the chart according to user settings.
This widget is part of the Wt charts example.
Definition at line 37 of file ChartConfig.h.
ChartConfig::ChartConfig | ( | Wt::Chart::WCartesianChart * | chart, |
Wt::WContainerWidget * | parent | ||
) |
Constructor.
Definition at line 63 of file ChartConfig.C.
: WContainerWidget(parent), chart_(chart), fill_(MinimumValueFill) { chart_->setLegendStyle(chart_->legendFont(), WPen(black), WBrush(WColor(0xFF, 0xFA, 0xE5))); chart->initLayout(); PanelList *list = new PanelList(this); WIntValidator *sizeValidator = new WIntValidator(200, 2000, this); sizeValidator->setMandatory(true); WDoubleValidator *anyNumberValidator = new WDoubleValidator(this); anyNumberValidator->setMandatory(true); WDoubleValidator *angleValidator = new WDoubleValidator(-90, 90, this); angleValidator->setMandatory(true); // ---- Chart properties ---- WStandardItemModel *orientation = new WStandardItemModel(0, 1, this); addEntry(orientation, "Vertical"); addEntry(orientation, "Horizontal"); WStandardItemModel *legendLocation = new WStandardItemModel(0, 1, this); addEntry(legendLocation, "Outside"); addEntry(legendLocation, "Inside"); WStandardItemModel *legendSide = new WStandardItemModel(0, 1, this); addEntry(legendSide, "Top"); addEntry(legendSide, "Right"); addEntry(legendSide, "Bottom"); addEntry(legendSide, "Left"); WStandardItemModel *legendAlignment = new WStandardItemModel(0, 1, this); addEntry(legendAlignment, "AlignLeft"); addEntry(legendAlignment, "AlignCenter"); addEntry(legendAlignment, "AlignRight"); addEntry(legendAlignment, "AlignTop"); addEntry(legendAlignment, "AlignMiddle"); addEntry(legendAlignment, "AlignBottom"); WTable *chartConfig = new WTable(); chartConfig->setMargin(WLength::Auto, Left | Right); int row = 0; chartConfig->elementAt(row, 0)->addWidget(new WText("Title:")); titleEdit_ = new WLineEdit(chartConfig->elementAt(row, 1)); connectSignals(titleEdit_); ++row; chartConfig->elementAt(row, 0)->addWidget(new WText("Width:")); chartWidthEdit_ = new WLineEdit(chartConfig->elementAt(row, 1)); chartWidthEdit_ ->setText(boost::lexical_cast<std::string>(chart_->width().value())); chartWidthEdit_->setValidator(sizeValidator); chartWidthEdit_->setMaxLength(4); connectSignals(chartWidthEdit_); ++row; chartConfig->elementAt(row, 0)->addWidget(new WText("Height:")); chartHeightEdit_ = new WLineEdit(chartConfig->elementAt(row, 1)); chartHeightEdit_ ->setText(boost::lexical_cast<std::string>(chart_->height().value())); chartHeightEdit_->setValidator(sizeValidator); chartHeightEdit_->setMaxLength(4); connectSignals(chartHeightEdit_); ++row; chartConfig->elementAt(row, 0)->addWidget(new WText("Orientation:")); chartOrientationEdit_ = new WComboBox(chartConfig->elementAt(row, 1)); chartOrientationEdit_->setModel(orientation); connectSignals(chartOrientationEdit_); ++row; chartConfig->elementAt(row, 0)->addWidget(new WText("Legend location:")); legendLocationEdit_ = new WComboBox(chartConfig->elementAt(row, 1)); legendLocationEdit_->setModel(legendLocation); connectSignals(legendLocationEdit_); ++row; chartConfig->elementAt(row, 0)->addWidget(new WText("Legend side:")); legendSideEdit_ = new WComboBox(chartConfig->elementAt(row, 1)); legendSideEdit_->setModel(legendSide); legendSideEdit_->setCurrentIndex(1); connectSignals(legendSideEdit_); ++row; chartConfig->elementAt(row, 0)->addWidget(new WText("Legend alignment:")); legendAlignmentEdit_ = new WComboBox(chartConfig->elementAt(row, 1)); legendAlignmentEdit_->setModel(legendAlignment); legendAlignmentEdit_->setCurrentIndex(4); connectSignals(legendAlignmentEdit_); ++row; for (int i = 0; i < chartConfig->rowCount(); ++i) { chartConfig->elementAt(i, 0)->setStyleClass("tdhead"); chartConfig->elementAt(i, 1)->setStyleClass("tddata"); } WPanel *p = list->addWidget("Chart properties", chartConfig); p->setMargin(WLength::Auto, Left | Right); p->resize(880, WLength::Auto); p->setMargin(20, Top | Bottom); if (chart_->isLegendEnabled()) chart_->setPlotAreaPadding(200, Right); // ---- Series properties ---- WStandardItemModel *types = new WStandardItemModel(0, 1, this); addEntry(types, "Points"); addEntry(types, "Line"); addEntry(types, "Curve"); addEntry(types, "Bar"); addEntry(types, "Line Area"); addEntry(types, "Curve Area"); addEntry(types, "Stacked Bar"); addEntry(types, "Stacked Line Area"); addEntry(types, "Stacked Curve Area"); WStandardItemModel *markers = new WStandardItemModel(0, 1, this); addEntry(markers, "None"); addEntry(markers, "Square"); addEntry(markers, "Circle"); addEntry(markers, "Cross"); addEntry(markers, "X cross"); addEntry(markers, "Triangle"); addEntry(markers, "Diamond"); WStandardItemModel *axes = new WStandardItemModel(0, 1, this); addEntry(axes, "1st Y axis"); addEntry(axes, "2nd Y axis"); WStandardItemModel *labels = new WStandardItemModel(0, 1, this); addEntry(labels, "None"); addEntry(labels, "X"); addEntry(labels, "Y"); addEntry(labels, "X: Y"); WTable *seriesConfig = new WTable(); seriesConfig->setMargin(WLength::Auto, Left | Right); ::addHeader(seriesConfig, "Name"); ::addHeader(seriesConfig, "Enabled"); ::addHeader(seriesConfig, "Type"); ::addHeader(seriesConfig, "Marker"); ::addHeader(seriesConfig, "Y axis"); ::addHeader(seriesConfig, "Legend"); ::addHeader(seriesConfig, "Shadow"); ::addHeader(seriesConfig, "Value labels"); seriesConfig->rowAt(0)->setStyleClass("trhead"); for (int j = 1; j < chart->model()->columnCount(); ++j) { SeriesControl sc; new WText(asString(chart->model()->headerData(j)), seriesConfig->elementAt(j, 0)); sc.enabledEdit = new WCheckBox(seriesConfig->elementAt(j, 1)); connectSignals(sc.enabledEdit); sc.typeEdit = new WComboBox(seriesConfig->elementAt(j, 2)); sc.typeEdit->setModel(types); connectSignals(sc.typeEdit); sc.markerEdit = new WComboBox(seriesConfig->elementAt(j, 3)); sc.markerEdit->setModel(markers); connectSignals(sc.markerEdit); sc.axisEdit = new WComboBox(seriesConfig->elementAt(j, 4)); sc.axisEdit->setModel(axes); connectSignals(sc.axisEdit); sc.legendEdit = new WCheckBox(seriesConfig->elementAt(j, 5)); connectSignals(sc.legendEdit); sc.shadowEdit = new WCheckBox(seriesConfig->elementAt(j, 6)); connectSignals(sc.shadowEdit); sc.labelsEdit = new WComboBox(seriesConfig->elementAt(j, 7)); sc.labelsEdit->setModel(labels); connectSignals(sc.labelsEdit); int si = seriesIndexOf(chart, j); if (si != -1) { sc.enabledEdit->setChecked(); const WDataSeries& s = chart_->series(j); switch (s.type()) { case PointSeries: sc.typeEdit->setCurrentIndex(0); break; case LineSeries: sc.typeEdit->setCurrentIndex(s.fillRange() != NoFill ? (s.isStacked() ? 7 : 4) : 1); break; case CurveSeries: sc.typeEdit->setCurrentIndex(s.fillRange() != NoFill ? (s.isStacked() ? 8 : 5) : 2); break; case BarSeries: sc.typeEdit->setCurrentIndex(s.isStacked() ? 6 : 3); } sc.markerEdit->setCurrentIndex((int)s.marker()); sc.legendEdit->setChecked(s.isLegendEnabled()); sc.shadowEdit->setChecked(s.shadow() != WShadow()); } seriesControls_.push_back(sc); seriesConfig->rowAt(j)->setStyleClass("trdata"); } p = list->addWidget("Series properties", seriesConfig); p->expand(); p->setMargin(WLength::Auto, Left | Right); p->resize(880, WLength::Auto); p->setMargin(20, Top | Bottom); // ---- Axis properties ---- WStandardItemModel *yScales = new WStandardItemModel(0, 1, this); addEntry(yScales, "Linear scale"); addEntry(yScales, "Log scale"); WStandardItemModel *xScales = new WStandardItemModel(0, 1, this); addEntry(xScales, "Categories"); addEntry(xScales, "Linear scale"); addEntry(xScales, "Log scale"); addEntry(xScales, "Date scale"); WTable *axisConfig = new WTable(); axisConfig->setMargin(WLength::Auto, Left | Right); ::addHeader(axisConfig, "Axis"); ::addHeader(axisConfig, "Visible"); ::addHeader(axisConfig, "Scale"); ::addHeader(axisConfig, "Automatic"); ::addHeader(axisConfig, "Minimum"); ::addHeader(axisConfig, "Maximum"); ::addHeader(axisConfig, "Gridlines"); ::addHeader(axisConfig, "Label angle"); axisConfig->rowAt(0)->setStyleClass("trhead"); for (int i = 0; i < 3; ++i) { const char *axisName[] = { "X axis", "1st Y axis", "2nd Y axis" }; int j = i + 1; const WAxis& axis = chart_->axis(static_cast<Axis>(i)); AxisControl sc; new WText(WString(axisName[i], UTF8), axisConfig->elementAt(j, 0)); sc.visibleEdit = new WCheckBox(axisConfig->elementAt(j, 1)); sc.visibleEdit->setChecked(axis.isVisible()); connectSignals(sc.visibleEdit); sc.scaleEdit = new WComboBox(axisConfig->elementAt(j, 2)); if (axis.scale() == CategoryScale) sc.scaleEdit->addItem("Category scale"); else { if (axis.id() == XAxis) { sc.scaleEdit->setModel(xScales); sc.scaleEdit->setCurrentIndex(axis.scale()); } else { sc.scaleEdit->setModel(yScales); sc.scaleEdit->setCurrentIndex(axis.scale() - 1); } } connectSignals(sc.scaleEdit); bool autoValues = axis.autoLimits() == (MinimumValue | MaximumValue); sc.minimumEdit = new WLineEdit(axisConfig->elementAt(j, 4)); sc.minimumEdit->setText(boost::lexical_cast<std::string>(axis.minimum())); sc.minimumEdit->setValidator(anyNumberValidator); sc.minimumEdit->setEnabled(!autoValues); connectSignals(sc.minimumEdit); sc.maximumEdit = new WLineEdit(axisConfig->elementAt(j, 5)); sc.maximumEdit->setText(boost::lexical_cast<std::string>(axis.maximum())); sc.maximumEdit->setValidator(anyNumberValidator); sc.maximumEdit->setEnabled(!autoValues); connectSignals(sc.maximumEdit); sc.autoEdit = new WCheckBox(axisConfig->elementAt(j, 3)); sc.autoEdit->setChecked(autoValues); connectSignals(sc.autoEdit); sc.autoEdit->checked().connect(sc.maximumEdit, &WLineEdit::disable); sc.autoEdit->unChecked().connect(sc.maximumEdit, &WLineEdit::enable); sc.autoEdit->checked().connect(sc.minimumEdit, &WLineEdit::disable); sc.autoEdit->unChecked().connect(sc.minimumEdit, &WLineEdit::enable); sc.gridLinesEdit = new WCheckBox(axisConfig->elementAt(j, 6)); connectSignals(sc.gridLinesEdit); sc.labelAngleEdit = new WLineEdit(axisConfig->elementAt(j, 7)); sc.labelAngleEdit->setText("0"); sc.labelAngleEdit->setValidator(angleValidator); connectSignals(sc.labelAngleEdit); axisConfig->rowAt(j)->setStyleClass("trdata"); axisControls_.push_back(sc); } p = list->addWidget("Axis properties", axisConfig); p->setMargin(WLength::Auto, Left | Right); p->resize(880, WLength::Auto); p->setMargin(20, Top | Bottom); /* * If we do not have JavaScript, then add a button to reflect changes to * the chart. */ if (!WApplication::instance()->environment().javaScript()) { WPushButton *b = new WPushButton(this); b->setText("Update chart"); b->setInline(false); // so we can add margin to center horizontally b->setMargin(WLength::Auto, Left | Right); b->clicked().connect(this, &ChartConfig::update); } }
void ChartConfig::connectSignals | ( | Wt::WFormWidget * | w | ) | [private] |
Definition at line 661 of file ChartConfig.C.
{ w->changed().connect(this, &ChartConfig::update); if (dynamic_cast<WLineEdit *>(w)) w->enterPressed().connect(this, &ChartConfig::update); }
void ChartConfig::setValueFill | ( | Wt::Chart::FillRangeType | fill | ) |
Definition at line 390 of file ChartConfig.C.
{ fill_ = fill; }
void ChartConfig::update | ( | ) | [private] |
Definition at line 395 of file ChartConfig.C.
{ bool haveLegend = false; std::vector<WDataSeries> series; for (int i = 1; i < chart_->model()->columnCount(); ++i) { SeriesControl& sc = seriesControls_[i-1]; if (sc.enabledEdit->isChecked()) { WDataSeries s(i); switch (sc.typeEdit->currentIndex()) { case 0: s.setType(PointSeries); if (sc.markerEdit->currentIndex() == 0) sc.markerEdit->setCurrentIndex(1); break; case 1: s.setType(LineSeries); sc.markerEdit->setCurrentIndex(0); break; case 2: s.setType(CurveSeries); sc.markerEdit->setCurrentIndex(0); break; case 3: s.setType(BarSeries); sc.markerEdit->setCurrentIndex(0); break; case 4: s.setType(LineSeries); s.setFillRange(fill_); sc.markerEdit->setCurrentIndex(0); break; case 5: s.setType(CurveSeries); s.setFillRange(fill_); sc.markerEdit->setCurrentIndex(0); break; case 6: s.setType(BarSeries); s.setStacked(true); sc.markerEdit->setCurrentIndex(0); break; case 7: s.setType(LineSeries); s.setFillRange(fill_); s.setStacked(true); sc.markerEdit->setCurrentIndex(0); break; case 8: s.setType(CurveSeries); s.setFillRange(fill_); s.setStacked(true); sc.markerEdit->setCurrentIndex(0); } //set WPainterPath to draw a diamond if(sc.markerEdit->currentIndex() == CustomMarker){ WPainterPath pp = WPainterPath(); pp.moveTo(-6, 0); pp.lineTo(0, 6); pp.lineTo(6, 0); pp.lineTo(0, -6); pp.lineTo(-6, 0); s.setCustomMarker(pp); } s.setMarker(static_cast<MarkerType>(sc.markerEdit->currentIndex())); if (sc.axisEdit->currentIndex() == 1) { s.bindToAxis(Y2Axis); } if (sc.legendEdit->isChecked()) { s.setLegendEnabled(true); haveLegend = true; } else s.setLegendEnabled(false); if (sc.shadowEdit->isChecked()) { s.setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3)); } else s.setShadow(WShadow()); switch (sc.labelsEdit->currentIndex()) { case 1: s.setLabelsEnabled(XAxis); break; case 2: s.setLabelsEnabled(YAxis); break; case 3: s.setLabelsEnabled(XAxis); s.setLabelsEnabled(YAxis); break; } series.push_back(s); } } chart_->setSeries(series); for (int i = 0; i < 3; ++i) { AxisControl& sc = axisControls_[i]; WAxis& axis = chart_->axis(static_cast<Axis>(i)); axis.setVisible(sc.visibleEdit->isChecked()); if (sc.scaleEdit->count() != 1) { int k = sc.scaleEdit->currentIndex(); if (axis.id() != XAxis) k += 1; else { if (k == 0) chart_->setType(CategoryChart); else chart_->setType(ScatterPlot); } switch (k) { case 1: axis.setScale(LinearScale); break; case 2: axis.setScale(LogScale); break; case 3: axis.setScale(DateScale); break; } } if (sc.autoEdit->isChecked()) axis.setAutoLimits(MinimumValue | MaximumValue); else { if (validate(sc.minimumEdit) && validate(sc.maximumEdit)) { double min, max; getDouble(sc.minimumEdit, min); getDouble(sc.maximumEdit, max); if (axis.scale() == LogScale) if (min <= 0) min = 0.0001; if (axis.scale() == DateScale){ //the number of julian days until year 1986 WDate dMin = WDate(1900,1,1); double gregDaysMin = (double)dMin.toJulianDay(); //the number of julian days until year 1988 WDate dMax = WDate(3000,1,1); double gregDaysMax = (double)dMax.toJulianDay(); bool greg_year_validation = (min > gregDaysMin && min < gregDaysMax && max > gregDaysMin && max < gregDaysMax); if(!greg_year_validation){ min = gregDaysMin; max = gregDaysMax; } } axis.setRange(min, max); } } if (validate(sc.labelAngleEdit)) { double angle; getDouble(sc.labelAngleEdit, angle); axis.setLabelAngle(angle); } axis.setGridLinesEnabled(sc.gridLinesEdit->isChecked()); } chart_->setTitle(titleEdit_->text()); if (validate(chartWidthEdit_) && validate(chartHeightEdit_)) { double width, height; getDouble(chartWidthEdit_, width); getDouble(chartHeightEdit_, height); chart_->resize(width, height); } switch (chartOrientationEdit_->currentIndex()) { case 0: chart_->setOrientation(Vertical); break; case 1: chart_->setOrientation(Horizontal); break; } chart_->setLegendEnabled(haveLegend); if (haveLegend) { LegendLocation location = LegendOutside; Side side = Right; AlignmentFlag alignment = AlignMiddle; switch (legendLocationEdit_->currentIndex()) { case 0: location = LegendOutside; break; case 1: location = LegendInside; break; } switch (legendSideEdit_->currentIndex()) { case 0: side = Top; break; case 1: side = Right; break; case 2: side = Bottom; break; case 3: side = Left; break; } if (side == Left || side == Right) { if (legendAlignmentEdit_->currentIndex() < 3) legendAlignmentEdit_->setCurrentIndex(4); } else { if (legendAlignmentEdit_->currentIndex() >= 3) legendAlignmentEdit_->setCurrentIndex(2); } switch (legendAlignmentEdit_->currentIndex()) { case 0: alignment = AlignLeft; break; case 1: alignment = AlignCenter; break; case 2: alignment = AlignRight; break; case 3: alignment = AlignTop; break; case 4: alignment = AlignMiddle; break; case 5: alignment = AlignBottom; break; } chart_->setLegendLocation(location, side, alignment); chart_->setLegendColumns((side == Top || side == Bottom ) ? 2 : 1, WLength(100)); } for (unsigned i = 0; i < 4; ++i) { Side sides[] = { Top, Right, Bottom, Left }; bool legendRoom = haveLegend && chart_->legendLocation() == LegendOutside && chart_->legendSide() == sides[i]; int padding; if (i % 2 == 0) padding = legendRoom ? 80 : 40; else padding = legendRoom ? 200 : 80; chart_->setPlotAreaPadding(padding, sides[i]); } }
bool ChartConfig::validate | ( | Wt::WFormWidget * | w | ) | [static, private] |
Definition at line 649 of file ChartConfig.C.
{ bool valid = w->validate() == WValidator::Valid; if (!WApplication::instance()->environment().javaScript()) { w->setStyleClass(valid ? "" : "Wt-invalid"); w->setToolTip(valid ? "" : "Invalid value"); } return valid; }
std::vector<AxisControl> ChartConfig::axisControls_ [private] |
Controls for axes.
Definition at line 76 of file ChartConfig.h.
Wt::Chart::WCartesianChart* ChartConfig::chart_ [private] |
Definition at line 47 of file ChartConfig.h.
Wt::WLineEdit* ChartConfig::chartHeightEdit_ [private] |
Definition at line 80 of file ChartConfig.h.
Wt::WComboBox* ChartConfig::chartOrientationEdit_ [private] |
Definition at line 81 of file ChartConfig.h.
Wt::WLineEdit* ChartConfig::chartWidthEdit_ [private] |
Definition at line 79 of file ChartConfig.h.
Wt::Chart::FillRangeType ChartConfig::fill_ [private] |
Definition at line 48 of file ChartConfig.h.
Wt::WComboBox* ChartConfig::legendAlignmentEdit_ [private] |
Definition at line 84 of file ChartConfig.h.
Wt::WComboBox* ChartConfig::legendLocationEdit_ [private] |
Definition at line 82 of file ChartConfig.h.
Wt::WComboBox* ChartConfig::legendSideEdit_ [private] |
Definition at line 83 of file ChartConfig.h.
std::vector<SeriesControl> ChartConfig::seriesControls_ [private] |
Controls for series.
Definition at line 62 of file ChartConfig.h.
Wt::WLineEdit* ChartConfig::titleEdit_ [private] |
Definition at line 78 of file ChartConfig.h.