Add generic FlameGraphView QML component

This allows us to reduce code duplication, but we first have to put
timeline and flame graph into the same library, so that we can use the
TimelineThere in FlameGraphView.

Change-Id: I72b27ffb1fc5aa6baf6a23d85e5ca6c610896b8c
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Ulf Hermann
2018-05-02 18:51:57 +02:00
parent bd91d938c4
commit 22e523e504
7 changed files with 404 additions and 314 deletions

View File

@@ -94,6 +94,11 @@ public:
setRoot(QModelIndex()); setRoot(QModelIndex());
} }
Q_INVOKABLE QVariant total(int role) const
{
return m_model ? m_model->data(m_root, role) : QVariant();
}
static FlameGraphAttached *qmlAttachedProperties(QObject *object); static FlameGraphAttached *qmlAttachedProperties(QObject *object);
signals: signals:

View File

@@ -0,0 +1,344 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import FlameGraph 1.0
import TimelineTheme 1.0
import QtQml 2.2
import QtQuick 2.9
import QtQuick.Controls 1.3
ScrollView {
id: root
property int selectedTypeId: -1
property int sizeRole: -1
property var model: null
property int typeIdRole: -1
property int sourceFileRole: -1
property int sourceLineRole: -1
property int sourceColumnRole: -1
property int detailsTitleRole: -1
property int summaryRole: -1
property int noteRole: -1
property var trRoleNames: []
property var modes: []
property var details: function(flameGraph) { return []; }
property var summary: function(attached) {
if (!attached.dataValid)
return qsTr("others");
return attached.data(summaryRole) + " (" + percent(sizeRole, attached) + "%)";
}
property var isHighlighted: function(node) {
return false;
}
function percent(role, attached) {
return Math.round(attached.data(role) / flamegraph.total(role) * 1000) / 10;
}
function toMap(previousValue, currentValue, currentIndex, array) {
if (currentIndex % 2 === 1)
previousValue[array[currentIndex - 1]] = array[currentIndex];
return previousValue;
}
function addDetail(role, format, model, attached) {
model.push(trRoleNames[role]);
model.push(format(role, attached)
+ (modes.indexOf(role) >= 0 ? " (" + percent(role, attached) + "%)" : ""));
}
property var detailFormats: {
"noop": function(role, flameGraph) {
return flameGraph.data(role);
},
"addLine": function(role, flameGraph) {
var result = flameGraph.data(role);
var line = flameGraph.data(root.sourceLineRole);
return line > 0 ? result + ":" + line : result;
},
"printTime": function(role, flameGraph) {
var time = flameGraph.data(role);
if (time <= 0)
return "0";
if (time < 1000)
return time + " ns";
time = Math.floor(time / 1000);
if (time < 1000)
return time + " μs";
if (time < 1e6)
return (time / 1000) + " ms";
return (time / 1e6) + " s";
},
"printMemory": function(role, flameGraph) {
var bytes = flameGraph.data(role);
if (bytes === 0)
return "0b";
var units = ["b", "kb", "Mb", "Gb"];
var div = 1;
for (var i = 0; i < units.length; ++i, div *= 1024) {
if (bytes > div * 1024)
continue;
bytes /= div;
var digitsAfterDot = Math.round(3 - Math.log(bytes) / Math.LN10);
var multiplier = Math.pow(10, digitsAfterDot);
return Math.round(bytes * multiplier) / multiplier + units[i];
}
}
}
onSelectedTypeIdChanged: tooltip.hoveredNode = null
MouseArea {
anchors.fill: parent
onClicked: {
tooltip.selectedNode = null;
if (model !== null)
model.typeSelected(-1);
}
onDoubleClicked: {
tooltip.selectedNode = null;
if (model !== null)
model.typeSelected(-1);
flamegraph.resetRoot();
}
}
Flickable {
id: flickable
contentHeight: flamegraph.height
boundsBehavior: Flickable.StopAtBounds
FlameGraph {
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)
property color grey1: "#B0B0B0"
property color highlight: Theme.color(Theme.Timeline_HighlightColor)
id: flamegraph
width: parent.width
height: Math.max(depth * delegateHeight, flickable.height)
model: root.model
sizeRole: root.sizeRole
sizeThreshold: 0.002
maximumDepth: 128
y: flickable.height > height ? flickable.height - height : 0
delegate: FlameGraphDelegate {
id: flamegraphItem
property var typeId: FlameGraph.data(root.typeIdRole) || -1
property bool isHighlighted: root.isHighlighted(flamegraphItem)
itemHeight: flamegraph.delegateHeight
isSelected: typeId !== -1 && typeId === root.selectedTypeId
borderColor: {
if (isSelected)
return flamegraph.blue2;
else if (tooltip.hoveredNode === flamegraphItem)
return flamegraph.blue1;
else if (note() !== "" || isHighlighted)
return flamegraph.highlight;
else
return flamegraph.grey1;
}
borderWidth: {
if (tooltip.hoveredNode === flamegraphItem ||
tooltip.selectedNode === flamegraphItem) {
return 2;
} else if (note() !== "" || isHighlighted) {
return 3;
} else {
return 1;
}
}
onIsSelectedChanged: {
if (isSelected && (tooltip.selectedNode === null ||
tooltip.selectedNode.typeId !== root.selectedTypeId)) {
tooltip.selectedNode = flamegraphItem;
} else if (!isSelected && tooltip.selectedNode === flamegraphItem) {
tooltip.selectedNode = null;
}
}
text: textVisible ? root.summary(FlameGraph) : ""
FlameGraph.onModelIndexChanged: {
if (textVisible)
text = root.summary(FlameGraph);
// refresh to trigger reevaluation
if (tooltip.selectedNode == flamegraphItem) {
var selectedNode = tooltip.selectedNode;
tooltip.selectedNode = null;
tooltip.selectedNode = selectedNode;
}
if (tooltip.hoveredNode == flamegraphItem) {
var hoveredNode = tooltip.hoveredNode;
tooltip.hoveredNode = null;
tooltip.hoveredNode = hoveredNode;
}
}
onMouseEntered: {
tooltip.hoveredNode = flamegraphItem;
}
onMouseExited: {
if (tooltip.hoveredNode === flamegraphItem) {
// Keep the window around until something else is hovered or selected.
if (tooltip.selectedNode === null
|| tooltip.selectedNode.typeId !== root.selectedTypeId) {
tooltip.selectedNode = flamegraphItem;
}
tooltip.hoveredNode = null;
}
}
function selectClicked() {
if (FlameGraph.dataValid) {
tooltip.selectedNode = flamegraphItem;
selectedTypeId = FlameGraph.data(root.typeIdRole);
model.typeSelected(selectedTypeId);
model.gotoSourceLocation(
FlameGraph.data(root.sourceFileRole),
FlameGraph.data(root.sourceLineRole),
FlameGraph.data(root.sourceColumnRole));
}
}
onClicked: selectClicked()
onDoubleClicked: {
flamegraph.root = FlameGraph.modelIndex;
selectClicked();
}
// 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(root.detailsTitleRole) || qsTr("unknown");
}
function note() {
return FlameGraph.data(root.noteRole) || "";
}
function details() {
return root.details(FlameGraph);
}
}
}
FlameGraphDetails {
id: tooltip
minimumX: 0
maximumX: flickable.width
minimumY: flickable.contentY
maximumY: flickable.contentY + flickable.height
titleBarColor: Theme.color(Theme.Timeline_PanelHeaderColor)
titleBarTextColor: Theme.color(Theme.PanelTextColorLight)
contentColor: Theme.color(Theme.Timeline_PanelBackgroundColor)
contentTextColor: Theme.color(Theme.Timeline_TextColor)
noteTextColor: Theme.color(Theme.Timeline_HighlightColor)
buttonHoveredColor: Theme.color(Theme.FancyToolButtonHoverColor)
buttonSelectedColor: Theme.color(Theme.FancyToolButtonSelectedColor)
borderWidth: 0
property var hoveredNode: null;
property var selectedNode: null;
property var currentNode: {
if (hoveredNode !== null)
return hoveredNode;
else if (selectedNode !== null)
return selectedNode;
else
return null;
}
onClearSelection: {
selectedTypeId = -1;
selectedNode = null;
root.model.typeSelected(-1);
}
dialogTitle: {
if (currentNode)
return currentNode.title();
else if (root.model === null || root.model.rowCount() === 0)
return qsTr("No data available");
else
return "";
}
model: currentNode ? currentNode.details() : []
note: currentNode ? currentNode.note() : ""
Connections {
target: root.model
onModelReset: {
tooltip.hoveredNode = null;
tooltip.selectedNode = null;
}
}
}
Button {
x: flickable.width - width
y: flickable.contentY
// It won't listen to anchors.margins and by default it doesn't add any margin. Great.
width: implicitWidth + 20
text: qsTr("Visualize %1").arg(trRoleNames[root.sizeRole])
menu: Menu {
id: modesMenu
Instantiator {
model: root.modes
MenuItem {
text: root.trRoleNames[modelData]
onTriggered: root.sizeRole = modelData
}
onObjectAdded: modesMenu.insertItem(index, object)
onObjectRemoved: modesMenu.removeItem(object)
}
}
}
}
}

