Qt:如何将 2 个 QQuickItems 合并为一个,然后将其保存为 png
Posted
技术标签:
【中文标题】Qt:如何将 2 个 QQuickItems 合并为一个,然后将其保存为 png【英文标题】:Qt: How to merge 2 QQuickItems into one before saving it into a png 【发布时间】:2017-07-10 07:32:22 【问题描述】:通过 *** 上的 this discussion,我可以将 QML 项目中的图像保存到文件中,格式为 png/jpeg
。
我如何叠加或合并两个不同的qml
图层并将它们合并为一个,以将其保存为 png/jpeg?
注意:我可以保存一个QQuickItem
。只需要知道怎么覆盖2个QQuickItem
s
【问题讨论】:
【参考方案1】:只需让两个 qml 对象成为根 Item
的子对象,然后抓取该根项目,它将捕获其所有内容。
只需确保根项足够大以包围子项,并且子项不在负空间中,因为它只会捕获根项足迹内的内容。
您还可以使用 C++ 甚至 QML 进行手动组合。
您评论中描述的问题是您无法移动东西,那么您该怎么办?您可以拥有两个Image
元素,而不是将原始QML对象作为同一根的父对象,然后捕获项目A并将捕获结果设置为图像A的源,然后对项目B执行相同的操作,最后,您捕获根项目,这将同时捕获两个图像。
好的,这是一个简单的例子,看起来有点复杂,因为抓取是异步的,你必须等待单个抓取结果完成才能抓取“最终”根项目,因此使用计时器。在此示例中,不同的项目排成一排,但您可以按照自己喜欢的方式组合它们:
ApplicationWindow
id: window
visible: true
width: 640
height: 480
Rectangle
id: s1
visible: false
width: 200
height: 200
color: "red"
Rectangle
id: s2
visible: false
width: 200
height: 200
color: "blue"
Row
id: joiner
visible: false
Image id: t1
Image id: t2
Image
id: result
y: 200
Timer
id: finish
interval: 10
onTriggered: joiner.grabToImage(function(res) result.source = res.url)
Component.onCompleted:
s1.grabToImage(function(res) t1.source = res.url)
s2.grabToImage(function(res) t2.source = res.url; finish.start() )
首先捕获两个矩形并将其用作joiner中图像的源,然后捕获joiner并显示在结果图像中,隐藏除最终结果图像之外的所有对象。
更简单的是,您可以使用这个漂亮的小助手将任意数量的项目快速连接到单个图像中:
Item
id: joinHelper
visible: false
property Component ic: Image
property var cb: null
Row id: joiner
Timer
id: finish
interval: 100
onTriggered: joiner.grabToImage(joinHelper.cb)
function join(callback)
if (arguments.length < 2) return // no items were passed
var i
if (joiner.children.length) // clean previous captures
for (i = 0; i < joiner.children.length; ++i)
joiner.children[i].destroy()
cb = callback // set callback for later
for (i = 1; i < arguments.length; ++i) // for every item passed
var img = ic.createObject(joiner) // create empty image
// need to capture img by "value" because of JS scoping rules
// otherwise you end up with only one image - the final one
arguments[i].grabToImage(function(temp) return function(res)temp.source = res.url(img))
finish.start() // trigger the finishing step
你可以这样使用它:
joinHelper.join(function(res) result.source = res.url , s1, s2)
它仍然使用一行,但您可以轻松地对其进行调整以进行自己的布局。它通过传递最终回调和您要捕获的所有项目来工作,在内部它为每个项目创建一个图像,将它们放入容器中,然后触发完成计时器。
请注意,根据系统的速度和项目的复杂程度以及它们的计数,您可能需要增加计时器间隔,因为最终的回调需要在所有捕获完成后执行,图像分配源并调整图像大小以使行具有适当的尺寸。
我还对大部分内容进行了注释,以便于理解。
【讨论】:
@HAL9000-Kernel 最终结果会发生什么由您传递的回调定义,将result.source = res.url
替换为您想要对抓取结果执行的任何操作。
但是是的,如果你想限制它只做一件事,你可以修改它以跳过传递回调,而是在计时器处理程序中编写回调。我编写它的方式为您提供了拥有任意抓取结果处理程序的灵活性。如果您删除回调参数,请确保修改代码以从零开始执行参数循环,而不是跳过一。
然后您将删除joiner.grabToImage(joinHelper.cb)
并将joiner
传递给save()
。这意味着您可以删除 callback
和拥有它的 cb
属性。您仍然需要使用计时器来让整个事情有时间完成。
只需将行替换为项目
您可以在Item
中使用width: childrenRect.width; height: childrenRect.height
使其自动放大以适应所有孩子。【参考方案2】:
这个问题似乎与this one密切相关
解决方案是将有问题的Item
s 渲染到第二个项目的纹理中,您不需要渲染到屏幕上。您可以通过添加多个ShaderEffectSource
s 作为子代,随意组合此Item
,根据需要将它们彼此相对放置,并将它们的来源设置为您想要抓取到图像的Item
s。
然后你将Item
抓取到Image。
一个通用示例,它公开了一个函数,用于将 Item
s 列表抓取到一个图像,其中每个 Item
相互堆叠,每个不透明度为 0.2:
import QtQuick 2.0
Rectangle
id: root
visible: false
color: 'white'
function grabMultipleToImage(url, objects)
imgRep.url = url
width = Math.max.apply(root, objects.map(function(e) return e.width ))
height = Math.max.apply(root, objects.map(function(e) return e.height ))
imgRep.ready = 0
imgRep.model = objects
Repeater
id: imgRep
onReadyChanged:
if (ready > 0 && ready === model.length)
console.log(root.width, root.height, imgRep.url)
root.grabToImage(function (res) res.saveToFile(imgRep.url); model = null )
property int ready: 0
property string url
delegate: ShaderEffectSource
sourceItem: modelData
width: modelData.width
height: modelData.height
opacity: 0.2
live: false
Component.onCompleted: imgRep.ready++
这个的用法是这样的:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Button
text: 'grab'
onClicked:
test.grabMultipleToImage('testimg.jpg', [rect1, rect2, rect3])
ImageGrabber
id: test
Rectangle
x: 100
y: 205
id: rect1
color: 'blue'
width: 10
height: 20
Rectangle
x: 250
y: 12
id: rect2
color: 'green'
width: 20
height: 30
Rectangle
x: 100
y: 100
id: rect3
color: 'red'
width: 100
height: 5
但如果您需要更复杂的合并,您也可以手动创建此对象并随时获取。
根据文档中的说明,不对其进行计量
Note:此函数会将项目渲染到屏幕外表面,并将该表面从 GPU 的内存复制到 CPU 的内存中,这可能会非常昂贵。对于“实时”预览,请使用图层或 ShaderEffectSource。
这个解决方案应该比使用Image
s 和ItemGrabResult
s 作为源更有效,因为它将内容保留在GPU 内存中,直到它被抓取和存储。
【讨论】:
感谢您添加此答案。我正在尝试此处的选项。这看起来也是一个简单的解决方案 但是一个项目旁边的另一个不是这里的意图。考虑到每个图像中都有一些带有透明背景的内容,它是将图像叠加在另一个图像之上 根据您的评论更改。 我尝试使用您的解决方案,将res.saveToFile(imgRep.url)
替换为对我的C++ 插槽的调用,该插槽接受QQuickItem
,就像ImageCapture.save(res)
一样。
res
不会是QQuickItem
。它是一个ItemGrabResult
,它继承了QObject
,但不是QQuickItem
。如果你需要 QQuickItem
你需要通过,在我的例子中 root
在 ImageGrabber.qml
或 test
在我的 main.qml
中。以上是关于Qt:如何将 2 个 QQuickItems 合并为一个,然后将其保存为 png的主要内容,如果未能解决你的问题,请参考以下文章