From 89766e83b11f6d3dfddb319148cc09b522f8eb12 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Mon, 27 Jun 2016 22:11:26 +0300 Subject: [PATCH] (Tree widget) Added possibility to colorize rows with profiler blocks' colors; * (Tree widget) Added displaying frame statistics for blocks. --- profiler_gui/blocks_tree_widget.cpp | 244 ++++++++++++++++++++++------ profiler_gui/blocks_tree_widget.h | 52 +++--- profiler_gui/main.cpp | 4 +- profiler_gui/main_window.cpp | 10 +- 4 files changed, 234 insertions(+), 76 deletions(-) diff --git a/profiler_gui/blocks_tree_widget.cpp b/profiler_gui/blocks_tree_widget.cpp index c2e521b..b1eac09 100644 --- a/profiler_gui/blocks_tree_widget.cpp +++ b/profiler_gui/blocks_tree_widget.cpp @@ -9,20 +9,48 @@ * description : The file contains implementation of TreeWidget and it's auxiliary classes * : for displyaing easy_profiler blocks tree. * ----------------- : -* change log : * 2016/06/26 Victor Zarubkin: moved sources from tree_view.h +* change log : * 2016/06/26 Victor Zarubkin: Moved sources from tree_view.h * : and renamed classes from My* to Prof*. -* : * +* : * 2016/06/27 Victor Zarubkin: Added possibility to colorize rows +* : with profiler blocks' colors. +* : Also added displaying frame statistics for blocks. +* : * * ----------------- : * license : TODO: add license text ************************************************************************/ #include #include +#include #include "blocks_tree_widget.h" ////////////////////////////////////////////////////////////////////////// -ProfTreeWidgetItem::ProfTreeWidgetItem(const BlocksTree* _block, QTreeWidgetItem* _parent) : QTreeWidgetItem(_parent), m_block(_block) +enum ColumnsIndexes +{ + COL_UNKNOWN = -1, + + COL_NAME = 0, + COL_DURATION, + COL_BEGIN, + COL_END, + COL_MIN_TOTAL, + COL_MAX_TOTAL, + COL_AVERAGE_TOTAL, + COL_NCALLS_TOTAL, + COL_MIN, + COL_MAX, + COL_AVERAGE, + COL_NCALLS, + + COL_COLUMNS_NUMBER +}; + +////////////////////////////////////////////////////////////////////////// + +ProfTreeWidgetItem::ProfTreeWidgetItem(const BlocksTree* _treeBlock, Parent* _parent) + : Parent(_parent) + , m_block(_treeBlock) { } @@ -30,6 +58,36 @@ ProfTreeWidgetItem::~ProfTreeWidgetItem() { } +bool ProfTreeWidgetItem::operator < (const Parent& _other) const +{ + const auto col = treeWidget()->sortColumn(); + + switch (col) + { + case COL_UNKNOWN: + case COL_NAME: + { + // column 0 - Name + return Parent::operator < (_other); + } + + case COL_NCALLS_TOTAL: + case COL_NCALLS: + { + // column 7 and 11 - N calls + return data(col, Qt::UserRole).toUInt() < _other.data(col, Qt::UserRole).toUInt(); + } + + default: + { + // columns [1; 6] and [8; 10] - durations + return data(col, Qt::UserRole).toULongLong() < _other.data(col, Qt::UserRole).toULongLong(); + } + } + + return false; +} + const BlocksTree* ProfTreeWidgetItem::block() const { return m_block; @@ -66,64 +124,105 @@ void ProfTreeWidgetItem::setTimeMs(int _column, const ::profiler::timestamp_t& _ setText(_column, QString::number(double(_time) * 1e-6, 'g', 9)); } +void ProfTreeWidgetItem::setBackgroundColor(const QColor& _color) +{ + m_customBGColor = _color; +} + +void ProfTreeWidgetItem::setTextColor(const QColor& _color) +{ + m_customTextColor = _color; +} + +void ProfTreeWidgetItem::colorize(bool _colorize) +{ + if (_colorize) + { + for (int i = 0; i < COL_COLUMNS_NUMBER; ++i) + { + setBackground(i, m_customBGColor); + setForeground(i, m_customTextColor); + } + } + else + { + const QBrush nobrush; + for (int i = 0; i < COL_COLUMNS_NUMBER; ++i) + { + setBackground(i, nobrush); + setForeground(i, nobrush); + } + } +} + ////////////////////////////////////////////////////////////////////////// -ProfTreeWidget::ProfTreeWidget(QWidget* _parent) : QTreeWidget(_parent), m_beginTime(-1) +ProfTreeWidget::ProfTreeWidget(QWidget* _parent) : Parent(_parent), m_beginTime(-1), m_bColorRows(false) { + setAutoFillBackground(false); setAlternatingRowColors(true); setItemsExpandable(true); setAnimated(true); setSortingEnabled(false); - setColumnCount(8); + setColumnCount(COL_COLUMNS_NUMBER); auto header = new QTreeWidgetItem(); - header->setText(0, "Name"); - header->setText(1, "Duration"); - header->setText(2, "Begin, ms"); - header->setText(3, "End, ms"); - header->setText(4, "Min dur."); - header->setText(5, "Max dur."); - header->setText(6, "Average dur."); - header->setText(7, "N Calls"); + header->setText(COL_NAME, "Name"); + header->setText(COL_DURATION, "Duration"); + header->setText(COL_BEGIN, "Begin, ms"); + header->setText(COL_END, "End, ms"); + header->setText(COL_MIN_TOTAL, "Min dur. total"); + header->setText(COL_MAX_TOTAL, "Max dur. total"); + header->setText(COL_AVERAGE_TOTAL, "Average dur. total"); + header->setText(COL_NCALLS_TOTAL, "N Calls total"); + header->setText(COL_MIN, "Min dur."); + header->setText(COL_MAX, "Max dur."); + header->setText(COL_AVERAGE, "Average dur."); + header->setText(COL_NCALLS, "N Calls"); setHeaderItem(header); } -ProfTreeWidget::ProfTreeWidget(const thread_blocks_tree_t& _blocksTree, QWidget* _parent) : ProfTreeWidget(_parent) +ProfTreeWidget::ProfTreeWidget(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree, QWidget* _parent) : This(_parent) { - setTreeInternal(_blocksTree); + setTreeInternal(_blocksNumber, _blocksTree); setSortingEnabled(true); - sortByColumn(0, Qt::AscendingOrder); - sortByColumn(2, Qt::AscendingOrder); + sortByColumn(COL_NAME, Qt::AscendingOrder); + sortByColumn(COL_BEGIN, Qt::AscendingOrder); - connect(this, &QTreeWidget::itemExpanded, this, &ProfTreeWidget::onItemExpand); + connect(this, &Parent::itemExpanded, this, &This::onItemExpand); } ProfTreeWidget::~ProfTreeWidget() { } -void ProfTreeWidget::setTree(const thread_blocks_tree_t& _blocksTree) +void ProfTreeWidget::setTree(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree) { + setSortingEnabled(false); + disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand); + + m_items.clear(); m_itemblocks.clear(); + QSignalBlocker b(this); clear(); - - disconnect(this, &QTreeWidget::itemExpanded, this, &ProfTreeWidget::onItemExpand); - setSortingEnabled(false); + b.unblock(); m_beginTime = -1; - setTreeInternal(_blocksTree); + setTreeInternal(_blocksNumber, _blocksTree); setSortingEnabled(true); - sortByColumn(0, Qt::AscendingOrder); - sortByColumn(2, Qt::AscendingOrder); + sortByColumn(COL_NAME, Qt::AscendingOrder); + sortByColumn(COL_BEGIN, Qt::AscendingOrder); - connect(this, &QTreeWidget::itemExpanded, this, &ProfTreeWidget::onItemExpand); + connect(this, &Parent::itemExpanded, this, &This::onItemExpand); } -void ProfTreeWidget::setTreeInternal(const thread_blocks_tree_t& _blocksTree) +void ProfTreeWidget::setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree) { + m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks + for (const auto& threadTree : _blocksTree) { const auto beginTime = threadTree.second.children.front().node->block()->getBegin(); @@ -136,7 +235,8 @@ void ProfTreeWidget::setTreeInternal(const thread_blocks_tree_t& _blocksTree) for (const auto& threadTree : _blocksTree) { auto item = new ProfTreeWidgetItem(&threadTree.second); - item->setText(0, QString("Thread %1").arg(threadTree.first)); + item->setText(COL_NAME, QString("Thread %1").arg(threadTree.first)); + m_items.push_back(item); setTreeInternal(threadTree.second.children, item); addTopLevelItem(item); } @@ -147,19 +247,38 @@ void ProfTreeWidget::setTreeInternal(const BlocksTree::children_t& _children, Pr for (const auto& child : _children) { auto item = new ProfTreeWidgetItem(&child, _parent); - item->setText(0, child.node->getBlockName()); - item->setTimeSmart(1, child.node->block()->duration()); - item->setTimeMs(2, child.node->block()->getBegin() - m_beginTime); - item->setTimeMs(3, child.node->block()->getEnd() - m_beginTime); + item->setText(COL_NAME, child.node->getBlockName()); + item->setTimeSmart(COL_DURATION, child.node->block()->duration()); + item->setTimeMs(COL_BEGIN, child.node->block()->getBegin() - m_beginTime); + item->setTimeMs(COL_END, child.node->block()->getEnd() - m_beginTime); if (child.total_statistics) { - item->setTimeSmart(4, child.total_statistics->min_duration); - item->setTimeSmart(5, child.total_statistics->max_duration); - item->setTimeSmart(6, child.total_statistics->average_duration()); - item->setText(7, QString::number(child.total_statistics->calls_number)); + item->setTimeSmart(COL_MIN_TOTAL, child.total_statistics->min_duration); + item->setTimeSmart(COL_MAX_TOTAL, child.total_statistics->max_duration); + item->setTimeSmart(COL_AVERAGE_TOTAL, child.total_statistics->average_duration()); + + item->setData(COL_NCALLS_TOTAL, Qt::UserRole, child.total_statistics->calls_number); + item->setText(COL_NCALLS_TOTAL, QString::number(child.total_statistics->calls_number)); } + if (child.frame_statistics) + { + item->setTimeSmart(COL_MIN, child.frame_statistics->min_duration); + item->setTimeSmart(COL_MAX, child.frame_statistics->max_duration); + item->setTimeSmart(COL_AVERAGE, child.frame_statistics->average_duration()); + + item->setData(COL_NCALLS, Qt::UserRole, child.frame_statistics->calls_number); + item->setText(COL_NCALLS, QString::number(child.frame_statistics->calls_number)); + } + + const auto color = child.node->block()->getColor(); + const auto bgColor = QColor(profiler::colors::get_red(color), profiler::colors::get_green(color), profiler::colors::get_blue(color)); + const auto fgColor = QColor(QRgb(0x00ffffff - bgColor.rgb())); + item->setBackgroundColor(bgColor); + item->setTextColor(fgColor); + + m_items.push_back(item); m_itemblocks[child.node] = item; if (!child.children.empty()) @@ -182,29 +301,39 @@ void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event) QMenu menu; auto action = new QAction("Expand all", nullptr); - connect(action, &QAction::triggered, this, &ProfTreeWidget::onExpandAllClicked); + connect(action, &QAction::triggered, this, &This::onExpandAllClicked); menu.addAction(action); action = new QAction("Collapse all", nullptr); - connect(action, &QAction::triggered, this, &ProfTreeWidget::onCollapseAllClicked); + connect(action, &QAction::triggered, this, &This::onCollapseAllClicked); + menu.addAction(action); + + menu.addSeparator(); + + action = new QAction("Color rows", nullptr); + action->setCheckable(true); + action->setChecked(m_bColorRows); + connect(action, &QAction::triggered, this, &This::onColorizeRowsTriggered); menu.addAction(action); switch (col) { - case 4: + case COL_MIN_TOTAL: + case COL_MIN: { menu.addSeparator(); auto itemAction = new ProfItemAction("Jump to such item", item); - connect(itemAction, &ProfItemAction::clicked, this, &ProfTreeWidget::onJumpToMinItemClicked); + connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToMinItemClicked); menu.addAction(itemAction); break; } - case 5: + case COL_MAX_TOTAL: + case COL_MAX: { menu.addSeparator(); auto itemAction = new ProfItemAction("Jump to such item", item); - connect(itemAction, &ProfItemAction::clicked, this, &ProfTreeWidget::onJumpToMaxItemClicked); + connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToMaxItemClicked); menu.addAction(itemAction); break; } @@ -242,15 +371,38 @@ void ProfTreeWidget::onCollapseAllClicked(bool) void ProfTreeWidget::onExpandAllClicked(bool) { - disconnect(this, &QTreeWidget::itemExpanded, this, &ProfTreeWidget::onItemExpand); + disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand); expandAll(); - resizeColumnToContents(0); - connect(this, &QTreeWidget::itemExpanded, this, &ProfTreeWidget::onItemExpand); + resizeColumnToContents(COL_NAME); + connect(this, &Parent::itemExpanded, this, &This::onItemExpand); } void ProfTreeWidget::onItemExpand(QTreeWidgetItem*) { - resizeColumnToContents(0); + resizeColumnToContents(COL_NAME); +} + +void ProfTreeWidget::onColorizeRowsTriggered(bool _colorize) +{ + const QSignalBlocker b(this); + + m_bColorRows = _colorize; + + auto current = currentItem(); + collapseAll(); // Without collapseAll() changing items process is VERY VERY SLOW. + // TODO: Find the reason of such behavior. QSignalBlocker(this) does not help. QSignalBlocker(item) does not work, because items are not inherited from QObject. + + for (auto item : m_items) + { + item->colorize(m_bColorRows); + } + + // Scroll back to previously selected item + if (current) + { + scrollToItem(current, QAbstractItemView::PositionAtCenter); + setCurrentItem(current); + } } ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/blocks_tree_widget.h b/profiler_gui/blocks_tree_widget.h index a0c2ef1..d12d1cd 100644 --- a/profiler_gui/blocks_tree_widget.h +++ b/profiler_gui/blocks_tree_widget.h @@ -11,6 +11,8 @@ * ----------------- : * change log : * 2016/06/26 Victor Zarubkin: moved sources from tree_view.h * : and renamed classes from My* to Prof*. +* : * 2016/06/27 Victor Zarubkin: Added possibility to colorize rows +* : with profiler blocks' colors. * : * * ----------------- : * license : TODO: add license text @@ -23,6 +25,7 @@ #include #include #include +#include #include "profiler/reader.h" ////////////////////////////////////////////////////////////////////////// @@ -73,38 +76,35 @@ namespace btw { class ProfTreeWidgetItem : public QTreeWidgetItem { - //Q_OBJECT + typedef QTreeWidgetItem Parent; + typedef ProfTreeWidgetItem This; const BlocksTree* m_block; + QBrush m_customBGColor; + QBrush m_customTextColor; public: - ProfTreeWidgetItem(const BlocksTree* _block, QTreeWidgetItem* _parent = nullptr); + using Parent::setBackgroundColor; + using Parent::setTextColor; + + ProfTreeWidgetItem(const BlocksTree* _treeBlock, Parent* _parent = nullptr); virtual ~ProfTreeWidgetItem(); + bool operator < (const Parent& _other) const override; + const BlocksTree* block() const; - inline bool operator < (const QTreeWidgetItem& _other) const - { - const auto col = treeWidget()->sortColumn(); - if (col > 0 && col < 7) - { -#ifndef _DEBUG - return data(col, Qt::UserRole).toULongLong() < _other.data(col, Qt::UserRole).toULongLong(); -#else - const auto selfdata = data(col, Qt::UserRole).toULongLong(); - const auto otherdata = _other.data(col, Qt::UserRole).toULongLong(); - return selfdata < otherdata; -#endif - } - - return QTreeWidgetItem::operator < (_other); - } - void setTimeSmart(int _column, const ::profiler::timestamp_t& _time); void setTimeMs(int _column, const ::profiler::timestamp_t& _time); + void setBackgroundColor(const QColor& _color); + + void setTextColor(const QColor& _color); + + void colorize(bool _colorize); + }; // END of class ProfTreeWidgetItem. ////////////////////////////////////////////////////////////////////////// @@ -146,24 +146,30 @@ class ProfTreeWidget : public QTreeWidget { Q_OBJECT + typedef QTreeWidget Parent; + typedef ProfTreeWidget This; + protected: + typedef ::std::vector Items; typedef ::std::unordered_map::hasher_t> BlockItemMap; + Items m_items; BlockItemMap m_itemblocks; ::profiler::timestamp_t m_beginTime; + bool m_bColorRows; public: ProfTreeWidget(QWidget* _parent = nullptr); - ProfTreeWidget(const thread_blocks_tree_t& _blocksTree, QWidget* _parent = nullptr); + ProfTreeWidget(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree, QWidget* _parent = nullptr); virtual ~ProfTreeWidget(); - void setTree(const thread_blocks_tree_t& _blocksTree); + void setTree(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree); protected: - void setTreeInternal(const thread_blocks_tree_t& _blocksTree); + void setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree); void setTreeInternal(const BlocksTree::children_t& _children, ProfTreeWidgetItem* _parent); @@ -181,6 +187,8 @@ private slots: void onItemExpand(QTreeWidgetItem*); + void onColorizeRowsTriggered(bool _colorize); + }; // END of class ProfTreeWidget. ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/main.cpp b/profiler_gui/main.cpp index 8c9a061..548352a 100644 --- a/profiler_gui/main.cpp +++ b/profiler_gui/main.cpp @@ -64,9 +64,9 @@ int main(int argc, char **argv) case 1: { thread_blocks_tree_t threaded_trees; - fillTreesFromFile("test.prof", threaded_trees, true); + auto nblocks = fillTreesFromFile("test.prof", threaded_trees, true); - ProfTreeWidget view(threaded_trees); + ProfTreeWidget view(nblocks, threaded_trees); view.show(); return app.exec(); diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index 6d21b52..aec267e 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -8,7 +8,8 @@ * ----------------- : * description : The file contains implementation of MainWindow for easy_profiler GUI. * ----------------- : -* change log : * 2016/06/26 Victor Zarubkin: initial commit. +* change log : * 2016/06/26 Victor Zarubkin: Initial commit. +* : * 2016/06/27 Victor Zarubkin: Passing blocks number to ProfTreeWidget::setTree(). * : * * ----------------- : * license : TODO: add license text @@ -20,9 +21,6 @@ #include #include #include -#include -#include -#include #include "main_window.h" #include "blocks_tree_widget.h" #include "blocks_graphics_view.h" @@ -101,7 +99,7 @@ void ProfMainWindow::onOpenFileClicked(bool) { m_lastFile.swap(stdfilename); m_currentProf.swap(prof_blocks); - static_cast(m_treeWidget->widget())->setTree(m_currentProf); + static_cast(m_treeWidget->widget())->setTree(nblocks, m_currentProf); static_cast(m_graphicsView->widget())->setTree(m_currentProf); } } @@ -121,7 +119,7 @@ void ProfMainWindow::onReloadFileClicked(bool) if (nblocks > 0) { m_currentProf.swap(prof_blocks); - static_cast(m_treeWidget->widget())->setTree(m_currentProf); + static_cast(m_treeWidget->widget())->setTree(nblocks, m_currentProf); static_cast(m_graphicsView->widget())->setTree(m_currentProf); } }