View File

@@ -6,6 +6,7 @@
<file>FlameGraphDelegate.qml</file> <file>FlameGraphDelegate.qml</file>
<file>FlameGraphDetails.qml</file> <file>FlameGraphDetails.qml</file>
<file>FlameGraphText.qml</file> <file>FlameGraphText.qml</file>
<file>FlameGraphView.qml</file>
<file>ico_edit.png</file> <file>ico_edit.png</file>
<file>ico_edit@2x.png</file> <file>ico_edit@2x.png</file>
<file>ico_rangeselected.png</file> <file>ico_rangeselected.png</file>

View File

@@ -208,7 +208,6 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const
case DurationRole: return stats.duration; case DurationRole: return stats.duration;
case CallCountRole: return stats.calls; case CallCountRole: return stats.calls;
case TimePerCallRole: return stats.duration / stats.calls; case TimePerCallRole: return stats.duration / stats.calls;
case TimeInPercentRole: return stats.duration * 100 / m_stackBottom.duration;
case AllocationsRole: return stats.allocations; case AllocationsRole: return stats.allocations;
case MemoryRole: return stats.memory; case MemoryRole: return stats.memory;
default: break; default: break;
@@ -321,7 +320,6 @@ QHash<int, QByteArray> FlameGraphModel::roleNames() const
{ColumnRole, "column"}, {ColumnRole, "column"},
{NoteRole, "note"}, {NoteRole, "note"},
{TimePerCallRole, "timePerCall"}, {TimePerCallRole, "timePerCall"},
{TimeInPercentRole, "timeInPercent"},
{RangeTypeRole, "rangeType"}, {RangeTypeRole, "rangeType"},
{LocationRole, "location" }, {LocationRole, "location" },
{AllocationsRole, "allocations" }, {AllocationsRole, "allocations" },

