Qt/QML:将 QImage 从 C++ 发送到 QML 并在 GUI 上显示 QImage

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20691414/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:16:58  来源:igfitidea点击:

Qt/QML : Send QImage From C++ to QML and Display The QImage On GUI

c++qtqml

提问by trianta2

I created a class Publisherwhich periodically emits a QImageobject.

我创建了一个Publisher定期发出QImage对象的类。

However I'm having a tough time drawing the QImageto a QML element. It appears that the Imageand CanvasQML components require a QUrlinstead of a QImage, but I'm not sure how to convert my QImageto a QUrl. Edit4: When I say QUrl, I don't mean I'm trying to convert an image to a URL. That's nonsense. I mean I want to generate a reference to this image, which is not on disk, and the data type that QML components are asking for is a URL.

但是,我很难将 绘制QImage到 QML 元素。似乎 theImageCanvasQML 组件需要 aQUrl而不是 a QImage,但我不确定如何将 my 转换QImageQUrl. Edit4:当我说 QUrl 时,我并不是说我正在尝试将图像转换为 URL。那是胡说八道。我的意思是我想生成对这个图像的引用,它不在磁盘上,而 QML 组件要求的数据类型是一个 URL。

I've done some research and found that QQuickImageProviderprovides a solution, but I haven't found any documentation explaining how to convert my QImagesignal to a QUrlthat I can use for drawing. Any example code or reference documentation would be appreciated.

我做了一些研究,发现它QQuickImageProvider提供了一个解决方案,但我没有找到任何文档来解释如何将我的QImage信号转换为QUrl我可以用于绘图的信号。任何示例代码或参考文档将不胜感激。

Thanks for your help!

谢谢你的帮助!

Edit1:

编辑1:

I've taken a look here: http://qt-project.org/doc/qt-5.0/qtquick/qquickimageprovider.htmland I do not see how I pass a QImage to the quick image provider and from it create a QUrl.

我在这里看了一下:http: //qt-project.org/doc/qt-5.0/qtquick/qquickimageprovider.html,我没有看到如何将 QImage 传递给快速图像提供程序并从中创建一个 QUrl .

Edit2. Here is the header. The implementation should not be important.

编辑2。这是标题。实施应该不重要。

class Publisher
{
    Q_OBJECT

public:
    Publisher(QObject* parent = 0);

    virtual ~Publisher(void);

Q_SIGNALS:

    void newImage(const QImage& newImage);
};

Edit 3. Here is my QML code, but I don't know how to draw my QImage, so this code is kind of meaningless.

编辑 3. 这是我的 QML 代码,但我不知道如何绘制我的 QImage,所以这段代码毫无意义。

my main.cpp file:

我的 main.cpp 文件

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

    qmlRegisterType<Publisher>("Components", 1, 0, "Publisher");

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

    return app.exec();
}

my main.qml file:

我的 main.qml 文件

import QtQuick 2.0
import Components 1.0

Rectangle {
    id : testRect
    width: 360
    height: 360

    Image{
        anchors.fill: parent
        id: myImage

        Publisher {
            id: myPub

            onNewImage: {
                myImage.source = newImage;  #I know this doesnt work, it needs a QUrl and not a QImage
            }
        }
    }
}

回答by peppe

