QML笔记整理——在Qt/C++应用中使用QML

一、介绍
为了在C++中使用QML,用到了QtDeclarative中有三个主要的类: QDeclarativeEngine、QDeclarativeComponent和QDeclarativeContext。很多QML元素也有对应方法获取用C++创建好的元素实例,如:Item<->QDeclarativeItem、Scale<->QGraphicsScale、Blur<->QGraphicsBlurEffect。为了使用QtDeclarative,需要在工程文件中加入QT += declarative

二、QDeclarativeView
这是一个简单易用的显示类,继承自QGraphicsView,主要用于快速的建立应用原型
示例如下:
#include <QApplication>
#include <QtDeclarative/QDeclarativeView>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QDeclarativeView canvas;
    canvas.setSource(QUrl("main.qml"));
    canvas.show();
    return app.exec();
}

三、QDeclarativeEngine
若想要在Qt/C++中访问QML,必须有一个QDeclarativeEngine实例。其提供了在C++中初始化QML控件的环境,通过它配置全局的QML设置。如果要提供不同的QML设置,需要实例化多个QDeclarativeEngine

四、QDeclarativeComponent
它对应了一个QML文档的实例,用来加载QML文件。加载的内容可以是路径,也可以是QML代码(URL可以是本地的文件或者QNetworkAccessManager支持的协议访问的网络文件)。这个类还包含了QML文件的状态信息,如:Null、Ready、Loading、Error等。

五、实例——初始化组件
    // Create the engine(root context create automatically as well
    QDeclarativeEngine engine;
    
    // Create a QML component associated with the engine
    // (Alternatively you could create an empty component and the set
    // its contents with setData().)
    QDeclarativeComponent component(&engine, QUrl("main.qml"));
    
    // Instantiate the component (as no context is given to create(),
    // the root context is used by default
    QDeclarativeItem* item = qobject_cast<QDeclarativeItem*>(component.create());
    
    // Add item to a view, etc...

六、QDeclarativeContext
1、每个QML组件初始化都会对应一个QDeclarativeContext(engine会自动建立root context)。子context可以根据需要而创建,它与父类的集成关系是由QDeclarativeEngine管理维护的。QML组件实例的数据都应该加入到engine的root环境中,同时,QML子组件的数据也应该加入到子环境中(sub-context)
2、使用context可以把C++的数据和对象暴露给QML
示例如下:
    QQmlApplicationEngine engine;
    // engine.rootContext() returns a QDeclarativeContext*
    engine.rootContext()->setContextProperty("myBackgroundColor",
                                             QColor(Qt::lightsteelblue));
    QDeclarativeComponent component(&engine, "main.qml");
    QObject* window = component.create(); // Create using the root context
3、这种机制可以被用来为QML中的View提供C++端的Model
4、 前面提到过,context是具有继承关系的,控件初始化的时候,可以使用对应的context里的数据,也可以访问到祖先context的数据。对于重复定义的数据,子context中的定义将会覆盖context中的定义
示例如下:
    QDeclarativeEngine engine;
    QDeclarativeContext context1(engine.rootContext());
    QDeclarativeContext context2(&context1);
    QDeclarativeContext context3(&context2);
    context1.setContextProperty("a", 12);
    context2.setContextProperty("b", 13);
    context3.setContextProperty("a", 14);
    context3.setContextProperty("c", 14);
    // Instantiate QDeclarativeComponents using the sub-context
    component1.create(&context1); // a = 12
    component2.create(&context2); // a = 12, b = 13
    component3.create(&context3); // a = 14, b = 13, c = 14

七、结构化数据
1、如果有很多数据需要暴露给QML,可以使用默认对象(default object)代替。所有默认对象中定义的属性都可以在QML控件中通过名字访问到。通过这种方式暴露的数据,可以在QML端被修改。同时,使用默认对象的速度比多次调用setContextProperty()快。
2、多个默认对象可以加到同一个QML组件实例中,先添加的默认对象是不会被后面添加的对象所覆盖。于此不同的是,使用setContextProperty()设置的属性,会被新的属性覆盖。
3、Model数据通常都是由C++端代码动态提供的,而不是一个静态QML的数据Model。在delegate里面通过model属性可以访问到数据模型,这是一个默认的属性。
4、可以使用的C++端的数据模型有:
    QList<QObject*> —— model.modelData.xxx(xxx是属性)
    QAbstractDataModel —— model.display(decoration)
    QStringList —— model.modelData
