Wt examples  3.2.3
Classes | Public Member Functions | Private Member Functions | Static Private Member Functions | Private Attributes
ChartConfig Class Reference

A class that allows configuration of a cartesian chart. More...

#include <ChartConfig.h>

Inheritance diagram for ChartConfig:
Inheritance graph
[legend]

List of all members.

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::WCartesianChartchart_
Wt::Chart::FillRangeType fill_
std::vector< SeriesControlseriesControls_
 Controls for series.
std::vector< AxisControlaxisControls_
 Controls for axes.
Wt::WLineEdittitleEdit_
Wt::WLineEditchartWidthEdit_
Wt::WLineEditchartHeightEdit_
Wt::WComboBoxchartOrientationEdit_
Wt::WComboBoxlegendLocationEdit_
Wt::WComboBoxlegendSideEdit_
Wt::WComboBoxlegendAlignmentEdit_

Detailed Description

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.


Constructor & Destructor Documentation

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);
  }
}

Member Function Documentation

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;
}

Member Data Documentation

std::vector<AxisControl> ChartConfig::axisControls_ [private]

Controls for axes.

Definition at line 76 of file ChartConfig.h.

Definition at line 47 of file ChartConfig.h.

Definition at line 80 of file ChartConfig.h.

Definition at line 81 of file ChartConfig.h.

Definition at line 79 of file ChartConfig.h.

Definition at line 48 of file ChartConfig.h.

Definition at line 84 of file ChartConfig.h.

Definition at line 82 of file ChartConfig.h.

Definition at line 83 of file ChartConfig.h.

Controls for series.

Definition at line 62 of file ChartConfig.h.

Definition at line 78 of file ChartConfig.h.


The documentation for this class was generated from the following files:

Generated on Thu Nov 1 2012 for the C++ Web Toolkit (Wt) by doxygen 1.7.5.1