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) :
|
FlameGraph::FlameGraph(QQuickItem *parent) :
|
||||||
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
|
QQmlComponent *FlameGraph::delegate() const
|
||||||
@@ -142,7 +145,7 @@ int FlameGraph::buildNode(const QModelIndex &parentIndex, QObject *parentObject,
|
|||||||
for (int row = 0; row < rowCount; ++row) {
|
for (int row = 0; row < rowCount; ++row) {
|
||||||
QModelIndex childIndex = m_model->index(row, 0, parentIndex);
|
QModelIndex childIndex = m_model->index(row, 0, parentIndex);
|
||||||
qreal size = m_model->data(childIndex, m_sizeRole).toReal();
|
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;
|
skipped += size;
|
||||||
continue;
|
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())
|
if (!parentIndex.isValid())
|
||||||
skipped = parentSize - position;
|
skipped = parentSize - position;
|
||||||
|
|
||||||
@@ -179,7 +182,13 @@ void FlameGraph::rebuild()
|
|||||||
return;
|
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);
|
emit depthChanged(m_depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class FLAMEGRAPH_EXPORT FlameGraph : public QQuickItem
|
|||||||
Q_PROPERTY(int maximumDepth READ maximumDepth WRITE setMaximumDepth
|
Q_PROPERTY(int maximumDepth READ maximumDepth WRITE setMaximumDepth
|
||||||
NOTIFY maximumDepthChanged)
|
NOTIFY maximumDepthChanged)
|
||||||
Q_PROPERTY(int depth READ depth NOTIFY depthChanged)
|
Q_PROPERTY(int depth READ depth NOTIFY depthChanged)
|
||||||
|
Q_PROPERTY(QPersistentModelIndex root READ root WRITE setRoot NOTIFY rootChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FlameGraph(QQuickItem *parent = 0);
|
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);
|
static FlameGraphAttached *qmlAttachedProperties(QObject *object);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -84,12 +103,14 @@ signals:
|
|||||||
void sizeThresholdChanged(qreal threshold);
|
void sizeThresholdChanged(qreal threshold);
|
||||||
void depthChanged(int depth);
|
void depthChanged(int depth);
|
||||||
void maximumDepthChanged();
|
void maximumDepthChanged();
|
||||||
|
void rootChanged(const QPersistentModelIndex &root);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void rebuild();
|
void rebuild();
|
||||||
|
|
||||||
QQmlComponent *m_delegate = nullptr;
|
QQmlComponent *m_delegate = nullptr;
|
||||||
QAbstractItemModel *m_model = nullptr;
|
QAbstractItemModel *m_model = nullptr;
|
||||||
|
QPersistentModelIndex m_root;
|
||||||
int m_sizeRole = 0;
|
int m_sizeRole = 0;
|
||||||
int m_depth = 0;
|
int m_depth = 0;
|
||||||
qreal m_sizeThreshold = 0;
|
qreal m_sizeThreshold = 0;
|
||||||
|
|||||||
@@ -40,11 +40,17 @@ class FLAMEGRAPH_EXPORT FlameGraphAttached : public QObject
|
|||||||
Q_PROPERTY(qreal relativePosition READ relativePosition WRITE setRelativePosition
|
Q_PROPERTY(qreal relativePosition READ relativePosition WRITE setRelativePosition
|
||||||
NOTIFY relativePositionChanged)
|
NOTIFY relativePositionChanged)
|
||||||
Q_PROPERTY(bool dataValid READ isDataValid NOTIFY dataValidChanged)
|
Q_PROPERTY(bool dataValid READ isDataValid NOTIFY dataValidChanged)
|
||||||
|
Q_PROPERTY(QModelIndex modelIndex READ modelIndex WRITE setModelIndex NOTIFY modelIndexChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FlameGraphAttached(QObject *parent = 0) :
|
FlameGraphAttached(QObject *parent = 0) :
|
||||||
QObject(parent), m_relativeSize(0), m_relativePosition(0) {}
|
QObject(parent), m_relativeSize(0), m_relativePosition(0) {}
|
||||||
|
|
||||||
|
QModelIndex modelIndex() const
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
Q_INVOKABLE QVariant data(int role) const
|
Q_INVOKABLE QVariant data(int role) const
|
||||||
{
|
{
|
||||||
return m_data.isValid() ? m_data.data(role) : QVariant();
|
return m_data.isValid() ? m_data.data(role) : QVariant();
|
||||||
@@ -88,13 +94,13 @@ public:
|
|||||||
m_data = data;
|
m_data = data;
|
||||||
if (validChanged)
|
if (validChanged)
|
||||||
emit dataValidChanged();
|
emit dataValidChanged();
|
||||||
emit dataChanged();
|
emit modelIndexChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dataChanged();
|
|
||||||
void dataValidChanged();
|
void dataValidChanged();
|
||||||
|
void modelIndexChanged();
|
||||||
void relativeSizeChanged();
|
void relativeSizeChanged();
|
||||||
void relativePositionChanged();
|
void relativePositionChanged();
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ Item {
|
|||||||
signal mouseEntered
|
signal mouseEntered
|
||||||
signal mouseExited
|
signal mouseExited
|
||||||
signal clicked
|
signal clicked
|
||||||
|
signal doubleClicked
|
||||||
|
|
||||||
property bool textVisible: width > 20 || isSelected
|
property bool textVisible: width > 20 || isSelected
|
||||||
property int level: parent.level !== undefined ? parent.level + 1 : 0
|
property int level: parent.level !== undefined ? parent.level + 1 : 0
|
||||||
@@ -76,7 +77,7 @@ Item {
|
|||||||
onEntered: flamegraphItem.mouseEntered()
|
onEntered: flamegraphItem.mouseEntered()
|
||||||
onExited: flamegraphItem.mouseExited()
|
onExited: flamegraphItem.mouseExited()
|
||||||
onClicked: flamegraphItem.clicked()
|
onClicked: flamegraphItem.clicked()
|
||||||
|
onDoubleClicked: flamegraphItem.doubleClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,8 +58,21 @@ ScrollView {
|
|||||||
contentHeight: flamegraph.height
|
contentHeight: flamegraph.height
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
tooltip.selectedNode = null;
|
||||||
|
flameGraphModel.typeSelected(-1);
|
||||||
|
}
|
||||||
|
onDoubleClicked: {
|
||||||
|
tooltip.selectedNode = null;
|
||||||
|
flameGraphModel.typeSelected(-1);
|
||||||
|
flamegraph.resetRoot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FlameGraph {
|
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 blue: "blue"
|
||||||
property color blue1: Qt.lighter(blue)
|
property color blue1: Qt.lighter(blue)
|
||||||
property color blue2: Qt.rgba(0.375, 0, 1, 1)
|
property color blue2: Qt.rgba(0.375, 0, 1, 1)
|
||||||
@@ -71,7 +84,7 @@ ScrollView {
|
|||||||
|
|
||||||
id: flamegraph
|
id: flamegraph
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: depth * delegateHeight
|
height: Math.max(depth * delegateHeight, flickable.height)
|
||||||
model: flameGraphModel
|
model: flameGraphModel
|
||||||
sizeRole: root.sizeRole
|
sizeRole: root.sizeRole
|
||||||
sizeThreshold: 0.002
|
sizeThreshold: 0.002
|
||||||
@@ -135,7 +148,7 @@ ScrollView {
|
|||||||
+ Math.floor(width / flamegraph.width * 1000) / 10 + "%)";
|
+ Math.floor(width / flamegraph.width * 1000) / 10 + "%)";
|
||||||
}
|
}
|
||||||
text: textVisible ? buildText() : ""
|
text: textVisible ? buildText() : ""
|
||||||
FlameGraph.onDataChanged: if (textVisible) text = buildText();
|
FlameGraph.onModelIndexChanged: if (textVisible) text = buildText();
|
||||||
|
|
||||||
onMouseEntered: {
|
onMouseEntered: {
|
||||||
tooltip.hoveredNode = flamegraphItem;
|
tooltip.hoveredNode = flamegraphItem;
|
||||||
@@ -152,7 +165,7 @@ ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
function selectClicked() {
|
||||||
if (flamegraphItem.FlameGraph.dataValid) {
|
if (flamegraphItem.FlameGraph.dataValid) {
|
||||||
tooltip.selectedNode = flamegraphItem;
|
tooltip.selectedNode = flamegraphItem;
|
||||||
flameGraphModel.typeSelected(flamegraphItem.FlameGraph.data(
|
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,
|
// Functions, not properties to limit the initial overhead when creating the nodes,
|
||||||
// and because FlameGraph.data(...) cannot be notified anyway.
|
// and because FlameGraph.data(...) cannot be notified anyway.
|
||||||
function title() { return FlameGraph.data(QmlProfilerFlameGraphModel.TypeRole) || ""; }
|
function title() { return FlameGraph.data(QmlProfilerFlameGraphModel.TypeRole) || ""; }
|
||||||
|
|||||||
Reference in New Issue
Block a user