statistics: implement primitive "restrict to selection" feature

Allow the user to restrict the analyzed dives based on the
current selection. One button restricts to the current selection
and one button resets the restriction.

Thus, the user can for example select bars in the bar chart
or a range in the scatter plot and perform statistics on
these sets.

The restriction works on top of the filter.

The UI can certainly be improved, but it is a start.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2021-02-12 10:56:48 +01:00 committed by Dirk Hohndel
parent 2943b1cbde
commit 5a8d7617ce
5 changed files with 97 additions and 3 deletions

View file

@ -84,6 +84,8 @@ StatsWidget::StatsWidget(QWidget *parent) : QWidget(parent)
connect(ui.var1Binner, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var1BinnerChanged); connect(ui.var1Binner, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var1BinnerChanged);
connect(ui.var2Binner, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2BinnerChanged); connect(ui.var2Binner, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2BinnerChanged);
connect(ui.var2Operation, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2OperationChanged); connect(ui.var2Operation, QOverload<int>::of(&QComboBox::activated), this, &StatsWidget::var2OperationChanged);
connect(ui.restrictButton, &QToolButton::clicked, this, &StatsWidget::restrict);
connect(ui.unrestrictButton, &QToolButton::clicked, this, &StatsWidget::unrestrict);
ui.stats->setSource(urlStatsView); ui.stats->setSource(urlStatsView);
ui.stats->setResizeMode(QQuickWidget::SizeRootObjectToView); ui.stats->setResizeMode(QQuickWidget::SizeRootObjectToView);
@ -141,6 +143,18 @@ void StatsWidget::updateUi()
view->plot(state); view->plot(state);
} }
void StatsWidget::updateRestrictionLabel()
{
if (!view)
return;
int num = view->restrictionCount();
if (num < 0)
ui.restrictionLabel->setText(tr("Analyzing all dives"));
else
ui.restrictionLabel->setText(tr("Analyzing subset (%L1) dives").arg(num));
ui.unrestrictButton->setEnabled(num > 0);
}
void StatsWidget::closeStats() void StatsWidget::closeStats()
{ {
MainWindow::instance()->setApplicationState(ApplicationState::Default); MainWindow::instance()->setApplicationState(ApplicationState::Default);
@ -192,6 +206,21 @@ void StatsWidget::featureChanged(int idx, bool status)
void StatsWidget::showEvent(QShowEvent *e) void StatsWidget::showEvent(QShowEvent *e)
{ {
unrestrict();
updateUi(); updateUi();
QWidget::showEvent(e); QWidget::showEvent(e);
} }
void StatsWidget::restrict()
{
if (view)
view->restrictToSelection();
updateRestrictionLabel();
}
void StatsWidget::unrestrict()
{
if (view)
view->unrestrict();
updateRestrictionLabel();
}

View file

@ -25,11 +25,14 @@ slots:
void var2BinnerChanged(int); void var2BinnerChanged(int);
void var2OperationChanged(int); void var2OperationChanged(int);
void featureChanged(int, bool); void featureChanged(int, bool);
void restrict();
void unrestrict();
private: private:
Ui::StatsWidget ui; Ui::StatsWidget ui;
StatsState state; StatsState state;
StatsView *view; StatsView *view;
void updateUi(); void updateUi();
void updateRestrictionLabel();
std::vector<std::unique_ptr<QCheckBox>> features; std::vector<std::unique_ptr<QCheckBox>> features;
ChartListModel charts; ChartListModel charts;

View file

@ -95,6 +95,32 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="restrictionGroup">
<property name="title">
<string>Restriction</string>
</property>
<layout class="QVBoxLayout" name="chartLayout">
<item>
<widget class="QLabel" name="restrictionLabel" />
</item>
<item>
<widget class="QToolButton" name="restrictButton">
<property name="text">
<string>Restrict to selection</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="unrestrictButton">
<property name="text">
<string>Reset restriction</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">

View file

@ -39,6 +39,7 @@ StatsView::StatsView(QQuickItem *parent) : QQuickItem(parent),
xAxis(nullptr), xAxis(nullptr),
yAxis(nullptr), yAxis(nullptr),
draggedItem(nullptr), draggedItem(nullptr),
restrictDives(false),
rootNode(nullptr) rootNode(nullptr)
{ {
setFlag(ItemHasContents, true); setFlag(ItemHasContents, true);
@ -496,6 +497,25 @@ void StatsView::reset()
grid.reset(); grid.reset();
} }
void StatsView::restrictToSelection()
{
restrictedDives = getDiveSelection();
std::sort(restrictedDives.begin(), restrictedDives.end()); // Sort by pointer for quick lookup
restrictDives = true;
plot(state);
}
void StatsView::unrestrict()
{
restrictDives = false;
plot(state);
}
int StatsView::restrictionCount() const
{
return restrictDives ? (int)restrictedDives.size() : -1;
}
void StatsView::plot(const StatsState &stateIn) void StatsView::plot(const StatsState &stateIn)
{ {
state = stateIn; state = stateIn;
@ -518,7 +538,19 @@ void StatsView::plotChart()
return; return;
reset(); reset();
const std::vector<dive *> dives = DiveFilter::instance()->visibleDives(); std::vector<dive *> dives;
if (restrictDives) {
std::vector<dive *> visible = DiveFilter::instance()->visibleDives();
dives.reserve(visible.size());
for (dive *d: visible) {
// binary search
auto it = std::lower_bound(restrictedDives.begin(), restrictedDives.end(), d);
if (it != restrictedDives.end() && *it == d)
dives.push_back(d);
}
} else {
dives = DiveFilter::instance()->visibleDives();
}
switch (state.type) { switch (state.type) {
case ChartType::DiscreteBar: case ChartType::DiscreteBar:
return plotBarChart(dives, state.subtype, state.var1, state.var1Binner, state.var2, return plotBarChart(dives, state.subtype, state.var1, state.var1Binner, state.var2,

View file

@ -46,13 +46,16 @@ public:
void plot(const StatsState &state); void plot(const StatsState &state);
void updateFeatures(const StatsState &state); // Updates the visibility of chart features, such as legend, regression, etc. void updateFeatures(const StatsState &state); // Updates the visibility of chart features, such as legend, regression, etc.
void restrictToSelection();
void unrestrict();
int restrictionCount() const; // <0: no restriction
QQuickWindow *w() const; // Make window available to items QQuickWindow *w() const; // Make window available to items
QSizeF size() const; QSizeF size() const;
QRectF plotArea() const; QRectF plotArea() const;
void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread! void addQSGNode(QSGNode *node, ChartZValue z); // Must only be called in render thread!
void registerChartItem(ChartItem &item); void registerChartItem(ChartItem &item);
void registerDirtyChartItem(ChartItem &item); void registerDirtyChartItem(ChartItem &item);
void emergencyShutdown(); // Called when QQuick decides to delete out root node. void emergencyShutdown(); // Called when QQuick decides to delete our root node.
// Create a chart item and add it to the scene. // Create a chart item and add it to the scene.
// The item must not be deleted by the caller, but can be // The item must not be deleted by the caller, but can be
@ -131,7 +134,6 @@ private:
// Helper functions to add feature to the chart // Helper functions to add feature to the chart
void addLineMarker(double pos, double low, double high, const QPen &pen, bool isHorizontal); void addLineMarker(double pos, double low, double high, const QPen &pen, bool isHorizontal);
StatsState state; StatsState state;
QFont titleFont; QFont titleFont;
std::vector<std::unique_ptr<StatsSeries>> series; std::vector<std::unique_ptr<StatsSeries>> series;
@ -148,6 +150,8 @@ private:
QPointF dragStartMouse, dragStartItem; QPointF dragStartMouse, dragStartItem;
SelectionModifier selectionModifier; SelectionModifier selectionModifier;
std::vector<dive *> oldSelection; std::vector<dive *> oldSelection;
bool restrictDives;
std::vector<dive *> restrictedDives; // sorted by pointer for quick lookup.
void hoverEnterEvent(QHoverEvent *event) override; void hoverEnterEvent(QHoverEvent *event) override;
void hoverMoveEvent(QHoverEvent *event) override; void hoverMoveEvent(QHoverEvent *event) override;