Skip to content

Commit 3ff3456

Browse files
committed
Multiple X axes for WCartesianChart
1 parent 45e184a commit 3ff3456

20 files changed

+1558
-717
lines changed

examples/charts/ChartConfig.C

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ namespace {
6363
return -1;
6464
}
6565

66-
WString axisName(Axis axis, int yAxis)
66+
WString axisName(Axis axis, int axisId)
6767
{
68-
if (axis == Axis::X)
69-
return Wt::utf8("X Axis");
70-
else {
71-
return Wt::utf8("Y axis {1}").arg(yAxis + 1);
68+
if (axis == Axis::X) {
69+
return Wt::utf8("X Axis {1}").arg(axisId + 1);
70+
} else {
71+
return Wt::utf8("Y axis {1}").arg(axisId + 1);
7272
}
7373
}
7474
}
@@ -221,6 +221,9 @@ ChartConfig::ChartConfig(WCartesianChart *chart)
221221
addEntry(markers, "Asterisk");
222222
addEntry(markers, "Diamond");
223223

224+
xAxesModel_ = std::make_shared<WStandardItemModel>(0, 1);
225+
addEntry(xAxesModel_, axisName(Axis::X, 0));
226+
224227
yAxesModel_ = std::make_shared<WStandardItemModel>(0, 1);
225228
addEntry(yAxesModel_, axisName(Axis::Y, 0));
226229
addEntry(yAxesModel_, axisName(Axis::Y, 1));
@@ -240,6 +243,7 @@ ChartConfig::ChartConfig(WCartesianChart *chart)
240243
::addHeader(seriesConfigPtr, "Enabled");
241244
::addHeader(seriesConfigPtr, "Type");
242245
::addHeader(seriesConfigPtr, "Marker");
246+
::addHeader(seriesConfigPtr, "X axis");
243247
::addHeader(seriesConfigPtr, "Y axis");
244248
::addHeader(seriesConfigPtr, "Legend");
245249
::addHeader(seriesConfigPtr, "Shadow");
@@ -265,18 +269,23 @@ ChartConfig::ChartConfig(WCartesianChart *chart)
265269
sc.markerEdit->setCurrentIndex(0);
266270
connectSignals(sc.markerEdit);
267271

268-
sc.axisEdit = seriesConfig->elementAt(j,4)->addNew<WComboBox>();
269-
sc.axisEdit->setModel(yAxesModel_);
270-
sc.axisEdit->setCurrentIndex(0);
271-
connectSignals(sc.axisEdit);
272+
sc.xAxisEdit = seriesConfig->elementAt(j, 4)->addNew<WComboBox>();
273+
sc.xAxisEdit->setModel(xAxesModel_);
274+
sc.xAxisEdit->setCurrentIndex(0);
275+
connectSignals(sc.xAxisEdit);
272276

273-
sc.legendEdit = seriesConfig->elementAt(j, 5)->addWidget(cpp14::make_unique<WCheckBox>());
277+
sc.yAxisEdit = seriesConfig->elementAt(j, 5)->addNew<WComboBox>();
278+
sc.yAxisEdit->setModel(yAxesModel_);
279+
sc.yAxisEdit->setCurrentIndex(0);
280+
connectSignals(sc.yAxisEdit);
281+
282+
sc.legendEdit = seriesConfig->elementAt(j, 6)->addWidget(cpp14::make_unique<WCheckBox>());
274283
connectSignals(sc.legendEdit);
275284

276-
sc.shadowEdit = seriesConfig->elementAt(j,6)->addWidget(cpp14::make_unique<WCheckBox>());
285+
sc.shadowEdit = seriesConfig->elementAt(j, 7)->addWidget(cpp14::make_unique<WCheckBox>());
277286
connectSignals(sc.shadowEdit);
278287

279-
sc.labelsEdit = seriesConfig->elementAt(j,7)->addWidget(cpp14::make_unique<WComboBox>());
288+
sc.labelsEdit = seriesConfig->elementAt(j, 8)->addWidget(cpp14::make_unique<WComboBox>());
280289
sc.labelsEdit->setModel(labels);
281290
sc.labelsEdit->setCurrentIndex(0);
282291
connectSignals(sc.labelsEdit);
@@ -350,12 +359,18 @@ ChartConfig::ChartConfig(WCartesianChart *chart)
350359
addAxis(Axis::Y, 0);
351360
addAxis(Axis::Y, 1);
352361