示例如下:
// MyDataSet.h
class MyData : ... {
    ...
    // The NOTIFY signal informs about changes in the property's value
    Q_PROPERTY(QAbstractItemModel* myModel READ model NOTIFY modelChanged)
    Q_PROPERTY(QString text READ text NOTIFY textChanged)
    ...
};
// SomeOtherPieceOfCode.cpp exposes the QObject using e.g. a sub-context
QDeclarativeEngine engine;
QDeclarativeContext context(engine.rootContext());
context.addContextObject(new MyDataSet(...));
QDeclarativeComponent component(&engine, "ListView {model = myModel}");
component.create(&context);

八、QML调用C++方法
所有QObject对象的public的槽方法都可以在QML中调用,如果你不想你的方法是槽方法,可以使用Q_INVOKABLE(如:Q_INVOKABLE void myMethod();)
示例一:
// In C++:
class LEDBlinker : public QObject {
    Q_OBJECT
    // ...
public slots:
    bool isRunning();
    void start();
    void stop();
};
int main(int argc, char *argv[]){
    // ...
    QDeclarativeContext* context = engine->rootContext();
    context->setContextProperty("ledBlinker", new LEDBlinker);
    // ...
}
// In QML:
Rectangle {
    MouseArea {
        anchors.fill: parent
        onClicked: {
            if (ledBlinker.isRunning())
                ledBlinker.stop()
            else
                ledBlinker.start();
        }
    }
}
示例二:
需要注意的是,我们完全可以通过声明一个“running”属性来达到同样的效果,这样的话,代码会更加优雅,省略掉了isRunning()和setRunning()两个方法
// In C++:
class LEDBlinker : public QObject {
    Q_OBJECT
    Q_PROPERTY(bool running READ isRunning WRITE setRunning)
    // ...
};
// In QML:
Rectangle {
    MouseArea {
        anchors.fill: parent
        onClicked: ledBlinker.running = !ledBlinker.running
    }
}

九、在C++中调用QML方法
很明显,反过来在C++中调用QML的方法也是可以的。在QML中定义的方法在C++中都是一个槽函数,同时,在QML中定义的信号可以与C++中定义的槽函数连接。

十、网络组件
前面讨论过,QML组件可以通过网络加载,这种方式可能会花费些时间,因为网络总是有一定的延时。所以,在C++中初始化网络上的QML控件的时候,需要观察控件的加载状态,只有当状态为Ready后,才能调用created()创建控件。
示例如下:
MyObject::MyObject() {
    component = new QDeclarativeComponent(engine,
                QUrl("http://www.example.com/mail.qml"));
    // Check for status before creating the object - notice that this kind of
    // code could (should?) be used regardless of where the component is located!
    if(component->isLoading())
        connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status),
                     this, SLOT(continueLoading())));
    else
        continueLoading(); // Not a network-based resource, load straight away
}
// A slot that emits the States parameter of the signal and uses the isXxxx()
// functions instead to check the status - both approaches work the same way
void MyObject::continueLoading() {
    if(component->isError()){
        qWarning() << component->errors();
    } else if(component->isReady()) {
        QObject* myObject = component->create();
    } // The other status checks here ...
}

十一、QML Components in Resource File
在Qt工程中最方便的方法还是把QML组件添加到资源文件中,所有的JavaScript文件也可以被放在资源文件中。这样更加容易访问文件,放入资源文件后,我们无需知道文件的路径,只需要使用一个指向资源文件中的文件的URL就可以了。同时,资源文件可以编译到二进制程序中,这样资源文件就可以与二进制文件一起分发了,非常的方便。
示例如下:
// MyApp.qrc
<!DOCTYPE RCC>
<RCC version="1.0">
    <qresource> <file>qml/main.qml</file> </qresource>
</RCC>
// MyObject.cpp
MyObject::MyObject() {
    component = new QDeclarativeComponent(engine,
                    QUrl("qrc:/qml/main.qml"));
    if(!component->isError()){
        QObject* myObject = component->create();
    }
}
Image {
    source: "images/background.png"
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值