0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-26 08:01:51 +08:00

#31 [Gui] Big update for arbitrary values viewer. It is now viable and can be used to inspect user metrics. Currently arbitrary values viewer is built into blocks list widget.

This commit is contained in:
Victor Zarubkin 2018-01-21 19:37:44 +03:00
parent 694497b5ca
commit a7a58acd1d
22 changed files with 951 additions and 528 deletions

View File

@ -303,9 +303,11 @@ EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val)
//////////////////////////////////////////////////////////////////////////
template <uint16_t N>
template <const uint16_t N>
class chunk_allocator
{
static_assert(N != 0, "chunk_allocator<N> N must be a positive value");
struct chunk { EASY_ALIGNED(char, data[N], EASY_ALIGNMENT_SIZE); chunk* prev = nullptr; };
struct chunk_list
@ -380,8 +382,8 @@ class chunk_allocator
};
// Used in serialize(): workaround for no constexpr support in MSVC 2013.
EASY_STATIC_CONSTEXPR int_fast32_t MAX_CHUNK_OFFSET = N - sizeof(uint16_t);
EASY_STATIC_CONSTEXPR uint16_t N_MINUS_ONE = N - 1;
EASY_STATIC_CONSTEXPR int_fast32_t MaxChunkOffset = N - sizeof(uint16_t);
EASY_STATIC_CONSTEXPR uint16_t OneBeforeN = static_cast<uint16_t>(N - 1);
chunk_list m_chunks; ///< List of chunks.
chunk* m_markedChunk; ///< Chunk marked by last closed frame
@ -421,7 +423,7 @@ public:
// If there is enough space for at least another payload size,
// set it to zero.
if (chunkOffset < N_MINUS_ONE)
if (chunkOffset < OneBeforeN)
unaligned_zero16(data + n);
return data;
@ -503,7 +505,7 @@ public:
isMarked = (current == m_markedChunk);
const char* data = current->data;
const int_fast32_t maxOffset = isMarked ? m_markedChunkOffset : MAX_CHUNK_OFFSET;
const int_fast32_t maxOffset = isMarked ? m_markedChunkOffset : MaxChunkOffset;
int_fast32_t chunkOffset = 0; // signed int so overflow is not checked.
auto payloadSize = unaligned_load16<uint16_t>(data);
@ -555,7 +557,7 @@ public:
// If there is enough space for at least another payload size,
// set it to zero.
if (chunkOffset < N_MINUS_ONE)
if (chunkOffset < OneBeforeN)
unaligned_zero16(data + n);
if (marked == m_chunks.last && chunkOffset > m_chunkOffset)

View File

