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

Posted

技术标签:

【中文标题】在 QML 中截取特定项目的屏幕截图的方法是啥?【英文标题】:What would be the way to take a screenshot of a particular Item in QML?在 QML 中截取特定项目的屏幕截图的方法是什么? 【发布时间】:2015-10-17 06:49:45 【问题描述】:

I know how to take a screenshot of the whole window in QML.

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

如何截取Rectangle而不是整个窗口?

【问题讨论】:

【参考方案1】:

这是一个非常有趣的问题。作为一种快速有效的解决方案,我可以建议您使用ItemgrabToImage 方法。它将第一个参数作为回调函数,第二个作为您要保存的路径。

我写了一个小函数来抓取任何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。代码已注释,因此希望一切都清楚。你可以拿它做一些黑客攻击。也欢迎拉取请求。

【讨论】:

“还有 QQuickItem::grabToImage 方法,但我很高兴看到任何适当的用法示例。” 你像这样使用它 void截图::takeScreenShot(QQuickItem* callingObject) auto itemScreenshot = callingObject->grabToImage(QSize(200, 200)); QObject::connect(itemScreenshot.get(), &QQuickItemGrabResult::ready, [itemScreenshot]() itemScreenshot->saveToFile("screenshot.png"); ); @mattsson 您能否自己回答一下,并说明您将如何使用 QML 中的此功能? @Rob 当然。我能做到。是你自己想知道吗?您在编写 C++ 类并通过 QML 与它们交互方面有多少经验?因为如果我必须包括从 QML 调用 C++ 方法所需的所有内容,这将是一个相当广泛的答案。在我之前的评论中,我只是假设大多数 QT/QML 程序员已经具备这些知识。 (我知道,我知道。在写解释时假设这样的事情是一件坏事。) @mattsson 如果您想使用大量链接并简要说明连接,那很好。据我了解,有很多方法可以将 QML 连接到 C++,不是吗?【参考方案2】:

看看ItemgrabToImage方法。

bool grabToImage(callback, targetSize)

将项目抓取到内存中的图像中。

抓取是异步发生的,javascript 函数回调 在抓取完成时调用。

使用 targetSize 指定目标图像的大小。默认情况下, 结果将具有与项目相同的大小。

如果无法启动抓取,则该函数返回 false。

下面的 sn-p 展示了如何抓取一个项目并存储结果 到一个文件。

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");
                       );

下面的 sn-p 显示了如何抓取一个项目并使用结果 另一个图像元素。

Image 
    id: image

    // ...
    source.grabToImage(function(result) 
                           image.source = result.url;
                       ,
                       Qt.size(50, 50));

注意:此函数会将项目渲染到屏幕外表面,并且 将该表面从 GPU 的内存复制到 CPU 的内存中,这 可能非常昂贵。对于“实时”预览,使用图层或 ShaderEffectSource。

这也适用于 QtQuick 2.0

【讨论】:

ShaderEffectSource 真的更快吗?从 Qt 文档中看起来并非如此 阅读ShaderEffectSource 的官方文档清楚地指出带有 Warning 标记,在大多数情况下它会降低性能并增加视频内存使用量。你能澄清一下吗?我正在寻找保存图像的最快方法。我还看到 Qt 官方文档指出 ShaderEffectSource 依赖于底层硬件,并且如果您的 QtApp 在多种 android 设备上运行,则可能无法在所有设备上运行相同。 是否可以只获取没有子项的 QQuickItem 的快照?【参考方案3】:

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

这有几个缺点——它比较慢,因为它需要抓取整个窗口和裁剪的开销,如果你的元素不是矩形和不透明的,它也会抓取元素下可见的东西。但是,如果它是一个不透明的矩形,并且性能不是问题,那么这是一种简单的方法。

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

【讨论】:

拜托,你能分享一下 ShaderEffectSource 解决方案的代码吗?

以上是关于在 QML 中截取特定项目的屏幕截图的方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

即使窗口重叠,如何在 Qt(Python、Linux)中截取特定窗口的屏幕截图?

vc 截取屏幕

APP,限制截屏功能,如何解决

android中怎么用代码实现截屏

手机截屏怎么截

以编程方式截取特定区域的屏幕截图