View File

@@ -68,7 +68,6 @@ public:
ColumnRole, ColumnRole,
NoteRole, NoteRole,
TimePerCallRole, TimePerCallRole,
TimeInPercentRole,
RangeTypeRole, RangeTypeRole,
LocationRole, LocationRole,
AllocationsRole, AllocationsRole,

View File

@@ -23,20 +23,30 @@
** **
****************************************************************************/ ****************************************************************************/
import QtQuick 2.0
import QtQuick.Controls 1.3
import FlameGraph 1.0
import QmlProfilerFlameGraphModel 1.0 import QmlProfilerFlameGraphModel 1.0
import TimelineTheme 1.0
import "../tracing/" import "../tracing/"
ScrollView { FlameGraphView {
id: root id: root
property int selectedTypeId: -1 model: flameGraphModel
property int sizeRole: QmlProfilerFlameGraphModel.DurationRole sizeRole: QmlProfilerFlameGraphModel.DurationRole
readonly property var trRoleNames: [ typeIdRole: QmlProfilerFlameGraphModel.TypeIdRole
sourceFileRole: QmlProfilerFlameGraphModel.FilenameRole
sourceLineRole: QmlProfilerFlameGraphModel.LineRole
sourceColumnRole: QmlProfilerFlameGraphModel.ColumnRole
detailsTitleRole: QmlProfilerFlameGraphModel.TypeRole
summaryRole: QmlProfilerFlameGraphModel.DetailsRole
noteRole: QmlProfilerFlameGraphModel.NoteRole
modes: [
QmlProfilerFlameGraphModel.DurationRole,
QmlProfilerFlameGraphModel.MemoryRole,
QmlProfilerFlameGraphModel.AllocationsRole
]
trRoleNames: [
QmlProfilerFlameGraphModel.DurationRole, qsTr("Total Time"), QmlProfilerFlameGraphModel.DurationRole, qsTr("Total Time"),
QmlProfilerFlameGraphModel.CallCountRole, qsTr("Calls"), QmlProfilerFlameGraphModel.CallCountRole, qsTr("Calls"),
QmlProfilerFlameGraphModel.DetailsRole, qsTr("Details"), QmlProfilerFlameGraphModel.DetailsRole, qsTr("Details"),
@@ -45,311 +55,47 @@ ScrollView {
QmlProfilerFlameGraphModel.LocationRole, qsTr("Location"), QmlProfilerFlameGraphModel.LocationRole, qsTr("Location"),
QmlProfilerFlameGraphModel.AllocationsRole, qsTr("Allocations"), QmlProfilerFlameGraphModel.AllocationsRole, qsTr("Allocations"),
QmlProfilerFlameGraphModel.MemoryRole, qsTr("Memory") QmlProfilerFlameGraphModel.MemoryRole, qsTr("Memory")
].reduce(function(previousValue, currentValue, currentIndex, array) { ].reduce(toMap, {})
if (currentIndex % 2 === 1)
previousValue[array[currentIndex - 1]] = array[currentIndex];
return previousValue;
}, {})
onSelectedTypeIdChanged: tooltip.hoveredNode = null details: function(flameGraph) {
Flickable {
id: flickable
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.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)
property color grey1: "#B0B0B0"
property color grey2: "#A0A0A0"
property color highlight: Theme.color(Theme.Timeline_HighlightColor)
function checkBindingLoop(otherTypeId) {return false;}
id: flamegraph
width: parent.width
height: Math.max(depth * delegateHeight, flickable.height)
model: flameGraphModel
sizeRole: root.sizeRole
sizeThreshold: 0.002
maximumDepth: 25
y: flickable.height > height ? flickable.height - height : 0
delegate: FlameGraphDelegate {
id: flamegraphItem
property int typeId: FlameGraph.data(QmlProfilerFlameGraphModel.TypeIdRole) || -1
property bool isBindingLoop: parent.checkBindingLoop(typeId)
itemHeight: flamegraph.delegateHeight
isSelected: typeId !== -1 && typeId === root.selectedTypeId
borderColor: {
if (isSelected)
return flamegraph.blue2;
else if (tooltip.hoveredNode === flamegraphItem)
return flamegraph.blue1;
else if (note() !== "" || isBindingLoop)
return flamegraph.highlight;
else
return flamegraph.grey1;
}
borderWidth: {
if (tooltip.hoveredNode === flamegraphItem ||
tooltip.selectedNode === flamegraphItem) {
return 2;
} else if (note() !== "") {
return 3;
} else {
return 1;
}
}
onIsSelectedChanged: {
if (isSelected && (tooltip.selectedNode === null ||
tooltip.selectedNode.typeId !== root.selectedTypeId)) {
tooltip.selectedNode = flamegraphItem;
} else if (!isSelected && tooltip.selectedNode === flamegraphItem) {
tooltip.selectedNode = null;
}
}
function checkBindingLoop(otherTypeId) {
if (typeId === otherTypeId) {
isBindingLoop = true;
return true;
} else {
return parent.checkBindingLoop(otherTypeId);
}
}
function buildText() {
if (!FlameGraph.dataValid)
return "<others>";
return FlameGraph.data(QmlProfilerFlameGraphModel.DetailsRole) + " ("
+ FlameGraph.data(QmlProfilerFlameGraphModel.TypeRole) + ", "
+ Math.floor(width / flamegraph.width * 1000) / 10 + "%)";
}
text: textVisible ? buildText() : ""
FlameGraph.onModelIndexChanged: {
if (textVisible)
text = buildText();
// refresh to trigger reevaluation
if (tooltip.selectedNode == flamegraphItem) {
var selectedNode = tooltip.selectedNode;
tooltip.selectedNode = null;
tooltip.selectedNode = selectedNode;
}
if (tooltip.hoveredNode == flamegraphItem) {
var hoveredNode = tooltip.hoveredNode;
tooltip.hoveredNode = null;
tooltip.hoveredNode = hoveredNode;
}
}
onMouseEntered: {
tooltip.hoveredNode = flamegraphItem;
}
onMouseExited: {
if (tooltip.hoveredNode === flamegraphItem) {
// Keep the window around until something else is hovered or selected.
if (tooltip.selectedNode === null
|| tooltip.selectedNode.typeId !== root.selectedTypeId) {
tooltip.selectedNode = flamegraphItem;
}
tooltip.hoveredNode = null;
}
}
function selectClicked() {
if (flamegraphItem.FlameGraph.dataValid) {
tooltip.selectedNode = flamegraphItem;
flameGraphModel.typeSelected(flamegraphItem.FlameGraph.data(
QmlProfilerFlameGraphModel.TypeIdRole));
flameGraphModel.gotoSourceLocation(
flamegraphItem.FlameGraph.data(
QmlProfilerFlameGraphModel.FilenameRole),
flamegraphItem.FlameGraph.data(
QmlProfilerFlameGraphModel.LineRole),
flamegraphItem.FlameGraph.data(
QmlProfilerFlameGraphModel.ColumnRole));
}
}
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) || ""; }
function note() { return FlameGraph.data(QmlProfilerFlameGraphModel.NoteRole) || ""; }
function details() {
var model = []; var model = [];
function addDetail(index, format) { if (!flameGraph.dataValid) {
model.push(trRoleNames[index]); model.push(trRoleNames[QmlProfilerFlameGraphModel.DetailsRole]);
model.push(format(FlameGraph.data(index)));
}
function printTime(t)
{
if (t <= 0)
return "0";
if (t < 1000)
return t + " ns";
t = Math.floor(t / 1000);
if (t < 1000)
return t + " μs";
if (t < 1e6)
return (t / 1000) + " ms";
return (t / 1e6) + " s";
}
function noop(a) {
return a;
}
function addPercent(a) {
return a + "%";
}
function printMemory(a) {
if (a === 0)
return "0b";
var units = ["b", "kb", "Mb", "Gb"];
var div = 1;
for (var i = 0; i < units.length; ++i, div *= 1024) {
if (a > div * 1024)
continue;
a /= div;
var digitsAfterDot = Math.round(3 - Math.log(a) / Math.LN10);
var multiplier = Math.pow(10, digitsAfterDot);
return Math.round(a * multiplier) / multiplier + units[i];
}
}
if (!FlameGraph.dataValid) {
model.push(qsTr("Details"));
model.push(qsTr("Various Events")); model.push(qsTr("Various Events"));
} else { } else {
addDetail(QmlProfilerFlameGraphModel.DetailsRole, noop); function addDetail(role, format) { root.addDetail(role, format, model, flameGraph); }
addDetail(QmlProfilerFlameGraphModel.CallCountRole, noop);
addDetail(QmlProfilerFlameGraphModel.DurationRole, printTime); addDetail(QmlProfilerFlameGraphModel.DetailsRole, detailFormats.noop);
addDetail(QmlProfilerFlameGraphModel.TimePerCallRole, printTime); addDetail(QmlProfilerFlameGraphModel.CallCountRole, detailFormats.noop);
addDetail(QmlProfilerFlameGraphModel.TimeInPercentRole, addPercent); addDetail(QmlProfilerFlameGraphModel.DurationRole, detailFormats.printTime);
addDetail(QmlProfilerFlameGraphModel.LocationRole, noop); addDetail(QmlProfilerFlameGraphModel.TimePerCallRole, detailFormats.printTime);
addDetail(QmlProfilerFlameGraphModel.MemoryRole, printMemory); addDetail(QmlProfilerFlameGraphModel.LocationRole, detailFormats.noop);
addDetail(QmlProfilerFlameGraphModel.AllocationsRole, noop); addDetail(QmlProfilerFlameGraphModel.MemoryRole, detailFormats.printMemory);
addDetail(QmlProfilerFlameGraphModel.AllocationsRole, detailFormats.noop);
} }
return model; return model;
} }
}
summary: function(attached) {
if (!attached.dataValid)
return qsTr("others");
return attached.data(QmlProfilerFlameGraphModel.DetailsRole) + " ("
+ attached.data(QmlProfilerFlameGraphModel.TypeRole) + ", "
+ root.percent(root.sizeRole, attached) + "%)";
} }
FlameGraphDetails { isHighlighted: function(node) {
id: tooltip function recurse(parentNode, typeId) {
if (!parentNode)
minimumX: 0 return false;
maximumX: flickable.width if (parentNode.typeId === typeId) {
minimumY: flickable.contentY parentNode.isHighlighted = true;
maximumY: flickable.contentY + flickable.height return true;
}
titleBarColor: Theme.color(Theme.Timeline_PanelHeaderColor) return recurse(parentNode.parent, typeId);
titleBarTextColor: Theme.color(Theme.PanelTextColorLight)
contentColor: Theme.color(Theme.Timeline_PanelBackgroundColor)
contentTextColor: Theme.color(Theme.Timeline_TextColor)
noteTextColor: Theme.color(Theme.Timeline_HighlightColor)
buttonHoveredColor: Theme.color(Theme.FancyToolButtonHoverColor)
buttonSelectedColor: Theme.color(Theme.FancyToolButtonSelectedColor)
borderWidth: 0
property var hoveredNode: null;
property var selectedNode: null;
property var currentNode: {
if (hoveredNode !== null)
return hoveredNode;
else if (selectedNode !== null)
return selectedNode;
else
return null;
} }
onClearSelection: { return recurse(node.parent, node.typeId);
selectedTypeId = -1;
selectedNode = null;
flameGraphModel.typeSelected(-1);
}
dialogTitle: {
if (currentNode)
return currentNode.title();
else if (flameGraphModel.rowCount() === 0)
return qsTr("No data available");
else
return "";
}
model: currentNode ? currentNode.details() : []
note: currentNode ? currentNode.note() : ""
Connections {
target: flameGraphModel
onModelReset: {
tooltip.hoveredNode = null;
tooltip.selectedNode = null;
}
}
}
Button {
x: flickable.width - width
y: flickable.contentY
// It won't listen to anchors.margins and by default it doesn't add any margin. Great.
width: implicitWidth + 20
text: qsTr("Visualize %1").arg(trRoleNames[root.sizeRole])
menu: Menu {
MenuItem {
text: trRoleNames[QmlProfilerFlameGraphModel.DurationRole]
onTriggered: root.sizeRole = QmlProfilerFlameGraphModel.DurationRole
}
MenuItem {
text: trRoleNames[QmlProfilerFlameGraphModel.MemoryRole]
onTriggered: root.sizeRole = QmlProfilerFlameGraphModel.MemoryRole
}
MenuItem {
text: trRoleNames[QmlProfilerFlameGraphModel.AllocationsRole]
onTriggered: root.sizeRole = QmlProfilerFlameGraphModel.AllocationsRole
}
}
}
} }
} }

