mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
statistics: draw title of axes
Easy enough to implement, but one weirdness: To get the height of the rotated text, one has to access the width() member of the boundingRect. I'm not sure if that makes sense, but so be it. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
4ab9f1c6b0
commit
8dfa3f6db3
3 changed files with 55 additions and 29 deletions
|
@ -23,10 +23,10 @@ static const double axisLabelSpaceVertical = 2.0; // Space between axis or ticks
|
||||||
static const double axisTitleSpaceHorizontal = 2.0; // Space between labels and title
|
static const double axisTitleSpaceHorizontal = 2.0; // Space between labels and title
|
||||||
static const double axisTitleSpaceVertical = 2.0; // Space between labels and title
|
static const double axisTitleSpaceVertical = 2.0; // Space between labels and title
|
||||||
|
|
||||||
StatsAxis::StatsAxis(QtCharts::QChart *chart, bool horizontal, bool labelsBetweenTicks) :
|
StatsAxis::StatsAxis(QtCharts::QChart *chart, const QString &titleIn, bool horizontal, bool labelsBetweenTicks) :
|
||||||
QGraphicsLineItem(chart),
|
QGraphicsLineItem(chart),
|
||||||
chart(chart), horizontal(horizontal), labelsBetweenTicks(labelsBetweenTicks),
|
chart(chart), horizontal(horizontal), labelsBetweenTicks(labelsBetweenTicks),
|
||||||
size(1.0), zeroOnScreen(0.0), min(0.0), max(1.0)
|
size(1.0), zeroOnScreen(0.0), min(0.0), max(1.0), labelWidth(0.0)
|
||||||
{
|
{
|
||||||
// use a Light version of the application fond for both labels and title
|
// use a Light version of the application fond for both labels and title
|
||||||
labelFont = QFont();
|
labelFont = QFont();
|
||||||
|
@ -34,6 +34,13 @@ StatsAxis::StatsAxis(QtCharts::QChart *chart, bool horizontal, bool labelsBetwee
|
||||||
titleFont = labelFont;
|
titleFont = labelFont;
|
||||||
setPen(QPen(axisColor, axisWidth));
|
setPen(QPen(axisColor, axisWidth));
|
||||||
setZValue(ZValues::axes);
|
setZValue(ZValues::axes);
|
||||||
|
if (!titleIn.isEmpty()) {
|
||||||
|
title = std::make_unique<QGraphicsSimpleTextItem>(titleIn, chart);
|
||||||
|
title->setFont(titleFont);
|
||||||
|
title->setBrush(darkLabelColor);
|
||||||
|
if (!horizontal)
|
||||||
|
title->setRotation(-90.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatsAxis::~StatsAxis()
|
StatsAxis::~StatsAxis()
|
||||||
|
@ -76,18 +83,19 @@ int StatsAxis::guessNumTicks(const std::vector<QString> &strings) const
|
||||||
return std::max(numTicks, 2);
|
return std::max(numTicks, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double StatsAxis::titleSpace() const
|
||||||
|
{
|
||||||
|
if (!title)
|
||||||
|
return 0.0;
|
||||||
|
return horizontal ? QFontMetrics(titleFont).height() + axisTitleSpaceHorizontal
|
||||||
|
: QFontMetrics(titleFont).height() + axisTitleSpaceVertical;
|
||||||
|
}
|
||||||
|
|
||||||
double StatsAxis::width() const
|
double StatsAxis::width() const
|
||||||
{
|
{
|
||||||
if (horizontal)
|
if (horizontal)
|
||||||
return 0.0; // Only supported for vertical axes
|
return 0.0; // Only supported for vertical axes
|
||||||
double labelWidth = 0.0;
|
return labelWidth + axisLabelSpaceVertical + titleSpace() +
|
||||||
for (const Label &label: labels) {
|
|
||||||
double w = label.label->boundingRect().width();
|
|
||||||
if (w > labelWidth)
|
|
||||||
labelWidth = w;
|
|
||||||
}
|
|
||||||
return labelWidth + axisLabelSpaceVertical +
|
|
||||||
QFontMetrics(titleFont).height() + axisTitleSpaceVertical +
|
|
||||||
(labelsBetweenTicks ? 0.0 : axisTickSizeVertical);
|
(labelsBetweenTicks ? 0.0 : axisTickSizeVertical);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +104,7 @@ double StatsAxis::height() const
|
||||||
if (!horizontal)
|
if (!horizontal)
|
||||||
return 0.0; // Only supported for horizontal axes
|
return 0.0; // Only supported for horizontal axes
|
||||||
return QFontMetrics(labelFont).height() + axisLabelSpaceHorizontal +
|
return QFontMetrics(labelFont).height() + axisLabelSpaceHorizontal +
|
||||||
QFontMetrics(titleFont).height() + axisTitleSpaceHorizontal +
|
titleSpace() +
|
||||||
(labelsBetweenTicks ? 0.0 : axisTickSizeHorizontal);
|
(labelsBetweenTicks ? 0.0 : axisTickSizeHorizontal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,6 +154,12 @@ void StatsAxis::setSize(double sizeIn)
|
||||||
{
|
{
|
||||||
size = sizeIn;
|
size = sizeIn;
|
||||||
updateLabels();
|
updateLabels();
|
||||||
|
labelWidth = 0.0;
|
||||||
|
for (const Label &label: labels) {
|
||||||
|
double w = label.label->boundingRect().width();
|
||||||
|
if (w > labelWidth)
|
||||||
|
labelWidth = w;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatsAxis::setPos(QPointF pos)
|
void StatsAxis::setPos(QPointF pos)
|
||||||
|
@ -164,6 +178,9 @@ void StatsAxis::setPos(QPointF pos)
|
||||||
tick.item->setLine(x, y, x, y + axisTickSizeHorizontal);
|
tick.item->setLine(x, y, x, y + axisTickSizeHorizontal);
|
||||||
}
|
}
|
||||||
setLine(zeroOnScreen, y, zeroOnScreen + size, y);
|
setLine(zeroOnScreen, y, zeroOnScreen + size, y);
|
||||||
|
if (title)
|
||||||
|
title->setPos(zeroOnScreen + (size - title->boundingRect().width()) / 2.0,
|
||||||
|
labelY + QFontMetrics(labelFont).height() + axisTitleSpaceHorizontal);
|
||||||
} else {
|
} else {
|
||||||
double fontHeight = QFontMetrics(labelFont).height();
|
double fontHeight = QFontMetrics(labelFont).height();
|
||||||
zeroOnScreen = pos.y();
|
zeroOnScreen = pos.y();
|
||||||
|
@ -178,12 +195,18 @@ void StatsAxis::setPos(QPointF pos)
|
||||||
double y = toScreen(tick.pos);
|
double y = toScreen(tick.pos);
|
||||||
tick.item->setLine(x, y, x - axisTickSizeVertical, y);
|
tick.item->setLine(x, y, x - axisTickSizeVertical, y);
|
||||||
}
|
}
|
||||||
|
// This is very confusing: even though we need the height of the title, the correct
|
||||||
|
// size is stored in boundingRect().width(). Presumably because the item is rotated
|
||||||
|
// by -90°. Apparently, the boundingRect is in item-local coordinates?
|
||||||
|
if (title)
|
||||||
|
title->setPos(labelX - labelWidth - QFontMetrics(labelFont).height() - axisTitleSpaceVertical,
|
||||||
|
zeroOnScreen - (size - title->boundingRect().width()) / 2.0);
|
||||||
setLine(x, zeroOnScreen, x, zeroOnScreen - size);
|
setLine(x, zeroOnScreen, x, zeroOnScreen - size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueAxis::ValueAxis(QtCharts::QChart *chart, double min, double max, int decimals, bool horizontal) :
|
ValueAxis::ValueAxis(QtCharts::QChart *chart, const QString &title, double min, double max, int decimals, bool horizontal) :
|
||||||
StatsAxis(chart, horizontal, false),
|
StatsAxis(chart, title, horizontal, false),
|
||||||
min(min), max(max), decimals(decimals)
|
min(min), max(max), decimals(decimals)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -237,8 +260,8 @@ void ValueAxis::updateLabels()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CountAxis::CountAxis(QtCharts::QChart *chart, int count, bool horizontal) :
|
CountAxis::CountAxis(QtCharts::QChart *chart, const QString &title, int count, bool horizontal) :
|
||||||
ValueAxis(chart, 0.0, (double)count, 0, horizontal),
|
ValueAxis(chart, title, 0.0, (double)count, 0, horizontal),
|
||||||
count(count)
|
count(count)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -290,8 +313,8 @@ void CountAxis::updateLabels()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CategoryAxis::CategoryAxis(QtCharts::QChart *chart, const std::vector<QString> &labelsIn, bool horizontal) :
|
CategoryAxis::CategoryAxis(QtCharts::QChart *chart, const QString &title, const std::vector<QString> &labelsIn, bool horizontal) :
|
||||||
StatsAxis(chart, horizontal, true)
|
StatsAxis(chart, title, horizontal, true)
|
||||||
{
|
{
|
||||||
labels.reserve(labelsIn.size());
|
labels.reserve(labelsIn.size());
|
||||||
ticks.reserve(labelsIn.size() + 1);
|
ticks.reserve(labelsIn.size() + 1);
|
||||||
|
@ -309,8 +332,8 @@ void CategoryAxis::updateLabels()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
HistogramAxis::HistogramAxis(QtCharts::QChart *chart, std::vector<HistogramAxisEntry> bins, bool horizontal) :
|
HistogramAxis::HistogramAxis(QtCharts::QChart *chart, const QString &title, std::vector<HistogramAxisEntry> bins, bool horizontal) :
|
||||||
StatsAxis(chart, horizontal, false),
|
StatsAxis(chart, title, horizontal, false),
|
||||||
bin_values(std::move(bins))
|
bin_values(std::move(bins))
|
||||||
{
|
{
|
||||||
if (bin_values.size() < 2) // Less than two makes no sense -> there must be at least one category
|
if (bin_values.size() < 2) // Less than two makes no sense -> there must be at least one category
|
||||||
|
@ -498,7 +521,7 @@ static std::vector<HistogramAxisEntry> timeRangeToBins(double from, double to)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
DateAxis::DateAxis(QtCharts::QChart *chart, double from, double to, bool horizontal) :
|
DateAxis::DateAxis(QtCharts::QChart *chart, const QString &title, double from, double to, bool horizontal) :
|
||||||
HistogramAxis(chart, timeRangeToBins(from, to), horizontal)
|
HistogramAxis(chart, title, timeRangeToBins(from, to), horizontal)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public:
|
||||||
double toScreen(double) const;
|
double toScreen(double) const;
|
||||||
double toValue(double) const;
|
double toValue(double) const;
|
||||||
protected:
|
protected:
|
||||||
StatsAxis(QtCharts::QChart *chart, bool horizontal, bool labelsBetweenTicks);
|
StatsAxis(QtCharts::QChart *chart, const QString &title, bool horizontal, bool labelsBetweenTicks);
|
||||||
QtCharts::QChart *chart;
|
QtCharts::QChart *chart;
|
||||||
|
|
||||||
struct Label {
|
struct Label {
|
||||||
|
@ -56,14 +56,18 @@ protected:
|
||||||
bool labelsBetweenTicks; // When labels are between ticks, they can be moved closer to the axis
|
bool labelsBetweenTicks; // When labels are between ticks, they can be moved closer to the axis
|
||||||
|
|
||||||
QFont labelFont, titleFont;
|
QFont labelFont, titleFont;
|
||||||
|
std::unique_ptr<QGraphicsSimpleTextItem> title;
|
||||||
double size; // width for horizontal, height for vertical
|
double size; // width for horizontal, height for vertical
|
||||||
double zeroOnScreen;
|
double zeroOnScreen;
|
||||||
double min, max;
|
double min, max;
|
||||||
|
double labelWidth; // Maximum width of labels
|
||||||
|
private:
|
||||||
|
double titleSpace() const; // Space needed for title
|
||||||
};
|
};
|
||||||
|
|
||||||
class ValueAxis : public StatsAxis {
|
class ValueAxis : public StatsAxis {
|
||||||
public:
|
public:
|
||||||
ValueAxis(QtCharts::QChart *chart, double min, double max, int decimals, bool horizontal);
|
ValueAxis(QtCharts::QChart *chart, const QString &title, double min, double max, int decimals, bool horizontal);
|
||||||
private:
|
private:
|
||||||
double min, max;
|
double min, max;
|
||||||
int decimals;
|
int decimals;
|
||||||
|
@ -72,7 +76,7 @@ private:
|
||||||
|
|
||||||
class CountAxis : public ValueAxis {
|
class CountAxis : public ValueAxis {
|
||||||
public:
|
public:
|
||||||
CountAxis(QtCharts::QChart *chart, int count, bool horizontal);
|
CountAxis(QtCharts::QChart *chart, const QString &title, int count, bool horizontal);
|
||||||
private:
|
private:
|
||||||
int count;
|
int count;
|
||||||
void updateLabels() override;
|
void updateLabels() override;
|
||||||
|
@ -80,7 +84,7 @@ private:
|
||||||
|
|
||||||
class CategoryAxis : public StatsAxis {
|
class CategoryAxis : public StatsAxis {
|
||||||
public:
|
public:
|
||||||
CategoryAxis(QtCharts::QChart *chart, const std::vector<QString> &labels, bool horizontal);
|
CategoryAxis(QtCharts::QChart *chart, const QString &title, const std::vector<QString> &labels, bool horizontal);
|
||||||
private:
|
private:
|
||||||
void updateLabels();
|
void updateLabels();
|
||||||
};
|
};
|
||||||
|
@ -93,7 +97,7 @@ struct HistogramAxisEntry {
|
||||||
|
|
||||||
class HistogramAxis : public StatsAxis {
|
class HistogramAxis : public StatsAxis {
|
||||||
public:
|
public:
|
||||||
HistogramAxis(QtCharts::QChart *chart, std::vector<HistogramAxisEntry> bin_values, bool horizontal);
|
HistogramAxis(QtCharts::QChart *chart, const QString &title, std::vector<HistogramAxisEntry> bin_values, bool horizontal);
|
||||||
private:
|
private:
|
||||||
void updateLabels() override;
|
void updateLabels() override;
|
||||||
std::vector<HistogramAxisEntry> bin_values;
|
std::vector<HistogramAxisEntry> bin_values;
|
||||||
|
@ -102,7 +106,7 @@ private:
|
||||||
|
|
||||||
class DateAxis : public HistogramAxis {
|
class DateAxis : public HistogramAxis {
|
||||||
public:
|
public:
|
||||||
DateAxis(QtCharts::QChart *chart, double from, double to, bool horizontal);
|
DateAxis(QtCharts::QChart *chart, const QString &title, double from, double to, bool horizontal);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -186,8 +186,7 @@ void StatsView::updateTitlePos()
|
||||||
template <typename T, class... Args>
|
template <typename T, class... Args>
|
||||||
T *StatsView::createAxis(const QString &title, Args&&... args)
|
T *StatsView::createAxis(const QString &title, Args&&... args)
|
||||||
{
|
{
|
||||||
// TODO: set title
|
T *res = new T(chart, title, std::forward<Args>(args)...);
|
||||||
T *res = new T(chart, std::forward<Args>(args)...);
|
|
||||||
axes.emplace_back(res);
|
axes.emplace_back(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue