From f0ef9d6061ba91690fdde4479ef59366bc22aa71 Mon Sep 17 00:00:00 2001 From: Lieven Hey Date: Fri, 3 Nov 2023 13:05:47 +0100 Subject: [PATCH] allow event source combobox to select multiple costs This patch allows the combobox in the timelinedelegate to select multiple costs as source. --- src/models/eventmodelproxy.cpp | 21 +++++++++++++++++++++ src/models/eventmodelproxy.h | 7 +++++++ src/resultsutil.cpp | 32 ++++++++++++++++++++++++++++++++ src/resultsutil.h | 1 + src/timelinewidget.cpp | 23 ++++++++++++++++++++--- 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/models/eventmodelproxy.cpp b/src/models/eventmodelproxy.cpp index 182e6c393..45d91c98d 100644 --- a/src/models/eventmodelproxy.cpp +++ b/src/models/eventmodelproxy.cpp @@ -21,6 +21,18 @@ EventModelProxy::EventModelProxy(QObject* parent) EventModelProxy::~EventModelProxy() = default; +void EventModelProxy::showCostId(qint32 costId) +{ + m_hiddenCostIds.remove(costId); + invalidate(); +} + +void EventModelProxy::hideCostId(qint32 costId) +{ + m_hiddenCostIds.insert(costId); + invalidate(); +} + bool EventModelProxy::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { // index is invalid -> we are at the root node @@ -31,6 +43,15 @@ bool EventModelProxy::filterAcceptsRow(int source_row, const QModelIndex& source return false; } + auto data = sourceModel() + ->index(source_row, EventModel::EventsColumn, source_parent) + .data(EventModel::EventsRole) + .value(); + + if (!data.empty()) { + return !m_hiddenCostIds.contains(data[0].type); + } + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } diff --git a/src/models/eventmodelproxy.h b/src/models/eventmodelproxy.h index eaedf37a9..a382aa3c3 100644 --- a/src/models/eventmodelproxy.h +++ b/src/models/eventmodelproxy.h @@ -7,6 +7,7 @@ #pragma once +#include #include class EventModelProxy : public QSortFilterProxyModel @@ -16,7 +17,13 @@ class EventModelProxy : public QSortFilterProxyModel explicit EventModelProxy(QObject* parent = nullptr); ~EventModelProxy() override; + void showCostId(qint32 costId); + void hideCostId(qint32 costId); + protected: bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; + +private: + QSet m_hiddenCostIds; }; diff --git a/src/resultsutil.cpp b/src/resultsutil.cpp index 06c1b2e62..35cafc12e 100644 --- a/src/resultsutil.cpp +++ b/src/resultsutil.cpp @@ -18,6 +18,8 @@ #include #include +#include + #include "models/costdelegate.h" #include "models/data.h" #include "models/filterandzoomstack.h" @@ -191,6 +193,36 @@ void fillEventSourceComboBox(QComboBox* combo, const Data::Costs& costs, const Q } } +void fillEventSourceComboBoxMultiSelect(QComboBox* combo, const Data::Costs& costs, const QString& /*tooltipTemplate*/) +{ + // restore selection if possible + const auto oldData = combo->currentData(); + + combo->clear(); + + auto model = new QStandardItemModel(costs.numTypes(), 1, combo); + int itemCounter = 0; + for (int costId = 0, c = costs.numTypes(); costId < c; costId++) { + if (!costs.totalCost(costId)) { + continue; + } + + auto item = new QStandardItem(costs.typeName(costId)); + item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + item->setData(Qt::Checked, Qt::CheckStateRole); + item->setData(costId, Qt::UserRole + 1); + model->setItem(itemCounter, item); + itemCounter++; + } + model->setRowCount(itemCounter); + combo->setModel(model); + + const auto index = combo->findData(oldData); + if (index != -1) { + combo->setCurrentIndex(index); + } +} + void setupResultsAggregation(QComboBox* costAggregationComboBox) { struct AggregationType diff --git a/src/resultsutil.h b/src/resultsutil.h index 77288b73d..f81534df4 100644 --- a/src/resultsutil.h +++ b/src/resultsutil.h @@ -98,6 +98,7 @@ void hideEmptyColumns(const Data::Costs& costs, QTreeView* view, int numBaseColu void hideTracepointColumns(const Data::Costs& costs, QTreeView* view, int numBaseColumns); void fillEventSourceComboBox(QComboBox* combo, const Data::Costs& costs, const QString& tooltipTemplate); +void fillEventSourceComboBoxMultiSelect(QComboBox* combo, const Data::Costs& costs, const QString& tooltipTemplate); void setupResultsAggregation(QComboBox* costAggregationComboBox); } diff --git a/src/timelinewidget.cpp b/src/timelinewidget.cpp index bafc21743..9d92256e3 100644 --- a/src/timelinewidget.cpp +++ b/src/timelinewidget.cpp @@ -17,9 +17,11 @@ #include "parsers/perf/perfparser.h" #include +#include #include #include #include +#include #include #include @@ -82,9 +84,24 @@ TimeLineWidget::TimeLineWidget(PerfParser* parser, QMenu* filterMenu, FilterAndZ connect(timeLineProxy, &QAbstractItemModel::rowsInserted, this, [this]() { ui->timeLineView->expandToDepth(1); }); connect(timeLineProxy, &QAbstractItemModel::modelReset, this, [this]() { ui->timeLineView->expandToDepth(1); }); - connect(m_parser, &PerfParser::bottomUpDataAvailable, this, [this](const Data::BottomUpResults& data) { - ResultsUtil::fillEventSourceComboBox(ui->timeLineEventSource, data.costs, tr("Show timeline for %1 events.")); - }); + connect(m_parser, &PerfParser::bottomUpDataAvailable, this, + [this, timeLineProxy](const Data::BottomUpResults& data) { + ResultsUtil::fillEventSourceComboBoxMultiSelect(ui->timeLineEventSource, data.costs, + tr("Show timeline for %1 events.")); + + auto model = qobject_cast(ui->timeLineEventSource->model()); + connect(ui->timeLineEventSource->model(), &QStandardItemModel::dataChanged, model, + [timeLineProxy](const QModelIndex& topLeft, const QModelIndex& /*bottomRight*/, + const QVector& /*roles*/) { + auto checkState = topLeft.data(Qt::CheckStateRole).value(); + + if (checkState == Qt::CheckState::Checked) { + timeLineProxy->showCostId(topLeft.data(Qt::UserRole + 1).toUInt()); + } else { + timeLineProxy->hideCostId(topLeft.data(Qt::UserRole + 1).toUInt()); + } + }); + }); connect(m_parser, &PerfParser::eventsAvailable, this, [this, eventModel](const Data::EventResults& data) { eventModel->setData(data);