| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466 |
- /************************************************************************
- * file name : blocks_graphics_view.cpp
- * ----------------- :
- * creation time : 2016/06/26
- * author : Victor Zarubkin
- * email : [email protected]
- * ----------------- :
- * description : The file contains implementation of GraphicsScene and GraphicsView and
- * : it's auxiliary classes for displyaing easy_profiler blocks tree.
- * ----------------- :
- * change log : * 2016/06/26 Victor Zarubkin: Moved sources from graphics_view.h
- * : and renamed classes from My* to Prof*.
- * :
- * : * 2016/06/27 Victor Zarubkin: Added text shifting relatively to it's parent item.
- * : Disabled border lines painting because of vertical lines painting bug.
- * : Changed height of blocks. Variable thread-block height.
- * :
- * : * 2016/06/29 Victor Zarubkin: Highly optimized painting performance and memory consumption.
- * :
- * : * 2016/06/30 Victor Zarubkin: Replaced doubles with floats (in ProfBlockItem) for less memory consumption.
- * :
- * : * 2016/09/15 Victor Zarubkin: Moved sources of EasyGraphicsItem and EasyChronometerItem to separate files.
- * :
- * : *
- * ----------------- :
- * license : Lightweight profiler library for c++
- * : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
- * :
- * : Licensed under either of
- * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
- * : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- * : at your option.
- * :
- * : The MIT License
- * :
- * : Permission is hereby granted, free of charge, to any person obtaining a copy
- * : of this software and associated documentation files (the "Software"), to deal
- * : in the Software without restriction, including without limitation the rights
- * : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * : of the Software, and to permit persons to whom the Software is furnished
- * : to do so, subject to the following conditions:
- * :
- * : The above copyright notice and this permission notice shall be included in all
- * : copies or substantial portions of the Software.
- * :
- * : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- * : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- * : USE OR OTHER DEALINGS IN THE SOFTWARE.
- * :
- * : The Apache License, Version 2.0 (the "License")
- * :
- * : You may not use this file except in compliance with the License.
- * : You may obtain a copy of the License at
- * :
- * : http://www.apache.org/licenses/LICENSE-2.0
- * :
- * : Unless required by applicable law or agreed to in writing, software
- * : distributed under the License is distributed on an "AS IS" BASIS,
- * : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * : See the License for the specific language governing permissions and
- * : limitations under the License.
- ************************************************************************/
- #include <math.h>
- #include <QGraphicsScene>
- #include <QGraphicsProxyWidget>
- #include <QWheelEvent>
- #include <QMouseEvent>
- #include <QKeyEvent>
- #include <QScrollBar>
- #include <QGridLayout>
- #include <QDebug>
- #include <QSignalBlocker>
- #include <QGraphicsDropShadowEffect>
- #include "blocks_graphics_view.h"
- #include "easy_graphics_item.h"
- #include "easy_chronometer_item.h"
- #include "easy_graphics_scrollbar.h"
- #include "globals.h"
- //////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////
- const qreal MIN_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT_INV, 70); // Up to 1000 sec scale
- const qreal MAX_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT, 45); // ~23000 --- Up to 10 ns scale
- const qreal BASE_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT_INV, 25); // ~0.003
- const uint16_t TIMELINE_ROW_SIZE = 20;
- const QRgb BACKGROUND_1 = ::profiler::colors::Grey300;
- const QRgb BACKGROUND_2 = ::profiler::colors::White;
- const QRgb TIMELINE_BACKGROUND = 0x20000000 | (::profiler::colors::Grey800 & 0x00ffffff);// 0x20303030;
- const int IDLE_TIMER_INTERVAL = 200; // 5Hz
- const uint64_t IDLE_TIME = 400;
- const int FLICKER_INTERVAL = 10; // 100Hz
- const qreal FLICKER_FACTOR = 16.0 / FLICKER_INTERVAL;
- const auto BG_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Bold);
- const auto CHRONOMETER_FONT = ::profiler_gui::EFont("Helvetica", 16, QFont::Bold);
- #ifdef max
- #undef max
- #endif
- #ifdef min
- #undef min
- #endif
- //////////////////////////////////////////////////////////////////////////
- inline int sign(int _value) { return _value < 0 ? -1 : 1; }
- inline int absmin(int _a, int _b) { return abs(_a) < abs(_b) ? _a : _b; }
- inline qreal clamp(qreal _minValue, qreal _value, qreal _maxValue) { return _value < _minValue ? _minValue : (_value > _maxValue ? _maxValue : _value); }
- //////////////////////////////////////////////////////////////////////////
- template <int N, class T>
- inline T logn(T _value)
- {
- static const double div = 1.0 / log2((double)N);
- return log2(_value) * div;
- }
- //////////////////////////////////////////////////////////////////////////
- EasyBoldLabel::EasyBoldLabel(const QString& _text, QWidget* _parent) : QLabel(_text, _parent)
- {
- auto f = font();
- f.setBold(true);
- setFont(f);
- }
- EasyBoldLabel::~EasyBoldLabel()
- {
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
- {
- auto const sceneView = static_cast<EasyGraphicsView*>(scene()->parent());
- const auto visibleSceneRect = sceneView->visibleSceneRect();
- const auto currentScale = sceneView->scale();
- const auto offset = sceneView->offset();
- const auto left = offset * currentScale;
- const auto h = visibleSceneRect.height();
- const auto visibleBottom = h - 1;
- QRectF rect;
- _painter->save();
- _painter->setTransform(QTransform::fromTranslate(-x(), -y()));
- const auto& items = sceneView->getItems();
- if (!items.empty())
- {
- static const uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
- static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)};
- int i = -1;
- // Draw background
- _painter->setPen(Qt::NoPen);
- for (auto item : items)
- {
- ++i;
- auto br = item->boundingRect();
- auto top = item->y() + br.top() - visibleSceneRect.top();
- auto bottom = top + br.height();
- if (top > h || bottom < 0)
- continue;
- if (item->threadId() == EASY_GLOBALS.selected_thread)
- _painter->setBrush(QBrush(QColor::fromRgb(::profiler_gui::SELECTED_THREAD_BACKGROUND)));
- else
- _painter->setBrush(brushes[i & 1]);
- rect.setRect(0, top - OVERLAP, visibleSceneRect.width(), br.height() + ::profiler_gui::THREADS_ROW_SPACING);
- const auto dh = rect.bottom() - visibleBottom;
- if (dh > 0)
- rect.setHeight(rect.height() - dh);
- if (rect.top() < 0)
- rect.setTop(0);
- _painter->drawRect(rect);
- }
- }
- // Draw timeline scale marks ----------------
- _painter->setBrush(QColor::fromRgba(TIMELINE_BACKGROUND));
- const auto sceneStep = sceneView->timelineStep();
- const auto factor = ::profiler_gui::timeFactor(sceneStep);
- const auto step = sceneStep * currentScale;
- auto first = static_cast<quint64>(offset / sceneStep);
- const int odd = first & 1;
- const auto nsteps = (1 + odd) * 2 + static_cast<int>(visibleSceneRect.width() / step);
- first -= odd;
- QPen pen(Qt::darkGray);
- pen.setWidth(2);
- _painter->setPen(pen);
- _painter->drawLine(QPointF(0, h), QPointF(visibleSceneRect.width(), h));
- _painter->setPen(Qt::darkGray);
- QLineF marks[20];
- qreal first_x = first * sceneStep;
- const auto textWidth = QFontMetricsF(_painter->font(), sceneView).width(QString::number(static_cast<quint64>(0.5 + first_x * factor))) * ::profiler_gui::FONT_METRICS_FACTOR + 10;
- const int n = 1 + static_cast<int>(textWidth / step);
- int next = first % n;
- if (next)
- next = n - next;
- first_x *= currentScale;
- for (int i = 0; i < nsteps; ++i, --next)
- {
- auto current = first_x - left + step * i;
- if ((i & 1) == 0)
- {
- rect.setRect(current, 0, step, h);
- _painter->drawRect(rect);
- for (int j = 0; j < 20; ++j)
- {
- auto xmark = current + j * step * 0.1;
- marks[j].setLine(xmark, h, xmark, h + ((j % 5) ? 4 : 8));
- }
- _painter->drawLines(marks, 20);
- }
- if (next <= 0)
- {
- next = n;
- _painter->setPen(Qt::black);
- _painter->drawText(QPointF(current + 1, h + 17), QString::number(static_cast<quint64>(0.5 + (current + left) * factor / currentScale)));
- _painter->setPen(Qt::darkGray);
- }
- // TEST
- // this is for testing (order of lines will be painted):
- //_painter->setPen(Qt::black);
- //_painter->drawText(QPointF(current + step * 0.4, h - 20), QString::number(i));
- //_painter->setPen(Qt::gray);
- // TEST
- }
- // END Draw timeline scale marks ~~~~~~~~~~~~
- _painter->restore();
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyTimelineIndicatorItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
- {
- const auto sceneView = static_cast<const EasyGraphicsView*>(scene()->parent());
- const auto visibleSceneRect = sceneView->visibleSceneRect();
- const auto step = sceneView->timelineStep() * sceneView->scale();
- const QString text = ::profiler_gui::autoTimeStringInt(units2microseconds(sceneView->timelineStep())); // Displayed text
- // Draw scale indicator
- _painter->save();
- _painter->setTransform(QTransform::fromTranslate(-x(), -y()));
- //_painter->setCompositionMode(QPainter::CompositionMode_Difference);
- _painter->setBrush(Qt::NoBrush);
- QPen pen(Qt::black);
- pen.setWidth(3);
- _painter->setPen(pen);
- _painter->drawLine(QLineF(visibleSceneRect.width() - 9 - step, visibleSceneRect.height() - 10, visibleSceneRect.width() - 11, visibleSceneRect.height() - 10));
- _painter->setPen(Qt::black);
- _painter->drawLine(QLineF(visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 6, visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 14));
- _painter->drawLine(QLineF(visibleSceneRect.width() - 10, visibleSceneRect.height() - 6, visibleSceneRect.width() - 10, visibleSceneRect.height() - 14));
- _painter->setPen(Qt::black);
- _painter->setFont(BG_FONT);
- _painter->drawText(QRectF(visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 63, step, 50), Qt::AlignRight | Qt::AlignBottom | Qt::TextDontClip, text);
- _painter->restore();
- }
- //////////////////////////////////////////////////////////////////////////
- EasyGraphicsView::EasyGraphicsView(QWidget* _parent)
- : Parent(_parent)
- , m_beginTime(::std::numeric_limits<decltype(m_beginTime)>::max())
- , m_sceneWidth(0)
- , m_scale(1)
- , m_offset(0)
- , m_timelineStep(0)
- , m_idleTime(0)
- , m_mouseButtons(Qt::NoButton)
- , m_pScrollbar(nullptr)
- , m_chronometerItem(nullptr)
- , m_chronometerItemAux(nullptr)
- , m_popupWidget(nullptr)
- , m_flickerSpeedX(0)
- , m_flickerSpeedY(0)
- , m_flickerCounterX(0)
- , m_flickerCounterY(0)
- , m_bDoubleClick(false)
- , m_bUpdatingRect(false)
- , m_bEmpty(true)
- {
- initMode();
- setScene(new QGraphicsScene(this));
- updateVisibleSceneRect();
- }
- EasyGraphicsView::~EasyGraphicsView()
- {
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::removePopup(bool _removeFromScene)
- {
- if (m_popupWidget != nullptr)
- {
- auto widget = m_popupWidget->widget();
- widget->setParent(nullptr);
- m_popupWidget->setWidget(nullptr);
- delete widget;
- if (_removeFromScene)
- scene()->removeItem(m_popupWidget);
- m_popupWidget = nullptr;
- }
- }
- //////////////////////////////////////////////////////////////////////////
- qreal EasyGraphicsView::sceneWidth() const
- {
- return m_sceneWidth;
- }
- qreal EasyGraphicsView::chronoTime() const
- {
- return m_chronometerItem->width();
- }
- qreal EasyGraphicsView::chronoTimeAux() const
- {
- return m_chronometerItemAux->width();
- }
- //////////////////////////////////////////////////////////////////////////
- EasyChronometerItem* EasyGraphicsView::createChronometer(bool _main)
- {
- auto chronoItem = new EasyChronometerItem(_main);
- chronoItem->setColor(_main ? ::profiler_gui::CHRONOMETER_COLOR : ::profiler_gui::CHRONOMETER_COLOR2);
- chronoItem->setBoundingRect(sceneRect());
- chronoItem->hide();
- scene()->addItem(chronoItem);
- return chronoItem;
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::clear()
- {
- const QSignalBlocker blocker(this), sceneBlocker(scene()); // block all scene signals (otherwise clear() would be extremely slow!)
- // Stop flicking
- m_flickerTimer.stop();
- m_flickerSpeedX = 0;
- m_flickerSpeedY = 0;
- m_flickerCounterX = 0;
- m_flickerCounterY = 0;
- // Clear all items
- removePopup();
- scene()->clear();
- m_items.clear();
- m_selectedBlocks.clear();
- m_beginTime = ::std::numeric_limits<decltype(m_beginTime)>::max(); // reset begin time
- m_scale = 1; // scale back to initial 100% scale
- m_timelineStep = 1;
- m_offset = 0; // scroll back to the beginning of the scene
- m_idleTimer.stop();
- m_idleTime = 0;
- // Reset necessary flags
- m_bEmpty = true;
- m_sceneWidth = 10;
- setSceneRect(0, 0, 10, 10);
- // notify ProfTreeWidget that selection was reset
- emit intervalChanged(m_selectedBlocks, m_beginTime, 0, 0, false);
- }
- void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTree)
- {
- // clear scene
- clear();
- if (_blocksTree.empty())
- {
- return;
- }
- auto bgItem = new EasyBackgroundItem();
- scene()->addItem(bgItem);
- // set new blocks tree
- // calculate scene size and fill it with items
- // Calculating start and end time
- ::profiler::timestamp_t finish = 0, busyTime = 0;
- ::profiler::thread_id_t longestTree = 0, mainTree = 0;
- for (const auto& threadTree : _blocksTree)
- {
- const auto& t = threadTree.second;
- auto timestart = m_beginTime;
- auto timefinish = finish;
-
- if (!t.children.empty())
- timestart = blocksTree(t.children.front()).node->begin();
- if (!t.sync.empty())
- timestart = ::std::min(timestart, blocksTree(t.sync.front()).node->begin());
- if (!t.children.empty())
- timefinish = blocksTree(t.children.back()).node->end();
- if (!t.sync.empty())
- timefinish = ::std::max(timefinish, blocksTree(t.sync.back()).node->end());
- if (m_beginTime > timestart)
- m_beginTime = timestart;
- if (finish < timefinish)
- finish = timefinish;
- if (t.profiled_time > busyTime) {
- busyTime = t.profiled_time;
- longestTree = threadTree.first;
- }
- if (mainTree == 0 && !strcmp(t.name(), "Main"))
- mainTree = threadTree.first;
- }
- const decltype(m_beginTime) additional_offset = (finish - m_beginTime) / 20; // Additional 5% before first block and after last block
- finish += additional_offset;
- m_beginTime -= ::std::min(m_beginTime, additional_offset);
- EASY_GLOBALS.begin_time = m_beginTime;
- // Sort threads by name
- ::std::vector<::std::reference_wrapper<const ::profiler::BlocksTreeRoot> > sorted_roots;
- sorted_roots.reserve(_blocksTree.size());
- for (const auto& threadTree : _blocksTree)
- sorted_roots.push_back(threadTree.second);
- ::std::sort(sorted_roots.begin(), sorted_roots.end(), [](const ::profiler::BlocksTreeRoot& _a, const ::profiler::BlocksTreeRoot& _b) {
- return _a.thread_name < _b.thread_name;
- });
- // Filling scene with items
- m_items.reserve(_blocksTree.size());
- qreal y = TIMELINE_ROW_SIZE;
- const EasyGraphicsItem *longestItem = nullptr, *mainThreadItem = nullptr;
- for (const ::profiler::BlocksTreeRoot& t : sorted_roots)
- {
- if (m_items.size() == 0xff)
- {
- qWarning() << "Warning: Maximum threads number (255 threads) exceeded! See EasyGraphicsView::setTree() : " << __LINE__ << " in file " << __FILE__;
- break;
- }
- // fill scene with new items
- qreal h = 0, x = 0;
-
- if (!t.children.empty())
- x = time2position(blocksTree(t.children.front()).node->begin());
- else if (!t.sync.empty())
- x = time2position(blocksTree(t.sync.front()).node->begin());
- auto item = new EasyGraphicsItem(static_cast<uint8_t>(m_items.size()), t);
- if (t.depth)
- item->setLevels(t.depth);
- item->setPos(0, y);
- qreal children_duration = 0;
- if (!t.children.empty())
- {
- uint32_t dummy = 0;
- children_duration = setTree(item, t.children, h, dummy, y, 0);
- }
- else
- {
- if (!t.sync.empty())
- children_duration = time2position(blocksTree(t.sync.back()).node->end()) - x;
- h = ::profiler_gui::GRAPHICS_ROW_SIZE;
- }
- item->setBoundingRect(0, 0, children_duration + x, h);
- m_items.push_back(item);
- scene()->addItem(item);
- y += h + ::profiler_gui::THREADS_ROW_SPACING;
- if (longestTree == t.thread_id)
- longestItem = item;
- if (mainTree == t.thread_id)
- mainThreadItem = item;
- }
- // Calculating scene rect
- m_sceneWidth = time2position(finish);
- setSceneRect(0, 0, m_sceneWidth, y + TIMELINE_ROW_SIZE);
- // Center view on the beginning of the scene
- 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_chronometerItemAux = createChronometer(false);
- m_chronometerItem = createChronometer(true);
- bgItem->setBoundingRect(0, 0, m_sceneWidth, y);
- auto indicator = new EasyTimelineIndicatorItem();
- indicator->setBoundingRect(0, 0, m_sceneWidth, y);
- scene()->addItem(indicator);
- // Setting flags
- m_bEmpty = false;
- scaleTo(BASE_SCALE);
- emit treeChanged();
- if (mainThreadItem != nullptr)
- {
- longestItem = mainThreadItem;
- }
- if (longestItem != nullptr)
- {
- EASY_GLOBALS.selected_thread = longestItem->threadId();
- emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId());
- scrollTo(longestItem);
- m_pScrollbar->setHistogramSource(longestItem->threadId(), longestItem->items(0));
- if (!longestItem->items(0).empty())
- m_pScrollbar->setValue(longestItem->items(0).front().left() - m_pScrollbar->sliderWidth() * 0.25);
- }
- m_idleTimer.start(IDLE_TIMER_INTERVAL);
- }
- const EasyGraphicsView::Items &EasyGraphicsView::getItems() const
- {
- return m_items;
- }
- qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, const ::profiler::BlocksTree::children_t& _children, qreal& _height, uint32_t& _maxDepthChild, qreal _y, short _level)
- {
- if (_children.empty())
- {
- return 0;
- }
- const auto level = static_cast<uint8_t>(_level);
- const auto n = static_cast<unsigned int>(_children.size());
- _item->reserve(level, n);
- _maxDepthChild = 0;
- uint8_t maxDepth = 0;
- const short next_level = _level + 1;
- bool warned = false;
- qreal total_duration = 0, prev_end = 0, maxh = 0;
- qreal start_time = -1;
- uint32_t j = 0;
- for (auto child_index : _children)
- {
- auto& gui_block = easyBlock(child_index);
- const auto& child = gui_block.tree;
- if (child.depth > maxDepth)
- {
- maxDepth = child.depth;
- _maxDepthChild = j;
- }
- auto xbegin = time2position(child.node->begin());
- if (start_time < 0)
- {
- start_time = xbegin;
- }
- auto duration = time2position(child.node->end()) - xbegin;
- //const auto dt = xbegin - prev_end;
- //if (dt < 0)
- //{
- // duration += dt;
- // xbegin -= dt;
- //}
- //static const qreal MIN_DURATION = 0.25;
- //if (duration < MIN_DURATION)
- // duration = MIN_DURATION;
- const auto i = _item->addItem(level);
- auto& b = _item->getItem(level, i);
- gui_block.graphics_item = _item->index();
- gui_block.graphics_item_level = level;
- gui_block.graphics_item_index = i;
- if (next_level < 256 && next_level < _item->levels() && !child.children.empty())
- {
- b.children_begin = static_cast<unsigned int>(_item->items(static_cast<uint8_t>(next_level)).size());
- }
- else
- {
- ::profiler_gui::set_max(b.children_begin);
- }
- qreal h = 0;
- qreal children_duration = 0;
- uint32_t maxDepthChild = 0;
- if (next_level < 256)
- {
- children_duration = setTree(_item, child.children, h, maxDepthChild, _y + ::profiler_gui::GRAPHICS_ROW_SIZE_FULL, next_level);
- }
- else if (!child.children.empty() && !warned)
- {
- warned = true;
- qWarning() << "Warning: Maximum blocks depth (255) exceeded! See EasyGraphicsView::setTree() : " << __LINE__ << " in file " << __FILE__;
- }
- if (duration < children_duration)
- {
- duration = children_duration;
- }
- if (h > maxh)
- {
- maxh = h;
- }
- b.block = child_index;// &child;
- #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
- b.neighbours = n;
- b.state = j > 0 || level == 0 ? 0 : -1;
- #else
- b.max_depth_child = maxDepthChild;
- #endif
- b.setPos(xbegin, duration);
- //b.totalHeight = ::profiler_gui::GRAPHICS_ROW_SIZE + h;
- prev_end = xbegin + duration;
- total_duration = prev_end - start_time;
- ++j;
- }
- _height += ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + maxh;
- return total_duration;
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::setScrollbar(EasyGraphicsScrollbar* _scrollbar)
- {
- auto const prevScrollbar = m_pScrollbar;
- const bool makeConnect = prevScrollbar == nullptr || prevScrollbar != _scrollbar;
- if (prevScrollbar != nullptr && prevScrollbar != _scrollbar)
- {
- disconnect(prevScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange);
- disconnect(prevScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel);
- }
- m_pScrollbar = _scrollbar;
- m_pScrollbar->clear();
- m_pScrollbar->setRange(0, m_sceneWidth);
- auto vbar = verticalScrollBar();
- const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0);
- m_pScrollbar->setSliderWidth(m_visibleSceneRect.width() + vbar_width);
- if (makeConnect)
- {
- connect(m_pScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange);
- connect(m_pScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel);
- }
- EASY_GLOBALS.selected_thread = 0;
- emit EASY_GLOBALS.events.selectedThreadChanged(0);
- }
- //////////////////////////////////////////////////////////////////////////
- int EasyGraphicsView::updateVisibleSceneRect()
- {
- m_visibleSceneRect = mapToScene(rect()).boundingRect();
- auto vbar = verticalScrollBar();
- int vbar_width = 0;
- if (vbar && vbar->isVisible())
- vbar_width = vbar->width() + 2;
- m_visibleSceneRect.setWidth(m_visibleSceneRect.width() - vbar_width);
- m_visibleSceneRect.setHeight(m_visibleSceneRect.height() - TIMELINE_ROW_SIZE);
- return vbar_width;
- }
- void EasyGraphicsView::updateTimelineStep(qreal _windowWidth)
- {
- const auto time = units2microseconds(_windowWidth);
- if (time < 100)
- m_timelineStep = 1e-2;
- else if (time < 10e3)
- m_timelineStep = 1;
- else if (time < 10e6)
- m_timelineStep = 1e3;
- else
- m_timelineStep = 1e6;
- const auto optimal_steps = static_cast<int>(40 * m_visibleSceneRect.width() / 1500);
- auto steps = time / m_timelineStep;
- while (steps > optimal_steps) {
- m_timelineStep *= 10;
- steps *= 0.1;
- }
- m_timelineStep = microseconds2units(m_timelineStep);
- }
- void EasyGraphicsView::repaintScene()
- {
- scene()->update(m_visibleSceneRect);
- emit sceneUpdated();
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::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);
- const int vbar_width = updateVisibleSceneRect();
- // Update slider width for scrollbar
- const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
- m_pScrollbar->setSliderWidth(windowWidth);
- updateTimelineStep(windowWidth);
- repaintScene();
- }
- void EasyGraphicsView::wheelEvent(QWheelEvent* _event)
- {
- m_idleTime = 0;
- if (!m_bEmpty)
- onWheel(mapToScene(_event->pos()).x(), _event->delta());
- _event->accept();
- }
- void EasyGraphicsView::onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta)
- {
- m_idleTime = 0;
- for (auto item : m_items)
- {
- if (item->threadId() == EASY_GLOBALS.selected_thread)
- {
- scrollTo(item);
- break;
- }
- }
- onWheel(_mouseX, _wheelDelta);
- }
- void EasyGraphicsView::scrollTo(const EasyGraphicsItem* _item)
- {
- m_bUpdatingRect = true;
- auto vbar = verticalScrollBar();
- vbar->setValue(_item->y() + (_item->boundingRect().height() - vbar->pageStep()) * 0.5);
- m_bUpdatingRect = false;
- }
- void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta)
- {
- const decltype(m_scale) scaleCoeff = _wheelDelta > 0 ? ::profiler_gui::SCALING_COEFFICIENT : ::profiler_gui::SCALING_COEFFICIENT_INV;
- // Remember current mouse position
- _mouseX = clamp(0., _mouseX, m_sceneWidth);
- const auto mousePosition = m_offset + _mouseX / m_scale;
- // 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
- // Update slider width for scrollbar
- auto vbar = verticalScrollBar();
- const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0);
- const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
- m_pScrollbar->setSliderWidth(windowWidth);
- // Calculate new offset to simulate QGraphicsView::AnchorUnderMouse scaling behavior
- m_offset = clamp(0., mousePosition - _mouseX / m_scale, m_sceneWidth - windowWidth);
- // Update slider position
- m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
- m_pScrollbar->setValue(m_offset);
- m_bUpdatingRect = false;
- updateVisibleSceneRect(); // Update scene rect
- updateTimelineStep(windowWidth);
- repaintScene(); // repaint scene
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::mousePressEvent(QMouseEvent* _event)
- {
- m_idleTime = 0;
- if (m_bEmpty)
- {
- _event->accept();
- return;
- }
- m_mouseButtons = _event->buttons();
- m_mousePressPos = _event->pos();
- if (m_mouseButtons & Qt::LeftButton)
- {
- if (m_chronometerItemAux->isVisible() && (m_chronometerItemAux->hoverLeft() || m_chronometerItemAux->hoverRight()))
- {
- m_chronometerItemAux->setReverse(m_chronometerItemAux->hoverLeft());
- m_bDoubleClick = true;
- }
- else if (m_chronometerItem->isVisible() && (m_chronometerItem->hoverLeft() || m_chronometerItem->hoverRight()))
- {
- m_chronometerItem->setReverse(m_chronometerItem->hoverLeft());
- m_mouseButtons = Qt::RightButton;
- return;
- }
- }
- if (m_mouseButtons & Qt::RightButton)
- {
- if (m_chronometerItem->isVisible() && (m_chronometerItem->hoverLeft() || m_chronometerItem->hoverRight()))
- {
- m_chronometerItem->setReverse(m_chronometerItem->hoverLeft());
- }
- else
- {
- const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale;
- m_chronometerItem->setLeftRight(mouseX, mouseX);
- m_chronometerItem->hide();
- m_pScrollbar->hideChrono();
- }
- }
- _event->accept();
- }
- void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event)
- {
- m_idleTime = 0;
- if (m_bEmpty)
- {
- _event->accept();
- return;
- }
- m_mouseButtons = _event->buttons();
- m_mousePressPos = _event->pos();
- m_bDoubleClick = true;
- if (m_mouseButtons & Qt::LeftButton)
- {
- const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale;
- m_chronometerItemAux->setLeftRight(mouseX, mouseX);
- m_chronometerItemAux->hide();
- emit sceneUpdated();
- }
- _event->accept();
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
- {
- m_idleTime = 0;
- if (m_bEmpty)
- {
- _event->accept();
- return;
- }
- bool chronoHidden = false;
- bool changedSelection = false, changedSelectedItem = false;
- if (m_mouseButtons & Qt::RightButton)
- {
- if (m_chronometerItem->isVisible() && m_chronometerItem->width() < 1e-6)
- {
- m_chronometerItem->hide();
- m_pScrollbar->hideChrono();
- }
- if (!m_selectedBlocks.empty())
- {
- changedSelection = true;
- m_selectedBlocks.clear();
- }
- if (m_chronometerItem->isVisible())
- {
- //printf("INTERVAL: {%lf, %lf} ms\n", m_chronometerItem->left(), m_chronometerItem->right());
- for (auto item : m_items)
- {
- if (!EASY_GLOBALS.only_current_thread_hierarchy || item->threadId() == EASY_GLOBALS.selected_thread)
- item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks);
- }
- if (!m_selectedBlocks.empty())
- {
- changedSelection = true;
- }
- }
- }
- const ::profiler_gui::EasyBlock* selectedBlock = nullptr;
- ::profiler::thread_id_t selectedBlockThread = 0;
- const auto previouslySelectedBlock = EASY_GLOBALS.selected_block;
- if (m_mouseButtons & Qt::LeftButton)
- {
- bool clicked = false;
- if (m_chronometerItemAux->isVisible() && m_chronometerItemAux->width() < 1e-6)
- {
- chronoHidden = true;
- m_chronometerItemAux->hide();
- }
- else if (m_chronometerItem->isVisible() && m_chronometerItem->hoverIndicator())
- {
- // Jump to selected zone
- clicked = true;
- m_flickerSpeedX = m_flickerSpeedY = 0;
- m_pScrollbar->setValue(m_chronometerItem->left() + m_chronometerItem->width() * 0.5 - m_pScrollbar->sliderHalfWidth());
- }
- if (!clicked && m_mouseMovePath.manhattanLength() < 5)
- {
- // Handle Click
- //clicked = true;
- auto mouseClickPos = mapToScene(m_mousePressPos);
- if (mouseClickPos.x() >= 0)
- {
- mouseClickPos.setX(m_offset + mouseClickPos.x() / m_scale);
- // Try to select one of item blocks
- for (auto item : m_items)
- {
- ::profiler::block_index_t i = ~0U;
- auto block = item->intersect(mouseClickPos, i);
- if (block)
- {
- 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 && !::profiler_gui::is_max(EASY_GLOBALS.selected_block))
- {
- changedSelectedItem = true;
- ::profiler_gui::set_max(EASY_GLOBALS.selected_block);
- ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
- }
- }
- }
- }
- m_bDoubleClick = false;
- m_mouseButtons = _event->buttons();
- m_mouseMovePath = QPoint();
- _event->accept();
- if (changedSelection)
- {
- emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
- }
- if (changedSelectedItem)
- {
- m_bUpdatingRect = true;
- if (selectedBlock != nullptr && previouslySelectedBlock == EASY_GLOBALS.selected_block && !selectedBlock->tree.children.empty())
- {
- EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded = !EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded;
- emit EASY_GLOBALS.events.itemsExpandStateChanged();
- }
- emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block);
- if (EASY_GLOBALS.selecting_block_changes_thread && selectedBlock != nullptr && EASY_GLOBALS.selected_thread != selectedBlockThread)
- {
- EASY_GLOBALS.selected_thread = selectedBlockThread;
- m_pScrollbar->lock();
- emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread);
- m_pScrollbar->unlock();
- }
- m_bUpdatingRect = false;
- if (selectedBlock != nullptr && selectedBlockThread == EASY_GLOBALS.selected_thread)
- m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id);
- else
- {
- for (auto item : m_items)
- {
- if (item->threadId() == EASY_GLOBALS.selected_thread)
- {
- m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0));
- break;
- }
- }
- }
- repaintScene();
- }
- else if (chronoHidden)
- {
- emit sceneUpdated();
- }
- }
- //////////////////////////////////////////////////////////////////////////
- bool EasyGraphicsView::moveChrono(EasyChronometerItem* _chronometerItem, qreal _mouseX)
- {
- if (_chronometerItem->reverse())
- {
- if (_mouseX > _chronometerItem->right())
- {
- _chronometerItem->setReverse(false);
- _chronometerItem->setLeftRight(_chronometerItem->right(), _mouseX);
- if (_chronometerItem->hoverLeft())
- {
- _chronometerItem->setHoverLeft(false);
- _chronometerItem->setHoverRight(true);
- }
- }
- else
- {
- _chronometerItem->setLeftRight(_mouseX, _chronometerItem->right());
- }
- }
- else
- {
- if (_mouseX < _chronometerItem->left())
- {
- _chronometerItem->setReverse(true);
- _chronometerItem->setLeftRight(_mouseX, _chronometerItem->left());
- if (_chronometerItem->hoverRight())
- {
- _chronometerItem->setHoverLeft(true);
- _chronometerItem->setHoverRight(false);
- }
- }
- else
- {
- _chronometerItem->setLeftRight(_chronometerItem->left(), _mouseX);
- }
- }
- if (!_chronometerItem->isVisible() && _chronometerItem->width() > 1e-6)
- {
- _chronometerItem->show();
- return true;
- }
- return false;
- }
- void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event)
- {
- m_idleTime = 0;
- if (m_bEmpty || (m_mouseButtons == 0 && !m_chronometerItem->isVisible() && !m_chronometerItemAux->isVisible()))
- {
- _event->accept();
- return;
- }
- bool needUpdate = false;
- const auto pos = _event->pos();
- const auto delta = pos - m_mousePressPos;
- m_mousePressPos = pos;
- if (m_mouseButtons != 0)
- {
- m_mouseMovePath.setX(m_mouseMovePath.x() + qAbs(delta.x()));
- m_mouseMovePath.setY(m_mouseMovePath.y() + qAbs(delta.y()));
- }
- auto mouseScenePos = mapToScene(m_mousePressPos);
- mouseScenePos.setX(m_offset + mouseScenePos.x() / m_scale);
- const auto x = clamp(0., mouseScenePos.x(), m_sceneWidth);
- if (m_mouseButtons & Qt::RightButton)
- {
- bool showItem = moveChrono(m_chronometerItem, x);
- m_pScrollbar->setChronoPos(m_chronometerItem->left(), m_chronometerItem->right());
- if (showItem)
- {
- m_pScrollbar->showChrono();
- }
- needUpdate = true;
- }
- if (m_mouseButtons & Qt::LeftButton)
- {
- if (m_bDoubleClick)
- {
- moveChrono(m_chronometerItemAux, x);
- }
- else
- {
- auto vbar = verticalScrollBar();
- m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once
- vbar->setValue(vbar->value() - delta.y());
- m_pScrollbar->setValue(m_pScrollbar->value() - delta.x() / m_scale);
- m_bUpdatingRect = false;
- // Seems like an ugly stub, but QSignalBlocker is also a bad decision
- // because if scrollbar does not emit valueChanged signal then viewport does not move
- updateVisibleSceneRect(); // Update scene visible rect only once
- // Update flicker speed
- m_flickerSpeedX += delta.x() >> 1;
- m_flickerSpeedY += delta.y();
- if (!m_flickerTimer.isActive())
- {
- // If flicker timer is not started, then start it
- m_flickerTimer.start(FLICKER_INTERVAL);
- }
- }
- needUpdate = true;
- }
- if (m_mouseButtons == 0)
- {
- if (m_chronometerItem->isVisible())
- {
- auto prevValue = m_chronometerItem->hoverIndicator();
- m_chronometerItem->setHoverIndicator(m_chronometerItem->indicatorContains(mouseScenePos));
- needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverIndicator());
- prevValue = m_chronometerItem->hoverLeft();
- m_chronometerItem->setHoverLeft(m_chronometerItem->hoverLeft(mouseScenePos.x()));
- needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverLeft());
- if (!m_chronometerItem->hoverLeft())
- {
- prevValue = m_chronometerItem->hoverRight();
- m_chronometerItem->setHoverRight(m_chronometerItem->hoverRight(mouseScenePos.x()));
- needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverRight());
- }
- }
- if (m_chronometerItemAux->isVisible())
- {
- auto prevValue = m_chronometerItemAux->hoverLeft();
- m_chronometerItemAux->setHoverLeft(m_chronometerItemAux->hoverLeft(mouseScenePos.x()));
- needUpdate = needUpdate || (prevValue != m_chronometerItemAux->hoverLeft());
- if (!m_chronometerItemAux->hoverLeft())
- {
- prevValue = m_chronometerItemAux->hoverRight();
- m_chronometerItemAux->setHoverRight(m_chronometerItemAux->hoverRight(mouseScenePos.x()));
- needUpdate = needUpdate || (prevValue != m_chronometerItemAux->hoverRight());
- }
- }
- }
- if (needUpdate)
- {
- repaintScene(); // repaint scene
- }
- _event->accept();
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::keyPressEvent(QKeyEvent* _event)
- {
- static const int KeyStep = 100;
- const int key = _event->key();
- m_idleTime = 0;
- switch (key)
- {
- case Qt::Key_Right:
- case Qt::Key_6:
- {
- m_pScrollbar->setValue(m_pScrollbar->value() + KeyStep / m_scale);
- break;
- }
- case Qt::Key_Left:
- case Qt::Key_4:
- {
- m_pScrollbar->setValue(m_pScrollbar->value() - KeyStep / m_scale);
- break;
- }
- case Qt::Key_Up:
- case Qt::Key_8:
- {
- auto vbar = verticalScrollBar();
- vbar->setValue(vbar->value() - KeyStep);
- break;
- }
- case Qt::Key_Down:
- case Qt::Key_2:
- {
- auto vbar = verticalScrollBar();
- vbar->setValue(vbar->value() + KeyStep);
- break;
- }
- case Qt::Key_Plus:
- case Qt::Key_Equal:
- {
- onWheel(mapToScene(mapFromGlobal(QCursor::pos())).x(), KeyStep);
- break;
- }
- case Qt::Key_Minus:
- {
- onWheel(mapToScene(mapFromGlobal(QCursor::pos())).x(), -KeyStep);
- break;
- }
- }
- //m_keys.insert(key);
- _event->accept();
- }
- void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event)
- {
- //const int key = _event->key();
- m_idleTime = 0;
- //m_keys.erase(key);
- _event->accept();
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::resizeEvent(QResizeEvent* _event)
- {
- Parent::resizeEvent(_event);
- const QRectF previousRect = m_visibleSceneRect;
- const int vbar_width = updateVisibleSceneRect(); // Update scene visible rect only once
- // Update slider width for scrollbar
- const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
- m_pScrollbar->setSliderWidth(windowWidth);
- // Calculate new offset to save old screen center
- const auto deltaWidth = m_visibleSceneRect.width() - previousRect.width();
- m_offset = clamp(0., m_offset - deltaWidth * 0.5 / m_scale, m_sceneWidth - windowWidth);
- // Update slider position
- m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
- m_pScrollbar->setValue(m_offset);
- m_bUpdatingRect = false;
- repaintScene(); // repaint scene
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::initMode()
- {
- // TODO: find mode with least number of bugs :)
- // There are always some display bugs...
- setCacheMode(QGraphicsView::CacheNone);
- setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
- setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
- setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange);
- connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout);
- connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout);
- auto globalSignals = &EASY_GLOBALS.events;
- connect(globalSignals, &::profiler_gui::EasyGlobalSignals::hierarchyFlagChanged, this, &This::onHierarchyFlagChange);
- connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange);
- 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->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0));
- break;
- }
- }
- }
- else
- {
- m_pScrollbar->setHistogramSource(0, nullptr);
- }
- }
- else
- m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id);
- onRefreshRequired();
- });
- connect(globalSignals, &::profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged);
- connect(globalSignals, &::profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged);
- connect(globalSignals, &::profiler_gui::EasyGlobalSignals::blocksTreeModeChanged, [this]()
- {
- if (!m_selectedBlocks.empty())
- emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
- });
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::onThreadViewChanged()
- {
- if (m_bEmpty)
- return;
- for (auto item : m_items)
- item->validateName();
- emit treeChanged();
- updateVisibleSceneRect();
- onHierarchyFlagChange(EASY_GLOBALS.only_current_thread_hierarchy);
- repaintScene();
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::onScrollbarValueChange(int)
- {
- if (!m_bUpdatingRect && !m_bEmpty)
- updateVisibleSceneRect();
- }
- void EasyGraphicsView::onGraphicsScrollbarValueChange(qreal _value)
- {
- if (!m_bEmpty)
- {
- m_offset = _value;
- if (!m_bUpdatingRect)
- {
- updateVisibleSceneRect();
- repaintScene();
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::onFlickerTimeout()
- {
- ++m_flickerCounterX;
- ++m_flickerCounterY;
- if (m_mouseButtons & Qt::LeftButton)
- {
- // Fast slow-down and stop if mouse button is pressed, no flicking.
- m_flickerSpeedX >>= 1;
- m_flickerSpeedY >>= 1;
- if (m_flickerSpeedX == -1) m_flickerSpeedX = 0;
- if (m_flickerSpeedY == -1) m_flickerSpeedY = 0;
- }
- else
- {
- // Flick when mouse button is not pressed
- auto vbar = verticalScrollBar();
- m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once
- m_pScrollbar->setValue(m_pScrollbar->value() - m_flickerSpeedX / m_scale);
- vbar->setValue(vbar->value() - m_flickerSpeedY);
- m_bUpdatingRect = false;
- // Seems like an ugly stub, but QSignalBlocker is also a bad decision
- // because if scrollbar does not emit valueChanged signal then viewport does not move
- updateVisibleSceneRect(); // Update scene visible rect only once
- repaintScene(); // repaint scene
- const int dx = static_cast<int>(sign(m_flickerSpeedX) * m_flickerCounterX / FLICKER_FACTOR);
- const int dy = static_cast<int>(sign(m_flickerSpeedY) * m_flickerCounterY / FLICKER_FACTOR);
- if (abs(dx) > 0)
- {
- m_flickerSpeedX -= absmin(dx, m_flickerSpeedX);
- m_flickerCounterX = 0;
- }
- if (abs(dy) > 0)
- {
- m_flickerSpeedY -= absmin(dy, m_flickerSpeedY);
- m_flickerCounterY = 0;
- }
- }
- if (m_flickerSpeedX == 0 && m_flickerSpeedY == 0)
- {
- // Flicker stopped, no timer needed.
- m_flickerTimer.stop();
- m_flickerSpeedX = 0;
- m_flickerSpeedY = 0;
- m_flickerCounterX = 0;
- m_flickerCounterY = 0;
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::onIdleTimeout()
- {
- m_idleTime += IDLE_TIMER_INTERVAL;
- if (m_idleTime < IDLE_TIME)
- {
- removePopup(true);
- return;
- }
- if (m_popupWidget != nullptr)
- return;
- auto scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
- if (scenePos.x() < m_visibleSceneRect.left() || scenePos.x() > m_visibleSceneRect.right())
- return;
- if (scenePos.y() < m_visibleSceneRect.top() || scenePos.y() > m_visibleSceneRect.bottom())
- return;
- decltype(scenePos) pos(m_offset + scenePos.x() / m_scale, scenePos.y());
- // Try to select one of context switches or items
- for (auto item : m_items)
- {
- ::profiler::block_index_t i = ~0U;
- auto block = item->intersect(pos, i);
- if (block)
- {
- const auto& itemBlock = block->tree;
- const auto& itemDesc = easyDescriptor(itemBlock.node->id());
- auto name = *itemBlock.node->name() != 0 ? itemBlock.node->name() : itemDesc.name();
- auto widget = new QWidget(nullptr, Qt::FramelessWindowHint);
- if (widget == nullptr)
- return;
- widget->setAttribute(Qt::WA_ShowWithoutActivating, true);
- widget->setFocusPolicy(Qt::NoFocus);
- auto lay = new QGridLayout(widget);
- if (lay == nullptr)
- return;
- int row = 0;
- if (itemDesc.type() == ::profiler::BLOCK_TYPE_BLOCK)
- {
- //lay->addWidget(new QLabel("Name:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new EasyBoldLabel(::profiler_gui::toUnicode(name), widget), row, 0, 1, 5, Qt::AlignHCenter);
- ++row;
- const auto duration = itemBlock.node->duration();
- lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, 1, 3, Qt::AlignLeft);
- ++row;
- ::profiler::timestamp_t children_duration = 0;
- for (auto child : itemBlock.children)
- children_duration += easyBlock(child).tree.node->duration();
- const auto self_duration = duration - children_duration;
- const auto self_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(self_duration, duration);
- lay->addWidget(new QLabel("Self:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, self_duration, 3)).arg(QString::number(self_percent, 'g', 3)), widget), row, 1, 1, 3, Qt::AlignLeft);
- ++row;
- }
- else
- {
- lay->addWidget(new EasyBoldLabel("User defined event", widget), row, 0, 1, 2, Qt::AlignHCenter);
- ++row;
- lay->addWidget(new QLabel("Name:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(::profiler_gui::toUnicode(name), widget), row, 1, Qt::AlignLeft);
- ++row;
- }
- if (itemBlock.per_thread_stats)
- {
- if (itemDesc.type() == ::profiler::BLOCK_TYPE_BLOCK)
- {
- const auto duration = itemBlock.node->duration();
- lay->addWidget(new QLabel("Average:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, itemBlock.per_thread_stats->average_duration(), 3), widget), row, 1, 1, 3, Qt::AlignLeft);
- ++row;
- // Calculate idle/active time
- {
- auto threadRoot = item->root();
- ::profiler::block_index_t ind = 0;
- auto it = ::std::lower_bound(threadRoot->sync.begin(), threadRoot->sync.end(), itemBlock.node->begin(), [](::profiler::block_index_t _cs_index, ::profiler::timestamp_t _val)
- {
- return EASY_GLOBALS.gui_blocks[_cs_index].tree.node->begin() < _val;
- });
- if (it != threadRoot->sync.end())
- {
- ind = it - threadRoot->sync.begin();
- if (ind > 0)
- --ind;
- }
- else
- {
- ind = static_cast<::profiler::block_index_t>(threadRoot->sync.size());
- }
- ::profiler::timestamp_t idleTime = 0;
- for (::profiler::block_index_t ncs = static_cast<::profiler::block_index_t>(threadRoot->sync.size()); ind < ncs; ++ind)
- {
- auto cs_index = threadRoot->sync[ind];
- const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
- if (cs->begin() > itemBlock.node->end())
- break;
- if (itemBlock.node->begin() <= cs->begin() && cs->end() <= itemBlock.node->end())
- idleTime += cs->duration();
- }
- const auto active_time = duration - idleTime;
- const auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
- lay->addWidget(new QLabel("Active time:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, active_time, 3)).arg(QString::number(active_percent, 'g', 3)), widget), row, 1, 1, 3, Qt::AlignLeft);
- ++row;
- }
- lay->addWidget(new EasyBoldLabel("-------- Statistics --------", widget), row, 0, 1, 5, Qt::AlignHCenter);
- lay->addWidget(new QLabel("per ", widget), row + 1, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("This %:", widget), row + 2, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("Sum %:", widget), row + 3, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("Sum self %:", widget), row + 4, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("N Calls:", widget), row + 5, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("Thread", widget), row + 1, 1, Qt::AlignHCenter);
- auto percent = ::profiler_gui::percentReal(duration, item->root()->profiled_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), row + 2, 1, Qt::AlignHCenter);
- lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->profiled_time)), widget), row + 3, 1, Qt::AlignHCenter);
- lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration - itemBlock.per_thread_stats->total_children_duration, item->root()->profiled_time)), widget), row + 4, 1, Qt::AlignHCenter);
- lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row + 5, 1, Qt::AlignHCenter);
- int col = 1;
- if (itemBlock.per_frame_stats->parent_block != i && !::profiler_gui::is_max(itemBlock.per_frame_stats->parent_block))
- {
- ++col;
- auto frame_duration = blocksTree(itemBlock.per_frame_stats->parent_block).node->duration();
- lay->addWidget(new QLabel("Frame", widget), row + 1, col, Qt::AlignHCenter);
- 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), row + 2, 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), row + 3, col, Qt::AlignHCenter);
- percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration - itemBlock.per_frame_stats->total_children_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), row + 4, col, Qt::AlignHCenter);
- lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), row + 5, col, Qt::AlignHCenter);
- }
- if (!::profiler_gui::is_max(itemBlock.per_parent_stats->parent_block))// != item->threadId())
- {
- ++col;
- auto parent_duration = blocksTree(itemBlock.per_parent_stats->parent_block).node->duration();
- lay->addWidget(new QLabel("Parent", widget), row + 1, col, Qt::AlignHCenter);
- 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), row + 2, 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), row + 3, col, Qt::AlignHCenter);
- percent = ::profiler_gui::percentReal(itemBlock.per_parent_stats->total_duration - itemBlock.per_parent_stats->total_children_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), row + 4, col, Qt::AlignHCenter);
- lay->addWidget(new QLabel(QString::number(itemBlock.per_parent_stats->calls_number), widget), row + 5, col, Qt::AlignHCenter);
- ++col;
- }
- }
- else
- {
- lay->addWidget(new QLabel("N calls/Thread:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row, 1, Qt::AlignLeft);
- }
- }
- m_popupWidget = new QGraphicsProxyWidget();
- m_popupWidget->setWidget(widget);
- break;
- }
- auto cse = item->intersectEvent(pos);
- if (cse)
- {
- const auto& itemBlock = cse->tree;
- auto widget = new QWidget(nullptr, Qt::FramelessWindowHint);
- if (widget == nullptr)
- return;
- widget->setAttribute(Qt::WA_ShowWithoutActivating, true);
- widget->setFocusPolicy(Qt::NoFocus);
- auto lay = new QGridLayout(widget);
- if (lay == nullptr)
- return;
- int row = 0;
- lay->addWidget(new EasyBoldLabel("Context switch event", widget), row, 0, 1, 3, Qt::AlignHCenter);
- ++row;
- lay->addWidget(new QLabel("Thread:", widget), row, 0, Qt::AlignRight);
- const char* process_name = "";
- ::profiler::thread_id_t tid = 0;
- if (EASY_GLOBALS.version < ::profiler_gui::V130)
- {
- tid = cse->tree.node->id();
- process_name = cse->tree.node->name();
- }
- else
- {
- tid = cse->tree.cs->tid();
- process_name = cse->tree.cs->name();
- }
- auto it = EASY_GLOBALS.profiler_blocks.find(tid);
- if (it != EASY_GLOBALS.profiler_blocks.end())
- {
- if (EASY_GLOBALS.hex_thread_id)
- lay->addWidget(new QLabel(QString("0x%1 %2").arg(tid, 0, 16).arg(it->second.name()), widget), row, 1, 1, 2, Qt::AlignLeft);
- else
- lay->addWidget(new QLabel(QString("%1 %2").arg(tid).arg(it->second.name()), widget), row, 1, 1, 2, Qt::AlignLeft);
- }
- else if (EASY_GLOBALS.hex_thread_id)
- lay->addWidget(new QLabel(QString("0x%1").arg(tid, 0, 16), widget), row, 1, 1, 2, Qt::AlignLeft);
- else
- lay->addWidget(new QLabel(QString::number(tid), widget), row, 1, 1, 2, Qt::AlignLeft);
- ++row;
- lay->addWidget(new QLabel("Process:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(process_name, widget), row, 1, 1, 2, Qt::AlignLeft);
- ++row;
- const auto duration = itemBlock.node->duration();
- lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, 1, 2, Qt::AlignLeft);
- ++row;
- if (itemBlock.per_thread_stats)
- {
- lay->addWidget(new QLabel("Sum:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, itemBlock.per_thread_stats->total_duration, 3), widget), row, 1, 1, 2, Qt::AlignLeft);
- ++row;
- lay->addWidget(new EasyBoldLabel("-------- Statistics --------", widget), row, 0, 1, 3, Qt::AlignHCenter);
- lay->addWidget(new QLabel("per ", widget), row + 1, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("This %:", widget), row + 2, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("Sum %:", widget), row + 3, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("N Calls:", widget), row + 4, 0, Qt::AlignRight);
- lay->addWidget(new QLabel("Thread", widget), row + 1, 1, Qt::AlignHCenter);
- auto percent = ::profiler_gui::percentReal(duration, item->root()->profiled_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), row + 2, 1, Qt::AlignHCenter);
- lay->addWidget(new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->profiled_time)), widget), row + 3, 1, Qt::AlignHCenter);
- lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row + 4, 1, Qt::AlignHCenter);
- if (itemBlock.per_frame_stats && !::profiler_gui::is_max(itemBlock.per_frame_stats->parent_block))
- {
- int col = 2;
- auto frame_duration = blocksTree(itemBlock.per_frame_stats->parent_block).node->duration();
- lay->addWidget(new QLabel("Frame", widget), row + 1, col, Qt::AlignHCenter);
- 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), row + 2, 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), row + 3, col, Qt::AlignHCenter);
- lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), row + 4, col, Qt::AlignHCenter);
- }
- }
- m_popupWidget = new QGraphicsProxyWidget();
- m_popupWidget->setWidget(widget);
- break;
- }
- }
- if (m_popupWidget != nullptr)
- {
- auto effect = new QGraphicsDropShadowEffect();
- effect->setBlurRadius(5);
- effect->setOffset(3, 3);
- m_popupWidget->setGraphicsEffect(effect);
- scene()->addItem(m_popupWidget);
- auto br = m_popupWidget->boundingRect();
- if (scenePos.y() + br.height() > m_visibleSceneRect.bottom())
- scenePos.setY(::std::max(scenePos.y() - br.height(), m_visibleSceneRect.top()));
- if (scenePos.x() + br.width() > m_visibleSceneRect.right())
- scenePos.setX(::std::max(scenePos.x() - br.width(), m_visibleSceneRect.left()));
- m_popupWidget->setPos(scenePos);
- m_popupWidget->setOpacity(0.95);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::onHierarchyFlagChange(bool)
- {
- bool changedSelection = false;
- if (!m_selectedBlocks.empty())
- {
- changedSelection = true;
- m_selectedBlocks.clear();
- }
- if (m_chronometerItem->isVisible())
- {
- for (auto item : m_items)
- {
- if (!EASY_GLOBALS.only_current_thread_hierarchy || item->threadId() == EASY_GLOBALS.selected_thread)
- item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks);
- }
- if (!m_selectedBlocks.empty())
- {
- changedSelection = true;
- }
- }
- if (changedSelection)
- {
- emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
- }
- }
- void EasyGraphicsView::onSelectedThreadChange(::profiler::thread_id_t _id)
- {
- if (m_pScrollbar == nullptr || m_pScrollbar->hystThread() == _id)
- {
- return;
- }
- if (_id == 0)
- {
- m_pScrollbar->setHistogramSource(0, nullptr);
- return;
- }
- for (auto item : m_items)
- {
- if (item->threadId() == _id)
- {
- m_pScrollbar->setHistogramSource(_id, item->items(0));
- bool changedSelection = false;
- if (EASY_GLOBALS.only_current_thread_hierarchy)
- {
- if (!m_selectedBlocks.empty())
- {
- changedSelection = true;
- m_selectedBlocks.clear();
- }
- if (m_chronometerItem->isVisible())
- {
- item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks);
- if (!m_selectedBlocks.empty())
- changedSelection = true;
- }
- }
- if (changedSelection)
- {
- emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
- }
- repaintScene();
- return;
- }
- }
- m_pScrollbar->setHistogramSource(0, nullptr);
- repaintScene();
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::onSelectedBlockChange(unsigned int _block_index)
- {
- if (!m_bUpdatingRect)
- {
- if (_block_index < EASY_GLOBALS.gui_blocks.size())
- {
- // Scroll to item
- const auto& guiblock = EASY_GLOBALS.gui_blocks[_block_index];
- const auto thread_item = m_items[guiblock.graphics_item];
- const auto& item = thread_item->items(guiblock.graphics_item_level)[guiblock.graphics_item_index];
- m_flickerSpeedX = m_flickerSpeedY = 0;
- 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->setHistogramSource(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->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0));
- break;
- }
- }
- }
- else
- {
- m_pScrollbar->setHistogramSource(0, nullptr);
- }
- updateVisibleSceneRect();
- repaintScene();
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void EasyGraphicsView::onRefreshRequired()
- {
- if (!m_bUpdatingRect)
- {
- repaintScene();
- }
- }
- //////////////////////////////////////////////////////////////////////////
- EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent)
- : QWidget(_parent)
- , m_scrollbar(new EasyGraphicsScrollbar(this))
- , m_view(new EasyGraphicsView(this))
- , m_threadNamesWidget(new EasyThreadNamesWidget(m_view, m_scrollbar->height(), this))
- {
- initWidget();
- }
- void EasyGraphicsViewWidget::initWidget()
- {
- auto lay = new QGridLayout(this);
- lay->setContentsMargins(1, 0, 1, 0);
- lay->addWidget(m_threadNamesWidget, 0, 0, 2, 1);
- lay->setSpacing(1);
- lay->addWidget(m_view, 0, 1);
- lay->setSpacing(1);
- lay->addWidget(m_scrollbar, 1, 1);
- setLayout(lay);
- m_view->setScrollbar(m_scrollbar);
- }
- EasyGraphicsViewWidget::~EasyGraphicsViewWidget()
- {
- }
- EasyGraphicsView* EasyGraphicsViewWidget::view()
- {
- return m_view;
- }
- void EasyGraphicsViewWidget::clear()
- {
- m_scrollbar->clear();
- m_threadNamesWidget->clear();
- m_view->clear();
- }
- //////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////
- void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
- {
- auto const parentView = static_cast<EasyThreadNamesWidget*>(scene()->parent());
- const auto view = parentView->view();
- const auto& items = view->getItems();
- if (items.empty())
- return;
- const auto visibleSceneRect = view->visibleSceneRect();
- const auto h = visibleSceneRect.height() + TIMELINE_ROW_SIZE - 2;
- const auto w = parentView->width();//parentView->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)};
- int i = -1;
- QRectF rect;
- _painter->resetTransform();
- // Draw thread names
- auto default_font = _painter->font();
- _painter->setFont(BG_FONT);
- for (auto item : items)
- {
- ++i;
- auto br = item->boundingRect();
- auto top = item->y() + br.top() - visibleSceneRect.top() - OVERLAP;
- auto hgt = br.height() + ::profiler_gui::THREADS_ROW_SPACING;
- auto bottom = top + hgt;
- if (top > h || bottom < 0)
- continue;
- if (item->threadId() == EASY_GLOBALS.selected_thread)
- _painter->setBrush(QBrush(QColor::fromRgb(::profiler_gui::SELECTED_THREAD_BACKGROUND)));
- else
- _painter->setBrush(brushes[i & 1]);
- if (top < 0)
- {
- hgt += top;
- top = 0;
- }
- const auto dh = top + hgt - h;
- if (dh > 0)
- hgt -= dh;
- rect.setRect(0, top, w, hgt);
- _painter->setPen(Qt::NoPen);
- _painter->drawRect(rect);
- rect.translate(-5, 0);
- _painter->setPen(QColor::fromRgb(::profiler::colors::Dark));
- _painter->drawText(rect, Qt::AlignRight | Qt::AlignVCenter, item->threadName());
- }
- const auto rect_bottom = rect.bottom();
- if (rect_bottom < h)
- {
- ++i;
- rect.translate(5, rect.height());
- rect.setHeight(h - rect_bottom);
- _painter->setBrush(brushes[i & 1]);
- _painter->setPen(Qt::NoPen);
- _painter->drawRect(rect);
- }
- // Draw separator between thread names area and information area
- _painter->setPen(Qt::darkGray);
- _painter->drawLine(QLineF(0, h, w, h));
- _painter->drawLine(QLineF(0, h + 2, w, h + 2));
- // 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, qreal th, QRgb color)
- {
- if (time > 0)
- {
- const QString text = ::profiler_gui::autoTimeStringReal(time); // Displayed text
- rect.setRect(0, y, w, th);
- _painter->setPen(color);
- _painter->drawText(rect, Qt::AlignCenter, text);
- y += th;
- }
- };
- drawTimeText(time1, th, ::profiler_gui::CHRONOMETER_COLOR.rgb() & 0x00ffffff);
- drawTimeText(time2, th, ::profiler_gui::CHRONOMETER_COLOR2.rgb() & 0x00ffffff);
- }
- //////////////////////////////////////////////////////////////////////////
- EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent)
- : Parent(_parent)
- , m_idleTime(0)
- , m_view(_view)
- , m_popupWidget(nullptr)
- , m_maxLength(100)
- , m_additionalHeight(_additionalHeight + 1)
- {
- setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
- setScene(new QGraphicsScene(this));
- setCacheMode(QGraphicsView::CacheNone);
- setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setFixedWidth(m_maxLength);
- 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);
- connect(m_view->verticalScrollBar(), &QScrollBar::rangeChanged, this, &This::setVerticalScrollbarRange);
- connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout);
- }
- EasyThreadNamesWidget::~EasyThreadNamesWidget()
- {
- }
- void EasyThreadNamesWidget::removePopup(bool _removeFromScene)
- {
- if (m_popupWidget != nullptr)
- {
- auto widget = m_popupWidget->widget();
- widget->setParent(nullptr);
- m_popupWidget->setWidget(nullptr);
- delete widget;
- if (_removeFromScene)
- scene()->removeItem(m_popupWidget);
- m_popupWidget = nullptr;
- }
- }
- void EasyThreadNamesWidget::clear()
- {
- const QSignalBlocker b(this);
- removePopup();
- scene()->clear();
- m_maxLength = 100;
- setFixedWidth(m_maxLength);
- m_idleTimer.stop();
- m_idleTime = 0;
- }
- void EasyThreadNamesWidget::setVerticalScrollbarRange(int _minValue, int _maxValue)
- {
- verticalScrollBar()->setRange(_minValue, _maxValue + m_additionalHeight);
- }
- void EasyThreadNamesWidget::onTreeChange()
- {
- const QSignalBlocker b(this);
- removePopup();
- scene()->clear();
- m_idleTimer.stop();
- m_idleTime = 0;
- QFontMetricsF fm(BG_FONT, this);
- qreal maxLength = 100;
- const auto& graphicsItems = m_view->getItems();
- for (auto graphicsItem : graphicsItems)
- maxLength = ::std::max(maxLength, (10 + fm.width(graphicsItem->threadName())) * ::profiler_gui::FONT_METRICS_FACTOR);
- auto vbar = verticalScrollBar();
- auto viewBar = m_view->verticalScrollBar();
- setVerticalScrollbarRange(viewBar->minimum(), viewBar->maximum());
- vbar->setSingleStep(viewBar->singleStep());
- vbar->setPageStep(viewBar->pageStep());
- auto r = m_view->sceneRect();
- setSceneRect(0, r.top(), maxLength, r.height() + m_additionalHeight);
- auto item = new EasyThreadNameItem();
- item->setPos(0, 0);
- item->setBoundingRect(sceneRect());
- scene()->addItem(item);
- m_maxLength = static_cast<int>(maxLength);
- setFixedWidth(m_maxLength);
- m_idleTimer.start(IDLE_TIMER_INTERVAL);
- }
- void EasyThreadNamesWidget::onIdleTimeout()
- {
- static const uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
- m_idleTime += IDLE_TIMER_INTERVAL;
- if (m_idleTime < IDLE_TIME)
- {
- removePopup(true);
- return;
- }
- if (m_popupWidget != nullptr)
- return;
- auto visibleSceneRect = mapToScene(rect()).boundingRect();
- auto scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
- if (scenePos.x() < visibleSceneRect.left() || scenePos.x() > visibleSceneRect.right())
- {
- if (m_idleTime > 3000)
- setFixedWidth(m_maxLength);
- return;
- }
- if (scenePos.y() < visibleSceneRect.top() || scenePos.y() > visibleSceneRect.bottom())
- {
- if (m_idleTime > 3000)
- setFixedWidth(m_maxLength);
- return;
- }
- auto const parentView = static_cast<EasyThreadNamesWidget*>(scene()->parent());
- const auto view = parentView->view();
- if (scenePos.y() > view->visibleSceneRect().bottom())
- {
- if (m_idleTime > 3000)
- setFixedWidth(m_maxLength);
- return;
- }
- const qreal y = scenePos.y() - visibleSceneRect.top();
- const auto& items = view->getItems();
- if (items.empty())
- {
- if (m_idleTime > 3000)
- setFixedWidth(m_maxLength);
- return;
- }
- EasyGraphicsItem* intersectingItem = nullptr;
- for (auto item : items)
- {
- auto br = item->boundingRect();
- auto top = item->y() + br.top() - visibleSceneRect.top() - OVERLAP;
- auto hgt = br.height() + ::profiler_gui::THREADS_ROW_SPACING;
- auto bottom = top + hgt;
- if (bottom < y || y < top)
- continue;
- intersectingItem = item;
- break;
- }
- if (intersectingItem != nullptr)
- {
- auto widget = new QWidget(nullptr, Qt::FramelessWindowHint);
- if (widget == nullptr)
- return;
- widget->setAttribute(Qt::WA_ShowWithoutActivating, true);
- widget->setFocusPolicy(Qt::NoFocus);
- auto lay = new QGridLayout(widget);
- if (lay == nullptr)
- return;
- int row = 0;
- lay->addWidget(new EasyBoldLabel(intersectingItem->threadName(), widget), row, 0, 1, 2, Qt::AlignHCenter);
- ++row;
- ::profiler::timestamp_t duration = 0;
- const auto& root = *intersectingItem->root();
- if (!root.children.empty())
- duration = easyBlock(root.children.back()).tree.node->end() - easyBlock(root.children.front()).tree.node->begin();
- lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, Qt::AlignLeft);
- ++row;
- lay->addWidget(new QLabel("Profiled:", widget), row, 0, Qt::AlignRight);
- if (duration)
- {
- lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.profiled_time, 3))
- .arg(QString::number(100. * (double)root.profiled_time / (double)duration, 'f', 2)), widget), row, 1, Qt::AlignLeft);
- }
- else
- {
- lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.profiled_time, 3), widget), row, 1, Qt::AlignLeft);
- }
- ++row;
- lay->addWidget(new QLabel("Wait:", widget), row, 0, Qt::AlignRight);
- if (duration)
- {
- lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.wait_time, 3))
- .arg(QString::number(100. * (double)root.wait_time / (double)duration, 'f', 2)), widget), row, 1, Qt::AlignLeft);
- }
- else
- {
- lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.wait_time, 3), widget), row, 1, Qt::AlignLeft);
- }
- ++row;
- const auto eventsSize = root.events.size();
- lay->addWidget(new QLabel("Frames:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(QString::number(root.frames_number), widget), row, 1, Qt::AlignLeft);
- ++row;
- lay->addWidget(new QLabel("Blocks:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(QString::number(root.blocks_number - eventsSize), widget), row, 1, Qt::AlignLeft);
- ++row;
- lay->addWidget(new QLabel("Markers:", widget), row, 0, Qt::AlignRight);
- lay->addWidget(new QLabel(QString::number(eventsSize), widget), row, 1, Qt::AlignLeft);
- ++row;
- m_popupWidget = new QGraphicsProxyWidget();
- if (m_popupWidget != nullptr)
- {
- auto effect = new QGraphicsDropShadowEffect();
- effect->setBlurRadius(5);
- effect->setOffset(3, 3);
- m_popupWidget->setGraphicsEffect(effect);
- m_popupWidget->setWidget(widget);
- scene()->addItem(m_popupWidget);
- auto br = m_popupWidget->boundingRect();
- if (maximumWidth() < br.width())
- {
- setFixedWidth(static_cast<int>(br.width()));
- visibleSceneRect.setWidth(br.width());
- }
- if (scenePos.y() + br.height() > visibleSceneRect.bottom())
- scenePos.setY(::std::max(scenePos.y() - br.height(), visibleSceneRect.top()));
- if (scenePos.x() + br.width() > visibleSceneRect.right())
- scenePos.setX(::std::max(scenePos.x() - br.width(), visibleSceneRect.left()));
- m_popupWidget->setPos(scenePos);
- m_popupWidget->setOpacity(0.95);
- }
- }
- }
- void EasyThreadNamesWidget::repaintScene()
- {
- scene()->update();
- }
- void EasyThreadNamesWidget::mousePressEvent(QMouseEvent* _event)
- {
- m_idleTime = 0;
- QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers());
- m_view->mousePressEvent(&e);
- _event->accept();
- }
- void EasyThreadNamesWidget::mouseDoubleClickEvent(QMouseEvent* _event)
- {
- static const auto OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
- m_idleTime = 0;
- auto y = mapToScene(_event->pos()).y();
- const auto& items = m_view->getItems();
- for (auto item : items)
- {
- auto br = item->boundingRect();
- auto top = item->y() + br.top() - OVERLAP;
- auto bottom = top + br.height() + OVERLAP;
- if (y < top || y > bottom)
- continue;
- const auto thread_id = item->threadId();
- if (thread_id != EASY_GLOBALS.selected_thread)
- {
- EASY_GLOBALS.selected_thread = thread_id;
- emit EASY_GLOBALS.events.selectedThreadChanged(thread_id);
- }
- break;
- }
- _event->accept();
- }
- void EasyThreadNamesWidget::mouseReleaseEvent(QMouseEvent* _event)
- {
- m_idleTime = 0;
- QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers());
- m_view->mouseReleaseEvent(&e);
- _event->accept();
- }
- void EasyThreadNamesWidget::mouseMoveEvent(QMouseEvent* _event)
- {
- m_idleTime = 0;
- QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers());
- m_view->mouseMoveEvent(&e);
- _event->accept();
- }
- void EasyThreadNamesWidget::keyPressEvent(QKeyEvent* _event)
- {
- m_idleTime = 0;
- m_view->keyPressEvent(_event);
- }
- void EasyThreadNamesWidget::keyReleaseEvent(QKeyEvent* _event)
- {
- m_idleTime = 0;
- m_view->keyReleaseEvent(_event);
- }
- void EasyThreadNamesWidget::wheelEvent(QWheelEvent* _event)
- {
- m_idleTime = 0;
- auto vbar = m_view->verticalScrollBar();
- if (vbar != nullptr)
- {
- _event->accept();
- vbar->setValue(vbar->value() - _event->delta());
- }
- }
- //////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////
|