@ -95,6 +95,9 @@ public:
EASY_CONSTEXPR uint16_t SIZEOF_BLOCK = sizeof(profiler::BaseBlockData) + 1 + sizeof(uint16_t); // SerializedBlock stores BaseBlockData + at least 1 character for name ('\0') + 2 bytes for size of serialized data
EASY_CONSTEXPR uint16_t SIZEOF_CSWITCH = sizeof(profiler::CSwitchEvent) + 1 + sizeof(uint16_t); // SerializedCSwitch also stores additional 4 bytes to be able to save 64-bit thread_id
static_assert((int)SIZEOF_BLOCK * 128 < 65536, "Chunk size for profiler::Block must be less than 65536");
static_assert((int)SIZEOF_CSWITCH * 128 < 65536, "Chunk size for CSwitchBlock must be less than 65536");
struct ThreadStorage EASY_FINAL
{
StackBuffer<NonscopedBlock> nonscopedBlocks;

View File

@ -57,8 +57,10 @@
#include <QColor>
#include <QHeaderView>
#include <QVBoxLayout>
#include <QSplitter>
#include <QResizeEvent>
#include <QVariant>
#include <QSettings>
#include <list>
#include "arbitrary_value_inspector.h"
#include "treeview_first_column_delegate.h"
@ -387,269 +389,504 @@ QPointF ArbitraryValuesCollection::point(const profiler::ArbitraryValue& _value)
//////////////////////////////////////////////////////////////////////////
EasyArbitraryValuesChartItem::EasyArbitraryValuesChartItem()
: Parent(nullptr)
ArbitraryValuesChartItem::ArbitraryValuesChartItem()
: Parent()
, m_workerMaxValue(0)
, m_workerMinValue(0)
{
}
EasyArbitraryValuesChartItem::~EasyArbitraryValuesChartItem()
ArbitraryValuesChartItem::~ArbitraryValuesChartItem()
{
}
void EasyArbitraryValuesChartItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
void ArbitraryValuesChartItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
{
if (m_collections.empty())
return;
const auto& chart = *reinterpret_cast<const EasyGraphicsChart*>(scene()->parent());
const auto scale = chart.xscale();
qreal minValue = 1e300, maxValue = -1e300;
for (const auto& c : m_collections)
{
const auto& collection = *c.ptr;
if (minValue > collection.minValue())
minValue = collection.minValue();
if (maxValue < collection.maxValue())
maxValue = collection.maxValue();
}
const qreal height = std::max(maxValue - minValue, 1.);
auto r = scene()->sceneRect();
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;
_painter->save();
_painter->setTransform(QTransform::fromScale(1.0 / currentScale, 1), true);
for (const auto& c : m_collections)
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)
{
const auto& points = c.ptr->points();
if (points.empty())
continue;
if (c.selected)
{
auto pen = _painter->pen();
pen.setColor(QColor::fromRgba(c.color));
pen.setWidth(3);
_painter->setPen(pen);
}
else
{
_painter->setPen(QColor::fromRgba(c.color));
}
if (points.size() == 1)
_painter->drawPoint(points.front());
else
{
auto gety = [&r, &minValue, &maxValue, &height] (qreal y)
{
y = maxValue - y;
y /= height;
y *= r.height() - 10;
y += r.top() + 5;
return y;
};
if (c.chartType == ChartType::Points)
{
for (const auto& p : points)
_painter->drawPoint(QPointF {p.x() * scale, gety(p.y())});
}
else
{
QPointF p1 = points.front();
for (int i = 1; i < points.size(); ++i)
{
const auto& p2 = points[i];
_painter->drawLine(QPointF {p1.x() * scale, gety(p1.y())}, QPointF {p2.x() * scale, gety(p2.y())});
p1 = p2;
}
}
//_painter->drawPolyline(points.data(), static_cast<int>(points.size()));
}
const auto range = bindMode ? widget->sliderWidth() : widget->range();
paintMouseIndicator(_painter, m_boundingRect.top(), bottom, width, m_boundingRect.height(), font_h,
widget->value(), range);
}
_painter->setPen(Qt::darkGray);
_painter->drawLine(QLineF(0, bottom, width, bottom));
_painter->drawLine(QLineF(0, m_boundingRect.top(), width, m_boundingRect.top()));
_painter->restore();
}
QRectF EasyArbitraryValuesChartItem::boundingRect() const
void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height,
int _font_h, qreal _visibleRegionLeft, qreal _visibleRegionWidth)
{
return m_boundingRect;
if (_font_h == 0)
return;
const auto x = m_mousePos.x();
const auto y = m_mousePos.y();
// Horizontal
const bool visibleY = (_top < y && y < _bottom);
if (visibleY)
{
const int half_font_h = _font_h >> 1;
const auto value = m_minValue + ((_bottom - 2 - y) / (_height - 4)) * (m_maxValue - m_minValue);
const auto mouseStr = QString::number(value, 'f', 3);
const int textWidth = _painter->fontMetrics().width(mouseStr) + 3;
const QRectF rect(0, y - _font_h - 2, _width, 4 + (_font_h << 1));
_painter->setPen(Qt::blue);
qreal left = 0, right = _width;
const Qt::AlignmentFlag alignment = x < textWidth ? Qt::AlignRight : Qt::AlignLeft;
if (y > _bottom - half_font_h)
{
_painter->drawText(rect, alignment | Qt::AlignTop, mouseStr);
}
else if (y < _top + half_font_h)
{
_painter->drawText(rect, alignment | Qt::AlignBottom, mouseStr);
}
else
{
_painter->drawText(rect, alignment | Qt::AlignVCenter, mouseStr);
if (x < textWidth)
right = _width - textWidth;
else
left = textWidth;
}
_painter->drawLine(QLineF(left, y, right, y));
}
// 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;
if (!visibleY)
_painter->setPen(Qt::blue);
const QRectF rect(left, _bottom + 2, textWidth, _font_h);
_painter->drawText(rect, Qt::AlignCenter, mouseStr);
_painter->drawLine(QLineF(x, _top, x, _bottom));
}
}
void EasyArbitraryValuesChartItem::setBoundingRect(const QRectF& _rect)
bool ArbitraryValuesChartItem::updateImage()
{
m_boundingRect = _rect;
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();
m_workerThread = std::thread(&This::updateImageAsync, this, m_boundingRect, widget->getWindowScale(),
widget->minimum(), widget->maximum(), widget->range(), widget->value(), widget->sliderWidth(),
widget->bindMode(), EASY_GLOBALS.begin_time, EASY_GLOBALS.auto_adjust_chart_height);
return true;
}
void EasyArbitraryValuesChartItem::setBoundingRect(qreal _left, qreal _top, qreal _width, qreal _height)
void ArbitraryValuesChartItem::onImageUpdated()
{
m_boundingRect.setRect(_left, _top, _width, _height);
m_maxValue = m_workerMaxValue;
m_minValue = m_workerMinValue;
}
void EasyArbitraryValuesChartItem::update(Collections _collections)
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())
{
leftBounds.emplace_back(points.end());
continue;
}
if (_bindMode)
{
auto first = std::lower_bound(points.begin(), points.end(), _minimum, [](const QPointF& point, qreal x)
{
return point.x() < x;
});
if (first != points.end())
{
if (first != points.begin())
--first;
}
else
{
first = points.begin() + points.size() - 1;
}
leftBounds.emplace_back(first);
if (_autoAdjust)
{
for (auto it = first; it != points.end() && it->x() < right; ++it)
{
if (it->x() < _value)
continue;
const auto value = it->y();
if (minValue > value)
minValue = value;
if (maxValue < value)
maxValue = value;
}
continue;
}
}
else
{
leftBounds.emplace_back(points.begin());
}
if (minValue > collection.minValue())
minValue = collection.minValue();
if (maxValue < collection.maxValue())
maxValue = collection.maxValue();
}
if (minValue > maxValue)
{
// No points
m_workerMinValue = 0;
m_workerMaxValue = 0;
setReady(true);
return;
}
m_workerMinValue = minValue;
m_workerMaxValue = maxValue;
if (isReady())
return;
const bool singleValue = fabs(maxValue - minValue) < 2 * std::numeric_limits<qreal>::epsilon();
const auto middle = _boundingRect.height() * 0.5;
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;
y *= _boundingRect.height() - 4;
y += 2;
}
return y;
};
size_t i = 0;
for (const auto& c : m_collections)
{
if (isReady())
return;
const auto& points = c.ptr->points();
if (points.empty())
{
++i;
continue;
}
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));
}
const auto first = leftBounds[i];
if (c.chartType == ChartType::Points)
{
for (auto it = first; it != points.end() && it->x() < _maximum; ++it)
{
if (it->x() < _minimum)
continue;
if (isReady())
return;
const qreal x = it->x() * realScale - offset;
const qreal y = gety(it->y());
p.drawPoint(QPointF {x, y});
}
}
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;
QPointF p2 = *it;
x = p2.x() * realScale - offset;
y = gety(p2.y());
p2.setX(x);
p2.setY(y);
if (it->x() >= _minimum)
p.drawLine(p1, p2);
if (it->x() >= _maximum)
break;
p1 = p2;
}
}
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));
p.setBrush(QColor::fromRgba(0xc0ffffff));
qreal prevX = -offset * 2, prevY = -500;
for (auto it = first; it != points.end() && it->x() < _maximum; ++it)
{
if (it->x() < _minimum)
continue;
if (isReady())
return;
const qreal x = it->x() * realScale - offset;
const qreal y = gety(it->y());
const auto dx = x - prevX, dy = y - prevY;
const auto delta = estd::sqr(dx) + estd::sqr(dy);
if (delta > 25)
{
p.drawEllipse(QPointF {x, y}, 3, 3);
prevX = x;
prevY = y;
}
}
}
++i;
}
setReady(true);
}
void ArbitraryValuesChartItem::clear()
{
cancelAnyJob();
m_boundaryTimer.stop();
m_collections.clear();
m_minValue = m_maxValue = 0;
}
void ArbitraryValuesChartItem::update(Collections _collections)
{
cancelImageUpdate();
m_collections = std::move(_collections);
updateImage();
}
void EasyArbitraryValuesChartItem::update(const ArbitraryValuesCollection* _selected)
void ArbitraryValuesChartItem::update(const ArbitraryValuesCollection* _selected)
{
cancelImageUpdate();
for (auto& collection : m_collections)
collection.selected = collection.ptr == _selected;
updateImage();
}
//////////////////////////////////////////////////////////////////////////
EasyGraphicsChart::EasyGraphicsChart(QWidget* _parent)
GraphicsChart::GraphicsChart(QWidget* _parent)
: Parent(_parent)
, m_chartItem(new EasyArbitraryValuesChartItem())
, m_left(0)
, m_right(100)
, m_offset(0)
, m_xscale(1)
, m_visibleRegionWidth(100)
, m_bBindMode(false)
, m_chartItem(new ArbitraryValuesChartItem())
{
setCacheMode(QGraphicsView::CacheNone);
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
//setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setContentsMargins(0, 0, 0, 0);
setScene(new QGraphicsScene(this));
scene()->setSceneRect(0, -250, 500, 500);
m_imageItem = m_chartItem;
scene()->addItem(m_chartItem);
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::sceneSizeChanged,
this, &This::onSceneSizeChanged, Qt::QueuedConnection);
const auto rect = scene()->sceneRect();
m_chartItem->setPos(0, 0);
m_chartItem->setBoundingRect(0, rect.top() + margin(), scene()->width(), rect.height() - margins() - 1);
onSceneSizeChanged();
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::autoAdjustChartChanged, [this]
{
if (m_chartItem->isVisible())
m_chartItem->onModeChanged();
});
if (!EASY_GLOBALS.scene.empty)
{
setRange(EASY_GLOBALS.scene.left, EASY_GLOBALS.scene.right);
setSliderWidth(EASY_GLOBALS.scene.window);
setValue(EASY_GLOBALS.scene.offset);
m_slider->show();
}
m_chartItem->updateImage();
}
EasyGraphicsChart::~EasyGraphicsChart()
GraphicsChart::~GraphicsChart()
{
}
void EasyGraphicsChart::onSceneSizeChanged()
void GraphicsChart::clear()
{
setRange(EASY_GLOBALS.scene_left, EASY_GLOBALS.scene_right);
m_chartItem->clear();
Parent::clear();
}
void EasyGraphicsChart::resizeEvent(QResizeEvent* _event)
{
auto size = _event->size();
onWindowSizeChanged(size.width(), size.height());
scene()->update();
}
void EasyGraphicsChart::clear()
{
m_chartItem->update(Collections {});
}
bool EasyGraphicsChart::bindMode() const
{
return m_bBindMode;
}
qreal EasyGraphicsChart::xscale() const
{
return m_xscale;
}
qreal EasyGraphicsChart::left() const
{
return m_left;
}
qreal EasyGraphicsChart::right() const
{
return m_right;
}
qreal EasyGraphicsChart::range() const
{
return m_right - m_left;
}
qreal EasyGraphicsChart::offset() const
{
return m_bBindMode ? m_offset : 0;
}
qreal EasyGraphicsChart::region() const
{
return m_bBindMode ? m_visibleRegionWidth : range();
}
void EasyGraphicsChart::setOffset(qreal _offset)
{
m_offset = std::min(std::max(m_left, m_offset), m_right - m_visibleRegionWidth);
}
void EasyGraphicsChart::setRange(qreal _left, qreal _right)
{
const auto oldRange = range();
const auto oldOffsetPart = oldRange < 1e-3 ? 0.0 : m_offset / oldRange;
m_left = _left;
m_right = _right;
if (m_left > m_right)
std::swap(m_left, m_right);
const auto sceneRange = range();
//scene()->setSceneRect(m_left, -(height() >> 1), sceneRange, height());
//m_chartItem->setBoundingRect(scene()->sceneRect());
m_offset = m_left + oldOffsetPart * sceneRange;
m_visibleRegionWidth = std::min(m_visibleRegionWidth, sceneRange);
//const auto oldXScale = m_xscale;
m_xscale = sceneRange < 1e-3 ? 1.0 : width() / sceneRange;
//scale(m_xscale / oldXScale, 1);
scene()->update();
}
void EasyGraphicsChart::setRegion(qreal _visibleRegionWidth)
{
m_visibleRegionWidth = std::min(_visibleRegionWidth, range());
setOffset(m_offset);
}
void EasyGraphicsChart::onWindowSizeChanged(qreal _width, qreal _height)
{
//const auto oldXScale = m_xscale;
const auto sceneRange = range();
m_xscale = sceneRange < 1e-3 ? 1.0 : _width / sceneRange;
scene()->setSceneRect(0, -_height * 0.5, _width, _height);
//scene()->setSceneRect(m_left, -_height * 0.5, sceneRange, _height);
m_chartItem->setBoundingRect(scene()->sceneRect());
//scale(m_xscale / oldXScale, 1);
}
void EasyGraphicsChart::update(Collections _collections)
void GraphicsChart::update(Collections _collections)
{
m_chartItem->update(std::move(_collections));
scene()->update();
}
void EasyGraphicsChart::update(const ArbitraryValuesCollection* _selected)
void GraphicsChart::update(const ArbitraryValuesCollection* _selected)
{
m_chartItem->update(_selected);
scene()->update();
}
//////////////////////////////////////////////////////////////////////////
@ -669,13 +906,13 @@ EASY_CONSTEXPR auto StdItemType = QTreeWidgetItem::UserType;
EASY_CONSTEXPR auto ValueItemType = QTreeWidgetItem::UserType + 1;
struct UsedValueTypes {
EasyArbitraryTreeWidgetItem* items[int_cast(profiler::DataType::TypesCount)];
ArbitraryTreeWidgetItem* items[int_cast(profiler::DataType::TypesCount)];
UsedValueTypes(int = 0) { memset(items, 0, sizeof(items)); }
};
//////////////////////////////////////////////////////////////////////////
EasyArbitraryTreeWidgetItem::EasyArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin)
ArbitraryTreeWidgetItem::ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin)
: Parent(_parent, ValueItemType)
, m_vin(_vin)
, m_color(_color)
@ -685,29 +922,29 @@ EasyArbitraryTreeWidgetItem::EasyArbitraryTreeWidgetItem(QTreeWidgetItem* _paren
setCheckState(CheckColumn, Qt::Unchecked);
}
EasyArbitraryTreeWidgetItem::~EasyArbitraryTreeWidgetItem()
ArbitraryTreeWidgetItem::~ArbitraryTreeWidgetItem()
{
}
QVariant EasyArbitraryTreeWidgetItem::data(int _column, int _role) const
QVariant ArbitraryTreeWidgetItem::data(int _column, int _role) const
{
if (_column == CheckColumn && _role == Qt::SizeHintRole)
return QSize(m_widthHint, 26);
return Parent::data(_column, _role);
}
void EasyArbitraryTreeWidgetItem::setWidthHint(int _width)
void ArbitraryTreeWidgetItem::setWidthHint(int _width)
{
m_widthHint = _width;
}
const ArbitraryValuesCollection* EasyArbitraryTreeWidgetItem::collection() const
const ArbitraryValuesCollection* ArbitraryTreeWidgetItem::collection() const
{
return m_collection.get();
}
void EasyArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId)
void ArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId)
{
if (!m_collection)
m_collection = CollectionPtr(new ArbitraryValuesCollection);
@ -717,7 +954,7 @@ void EasyArbitraryTreeWidgetItem::collectValues(profiler::thread_id_t _threadId)
m_collection->collectValues(_threadId, m_vin, text(int_cast(ArbitraryColumns::Name)).toStdString().c_str(), EASY_GLOBALS.begin_time);
}
void EasyArbitraryTreeWidgetItem::interrupt()
void ArbitraryTreeWidgetItem::interrupt()
{
if (!m_collection)
return;
@ -726,24 +963,29 @@ void EasyArbitraryTreeWidgetItem::interrupt()
m_collection.release();
}
profiler::color_t EasyArbitraryTreeWidgetItem::color() const
profiler::color_t ArbitraryTreeWidgetItem::color() const
{
return m_color;
}
//////////////////////////////////////////////////////////////////////////
EasyArbitraryValuesWidget::EasyArbitraryValuesWidget(QWidget* _parent)
ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent)
: Parent(_parent)
, m_splitter(new QSplitter(Qt::Horizontal, this))
, m_treeWidget(new QTreeWidget(this))
, m_chart(new EasyGraphicsChart(this))
, m_chart(new GraphicsChart(this))
{
auto layout = new QHBoxLayout(this);
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);
auto layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_treeWidget);
layout->addWidget(m_chart);
layout->setStretch(0, 1);
layout->setStretch(1, 1);
layout->addWidget(m_splitter);
m_treeWidget->setAutoFillBackground(false);
m_treeWidget->setAlternatingRowColors(true);
@ -761,10 +1003,6 @@ EasyArbitraryValuesWidget::EasyArbitraryValuesWidget(QWidget* _parent)
headerItem->setText(int_cast(ArbitraryColumns::Vin), "ID");
m_treeWidget->setHeaderItem(headerItem);
// auto mainLayout = new QVBoxLayout(this);
// mainLayout->setContentsMargins(1, 1, 1, 1);
// mainLayout->addWidget(m_treeWidget);
connect(&m_timer, &QTimer::timeout, this, &This::rebuild);
connect(&m_collectionsTimer, &QTimer::timeout, this, &This::onCollectionsTimeout);
@ -775,14 +1013,16 @@ EasyArbitraryValuesWidget::EasyArbitraryValuesWidget(QWidget* _parent)
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChanged);
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChanged);
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockIdChanged, this, &This::onSelectedBlockIdChanged);
loadSettings();
}
EasyArbitraryValuesWidget::~EasyArbitraryValuesWidget()
ArbitraryValuesWidget::~ArbitraryValuesWidget()
{
saveSettings();
}
void EasyArbitraryValuesWidget::clear()
void ArbitraryValuesWidget::clear()
{
if (m_collectionsTimer.isActive())
m_collectionsTimer.stop();
@ -792,25 +1032,25 @@ void EasyArbitraryValuesWidget::clear()
m_treeWidget->clear();
}
void EasyArbitraryValuesWidget::onSelectedThreadChanged(::profiler::thread_id_t)
void ArbitraryValuesWidget::onSelectedThreadChanged(::profiler::thread_id_t)
{
if (!m_timer.isActive())
m_timer.start(100);
}
void EasyArbitraryValuesWidget::onSelectedBlockChanged(uint32_t)
void ArbitraryValuesWidget::onSelectedBlockChanged(uint32_t)
{
if (!m_timer.isActive())
m_timer.start(100);
}
void EasyArbitraryValuesWidget::onSelectedBlockIdChanged(::profiler::block_id_t)
void ArbitraryValuesWidget::onSelectedBlockIdChanged(::profiler::block_id_t)
{
if (!m_timer.isActive())
m_timer.start(100);
}
void EasyArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int)
void ArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int)
{
if (_item == nullptr || _item->type() != ValueItemType)
return;
@ -818,12 +1058,12 @@ void EasyArbitraryValuesWidget::onItemDoubleClicked(QTreeWidgetItem* _item, int)
_item->setCheckState(CheckColumn, _item->checkState(CheckColumn) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
}
void EasyArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column)
void ArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _column)
{
if (_item == nullptr || _item->type() != ValueItemType || _column != CheckColumn)
return;
auto item = static_cast<EasyArbitraryTreeWidgetItem*>(_item);
auto item = static_cast<ArbitraryTreeWidgetItem*>(_item);
if (item->checkState(CheckColumn) == Qt::Checked)
{
@ -840,7 +1080,7 @@ void EasyArbitraryValuesWidget::onItemChanged(QTreeWidgetItem* _item, int _colum
}
}
void EasyArbitraryValuesWidget::onCurrentItemChanged(QTreeWidgetItem* _current, QTreeWidgetItem*)
void ArbitraryValuesWidget::onCurrentItemChanged(QTreeWidgetItem* _current, QTreeWidgetItem*)
{
if (_current == nullptr || _current->type() != ValueItemType)
{
@ -848,11 +1088,11 @@ void EasyArbitraryValuesWidget::onCurrentItemChanged(QTreeWidgetItem* _current,
return;
}
auto item = static_cast<const EasyArbitraryTreeWidgetItem*>(_current);
auto item = static_cast<const ArbitraryTreeWidgetItem*>(_current);
m_chart->update(item->collection());
}
void EasyArbitraryValuesWidget::rebuild()
void ArbitraryValuesWidget::rebuild()
{
clear();
@ -863,7 +1103,7 @@ void EasyArbitraryValuesWidget::rebuild()
m_treeWidget->resizeColumnToContents(i);
}
void EasyArbitraryValuesWidget::onCollectionsTimeout()
void ArbitraryValuesWidget::onCollectionsTimeout()
{
if (m_checkedItems.isEmpty())
{
@ -879,7 +1119,7 @@ void EasyArbitraryValuesWidget::onCollectionsTimeout()
{
if (item->collection()->status() != ArbitraryValuesCollection::InProgress)
{
collections.push_back(EasyCollectionPaintData {item->collection(), item->color(),
collections.push_back(CollectionPaintData {item->collection(), item->color(),
ChartType::Line, item == m_treeWidget->currentItem()});
}
}
@ -892,7 +1132,7 @@ void EasyArbitraryValuesWidget::onCollectionsTimeout()
}
}
void EasyArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId)
void ArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId)
{
m_treeWidget->clear();
m_treeWidget->setColumnHidden(int_cast(ArbitraryColumns::Value), profiler_gui::is_max(_blockIndex));
@ -916,7 +1156,7 @@ void EasyArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profi
}
}
QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId)
QTreeWidgetItem* ArbitraryValuesWidget::buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId)
{
auto fm = m_treeWidget->fontMetrics();
@ -932,7 +1172,7 @@ QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::B
const auto& desc = easyDescriptor(block.node->id());
if (desc.type() == profiler::BlockType::Value)
{
auto valueItem = new EasyArbitraryTreeWidgetItem(rootItem, desc.color(), block.value->value_id());
auto valueItem = new ArbitraryTreeWidgetItem(rootItem, desc.color(), block.value->value_id());
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));
@ -1005,8 +1245,8 @@ QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::B
const auto typeIndex = int_cast(child.value->type());
auto vin = child.value->value_id();
EasyArbitraryTreeWidgetItem** usedItems = nullptr;
EasyArbitraryTreeWidgetItem* valueItem = nullptr;
ArbitraryTreeWidgetItem** usedItems = nullptr;
ArbitraryTreeWidgetItem* valueItem = nullptr;
if (vin == 0)
{
auto result = names.emplace(desc.name(), 0);
@ -1030,7 +1270,7 @@ QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::B
}
}
valueItem = new EasyArbitraryTreeWidgetItem(blockItem, desc.color(), vin);
valueItem = new ArbitraryTreeWidgetItem(blockItem, desc.color(), vin);
valueItem->setText(int_cast(ArbitraryColumns::Type), profiler_gui::valueTypeString(*child.value));
valueItem->setText(int_cast(ArbitraryColumns::Name), desc.name());
valueItem->setText(int_cast(ArbitraryColumns::Vin), QString("0x%1").arg(vin, 0, 16));
@ -1058,3 +1298,27 @@ QTreeWidgetItem* EasyArbitraryValuesWidget::buildTreeForThread(const profiler::B
return rootItem;
}
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();
}

