Qt - 双显示器设置中的QML屏幕镜像

时间:2018-06-14 23:00:25

标签: qt qml

我开发了一个应用程序,使用QML完成了UI。我现在被要求做的是,当使用第二台显示器时,第二台显示器会显示程序正在执行的所有操作。起初我想过告诉客户端配置Windows来克隆它的屏幕。但是,当应用程序使用其部分功能时,克隆屏幕需要在克隆屏幕中显示某些指示符,但不在原始屏幕上显示。

所以我的问题是,我怎样才能做到这一点。如何在一个屏幕上镜像正在发生的事情,同时保持足够的控制来绘制一个而不是另一个屏幕。

我唯一的想法是使用计时器定期拍摄屏幕截图并在第二个屏幕中显示该图像。

这可行吗?

2 个答案:

答案 0 :(得分:0)

你可以use particular QML elements as texture sources and easily duplicate them via trivial fragment shaders

你绝对不想拍摄截图并将图像绘制回来,效率非常低。

请记住,它只是一个视觉副本,它不会接受用户输入。如果您希望它在两个窗口中都是交互式的,那么您应该只使用一个数据对象并将其连接到两个单独的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 引擎以在生成新帧时提取图像。

工作 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
        }
    }
}

部分 C++ 解决方案

第二个更有效但不完整的解决方案超出了我目前的经验水平。本质上,您将从 QQuickWindow 继承并通过 QQuickWindow::afterRendering()QQuickWindow::frameSwapped() 信号将图像发送到第二个窗口。您将使用 QQuickFramebufferObject 在屏幕外渲染和抓取帧,这意味着使用 OpenGL 作为渲染器,这可能会限制 Qt 6 中的平台可移植性,或者至少会抵消使用本机渲染带来的所有性能优势管道。

https://doc.qt.io/qt-6/qtquick-visualcanvas-scenegraph.html

以下 Giuseppe D'Angelo 的演讲展示了其中一些是如何设置的。它没有展示如何将内容复制到另一个窗口/屏幕,但它可以在这方面提供帮助。

https://www.youtube.com/watch?v=V_idc9BBRuI

https://www.youtube.com/watch?v=D-7fVGIBz6k