我需要抓取每个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();
}
答案 0 :(得分:8)
下面的示例可以将任何qml内容抓取到FBO,然后通过信号将其作为Image发送。 这种方法只有一个问题是可见性,抓取窗口必须是可见的才能成功抓取。如果有人知道如何防止这种情况,你可以帮助我并提供更高级的方法。
// 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());
}
}
答案 1 :(得分:2)
我设法找到了一个技巧,当窗口是&#34;不可见&#34;时,makeWindow()会工作。诀窍是设置窗口visibility: Window.Minimized
和flags: Qt.Tool
。该窗口不会显示给用户,而是显示在Qt的内部,它似乎是可见的,grabWindow()
方法调用按预期工作。请记住,只有在场景初始化后才调用该方法。
此解决方案(我遇到过)的唯一问题是,如果窗口的color
属性设置为transparent
,则捕获的内容为黑色背景。
答案 2 :(得分:0)
在更高版本的 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;
}