Colorize model cost cells background based on relative cost to total.
authorMilian Wolff <mail@milianw.de>
Tue, 15 Mar 2016 19:18:00 +0000 (20:18 +0100)
committerMilian Wolff <mail@milianw.de>
Tue, 15 Mar 2016 19:18:00 +0000 (20:18 +0100)
Maniphest Task: https://phabricator.kde.org/T1709

gui/CMakeLists.txt
gui/costdelegate.cpp [new file with mode: 0644]
gui/costdelegate.h [new file with mode: 0644]
gui/mainwindow.cpp
gui/parser.h
gui/treemodel.cpp
gui/treemodel.h

index 18cb20b..37c336f 100644 (file)
@@ -16,6 +16,7 @@ add_executable(heaptrack_gui
     mainwindow.cpp
     treemodel.cpp
     treeproxy.cpp
+    costdelegate.cpp
     chartwidget.cpp
     chartmodel.cpp
     chartproxy.cpp
diff --git a/gui/costdelegate.cpp b/gui/costdelegate.cpp
new file mode 100644 (file)
index 0000000..eb68f7e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "costdelegate.h"
+
+#include "treemodel.h"
+
+#include <QDebug>
+#include <QPainter>
+
+CostDelegate::CostDelegate(QObject* parent)
+    : QStyledItemDelegate(parent)
+{
+}
+
+CostDelegate::~CostDelegate() = default;
+
+void CostDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
+{
+    const uint64_t cost = index.data(TreeModel::SortRole).toULongLong();
+    if (cost == 0) {
+        QStyledItemDelegate::paint(painter, option, index);
+        return;
+    }
+
+    const uint64_t maxCost = index.data(TreeModel::MaxCostRole).toULongLong();
+    // top-down can miscalculate the peak cost
+    const auto fraction = std::min(1.f, float(cost) / maxCost);
+    auto rect = option.rect;
+    rect.setWidth(rect.width() * fraction);
+
+    const auto& brush = painter->brush();
+    const auto& pen = painter->pen();
+
+    painter->setPen(Qt::NoPen);
+
+    if (option.features & QStyleOptionViewItem::Alternate) {
+        // we must handle this ourselves as otherwise the custom background
+        // would get painted over with the alternate background color
+        painter->setBrush(option.palette.alternateBase());
+        painter->drawRect(option.rect);
+    }
+
+    auto color = QColor::fromHsv(120 - fraction * 120, 255, 255,
+                                 (-((fraction-1) * (fraction-1))) * 120 + 120);
+    painter->setBrush(color);
+    painter->drawRect(rect);
+
+    painter->setBrush(brush);
+    painter->setPen(pen);
+
+    if (option.features & QStyleOptionViewItem::Alternate) {
+        auto o = option;
+        o.features &= ~QStyleOptionViewItem::Alternate;
+        QStyledItemDelegate::paint(painter, o, index);
+    } else {
+        QStyledItemDelegate::paint(painter, option, index);
+    }
+}
+
diff --git a/gui/costdelegate.h b/gui/costdelegate.h
new file mode 100644 (file)
index 0000000..b305ebb
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef COSTDELEGATE_H
+#define COSTDELEGATE_H
+
+#include <QStyledItemDelegate>
+
+class CostDelegate : public QStyledItemDelegate
+{
+    Q_OBJECT
+public:
+    explicit CostDelegate(QObject *parent = nullptr);
+    ~CostDelegate();
+
+    void paint(QPainter *painter, const QStyleOptionViewItem &option,
+               const QModelIndex &index) const override;
+};
+
+#endif // COSTDELEGATE_H
index 530c411..c6b5c52 100644 (file)
@@ -33,6 +33,7 @@
 #include "treemodel.h"
 #include "treeproxy.h"
 #include "topproxy.h"
+#include "costdelegate.h"
 #include "parser.h"
 #include "chartmodel.h"
 #include "chartproxy.h"
@@ -147,7 +148,9 @@ MainWindow::MainWindow(QWidget* parent)
                 m_ui->tabWidget->setTabEnabled(m_ui->tabWidget->indexOf(m_ui->sizesTab), true);
             });
     connect(m_parser, &Parser::summaryAvailable,
-            this, [this] (const SummaryData& data) {
+            this, [this, bottomUpModel, topDownModel] (const SummaryData& data) {
+                bottomUpModel->setSummary(data);
+                topDownModel->setSummary(data);
                 KFormat format;
                 QString textLeft;
                 QString textCenter;
@@ -209,6 +212,12 @@ MainWindow::MainWindow(QWidget* parent)
     bottomUpProxy->setSourceModel(bottomUpModel);
     bottomUpProxy->setSortRole(TreeModel::SortRole);
     m_ui->bottomUpResults->setModel(bottomUpProxy);
+    auto costDelegate = new CostDelegate(this);
+    m_ui->bottomUpResults->setItemDelegateForColumn(TreeModel::PeakColumn, costDelegate);
+    m_ui->bottomUpResults->setItemDelegateForColumn(TreeModel::AllocatedColumn, costDelegate);
+    m_ui->bottomUpResults->setItemDelegateForColumn(TreeModel::LeakedColumn, costDelegate);
+    m_ui->bottomUpResults->setItemDelegateForColumn(TreeModel::AllocationsColumn, costDelegate);
+    m_ui->bottomUpResults->setItemDelegateForColumn(TreeModel::TemporaryColumn, costDelegate);
     m_ui->bottomUpResults->hideColumn(TreeModel::FunctionColumn);
     m_ui->bottomUpResults->hideColumn(TreeModel::FileColumn);
     m_ui->bottomUpResults->hideColumn(TreeModel::LineColumn);
@@ -224,6 +233,11 @@ MainWindow::MainWindow(QWidget* parent)
     topDownProxy->setSourceModel(topDownModel);
     topDownProxy->setSortRole(TreeModel::SortRole);
     m_ui->topDownResults->setModel(topDownProxy);
+    m_ui->topDownResults->setItemDelegateForColumn(TreeModel::PeakColumn, costDelegate);
+    m_ui->topDownResults->setItemDelegateForColumn(TreeModel::AllocatedColumn, costDelegate);
+    m_ui->topDownResults->setItemDelegateForColumn(TreeModel::LeakedColumn, costDelegate);
+    m_ui->topDownResults->setItemDelegateForColumn(TreeModel::AllocationsColumn, costDelegate);
+    m_ui->topDownResults->setItemDelegateForColumn(TreeModel::TemporaryColumn, costDelegate);
     m_ui->topDownResults->hideColumn(TreeModel::FunctionColumn);
     m_ui->topDownResults->hideColumn(TreeModel::FileColumn);
     m_ui->topDownResults->hideColumn(TreeModel::LineColumn);
@@ -241,10 +255,15 @@ MainWindow::MainWindow(QWidget* parent)
     setupStacks();
 
     setupTopView(bottomUpModel, m_ui->topPeak, TopProxy::Peak);
+    m_ui->topPeak->setItemDelegate(costDelegate);
     setupTopView(bottomUpModel, m_ui->topLeaked, TopProxy::Leaked);
+    m_ui->topLeaked->setItemDelegate(costDelegate);
     setupTopView(bottomUpModel, m_ui->topAllocations, TopProxy::Allocations);
+    m_ui->topAllocations->setItemDelegate(costDelegate);
     setupTopView(bottomUpModel, m_ui->topTemporary, TopProxy::Temporary);
+    m_ui->topTemporary->setItemDelegate(costDelegate);
     setupTopView(bottomUpModel, m_ui->topAllocated, TopProxy::Allocated);
+    m_ui->topAllocated->setItemDelegate(costDelegate);
 
     setWindowTitle(i18n("Heaptrack"));
 }
index 33f33ad..113eadf 100644 (file)
 #include "chartmodel.h"
 #include "histogrammodel.h"
 
-struct SummaryData
-{
-    QString debuggee;
-    uint64_t totalTime;
-    uint64_t peakTime;
-    uint64_t peak;
-    uint64_t leaked;
-    uint64_t allocations;
-    uint64_t temporary;
-    uint64_t allocated;
-    uint64_t peakRSS;
-    uint64_t totalSystemMemory;
-};
-Q_DECLARE_METATYPE(SummaryData);
-
 class Parser : public QObject
 {
     Q_OBJECT
index 1569153..e9bcc57 100644 (file)
@@ -143,11 +143,13 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
     if (index.row() < 0 || index.column() < 0 || index.column() > NUM_COLUMNS) {
         return {};
     }
-    const auto row = toRow(index);
-    if (role == Qt::DisplayRole || role == SortRole) {
+
+    const auto row = (role == MaxCostRole) ? &m_maxCost : toRow(index);
+
+    if (role == Qt::DisplayRole || role == SortRole || role == MaxCostRole) {
         switch (static_cast<Columns>(index.column())) {
         case AllocatedColumn:
-            if (role == SortRole) {
+            if (role == SortRole || role == MaxCostRole) {
                 return row->allocated;
             } else {
                 return m_format.formatByteSize(row->allocated);
@@ -157,13 +159,13 @@ QVariant TreeModel::data(const QModelIndex& index, int role) const
         case TemporaryColumn:
             return row->temporary;
         case PeakColumn:
-            if (role == SortRole) {
+            if (role == SortRole || role == MaxCostRole) {
                 return row->peak;
             } else {
                 return m_format.formatByteSize(row->peak);
             }
         case LeakedColumn:
-            if (role == SortRole) {
+            if (role == SortRole || role == MaxCostRole) {
                 return row->leaked;
             } else {
                 return m_format.formatByteSize(row->leaked);
@@ -269,6 +271,17 @@ void TreeModel::resetData(const TreeData& data)
     endResetModel();
 }
 
+void TreeModel::setSummary(const SummaryData& data)
+{
+    beginResetModel();
+    m_maxCost.allocated = data.allocated;
+    m_maxCost.leaked = data.leaked;
+    m_maxCost.peak = data.peak;
+    m_maxCost.allocations = data.allocations;
+    m_maxCost.temporary = data.temporary;
+    endResetModel();
+}
+
 const RowData* TreeModel::toRow(const QModelIndex& index) const
 {
     if (!index.isValid()) {
index 220346d..f90873b 100644 (file)
 
 #include <memory>
 
+struct SummaryData
+{
+    QString debuggee;
+    uint64_t totalTime;
+    uint64_t peakTime;
+    uint64_t peak;
+    uint64_t leaked;
+    uint64_t allocations;
+    uint64_t temporary;
+    uint64_t allocated;
+    uint64_t peakRSS;
+    uint64_t totalSystemMemory;
+};
+Q_DECLARE_METATYPE(SummaryData);
+
 struct LocationData
 {
     QString function;
@@ -108,7 +123,8 @@ public:
     };
 
     enum Roles {
-        SortRole = Qt::UserRole
+        SortRole = Qt::UserRole,
+        MaxCostRole = Qt::UserRole + 1
     };
 
     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
@@ -120,6 +136,7 @@ public:
 
 public slots:
     void resetData(const TreeData& data);
+    void setSummary(const SummaryData& data);
 
 private:
     /// @return the row resembled by @p index
@@ -128,6 +145,7 @@ private:
     int rowOf(const RowData* row) const;
 
     TreeData m_data;
+    RowData m_maxCost;
     // TODO: update via global event filter when the locale changes (changeEvent)
     KFormat m_format;
 };