1 /***************************************************************************
2 qgsprofilerpanelwidget.cpp
3 -------------------------
4 begin : May 2020
5 copyright : (C) 2020 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgsprofilerpanelwidget.h"
17 #include "qgsruntimeprofiler.h"
18 #include "qgslogger.h"
19 #include "qgis.h"
20 #include <QPainter>
21 #include <cmath>
22
23 //
24 // QgsProfilerPanelWidget
25 //
26
QgsProfilerPanelWidget(QgsRuntimeProfiler * profiler,QWidget * parent)27 QgsProfilerPanelWidget::QgsProfilerPanelWidget( QgsRuntimeProfiler *profiler, QWidget *parent )
28 : QgsDevToolWidget( parent )
29 , mProfiler( profiler )
30 {
31 setupUi( this );
32
33 mProxyModel = new QgsProfilerProxyModel( profiler, this );
34
35 mTreeView->setModel( mProxyModel );
36 mTreeView->setSortingEnabled( true );
37
38 //mTreeView->resizeColumnToContents( 0 );
39 //mTreeView->resizeColumnToContents( 1 );
40
41 mTreeView->setItemDelegateForColumn( 1, new CostDelegate( QgsRuntimeProfilerNode::Elapsed, QgsRuntimeProfilerNode::ParentElapsed, mTreeView ) );
42
43 connect( mProfiler, &QgsRuntimeProfiler::groupAdded, this, [ = ]( const QString & group )
44 {
45 mCategoryComboBox->addItem( QgsRuntimeProfiler::translateGroupName( group ).isEmpty() ? group : QgsRuntimeProfiler::translateGroupName( group ), group );
46 if ( mCategoryComboBox->count() == 1 )
47 {
48 mCategoryComboBox->setCurrentIndex( 0 );
49 mProxyModel->setGroup( mCategoryComboBox->currentData().toString() );
50 }
51 } );
52
53 connect( mCategoryComboBox, qgis::overload< int >::of( &QComboBox::currentIndexChanged ), this, [ = ]( int )
54 {
55 mProxyModel->setGroup( mCategoryComboBox->currentData().toString() );
56 } );
57
58 const QSet<QString> groups = mProfiler->groups();
59 for ( const QString &group : groups )
60 {
61 mCategoryComboBox->addItem( QgsRuntimeProfiler::translateGroupName( group ).isEmpty() ? group : QgsRuntimeProfiler::translateGroupName( group ), group );
62 }
63
64 if ( mCategoryComboBox->count() > 0 )
65 {
66 mCategoryComboBox->setCurrentIndex( 0 );
67 mProxyModel->setGroup( mCategoryComboBox->currentData().toString() );
68 }
69 }
70
71 //
72 // QgsProfilerProxyModel
73 //
74
QgsProfilerProxyModel(QgsRuntimeProfiler * profiler,QObject * parent)75 QgsProfilerProxyModel::QgsProfilerProxyModel( QgsRuntimeProfiler *profiler, QObject *parent )
76 : QSortFilterProxyModel( parent )
77 {
78 setSourceModel( profiler );
79 }
80
setGroup(const QString & group)81 void QgsProfilerProxyModel::setGroup( const QString &group )
82 {
83 mGroup = group;
84 invalidateFilter();
85 }
86
filterAcceptsRow(int row,const QModelIndex & source_parent) const87 bool QgsProfilerProxyModel::filterAcceptsRow( int row, const QModelIndex &source_parent ) const
88 {
89 QModelIndex index = sourceModel()->index( row, 0, source_parent );
90 return sourceModel()->data( index, QgsRuntimeProfilerNode::Group ).toString() == mGroup;
91 }
92
93
94 //
95 // CostDelegate
96 //
97
98 // adapted from KDAB's excellent "hotspot" application!
99
CostDelegate(quint32 sortRole,quint32 totalCostRole,QObject * parent)100 CostDelegate::CostDelegate( quint32 sortRole, quint32 totalCostRole, QObject *parent )
101 : QStyledItemDelegate( parent )
102 , m_sortRole( sortRole )
103 , m_totalCostRole( totalCostRole )
104 {
105 }
106
107 CostDelegate::~CostDelegate() = default;
108
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const109 void CostDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
110 {
111 // TODO: handle negative values
112 const auto cost = index.data( m_sortRole ).toDouble();
113 if ( cost == 0 )
114 {
115 QStyledItemDelegate::paint( painter, option, index );
116 return;
117 }
118
119 const auto totalCost = index.data( m_totalCostRole ).toDouble();
120 const auto fraction = std::abs( float( cost ) / totalCost );
121
122 auto rect = option.rect;
123 rect.setWidth( rect.width() * fraction );
124
125 const auto &brush = painter->brush();
126 const auto &pen = painter->pen();
127
128 painter->setPen( Qt::NoPen );
129
130 if ( option.features & QStyleOptionViewItem::Alternate )
131 {
132 // we must handle this ourselves as otherwise the custom background
133 // would get painted over with the alternate background color
134 painter->setBrush( option.palette.alternateBase() );
135 painter->drawRect( option.rect );
136 }
137
138 auto color = QColor::fromHsv( 120 - fraction * 120, 255, 255, ( -( ( fraction - 1 ) * ( fraction - 1 ) ) ) * 120 + 120 );
139 painter->setBrush( color );
140 painter->drawRect( rect );
141
142 painter->setBrush( brush );
143 painter->setPen( pen );
144
145 if ( option.features & QStyleOptionViewItem::Alternate )
146 {
147 auto o = option;
148 o.features &= ~QStyleOptionViewItem::Alternate;
149 QStyledItemDelegate::paint( painter, o, index );
150 }
151 else
152 {
153 QStyledItemDelegate::paint( painter, option, index );
154 }
155 }
156
157