mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
statistics: add a model that describes a list of charts
Qt's comboboxes are controlled by models, there's no way around that. To customize the chart-selection widget this must therefore be abstracted into a model. On the upside, this hopefully can be used for desktop and mobile. The model provides icons and paints a warning-symbol on it if the statistics core code deems the chart to be not recommended. Notably, when plotting a categorical bar chart against a numerical value (in such a case histograms are preferred). Includes a fix for a silly oversight in CMakelist.txt: add the statstranslations.h header. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
fbb17871c9
commit
319a7af31a
3 changed files with 176 additions and 0 deletions
|
@ -9,6 +9,8 @@ set(SUBSURFACE_STATS_SRCS
|
|||
barseries.cpp
|
||||
boxseries.h
|
||||
boxseries.cpp
|
||||
chartlistmodel.h
|
||||
chartlistmodel.cpp
|
||||
informationbox.h
|
||||
informationbox.cpp
|
||||
legend.h
|
||||
|
@ -25,6 +27,7 @@ set(SUBSURFACE_STATS_SRCS
|
|||
statsseries.cpp
|
||||
statsstate.h
|
||||
statsstate.cpp
|
||||
statstranslations.h
|
||||
statsvariables.h
|
||||
statsvariables.cpp
|
||||
statsview.h
|
||||
|
|
119
stats/chartlistmodel.cpp
Normal file
119
stats/chartlistmodel.cpp
Normal file
|
@ -0,0 +1,119 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "chartlistmodel.h"
|
||||
#include "core/metrics.h"
|
||||
#include "core/qthelper.h"
|
||||
#include <QIcon>
|
||||
#include <QFontMetrics>
|
||||
#include <QPainter>
|
||||
|
||||
ChartListModel::ChartListModel() :
|
||||
itemFont(defaultModelFont()),
|
||||
headerFont(itemFont.family(), itemFont.pointSize(), itemFont.weight(), true)
|
||||
{
|
||||
QFontMetrics fm(itemFont);
|
||||
int fontHeight = fm.height();
|
||||
|
||||
int iconSize = fontHeight * 3;
|
||||
warningPixmap = QPixmap::fromImage(renderSVGIcon(":chart-warning-icon", fontHeight, true));
|
||||
initIcon(ChartSubType::Vertical, ":chart-bar-vertical-icon", iconSize);
|
||||
initIcon(ChartSubType::VerticalGrouped, ":chart-bar-grouped-vertical-icon", iconSize);
|
||||
initIcon(ChartSubType::VerticalStacked, ":chart-bar-stacked-vertical-icon", iconSize);
|
||||
initIcon(ChartSubType::Horizontal, ":chart-bar-horizontal-icon", iconSize);
|
||||
initIcon(ChartSubType::HorizontalGrouped, ":chart-bar-grouped-horizontal-icon", iconSize);
|
||||
initIcon(ChartSubType::HorizontalStacked, ":chart-bar-stacked-horizontal-icon", iconSize);
|
||||
initIcon(ChartSubType::Dots, ":chart-points-icon", iconSize);
|
||||
initIcon(ChartSubType::Box, ":chart-box-icon", iconSize);
|
||||
initIcon(ChartSubType::Pie, ":chart-pie-icon", iconSize);
|
||||
}
|
||||
|
||||
ChartListModel::~ChartListModel()
|
||||
{
|
||||
}
|
||||
|
||||
void ChartListModel::initIcon(ChartSubType type, const char *name, int iconSize)
|
||||
{
|
||||
QPixmap icon = QPixmap::fromImage(renderSVGIcon(name, iconSize, true));
|
||||
QPixmap iconWarning = icon.copy();
|
||||
QPainter painter(&iconWarning);
|
||||
painter.drawPixmap(0, 0, warningPixmap);
|
||||
subTypeIcons[(size_t)type].normal = icon;
|
||||
subTypeIcons[(size_t)type].warning = iconWarning;
|
||||
}
|
||||
|
||||
const QPixmap &ChartListModel::getIcon(ChartSubType type, bool warning) const
|
||||
{
|
||||
int idx = std::clamp((int)type, 0, (int)ChartSubType::Count);
|
||||
return warning ? subTypeIcons[idx].warning : subTypeIcons[idx].normal;
|
||||
}
|
||||
|
||||
int ChartListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : (int)items.size();
|
||||
}
|
||||
|
||||
Qt::ItemFlags ChartListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (index.parent().isValid() || row < 0 || row >= (int)items.size())
|
||||
return Qt::NoItemFlags;
|
||||
return items[row].isHeader ? Qt::ItemIsEnabled
|
||||
: Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
QVariant ChartListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (index.parent().isValid() || row < 0 || row >= (int)items.size())
|
||||
return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case Qt::FontRole:
|
||||
return items[row].isHeader ? headerFont : itemFont;
|
||||
case Qt::DisplayRole:
|
||||
return items[row].fullName;
|
||||
case Qt::DecorationRole:
|
||||
return items[row].warning ? QVariant::fromValue(QIcon(warningPixmap))
|
||||
: QVariant();
|
||||
case IconRole:
|
||||
return items[row].isHeader ? QVariant()
|
||||
: QVariant::fromValue(getIcon(items[row].subtype, items[row].warning));
|
||||
case IconSizeRole:
|
||||
return items[row].isHeader ? QVariant()
|
||||
: QVariant::fromValue(getIcon(items[row].subtype, items[row].warning).size());
|
||||
case ChartNameRole:
|
||||
return items[row].name;
|
||||
case IsHeaderRole:
|
||||
return items[row].isHeader;
|
||||
case Qt::UserRole:
|
||||
return items[row].id;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int ChartListModel::update(const StatsState::ChartList &charts)
|
||||
{
|
||||
// Sort non-recommended entries to the back
|
||||
std::vector<StatsState::Chart> sorted;
|
||||
sorted.reserve(charts.charts.size());
|
||||
std::copy_if(charts.charts.begin(), charts.charts.end(), std::back_inserter(sorted),
|
||||
[] (const StatsState::Chart &chart) { return !chart.warning; });
|
||||
std::copy_if(charts.charts.begin(), charts.charts.end(), std::back_inserter(sorted),
|
||||
[] (const StatsState::Chart &chart) { return chart.warning; });
|
||||
|
||||
beginResetModel();
|
||||
items.clear();
|
||||
QString act;
|
||||
int res = -1;
|
||||
for (const StatsState::Chart &chart: sorted) {
|
||||
if (act != chart.name) {
|
||||
items.push_back({ true, chart.name, QString(), (ChartSubType)-1, -1, false });
|
||||
act = chart.name;
|
||||
}
|
||||
if (charts.selected == chart.id)
|
||||
res = (int)items.size();
|
||||
QString fullName = QString("%1 / %2").arg(chart.name, chart.subtypeName);
|
||||
items.push_back({ false, chart.subtypeName, fullName, chart.subtype, chart.id, chart.warning });
|
||||
}
|
||||
endResetModel();
|
||||
return res;
|
||||
}
|
54
stats/chartlistmodel.h
Normal file
54
stats/chartlistmodel.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// A model to feed to the chart-selection combobox
|
||||
#ifndef CHART_LIST_MODEL_H
|
||||
#define CHART_LIST_MODEL_H
|
||||
|
||||
#include "statsstate.h"
|
||||
#include <vector>
|
||||
#include <QAbstractListModel>
|
||||
#include <QString>
|
||||
#include <QFont>
|
||||
#include <QIcon>
|
||||
#include <QPixmap>
|
||||
|
||||
class ChartListModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChartListModel();
|
||||
~ChartListModel();
|
||||
|
||||
// Returns index of selected item
|
||||
int update(const StatsState::ChartList &charts);
|
||||
|
||||
static const constexpr int ChartNameRole = Qt::UserRole + 1;
|
||||
static const constexpr int IsHeaderRole = Qt::UserRole + 2;
|
||||
static const constexpr int IconRole = Qt::UserRole + 3;
|
||||
static const constexpr int IconSizeRole = Qt::UserRole + 4;
|
||||
private:
|
||||
struct Item {
|
||||
bool isHeader;
|
||||
QString name;
|
||||
QString fullName;
|
||||
ChartSubType subtype;
|
||||
int id;
|
||||
bool warning;
|
||||
};
|
||||
|
||||
struct SubTypeIcons {
|
||||
QPixmap normal;
|
||||
QPixmap warning;
|
||||
};
|
||||
QPixmap warningPixmap;
|
||||
SubTypeIcons subTypeIcons[(size_t)ChartSubType::Count];
|
||||
|
||||
QFont itemFont;
|
||||
QFont headerFont;
|
||||
std::vector<Item> items;
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
void initIcon(ChartSubType type, const char *name, int iconSize);
|
||||
const QPixmap &getIcon(ChartSubType type, bool warning) const;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue