我目前正在尝试用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++。
关于这个主题有两个很好的指南,可以更详细地解释这一点,并附带源代码示例:
- QML体系结构提示以及为什么/如何在Qt应用程序中避免C++
- QML体系结构最佳实践和示例