0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-27 08:41:02 +08:00

#31 [Gui] Fixed mouse indicator for complexity chart; fixed buildTree(); added stub for future export values to .csv

This commit is contained in:
Victor Zarubkin 2018-02-20 22:11:43 +03:00
parent 87bc825980
commit 1db116c791
8 changed files with 236 additions and 127 deletions

View File

@ -56,9 +56,12 @@
#include <QActionGroup> #include <QActionGroup>
#include <QColor> #include <QColor>
#include <QComboBox> #include <QComboBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QHeaderView> #include <QHeaderView>
#include <QLabel> #include <QLabel>
#include <QMessageBox>
#include <QMouseEvent> #include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QPainterPath> #include <QPainterPath>
@ -309,13 +312,13 @@ void ArbitraryValuesCollection::collectValues(ChartType _chartType, profiler::th
} }
if (_valueId == 0) if (_valueId == 0)
m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId, _directParent); }, m_bInterrupt); m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId); }, m_bInterrupt);
else else
m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId, _directParent); }, m_bInterrupt); m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId); }, m_bInterrupt);
} }
void ArbitraryValuesCollection::collectValuesAndPoints(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId void ArbitraryValuesCollection::collectValuesAndPoints(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId
, const char* _valueName, profiler::timestamp_t _beginTime, profiler::block_id_t _parentBlockId, bool _directParent) , const char* _valueName, profiler::timestamp_t _beginTime, profiler::block_id_t _parentBlockId)
{ {
interrupt(); interrupt();
@ -334,9 +337,9 @@ void ArbitraryValuesCollection::collectValuesAndPoints(ChartType _chartType, pro
} }
if (_valueId == 0) if (_valueId == 0)
m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId, _directParent); }, m_bInterrupt); m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId); }, m_bInterrupt);
else else
m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId, _directParent); }, m_bInterrupt); m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId); }, m_bInterrupt);
} }
bool ArbitraryValuesCollection::calculatePoints(profiler::timestamp_t _beginTime) bool ArbitraryValuesCollection::calculatePoints(profiler::timestamp_t _beginTime)
@ -379,7 +382,7 @@ void ArbitraryValuesCollection::setStatus(JobStatus _status)
} }
void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId
, profiler::block_id_t _parentBlockId, bool _directParent) , profiler::block_id_t _parentBlockId)
{ {
const bool doCalculatePoints = (m_jobType & PointsJob) != 0; const bool doCalculatePoints = (m_jobType & PointsJob) != 0;
@ -392,7 +395,7 @@ void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, pro
for (const auto& it : EASY_GLOBALS.profiler_blocks) for (const auto& it : EASY_GLOBALS.profiler_blocks)
{ {
if (!collectByIdForThread(it.second, _valueId, calculatePointsInner, _parentBlockId, _directParent)) if (!collectByIdForThread(it.second, _valueId, calculatePointsInner, _parentBlockId))
return; return;
} }
@ -404,7 +407,7 @@ void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, pro
{ {
const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId); const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId);
if (t != EASY_GLOBALS.profiler_blocks.end() && if (t != EASY_GLOBALS.profiler_blocks.end() &&
!collectByIdForThread(t->second, _valueId, doCalculatePoints, _parentBlockId, _directParent)) !collectByIdForThread(t->second, _valueId, doCalculatePoints, _parentBlockId))
{ {
return; return;
} }
@ -414,7 +417,7 @@ void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, pro
} }
bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot
, profiler::vin_t _valueId, bool _calculatePoints, profiler::block_id_t _parentBlockId, bool _directParent) , profiler::vin_t _valueId, bool _calculatePoints, profiler::block_id_t _parentBlockId)
{ {
if (profiler_gui::is_max(_parentBlockId)) if (profiler_gui::is_max(_parentBlockId))
{ {
@ -443,12 +446,12 @@ bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeR
return true; return true;
} }
return depthFirstSearch(_threadRoot, _calculatePoints, _parentBlockId, _directParent return depthFirstSearch(_threadRoot, _calculatePoints, _parentBlockId
, [=] (profiler::vin_t _id, const char*) -> bool { return _id == _valueId; }); , [=] (profiler::vin_t _id, const char*) -> bool { return _id == _valueId; });
} }
void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, const std::string _valueName void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, const std::string _valueName
, profiler::block_id_t _parentBlockId, bool _directParent) , profiler::block_id_t _parentBlockId)
{ {
const bool doCalculatePoints = (m_jobType & PointsJob) != 0; const bool doCalculatePoints = (m_jobType & PointsJob) != 0;
@ -461,7 +464,7 @@ void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, c
for (const auto& it : EASY_GLOBALS.profiler_blocks) for (const auto& it : EASY_GLOBALS.profiler_blocks)
{ {
if (!collectByNameForThread(it.second, _valueName, calculatePointsInner, _parentBlockId, _directParent)) if (!collectByNameForThread(it.second, _valueName, calculatePointsInner, _parentBlockId))
return; return;
} }
@ -473,7 +476,7 @@ void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, c
{ {
const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId); const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId);
if (t != EASY_GLOBALS.profiler_blocks.end() && if (t != EASY_GLOBALS.profiler_blocks.end() &&
!collectByNameForThread(t->second, _valueName, doCalculatePoints, _parentBlockId, _directParent)) !collectByNameForThread(t->second, _valueName, doCalculatePoints, _parentBlockId))
{ {
return; return;
} }
@ -483,7 +486,7 @@ void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, c
} }
bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot
, const std::string& _valueName, bool _calculatePoints, profiler::block_id_t _parentBlockId, bool _directParent) , const std::string& _valueName, bool _calculatePoints, profiler::block_id_t _parentBlockId)
{ {
if (profiler_gui::is_max(_parentBlockId)) if (profiler_gui::is_max(_parentBlockId))
{ {
@ -508,12 +511,12 @@ bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTre
return true; return true;
} }
return depthFirstSearch(_threadRoot, _calculatePoints, _parentBlockId, _directParent return depthFirstSearch(_threadRoot, _calculatePoints, _parentBlockId
, [&_valueName] (profiler::vin_t, const char* _name) -> bool { return _valueName == _name; }); , [&_valueName] (profiler::vin_t, const char* _name) -> bool { return _valueName == _name; });
} }
bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot& _threadRoot, bool _calculatePoints bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot& _threadRoot, bool _calculatePoints
, profiler::block_id_t _parentBlockId, bool _directParent, std::function<bool(profiler::vin_t, const char*)> _isSuitableValue) , profiler::block_id_t _parentBlockId, std::function<bool(profiler::vin_t, const char*)> _isSuitableValue)
{ {
if (_threadRoot.children.empty()) if (_threadRoot.children.empty())
return true; return true;
@ -544,9 +547,6 @@ bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot&
auto& first = top.second; auto& first = top.second;
const auto i = top.first; const auto i = top.first;
const auto parent = stack.size() > 1 ? (++stack.rbegin())->first :
profiler_gui::numeric_max<profiler::block_index_t>();
const auto& block = easyBlock(i).tree; const auto& block = easyBlock(i).tree;
const auto& desc = easyDescriptor(block.node->id()); const auto& desc = easyDescriptor(block.node->id());
@ -554,19 +554,6 @@ bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot&
{ {
const auto value = block.value; const auto value = block.value;
if (_isSuitableValue(value->value_id(), desc.name())) if (_isSuitableValue(value->value_id(), desc.name()))
{
bool hit = false;
if (!_directParent)
{
hit = true;
}
else
{
const auto parentId = easyBlock(parent).tree.node->id();
hit = parentId == _parentBlockId || easyDescriptor(parentId).id() == _parentBlockId;
}
if (hit)
{ {
valuesList.push_back(value); valuesList.push_back(value);
if (_calculatePoints) if (_calculatePoints)
@ -583,7 +570,6 @@ bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot&
} }
} }
} }
}
if (first < block.children.size()) if (first < block.children.size())
{ {
@ -696,6 +682,7 @@ void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _to
const auto x = m_mousePos.x(); const auto x = m_mousePos.x();
auto y = m_mousePos.y(); auto y = m_mousePos.y();
QString valueString;
// Horizontal // Horizontal
const bool visibleY = (_top < y && y < _bottom); const bool visibleY = (_top < y && y < _bottom);
@ -706,9 +693,20 @@ void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _to
const int half_font_h = _font_h >> 1; const int half_font_h = _font_h >> 1;
const auto yvalue = estd::clamp(_top + ChartBound, y, _bottom - ChartBound); const auto yvalue = estd::clamp(_top + ChartBound, y, _bottom - ChartBound);
if (m_chartType == ChartType::Regular)
{
const auto value = m_minValue + ((_bottom - ChartBound - yvalue) / _height) * (m_maxValue - m_minValue); const auto value = m_minValue + ((_bottom - ChartBound - yvalue) / _height) * (m_maxValue - m_minValue);
const auto mouseStr = QString::number(value, 'f', 3); valueString = QString::number(value, 'f', 3);
const int textWidth = _painter->fontMetrics().width(mouseStr) + 3; }
else
{
const auto value = m_minDuration +
static_cast<profiler::timestamp_t>(((_bottom - ChartBound - yvalue) / _height) * (m_maxDuration - m_minDuration));
valueString = profiler_gui::autoTimeStringRealNs(value, 3);
}
const int textWidth = _painter->fontMetrics().width(valueString) + 3;
const QRectF rect(0, y - _font_h - 2, _width - 3, 4 + (_font_h << 1)); const QRectF rect(0, y - _font_h - 2, _width - 3, 4 + (_font_h << 1));
_painter->setPen(Qt::blue); _painter->setPen(Qt::blue);
@ -718,15 +716,15 @@ void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _to
if (y > _bottom - half_font_h) if (y > _bottom - half_font_h)
{ {
_painter->drawText(rect, alignment | Qt::AlignTop, mouseStr); _painter->drawText(rect, alignment | Qt::AlignTop, valueString);
} }
else if (y < _top + half_font_h) else if (y < _top + half_font_h)
{ {
_painter->drawText(rect, alignment | Qt::AlignBottom, mouseStr); _painter->drawText(rect, alignment | Qt::AlignBottom, valueString);
} }
else else
{ {
_painter->drawText(rect, alignment | Qt::AlignVCenter, mouseStr); _painter->drawText(rect, alignment | Qt::AlignVCenter, valueString);
if (x < textWidth) if (x < textWidth)
right = _width - textWidth - 3; right = _width - textWidth - 3;
else else
@ -738,11 +736,20 @@ void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _to
} }
// Vertical // Vertical
if (0 < x && x < m_boundingRect.width()) if (0 <= x && x <= _width)
{
if (m_chartType == ChartType::Regular)
{ {
const auto value = _visibleRegionLeft + _visibleRegionWidth * x / _width; const auto value = _visibleRegionLeft + _visibleRegionWidth * x / _width;
const auto mouseStr = profiler_gui::timeStringReal(EASY_GLOBALS.time_units, value, 3); valueString = profiler_gui::timeStringReal(EASY_GLOBALS.time_units, value, 3);
const int textWidth = _painter->fontMetrics().width(mouseStr) + 6; }
else
{
const auto value = m_minValue + (m_maxValue - m_minValue) * x / _width;
valueString = QString::number(value, 'f', 3);
}
const int textWidth = _painter->fontMetrics().width(valueString) + 6;
const int textWidthHalf = textWidth >> 1; const int textWidthHalf = textWidth >> 1;
qreal left = x - textWidthHalf; qreal left = x - textWidthHalf;
@ -755,7 +762,7 @@ void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _to
// _painter->setPen(Qt::blue); // _painter->setPen(Qt::blue);
const QRectF rect(left, _bottom + 2, textWidth, _font_h); const QRectF rect(left, _bottom + 2, textWidth, _font_h);
_painter->drawText(rect, Qt::AlignCenter, mouseStr); _painter->drawText(rect, Qt::AlignCenter, valueString);
_painter->drawLine(QLineF(x, _top, x, _bottom)); _painter->drawLine(QLineF(x, _top, x, _bottom));
} }
} }
@ -1573,7 +1580,7 @@ ArbitraryTreeWidgetItem::~ArbitraryTreeWidgetItem()
QVariant ArbitraryTreeWidgetItem::data(int _column, int _role) const QVariant ArbitraryTreeWidgetItem::data(int _column, int _role) const
{ {
if (_column == CheckColumn && _role == Qt::SizeHintRole) if (_column == CheckColumn && _role == Qt::SizeHintRole)
return QSize(static_cast<int>(m_widthHint * m_font.bold() ? 1.2f : 1.f), 26); return QSize(static_cast<int>(m_widthHint * (m_font.bold() ? 1.2f : 1.f)), 26);
if (_role == Qt::FontRole) if (_role == Qt::FontRole)
return m_font; return m_font;
return Parent::data(_column, _role); return Parent::data(_column, _role);
@ -1611,14 +1618,11 @@ void ArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId, Cha
bool directParent = false; bool directParent = false;
if (parentItem->data(int_cast(ArbitraryColumns::Type), Qt::UserRole).toInt() == 1) if (parentItem->data(int_cast(ArbitraryColumns::Type), Qt::UserRole).toInt() == 1)
{
parentBlockId = parentItem->data(int_cast(ArbitraryColumns::Vin), Qt::UserRole).toUInt(); parentBlockId = parentItem->data(int_cast(ArbitraryColumns::Vin), Qt::UserRole).toUInt();
directParent = true;
}
m_collection->collectValuesAndPoints(_chartType, _threadId, m_vin m_collection->collectValuesAndPoints(_chartType, _threadId, m_vin
, text(int_cast(ArbitraryColumns::Name)).toStdString().c_str(), EASY_GLOBALS.begin_time , text(int_cast(ArbitraryColumns::Name)).toStdString().c_str(), EASY_GLOBALS.begin_time
, parentBlockId, directParent); , parentBlockId);
} }
void ArbitraryTreeWidgetItem::interrupt() void ArbitraryTreeWidgetItem::interrupt()
@ -1646,6 +1650,7 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent)
, m_filterComboBox(new QComboBox(this)) , m_filterComboBox(new QComboBox(this))
, m_filterWindowLabel(new QLabel(tr(" Window size:"), this)) , m_filterWindowLabel(new QLabel(tr(" Window size:"), this))
, m_filterWindowPicker(new QSpinBox(this)) , m_filterWindowPicker(new QSpinBox(this))
, m_exportToCsvAction(nullptr)
, m_boldItem(nullptr) , m_boldItem(nullptr)
{ {
m_splitter->setHandleWidth(1); m_splitter->setHandleWidth(1);
@ -1668,9 +1673,14 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent)
tb->setIconSize(applicationIconsSize()); tb->setIconSize(applicationIconsSize());
auto action = tb->addAction(QIcon(imagePath("reload")), tr("Refresh values list")); auto action = tb->addAction(QIcon(imagePath("reload")), tr("Refresh values list"));
action->setToolTip(tr("Refresh arbitrary values list."));
connect(action, &QAction::triggered, this, &This::rebuild); connect(action, &QAction::triggered, this, &This::rebuild);
m_exportToCsvAction = tb->addAction(QIcon(imagePath("csv")), tr("Export to csv"));
connect(m_exportToCsvAction, &QAction::triggered, this, &This::onExportToCsvClicked);
m_exportToCsvAction->setEnabled(false);
tb->addSeparator();
auto actionGroup = new QActionGroup(this); auto actionGroup = new QActionGroup(this);
actionGroup->setExclusive(true); actionGroup->setExclusive(true);
@ -1762,6 +1772,7 @@ void ArbitraryValuesWidget::clear()
// in m_treeWidget->clear() // in m_treeWidget->clear()
m_chart->cancelImageUpdate(); m_chart->cancelImageUpdate();
m_exportToCsvAction->setEnabled(false);
m_checkedItems.clear(); m_checkedItems.clear();
m_treeWidget->clear(); m_treeWidget->clear();
m_boldItem = nullptr; m_boldItem = nullptr;
@ -1817,6 +1828,7 @@ void ArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column)
if (item->checkState(CheckColumn) == Qt::Checked) if (item->checkState(CheckColumn) == Qt::Checked)
{ {
m_exportToCsvAction->setEnabled(true);
m_checkedItems.push_back(item); m_checkedItems.push_back(item);
item->collectValues(EASY_GLOBALS.selected_thread, m_chart->chartType()); item->collectValues(EASY_GLOBALS.selected_thread, m_chart->chartType());
if (!m_collectionsTimer.isActive()) if (!m_collectionsTimer.isActive())
@ -1833,6 +1845,7 @@ void ArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column)
// !!! // !!!
m_checkedItems.removeOne(item); m_checkedItems.removeOne(item);
m_exportToCsvAction->setEnabled(!m_checkedItems.empty());
onCollectionsTimeout(); onCollectionsTimeout();
item->interrupt(); item->interrupt();
} }
@ -1863,7 +1876,7 @@ void ArbitraryValuesWidget::rebuild()
void ArbitraryValuesWidget::onCollectionsTimeout() void ArbitraryValuesWidget::onCollectionsTimeout()
{ {
if (m_checkedItems.isEmpty()) if (m_checkedItems.empty())
{ {
if (m_collectionsTimer.isActive()) if (m_collectionsTimer.isActive())
m_collectionsTimer.stop(); m_collectionsTimer.stop();
@ -1872,7 +1885,7 @@ void ArbitraryValuesWidget::onCollectionsTimeout()
} }
Collections collections; Collections collections;
collections.reserve(m_checkedItems.size()); collections.reserve(static_cast<size_t>(m_checkedItems.size()));
for (auto item : m_checkedItems) for (auto item : m_checkedItems)
{ {
if (item->collection()->status() != ArbitraryValuesCollection::InProgress) if (item->collection()->status() != ArbitraryValuesCollection::InProgress)
@ -1963,6 +1976,27 @@ void ArbitraryValuesWidget::onFilterWindowSizeChanged(int _size)
m_chart->setFilterWindowSize(_size); m_chart->setFilterWindowSize(_size);
} }
void ArbitraryValuesWidget::onExportToCsvClicked(bool)
{
if (m_checkedItems.empty())
return;
auto filename = QFileDialog::getSaveFileName(this, "Export arbitrary values to csv", EASY_GLOBALS.lastFileDir,
"CSV File Format (*.csv)");
if (filename.isEmpty())
return;
QFileInfo fileinfo(filename);
EASY_GLOBALS.lastFileDir = fileinfo.absoluteDir().canonicalPath();
if (fileinfo.suffix() != QStringLiteral("csv"))
filename += QStringLiteral(".csv");
QMessageBox::warning(this, "Warning", "Export to csv is not implemented yet", QMessageBox::Close);
// TODO: Implement export to csv
}
void ArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId) void ArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId)
{ {
m_treeWidget->clear(); m_treeWidget->clear();
@ -1997,8 +2031,8 @@ QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::Block
profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, _threadRoot, EASY_GLOBALS.hex_thread_id)); profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, _threadRoot, EASY_GLOBALS.hex_thread_id));
rootItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 0); rootItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 0);
const bool hasConcreteBlock = !profiler_gui::is_max(_blockIndex); const bool hasParticularBlockIndex = !profiler_gui::is_max(_blockIndex);
if (hasConcreteBlock) if (hasParticularBlockIndex)
{ {
const auto& block = easyBlocksTree(_blockIndex); const auto& block = easyBlocksTree(_blockIndex);
const auto& desc = easyDescriptor(block.node->id()); const auto& desc = easyDescriptor(block.node->id());
@ -2009,6 +2043,7 @@ QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::Block
valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name()); valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name());
valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(block.value->value_id(), 0, 16)); valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(block.value->value_id(), 0, 16));
valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*block.value)); valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*block.value));
valueItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 2);
const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width();
valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32); valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32);
@ -2019,51 +2054,99 @@ QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::Block
_blockId = block.node->id(); _blockId = block.node->id();
} }
const bool noId = profiler_gui::is_max(_blockId); const bool anyBlockId = profiler_gui::is_max(_blockId);
const bool hasParticularBlockId = !anyBlockId;
using Vins = std::unordered_map<profiler::vin_t, UsedValueTypes, estd::hash<profiler::vin_t> >;
using BlocksMap = std::unordered_map<profiler::block_id_t, QTreeWidgetItem*, estd::hash<profiler::block_id_t> >;
using GlobalValues = std::unordered_map<std::string, UsedValueTypes>;
using StackEntry = std::pair<profiler::block_index_t, profiler::block_index_t>;
using Stack = std::vector<StackEntry>;
Vins vins;
BlocksMap blocks;
GlobalValues globalValues;
QTreeWidgetItem* blockItem = nullptr; QTreeWidgetItem* blockItem = nullptr;
if (!noId) if (hasParticularBlockId)
{ {
blockItem = new QTreeWidgetItem(rootItem, StdItemType); blockItem = new QTreeWidgetItem(rootItem, StdItemType);
blockItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Block")); blockItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Block"));
if (hasConcreteBlock) if (hasParticularBlockIndex)
blockItem->setText(int_cast(ArbitraryColumns::Name), easyBlockName(_blockIndex)); blockItem->setText(int_cast(ArbitraryColumns::Name), easyBlockName(_blockIndex));
else else
blockItem->setText(int_cast(ArbitraryColumns::Name), easyDescriptor(_blockId).name()); blockItem->setText(int_cast(ArbitraryColumns::Name), easyDescriptor(_blockId).name());
blockItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 1); blockItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 1);
blockItem->setData(int_cast(ArbitraryColumns::Vin), Qt::UserRole, _blockId); blockItem->setData(int_cast(ArbitraryColumns::Vin), Qt::UserRole, _blockId);
blocks[_blockId] = blockItem;
} }
std::unordered_map<profiler::block_id_t, QTreeWidgetItem*, estd::hash<profiler::block_id_t> > blocks; // Depth-first search traverse
std::unordered_map<profiler::vin_t, UsedValueTypes, estd::hash<profiler::vin_t> > vins;
std::unordered_map<std::string, UsedValueTypes> names;
std::vector<profiler::block_index_t> stack; Stack stack;
for (auto childIndex : _threadRoot.children) for (const auto index : _threadRoot.children)
{ {
stack.push_back(childIndex); size_t matchedParentIdStackDepth = 0;
bool matchedParentId = anyBlockId;
stack.clear();
stack.emplace_back(index, static_cast<profiler::block_index_t>(0));
if (anyBlockId)
blockItem = nullptr;
while (!stack.empty()) while (!stack.empty())
{ {
const auto i = stack.back(); auto& top = stack.back();
stack.pop_back(); auto& first = top.second;
const auto i = top.first;
const auto& block = easyBlocksTree(i); const auto& block = easyBlock(i).tree;
if (noId || block.node->id() == _blockId || easyDescriptor(block.node->id()).id() == _blockId) const auto& desc = easyDescriptor(block.node->id());
if (desc.type() == profiler::BlockType::Value && matchedParentId)
{ {
for (auto c : block.children) const auto value = block.value;
const auto typeIndex = int_cast(value->type());
auto vin = value->value_id();
ArbitraryTreeWidgetItem** usedItems = nullptr;
ArbitraryTreeWidgetItem* valueItem = nullptr;
if (vin == 0)
{ {
if (noId) auto result = globalValues.emplace(desc.name(), 0);
stack.push_back(c); usedItems = result.first->second.items;
if (!result.second)
const auto& child = easyBlocksTree(c); valueItem = *(usedItems + typeIndex);
const auto& desc = easyDescriptor(child.node->id()); }
if (desc.type() != profiler::BlockType::Value) else
continue; {
auto result = vins.emplace(vin, 0);
usedItems = result.first->second.items;
if (!result.second)
valueItem = *(usedItems + typeIndex);
}
if (valueItem != nullptr)
{
if (i == _blockIndex)
valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*value));
//continue; // already in set
}
else
{
if (blockItem == nullptr) if (blockItem == nullptr)
{ {
const auto id = block.node->id(); // Enter here only if anyBlockId is true.
// matchedParentIdStackDepth is always == 0 in such case.
const auto parentBlockIndex = stack[matchedParentIdStackDepth].first;
const auto& parentBlock = easyBlock(parentBlockIndex).tree;
const auto id = parentBlock.node->id();
auto it = blocks.find(id); auto it = blocks.find(id);
if (it != blocks.end()) if (it != blocks.end())
{ {
@ -2073,62 +2156,50 @@ QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::Block
{ {
blockItem = new QTreeWidgetItem(rootItem, StdItemType); blockItem = new QTreeWidgetItem(rootItem, StdItemType);
blockItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Block")); blockItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Block"));
blockItem->setText(int_cast(ArbitraryColumns::Name), easyBlockName(block)); blockItem->setText(int_cast(ArbitraryColumns::Name), easyBlockName(parentBlock));
blockItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 1); blockItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 1);
blockItem->setData(int_cast(ArbitraryColumns::Vin), Qt::UserRole, id); blockItem->setData(int_cast(ArbitraryColumns::Vin), Qt::UserRole, id);
blocks.emplace(id, blockItem); blocks.emplace(id, blockItem);
} }
} }
const auto typeIndex = int_cast(child.value->type());
auto vin = child.value->value_id();
ArbitraryTreeWidgetItem** usedItems = nullptr;
ArbitraryTreeWidgetItem* valueItem = nullptr;
if (vin == 0)
{
auto result = names.emplace(desc.name(), 0);
usedItems = result.first->second.items;
if (!result.second && (valueItem = *(usedItems + typeIndex)))
{
if (i == _blockIndex)
valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*child.value));
continue; // already in set
}
}
else
{
auto result = vins.emplace(vin, 0);
usedItems = result.first->second.items;
if (!result.second && (valueItem = *(usedItems + typeIndex)))
{
if (i == _blockIndex)
valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*child.value));
continue; // already in set
}
}
valueItem = new ArbitraryTreeWidgetItem(blockItem, desc.color(), vin); valueItem = new ArbitraryTreeWidgetItem(blockItem, desc.color(), vin);
valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*child.value)); valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*value));
valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name()); valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name());
valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(vin, 0, 16)); valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(vin, 0, 16));
valueItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 2);
if (i == _blockIndex) if (i == _blockIndex)
valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*child.value)); valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*value));
const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width();
valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32); valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32);
*(usedItems + typeIndex) = valueItem; *(usedItems + typeIndex) = valueItem;
} }
}
if (noId) if (first < block.children.size())
blockItem = nullptr; {
if (hasParticularBlockId && (block.node->id() == _blockId || desc.id() == _blockId))
{
if (!matchedParentId)
matchedParentIdStackDepth = stack.size();
matchedParentId = true;
}
const auto child = block.children[first++];
stack.emplace_back(child, static_cast<profiler::block_index_t>(0));
} }
else else
{ {
for (auto c : block.children) if (hasParticularBlockId && stack.size() == matchedParentIdStackDepth)
stack.push_back(c); {
matchedParentId = false;
blockItem = nullptr;
}
stack.pop_back();
} }
} }
} }

