【问题标题】:Capture QML drawing buffer, without displaying捕获 QML 绘图缓冲区,不显示
【发布时间】:2013-06-13 08:40:18
【问题描述】:

我需要获取每个 QML (QtQuick 2) 绘图框架并通过网络发送。 目前我使用了下面列出的方法,但是这种方法有两个很大的缺点

1) 由于 Qt5 文档中的 grabWindow() 函数存在性能问题

2) 它不能与隐藏的 QML 窗口一起使用

是否可以在 QQuickWindow::afterRendering 之后立即获得 OpenGL 渲染缓冲区? 使用 FBO 吗?共享 opengl 上下文?

class Grab: public QObject
{
 public:
 Grab( QQuickWindow * wnd ) : wnd_(wnd) {}

 public slots:

    void Grabme()
    {
       QImage image = wnd_->grabWindow();
    }

private:

QQuickWindow *wnd_;
};

int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);


QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/grab1/main.qml"));
viewer.showExpanded();

Grab grab( &viewer );
QObject::connect( &viewer, &QtQuick2ApplicationViewer::frameSwapped,
                  &grab, &Grab::Grabme, Qt::DirectConnection );


return app.exec();
}

【问题讨论】:

    标签: qt opengl qml qt5 screen-capture


    【解决方案1】:

    对于更高版本的 Qt 5.X,您还可以使用软件渲染后端。 以下渲染背景中的任何场景,没有任何可见的窗口或 OpenGL 技巧:

    // main.cpp
    #include <QGuiApplication>
    #include <QQmlEngine>
    #include <QQmlComponent>
    #include <QQuickItem>
    #include <QQuickWindow>
    #include <QQuickRenderControl>
    
    int main(int argc, char *argv[])
    {
        const char *source = "qrc:/main.qml";
        if (argc > 1) source = argv[1];
    
        QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
    
        QGuiApplication app{argc, argv};
    
        QQuickRenderControl renderControl;
    
        QQuickWindow window{&renderControl};
    
        QQmlEngine engine;
    
        QQmlComponent component{
            &engine,
            QUrl{QString::fromUtf8(source)}
        };
    
        QQuickItem *rootItem = qobject_cast<QQuickItem *>(component.create());
        window.contentItem()->setSize(rootItem->size());
        rootItem->setParentItem(window.contentItem());
    
        window.resize(rootItem->size().width(), rootItem->size().height());
    
        QImage image = renderControl.grab();
        image.save("output.png");
    
        return 0;
    }
    

    【讨论】:

      【解决方案2】:

      当窗口“不可见”时,我设法找到了让 grabWindow() 工作的技巧。诀窍是设置窗口的visibility: Window.Minimizedflags: Qt.Tool。该窗口不向用户显示,但对于 Qt 的内部,它似乎是可见的,并且 grabWindow() 方法调用按预期工作。请记住仅在场景初始化后才调用该方法。

      这个解决方案(我遇到过)的唯一问题是,如果窗口的color 属性设置为transparent,则捕获的内容具有黑色背景。

      【讨论】:

        【解决方案3】:

        下面的示例可以将任何 qml 内容抓取到 FBO,然后通过信号将其作为图像发送。 这种方法的唯一问题是可见性,抓取窗口必须可见才能成功抓取。如果有人知道如何防止这种情况,您可以帮助我并提供更高级的方法。

        // main.cpp
        int main(int argc, char* argv[])
        {
          QApplication app(argc, argv);
        
          GrabWindow grab;
          grab.setResizeMode( QQuickView::SizeViewToRootObject );
          grab.setSource( QUrl::fromLocalFile("qml/main.qml") );
          grab.setFlags( Qt::Popup );
        
          grab.show();
          return app.exec();
        }
        
        
        // grabwindow.hpp
        #pragma once
        #include <QOpenGLFramebufferObject>
        #include <QScopedPointer>
        #include <QQuickView>
        #include <QImage>
        
        class GrabWindow: public QQuickView
        {
          Q_OBJECT
        
        signals:
             void changeImage( const QImage &image );
        
        public:
            GrabWindow( QWindow * parent = 0 );
        
        private slots:
            void afterRendering();
            void beforeRendering();
        
        private:
            QScopedPointer<QOpenGLFramebufferObject> fbo_;
        };
        
        // grabwindow.cpp
        #include "grabwindow.hpp"
        #include <limits>
        
        GrabWindow::GrabWindow( QWindow * parent ) :
            QQuickView( parent )
        {
          setClearBeforeRendering( false );
          setPosition( std::numeric_limits<unsigned short>::max(), std::numeric_limits<unsigned short>::max() );
        
          connect( this, SIGNAL( afterRendering()  ), SLOT( afterRendering()  ), Qt::DirectConnection );
          connect( this, SIGNAL( beforeRendering() ), SLOT( beforeRendering() ), Qt::DirectConnection );
        }
        
        void GrabWindow::afterRendering()
        {
          if( !fbo_.isNull() )
          {
            emit changeImage( fbo_->toImage() );
          }
        }
        
        void GrabWindow::beforeRendering()
        {
          if (!fbo_)
          {
                fbo_.reset(new QOpenGLFramebufferObject( size(), QOpenGLFramebufferObject::NoAttachment) );
                setRenderTarget(fbo_.data());
          }
        }
        

        【讨论】:

        • 此代码给我错误:错误 C2027:使用未定义类型 'QOpenGLFramebufferObject' 1> C:\Qt\Qt5.1.0\5.1.0\msvc2010\include\QtQuick/qquickwindow.h(57 ) : 参见 'QOpenGLFramebufferObject' 的声明 1>grab_window.cpp(16): error C2232: '->QScopedPointer::isNull' : 左操作数有 'class' 类型,使用 '。
        • 你应该检查你的Qt版本是否支持OpenGL编译,如果你从官方网站下载,应该是。此外,关于 msvc2010,您需要创建一个包含库的正确项目文件。我同时使用 cmake 和 .pro 项目文件,一切都在 Qt 5.0.2 上运行
        • 是的,这就是原因,我正在使用 Visual Studio 2010 在 Windows 7 上开发,我使用了 QT_NO_OPENGL 宏,否则找不到 GLES2 头文件。
        • 我已经尝试过上面的例子,不幸的是“beforeRendering”信号根本没有调用。有人可以对此表示支持吗?
        • @Ashif 请提供您的 Qt 版本
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-31
        • 2017-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多