2017-11-30 22:21:08 +03:00
|
|
|
/************************************************************************
|
|
|
|
* file name : arbitrary_value_inspector.cpp
|
|
|
|
* ----------------- :
|
|
|
|
* creation time : 2017/11/30
|
|
|
|
* author : Victor Zarubkin
|
|
|
|
* email : v.s.zarubkin@gmail.com
|
|
|
|
* ----------------- :
|
|
|
|
* description : The file contains implementation of .
|
|
|
|
* ----------------- :
|
|
|
|
* change log : * 2017/11/30 Victor Zarubkin: initial commit.
|
|
|
|
* :
|
|
|
|
* : *
|
|
|
|
* ----------------- :
|
|
|
|
* license : Lightweight profiler library for c++
|
2018-01-29 23:29:43 +03:00
|
|
|
* : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin
|
2017-11-30 22:21:08 +03:00
|
|
|
* :
|
|
|
|
* : Licensed under either of
|
|
|
|
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
|
|
|
|
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
* : at your option.
|
|
|
|
* :
|
|
|
|
* : The MIT License
|
|
|
|
* :
|
|
|
|
* : Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* : of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* : in the Software without restriction, including without limitation the rights
|
|
|
|
* : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
|
|
* : of the Software, and to permit persons to whom the Software is furnished
|
|
|
|
* : to do so, subject to the following conditions:
|
|
|
|
* :
|
|
|
|
* : The above copyright notice and this permission notice shall be included in all
|
|
|
|
* : copies or substantial portions of the Software.
|
|
|
|
* :
|
|
|
|
* : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
|
|
* : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
|
|
* : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
* : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
* : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
|
|
* : USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
* :
|
|
|
|
* : The Apache License, Version 2.0 (the "License")
|
|
|
|
* :
|
|
|
|
* : You may not use this file except in compliance with the License.
|
|
|
|
* : You may obtain a copy of the License at
|
|
|
|
* :
|
|
|
|
* : http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
* :
|
|
|
|
* : Unless required by applicable law or agreed to in writing, software
|
|
|
|
* : distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* : See the License for the specific language governing permissions and
|
|
|
|
* : limitations under the License.
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QGraphicsScene>
|
|
|
|
#include <QColor>
|
2017-12-04 20:29:45 +03:00
|
|
|
#include <QHeaderView>
|
|
|
|
#include <QVBoxLayout>
|
2018-01-21 19:37:44 +03:00
|
|
|
#include <QSplitter>
|
2017-12-28 00:23:14 +03:00
|
|
|
#include <QResizeEvent>
|
|
|
|
#include <QVariant>
|
2018-01-21 19:37:44 +03:00
|
|
|
#include <QSettings>
|
2018-01-25 23:21:56 +03:00
|
|
|
#include <QToolBar>
|
|
|
|
#include <QAction>
|
2017-11-30 22:21:08 +03:00
|
|
|
#include <list>
|
2018-01-28 02:01:49 +03:00
|
|
|
#include <cmath>
|
2017-11-30 22:21:08 +03:00
|
|
|
#include "arbitrary_value_inspector.h"
|
2017-12-07 22:36:53 +03:00
|
|
|
#include "treeview_first_column_delegate.h"
|
2017-11-30 22:21:08 +03:00
|
|
|
#include "globals.h"
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-01-28 20:52:17 +03:00
|
|
|
EASY_CONSTEXPR int ChartBound = 2; ///< Top and bottom bounds for chart
|
|
|
|
EASY_CONSTEXPR int ChartBounds = ChartBound << 1;
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
void getChartPoints(const ArbitraryValuesCollection& _collection, Points& _points, qreal& _minValue, qreal& _maxValue)
|
|
|
|
{
|
|
|
|
_minValue = 1e300;
|
|
|
|
_maxValue = -1e300;
|
|
|
|
|
|
|
|
const auto size = _collection.size();
|
|
|
|
_points.clear();
|
|
|
|
_points.reserve(size);
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const auto& valuesByThread = _collection.valuesMap();
|
|
|
|
|
|
|
|
if (valuesByThread.size() == 1)
|
|
|
|
{
|
|
|
|
const auto& values = valuesByThread.begin()->second;
|
|
|
|
for (auto value : values)
|
|
|
|
{
|
|
|
|
const qreal x = sceneX(value->begin());
|
|
|
|
const qreal y = profiler_gui::value2real(*value);
|
|
|
|
_points.emplace_back(x, y);
|
|
|
|
|
|
|
|
if (y > _maxValue)
|
|
|
|
_maxValue = y;
|
|
|
|
if (y < _minValue)
|
|
|
|
_minValue = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::list<profiler::thread_id_t> threadIds;
|
|
|
|
for (const auto& it : valuesByThread)
|
|
|
|
threadIds.push_back(it.first);
|
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
while (!threadIds.empty())
|
|
|
|
{
|
|
|
|
for (auto it = threadIds.begin(); it != threadIds.end();)
|
|
|
|
{
|
|
|
|
const auto& values = valuesByThread.at(*it);
|
|
|
|
if (i >= values.size())
|
|
|
|
{
|
|
|
|
it = threadIds.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const qreal x = sceneX(values[i]->begin());
|
|
|
|
const qreal y = profiler_gui::value2real(*values[i]);
|
|
|
|
_points.emplace_back(x, y);
|
|
|
|
|
|
|
|
if (y > _maxValue)
|
|
|
|
_maxValue = y;
|
|
|
|
if (y < _minValue)
|
|
|
|
_minValue = y;
|
|
|
|
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::sort(_points.begin(), _points.end(), [](const QPointF& lhs, const QPointF& rhs) -> bool {
|
|
|
|
return lhs.x() < rhs.x();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2017-11-30 22:21:08 +03:00
|
|
|
ArbitraryValuesCollection::ArbitraryValuesCollection()
|
2017-12-28 00:23:14 +03:00
|
|
|
: m_beginTime(0)
|
|
|
|
, m_minValue(1e300)
|
|
|
|
, m_maxValue(-1e300)
|
|
|
|
, m_jobType(0)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
m_status = ATOMIC_VAR_INIT(Idle);
|
2017-11-30 22:21:08 +03:00
|
|
|
m_bInterrupt = ATOMIC_VAR_INIT(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
ArbitraryValuesCollection::~ArbitraryValuesCollection()
|
|
|
|
{
|
2018-01-28 20:52:17 +03:00
|
|
|
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
const ArbitraryValuesMap& ArbitraryValuesCollection::valuesMap() const
|
|
|
|
{
|
|
|
|
return m_values;
|
|
|
|
}
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
const Points& ArbitraryValuesCollection::points() const
|
|
|
|
{
|
|
|
|
return m_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
ArbitraryValuesCollection::JobStatus ArbitraryValuesCollection::status() const
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
return static_cast<JobStatus>(m_status.load(std::memory_order_acquire));
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t ArbitraryValuesCollection::size() const
|
|
|
|
{
|
|
|
|
size_t totalSize = 0;
|
|
|
|
for (const auto& it : m_values)
|
|
|
|
totalSize += it.second.size();
|
|
|
|
return totalSize;
|
|
|
|
}
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
qreal ArbitraryValuesCollection::minValue() const
|
|
|
|
{
|
|
|
|
return m_minValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal ArbitraryValuesCollection::maxValue() const
|
|
|
|
{
|
|
|
|
return m_maxValue;
|
|
|
|
}
|
|
|
|
|
2018-02-02 22:44:38 +03:00
|
|
|
void ArbitraryValuesCollection::collectValues(profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName
|
|
|
|
, profiler::block_id_t _parentBlockId, bool _directParent)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
interrupt();
|
|
|
|
|
|
|
|
setStatus(InProgress);
|
|
|
|
m_points.clear();
|
|
|
|
m_jobType = ValuesJob;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
if (_valueId == 0)
|
2018-02-02 22:44:38 +03:00
|
|
|
m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId, _directParent); }, m_bInterrupt);
|
2017-12-28 00:23:14 +03:00
|
|
|
else
|
2018-02-02 22:44:38 +03:00
|
|
|
m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId, _directParent); }, m_bInterrupt);
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
|
2018-02-02 22:44:38 +03:00
|
|
|
void ArbitraryValuesCollection::collectValues(profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName
|
|
|
|
, profiler::timestamp_t _beginTime, profiler::block_id_t _parentBlockId, bool _directParent)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2017-11-30 22:21:08 +03:00
|
|
|
interrupt();
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
setStatus(InProgress);
|
|
|
|
m_points.clear();
|
|
|
|
m_beginTime = _beginTime;
|
|
|
|
m_minValue = 1e300;
|
|
|
|
m_maxValue = -1e300;
|
|
|
|
m_jobType = ValuesJob | PointsJob;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
|
|
|
if (_valueId == 0)
|
2018-02-02 22:44:38 +03:00
|
|
|
m_worker.enqueue([=] { collectByName(_threadId, _valueName, _parentBlockId, _directParent); }, m_bInterrupt);
|
2017-11-30 22:21:08 +03:00
|
|
|
else
|
2018-02-02 22:44:38 +03:00
|
|
|
m_worker.enqueue([=] { collectById(_threadId, _valueId, _parentBlockId, _directParent); }, m_bInterrupt);
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
bool ArbitraryValuesCollection::calculatePoints(profiler::timestamp_t _beginTime)
|
|
|
|
{
|
|
|
|
if (status() != Ready || m_values.empty())
|
|
|
|
return false;
|
|
|
|
|
2018-01-28 20:52:17 +03:00
|
|
|
m_worker.dequeue();
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
setStatus(InProgress);
|
|
|
|
m_points.clear();
|
|
|
|
m_beginTime = _beginTime;
|
|
|
|
m_minValue = 1e300;
|
|
|
|
m_maxValue = -1e300;
|
|
|
|
m_jobType = PointsJob;
|
|
|
|
|
2018-01-28 20:52:17 +03:00
|
|
|
m_worker.enqueue([this]
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
getChartPoints(*this, m_points, m_minValue, m_maxValue);
|
|
|
|
setStatus(Ready);
|
2018-01-28 20:52:17 +03:00
|
|
|
}, m_bInterrupt);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-30 22:21:08 +03:00
|
|
|
void ArbitraryValuesCollection::interrupt()
|
|
|
|
{
|
2018-01-28 20:52:17 +03:00
|
|
|
m_worker.dequeue();
|
|
|
|
setStatus(Idle);
|
|
|
|
m_jobType = None;
|
|
|
|
m_values.clear();
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
void ArbitraryValuesCollection::setStatus(JobStatus _status)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
m_status.store(static_cast<uint8_t>(_status), std::memory_order_release);
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
2018-02-02 22:44:38 +03:00
|
|
|
void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, profiler::vin_t _valueId
|
|
|
|
, profiler::block_id_t _parentBlockId, bool _directParent)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
|
|
|
if (_threadId == 0)
|
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
const auto threadsCount = EASY_GLOBALS.profiler_blocks.size();
|
|
|
|
const bool calculatePointsInner = (m_jobType & PointsJob) != 0 && threadsCount == 1;
|
|
|
|
|
2017-11-30 22:21:08 +03:00
|
|
|
for (const auto& it : EASY_GLOBALS.profiler_blocks)
|
|
|
|
{
|
2018-02-02 22:44:38 +03:00
|
|
|
if (!collectByIdForThread(it.second, _valueId, calculatePointsInner, _parentBlockId, _directParent))
|
2017-11-30 22:21:08 +03:00
|
|
|
return;
|
|
|
|
}
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
if ((m_jobType & PointsJob) != 0 && threadsCount > 1)
|
|
|
|
getChartPoints(*this, m_points, m_minValue, m_maxValue);
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-12-07 22:36:53 +03:00
|
|
|
const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId);
|
2018-02-02 22:44:38 +03:00
|
|
|
if (t != EASY_GLOBALS.profiler_blocks.end() &&
|
|
|
|
!collectByIdForThread(t->second, _valueId, (m_jobType & PointsJob) != 0, _parentBlockId, _directParent))
|
|
|
|
{
|
2017-11-30 22:21:08 +03:00
|
|
|
return;
|
2018-02-02 22:44:38 +03:00
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
setStatus(Ready);
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
2018-02-02 22:44:38 +03:00
|
|
|
bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeRoot& _threadRoot
|
|
|
|
, profiler::vin_t _valueId, bool _calculatePoints, profiler::block_id_t _parentBlockId, bool _directParent)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
2017-12-07 22:36:53 +03:00
|
|
|
auto& valuesList = m_values[_threadRoot.thread_id];
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2017-12-07 22:36:53 +03:00
|
|
|
for (auto i : _threadRoot.events)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
|
|
|
if (m_bInterrupt.load(std::memory_order_acquire))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const auto& block = easyBlock(i).tree;
|
|
|
|
const auto& desc = easyDescriptor(block.node->id());
|
|
|
|
if (desc.type() != profiler::BlockType::Value)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const auto value = block.value;
|
|
|
|
if (value->value_id() != _valueId)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
valuesList.push_back(value);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
if (_calculatePoints)
|
|
|
|
{
|
|
|
|
const auto p = point(*block.value);
|
|
|
|
|
|
|
|
if (p.y() > m_maxValue)
|
|
|
|
m_maxValue = p.y();
|
|
|
|
if (p.y() < m_minValue)
|
|
|
|
m_minValue = p.y();
|
|
|
|
|
|
|
|
m_points.push_back(p);
|
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-02 22:44:38 +03:00
|
|
|
void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, const std::string _valueName
|
|
|
|
, profiler::block_id_t _parentBlockId, bool _directParent)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
|
|
|
if (_threadId == 0)
|
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
const auto threadsCount = EASY_GLOBALS.profiler_blocks.size();
|
|
|
|
const bool calculatePointsInner = (m_jobType & PointsJob) != 0 && threadsCount == 1;
|
|
|
|
|
2017-11-30 22:21:08 +03:00
|
|
|
for (const auto& it : EASY_GLOBALS.profiler_blocks)
|
|
|
|
{
|
2018-02-02 22:44:38 +03:00
|
|
|
if (!collectByNameForThread(it.second, _valueName, calculatePointsInner, _parentBlockId, _directParent))
|
2017-11-30 22:21:08 +03:00
|
|
|
return;
|
|
|
|
}
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
if ((m_jobType & PointsJob) != 0 && threadsCount > 1)
|
|
|
|
getChartPoints(*this, m_points, m_minValue, m_maxValue);
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-12-07 22:36:53 +03:00
|
|
|
const auto t = EASY_GLOBALS.profiler_blocks.find(_threadId);
|
2018-02-02 22:44:38 +03:00
|
|
|
if (t != EASY_GLOBALS.profiler_blocks.end() &&
|
|
|
|
!collectByNameForThread(t->second, _valueName, (m_jobType & PointsJob) != 0, _parentBlockId, _directParent))
|
|
|
|
{
|
2017-11-30 22:21:08 +03:00
|
|
|
return;
|
2018-02-02 22:44:38 +03:00
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
setStatus(Ready);
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
2018-02-02 22:44:38 +03:00
|
|
|
bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTreeRoot& _threadRoot
|
|
|
|
, const std::string& _valueName, bool _calculatePoints, profiler::block_id_t _parentBlockId, bool _directParent)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
2017-12-07 22:36:53 +03:00
|
|
|
auto& valuesList = m_values[_threadRoot.thread_id];
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2017-12-07 22:36:53 +03:00
|
|
|
for (auto i : _threadRoot.events)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
|
|
|
if (m_bInterrupt.load(std::memory_order_acquire))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const auto& block = easyBlock(i).tree;
|
|
|
|
const auto& desc = easyDescriptor(block.node->id());
|
|
|
|
if (desc.type() != profiler::BlockType::Value || _valueName != desc.name())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
valuesList.push_back(block.value);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
if (_calculatePoints)
|
|
|
|
{
|
|
|
|
const auto p = point(*block.value);
|
|
|
|
|
|
|
|
if (p.y() > m_maxValue)
|
|
|
|
m_maxValue = p.y();
|
|
|
|
if (p.y() < m_minValue)
|
|
|
|
m_minValue = p.y();
|
|
|
|
|
|
|
|
m_points.push_back(p);
|
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
QPointF ArbitraryValuesCollection::point(const profiler::ArbitraryValue& _value) const
|
|
|
|
{
|
|
|
|
const qreal x = PROF_MICROSECONDS(qreal(_value.begin() - m_beginTime));
|
|
|
|
const qreal y = profiler_gui::value2real(_value);
|
|
|
|
return {x, y};
|
|
|
|
}
|
|
|
|
|
2017-11-30 22:21:08 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
ArbitraryValuesChartItem::ArbitraryValuesChartItem()
|
|
|
|
: Parent()
|
|
|
|
, m_workerMaxValue(0)
|
|
|
|
, m_workerMinValue(0)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
ArbitraryValuesChartItem::~ArbitraryValuesChartItem()
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesChartItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
|
2017-11-30 22:21:08 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
const auto widget = static_cast<const GraphicsSliderArea*>(scene()->parent());
|
|
|
|
const auto currentScale = widget->getWindowScale();
|
|
|
|
const bool bindMode = widget->bindMode();
|
|
|
|
const auto bottom = m_boundingRect.bottom();
|
|
|
|
const auto width = m_boundingRect.width() * currentScale;
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
_painter->save();
|
|
|
|
_painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (!bindMode)
|
|
|
|
paintImage(_painter);
|
|
|
|
else
|
|
|
|
paintImage(_painter, currentScale, widget->minimum(), widget->maximum(), widget->value(), widget->sliderWidth());
|
|
|
|
|
|
|
|
const auto font_h = widget->fontHeight();
|
|
|
|
QRectF rect(0, m_boundingRect.top() - widget->margin(), width - 3, m_boundingRect.height() + widget->margins());
|
|
|
|
_painter->setPen(profiler_gui::TEXT_COLOR);
|
|
|
|
_painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, bindMode ? " Mode: Zoom" : " Mode: Overview");
|
|
|
|
|
|
|
|
if (!EASY_GLOBALS.scene.empty)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
const auto range = bindMode ? widget->sliderWidth() : widget->range();
|
2018-01-28 20:52:17 +03:00
|
|
|
paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width,
|
|
|
|
m_boundingRect.height(), font_h, widget->value(), range);
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
_painter->setPen(Qt::darkGray);
|
|
|
|
_painter->drawLine(QLineF(0, bottom, width, bottom));
|
|
|
|
_painter->drawLine(QLineF(0, m_boundingRect.top(), width, m_boundingRect.top()));
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
_painter->restore();
|
|
|
|
}
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height,
|
|
|
|
int _font_h, qreal _visibleRegionLeft, qreal _visibleRegionWidth)
|
|
|
|
{
|
|
|
|
if (_font_h == 0)
|
|
|
|
return;
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const auto x = m_mousePos.x();
|
2018-01-28 20:52:17 +03:00
|
|
|
auto y = m_mousePos.y();
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
// Horizontal
|
|
|
|
const bool visibleY = (_top < y && y < _bottom);
|
2018-01-28 20:52:17 +03:00
|
|
|
y = estd::clamp(_top, y, _bottom);
|
|
|
|
//if (visibleY)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-28 20:52:17 +03:00
|
|
|
_height -= ChartBounds;
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const int half_font_h = _font_h >> 1;
|
2018-01-28 20:52:17 +03:00
|
|
|
const auto yvalue = estd::clamp(_top + ChartBound, y, _bottom - ChartBound);
|
|
|
|
const auto value = m_minValue + ((_bottom - ChartBound - yvalue) / _height) * (m_maxValue - m_minValue);
|
2018-01-21 19:37:44 +03:00
|
|
|
const auto mouseStr = QString::number(value, 'f', 3);
|
|
|
|
const int textWidth = _painter->fontMetrics().width(mouseStr) + 3;
|
2018-01-28 20:52:17 +03:00
|
|
|
const QRectF rect(0, y - _font_h - 2, _width - 3, 4 + (_font_h << 1));
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
_painter->setPen(Qt::blue);
|
|
|
|
|
2018-01-28 20:52:17 +03:00
|
|
|
qreal left = 0, right = _width - 3;
|
2018-01-21 19:37:44 +03:00
|
|
|
const Qt::AlignmentFlag alignment = x < textWidth ? Qt::AlignRight : Qt::AlignLeft;
|
|
|
|
|
|
|
|
if (y > _bottom - half_font_h)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
_painter->drawText(rect, alignment | Qt::AlignTop, mouseStr);
|
|
|
|
}
|
|
|
|
else if (y < _top + half_font_h)
|
|
|
|
{
|
|
|
|
_painter->drawText(rect, alignment | Qt::AlignBottom, mouseStr);
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
_painter->drawText(rect, alignment | Qt::AlignVCenter, mouseStr);
|
|
|
|
if (x < textWidth)
|
2018-01-28 20:52:17 +03:00
|
|
|
right = _width - textWidth - 3;
|
2018-01-21 19:37:44 +03:00
|
|
|
else
|
|
|
|
left = textWidth;
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
|
2018-01-28 20:52:17 +03:00
|
|
|
if (visibleY)
|
|
|
|
_painter->drawLine(QLineF(left, y, right, y));
|
2018-01-21 19:37:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Vertical
|
|
|
|
if (0 < x && x < m_boundingRect.width())
|
|
|
|
{
|
|
|
|
const auto value = _visibleRegionLeft + _visibleRegionWidth * x / _width;
|
|
|
|
const auto mouseStr = profiler_gui::timeStringReal(EASY_GLOBALS.time_units, value, 3);
|
|
|
|
const int textWidth = _painter->fontMetrics().width(mouseStr) + 6;
|
|
|
|
const int textWidthHalf = textWidth >> 1;
|
|
|
|
|
|
|
|
qreal left = x - textWidthHalf;
|
|
|
|
if (x < textWidthHalf)
|
|
|
|
left = 0;
|
|
|
|
else if (x > (_width - textWidthHalf))
|
|
|
|
left = _width - textWidth;
|
|
|
|
|
2018-01-28 20:52:17 +03:00
|
|
|
//if (!visibleY)
|
|
|
|
// _painter->setPen(Qt::blue);
|
2018-01-21 19:37:44 +03:00
|
|
|
|
|
|
|
const QRectF rect(left, _bottom + 2, textWidth, _font_h);
|
|
|
|
_painter->drawText(rect, Qt::AlignCenter, mouseStr);
|
|
|
|
_painter->drawLine(QLineF(x, _top, x, _bottom));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ArbitraryValuesChartItem::updateImage()
|
|
|
|
{
|
|
|
|
if (!Parent::updateImage())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const auto widget = static_cast<const GraphicsSliderArea*>(scene()->parent());
|
|
|
|
|
|
|
|
m_imageScaleUpdate = widget->range() / widget->sliderWidth();
|
|
|
|
m_imageOriginUpdate = widget->bindMode() ? (widget->value() - widget->sliderWidth() * 3) : widget->minimum();
|
|
|
|
|
2018-01-28 20:52:17 +03:00
|
|
|
// Ugly, but doen't use exceeded count of threads
|
|
|
|
const auto rect = m_boundingRect;
|
|
|
|
const auto scale = widget->getWindowScale();
|
|
|
|
const auto left = widget->minimum();
|
|
|
|
const auto right = widget->maximum();
|
|
|
|
const auto value = widget->value();
|
|
|
|
const auto window = widget->sliderWidth();
|
|
|
|
const auto bindMode = widget->bindMode();
|
|
|
|
const auto beginTime = EASY_GLOBALS.begin_time;
|
|
|
|
const auto autoHeight = EASY_GLOBALS.auto_adjust_chart_height;
|
2018-01-29 23:42:18 +03:00
|
|
|
m_worker.enqueue([=] {
|
2018-01-28 20:52:17 +03:00
|
|
|
updateImageAsync(rect, scale, left, right, right - left, value, window, bindMode,
|
|
|
|
beginTime, autoHeight);
|
|
|
|
}, m_bReady);
|
2018-01-21 19:37:44 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArbitraryValuesChartItem::onImageUpdated()
|
|
|
|
{
|
|
|
|
m_maxValue = m_workerMaxValue;
|
|
|
|
m_minValue = m_workerMinValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArbitraryValuesChartItem::updateImageAsync(QRectF _boundingRect, qreal _current_scale, qreal _minimum, qreal _maximum, qreal _range,
|
|
|
|
qreal _value, qreal _width, bool _bindMode, profiler::timestamp_t _begin_time, bool _autoAdjust)
|
|
|
|
{
|
|
|
|
const auto screenWidth = _boundingRect.width() * _current_scale;
|
|
|
|
//const auto maxColumnHeight = _boundingRect.height();
|
|
|
|
const auto viewScale = _range / _width;
|
|
|
|
|
|
|
|
if (_bindMode)
|
|
|
|
{
|
|
|
|
m_workerImageScale = viewScale;
|
|
|
|
m_workerImageOrigin = _value - _width * 3;
|
|
|
|
m_workerImage = new QImage(screenWidth * 7 + 0.5, _boundingRect.height(), QImage::Format_ARGB32);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_workerImageScale = 1;
|
|
|
|
m_workerImageOrigin = _minimum;
|
|
|
|
m_workerImage = new QImage(screenWidth + 0.5, _boundingRect.height(), QImage::Format_ARGB32);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_workerImage->fill(0);
|
|
|
|
QPainter p(m_workerImage);
|
|
|
|
p.setBrush(Qt::NoBrush);
|
|
|
|
p.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
|
|
|
|
// Draw grid
|
|
|
|
{
|
|
|
|
auto pen = p.pen();
|
|
|
|
pen.setColor(Qt::darkGray);
|
|
|
|
pen.setStyle(Qt::DotLine);
|
|
|
|
p.setPen(pen);
|
|
|
|
|
|
|
|
const int left = 0;
|
|
|
|
const int top = 0;
|
|
|
|
const int w = m_workerImage->width();
|
|
|
|
const int h = m_workerImage->height();
|
|
|
|
|
|
|
|
const int hlines = h / 20;
|
|
|
|
for (int i = 0; i < hlines; ++i)
|
|
|
|
{
|
|
|
|
const auto y = top + 20 + i * 20;
|
|
|
|
p.drawLine(left, y, left + w, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int vlines = w / 20;
|
|
|
|
for (int i = 0; i < vlines; ++i)
|
|
|
|
{
|
|
|
|
const auto x = left + 20 + i * 20;
|
|
|
|
p.drawLine(x, top, x, top + h);
|
|
|
|
}
|
|
|
|
|
|
|
|
p.setPen(Qt::SolidLine);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_collections.empty() || isReady())
|
|
|
|
{
|
|
|
|
setReady(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
using LeftBounds = std::vector<Points::const_iterator>;
|
|
|
|
qreal realScale = _current_scale, offset = 0;
|
|
|
|
|
|
|
|
LeftBounds leftBounds;
|
|
|
|
leftBounds.reserve(m_collections.size());
|
|
|
|
|
|
|
|
if (_bindMode)
|
|
|
|
{
|
|
|
|
_minimum = m_workerImageOrigin;
|
|
|
|
_maximum = m_workerImageOrigin + _width * 7;
|
|
|
|
realScale *= viewScale;
|
|
|
|
offset = _minimum * realScale;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto right = std::min(_value + _width, _maximum);
|
|
|
|
qreal minValue = 1e300, maxValue = -1e300;
|
|
|
|
for (const auto& c : m_collections)
|
|
|
|
{
|
|
|
|
if (isReady())
|
|
|
|
return;
|
|
|
|
|
|
|
|
const auto& collection = *c.ptr;
|
|
|
|
const auto& points = collection.points();
|
|
|
|
|
|
|
|
if (points.empty())
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
leftBounds.emplace_back(points.end());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_bindMode)
|
|
|
|
{
|
|
|
|
auto first = std::lower_bound(points.begin(), points.end(), _minimum, [](const QPointF& point, qreal x)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
return point.x() < x;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (first != points.end())
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
if (first != points.begin())
|
|
|
|
--first;
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
first = points.begin() + points.size() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
leftBounds.emplace_back(first);
|
|
|
|
|
|
|
|
if (_autoAdjust)
|
|
|
|
{
|
|
|
|
for (auto it = first; it != points.end() && it->x() < right; ++it)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
if (it->x() < _value)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const auto value = it->y();
|
|
|
|
|
|
|
|
if (minValue > value)
|
|
|
|
minValue = value;
|
|
|
|
|
|
|
|
if (maxValue < value)
|
|
|
|
maxValue = value;
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
2018-01-21 19:37:44 +03:00
|
|
|
|
|
|
|
continue;
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
}
|
2018-01-21 19:37:44 +03:00
|
|
|
else
|
|
|
|
{
|
|
|
|
leftBounds.emplace_back(points.begin());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (minValue > collection.minValue())
|
|
|
|
minValue = collection.minValue();
|
|
|
|
|
|
|
|
if (maxValue < collection.maxValue())
|
|
|
|
maxValue = collection.maxValue();
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (minValue > maxValue)
|
|
|
|
{
|
|
|
|
// No points
|
|
|
|
m_workerMinValue = 0;
|
|
|
|
m_workerMaxValue = 0;
|
|
|
|
setReady(true);
|
|
|
|
return;
|
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
m_workerMinValue = minValue;
|
|
|
|
m_workerMaxValue = maxValue;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (isReady())
|
|
|
|
return;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const bool singleValue = fabs(maxValue - minValue) < 2 * std::numeric_limits<qreal>::epsilon();
|
|
|
|
const auto middle = _boundingRect.height() * 0.5;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const qreal height = std::max(maxValue - minValue, 0.01);
|
|
|
|
const auto gety = [&_boundingRect, maxValue, height, singleValue, middle] (qreal y)
|
|
|
|
{
|
|
|
|
if (singleValue)
|
|
|
|
{
|
|
|
|
y = middle;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
y = maxValue - y;
|
|
|
|
y /= height;
|
2018-01-28 20:52:17 +03:00
|
|
|
y *= _boundingRect.height() - ChartBounds;
|
|
|
|
y += ChartBound;
|
2018-01-21 19:37:44 +03:00
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
return y;
|
|
|
|
};
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
size_t i = 0;
|
|
|
|
for (const auto& c : m_collections)
|
|
|
|
{
|
|
|
|
if (isReady())
|
|
|
|
return;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const auto& points = c.ptr->points();
|
|
|
|
if (points.empty())
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (c.selected)
|
|
|
|
{
|
|
|
|
auto pen = p.pen();
|
|
|
|
pen.setColor(QColor::fromRgba(c.color));
|
|
|
|
pen.setWidth(2);
|
|
|
|
p.setPen(pen);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p.setPen(QColor::fromRgba(c.color));
|
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const auto first = leftBounds[i];
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-02-02 22:44:38 +03:00
|
|
|
if (c.chartType == ChartViewType::Points)
|
2018-01-21 19:37:44 +03:00
|
|
|
{
|
2018-01-28 02:01:49 +03:00
|
|
|
qreal prevX = 1e300, prevY = 1e300;
|
2018-01-21 19:37:44 +03:00
|
|
|
for (auto it = first; it != points.end() && it->x() < _maximum; ++it)
|
|
|
|
{
|
|
|
|
if (it->x() < _minimum)
|
|
|
|
continue;
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (isReady())
|
|
|
|
return;
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const qreal x = it->x() * realScale - offset;
|
|
|
|
const qreal y = gety(it->y());
|
2018-01-28 02:01:49 +03:00
|
|
|
const auto dx = fabs(x - prevX), dy = fabs(y - prevY);
|
|
|
|
|
|
|
|
if (dx > 1 || dy > 1)
|
|
|
|
{
|
|
|
|
p.drawPoint(QPointF{x, y});
|
|
|
|
prevX = x;
|
|
|
|
prevY = y;
|
|
|
|
}
|
2018-01-21 19:37:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (first != points.end() && first->x() < _maximum)
|
|
|
|
{
|
|
|
|
QPointF p1 = *first;
|
|
|
|
qreal x = p1.x() * realScale - offset;
|
|
|
|
qreal y = gety(p1.y());
|
|
|
|
p1.setX(x);
|
|
|
|
p1.setY(y);
|
|
|
|
|
|
|
|
auto it = first;
|
|
|
|
for (++it; it != points.end(); ++it)
|
|
|
|
{
|
|
|
|
if (isReady())
|
|
|
|
return;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
QPointF p2 = *it;
|
|
|
|
x = p2.x() * realScale - offset;
|
|
|
|
y = gety(p2.y());
|
|
|
|
p2.setX(x);
|
|
|
|
p2.setY(y);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (it->x() >= _minimum)
|
2018-01-28 02:01:49 +03:00
|
|
|
{
|
|
|
|
const auto dx = fabs(x - p1.x()), dy = fabs(y - p1.y());
|
|
|
|
if (dx > 1 || dy > 1)
|
|
|
|
p.drawLine(p1, p2);
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (it->x() >= _maximum)
|
|
|
|
break;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
p1 = p2;
|
|
|
|
}
|
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (c.selected)
|
|
|
|
{
|
|
|
|
auto color = profiler_gui::darken(c.color, 0.65f);
|
|
|
|
if (profiler_gui::alpha(color) < 0xc0)
|
|
|
|
p.setPen(QColor::fromRgba(profiler::colors::modify_alpha32(0xc0000000, color)));
|
|
|
|
else
|
|
|
|
p.setPen(QColor::fromRgba(color));
|
2018-01-28 02:01:49 +03:00
|
|
|
p.setBrush(QColor::fromRgba(0xc8ffffff));
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
qreal prevX = -offset * 2, prevY = -500;
|
|
|
|
for (auto it = first; it != points.end() && it->x() < _maximum; ++it)
|
|
|
|
{
|
|
|
|
if (it->x() < _minimum)
|
|
|
|
continue;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (isReady())
|
|
|
|
return;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const qreal x = it->x() * realScale - offset;
|
|
|
|
const qreal y = gety(it->y());
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const auto dx = x - prevX, dy = y - prevY;
|
|
|
|
const auto delta = estd::sqr(dx) + estd::sqr(dy);
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
if (delta > 25)
|
|
|
|
{
|
|
|
|
p.drawEllipse(QPointF {x, y}, 3, 3);
|
|
|
|
prevX = x;
|
|
|
|
prevY = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
setReady(true);
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesChartItem::clear()
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
cancelAnyJob();
|
|
|
|
m_boundaryTimer.stop();
|
|
|
|
m_collections.clear();
|
|
|
|
m_minValue = m_maxValue = 0;
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesChartItem::update(Collections _collections)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
cancelImageUpdate();
|
|
|
|
m_collections = std::move(_collections);
|
|
|
|
updateImage();
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesChartItem::update(const ArbitraryValuesCollection* _selected)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
cancelImageUpdate();
|
|
|
|
|
|
|
|
for (auto& collection : m_collections)
|
|
|
|
collection.selected = collection.ptr == _selected;
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
updateImage();
|
|
|
|
}
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
GraphicsChart::GraphicsChart(QWidget* _parent)
|
|
|
|
: Parent(_parent)
|
|
|
|
, m_chartItem(new ArbitraryValuesChartItem())
|
|
|
|
{
|
|
|
|
m_imageItem = m_chartItem;
|
|
|
|
scene()->addItem(m_chartItem);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const auto rect = scene()->sceneRect();
|
|
|
|
m_chartItem->setPos(0, 0);
|
|
|
|
m_chartItem->setBoundingRect(0, rect.top() + margin(), scene()->width(), rect.height() - margins() - 1);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-28 20:52:17 +03:00
|
|
|
connect(&EASY_GLOBALS.events, &profiler_gui::GlobalSignals::autoAdjustChartChanged, this, &This::onAutoAdjustChartChanged);
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
m_chartItem->updateImage();
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
2017-11-30 22:21:08 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
GraphicsChart::~GraphicsChart()
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
}
|
2017-12-04 20:29:45 +03:00
|
|
|
|
2018-01-28 02:01:49 +03:00
|
|
|
void GraphicsChart::onAutoAdjustChartChanged()
|
|
|
|
{
|
|
|
|
if (m_chartItem->isVisible())
|
|
|
|
m_chartItem->onModeChanged();
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void GraphicsChart::clear()
|
|
|
|
{
|
2018-01-28 02:01:49 +03:00
|
|
|
cancelImageUpdate();
|
2018-01-21 19:37:44 +03:00
|
|
|
Parent::clear();
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
|
2018-01-28 02:01:49 +03:00
|
|
|
void GraphicsChart::cancelImageUpdate()
|
|
|
|
{
|
|
|
|
m_chartItem->clear();
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void GraphicsChart::update(Collections _collections)
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
m_chartItem->update(std::move(_collections));
|
|
|
|
}
|
2017-12-04 20:29:45 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void GraphicsChart::update(const ArbitraryValuesCollection* _selected)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
m_chartItem->update(_selected);
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2017-12-07 22:36:53 +03:00
|
|
|
enum class ArbitraryColumns : uint8_t
|
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
Name = 0,
|
|
|
|
Type,
|
2017-12-07 22:36:53 +03:00
|
|
|
Value,
|
|
|
|
Vin,
|
|
|
|
|
|
|
|
Count
|
|
|
|
};
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
EASY_CONSTEXPR auto CheckColumn = int_cast(ArbitraryColumns::Name);
|
|
|
|
EASY_CONSTEXPR auto StdItemType = QTreeWidgetItem::UserType;
|
|
|
|
EASY_CONSTEXPR auto ValueItemType = QTreeWidgetItem::UserType + 1;
|
|
|
|
|
2017-12-14 23:12:27 +03:00
|
|
|
struct UsedValueTypes {
|
2018-01-21 19:37:44 +03:00
|
|
|
ArbitraryTreeWidgetItem* items[int_cast(profiler::DataType::TypesCount)];
|
2017-12-14 23:12:27 +03:00
|
|
|
UsedValueTypes(int = 0) { memset(items, 0, sizeof(items)); }
|
|
|
|
};
|
2017-12-07 22:36:53 +03:00
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
ArbitraryTreeWidgetItem::ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin)
|
2017-12-28 00:23:14 +03:00
|
|
|
: Parent(_parent, ValueItemType)
|
|
|
|
, m_vin(_vin)
|
|
|
|
, m_color(_color)
|
|
|
|
, m_widthHint(0)
|
|
|
|
{
|
|
|
|
setFlags(flags() | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
|
|
|
|
setCheckState(CheckColumn, Qt::Unchecked);
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
ArbitraryTreeWidgetItem::~ArbitraryTreeWidgetItem()
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
2018-01-28 02:01:49 +03:00
|
|
|
interrupt();
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
QVariant ArbitraryTreeWidgetItem::data(int _column, int _role) const
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
if (_column == CheckColumn && _role == Qt::SizeHintRole)
|
2018-01-25 23:21:56 +03:00
|
|
|
return QSize(static_cast<int>(m_widthHint * m_font.bold() ? 1.2f : 1.f), 26);
|
|
|
|
if (_role == Qt::FontRole)
|
|
|
|
return m_font;
|
2017-12-28 00:23:14 +03:00
|
|
|
return Parent::data(_column, _role);
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryTreeWidgetItem::setWidthHint(int _width)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
m_widthHint = _width;
|
|
|
|
}
|
|
|
|
|
2018-01-25 23:21:56 +03:00
|
|
|
void ArbitraryTreeWidgetItem::setBold(bool _isBold)
|
|
|
|
{
|
|
|
|
m_font.setBold(_isBold);
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
const ArbitraryValuesCollection* ArbitraryTreeWidgetItem::collection() const
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
return m_collection.get();
|
|
|
|
}
|
|
|
|
|
2018-01-28 02:01:49 +03:00
|
|
|
ArbitraryValuesCollection* ArbitraryTreeWidgetItem::collection()
|
|
|
|
{
|
|
|
|
return m_collection.get();
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
if (!m_collection)
|
|
|
|
m_collection = CollectionPtr(new ArbitraryValuesCollection);
|
|
|
|
else
|
|
|
|
m_collection->interrupt();
|
|
|
|
|
2018-02-02 22:44:38 +03:00
|
|
|
auto parentItem = parent();
|
|
|
|
auto parentBlockId = profiler_gui::numeric_max<profiler::block_id_t>();
|
|
|
|
bool directParent = false;
|
|
|
|
|
|
|
|
if (parentItem->data(int_cast(ArbitraryColumns::Type), Qt::UserRole).toInt() == 1)
|
|
|
|
{
|
|
|
|
parentBlockId = parentItem->data(int_cast(ArbitraryColumns::Vin), Qt::UserRole).toUInt();
|
|
|
|
directParent = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_collection->collectValues(_threadId, m_vin, text(int_cast(ArbitraryColumns::Name)).toStdString().c_str(),
|
|
|
|
EASY_GLOBALS.begin_time, parentBlockId, directParent);
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryTreeWidgetItem::interrupt()
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
if (!m_collection)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_collection->interrupt();
|
|
|
|
m_collection.release();
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
profiler::color_t ArbitraryTreeWidgetItem::color() const
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
return m_color;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent)
|
2017-12-04 20:29:45 +03:00
|
|
|
: Parent(_parent)
|
2018-01-21 19:37:44 +03:00
|
|
|
, m_splitter(new QSplitter(Qt::Horizontal, this))
|
2017-12-04 20:29:45 +03:00
|
|
|
, m_treeWidget(new QTreeWidget(this))
|
2018-01-21 19:37:44 +03:00
|
|
|
, m_chart(new GraphicsChart(this))
|
2018-01-25 23:21:56 +03:00
|
|
|
, m_boldItem(nullptr)
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
m_splitter->setHandleWidth(1);
|
|
|
|
m_splitter->setContentsMargins(0, 0, 0, 0);
|
|
|
|
m_splitter->addWidget(m_treeWidget);
|
|
|
|
m_splitter->addWidget(m_chart);
|
|
|
|
m_splitter->setStretchFactor(0, 1);
|
|
|
|
m_splitter->setStretchFactor(1, 1);
|
|
|
|
|
2018-01-25 23:21:56 +03:00
|
|
|
auto tb = new QToolBar(this);
|
2018-02-01 23:17:01 +03:00
|
|
|
tb->setIconSize(applicationIconsSize());
|
2018-01-25 23:21:56 +03:00
|
|
|
auto refreshButton = tb->addAction(QIcon(imagePath("reload")), tr("Refresh values list"));
|
|
|
|
refreshButton->setToolTip(tr("Refresh arbitrary values list."));
|
|
|
|
connect(refreshButton, &QAction::triggered, this, &This::rebuild);
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
auto layout = new QVBoxLayout(this);
|
2017-12-28 00:23:14 +03:00
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
2018-01-25 23:21:56 +03:00
|
|
|
layout->addWidget(tb);
|
2018-01-21 19:37:44 +03:00
|
|
|
layout->addWidget(m_splitter);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
2017-12-04 20:29:45 +03:00
|
|
|
m_treeWidget->setAutoFillBackground(false);
|
|
|
|
m_treeWidget->setAlternatingRowColors(true);
|
|
|
|
m_treeWidget->setItemsExpandable(true);
|
|
|
|
m_treeWidget->setAnimated(true);
|
|
|
|
//m_treeWidget->setSortingEnabled(false);
|
2017-12-07 22:36:53 +03:00
|
|
|
m_treeWidget->setColumnCount(int_cast(ArbitraryColumns::Count));
|
2017-12-04 20:29:45 +03:00
|
|
|
m_treeWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
2018-01-28 20:52:17 +03:00
|
|
|
m_treeWidget->setItemDelegateForColumn(0, new TreeViewFirstColumnItemDelegate(this));
|
2017-12-04 20:29:45 +03:00
|
|
|
|
2017-12-07 22:36:53 +03:00
|
|
|
auto headerItem = new QTreeWidgetItem();
|
|
|
|
headerItem->setText(int_cast(ArbitraryColumns::Type), "Type");
|
2017-12-14 23:12:27 +03:00
|
|
|
headerItem->setText(int_cast(ArbitraryColumns::Name), "Name");
|
2017-12-07 22:36:53 +03:00
|
|
|
headerItem->setText(int_cast(ArbitraryColumns::Value), "Value");
|
|
|
|
headerItem->setText(int_cast(ArbitraryColumns::Vin), "ID");
|
|
|
|
m_treeWidget->setHeaderItem(headerItem);
|
2017-12-04 20:29:45 +03:00
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
connect(&m_collectionsTimer, &QTimer::timeout, this, &This::onCollectionsTimeout);
|
|
|
|
|
|
|
|
connect(m_treeWidget, &QTreeWidget::itemDoubleClicked, this, &This::onItemDoubleClicked);
|
|
|
|
connect(m_treeWidget, &QTreeWidget::itemChanged, this, &This::onItemChanged);
|
|
|
|
connect(m_treeWidget, &QTreeWidget::currentItemChanged, this, &This::onCurrentItemChanged);
|
|
|
|
|
2018-01-25 23:21:56 +03:00
|
|
|
auto globalEvents = &EASY_GLOBALS.events;
|
2018-01-28 20:52:17 +03:00
|
|
|
connect(globalEvents, &profiler_gui::GlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChanged);
|
|
|
|
connect(globalEvents, &profiler_gui::GlobalSignals::selectedBlockIdChanged, this, &This::onSelectedBlockIdChanged);
|
|
|
|
connect(globalEvents, &profiler_gui::GlobalSignals::fileOpened, this, &This::rebuild);
|
2018-01-21 19:37:44 +03:00
|
|
|
|
|
|
|
loadSettings();
|
2018-01-25 23:21:56 +03:00
|
|
|
|
|
|
|
rebuild();
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
ArbitraryValuesWidget::~ArbitraryValuesWidget()
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
saveSettings();
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::clear()
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
if (m_collectionsTimer.isActive())
|
|
|
|
m_collectionsTimer.stop();
|
2018-01-28 02:01:49 +03:00
|
|
|
m_chart->cancelImageUpdate();
|
2017-12-28 00:23:14 +03:00
|
|
|
m_checkedItems.clear();
|
2017-12-07 22:36:53 +03:00
|
|
|
m_treeWidget->clear();
|
2018-01-25 23:21:56 +03:00
|
|
|
m_boldItem = nullptr;
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::onSelectedBlockChanged(uint32_t)
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2018-01-25 23:21:56 +03:00
|
|
|
if (profiler_gui::is_max(EASY_GLOBALS.selected_block))
|
|
|
|
{
|
|
|
|
onSelectedBlockIdChanged(EASY_GLOBALS.selected_block_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!profiler_gui::is_max(EASY_GLOBALS.selected_block))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// TODO: find item corresponding to selected_block and make it bold
|
2017-12-07 22:36:53 +03:00
|
|
|
}
|
2017-12-04 20:29:45 +03:00
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::onSelectedBlockIdChanged(::profiler::block_id_t)
|
2017-12-07 22:36:53 +03:00
|
|
|
{
|
2018-01-25 23:21:56 +03:00
|
|
|
if (!profiler_gui::is_max(EASY_GLOBALS.selected_block))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (profiler_gui::is_max(EASY_GLOBALS.selected_block_id))
|
|
|
|
{
|
|
|
|
if (m_boldItem != nullptr)
|
|
|
|
{
|
|
|
|
m_boldItem->setBold(false);
|
|
|
|
m_boldItem = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: find item corresponding to selected_block_id and make it bold
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
if (_item == nullptr || _item->type() != ValueItemType)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_item->setCheckState(CheckColumn, _item->checkState(CheckColumn) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
if (_item == nullptr || _item->type() != ValueItemType || _column != CheckColumn)
|
|
|
|
return;
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
auto item = static_cast<ArbitraryTreeWidgetItem*>(_item);
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
if (item->checkState(CheckColumn) == Qt::Checked)
|
|
|
|
{
|
|
|
|
m_checkedItems.push_back(item);
|
|
|
|
item->collectValues(EASY_GLOBALS.selected_thread);
|
|
|
|
if (!m_collectionsTimer.isActive())
|
|
|
|
m_collectionsTimer.start(100);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_checkedItems.removeOne(item);
|
|
|
|
item->interrupt();
|
|
|
|
onCollectionsTimeout();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::onCurrentItemChanged(QTreeWidgetItem* _current, QTreeWidgetItem*)
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
if (_current == nullptr || _current->type() != ValueItemType)
|
|
|
|
{
|
|
|
|
m_chart->update(nullptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
auto item = static_cast<const ArbitraryTreeWidgetItem*>(_current);
|
2017-12-28 00:23:14 +03:00
|
|
|
m_chart->update(item->collection());
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::rebuild()
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2017-12-07 22:36:53 +03:00
|
|
|
clear();
|
2017-12-04 20:29:45 +03:00
|
|
|
|
2017-12-07 22:36:53 +03:00
|
|
|
buildTree(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block, EASY_GLOBALS.selected_block_id);
|
|
|
|
|
|
|
|
m_treeWidget->expandAll();
|
|
|
|
for (int i = 0, columns = m_treeWidget->columnCount(); i < columns; ++i)
|
|
|
|
m_treeWidget->resizeColumnToContents(i);
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::onCollectionsTimeout()
|
2017-12-28 00:23:14 +03:00
|
|
|
{
|
|
|
|
if (m_checkedItems.isEmpty())
|
|
|
|
{
|
|
|
|
if (m_collectionsTimer.isActive())
|
|
|
|
m_collectionsTimer.stop();
|
|
|
|
m_chart->update(Collections {});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Collections collections;
|
|
|
|
collections.reserve(m_checkedItems.size());
|
|
|
|
for (auto item : m_checkedItems)
|
|
|
|
{
|
|
|
|
if (item->collection()->status() != ArbitraryValuesCollection::InProgress)
|
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
collections.push_back(CollectionPaintData {item->collection(), item->color(),
|
2018-02-02 22:44:38 +03:00
|
|
|
ChartViewType::Line, item == m_treeWidget->currentItem()});
|
2017-12-28 00:23:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (collections.size() == m_checkedItems.size())
|
|
|
|
{
|
|
|
|
if (m_collectionsTimer.isActive())
|
|
|
|
m_collectionsTimer.stop();
|
|
|
|
m_chart->update(std::move(collections));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId)
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
|
|
|
m_treeWidget->clear();
|
2017-12-14 23:12:27 +03:00
|
|
|
m_treeWidget->setColumnHidden(int_cast(ArbitraryColumns::Value), profiler_gui::is_max(_blockIndex));
|
2017-12-04 20:29:45 +03:00
|
|
|
|
|
|
|
if (_threadId != 0)
|
|
|
|
{
|
|
|
|
auto it = EASY_GLOBALS.profiler_blocks.find(_threadId);
|
|
|
|
if (it != EASY_GLOBALS.profiler_blocks.end())
|
|
|
|
{
|
|
|
|
auto threadItem = buildTreeForThread(it->second, _blockIndex, _blockId);
|
|
|
|
m_treeWidget->addTopLevelItem(threadItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (const auto& it : EASY_GLOBALS.profiler_blocks)
|
|
|
|
{
|
|
|
|
auto threadItem = buildTreeForThread(it.second, _blockIndex, _blockId);
|
|
|
|
m_treeWidget->addTopLevelItem(threadItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId)
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
auto fm = m_treeWidget->fontMetrics();
|
|
|
|
|
|
|
|
auto rootItem = new QTreeWidgetItem(StdItemType);
|
2017-12-14 23:12:27 +03:00
|
|
|
rootItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Thread"));
|
|
|
|
rootItem->setText(int_cast(ArbitraryColumns::Name),
|
|
|
|
profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, _threadRoot, EASY_GLOBALS.hex_thread_id));
|
2018-02-02 22:44:38 +03:00
|
|
|
rootItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 0);
|
2017-12-07 22:36:53 +03:00
|
|
|
|
2017-12-14 23:12:27 +03:00
|
|
|
const bool hasConcreteBlock = !profiler_gui::is_max(_blockIndex);
|
|
|
|
if (hasConcreteBlock)
|
2017-12-07 22:36:53 +03:00
|
|
|
{
|
|
|
|
const auto& block = easyBlocksTree(_blockIndex);
|
2017-12-14 23:12:27 +03:00
|
|
|
const auto& desc = easyDescriptor(block.node->id());
|
|
|
|
if (desc.type() == profiler::BlockType::Value)
|
2017-12-07 22:36:53 +03:00
|
|
|
{
|
2018-01-21 19:37:44 +03:00
|
|
|
auto valueItem = new ArbitraryTreeWidgetItem(rootItem, desc.color(), block.value->value_id());
|
2017-12-14 23:12:27 +03:00
|
|
|
valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*block.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));
|
2017-12-28 00:23:14 +03:00
|
|
|
|
|
|
|
const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width();
|
|
|
|
valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32);
|
|
|
|
|
2017-12-14 23:12:27 +03:00
|
|
|
return rootItem;
|
2017-12-07 22:36:53 +03:00
|
|
|
}
|
|
|
|
|
2017-12-14 23:12:27 +03:00
|
|
|
_blockId = block.node->id();
|
2017-12-07 22:36:53 +03:00
|
|
|
}
|
|
|
|
|
2017-12-14 23:12:27 +03:00
|
|
|
const bool noId = profiler_gui::is_max(_blockId);
|
|
|
|
QTreeWidgetItem* blockItem = nullptr;
|
|
|
|
if (!noId)
|
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
blockItem = new QTreeWidgetItem(rootItem, StdItemType);
|
2017-12-14 23:12:27 +03:00
|
|
|
blockItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Block"));
|
2018-02-02 22:44:38 +03:00
|
|
|
|
2017-12-14 23:12:27 +03:00
|
|
|
if (hasConcreteBlock)
|
|
|
|
blockItem->setText(int_cast(ArbitraryColumns::Name), easyBlockName(_blockIndex));
|
|
|
|
else
|
|
|
|
blockItem->setText(int_cast(ArbitraryColumns::Name), easyDescriptor(_blockId).name());
|
2018-02-02 22:44:38 +03:00
|
|
|
|
|
|
|
blockItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 1);
|
|
|
|
blockItem->setData(int_cast(ArbitraryColumns::Vin), Qt::UserRole, _blockId);
|
2017-12-14 23:12:27 +03:00
|
|
|
}
|
2017-12-07 22:36:53 +03:00
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
std::unordered_map<profiler::block_id_t, QTreeWidgetItem*, estd::hash<profiler::block_id_t> > blocks;
|
|
|
|
std::unordered_map<profiler::vin_t, UsedValueTypes, estd::hash<profiler::vin_t> > vins;
|
2017-12-14 23:12:27 +03:00
|
|
|
std::unordered_map<std::string, UsedValueTypes> names;
|
2017-12-04 20:29:45 +03:00
|
|
|
|
|
|
|
std::vector<profiler::block_index_t> stack;
|
2017-12-07 22:36:53 +03:00
|
|
|
for (auto childIndex : _threadRoot.children)
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
|
|
|
stack.push_back(childIndex);
|
|
|
|
while (!stack.empty())
|
|
|
|
{
|
|
|
|
const auto i = stack.back();
|
|
|
|
stack.pop_back();
|
|
|
|
|
2017-12-07 22:36:53 +03:00
|
|
|
const auto& block = easyBlocksTree(i);
|
2017-12-14 23:12:27 +03:00
|
|
|
if (noId || block.node->id() == _blockId || easyDescriptor(block.node->id()).id() == _blockId)
|
2017-12-07 22:36:53 +03:00
|
|
|
{
|
|
|
|
for (auto c : block.children)
|
|
|
|
{
|
2017-12-14 23:12:27 +03:00
|
|
|
if (noId)
|
|
|
|
stack.push_back(c);
|
|
|
|
|
2017-12-07 22:36:53 +03:00
|
|
|
const auto& child = easyBlocksTree(c);
|
|
|
|
const auto& desc = easyDescriptor(child.node->id());
|
2017-12-14 23:12:27 +03:00
|
|
|
if (desc.type() != profiler::BlockType::Value)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (blockItem == nullptr)
|
2017-12-07 22:36:53 +03:00
|
|
|
{
|
2017-12-14 23:12:27 +03:00
|
|
|
const auto id = block.node->id();
|
|
|
|
auto it = blocks.find(id);
|
|
|
|
if (it != blocks.end())
|
|
|
|
{
|
|
|
|
blockItem = it->second;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-12-28 00:23:14 +03:00
|
|
|
blockItem = new QTreeWidgetItem(rootItem, StdItemType);
|
2017-12-14 23:12:27 +03:00
|
|
|
blockItem->setText(int_cast(ArbitraryColumns::Type), QStringLiteral("Block"));
|
|
|
|
blockItem->setText(int_cast(ArbitraryColumns::Name), easyBlockName(block));
|
2018-02-02 22:44:38 +03:00
|
|
|
blockItem->setData(int_cast(ArbitraryColumns::Type), Qt::UserRole, 1);
|
|
|
|
blockItem->setData(int_cast(ArbitraryColumns::Vin), Qt::UserRole, id);
|
2017-12-14 23:12:27 +03:00
|
|
|
blocks.emplace(id, blockItem);
|
|
|
|
}
|
2017-12-07 22:36:53 +03:00
|
|
|
}
|
2017-12-14 23:12:27 +03:00
|
|
|
|
|
|
|
const auto typeIndex = int_cast(child.value->type());
|
|
|
|
auto vin = child.value->value_id();
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
ArbitraryTreeWidgetItem** usedItems = nullptr;
|
|
|
|
ArbitraryTreeWidgetItem* valueItem = nullptr;
|
2017-12-14 23:12:27 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
valueItem = new ArbitraryTreeWidgetItem(blockItem, desc.color(), vin);
|
2017-12-14 23:12:27 +03:00
|
|
|
valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*child.value));
|
|
|
|
valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name());
|
|
|
|
valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(vin, 0, 16));
|
|
|
|
|
|
|
|
if (i == _blockIndex)
|
|
|
|
valueItem->setText(int_cast(ArbitraryColumns::Value), profiler_gui::valueString(*child.value));
|
|
|
|
|
2017-12-28 00:23:14 +03:00
|
|
|
const auto sizeHintWidth = valueItem->sizeHint(CheckColumn).width();
|
|
|
|
valueItem->setWidthHint(std::max(sizeHintWidth, fm.width(valueItem->text(CheckColumn))) + 32);
|
|
|
|
|
2017-12-14 23:12:27 +03:00
|
|
|
*(usedItems + typeIndex) = valueItem;
|
2017-12-07 22:36:53 +03:00
|
|
|
}
|
2017-12-14 23:12:27 +03:00
|
|
|
|
|
|
|
if (noId)
|
|
|
|
blockItem = nullptr;
|
2017-12-07 22:36:53 +03:00
|
|
|
}
|
|
|
|
else
|
2017-12-04 20:29:45 +03:00
|
|
|
{
|
2017-12-07 22:36:53 +03:00
|
|
|
for (auto c : block.children)
|
|
|
|
stack.push_back(c);
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-07 22:36:53 +03:00
|
|
|
return rootItem;
|
2017-12-04 20:29:45 +03:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:37:44 +03:00
|
|
|
void ArbitraryValuesWidget::loadSettings()
|
|
|
|
{
|
|
|
|
QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
|
|
|
|
settings.beginGroup("ArbitraryValuesWidget");
|
|
|
|
|
|
|
|
auto geometry = settings.value("hsplitter/geometry").toByteArray();
|
|
|
|
if (!geometry.isEmpty())
|
|
|
|
m_splitter->restoreGeometry(geometry);
|
|
|
|
|
|
|
|
auto state = settings.value("hsplitter/state").toByteArray();
|
|
|
|
if (!state.isEmpty())
|
|
|
|
m_splitter->restoreState(state);
|
|
|
|
|
|
|
|
settings.endGroup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ArbitraryValuesWidget::saveSettings()
|
|
|
|
{
|
|
|
|
QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
|
|
|
|
settings.beginGroup("ArbitraryValuesWidget");
|
|
|
|
settings.setValue("hsplitter/geometry", m_splitter->saveGeometry());
|
|
|
|
settings.setValue("hsplitter/state", m_splitter->saveState());
|
|
|
|
settings.endGroup();
|
|
|
|
}
|