C++/QML体系结构-一种在QML中重现C++结构的方法



我目前正在尝试用Qt 5.2和Qt Quick 2开发一个非常重要的应用程序(类似操作系统(;我想做的是用C++实现所有的逻辑,UI是通过QML声明的。在这一点上,这似乎是合乎逻辑的,也是绕过的方法。然而,我想不出该怎么干净地做。。我读了很多文档、教程和示例,但没有什么大的…

让我们来解释一下我想把什么作为一个体系结构;假设我们有一个Scene对象,它可能包含未定义数量的Application对象。我想在CPP中定义逻辑(如何从XML加载应用程序,场景应该具有什么属性,…(,然后用QML显示场景。此外,我们必须注意场景和应用程序元素应该重新用作组件;因此,这里有一个基本的想法:我想定义图形样式,这些样式对于QML中的一个文件(扩展CPP类型(中的每个对象都是通用的。

例如,我可以创建一个包含以下内容的文件:

Application {
    Rectangle { ... }
}

表示应用程序应表示为矩形;然后,当我创建一个有应用程序列表的场景对象(或者一个唯一的应用程序(时,我希望它能自动显示(因为这是场景对象的属性(。这可能吗?我该怎么做?

我想,如果我扩展C++对象并为它声明一些图形元素,它将是自动的。。但实际上并不是那样的!

也许还有别的办法?

感谢

我不太喜欢这个问题,因为它实际上并没有问什么特别的问题。Qt文档非常全面,所以当人们说他们已经阅读了文档、教程和示例,但仍然没有找到他们想要的东西时,我经常觉得很奇怪。然而,我想我理解你所问的要点,并认为答案可能对一些人有用,所以我会尽力回答。

main.cpp

#include <QtGui/QGuiApplication>
#include <QtQml>
#include <QQuickItem>
#include "qtquick2applicationviewer.h"
class ApplicationItem : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QString title MEMBER mTitle NOTIFY titleChanged)
public:
    ApplicationItem(QQuickItem *parent = 0) : QQuickItem(parent) {
    }
public slots:
    void close() {
        emit closed(this);
    }
signals:
    void titleChanged(QString title);
    void closed(ApplicationItem *app);
private:
    QString mTitle;
};
class SceneItem : public QQuickItem
{
    Q_OBJECT
public:
    SceneItem() {
    }
public slots:
    void startApp(const QString &qmlFile) {
        QQmlComponent *component = new QQmlComponent(qmlEngine(this), QUrl(qmlFile));
        if (component->isLoading()) {
            QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
                this, SLOT(componentStatusChanged()));
        } else {
            // The component was synchronously loaded, but it may have errors.
            if (component->isError()) {
                qWarning() << "Failed to start application:" << component->errorString();
            } else {
                addApp(component);
            }
        }
    }
    void componentStatusChanged(QQmlComponent::Status status) {
        QQmlComponent *component = qobject_cast<QQmlComponent*>(sender());
        if (status == QQmlComponent::Ready) {
            addApp(component);
        } else if (status == QQmlComponent::Error) {
            qWarning() << "Failed to start application:" << component->errorString();
        }
    }
    void appClosed(ApplicationItem *app) {
        int appIndex = mApplications.indexOf(app);
        if (appIndex != -1) {
            mApplications.removeAt(appIndex);
            app->deleteLater();
        }
    }
private:
    void addApp(QQmlComponent *component) {
        ApplicationItem *appItem = qobject_cast<ApplicationItem*>(component->create());
        appItem->setParentItem(this);
        connect(appItem, SIGNAL(closed(ApplicationItem*)), this, SLOT(appClosed(ApplicationItem*)));
        mApplications.append(appItem);
        delete component;
    }
    QList<ApplicationItem*> mApplications;
};
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QtQuick2ApplicationViewer viewer;
    qmlRegisterType<ApplicationItem>("Test", 1, 0, "ApplicationItem");
    qmlRegisterType<SceneItem>("Test", 1, 0, "SceneItem");
    viewer.setMainQmlFile(QStringLiteral("qml/quick/main.qml"));
    viewer.showExpanded();
    return app.exec();
}
#include "main.moc"

我将这两个类表示为QQuickItem子类。SceneItem由许多ApplicationItem实例组成,这些实例通过调用startApp()添加到场景中。此槽以QML文件的路径作为其参数。该文件可以通过网络加载,也可以从本地文件加载,因此我们考虑了同步和异步加载的可能性。

QML文件应该描述应用程序的视觉外观,并且场景希望其根类型为ApplicationItem。例如,MySweetApp.qml:

import QtQuick 2.0
import QtQuick.Controls 1.0
import Test 1.0
ApplicationItem {
    id: someAppStyle
    title: "My Sweet App"
    width: 100
    height: 100
    MouseArea {
        anchors.fill: parent
        drag.target: parent
    }
    Rectangle {
        radius: 4
        color: "lightblue"
        anchors.fill: parent
        Text {
            anchors.left: parent.left
            anchors.right: closeButton.right
            anchors.leftMargin: 4
            anchors.top: parent.top
            anchors.topMargin: 4
            text: someAppStyle.title
        }
        Button {
            id: closeButton
            anchors.right: parent.right
            anchors.rightMargin: 4
            anchors.top: parent.top
            anchors.topMargin: 2
            onClicked: close()
            text: "x"
            width: 20
            height: width
        }
    }
}

应用程序可以通过调用ApplicationItem中声明的close()插槽来关闭自己。

这是main.qml

import QtQuick 2.0
import QtQuick.Controls 1.0
import Test 1.0
SceneItem {
    id: scene
    width: 360
    height: 360
    Button {
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.bottom: parent.bottom
        text: "Launch app"
        onClicked: scene.startApp("qml/quick/MySweetApp.qml")
    }
}

这就是声明SceneItem的地方,还有一个简单的接口来启动My Sweet应用程序的几个实例(这是一个非常有用的应用程序(。

我相信这是做你要求的事情最合适的方式。它避免了在C++中设置暴露于QML的ApplicationItems列表的麻烦(实际上没有那么难,但这是文档可能更明显的一个领域(,并允许操作系统的用户自由地查看应用程序的外观。如果你想更严格地设计风格,我建议你研究一下Qt Quick Controls是如何设计风格的。

我建议不要在逻辑中使用C++,除非你真的需要-如果你有高性能的需求,比如需要每秒处理10倍的实时数据,那么就使用casese来使用C++。

由于大多数用例都没有这一要求,因此最好也将QML用于应用程序逻辑,因为与C++相比,它将节省高达90%的源代码(和时间(。尤其是在开发初期,您可以更快地在QML中对逻辑进行编码,并更快地获得结果。如果需要的话,您以后仍然可以将逻辑移到C++。

关于这个主题有两个很好的指南,可以更详细地解释这一点,并附带源代码示例:

  1. QML体系结构提示以及为什么/如何在Qt应用程序中避免C++
  2. QML体系结构最佳实践和示例

相关内容

  • 没有找到相关文章

最新更新