从StackOverflow上的this discussion开始,我可以将QML项目中的图像保存为png/jpeg
文件。
我如何叠加或合并两个不同的qml
图层&将它们合并为一个,将其保存为png / jpeg?
注意:我可以保存一个QQuickItem
。只需要知道如何覆盖2 QQuickItem
s
答案 0 :(得分:5)
让两个qml对象成为根Item
的子对象,然后获取该根项,它将捕获其所有内容。
确保根项目足够大以封闭子项,并且子项不在负空间中,因为它只会捕获根项目内部的内容。
您也可以使用C ++甚至QML进行手动合成。
您评论中描述的问题是您无法移动内容,那么您可以做些什么?您可以拥有两个Image
元素,而不是将原始QML对象作为同一根的父项,然后捕获项目A并将捕获结果设置为图像A的源,然后对项目执行相同操作B,最后,你捕获根项目,它将两个图像一起捕获。
好的,这是一个快速的例子,它看起来有点复杂,因为抓取是异步的,你必须等待个人抓取结果才能完成,然后才能抓住" final" root项,因此计时器的用法。在此示例中,不同的项目连续排列,但您可以按照自己喜欢的方式组合它们:
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() })
}
}
首先捕获两个矩形并将其用作木匠中图像的来源,然后捕获木匠并在结果图像中显示,除最终结果图像外的所有对象都被隐藏。
更简单的是,您可以使用这个漂亮的小帮手快速加入单个图像中的任意数量的项目:
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)
它仍然使用一行,但您可以轻松调整它以进行自己的布局。它通过传递最终回调和你想要捕获的所有项目来工作,在内部它为每个项目创建一个图像,将它们放入容器中,然后触发完成计时器。
请注意,根据系统的速度以及项目的复杂程度和计数,您可能需要延长计时器间隔,因为最终的回调需要在所有捕获完成后才能执行,图像分配了来源并调整了图像大小以使该行具有适当的尺寸。
我还注释了大多数事情,以便更容易理解。
答案 1 :(得分:2)
这个问题似乎与this one
密切相关解决方案是将有问题的Item
呈现为第二项的纹理,您不需要渲染到屏幕上。您可以根据需要添加多个Item
,根据需要将它们相对于彼此放置,并将它们的来源设置为您想要抓取的ShaderEffectSource
s,然后根据需要撰写此Item
到图像。
然后你抓住Item
到图像。
一个通用示例,它公开了一个函数,用于将Item
列表抓取到一个图像,其中每个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的内存中,这可能非常昂贵。对于&#34;直播&#34;预览,使用图层或ShaderEffectSource。
这个解决方案应该比使用带有Image
的{{1}}更有效,因为它可以将内容保存在GPU内存中,直到它被抓取并存储。