View File

@ -159,19 +159,19 @@ public:
void collectValues(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::block_id_t _parentBlockId, bool _directParent); void collectValues(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::block_id_t _parentBlockId, bool _directParent);
bool calculatePoints(profiler::timestamp_t _beginTime); bool calculatePoints(profiler::timestamp_t _beginTime);
void collectValuesAndPoints(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::timestamp_t _beginTime, profiler::block_id_t _parentBlockId, bool _directParent); void collectValuesAndPoints(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::timestamp_t _beginTime, profiler::block_id_t _parentBlockId);
void interrupt(); void interrupt();
private: private:
void setStatus(JobStatus _status); void setStatus(JobStatus _status);
void collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId, profiler::block_id_t _parentBlockId, bool _directParent); void collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId, profiler::block_id_t _parentBlockId);
void collectByName(profiler::thread_id_t _threadId, const std::string _valueName, profiler::block_id_t _parentBlockId, bool _directParent); void collectByName(profiler::thread_id_t _threadId, const std::string _valueName, profiler::block_id_t _parentBlockId);
bool collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::vin_t _valueId, bool _calculatePoints, profiler::block_id_t _parentBlockId, bool _directParent); bool collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::vin_t _valueId, bool _calculatePoints, profiler::block_id_t _parentBlockId);
bool collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot, const std::string& _valueName, bool _calculatePoints, profiler::block_id_t _parentBlockId, bool _directParent); bool collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot, const std::string& _valueName, bool _calculatePoints, profiler::block_id_t _parentBlockId);
bool depthFirstSearch(const profiler::BlocksTreeRoot& _threadRoot, bool _calculatePoints bool depthFirstSearch(const profiler::BlocksTreeRoot& _threadRoot, bool _calculatePoints
, profiler::block_id_t _parentBlockId, bool _directParent, std::function<bool(profiler::vin_t, const char*)> _isSuitableValue); , profiler::block_id_t _parentBlockId, std::function<bool(profiler::vin_t, const char*)> _isSuitableValue);
double addPoint(const profiler::ArbitraryValue& _value); double addPoint(const profiler::ArbitraryValue& _value);
QPointF point(const profiler::ArbitraryValue& _value) const; QPointF point(const profiler::ArbitraryValue& _value) const;
@ -341,6 +341,7 @@ class ArbitraryValuesWidget : public QWidget
class QComboBox* m_filterComboBox; class QComboBox* m_filterComboBox;
class QLabel* m_filterWindowLabel; class QLabel* m_filterWindowLabel;
class QSpinBox* m_filterWindowPicker; class QSpinBox* m_filterWindowPicker;
class QAction* m_exportToCsvAction;
ArbitraryTreeWidgetItem* m_boldItem; ArbitraryTreeWidgetItem* m_boldItem;
public: public:
@ -367,6 +368,7 @@ private slots:
void onComplexityChartTypeChecked(bool _checked); void onComplexityChartTypeChecked(bool _checked);
void onFilterComboBoxChanged(int _index); void onFilterComboBoxChanged(int _index);
void onFilterWindowSizeChanged(int _size); void onFilterWindowSizeChanged(int _size);
void onExportToCsvClicked(bool);
private: private:

