QtQuick,动态图像和c++



我是Qt的新手,从我在qt-project.org和其他地方读到的东西;QtQuick似乎是一个有吸引力的选择,因为它能够在指针和触摸设备上工作。我的问题是让它在c++中很好地工作。

我决定写一个康威的生活游戏的变体,作为"你好,世界"之后的下一步。我完全困惑于如何将"board"(一个[height][width][bytes-per-pixel] 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*图像链接到a ??在QML中,使QML可以开始/停止"生命"循环,使"生命"循环和更新字符数组,并通知QML重新绘制它?


注意:我已经看了子类QQuickImageProvider基于这里的信息。这种方法的问题是,我不知道如何让c++"驱动"屏幕上的图像。我希望将控制从QML传递到c++,并让c++告诉QML何时使用更改的图像更新显示。这种方法有解决方案吗?或者完全是另一种方法

第一种方法是在QML中为每个游戏像素创建一个矩形,这对于8x8的棋盘来说可能很有趣,但对于100x100的棋盘来说就不一样了,因为你需要为每个像素手动编写QML代码。

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

像这样从QML中调用图像:

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

现在gameoflife是映像提供程序的名称,board是您稍后可以使用的所谓的id

Register gameoflife in you main.cpp

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

其中engine是你的主QQmlApplicationEngine, life是你的Life游戏引擎的实例。

LifeImageProvider是创建pixeldata的类。以

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

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

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
    }
}

只是为了好玩,并且冒着因为一个完全无关的答案而被拒绝的风险,这里是一个完全用QML实现的GameOfLife,只需将其放入。QML文件中并使用qmlscene运行它。在Qt 5.3.0上工作,并且在旧的Core 2 Duo lappy上运行速度惊人(对我来说)。我敢肯定,它永远不会像基于c++的QQuickImageProvider解决方案那样快速/高效,但它确实表明,在不诉诸c++的情况下,在QML中可以做很多事情。

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)计算GPU上的生命游戏规则的纯QML版本,请参阅此处。

相关内容

  • 没有找到相关文章

最新更新