Skip to content

Commit dfbdb76

Browse files
committed
add Favourites to TimeLineWidget
this allows the user to group important timelines together so that he can compare them better
1 parent 77e0897 commit dfbdb76

File tree

6 files changed

+214
-61
lines changed

6 files changed

+214
-61
lines changed

src/models/eventmodel.cpp

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ enum class Tag : quint8
2424
Processes,
2525
Threads,
2626
Tracepoints,
27+
Favorites,
2728
};
2829

2930
enum OverviewRow : quint8
3031
{
3132
CpuRow,
3233
ProcessRow,
3334
TracepointRow,
35+
FavoriteRow,
3436
};
35-
constexpr auto numRows = TracepointRow + 1;
37+
constexpr auto numRows = FavoriteRow + 1;
3638

37-
constexpr auto LAST_TAG = Tag::Tracepoints;
39+
constexpr auto LAST_TAG = Tag::Favorites;
3840

3941
const auto DATATAG_SHIFT = sizeof(Tag) * 8;
4042
const auto DATATAG_UNSHIFT = (sizeof(quintptr) - sizeof(Tag)) * 8;
@@ -88,6 +90,7 @@ int EventModel::rowCount(const QModelIndex& parent) const
8890
case Tag::Cpus:
8991
case Tag::Threads:
9092
case Tag::Tracepoints:
93+
case Tag::Favorites:
9194
return 0;
9295
case Tag::Processes:
9396
return m_processes.value(parent.row()).threads.size();
@@ -99,6 +102,8 @@ int EventModel::rowCount(const QModelIndex& parent) const
99102
return m_processes.size();
100103
case OverviewRow::TracepointRow:
101104
return m_data.tracepoints.size();
105+
case OverviewRow::FavoriteRow:
106+
return m_favourites.size();
102107
}
103108
Q_UNREACHABLE();
104109
case Tag::Root:
@@ -166,6 +171,8 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
166171
return tr("Processes");
167172
case OverviewRow::TracepointRow:
168173
return tr("Tracepoints");
174+
case OverviewRow::FavoriteRow:
175+
return tr("Favorites");
169176
}
170177
} else if (role == Qt::ToolTipRole) {
171178
switch (static_cast<OverviewRow>(index.row())) {
@@ -176,6 +183,8 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
176183
return tr("Event timelines for the individual threads and processes.");
177184
case OverviewRow::TracepointRow:
178185
return tr("Event timelines for tracepoints");
186+
case OverviewRow::FavoriteRow:
187+
return tr("A list of favourites to group important events");
179188
}
180189
} else if (role == SortRole) {
181190
return index.row();
@@ -243,6 +252,13 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
243252
Q_ASSERT(thread);
244253
} else if (tag == Tag::Tracepoints) {
245254
tracepoint = &m_data.tracepoints[index.row()];
255+
} else if (tag == Tag::Favorites) {
256+
if (role == IsFavoriteRole) {
257+
return true;
258+
}
259+
260+
const auto& favourite = m_favourites[index.row()];
261+
return data(favourite.siblingAtColumn(index.column()), role);
246262
}
247263

248264
if (role == ThreadStartRole) {
@@ -256,6 +272,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
256272
case Tag::Processes:
257273
case Tag::Tracepoints:
258274
return m_time.start;
275+
case Tag::Favorites:
276+
// they are handled elsewhere
277+
Q_UNREACHABLE();
259278
}
260279
} else if (role == ThreadEndRole) {
261280
switch (tag) {
@@ -268,6 +287,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
268287
case Tag::Processes:
269288
case Tag::Tracepoints:
270289
return m_time.end;
290+
case Tag::Favorites:
291+
// they are handled elsewhere
292+
Q_UNREACHABLE();
271293
}
272294
} else if (role == ThreadNameRole) {
273295
switch (tag) {
@@ -281,6 +303,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
281303
case Tag::Processes:
282304
case Tag::Tracepoints:
283305
return {};
306+
case Tag::Favorites:
307+
// they are handled elsewhere
308+
Q_UNREACHABLE();
284309
}
285310
} else if (role == ThreadIdRole) {
286311
switch (tag) {
@@ -293,6 +318,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
293318
case Tag::Processes:
294319
case Tag::Tracepoints:
295320
return Data::INVALID_TID;
321+
case Tag::Favorites:
322+
// they are handled elsewhere
323+
Q_UNREACHABLE();
296324
}
297325
} else if (role == ProcessIdRole) {
298326
switch (tag) {
@@ -305,6 +333,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
305333
case Tag::Processes:
306334
case Tag::Tracepoints:
307335
return Data::INVALID_PID;
336+
case Tag::Favorites:
337+
// they are handled elsewhere
338+
Q_UNREACHABLE();
308339
}
309340
} else if (role == CpuIdRole) {
310341
switch (tag) {
@@ -317,6 +348,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
317348
case Tag::Threads:
318349
case Tag::Tracepoints:
319350
return Data::INVALID_CPU_ID;
351+
case Tag::Favorites:
352+
// they are handled elsewhere
353+
Q_UNREACHABLE();
320354
}
321355
} else if (role == EventsRole) {
322356
switch (tag) {
@@ -331,6 +365,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
331365
case Tag::Overview:
332366
case Tag::Processes:
333367
return {};
368+
case Tag::Favorites:
369+
// they are handled elsewhere
370+
Q_UNREACHABLE();
334371
}
335372
} else if (role == SortRole) {
336373
if (index.column() == ThreadColumn) {
@@ -346,6 +383,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
346383
case Tag::Overview:
347384
case Tag::Processes:
348385
return {};
386+
case Tag::Favorites:
387+
// they are handled elsewhere
388+
Q_UNREACHABLE();
349389
}
350390
} else {
351391
switch (tag) {
@@ -360,8 +400,13 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
360400
case Tag::Overview:
361401
case Tag::Processes:
362402
return {};
403+
case Tag::Favorites:
404+
// they are handled elsewhere
405+
Q_UNREACHABLE();
363406
}
364407
}
408+
} else if (role == IsFavoriteRole) {
409+
return false;
365410
}
366411