353-
WPushButton *addAxisBtn =
362+
WPushButton *addXAxisBtn =
363+
axisConfig->addNew<WPushButton>(Wt::utf8("Add X axis"));
364+
addXAxisBtn->clicked().connect(this, &ChartConfig::addXAxis);
365+
WPushButton *clearXAxesBtn =
366+
axisConfig->addNew<WPushButton>(Wt::utf8("Clear X axes"));
367+
clearXAxesBtn->clicked().connect(this, &ChartConfig::clearXAxes);
368+
WPushButton *addYAxisBtn =
354369
axisConfig->addNew<WPushButton>(utf8("Add Y axis"));
355-
addAxisBtn->clicked().connect(this, &ChartConfig::addYAxis);
356-
WPushButton *clearAxesBtn =
370+
addYAxisBtn->clicked().connect(this, &ChartConfig::addYAxis);
371+
WPushButton *clearYAxesBtn =
357372
axisConfig->addNew<WPushButton>(utf8("Clear Y axes"));
358-
clearAxesBtn->clicked().connect(this, &ChartConfig::clearYAxes);
373+
clearYAxesBtn->clicked().connect(this, &ChartConfig::clearYAxes);
359374

360375
p = list->addWidget("Axis properties", std::move(axisConfig));
361376
p->setMargin(WLength::Auto, Side::Left | Side::Right);
@@ -440,7 +455,8 @@ void ChartConfig::update()
440455

441456
s->setMarker(static_cast<MarkerType>(sc.markerEdit->currentIndex()));
442457

443-
s->bindToYAxis(sc.axisEdit->currentIndex());
458+
s->bindToXAxis(sc.xAxisEdit->currentIndex());
459+
s->bindToYAxis(sc.yAxisEdit->currentIndex());
444460

445461
if (sc.legendEdit->isChecked()) {
446462
s->setLegendEnabled(true);
@@ -474,7 +490,7 @@ void ChartConfig::update()
474490

475491
for (std::size_t i = 0; i < axisControls_.size(); ++i) {
476492
AxisControl& sc = axisControls_[i];
477-
WAxis& axis = i == 0 ? chart_->axis(Axis::X) : chart_->yAxis(i - 1);
493+
WAxis& axis = static_cast<int>(i) < chart_->xAxisCount() ? chart_->xAxis(i) : chart_->yAxis(i - chart_->xAxisCount());
478494

479495
axis.setVisible(sc.visibleEdit->isChecked());
480496

@@ -655,6 +671,15 @@ void ChartConfig::connectSignals(WFormWidget *w)
655671
w->enterPressed().connect(this, &ChartConfig::update);
656672
}
657673

674+
void ChartConfig::addXAxis()
675+
{
676+
int xAxis = chart_->addXAxis(cpp14::make_unique<WAxis>());
677+
addAxis(Axis::X, xAxis);
678+
addEntry(xAxesModel_, axisName(Axis::X, xAxis));
679+
if (xAxis == 0)
680+
update();
681+
}
682+
658683
void ChartConfig::addYAxis()
659684
{
660685
int yAxis = chart_->addYAxis(cpp14::make_unique<WAxis>());
@@ -664,13 +689,14 @@ void ChartConfig::addYAxis()
664689
update();
665690
}
666691

667-
void ChartConfig::addAxis(Axis ax, int yAxis)
692+
void ChartConfig::addAxis(Axis ax, int axisId)
668693
{
669-
int j = ax == Axis::X ? 1 : yAxis + 2;
694+
int j = ax == Axis::X ? 1 + axisId : 1 + chart_->xAxisCount() + axisId;
670695

671-
const WAxis& axis = ax == Axis::X ? chart_->axis(Axis::X) : chart_->yAxis(yAxis);
696+
const WAxis& axis = ax == Axis::X ? chart_->xAxis(axisId) : chart_->yAxis(axisId);
672697
AxisControl sc;
673698

699+
axisConfig_->insertRow(j);
674700
axisConfig_->elementAt(j, 0)->addNew<WText>(axisName(axis.id(), axis.yAxisId()));
675701

676702
sc.visibleEdit = axisConfig_->elementAt(j, 1)->addNew<WCheckBox>();
@@ -752,15 +778,31 @@ void ChartConfig::addAxis(Axis ax, int yAxis)
752778
}
753779
connectSignals(sc.locationEdit);
754780

755-
if (ax != Axis::X) {
756-
WPushButton *removeAxisButton =
757-
axisConfig_->elementAt(j, 12)->addNew<WPushButton>(utf8("x"));
781+
WPushButton *removeAxisButton =
782+
axisConfig_->elementAt(j, 12)->addNew<WPushButton>(utf8("x"));
783+
if (ax == Axis::X) {
784+
removeAxisButton->clicked().connect(std::bind(&ChartConfig::removeXAxis, this, &axis));
785+
} else {
758786
removeAxisButton->clicked().connect(std::bind(&ChartConfig::removeYAxis, this, &axis));
759787
}
760788

761789
axisConfig_->rowAt(j)->setStyleClass("trdata");
762790

763-
axisControls_.push_back(sc);
791+
axisControls_.insert(axisControls_.begin() + j - 1, sc);
792+
}
793+
794+
void ChartConfig::removeXAxis(const WAxis *axis)
795+
{
796+
int xAxis = axis->xAxisId();
797+
for (std::size_t i = 0; i < chart_->series().size(); ++i) {
798+
if (chart_->series()[i]->xAxis() == xAxis)
799+
chart_->series()[i]->bindToXAxis(-1);
800+
}
801+
chart_->removeXAxis(xAxis);
802+
axisConfig_->removeRow(1 + xAxis);
803+
xAxesModel_->removeRow(xAxis);
804+
axisControls_.erase(axisControls_.begin() + xAxis);
805+
update();
764806
}
765807

766808
void ChartConfig::removeYAxis(const WAxis *axis)
@@ -771,12 +813,29 @@ void ChartConfig::removeYAxis(const WAxis *axis)
771813
chart_->series()[i]->bindToYAxis(-1);
772814
}
773815
chart_->removeYAxis(yAxis);
774-
axisConfig_->removeRow(yAxis + 2);
816+
axisConfig_->removeRow(1 + chart_->xAxisCount() + yAxis);
775817
yAxesModel_->removeRow(yAxis);
776-
axisControls_.erase(axisControls_.begin() + yAxis + 1);
818+
axisControls_.erase(axisControls_.begin() + chart_->xAxisCount() + yAxis);
777819
update();
778820
}
779821

