partition_widget.cpp
Go to the documentation of this file.
1 // *****************************************************************************
2 //
3 // Copyright (c) 2015, Southwest Research Institute® (SwRI®)
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Southwest Research Institute® (SwRI®) nor the
14 // names of its contributors may be used to endorse or promote products
15 // derived from this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL Southwest Research Institute® BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27 // DAMAGE.
28 //
29 // *****************************************************************************
31 
32 #include <QPainter>
33 #include <QVBoxLayout>
34 #include <QToolTip>
35 #include <QHelpEvent>
36 #include <QMouseEvent>
37 #include <QDebug>
38 
42 
43 namespace swri_profiler_tools
44 {
45 static QColor colorFromString(const QString &name)
46 {
47  size_t name_hash = std::hash<std::string>{}(name.toStdString());
48 
49  int h = (name_hash >> 0) % 255;
50  int s = (name_hash >> 8) % 200 + 55;
51  int v = (name_hash >> 16) % 200 + 55;
52  return QColor::fromHsv(h, s, v);
53 }
54 
56  :
57  QWidget(parent),
58  db_(NULL)
59 {
60  view_animator_ = new VariantAnimation(this);
61  view_animator_->setEasingCurve(QEasingCurve::InOutCubic);
62  QObject::connect(view_animator_, SIGNAL(valueChanged(const QVariant &)),
63  this, SLOT(update()));
64 }
65 
67 {
68 }
69 
71 {
72  if (db_) {
73  // note(exjohnson): we can implement this later if desired, but
74  // currently no use case for it.
75  qWarning("PartitionWidget: Cannot change the profile database.");
76  return;
77  }
78 
79  db_ = db;
80 
81  updateData();
82  QObject::connect(db_, SIGNAL(dataAdded(int)), this, SLOT(updateData()));
83  QObject::connect(db_, SIGNAL(profileAdded(int)), this, SLOT(updateData()));
84  QObject::connect(db_, SIGNAL(nodesAdded(int)), this, SLOT(updateData()));
85 }
86 
88 {
89  if (!active_key_.isValid()) {
90  return;
91  }
92 
93  const Profile &profile = db_->profile(active_key_.profileKey());
94  Layout layout = layoutProfile(profile);
95  QRectF data_rect = dataRect(layout);
96  view_animator_->setEndValue(data_rect);
97  current_layout_ = layout;
98 
99  update();
100 }
101 
102 void PartitionWidget::paintEvent(QPaintEvent *)
103 {
104  QPainter painter(this);
105 
106  if (current_layout_.empty()) {
107  QRect win_rect(0,0,width(), height());
108  painter.setBrush(Qt::white);
109  painter.drawRect(win_rect.adjusted(1,1,-1,-1));
110  return;
111  }
112 
113  QRectF data_rect = view_animator_->currentValue().toRectF();
114  QRectF win_rect(QPointF(0, 0), QPointF(width()-1, height()-1));
115  win_rect = win_rect.adjusted(1,1,-1,-1);
116  win_from_data_ = getTransform(win_rect, data_rect);
117 
118  const Profile &profile = db_->profile(active_key_.profileKey());
119  renderLayout(painter, win_from_data_, current_layout_, profile);
120 }
121 
122 QRectF PartitionWidget::dataRect(const Layout &layout) const
123 {
124  if (layout.empty()) {
125  return QRectF(0.0, 0.0, 1.0, 1.0);
126  }
127 
128  double right = layout.back().rect.right();
129 
130  for (auto const &item : layout) {
131  if (active_key_.nodeKey() == item.node_key && item.exclusive == false) {
132  QRectF rect = item.rect;
133 
134  rect.setLeft(std::max(0.0, rect.left()-0.2));
135  rect.setRight(right);
136 
137  double margin = 0.05 * rect.height();
138  rect.setTop(std::max(0.0, rect.top() - margin));
139  rect.setBottom(std::min(1.0, rect.bottom() + margin));
140  return rect;
141  }
142  }
143 
144  qWarning("Active node key was not found in layout");
145  return QRectF(QPointF(0.0, 0.0), QPointF(right, 1.0));
146 }
147 
148 void PartitionWidget::setActiveNode(int profile_key, int node_key)
149 {
150  const DatabaseKey new_key(profile_key, node_key);
151 
152  if (new_key == active_key_) {
153  return;
154  }
155 
156  bool first = true;
157  if (active_key_.isValid()) {
158  first = false;
159  }
160 
161  active_key_ = new_key;
162 
163  const Profile &profile = db_->profile(active_key_.profileKey());
164  Layout layout = layoutProfile(profile);
165  QRectF data_rect = dataRect(layout);
166  current_layout_ = layout;
167 
168  if (!first) {
169  view_animator_->stop();
170  view_animator_->setStartValue(view_animator_->endValue());
171  view_animator_->setEndValue(data_rect);
172  view_animator_->setDuration(500);
173  view_animator_->start();
174  } else {
175  view_animator_->setStartValue(data_rect);
176  view_animator_->setEndValue(data_rect);
177  }
178 
179  emit activeNodeChanged(profile_key, node_key);
180 }
181 
183 {
184  Layout layout;
185 
186  const ProfileNode &root_node = profile.rootNode();
187  if (!root_node.isValid()) {
188  qWarning("Profile returned invalid root node.");
189  return layout;
190  }
191 
192  if (root_node.data().empty()) {
193  return layout;
194  }
195 
196  double time_scale = root_node.data().back().cumulative_inclusive_duration_ns;
197 
198  int column = 0;
199  LayoutItem root_item;
200  root_item.node_key = root_node.nodeKey();
201  root_item.exclusive = false;
202  root_item.rect = QRectF(column, 0.0, 1, 1.0);
203  layout.push_back(root_item);
204 
205  bool keep_going = root_node.hasChildren();
206 
207  std::vector<LayoutItem> parents;
208  std::vector<LayoutItem> children;
209  parents.push_back(root_item);
210 
211  while (keep_going) {
212  // We going to stop unless we see some children.
213  keep_going = false;
214  column++;
215 
216  double span_start = 0.0;
217  for (auto const &parent_item : parents) {
218  const ProfileNode &parent_node = profile.node(parent_item.node_key);
219 
220  // Add the carry-over exclusive item.
221  {
222  double height = parent_node.data().back().cumulative_exclusive_duration_ns/time_scale;
223  LayoutItem item;
224  item.node_key = parent_item.node_key;
225  item.exclusive = true;
226  item.rect = QRectF(column, span_start, 1, height);
227  children.push_back(item);
228  span_start = item.rect.bottom();
229  }
230 
231  // Don't add children for an exclusive item because they've already been added.
232  if (parent_item.exclusive) {
233  continue;
234  }
235 
236  for (int child_key : parent_node.childKeys()) {
237  const ProfileNode &child_node = profile.node(child_key);
238  double height = child_node.data().back().cumulative_inclusive_duration_ns / time_scale;
239 
240  LayoutItem item;
241  item.node_key = child_key;
242  item.exclusive = false;
243  item.rect = QRectF(column, span_start, 1, height);
244  children.push_back(item);
245  span_start = item.rect.bottom();
246 
247  keep_going |= child_node.hasChildren();
248  }
249  }
250 
251  layout.insert(layout.end(), children.begin(), children.end());
252  parents.swap(children);
253  children.clear();
254  }
255 
256  return layout;
257 }
258 
259 void PartitionWidget::renderLayout(QPainter &painter,
260  const QTransform &win_from_data,
261  const Layout &layout,
262  const Profile &profile)
263 {
264  // Set painter to use a single-pixel black pen.
265  painter.setPen(Qt::black);
266 
267  double right = layout.back().rect.right();
268 
269  for (auto const &item : layout) {
270  if (item.exclusive) {
271  continue;
272  }
273 
274  const ProfileNode &node = profile.node(item.node_key);
275  QColor color = colorFromString(node.name());
276 
277  QRectF data_rect = item.rect;
278  data_rect.setRight(right);
279  QRectF win_rect = win_from_data.mapRect(data_rect);
280  QRect int_rect = roundRectF(win_rect);
281 
282  painter.setBrush(color);
283  painter.drawRect(int_rect.adjusted(0,0,-1,-1));
284  }
285 }
286 
287 QTransform PartitionWidget::getTransform(const QRectF &win_rect,
288  const QRectF &data_rect)
289 {
290  double sx = win_rect.width() / data_rect.width();
291  double sy = win_rect.height() / data_rect.height();
292  double tx = win_rect.topLeft().x() - sx*data_rect.topLeft().x();
293  double ty = win_rect.topLeft().y() - sy*data_rect.topLeft().y();
294 
295  QTransform win_from_data(sx, 0.0, 0.0,
296  0.0, sy, 0.0,
297  tx, ty, 1.0);
298  return win_from_data;
299 }
300 
301 int PartitionWidget::itemAtPoint(const QPointF &point) const
302 {
303  for (size_t i = 0; i < current_layout_.size(); i++) {
304  auto const &item = current_layout_[i];
305  if (item.rect.contains(point)) {
306  return i;
307  }
308  }
309  return -1;
310 }
311 
313 {
314  if (event->type() == QEvent::ToolTip) {
315  toolTipEvent(static_cast<QHelpEvent*>(event));
316  event->accept();
317  return true;
318  }
319  return QWidget::event(event);
320 }
321 
323 {
324  QTransform data_from_win = win_from_data_.inverted();
325  int index = itemAtPoint(data_from_win.map(QPointF(event->pos())));
326 
327  if (index < 0) {
328  QToolTip::hideText();
329  return;
330  }
331 
332  const Profile &profile = db_->profile(active_key_.profileKey());
333  const LayoutItem &item = current_layout_[index];
334 
335  QString tool_tip;
336  if (item.node_key == profile.rootKey()) {
337  tool_tip = profile.name();
338  } else {
339  tool_tip = profile.node(item.node_key).path();
340  if (item.exclusive) {
341  tool_tip += " [exclusive]";
342  }
343  }
344 
345  QRectF win_rect = win_from_data_.mapRect(item.rect);
346  QRect int_rect = roundRectF(win_rect);
347  QToolTip::showText(event->globalPos(), tool_tip, this, int_rect);
348 }
349 
351 {
352 }
353 
355 {
356  QTransform data_from_win = win_from_data_.inverted();
357 #if QT_VERSION >= 0x050000
358  int index = itemAtPoint(data_from_win.map(event->localPos()));
359 #else
360  int index = itemAtPoint(data_from_win.map(event->posF()));
361 #endif
362 
363  if (index < 0) {
364  return;
365  }
366 
367  const LayoutItem &item = current_layout_[index];
369 }
370 } // namespace swri_profiler_tools
static QColor colorFromString(const QString &name)
const ProfileNode & rootNode() const
Definition: profile.cpp:467
void activeNodeChanged(int profile_key, int node_key)
void setDatabase(ProfileDatabase *db)
void setActiveNode(int profile_key, int node_key)
void renderLayout(QPainter &painter, const QTransform &win_from_rect, const Layout &layout, const Profile &profile)
const std::vector< int > & childKeys() const
Definition: profile.h:137
XmlRpcServer s
const QString & path() const
Definition: profile.h:132
void mousePressEvent(QMouseEvent *event)
const ProfileNode & node(int node_key) const
Definition: profile.cpp:457
Layout layoutProfile(const Profile &profile)
std::vector< LayoutItem > Layout
void update(const std::string &key, const XmlRpc::XmlRpcValue &v)
QRect roundRectF(const QRectF &src)
Definition: util.h:47
const QString & name() const
Definition: profile.h:211
int itemAtPoint(const QPointF &point) const
const QString & name() const
Definition: profile.h:131
void mouseDoubleClickEvent(QMouseEvent *event)
QTransform getTransform(const QRectF &win_rect, const QRectF &data_rect)
const std::deque< ProfileEntry > & data() const
Definition: profile.h:134
QRectF dataRect(const Layout &layout) const


swri_profiler_tools
Author(s):
autogenerated on Fri Nov 27 2020 03:44:18