View File

@ -58,8 +58,6 @@
#include <QWidget>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QGraphicsItem>
#include <QGraphicsView>
#include <QTimer>
#include <QPointF>
#include <QList>
@ -72,6 +70,7 @@
#include <thread>
#include <atomic>
#include <memory>
#include "graphics_slider_area.h"
//////////////////////////////////////////////////////////////////////////
@ -136,7 +135,7 @@ enum class ChartType : uint8_t
Points
};
struct EasyCollectionPaintData EASY_FINAL
struct CollectionPaintData EASY_FINAL
{
const ArbitraryValuesCollection* ptr;
QRgb color;
@ -144,44 +143,58 @@ struct EasyCollectionPaintData EASY_FINAL
bool selected;
};
using Collections = std::vector<EasyCollectionPaintData>;
using Collections = std::vector<CollectionPaintData>;
//////////////////////////////////////////////////////////////////////////
class EasyArbitraryValuesChartItem : public QGraphicsItem
class ArbitraryValuesChartItem : public GraphicsImageItem
{
using Parent = QGraphicsItem;
using This = EasyArbitraryValuesChartItem;
using Parent = GraphicsImageItem;
using This = ArbitraryValuesChartItem;
Collections m_collections;
QRectF m_boundingRect;
qreal m_workerMaxValue;
qreal m_workerMinValue;
public:
explicit EasyArbitraryValuesChartItem();
~EasyArbitraryValuesChartItem() override;
explicit ArbitraryValuesChartItem();
~ArbitraryValuesChartItem() override;
void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override;
void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget) override;
QRectF boundingRect() const override;
void setBoundingRect(const QRectF& _rect);
void setBoundingRect(qreal _left, qreal _top, qreal _width, qreal _height);
bool updateImage() override;
protected:
void onImageUpdated() override;
public:
void clear();
void update(Collections _collections);
void update(const ArbitraryValuesCollection* _selected);
}; // end of class EasyArbitraryValuesChartItem.
private:
class EasyGraphicsChart : public QGraphicsView
void paintMouseIndicator(QPainter* _painter, qreal _top, qreal _bottom, qreal _width, qreal _height, int _font_h,
qreal _visibleRegionLeft, qreal _visibleRegionWidth);
void 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);
}; // end of class ArbitraryValuesChartItem.
class GraphicsChart : public GraphicsSliderArea
{
Q_OBJECT
private:
using Parent = QGraphicsView;
using This = EasyGraphicsChart;
using Parent = GraphicsSliderArea;
using This = GraphicsChart;
EasyArbitraryValuesChartItem* m_chartItem;
ArbitraryValuesChartItem* m_chartItem;
qreal m_left;
qreal m_right;
qreal m_offset;
@ -191,42 +204,24 @@ private:
public:
explicit EasyGraphicsChart(QWidget* _parent = nullptr);
~EasyGraphicsChart() override;
explicit GraphicsChart(QWidget* _parent = nullptr);
~GraphicsChart() override;
void resizeEvent(QResizeEvent* _event) override;
void clear() override;
void clear();
bool bindMode() const;
qreal xscale() const;
qreal left() const;
qreal right() const;
qreal range() const;
qreal offset() const;
qreal region() const;
void setOffset(qreal _offset);
void setRange(qreal _left, qreal _right);
void setRegion(qreal _visibleRegionWidth);
public:
void update(Collections _collections);
void update(const ArbitraryValuesCollection* _selected);
private slots:
void onSceneSizeChanged();
void onWindowSizeChanged(qreal _width, qreal _height);
}; // end of class EasyGraphicsChart.
}; // end of class GraphicsChart.
//////////////////////////////////////////////////////////////////////////
class EasyArbitraryTreeWidgetItem : public QTreeWidgetItem
class ArbitraryTreeWidgetItem : public QTreeWidgetItem
{
using Parent = QTreeWidgetItem;
using This = EasyArbitraryTreeWidgetItem;
using This = ArbitraryTreeWidgetItem;
using CollectionPtr = std::unique_ptr<ArbitraryValuesCollection>;
CollectionPtr m_collection;
@ -236,8 +231,8 @@ class EasyArbitraryTreeWidgetItem : public QTreeWidgetItem
public:
explicit EasyArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin = 0);
~EasyArbitraryTreeWidgetItem() override;
explicit ArbitraryTreeWidgetItem(QTreeWidgetItem* _parent, profiler::color_t _color, profiler::vin_t _vin = 0);
~ArbitraryTreeWidgetItem() override;
QVariant data(int _column, int _role) const override;
@ -249,27 +244,28 @@ public:
profiler::color_t color() const;
}; // end of class EasyArbitraryTreeWidgetItem.
}; // end of class ArbitraryTreeWidgetItem.
//////////////////////////////////////////////////////////////////////////
class EasyArbitraryValuesWidget : public QWidget
class ArbitraryValuesWidget : public QWidget
{
Q_OBJECT
using Parent = QWidget;
using This = EasyArbitraryValuesWidget;
using This = ArbitraryValuesWidget;
QTimer m_timer;
QTimer m_collectionsTimer;
QList<EasyArbitraryTreeWidgetItem*> m_checkedItems;
QTreeWidget* m_treeWidget;
EasyGraphicsChart* m_chart;
QTimer m_timer;
QTimer m_collectionsTimer;
QList<ArbitraryTreeWidgetItem*> m_checkedItems;
class QSplitter* m_splitter;
QTreeWidget* m_treeWidget;
GraphicsChart* m_chart;
public:
explicit EasyArbitraryValuesWidget(QWidget* _parent = nullptr);
~EasyArbitraryValuesWidget() override;
explicit ArbitraryValuesWidget(QWidget* _parent = nullptr);
~ArbitraryValuesWidget() override;
void clear();
@ -292,7 +288,10 @@ private:
void buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId);
QTreeWidgetItem* buildTreeForThread(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId);
}; // end of class EasyArbitraryValuesWidget.
void loadSettings();
void saveSettings();
}; // end of class ArbitraryValuesWidget.
//////////////////////////////////////////////////////////////////////////

