QtQuick,动态图像和C ++

时间:2014-05-14 23:55:20

标签: c++ qml qt5 qt-quick

我是Qt的新手,也是我在qt-project.org和其他地方读过的内容。 QtQuick似乎是一个很有吸引力的选择,因为它能够在指针和触摸设备上工作。我的问题是让它与c ++一起使用。

我决定在“Hello World”之后编写一个Conway的生命游戏变体作为下一步。关于如何获得“板” - 一个[高度] [宽度] [字节每像素] char数组 - 我已经完全神秘化了 - 集成到场景图中。

基本上,过程是“LifeBoard”遍历其规则并更新char * / image。我有这个简单的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类:

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

我没有任何线索,花时间浏览教程并没有帮助。如何将c ++中的char *图像链接到???在QML中,QML可以启动/停止“Life”循环,以便“Life”循环并更新char数组并通知QML重绘它?


注意:我已经根据信息here查看了对QQuickImageProvider的子类化。这种方法的问题在于我无法看到如何让c ++“驱动”屏幕上的图像。我希望将控制从QML传递给c ++,让c ++告诉QML何时用更改的图像更新显示。这种方法有解决方案吗?或完全是另一种方法。

2 个答案:

答案 0 :(得分:7)

这样做的第一种方法是为QML中的每个游戏像素创建一个Rectangle,这对于8x8板可能很有用,但对于100x100板则不行,因为你需要手动编写QML代码每个像素。

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

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

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

现在gameoflife是图片提供者的名称,board是您稍后可以使用的所谓id

gameoflife

中注册main.cpp
LifeImageProvider *lifeIP = new LifeImageProvider(life);
engine.addImageProvider("gameoflife", lifeIP);

其中engine是您的主QQmlApplicationEnginelife Life游戏引擎的实例。

LifeImageProvider是您创建pixeldata的类。以某种方式开始像

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

private:
    Life *myLifeEngine_;
};

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

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

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

您可以将信号绑定到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
    }
}

答案 1 :(得分:1)

只是为了好玩,而且冒着完全切向回答的风险,这里是一个完全用QML实现的GameOfLife,只需将它放在一个.qml文件中并用qmlscene运行它。在Qt 5.3.0上运行,并且在一个旧的Core 2 Duo lappy上以令人惊讶的速度(对我而言)运行。我确信它永远不会像基于C ++ QQuickImageProvider的解决方案那样快速/高效,但它确实说明了在QML中做很多事情而不诉诸C ++。

import QtQuick 2.2

Rectangle {
  id: main
  width: 640
  height: 640
  color: '#000088'

  Timer {
    interval: 1000/60
    running: true
    repeat: true
    onTriggered: {advance();display();}
  }

  Component {
    id: cellComponent
    Rectangle {
      objectName: 'cell'
      property int row: 0
      property int col: 0      
      x: main.width/2+width*col
      y: main.height/2+height*row      
      width: 5
      height: 5
      radius: 2
      smooth: true
      color: '#ffcc00'
    }
  }

  property var cells: null

  Component.onCompleted: {
    cells=[[-1, 0],[-1, 1],[ 0,-1],[ 0, 0],[ 1, 0]];
    display();
  }

  function display() {
    // Just completely regenerate display field each frame
    // TODO: might be nicer to do differential updates, would allow birth/death animations   

    // Nuke all previously displayed cells
    for (var i=0;i<children.length;i++) {
      if (children[i].objectName=='cell') {
        children[i].destroy();
      }
    }

    // Show current set of cells
    for (var i=0;i<cells.length;i++) {
      var c=cellComponent.createObject(
        main,
        {'row':cells[i][0],'col':cells[i][1]}
      );
    }
  }

  function advance() {

    // Build a hash of the currently alive cells and a neighbour count (includes self)
    var a=new Object;
    var n=new Object;
    for (var i=0;i<cells.length;i++) {
      var p=cells[i]
      var r=p[0];
      var c=p[1];
      if (!(r in a)) a[r]=new Object;
      a[r][c]=1;
      for (var dr=r-1;dr<=r+1;dr++) {
        for (var dc=c-1;dc<=c+1;dc++) {
          if (!(dr in n)) n[dr]=new Object;
          if (!(dc in n[dr])) n[dr][dc]=0;
          n[dr][dc]+=1;
        }
      }
    }

    // For all live cells, assess viability
    var kill=[];
    var stay=[];
    for (var r in a) {
      for (var c in a[r]) {
        if (n[r][c]-1<2 || n[r][c]-1>3)
          kill.push([Number(r),Number(c)]);
        else
          stay.push([Number(r),Number(c)]);
      }
    }

    // For neighbours of live cells, assess potential for births
    var born=[];
    for (var r in n) {
      for (var c in n[r]) {
        if (!((r in a) && (c in a[r]))) {
          if (n[r][c]==3)
            born.push([Number(r),Number(c)]);
        }
      }
    }   

    cells=stay.concat(born)
  }
}

对于使用GLSL(通过递归QML ShaderEffect)的纯QML版本来计算GPU see here上的生命游戏规则。