In other words, you have a class emitting a signal carrying a QImage and want to update an item in QML with that image? There are various solutions, none of which involves "converting a QImage to a QUrl" (whatever that means, surely you don't need to get a dataURL carrying your image data...)

换句话说,您有一个类发出携带 QImage 的信号,并想用该图像更新 QML 中的项目?有多种解决方案,其中没有一个涉及“将 QImage 转换为 QUrl”(无论这意味着什么,您肯定不需要获取data带有图像数据的URL...)

Use an image provider

使用图像提供程序

This means you can use a plain Imageitem in your QML files.

这意味着您可以Image在 QML 文件中使用普通项目。

  1. Create a QQuickImageProvidersubclass; give it a QImagemember (the image to provider), override requestImageto provide that image (the actual idrequested does not really matter, see below), and a slot that receives a QImageand updates the member.
  2. Connect your Publishersignal to your provider's slot
  3. Install the provider into the QML engine via QQmlEngine::addImageProvider(see QQuickView::engine); again the iddoes not really matter, just use a sensible one
  4. In QML, just use a plain Imageelement with a source like this

    Image {
        id: myImage
        source: "image://providerIdPassedToAddImageProvider/foobar"
    }
    

    foobarwill be passed to your provider, but again, it doesn't really matter.

  5. We're almost there, we now only need a way to pushthe image updates to the QML world (otherwise Image will never know when to update itself). See my answer herefor how to do that with a Connectionselement and a bit of JS.

    Note that in general you don't need to make Publishera QML type, you just need to create oneinstance in C++ and expose it to the QML world via QQmlContext::setContextProperty.

  1. 创建QQuickImageProvider子类;给它一个QImage成员(提供者的图像),覆盖requestImage以提供该图像(实际id请求并不重要,见下文),以及一个接收 aQImage并更新成员的插槽。
  2. 将您的Publisher信号连接到您的提供商的插槽
  3. 通过QQmlEngine::addImageProvider(请参阅QQuickView::engine)将提供程序安装到 QML 引擎中;再次这id并不重要,只需使用明智的
  4. 在 QML 中,只需使用Image带有这样的源的普通元素

    Image {
        id: myImage
        source: "image://providerIdPassedToAddImageProvider/foobar"
    }
    

    foobar将传递给您的提供者,但同样,这并不重要。

  5. 我们快到了,我们现在只需要一种图像更新送到 QML 世界的方法(否则图像将永远不知道何时更新自己)。请参阅我的答案,了解如何使用Connections元素和一些 JS来做到这一点。

    请注意,通常您不需要创建PublisherQML 类型,您只需要在 C++ 中创建一个实例并通过QQmlContext::setContextProperty.

Use a custom Qt Quick 2 Item

使用自定义 Qt Quick 2 项目

QQuickPaintedItemis probably the most convenient for the job as it offers a paintmethod taking a QPainter. Hence the big plan is

QQuickPaintedItem可能是最方便的工作,因为它提供了paint一种采用QPainter. 因此,大计划是

  1. Subclass QQuickPaintedItem: the subclass stores the QImageto be painted and has a slot that sets the new QImage. Also its paintimplementation simply paints the image using QPainter::drawImage.
  2. Expose the subclass to the QML world via qmlRegisterType(so that you can use it in QML)
  3. Figure out a way to connect the signal carrying the new image to the items' slot.

    This might be the tricky part.

    To perform the connection in C++ you need a way to figure out that the item has been created (and get a pointer to it); usually one does this by means of assigning the objectNameproperty to some value, then using findChildon the root object (as returned by QQuickView::rootObject()) to get a pointer to the item itself. Then you can use connectas usual.

    Or, could instead perform the connection in QML, just like above, via a Connectionselement on the publisher C++ object exposed to the QML world:

    MyItem {
        id: myItem
    }        
    
    Connections {
        target: thePublisherObjectExposedFromC++
        onNewImage: myItem.setImage(image)
    }
    

    This has the advantage of working no matter when you create the MyItem instance; but I'm not 100% sure it will work because I'm not sure you can handle the QImagetype in QML.

  1. 子类QQuickPaintedItem:子类存储QImage要绘制的对象,并有一个用于设置新 QImage 的插槽。此外,它的paint实现只是使用QPainter::drawImage.
  2. 通过将子类暴露给 QML 世界qmlRegisterType(以便您可以在 QML 中使用它)
  3. 想办法将携带新图像的信号连接到物品的插槽。

    这可能是棘手的部分。

    要在 C++ 中执行连接,您需要一种方法来确定该项目已被创建(并获得指向它的指针);通常通过将objectName属性分配给某个值,然后使用findChild根对象(由 返回QQuickView::rootObject())来获取指向项目本身的指针来实现这一点。然后就可以connect照常使用了。

    或者,可以改为在 QML 中执行连接,就像上面一样,通过Connections向 QML 世界公开的发布者 C++ 对象上的元素:

    MyItem {
        id: myItem
    }        
    
    Connections {
        target: thePublisherObjectExposedFromC++
        onNewImage: myItem.setImage(image)
    }
    

    无论您何时创建 MyItem 实例,这都有其优势;但我不是 100% 确定它会起作用,因为我不确定您是否可以处理QImageQML 中的类型。

回答by timday

When I've had image-producing C++ classes I've wanted to embed in QML, I've always done it by making the C++ class a subclass of QDeclarativeItem(there'll be a new QtQuick 2.0 equivalent of course), overriding the paint method with the appropriate drawing code, which maybe as simple as

当我拥有想要嵌入 QML 的图像生成 C++ 类时,我总是通过使 C++ 类成为QDeclarativeItem(当然会有一个新的 QtQuick 2.0 等效项)的子类来完成它,覆盖绘制具有适当绘图代码的方法,这可能就像

void MyItem::paint(QPainter* painter,const QStyleOptionGraphicsItem*,QWidget*) {
  painter->drawImage(QPointF(0.0f,0.0f),_image);
}

if you have a QImage of the right size already... and Job Done. For animation, just ping update() when there's something new to draw.

如果你已经有一个合适大小的 QImage ......并且工作完成了。对于动画,只需在有新内容要绘制时 ping update() 即可。

回答by Doug Rogers

Use QQuickItem. There is a Qt example that does this.

使用 QQuickItem。有一个 Qt 示例可以做到这一点。

C:\Qt\Examples\Qt-5.14.1\quick\scenegraph\threadedanimation

C:\Qt\Examples\Qt-5.14.1\quick\scenegraph\threadedanimation

You make a class derived from QQuickItem and register it with qmlRegisterType. Provide the override function 'updatePaintNode' in your class. In the example, the class is 'Spinner'

您创建一个从 QQuickItem 派生的类并使用 qmlRegisterType 注册它。在您的课程中提供覆盖函数“updatePaintNode”。在示例中,类是“Spinner”

In updatePaintNode:

在 updatePaintNode 中:

Create a node class derived from QObject and QSGTransformNode

创建从 QObject 和 QSGTransformNode 派生的节点类

In the node class constructor:

在节点类构造函数中:

Convert your Qimage to an QSGTexture with createTextureFromImage.

使用 createTextureFromImage 将 Qimage 转换为 QSGTexture。

Create QSGSimpleTextureNode, set QSGTexture using setTexture

创建 QSGSimpleTextureNode,使用 setTexture 设置 QSGTexture

appendChildNode with QSGSimpleTextureNode

带有 QSGSimpleTextureNode 的 appendChildNode

In QML add

在 QML 中添加

import Spinner 1.0

and

Spinner {
    anchors.centerIn: parent
    anchors.horizontalCenterOffset: 80
    spinning: true
}