c++ - QTQuick设计: how to have a global class available from both C++ and QML?

标签 c++ qt class global

我正在转换一个用 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/

相关文章:

qt - 如何在 QML 中按下回车键时触发按钮单击

c++ - 为什么在所有地方都使用套接字指针而不是套接字实例?

c++ - 如何将 "extern template"与由同一类中的模板化成员使用的嵌套类一起使用?

QTextCodec 子类 - 如何注册我的编解码器

c++ - 在qt中创建线程时出错

flutter - 创建可重用小部件的函数和类之间有什么区别?

javascript - .hover 不适用于我动态分配类的元素

c++ - 数据结构优化

c++ - 关于指向指针数组的指针

python - 在Python中使用@property构建一个类