QtQuick、动态图像和 C++

2022-01-19 00:00:00 qt5 qml qt-quick c++

我是 Qt 新手,根据我在 qt-project.org 等地;QtQuick 似乎是一个有吸引力的选择,因为它能够在基于指针和触摸的设备上工作.我的问题是让它与 c++ 一起工作.

I'm new to Qt, and from what I've read on qt-project.org and other places; QtQuick seems like an attractive option because of its ability to work on both pointer and touch based devices. My problem is getting it to work well with c++.

我决定写一个康威生命游戏的变体,作为Hello World"之后的下一步.对于如何将板"――一个 [height][width][bytes-per-pixel] 字符数组――集成到场景图中,我完全感到困惑.

I decided to write a variant of Conway's Game of Life as a next step after "Hello World". I am thoroughly mystified as to how to get the "board" -- a [height][width][bytes-per-pixel] array of char -- integrated into the scene graph.

基本上,该过程是LifeBoard"迭代其规则并更新 char*/image.我有这个简单的 QML:

Basically, the process is that the "LifeBoard" iterates through its rules and updates the char*/image. I've got this simple QML:

:::QML
ApplicationWindow {
    id:         life_app_window
    visible:    true
    title: qsTr("Life")

    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("Quit")
                onTriggered: Qt.quit();
            }
        }
    }

    toolBar: ToolBar {
        id: lifeToolBar;
        ToolButton {
            id: toolButtonQuit
            text: qsTr("Quit")
            onClicked: Qt.quit()
        }
        ToolButton {
            id: toolButtonStop
            text: qsTr("Stop")
            enabled: false
            //onClicked:
        }
        ToolButton {
            id: toolButtonStart
            text: qsTr("Start")
            enabled: true
            //onClicked: //Start life.
        }
        ToolButton {
            id: toolButtonReset
            text: qsTr("Stop")
           // onClicked: //Reset life.
        }
    }

    Flow {
        id: flow1
        anchors.fill: parent
        //*****
        // WHAT GOES HERE
        //*****
    }

    statusBar: StatusBar {
        enabled: false
        Text {
            // Get me from number of iterations
            text: qsTr("Iterations.")
        }
    }
}

我希望图像来自一个具有类似这样的 api 的类:

I want to image to come from a class with a api kinda like this:

class Life {
    public:
        QImage getImage() {}
        // Or
        char* getPixels(int h, int w, QImage::Format_ARGB8888) {}
}

我没有任何线索,而且花费数小时阅读教程也无济于事.如何将 C++ 中的 char* 图像链接到 ???在 QML 中,以便 QML 可以启动/停止Life"循环,以便Life"循环并更新 char 数组并通知 QML 重绘它?

I have no clue, and hours wading through tutorials did not help. How does one link a char* image in c++ to a ??? in QML so that the QML can start/stop the "Life" loop and so that the "Life" loop and update the char array and notify QML to redraw it?

注意:我已经根据 这里.这种方法的问题是我看不到如何让 c++ 驱动"屏幕上的图像.我希望将控制权从 QML 传递给 c++,并让 c++ 告诉 QML 何时使用更改后的图像更新显示.这种方法有解决方案吗?或者完全是另一种方法.

Note: I've looked at subclassing QQuickImageProvider based on the info here. The problem with this approach is that I cannot see how to let c++ "drive" the on screen image. I wish to pass control from QML to c++ and let c++ tell QML when to update the display with the changed image. Is there a solution with this approach? Or another approach entirely.

推荐答案

第一种方法是创建一个 Rectangle 用于 QML 中的每个游戏像素,这对于 8x8 棋盘来说可能很有趣,但对于 100x100 棋盘则不合适,因为您需要为每个像素手动编写 QML 代码.

First way to do that would be creating a Rectangle for each game pixel in QML, which might be fancy for a 8x8 board, but not for a 100x100 board, since you need to write the QML code manually for each pixel.

因此,我会选择用 C++ 创建并暴露给 QML 的图像.您可以通过 图像提供程序 调用它们以允许异步加载.让Life只做逻辑.

Thus I'd go for images created in C++ and exposed to QML. You call them via an image provider to allow asynchronous loading. Let Life do the logic only.

图像是从 QML 中调用的,如下所示:

The image is called from QML like this:

Image {
    id: board
    source: "image://gameoflife/board"
    height: 400
    width: 400
}

现在gameoflife是图片提供者的名字,board就是所谓的id,以后可以使用.

Now gameoflife is the name of the image provider and board the so-called id you can use later.

在你的main.cpp

LifeImageProvider *lifeIP = new LifeImageProvider(life);
engine.addImageProvider("gameoflife", lifeIP);

其中 engine 是您的主要 QQmlApplicationEngine 并且 life 是您的 Life 游戏引擎的一个实例.

where engine is your main QQmlApplicationEngine and life an instance of your Life game engine.

LifeImageProvider 是您创建像素数据的类.以某种方式开始

LifeImageProvider is your class to create pixeldata. Starts somehow like

class LifeImageProvider : public QQuickImageProvider
{
public:
    LifeImageProvider(Life *myLifeEngine);
    QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);

private:
    Life *myLifeEngine_;
};

重要的方法是从QML调用的requestPixmap.你需要实现它.

The important method is requestPixmap, which is called from QML. You need to implement it.

要在 Life 发送 stateChanged() 信号时刷新游戏板,请将 life 作为全局对象公开给 QML:

To refresh the game board when Life sends a stateChanged() signal, expose life as a global object to QML:

context->setContextProperty("life", &life);

您可以将信号绑定到 QML

You can bind the signal to QML

Image {
    id: board
    source: "image://gameoflife/board"
    height: 400
    width: 400
}

Connections {
    target: life
    onStateChanged: {
        board.source = "image://gameoflife/board?" + Math.random()
        // change URL to refresh image. Add random URL part to avoid caching
    }
}

相关文章