367412
switch (static_cast<Columns>(index.column())) {
@@ -379,6 +424,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
379424
case Tag::Overview:
380425
case Tag::Processes:
381426
return {};
427+
case Tag::Favorites:
428+
// they are handled elsewhere
429+
Q_UNREACHABLE();
382430
}
383431
} else if (role == Qt::ToolTipRole) {
384432
QString tooltip;
@@ -419,6 +467,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
419467
case Tag::Overview:
420468
case Tag::Processes:
421469
return {};
470+
case Tag::Favorites:
471+
// they are handled elsewhere
472+
Q_UNREACHABLE();
422473
}
423474

424475
tooltip += tr("Number of Events: %1 (%2% of the total)")
@@ -440,6 +491,9 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
440491
case Tag::Overview:
441492
case Tag::Processes:
442493
return {};
494+
case Tag::Favorites:
495+
// they are handled elsewhere
496+
Q_UNREACHABLE();
443497
}
444498
}
445499
break;
@@ -454,6 +508,8 @@ QVariant EventModel::data(const QModelIndex& index, int role) const
454508
void EventModel::setData(const Data::EventResults& data)
455509
{
456510
beginResetModel();
511+
m_favourites.clear();
512+
457513
m_data = data;
458514
m_totalEvents = 0;
459515
m_maxCost = 0;
@@ -514,6 +570,7 @@ QModelIndex EventModel::index(int row, int column, const QModelIndex& parent) co
514570
case Tag::Cpus:
515571
case Tag::Tracepoints:
516572
case Tag::Threads:
573+
case Tag::Favorites:
517574
break;
518575
case Tag::Root: // root has the 1st level children: Overview
519576
return createIndex(row, column, static_cast<quintptr>(Tag::Overview));
@@ -525,6 +582,8 @@ QModelIndex EventModel::index(int row, int column, const QModelIndex& parent) co
525582
return createIndex(row, column, static_cast<quintptr>(Tag::Processes));
526583
case OverviewRow::TracepointRow:
527584
return createIndex(row, column, static_cast<quintptr>(Tag::Tracepoints));
585+
case OverviewRow::FavoriteRow:
586+
return createIndex(row, column, static_cast<quintptr>(Tag::Favorites));
528587
}
529588
Q_UNREACHABLE();
530589
case Tag::Processes: // 3rd level children: Threads
@@ -546,12 +605,48 @@ QModelIndex EventModel::parent(const QModelIndex& child) const
546605
case Tag::Processes:
547606
return createIndex(OverviewRow::ProcessRow, 0, static_cast<quintptr>(Tag::Overview));
548607
case Tag::Tracepoints:
549-
return createIndex(OverviewRow::TracepointRow, 0, static_cast<qintptr>(Tag::Overview));
550-
case Tag::Threads: {
608+
return createIndex(OverviewRow::TracepointRow, 0, static_cast<quintptr>(Tag::Overview));
609+
case Tag::Favorites:
610+
return createIndex(OverviewRow::FavoriteRow, 0, static_cast<quintptr>(Tag::Overview));
611+
case Tag::Threads:
551612
const auto parentRow = tagData(child.internalId());
552613
return createIndex(parentRow, 0, static_cast<quintptr>(Tag::Processes));
553614
}
554-
}
555615

