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

(GUI) Added tooltips into Settings->View menu;

(GUI) More informative popup window view;
(GUI) Added hystogram for current selected block;
This commit is contained in:
Victor Zarubkin 2016-12-04 16:51:27 +03:00
parent ced93de1b3
commit 15f3c7fc87
13 changed files with 1260 additions and 188 deletions

View File

@ -24,6 +24,8 @@ add_executable(${PROJECT_NAME}
easy_graphics_item.cpp
easy_graphics_scrollbar.h
easy_graphics_scrollbar.cpp
easy_qtimer.h
easy_qtimer.cpp
globals.h
globals.cpp
globals_qobjects.cpp

View File

@ -428,6 +428,7 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
finish += ADDITIONAL_OFFSET << 1;
m_beginTime -= ::std::min(m_beginTime, ADDITIONAL_OFFSET);
EASY_GLOBALS.begin_time = m_beginTime;
// Filling scene with items
m_items.reserve(_blocksTree.size());
@ -515,11 +516,11 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
if (longestItem != nullptr)
{
m_pScrollbar->setMinimapFrom(longestItem->threadId(), longestItem->items(0));
EASY_GLOBALS.selected_thread = longestItem->threadId();
emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId());
scrollTo(longestItem);
m_pScrollbar->setHystogramFrom(longestItem->threadId(), longestItem->items(0));
}
m_idleTimer.start(IDLE_TIMER_INTERVAL);
@ -882,6 +883,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
}
const ::profiler_gui::EasyBlock* selectedBlock = nullptr;
::profiler::thread_id_t selectedBlockThread = 0;
const auto previouslySelectedBlock = EASY_GLOBALS.selected_block;
if (m_mouseButtons & Qt::LeftButton)
{
@ -919,13 +921,14 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
{
changedSelectedItem = true;
selectedBlock = block;
selectedBlockThread = item->threadId();
EASY_GLOBALS.selected_block = i;
EASY_GLOBALS.selected_block_id = easyBlock(i).tree.node->id();
break;
}
}
if (!changedSelectedItem && EASY_GLOBALS.selected_block != ::profiler_gui::numeric_max(EASY_GLOBALS.selected_block))
if (!changedSelectedItem && !::profiler_gui::is_max(EASY_GLOBALS.selected_block))
{
changedSelectedItem = true;
::profiler_gui::set_max(EASY_GLOBALS.selected_block);
@ -954,8 +957,31 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
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.selectedThreadChanged(EASY_GLOBALS.selected_thread);
m_pScrollbar->unlock();
}
m_bUpdatingRect = false;
if (selectedBlock != nullptr && selectedBlockThread == EASY_GLOBALS.selected_thread)
m_pScrollbar->setHystogramFrom(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id);
else
{
for (auto item : m_items)
{
if (item->threadId() == EASY_GLOBALS.selected_thread)
{
m_pScrollbar->setHystogramFrom(EASY_GLOBALS.selected_thread, item->items(0));
break;
}
}
}
repaintScene();
}
else if (chronoHidden)
@ -1203,6 +1229,31 @@ void EasyGraphicsView::initMode()
connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange);
connect(globalSignals, &::profiler_gui::EasyGlobalSignals::itemsExpandStateChanged, this, &This::onRefreshRequired);
connect(globalSignals, &::profiler_gui::EasyGlobalSignals::refreshRequired, this, &This::onRefreshRequired);
connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedBlockIdChanged, [this](::profiler::block_id_t)
{
if (::profiler_gui::is_max(EASY_GLOBALS.selected_block_id))
{
if (EASY_GLOBALS.selected_thread != 0)
{
for (auto item : m_items)
{
if (item->threadId() == EASY_GLOBALS.selected_thread)
{
m_pScrollbar->setHystogramFrom(EASY_GLOBALS.selected_thread, item->items(0));
break;
}
}
}
else
{
m_pScrollbar->setHystogramFrom(0, nullptr);
}
}
else
m_pScrollbar->setHystogramFrom(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id);
onRefreshRequired();
});
}
//////////////////////////////////////////////////////////////////////////
@ -1338,58 +1389,60 @@ void EasyGraphicsView::onIdleTimeout()
{
const auto duration = itemBlock.node->duration();
lay->addWidget(new QLabel("%", widget), 2, 1, Qt::AlignHCenter);
lay->addWidget(new QLabel("%/N", widget), 2, 2, Qt::AlignHCenter);
lay->addWidget(new QLabel("N", widget), 2, 3, Qt::AlignHCenter);
lay->addWidget(new QLabel("-------- Statistics --------", widget), 2, 0, 1, 4, Qt::AlignHCenter);
lay->addWidget(new QLabel("per ", widget), 3, 0, Qt::AlignRight);
lay->addWidget(new QLabel("This %:", widget), 4, 0, Qt::AlignRight);
lay->addWidget(new QLabel("Sum %:", widget), 5, 0, Qt::AlignRight);
lay->addWidget(new QLabel("N Calls:", widget), 6, 0, Qt::AlignRight);
lay->addWidget(new QLabel("Thread:", widget), 3, 0, Qt::AlignRight);
lay->addWidget(new QLabel("Thread", widget), 3, 1, Qt::AlignHCenter);
auto percent = ::profiler_gui::percentReal(duration, item->root()->active_time);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), 3, 1, Qt::AlignHCenter);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), 4, 1, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->active_time)), widget), 3, 2, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->active_time)), widget), 5, 1, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), 3, 3, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), 6, 1, Qt::AlignHCenter);
int r = 3;
int col = 1;
if (itemBlock.per_frame_stats->parent_block != i)
{
++r;
++col;
auto frame_duration = blocksTree(itemBlock.per_frame_stats->parent_block).node->duration();
lay->addWidget(new QLabel("Frame:", widget), r, 0, Qt::AlignRight);
lay->addWidget(new QLabel("Frame", widget), 3, col, Qt::AlignRight);
percent = ::profiler_gui::percentReal(duration, frame_duration);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), r, 1, Qt::AlignHCenter);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), 4, col, Qt::AlignHCenter);
percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration, frame_duration);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), r, 2, Qt::AlignHCenter);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), 5, col, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), r, 3, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), 6, col, Qt::AlignHCenter);
}
if (itemBlock.per_parent_stats->parent_block != item->threadId())
{
++r;
++col;
auto parent_duration = blocksTree(itemBlock.per_parent_stats->parent_block).node->duration();
lay->addWidget(new QLabel("Parent:", widget), r, 0, Qt::AlignRight);
lay->addWidget(new QLabel("Parent", widget), 3, col, Qt::AlignRight);
percent = ::profiler_gui::percentReal(duration, parent_duration);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), r, 1, Qt::AlignHCenter);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), 4, col, Qt::AlignHCenter);
percent = ::profiler_gui::percentReal(itemBlock.per_parent_stats->total_duration, parent_duration);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), r, 2, Qt::AlignHCenter);
lay->addWidget(new QLabel(0.005 < percent && percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent)), widget), 5, col, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(itemBlock.per_parent_stats->calls_number), widget), r, 3, Qt::AlignHCenter);
lay->addWidget(new QLabel(QString::number(itemBlock.per_parent_stats->calls_number), widget), 6, col, Qt::AlignHCenter);
++r;
++col;
}
}
else
{
lay->addWidget(new QLabel("N/Thread:", widget), 1, 0, Qt::AlignRight);
lay->addWidget(new QLabel("N calls/Thread:", widget), 1, 0, Qt::AlignRight);
lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), 1, 1, Qt::AlignLeft);
}
}
@ -1481,14 +1534,14 @@ void EasyGraphicsView::onHierarchyFlagChange(bool)
void EasyGraphicsView::onSelectedThreadChange(::profiler::thread_id_t _id)
{
if (m_pScrollbar == nullptr || m_pScrollbar->minimapThread() == _id)
if (m_pScrollbar == nullptr || m_pScrollbar->hystThread() == _id)
{
return;
}
if (_id == 0)
{
m_pScrollbar->setMinimapFrom(0, nullptr);
m_pScrollbar->setHystogramFrom(0, nullptr);
return;
}
@ -1496,7 +1549,7 @@ void EasyGraphicsView::onSelectedThreadChange(::profiler::thread_id_t _id)
{
if (item->threadId() == _id)
{
m_pScrollbar->setMinimapFrom(_id, item->items(0));
m_pScrollbar->setHystogramFrom(_id, item->items(0));
bool changedSelection = false;
if (EASY_GLOBALS.only_current_thread_hierarchy)
@ -1525,7 +1578,7 @@ void EasyGraphicsView::onSelectedThreadChange(::profiler::thread_id_t _id)
}
}
m_pScrollbar->setMinimapFrom(0, nullptr);
m_pScrollbar->setHystogramFrom(0, nullptr);
repaintScene();
}
@ -1548,8 +1601,35 @@ void EasyGraphicsView::onSelectedBlockChange(unsigned int _block_index)
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());
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.selectedThreadChanged(EASY_GLOBALS.selected_thread);
m_pScrollbar->unlock();
}
m_pScrollbar->setHystogramFrom(EASY_GLOBALS.selected_thread, guiblock.tree.node->id());
m_bUpdatingRect = false;
}
else if (EASY_GLOBALS.selected_thread != 0)
{
for (auto item : m_items)
{
if (item->threadId() == EASY_GLOBALS.selected_thread)
{
m_pScrollbar->setHystogramFrom(EASY_GLOBALS.selected_thread, item->items(0));
break;
}
}
}
else
{
m_pScrollbar->setHystogramFrom(0, nullptr);
}
updateVisibleSceneRect();
repaintScene();
@ -1621,7 +1701,8 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte
const auto visibleSceneRect = view->visibleSceneRect();
const auto h = visibleSceneRect.height() + TIMELINE_ROW_SIZE - 2;
const auto w = parentView->sceneRect().width();
const auto sceneRect = parentView->sceneRect();
const auto w = sceneRect.width();
static const uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)};
@ -1632,6 +1713,7 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte
_painter->resetTransform();
// Draw thread names
auto default_font = _painter->font();
_painter->setFont(BG_FONT);
for (auto item : items)
{
@ -1689,17 +1771,17 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte
// Draw information
_painter->setFont(CHRONOMETER_FONT);
QFontMetricsF fm(CHRONOMETER_FONT, parentView);
const qreal th = fm.height(); // Calculate displayed text height
const qreal time1 = view->chronoTime();
const qreal time2 = view->chronoTimeAux();
auto y = h + 2;
auto drawTimeText = [&rect, &w, &y, &fm, &_painter](qreal time, QRgb color)
auto drawTimeText = [&rect, &w, &y, &fm, &_painter](qreal time, qreal th, QRgb color)
{
if (time > 0)
{
const QString text = ::profiler_gui::autoTimeStringReal(time); // Displayed text
const auto th = fm.height(); // Calculate displayed text height
rect.setRect(0, y, w, th);
_painter->setPen(color);
@ -1709,8 +1791,8 @@ void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte
}
};
drawTimeText(time1, ::profiler_gui::CHRONOMETER_COLOR.rgb() & 0x00ffffff);
drawTimeText(time2, ::profiler_gui::CHRONOMETER_COLOR2.rgb() & 0x00ffffff);
drawTimeText(time1, th, ::profiler_gui::CHRONOMETER_COLOR.rgb() & 0x00ffffff);
drawTimeText(time2, th, ::profiler_gui::CHRONOMETER_COLOR2.rgb() & 0x00ffffff);
}
//////////////////////////////////////////////////////////////////////////
@ -1730,7 +1812,7 @@ EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, int _addit
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setFixedWidth(100);
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange);
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, [this](::profiler::thread_id_t){ repaintScene(); });
connect(m_view, &EasyGraphicsView::treeChanged, this, &This::onTreeChange);
connect(m_view, &EasyGraphicsView::sceneUpdated, this, &This::repaintScene);
connect(m_view->verticalScrollBar(), &QScrollBar::valueChanged, verticalScrollBar(), &QScrollBar::setValue);
@ -1783,11 +1865,6 @@ void EasyThreadNamesWidget::onTreeChange()
setFixedWidth(maxLength);
}
void EasyThreadNamesWidget::onSelectedThreadChange(::profiler::thread_id_t)
{
scene()->update();
}
void EasyThreadNamesWidget::repaintScene()
{
scene()->update();

View File

@ -280,7 +280,6 @@ private slots:
void setVerticalScrollbarRange(int _minValue, int _maxValue);
void onTreeChange();
void onSelectedThreadChange(::profiler::thread_id_t _id);
void repaintScene();
}; // END of class EasyThreadNamesWidget.

