我是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版本,请参阅此处。