diff options
-rw-r--r-- | desktop-widgets/statswidget.cpp | 208 | ||||
-rw-r--r-- | desktop-widgets/statswidget.h | 38 | ||||
-rw-r--r-- | desktop-widgets/statswidget.ui | 131 |
3 files changed, 377 insertions, 0 deletions
diff --git a/desktop-widgets/statswidget.cpp b/desktop-widgets/statswidget.cpp new file mode 100644 index 000000000..874a6db15 --- /dev/null +++ b/desktop-widgets/statswidget.cpp @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "statswidget.h" +#include "mainwindow.h" +#include "stats/statsview.h" +#include <QCheckBox> +#include <QPainter> +#include <QStyledItemDelegate> + +class ChartItemDelegate : public QStyledItemDelegate { +private: + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; +}; + +// Number of pixels that non-toplevel items are indented +static int indent(const QFontMetrics &fm) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) + return 4 * fm.width('-'); +#else + return 4 * fm.horizontalAdvance('-'); +#endif +} + +static const int iconSpace = 2; // Number of pixels between icon and text +static const int topSpace = 2; // Number of pixels above icon + +void ChartItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QFont font = index.data(Qt::FontRole).value<QFont>(); + QFontMetrics fm(font); + QString name = index.data(ChartListModel::ChartNameRole).value<QString>(); + painter->setFont(font); + QRect rect = option.rect; + if (option.state & QStyle::State_Selected) { + painter->save(); + painter->setBrush(option.palette.highlight()); + painter->setPen(Qt::NoPen); + painter->drawRect(rect); + painter->restore(); + } + bool isHeader = index.data(ChartListModel::IsHeaderRole).value<bool>(); + if (!isHeader) + rect.translate(indent(fm), 0); + QPixmap icon = index.data(ChartListModel::IconRole).value<QPixmap>(); + if (!icon.isNull()) { + rect.translate(0, topSpace); + painter->drawPixmap(rect.topLeft(), icon); + rect.translate(icon.size().width() + iconSpace, (icon.size().height() - fm.height()) / 2); + } + + painter->drawText(rect, name); +} + +QSize ChartItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QFont font = index.data(Qt::FontRole).value<QFont>(); + QFontMetrics fm(font); + QString name = index.data(ChartListModel::ChartNameRole).value<QString>(); + QSize iconSize = index.data(ChartListModel::IconSizeRole).value<QSize>(); + QSize size = fm.size(Qt::TextSingleLine, name); + bool isHeader = index.data(ChartListModel::IsHeaderRole).value<bool>(); + if (!isHeader) + size += QSize(indent(fm), 0); + if (iconSize.isValid()) + size = QSize(size.width() + iconSize.width() + iconSpace, + std::max(size.height(), iconSize.height()) + 2 * topSpace); + return size; +} + +StatsWidget::StatsWidget(QWidget *parent) : QWidget(parent) +{ + ui.setupUi(this); + + connect(ui.close, &QToolButton::clicked, this, &StatsWidget::closeStats); + + ui.chartType->setModel(&charts); + connect(ui.chartType, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::chartTypeChanged); + connect(ui.var1, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var1Changed); + connect(ui.var2, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2Changed); + connect(ui.var1Binner, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var1BinnerChanged); + connect(ui.var2Binner, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2BinnerChanged); + connect(ui.var2Operation, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2OperationChanged); +} + +// Initialize QComboBox with list of variables +static void setVariableList(QComboBox *combo, const StatsState::VariableList &list) +{ + combo->clear(); + for (const StatsState::Variable &v: list.variables) + combo->addItem(v.name, QVariant(v.id)); + combo->setCurrentIndex(list.selected); +} + +// Initialize QComboBox and QLabel of binners. Hide if there are no binners. +static void setBinList(QLabel *label, QComboBox *combo, const QString &varName, const StatsState::BinnerList &list) +{ + combo->clear(); + if (list.binners.empty()) { + label->hide(); + combo->hide(); + return; + } + + label->show(); + combo->show(); + + for (const QString &s: list.binners) + combo->addItem(s); + combo->setCurrentIndex(list.selected); +} + +// Initialize QComboBox and QLabel of operations. Hide if there are no operations. +static void setOperationList(QLabel *label, QComboBox *combo, const QString &varName, const StatsState::VariableList &list) +{ + combo->clear(); + if (list.variables.empty()) { + label->hide(); + combo->hide(); + return; + } + + label->show(); + combo->show(); + + setVariableList(combo, list); +} + +void StatsWidget::updateUi() +{ + StatsState::UIState uiState = state.getUIState(); + setVariableList(ui.var1, uiState.var1); + setVariableList(ui.var2, uiState.var2); + int pos = charts.update(uiState.charts); + ui.chartType->setCurrentIndex(pos); + ui.chartType->setItemDelegate(new ChartItemDelegate); + setBinList(ui.var1BinnerLabel, ui.var1Binner, uiState.var1Name, uiState.binners1); + setBinList(ui.var2BinnerLabel, ui.var2Binner, uiState.var2Name, uiState.binners2); + setOperationList(ui.var2OperationLabel, ui.var2Operation, uiState.var2Name, uiState.operations2); + + // Add checkboxes for additional features + features.clear(); + for (const StatsState::Feature &f: uiState.features) { + features.emplace_back(new QCheckBox(f.name)); + QCheckBox *check = features.back().get(); + check->setChecked(f.selected); + int id = f.id; + connect(check, &QCheckBox::stateChanged, [this,id] (int state) { featureChanged(id, state); }); + ui.features->addWidget(check); + } + + ui.stats->plot(state); +} + +void StatsWidget::closeStats() +{ + MainWindow::instance()->setApplicationState(ApplicationState::Default); +} + +void StatsWidget::chartTypeChanged(int idx) +{ + state.chartChanged(ui.chartType->itemData(idx).toInt()); + updateUi(); +} + +void StatsWidget::var1Changed(int idx) +{ + state.var1Changed(ui.var1->itemData(idx).toInt()); + updateUi(); +} + +void StatsWidget::var2Changed(int idx) +{ + state.var2Changed(ui.var2->itemData(idx).toInt()); + updateUi(); +} + +void StatsWidget::var1BinnerChanged(int idx) +{ + state.binner1Changed(idx); + updateUi(); +} + +void StatsWidget::var2BinnerChanged(int idx) +{ + state.binner2Changed(idx); + updateUi(); +} + +void StatsWidget::var2OperationChanged(int idx) +{ + state.var2OperationChanged(ui.var2Operation->itemData(idx).toInt()); + updateUi(); +} + +void StatsWidget::featureChanged(int idx, bool status) +{ + state.featureChanged(idx, status); + updateUi(); +} + +void StatsWidget::showEvent(QShowEvent *e) +{ + updateUi(); + QWidget::showEvent(e); +} diff --git a/desktop-widgets/statswidget.h b/desktop-widgets/statswidget.h new file mode 100644 index 000000000..40e6d106c --- /dev/null +++ b/desktop-widgets/statswidget.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef STATSWIDGET_H +#define STATSWIDGET_H + +#include "stats/statsstate.h" +#include "stats/chartlistmodel.h" +#include "ui_statswidget.h" +#include <vector> +#include <memory> + +class QCheckBox; + +class StatsWidget : public QWidget { + Q_OBJECT +public: + StatsWidget(QWidget *parent = 0); +private +slots: + void closeStats(); + void chartTypeChanged(int); + void var1Changed(int); + void var2Changed(int); + void var1BinnerChanged(int); + void var2BinnerChanged(int); + void var2OperationChanged(int); + void featureChanged(int, bool); +private: + Ui::StatsWidget ui; + StatsState state; + void updateUi(); + std::vector<std::unique_ptr<QCheckBox>> features; + + ChartListModel charts; + //QStringListModel charts; + void showEvent(QShowEvent *) override; +}; + +#endif // STATSWIDGET_H diff --git a/desktop-widgets/statswidget.ui b/desktop-widgets/statswidget.ui new file mode 100644 index 000000000..684b0fa58 --- /dev/null +++ b/desktop-widgets/statswidget.ui @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>StatsWidget</class> + <widget class="QWidget" name="Statistics"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>555</width> + <height>848</height> + </rect> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="headerLayout"> + <item> + <widget class="QToolButton" name="close"> + <property name="text"> + <string>Close</string> + </property> + <property name="icon"> + <iconset><normaloff>:filter-close</normaloff>:filter-close</iconset> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="var1Group"> + <property name="title"> + <string>Base variable</string> + </property> + <layout class="QVBoxLayout" name="var1Layout"> + <item> + <widget class="QComboBox" name="var1" /> + </item> + <item> + <widget class="QLabel" name="var1BinnerLabel"> + <property name="text"> + <string>Binning</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="var1Binner" /> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="var2Group"> + <property name="title"> + <string>Data</string> + </property> + <layout class="QVBoxLayout" name="var2Layout"> + <item> + <widget class="QComboBox" name="var2" /> + </item> + <item> + <widget class="QLabel" name="var2BinnerLabel"> + <property name="text"> + <string>Binning</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="var2Binner" /> + </item> + <item> + <widget class="QLabel" name="var2OperationLabel"> + <property name="text"> + <string>Operation</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="var2Operation" /> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="chartGroup"> + <property name="title"> + <string>Chart</string> + </property> + <layout class="QVBoxLayout" name="chartLayout"> + <item> + <widget class="QComboBox" name="chartType" /> + </item> + <item> + <layout class="QVBoxLayout" name="features" /> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="StatsView" name="stats"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>StatsView</class> + <extends>QQuickWidget</extends> + <header>stats/statsview.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources> + <include location="../subsurface.qrc"/> + </resources> + <connections/> +</ui> |