View File

@ -479,14 +479,14 @@ void EasyDescTreeWidget::onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidget
if (EASY_GLOBALS.selected_block_id != id)
{
EASY_GLOBALS.selected_block_id = id;
emit EASY_GLOBALS.events.refreshRequired();
emit EASY_GLOBALS.events.selectedBlockIdChanged(id);
}
}
}
else if (::profiler_gui::is_max(EASY_GLOBALS.selected_block) && !::profiler_gui::is_max(EASY_GLOBALS.selected_block_id))
{
::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
emit EASY_GLOBALS.events.refreshRequired();
emit EASY_GLOBALS.events.selectedBlockIdChanged(EASY_GLOBALS.selected_block_id);
}
}

View File

@ -70,13 +70,7 @@ inline QRgb selectedItemBorderColor(::profiler::color_t _color) {
return ::profiler_gui::isLightColor(_color, 192) ? ::profiler::colors::Black : ::profiler::colors::RichRed;
}
inline QRgb highlightItemColor(bool _is_light) {
return _is_light ? ::profiler::colors::RichRed : ::profiler::colors::White;
}
#define HIGHLIGHT_COLOR(_is_light) ::profiler::colors::White
//#define HIGHLIGHT_COLOR(_is_light) highlightItemColor(_is_light)
const QPen HIGHLIGHTER_PEN = ([]() -> QPen { QPen p(::profiler::colors::Black); p.setStyle(Qt::DotLine); p.setWidth(2); return p; })();
const auto ITEMS_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Medium);
const auto SELECTED_ITEM_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Bold);
@ -362,7 +356,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
if (previousPenStyle != Qt::DotLine)
{
previousPenStyle = Qt::DotLine;
_painter->setPen(HIGHLIGHT_COLOR(is_light));
_painter->setPen(HIGHLIGHTER_PEN);
}
}
else if (EASY_GLOBALS.draw_graphics_items_borders)
@ -419,7 +413,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
if (previousPenStyle != Qt::DotLine)
{
previousPenStyle = Qt::DotLine;
_painter->setPen(HIGHLIGHT_COLOR(is_light));
_painter->setPen(HIGHLIGHTER_PEN);
}
}
else if (EASY_GLOBALS.draw_graphics_items_borders)
@ -497,7 +491,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
_painter->setPen(Qt::NoPen);
else if (previousPenStyle == Qt::DotLine)
{
_painter->setPen(HIGHLIGHT_COLOR(is_light));
_painter->setPen(HIGHLIGHTER_PEN);
}
else
_painter->setPen(BORDERS_COLOR);// BORDERS_COLOR & inverseColor); // restore pen for rectangle painting

