summaryrefslogtreecommitdiffstats
path: root/stats/barseries.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'stats/barseries.cpp')
-rw-r--r--stats/barseries.cpp132
1 files changed, 56 insertions, 76 deletions
diff --git a/stats/barseries.cpp b/stats/barseries.cpp
index 49170bf80..766843703 100644
--- a/stats/barseries.cpp
+++ b/stats/barseries.cpp
@@ -4,6 +4,7 @@
#include "statscolors.h"
#include "statshelper.h"
#include "statstranslations.h"
+#include "statsview.h"
#include "zvalues.h"
#include <math.h> // for lrint()
@@ -12,6 +13,7 @@
// Constants that control the bar layout
static const double barWidth = 0.8; // 1.0 = full width of category
static const double subBarWidth = 0.9; // For grouped bar charts
+static const double barBorderWidth = 1.0;
// Default constructor: invalid index.
BarSeries::Index::Index() : bar(-1), subitem(-1)
@@ -27,19 +29,19 @@ bool BarSeries::Index::operator==(const Index &i2) const
return std::tie(bar, subitem) == std::tie(i2.bar, i2.subitem);
}
-BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis,
+BarSeries::BarSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis,
bool horizontal, bool stacked, const QString &categoryName,
const StatsVariable *valueVariable, std::vector<QString> valueBinNames) :
- StatsSeries(scene, xAxis, yAxis),
+ StatsSeries(view, xAxis, yAxis),
horizontal(horizontal), stacked(stacked), categoryName(categoryName),
valueVariable(valueVariable), valueBinNames(std::move(valueBinNames))
{
}
-BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis,
+BarSeries::BarSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis,
bool horizontal, const QString &categoryName,
const std::vector<CountItem> &items) :
- BarSeries(scene, xAxis, yAxis, horizontal, false, categoryName, nullptr, std::vector<QString>())
+ BarSeries(view, xAxis, yAxis, horizontal, false, categoryName, nullptr, std::vector<QString>())
{
for (const CountItem &item: items) {
StatsOperationResults res;
@@ -50,10 +52,10 @@ BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis,
}
}
-BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis,
+BarSeries::BarSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis,
bool horizontal, const QString &categoryName, const StatsVariable *valueVariable,
const std::vector<ValueItem> &items) :
- BarSeries(scene, xAxis, yAxis, horizontal, false, categoryName, valueVariable, std::vector<QString>())
+ BarSeries(view, xAxis, yAxis, horizontal, false, categoryName, valueVariable, std::vector<QString>())
{
for (const ValueItem &item: items) {
add_item(item.lowerBound, item.upperBound, makeSubItems(item.value, item.label),
@@ -61,11 +63,11 @@ BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis,
}
}
-BarSeries::BarSeries(QGraphicsScene *scene, StatsAxis *xAxis, StatsAxis *yAxis,
+BarSeries::BarSeries(StatsView &view, StatsAxis *xAxis, StatsAxis *yAxis,
bool horizontal, bool stacked, const QString &categoryName, const StatsVariable *valueVariable,
std::vector<QString> valueBinNames,
const std::vector<MultiItem> &items) :
- BarSeries(scene, xAxis, yAxis, horizontal, stacked, categoryName, valueVariable, std::move(valueBinNames))
+ BarSeries(view, xAxis, yAxis, horizontal, stacked, categoryName, valueVariable, std::move(valueBinNames))
{
for (const MultiItem &item: items) {
StatsOperationResults res;
@@ -85,97 +87,79 @@ BarSeries::~BarSeries()
{
}
-BarSeries::BarLabel::BarLabel(QGraphicsScene *scene, const std::vector<QString> &labels, int bin_nr, int binCount) :
- totalWidth(0.0), totalHeight(0.0), isOutside(false)
+BarSeries::BarLabel::BarLabel(StatsView &view, const std::vector<QString> &labels, int bin_nr, int binCount) :
+ isOutside(false)
{
- items.reserve(labels.size());
- for (const QString &label: labels) {
- items.emplace_back(createItem<QGraphicsSimpleTextItem>(scene));
- items.back()->setText(label);
- items.back()->setZValue(ZValues::seriesLabels);
- QRectF rect = items.back()->boundingRect();
- if (rect.width() > totalWidth)
- totalWidth = rect.width();
- totalHeight += rect.height();
- }
- highlight(false, bin_nr, binCount);
+ QFont f; // make configurable
+ item = view.createChartItem<ChartTextItem>(ChartZValue::SeriesLabels, f, labels, true);
+ //highlight(false, bin_nr, binCount);
}
void BarSeries::BarLabel::setVisible(bool visible)
{
- for (auto &item: items)
- item->setVisible(visible);
+ item->setVisible(visible);
}
-void BarSeries::BarLabel::highlight(bool highlight, int bin_nr, int binCount)
+void BarSeries::BarLabel::highlight(bool highlight, int bin_nr, int binCount, const QColor &background)
{
- QBrush brush(highlight || isOutside ? darkLabelColor : labelColor(bin_nr, binCount));
- for (auto &item: items)
- item->setBrush(brush);
+ // For labels that are on top of a bar, use the corresponding bar color
+ // as background. Rendering on a transparent background gives ugly artifacts.
+ item->setColor(highlight || isOutside ? darkLabelColor : labelColor(bin_nr, binCount),
+ isOutside ? Qt::transparent : background);
}
void BarSeries::BarLabel::updatePosition(bool horizontal, bool center, const QRectF &rect,
- int bin_nr, int binCount)
+ int bin_nr, int binCount, const QColor &background)
{
+ QSizeF itemSize = item->getRect().size();
if (!horizontal) {
- if (totalWidth > rect.width()) {
+ if (itemSize.width() > rect.width()) {
setVisible(false);
return;
}
QPointF pos = rect.center();
+ pos.rx() -= round(itemSize.width() / 2.0);
// Heuristics: if the label fits nicely into the bar (bar height is at least twice the label height),
// then put the label in the middle of the bar. Otherwise, put it at the top of the bar.
- isOutside = !center && rect.height() < 2.0 * totalHeight;
+ isOutside = !center && rect.height() < 2.0 * itemSize.height();
if (isOutside) {
- pos.ry() = rect.top() - (totalHeight + 2.0); // Leave two pixels(?) space
+ pos.ry() = rect.top() - (itemSize.height() + 2.0); // Leave two pixels(?) space
} else {
- if (totalHeight > rect.height()) {
+ if (itemSize.height() > rect.height()) {
setVisible(false);
return;
}
- pos.ry() -= totalHeight / 2.0;
- }
- for (auto &it: items) {
- QPointF itemPos = pos;
- QRectF rect = it->boundingRect();
- itemPos.rx() -= rect.width() / 2.0;
- it->setPos(itemPos);
- pos.ry() += rect.height();
+ pos.ry() -= round(itemSize.height() / 2.0);
}
+ item->setPos(pos);
} else {
- if (totalHeight > rect.height()) {
+ if (itemSize.height() > rect.height()) {
setVisible(false);
return;
}
QPointF pos = rect.center();
- pos.ry() -= totalHeight / 2.0;
+ pos.ry() -= round(itemSize.height() / 2.0);
// Heuristics: if the label fits nicely into the bar (bar width is at least twice the label height),
// then put the label in the middle of the bar. Otherwise, put it to the right of the bar.
- isOutside = !center && rect.width() < 2.0 * totalWidth;
+ isOutside = !center && rect.width() < 2.0 * itemSize.width();
if (isOutside) {
- pos.rx() = rect.right() + (totalWidth / 2.0 + 2.0); // Leave two pixels(?) space
+ pos.rx() = round(rect.right() + 2.0); // Leave two pixels(?) space
} else {
- if (totalWidth > rect.width()) {
+ if (itemSize.width() > rect.width()) {
setVisible(false);
return;
}
}
- for (auto &it: items) {
- QPointF itemPos = pos;
- QRectF rect = it->boundingRect();
- itemPos.rx() -= rect.width() / 2.0;
- it->setPos(itemPos);
- pos.ry() += rect.height();
- }
+ item->setPos(pos);
}
setVisible(true);
// If label changed from inside to outside, or vice-versa, the color might change.
- highlight(false, bin_nr, binCount);
+ highlight(false, bin_nr, binCount, background);
}
-BarSeries::Item::Item(QGraphicsScene *scene, BarSeries *series, double lowerBound, double upperBound,
+BarSeries::Item::Item(BarSeries *series, double lowerBound, double upperBound,
std::vector<SubItem> subitemsIn,
const QString &binName, const StatsOperationResults &res, int total,
bool horizontal, bool stacked, int binCount) :
@@ -186,10 +170,8 @@ BarSeries::Item::Item(QGraphicsScene *scene, BarSeries *series, double lowerBoun
res(res),
total(total)
{
- for (SubItem &item: subitems) {
- item.item->setZValue(ZValues::series);
+ for (SubItem &item: subitems)
item.highlight(false, binCount);
- }
updatePosition(series, horizontal, stacked, binCount);
}
@@ -202,15 +184,11 @@ void BarSeries::Item::highlight(int subitem, bool highlight, int binCount)
void BarSeries::SubItem::highlight(bool highlight, int binCount)
{
- if (highlight) {
- item->setBrush(QBrush(highlightedColor));
- item->setPen(QPen(highlightedBorderColor));
- } else {
- item->setBrush(QBrush(binColor(bin_nr, binCount)));
- item->setPen(QPen(::borderColor));
- }
+ fill = highlight ? highlightedColor : binColor(bin_nr, binCount);
+ QColor border = highlight ? highlightedBorderColor : ::borderColor;
+ item->setColor(fill, border);
if (label)
- label->highlight(highlight, bin_nr, binCount);
+ label->highlight(highlight, bin_nr, binCount, fill);
}
void BarSeries::Item::updatePosition(BarSeries *series, bool horizontal, bool stacked, int binCount)
@@ -234,9 +212,9 @@ void BarSeries::Item::updatePosition(BarSeries *series, bool horizontal, bool st
double center = (idx + 0.5) * fullSubWidth + from;
item.updatePosition(series, horizontal, stacked, center - subWidth / 2.0, center + subWidth / 2.0, binCount);
}
- rect = subitems[0].item->rect();
+ rect = subitems[0].item->getRect();
for (auto it = std::next(subitems.begin()); it != subitems.end(); ++it)
- rect = rect.united(it->item->rect());
+ rect = rect.united(it->item->getRect());
}
void BarSeries::SubItem::updatePosition(BarSeries *series, bool horizontal, bool stacked,
@@ -253,7 +231,7 @@ void BarSeries::SubItem::updatePosition(BarSeries *series, bool horizontal, bool
QRectF rect(topLeft, bottomRight);
item->setRect(rect);
if (label)
- label->updatePosition(horizontal, stacked, rect, bin_nr, binCount);
+ label->updatePosition(horizontal, stacked, rect, bin_nr, binCount, fill);
}
std::vector<BarSeries::SubItem> BarSeries::makeSubItems(const std::vector<std::pair<double, std::vector<QString>>> &values) const
@@ -264,9 +242,10 @@ std::vector<BarSeries::SubItem> BarSeries::makeSubItems(const std::vector<std::p
int bin_nr = 0;
for (auto &[v, label]: values) {
if (v > 0.0) {
- res.push_back({ createItemPtr<QGraphicsRectItem>(scene), {}, from, from + v, bin_nr });
+ res.push_back({ view.createChartItem<ChartBarItem>(ChartZValue::Series, barBorderWidth, horizontal),
+ {}, from, from + v, bin_nr });
if (!label.empty())
- res.back().label = std::make_unique<BarLabel>(scene, label, bin_nr, binCount());
+ res.back().label = std::make_unique<BarLabel>(view, label, bin_nr, binCount());
}
if (stacked)
from += v;
@@ -292,7 +271,7 @@ void BarSeries::add_item(double lowerBound, double upperBound, std::vector<SubIt
// Don't add empty items, as that messes with the "find item under mouse" routine.
if (subitems.empty())
return;
- items.emplace_back(scene, this, lowerBound, upperBound, std::move(subitems), binName, res,
+ items.emplace_back(this, lowerBound, upperBound, std::move(subitems), binName, res,
total, horizontal, stacked, binCount());
}
@@ -322,10 +301,10 @@ int BarSeries::Item::getSubItemUnderMouse(const QPointF &point, bool horizontal,
// Search the first item whose "end" position is greater than the cursor position.
bool search_x = horizontal == stacked;
auto it = search_x ? std::lower_bound(subitems.begin(), subitems.end(), point.x(),
- [] (const SubItem &item, double x) { return item.item->rect().right() < x; })
+ [] (const SubItem &item, double x) { return item.item->getRect().right() < x; })
: std::lower_bound(subitems.begin(), subitems.end(), point.y(),
- [] (const SubItem &item, double y) { return item.item->rect().top() > y; });
- return it != subitems.end() && it->item->rect().contains(point) ? it - subitems.begin() : -1;
+ [] (const SubItem &item, double y) { return item.item->getRect().top() > y; });
+ return it != subitems.end() && it->item->getRect().contains(point) ? it - subitems.begin() : -1;
}
// Format information in a count-based bar chart.
@@ -403,10 +382,11 @@ bool BarSeries::hover(QPointF pos)
Item &item = items[highlighted.bar];
item.highlight(index.subitem, true, binCount());
if (!information)
- information = createItemPtr<InformationBox>(scene);
+ information = view.createChartItem<InformationBox>();
information->setText(makeInfo(item, highlighted.subitem), pos);
+ information->setVisible(true);
} else {
- information.reset();
+ information->setVisible(false);
}
return highlighted.bar >= 0;