QML Item.grabToImage returns empty image on Repeater?

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 Items 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 Rectangles' Component.completed signal is triggered before the grabToImage code runs so they seem to be instantiated.

** Update **

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:

repeater.png

Repeater

rectangle.png

Rectangle

Upvotes: 0

Views: 1578

Answers (1)

lolo
lolo

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

Related Questions