在 QML 中截取特定项目的屏幕截图的方法是什么?

What would be the way to take a screenshot of a particular Item in QML?

.

我在 QML window 中有一个 Video 元素。该视频显示在 Rectangle 中。

如何截取 Rectangle 而不是整个 window ?

您可以使用grabWindow()方法,然后裁剪生成的图像。你需要找到QML元素在window上的绝对位置,只需要获取它的位置并添加到每个父元素的位置,直到你找到根元素,然后使用QImage::copy(x, y, w, h)裁剪window 图像到元素位置和大小。

这样做有一些缺点 - 它速度较慢,因为它有抓取整个 window 和裁剪的开销,如果您的元素不是矩形且不透明,它还会抓取下方可见的内容元素。但如果它是一个不透明的矩形,并且性能不是问题,那么这是最简单的方法。

困难但更快的方法:您可以使用在 QML 中创建一个 ShaderEffectSource 并将其 sourceItem 设置为您想要的元素。这将有效地将该元素渲染到纹理中,以便您可以在其上使用着色器效果。然后在 C++ 端,从 QQuickShaderEffectSource 你可以使用它的 QSGTextureProvider *textureProvider() 方法来获取纹理提供者,然后从它你使用 QSGTexture * QSGTextureProvider::texture() 来获取纹理,并且从纹理中你可以找到int QSGTexture::textureId() 的纹理 ID。然后最后,你可以 get an image from the texture id 并使用原始数据构造一个 QImage.

看看 ItemgrabToImage 方法。

bool grabToImage(callback, targetSize)

Grabs the item into an in-memory image.

The grab happens asynchronously and the JavaScript function callback is invoked when the grab is completed.

Use targetSize to specify the size of the target image. By default, the result will have the same size as the item.

If the grab could not be initiated, the function returns false.

The following snippet shows how to grab an item and store the results to a file.

Rectangle {
    id: source
    width: 100
    height: 100
    gradient: Gradient {
        GradientStop { position: 0; color: "steelblue" }
        GradientStop { position: 1; color: "black" }
    }
}
    // ...
    source.grabToImage(function(result) {
                           result.saveToFile("something.png");
                       });

The following snippet shows how to grab an item and use the results in another image element.

Image {
    id: image
}
    // ...
    source.grabToImage(function(result) {
                           image.source = result.url;
                       },
                       Qt.size(50, 50));

Note: This function will render the item to an offscreen surface and copy that surface from the GPU's memory into the CPU's memory, which can be quite costly. For "live" preview, use layers or ShaderEffectSource.

这也适用于 QtQuick 2.0

这是个很有趣的问题。作为一种快速有效的解决方案,我建议您使用 grabToImage method of Item。它接受第一个参数作为回调函数,第二个参数作为你想要保存的路径。

我写了一个小函数来抓取任何 Item:

// what -- name of item needed to be grabbed
// where -- string
function render(what, where) {
    // Find existent item with given name `what`
    var i = 0
    var found = false
    for (i = 0; i < window.contentItem.children.length; i++) {
        if (window.contentItem.children[i].objectName === what) {
            // We found respective item
            found = true
            break
        }
    }
    if (found) {
        console.log("We found item " + what + ". Grabbing it to " + where)
        var item = window.contentItem.children[i]
        // Grab image and save it (via callback f-ion)
        item.grabToImage( function(result) { result.saveToFile(where) })
    } else {
        console.warn("No item called " + what)
    }
}

所以你可以像在 QML/QtQuick 端一样使用它,就像在 Qt 上一样(使用 QMetaObject::invokeMethod)。


还有 QQuickItem::grabToImage 方法,但我会很高兴看到任何适当的用法示例。


另一种抓取方式是使用ShaderEffectSource,随意使用。


我上面写的所有内容都是作为一个项目准备的,位于 github。代码已注释,所以希望一切都会清楚。您可以拿走它并进行一些黑客攻击。也欢迎拉取请求。