forked from qt-creator/qt-creator
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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) || ""; }
|
||||
|
||||
Reference in New Issue
Block a user