在 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】:这是一个非常有趣的问题。作为一种快速有效的解决方案,我可以建议您使用Item
的grabToImage
方法。它将第一个参数作为回调函数,第二个作为您要保存的路径。
我写了一个小函数来抓取任何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】:看看Item
的grabToImage
方法。
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 中截取特定项目的屏幕截图的方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章