FlameGraph: Allow zooming into items

Double clicking an item will now rebuild the flame graph with that item
as root. Double clicking on an empty area will reset the root.

Change-Id: I16dd4b00d0dd09ff922a01acee67f0d553da6323
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Milian Wolff <milian.wolff@kdab.com>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Ulf Hermann
2018-01-23 18:03:14 +01:00
parent 438db9a488
commit 27c51ed4c8
5 changed files with 66 additions and 10 deletions

View File

@@ -30,6 +30,9 @@ namespace FlameGraph {
FlameGraph::FlameGraph(QQuickItem *parent) :
QQuickItem(parent)
{
// Queue the rebuild in this case so that a delegate can set the root without getting deleted
// during the call.
connect(this, &FlameGraph::rootChanged, this, &FlameGraph::rebuild, Qt::QueuedConnection);
}
QQmlComponent *FlameGraph::delegate() const
@@ -142,7 +145,7 @@ int FlameGraph::buildNode(const QModelIndex &parentIndex, QObject *parentObject,
for (int row = 0; row < rowCount; ++row) {
QModelIndex childIndex = m_model->index(row, 0, parentIndex);
qreal size = m_model->data(childIndex, m_sizeRole).toReal();
if (size / m_model->data(QModelIndex(), m_sizeRole).toReal() < m_sizeThreshold) {
if (size / m_model->data(m_root, m_sizeRole).toReal() < m_sizeThreshold) {
skipped += size;
continue;
}
@@ -155,7 +158,7 @@ int FlameGraph::buildNode(const QModelIndex &parentIndex, QObject *parentObject,
}
}
// Root object: attribute all remaining width to "others"
// Invisible Root object: attribute all remaining width to "others"
if (!parentIndex.isValid())
skipped = parentSize - position;
@@ -179,7 +182,13 @@ void FlameGraph::rebuild()
return;
}
m_depth = buildNode(QModelIndex(), this, 0, m_maximumDepth);
if (m_root.isValid()) {
QObject *parentObject = appendChild(this, this, qmlContext(this), m_root, 0, 1);
m_depth = buildNode(m_root, parentObject, 1, m_maximumDepth);
} else {
m_depth = buildNode(m_root, this, 0, m_maximumDepth);
}
emit depthChanged(m_depth);
}

View File

@@ -44,6 +44,7 @@ class FLAMEGRAPH_EXPORT FlameGraph : public QQuickItem
Q_PROPERTY(int maximumDepth READ maximumDepth WRITE setMaximumDepth
NOTIFY maximumDepthChanged)
Q_PROPERTY(int depth READ depth NOTIFY depthChanged)
Q_PROPERTY(QPersistentModelIndex root READ root WRITE setRoot NOTIFY rootChanged)
public:
FlameGraph(QQuickItem *parent = 0);
@@ -75,6 +76,24 @@ public:
}
}
QPersistentModelIndex root() const
{
return m_root;
}
void setRoot(const QPersistentModelIndex &root)
{
if (root != m_root) {
m_root = root;
emit rootChanged(root);
}
}
Q_INVOKABLE void resetRoot()
{
setRoot(QModelIndex());
}
static FlameGraphAttached *qmlAttachedProperties(QObject *object);
signals:
@@ -84,12 +103,14 @@ signals:
void sizeThresholdChanged(qreal threshold);
void depthChanged(int depth);
void maximumDepthChanged();
void rootChanged(const QPersistentModelIndex &root);
private:
void rebuild();
QQmlComponent *m_delegate = nullptr;
QAbstractItemModel *m_model = nullptr;
QPersistentModelIndex m_root;
int m_sizeRole = 0;
int m_depth = 0;
qreal m_sizeThreshold = 0;

View File

@@ -40,11 +40,17 @@ class FLAMEGRAPH_EXPORT FlameGraphAttached : public QObject
Q_PROPERTY(qreal relativePosition READ relativePosition WRITE setRelativePosition
NOTIFY relativePositionChanged)
Q_PROPERTY(bool dataValid READ isDataValid NOTIFY dataValidChanged)
Q_PROPERTY(QModelIndex modelIndex READ modelIndex WRITE setModelIndex NOTIFY modelIndexChanged)
public:
FlameGraphAttached(QObject *parent = 0) :
QObject(parent), m_relativeSize(0), m_relativePosition(0) {}
QModelIndex modelIndex() const
{
return m_data;
}
Q_INVOKABLE QVariant data(int role) const
{
return m_data.isValid() ? m_data.data(role) : QVariant();
@@ -88,13 +94,13 @@ public:
m_data = data;
if (validChanged)
emit dataValidChanged();
emit dataChanged();
emit modelIndexChanged();
}
}
signals:
void dataChanged();
void dataValidChanged();
void modelIndexChanged();
void relativeSizeChanged();
void relativePositionChanged();

View File

@@ -37,6 +37,7 @@ Item {
signal mouseEntered
signal mouseExited
signal clicked
signal doubleClicked
property bool textVisible: width > 20 || isSelected
property int level: parent.level !== undefined ? parent.level + 1 : 0
@@ -76,7 +77,7 @@ Item {
onEntered: flamegraphItem.mouseEntered()
onExited: flamegraphItem.mouseExited()
onClicked: flamegraphItem.clicked()
onDoubleClicked: flamegraphItem.doubleClicked()
}
}
}

View File

@@ -58,8 +58,21 @@ ScrollView {
contentHeight: flamegraph.height
boundsBehavior: Flickable.StopAtBounds
MouseArea {
anchors.fill: parent
onClicked: {
tooltip.selectedNode = null;
flameGraphModel.typeSelected(-1);
}
onDoubleClicked: {
tooltip.selectedNode = null;
flameGraphModel.typeSelected(-1);
flamegraph.resetRoot();
}
}
FlameGraph {
property int delegateHeight: Math.max(30, flickable.height / depth)
property int delegateHeight: Math.min(60, Math.max(30, flickable.height / depth))
property color blue: "blue"
property color blue1: Qt.lighter(blue)
property color blue2: Qt.rgba(0.375, 0, 1, 1)
@@ -71,7 +84,7 @@ ScrollView {
id: flamegraph
width: parent.width
height: depth * delegateHeight
height: Math.max(depth * delegateHeight, flickable.height)
model: flameGraphModel
sizeRole: root.sizeRole
sizeThreshold: 0.002
@@ -135,7 +148,7 @@ ScrollView {
+ Math.floor(width / flamegraph.width * 1000) / 10 + "%)";
}
text: textVisible ? buildText() : ""
FlameGraph.onDataChanged: if (textVisible) text = buildText();
FlameGraph.onModelIndexChanged: if (textVisible) text = buildText();
onMouseEntered: {
tooltip.hoveredNode = flamegraphItem;
@@ -152,7 +165,7 @@ ScrollView {
}
}
onClicked: {
function selectClicked() {
if (flamegraphItem.FlameGraph.dataValid) {
tooltip.selectedNode = flamegraphItem;
flameGraphModel.typeSelected(flamegraphItem.FlameGraph.data(
@@ -167,6 +180,12 @@ ScrollView {
}
}
onClicked: selectClicked()
onDoubleClicked: {
selectClicked();
flamegraph.root = FlameGraph.modelIndex;
}
// Functions, not properties to limit the initial overhead when creating the nodes,
// and because FlameGraph.data(...) cannot be notified anyway.
function title() { return FlameGraph.data(QmlProfilerFlameGraphModel.TypeRole) || ""; }