mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-26 16:11:02 +08:00
(GraphicsView) Fixed test();
(GraphicsView) Added a lot of comments; (GraphicsView) Changed base unit from milliseconds to microseconds; (GraphicsScrollbar) Fixed fluttering on very small scale; (GraphicsView)+(TreeWidget) Chronometer item behavior: if selection made from left to right - selecting all items by intersection with rectangle; if selection made from right to left - selecting all items which are strictly inside selection rectangle.
This commit is contained in:
parent
7bf8d871c1
commit
b0f6b3f268
@ -39,14 +39,21 @@
|
||||
|
||||
const qreal SCALING_COEFFICIENT = 1.25;
|
||||
const qreal SCALING_COEFFICIENT_INV = 1.0 / SCALING_COEFFICIENT;
|
||||
const qreal MIN_SCALE = pow(SCALING_COEFFICIENT_INV, 70);
|
||||
const qreal MAX_SCALE = pow(SCALING_COEFFICIENT, 30); // ~800
|
||||
const qreal BASE_SCALE = pow(SCALING_COEFFICIENT_INV, 25); // ~0.003
|
||||
|
||||
const unsigned short GRAPHICS_ROW_SIZE = 16;
|
||||
const unsigned short GRAPHICS_ROW_SIZE_FULL = GRAPHICS_ROW_SIZE + 2;
|
||||
const unsigned short ROW_SPACING = 4;
|
||||
|
||||
const QRgb BORDERS_COLOR = 0x00a07050;
|
||||
const QRgb BACKGROUND_1 = 0x00f8f8f8;
|
||||
const QRgb BACKGROUND_2 = 0x00ffffff;// 0x00e0e0e0;
|
||||
const QRgb BACKGROUND_1 = 0x00d8d8d8;
|
||||
const QRgb BACKGROUND_2 = 0x00ffffff;
|
||||
const QColor CHRONOMETER_COLOR = QColor(64, 64, 64, 64);
|
||||
const QRgb CHRONOMETER_TEXT_COLOR = 0xff302010;
|
||||
|
||||
const int TEST_PROGRESSION_BASE = 4;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -56,6 +63,15 @@ auto const clamp = [](qreal _minValue, qreal _value, qreal _maxValue) { return _
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <int N, class T>
|
||||
inline T logn(T _value)
|
||||
{
|
||||
static const double div = 1.0 / log2((double)N);
|
||||
return log2(_value) * div;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
QRgb BG1();
|
||||
QRgb BG2();
|
||||
QRgb (*GetBackgroundColor)() = BG1;
|
||||
@ -171,7 +187,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
m_levelsIndexes[i] = -1;
|
||||
|
||||
|
||||
// Search for first visible top item
|
||||
// Search for first visible top-level item
|
||||
auto& level0 = m_levels[0];
|
||||
auto first = ::std::lower_bound(level0.begin(), level0.end(), sceneLeft, [](const ProfBlockItem& _item, qreal _value)
|
||||
{
|
||||
@ -203,7 +219,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
// (it seems there is a bug in Qt5.6 when drawText called for big coordinates,
|
||||
// drawRect at the same time called for actually same coordinates
|
||||
// works fine without using this additional shifting)
|
||||
const auto dx = level0[m_levelsIndexes[0]].left() * currentScale;
|
||||
auto dx = level0[m_levelsIndexes[0]].left() * currentScale;
|
||||
|
||||
// Shifting coordinates to current screen offset
|
||||
_painter->setTransform(QTransform::fromTranslate(dx - offset * currentScale, -y()), true);
|
||||
@ -229,6 +245,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
|
||||
if (item.right() < sceneLeft || state == -1 || (l == 0 && (item.top() > visibleSceneRect.bottom() || (item.top() + item.totalHeight) < visibleSceneRect.top())))
|
||||
{
|
||||
// This item is not visible
|
||||
++m_levelsIndexes[l];
|
||||
continue;
|
||||
}
|
||||
@ -239,6 +256,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
// Items which width is less than 20 will be painted as big rectangles which are hiding it's children
|
||||
if (item.left() > sceneRight)
|
||||
{
|
||||
// This is first totally invisible item. No need to check other items.
|
||||
break;
|
||||
}
|
||||
|
||||
@ -256,6 +274,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
|
||||
if (previousColor != item.color)
|
||||
{
|
||||
// Set background color brush for rectangle
|
||||
previousColor = item.color;
|
||||
brush.setColor(previousColor);
|
||||
_painter->setBrush(brush);
|
||||
@ -263,6 +282,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
|
||||
if (w < 3)
|
||||
{
|
||||
// Do not paint borders for very narrow items
|
||||
if (previousPenStyle != Qt::NoPen)
|
||||
{
|
||||
previousPenStyle = Qt::NoPen;
|
||||
@ -271,15 +291,18 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
}
|
||||
else if (previousPenStyle != Qt::SolidLine)
|
||||
{
|
||||
// Restore pen for item which is wide enough to paint borders
|
||||
previousPenStyle = Qt::SolidLine;
|
||||
_painter->setPen(BORDERS_COLOR);
|
||||
}
|
||||
|
||||
// Draw rectangle
|
||||
rect.setRect(x, item.top(), w, item.totalHeight);
|
||||
_painter->drawRect(rect);
|
||||
|
||||
if (next_level < levelsNumber && item.children_begin != -1)
|
||||
{
|
||||
// Mark that we would not paint children of current item
|
||||
m_levels[next_level][item.children_begin].state = -1;
|
||||
}
|
||||
|
||||
@ -290,14 +313,17 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
{
|
||||
if (m_levelsIndexes[next_level] == -1)
|
||||
{
|
||||
// Mark first potentially visible child item on next sublevel
|
||||
m_levelsIndexes[next_level] = item.children_begin;
|
||||
}
|
||||
|
||||
// Mark children items that we want to draw them
|
||||
m_levels[next_level][item.children_begin].state = 1;
|
||||
}
|
||||
|
||||
if (item.left() > sceneRight)
|
||||
{
|
||||
// This is first totally invisible item. No need to check other items.
|
||||
break;
|
||||
}
|
||||
|
||||
@ -310,11 +336,14 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
//} else
|
||||
if (previousColor != item.color)
|
||||
{
|
||||
// Set background color brush for rectangle
|
||||
previousColor = item.color;
|
||||
brush.setColor(previousColor);
|
||||
_painter->setBrush(brush);
|
||||
}
|
||||
|
||||
// Draw text-----------------------------------
|
||||
// calculating text coordinates
|
||||
const auto x = item.left() * currentScale - dx;
|
||||
rect.setRect(x, item.top(), w, item.height());
|
||||
_painter->drawRect(rect);
|
||||
@ -322,16 +351,20 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
auto xtext = x;
|
||||
if (item.left() < sceneLeft)
|
||||
{
|
||||
// if item left border is out of screen then attach text to the left border of the screen
|
||||
// to ensure text is always visible for items presenting on the screen.
|
||||
w += (item.left() - sceneLeft) * currentScale;
|
||||
xtext = sceneLeft * currentScale - dx;
|
||||
}
|
||||
|
||||
rect.setRect(xtext, item.top(), w, item.height());
|
||||
rect.setRect(xtext + 1, item.top(), w - 1, item.height());
|
||||
|
||||
// text will be painted with inverse color
|
||||
auto textColor = 0x00ffffff - previousColor;
|
||||
if (textColor == previousColor) textColor = 0;
|
||||
_painter->setPen(textColor); // Text is painted with inverse color
|
||||
_painter->setPen(textColor);
|
||||
|
||||
// drawing text
|
||||
if (m_bTest)
|
||||
{
|
||||
char text[128] = {0};
|
||||
@ -343,6 +376,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
_painter->drawText(rect, 0, item.block->node->getBlockName());
|
||||
}
|
||||
|
||||
// restore previous pen color
|
||||
if (previousPenStyle == Qt::NoPen)
|
||||
{
|
||||
_painter->setPen(Qt::NoPen);
|
||||
@ -351,6 +385,7 @@ void ProfGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
{
|
||||
_painter->setPen(BORDERS_COLOR); // restore pen for rectangle painting
|
||||
}
|
||||
// END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,7 +401,7 @@ void ProfGraphicsItem::getBlocks(qreal _left, qreal _right, TreeBlocks& _blocks)
|
||||
// return;
|
||||
//}
|
||||
|
||||
// Search for first top item
|
||||
// Search for first visible top-level item
|
||||
auto& level0 = m_levels[0];
|
||||
auto first = ::std::lower_bound(level0.begin(), level0.end(), _left, [](const ProfBlockItem& _item, qreal _value)
|
||||
{
|
||||
@ -385,17 +420,21 @@ void ProfGraphicsItem::getBlocks(qreal _left, qreal _right, TreeBlocks& _blocks)
|
||||
itemIndex = level0.size() - 1;
|
||||
}
|
||||
|
||||
// Add all visible top-level items into array of visible blocks
|
||||
for (size_t i = itemIndex, end = level0.size(); i < end; ++i)
|
||||
{
|
||||
const auto& item = level0[i];
|
||||
|
||||
if (item.left() > _right)
|
||||
{
|
||||
// First invisible item. No need to check other items.
|
||||
break;
|
||||
}
|
||||
|
||||
if (item.right() < _left)
|
||||
{
|
||||
// This item is not visible yet
|
||||
// This is just to be sure
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -500,50 +539,50 @@ void ProfChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt
|
||||
|
||||
if (m_left > sceneRight || m_right < sceneLeft)
|
||||
{
|
||||
// This item is out of screen
|
||||
return;
|
||||
}
|
||||
|
||||
const auto time = width();
|
||||
QRectF rect(m_left * currentScale, visibleSceneRect.top(), ::std::max(time * currentScale, 1.0), visibleSceneRect.height());
|
||||
const auto selectedInterval = width();
|
||||
QRectF rect((m_left - offset) * currentScale, visibleSceneRect.top(), ::std::max(selectedInterval * currentScale, 1.0), visibleSceneRect.height());
|
||||
|
||||
QString text;
|
||||
if (time < 1e-3)
|
||||
{
|
||||
text = ::std::move(QString("%1 ns").arg(time * 1e6, 0, 'f', 1));
|
||||
}
|
||||
else if (time < 1)
|
||||
{
|
||||
text = ::std::move(QString("%1 us").arg(time * 1e3, 0, 'f', 1));
|
||||
}
|
||||
else if (time < 1e3)
|
||||
{
|
||||
text = ::std::move(QString("%1 ms").arg(time, 0, 'f', 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
text = ::std::move(QString("%1 sec").arg(time * 1e-3, 0, 'f', 1));
|
||||
}
|
||||
QString text; // Displayed text
|
||||
if (selectedInterval < 1.0) // interval in nanoseconds
|
||||
text = ::std::move(QString("%1 ns").arg(selectedInterval * 1e3, 0, 'f', 1));
|
||||
else if (selectedInterval < 1e3) // interval in microseconds
|
||||
text = ::std::move(QString("%1 us").arg(selectedInterval, 0, 'f', 1));
|
||||
else if (selectedInterval < 1e6) // interval in milliseconds
|
||||
text = ::std::move(QString("%1 ms").arg(selectedInterval * 1e-3, 0, 'f', 1));
|
||||
else // interval in seconds
|
||||
text = ::std::move(QString("%1 sec").arg(selectedInterval * 1e-6, 0, 'f', 1));
|
||||
|
||||
const auto textRect = QFontMetrics(m_font).boundingRect(text);
|
||||
const auto textRect = QFontMetrics(m_font).boundingRect(text); // Calculate displayed text boundingRect
|
||||
|
||||
// Paint!--------------------------
|
||||
_painter->save();
|
||||
|
||||
_painter->setTransform(QTransform::fromTranslate(-x() - offset * currentScale, -y()), true);
|
||||
_painter->setBrush(QColor(64, 64, 64, 64));
|
||||
// instead of scrollbar we're using manual offset
|
||||
_painter->setTransform(QTransform::fromTranslate(-x(), -y()), true);
|
||||
|
||||
// draw transparent rectangle
|
||||
_painter->setBrush(CHRONOMETER_COLOR);
|
||||
_painter->setPen(Qt::NoPen);
|
||||
_painter->drawRect(rect);
|
||||
|
||||
_painter->setPen(0xff302010);
|
||||
// draw text
|
||||
_painter->setPen(CHRONOMETER_TEXT_COLOR);
|
||||
_painter->setFont(m_font);
|
||||
|
||||
//if (m_left > sceneLeft && m_right < sceneRight)
|
||||
{
|
||||
if (textRect.width() < rect.width())
|
||||
{
|
||||
// Text will be drawed inside rectangle
|
||||
_painter->drawText(rect, Qt::AlignCenter, text);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Text will be drawed aside of rectangle
|
||||
_painter->drawText(QPointF(rect.right(), rect.top() + rect.height() * 0.5 + textRect.height() * 0.33), text);
|
||||
}
|
||||
}
|
||||
@ -563,6 +602,7 @@ void ProfChronometerItem::paint(QPainter* _painter, const QStyleOptionGraphicsIt
|
||||
//}
|
||||
|
||||
_painter->restore();
|
||||
// END Paint!~~~~~~~~~~~~~~~~~~~~~~
|
||||
}
|
||||
|
||||
void ProfChronometerItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h)
|
||||
@ -608,13 +648,14 @@ ProfGraphicsView::ProfGraphicsView(bool _test)
|
||||
, m_bUpdatingRect(false)
|
||||
, m_bTest(_test)
|
||||
, m_bEmpty(true)
|
||||
, m_bStrictSelection(false)
|
||||
{
|
||||
initMode();
|
||||
setScene(new QGraphicsScene(this));
|
||||
|
||||
if (_test)
|
||||
{
|
||||
test(18000, 40000000, 5);
|
||||
test(18000, 40000000, 2);
|
||||
}
|
||||
|
||||
updateVisibleSceneRect();
|
||||
@ -632,6 +673,7 @@ ProfGraphicsView::ProfGraphicsView(const thread_blocks_tree_t& _blocksTree)
|
||||
, m_bUpdatingRect(false)
|
||||
, m_bTest(false)
|
||||
, m_bEmpty(true)
|
||||
, m_bStrictSelection(false)
|
||||
{
|
||||
initMode();
|
||||
setScene(new QGraphicsScene(this));
|
||||
@ -645,10 +687,10 @@ ProfGraphicsView::~ProfGraphicsView()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void fillChildren(ProfGraphicsItem* _item, int _level, qreal _x, qreal _y, size_t _childrenNumber, size_t& _total_items)
|
||||
void ProfGraphicsView::fillTestChildren(ProfGraphicsItem* _item, const int _maxlevel, int _level, qreal _x, qreal _y, size_t _childrenNumber, size_t& _total_items)
|
||||
{
|
||||
size_t nchildren = _childrenNumber;
|
||||
_childrenNumber >>= 1;
|
||||
_childrenNumber = TEST_PROGRESSION_BASE;
|
||||
|
||||
for (size_t i = 0; i < nchildren; ++i)
|
||||
{
|
||||
@ -657,12 +699,12 @@ void fillChildren(ProfGraphicsItem* _item, int _level, qreal _x, qreal _y, size_
|
||||
b.color = toRgb(30 + rand() % 225, 30 + rand() % 225, 30 + rand() % 225);
|
||||
b.state = 0;
|
||||
|
||||
if (_childrenNumber > 0)
|
||||
if (_level < _maxlevel)
|
||||
{
|
||||
const auto& children = _item->items(_level + 1);
|
||||
b.children_begin = static_cast<unsigned int>(children.size());
|
||||
|
||||
fillChildren(_item, _level + 1, _x, _y + GRAPHICS_ROW_SIZE_FULL, _childrenNumber, _total_items);
|
||||
fillTestChildren(_item, _maxlevel, _level + 1, _x, _y + GRAPHICS_ROW_SIZE_FULL, _childrenNumber, _total_items);
|
||||
|
||||
const auto& last = children.back();
|
||||
b.setRect(_x, _y, last.right() - _x, GRAPHICS_ROW_SIZE);
|
||||
@ -670,7 +712,7 @@ void fillChildren(ProfGraphicsItem* _item, int _level, qreal _x, qreal _y, size_
|
||||
}
|
||||
else
|
||||
{
|
||||
b.setRect(_x, _y, 10 + rand() % 40, GRAPHICS_ROW_SIZE);
|
||||
b.setRect(_x, _y, to_microseconds(10 + rand() % 190), GRAPHICS_ROW_SIZE);
|
||||
b.totalHeight = GRAPHICS_ROW_SIZE;
|
||||
b.children_begin = -1;
|
||||
}
|
||||
@ -680,71 +722,112 @@ void fillChildren(ProfGraphicsItem* _item, int _level, qreal _x, qreal _y, size_
|
||||
}
|
||||
}
|
||||
|
||||
void ProfGraphicsView::test(size_t _frames_number, size_t _total_items_number_estimate, int _depth)
|
||||
void ProfGraphicsView::test(size_t _frames_number, size_t _total_items_number_estimate, int _rows)
|
||||
{
|
||||
static const qreal X_BEGIN = 50;
|
||||
static const qreal Y_BEGIN = 0;
|
||||
|
||||
const auto children_per_frame = _total_items_number_estimate / _frames_number;
|
||||
const size_t first_level_children_count = sqrt(pow(2, _depth - 1)) * pow(children_per_frame, 1.0 / double(_depth)) + 0.5;
|
||||
clearSilent(); // Clear scene
|
||||
|
||||
auto item = new ProfGraphicsItem(true);
|
||||
item->setPos(0, Y_BEGIN);
|
||||
// Calculate items number for first level
|
||||
_rows = ::std::max(1, _rows);
|
||||
const size_t children_per_frame = static_cast<size_t>(0.5 + static_cast<double>(_total_items_number_estimate) / static_cast<double>(_rows * _frames_number));
|
||||
const int max_depth = logn<TEST_PROGRESSION_BASE>(children_per_frame * (TEST_PROGRESSION_BASE - 1) * 0.5 + 1);
|
||||
const size_t first_level_children_count = children_per_frame * (1.0 - TEST_PROGRESSION_BASE) / (1.0 - pow(TEST_PROGRESSION_BASE, max_depth)) + 0.5;
|
||||
|
||||
item->setLevels(_depth + 1);
|
||||
item->reserve(0, _frames_number);
|
||||
::std::vector<ProfGraphicsItem*> thread_items(_rows);
|
||||
for (int i = 0; i < _rows; ++i)
|
||||
{
|
||||
auto item = new ProfGraphicsItem(true);
|
||||
thread_items[i] = item;
|
||||
|
||||
item->setPos(0, Y_BEGIN + i * (max_depth * GRAPHICS_ROW_SIZE_FULL + ROW_SPACING * 5));
|
||||
|
||||
item->setLevels(max_depth + 1);
|
||||
item->reserve(0, _frames_number);
|
||||
}
|
||||
|
||||
// Calculate items number for each sublevel
|
||||
size_t chldrn = first_level_children_count;
|
||||
size_t tot = first_level_children_count;
|
||||
for (int i = 1; i <= _depth; ++i)
|
||||
for (int i = 1; i <= max_depth; ++i)
|
||||
{
|
||||
item->reserve(i, tot * _frames_number);
|
||||
chldrn >>= 1;
|
||||
tot *= chldrn;
|
||||
for (int i = 0; i < _rows; ++i)
|
||||
{
|
||||
auto item = thread_items[i];
|
||||
item->reserve(i, chldrn * _frames_number);
|
||||
}
|
||||
|
||||
chldrn *= TEST_PROGRESSION_BASE;
|
||||
}
|
||||
|
||||
// Create required number of items
|
||||
size_t total_items = 0;
|
||||
qreal x = X_BEGIN;
|
||||
for (unsigned int i = 0; i < _frames_number; ++i)
|
||||
qreal maxX = 0;
|
||||
for (int i = 0; i < _rows; ++i)
|
||||
{
|
||||
size_t j = item->addItem(0);
|
||||
auto& b = item->getItem(0, j);
|
||||
b.color = toRgb(30 + rand() % 225, 30 + rand() % 225, 30 + rand() % 225);
|
||||
b.state = 0;
|
||||
auto item = thread_items[i];
|
||||
qreal x = X_BEGIN, y = item->y();
|
||||
for (unsigned int i = 0; i < _frames_number; ++i)
|
||||
{
|
||||
size_t j = item->addItem(0);
|
||||
auto& b = item->getItem(0, j);
|
||||
b.color = toRgb(30 + rand() % 225, 30 + rand() % 225, 30 + rand() % 225);
|
||||
b.state = 0;
|
||||
|
||||
const auto& children = item->items(1);
|
||||
b.children_begin = static_cast<unsigned int>(children.size());
|
||||
const auto& children = item->items(1);
|
||||
b.children_begin = static_cast<unsigned int>(children.size());
|
||||
|
||||
fillChildren(item, 1, x, Y_BEGIN + GRAPHICS_ROW_SIZE_FULL, first_level_children_count, total_items);
|
||||
fillTestChildren(item, max_depth, 1, x, y + GRAPHICS_ROW_SIZE_FULL, first_level_children_count, total_items);
|
||||
|
||||
const auto& last = children.back();
|
||||
b.setRect(x, Y_BEGIN, last.right() - x, GRAPHICS_ROW_SIZE);
|
||||
b.totalHeight = GRAPHICS_ROW_SIZE_FULL + last.totalHeight;
|
||||
const auto& last = children.back();
|
||||
b.setRect(x, y, last.right() - x, GRAPHICS_ROW_SIZE);
|
||||
b.totalHeight = GRAPHICS_ROW_SIZE_FULL + last.totalHeight;
|
||||
|
||||
x += b.width() + 500;
|
||||
x += b.width() * 1.2;
|
||||
|
||||
++total_items;
|
||||
++total_items;
|
||||
}
|
||||
|
||||
const auto h = item->getItem(0, 0).totalHeight;
|
||||
item->setBoundingRect(0, 0, x, h);
|
||||
if (_rows > 1)
|
||||
{
|
||||
item->setBackgroundColor(GetBackgroundColor());
|
||||
}
|
||||
|
||||
m_items.push_back(item);
|
||||
scene()->addItem(item);
|
||||
|
||||
if (maxX < x)
|
||||
{
|
||||
maxX = x;
|
||||
}
|
||||
}
|
||||
|
||||
const auto h = item->getItem(0, 0).totalHeight;
|
||||
item->setBoundingRect(0, 0, x, h);
|
||||
printf("TOTAL ITEMS = %llu\n", total_items);
|
||||
|
||||
clearSilent();
|
||||
m_items.push_back(item);
|
||||
scene()->addItem(item);
|
||||
scene()->setSceneRect(0, 0, x, Y_BEGIN + h);
|
||||
// Calculate scene rect
|
||||
auto item = thread_items.back();
|
||||
scene()->setSceneRect(0, 0, maxX, item->y() + item->getItem(0, 0).totalHeight);
|
||||
|
||||
// Reset necessary values
|
||||
m_offset = 0;
|
||||
updateVisibleSceneRect();
|
||||
setScrollbar(m_pScrollbar);
|
||||
|
||||
// Create new chronometer item (previous item was destroyed by scene on scene()->clear()).
|
||||
// It will be shown on mouse right button click.
|
||||
m_chronometerItem = new ProfChronometerItem();
|
||||
m_chronometerItem->setZValue(10);
|
||||
m_chronometerItem->setBoundingRect(scene()->sceneRect());
|
||||
m_chronometerItem->hide();
|
||||
scene()->addItem(m_chronometerItem);
|
||||
|
||||
// Set necessary flags
|
||||
m_bTest = true;
|
||||
m_bEmpty = false;
|
||||
|
||||
scaleTo(BASE_SCALE);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -752,15 +835,28 @@ void ProfGraphicsView::test(size_t _frames_number, size_t _total_items_number_es
|
||||
void ProfGraphicsView::clearSilent()
|
||||
{
|
||||
const QSignalBlocker blocker(this), sceneBlocker(scene()); // block all scene signals (otherwise clear() would be extremely slow!)
|
||||
|
||||
GetBackgroundColor = BG1; // reset background color
|
||||
|
||||
// Stop flicking
|
||||
m_flickerTimer.stop();
|
||||
m_flickerSpeed = 0;
|
||||
|
||||
// Clear all items
|
||||
scene()->clear();
|
||||
m_items.clear();
|
||||
m_selectedBlocks.clear();
|
||||
|
||||
m_beginTime = -1; // reset begin time
|
||||
m_scale = 1; // scale back to initial 100% scale
|
||||
m_offset = 0; // scroll back to the beginning of the scene
|
||||
|
||||
// Reset necessary flags
|
||||
m_bTest = false;
|
||||
m_bEmpty = true;
|
||||
emit treeblocksChanged(m_selectedBlocks, m_beginTime);
|
||||
|
||||
// notify ProfTreeWidget that selection was reset
|
||||
emit intervalChanged(m_selectedBlocks, m_beginTime, 0, 0, false);
|
||||
}
|
||||
|
||||
void ProfGraphicsView::setTree(const thread_blocks_tree_t& _blocksTree)
|
||||
@ -811,21 +907,21 @@ void ProfGraphicsView::setTree(const thread_blocks_tree_t& _blocksTree)
|
||||
}
|
||||
|
||||
// Calculating scene rect
|
||||
const qreal endX = time2position(finish + 1000000);
|
||||
const qreal endX = time2position(finish) + 1.0;
|
||||
scene()->setSceneRect(0, 0, endX, y);
|
||||
|
||||
// Painting stub...
|
||||
// Stub! :O
|
||||
for (auto item : m_items)
|
||||
{
|
||||
item->setBoundingRect(0, 0, endX, item->boundingRect().height());
|
||||
}
|
||||
|
||||
// Center view on the beginning of the scene
|
||||
m_offset = 0;
|
||||
updateVisibleSceneRect();
|
||||
setScrollbar(m_pScrollbar);
|
||||
|
||||
// Adding invisible chronometer item (it will be shown on mouse right button click)
|
||||
// Create new chronometer item (previous item was destroyed by scene on scene()->clear()).
|
||||
// It will be shown on mouse right button click.
|
||||
m_chronometerItem = new ProfChronometerItem();
|
||||
m_chronometerItem->setZValue(10);
|
||||
m_chronometerItem->setBoundingRect(scene()->sceneRect());
|
||||
@ -835,10 +931,14 @@ void ProfGraphicsView::setTree(const thread_blocks_tree_t& _blocksTree)
|
||||
// Setting flags
|
||||
m_bTest = false;
|
||||
m_bEmpty = false;
|
||||
|
||||
scaleTo(BASE_SCALE);
|
||||
}
|
||||
|
||||
qreal ProfGraphicsView::setTree(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y, unsigned short _level)
|
||||
{
|
||||
static const qreal MIN_DURATION = 0.25;
|
||||
|
||||
if (_children.empty())
|
||||
{
|
||||
return 0;
|
||||
@ -866,9 +966,9 @@ qreal ProfGraphicsView::setTree(ProfGraphicsItem* _item, const BlocksTree::child
|
||||
xbegin -= dt;
|
||||
}
|
||||
|
||||
if (duration < 0.1)
|
||||
if (duration < MIN_DURATION)
|
||||
{
|
||||
duration = 0.1;
|
||||
duration = MIN_DURATION;
|
||||
}
|
||||
|
||||
auto i = _item->addItem(_level);
|
||||
@ -940,6 +1040,25 @@ void ProfGraphicsView::updateScene()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProfGraphicsView::scaleTo(qreal _scale)
|
||||
{
|
||||
if (m_bEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// have to limit scale because of Qt's QPainter feature: it doesn't draw text
|
||||
// with very big coordinates (but it draw rectangles with the same coordinates good).
|
||||
m_scale = clamp(MIN_SCALE, _scale, MAX_SCALE);
|
||||
updateVisibleSceneRect();
|
||||
|
||||
// Update slider width for scrollbar
|
||||
const auto windowWidth = m_visibleSceneRect.width() / m_scale;
|
||||
m_pScrollbar->setSliderWidth(windowWidth);
|
||||
|
||||
updateScene();
|
||||
}
|
||||
|
||||
void ProfGraphicsView::wheelEvent(QWheelEvent* _event)
|
||||
{
|
||||
if (m_bEmpty)
|
||||
@ -954,9 +1073,9 @@ void ProfGraphicsView::wheelEvent(QWheelEvent* _event)
|
||||
const auto mouseX = mapToScene(_event->pos()).x();
|
||||
const auto mousePosition = m_offset + mouseX / m_scale;
|
||||
|
||||
m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
|
||||
m_scale *= scaleCoeff;
|
||||
m_bUpdatingRect = false;
|
||||
// have to limit scale because of Qt's QPainter feature: it doesn't draw text
|
||||
// with very big coordinates (but it draw rectangles with the same coordinates good).
|
||||
m_scale = clamp(MIN_SCALE, m_scale * scaleCoeff, MAX_SCALE);
|
||||
|
||||
updateVisibleSceneRect(); // Update scene rect
|
||||
|
||||
@ -968,7 +1087,7 @@ void ProfGraphicsView::wheelEvent(QWheelEvent* _event)
|
||||
m_offset = clamp(0., mousePosition - mouseX / m_scale, scene()->width() - windowWidth);
|
||||
|
||||
// Update slider position
|
||||
m_bUpdatingRect = true;
|
||||
m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
|
||||
m_pScrollbar->setValue(m_offset);
|
||||
m_bUpdatingRect = false;
|
||||
|
||||
@ -995,6 +1114,7 @@ void ProfGraphicsView::mousePressEvent(QMouseEvent* _event)
|
||||
|
||||
if (m_mouseButtons & Qt::RightButton)
|
||||
{
|
||||
m_bStrictSelection = false;
|
||||
const auto mouseX = m_offset + mapToScene(_event->pos()).x() / m_scale;
|
||||
m_chronometerItem->setLeftRight(mouseX, mouseX);
|
||||
m_chronometerItem->hide();
|
||||
@ -1048,8 +1168,10 @@ void ProfGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
|
||||
|
||||
if (changedSelection)
|
||||
{
|
||||
emit treeblocksChanged(m_selectedBlocks, m_beginTime);
|
||||
emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_bStrictSelection);
|
||||
}
|
||||
|
||||
m_bStrictSelection = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -1067,13 +1189,29 @@ void ProfGraphicsView::mouseMoveEvent(QMouseEvent* _event)
|
||||
if (m_mouseButtons & Qt::RightButton)
|
||||
{
|
||||
const auto mouseX = m_offset + mapToScene(_event->pos()).x() / m_scale;
|
||||
if (mouseX > m_chronometerItem->left())
|
||||
if (m_bStrictSelection)
|
||||
{
|
||||
m_chronometerItem->setLeftRight(m_chronometerItem->left(), mouseX);
|
||||
if (mouseX > m_chronometerItem->right())
|
||||
{
|
||||
m_bStrictSelection = false;
|
||||
m_chronometerItem->setLeftRight(m_chronometerItem->right(), mouseX);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_chronometerItem->setLeftRight(mouseX, m_chronometerItem->right());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_chronometerItem->setLeftRight(mouseX, m_chronometerItem->right());
|
||||
if (mouseX < m_chronometerItem->left())
|
||||
{
|
||||
m_bStrictSelection = true;
|
||||
m_chronometerItem->setLeftRight(mouseX, m_chronometerItem->left());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_chronometerItem->setLeftRight(m_chronometerItem->left(), mouseX);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_chronometerItem->isVisible() && m_chronometerItem->width() > 1e-6)
|
||||
@ -1204,19 +1342,23 @@ ProfGraphicsViewWidget::ProfGraphicsViewWidget(bool _test)
|
||||
, m_view(new ProfGraphicsView(_test))
|
||||
{
|
||||
auto lay = new QVBoxLayout(this);
|
||||
lay->setContentsMargins(1, 0, 1, 0);
|
||||
lay->addWidget(m_view);
|
||||
lay->setSpacing(1);
|
||||
lay->addWidget(m_scrollbar);
|
||||
setLayout(lay);
|
||||
m_view->setScrollbar(m_scrollbar);
|
||||
}
|
||||
|
||||
ProfGraphicsViewWidget::ProfGraphicsViewWidget(const thread_blocks_tree_t& _blocksTree)
|
||||
: QWidget(false)
|
||||
: QWidget(nullptr)
|
||||
, m_scrollbar(new GraphicsHorizontalScrollbar(nullptr))
|
||||
, m_view(new ProfGraphicsView(_blocksTree))
|
||||
{
|
||||
auto lay = new QVBoxLayout(this);
|
||||
lay->setContentsMargins(1, 0, 1, 0);
|
||||
lay->addWidget(m_view);
|
||||
lay->setSpacing(1);
|
||||
lay->addWidget(m_scrollbar);
|
||||
setLayout(lay);
|
||||
m_view->setScrollbar(m_scrollbar);
|
||||
|
@ -42,15 +42,15 @@
|
||||
#pragma pack(push, 1)
|
||||
struct ProfBlockItem
|
||||
{
|
||||
const BlocksTree* block;
|
||||
qreal x;
|
||||
float w;
|
||||
float y;
|
||||
float h;
|
||||
QRgb color;
|
||||
unsigned int children_begin;
|
||||
unsigned short totalHeight;
|
||||
char state;
|
||||
const BlocksTree* block; ///< Pointer to profiler block
|
||||
qreal x; ///< x coordinate of the item (this is made qreal=double to avoid mistakes on very wide scene)
|
||||
float w; ///< Width of the item
|
||||
float y; ///< y coordinate of the item
|
||||
float h; ///< Height of the item
|
||||
QRgb color; ///< Background color of the item
|
||||
unsigned int children_begin; ///< Index of first child item on the next sublevel
|
||||
unsigned short totalHeight; ///< Total height of the item including heights of all it's children
|
||||
char state; ///< 0 = no change, 1 = paint, -1 = do not paint
|
||||
|
||||
void setRect(qreal _x, float _y, float _w, float _h);
|
||||
qreal left() const;
|
||||
@ -72,14 +72,14 @@ class ProfGraphicsItem : public QGraphicsItem
|
||||
typedef ::std::vector<unsigned int> DrawIndexes;
|
||||
typedef ::std::vector<Children> Sublevels;
|
||||
|
||||
DrawIndexes m_levelsIndexes;
|
||||
Sublevels m_levels;
|
||||
DrawIndexes m_levelsIndexes; ///< Indexes of first item on each level from which we must start painting
|
||||
Sublevels m_levels; ///< Arrays of items for each level
|
||||
|
||||
QRectF m_boundingRect;
|
||||
const BlocksTree* m_pRoot;
|
||||
::profiler::thread_id_t m_thread_id;
|
||||
QRgb m_backgroundColor;
|
||||
const bool m_bTest;
|
||||
QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem)
|
||||
const BlocksTree* m_pRoot; ///< Pointer to the root profiler block (thread block). Used by ProfTreeWidget to restore hierarchy.
|
||||
::profiler::thread_id_t m_thread_id; ///< Thread id to which this item belongs
|
||||
QRgb m_backgroundColor; ///< Background color (to enable AlternateColors behavior like in QTreeWidget)
|
||||
const bool m_bTest; ///< If true then we are running test()
|
||||
|
||||
public:
|
||||
|
||||
@ -88,30 +88,92 @@ public:
|
||||
ProfGraphicsItem(::profiler::thread_id_t _thread_id, const BlocksTree* _root);
|
||||
virtual ~ProfGraphicsItem();
|
||||
|
||||
// Public virtual methods
|
||||
|
||||
QRectF boundingRect() const override;
|
||||
void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override;
|
||||
|
||||
public:
|
||||
|
||||
// Public non-virtual methods
|
||||
|
||||
void setBoundingRect(qreal x, qreal y, qreal w, qreal h);
|
||||
void setBoundingRect(const QRectF& _rect);
|
||||
|
||||
void setBackgroundColor(QRgb _color);
|
||||
|
||||
///< Returns number of levels
|
||||
unsigned short levels() const;
|
||||
|
||||
/** \brief Sets number of levels.
|
||||
|
||||
\note Must be set before doing anything else.
|
||||
|
||||
\param _levels Desired number of levels */
|
||||
void setLevels(unsigned short _levels);
|
||||
|
||||
/** \brief Reserves memory for desired number of items on specified level.
|
||||
|
||||
\param _level Index of the level
|
||||
\param _items Desired number of items on this level */
|
||||
void reserve(unsigned short _level, size_t _items);
|
||||
|
||||
/**\brief Returns reference to the array of items of specified level.
|
||||
|
||||
\param _level Index of the level */
|
||||
const Children& items(unsigned short _level) const;
|
||||
|
||||
/**\brief Returns reference to the item with required index on specified level.
|
||||
|
||||
\param _level Index of the level
|
||||
\param _index Index of required item */
|
||||
const ProfBlockItem& getItem(unsigned short _level, size_t _index) const;
|
||||
|
||||
/**\brief Returns reference to the item with required index on specified level.
|
||||
|
||||
\param _level Index of the level
|
||||
\param _index Index of required item */
|
||||
ProfBlockItem& getItem(unsigned short _level, size_t _index);
|
||||
|
||||
/** \brief Adds new item to required level.
|
||||
|
||||
\param _level Index of the level
|
||||
|
||||
\retval Index of the new created item */
|
||||
size_t addItem(unsigned short _level);
|
||||
|
||||
/** \brief Adds new item to required level.
|
||||
|
||||
Constructs new item using copy constructor.
|
||||
|
||||
\param _level Index of the level
|
||||
\param _item Reference to the source item to copy from
|
||||
|
||||
\retval Index of the new created item */
|
||||
size_t addItem(unsigned short _level, const ProfBlockItem& _item);
|
||||
|
||||
/** \brief Adds new item to required level.
|
||||
|
||||
Constructs new item using move constructor.
|
||||
|
||||
\param _level Index of the level
|
||||
\param _item Reference to the source item to move from
|
||||
|
||||
\retval Index of the new created item */
|
||||
size_t addItem(unsigned short _level, ProfBlockItem&& _item);
|
||||
|
||||
/** \brief Finds top-level blocks which are intersects with required selection zone.
|
||||
|
||||
\note Found blocks will be added into the array of selected blocks.
|
||||
|
||||
\param _left Left bound of the selection zone
|
||||
\param _right Right bound of the selection zone
|
||||
\param _blocks Reference to the array of selected blocks */
|
||||
void getBlocks(qreal _left, qreal _right, TreeBlocks& _blocks) const;
|
||||
|
||||
private:
|
||||
|
||||
///< Returns pointer to the ProfGraphicsView widget.
|
||||
const ProfGraphicsView* view() const;
|
||||
|
||||
}; // END of class ProfGraphicsItem.
|
||||
@ -120,20 +182,24 @@ private:
|
||||
|
||||
class ProfChronometerItem : public QGraphicsItem
|
||||
{
|
||||
QFont m_font;
|
||||
QRectF m_boundingRect;
|
||||
qreal m_left, m_right;
|
||||
QFont m_font; ///< Font which is used to draw text
|
||||
QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem)
|
||||
qreal m_left, m_right; ///< Left and right bounds of the selection zone
|
||||
|
||||
public:
|
||||
|
||||
ProfChronometerItem();
|
||||
virtual ~ProfChronometerItem();
|
||||
|
||||
// Public virtual methods
|
||||
|
||||
QRectF boundingRect() const override;
|
||||
void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override;
|
||||
|
||||
public:
|
||||
|
||||
// Public non-virtual methods
|
||||
|
||||
void setBoundingRect(qreal x, qreal y, qreal w, qreal h);
|
||||
void setBoundingRect(const QRectF& _rect);
|
||||
void setLeftRight(qreal _left, qreal _right);
|
||||
@ -155,6 +221,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
///< Returns pointer to the ProfGraphicsView widget.
|
||||
const ProfGraphicsView* view() const;
|
||||
|
||||
}; // END of class ProfChronometerItem.
|
||||
@ -170,21 +237,22 @@ private:
|
||||
typedef ProfGraphicsView This;
|
||||
typedef ::std::vector<ProfGraphicsItem*> Items;
|
||||
|
||||
Items m_items;
|
||||
TreeBlocks m_selectedBlocks;
|
||||
QTimer m_flickerTimer;
|
||||
QRectF m_visibleSceneRect;
|
||||
::profiler::timestamp_t m_beginTime;
|
||||
qreal m_scale;
|
||||
Items m_items; ///< Array of all ProfGraphicsItem items
|
||||
TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (ProfChronometerItem)
|
||||
QTimer m_flickerTimer; ///< Timer for flicking behavior
|
||||
QRectF m_visibleSceneRect; ///< Visible scene rectangle
|
||||
::profiler::timestamp_t m_beginTime; ///< Begin time of profiler session. Used to reduce values of all begin and end times of profiler blocks.
|
||||
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 :(
|
||||
QPoint m_mousePressPos;
|
||||
Qt::MouseButtons m_mouseButtons;
|
||||
GraphicsHorizontalScrollbar* m_pScrollbar;
|
||||
ProfChronometerItem* m_chronometerItem;
|
||||
int m_flickerSpeed;
|
||||
bool m_bUpdatingRect;
|
||||
bool m_bTest;
|
||||
bool m_bEmpty;
|
||||
QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent)
|
||||
Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons
|
||||
GraphicsHorizontalScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget
|
||||
ProfChronometerItem* m_chronometerItem; ///< Pointer to the ProfChronometerItem which is displayed when you press right mouse button and move mouse left or right
|
||||
int m_flickerSpeed; ///< Current flicking speed
|
||||
bool m_bUpdatingRect; ///< Stub flag which is used to avoid excess calculations on some scene update (flicking, scaling and so on)
|
||||
bool m_bTest; ///< Testing flag (true when test() is called)
|
||||
bool m_bEmpty; ///< Indicates whether scene is empty and has no items
|
||||
bool m_bStrictSelection; ///< Strict selection flag used by ProfTreeWidget to interpret left and right bounds of selection zone in different ways
|
||||
|
||||
public:
|
||||
|
||||
@ -192,12 +260,53 @@ public:
|
||||
ProfGraphicsView(const thread_blocks_tree_t& _blocksTree);
|
||||
virtual ~ProfGraphicsView();
|
||||
|
||||
// Public virtual methods
|
||||
|
||||
void wheelEvent(QWheelEvent* _event) override;
|
||||
void mousePressEvent(QMouseEvent* _event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* _event) override;
|
||||
void mouseMoveEvent(QMouseEvent* _event) override;
|
||||
void resizeEvent(QResizeEvent* _event) override;
|
||||
|
||||
public:
|
||||
|
||||
// Public non-virtual methods
|
||||
|
||||
void setScrollbar(GraphicsHorizontalScrollbar* _scrollbar);
|
||||
void clearSilent();
|
||||
|
||||
void test(size_t _frames_number, size_t _total_items_number_estimate, int _rows);
|
||||
void setTree(const thread_blocks_tree_t& _blocksTree);
|
||||
|
||||
signals:
|
||||
|
||||
// Signals
|
||||
|
||||
void intervalChanged(const TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict);
|
||||
|
||||
private:
|
||||
|
||||
// Private non-virtual methods
|
||||
|
||||
void initMode();
|
||||
void updateVisibleSceneRect();
|
||||
void updateScene();
|
||||
void scaleTo(qreal _scale);
|
||||
qreal setTree(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y, unsigned short _level);
|
||||
void fillTestChildren(ProfGraphicsItem* _item, const int _maxlevel, int _level, qreal _x, qreal _y, size_t _childrenNumber, size_t& _total_items);
|
||||
|
||||
private slots:
|
||||
|
||||
// Private Slots
|
||||
|
||||
void onScrollbarValueChange(int);
|
||||
void onGraphicsScrollbarValueChange(qreal);
|
||||
void onFlickerTimeout();
|
||||
|
||||
public:
|
||||
|
||||
// Public inline methods
|
||||
|
||||
inline qreal scale() const
|
||||
{
|
||||
return m_scale;
|
||||
@ -213,33 +322,33 @@ public:
|
||||
return m_visibleSceneRect;
|
||||
}
|
||||
|
||||
inline qreal time2position(const profiler::timestamp_t& _time) const
|
||||
{
|
||||
return qreal(_time - m_beginTime) * 1e-6;
|
||||
}
|
||||
|
||||
void setScrollbar(GraphicsHorizontalScrollbar* _scrollbar);
|
||||
void clearSilent();
|
||||
|
||||
void test(size_t _frames_number, size_t _total_items_number_estimate, int _depth);
|
||||
void setTree(const thread_blocks_tree_t& _blocksTree);
|
||||
|
||||
signals:
|
||||
|
||||
void treeblocksChanged(const TreeBlocks& _blocks, ::profiler::timestamp_t _begin_time);
|
||||
|
||||
private:
|
||||
|
||||
void initMode();
|
||||
void updateVisibleSceneRect();
|
||||
void updateScene();
|
||||
qreal setTree(ProfGraphicsItem* _item, const BlocksTree::children_t& _children, qreal& _height, qreal _y, unsigned short _level);
|
||||
// Private inline methods
|
||||
|
||||
private slots:
|
||||
inline qreal time2position(const profiler::timestamp_t& _time) const
|
||||
{
|
||||
return PROF_MICROSECONDS(qreal(_time - m_beginTime));
|
||||
//return PROF_MILLISECONDS(qreal(_time - m_beginTime));
|
||||
}
|
||||
|
||||
void onScrollbarValueChange(int);
|
||||
void onGraphicsScrollbarValueChange(qreal);
|
||||
void onFlickerTimeout();
|
||||
inline ::profiler::timestamp_t position2time(qreal _pos) const
|
||||
{
|
||||
return PROF_FROM_MICROSECONDS(_pos);
|
||||
//return PROF_FROM_MILLISECONDS(_pos);
|
||||
}
|
||||
|
||||
inline qreal to_microseconds(qreal _value) const
|
||||
{
|
||||
return _value;
|
||||
//return _value * 1e-3;
|
||||
}
|
||||
|
||||
inline qreal to_milliseconds(qreal _value) const
|
||||
{
|
||||
return _value * 1e3;
|
||||
//return _value;
|
||||
}
|
||||
|
||||
}; // END of class ProfGraphicsView.
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
* license : TODO: add license text
|
||||
************************************************************************/
|
||||
|
||||
#include <algorithm>
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QSignalBlocker>
|
||||
@ -102,33 +103,36 @@ const BlocksTree* ProfTreeWidgetItem::block() const
|
||||
|
||||
void ProfTreeWidgetItem::setTimeSmart(int _column, const ::profiler::timestamp_t& _time)
|
||||
{
|
||||
setData(_column, Qt::UserRole, (quint64)_time);
|
||||
const ::profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
||||
|
||||
setToolTip(_column, QString("%1 ns").arg(_time));
|
||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||
|
||||
setToolTip(_column, QString("%1 ns").arg(nanosecondsTime));
|
||||
|
||||
if (_time < 1e3)
|
||||
{
|
||||
setText(_column, QString("%1 ns").arg(_time));
|
||||
setText(_column, QString("%1 ns").arg(nanosecondsTime));
|
||||
}
|
||||
else if (_time < 1e6)
|
||||
{
|
||||
setText(_column, QString("%1 us").arg(double(_time) * 1e-3, 0, 'f', 3));
|
||||
setText(_column, QString("%1 us").arg(double(nanosecondsTime) * 1e-3, 0, 'f', 3));
|
||||
}
|
||||
else if (_time < 1e9)
|
||||
{
|
||||
setText(_column, QString("%1 ms").arg(double(_time) * 1e-6, 0, 'f', 3));
|
||||
setText(_column, QString("%1 ms").arg(double(nanosecondsTime) * 1e-6, 0, 'f', 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
setText(_column, QString("%1 s").arg(double(_time) * 1e-9, 0, 'f', 3));
|
||||
setText(_column, QString("%1 s").arg(double(nanosecondsTime) * 1e-9, 0, 'f', 3));
|
||||
}
|
||||
}
|
||||
|
||||
void ProfTreeWidgetItem::setTimeMs(int _column, const ::profiler::timestamp_t& _time)
|
||||
{
|
||||
setData(_column, Qt::UserRole, (quint64)_time);
|
||||
setToolTip(_column, QString("%1 ns").arg(_time));
|
||||
setText(_column, QString::number(double(_time) * 1e-6, 'g', 9));
|
||||
const ::profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||
setToolTip(_column, QString("%1 ns").arg(nanosecondsTime));
|
||||
setText(_column, QString::number(double(nanosecondsTime) * 1e-6, 'g', 9));
|
||||
}
|
||||
|
||||
void ProfTreeWidgetItem::setBackgroundColor(const QColor& _color)
|
||||
@ -237,12 +241,14 @@ void ProfTreeWidget::setTree(const unsigned int _blocksNumber, const thread_bloc
|
||||
connect(this, &Parent::itemExpanded, this, &This::onItemExpand);
|
||||
}
|
||||
|
||||
void ProfTreeWidget::setTreeBlocks(const TreeBlocks& _blocks, ::profiler::timestamp_t _begin_time)
|
||||
void ProfTreeWidget::setTreeBlocks(const TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict)
|
||||
{
|
||||
clearSilent();
|
||||
|
||||
m_beginTime = _begin_time;
|
||||
setTreeInternal(_blocks);
|
||||
m_beginTime = _session_begin_time;
|
||||
_left += m_beginTime;// - ::std::min(m_beginTime, 1000ULL);
|
||||
_right += m_beginTime;// + 1000;
|
||||
setTreeInternal(_blocks, _left, _right, _strict);
|
||||
|
||||
setSortingEnabled(true);
|
||||
sortByColumn(COL_BEGIN, Qt::AscendingOrder);
|
||||
@ -268,19 +274,29 @@ void ProfTreeWidget::clearSilent()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProfTreeWidget::setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree)
|
||||
size_t ProfTreeWidget::setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree)
|
||||
{
|
||||
m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks
|
||||
|
||||
decltype(m_beginTime) finishtime = 0;
|
||||
for (const auto& threadTree : _blocksTree)
|
||||
{
|
||||
const auto beginTime = threadTree.second.children.front().node->block()->getBegin();
|
||||
if (m_beginTime > beginTime)
|
||||
const auto node_block = threadTree.second.children.front().node->block();
|
||||
const auto startTime = node_block->getBegin();
|
||||
const auto endTime = node_block->getEnd();
|
||||
|
||||
if (m_beginTime > startTime)
|
||||
{
|
||||
m_beginTime = beginTime;
|
||||
m_beginTime = startTime;
|
||||
}
|
||||
|
||||
if (finishtime < endTime)
|
||||
{
|
||||
finishtime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
size_t total_items = 0;
|
||||
const QSignalBlocker b(this);
|
||||
for (const auto& threadTree : _blocksTree)
|
||||
{
|
||||
@ -289,22 +305,32 @@ void ProfTreeWidget::setTreeInternal(const unsigned int _blocksNumber, const thr
|
||||
|
||||
m_items.push_back(item);
|
||||
|
||||
setTreeInternal(threadTree.second.children, item);
|
||||
const auto children_items_number = setTreeInternal(threadTree.second.children, item, m_beginTime, finishtime + 1000000000, false);
|
||||
|
||||
addTopLevelItem(item);
|
||||
|
||||
if (m_bColorRows)
|
||||
if (children_items_number > 0)
|
||||
{
|
||||
item->colorize(m_bColorRows);
|
||||
total_items += children_items_number + 1;
|
||||
addTopLevelItem(item);
|
||||
|
||||
if (m_bColorRows)
|
||||
{
|
||||
item->colorize(m_bColorRows);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
return total_items;
|
||||
}
|
||||
|
||||
void ProfTreeWidget::setTreeInternal(const TreeBlocks& _blocks)
|
||||
size_t ProfTreeWidget::setTreeInternal(const TreeBlocks& _blocks, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict)
|
||||
{
|
||||
if (_blocks.empty())
|
||||
{
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t blocksNumber = 0;
|
||||
@ -318,9 +344,17 @@ void ProfTreeWidget::setTreeInternal(const TreeBlocks& _blocks)
|
||||
typedef ::std::unordered_map<::profiler::thread_id_t, ProfTreeWidgetItem*, ::btw::do_no_hash<::profiler::thread_id_t>::hasher_t> ThreadsMap;
|
||||
ThreadsMap threadsMap;
|
||||
|
||||
size_t total_items = 0;
|
||||
const QSignalBlocker b(this);
|
||||
for (const auto& block : _blocks)
|
||||
{
|
||||
const auto startTime = block.tree->node->block()->getBegin();
|
||||
const auto endTime = block.tree->node->block()->getEnd();
|
||||
if (startTime > _right || endTime < _left)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ProfTreeWidgetItem* thread_item = nullptr;
|
||||
auto thread_item_it = threadsMap.find(block.thread_id);
|
||||
if (thread_item_it != threadsMap.end())
|
||||
@ -331,15 +365,14 @@ void ProfTreeWidget::setTreeInternal(const TreeBlocks& _blocks)
|
||||
{
|
||||
thread_item = new ProfTreeWidgetItem(block.thread_tree);
|
||||
thread_item->setText(COL_NAME, QString("Thread %1").arg(block.thread_id));
|
||||
m_items.push_back(thread_item);
|
||||
threadsMap.insert(::std::make_pair(block.thread_id, thread_item));
|
||||
}
|
||||
|
||||
auto item = new ProfTreeWidgetItem(block.tree, thread_item);
|
||||
item->setText(COL_NAME, block.tree->node->getBlockName());
|
||||
item->setTimeSmart(COL_DURATION, block.tree->node->block()->duration());
|
||||
item->setTimeMs(COL_BEGIN, block.tree->node->block()->getBegin() - m_beginTime);
|
||||
item->setTimeMs(COL_END, block.tree->node->block()->getEnd() - m_beginTime);
|
||||
item->setTimeMs(COL_BEGIN, startTime - m_beginTime);
|
||||
item->setTimeMs(COL_END, endTime - m_beginTime);
|
||||
|
||||
if (block.tree->total_statistics)
|
||||
{
|
||||
@ -368,39 +401,70 @@ void ProfTreeWidget::setTreeInternal(const TreeBlocks& _blocks)
|
||||
item->setTextColor(fgColor);
|
||||
|
||||
m_items.push_back(item);
|
||||
m_itemblocks[block.tree->node] = item;
|
||||
|
||||
size_t children_items_number = 0;
|
||||
if (!block.tree->children.empty())
|
||||
{
|
||||
setTreeInternal(block.tree->children, item);
|
||||
children_items_number = setTreeInternal(block.tree->children, item, _left, _right, _strict);
|
||||
}
|
||||
|
||||
if (m_bColorRows)
|
||||
if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
|
||||
{
|
||||
item->colorize(m_bColorRows);
|
||||
total_items += children_items_number + 1;
|
||||
m_itemblocks[block.tree->node] = item;
|
||||
|
||||
if (m_bColorRows)
|
||||
{
|
||||
item->colorize(m_bColorRows);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.pop_back();
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it : threadsMap)
|
||||
for (auto& it : threadsMap)
|
||||
{
|
||||
addTopLevelItem(it.second);
|
||||
|
||||
if (m_bColorRows)
|
||||
if (it.second->childCount() > 0)
|
||||
{
|
||||
it.second->colorize(m_bColorRows);
|
||||
addTopLevelItem(it.second);
|
||||
|
||||
if (m_bColorRows)
|
||||
{
|
||||
it.second->colorize(m_bColorRows);
|
||||
}
|
||||
|
||||
m_items.push_back(it.second);
|
||||
++total_items;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete it.second;
|
||||
}
|
||||
}
|
||||
|
||||
return total_items;
|
||||
}
|
||||
|
||||
void ProfTreeWidget::setTreeInternal(const BlocksTree::children_t& _children, ProfTreeWidgetItem* _parent)
|
||||
size_t ProfTreeWidget::setTreeInternal(const BlocksTree::children_t& _children, ProfTreeWidgetItem* _parent, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict)
|
||||
{
|
||||
size_t total_items = 0;
|
||||
for (const auto& child : _children)
|
||||
{
|
||||
const auto startTime = child.node->block()->getBegin();
|
||||
const auto endTime = child.node->block()->getEnd();
|
||||
if (startTime > _right || endTime < _left)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto item = new ProfTreeWidgetItem(&child, _parent);
|
||||
item->setText(COL_NAME, child.node->getBlockName());
|
||||
item->setTimeSmart(COL_DURATION, child.node->block()->duration());
|
||||
item->setTimeMs(COL_BEGIN, child.node->block()->getBegin() - m_beginTime);
|
||||
item->setTimeMs(COL_END, child.node->block()->getEnd() - m_beginTime);
|
||||
item->setTimeMs(COL_BEGIN, startTime - m_beginTime);
|
||||
item->setTimeMs(COL_END, endTime - m_beginTime);
|
||||
|
||||
if (child.total_statistics)
|
||||
{
|
||||
@ -429,18 +493,31 @@ void ProfTreeWidget::setTreeInternal(const BlocksTree::children_t& _children, Pr
|
||||
item->setTextColor(fgColor);
|
||||
|
||||
m_items.push_back(item);
|
||||
m_itemblocks[child.node] = item;
|
||||
|
||||
auto children_items_number = 0;
|
||||
if (!child.children.empty())
|
||||
{
|
||||
setTreeInternal(child.children, item);
|
||||
children_items_number = setTreeInternal(child.children, item, _left, _right, _strict);
|
||||
}
|
||||
|
||||
if (m_bColorRows)
|
||||
if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
|
||||
{
|
||||
item->colorize(m_bColorRows);
|
||||
total_items += children_items_number + 1;
|
||||
m_itemblocks[child.node] = item;
|
||||
|
||||
if (m_bColorRows)
|
||||
{
|
||||
item->colorize(m_bColorRows);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_items.pop_back();
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
return total_items;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -449,12 +526,6 @@ void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
{
|
||||
const auto col = currentColumn();
|
||||
auto item = static_cast<ProfTreeWidgetItem*>(currentItem());
|
||||
|
||||
if (item == nullptr || col < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu menu;
|
||||
|
||||
auto action = new QAction("Expand all", nullptr);
|
||||
@ -465,15 +536,18 @@ void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
connect(action, &QAction::triggered, this, &This::onCollapseAllClicked);
|
||||
menu.addAction(action);
|
||||
|
||||
menu.addSeparator();
|
||||
if (item != nullptr && col >= 0)
|
||||
{
|
||||
menu.addSeparator();
|
||||
|
||||
action = new QAction("Expand all children", nullptr);
|
||||
connect(action, &QAction::triggered, this, &This::onExpandAllChildrenClicked);
|
||||
menu.addAction(action);
|
||||
action = new QAction("Expand all children", nullptr);
|
||||
connect(action, &QAction::triggered, this, &This::onExpandAllChildrenClicked);
|
||||
menu.addAction(action);
|
||||
|
||||
action = new QAction("Collapse all children", nullptr);
|
||||
connect(action, &QAction::triggered, this, &This::onCollapseAllChildrenClicked);
|
||||
menu.addAction(action);
|
||||
action = new QAction("Collapse all children", nullptr);
|
||||
connect(action, &QAction::triggered, this, &This::onCollapseAllChildrenClicked);
|
||||
menu.addAction(action);
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
@ -483,26 +557,29 @@ void ProfTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
connect(action, &QAction::triggered, this, &This::onColorizeRowsTriggered);
|
||||
menu.addAction(action);
|
||||
|
||||
switch (col)
|
||||
if (item != nullptr && col >= 0)
|
||||
{
|
||||
case COL_MIN_TOTAL:
|
||||
case COL_MIN:
|
||||
switch (col)
|
||||
{
|
||||
menu.addSeparator();
|
||||
auto itemAction = new ProfItemAction("Jump to such item", item);
|
||||
connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToMinItemClicked);
|
||||
menu.addAction(itemAction);
|
||||
break;
|
||||
}
|
||||
case COL_MIN_TOTAL:
|
||||
case COL_MIN:
|
||||
{
|
||||
menu.addSeparator();
|
||||
auto itemAction = new ProfItemAction("Jump to such item", item);
|
||||
connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToMinItemClicked);
|
||||
menu.addAction(itemAction);
|
||||
break;
|
||||
}
|
||||
|
||||
case COL_MAX_TOTAL:
|
||||
case COL_MAX:
|
||||
{
|
||||
menu.addSeparator();
|
||||
auto itemAction = new ProfItemAction("Jump to such item", item);
|
||||
connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToMaxItemClicked);
|
||||
menu.addAction(itemAction);
|
||||
break;
|
||||
case COL_MAX_TOTAL:
|
||||
case COL_MAX:
|
||||
{
|
||||
menu.addSeparator();
|
||||
auto itemAction = new ProfItemAction("Jump to such item", item);
|
||||
connect(itemAction, &ProfItemAction::clicked, this, &This::onJumpToMaxItemClicked);
|
||||
menu.addAction(itemAction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,15 +180,15 @@ public slots:
|
||||
|
||||
void setTree(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree);
|
||||
|
||||
void setTreeBlocks(const TreeBlocks& _blocks, ::profiler::timestamp_t _begin_time);
|
||||
void setTreeBlocks(const TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict);
|
||||
|
||||
protected:
|
||||
|
||||
void setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree);
|
||||
size_t setTreeInternal(const unsigned int _blocksNumber, const thread_blocks_tree_t& _blocksTree);
|
||||
|
||||
void setTreeInternal(const TreeBlocks& _blocks);
|
||||
size_t setTreeInternal(const TreeBlocks& _blocks, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict);
|
||||
|
||||
void setTreeInternal(const BlocksTree::children_t& _children, ProfTreeWidgetItem* _parent);
|
||||
size_t setTreeInternal(const BlocksTree::children_t& _children, ProfTreeWidgetItem* _parent, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict);
|
||||
|
||||
void contextMenuEvent(QContextMenuEvent* _event) override;
|
||||
|
||||
|
@ -27,6 +27,23 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define PROF_MICROSECONDS(timestamp) ((timestamp) * 1e-3)
|
||||
//#define PROF_MICROSECONDS(timestamp) (timestamp)
|
||||
|
||||
#define PROF_FROM_MICROSECONDS(to_timestamp) ((to_timestamp) * 1e3)
|
||||
//#define PROF_FROM_MICROSECONDS(to_timestamp) (to_timestamp)
|
||||
|
||||
#define PROF_MILLISECONDS(timestamp) ((timestamp) * 1e-6)
|
||||
//#define PROF_MILLISECONDS(timestamp) ((timestamp) * 1e-3)
|
||||
|
||||
#define PROF_FROM_MILLISECONDS(to_timestamp) ((to_timestamp) * 1e6)
|
||||
//#define PROF_FROM_MILLISECONDS(to_timestamp) ((to_timestamp) * 1e3)
|
||||
|
||||
#define PROF_NANOSECONDS(timestamp) (timestamp)
|
||||
//#define PROF_NANOSECONDS(timestamp) ((timestamp) * 1000)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const QRgb DEFAULT_COLOR = 0x00f0e094;
|
||||
|
||||
inline QRgb toRgb(unsigned int _red, unsigned int _green, unsigned int _blue)
|
||||
|
@ -172,7 +172,7 @@ qreal GraphicsHorizontalScrollbar::value() const
|
||||
|
||||
void GraphicsHorizontalScrollbar::setValue(qreal _value)
|
||||
{
|
||||
m_value = clamp(m_minimumValue, _value, m_maximumValue - m_slider->width());
|
||||
m_value = 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);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ ProfMainWindow::ProfMainWindow() : QMainWindow(), m_treeWidget(nullptr), m_graph
|
||||
menuBar()->addMenu(menu);
|
||||
|
||||
|
||||
connect(graphicsView->view(), &ProfGraphicsView::treeblocksChanged, treeWidget, &ProfTreeWidget::setTreeBlocks);
|
||||
connect(graphicsView->view(), &ProfGraphicsView::intervalChanged, treeWidget, &ProfTreeWidget::setTreeBlocks);
|
||||
|
||||
|
||||
if(QCoreApplication::arguments().size() > 1)
|
||||
@ -155,8 +155,8 @@ void ProfMainWindow::onTestViewportClicked(bool)
|
||||
view->clearSilent();
|
||||
m_currentProf.clear();
|
||||
|
||||
view->test(18000, 40000000, 5);
|
||||
//view->test(3, 300, 4);
|
||||
view->test(18000, 40000000, 2);
|
||||
//view->test(3, 300, 1);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
x
Reference in New Issue
Block a user