822+
void ChartConfig::clearXAxes()
823+
{
824+
if (chart_->xAxisCount() == 0)
825+
return;
826+
827+
for (std::size_t i = 0; i < chart_->series().size(); ++i) {
828+
chart_->series()[i]->bindToXAxis(-1);
829+
}
830+
const int xAxisCount = chart_->xAxisCount();
831+
chart_->clearXAxes();
832+
for (int i = 0; i < xAxisCount; ++i) {
833+
axisConfig_->removeRow(1);
834+
}
835+
xAxesModel_->clear();
836+
axisControls_.erase(axisControls_.begin(), axisControls_.begin() + xAxisCount);
837+
}
838+
780839
void ChartConfig::clearYAxes()
781840
{
782841
if (chart_->yAxisCount() == 0)
@@ -785,9 +844,11 @@ void ChartConfig::clearYAxes()
785844
for (std::size_t i = 0; i < chart_->series().size(); ++i) {
786845
chart_->series()[i]->bindToYAxis(-1);
787846
}
847+
const int yAxisCount = chart_->yAxisCount();
788848
chart_->clearYAxes();
789-
while (axisConfig_->rowCount() > 2)
790-
axisConfig_->removeRow(2);
849+
for (int i = 0; i < yAxisCount; ++i) {
850+
axisConfig_->removeRow(axisConfig_->rowCount() - 1);
851+
}
791852
yAxesModel_->clear();
792-
axisControls_.resize(1);
853+
axisControls_.resize(chart_->xAxisCount());
793854
}

examples/charts/ChartConfig.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ class ChartConfig : public Wt::WContainerWidget
5555
Wt::WCheckBox *enabledEdit;
5656
Wt::WComboBox *typeEdit;
5757
Wt::WComboBox *markerEdit;
58-
Wt::WComboBox *axisEdit;
58+
Wt::WComboBox *xAxisEdit;
59+
Wt::WComboBox *yAxisEdit;
5960
Wt::WCheckBox *legendEdit;
6061
Wt::WCheckBox *shadowEdit;
6162
Wt::WComboBox *labelsEdit;
@@ -91,15 +92,18 @@ class ChartConfig : public Wt::WContainerWidget
9192
Wt::WComboBox *legendAlignmentEdit_;
9293
Wt::WCheckBox *borderEdit_;
9394

