QML负责GUI,C++负责业务逻辑的范例

在declarative目录中,有个minehunt范例,实现了在C++中加载QML界面,并用C++来处理QML界面上的鼠标动作.这种思路和传统的GUI相似,感觉比较顺畅.否则运行一个QML,还要使用qmlviewer,上面带一大堆菜单按钮,看着够别扭的.

在main函数中,创建了一个QDeclarativeView实例,这个实例负责显示QML界面.接着创建负责处理业务逻辑的MinehuntGame实例,并在view加载QML文件后,将其设置为引擎的上下文对象.这样就可以直接在QML中使用MinehuntGame类中的属性和方法了.感觉设置上下文后,将上下文类实例与QML界面做了融合,QML中的鼠标点击等事件就可以调用类中方法进行处理,并可以绑定到实例的属性.

#include <QtGui/QApplication>
#include <QtDeclarative/QDeclarativeView>
#include <QtDeclarative/QDeclarativeContext>
#include <QtDeclarative/QDeclarativeEngine>

#include "minehunt.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QDeclarativeView canvas;
    qmlRegisterType<TileData>();//注册TileData类,这个类不需要在QML中实例化.
    MinehuntGame* game = new MinehuntGame();
    canvas.engine()->rootContext()->setContextObject(game);        
    canvas.setSource(QString("qrc:minehunt.qml"));
    QObject::connect(canvas.engine(), SIGNAL(quit()), &app, SLOT(quit()));  //QML退出,应用程序也退出
    canvas.setGeometry(QRect(100, 100, 450, 450));
    canvas.show();
    return app.exec();
}

在程序中定义了代表扫雷界面中每个方块的TileData类,用来记录这个方块的状态,如做标记的,被点开的,或有雷的.那么类的实例是如何与QML进行关联同步的呢?在MinehuntGame中定义了一个属性QDeclarativeListProperty<TileData> tiles(),与_tiles成员绑定,并在类构造时向tiles中填充多个TileData实例.在QML中可以看到如下声明:

    Grid {
        anchors.horizontalCenter: parent.horizontalCenter
        columns: 9; spacing: 1

        Repeater {
            id: repeater
            model: tiles
            delegate: Tile {}
        }
    }

这样就按MinehuntGame实例中的tiles属性进行构造Grid中的元素了,每个TileData实例生成一个Tile组件.在操作Tile组件时又会反过来调用MinehuntGame中的方法.先看看那个表情图标,点击时会重置界面.

    Image {
        anchors.bottom: field.bottom; anchors.bottomMargin: 15
        anchors.right: field.right; anchors.rightMargin: 20
        source: isPlaying ? 'MinehuntCore/pics/face-smile.png' ://isPlaying,hasWon是MinehuntGame实例中的属性,这里做了绑定,如果实例中的属性发生变化,则图标自动变化
        hasWon ? 'MinehuntCore/pics/face-smile-big.png': 'MinehuntCore/pics/face-sad.png'

        MouseArea { anchors.fill: parent; onPressed: reset() }//点击的时候调用MinehuntGame实例的reset方法,重置雷区,上面也有个关于雷区的绑定,重置后界面自动更新
    }

在看看点击雷区的一个方块时的动作触发过程.这里有两个成员对象,index和modelData,都是和模型绑定有关的,index表示当前选中的模型元素索引,modelData表示当前选中子模型对应的元素.在QML中可通过modelData访问TileData元素属性.下面的声明中指定,鼠标左键点击调用MinehuntGame的flip方法,右击调用flag方法.在这两个C++函数中判断是否触雷等逻辑,并修改类实例的状态,通过绑定,界面元素自动更新.

    MouseArea {
        anchors.fill: parent
        acceptedButtons: Qt.LeftButton | Qt.RightButton
        onClicked: {
            field.clickx = flipable.x//鼠标点击时设置主界面的clickx,clicky属性
            field.clicky = flipable.y
            var row = Math.floor(index / 9)
            var col = index - (Math.floor(index / 9) * 9)
            if (mouse.button == undefined || mouse.button == Qt.RightButton) {
                flag(row, col)
            } else {
                flip(row, col)
            }
        }
        onPressAndHold: {
            field.clickx = flipable.x
            field.clicky = flipable.y
            var row = Math.floor(index / 9)
            var col = index - (Math.floor(index / 9) * 9)
            flag(row, col)
        }
    }