556616
return {};
557617
}
618+
619+
void EventModel::addToFavorites(const QModelIndex& index)
620+
{
621+
Q_ASSERT(index.model() == this);
622+
623+
if (index.column() != 0) {
624+
// we only want one index per row, so we force it to be column zero
625+
// this way we can easily check if we have duplicate rows
626+
addToFavorites(index.siblingAtColumn(0));
627+
return;
628+
}
629+
630+
if (m_favourites.contains(index)) {
631+
return;
632+
}
633+
634+
const auto row = m_favourites.size();
635+
636+
beginInsertRows(createIndex(FavoriteRow, 0, static_cast<quintptr>(Tag::Overview)), row, row);
637+
m_favourites.push_back(index);
638+
endInsertRows();
639+
}
640+
641+
void EventModel::removeFromFavorites(const QModelIndex& index)
642+
{
643+
Q_ASSERT(index.model() == this);
644+
Q_ASSERT(dataTag(index) == Tag::Favorites);
645+
646+
const auto row = index.row();
647+
Q_ASSERT(row >= 0 && row < m_favourites.size());
648+
649+
beginRemoveRows(createIndex(FavoriteRow, 0, static_cast<quintptr>(Tag::Overview)), row, row);
650+
m_favourites.remove(row);
651+
endRemoveRows();
652+
}

src/models/eventmodel.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class EventModel : public QAbstractItemModel
4242
SortRole,
4343
TotalCostsRole,
4444
EventResultsRole,
45+
IsFavoriteRole,
4546
};
4647

4748
int rowCount(const QModelIndex& parent = {}) const override;
@@ -71,9 +72,14 @@ class EventModel : public QAbstractItemModel
7172
QString name;
7273
};
7374

75+
public:
76+
void addToFavorites(const QModelIndex& index);
77+
void removeFromFavorites(const QModelIndex& index);
78+
7479
private:
7580
Data::EventResults m_data;
7681
QVector<Process> m_processes;
82+
QVector<QModelIndex> m_favourites;
7783
Data::TimeRange m_time;
7884
quint64 m_totalOnCpuTime = 0;
7985
quint64 m_totalOffCpuTime = 0;

src/models/timelinedelegate.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <QHelpEvent>
1414
#include <QMenu>
1515
#include <QPainter>
16+
#include <QSortFilterProxyModel>
1617
#include <QToolTip>
1718

1819
#include "../util.h"
@@ -459,6 +460,20 @@ bool TimeLineDelegate::eventFilter(QObject* watched, QEvent* event)
459460
const auto isMainThread = threadStartTime == minTime && threadEndTime == maxTime;
460461
const auto cpuId = index.data(EventModel::CpuIdRole).value<quint32>();
461462
const auto numCpus = index.data(EventModel::NumCpusRole).value<uint>();
463+
const auto isFavorite = index.data(EventModel::IsFavoriteRole).value<bool>();
464+
465+
contextMenu->addAction(QIcon::fromTheme(QStringLiteral("favorite")),
466+
isFavorite ? tr("Remove from favorites") : tr("Add to favorites"), this,
467+
[this, index, isFavorite] {
468+
auto model = qobject_cast<const QSortFilterProxyModel*>(index.model());
469+
Q_ASSERT(model);
470+
if (isFavorite) {
471+
emit removeFromFavorites(model->mapToSource(index));
472+
} else {
473+
emit addToFavorites(model->mapToSource(index));
474+
}
475+
});
476+
462477
if (isTimeSpanSelected && (minTime != timeSlice.start || maxTime != timeSlice.end)) {
463478
contextMenu->addAction(QIcon::fromTheme(QStringLiteral("zoom-in")), tr("Zoom In On Selection"), this,
464479
[this, timeSlice]() { m_filterAndZoomStack->zoomIn(timeSlice); });

src/models/timelinedelegate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class TimeLineDelegate : public QStyledItemDelegate
6464

6565
signals:
6666
void stacksHovered(const QSet<qint32>& stacks);
67+
void addToFavorites(const QModelIndex& index);
68+
void removeFromFavorites(const QModelIndex& index);
6769

6870
protected:
6971
bool eventFilter(QObject* watched, QEvent* event) override;

src/timelinewidget.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ TimeLineWidget::TimeLineWidget(PerfParser* parser, QMenu* filterMenu, FilterAndZ
110110
m_timeLineDelegate->setEventType(typeId);
111111
});
112112

113+
connect(m_timeLineDelegate, &TimeLineDelegate::addToFavorites, this,
114+
[eventModel](const QModelIndex& index) { eventModel->addToFavorites(index); });
115+
connect(m_timeLineDelegate, &TimeLineDelegate::removeFromFavorites, this,
116+
[eventModel](const QModelIndex& index) { eventModel->removeFromFavorites(index); });
117+
113118
connect(m_timeLineDelegate, &TimeLineDelegate::stacksHovered, this, [this](const QSet<qint32>& stackIds) {
114119
if (stackIds.isEmpty()) {
115120
++m_currentHoverStacksJobId;

0 commit comments

Comments
 (0)