File diff suppressed because it is too large Load Diff

View File

@ -46,14 +46,55 @@
#define EASY__GRAPHICS_SCROLLBAR__H
#include <stdlib.h>
#include <thread>
#include <atomic>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QAction>
#include <QPolygonF>
#include <QImage>
#include "easy_qtimer.h"
#include "common_types.h"
//////////////////////////////////////////////////////////////////////////
// TODO: use profiler_core/spin_lock.h
#define EASY_GUI_USE_CRITICAL_SECTION // Use CRITICAL_SECTION instead of std::atomic_flag
#if defined(_WIN32) && defined(EASY_GUI_USE_CRITICAL_SECTION)
namespace profiler_gui {
// std::atomic_flag on Windows works slower than critical section, so we will use it instead of std::atomic_flag...
// By the way, Windows critical sections are slower than std::atomic_flag on Unix.
class spin_lock { void* m_lock; public:
void lock();
void unlock();
spin_lock();
~spin_lock();
};
#else
namespace profiler_gui {
// std::atomic_flag on Unix works fine and very fast (almost instant!)
class spin_lock {
::std::atomic_flag m_lock; public:
void lock() {
while (m_lock.test_and_set(::std::memory_order_acquire));
}
void unlock() {
m_lock.clear(::std::memory_order_release);
}
spin_lock() {
m_lock.clear();
}
};
#endif
} // END of namespace profiler_gui.
//////////////////////////////////////////////////////////////////////////
class EasyGraphicsSliderItem : public QGraphicsRectItem
{
typedef QGraphicsRectItem Parent;
@ -85,24 +126,40 @@ public:
//////////////////////////////////////////////////////////////////////////
class EasyMinimapItem : public QGraphicsItem
class EasyHystogramItem : public QGraphicsItem
{
typedef QGraphicsItem Parent;
typedef EasyMinimapItem This;
typedef EasyHystogramItem This;
QRectF m_boundingRect;
qreal m_maxDuration;
qreal m_minDuration;
QString m_maxDurationStr;
QString m_minDurationStr;
const ::profiler_gui::EasyItems* m_pSource;
::profiler::thread_id_t m_threadId;
::profiler_gui::TimeUnits m_timeUnits;
enum HystRegime : uint8_t { Hyst_Pointer, Hyst_Id };
QRectF m_boundingRect;
qreal m_maxDuration;
qreal m_minDuration;
QString m_maxDurationStr;
QString m_minDurationStr;
QString m_threadName;
::profiler::BlocksTree::children_t m_selectedBlocks;
QImage m_mainImage;
EasyQTimer m_timer;
::std::thread m_workerThread;
::profiler::timestamp_t m_threadDuration;
::profiler::timestamp_t m_threadActiveTime;
const ::profiler_gui::EasyItems* m_pSource;
QImage* m_temporaryImage;
::profiler::thread_id_t m_threadId;
::profiler::block_index_t m_blockId;
int m_timeouts;
::profiler_gui::TimeUnits m_timeUnits;
HystRegime m_regime;
bool m_bUpdatingImage;
::profiler_gui::spin_lock m_spin;
::std::atomic_bool m_bReady;
public:
explicit EasyMinimapItem();
virtual ~EasyMinimapItem();
explicit EasyHystogramItem();
virtual ~EasyHystogramItem();
// Public virtual methods
@ -119,8 +176,20 @@ public:
void setBoundingRect(qreal x, qreal y, qreal w, qreal h);
void setSource(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems* _items);
void setSource(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id);
void updateImage();
}; // END of class EasyMinimapItem.
private:
void paintByPtr(QPainter* _painter);
void paintById(QPainter* _painter);
void onTimeout();
void updateImage(HystRegime _regime, qreal _current_scale,
qreal _minimum, qreal _maximum, qreal _range,
qreal _value, qreal _width, bool _bindMode,
float _frame_time, ::profiler::timestamp_t _begin_time);
}; // END of class EasyHystogramItem.
//////////////////////////////////////////////////////////////////////////
@ -141,9 +210,11 @@ private:
Qt::MouseButtons m_mouseButtons;
EasyGraphicsSliderItem* m_slider;
EasyGraphicsSliderItem* m_chronometerIndicator;
EasyMinimapItem* m_minimap;
EasyHystogramItem* m_hystogramItem;
int m_defaultFontHeight;
bool m_bScrolling;
bool m_bBindMode;
bool m_bLocked;
public:
@ -169,7 +240,7 @@ public:
bool bindMode() const;
qreal getWindowScale() const;
::profiler::thread_id_t minimapThread() const;
::profiler::thread_id_t hystThread() const;
qreal minimum() const;
qreal maximum() const;
@ -177,6 +248,7 @@ public:
qreal value() const;
qreal sliderWidth() const;
qreal sliderHalfWidth() const;
int defaultFontHeight() const;
void setValue(qreal _value);
void setRange(qreal _minValue, qreal _maxValue);
@ -185,11 +257,22 @@ public:
void showChrono();
void hideChrono();
void setMinimapFrom(::profiler::thread_id_t _thread_id, const::profiler_gui::EasyItems* _items);
void setHystogramFrom(::profiler::thread_id_t _thread_id, const::profiler_gui::EasyItems* _items);
void setHystogramFrom(::profiler::thread_id_t _thread_id, ::profiler::block_id_t _block_id);
inline void setMinimapFrom(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems& _items)
inline void setHystogramFrom(::profiler::thread_id_t _thread_id, const ::profiler_gui::EasyItems& _items)
{
setMinimapFrom(_thread_id, &_items);
setHystogramFrom(_thread_id, &_items);
}
inline void lock()
{
m_bLocked = true;
}
inline void unlock()
{
m_bLocked = false;
}
signals:

View File

@ -0,0 +1,74 @@
/************************************************************************
* file name : easy_qtimer.h
* ----------------- :
* creation time : 2016/12/05
* author : Victor Zarubkin
* email : v.s.zarubkin@gmail.com
* ----------------- :
* description : This file contains implementation of EasyQTimer class used to
* : connect QTimer to non-QObject classes.
* ----------------- :
* change log : * 2016/12/05 Victor Zarubkin: Initial commit.
* :
* : *
* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
* :
* :
* : Licensed under the Apache License, Version 2.0 (the "License");
* : you may not use this file except in compliance with the License.
* : You may obtain a copy of the License at
* :
* : http://www.apache.org/licenses/LICENSE-2.0
* :
* : Unless required by applicable law or agreed to in writing, software
* : distributed under the License is distributed on an "AS IS" BASIS,
* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* : See the License for the specific language governing permissions and
* : limitations under the License.
* :
* :
* : GNU General Public License Usage
* : Alternatively, this file may be used under the terms of the GNU
* : General Public License as published by the Free Software Foundation,
* : either version 3 of the License, or (at your option) any later version.
* :
* : This program is distributed in the hope that it will be useful,
* : but WITHOUT ANY WARRANTY; without even the implied warranty of
* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* : GNU General Public License for more details.
* :
* : You should have received a copy of the GNU General Public License
* : along with this program.If not, see <http://www.gnu.org/licenses/>.
************************************************************************/
#include "easy_qtimer.h"
//////////////////////////////////////////////////////////////////////////
EasyQTimer::EasyQTimer()
: QObject()
{
connect(&m_timer, &QTimer::timeout, [this](){ m_handler(); });
}
EasyQTimer::EasyQTimer(::std::function<void()>&& _handler)
: QObject()
, m_handler(::std::forward<::std::function<void()>&&>(_handler))
{
connect(&m_timer, &QTimer::timeout, [this](){ m_handler(); });
}
EasyQTimer::~EasyQTimer()
{
}
void EasyQTimer::setHandler(::std::function<void()>&& _handler)
{
m_handler = _handler;
}
//////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,79 @@
/************************************************************************
* file name : easy_qtimer.h
* ----------------- :
* creation time : 2016/12/05
* author : Victor Zarubkin
* email : v.s.zarubkin@gmail.com
* ----------------- :
* description : This file contains declaration of EasyQTimer class used to
* : connect QTimer to non-QObject classes.
* ----------------- :
* change log : * 2016/12/05 Victor Zarubkin: Initial commit.
* :
* : *
* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
* :
* :
* : Licensed under the Apache License, Version 2.0 (the "License");
* : you may not use this file except in compliance with the License.
* : You may obtain a copy of the License at
* :
* : http://www.apache.org/licenses/LICENSE-2.0
* :
* : Unless required by applicable law or agreed to in writing, software
* : distributed under the License is distributed on an "AS IS" BASIS,
* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* : See the License for the specific language governing permissions and
* : limitations under the License.
* :
* :
* : GNU General Public License Usage
* : Alternatively, this file may be used under the terms of the GNU
* : General Public License as published by the Free Software Foundation,
* : either version 3 of the License, or (at your option) any later version.
* :
* : This program is distributed in the hope that it will be useful,
* : but WITHOUT ANY WARRANTY; without even the implied warranty of
* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* : GNU General Public License for more details.
* :
* : You should have received a copy of the GNU General Public License
* : along with this program.If not, see <http://www.gnu.org/licenses/>.
************************************************************************/
#ifndef EASY__QTIMER__H
#define EASY__QTIMER__H
#include <QTimer>
#include <functional>
//////////////////////////////////////////////////////////////////////////
class EasyQTimer : public QObject
{
Q_OBJECT
private:
QTimer m_timer;
::std::function<void()> m_handler;
public:
EasyQTimer();
EasyQTimer(::std::function<void()>&& _handler);
virtual ~EasyQTimer();
void setHandler(::std::function<void()>&& _handler);
inline void start(int msec) { m_timer.start(msec); }
inline void stop() { m_timer.stop(); }
inline bool isActive() const { return m_timer.isActive(); }
}; // END of class EasyQTimer.
//////////////////////////////////////////////////////////////////////////
#endif // EASY__QTIMER__H

View File

@ -61,6 +61,7 @@ namespace profiler_gui {
: selected_thread(0U)
, selected_block(::profiler_gui::numeric_max<decltype(selected_block)>())
, selected_block_id(::profiler_gui::numeric_max<decltype(selected_block_id)>())
, begin_time(0)
, frame_time(4e4f)
, blocks_spacing(2)
, blocks_size_min(3)
@ -79,6 +80,7 @@ namespace profiler_gui {
, all_items_expanded_by_default(true)
, only_current_thread_hierarchy(false)
, highlight_blocks_with_same_id(true)
, selecting_block_changes_thread(true)
, bind_scene_and_tree_expand_status(true)
{

View File

@ -108,6 +108,7 @@ namespace profiler_gui {
::profiler::thread_blocks_tree_t profiler_blocks; ///< Profiler blocks tree loaded from file
::profiler::descriptors_list_t descriptors; ///< Profiler block descriptors list
EasyBlocks gui_blocks; ///< Profiler graphics blocks builded by GUI
::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
@ -129,6 +130,7 @@ namespace profiler_gui {
bool all_items_expanded_by_default; ///< Expand all items after file is opened
bool only_current_thread_hierarchy; ///< Build hierarchy tree for current thread only
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 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

@ -49,6 +49,7 @@ namespace profiler_gui {
void selectedThreadChanged(::profiler::thread_id_t _id);
void selectedBlockChanged(uint32_t _block_index);
void selectedBlockIdChanged(::profiler::block_id_t _id);
void itemsExpandStateChanged();
void blockStatusChanged(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status);
void connectionChanged(bool _connected);

View File

@ -246,22 +246,27 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
menu->addSeparator();
auto submenu = menu->addMenu("View");
submenu->setToolTipsVisible(true);
action = submenu->addAction("Draw items' borders");
action->setToolTip("Draw borders for blocks on diagram.\nThis reduces performance.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.draw_graphics_items_borders);
connect(action, &QAction::triggered, this, &This::onDrawBordersChanged);
action = submenu->addAction("Hide narrow children");
action->setToolTip("Children blocks will be hidden by narrow\nparent blocks. See also \"Blocks narrow size\".\nThis improves performance.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.hide_narrow_children);
connect(action, &QAction::triggered, this, &This::onHideNarrowChildrenChanged);
action = submenu->addAction("Build hierarchy only for current thread");
action->setToolTip("Hierarchy tree will be built\nfor blocks from current thread only.\nThis improves performance\nand saves a lot of memory.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.only_current_thread_hierarchy);
connect(action, &QAction::triggered, this, &This::onHierarchyFlagChange);
action = submenu->addAction("Add zero blocks to hierarchy");
action->setToolTip("Zero duration blocks will be added into hierarchy tree.\nThis reduces performance and increases memory consumption.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.add_zero_blocks_to_hierarchy);
connect(action, &QAction::triggered, [this](bool _checked)
@ -270,32 +275,44 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
emit EASY_GLOBALS.events.hierarchyFlagChanged(_checked);
});
action = submenu->addAction("Enable zero length blocks");
action = submenu->addAction("Enable zero duration blocks on diagram");
action->setToolTip("If checked then allows diagram to paint zero duration blocks\nwith 1px width on each scale. Otherwise, such blocks will be resized\nto 250ns duration.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.enable_zero_length);
connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.enable_zero_length = _checked; refreshDiagram(); });
action = submenu->addAction("Highlight similar blocks");
action->setToolTip("Highlight all visible blocks which are similar\nto the current selected block.\nThis reduces performance.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.highlight_blocks_with_same_id);
connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.highlight_blocks_with_same_id = _checked; refreshDiagram(); });
action = submenu->addAction("Collapse items on tree reset");
action = submenu->addAction("Collapse blocks on tree reset");
action->setToolTip("This collapses all blocks on diagram\nafter hierarchy tree reset.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.collapse_items_on_tree_close);
connect(action, &QAction::triggered, this, &This::onCollapseItemsAfterCloseChanged);
action = submenu->addAction("Expand all on file open");
action->setToolTip("If checked then all blocks on diagram\nwill be initially expanded.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.all_items_expanded_by_default);
connect(action, &QAction::triggered, this, &This::onAllItemsExpandedByDefaultChange);
action = submenu->addAction("Bind scene and tree expand");
action = submenu->addAction("Bind diagram and tree expand");
action->setToolTip("Expanding/collapsing blocks at diagram expands/collapses\nblocks at hierarchy tree and wise versa.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.bind_scene_and_tree_expand_status);
connect(action, &QAction::triggered, this, &This::onBindExpandStatusChange);
action = submenu->addAction("Selecting block changes current thread");
action->setToolTip("Automatically select thread while selecting a block.\nIf not checked then you will have to select current thread\nmanually double clicking on thread name on a diagram.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.selecting_block_changes_thread);
connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.selecting_block_changes_thread = _checked; });
action = submenu->addAction("Draw event indicators");
action->setToolTip("Display event indicators under the blocks\n(even if event-blocks are not visible).\nThis slightly reduces performance.");
action->setCheckable(true);
action->setChecked(EASY_GLOBALS.enable_event_indicators);
connect(action, &QAction::triggered, this, &This::onEventIndicatorsChange);
@ -305,6 +322,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
actionGroup->setExclusive(true);
action = new QAction("Chrono text at top", actionGroup);
action->setToolTip("Draw duration of selected interval\nat the top of the screen.");
action->setCheckable(true);
action->setData(static_cast<int>(::profiler_gui::ChronoTextPosition_Top));
if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Top)
@ -313,6 +331,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged);
action = new QAction("Chrono text at center", actionGroup);
action->setToolTip("Draw duration of selected interval\nat the center of the screen.");
action->setCheckable(true);
action->setData(static_cast<int>(::profiler_gui::ChronoTextPosition_Center));
if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Center)
@ -321,6 +340,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged);
action = new QAction("Chrono text at bottom", actionGroup);
action->setToolTip("Draw duration of selected interval\nat the bottom of the screen.");
action->setCheckable(true);
action->setData(static_cast<int>(::profiler_gui::ChronoTextPosition_Bottom));
if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Bottom)
@ -332,7 +352,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
auto w = new QWidget(submenu);
auto l = new QHBoxLayout(w);
l->setContentsMargins(33, 1, 1, 1);
l->addWidget(new QLabel("Blocks spacing", w), 0, Qt::AlignLeft);
l->addWidget(new QLabel("Min blocks spacing, px", w), 0, Qt::AlignLeft);
auto spinbox = new QSpinBox(w);
spinbox->setMinimum(0);
spinbox->setValue(EASY_GLOBALS.blocks_spacing);
@ -347,7 +367,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
w = new QWidget(submenu);
l = new QHBoxLayout(w);
l->setContentsMargins(33, 1, 1, 1);
l->addWidget(new QLabel("Blocks size min", w), 0, Qt::AlignLeft);
l->addWidget(new QLabel("Min blocks size, px", w), 0, Qt::AlignLeft);
spinbox = new QSpinBox(w);
spinbox->setMinimum(1);
spinbox->setValue(EASY_GLOBALS.blocks_size_min);
@ -362,7 +382,7 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
w = new QWidget(submenu);
l = new QHBoxLayout(w);
l->setContentsMargins(33, 1, 1, 1);
l->addWidget(new QLabel("Blocks narrow size", w), 0, Qt::AlignLeft);
l->addWidget(new QLabel("Blocks narrow size, px", w), 0, Qt::AlignLeft);
spinbox = new QSpinBox(w);
spinbox->setMinimum(1);
spinbox->setValue(EASY_GLOBALS.blocks_narrow_size);
@ -950,6 +970,10 @@ void EasyMainWindow::loadSettings()
if (!flag.isNull())
EASY_GLOBALS.bind_scene_and_tree_expand_status = flag.toBool();
flag = settings.value("selecting_block_changes_thread");
if (!flag.isNull())
EASY_GLOBALS.selecting_block_changes_thread = flag.toBool();
flag = settings.value("enable_event_indicators");
if (!flag.isNull())
EASY_GLOBALS.enable_event_indicators = flag.toBool();
@ -1007,6 +1031,7 @@ void EasyMainWindow::saveSettingsAndGeometry()
settings.setValue("add_zero_blocks_to_hierarchy", EASY_GLOBALS.add_zero_blocks_to_hierarchy);
settings.setValue("highlight_blocks_with_same_id", EASY_GLOBALS.highlight_blocks_with_same_id);
settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status);
settings.setValue("selecting_block_changes_thread", EASY_GLOBALS.selecting_block_changes_thread);
settings.setValue("enable_event_indicators", EASY_GLOBALS.enable_event_indicators);
settings.setValue("enable_statistics", EASY_GLOBALS.enable_statistics);
settings.setValue("encoding", QTextCodec::codecForLocale()->name());
@ -1403,7 +1428,8 @@ void EasyMainWindow::onCaptureClicked(bool)
m_listener.startCapture();
m_listenerTimer.start(250);
m_listenerDialog = new QMessageBox(QMessageBox::Information, "Capturing frames...", "Close this dialog to stop capturing.", QMessageBox::Close, this);
m_listenerDialog = new QMessageBox(QMessageBox::Information, "Capturing frames...", "Close this dialog to stop capturing.", QMessageBox::NoButton, this);
m_listenerDialog->addButton("Stop", QMessageBox::AcceptRole);
m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true);
connect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose);
m_listenerDialog->show();