View File

@ -72,9 +72,12 @@
#include <QKeyEvent>
#include <QScrollBar>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QSplitter>
#include <QDebug>
#include <QSignalBlocker>
#include <QGraphicsDropShadowEffect>
#include <QSettings>
#include "blocks_graphics_view.h"
#include "easy_graphics_item.h"
#include "easy_chronometer_item.h"
@ -286,6 +289,7 @@ EasyGraphicsView::EasyGraphicsView(QWidget* _parent)
, m_sceneWidth(0)
, m_scale(1)
, m_offset(0)
, m_visibleRegionWidth(0)
, m_timelineStep(0)
, m_idleTime(0)
, m_mouseButtons(Qt::NoButton)
@ -389,21 +393,64 @@ void EasyGraphicsView::clear()
m_bEmpty = true;
m_sceneWidth = 10;
m_visibleRegionWidth = 10;
setSceneRect(0, 0, 10, 10);
auto& sceneData = EASY_GLOBALS.scene;
sceneData.left = 0;
sceneData.right = m_sceneWidth;
sceneData.window = m_visibleRegionWidth;
sceneData.offset = m_offset;
sceneData.empty = true;
// notify ProfTreeWidget that selection was reset
emit intervalChanged(m_selectedBlocks, m_beginTime, 0, 0, false);
EASY_GLOBALS.selected_thread = 0;
emit EASY_GLOBALS.events.selectedThreadChanged(0);
}
void EasyGraphicsView::notifySceneSizeChange()
{
EASY_GLOBALS.scene.left = 0;
EASY_GLOBALS.scene.right = m_sceneWidth;
emit EASY_GLOBALS.events.sceneSizeChanged(0, m_sceneWidth);
}
void EasyGraphicsView::notifyVisibleRegionSizeChange()
{
auto vbar = verticalScrollBar();
const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0);
notifyVisibleRegionSizeChange((m_visibleSceneRect.width() + vbar_width) / m_scale);
}
void EasyGraphicsView::notifyVisibleRegionSizeChange(qreal _size)
{
m_visibleRegionWidth = _size;
EASY_GLOBALS.scene.window = _size;
emit EASY_GLOBALS.events.sceneVisibleRegionSizeChanged(_size);
}
void EasyGraphicsView::notifyVisibleRegionPosChange()
{
EASY_GLOBALS.scene.offset = m_offset;
emit EASY_GLOBALS.events.sceneVisibleRegionPosChanged(m_offset);
}
void EasyGraphicsView::notifyVisibleRegionPosChange(qreal _pos)
{
m_offset = estd::clamp(0., _pos, m_sceneWidth - m_visibleRegionWidth);
notifyVisibleRegionPosChange();
}
void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTree)
{
// clear scene
clear();
emit EASY_GLOBALS.events.sceneCleared();
if (_blocksTree.empty())
{
return;
}
auto bgItem = new EasyBackgroundItem();
scene()->addItem(bgItem);
@ -515,14 +562,14 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
// Calculating scene rect
m_sceneWidth = time2position(finish);
setSceneRect(0, 0, m_sceneWidth, y + TIMELINE_ROW_SIZE);
EASY_GLOBALS.scene_left = 0;
EASY_GLOBALS.scene_right = m_sceneWidth;
emit EASY_GLOBALS.events.sceneSizeChanged();
EASY_GLOBALS.scene.empty = false;
// Center view on the beginning of the scene
updateVisibleSceneRect();
setScrollbar(m_pScrollbar);
//setScrollbar(m_pScrollbar);
notifySceneSizeChange();
notifyVisibleRegionSizeChange();
// Create new chronometer item (previous item was destroyed by scene on scene()->clear()).
// It will be shown on mouse right button click.
@ -543,9 +590,7 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
emit treeChanged();
if (mainThreadItem != nullptr)
{
longestItem = mainThreadItem;
}
if (longestItem != nullptr)
{
@ -555,7 +600,7 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
scrollTo(longestItem);
m_pScrollbar->setHistogramSource(longestItem->threadId(), longestItem->items(0));
if (!longestItem->items(0).empty())
m_pScrollbar->setValue(longestItem->items(0).front().left() - m_pScrollbar->sliderWidth() * 0.25);
notifyVisibleRegionPosChange(longestItem->items(0).front().left() - m_visibleRegionWidth * 0.25);
}
m_idleTimer.start(IDLE_TIMER_INTERVAL);
@ -683,11 +728,8 @@ void EasyGraphicsView::setScrollbar(EasyGraphicsScrollbar* _scrollbar)
auto const prevScrollbar = m_pScrollbar;
const bool makeConnect = prevScrollbar == nullptr || prevScrollbar != _scrollbar;
if (prevScrollbar != nullptr && prevScrollbar != _scrollbar)
{
disconnect(prevScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange);
disconnect(prevScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel);
}
disconnect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::chartSliderChanged, this, &This::onGraphicsScrollbarValueChange);
disconnect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::chartWheeled, this, &This::onGraphicsScrollbarWheel);
m_pScrollbar = _scrollbar;
m_pScrollbar->clear();
@ -697,11 +739,8 @@ void EasyGraphicsView::setScrollbar(EasyGraphicsScrollbar* _scrollbar)
const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0);
m_pScrollbar->setSliderWidth(m_visibleSceneRect.width() + vbar_width);
if (makeConnect)
{
connect(m_pScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange);
connect(m_pScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel);
}
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::chartSliderChanged, this, &This::onGraphicsScrollbarValueChange);
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::chartWheeled, this, &This::onGraphicsScrollbarWheel);
EASY_GLOBALS.selected_thread = 0;
emit EASY_GLOBALS.events.selectedThreadChanged(0);
@ -768,7 +807,7 @@ void EasyGraphicsView::scaleTo(qreal _scale)
// Update slider width for scrollbar
const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
m_pScrollbar->setSliderWidth(windowWidth);
notifyVisibleRegionSizeChange(windowWidth);
updateTimelineStep(windowWidth);
repaintScene();
@ -803,7 +842,7 @@ void EasyGraphicsView::scrollTo(const EasyGraphicsItem* _item)
{
m_bUpdatingRect = true;
auto vbar = verticalScrollBar();
vbar->setValue(_item->y() + (_item->boundingRect().height() - vbar->pageStep()) * 0.5);
vbar->setValue(static_cast<int>(_item->y() + (_item->boundingRect().height() - vbar->pageStep()) * 0.5));
m_bUpdatingRect = false;
}
@ -822,21 +861,18 @@ void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta)
//updateVisibleSceneRect(); // Update scene rect
// Update slider width for scrollbar
auto vbar = verticalScrollBar();
const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0);
const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
m_pScrollbar->setSliderWidth(windowWidth);
notifyVisibleRegionSizeChange();
// Calculate new offset to simulate QGraphicsView::AnchorUnderMouse scaling behavior
m_offset = clamp(0., mousePosition - _mouseX / m_scale, m_sceneWidth - windowWidth);
m_offset = clamp(0., mousePosition - _mouseX / m_scale, m_sceneWidth - m_visibleRegionWidth);
// Update slider position
m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
m_pScrollbar->setValue(m_offset);
m_bUpdatingRect = false;
profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // To be sure that updateVisibleSceneRect will not be called by scrollbar change
notifyVisibleRegionPosChange();
guard.restore();
updateVisibleSceneRect(); // Update scene rect
updateTimelineStep(windowWidth);
updateTimelineStep(m_visibleRegionWidth);
repaintScene(); // repaint scene
}
@ -975,7 +1011,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
// Jump to selected zone
clicked = true;
m_flickerSpeedX = m_flickerSpeedY = 0;
m_pScrollbar->setValue(m_chronometerItem->left() + m_chronometerItem->width() * 0.5 - m_pScrollbar->sliderHalfWidth());
notifyVisibleRegionPosChange(m_chronometerItem->left() + (m_chronometerItem->width() - m_visibleRegionWidth) * 0.5);
}
if (!clicked && m_mouseMovePath.manhattanLength() < 5)
@ -993,7 +1029,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
{
::profiler::block_index_t i = ~0U;
auto block = item->intersect(mouseClickPos, i);
if (block)
if (block != nullptr)
{
changedSelectedItem = true;
selectedBlock = block;
@ -1026,23 +1062,26 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
if (changedSelectedItem)
{
m_bUpdatingRect = true;
profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true);
if (selectedBlock != nullptr && previouslySelectedBlock == EASY_GLOBALS.selected_block && !selectedBlock->tree.children.empty())
{
EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded = !EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded;
emit EASY_GLOBALS.events.itemsExpandStateChanged();
}
emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block);
if (EASY_GLOBALS.selecting_block_changes_thread && selectedBlock != nullptr && EASY_GLOBALS.selected_thread != selectedBlockThread)
{
EASY_GLOBALS.selected_thread = selectedBlockThread;
m_pScrollbar->lock();
emit EASY_GLOBALS.events.lockCharts();
emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread);
m_pScrollbar->unlock();
emit EASY_GLOBALS.events.unlockCharts();
}
m_bUpdatingRect = false;
guard.restore();
if (selectedBlock != nullptr && selectedBlockThread == EASY_GLOBALS.selected_thread)
m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id);
@ -1164,10 +1203,10 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event)
{
auto vbar = verticalScrollBar();
m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once
profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // Block scrollbars from updating scene rect to make it possible to do it only once
vbar->setValue(vbar->value() - delta.y());
m_pScrollbar->setValue(m_pScrollbar->value() - delta.x() / m_scale);
m_bUpdatingRect = false;
notifyVisibleRegionPosChange(m_offset - delta.x() / m_scale);
guard.restore();
// Seems like an ugly stub, but QSignalBlocker is also a bad decision
// because if scrollbar does not emit valueChanged signal then viewport does not move
@ -1243,14 +1282,14 @@ void EasyGraphicsView::keyPressEvent(QKeyEvent* _event)
case Qt::Key_Right:
case Qt::Key_6:
{
m_pScrollbar->setValue(m_pScrollbar->value() + KeyStep / m_scale);
notifyVisibleRegionPosChange(m_offset + KeyStep / m_scale);
break;
}
case Qt::Key_Left:
case Qt::Key_4:
{
m_pScrollbar->setValue(m_pScrollbar->value() - KeyStep / m_scale);
notifyVisibleRegionPosChange(m_offset - KeyStep / m_scale);
break;
}
@ -1308,16 +1347,16 @@ void EasyGraphicsView::resizeEvent(QResizeEvent* _event)
// Update slider width for scrollbar
const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
m_pScrollbar->setSliderWidth(windowWidth);
notifyVisibleRegionSizeChange(windowWidth);
// Calculate new offset to save old screen center
const auto deltaWidth = m_visibleSceneRect.width() - previousRect.width();
m_offset = clamp(0., m_offset - deltaWidth * 0.5 / m_scale, m_sceneWidth - windowWidth);
// Update slider position
m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
m_pScrollbar->setValue(m_offset);
m_bUpdatingRect = false;
profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // To be sure that updateVisibleSceneRect will not be called by scrollbar change
notifyVisibleRegionPosChange();
guard.restore();
repaintScene(); // repaint scene
}
@ -1379,6 +1418,9 @@ void EasyGraphicsView::initMode()
if (!m_selectedBlocks.empty())
emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
});
connect(globalSignals, &profiler_gui::EasyGlobalSignals::chartSliderChanged, this, &This::onGraphicsScrollbarValueChange);
connect(globalSignals, &profiler_gui::EasyGlobalSignals::chartWheeled, this, &This::onGraphicsScrollbarWheel);
}
//////////////////////////////////////////////////////////////////////////
@ -1444,10 +1486,10 @@ void EasyGraphicsView::onFlickerTimeout()
auto vbar = verticalScrollBar();
m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once
m_pScrollbar->setValue(m_pScrollbar->value() - m_flickerSpeedX / m_scale);
profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true); // Block scrollbars from updating scene rect to make it possible to do it only once
notifyVisibleRegionPosChange(m_offset - m_flickerSpeedX / m_scale);
vbar->setValue(vbar->value() - m_flickerSpeedY);
m_bUpdatingRect = false;
guard.restore();
// Seems like an ugly stub, but QSignalBlocker is also a bad decision
// because if scrollbar does not emit valueChanged signal then viewport does not move
@ -1954,22 +1996,20 @@ void EasyGraphicsView::onSelectedBlockChange(unsigned int _block_index)
m_flickerSpeedX = m_flickerSpeedY = 0;
m_bUpdatingRect = true;
const profiler_gui::BoolFlagGuard guard(m_bUpdatingRect, true);
verticalScrollBar()->setValue(static_cast<int>(thread_item->levelY(guiblock.graphics_item_level) - m_visibleSceneRect.height() * 0.5));
m_pScrollbar->setValue(item.left() + item.width() * 0.5 - m_pScrollbar->sliderHalfWidth());
notifyVisibleRegionPosChange(item.left() + (item.width() - m_visibleRegionWidth) * 0.5);
if (EASY_GLOBALS.selecting_block_changes_thread && EASY_GLOBALS.selected_thread != thread_item->threadId())
{
EASY_GLOBALS.selected_thread = thread_item->threadId();
m_pScrollbar->lock();
emit EASY_GLOBALS.events.lockCharts();
emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread);
m_pScrollbar->unlock();
emit EASY_GLOBALS.events.unlockCharts();
}
m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, guiblock.tree.node->id());
m_bUpdatingRect = false;
}
else if (EASY_GLOBALS.selected_thread != 0)
{
@ -2006,7 +2046,8 @@ void EasyGraphicsView::onRefreshRequired()
EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent)
: QWidget(_parent)
, m_scrollbar(new EasyGraphicsScrollbar(true, 85 + (QFontMetrics(font()).height() << 1), this))
, m_splitter(new QSplitter(Qt::Vertical, this))
, m_scrollbar(new EasyGraphicsScrollbar(85 + (QFontMetrics(font()).height() << 1), this))
, m_view(new EasyGraphicsView(this))
, m_threadNamesWidget(new EasyThreadNamesWidget(m_view, m_scrollbar->height(), this))
{
@ -2015,13 +2056,18 @@ EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent)
void EasyGraphicsViewWidget::initWidget()
{
auto lay = new QGridLayout(this);
m_splitter->setHandleWidth(1);
m_splitter->setContentsMargins(0, 0, 0, 0);
m_splitter->addWidget(m_view);
m_splitter->addWidget(m_scrollbar);
m_splitter->setStretchFactor(0, 500);
m_splitter->setStretchFactor(1, 1);
auto lay = new QHBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(1);
lay->addWidget(m_threadNamesWidget, 0, 0, 2, 1);
lay->addWidget(m_view, 0, 1);
lay->addWidget(m_scrollbar, 1, 1);
setLayout(lay);
lay->addWidget(m_threadNamesWidget);
lay->addWidget(m_splitter);
m_view->setScrollbar(m_scrollbar);
}
@ -2043,6 +2089,23 @@ void EasyGraphicsViewWidget::clear()
m_view->clear();
}
void EasyGraphicsViewWidget::save(QSettings& settings)
{
settings.setValue("diagram/vsplitter/geometry", m_splitter->saveGeometry());
settings.setValue("diagram/vsplitter/state", m_splitter->saveState());
}
void EasyGraphicsViewWidget::restore(QSettings& settings)
{
auto geometry = settings.value("diagram/vsplitter/geometry").toByteArray();
if (!geometry.isEmpty())
m_splitter->restoreGeometry(geometry);
auto state = settings.value("diagram/vsplitter/state").toByteArray();
if (!state.isEmpty())
m_splitter->restoreState(state);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@ -2117,6 +2180,9 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte
_painter->drawRect(rect);
}
if (h + 2 >= parentView->height())
return;
// Draw separator between thread names area and information area
_painter->setPen(::profiler_gui::SYSTEM_BORDER_COLOR);
_painter->drawLine(QLineF(0, h, w, h));

