00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <swri_profiler_tools/partition_widget.h>
00031
00032 #include <QPainter>
00033 #include <QVBoxLayout>
00034 #include <QToolTip>
00035 #include <QHelpEvent>
00036 #include <QMouseEvent>
00037 #include <QDebug>
00038
00039 #include <swri_profiler_tools/util.h>
00040 #include <swri_profiler_tools/profile_database.h>
00041 #include <swri_profiler_tools/variant_animation.h>
00042
00043 namespace swri_profiler_tools
00044 {
00045 static QColor colorFromString(const QString &name)
00046 {
00047 size_t name_hash = std::hash<std::string>{}(name.toStdString());
00048
00049 int h = (name_hash >> 0) % 255;
00050 int s = (name_hash >> 8) % 200 + 55;
00051 int v = (name_hash >> 16) % 200 + 55;
00052 return QColor::fromHsv(h, s, v);
00053 }
00054
00055 PartitionWidget::PartitionWidget(QWidget *parent)
00056 :
00057 QWidget(parent),
00058 db_(NULL)
00059 {
00060 view_animator_ = new VariantAnimation(this);
00061 view_animator_->setEasingCurve(QEasingCurve::InOutCubic);
00062 QObject::connect(view_animator_, SIGNAL(valueChanged(const QVariant &)),
00063 this, SLOT(update()));
00064 }
00065
00066 PartitionWidget::~PartitionWidget()
00067 {
00068 }
00069
00070 void PartitionWidget::setDatabase(ProfileDatabase *db)
00071 {
00072 if (db_) {
00073
00074
00075 qWarning("PartitionWidget: Cannot change the profile database.");
00076 return;
00077 }
00078
00079 db_ = db;
00080
00081 updateData();
00082 QObject::connect(db_, SIGNAL(dataAdded(int)), this, SLOT(updateData()));
00083 QObject::connect(db_, SIGNAL(profileAdded(int)), this, SLOT(updateData()));
00084 QObject::connect(db_, SIGNAL(nodesAdded(int)), this, SLOT(updateData()));
00085 }
00086
00087 void PartitionWidget::updateData()
00088 {
00089 if (!active_key_.isValid()) {
00090 return;
00091 }
00092
00093 const Profile &profile = db_->profile(active_key_.profileKey());
00094 Layout layout = layoutProfile(profile);
00095 QRectF data_rect = dataRect(layout);
00096 view_animator_->setEndValue(data_rect);
00097 current_layout_ = layout;
00098
00099 update();
00100 }
00101
00102 void PartitionWidget::paintEvent(QPaintEvent *)
00103 {
00104 QPainter painter(this);
00105
00106 if (current_layout_.empty()) {
00107 QRect win_rect(0,0,width(), height());
00108 painter.setBrush(Qt::white);
00109 painter.drawRect(win_rect.adjusted(1,1,-1,-1));
00110 return;
00111 }
00112
00113 QRectF data_rect = view_animator_->currentValue().toRectF();
00114 QRectF win_rect(QPointF(0, 0), QPointF(width()-1, height()-1));
00115 win_rect = win_rect.adjusted(1,1,-1,-1);
00116 win_from_data_ = getTransform(win_rect, data_rect);
00117
00118 const Profile &profile = db_->profile(active_key_.profileKey());
00119 renderLayout(painter, win_from_data_, current_layout_, profile);
00120 }
00121
00122 QRectF PartitionWidget::dataRect(const Layout &layout) const
00123 {
00124 if (layout.empty()) {
00125 return QRectF(0.0, 0.0, 1.0, 1.0);
00126 }
00127
00128 double right = layout.back().rect.right();
00129
00130 for (auto const &item : layout) {
00131 if (active_key_.nodeKey() == item.node_key && item.exclusive == false) {
00132 QRectF rect = item.rect;
00133
00134 rect.setLeft(std::max(0.0, rect.left()-0.2));
00135 rect.setRight(right);
00136
00137 double margin = 0.05 * rect.height();
00138 rect.setTop(std::max(0.0, rect.top() - margin));
00139 rect.setBottom(std::min(1.0, rect.bottom() + margin));
00140 return rect;
00141 }
00142 }
00143
00144 qWarning("Active node key was not found in layout");
00145 return QRectF(QPointF(0.0, 0.0), QPointF(right, 1.0));
00146 }
00147
00148 void PartitionWidget::setActiveNode(int profile_key, int node_key)
00149 {
00150 const DatabaseKey new_key(profile_key, node_key);
00151
00152 if (new_key == active_key_) {
00153 return;
00154 }
00155
00156 bool first = true;
00157 if (active_key_.isValid()) {
00158 first = false;
00159 }
00160
00161 active_key_ = new_key;
00162
00163 const Profile &profile = db_->profile(active_key_.profileKey());
00164 Layout layout = layoutProfile(profile);
00165 QRectF data_rect = dataRect(layout);
00166 current_layout_ = layout;
00167
00168 if (!first) {
00169 view_animator_->stop();
00170 view_animator_->setStartValue(view_animator_->endValue());
00171 view_animator_->setEndValue(data_rect);
00172 view_animator_->setDuration(500);
00173 view_animator_->start();
00174 } else {
00175 view_animator_->setStartValue(data_rect);
00176 view_animator_->setEndValue(data_rect);
00177 }
00178
00179 emit activeNodeChanged(profile_key, node_key);
00180 }
00181
00182 PartitionWidget::Layout PartitionWidget::layoutProfile(const Profile &profile)
00183 {
00184 Layout layout;
00185
00186 const ProfileNode &root_node = profile.rootNode();
00187 if (!root_node.isValid()) {
00188 qWarning("Profile returned invalid root node.");
00189 return layout;
00190 }
00191
00192 if (root_node.data().empty()) {
00193 return layout;
00194 }
00195
00196 double time_scale = root_node.data().back().cumulative_inclusive_duration_ns;
00197
00198 int column = 0;
00199 LayoutItem root_item;
00200 root_item.node_key = root_node.nodeKey();
00201 root_item.exclusive = false;
00202 root_item.rect = QRectF(column, 0.0, 1, 1.0);
00203 layout.push_back(root_item);
00204
00205 bool keep_going = root_node.hasChildren();
00206
00207 std::vector<LayoutItem> parents;
00208 std::vector<LayoutItem> children;
00209 parents.push_back(root_item);
00210
00211 while (keep_going) {
00212
00213 keep_going = false;
00214 column++;
00215
00216 double span_start = 0.0;
00217 for (auto const &parent_item : parents) {
00218 const ProfileNode &parent_node = profile.node(parent_item.node_key);
00219
00220
00221 {
00222 double height = parent_node.data().back().cumulative_exclusive_duration_ns/time_scale;
00223 LayoutItem item;
00224 item.node_key = parent_item.node_key;
00225 item.exclusive = true;
00226 item.rect = QRectF(column, span_start, 1, height);
00227 children.push_back(item);
00228 span_start = item.rect.bottom();
00229 }
00230
00231
00232 if (parent_item.exclusive) {
00233 continue;
00234 }
00235
00236 for (int child_key : parent_node.childKeys()) {
00237 const ProfileNode &child_node = profile.node(child_key);
00238 double height = child_node.data().back().cumulative_inclusive_duration_ns / time_scale;
00239
00240 LayoutItem item;
00241 item.node_key = child_key;
00242 item.exclusive = false;
00243 item.rect = QRectF(column, span_start, 1, height);
00244 children.push_back(item);
00245 span_start = item.rect.bottom();
00246
00247 keep_going |= child_node.hasChildren();
00248 }
00249 }
00250
00251 layout.insert(layout.end(), children.begin(), children.end());
00252 parents.swap(children);
00253 children.clear();
00254 }
00255
00256 return layout;
00257 }
00258
00259 void PartitionWidget::renderLayout(QPainter &painter,
00260 const QTransform &win_from_data,
00261 const Layout &layout,
00262 const Profile &profile)
00263 {
00264
00265 painter.setPen(Qt::black);
00266
00267 double right = layout.back().rect.right();
00268
00269 for (auto const &item : layout) {
00270 if (item.exclusive) {
00271 continue;
00272 }
00273
00274 const ProfileNode &node = profile.node(item.node_key);
00275 QColor color = colorFromString(node.name());
00276
00277 QRectF data_rect = item.rect;
00278 data_rect.setRight(right);
00279 QRectF win_rect = win_from_data.mapRect(data_rect);
00280 QRect int_rect = roundRectF(win_rect);
00281
00282 painter.setBrush(color);
00283 painter.drawRect(int_rect.adjusted(0,0,-1,-1));
00284 }
00285 }
00286
00287 QTransform PartitionWidget::getTransform(const QRectF &win_rect,
00288 const QRectF &data_rect)
00289 {
00290 double sx = win_rect.width() / data_rect.width();
00291 double sy = win_rect.height() / data_rect.height();
00292 double tx = win_rect.topLeft().x() - sx*data_rect.topLeft().x();
00293 double ty = win_rect.topLeft().y() - sy*data_rect.topLeft().y();
00294
00295 QTransform win_from_data(sx, 0.0, 0.0,
00296 0.0, sy, 0.0,
00297 tx, ty, 1.0);
00298 return win_from_data;
00299 }
00300
00301 int PartitionWidget::itemAtPoint(const QPointF &point) const
00302 {
00303 for (size_t i = 0; i < current_layout_.size(); i++) {
00304 auto const &item = current_layout_[i];
00305 if (item.rect.contains(point)) {
00306 return i;
00307 }
00308 }
00309 return -1;
00310 }
00311
00312 bool PartitionWidget::event(QEvent *event)
00313 {
00314 if (event->type() == QEvent::ToolTip) {
00315 toolTipEvent(static_cast<QHelpEvent*>(event));
00316 event->accept();
00317 return true;
00318 }
00319 return QWidget::event(event);
00320 }
00321
00322 void PartitionWidget::toolTipEvent(QHelpEvent *event)
00323 {
00324 QTransform data_from_win = win_from_data_.inverted();
00325 int index = itemAtPoint(data_from_win.map(QPointF(event->pos())));
00326
00327 if (index < 0) {
00328 QToolTip::hideText();
00329 return;
00330 }
00331
00332 const Profile &profile = db_->profile(active_key_.profileKey());
00333 const LayoutItem &item = current_layout_[index];
00334
00335 QString tool_tip;
00336 if (item.node_key == profile.rootKey()) {
00337 tool_tip = profile.name();
00338 } else {
00339 tool_tip = profile.node(item.node_key).path();
00340 if (item.exclusive) {
00341 tool_tip += " [exclusive]";
00342 }
00343 }
00344
00345 QRectF win_rect = win_from_data_.mapRect(item.rect);
00346 QRect int_rect = roundRectF(win_rect);
00347 QToolTip::showText(event->globalPos(), tool_tip, this, int_rect);
00348 }
00349
00350 void PartitionWidget::mousePressEvent(QMouseEvent *event)
00351 {
00352 }
00353
00354 void PartitionWidget::mouseDoubleClickEvent(QMouseEvent *event)
00355 {
00356 QTransform data_from_win = win_from_data_.inverted();
00357 #if QT_VERSION >= 0x050000
00358 int index = itemAtPoint(data_from_win.map(event->localPos()));
00359 #else
00360 int index = itemAtPoint(data_from_win.map(event->posF()));
00361 #endif
00362
00363 if (index < 0) {
00364 return;
00365 }
00366
00367 const LayoutItem &item = current_layout_[index];
00368 setActiveNode(active_key_.profileKey(), item.node_key);
00369 }
00370 }