我开发了一个应用程序,使用QML完成了UI。我现在被要求做的是,当使用第二台显示器时,第二台显示器会显示程序正在执行的所有操作。起初我想过告诉客户端配置Windows来克隆它的屏幕。但是,当应用程序使用其部分功能时,克隆屏幕需要在克隆屏幕中显示某些指示符,但不在原始屏幕上显示。
所以我的问题是,我怎样才能做到这一点。如何在一个屏幕上镜像正在发生的事情,同时保持足够的控制来绘制一个而不是另一个屏幕。
我唯一的想法是使用计时器定期拍摄屏幕截图并在第二个屏幕中显示该图像。
这可行吗?
答案 0 :(得分:0)
你绝对不想拍摄截图并将图像绘制回来,效率非常低。
请记住,它只是一个视觉副本,它不会接受用户输入。如果您希望它在两个窗口中都是交互式的,那么您应该只使用一个数据对象并将其连接到两个单独的GUI元素。
好的,这是代码,但不幸的是,它显然揭示了QML中的一个错误,因为实现似乎并不适用于不同的窗口:
Window {
id: mainw
visible: true
width: 640
height: 480
title: qsTr("main window")
Row {
spacing: 5
Rectangle {
id: source
width: 100
height: 100
color: ma.containsPress ? "red" : "cyan"
Text {
text: "adoy"
anchors.centerIn: parent
}
MouseArea {
id: ma
anchors.fill: parent
}
}
ShaderEffectSource {
width: source.width
height: source.height
live: true
sourceItem: source
}
}
Window {
visible: true
width: 640
height: 480
title: qsTr("another window")
x: mainw.x + width + 10
y: mainw.y
Row {
ShaderEffectSource {
width: source.width
height: source.height
live: true
sourceItem: source
}
Rectangle {
width: 100
height: 100
color: "blue"
}
}
}
}
答案 1 :(得分:0)
定期截取屏幕截图虽然完全可行,但由于会影响性能,因此不受欢迎。相反,您应该使用主窗口的 onFrameSwapped()
信号,仅在生成新帧时抓取图像。
理想情况下,您应该使用 @dtech 建议的 Layer 或 ShaderEffectSource,直接从 GPU 读取和重新渲染帧。不幸的是,由于 Qt Quick 的场景图的限制,不可能在不破坏源窗口场景图的情况下跨单独的窗口实现这一点。
选项是通过 CPU 复制帧,或用 C++ 重新实现部分 QML 引擎以在生成新帧时提取图像。
最简单的方法是使用 Item 的 grabToImage() 函数从 QML 抓取帧。由于 Window 本身不是一个项目,因此您必须从其元素之一中获取图像。在此示例中,每次在 mainWindow 上交换帧时,我都会使用 onFrameSwapped() 信号从名为 viewport 的项目中抓取图像。然后将内存中该图像的路径设置为第二个窗口中图像的源,名为projectionWindow。第二个窗口将在由 screenID 变量设置的屏幕上打开;它也被设置为无框窗口,其可见性设置为最大化或全屏,这样它是第二个屏幕上看到的唯一窗口。
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: mainWindow
title: qsTr("Main Window")
visible: true
width: 400
height: 200
color: "#0F0"
Rectangle {
id: viewport
color: "#00F"
width: parent.width/2
height: parent.height/2
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
onFrameSwapped: {
viewport.grabToImage(function(result) {
projectionWindow.frame = String(result.url);
});
}
Window {
id: projectionWindow
property int screenID: 1
property alias frame: img.source
transientParent: mainWindow
visible: true
x: Qt.application.screens[screenID].virtualX
y: Qt.application.screens[screenID].virtualY
width: Qt.application.screens[screenID].desktopAvailableWidth
height: Qt.application.screens[screenID].desktopAvailableHeight
flags: Qt.FramelessWindowHint
color: "#000"
visibility: Window.Maximized
// visibility: Window.FullScreen
Image {
id: img
anchors.fill: parent
fillMode: Image.PreserveAspectFit
// Performance tweaks
asynchronous: true
cache: false
}
}
}
第二个更有效但不完整的解决方案超出了我目前的经验水平。本质上,您将从 QQuickWindow 继承并通过 QQuickWindow::afterRendering()
或 QQuickWindow::frameSwapped()
信号将图像发送到第二个窗口。您将使用 QQuickFramebufferObject
在屏幕外渲染和抓取帧,这意味着使用 OpenGL 作为渲染器,这可能会限制 Qt 6 中的平台可移植性,或者至少会抵消使用本机渲染带来的所有性能优势管道。
https://doc.qt.io/qt-6/qtquick-visualcanvas-scenegraph.html
以下 Giuseppe D'Angelo 的演讲展示了其中一些是如何设置的。它没有展示如何将内容复制到另一个窗口/屏幕,但它可以在这方面提供帮助。