为啥使用 QQuickWindow::grabWindow() 会导致窗口变成图像?

Posted

技术标签:

【中文标题】为啥使用 QQuickWindow::grabWindow() 会导致窗口变成图像?【英文标题】:Why using QQuickWindow::grabWindow() causes a window to turn into an image?为什么使用 QQuickWindow::grabWindow() 会导致窗口变成图像? 【发布时间】:2020-11-08 22:26:05 【问题描述】:

我有一个 QQuickWidget 并想使用 QQuickWindow::grabWindow() 抓取屏幕截图。但是,当我这样做时,QQuickWindow 会变成图像并且没有响应。

以下是可重现的最小代码: 该错误在 Qt5.13 到 Qt5.15.1 的发布模式下可重现(出于某种原因,Qt 在调试中抛出了一个断言)

//TestWidget.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets quickwidgets

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h \
    windowgrabber.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$$TARGET/bin
else: unix:!android: target.path = /opt/$$TARGET/bin
!isEmpty(target.path): INSTALLS += target

DISTFILES += \
    Main.qml

RESOURCES += \
    qml.qrc

//main.cpp

#include <QApplication>
#include <QQuickWidget>
#include <QQmlContext>
#include "windowgrabber.h"

int main(int argc, char *argv[])

    QApplication a(argc, argv);

    QQuickWidget *quickWidget = new QQuickWidget;
    quickWidget->rootContext()->setContextProperty("windowGrabber", new WindowGrabber(quickWidget));
    quickWidget->setSource(QUrl("qrc:/Main.qml"));
    quickWidget->show();

    return a.exec();


//Main.qml

import QtQuick 2.0
import QtQuick.Controls 2.0

Page 
    Button 
        id: button
        text: "grab window"
        onClicked: windowGrabber.grabWindow(button)
    

//WindowGrabber.h

#ifndef WINDOWGRABBER_H
#define WINDOWGRABBER_H

#include <QObject>
#include <QQuickItem>
#include <QQuickWindow>

class WindowGrabber : public QObject

    Q_OBJECT
public:
    WindowGrabber(QObject *parent = nullptr) : QObject(parent) 
    Q_INVOKABLE static void grabWindow(QQuickItem *item) 
        item->window()->grabWindow();
    
;

#endif // WINDOWGRABBER_H

代码创建了一个源设置为 Main.qml 的 QQuickWidget。我想在单击 qml 内的按钮时截屏。但是在单击按钮后,quickwidget 中的 QQuickWindow 变成了图像,按钮也变成了图像。我已经用 QWidget::createWindowContainer 进行了测试,它可以工作,但最好的解决方案是使用 QQuickWidget。任何人都知道为什么会发生这种情况?

【问题讨论】:

我的同事写了原帖,但被删除了。他让我代表他重写这个问题。我希望措辞现在使问题更清楚。很抱歉有任何困惑 @eyllanesc 他没有删除这个问题,但它被社区删除了,因为它不可重现,所以我重写了这个问题,因为我们找不到解决它的方法。 @eyllanesc 是的,他的帖子已被社区删除。上面写着“此帖子已隐藏。昨天已被社区自动删除” @eyllanesc ***.com/questions/64544449/… @eyllanesc 谢谢你的信息。我还在学习操作系统社区,所以我的帖子可能看起来不完美。但我们正在努力寻找替代解决方案,并希望有人能解决这个问题。 【参考方案1】:

QQuickWidget 使用不可见的 QQuickWindow 来渲染项目,并且该渲染在 QWidget 中再次渲染,因此在尝试保存图像时它会干扰,导致延迟,正如我在 this post 中已经指出的那样。

一种可能的解决方案是直接从 QQuickWidget 中抓取:

#ifndef WINDOWGRABBER_H
#define WINDOWGRABBER_H

#include <QObject>
#include <QQuickWidget>

class WindowGrabber : public QObject

    Q_OBJECT
public:
    WindowGrabber(QQuickWidget *widget): m_widget(widget)  
    Q_INVOKABLE void grabWindow() 
        if(m_widget)
            QPixmap img = m_widget->grab();
            qDebug() << img;
        
    
private:
    QPointer<QQuickWidget> m_widget;
;


#endif // WINDOWGRABBER_H
#include <QApplication>
#include <QQuickWidget>
#include <QQmlContext>
#include "windowgrabber.h"

int main(int argc, char *argv[])

    QApplication a(argc, argv);

    QQuickWidget quickWidget;
    WindowGrabber grabber(&quickWidget);
    quickWidget.rootContext()->setContextProperty("windowGrabber", &grabber);
    quickWidget.setSource(QUrl("qrc:/main.qml"));
    quickWidget.show();

    return a.exec();

Button 
    id: button
    text: "grab window"
    onClicked: windowGrabber.grabWindow()

【讨论】:

以上是关于为啥使用 QQuickWindow::grabWindow() 会导致窗口变成图像?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在参数周围使用 /*、*/ 以及为啥在提取数组长度时使用 >>>? [复制]

为啥我们使用 hadoop mapreduce 进行数据处理?为啥不在本地机器上做呢?

为啥 CoreGui Roblox 锁定在 DataModel 中,为啥受信任的用户不能使用 CoreScripts?

为啥有人应该在 git commit 之前使用 git add?或者为啥有人应该使用 git add 呢?

为啥刷新令牌更安全?如果刷新令牌也可能被盗,为啥我们还要使用它?

为啥使用有状态的 Web 服务是不好的编程,为啥会被允许?