View File

@ -136,6 +136,7 @@ private:
qreal m_sceneWidth; ///<
qreal m_scale; ///< Current scale
qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :(
qreal m_visibleRegionWidth; ///< Visible scene rectangle in scene coordinates + width of vertical scrollbar (if visible)
qreal m_timelineStep; ///<
uint64_t m_idleTime; ///<
QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent)
@ -156,7 +157,7 @@ private:
public:
explicit EasyGraphicsView(QWidget* _parent = nullptr);
virtual ~EasyGraphicsView();
~EasyGraphicsView() override;
// Public virtual methods
@ -198,6 +199,12 @@ private:
// Private non-virtual methods
void notifySceneSizeChange();
void notifyVisibleRegionSizeChange();
void notifyVisibleRegionSizeChange(qreal _size);
void notifyVisibleRegionPosChange();
void notifyVisibleRegionPosChange(qreal _pos);
void removePopup(bool _removeFromScene = false);
EasyChronometerItem* createChronometer(bool _main = true);
@ -230,33 +237,33 @@ public:
// Public inline methods
inline qreal scale() const
qreal scale() const
{
return m_scale;
}
inline qreal offset() const
qreal offset() const
{
return m_offset;
}
inline const QRectF& visibleSceneRect() const
const QRectF& visibleSceneRect() const
{
return m_visibleSceneRect;
}
inline qreal timelineStep() const
qreal timelineStep() const
{
return m_timelineStep;
}
inline qreal time2position(const profiler::timestamp_t& _time) const
qreal time2position(const profiler::timestamp_t& _time) const
{
return PROF_MICROSECONDS(qreal(_time - m_beginTime));
//return PROF_MILLISECONDS(qreal(_time - m_beginTime));
}
inline ::profiler::timestamp_t position2time(qreal _pos) const
::profiler::timestamp_t position2time(qreal _pos) const
{
return PROF_FROM_MICROSECONDS(_pos);
//return PROF_FROM_MILLISECONDS(_pos);
@ -272,8 +279,8 @@ class EasyThreadNamesWidget : public QGraphicsView
private:
typedef QGraphicsView Parent;
typedef EasyThreadNamesWidget This;
using Parent = QGraphicsView;
using This = EasyThreadNamesWidget;
QTimer m_idleTimer; ///<
uint64_t m_idleTime; ///<
@ -285,7 +292,7 @@ private:
public:
explicit EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent = nullptr);
virtual ~EasyThreadNamesWidget();
~EasyThreadNamesWidget() override;
void mousePressEvent(QMouseEvent* _event) override;
void mouseDoubleClickEvent(QMouseEvent* _event) override;
@ -325,6 +332,7 @@ class EasyGraphicsViewWidget : public QWidget
private:
class QSplitter* m_splitter;
EasyGraphicsScrollbar* m_scrollbar;
EasyGraphicsView* m_view;
EasyThreadNamesWidget* m_threadNamesWidget;
@ -332,11 +340,14 @@ private:
public:
explicit EasyGraphicsViewWidget(QWidget* _parent = nullptr);
virtual ~EasyGraphicsViewWidget();
~EasyGraphicsViewWidget() override;
EasyGraphicsView* view();
void clear();
void save(class QSettings& settings);
void restore(class QSettings& settings);
private:
void initWidget();