那么触雷后的例子系统是如何声明的呢?

    transitions: Transition {
        SequentialAnimation {
            ScriptAction {
                script: {
                    var ret = Math.abs(flipable.x - field.clickx)
                        + Math.abs(flipable.y - field.clicky);
                    if (modelData.hasMine && modelData.flipped)
                        pauseDur = ret * 3
                    else
                        pauseDur = ret
                }
            }
            PauseAnimation {
                duration: pauseDur
            }
            RotationAnimation { easing.type: Easing.InOutQuad }
            ScriptAction { script: if (modelData.hasMine && modelData.flipped) { expl.explode = true } }//这里指定如果modelData即TileData数据对象属性hasMine或flipped为true,则触发例子效果.
        }
    }

expl的定义:Explosion { id: expl }.

Explosion的定义:

import QtQuick 1.0
import Qt.labs.particles 1.0

Item {
    property bool explode : false

    Particles {//定义了一个粒子对象
        id: particles
        width: 40
        height: 40
        lifeSpan: 1000
        lifeSpanDeviation: 0
        source: "pics/star.png"//粒子图像
        count: 0
        angle: 270
        angleDeviation: 360
        velocity: 100
        velocityDeviation: 20
        z: 100
    }
    states: State { name: "exploding"; when: explode
        StateChangeScript {script: particles.burst(200); }
    }

}

总结:要向将QML和C++结合起来,实现传统的开发方式,核心的代码就是canvas.engine()->rootContext()->setContextObject(game);这样就可以在QML中调用game实例的属性和函数了.

**************测试如何在C++中获取TextInput元素中输入的内容**************

根据上面的范例分析可以在QML中访问C++中定义的函数或属性,那么C++中怎么获取到QML中的界面内容呢?网上说有三种方法,一种是在C++的插件获取,一种是在C++中解析QML中的元素,最后一种是在C++中定义带参数的槽函数,在QML中的事件中调用这个槽,并将TextInput中的内容作为参数传到C++中.感觉最后一种方法最方便了,因此做了如下测试:

定义QML文件:

import QtQuick 1.0

Rectangle{
    id: window
    width: 240; height: 250
    BorderImage{
        y:0
        id:button
        source:"pics/back.png"
        MouseArea{
            id:mouseArea
            anchors.fill:parent
            onClicked:buttonClicked(textInput.text)//按钮点击的时候调用C++中的buttonClicked槽函数
        }
        Rectangle{
            id:shade
            anchors.fill:button; radius: 10; color: "black"; opacity: 0
        }
        states:State{
            name:"pressed";when: mouseArea.pressed == true
            PropertyChanges{ target: shade; opacity: 0.4}
        }
    }
    BorderImage
    {
        source: "pics/back.png";width:82;height:22;y:50
        TextInput{
            id:textInput
            text: qsTr("text")
            y:1;x:1;width:80;height:20;
        }
    }
}

C++类:

头文件

#ifndef QMLCONTROL_H
#define QMLCONTROL_H

#include <QObject>

class QmlControl : public QObject
{
 Q_OBJECT//必须加上Q_OBJECT宏,否则在QML中无法触发buttonClicked函数
public:
 QmlControl(void);
 ~QmlControl(void);
public slots:
 Q_INVOKABLE void buttonClicked(QString textInput);
};
#endif

cpp文件

#include "QmlControl.h"
#include <QMessageBox>

QmlControl::QmlControl(void)
{
 setObjectName("mainObject");
}

QmlControl::~QmlControl(void)
{
}

void QmlControl::buttonClicked(QString textInput)
{
 QMessageBox::information(NULL, "", textInput);
}

界面上放一个QDeclarativeView控件,并在构造函数中加载QML,设置引擎中的对象:

GetQMLInputTextValue::GetQMLInputTextValue(QWidget *parent, Qt::WFlags flags)
 : QMainWindow(parent, flags)
{
 ui.setupUi(this);
 qmlRegisterType<QmlControl>();
 QmlControl *ctrl = new QmlControl();
 ui.declarativeView->engine()->rootContext()->setContextObject(ctrl);
 ui.declarativeView->setSource(QString("qrc:/Resources/test.qml"));
 connect(ui.declarativeView->engine(),SIGNAL(quit()), qApp, SLOT(quit()));
}

注意我将QML和其中的图片放入资源中,这里引用QML的时候需要以qrc开头,否则访问不到图片文件.

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值