View File

@@ -158,8 +158,6 @@ void FlameGraphModelTest::testData()
QCOMPARE(model.data(index2, FlameGraphModel::NoteRole).toString(), QString()); QCOMPARE(model.data(index2, FlameGraphModel::NoteRole).toString(), QString());
QCOMPARE(model.data(index, FlameGraphModel::TimePerCallRole).toLongLong(), 20); QCOMPARE(model.data(index, FlameGraphModel::TimePerCallRole).toLongLong(), 20);
QCOMPARE(model.data(index2, FlameGraphModel::TimePerCallRole).toLongLong(), 12); QCOMPARE(model.data(index2, FlameGraphModel::TimePerCallRole).toLongLong(), 12);
QCOMPARE(model.data(index, FlameGraphModel::TimeInPercentRole).toInt(), 62);
QCOMPARE(model.data(index2, FlameGraphModel::TimeInPercentRole).toInt(), 37);
QCOMPARE(model.data(index, FlameGraphModel::RangeTypeRole).toInt(), QCOMPARE(model.data(index, FlameGraphModel::RangeTypeRole).toInt(),
static_cast<int>(Javascript)); static_cast<int>(Javascript));
QCOMPARE(model.data(index2, FlameGraphModel::RangeTypeRole).toInt(), QCOMPARE(model.data(index2, FlameGraphModel::RangeTypeRole).toInt(),
@@ -201,7 +199,6 @@ void FlameGraphModelTest::testRoleNames()
QCOMPARE(names[FlameGraphModel::ColumnRole], QByteArray("column")); QCOMPARE(names[FlameGraphModel::ColumnRole], QByteArray("column"));
QCOMPARE(names[FlameGraphModel::NoteRole], QByteArray("note")); QCOMPARE(names[FlameGraphModel::NoteRole], QByteArray("note"));
QCOMPARE(names[FlameGraphModel::TimePerCallRole], QByteArray("timePerCall")); QCOMPARE(names[FlameGraphModel::TimePerCallRole], QByteArray("timePerCall"));
QCOMPARE(names[FlameGraphModel::TimeInPercentRole], QByteArray("timeInPercent"));
QCOMPARE(names[FlameGraphModel::RangeTypeRole], QByteArray("rangeType")); QCOMPARE(names[FlameGraphModel::RangeTypeRole], QByteArray("rangeType"));
} }