94-
std::shared_ptr<Wt::WStandardItemModel> yAxesModel_, xScales_, yScales_;
95+
std::shared_ptr<Wt::WStandardItemModel> xAxesModel_, yAxesModel_, xScales_, yScales_;
9596
Wt::WTable *axisConfig_;
9697
std::shared_ptr<Wt::WValidator> anyNumberValidator_, angleValidator_;
9798

9899
void connectSignals(Wt::WFormWidget *w);
99100
void update();
101+
void addXAxis();
100102
void addYAxis();
101-
void addAxis(Wt::Chart::Axis axis, int yAxis);
103+
void addAxis(Wt::Chart::Axis axis, int axisId);
104+
void removeXAxis(const Wt::Chart::WAxis *axis);
102105
void removeYAxis(const Wt::Chart::WAxis *axis);
106+
void clearXAxes();
103107
void clearYAxes();
104108

105109
static bool validate(Wt::WFormWidget *w);

src/Wt/Chart/WAbstractChartImplementation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class WAbstractChartImplementation {
3131
virtual int numberOfCategories(Axis axis = Axis::X) const = 0;
3232
virtual WString categoryLabel(int u, Axis axis) const = 0;
3333

34-
virtual RenderRange computeRenderRange(Axis axis, int yAxis, AxisScale scale) const = 0;
34+
virtual RenderRange computeRenderRange(Axis axis, int xAxis, int yAxis, AxisScale scale) const = 0;
3535

3636
virtual bool onDemandLoadingEnabled() const = 0;
3737

src/Wt/Chart/WAxis.C

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ WAxis::Segment::Segment(const Segment &other)
146146
WAxis::WAxis()
147147
: chart_(nullptr),
148148
axis_(Axis::X),
149+
xAxis_(0),
149150
yAxis_(0),
150151
visible_(true),
151152
location_(AxisValue::Minimum),
@@ -194,6 +195,7 @@ void WAxis::init(WAbstractChartImplementation* chart,
194195
{
195196
chart_ = chart;
196197
axis_ = axis;
198+
xAxis_ = 0;
197199
yAxis_ = axis == Axis::Y2 ? 1 : 0;
198200

199201
if (axis == Axis::X || axis_ == Axis::X3D || axis_ == Axis::Y3D) {
@@ -204,6 +206,18 @@ void WAxis::init(WAbstractChartImplementation* chart,
204206
}
205207
}
206208

209+
void WAxis::initXAxis(WAbstractChartImplementation* chart,
210+
int xAxis)
211+
{
212+
if (xAxis == 0)
213+
init(chart, Axis::X);
214+
else {
215+
chart_ = chart;
216+
axis_ = Axis::X;
217+
xAxis_ = xAxis;
218+
}
219+
}
220+
207221
void WAxis::initYAxis(WAbstractChartImplementation* chart,
208222
int yAxis)
209223
{
@@ -878,7 +892,7 @@ void WAxis::computeRange(const Segment& segment) const
878892
double maximum = -std::numeric_limits<double>::max();
879893

880894
WAbstractChartImplementation::RenderRange rr =
881-
chart_->computeRenderRange(axis_, yAxis_, scale_);
895+
chart_->computeRenderRange(axis_, xAxis_, yAxis_, scale_);
882896
minimum = rr.minimum;
883897
maximum = rr.maximum;
884898

src/Wt/Chart/WAxis.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ class WT_API WAxis
210210
*/
211211
Axis id() const { return axis_; }
212212

213+
/*! \brief Returns the X axis id.
214+
*
215+
* Returns 0 if this axis is not a X axis.
216+
*
217+
* \sa chart(), WCartesianChart::xAxis()
218+
*/
219+
int xAxisId() const { return xAxis_; }
220+
213221
/*! \brief Returns the Y axis id.
214222
*
215223
* Returns 0 if this axis is not a Y axis.
@@ -1029,6 +1037,7 @@ class WT_API WAxis
10291037

10301038
WAbstractChartImplementation *chart_;
10311039
Axis axis_;
1040+
int xAxis_;
10321041
int yAxis_;
10331042
bool visible_;
10341043
AxisValue location_;
@@ -1083,6 +1092,7 @@ class WT_API WAxis
10831092
mutable double fullRenderLength_; // Render length including padding
10841093

10851094
void init(WAbstractChartImplementation* chart, Axis axis);
1095+
void initXAxis(WAbstractChartImplementation* chart, int xAxis);
10861096
void initYAxis(WAbstractChartImplementation* chart, int yAxis);
10871097
void update();
10881098

0 commit comments

Comments
 (0)