blocks_graphics_view.cpp 86 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466
  1. /************************************************************************
  2. * file name : blocks_graphics_view.cpp
  3. * ----------------- :
  4. * creation time : 2016/06/26
  5. * author : Victor Zarubkin
  6. * email : [email protected]
  7. * ----------------- :
  8. * description : The file contains implementation of GraphicsScene and GraphicsView and
  9. * : it's auxiliary classes for displyaing easy_profiler blocks tree.
  10. * ----------------- :
  11. * change log : * 2016/06/26 Victor Zarubkin: Moved sources from graphics_view.h
  12. * : and renamed classes from My* to Prof*.
  13. * :
  14. * : * 2016/06/27 Victor Zarubkin: Added text shifting relatively to it's parent item.
  15. * : Disabled border lines painting because of vertical lines painting bug.
  16. * : Changed height of blocks. Variable thread-block height.
  17. * :
  18. * : * 2016/06/29 Victor Zarubkin: Highly optimized painting performance and memory consumption.
  19. * :
  20. * : * 2016/06/30 Victor Zarubkin: Replaced doubles with floats (in ProfBlockItem) for less memory consumption.
  21. * :
  22. * : * 2016/09/15 Victor Zarubkin: Moved sources of EasyGraphicsItem and EasyChronometerItem to separate files.
  23. * :
  24. * : *
  25. * ----------------- :
  26. * license : Lightweight profiler library for c++
  27. * : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
  28. * :
  29. * : Licensed under either of
  30. * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
  31. * : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
  32. * : at your option.
  33. * :
  34. * : The MIT License
  35. * :
  36. * : Permission is hereby granted, free of charge, to any person obtaining a copy
  37. * : of this software and associated documentation files (the "Software"), to deal
  38. * : in the Software without restriction, including without limitation the rights
  39. * : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  40. * : of the Software, and to permit persons to whom the Software is furnished
  41. * : to do so, subject to the following conditions:
  42. * :
  43. * : The above copyright notice and this permission notice shall be included in all
  44. * : copies or substantial portions of the Software.
  45. * :
  46. * : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  47. * : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  48. * : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  49. * : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  50. * : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  51. * : USE OR OTHER DEALINGS IN THE SOFTWARE.
  52. * :
  53. * : The Apache License, Version 2.0 (the "License")
  54. * :
  55. * : You may not use this file except in compliance with the License.
  56. * : You may obtain a copy of the License at
  57. * :
  58. * : http://www.apache.org/licenses/LICENSE-2.0
  59. * :
  60. * : Unless required by applicable law or agreed to in writing, software
  61. * : distributed under the License is distributed on an "AS IS" BASIS,
  62. * : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  63. * : See the License for the specific language governing permissions and
  64. * : limitations under the License.
  65. ************************************************************************/
  66. #include <math.h>
  67. #include <QGraphicsScene>
  68. #include <QGraphicsProxyWidget>
  69. #include <QWheelEvent>
  70. #include <QMouseEvent>
  71. #include <QKeyEvent>
  72. #include <QScrollBar>
  73. #include <QGridLayout>
  74. #include <QDebug>
  75. #include <QSignalBlocker>
  76. #include <QGraphicsDropShadowEffect>
  77. #include "blocks_graphics_view.h"
  78. #include "easy_graphics_item.h"
  79. #include "easy_chronometer_item.h"
  80. #include "easy_graphics_scrollbar.h"
  81. #include "globals.h"
  82. //////////////////////////////////////////////////////////////////////////
  83. //////////////////////////////////////////////////////////////////////////
  84. const qreal MIN_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT_INV, 70); // Up to 1000 sec scale
  85. const qreal MAX_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT, 45); // ~23000 --- Up to 10 ns scale
  86. const qreal BASE_SCALE = pow(::profiler_gui::SCALING_COEFFICIENT_INV, 25); // ~0.003
  87. const uint16_t TIMELINE_ROW_SIZE = 20;
  88. const QRgb BACKGROUND_1 = ::profiler::colors::Grey300;
  89. const QRgb BACKGROUND_2 = ::profiler::colors::White;
  90. const QRgb TIMELINE_BACKGROUND = 0x20000000 | (::profiler::colors::Grey800 & 0x00ffffff);// 0x20303030;
  91. const int IDLE_TIMER_INTERVAL = 200; // 5Hz
  92. const uint64_t IDLE_TIME = 400;
  93. const int FLICKER_INTERVAL = 10; // 100Hz
  94. const qreal FLICKER_FACTOR = 16.0 / FLICKER_INTERVAL;
  95. const auto BG_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Bold);
  96. const auto CHRONOMETER_FONT = ::profiler_gui::EFont("Helvetica", 16, QFont::Bold);
  97. #ifdef max
  98. #undef max
  99. #endif
  100. #ifdef min
  101. #undef min
  102. #endif
  103. //////////////////////////////////////////////////////////////////////////
  104. inline int sign(int _value) { return _value < 0 ? -1 : 1; }
  105. inline int absmin(int _a, int _b) { return abs(_a) < abs(_b) ? _a : _b; }
  106. inline qreal clamp(qreal _minValue, qreal _value, qreal _maxValue) { return _value < _minValue ? _minValue : (_value > _maxValue ? _maxValue : _value); }
  107. //////////////////////////////////////////////////////////////////////////
  108. template <int N, class T>
  109. inline T logn(T _value)
  110. {
  111. static const double div = 1.0 / log2((double)N);
  112. return log2(_value) * div;
  113. }
  114. //////////////////////////////////////////////////////////////////////////
  115. EasyBoldLabel::EasyBoldLabel(const QString& _text, QWidget* _parent) : QLabel(_text, _parent)
  116. {
  117. auto f = font();
  118. f.setBold(true);
  119. setFont(f);
  120. }
  121. EasyBoldLabel::~EasyBoldLabel()
  122. {
  123. }
  124. //////////////////////////////////////////////////////////////////////////
  125. void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
  126. {
  127. auto const sceneView = static_cast<EasyGraphicsView*>(scene()->parent());
  128. const auto visibleSceneRect = sceneView->visibleSceneRect();
  129. const auto currentScale = sceneView->scale();
  130. const auto offset = sceneView->offset();
  131. const auto left = offset * currentScale;
  132. const auto h = visibleSceneRect.height();
  133. const auto visibleBottom = h - 1;
  134. QRectF rect;
  135. _painter->save();
  136. _painter->setTransform(QTransform::fromTranslate(-x(), -y()));
  137. const auto& items = sceneView->getItems();
  138. if (!items.empty())
  139. {
  140. static const uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
  141. static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)};
  142. int i = -1;
  143. // Draw background
  144. _painter->setPen(Qt::NoPen);
  145. for (auto item : items)
  146. {
  147. ++i;
  148. auto br = item->boundingRect();
  149. auto top = item->y() + br.top() - visibleSceneRect.top();
  150. auto bottom = top + br.height();
  151. if (top > h || bottom < 0)
  152. continue;
  153. if (item->threadId() == EASY_GLOBALS.selected_thread)
  154. _painter->setBrush(QBrush(QColor::fromRgb(::profiler_gui::SELECTED_THREAD_BACKGROUND)));
  155. else
  156. _painter->setBrush(brushes[i & 1]);
  157. rect.setRect(0, top - OVERLAP, visibleSceneRect.width(), br.height() + ::profiler_gui::THREADS_ROW_SPACING);
  158. const auto dh = rect.bottom() - visibleBottom;
  159. if (dh > 0)
  160. rect.setHeight(rect.height() - dh);
  161. if (rect.top() < 0)
  162. rect.setTop(0);
  163. _painter->drawRect(rect);
  164. }
  165. }
  166. // Draw timeline scale marks ----------------
  167. _painter->setBrush(QColor::fromRgba(TIMELINE_BACKGROUND));
  168. const auto sceneStep = sceneView->timelineStep();
  169. const auto factor = ::profiler_gui::timeFactor(sceneStep);
  170. const auto step = sceneStep * currentScale;
  171. auto first = static_cast<quint64>(offset / sceneStep);
  172. const int odd = first & 1;
  173. const auto nsteps = (1 + odd) * 2 + static_cast<int>(visibleSceneRect.width() / step);
  174. first -= odd;
  175. QPen pen(Qt::darkGray);
  176. pen.setWidth(2);
  177. _painter->setPen(pen);
  178. _painter->drawLine(QPointF(0, h), QPointF(visibleSceneRect.width(), h));
  179. _painter->setPen(Qt::darkGray);
  180. QLineF marks[20];
  181. qreal first_x = first * sceneStep;
  182. const auto textWidth = QFontMetricsF(_painter->font(), sceneView).width(QString::number(static_cast<quint64>(0.5 + first_x * factor))) * ::profiler_gui::FONT_METRICS_FACTOR + 10;
  183. const int n = 1 + static_cast<int>(textWidth / step);
  184. int next = first % n;
  185. if (next)
  186. next = n - next;
  187. first_x *= currentScale;
  188. for (int i = 0; i < nsteps; ++i, --next)
  189. {
  190. auto current = first_x - left + step * i;
  191. if ((i & 1) == 0)
  192. {
  193. rect.setRect(current, 0, step, h);
  194. _painter->drawRect(rect);
  195. for (int j = 0; j < 20; ++j)
  196. {
  197. auto xmark = current + j * step * 0.1;
  198. marks[j].setLine(xmark, h, xmark, h + ((j % 5) ? 4 : 8));
  199. }
  200. _painter->drawLines(marks, 20);
  201. }
  202. if (next <= 0)
  203. {
  204. next = n;
  205. _painter->setPen(Qt::black);
  206. _painter->drawText(QPointF(current + 1, h + 17), QString::number(static_cast<quint64>(0.5 + (current + left) * factor / currentScale)));
  207. _painter->setPen(Qt::darkGray);
  208. }
  209. // TEST
  210. // this is for testing (order of lines will be painted):
  211. //_painter->setPen(Qt::black);
  212. //_painter->drawText(QPointF(current + step * 0.4, h - 20), QString::number(i));
  213. //_painter->setPen(Qt::gray);
  214. // TEST
  215. }
  216. // END Draw timeline scale marks ~~~~~~~~~~~~
  217. _painter->restore();
  218. }
  219. //////////////////////////////////////////////////////////////////////////
  220. void EasyTimelineIndicatorItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
  221. {
  222. const auto sceneView = static_cast<const EasyGraphicsView*>(scene()->parent());
  223. const auto visibleSceneRect = sceneView->visibleSceneRect();
  224. const auto step = sceneView->timelineStep() * sceneView->scale();
  225. const QString text = ::profiler_gui::autoTimeStringInt(units2microseconds(sceneView->timelineStep())); // Displayed text
  226. // Draw scale indicator
  227. _painter->save();
  228. _painter->setTransform(QTransform::fromTranslate(-x(), -y()));
  229. //_painter->setCompositionMode(QPainter::CompositionMode_Difference);
  230. _painter->setBrush(Qt::NoBrush);
  231. QPen pen(Qt::black);
  232. pen.setWidth(3);
  233. _painter->setPen(pen);
  234. _painter->drawLine(QLineF(visibleSceneRect.width() - 9 - step, visibleSceneRect.height() - 10, visibleSceneRect.width() - 11, visibleSceneRect.height() - 10));
  235. _painter->setPen(Qt::black);
  236. _painter->drawLine(QLineF(visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 6, visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 14));
  237. _painter->drawLine(QLineF(visibleSceneRect.width() - 10, visibleSceneRect.height() - 6, visibleSceneRect.width() - 10, visibleSceneRect.height() - 14));
  238. _painter->setPen(Qt::black);
  239. _painter->setFont(BG_FONT);
  240. _painter->drawText(QRectF(visibleSceneRect.width() - 10 - step, visibleSceneRect.height() - 63, step, 50), Qt::AlignRight | Qt::AlignBottom | Qt::TextDontClip, text);
  241. _painter->restore();
  242. }
  243. //////////////////////////////////////////////////////////////////////////
  244. EasyGraphicsView::EasyGraphicsView(QWidget* _parent)
  245. : Parent(_parent)
  246. , m_beginTime(::std::numeric_limits<decltype(m_beginTime)>::max())
  247. , m_sceneWidth(0)
  248. , m_scale(1)
  249. , m_offset(0)
  250. , m_timelineStep(0)
  251. , m_idleTime(0)
  252. , m_mouseButtons(Qt::NoButton)
  253. , m_pScrollbar(nullptr)
  254. , m_chronometerItem(nullptr)
  255. , m_chronometerItemAux(nullptr)
  256. , m_popupWidget(nullptr)
  257. , m_flickerSpeedX(0)
  258. , m_flickerSpeedY(0)
  259. , m_flickerCounterX(0)
  260. , m_flickerCounterY(0)
  261. , m_bDoubleClick(false)
  262. , m_bUpdatingRect(false)
  263. , m_bEmpty(true)
  264. {
  265. initMode();
  266. setScene(new QGraphicsScene(this));
  267. updateVisibleSceneRect();
  268. }
  269. EasyGraphicsView::~EasyGraphicsView()
  270. {
  271. }
  272. //////////////////////////////////////////////////////////////////////////
  273. void EasyGraphicsView::removePopup(bool _removeFromScene)
  274. {
  275. if (m_popupWidget != nullptr)
  276. {
  277. auto widget = m_popupWidget->widget();
  278. widget->setParent(nullptr);
  279. m_popupWidget->setWidget(nullptr);
  280. delete widget;
  281. if (_removeFromScene)
  282. scene()->removeItem(m_popupWidget);
  283. m_popupWidget = nullptr;
  284. }
  285. }
  286. //////////////////////////////////////////////////////////////////////////
  287. qreal EasyGraphicsView::sceneWidth() const
  288. {
  289. return m_sceneWidth;
  290. }
  291. qreal EasyGraphicsView::chronoTime() const
  292. {
  293. return m_chronometerItem->width();
  294. }
  295. qreal EasyGraphicsView::chronoTimeAux() const
  296. {
  297. return m_chronometerItemAux->width();
  298. }
  299. //////////////////////////////////////////////////////////////////////////
  300. EasyChronometerItem* EasyGraphicsView::createChronometer(bool _main)
  301. {
  302. auto chronoItem = new EasyChronometerItem(_main);
  303. chronoItem->setColor(_main ? ::profiler_gui::CHRONOMETER_COLOR : ::profiler_gui::CHRONOMETER_COLOR2);
  304. chronoItem->setBoundingRect(sceneRect());
  305. chronoItem->hide();
  306. scene()->addItem(chronoItem);
  307. return chronoItem;
  308. }
  309. //////////////////////////////////////////////////////////////////////////
  310. void EasyGraphicsView::clear()
  311. {
  312. const QSignalBlocker blocker(this), sceneBlocker(scene()); // block all scene signals (otherwise clear() would be extremely slow!)
  313. // Stop flicking
  314. m_flickerTimer.stop();
  315. m_flickerSpeedX = 0;
  316. m_flickerSpeedY = 0;
  317. m_flickerCounterX = 0;
  318. m_flickerCounterY = 0;
  319. // Clear all items
  320. removePopup();
  321. scene()->clear();
  322. m_items.clear();
  323. m_selectedBlocks.clear();
  324. m_beginTime = ::std::numeric_limits<decltype(m_beginTime)>::max(); // reset begin time
  325. m_scale = 1; // scale back to initial 100% scale
  326. m_timelineStep = 1;
  327. m_offset = 0; // scroll back to the beginning of the scene
  328. m_idleTimer.stop();
  329. m_idleTime = 0;
  330. // Reset necessary flags
  331. m_bEmpty = true;
  332. m_sceneWidth = 10;
  333. setSceneRect(0, 0, 10, 10);
  334. // notify ProfTreeWidget that selection was reset
  335. emit intervalChanged(m_selectedBlocks, m_beginTime, 0, 0, false);
  336. }
  337. void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTree)
  338. {
  339. // clear scene
  340. clear();
  341. if (_blocksTree.empty())
  342. {
  343. return;
  344. }
  345. auto bgItem = new EasyBackgroundItem();
  346. scene()->addItem(bgItem);
  347. // set new blocks tree
  348. // calculate scene size and fill it with items
  349. // Calculating start and end time
  350. ::profiler::timestamp_t finish = 0, busyTime = 0;
  351. ::profiler::thread_id_t longestTree = 0, mainTree = 0;
  352. for (const auto& threadTree : _blocksTree)
  353. {
  354. const auto& t = threadTree.second;
  355. auto timestart = m_beginTime;
  356. auto timefinish = finish;
  357. if (!t.children.empty())
  358. timestart = blocksTree(t.children.front()).node->begin();
  359. if (!t.sync.empty())
  360. timestart = ::std::min(timestart, blocksTree(t.sync.front()).node->begin());
  361. if (!t.children.empty())
  362. timefinish = blocksTree(t.children.back()).node->end();
  363. if (!t.sync.empty())
  364. timefinish = ::std::max(timefinish, blocksTree(t.sync.back()).node->end());
  365. if (m_beginTime > timestart)
  366. m_beginTime = timestart;
  367. if (finish < timefinish)
  368. finish = timefinish;
  369. if (t.profiled_time > busyTime) {
  370. busyTime = t.profiled_time;
  371. longestTree = threadTree.first;
  372. }
  373. if (mainTree == 0 && !strcmp(t.name(), "Main"))
  374. mainTree = threadTree.first;
  375. }
  376. const decltype(m_beginTime) additional_offset = (finish - m_beginTime) / 20; // Additional 5% before first block and after last block
  377. finish += additional_offset;
  378. m_beginTime -= ::std::min(m_beginTime, additional_offset);
  379. EASY_GLOBALS.begin_time = m_beginTime;
  380. // Sort threads by name
  381. ::std::vector<::std::reference_wrapper<const ::profiler::BlocksTreeRoot> > sorted_roots;
  382. sorted_roots.reserve(_blocksTree.size());
  383. for (const auto& threadTree : _blocksTree)
  384. sorted_roots.push_back(threadTree.second);
  385. ::std::sort(sorted_roots.begin(), sorted_roots.end(), [](const ::profiler::BlocksTreeRoot& _a, const ::profiler::BlocksTreeRoot& _b) {
  386. return _a.thread_name < _b.thread_name;
  387. });
  388. // Filling scene with items
  389. m_items.reserve(_blocksTree.size());
  390. qreal y = TIMELINE_ROW_SIZE;
  391. const EasyGraphicsItem *longestItem = nullptr, *mainThreadItem = nullptr;
  392. for (const ::profiler::BlocksTreeRoot& t : sorted_roots)
  393. {
  394. if (m_items.size() == 0xff)
  395. {
  396. qWarning() << "Warning: Maximum threads number (255 threads) exceeded! See EasyGraphicsView::setTree() : " << __LINE__ << " in file " << __FILE__;
  397. break;
  398. }
  399. // fill scene with new items
  400. qreal h = 0, x = 0;
  401. if (!t.children.empty())
  402. x = time2position(blocksTree(t.children.front()).node->begin());
  403. else if (!t.sync.empty())
  404. x = time2position(blocksTree(t.sync.front()).node->begin());
  405. auto item = new EasyGraphicsItem(static_cast<uint8_t>(m_items.size()), t);
  406. if (t.depth)
  407. item->setLevels(t.depth);
  408. item->setPos(0, y);
  409. qreal children_duration = 0;
  410. if (!t.children.empty())
  411. {
  412. uint32_t dummy = 0;
  413. children_duration = setTree(item, t.children, h, dummy, y, 0);
  414. }
  415. else
  416. {
  417. if (!t.sync.empty())
  418. children_duration = time2position(blocksTree(t.sync.back()).node->end()) - x;
  419. h = ::profiler_gui::GRAPHICS_ROW_SIZE;
  420. }
  421. item->setBoundingRect(0, 0, children_duration + x, h);
  422. m_items.push_back(item);
  423. scene()->addItem(item);
  424. y += h + ::profiler_gui::THREADS_ROW_SPACING;
  425. if (longestTree == t.thread_id)
  426. longestItem = item;
  427. if (mainTree == t.thread_id)
  428. mainThreadItem = item;
  429. }
  430. // Calculating scene rect
  431. m_sceneWidth = time2position(finish);
  432. setSceneRect(0, 0, m_sceneWidth, y + TIMELINE_ROW_SIZE);
  433. // Center view on the beginning of the scene
  434. updateVisibleSceneRect();
  435. setScrollbar(m_pScrollbar);
  436. // Create new chronometer item (previous item was destroyed by scene on scene()->clear()).
  437. // It will be shown on mouse right button click.
  438. m_chronometerItemAux = createChronometer(false);
  439. m_chronometerItem = createChronometer(true);
  440. bgItem->setBoundingRect(0, 0, m_sceneWidth, y);
  441. auto indicator = new EasyTimelineIndicatorItem();
  442. indicator->setBoundingRect(0, 0, m_sceneWidth, y);
  443. scene()->addItem(indicator);
  444. // Setting flags
  445. m_bEmpty = false;
  446. scaleTo(BASE_SCALE);
  447. emit treeChanged();
  448. if (mainThreadItem != nullptr)
  449. {
  450. longestItem = mainThreadItem;
  451. }
  452. if (longestItem != nullptr)
  453. {
  454. EASY_GLOBALS.selected_thread = longestItem->threadId();
  455. emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId());
  456. scrollTo(longestItem);
  457. m_pScrollbar->setHistogramSource(longestItem->threadId(), longestItem->items(0));
  458. if (!longestItem->items(0).empty())
  459. m_pScrollbar->setValue(longestItem->items(0).front().left() - m_pScrollbar->sliderWidth() * 0.25);
  460. }
  461. m_idleTimer.start(IDLE_TIMER_INTERVAL);
  462. }
  463. const EasyGraphicsView::Items &EasyGraphicsView::getItems() const
  464. {
  465. return m_items;
  466. }
  467. qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, const ::profiler::BlocksTree::children_t& _children, qreal& _height, uint32_t& _maxDepthChild, qreal _y, short _level)
  468. {
  469. if (_children.empty())
  470. {
  471. return 0;
  472. }
  473. const auto level = static_cast<uint8_t>(_level);
  474. const auto n = static_cast<unsigned int>(_children.size());
  475. _item->reserve(level, n);
  476. _maxDepthChild = 0;
  477. uint8_t maxDepth = 0;
  478. const short next_level = _level + 1;
  479. bool warned = false;
  480. qreal total_duration = 0, prev_end = 0, maxh = 0;
  481. qreal start_time = -1;
  482. uint32_t j = 0;
  483. for (auto child_index : _children)
  484. {
  485. auto& gui_block = easyBlock(child_index);
  486. const auto& child = gui_block.tree;
  487. if (child.depth > maxDepth)
  488. {
  489. maxDepth = child.depth;
  490. _maxDepthChild = j;
  491. }
  492. auto xbegin = time2position(child.node->begin());
  493. if (start_time < 0)
  494. {
  495. start_time = xbegin;
  496. }
  497. auto duration = time2position(child.node->end()) - xbegin;
  498. //const auto dt = xbegin - prev_end;
  499. //if (dt < 0)
  500. //{
  501. // duration += dt;
  502. // xbegin -= dt;
  503. //}
  504. //static const qreal MIN_DURATION = 0.25;
  505. //if (duration < MIN_DURATION)
  506. // duration = MIN_DURATION;
  507. const auto i = _item->addItem(level);
  508. auto& b = _item->getItem(level, i);
  509. gui_block.graphics_item = _item->index();
  510. gui_block.graphics_item_level = level;
  511. gui_block.graphics_item_index = i;
  512. if (next_level < 256 && next_level < _item->levels() && !child.children.empty())
  513. {
  514. b.children_begin = static_cast<unsigned int>(_item->items(static_cast<uint8_t>(next_level)).size());
  515. }
  516. else
  517. {
  518. ::profiler_gui::set_max(b.children_begin);
  519. }
  520. qreal h = 0;
  521. qreal children_duration = 0;
  522. uint32_t maxDepthChild = 0;
  523. if (next_level < 256)
  524. {
  525. children_duration = setTree(_item, child.children, h, maxDepthChild, _y + ::profiler_gui::GRAPHICS_ROW_SIZE_FULL, next_level);
  526. }
  527. else if (!child.children.empty() && !warned)
  528. {
  529. warned = true;
  530. qWarning() << "Warning: Maximum blocks depth (255) exceeded! See EasyGraphicsView::setTree() : " << __LINE__ << " in file " << __FILE__;
  531. }
  532. if (duration < children_duration)
  533. {
  534. duration = children_duration;
  535. }
  536. if (h > maxh)
  537. {
  538. maxh = h;
  539. }
  540. b.block = child_index;// &child;
  541. #ifndef EASY_GRAPHICS_ITEM_RECURSIVE_PAINT
  542. b.neighbours = n;
  543. b.state = j > 0 || level == 0 ? 0 : -1;
  544. #else
  545. b.max_depth_child = maxDepthChild;
  546. #endif
  547. b.setPos(xbegin, duration);
  548. //b.totalHeight = ::profiler_gui::GRAPHICS_ROW_SIZE + h;
  549. prev_end = xbegin + duration;
  550. total_duration = prev_end - start_time;
  551. ++j;
  552. }
  553. _height += ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + maxh;
  554. return total_duration;
  555. }
  556. //////////////////////////////////////////////////////////////////////////
  557. void EasyGraphicsView::setScrollbar(EasyGraphicsScrollbar* _scrollbar)
  558. {
  559. auto const prevScrollbar = m_pScrollbar;
  560. const bool makeConnect = prevScrollbar == nullptr || prevScrollbar != _scrollbar;
  561. if (prevScrollbar != nullptr && prevScrollbar != _scrollbar)
  562. {
  563. disconnect(prevScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange);
  564. disconnect(prevScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel);
  565. }
  566. m_pScrollbar = _scrollbar;
  567. m_pScrollbar->clear();
  568. m_pScrollbar->setRange(0, m_sceneWidth);
  569. auto vbar = verticalScrollBar();
  570. const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0);
  571. m_pScrollbar->setSliderWidth(m_visibleSceneRect.width() + vbar_width);
  572. if (makeConnect)
  573. {
  574. connect(m_pScrollbar, &EasyGraphicsScrollbar::valueChanged, this, &This::onGraphicsScrollbarValueChange);
  575. connect(m_pScrollbar, &EasyGraphicsScrollbar::wheeled, this, &This::onGraphicsScrollbarWheel);
  576. }
  577. EASY_GLOBALS.selected_thread = 0;
  578. emit EASY_GLOBALS.events.selectedThreadChanged(0);
  579. }
  580. //////////////////////////////////////////////////////////////////////////
  581. int EasyGraphicsView::updateVisibleSceneRect()
  582. {
  583. m_visibleSceneRect = mapToScene(rect()).boundingRect();
  584. auto vbar = verticalScrollBar();
  585. int vbar_width = 0;
  586. if (vbar && vbar->isVisible())
  587. vbar_width = vbar->width() + 2;
  588. m_visibleSceneRect.setWidth(m_visibleSceneRect.width() - vbar_width);
  589. m_visibleSceneRect.setHeight(m_visibleSceneRect.height() - TIMELINE_ROW_SIZE);
  590. return vbar_width;
  591. }
  592. void EasyGraphicsView::updateTimelineStep(qreal _windowWidth)
  593. {
  594. const auto time = units2microseconds(_windowWidth);
  595. if (time < 100)
  596. m_timelineStep = 1e-2;
  597. else if (time < 10e3)
  598. m_timelineStep = 1;
  599. else if (time < 10e6)
  600. m_timelineStep = 1e3;
  601. else
  602. m_timelineStep = 1e6;
  603. const auto optimal_steps = static_cast<int>(40 * m_visibleSceneRect.width() / 1500);
  604. auto steps = time / m_timelineStep;
  605. while (steps > optimal_steps) {
  606. m_timelineStep *= 10;
  607. steps *= 0.1;
  608. }
  609. m_timelineStep = microseconds2units(m_timelineStep);
  610. }
  611. void EasyGraphicsView::repaintScene()
  612. {
  613. scene()->update(m_visibleSceneRect);
  614. emit sceneUpdated();
  615. }
  616. //////////////////////////////////////////////////////////////////////////
  617. void EasyGraphicsView::scaleTo(qreal _scale)
  618. {
  619. if (m_bEmpty)
  620. {
  621. return;
  622. }
  623. // have to limit scale because of Qt's QPainter feature: it doesn't draw text
  624. // with very big coordinates (but it draw rectangles with the same coordinates good).
  625. m_scale = clamp(MIN_SCALE, _scale, MAX_SCALE);
  626. const int vbar_width = updateVisibleSceneRect();
  627. // Update slider width for scrollbar
  628. const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
  629. m_pScrollbar->setSliderWidth(windowWidth);
  630. updateTimelineStep(windowWidth);
  631. repaintScene();
  632. }
  633. void EasyGraphicsView::wheelEvent(QWheelEvent* _event)
  634. {
  635. m_idleTime = 0;
  636. if (!m_bEmpty)
  637. onWheel(mapToScene(_event->pos()).x(), _event->delta());
  638. _event->accept();
  639. }
  640. void EasyGraphicsView::onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta)
  641. {
  642. m_idleTime = 0;
  643. for (auto item : m_items)
  644. {
  645. if (item->threadId() == EASY_GLOBALS.selected_thread)
  646. {
  647. scrollTo(item);
  648. break;
  649. }
  650. }
  651. onWheel(_mouseX, _wheelDelta);
  652. }
  653. void EasyGraphicsView::scrollTo(const EasyGraphicsItem* _item)
  654. {
  655. m_bUpdatingRect = true;
  656. auto vbar = verticalScrollBar();
  657. vbar->setValue(_item->y() + (_item->boundingRect().height() - vbar->pageStep()) * 0.5);
  658. m_bUpdatingRect = false;
  659. }
  660. void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta)
  661. {
  662. const decltype(m_scale) scaleCoeff = _wheelDelta > 0 ? ::profiler_gui::SCALING_COEFFICIENT : ::profiler_gui::SCALING_COEFFICIENT_INV;
  663. // Remember current mouse position
  664. _mouseX = clamp(0., _mouseX, m_sceneWidth);
  665. const auto mousePosition = m_offset + _mouseX / m_scale;
  666. // have to limit scale because of Qt's QPainter feature: it doesn't draw text
  667. // with very big coordinates (but it draw rectangles with the same coordinates good).
  668. m_scale = clamp(MIN_SCALE, m_scale * scaleCoeff, MAX_SCALE);
  669. //updateVisibleSceneRect(); // Update scene rect
  670. // Update slider width for scrollbar
  671. auto vbar = verticalScrollBar();
  672. const int vbar_width = (vbar != nullptr && vbar->isVisible() ? vbar->width() + 2 : 0);
  673. const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
  674. m_pScrollbar->setSliderWidth(windowWidth);
  675. // Calculate new offset to simulate QGraphicsView::AnchorUnderMouse scaling behavior
  676. m_offset = clamp(0., mousePosition - _mouseX / m_scale, m_sceneWidth - windowWidth);
  677. // Update slider position
  678. m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
  679. m_pScrollbar->setValue(m_offset);
  680. m_bUpdatingRect = false;
  681. updateVisibleSceneRect(); // Update scene rect
  682. updateTimelineStep(windowWidth);
  683. repaintScene(); // repaint scene
  684. }
  685. //////////////////////////////////////////////////////////////////////////
  686. void EasyGraphicsView::mousePressEvent(QMouseEvent* _event)
  687. {
  688. m_idleTime = 0;
  689. if (m_bEmpty)
  690. {
  691. _event->accept();
  692. return;
  693. }
  694. m_mouseButtons = _event->buttons();
  695. m_mousePressPos = _event->pos();
  696. if (m_mouseButtons & Qt::LeftButton)
  697. {
  698. if (m_chronometerItemAux->isVisible() && (m_chronometerItemAux->hoverLeft() || m_chronometerItemAux->hoverRight()))
  699. {
  700. m_chronometerItemAux->setReverse(m_chronometerItemAux->hoverLeft());
  701. m_bDoubleClick = true;
  702. }
  703. else if (m_chronometerItem->isVisible() && (m_chronometerItem->hoverLeft() || m_chronometerItem->hoverRight()))
  704. {
  705. m_chronometerItem->setReverse(m_chronometerItem->hoverLeft());
  706. m_mouseButtons = Qt::RightButton;
  707. return;
  708. }
  709. }
  710. if (m_mouseButtons & Qt::RightButton)
  711. {
  712. if (m_chronometerItem->isVisible() && (m_chronometerItem->hoverLeft() || m_chronometerItem->hoverRight()))
  713. {
  714. m_chronometerItem->setReverse(m_chronometerItem->hoverLeft());
  715. }
  716. else
  717. {
  718. const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale;
  719. m_chronometerItem->setLeftRight(mouseX, mouseX);
  720. m_chronometerItem->hide();
  721. m_pScrollbar->hideChrono();
  722. }
  723. }
  724. _event->accept();
  725. }
  726. void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event)
  727. {
  728. m_idleTime = 0;
  729. if (m_bEmpty)
  730. {
  731. _event->accept();
  732. return;
  733. }
  734. m_mouseButtons = _event->buttons();
  735. m_mousePressPos = _event->pos();
  736. m_bDoubleClick = true;
  737. if (m_mouseButtons & Qt::LeftButton)
  738. {
  739. const auto mouseX = m_offset + mapToScene(m_mousePressPos).x() / m_scale;
  740. m_chronometerItemAux->setLeftRight(mouseX, mouseX);
  741. m_chronometerItemAux->hide();
  742. emit sceneUpdated();
  743. }
  744. _event->accept();
  745. }
  746. //////////////////////////////////////////////////////////////////////////
  747. void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
  748. {
  749. m_idleTime = 0;
  750. if (m_bEmpty)
  751. {
  752. _event->accept();
  753. return;
  754. }
  755. bool chronoHidden = false;
  756. bool changedSelection = false, changedSelectedItem = false;
  757. if (m_mouseButtons & Qt::RightButton)
  758. {
  759. if (m_chronometerItem->isVisible() && m_chronometerItem->width() < 1e-6)
  760. {
  761. m_chronometerItem->hide();
  762. m_pScrollbar->hideChrono();
  763. }
  764. if (!m_selectedBlocks.empty())
  765. {
  766. changedSelection = true;
  767. m_selectedBlocks.clear();
  768. }
  769. if (m_chronometerItem->isVisible())
  770. {
  771. //printf("INTERVAL: {%lf, %lf} ms\n", m_chronometerItem->left(), m_chronometerItem->right());
  772. for (auto item : m_items)
  773. {
  774. if (!EASY_GLOBALS.only_current_thread_hierarchy || item->threadId() == EASY_GLOBALS.selected_thread)
  775. item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks);
  776. }
  777. if (!m_selectedBlocks.empty())
  778. {
  779. changedSelection = true;
  780. }
  781. }
  782. }
  783. const ::profiler_gui::EasyBlock* selectedBlock = nullptr;
  784. ::profiler::thread_id_t selectedBlockThread = 0;
  785. const auto previouslySelectedBlock = EASY_GLOBALS.selected_block;
  786. if (m_mouseButtons & Qt::LeftButton)
  787. {
  788. bool clicked = false;
  789. if (m_chronometerItemAux->isVisible() && m_chronometerItemAux->width() < 1e-6)
  790. {
  791. chronoHidden = true;
  792. m_chronometerItemAux->hide();
  793. }
  794. else if (m_chronometerItem->isVisible() && m_chronometerItem->hoverIndicator())
  795. {
  796. // Jump to selected zone
  797. clicked = true;
  798. m_flickerSpeedX = m_flickerSpeedY = 0;
  799. m_pScrollbar->setValue(m_chronometerItem->left() + m_chronometerItem->width() * 0.5 - m_pScrollbar->sliderHalfWidth());
  800. }
  801. if (!clicked && m_mouseMovePath.manhattanLength() < 5)
  802. {
  803. // Handle Click
  804. //clicked = true;
  805. auto mouseClickPos = mapToScene(m_mousePressPos);
  806. if (mouseClickPos.x() >= 0)
  807. {
  808. mouseClickPos.setX(m_offset + mouseClickPos.x() / m_scale);
  809. // Try to select one of item blocks
  810. for (auto item : m_items)
  811. {
  812. ::profiler::block_index_t i = ~0U;
  813. auto block = item->intersect(mouseClickPos, i);
  814. if (block)
  815. {
  816. changedSelectedItem = true;
  817. selectedBlock = block;
  818. selectedBlockThread = item->threadId();
  819. EASY_GLOBALS.selected_block = i;
  820. EASY_GLOBALS.selected_block_id = easyBlock(i).tree.node->id();
  821. break;
  822. }
  823. }
  824. if (!changedSelectedItem && !::profiler_gui::is_max(EASY_GLOBALS.selected_block))
  825. {
  826. changedSelectedItem = true;
  827. ::profiler_gui::set_max(EASY_GLOBALS.selected_block);
  828. ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
  829. }
  830. }
  831. }
  832. }
  833. m_bDoubleClick = false;
  834. m_mouseButtons = _event->buttons();
  835. m_mouseMovePath = QPoint();
  836. _event->accept();
  837. if (changedSelection)
  838. {
  839. emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
  840. }
  841. if (changedSelectedItem)
  842. {
  843. m_bUpdatingRect = true;
  844. if (selectedBlock != nullptr && previouslySelectedBlock == EASY_GLOBALS.selected_block && !selectedBlock->tree.children.empty())
  845. {
  846. EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded = !EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded;
  847. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  848. }
  849. emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block);
  850. if (EASY_GLOBALS.selecting_block_changes_thread && selectedBlock != nullptr && EASY_GLOBALS.selected_thread != selectedBlockThread)
  851. {
  852. EASY_GLOBALS.selected_thread = selectedBlockThread;
  853. m_pScrollbar->lock();
  854. emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread);
  855. m_pScrollbar->unlock();
  856. }
  857. m_bUpdatingRect = false;
  858. if (selectedBlock != nullptr && selectedBlockThread == EASY_GLOBALS.selected_thread)
  859. m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id);
  860. else
  861. {
  862. for (auto item : m_items)
  863. {
  864. if (item->threadId() == EASY_GLOBALS.selected_thread)
  865. {
  866. m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0));
  867. break;
  868. }
  869. }
  870. }
  871. repaintScene();
  872. }
  873. else if (chronoHidden)
  874. {
  875. emit sceneUpdated();
  876. }
  877. }
  878. //////////////////////////////////////////////////////////////////////////
  879. bool EasyGraphicsView::moveChrono(EasyChronometerItem* _chronometerItem, qreal _mouseX)
  880. {
  881. if (_chronometerItem->reverse())
  882. {
  883. if (_mouseX > _chronometerItem->right())
  884. {
  885. _chronometerItem->setReverse(false);
  886. _chronometerItem->setLeftRight(_chronometerItem->right(), _mouseX);
  887. if (_chronometerItem->hoverLeft())
  888. {
  889. _chronometerItem->setHoverLeft(false);
  890. _chronometerItem->setHoverRight(true);
  891. }
  892. }
  893. else
  894. {
  895. _chronometerItem->setLeftRight(_mouseX, _chronometerItem->right());
  896. }
  897. }
  898. else
  899. {
  900. if (_mouseX < _chronometerItem->left())
  901. {
  902. _chronometerItem->setReverse(true);
  903. _chronometerItem->setLeftRight(_mouseX, _chronometerItem->left());
  904. if (_chronometerItem->hoverRight())
  905. {
  906. _chronometerItem->setHoverLeft(true);
  907. _chronometerItem->setHoverRight(false);
  908. }
  909. }
  910. else
  911. {
  912. _chronometerItem->setLeftRight(_chronometerItem->left(), _mouseX);
  913. }
  914. }
  915. if (!_chronometerItem->isVisible() && _chronometerItem->width() > 1e-6)
  916. {
  917. _chronometerItem->show();
  918. return true;
  919. }
  920. return false;
  921. }
  922. void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event)
  923. {
  924. m_idleTime = 0;
  925. if (m_bEmpty || (m_mouseButtons == 0 && !m_chronometerItem->isVisible() && !m_chronometerItemAux->isVisible()))
  926. {
  927. _event->accept();
  928. return;
  929. }
  930. bool needUpdate = false;
  931. const auto pos = _event->pos();
  932. const auto delta = pos - m_mousePressPos;
  933. m_mousePressPos = pos;
  934. if (m_mouseButtons != 0)
  935. {
  936. m_mouseMovePath.setX(m_mouseMovePath.x() + qAbs(delta.x()));
  937. m_mouseMovePath.setY(m_mouseMovePath.y() + qAbs(delta.y()));
  938. }
  939. auto mouseScenePos = mapToScene(m_mousePressPos);
  940. mouseScenePos.setX(m_offset + mouseScenePos.x() / m_scale);
  941. const auto x = clamp(0., mouseScenePos.x(), m_sceneWidth);
  942. if (m_mouseButtons & Qt::RightButton)
  943. {
  944. bool showItem = moveChrono(m_chronometerItem, x);
  945. m_pScrollbar->setChronoPos(m_chronometerItem->left(), m_chronometerItem->right());
  946. if (showItem)
  947. {
  948. m_pScrollbar->showChrono();
  949. }
  950. needUpdate = true;
  951. }
  952. if (m_mouseButtons & Qt::LeftButton)
  953. {
  954. if (m_bDoubleClick)
  955. {
  956. moveChrono(m_chronometerItemAux, x);
  957. }
  958. else
  959. {
  960. auto vbar = verticalScrollBar();
  961. m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once
  962. vbar->setValue(vbar->value() - delta.y());
  963. m_pScrollbar->setValue(m_pScrollbar->value() - delta.x() / m_scale);
  964. m_bUpdatingRect = false;
  965. // Seems like an ugly stub, but QSignalBlocker is also a bad decision
  966. // because if scrollbar does not emit valueChanged signal then viewport does not move
  967. updateVisibleSceneRect(); // Update scene visible rect only once
  968. // Update flicker speed
  969. m_flickerSpeedX += delta.x() >> 1;
  970. m_flickerSpeedY += delta.y();
  971. if (!m_flickerTimer.isActive())
  972. {
  973. // If flicker timer is not started, then start it
  974. m_flickerTimer.start(FLICKER_INTERVAL);
  975. }
  976. }
  977. needUpdate = true;
  978. }
  979. if (m_mouseButtons == 0)
  980. {
  981. if (m_chronometerItem->isVisible())
  982. {
  983. auto prevValue = m_chronometerItem->hoverIndicator();
  984. m_chronometerItem->setHoverIndicator(m_chronometerItem->indicatorContains(mouseScenePos));
  985. needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverIndicator());
  986. prevValue = m_chronometerItem->hoverLeft();
  987. m_chronometerItem->setHoverLeft(m_chronometerItem->hoverLeft(mouseScenePos.x()));
  988. needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverLeft());
  989. if (!m_chronometerItem->hoverLeft())
  990. {
  991. prevValue = m_chronometerItem->hoverRight();
  992. m_chronometerItem->setHoverRight(m_chronometerItem->hoverRight(mouseScenePos.x()));
  993. needUpdate = needUpdate || (prevValue != m_chronometerItem->hoverRight());
  994. }
  995. }
  996. if (m_chronometerItemAux->isVisible())
  997. {
  998. auto prevValue = m_chronometerItemAux->hoverLeft();
  999. m_chronometerItemAux->setHoverLeft(m_chronometerItemAux->hoverLeft(mouseScenePos.x()));
  1000. needUpdate = needUpdate || (prevValue != m_chronometerItemAux->hoverLeft());
  1001. if (!m_chronometerItemAux->hoverLeft())
  1002. {
  1003. prevValue = m_chronometerItemAux->hoverRight();
  1004. m_chronometerItemAux->setHoverRight(m_chronometerItemAux->hoverRight(mouseScenePos.x()));
  1005. needUpdate = needUpdate || (prevValue != m_chronometerItemAux->hoverRight());
  1006. }
  1007. }
  1008. }
  1009. if (needUpdate)
  1010. {
  1011. repaintScene(); // repaint scene
  1012. }
  1013. _event->accept();
  1014. }
  1015. //////////////////////////////////////////////////////////////////////////
  1016. void EasyGraphicsView::keyPressEvent(QKeyEvent* _event)
  1017. {
  1018. static const int KeyStep = 100;
  1019. const int key = _event->key();
  1020. m_idleTime = 0;
  1021. switch (key)
  1022. {
  1023. case Qt::Key_Right:
  1024. case Qt::Key_6:
  1025. {
  1026. m_pScrollbar->setValue(m_pScrollbar->value() + KeyStep / m_scale);
  1027. break;
  1028. }
  1029. case Qt::Key_Left:
  1030. case Qt::Key_4:
  1031. {
  1032. m_pScrollbar->setValue(m_pScrollbar->value() - KeyStep / m_scale);
  1033. break;
  1034. }
  1035. case Qt::Key_Up:
  1036. case Qt::Key_8:
  1037. {
  1038. auto vbar = verticalScrollBar();
  1039. vbar->setValue(vbar->value() - KeyStep);
  1040. break;
  1041. }
  1042. case Qt::Key_Down:
  1043. case Qt::Key_2:
  1044. {
  1045. auto vbar = verticalScrollBar();
  1046. vbar->setValue(vbar->value() + KeyStep);
  1047. break;
  1048. }
  1049. case Qt::Key_Plus:
  1050. case Qt::Key_Equal:
  1051. {
  1052. onWheel(mapToScene(mapFromGlobal(QCursor::pos())).x(), KeyStep);
  1053. break;
  1054. }
  1055. case Qt::Key_Minus:
  1056. {
  1057. onWheel(mapToScene(mapFromGlobal(QCursor::pos())).x(), -KeyStep);
  1058. break;
  1059. }
  1060. }
  1061. //m_keys.insert(key);
  1062. _event->accept();
  1063. }
  1064. void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event)
  1065. {
  1066. //const int key = _event->key();
  1067. m_idleTime = 0;
  1068. //m_keys.erase(key);
  1069. _event->accept();
  1070. }
  1071. //////////////////////////////////////////////////////////////////////////
  1072. void EasyGraphicsView::resizeEvent(QResizeEvent* _event)
  1073. {
  1074. Parent::resizeEvent(_event);
  1075. const QRectF previousRect = m_visibleSceneRect;
  1076. const int vbar_width = updateVisibleSceneRect(); // Update scene visible rect only once
  1077. // Update slider width for scrollbar
  1078. const auto windowWidth = (m_visibleSceneRect.width() + vbar_width) / m_scale;
  1079. m_pScrollbar->setSliderWidth(windowWidth);
  1080. // Calculate new offset to save old screen center
  1081. const auto deltaWidth = m_visibleSceneRect.width() - previousRect.width();
  1082. m_offset = clamp(0., m_offset - deltaWidth * 0.5 / m_scale, m_sceneWidth - windowWidth);
  1083. // Update slider position
  1084. m_bUpdatingRect = true; // To be sure that updateVisibleSceneRect will not be called by scrollbar change
  1085. m_pScrollbar->setValue(m_offset);
  1086. m_bUpdatingRect = false;
  1087. repaintScene(); // repaint scene
  1088. }
  1089. //////////////////////////////////////////////////////////////////////////
  1090. void EasyGraphicsView::initMode()
  1091. {
  1092. // TODO: find mode with least number of bugs :)
  1093. // There are always some display bugs...
  1094. setCacheMode(QGraphicsView::CacheNone);
  1095. setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
  1096. setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
  1097. setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
  1098. setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  1099. connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange);
  1100. connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout);
  1101. connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout);
  1102. auto globalSignals = &EASY_GLOBALS.events;
  1103. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::hierarchyFlagChanged, this, &This::onHierarchyFlagChange);
  1104. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange);
  1105. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange);
  1106. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::itemsExpandStateChanged, this, &This::onRefreshRequired);
  1107. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::refreshRequired, this, &This::onRefreshRequired);
  1108. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedBlockIdChanged, [this](::profiler::block_id_t)
  1109. {
  1110. if (::profiler_gui::is_max(EASY_GLOBALS.selected_block_id))
  1111. {
  1112. if (EASY_GLOBALS.selected_thread != 0)
  1113. {
  1114. for (auto item : m_items)
  1115. {
  1116. if (item->threadId() == EASY_GLOBALS.selected_thread)
  1117. {
  1118. m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0));
  1119. break;
  1120. }
  1121. }
  1122. }
  1123. else
  1124. {
  1125. m_pScrollbar->setHistogramSource(0, nullptr);
  1126. }
  1127. }
  1128. else
  1129. m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, EASY_GLOBALS.selected_block_id);
  1130. onRefreshRequired();
  1131. });
  1132. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::threadNameDecorationChanged, this, &This::onThreadViewChanged);
  1133. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::hexThreadIdChanged, this, &This::onThreadViewChanged);
  1134. connect(globalSignals, &::profiler_gui::EasyGlobalSignals::blocksTreeModeChanged, [this]()
  1135. {
  1136. if (!m_selectedBlocks.empty())
  1137. emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
  1138. });
  1139. }
  1140. //////////////////////////////////////////////////////////////////////////
  1141. void EasyGraphicsView::onThreadViewChanged()
  1142. {
  1143. if (m_bEmpty)
  1144. return;
  1145. for (auto item : m_items)
  1146. item->validateName();
  1147. emit treeChanged();
  1148. updateVisibleSceneRect();
  1149. onHierarchyFlagChange(EASY_GLOBALS.only_current_thread_hierarchy);
  1150. repaintScene();
  1151. }
  1152. //////////////////////////////////////////////////////////////////////////
  1153. void EasyGraphicsView::onScrollbarValueChange(int)
  1154. {
  1155. if (!m_bUpdatingRect && !m_bEmpty)
  1156. updateVisibleSceneRect();
  1157. }
  1158. void EasyGraphicsView::onGraphicsScrollbarValueChange(qreal _value)
  1159. {
  1160. if (!m_bEmpty)
  1161. {
  1162. m_offset = _value;
  1163. if (!m_bUpdatingRect)
  1164. {
  1165. updateVisibleSceneRect();
  1166. repaintScene();
  1167. }
  1168. }
  1169. }
  1170. //////////////////////////////////////////////////////////////////////////
  1171. void EasyGraphicsView::onFlickerTimeout()
  1172. {
  1173. ++m_flickerCounterX;
  1174. ++m_flickerCounterY;
  1175. if (m_mouseButtons & Qt::LeftButton)
  1176. {
  1177. // Fast slow-down and stop if mouse button is pressed, no flicking.
  1178. m_flickerSpeedX >>= 1;
  1179. m_flickerSpeedY >>= 1;
  1180. if (m_flickerSpeedX == -1) m_flickerSpeedX = 0;
  1181. if (m_flickerSpeedY == -1) m_flickerSpeedY = 0;
  1182. }
  1183. else
  1184. {
  1185. // Flick when mouse button is not pressed
  1186. auto vbar = verticalScrollBar();
  1187. m_bUpdatingRect = true; // Block scrollbars from updating scene rect to make it possible to do it only once
  1188. m_pScrollbar->setValue(m_pScrollbar->value() - m_flickerSpeedX / m_scale);
  1189. vbar->setValue(vbar->value() - m_flickerSpeedY);
  1190. m_bUpdatingRect = false;
  1191. // Seems like an ugly stub, but QSignalBlocker is also a bad decision
  1192. // because if scrollbar does not emit valueChanged signal then viewport does not move
  1193. updateVisibleSceneRect(); // Update scene visible rect only once
  1194. repaintScene(); // repaint scene
  1195. const int dx = static_cast<int>(sign(m_flickerSpeedX) * m_flickerCounterX / FLICKER_FACTOR);
  1196. const int dy = static_cast<int>(sign(m_flickerSpeedY) * m_flickerCounterY / FLICKER_FACTOR);
  1197. if (abs(dx) > 0)
  1198. {
  1199. m_flickerSpeedX -= absmin(dx, m_flickerSpeedX);
  1200. m_flickerCounterX = 0;
  1201. }
  1202. if (abs(dy) > 0)
  1203. {
  1204. m_flickerSpeedY -= absmin(dy, m_flickerSpeedY);
  1205. m_flickerCounterY = 0;
  1206. }
  1207. }
  1208. if (m_flickerSpeedX == 0 && m_flickerSpeedY == 0)
  1209. {
  1210. // Flicker stopped, no timer needed.
  1211. m_flickerTimer.stop();
  1212. m_flickerSpeedX = 0;
  1213. m_flickerSpeedY = 0;
  1214. m_flickerCounterX = 0;
  1215. m_flickerCounterY = 0;
  1216. }
  1217. }
  1218. //////////////////////////////////////////////////////////////////////////
  1219. void EasyGraphicsView::onIdleTimeout()
  1220. {
  1221. m_idleTime += IDLE_TIMER_INTERVAL;
  1222. if (m_idleTime < IDLE_TIME)
  1223. {
  1224. removePopup(true);
  1225. return;
  1226. }
  1227. if (m_popupWidget != nullptr)
  1228. return;
  1229. auto scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
  1230. if (scenePos.x() < m_visibleSceneRect.left() || scenePos.x() > m_visibleSceneRect.right())
  1231. return;
  1232. if (scenePos.y() < m_visibleSceneRect.top() || scenePos.y() > m_visibleSceneRect.bottom())
  1233. return;
  1234. decltype(scenePos) pos(m_offset + scenePos.x() / m_scale, scenePos.y());
  1235. // Try to select one of context switches or items
  1236. for (auto item : m_items)
  1237. {
  1238. ::profiler::block_index_t i = ~0U;
  1239. auto block = item->intersect(pos, i);
  1240. if (block)
  1241. {
  1242. const auto& itemBlock = block->tree;
  1243. const auto& itemDesc = easyDescriptor(itemBlock.node->id());
  1244. auto name = *itemBlock.node->name() != 0 ? itemBlock.node->name() : itemDesc.name();
  1245. auto widget = new QWidget(nullptr, Qt::FramelessWindowHint);
  1246. if (widget == nullptr)
  1247. return;
  1248. widget->setAttribute(Qt::WA_ShowWithoutActivating, true);
  1249. widget->setFocusPolicy(Qt::NoFocus);
  1250. auto lay = new QGridLayout(widget);
  1251. if (lay == nullptr)
  1252. return;
  1253. int row = 0;
  1254. if (itemDesc.type() == ::profiler::BLOCK_TYPE_BLOCK)
  1255. {
  1256. //lay->addWidget(new QLabel("Name:", widget), row, 0, Qt::AlignRight);
  1257. lay->addWidget(new EasyBoldLabel(::profiler_gui::toUnicode(name), widget), row, 0, 1, 5, Qt::AlignHCenter);
  1258. ++row;
  1259. const auto duration = itemBlock.node->duration();
  1260. lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight);
  1261. lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, 1, 3, Qt::AlignLeft);
  1262. ++row;
  1263. ::profiler::timestamp_t children_duration = 0;
  1264. for (auto child : itemBlock.children)
  1265. children_duration += easyBlock(child).tree.node->duration();
  1266. const auto self_duration = duration - children_duration;
  1267. const auto self_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(self_duration, duration);
  1268. lay->addWidget(new QLabel("Self:", widget), row, 0, Qt::AlignRight);
  1269. 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);
  1270. ++row;
  1271. }
  1272. else
  1273. {
  1274. lay->addWidget(new EasyBoldLabel("User defined event", widget), row, 0, 1, 2, Qt::AlignHCenter);
  1275. ++row;
  1276. lay->addWidget(new QLabel("Name:", widget), row, 0, Qt::AlignRight);
  1277. lay->addWidget(new QLabel(::profiler_gui::toUnicode(name), widget), row, 1, Qt::AlignLeft);
  1278. ++row;
  1279. }
  1280. if (itemBlock.per_thread_stats)
  1281. {
  1282. if (itemDesc.type() == ::profiler::BLOCK_TYPE_BLOCK)
  1283. {
  1284. const auto duration = itemBlock.node->duration();
  1285. lay->addWidget(new QLabel("Average:", widget), row, 0, Qt::AlignRight);
  1286. 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);
  1287. ++row;
  1288. // Calculate idle/active time
  1289. {
  1290. auto threadRoot = item->root();
  1291. ::profiler::block_index_t ind = 0;
  1292. auto it = ::std::lower_bound(threadRoot->sync.begin(), threadRoot->sync.end(), itemBlock.node->begin(), [](::profiler::block_index_t _cs_index, ::profiler::timestamp_t _val)
  1293. {
  1294. return EASY_GLOBALS.gui_blocks[_cs_index].tree.node->begin() < _val;
  1295. });
  1296. if (it != threadRoot->sync.end())
  1297. {
  1298. ind = it - threadRoot->sync.begin();
  1299. if (ind > 0)
  1300. --ind;
  1301. }
  1302. else
  1303. {
  1304. ind = static_cast<::profiler::block_index_t>(threadRoot->sync.size());
  1305. }
  1306. ::profiler::timestamp_t idleTime = 0;
  1307. for (::profiler::block_index_t ncs = static_cast<::profiler::block_index_t>(threadRoot->sync.size()); ind < ncs; ++ind)
  1308. {
  1309. auto cs_index = threadRoot->sync[ind];
  1310. const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
  1311. if (cs->begin() > itemBlock.node->end())
  1312. break;
  1313. if (itemBlock.node->begin() <= cs->begin() && cs->end() <= itemBlock.node->end())
  1314. idleTime += cs->duration();
  1315. }
  1316. const auto active_time = duration - idleTime;
  1317. const auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
  1318. lay->addWidget(new QLabel("Active time:", widget), row, 0, Qt::AlignRight);
  1319. 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);
  1320. ++row;
  1321. }
  1322. lay->addWidget(new EasyBoldLabel("-------- Statistics --------", widget), row, 0, 1, 5, Qt::AlignHCenter);
  1323. lay->addWidget(new QLabel("per ", widget), row + 1, 0, Qt::AlignRight);
  1324. lay->addWidget(new QLabel("This %:", widget), row + 2, 0, Qt::AlignRight);
  1325. lay->addWidget(new QLabel("Sum %:", widget), row + 3, 0, Qt::AlignRight);
  1326. lay->addWidget(new QLabel("Sum self %:", widget), row + 4, 0, Qt::AlignRight);
  1327. lay->addWidget(new QLabel("N Calls:", widget), row + 5, 0, Qt::AlignRight);
  1328. lay->addWidget(new QLabel("Thread", widget), row + 1, 1, Qt::AlignHCenter);
  1329. auto percent = ::profiler_gui::percentReal(duration, item->root()->profiled_time);
  1330. 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);
  1331. 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);
  1332. 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);
  1333. lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row + 5, 1, Qt::AlignHCenter);
  1334. int col = 1;
  1335. if (itemBlock.per_frame_stats->parent_block != i && !::profiler_gui::is_max(itemBlock.per_frame_stats->parent_block))
  1336. {
  1337. ++col;
  1338. auto frame_duration = blocksTree(itemBlock.per_frame_stats->parent_block).node->duration();
  1339. lay->addWidget(new QLabel("Frame", widget), row + 1, col, Qt::AlignHCenter);
  1340. percent = ::profiler_gui::percentReal(duration, frame_duration);
  1341. 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);
  1342. percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration, frame_duration);
  1343. 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);
  1344. percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration - itemBlock.per_frame_stats->total_children_duration, frame_duration);
  1345. 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);
  1346. lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), row + 5, col, Qt::AlignHCenter);
  1347. }
  1348. if (!::profiler_gui::is_max(itemBlock.per_parent_stats->parent_block))// != item->threadId())
  1349. {
  1350. ++col;
  1351. auto parent_duration = blocksTree(itemBlock.per_parent_stats->parent_block).node->duration();
  1352. lay->addWidget(new QLabel("Parent", widget), row + 1, col, Qt::AlignHCenter);
  1353. percent = ::profiler_gui::percentReal(duration, parent_duration);
  1354. 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);
  1355. percent = ::profiler_gui::percentReal(itemBlock.per_parent_stats->total_duration, parent_duration);
  1356. 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);
  1357. percent = ::profiler_gui::percentReal(itemBlock.per_parent_stats->total_duration - itemBlock.per_parent_stats->total_children_duration, parent_duration);
  1358. 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);
  1359. lay->addWidget(new QLabel(QString::number(itemBlock.per_parent_stats->calls_number), widget), row + 5, col, Qt::AlignHCenter);
  1360. ++col;
  1361. }
  1362. }
  1363. else
  1364. {
  1365. lay->addWidget(new QLabel("N calls/Thread:", widget), row, 0, Qt::AlignRight);
  1366. lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row, 1, Qt::AlignLeft);
  1367. }
  1368. }
  1369. m_popupWidget = new QGraphicsProxyWidget();
  1370. m_popupWidget->setWidget(widget);
  1371. break;
  1372. }
  1373. auto cse = item->intersectEvent(pos);
  1374. if (cse)
  1375. {
  1376. const auto& itemBlock = cse->tree;
  1377. auto widget = new QWidget(nullptr, Qt::FramelessWindowHint);
  1378. if (widget == nullptr)
  1379. return;
  1380. widget->setAttribute(Qt::WA_ShowWithoutActivating, true);
  1381. widget->setFocusPolicy(Qt::NoFocus);
  1382. auto lay = new QGridLayout(widget);
  1383. if (lay == nullptr)
  1384. return;
  1385. int row = 0;
  1386. lay->addWidget(new EasyBoldLabel("Context switch event", widget), row, 0, 1, 3, Qt::AlignHCenter);
  1387. ++row;
  1388. lay->addWidget(new QLabel("Thread:", widget), row, 0, Qt::AlignRight);
  1389. const char* process_name = "";
  1390. ::profiler::thread_id_t tid = 0;
  1391. if (EASY_GLOBALS.version < ::profiler_gui::V130)
  1392. {
  1393. tid = cse->tree.node->id();
  1394. process_name = cse->tree.node->name();
  1395. }
  1396. else
  1397. {
  1398. tid = cse->tree.cs->tid();
  1399. process_name = cse->tree.cs->name();
  1400. }
  1401. auto it = EASY_GLOBALS.profiler_blocks.find(tid);
  1402. if (it != EASY_GLOBALS.profiler_blocks.end())
  1403. {
  1404. if (EASY_GLOBALS.hex_thread_id)
  1405. lay->addWidget(new QLabel(QString("0x%1 %2").arg(tid, 0, 16).arg(it->second.name()), widget), row, 1, 1, 2, Qt::AlignLeft);
  1406. else
  1407. lay->addWidget(new QLabel(QString("%1 %2").arg(tid).arg(it->second.name()), widget), row, 1, 1, 2, Qt::AlignLeft);
  1408. }
  1409. else if (EASY_GLOBALS.hex_thread_id)
  1410. lay->addWidget(new QLabel(QString("0x%1").arg(tid, 0, 16), widget), row, 1, 1, 2, Qt::AlignLeft);
  1411. else
  1412. lay->addWidget(new QLabel(QString::number(tid), widget), row, 1, 1, 2, Qt::AlignLeft);
  1413. ++row;
  1414. lay->addWidget(new QLabel("Process:", widget), row, 0, Qt::AlignRight);
  1415. lay->addWidget(new QLabel(process_name, widget), row, 1, 1, 2, Qt::AlignLeft);
  1416. ++row;
  1417. const auto duration = itemBlock.node->duration();
  1418. lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight);
  1419. lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, 1, 2, Qt::AlignLeft);
  1420. ++row;
  1421. if (itemBlock.per_thread_stats)
  1422. {
  1423. lay->addWidget(new QLabel("Sum:", widget), row, 0, Qt::AlignRight);
  1424. 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);
  1425. ++row;
  1426. lay->addWidget(new EasyBoldLabel("-------- Statistics --------", widget), row, 0, 1, 3, Qt::AlignHCenter);
  1427. lay->addWidget(new QLabel("per ", widget), row + 1, 0, Qt::AlignRight);
  1428. lay->addWidget(new QLabel("This %:", widget), row + 2, 0, Qt::AlignRight);
  1429. lay->addWidget(new QLabel("Sum %:", widget), row + 3, 0, Qt::AlignRight);
  1430. lay->addWidget(new QLabel("N Calls:", widget), row + 4, 0, Qt::AlignRight);
  1431. lay->addWidget(new QLabel("Thread", widget), row + 1, 1, Qt::AlignHCenter);
  1432. auto percent = ::profiler_gui::percentReal(duration, item->root()->profiled_time);
  1433. 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);
  1434. 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);
  1435. lay->addWidget(new QLabel(QString::number(itemBlock.per_thread_stats->calls_number), widget), row + 4, 1, Qt::AlignHCenter);
  1436. if (itemBlock.per_frame_stats && !::profiler_gui::is_max(itemBlock.per_frame_stats->parent_block))
  1437. {
  1438. int col = 2;
  1439. auto frame_duration = blocksTree(itemBlock.per_frame_stats->parent_block).node->duration();
  1440. lay->addWidget(new QLabel("Frame", widget), row + 1, col, Qt::AlignHCenter);
  1441. percent = ::profiler_gui::percentReal(duration, frame_duration);
  1442. 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);
  1443. percent = ::profiler_gui::percentReal(itemBlock.per_frame_stats->total_duration, frame_duration);
  1444. 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);
  1445. lay->addWidget(new QLabel(QString::number(itemBlock.per_frame_stats->calls_number), widget), row + 4, col, Qt::AlignHCenter);
  1446. }
  1447. }
  1448. m_popupWidget = new QGraphicsProxyWidget();
  1449. m_popupWidget->setWidget(widget);
  1450. break;
  1451. }
  1452. }
  1453. if (m_popupWidget != nullptr)
  1454. {
  1455. auto effect = new QGraphicsDropShadowEffect();
  1456. effect->setBlurRadius(5);
  1457. effect->setOffset(3, 3);
  1458. m_popupWidget->setGraphicsEffect(effect);
  1459. scene()->addItem(m_popupWidget);
  1460. auto br = m_popupWidget->boundingRect();
  1461. if (scenePos.y() + br.height() > m_visibleSceneRect.bottom())
  1462. scenePos.setY(::std::max(scenePos.y() - br.height(), m_visibleSceneRect.top()));
  1463. if (scenePos.x() + br.width() > m_visibleSceneRect.right())
  1464. scenePos.setX(::std::max(scenePos.x() - br.width(), m_visibleSceneRect.left()));
  1465. m_popupWidget->setPos(scenePos);
  1466. m_popupWidget->setOpacity(0.95);
  1467. }
  1468. }
  1469. //////////////////////////////////////////////////////////////////////////
  1470. void EasyGraphicsView::onHierarchyFlagChange(bool)
  1471. {
  1472. bool changedSelection = false;
  1473. if (!m_selectedBlocks.empty())
  1474. {
  1475. changedSelection = true;
  1476. m_selectedBlocks.clear();
  1477. }
  1478. if (m_chronometerItem->isVisible())
  1479. {
  1480. for (auto item : m_items)
  1481. {
  1482. if (!EASY_GLOBALS.only_current_thread_hierarchy || item->threadId() == EASY_GLOBALS.selected_thread)
  1483. item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks);
  1484. }
  1485. if (!m_selectedBlocks.empty())
  1486. {
  1487. changedSelection = true;
  1488. }
  1489. }
  1490. if (changedSelection)
  1491. {
  1492. emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
  1493. }
  1494. }
  1495. void EasyGraphicsView::onSelectedThreadChange(::profiler::thread_id_t _id)
  1496. {
  1497. if (m_pScrollbar == nullptr || m_pScrollbar->hystThread() == _id)
  1498. {
  1499. return;
  1500. }
  1501. if (_id == 0)
  1502. {
  1503. m_pScrollbar->setHistogramSource(0, nullptr);
  1504. return;
  1505. }
  1506. for (auto item : m_items)
  1507. {
  1508. if (item->threadId() == _id)
  1509. {
  1510. m_pScrollbar->setHistogramSource(_id, item->items(0));
  1511. bool changedSelection = false;
  1512. if (EASY_GLOBALS.only_current_thread_hierarchy)
  1513. {
  1514. if (!m_selectedBlocks.empty())
  1515. {
  1516. changedSelection = true;
  1517. m_selectedBlocks.clear();
  1518. }
  1519. if (m_chronometerItem->isVisible())
  1520. {
  1521. item->getBlocks(m_chronometerItem->left(), m_chronometerItem->right(), m_selectedBlocks);
  1522. if (!m_selectedBlocks.empty())
  1523. changedSelection = true;
  1524. }
  1525. }
  1526. if (changedSelection)
  1527. {
  1528. emit intervalChanged(m_selectedBlocks, m_beginTime, position2time(m_chronometerItem->left()), position2time(m_chronometerItem->right()), m_chronometerItem->reverse());
  1529. }
  1530. repaintScene();
  1531. return;
  1532. }
  1533. }
  1534. m_pScrollbar->setHistogramSource(0, nullptr);
  1535. repaintScene();
  1536. }
  1537. //////////////////////////////////////////////////////////////////////////
  1538. void EasyGraphicsView::onSelectedBlockChange(unsigned int _block_index)
  1539. {
  1540. if (!m_bUpdatingRect)
  1541. {
  1542. if (_block_index < EASY_GLOBALS.gui_blocks.size())
  1543. {
  1544. // Scroll to item
  1545. const auto& guiblock = EASY_GLOBALS.gui_blocks[_block_index];
  1546. const auto thread_item = m_items[guiblock.graphics_item];
  1547. const auto& item = thread_item->items(guiblock.graphics_item_level)[guiblock.graphics_item_index];
  1548. m_flickerSpeedX = m_flickerSpeedY = 0;
  1549. m_bUpdatingRect = true;
  1550. verticalScrollBar()->setValue(static_cast<int>(thread_item->levelY(guiblock.graphics_item_level) - m_visibleSceneRect.height() * 0.5));
  1551. m_pScrollbar->setValue(item.left() + item.width() * 0.5 - m_pScrollbar->sliderHalfWidth());
  1552. if (EASY_GLOBALS.selecting_block_changes_thread && EASY_GLOBALS.selected_thread != thread_item->threadId())
  1553. {
  1554. EASY_GLOBALS.selected_thread = thread_item->threadId();
  1555. m_pScrollbar->lock();
  1556. emit EASY_GLOBALS.events.selectedThreadChanged(EASY_GLOBALS.selected_thread);
  1557. m_pScrollbar->unlock();
  1558. }
  1559. m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, guiblock.tree.node->id());
  1560. m_bUpdatingRect = false;
  1561. }
  1562. else if (EASY_GLOBALS.selected_thread != 0)
  1563. {
  1564. for (auto item : m_items)
  1565. {
  1566. if (item->threadId() == EASY_GLOBALS.selected_thread)
  1567. {
  1568. m_pScrollbar->setHistogramSource(EASY_GLOBALS.selected_thread, item->items(0));
  1569. break;
  1570. }
  1571. }
  1572. }
  1573. else
  1574. {
  1575. m_pScrollbar->setHistogramSource(0, nullptr);
  1576. }
  1577. updateVisibleSceneRect();
  1578. repaintScene();
  1579. }
  1580. }
  1581. //////////////////////////////////////////////////////////////////////////
  1582. void EasyGraphicsView::onRefreshRequired()
  1583. {
  1584. if (!m_bUpdatingRect)
  1585. {
  1586. repaintScene();
  1587. }
  1588. }
  1589. //////////////////////////////////////////////////////////////////////////
  1590. EasyGraphicsViewWidget::EasyGraphicsViewWidget(QWidget* _parent)
  1591. : QWidget(_parent)
  1592. , m_scrollbar(new EasyGraphicsScrollbar(this))
  1593. , m_view(new EasyGraphicsView(this))
  1594. , m_threadNamesWidget(new EasyThreadNamesWidget(m_view, m_scrollbar->height(), this))
  1595. {
  1596. initWidget();
  1597. }
  1598. void EasyGraphicsViewWidget::initWidget()
  1599. {
  1600. auto lay = new QGridLayout(this);
  1601. lay->setContentsMargins(1, 0, 1, 0);
  1602. lay->addWidget(m_threadNamesWidget, 0, 0, 2, 1);
  1603. lay->setSpacing(1);
  1604. lay->addWidget(m_view, 0, 1);
  1605. lay->setSpacing(1);
  1606. lay->addWidget(m_scrollbar, 1, 1);
  1607. setLayout(lay);
  1608. m_view->setScrollbar(m_scrollbar);
  1609. }
  1610. EasyGraphicsViewWidget::~EasyGraphicsViewWidget()
  1611. {
  1612. }
  1613. EasyGraphicsView* EasyGraphicsViewWidget::view()
  1614. {
  1615. return m_view;
  1616. }
  1617. void EasyGraphicsViewWidget::clear()
  1618. {
  1619. m_scrollbar->clear();
  1620. m_threadNamesWidget->clear();
  1621. m_view->clear();
  1622. }
  1623. //////////////////////////////////////////////////////////////////////////
  1624. //////////////////////////////////////////////////////////////////////////
  1625. void EasyThreadNameItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
  1626. {
  1627. auto const parentView = static_cast<EasyThreadNamesWidget*>(scene()->parent());
  1628. const auto view = parentView->view();
  1629. const auto& items = view->getItems();
  1630. if (items.empty())
  1631. return;
  1632. const auto visibleSceneRect = view->visibleSceneRect();
  1633. const auto h = visibleSceneRect.height() + TIMELINE_ROW_SIZE - 2;
  1634. const auto w = parentView->width();//parentView->sceneRect().width();
  1635. static const uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
  1636. static const QBrush brushes[2] = {QColor::fromRgb(BACKGROUND_1), QColor::fromRgb(BACKGROUND_2)};
  1637. int i = -1;
  1638. QRectF rect;
  1639. _painter->resetTransform();
  1640. // Draw thread names
  1641. auto default_font = _painter->font();
  1642. _painter->setFont(BG_FONT);
  1643. for (auto item : items)
  1644. {
  1645. ++i;
  1646. auto br = item->boundingRect();
  1647. auto top = item->y() + br.top() - visibleSceneRect.top() - OVERLAP;
  1648. auto hgt = br.height() + ::profiler_gui::THREADS_ROW_SPACING;
  1649. auto bottom = top + hgt;
  1650. if (top > h || bottom < 0)
  1651. continue;
  1652. if (item->threadId() == EASY_GLOBALS.selected_thread)
  1653. _painter->setBrush(QBrush(QColor::fromRgb(::profiler_gui::SELECTED_THREAD_BACKGROUND)));
  1654. else
  1655. _painter->setBrush(brushes[i & 1]);
  1656. if (top < 0)
  1657. {
  1658. hgt += top;
  1659. top = 0;
  1660. }
  1661. const auto dh = top + hgt - h;
  1662. if (dh > 0)
  1663. hgt -= dh;
  1664. rect.setRect(0, top, w, hgt);
  1665. _painter->setPen(Qt::NoPen);
  1666. _painter->drawRect(rect);
  1667. rect.translate(-5, 0);
  1668. _painter->setPen(QColor::fromRgb(::profiler::colors::Dark));
  1669. _painter->drawText(rect, Qt::AlignRight | Qt::AlignVCenter, item->threadName());
  1670. }
  1671. const auto rect_bottom = rect.bottom();
  1672. if (rect_bottom < h)
  1673. {
  1674. ++i;
  1675. rect.translate(5, rect.height());
  1676. rect.setHeight(h - rect_bottom);
  1677. _painter->setBrush(brushes[i & 1]);
  1678. _painter->setPen(Qt::NoPen);
  1679. _painter->drawRect(rect);
  1680. }
  1681. // Draw separator between thread names area and information area
  1682. _painter->setPen(Qt::darkGray);
  1683. _painter->drawLine(QLineF(0, h, w, h));
  1684. _painter->drawLine(QLineF(0, h + 2, w, h + 2));
  1685. // Draw information
  1686. _painter->setFont(CHRONOMETER_FONT);
  1687. QFontMetricsF fm(CHRONOMETER_FONT, parentView);
  1688. const qreal th = fm.height(); // Calculate displayed text height
  1689. const qreal time1 = view->chronoTime();
  1690. const qreal time2 = view->chronoTimeAux();
  1691. auto y = h + 2;
  1692. auto drawTimeText = [&rect, &w, &y, &fm, &_painter](qreal time, qreal th, QRgb color)
  1693. {
  1694. if (time > 0)
  1695. {
  1696. const QString text = ::profiler_gui::autoTimeStringReal(time); // Displayed text
  1697. rect.setRect(0, y, w, th);
  1698. _painter->setPen(color);
  1699. _painter->drawText(rect, Qt::AlignCenter, text);
  1700. y += th;
  1701. }
  1702. };
  1703. drawTimeText(time1, th, ::profiler_gui::CHRONOMETER_COLOR.rgb() & 0x00ffffff);
  1704. drawTimeText(time2, th, ::profiler_gui::CHRONOMETER_COLOR2.rgb() & 0x00ffffff);
  1705. }
  1706. //////////////////////////////////////////////////////////////////////////
  1707. EasyThreadNamesWidget::EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent)
  1708. : Parent(_parent)
  1709. , m_idleTime(0)
  1710. , m_view(_view)
  1711. , m_popupWidget(nullptr)
  1712. , m_maxLength(100)
  1713. , m_additionalHeight(_additionalHeight + 1)
  1714. {
  1715. setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
  1716. setScene(new QGraphicsScene(this));
  1717. setCacheMode(QGraphicsView::CacheNone);
  1718. setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
  1719. setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  1720. setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  1721. setFixedWidth(m_maxLength);
  1722. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, [this](::profiler::thread_id_t){ repaintScene(); });
  1723. connect(m_view, &EasyGraphicsView::treeChanged, this, &This::onTreeChange);
  1724. connect(m_view, &EasyGraphicsView::sceneUpdated, this, &This::repaintScene);
  1725. connect(m_view->verticalScrollBar(), &QScrollBar::valueChanged, verticalScrollBar(), &QScrollBar::setValue);
  1726. connect(m_view->verticalScrollBar(), &QScrollBar::rangeChanged, this, &This::setVerticalScrollbarRange);
  1727. connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout);
  1728. }
  1729. EasyThreadNamesWidget::~EasyThreadNamesWidget()
  1730. {
  1731. }
  1732. void EasyThreadNamesWidget::removePopup(bool _removeFromScene)
  1733. {
  1734. if (m_popupWidget != nullptr)
  1735. {
  1736. auto widget = m_popupWidget->widget();
  1737. widget->setParent(nullptr);
  1738. m_popupWidget->setWidget(nullptr);
  1739. delete widget;
  1740. if (_removeFromScene)
  1741. scene()->removeItem(m_popupWidget);
  1742. m_popupWidget = nullptr;
  1743. }
  1744. }
  1745. void EasyThreadNamesWidget::clear()
  1746. {
  1747. const QSignalBlocker b(this);
  1748. removePopup();
  1749. scene()->clear();
  1750. m_maxLength = 100;
  1751. setFixedWidth(m_maxLength);
  1752. m_idleTimer.stop();
  1753. m_idleTime = 0;
  1754. }
  1755. void EasyThreadNamesWidget::setVerticalScrollbarRange(int _minValue, int _maxValue)
  1756. {
  1757. verticalScrollBar()->setRange(_minValue, _maxValue + m_additionalHeight);
  1758. }
  1759. void EasyThreadNamesWidget::onTreeChange()
  1760. {
  1761. const QSignalBlocker b(this);
  1762. removePopup();
  1763. scene()->clear();
  1764. m_idleTimer.stop();
  1765. m_idleTime = 0;
  1766. QFontMetricsF fm(BG_FONT, this);
  1767. qreal maxLength = 100;
  1768. const auto& graphicsItems = m_view->getItems();
  1769. for (auto graphicsItem : graphicsItems)
  1770. maxLength = ::std::max(maxLength, (10 + fm.width(graphicsItem->threadName())) * ::profiler_gui::FONT_METRICS_FACTOR);
  1771. auto vbar = verticalScrollBar();
  1772. auto viewBar = m_view->verticalScrollBar();
  1773. setVerticalScrollbarRange(viewBar->minimum(), viewBar->maximum());
  1774. vbar->setSingleStep(viewBar->singleStep());
  1775. vbar->setPageStep(viewBar->pageStep());
  1776. auto r = m_view->sceneRect();
  1777. setSceneRect(0, r.top(), maxLength, r.height() + m_additionalHeight);
  1778. auto item = new EasyThreadNameItem();
  1779. item->setPos(0, 0);
  1780. item->setBoundingRect(sceneRect());
  1781. scene()->addItem(item);
  1782. m_maxLength = static_cast<int>(maxLength);
  1783. setFixedWidth(m_maxLength);
  1784. m_idleTimer.start(IDLE_TIMER_INTERVAL);
  1785. }
  1786. void EasyThreadNamesWidget::onIdleTimeout()
  1787. {
  1788. static const uint16_t OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
  1789. m_idleTime += IDLE_TIMER_INTERVAL;
  1790. if (m_idleTime < IDLE_TIME)
  1791. {
  1792. removePopup(true);
  1793. return;
  1794. }
  1795. if (m_popupWidget != nullptr)
  1796. return;
  1797. auto visibleSceneRect = mapToScene(rect()).boundingRect();
  1798. auto scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
  1799. if (scenePos.x() < visibleSceneRect.left() || scenePos.x() > visibleSceneRect.right())
  1800. {
  1801. if (m_idleTime > 3000)
  1802. setFixedWidth(m_maxLength);
  1803. return;
  1804. }
  1805. if (scenePos.y() < visibleSceneRect.top() || scenePos.y() > visibleSceneRect.bottom())
  1806. {
  1807. if (m_idleTime > 3000)
  1808. setFixedWidth(m_maxLength);
  1809. return;
  1810. }
  1811. auto const parentView = static_cast<EasyThreadNamesWidget*>(scene()->parent());
  1812. const auto view = parentView->view();
  1813. if (scenePos.y() > view->visibleSceneRect().bottom())
  1814. {
  1815. if (m_idleTime > 3000)
  1816. setFixedWidth(m_maxLength);
  1817. return;
  1818. }
  1819. const qreal y = scenePos.y() - visibleSceneRect.top();
  1820. const auto& items = view->getItems();
  1821. if (items.empty())
  1822. {
  1823. if (m_idleTime > 3000)
  1824. setFixedWidth(m_maxLength);
  1825. return;
  1826. }
  1827. EasyGraphicsItem* intersectingItem = nullptr;
  1828. for (auto item : items)
  1829. {
  1830. auto br = item->boundingRect();
  1831. auto top = item->y() + br.top() - visibleSceneRect.top() - OVERLAP;
  1832. auto hgt = br.height() + ::profiler_gui::THREADS_ROW_SPACING;
  1833. auto bottom = top + hgt;
  1834. if (bottom < y || y < top)
  1835. continue;
  1836. intersectingItem = item;
  1837. break;
  1838. }
  1839. if (intersectingItem != nullptr)
  1840. {
  1841. auto widget = new QWidget(nullptr, Qt::FramelessWindowHint);
  1842. if (widget == nullptr)
  1843. return;
  1844. widget->setAttribute(Qt::WA_ShowWithoutActivating, true);
  1845. widget->setFocusPolicy(Qt::NoFocus);
  1846. auto lay = new QGridLayout(widget);
  1847. if (lay == nullptr)
  1848. return;
  1849. int row = 0;
  1850. lay->addWidget(new EasyBoldLabel(intersectingItem->threadName(), widget), row, 0, 1, 2, Qt::AlignHCenter);
  1851. ++row;
  1852. ::profiler::timestamp_t duration = 0;
  1853. const auto& root = *intersectingItem->root();
  1854. if (!root.children.empty())
  1855. duration = easyBlock(root.children.back()).tree.node->end() - easyBlock(root.children.front()).tree.node->begin();
  1856. lay->addWidget(new QLabel("Duration:", widget), row, 0, Qt::AlignRight);
  1857. lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, duration, 3), widget), row, 1, Qt::AlignLeft);
  1858. ++row;
  1859. lay->addWidget(new QLabel("Profiled:", widget), row, 0, Qt::AlignRight);
  1860. if (duration)
  1861. {
  1862. lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.profiled_time, 3))
  1863. .arg(QString::number(100. * (double)root.profiled_time / (double)duration, 'f', 2)), widget), row, 1, Qt::AlignLeft);
  1864. }
  1865. else
  1866. {
  1867. lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.profiled_time, 3), widget), row, 1, Qt::AlignLeft);
  1868. }
  1869. ++row;
  1870. lay->addWidget(new QLabel("Wait:", widget), row, 0, Qt::AlignRight);
  1871. if (duration)
  1872. {
  1873. lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.wait_time, 3))
  1874. .arg(QString::number(100. * (double)root.wait_time / (double)duration, 'f', 2)), widget), row, 1, Qt::AlignLeft);
  1875. }
  1876. else
  1877. {
  1878. lay->addWidget(new QLabel(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, root.wait_time, 3), widget), row, 1, Qt::AlignLeft);
  1879. }
  1880. ++row;
  1881. const auto eventsSize = root.events.size();
  1882. lay->addWidget(new QLabel("Frames:", widget), row, 0, Qt::AlignRight);
  1883. lay->addWidget(new QLabel(QString::number(root.frames_number), widget), row, 1, Qt::AlignLeft);
  1884. ++row;
  1885. lay->addWidget(new QLabel("Blocks:", widget), row, 0, Qt::AlignRight);
  1886. lay->addWidget(new QLabel(QString::number(root.blocks_number - eventsSize), widget), row, 1, Qt::AlignLeft);
  1887. ++row;
  1888. lay->addWidget(new QLabel("Markers:", widget), row, 0, Qt::AlignRight);
  1889. lay->addWidget(new QLabel(QString::number(eventsSize), widget), row, 1, Qt::AlignLeft);
  1890. ++row;
  1891. m_popupWidget = new QGraphicsProxyWidget();
  1892. if (m_popupWidget != nullptr)
  1893. {
  1894. auto effect = new QGraphicsDropShadowEffect();
  1895. effect->setBlurRadius(5);
  1896. effect->setOffset(3, 3);
  1897. m_popupWidget->setGraphicsEffect(effect);
  1898. m_popupWidget->setWidget(widget);
  1899. scene()->addItem(m_popupWidget);
  1900. auto br = m_popupWidget->boundingRect();
  1901. if (maximumWidth() < br.width())
  1902. {
  1903. setFixedWidth(static_cast<int>(br.width()));
  1904. visibleSceneRect.setWidth(br.width());
  1905. }
  1906. if (scenePos.y() + br.height() > visibleSceneRect.bottom())
  1907. scenePos.setY(::std::max(scenePos.y() - br.height(), visibleSceneRect.top()));
  1908. if (scenePos.x() + br.width() > visibleSceneRect.right())
  1909. scenePos.setX(::std::max(scenePos.x() - br.width(), visibleSceneRect.left()));
  1910. m_popupWidget->setPos(scenePos);
  1911. m_popupWidget->setOpacity(0.95);
  1912. }
  1913. }
  1914. }
  1915. void EasyThreadNamesWidget::repaintScene()
  1916. {
  1917. scene()->update();
  1918. }
  1919. void EasyThreadNamesWidget::mousePressEvent(QMouseEvent* _event)
  1920. {
  1921. m_idleTime = 0;
  1922. QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers());
  1923. m_view->mousePressEvent(&e);
  1924. _event->accept();
  1925. }
  1926. void EasyThreadNamesWidget::mouseDoubleClickEvent(QMouseEvent* _event)
  1927. {
  1928. static const auto OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
  1929. m_idleTime = 0;
  1930. auto y = mapToScene(_event->pos()).y();
  1931. const auto& items = m_view->getItems();
  1932. for (auto item : items)
  1933. {
  1934. auto br = item->boundingRect();
  1935. auto top = item->y() + br.top() - OVERLAP;
  1936. auto bottom = top + br.height() + OVERLAP;
  1937. if (y < top || y > bottom)
  1938. continue;
  1939. const auto thread_id = item->threadId();
  1940. if (thread_id != EASY_GLOBALS.selected_thread)
  1941. {
  1942. EASY_GLOBALS.selected_thread = thread_id;
  1943. emit EASY_GLOBALS.events.selectedThreadChanged(thread_id);
  1944. }
  1945. break;
  1946. }
  1947. _event->accept();
  1948. }
  1949. void EasyThreadNamesWidget::mouseReleaseEvent(QMouseEvent* _event)
  1950. {
  1951. m_idleTime = 0;
  1952. QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers());
  1953. m_view->mouseReleaseEvent(&e);
  1954. _event->accept();
  1955. }
  1956. void EasyThreadNamesWidget::mouseMoveEvent(QMouseEvent* _event)
  1957. {
  1958. m_idleTime = 0;
  1959. QMouseEvent e(_event->type(), _event->pos() - QPointF(sceneRect().width(), 0), _event->button(), _event->buttons() & ~Qt::RightButton, _event->modifiers());
  1960. m_view->mouseMoveEvent(&e);
  1961. _event->accept();
  1962. }
  1963. void EasyThreadNamesWidget::keyPressEvent(QKeyEvent* _event)
  1964. {
  1965. m_idleTime = 0;
  1966. m_view->keyPressEvent(_event);
  1967. }
  1968. void EasyThreadNamesWidget::keyReleaseEvent(QKeyEvent* _event)
  1969. {
  1970. m_idleTime = 0;
  1971. m_view->keyReleaseEvent(_event);
  1972. }
  1973. void EasyThreadNamesWidget::wheelEvent(QWheelEvent* _event)
  1974. {
  1975. m_idleTime = 0;
  1976. auto vbar = m_view->verticalScrollBar();
  1977. if (vbar != nullptr)
  1978. {
  1979. _event->accept();
  1980. vbar->setValue(vbar->value() - _event->delta());
  1981. }
  1982. }
  1983. //////////////////////////////////////////////////////////////////////////
  1984. //////////////////////////////////////////////////////////////////////////