From 5e632bedee27f627ce3f612c82b5ab1eb4673962 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 7 Mar 2018 20:52:39 +0300 Subject: [PATCH] #31 [Core] Arbitrary values: replaced size_t with uint16_t; [Gui] Added support of arrays to arbitrary values tree --- .../include/easy/arbitrary_value.h | 34 +- .../include/easy/serialized_block.h | 4 + easy_profiler_core/profile_manager.cpp | 6 +- easy_profiler_core/profile_manager.h | 2 +- easy_profiler_core/thread_storage.cpp | 6 +- easy_profiler_core/thread_storage.h | 2 +- profiler_gui/arbitrary_value_inspector.cpp | 322 ++++++++++++++---- profiler_gui/arbitrary_value_inspector.h | 41 ++- profiler_gui/blocks_graphics_view.cpp | 2 +- profiler_gui/common_functions.cpp | 137 +++++++- profiler_gui/common_functions.h | 3 + .../images/default/check-partial-disabled.svg | 7 + profiler_gui/images/default/check-partial.svg | 7 + profiler_gui/resources.qrc | 2 + profiler_gui/themes/default.css | 8 +- profiler_gui/themes/default.scss | 5 +- sample/main.cpp | 3 +- 17 files changed, 472 insertions(+), 119 deletions(-) create mode 100644 profiler_gui/images/default/check-partial-disabled.svg create mode 100644 profiler_gui/images/default/check-partial.svg diff --git a/easy_profiler_core/include/easy/arbitrary_value.h b/easy_profiler_core/include/easy/arbitrary_value.h index 36a1899..d2f570d 100644 --- a/easy_profiler_core/include/easy/arbitrary_value.h +++ b/easy_profiler_core/include/easy/arbitrary_value.h @@ -129,7 +129,7 @@ void foo(const A& a) { \note Also stores a time-stamp of it's occurrence like an Event. -\note To store an array, please, use EASY_ARRAY macro. +\note To store a dynamic array (which size is unknown at compile time), please, use EASY_ARRAY macro. \note Currently arbitrary values support only compile-time names. @@ -149,6 +149,8 @@ void foo(const A& a) { \note Currently arbitrary values support only compile-time names. +\warning Max array size is 4096. Passing bigger size has undefined behavior. + \sa EASY_VALUE, EASY_TEXT, EASY_STRING \ingroup profiler @@ -167,6 +169,8 @@ Could be C-string or std::string. \note Currently arbitrary values support only compile-time names. +\warning Max string length is 4096 (including trailing '\0'). Passing bigger size has undefined behavior. + \sa EASY_VALUE, EASY_ARRAY, EASY_STRING \ingroup profiler @@ -188,6 +192,8 @@ Use this for C-strings of known length (compile-time or run-time). \note Currently arbitrary values support only compile-time names. +\warning Max string length is 4096 (including trailing '\0'). Passing bigger size has undefined behavior. + \sa EASY_VALUE, EASY_ARRAY, EASY_TEXT \ingroup profiler @@ -201,10 +207,10 @@ Use this for C-strings of known length (compile-time or run-time). namespace profiler { - EASY_CONSTEXPR uint16_t MaxArbitraryValuesArraySize = 65535; + EASY_CONSTEXPR uint16_t MaxArbitraryValuesArraySize = 4096; extern "C" PROFILER_API void storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, - size_t _size, bool _isArray, ValueId _vin); + uint16_t _size, bool _isArray, ValueId _vin); template inline void setValue(const BaseBlockDescriptor* _desc, T _value, ValueId _vin) @@ -217,16 +223,17 @@ namespace profiler static_assert(StdToDataType::data_type != DataType::TypesCount, "You should use standard builtin scalar types as profiler::Value type!"); - storeValue(_desc, StdToDataType::data_type, &_value, sizeof(Type), false, _vin); + storeValue(_desc, StdToDataType::data_type, &_value, static_cast(sizeof(Type)), false, _vin); } + ///< WARNING: Passing _arraySize > 4096 may cause undefined behavior! template inline void setValue(const BaseBlockDescriptor* _desc, const T* _valueArray, ValueId _vin, uint16_t _arraySize) { static_assert(StdToDataType::data_type != DataType::TypesCount, "You should use standard builtin scalar types as profiler::Value type!"); - storeValue(_desc, StdToDataType::data_type, _valueArray, sizeof(T) * _arraySize, true, _vin); + storeValue(_desc, StdToDataType::data_type, _valueArray, static_cast(sizeof(T) * _arraySize), true, _vin); } template @@ -235,31 +242,34 @@ namespace profiler static_assert(StdToDataType::data_type != DataType::TypesCount, "You should use standard builtin scalar types as profiler::Value type!"); - static_assert(N <= MaxArbitraryValuesArraySize, "Maximum arbitrary values array size is 65535."); + static_assert(N <= MaxArbitraryValuesArraySize, "Maximum arbitrary values array size is 4096."); - storeValue(_desc, StdToDataType::data_type, _value, sizeof(_value), true, _vin); + storeValue(_desc, StdToDataType::data_type, _value, static_cast(sizeof(_value)), true, _vin); } + ///< WARNING: Passing _textLength > 4096 may cause undefined behavior! inline void setText(const BaseBlockDescriptor* _desc, const char* _text, ValueId _vin, uint16_t _textLength) { storeValue(_desc, DataType::String, _text, _textLength, true, _vin); } + ///< WARNING: Passing _text with length > 4096 may cause undefined behavior! inline void setText(const BaseBlockDescriptor* _desc, const char* _text, ValueId _vin) { - storeValue(_desc, DataType::String, _text, strlen(_text) + 1, true, _vin); + storeValue(_desc, DataType::String, _text, static_cast(strlen(_text) + 1), true, _vin); } + ///< WARNING: Passing _text with length > 4096 may cause undefined behavior! inline void setText(const BaseBlockDescriptor* _desc, const ::std::string& _text, ValueId _vin) { - storeValue(_desc, DataType::String, _text.c_str(), _text.size() + 1, true, _vin); + storeValue(_desc, DataType::String, _text.c_str(), static_cast(_text.size() + 1), true, _vin); } template inline void setText(const BaseBlockDescriptor* _desc, const char (&_text)[N], ValueId _vin) { - static_assert(N <= MaxArbitraryValuesArraySize, "Maximum arbitrary values array size is 65535."); - storeValue(_desc, DataType::String, &_text[0], N, true, _vin); + static_assert(N <= MaxArbitraryValuesArraySize, "Maximum arbitrary values array size is 4096."); + storeValue(_desc, DataType::String, &_text[0], static_cast(N), true, _vin); } } // end of namespace profiler. @@ -277,7 +287,7 @@ namespace profiler namespace profiler { - inline void storeValue(const BaseBlockDescriptor*, DataType, const void*, size_t, bool, ValueId) {} + inline void storeValue(const BaseBlockDescriptor*, DataType, const void*, uint16_t, bool, ValueId) {} template inline void setValue(const BaseBlockDescriptor*, T, ValueId) {} diff --git a/easy_profiler_core/include/easy/serialized_block.h b/easy_profiler_core/include/easy/serialized_block.h index 1708c72..93828f8 100644 --- a/easy_profiler_core/include/easy/serialized_block.h +++ b/easy_profiler_core/include/easy/serialized_block.h @@ -202,6 +202,10 @@ namespace profiler { return reinterpret_cast(this) + sizeof(ArbitraryValue); } + uint16_t data_size() const { + return m_size; + } + vin_t value_id() const { return m_value_id; } diff --git a/easy_profiler_core/profile_manager.cpp b/easy_profiler_core/profile_manager.cpp index 99950bc..61a0ab6 100644 --- a/easy_profiler_core/profile_manager.cpp +++ b/easy_profiler_core/profile_manager.cpp @@ -321,7 +321,7 @@ extern "C" { return MANAGER.isEnabled(); } - PROFILER_API void storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, size_t _size, bool _isArray, ValueId _vin) + PROFILER_API void storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, uint16_t _size, bool _isArray, ValueId _vin) { MANAGER.storeValue(_desc, _type, _data, _size, _isArray, _vin); } @@ -487,7 +487,7 @@ extern "C" { PROFILER_API void endBlock() { } PROFILER_API void setEnabled(bool) { } PROFILER_API bool isEnabled() { return false; } - PROFILER_API void storeValue(const BaseBlockDescriptor*, DataType, const void*, size_t, bool, ValueId) {} + PROFILER_API void storeValue(const BaseBlockDescriptor*, DataType, const void*, uint16_t, bool, ValueId) {} PROFILER_API void storeEvent(const BaseBlockDescriptor*, const char*) { } PROFILER_API void storeBlock(const BaseBlockDescriptor*, const char*, timestamp_t, timestamp_t) { } PROFILER_API void beginBlock(Block&) { } @@ -781,7 +781,7 @@ const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _d ////////////////////////////////////////////////////////////////////////// -void ProfileManager::storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, size_t _size, bool _isArray, ValueId _vin) +void ProfileManager::storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, uint16_t _size, bool _isArray, ValueId _vin) { if (!isEnabled() || (_desc->m_status & profiler::ON) == 0) return; diff --git a/easy_profiler_core/profile_manager.h b/easy_profiler_core/profile_manager.h index 24abc1d..805bd68 100644 --- a/easy_profiler_core/profile_manager.h +++ b/easy_profiler_core/profile_manager.h @@ -140,7 +140,7 @@ public: profiler::color_t _color, bool _copyName = false); - void storeValue(const profiler::BaseBlockDescriptor* _desc, profiler::DataType _type, const void* _data, size_t _size, bool _isArray, profiler::ValueId _vin); + void storeValue(const profiler::BaseBlockDescriptor* _desc, profiler::DataType _type, const void* _data, uint16_t _size, bool _isArray, profiler::ValueId _vin); bool storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName); bool storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime); void beginBlock(profiler::Block& _block); diff --git a/easy_profiler_core/thread_storage.cpp b/easy_profiler_core/thread_storage.cpp index 9aa2f7e..760f681 100644 --- a/easy_profiler_core/thread_storage.cpp +++ b/easy_profiler_core/thread_storage.cpp @@ -57,12 +57,12 @@ ThreadStorage::ThreadStorage() expired = ATOMIC_VAR_INIT(0); } -void ThreadStorage::storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, size_t _size, bool _isArray, profiler::ValueId _vin) +void ThreadStorage::storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, uint16_t _size, bool _isArray, profiler::ValueId _vin) { - const uint16_t serializedDataSize = static_cast(sizeof(profiler::ArbitraryValue) + _size); + const uint16_t serializedDataSize = _size + static_cast(sizeof(profiler::ArbitraryValue)); void* data = blocks.closedList.allocate(serializedDataSize); - ::new (data) profiler::ArbitraryValue(_timestamp, _vin.m_id, _id, static_cast(_size), _type, _isArray); + ::new (data) profiler::ArbitraryValue(_timestamp, _vin.m_id, _id, _size, _type, _isArray); char* cdata = reinterpret_cast(data); memcpy(cdata + sizeof(profiler::ArbitraryValue), _data, _size); diff --git a/easy_profiler_core/thread_storage.h b/easy_profiler_core/thread_storage.h index 699be59..27c1489 100644 --- a/easy_profiler_core/thread_storage.h +++ b/easy_profiler_core/thread_storage.h @@ -114,7 +114,7 @@ struct ThreadStorage EASY_FINAL bool guarded; ///< True if thread has been registered using ThreadGuard bool frameOpened; ///< Is new frame opened (this does not depend on profiling status) \sa profiledFrameOpened - void storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, size_t _size, bool _isArray, profiler::ValueId _vin); + void storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, uint16_t _size, bool _isArray, profiler::ValueId _vin); void storeBlock(const profiler::Block& _block); void storeBlockForce(const profiler::Block& _block); void storeCSwitch(const CSwitchBlock& _block); diff --git a/profiler_gui/arbitrary_value_inspector.cpp b/profiler_gui/arbitrary_value_inspector.cpp index 3332332..41cb5fc 100644 --- a/profiler_gui/arbitrary_value_inspector.cpp +++ b/profiler_gui/arbitrary_value_inspector.cpp @@ -246,7 +246,7 @@ qreal ArbitraryValuesCollection::maxValue() const } void ArbitraryValuesCollection::collectValues(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId - , const char* _valueName, profiler::block_id_t _parentBlockId) + , const char* _valueName, profiler::block_id_t _parentBlockId, int _index) { interrupt(); @@ -262,13 +262,13 @@ void ArbitraryValuesCollection::collectValues(ChartType _chartType, profiler::th } if (_valueId == 0) - m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId); }, m_bInterrupt); + m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId, _index); }, m_bInterrupt); else - m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId); }, m_bInterrupt); + m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId, _index); }, m_bInterrupt); } 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) + , const char* _valueName, profiler::timestamp_t _beginTime, profiler::block_id_t _parentBlockId, int _index) { interrupt(); @@ -287,9 +287,9 @@ void ArbitraryValuesCollection::collectValuesAndPoints(ChartType _chartType, pro } if (_valueId == 0) - m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId); }, m_bInterrupt); + m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId, _index); }, m_bInterrupt); else - m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId); }, m_bInterrupt); + m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId, _index); }, m_bInterrupt); } bool ArbitraryValuesCollection::calculatePoints(profiler::timestamp_t _beginTime) @@ -332,7 +332,7 @@ void ArbitraryValuesCollection::setStatus(JobStatus _status) } void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId - , profiler::block_id_t _parentBlockId) + , profiler::block_id_t _parentBlockId, int _index) { const bool doCalculatePoints = (m_jobType & PointsJob) != 0; @@ -345,7 +345,7 @@ void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, pro for (const auto& it : EASY_GLOBALS.profiler_blocks) { - if (!collectByIdForThread(it.second, _valueId, calculatePointsInner, _parentBlockId)) + if (!collectByIdForThread(it.second, _valueId, calculatePointsInner, _parentBlockId, _index)) return; } @@ -365,7 +365,7 @@ void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, pro { const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId); if (t != EASY_GLOBALS.profiler_blocks.end() && - !collectByIdForThread(t->second, _valueId, doCalculatePoints, _parentBlockId)) + !collectByIdForThread(t->second, _valueId, doCalculatePoints, _parentBlockId, _index)) { return; } @@ -386,7 +386,7 @@ void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, pro } bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot - , profiler::vin_t _valueId, bool _calculatePoints, profiler::block_id_t _parentBlockId) + , profiler::vin_t _valueId, bool _calculatePoints, profiler::block_id_t _parentBlockId, int _index) { if (profiler_gui::is_max(_parentBlockId)) { @@ -406,20 +406,23 @@ bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeR if (value->value_id() != _valueId) continue; + if (_index >= 0 && (!value->isArray() || profiler_gui::valueArraySize(*value) <= _index)) + continue; + m_values.push_back(value); if (_calculatePoints) - addPoint(*value); + addPoint(*value, _index); } return true; } - return depthFirstSearch(_threadRoot, _calculatePoints, _parentBlockId + return depthFirstSearch(_threadRoot, _calculatePoints, _parentBlockId, _index , [=] (profiler::vin_t _id, const char*) -> bool { return _id == _valueId; }); } void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, const std::string _valueName - , profiler::block_id_t _parentBlockId) + , profiler::block_id_t _parentBlockId, int _index) { const bool doCalculatePoints = (m_jobType & PointsJob) != 0; @@ -432,7 +435,7 @@ void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, c for (const auto& it : EASY_GLOBALS.profiler_blocks) { - if (!collectByNameForThread(it.second, _valueName, calculatePointsInner, _parentBlockId)) + if (!collectByNameForThread(it.second, _valueName, calculatePointsInner, _parentBlockId, _index)) return; } @@ -452,7 +455,7 @@ void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, c { const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId); if (t != EASY_GLOBALS.profiler_blocks.end() && - !collectByNameForThread(t->second, _valueName, doCalculatePoints, _parentBlockId)) + !collectByNameForThread(t->second, _valueName, doCalculatePoints, _parentBlockId, _index)) { return; } @@ -473,7 +476,7 @@ void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, c } bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot - , const std::string& _valueName, bool _calculatePoints, profiler::block_id_t _parentBlockId) + , const std::string& _valueName, bool _calculatePoints, profiler::block_id_t _parentBlockId, int _index) { if (profiler_gui::is_max(_parentBlockId)) { @@ -489,20 +492,24 @@ bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTre if (desc.type() != profiler::BlockType::Value || _valueName != desc.name()) continue; - m_values.push_back(block.value); + const auto value = block.value; + if (_index >= 0 && (!value->isArray() || profiler_gui::valueArraySize(*value) <= _index)) + continue; + + m_values.push_back(value); if (_calculatePoints) - addPoint(*block.value); + addPoint(*value, _index); } return true; } - return depthFirstSearch(_threadRoot, _calculatePoints, _parentBlockId + return depthFirstSearch(_threadRoot, _calculatePoints, _parentBlockId, _index , [&_valueName] (profiler::vin_t, const char* _name) -> bool { return _valueName == _name; }); } bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot& _threadRoot, bool _calculatePoints - , profiler::block_id_t _parentBlockId, std::function _isSuitableValue) + , profiler::block_id_t _parentBlockId, int _index, std::function _isSuitableValue) { if (_threadRoot.children.empty()) return true; @@ -539,17 +546,20 @@ bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot& const auto value = block.value; if (_isSuitableValue(value->value_id(), desc.name())) { - m_values.push_back(value); - if (_calculatePoints) + if (_index < 0 || (value->isArray() && _index < profiler_gui::valueArraySize(*value))) { - const auto val = addPoint(*value); - if (m_chartType == ChartType::Complexity) + m_values.push_back(value); + if (_calculatePoints) { - m_complexityMap[val].push_back(lastMatchedParentDuration); - if (lastMatchedParentDuration < m_minDuration) - m_minDuration = lastMatchedParentDuration; - if (lastMatchedParentDuration > m_maxDuration) - m_maxDuration = lastMatchedParentDuration; + const auto val = addPoint(*value, _index); + if (m_chartType == ChartType::Complexity) + { + m_complexityMap[val].push_back(lastMatchedParentDuration); + if (lastMatchedParentDuration < m_minDuration) + m_minDuration = lastMatchedParentDuration; + if (lastMatchedParentDuration > m_maxDuration) + m_maxDuration = lastMatchedParentDuration; + } } } } @@ -580,9 +590,9 @@ bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot& return true; } -double ArbitraryValuesCollection::addPoint(const profiler::ArbitraryValue& _value) +double ArbitraryValuesCollection::addPoint(const profiler::ArbitraryValue& _value, int _index) { - const auto p = point(_value); + const auto p = point(_value, _index); if (p.y() > m_maxValue) m_maxValue = p.y(); @@ -595,10 +605,10 @@ double ArbitraryValuesCollection::addPoint(const profiler::ArbitraryValue& _valu return p.y(); } -QPointF ArbitraryValuesCollection::point(const profiler::ArbitraryValue& _value) const +QPointF ArbitraryValuesCollection::point(const profiler::ArbitraryValue& _value, int _index) const { const qreal x = PROF_MICROSECONDS(qreal(_value.begin() - m_beginTime)); - const qreal y = profiler_gui::value2real(_value); + const qreal y = profiler_gui::value2real(_value, std::max(_index, 0)); return {x, y}; } @@ -1297,12 +1307,9 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, continue; } - const auto first = leftBounds[i]; - if (first == complexityMap.end()) - { - ++i; + const auto first = leftBounds[i++]; + if (first == complexityMap.end() || first->first > right) continue; - } if (c.selected) { @@ -1323,9 +1330,12 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, averages.reserve(complexityMap.size()); auto it = first; - while (it->first < left) + while (it != complexityMap.end() && it->first < left) ++it; + if (it == complexityMap.end() || it->first > right) + continue; + qreal x = getx(it->first); profiler::timestamp_t average = 0; @@ -1667,14 +1677,21 @@ struct UsedValueTypes { ////////////////////////////////////////////////////////////////////////// -ArbitraryTreeWidgetItem::ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin) +ArbitraryTreeWidgetItem::ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, bool _checkable, profiler::color_t _color, profiler::vin_t _vin) : Parent(_parent, ValueItemType) , m_vin(_vin) , m_color(_color) , m_widthHint(0) { - setFlags(flags() | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable); - setCheckState(CheckColumn, Qt::Unchecked); + if (_checkable) + { + setFlags(flags() | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable); + setCheckState(CheckColumn, Qt::Unchecked); + } + else + { + setFlags(flags() & ~Qt::ItemIsUserCheckable); + } } ArbitraryTreeWidgetItem::~ArbitraryTreeWidgetItem() @@ -1711,6 +1728,35 @@ ArbitraryValuesCollection* ArbitraryTreeWidgetItem::collection() return m_collection.get(); } +bool ArbitraryTreeWidgetItem::isArrayItem() const +{ + return childCount() != 0; +} + +profiler::block_id_t ArbitraryTreeWidgetItem::getParentBlockId(QTreeWidgetItem* _item) const +{ + auto parentItem = _item->parent(); + const auto parentRole = parentItem->data(int_cast(ArbitraryColumns::Type), Qt::UserRole).toInt(); + switch (parentRole) + { + case 1: + return parentItem->data(int_cast(ArbitraryColumns::Vin), Qt::UserRole).toUInt(); + + case 2: + return getParentBlockId(parentItem); + + default: + return EASY_GLOBALS.selected_block_id; + } +} + +int ArbitraryTreeWidgetItem::getSelfIndexInArray() +{ + if (data(int_cast(ArbitraryColumns::Type), Qt::UserRole).toInt() != 3) + return -1; + return parent()->indexOfChild(this); +} + void ArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId, ChartType _chartType) { if (!m_collection) @@ -1718,17 +1764,14 @@ void ArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId, Cha else m_collection->interrupt(); - auto parentItem = parent(); + const auto parentBlockId = getParentBlockId(this); + const int index = getSelfIndexInArray(); - profiler::block_id_t parentBlockId = 0; - if (parentItem->data(int_cast(ArbitraryColumns::Type), Qt::UserRole).toInt() == 1) - parentBlockId = parentItem->data(int_cast(ArbitraryColumns::Vin), Qt::UserRole).toUInt(); - else - parentBlockId = EASY_GLOBALS.selected_block_id; + EASY_CONSTEXPR auto nameColumn = int_cast(ArbitraryColumns::Name); + const auto name = index < 0 ? text(nameColumn).toStdString() : parent()->text(nameColumn).toStdString(); - m_collection->collectValuesAndPoints(_chartType, _threadId, m_vin - , text(int_cast(ArbitraryColumns::Name)).toStdString().c_str(), EASY_GLOBALS.begin_time - , parentBlockId); + m_collection->collectValuesAndPoints(_chartType, _threadId, m_vin, name.c_str(), + EASY_GLOBALS.begin_time, parentBlockId, index); } void ArbitraryTreeWidgetItem::interrupt() @@ -1918,10 +1961,10 @@ void ArbitraryValuesWidget::onSelectedBlockIdChanged(::profiler::block_id_t) void ArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int) { - if (_item == nullptr || _item->type() != ValueItemType) + if (_item == nullptr || _item->type() != ValueItemType || (_item->flags() & Qt::ItemIsUserCheckable) == 0) return; - _item->setCheckState(CheckColumn, _item->checkState(CheckColumn) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + _item->setCheckState(CheckColumn, _item->checkState(CheckColumn) != Qt::Unchecked ? Qt::Unchecked : Qt::Checked); } void ArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column) @@ -1929,15 +1972,61 @@ void ArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column) if (_item == nullptr || _item->type() != ValueItemType || _column != CheckColumn) return; + if (_item->checkState(CheckColumn) == Qt::PartiallyChecked) + return; + auto item = static_cast(_item); if (item->checkState(CheckColumn) == Qt::Checked) { m_exportToCsvAction->setEnabled(true); - m_checkedItems.push_back(item); - item->collectValues(EASY_GLOBALS.selected_thread, m_chart->chartType()); - if (!m_collectionsTimer.isActive()) - m_collectionsTimer.start(100); + + const auto prevSize = m_checkedItems.size(); + if (!item->isArrayItem()) + { + m_checkedItems.push_back(item); + item->collectValues(EASY_GLOBALS.selected_thread, m_chart->chartType()); + + if (item->getSelfIndexInArray() >= 0) + { + Qt::CheckState newState = Qt::Checked; + auto parentItem = item->parent(); + for (int i = 0; i < parentItem->childCount(); ++i) + { + auto child = parentItem->child(i); + if (child->checkState(CheckColumn) != Qt::Checked) + { + newState = Qt::PartiallyChecked; + break; + } + } + + disconnect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + parentItem->setCheckState(CheckColumn, newState); + connect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + } + } + else + { + disconnect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + for (int i = 0; i < item->childCount(); ++i) + { + auto child = static_cast(item->child(i)); + if (child->checkState(CheckColumn) != Qt::Checked) + { + child->setCheckState(CheckColumn, Qt::Checked); + m_checkedItems.push_back(child); + child->collectValues(EASY_GLOBALS.selected_thread, m_chart->chartType()); + } + } + connect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + } + + if (prevSize != m_checkedItems.size()) + { + if (!m_collectionsTimer.isActive()) + m_collectionsTimer.start(100); + } } else { @@ -1949,10 +2038,56 @@ void ArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column) // in interrupt(). // !!! - m_checkedItems.removeOne(item); - m_exportToCsvAction->setEnabled(!m_checkedItems.empty()); - onCollectionsTimeout(); - item->interrupt(); + decltype(m_checkedItems) uncheckedItems; + + if (!item->isArrayItem()) + { + uncheckedItems.push_back(item); + m_checkedItems.removeOne(item); + + if (item->getSelfIndexInArray() >= 0) + { + Qt::CheckState newState = Qt::Unchecked; + auto parentItem = item->parent(); + for (int i = 0; i < parentItem->childCount(); ++i) + { + auto child = parentItem->child(i); + if (child->checkState(CheckColumn) != Qt::Unchecked) + { + newState = Qt::PartiallyChecked; + break; + } + } + + disconnect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + parentItem->setCheckState(CheckColumn, newState); + connect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + } + } + else + { + disconnect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + for (int i = 0; i < item->childCount(); ++i) + { + auto child = static_cast(item->child(i)); + if (child->checkState(CheckColumn) == Qt::Checked) + { + child->setCheckState(CheckColumn, Qt::Unchecked); + uncheckedItems.push_back(child); + m_checkedItems.removeOne(child); + } + } + connect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged); + } + + if (!uncheckedItems.isEmpty()) + { + m_exportToCsvAction->setEnabled(!m_checkedItems.empty()); + onCollectionsTimeout(); + + for (auto uncheckedItem : uncheckedItems) + uncheckedItem->interrupt(); + } } } @@ -2214,11 +2349,13 @@ QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::Block const auto& desc = easyDescriptor(block.node->id()); if (desc.type() == profiler::BlockType::Value) { - auto valueItem = new ArbitraryTreeWidgetItem(rootItem, desc.color(), block.value->value_id()); - valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*block.value)); + auto value = block.value; + const bool isString = value->type() == profiler::DataType::String; + auto valueItem = new ArbitraryTreeWidgetItem(rootItem, !isString, desc.color(), value->value_id()); + valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*value)); 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::Value), profiler_gui::valueString(*block.value)); + valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(value->value_id(), 0, 16)); + valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*value)); valueItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 2); const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); @@ -2311,8 +2448,31 @@ QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::Block if (valueItem != nullptr) { if (i == _blockIndex) - valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*value)); + valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::shortValueString(*value)); //continue; // already in set + + if (value->isArray() && value->type() != profiler::DataType::String) + { + const int size = profiler_gui::valueArraySize(*value); + if (valueItem->childCount() < size) + { + for (int childIndex = valueItem->childCount(); childIndex < size; ++childIndex) + { + auto item = new ArbitraryTreeWidgetItem(valueItem, true, desc.color(), vin); + item->setText(int_cast(ArbitraryColumns::Name), QString("%1[%2]").arg(desc.name()).arg(childIndex)); + item->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 3); + + if (i == _blockIndex) + item->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*value, childIndex)); + + const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); + item->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32); + } + + auto typeString = profiler_gui::valueTypeString(*value); + valueItem->setText(int_cast(ArbitraryColumns::Type), typeString.replace("[]", "[%1]").arg(size)); + } + } } else { @@ -2339,19 +2499,43 @@ QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::Block } } - valueItem = new ArbitraryTreeWidgetItem(blockItem, desc.color(), vin); + const bool isString = value->type() == profiler::DataType::String; + valueItem = new ArbitraryTreeWidgetItem(blockItem, !isString, desc.color(), vin); valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*value)); valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name()); 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) - valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*value)); + valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::shortValueString(*value)); - const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); + auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32); *(usedItems + typeIndex) = valueItem; + + if (value->isArray() && !isString) + { + const int size = profiler_gui::valueArraySize(*value); + if (valueItem->childCount() < size) + { + for (int childIndex = valueItem->childCount(); childIndex < size; ++childIndex) + { + auto item = new ArbitraryTreeWidgetItem(valueItem, true, desc.color(), vin); + item->setText(int_cast(ArbitraryColumns::Name), QString("%1[%2]").arg(desc.name()).arg(childIndex)); + item->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 3); + + if (i == _blockIndex) + item->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*value, childIndex)); + + sizeHintWidth = valueItem->sizeHint(CheckColumn).width(); + item->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32); + } + + auto typeString = profiler_gui::valueTypeString(*value); + valueItem->setText(int_cast(ArbitraryColumns::Type), typeString.replace("[]", "[%1]").arg(size)); + } + } } } diff --git a/profiler_gui/arbitrary_value_inspector.h b/profiler_gui/arbitrary_value_inspector.h index 658dc65..a8ded06 100644 --- a/profiler_gui/arbitrary_value_inspector.h +++ b/profiler_gui/arbitrary_value_inspector.h @@ -142,25 +142,39 @@ public: qreal minValue() const; qreal maxValue() const; - void collectValues(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::block_id_t _parentBlockId); + void collectValues(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId + , const char* _valueName, profiler::block_id_t _parentBlockId, int _index = -1); + 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); + 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 + , int _index = -1); + void interrupt(); private: void setStatus(JobStatus _status); - 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 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); + + void collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId + , profiler::block_id_t _parentBlockId, int _index); + + void collectByName(profiler::thread_id_t _threadId, const std::string _valueName + , profiler::block_id_t _parentBlockId, int _index); + + bool collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::vin_t _valueId + , bool _calculatePoints, profiler::block_id_t _parentBlockId, int _index); + + bool collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot, const std::string& _valueName + , bool _calculatePoints, profiler::block_id_t _parentBlockId, int _index); bool depthFirstSearch(const profiler::BlocksTreeRoot& _threadRoot, bool _calculatePoints - , profiler::block_id_t _parentBlockId, std::function _isSuitableValue); + , profiler::block_id_t _parentBlockId, int _index + , std::function _isSuitableValue); - double addPoint(const profiler::ArbitraryValue& _value); - QPointF point(const profiler::ArbitraryValue& _value) const; + double addPoint(const profiler::ArbitraryValue& _value, int _index); + QPointF point(const profiler::ArbitraryValue& _value, int _index) const; }; // end of class ArbitraryValuesCollection. @@ -290,7 +304,7 @@ class ArbitraryTreeWidgetItem : public QTreeWidgetItem public: - explicit ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin = 0); + explicit ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, bool _checkable, profiler::color_t _color, profiler::vin_t _vin = 0); ~ArbitraryTreeWidgetItem() override; QVariant data(int _column, int _role) const override; @@ -305,6 +319,13 @@ public: profiler::color_t color() const; + bool isArrayItem() const; + int getSelfIndexInArray(); + +private: + + profiler::block_id_t getParentBlockId(QTreeWidgetItem* _item) const; + }; // end of class ArbitraryTreeWidgetItem. ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp index 17f9222..cc9dc59 100644 --- a/profiler_gui/blocks_graphics_view.cpp +++ b/profiler_gui/blocks_graphics_view.cpp @@ -1743,7 +1743,7 @@ void BlocksGraphicsView::onIdleTimeout() ++row; lay->addWidget(new QLabel("Value:", widget), row, 0, Qt::AlignRight); - lay->addWidget(new QLabel(::profiler_gui::valueString(*itemBlock.value), widget), row, 1, Qt::AlignLeft); + lay->addWidget(new QLabel(::profiler_gui::shortValueString(*itemBlock.value), widget), row, 1, Qt::AlignLeft); ++row; lay->addWidget(new QLabel("VIN:", widget), row, 0, Qt::AlignRight); diff --git a/profiler_gui/common_functions.cpp b/profiler_gui/common_functions.cpp index ebdd287..53316a2 100644 --- a/profiler_gui/common_functions.cpp +++ b/profiler_gui/common_functions.cpp @@ -54,6 +54,11 @@ #include "common_functions.h" +template +static QString toString(const profiler::ArbitraryValue& _serializedValue, int _index) { + return QString::number(_serializedValue.toArray()->at(_index)); +} + template static QString toString(const profiler::ArbitraryValue& _serializedValue) { return QString::number(_serializedValue.toValue()->value()); @@ -69,6 +74,58 @@ static double toReal(const profiler::ArbitraryValue& _serializedValue) { return static_cast(_serializedValue.toValue()->value()); } +template +inline EASY_CONSTEXPR_FCN uint16_t sizeOf() { + return static_cast(sizeof(typename profiler::StdType::value_type)); +} + +QString arrayToString(const profiler::ArbitraryValue& _serializedValue, int _index) +{ + switch (_serializedValue.type()) + { + case profiler::DataType::Bool: + { + const auto value = _serializedValue.toArray()->at(_index); + return value ? QStringLiteral("true") : QStringLiteral("false"); + } + + case profiler::DataType::Char: return QChar(_serializedValue.toArray ()->at(_index)); + case profiler::DataType::Int8: return QChar(_serializedValue.toArray()->at(_index)); + case profiler::DataType::Uint8: return toString (_serializedValue, _index); + case profiler::DataType::Int16: return toString (_serializedValue, _index); + case profiler::DataType::Uint16: return toString(_serializedValue, _index); + case profiler::DataType::Int32: return toString (_serializedValue, _index); + case profiler::DataType::Uint32: return toString(_serializedValue, _index); + case profiler::DataType::Int64: return toString (_serializedValue, _index); + case profiler::DataType::Uint64: return toString(_serializedValue, _index); + case profiler::DataType::Float: return toString (_serializedValue, _index); + case profiler::DataType::Double: return toString (_serializedValue, _index); + case profiler::DataType::String: return QChar(_serializedValue.data()[_index]); + default: return QStringLiteral("??"); + } +} + +QString singleValueToString(const profiler::ArbitraryValue& _serializedValue) +{ + switch (_serializedValue.type()) + { + case profiler::DataType::Bool: return _serializedValue.toValue()->value() ? QStringLiteral("true") : QStringLiteral("false"); + case profiler::DataType::Char: return QChar(_serializedValue.toValue ()->value()); + case profiler::DataType::Int8: return QChar(_serializedValue.toValue()->value()); + case profiler::DataType::Uint8: return toString (_serializedValue); + case profiler::DataType::Int16: return toString (_serializedValue); + case profiler::DataType::Uint16: return toString(_serializedValue); + case profiler::DataType::Int32: return toString (_serializedValue); + case profiler::DataType::Uint32: return toString(_serializedValue); + case profiler::DataType::Int64: return toString (_serializedValue); + case profiler::DataType::Uint64: return toString(_serializedValue); + case profiler::DataType::Float: return toString (_serializedValue); + case profiler::DataType::Double: return toString (_serializedValue); + case profiler::DataType::String: return _serializedValue.data(); + default: return QStringLiteral("??"); + } +} + namespace profiler_gui { ////////////////////////////////////////////////////////////////////////// @@ -281,26 +338,74 @@ namespace profiler_gui { { if (_serializedValue.type() == ::profiler::DataType::String) return _serializedValue.data(); - return QStringLiteral("[...] array"); + + auto str = QString("[%1").arg(valueString(_serializedValue, 0)); + const int size = valueArraySize(_serializedValue); + for (int i = 1; i < size; ++i) + str.append(QString(", %1").arg(valueString(_serializedValue, i))); + str.append(QChar(']')); + + return str; } - switch (_serializedValue.type()) + return singleValueToString(_serializedValue); + } + + QString shortValueString(const ::profiler::ArbitraryValue& _serializedValue) + { + if (_serializedValue.isArray()) { - case ::profiler::DataType::Bool: return _serializedValue.toValue()->value() ? QStringLiteral("true") : QStringLiteral("false"); - case ::profiler::DataType::Char: return QChar(_serializedValue.toValue ()->value()); - case ::profiler::DataType::Int8: return QChar(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint8: return toString (_serializedValue); - case ::profiler::DataType::Int16: return toString (_serializedValue); - case ::profiler::DataType::Uint16: return toString(_serializedValue); - case ::profiler::DataType::Int32: return toString (_serializedValue); - case ::profiler::DataType::Uint32: return toString(_serializedValue); - case ::profiler::DataType::Int64: return toString (_serializedValue); - case ::profiler::DataType::Uint64: return toString(_serializedValue); - case ::profiler::DataType::Float: return toString (_serializedValue); - case ::profiler::DataType::Double: return toString (_serializedValue); - case ::profiler::DataType::String: return _serializedValue.data(); - default: return QStringLiteral("Unknown"); + if (_serializedValue.type() == ::profiler::DataType::String) + return _serializedValue.data(); + + auto str = QString("[%1").arg(valueString(_serializedValue, 0)); + const int size = valueArraySize(_serializedValue); + if (size < 7) + { + for (int i = 1; i < size; ++i) + str.append(QString(", %1").arg(valueString(_serializedValue, i))); + } + else + { + for (int i = 1; i < 6; ++i) + str.append(QString(", %1").arg(valueString(_serializedValue, i))); + str.append(QString(", ..., %1").arg(valueString(_serializedValue, size - 1))); + } + + str.append(QChar(']')); + return str; } + + return singleValueToString(_serializedValue); + } + + QString valueString(const ::profiler::ArbitraryValue& _serializedValue, int _index) + { + if (_serializedValue.isArray()) + return arrayToString(_serializedValue, _index); + return singleValueToString(_serializedValue); + } + + int valueArraySize(const ::profiler::ArbitraryValue& _serializedValue) + { + EASY_STATIC_CONSTEXPR uint16_t DataSizes[] = { + sizeOf<::profiler::DataType::Bool>(), + sizeOf<::profiler::DataType::Char>(), + sizeOf<::profiler::DataType::Int8>(), + sizeOf<::profiler::DataType::Uint8>(), + sizeOf<::profiler::DataType::Int16>(), + sizeOf<::profiler::DataType::Uint16>(), + sizeOf<::profiler::DataType::Int32>(), + sizeOf<::profiler::DataType::Uint32>(), + sizeOf<::profiler::DataType::Int64>(), + sizeOf<::profiler::DataType::Uint64>(), + sizeOf<::profiler::DataType::Float>(), + sizeOf<::profiler::DataType::Double>(), + sizeOf<::profiler::DataType::String>(), + 1 + }; + + return _serializedValue.data_size() / DataSizes[int_cast(_serializedValue.type())]; } double value2real(const ::profiler::ArbitraryValue& _serializedValue, int _index) diff --git a/profiler_gui/common_functions.h b/profiler_gui/common_functions.h index 423b85e..b0b0407 100644 --- a/profiler_gui/common_functions.h +++ b/profiler_gui/common_functions.h @@ -204,6 +204,9 @@ inline QFont EFont(const char* _family, int _size, int _weight = -1) { QString valueTypeString(::profiler::DataType _dataType); QString valueTypeString(const ::profiler::ArbitraryValue& _serializedValue); QString valueString(const ::profiler::ArbitraryValue& _serializedValue); +QString shortValueString(const ::profiler::ArbitraryValue& _serializedValue); +QString valueString(const ::profiler::ArbitraryValue& _serializedValue, int _index); +int valueArraySize(const ::profiler::ArbitraryValue& _serializedValue); double value2real(const ::profiler::ArbitraryValue& _serializedValue, int _index = 0); ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/images/default/check-partial-disabled.svg b/profiler_gui/images/default/check-partial-disabled.svg new file mode 100644 index 0000000..57a0277 --- /dev/null +++ b/profiler_gui/images/default/check-partial-disabled.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/profiler_gui/images/default/check-partial.svg b/profiler_gui/images/default/check-partial.svg new file mode 100644 index 0000000..45a6760 --- /dev/null +++ b/profiler_gui/images/default/check-partial.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/profiler_gui/resources.qrc b/profiler_gui/resources.qrc index 641e5de..42b4d4c 100644 --- a/profiler_gui/resources.qrc +++ b/profiler_gui/resources.qrc @@ -31,6 +31,8 @@ images/default/check-disabled.svg images/default/radio-indicator.svg images/default/radio-indicator-disabled.svg + images/default/check-partial.svg + images/default/check-partial-disabled.svg images/default/maximize-white.svg images/default/maximize-white-hover.svg images/default/maximize-white-pressed.svg diff --git a/profiler_gui/themes/default.css b/profiler_gui/themes/default.css index 8b7213e..273317a 100644 --- a/profiler_gui/themes/default.css +++ b/profiler_gui/themes/default.css @@ -175,7 +175,7 @@ QTreeView::indicator { padding: 1px; margin: 0; } -QTreeView::indicator:hover, QTreeView::indicator:checked { +QTreeView::indicator:hover, QTreeView::indicator:checked, QTreeView::indicator:indeterminate { background-color: white; border: 1px solid #cccccc; } @@ -185,6 +185,12 @@ QTreeView::indicator:checked { QTreeView::indicator:checked:disabled { image: url(":/images/default/check-disabled"); } +QTreeView::indicator:indeterminate { + image: url(":/images/default/partial-check"); } + +QTreeView::indicator:indeterminate:disabled { + image: url(":/images/default/partial-check-disabled"); } + /* ****************************************************************************************************************** */ QMenu { background-color: white; diff --git a/profiler_gui/themes/default.scss b/profiler_gui/themes/default.scss index 7b81bc3..61dc28e 100644 --- a/profiler_gui/themes/default.scss +++ b/profiler_gui/themes/default.scss @@ -200,7 +200,7 @@ QTreeView::indicator { margin: 0; } -QTreeView::indicator:hover, QTreeView::indicator:checked { +QTreeView::indicator:hover, QTreeView::indicator:checked, QTreeView::indicator:indeterminate { background-color: $BackgroundColor; border: 1px solid $BorderColor; } @@ -208,6 +208,9 @@ QTreeView::indicator:hover, QTreeView::indicator:checked { QTreeView::indicator:checked { image: url(":/images/default/check"); } QTreeView::indicator:checked:disabled { image: url(":/images/default/check-disabled"); } +QTreeView::indicator:indeterminate { image: url(":/images/default/partial-check"); } +QTreeView::indicator:indeterminate:disabled { image: url(":/images/default/partial-check-disabled"); } + /* ****************************************************************************************************************** */ QMenu { background-color: $BackgroundColor; diff --git a/sample/main.cpp b/sample/main.cpp index 2cd9a90..cf8668b 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -185,7 +185,8 @@ void modellingThread(){ localSleep(1200000); ++step; - EASY_VALUE("step", sin((double)step), profiler::colors::Gold, EASY_VIN(step)); + double vals[] = {(double)step, sin((double)step), cos((double)step)}; + EASY_VALUE("step", vals, profiler::colors::Gold, EASY_VIN(step)); if (step > 10000000) step = 0;