View File

@ -128,6 +128,10 @@ void set_max(T& _value) {
//////////////////////////////////////////////////////////////////////////
inline EASY_CONSTEXPR_FCN QRgb alpha(::profiler::color_t _color) {
return (_color & 0xff000000) >> 24;
}
inline EASY_CONSTEXPR_FCN QRgb toRgb(uint32_t _red, uint32_t _green, uint32_t _blue) {
return (_red << 16) + (_green << 8) + _blue;
}
@ -136,6 +140,13 @@ inline EASY_CONSTEXPR_FCN QRgb fromProfilerRgb(uint32_t _red, uint32_t _green, u
return _red == 0 && _green == 0 && _blue == 0 ? ::profiler::colors::Default : toRgb(_red, _green, _blue) | 0x00141414;
}
inline QRgb darken(::profiler::color_t _color, float _part) {
const uint32_t r = (_color & 0x00ff0000) >> 16;
const uint32_t g = (_color & 0x0000ff00) >> 8;
const uint32_t b = _color & 0x000000ff;
return (_color & 0xff000000) | toRgb(r - static_cast<uint32_t>(r * _part), g - static_cast<uint32_t>(g * _part), b - static_cast<uint32_t>(b * _part));
}
EASY_FORCE_INLINE EASY_CONSTEXPR_FCN qreal colorSum(::profiler::color_t _color) {
return 255. - (((_color & 0x00ff0000) >> 16) * 0.299 + ((_color & 0x0000ff00) >> 8) * 0.587 + (_color & 0x000000ff) * 0.114);
}

View File

@ -165,6 +165,20 @@ enum TimeUnits : int8_t
//////////////////////////////////////////////////////////////////////////
class BoolFlagGuard EASY_FINAL
{
bool& m_ref;
bool m_restore;
public:
explicit BoolFlagGuard(bool& flag) : m_ref(flag), m_restore(!flag) {}
explicit BoolFlagGuard(bool& flag, bool value) : m_ref(flag), m_restore(!value) { m_ref = value; }
~BoolFlagGuard() { restore(); }
void restore() { m_ref = m_restore; }
};
} // END of namespace profiler_gui.
template <typename ... Args>

View File

@ -67,6 +67,7 @@
#include <QToolBar>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QSplitter>
#include <QVariant>
#include <QTimer>
#include <thread>
@ -765,14 +766,20 @@ int EasyDescTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags)
//////////////////////////////////////////////////////////////////////////
EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent)
, m_splitter(new QSplitter(Qt::Vertical, this))
, m_tree(new EasyDescTreeWidget(this))
, m_values(new EasyArbitraryValuesWidget(this))
, m_values(new ArbitraryValuesWidget(this))
, m_searchBox(new QLineEdit(this))
, m_foundNumber(new QLabel("Found 0 matches", this))
, m_searchButton(nullptr)
, m_bCaseSensitiveSearch(false)
{
loadSettings();
m_splitter->setHandleWidth(1);
m_splitter->setContentsMargins(0, 0, 0, 0);
m_splitter->addWidget(m_tree);
m_splitter->addWidget(m_values);
m_splitter->setStretchFactor(0, 1);
m_splitter->setStretchFactor(1, 1);
m_searchBox->setFixedWidth(300);
m_searchBox->setContentsMargins(5, 0, 0, 0);
@ -812,6 +819,7 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent)
a->setChecked(m_bCaseSensitiveSearch);
connect(a, &QAction::triggered, [this](bool _checked){ m_bCaseSensitiveSearch = _checked; });
menu->addAction(a);
QAction* caseSensitiveSwitch = a;
menu->addSeparator();
auto headerItem = m_tree->headerItem();
@ -845,11 +853,13 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent)
auto lay = new QVBoxLayout(this);
lay->setContentsMargins(1, 1, 1, 1);
lay->addLayout(searchbox);
lay->addWidget(m_tree);
lay->addWidget(m_values);
lay->addWidget(m_splitter);
connect(m_searchBox, &QLineEdit::returnPressed, this, &This::onSeachBoxReturnPressed);
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::connectionChanged, refreshButton, &QAction::setEnabled);
loadSettings();
caseSensitiveSwitch->setChecked(m_bCaseSensitiveSearch);
}
EasyDescWidget::~EasyDescWidget()
@ -866,6 +876,14 @@ void EasyDescWidget::loadSettings()
if (!val.isNull())
m_bCaseSensitiveSearch = val.toBool();
auto geometry = settings.value("vsplitter/geometry").toByteArray();
if (!geometry.isEmpty())
m_splitter->restoreGeometry(geometry);
auto state = settings.value("vsplitter/state").toByteArray();
if (!state.isEmpty())
m_splitter->restoreState(state);
settings.endGroup();
}
@ -874,6 +892,8 @@ void EasyDescWidget::saveSettings()
QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
settings.beginGroup("EasyDescWidget");
settings.setValue("case_sensitive", m_bCaseSensitiveSearch);
settings.setValue("vsplitter/geometry", m_splitter->saveGeometry());
settings.setValue("vsplitter/state", m_splitter->saveState());
settings.endGroup();
}

View File

