Reputation: 9555
I have a QML application whose look I want to pin down via automated comparisons between a wanted state and the current state.
First I wanted to go with QML's TestCase.grabImage
but the returned QImage
wrapper object does not allow saving its contents to a file which would be necessary to (re-)generate the comparison images.
Then I found out about QML's Item.grabToImage
which returns an object that can store to a file and give me the actual underlying QImage
object. After writing a wrapper function to obtain a blocking version I could generate images of simple QML Item
s like a Rectangle
and nested version thereof. However when using something more complicated like
Repeater {
id: rect
model: 10
width: 100
height: 10
delegate: Rectangle {
color: "red"
x: (width + 1)* index
width: 5
height: 10
}
}
I only get an empty image with the same dimensions as the repeater object.
This is the function that I wrote to grab the images (beware, this is WIP!):
function grabToImage(item, timeoutInMilliseconds) {
var result = null;
item.grabToImage(function(image) {
result = image;
});
waitForRendering(item, timeoutInMilliseconds);
console.log("After waitForRendering ", result);
// wait for result for exponentially increasing time until timeout is exceeded
var waitInterval = 100;
var remainingTimeout = timeoutInMilliseconds;
while(result === null && remainingTimeout > 0) {
var waitFor = Math.min(waitInterval, remainingTimeout);
wait(waitFor);
remainingTimeout -= waitFor;
waitInterval *= 2;
}
return result;
}
All Rectangle
s' Component.completed
signal is triggered before the grabToImage
code runs so they seem to be instantiated.
As requested by user2436719 I append the surrounding, calling code:
import QtQuick 2.3
import QtTest 1.0
TestCase {
name: "UI tests"
id: tests
when: windowShown
function grabToImage(item, timeoutInMilliseconds) {
var result = null;
item.grabToImage(function(image) {
result = image;
});
waitForRendering(item, timeoutInMilliseconds);
// wait for result with exponentially increasing wait amounts until timeout is exceeded
var waitInterval = 100;
var remainingTimeout = timeoutInMilliseconds;
while(result === null && remainingTimeout > 0) {
var waitFor = Math.min(waitInterval, remainingTimeout);
wait(waitFor);
remainingTimeout -= waitFor;
waitInterval *= 2;
}
return result;
}
Repeater {
id: repeater
model: 10
width: 100
height: 10
delegate: Rectangle {
color: "red"
x: (width + 1)* index
width: 5
height: 10
}
}
Rectangle {
id: rect
color: "blue"
width: 100
height: 10
Rectangle {
color: "red"
x: 1
width: 5
height: 10
}
Rectangle {
color: "red"
x: 7
width: 5
height: 10
}
}
function test_Problem() {
grabToImage(repeater, 5000).saveToFile("repeater.png");
grabToImage(rect, 5000).saveToFile("rectangle.png");
}
}
Which generates following images:
Upvotes: 0
Views: 1578
Reputation: 714
You have to enclose your Repeater
into another element (Item
, Row
...) and call grabToImage
on it. Actually, the Repeater
is not the parent of delegate elements: the Repeater
's parent is.
Window {
visible: true
width: 640
height: 480
function grabToImage(item, fileName) {
item.grabToImage(function(image) {
image.saveToFile(fileName);
});
}
Row{
id: row
spacing: 2
Repeater {
id: repeater
model: 10
delegate: Rectangle {
color: "red"
width: 5
height: 10
}
}
}
Rectangle {
id: rect
color: "blue"
width: 100
height: 10
y: 50
Rectangle {
color: "red"
x: 1
width: 5
height: 10
}
Rectangle {
color: "red"
x: 7
width: 5
height: 10
}
}
Component.onCompleted: {
grabToImage(row, "repeater.png");
grabToImage(rect, "rectangle.png");
}
}
Upvotes: 1