View File

@ -197,6 +197,7 @@ namespace profiler_gui {
EasyBlocks gui_blocks; ///< Profiler graphics blocks builded by GUI EasyBlocks gui_blocks; ///< Profiler graphics blocks builded by GUI
QString theme; ///< Current UI theme name QString theme; ///< Current UI theme name
QString lastFileDir;
Fonts font; ///< Fonts Fonts font; ///< Fonts
SceneData scene; ///< Diagram scene sizes and visible area position SceneData scene; ///< Diagram scene sizes and visible area position

View File

@ -42,3 +42,5 @@ default/arrow-up-disabled.svg - Icon made by Freepik from www.flaticon.com
default/arrow-left.svg - Icon made by Freepik from www.flaticon.com default/arrow-left.svg - Icon made by Freepik from www.flaticon.com
default/arrow-right.svg - Icon made by Freepik from www.flaticon.com default/arrow-right.svg - Icon made by Freepik from www.flaticon.com
default/yx.svg - Icon made by Freepik from www.flaticon.com default/yx.svg - Icon made by Freepik from www.flaticon.com
default/svg2.svg - Icon made by Freepik from www.flaticon.com
default/svg3.svg - Icon made by Freepik from www.flaticon.com

View File

@ -0,0 +1,15 @@
<?xml version="1.0" ?><svg id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g>
<polygon id="back" fill="#484848" points="330.7,6 87.9,6 87.9,506 449.2,506 449.2,122.8 "/>
<polygon id="corner" fill="#f44336" points="330.7,6 449.2,122.8 330.7,122.8 "/>
<rect id="csvback" fill="#f44336" height="156.1" width="329" x="62.8" y="298.8"/>
<polygon id="scvbackcorner" fill="#c41306" points="62.8,454.9 87.9,476.1 87.9,454.9 "/>
</g>
<g>
<path id="c" fill="#ffffff" d="M149,400.1c3.1,0,5.4-0.8,6.9-2.5c1.5-1.7,2.3-4.1,2.3-7.2h20.6l0.1,0.4 c0.2,7.9-2.5,14.3-8.2,19.3c-5.6,5-12.9,7.4-21.7,7.4c-11,0-19.5-3.4-25.4-10.2c-5.9-6.8-8.9-15.6-8.9-26.5v-1.5 c0-10.9,3-19.7,8.9-26.5c5.9-6.8,14.4-10.2,25.3-10.2c9.2,0,16.5,2.5,22,7.6c5.5,5.1,8.2,12.1,8,20.9l-0.1,0.4h-20.6 c0-3.4-0.8-6.2-2.3-8.4c-1.6-2.2-3.9-3.2-6.9-3.2c-4.4,0-7.4,1.7-9.1,5.2c-1.7,3.5-2.6,8.2-2.6,14.1v1.5c0,6.1,0.9,10.8,2.6,14.2 C141.4,398.4,144.5,400.1,149,400.1z"/>
<path id="s" fill="#ffffff" d="M235.3,395.8c0-1.8-1-3.4-3-4.8c-2-1.4-5.7-2.7-11.1-3.8c-8.4-1.6-14.8-4.2-19-7.6 c-4.3-3.4-6.4-8.2-6.4-14.3c0-6.4,2.7-11.8,8-16.2c5.3-4.4,12.6-6.6,21.7-6.6c9.6,0,17.3,2.1,23,6.4c5.7,4.3,8.4,9.9,8.1,16.7 l-0.1,0.4h-21.9c0-2.8-0.7-5-2.1-6.5c-1.4-1.5-3.8-2.2-7-2.2c-2.4,0-4.3,0.6-5.9,1.9c-1.6,1.3-2.4,2.9-2.4,4.9 c0,1.9,0.9,3.5,2.8,4.9c1.8,1.3,5.6,2.5,11.2,3.6c8.9,1.7,15.4,4.3,19.6,7.8c4.2,3.5,6.3,8.3,6.3,14.7c0,6.5-2.9,11.9-8.6,16.1 c-5.8,4.2-13.4,6.3-22.9,6.3c-9.8,0-17.6-2.5-23.3-7.5c-5.7-5-8.4-10.6-8.2-16.7l0.1-0.4h20.4c0.1,3.4,1.1,5.9,3.1,7.4 c2,1.5,4.8,2.3,8.4,2.3c3,0,5.3-0.6,6.9-1.8C234.5,399.6,235.3,398,235.3,395.8z"/>
<path id="v" fill="#ffffff" d="M304.2,386.3l1.3,7.7l0.4,0.1l1.5-7.7l9.6-42.5h23.7l-23.5,72.3h-22.9l-23.5-72.3h23.8 L304.2,386.3z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -801,6 +801,14 @@ MainWindow::~MainWindow()
{ {
} }
void MainWindow::validateLastDir()
{
if (m_lastFiles.empty())
EASY_GLOBALS.lastFileDir = QString();
else
EASY_GLOBALS.lastFileDir = QFileInfo(m_lastFiles.front()).absoluteDir().canonicalPath();
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void MainWindow::dragEnterEvent(QDragEnterEvent* drag_event) void MainWindow::dragEnterEvent(QDragEnterEvent* drag_event)
@ -901,6 +909,7 @@ void MainWindow::onOpenFileClicked(bool)
void MainWindow::addFileToList(const QString& filename) void MainWindow::addFileToList(const QString& filename)
{ {
m_lastFiles.push_front(filename); m_lastFiles.push_front(filename);
validateLastDir();
auto action = new QAction(filename, this); auto action = new QAction(filename, this);
connect(action, &QAction::triggered, this, &This::onOpenFileClicked); connect(action, &QAction::triggered, this, &This::onOpenFileClicked);
@ -985,7 +994,7 @@ void MainWindow::onSaveFileClicked(bool)
auto action = m_loadActionMenu->actions().front(); auto action = m_loadActionMenu->actions().front();
m_loadActionMenu->removeAction(action); m_loadActionMenu->removeAction(action);
delete action; delete action;
validateLastDir();
return; return;
} }
@ -1060,6 +1069,7 @@ void MainWindow::onSaveFileClicked(bool)
auto action = m_loadActionMenu->actions().front(); auto action = m_loadActionMenu->actions().front();
m_loadActionMenu->removeAction(action); m_loadActionMenu->removeAction(action);
delete action; delete action;
validateLastDir();
} }
addFileToList(filename); addFileToList(filename);
@ -1348,7 +1358,10 @@ void MainWindow::loadSettings()
auto last_files = settings.value("last_files"); auto last_files = settings.value("last_files");
if (!last_files.isNull()) if (!last_files.isNull())
{
m_lastFiles = last_files.toStringList(); m_lastFiles = last_files.toStringList();
validateLastDir();
}
auto last_addr = settings.value("ip_address"); auto last_addr = settings.value("ip_address");
if (!last_addr.isNull()) if (!last_addr.isNull())
@ -1846,6 +1859,7 @@ void MainWindow::onFileReaderTimeout()
auto action = fileActions.at(index); auto action = fileActions.at(index);
m_loadActionMenu->removeAction(action); m_loadActionMenu->removeAction(action);
m_loadActionMenu->insertAction(fileActions.front(), action); m_loadActionMenu->insertAction(fileActions.front(), action);
validateLastDir();
} }
m_bOpenedCacheFile = filename.contains(NETWORK_CACHE_FILE); m_bOpenedCacheFile = filename.contains(NETWORK_CACHE_FILE);
@ -1908,6 +1922,7 @@ void MainWindow::onFileReaderTimeout()
auto action = m_loadActionMenu->actions().at(index); auto action = m_loadActionMenu->actions().at(index);
m_loadActionMenu->removeAction(action); m_loadActionMenu->removeAction(action);
delete action; delete action;
validateLastDir();
} }
} }
} }

View File

@ -330,6 +330,8 @@ protected slots:
void checkFrameTimeReady(); void checkFrameTimeReady();
void validateLastDir();
private: private:
// Private non-virtual methods // Private non-virtual methods

View File

@ -49,5 +49,6 @@
<file alias="arrow-down-disabled">images/default/arrow-down-disabled.svg</file> <file alias="arrow-down-disabled">images/default/arrow-down-disabled.svg</file>
<file alias="yx-chart">images/default/yx.svg</file> <file alias="yx-chart">images/default/yx.svg</file>
<file alias="big-o-chart">images/default/big-o.svg</file> <file alias="big-o-chart">images/default/big-o.svg</file>
<file alias="csv">images/default/csv.svg</file>
</qresource> </qresource>
</RCC> </RCC>