@ -90,7 +90,7 @@ private:
public:
explicit EasyDescWidgetItem(::profiler::block_id_t _desc, Parent* _parent = nullptr);
virtual ~EasyDescWidgetItem();
~EasyDescWidgetItem() override;
bool operator < (const Parent& _other) const override;
QVariant data(int _column, int _role) const override;
@ -99,12 +99,12 @@ public:
// Public inline methods
inline ::profiler::block_id_t desc() const
::profiler::block_id_t desc() const
{
return m_desc;
}
inline void setType(Type _type)
void setType(Type _type)
{
m_type = _type;
}
@ -117,12 +117,12 @@ class EasyDescTreeWidget : public QTreeWidget
{
Q_OBJECT
typedef QTreeWidget Parent;
typedef EasyDescTreeWidget This;
using Parent = QTreeWidget;
using This = EasyDescTreeWidget;
typedef ::std::vector<EasyDescWidgetItem*> Items;
typedef ::std::vector<QTreeWidgetItem*> TreeItems;
typedef ::std::unordered_set<::std::string> ExpandedFiles;
using Items = ::std::vector<EasyDescWidgetItem*>;
using TreeItems = ::std::vector<QTreeWidgetItem*>;
using ExpandedFiles = ::std::unordered_set<::std::string>;
protected:
@ -140,7 +140,7 @@ public:
// Public virtual methods
explicit EasyDescTreeWidget(QWidget* _parent = nullptr);
virtual ~EasyDescTreeWidget();
~EasyDescTreeWidget() override;
void contextMenuEvent(QContextMenuEvent* _event) override;
public:
@ -183,13 +183,14 @@ class EasyDescWidget : public QWidget
{
Q_OBJECT
typedef QWidget Parent;
typedef EasyDescWidget This;
using Parent = QWidget;
using This = EasyDescWidget;
private:
class QSplitter* m_splitter;
EasyDescTreeWidget* m_tree;
class EasyArbitraryValuesWidget* m_values;
class ArbitraryValuesWidget* m_values;
class QLineEdit* m_searchBox;
class QLabel* m_foundNumber;
class QAction* m_searchButton;
@ -200,7 +201,7 @@ public:
// Public virtual methods
explicit EasyDescWidget(QWidget* _parent = nullptr);
virtual ~EasyDescWidget();
~EasyDescWidget() override;
void keyPressEvent(QKeyEvent* _event) override;
void contextMenuEvent(QContextMenuEvent* _event) override;

View File

@ -772,44 +772,12 @@ void GraphicsHistogramItem::pickFrameTime(qreal _y) const
//////////////////////////////////////////////////////////////////////////
void GraphicsHistogramItem::onValueChanged()
{
const auto widget = static_cast<const EasyGraphicsScrollbar*>(scene()->parent());
if (!widget->bindMode())
return;
m_boundaryTimer.stop();
const auto sliderWidth_inv = 1.0 / widget->sliderWidth();
const auto k = widget->range() * sliderWidth_inv;
const auto deltaScale = m_imageScaleUpdate < k ? (k / m_imageScaleUpdate) : (m_imageScaleUpdate / k);
if (deltaScale > 4)
{
updateImage();
return;
}
const auto deltaOffset = (widget->value() - m_imageOriginUpdate) * sliderWidth_inv;
if (deltaOffset < 1.5 || deltaOffset > 4.5)
{
updateImage();
return;
}
m_boundaryTimer.start();
}
//////////////////////////////////////////////////////////////////////////
void GraphicsHistogramItem::onModeChanged()
{
if (!isImageUpdatePermitted())
return;
const auto widget = static_cast<const EasyGraphicsScrollbar*>(scene()->parent());
if (!widget->bindMode() && EASY_GLOBALS.auto_adjust_histogram_height)
{
m_topValue = m_maxValue;
@ -827,7 +795,7 @@ bool GraphicsHistogramItem::updateImage()
if (!Parent::updateImage())
return false;
const auto widget = static_cast<const EasyGraphicsScrollbar*>(scene()->parent());
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();
@ -1151,62 +1119,21 @@ void GraphicsHistogramItem::updateImageAsync(QRectF _boundingRect, HistRegime _r
//////////////////////////////////////////////////////////////////////////
EasyGraphicsScrollbar::EasyGraphicsScrollbar(bool _fixedHeight, int _height, QWidget* _parent)
EasyGraphicsScrollbar::EasyGraphicsScrollbar(int _initialHeight, QWidget* _parent)
: Parent(_parent)
, m_minimumValue(0)
, m_maximumValue(500)
, m_value(10)
, m_windowScale(1)
, m_mouseButtons(Qt::NoButton)
, m_slider(nullptr)
, m_selectionIndicator(nullptr)
, m_histogramItem(nullptr)
, m_fontHeight(0)
, m_bScrolling(false)
, m_bBindMode(false)
, m_bLocked(false)
{
setCacheMode(QGraphicsView::CacheNone);
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
//setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setContentsMargins(0, 0, 0, 0);
setScene(new QGraphicsScene(this));
m_fontHeight = QFontMetrics(font()).height();
const int sceneHeight = _height - 2;
const int sceneHeight = _initialHeight - 2;
scene()->setSceneRect(0, -(sceneHeight >> 1), 500, sceneHeight);
if (_fixedHeight)
setFixedHeight(_height);
m_histogramItem = new GraphicsHistogramItem();
m_imageItem = m_histogramItem;
scene()->addItem(m_histogramItem);
m_histogramItem->setPos(0, 0);
m_histogramItem->setBoundingRect(0, scene()->sceneRect().top() + margin(), scene()->width(), sceneHeight - margins() - 1);
m_histogramItem->hide();
m_selectionIndicator = new GraphicsSliderItem(6, false);
scene()->addItem(m_selectionIndicator);
m_selectionIndicator->setPos(0, 0);
m_selectionIndicator->setColor(0x40000000 | profiler_gui::CHRONOMETER_COLOR.rgba());
m_selectionIndicator->hide();
m_slider = new GraphicsSliderItem(6, true);
scene()->addItem(m_slider);
m_slider->setPos(0, 0);
m_slider->setColor(0x40c0c0c0);
m_slider->hide();
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::expectedFrameTimeChanged, [this]()
{
if (m_histogramItem->isVisible())
@ -1231,7 +1158,13 @@ EasyGraphicsScrollbar::EasyGraphicsScrollbar(bool _fixedHeight, int _height, QWi
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged);
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged);
centerOn(0, 0);
if (!EASY_GLOBALS.scene.empty)
{
setRange(EASY_GLOBALS.scene.left, EASY_GLOBALS.scene.right);
setSliderWidth(EASY_GLOBALS.scene.window);
setValue(EASY_GLOBALS.scene.offset);
m_slider->show();
}
}
EasyGraphicsScrollbar::~EasyGraphicsScrollbar()

View File

@ -112,7 +112,6 @@ public:
bool decreaseBottomValue() override;
void onModeChanged() override;
void onValueChanged() override;
public:
@ -156,7 +155,7 @@ private:
public:
explicit EasyGraphicsScrollbar(bool _fixedHeight, int _height, QWidget* _parent = nullptr);
explicit EasyGraphicsScrollbar(int _initialHeight, QWidget* _parent = nullptr);
~EasyGraphicsScrollbar() override;
void clear() override;

View File

@ -75,8 +75,6 @@ namespace profiler_gui {
, chronometer_font(::profiler_gui::EFont("DejaVu Sans", 16, QFont::Bold))
, items_font(::profiler_gui::EFont("DejaVu Sans", 10, QFont::Medium))
, selected_item_font(::profiler_gui::EFont("DejaVu Sans", 10, QFont::Medium))
, scene_left(0)
, scene_right(100)
, begin_time(0)
, selected_thread(0U)
, selected_block(::profiler_gui::numeric_max<decltype(selected_block)>())
@ -109,6 +107,7 @@ namespace profiler_gui {
, highlight_blocks_with_same_id(true)
, selecting_block_changes_thread(true)
, auto_adjust_histogram_height(true)
, auto_adjust_chart_height(true)
, display_only_frames_on_histogram(false)
, bind_scene_and_tree_expand_status(true)
{

View File

@ -162,6 +162,15 @@ namespace profiler_gui {
//////////////////////////////////////////////////////////////////////////
struct SceneData Q_DECL_FINAL
{
qreal left = 0;
qreal right = 100;
qreal window = 100;
qreal offset = 0;
bool empty = true;
};
struct EasyGlobals Q_DECL_FINAL
{
static EasyGlobals& instance();
@ -177,9 +186,8 @@ namespace profiler_gui {
QFont items_font; ///< Font for easy_graphics_item
QFont selected_item_font; ///< Font for easy_graphics_item
double scene_left; ///< Graphics scene left boundary
double scene_right; ///< Graphics scene right boundary
::profiler::timestamp_t begin_time; ///<
SceneData scene; ///<
::profiler::timestamp_t begin_time; ///<
::profiler::thread_id_t selected_thread; ///< Current selected thread id
::profiler::block_index_t selected_block; ///< Current selected profiler block index
::profiler::block_id_t selected_block_id; ///< Current selected profiler block id
@ -211,6 +219,7 @@ namespace profiler_gui {
bool highlight_blocks_with_same_id; ///< Highlight all blocks with same id on diagram
bool selecting_block_changes_thread; ///< If true then current selected thread will change every time you select block
bool auto_adjust_histogram_height; ///< Automatically adjust histogram height to the visible region
bool auto_adjust_chart_height; ///< Automatically adjust arbitrary value chart height to the visible region
bool display_only_frames_on_histogram; ///< Display only top-level blocks on histogram when drawing histogram by block id
bool bind_scene_and_tree_expand_status; /** \brief If true then items on graphics scene and in the tree (blocks hierarchy) are binded on each other
so expanding/collapsing items on scene also expands/collapse items in the tree. */

View File

@ -67,7 +67,7 @@ namespace profiler_gui {
public:
EasyGlobalSignals();
virtual ~EasyGlobalSignals();
~EasyGlobalSignals() Q_DECL_OVERRIDE;
signals:
@ -80,13 +80,23 @@ namespace profiler_gui {
void blocksRefreshRequired(bool);
void expectedFrameTimeChanged();
void autoAdjustHistogramChanged();
void autoAdjustChartChanged();
void displayOnlyFramesOnHistogramChanged();
void hierarchyFlagChanged(bool);
void threadNameDecorationChanged();
void hexThreadIdChanged();
void refreshRequired();
void blocksTreeModeChanged();
void sceneSizeChanged();
void sceneCleared();
void sceneSizeChanged(qreal left, qreal right);
void sceneVisibleRegionSizeChanged(qreal width);
void sceneVisibleRegionPosChanged(qreal pos);
void lockCharts();
void unlockCharts();
void chartWheeled(qreal pos, int delta);
void chartSliderChanged(qreal pos);
}; // END of class EasyGlobalSignals.

View File

@ -71,14 +71,10 @@ bool GraphicsImageItem::updateImage()
return true;
}
void GraphicsImageItem::onValueChanged()
{
const auto widget = qobject_cast<const GraphicsSliderArea*>(scene()->parent());
if (widget == nullptr)
return;
if (!widget->bindMode())
if (widget == nullptr || !widget->bindMode())
return;
m_boundaryTimer.stop();

View File

@ -55,7 +55,6 @@ public:
virtual bool updateImage();
virtual void onModeChanged();
virtual void onValueChanged();
protected:
@ -63,6 +62,7 @@ protected:
public:
void onValueChanged();
void setMousePos(const QPointF& pos);
void setMousePos(qreal x, qreal y);
void setBoundingRect(const QRectF& _rect);

View File

@ -172,11 +172,13 @@ GraphicsSliderArea::GraphicsSliderArea(QWidget* _parent)
, m_mouseButtons(Qt::NoButton)
, m_slider(nullptr)
, m_selectionIndicator(nullptr)
, m_histogramItem(nullptr)
, m_imageItem(nullptr)
, m_fontHeight(0)
, m_bScrolling(false)
, m_bBindMode(false)
, m_bLocked(false)
, m_bUpdatingPos(false)
, m_bEmitChange(true)
{
setCacheMode(QGraphicsView::CacheNone);
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
@ -191,19 +193,13 @@ GraphicsSliderArea::GraphicsSliderArea(QWidget* _parent)
setScene(new QGraphicsScene(this));
m_fontHeight = QFontMetrics(font()).height();
m_fontHeight = QFontMetrics(font()).height() + 1;
EASY_CONSTEXPR int SceneHeight = 500;
scene()->setSceneRect(0, -(SceneHeight >> 1), 500, SceneHeight);
m_histogramItem = new GraphicsHistogramItem();
scene()->addItem(m_histogramItem);
m_histogramItem->setPos(0, 0);
m_histogramItem->setBoundingRect(0, scene()->sceneRect().top() + margin(), scene()->width(), SceneHeight - margins() - 1);
m_histogramItem->hide();
m_selectionIndicator = new GraphicsSliderItem(6, false);
m_selectionIndicator->setZValue(1);
scene()->addItem(m_selectionIndicator);
m_selectionIndicator->setPos(0, 0);
@ -211,16 +207,38 @@ GraphicsSliderArea::GraphicsSliderArea(QWidget* _parent)
m_selectionIndicator->hide();
m_slider = new GraphicsSliderItem(6, true);
m_slider->setZValue(2);
scene()->addItem(m_slider);
m_slider->setPos(0, 0);
m_slider->setColor(0x40c0c0c0);
m_slider->hide();
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged);
connect(&EASY_GLOBALS.events, &profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged);
centerOn(0, 0);
auto globalEvents = &EASY_GLOBALS.events;
connect(globalEvents, &profiler_gui::EasyGlobalSignals::sceneCleared, this, &This::clear);
connect(globalEvents, &profiler_gui::EasyGlobalSignals::sceneVisibleRegionSizeChanged, this, &This::setSliderWidth);
connect(globalEvents, &profiler_gui::EasyGlobalSignals::sceneVisibleRegionPosChanged, this, &This::setValue);
connect(globalEvents, &profiler_gui::EasyGlobalSignals::chartSliderChanged, [this] (qreal pos)
{
if (!m_bUpdatingPos)
{
m_bEmitChange = false;
setValue(pos);
m_bEmitChange = true;
}
});
connect(globalEvents, &profiler_gui::EasyGlobalSignals::sceneSizeChanged, [this] (qreal left, qreal right)
{
setRange(left, right);
m_slider->show();
});
connect(globalEvents, &profiler_gui::EasyGlobalSignals::lockCharts, this, &This::lock);
connect(globalEvents, &profiler_gui::EasyGlobalSignals::unlockCharts, this, &This::unlock);
}
GraphicsSliderArea::~GraphicsSliderArea()
@ -232,10 +250,11 @@ GraphicsSliderArea::~GraphicsSliderArea()
void GraphicsSliderArea::clear()
{
m_selectionIndicator->hide();
setRange(0, 100);
setSliderWidth(2);
setValue(0);
m_selectionIndicator->hide();
m_slider->hide();
}
//////////////////////////////////////////////////////////////////////////
@ -299,10 +318,18 @@ int GraphicsSliderArea::margins() const
void GraphicsSliderArea::setValue(qreal _value)
{
using estd::clamp;
m_value = clamp(m_minimumValue, _value, std::max(m_minimumValue, m_maximumValue - m_slider->width()));
if (m_bUpdatingPos)
return;
const profiler_gui::BoolFlagGuard guard(m_bUpdatingPos, true);
m_value = estd::clamp(m_minimumValue, _value, std::max(m_minimumValue, m_maximumValue - m_slider->width()));
m_slider->setX(m_value + m_slider->halfwidth());
emit valueChanged(m_value);
if (m_bEmitChange)
{
emit EASY_GLOBALS.events.chartSliderChanged(m_value);
}
if (m_imageItem->isVisible())
m_imageItem->onValueChanged();
@ -324,8 +351,6 @@ void GraphicsSliderArea::setRange(qreal _minValue, qreal _maxValue)
m_imageItem->cancelImageUpdate();
m_imageItem->setBoundingRect(_minValue, histogramRect.top(), range, histogramRect.height());
emit rangeChanged();
setValue(_minValue + oldValue * range);
onWindowWidthChange(width());
@ -432,7 +457,7 @@ void GraphicsSliderArea::mouseMoveEvent(QMouseEvent* _event)
}
}
m_imageItem->setMousePos(mapToScene(pos));
m_imageItem->setMousePos(pos.x(), mapToScene(pos).y());
if (m_imageItem->isVisible())
scene()->update();
}
@ -475,28 +500,52 @@ void GraphicsSliderArea::wheelEvent(QWheelEvent* _event)
{
const auto w = m_slider->halfwidth() * (_event->delta() < 0 ? profiler_gui::SCALING_COEFFICIENT : profiler_gui::SCALING_COEFFICIENT_INV);
setValue(mapToScene(_event->pos()).x() - m_minimumValue - w);
emit wheeled(w * m_windowScale, _event->delta());
emit EASY_GLOBALS.events.chartWheeled(w * m_windowScale, _event->delta());
}
else
{
const auto x = (mapToScene(_event->pos()).x() - m_minimumValue) * m_windowScale;
emit wheeled(x, _event->delta());
emit EASY_GLOBALS.events.chartWheeled(x, _event->delta());
}
}
void GraphicsSliderArea::resizeEvent(QResizeEvent* _event)
{
const int h = _event->size().height();
if (h == 0)
{
if (m_imageItem->isVisible())
m_imageItem->cancelImageUpdate();
onWindowWidthChange(_event->size().width());
return;
}
if (_event->oldSize().height() != h)
{
auto rect = scene()->sceneRect();
const int sceneHeight = h - 2;
scene()->setSceneRect(0, -(sceneHeight >> 1), 500, sceneHeight);
const int top = -(sceneHeight >> 1);
scene()->setSceneRect(rect.left(), top, rect.width(), sceneHeight);
const auto br = m_imageItem->boundingRect();
m_imageItem->setBoundingRect(br.left(), scene()->sceneRect().top() + margin(), br.width(), sceneHeight - margins() - 1);
m_imageItem->setBoundingRect(br.left(), top + margin(), br.width(), sceneHeight - margins() - 1);
rect = m_slider->rect();
m_slider->setRect(rect.left(), top, rect.width(), sceneHeight);
if (m_selectionIndicator->isVisible())
{
rect = m_selectionIndicator->rect();
m_selectionIndicator->setRect(rect.left(), top, rect.width(), sceneHeight);
}
}
onWindowWidthChange(_event->size().width());
if (m_imageItem->isVisible())
m_imageItem->updateImage();
}

View File

@ -101,12 +101,13 @@ protected:
Qt::MouseButtons m_mouseButtons;
GraphicsSliderItem* m_slider;
GraphicsSliderItem* m_selectionIndicator;
GraphicsHistogramItem* m_histogramItem;
GraphicsImageItem* m_imageItem;
int m_fontHeight;
bool m_bScrolling;
bool m_bBindMode;
bool m_bLocked;
bool m_bUpdatingPos;
bool m_bEmitChange;
public:
@ -121,6 +122,7 @@ public:
void wheelEvent(QWheelEvent* _event) override;
void resizeEvent(QResizeEvent* _event) override;
void dragEnterEvent(QDragEnterEvent*) override {}
void contextMenuEvent(QContextMenuEvent*) override {}
virtual void clear();
@ -148,19 +150,10 @@ public:
void showSelectionIndicator();
void hideSelectionIndicator();
inline void lock() {
m_bLocked = true;
}
public slots:
inline void unlock() {
m_bLocked = false;
}
signals:
void rangeChanged();
void valueChanged(qreal _value);
void wheeled(qreal _mouseX, int _wheelDelta);
void lock() { m_bLocked = true; }
void unlock() { m_bLocked = false; }
protected slots:

View File

@ -215,6 +215,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_theme("default"), m_lastAddress("
m_graphicsView->setAllowedAreas(Qt::AllDockWidgetAreas);
auto graphicsView = new EasyGraphicsViewWidget(this);
graphicsView->setObjectName("ProfilerGUI_Diagram_GraphicsView");
m_graphicsView->setWidget(graphicsView);
m_treeWidget = new EasyDockWidget("Hierarchy", this);
@ -449,6 +450,16 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_theme("default"), m_lastAddress("
emit EASY_GLOBALS.events.autoAdjustHistogramChanged();
});
action = submenu->addAction("Automatically adjust chart height");
action->setToolTip("Same as similar option for histogram\nbut used for arbitrary values charts.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.auto_adjust_chart_height);
connect(action, &QAction::triggered, [](bool _checked)
{
EASY_GLOBALS.auto_adjust_chart_height = _checked;
emit EASY_GLOBALS.events.autoAdjustChartChanged();
});
action = submenu->addAction("Use decorated thread names");
action->setToolTip("Add \'Thread\' word into thread name if there is no one already.\nExamples: \'Render\' will change to \'Render Thread\'\n\'WorkerThread\' will not change.");
action->setCheckable(true);
@ -1236,7 +1247,6 @@ void EasyMainWindow::onEditBlocksClicked(bool)
m_descTreeDialog = new QDialog();
m_descTreeDialog->setAttribute(Qt::WA_DeleteOnClose, true);
m_descTreeDialog->setWindowTitle(EASY_DEFAULT_WINDOW_TITLE);
m_descTreeDialog->resize(800, 600);
connect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose);
auto l = new QVBoxLayout(m_descTreeDialog);
@ -1379,6 +1389,10 @@ void EasyMainWindow::loadSettings()
if (!flag.isNull())
EASY_GLOBALS.auto_adjust_histogram_height = flag.toBool();
flag = settings.value("auto_adjust_chart_height");
if (!flag.isNull())
EASY_GLOBALS.auto_adjust_chart_height = flag.toBool();
flag = settings.value("display_only_frames_on_histogram");
if (!flag.isNull())
EASY_GLOBALS.display_only_frames_on_histogram = flag.toBool();
@ -1434,6 +1448,16 @@ void EasyMainWindow::loadGeometry()
if (!geometry.isEmpty())
restoreGeometry(geometry);
geometry = settings.value("fpsGeometry").toByteArray();
if (!geometry.isEmpty())
m_fpsViewer->restoreGeometry(geometry);
geometry = settings.value("diagramGeometry").toByteArray();
if (!geometry.isEmpty())
m_graphicsView->restoreGeometry(geometry);
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->restore(settings);
auto state = settings.value("windowState").toByteArray();
if (!state.isEmpty())
restoreState(state);
@ -1447,7 +1471,12 @@ void EasyMainWindow::saveSettingsAndGeometry()
settings.beginGroup("main");
settings.setValue("geometry", this->saveGeometry());
settings.setValue("fpsGeometry", m_fpsViewer->saveGeometry());
settings.setValue("diagramGeometry", m_graphicsView->saveGeometry());
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->save(settings);
settings.setValue("windowState", this->saveState());
settings.setValue("last_files", m_lastFiles);
settings.setValue("ip_address", m_lastAddress);
settings.setValue("port", (quint32)m_lastPort);
@ -1470,6 +1499,7 @@ void EasyMainWindow::saveSettingsAndGeometry()
settings.setValue("selecting_block_changes_thread", EASY_GLOBALS.selecting_block_changes_thread);
settings.setValue("enable_event_indicators", EASY_GLOBALS.enable_event_markers);
settings.setValue("auto_adjust_histogram_height", EASY_GLOBALS.auto_adjust_histogram_height);
settings.setValue("auto_adjust_chart_height", EASY_GLOBALS.auto_adjust_chart_height);
settings.setValue("display_only_frames_on_histogram", EASY_GLOBALS.display_only_frames_on_histogram);
settings.setValue("use_decorated_thread_name", EASY_GLOBALS.use_decorated_thread_name);
settings.setValue("hex_thread_id", EASY_GLOBALS.hex_thread_id);

View File

@ -30,6 +30,12 @@ QToolTip {
QGraphicsView {
border: 1px solid #cccccc; }
QSplitter::handle:hover {
background-color: rgba(244, 67, 54, 0.5); }
QSplitter::handle:pressed {
background-color: #f44336; }
/* ****************************************************************************************************************** */
QLineEdit, QComboBox, QSpinBox {
height: 24px;

View File

@ -55,6 +55,14 @@ QGraphicsView {
border: 1px solid $BorderColor;
}
QSplitter::handle:hover {
background-color: rgb_a($MainColor, 0.5);
}
QSplitter::handle:pressed {
background-color: $MainColor;
}
/* ****************************************************************************************************************** */
QLineEdit, QComboBox, QSpinBox {
height: $DefaultHeight;