我正在转换一个用 Borland C++Builder 制作的项目。 在那里,我有一个类来管理来自电子设备的消息。该类(称之为MsgManager)封装了串口和tcpsocket,无论使用何种连接,只要有一些属性,都提供统一的方法和事件,并处理整个协议(protocol)检查。
我希望这个类是“全局的”:所以我的所有其他类都有一个指向 MsgManager 的指针,而且 QML 页面也需要使用他的方法和属性。 QML 页面唯一不会使用的是各种“事件”(例如数据接收事件或一些错误(例如超时等))
1) 对于 QT 应用程序来说,这是一个糟糕的设计吗?看来我的旧习惯在这个新环境中有时是错误的......
2)如果没有(或者至少没那么糟糕)我怎样才能获得这个?
我尝试使用
qmlRegisterType<cMessageManager>("Phase.MessageManager", 1, 0, "MessageManager");
在 main.cpp 中,然后
MessageManager {
id: msgman;
}
在 mainForm.qml 中,但我无法从 C++“指向”该类...并且当我在 C++ 中创建该类时,我无法从 QML 查看该类
谢谢。
-编辑- 添加代码并要求澄清:
@leemes: 感谢您的详细(但对菜鸟友好:))回答。
我还是有烦恼...... 这是我以前的工作代码(甚至在尝试添加我的新类之前):
#include <QtGui/QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QtQuick/QQuickView> // Necessario per QQuickWindow
#include "ui_updater.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<UI_Updater>("Phase.UI_Updater", 1, 0, "UI_Updater");
QQmlApplicationEngine engine(QUrl("qrc:/qml/MainForm.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
if ( !window ) {
qWarning("Error: Your root item has to be a Window.");
return -1;
}
window->show();
return app.exec();
}
我尝试了以下更改:
#include <QtGui/QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QtQuick/QQuickView> // Necessario per QQuickWindow
#include "ui_updater.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// --- CHANGES TO TRY setContextProperty SOLUTION -------------------------
//qmlRegisterType<UI_Updater>("Phase.UI_Updater", 1, 0, "UI_Updater");
//QQmlApplicationEngine engine(QUrl("qrc:/qml/MainForm.qml"));
UI_Updater* ui_up;
ui_up = new UI_Updater();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("UI_Updater",ui_up);
engine.setBaseUrl(QUrl("qrc:/qml/MainForm.qml"));
//-------------------------------------------------------------------------
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
if ( !window ) {
qWarning("Error: Your root item has to be a Window.");
return -1;
}
window->show();
return app.exec();
}
但是调试器在反汇编器窗口中的某个位置中断并出现“段错误”错误。 我发现我没有您提到的 QQuickView (或 QtQuickApplicationViewer)对象,而且我不记得为什么我最终得到了现在的代码(但这是我发现使它工作的唯一方法:( )
那么,你能再帮我一点吗?
最佳答案
首先,您想要导出到 QML 世界的类型需要可以通过 Qt 元对象系统访问,以便在 QML 中调用它的函数。这需要类型继承 QObject
以及作为槽的函数(或者正确的“可调用方法”)。您还可以在该类中添加“属性”(以在表达式中使用对象的属性),为此请参阅 Q_PROPERTY macro .
在 C++ 中创建实例并将其地址导出到 QML 是赋予 QML 文档“全局”调用 C++ 方法的能力的一种非常常见的方法。在很多用例中您都希望做到这一点,因此您走在正确的道路上。
如果您想将类型从C++导出到QML并在那里实例化,您可以通过类型注册来完成此操作,就像在代码片段中一样。但这并不是您真正想要的,因为您想在 C++ 中创建实例并且只提供指向 QML 的指针。
如果您想将某种类型的实例从 C++ 导出到 QML,可以使用上下文属性来实现:
MsgManager *instance = ...;
view->rootContext()->setContextProperty("msgman", instance);
哪里view
是你的QQuickView
(或QtQuickApplicationViewer
)。请注意,必须在设置 QML 文件名之前设置此上下文属性,以便从一开始就可用。
另请参阅:Embedding C++ Objects into QML with Context Properties
如果您不使用QQuickView
但想实例化一个Window
(或 ApplicationWindow
)在 QML 中,您通常使用 QQmlEngine
直接地。设置根上下文属性后加载 QML 文件的代码可能如下所示:
// QML engine:
QQmlEngine qml;
qml.rootContext()->setContextProperty("msgman", instance); // <-- property
// Load main window (in my example qml/main.qml):
QQmlComponent windowComponent(&qml, QUrl::fromLocalFile("qml/main.qml"));
if (windowComponent.isError()) {
for (auto error : windowComponent.errors())
qDebug() << error;
return 1; // exit the main function immediately
}
// Some error checking and then casting the object to a window
QObject *object = windowComponent.create();
if (!object)
qFatal("Failed to create an instance of main.qml!");
QQuickWindow *window = qobject_cast<QQuickWindow*>(object);
if (!window)
qFatal("Top-level item in main.qml is not derived from QQuickWindow!");
// Finally show the window
window->show();
请注意,除了访问该实例之外,QML 中从未提及该实例(您的 QML 文件中没有诸如 MessageManager { id: msgman; }
之类的代码)。只需使用名称 msgman
就好像有这样的代码。此外,该实例将在您包含的所有子文档(也称为“组件”)中可用。
示例:您希望为您的经理显示状态。它应该显示一些您在类中提供的“就绪”属性,报告设备是否已准备好进行通信。为此,您已经使用 Q_PROPERTY 宏声明了该属性,现在想要在 QML 中访问它:
MsgManagerStatus.qml:
import QtQuick 2.0
Text {
text: msgman.ready ? "Device is ready." : "Device is not ready."
color: msgman.ready ? "black" : "red"
}
最后要提的是:如果您的 QML 文档实例化自定义 C++ 类型(自定义可视项、C++ 模型、辅助类等),您将无法在构建这些类期间访问这些上下文属性,就像您可以使用QML 组件。只有在完成构造之后,该类才能访问它所在的 QML 上下文。有几种解决方法,但没有一种是完美的。但我想这个话题超出了你的提问范围。请记住,QML 中的自定义 C++ 类型的实例只有在完成构造后才能访问 QML 上下文属性。
关于c++ - QTQuick设计: how to have a global class available from both C++ and QML?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23057736/