C++ 中的 Qt Quick / QML 标志更改回 UI
Qt Quick / QML flag changes in C++ back to UI
我很久以前就熟悉 Qt,但我可以使用一些 QML/QTQuick 与 C++ 交互的指导。我觉得我在这里遗漏了一些简单的东西,但有点卡住了。
我正在开发一个嵌入式系统来显示在串行总线上通信的分布式交换机的状态。串行总线在 C++(一直是 Qt)中作为单独的线程运行,并以循环方式自动轮询设备以从设备获取更新。
对此,我发现了一个非常简单的示例,该示例使用 QAbstractList 模型通过支持的 C++ 中的 QML 属性共享状态。 https://www.youtube.com/watch?v=9BcAYDlpuT8&list=PLo12gBvZwC78nnrZHCBowKf36ZAi7iOLW&index=4&t=0s
最初模型看起来很棒,我只是加载了一个包含我需要的信息的列表,并且可以从 UI 中看到它。问题是,当更新在后台发生变化时,我如何从 C++ 访问模型以使更新冒泡到 UI。
到目前为止我做了什么:
注册模型:
qmlReisterType<ModelDerrivedClass>("DeviceListModel",1,0,"DeviceList")
定义角色:
enum {
OpenRole = Qt::UserRole,
StatusRole
}
为模型
定义哈希table
QHash<int,QByteArray> ModelDerrivedClass::roleNames() const
{
QHash<int, QByteArray> names;
names[OpenRole] = "openstatus";
names[StatusRole] = "devicestatus";
return names;
}
使用适当的信息创建简单的结构列表,然后实施必要的方法,等等......自上而下的工作就像一个魅力。
如何自下而上访问模型?这些设备将根据 UI 不需要知道的外部输入更新状态,但这些事件需要能够驱动前端。看起来几乎没有考虑到这种情况。
我也研究过使用 QML 注册设备类型,但无法弄清楚如何将 QML 对象 link 转换为 C++ 对象,以便 READ/WRITE/NOTIFY 属性适用于个人列表中的 QML 对象。在这种情况下,我会将 OPEN 和 STATUS 注册为可直接在 QML 代码中使用的 QML 类型的属性,但我需要将对象实例 int C++ 与 QML 对象实例相关联。这是 QAbstractListModel 适合解决的问题。
如有任何帮助,我们将不胜感激。
你有两个选择,但我认为第一个更好:
选项 1:rootContext
您可以在 QQMlEngine
的 rootContext 上设置 属性,它将在该 qml 引擎加载的每个 QML 文件中可用(参见 Qt docs):
QQuickView view;
view.rootContext()->setContextProperty("currentDateTime",
QDateTime::currentDateTime());
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
您应该有一个模型实例:
ModelDerivedClass devices;
QQuickView view;
view.rootContext()->setContextProperty("devices", &devices);
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
您现在可以使用 setData
或您自己的方法对 ModelDerivedClass
进行更改。
顺便说一句,在此选项中,您不应注册为类型,而应注册为不可创建的类型:
qmlRegisterUncreatableType<ModelDerrivedClass>("DeviceListModel",1,0,"DeviceList", "available as rootContext property 'devices'");
方案二:单例
您可以将 ModelDerivedClass
设为单例,单例只能使用一次。将 instance
函数添加到 class:
class ModelDerivedClass
{
...
ModelDerivedClass* instance() {
static ModelDerivedClass theInstance;
return &theInstance;
}
...
}
在 C++ 中,您可以使用 ModelDerivedClass::instance()
来操作更改。
您还应该将其作为单例注册到 QML:
qmlRegisterSingletonType<ModelDerivedClass>("DeviceListModel", 1, 0, "DeviceList",
[](QQmlEngine *eng, QJSEngine *js) -> QObject*
{
return ModelDerivedClass::instance();
});
但在这种情况下,您对对象的生命周期没有太多控制,这可以说是不好的(可以找到很多关于这个主题的讨论)。所以我建议选择选项 1。
要认识到的主要事情是,您的主线程是一个特殊线程,它必须是访问和更改有关 GUI 状态的任何内容的唯一线程。大多数关于 Qt GUI 和 QML 的东西都是单线程的。
因此,您需要将更改从串行总线线程传递到主线程。这可以通过将 slot/signal 从串行总线线程连接到主线程上的适当对象来完成。
Qt 知道您何时使用这样的连接跨越了线程边界,并将post它以某种方式到达主线程,以确保它以单线程方式得到处理。
我很久以前就熟悉 Qt,但我可以使用一些 QML/QTQuick 与 C++ 交互的指导。我觉得我在这里遗漏了一些简单的东西,但有点卡住了。
我正在开发一个嵌入式系统来显示在串行总线上通信的分布式交换机的状态。串行总线在 C++(一直是 Qt)中作为单独的线程运行,并以循环方式自动轮询设备以从设备获取更新。
对此,我发现了一个非常简单的示例,该示例使用 QAbstractList 模型通过支持的 C++ 中的 QML 属性共享状态。 https://www.youtube.com/watch?v=9BcAYDlpuT8&list=PLo12gBvZwC78nnrZHCBowKf36ZAi7iOLW&index=4&t=0s
最初模型看起来很棒,我只是加载了一个包含我需要的信息的列表,并且可以从 UI 中看到它。问题是,当更新在后台发生变化时,我如何从 C++ 访问模型以使更新冒泡到 UI。
到目前为止我做了什么:
注册模型:
qmlReisterType<ModelDerrivedClass>("DeviceListModel",1,0,"DeviceList")
定义角色:
enum {
OpenRole = Qt::UserRole,
StatusRole
}
为模型
定义哈希tableQHash<int,QByteArray> ModelDerrivedClass::roleNames() const
{
QHash<int, QByteArray> names;
names[OpenRole] = "openstatus";
names[StatusRole] = "devicestatus";
return names;
}
使用适当的信息创建简单的结构列表,然后实施必要的方法,等等......自上而下的工作就像一个魅力。
如何自下而上访问模型?这些设备将根据 UI 不需要知道的外部输入更新状态,但这些事件需要能够驱动前端。看起来几乎没有考虑到这种情况。
我也研究过使用 QML 注册设备类型,但无法弄清楚如何将 QML 对象 link 转换为 C++ 对象,以便 READ/WRITE/NOTIFY 属性适用于个人列表中的 QML 对象。在这种情况下,我会将 OPEN 和 STATUS 注册为可直接在 QML 代码中使用的 QML 类型的属性,但我需要将对象实例 int C++ 与 QML 对象实例相关联。这是 QAbstractListModel 适合解决的问题。
如有任何帮助,我们将不胜感激。
你有两个选择,但我认为第一个更好:
选项 1:rootContext
您可以在 QQMlEngine
的 rootContext 上设置 属性,它将在该 qml 引擎加载的每个 QML 文件中可用(参见 Qt docs):
QQuickView view;
view.rootContext()->setContextProperty("currentDateTime",
QDateTime::currentDateTime());
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
您应该有一个模型实例:
ModelDerivedClass devices;
QQuickView view;
view.rootContext()->setContextProperty("devices", &devices);
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
您现在可以使用 setData
或您自己的方法对 ModelDerivedClass
进行更改。
顺便说一句,在此选项中,您不应注册为类型,而应注册为不可创建的类型:
qmlRegisterUncreatableType<ModelDerrivedClass>("DeviceListModel",1,0,"DeviceList", "available as rootContext property 'devices'");
方案二:单例
您可以将 ModelDerivedClass
设为单例,单例只能使用一次。将 instance
函数添加到 class:
class ModelDerivedClass
{
...
ModelDerivedClass* instance() {
static ModelDerivedClass theInstance;
return &theInstance;
}
...
}
在 C++ 中,您可以使用 ModelDerivedClass::instance()
来操作更改。
您还应该将其作为单例注册到 QML:
qmlRegisterSingletonType<ModelDerivedClass>("DeviceListModel", 1, 0, "DeviceList",
[](QQmlEngine *eng, QJSEngine *js) -> QObject*
{
return ModelDerivedClass::instance();
});
但在这种情况下,您对对象的生命周期没有太多控制,这可以说是不好的(可以找到很多关于这个主题的讨论)。所以我建议选择选项 1。
要认识到的主要事情是,您的主线程是一个特殊线程,它必须是访问和更改有关 GUI 状态的任何内容的唯一线程。大多数关于 Qt GUI 和 QML 的东西都是单线程的。
因此,您需要将更改从串行总线线程传递到主线程。这可以通过将 slot/signal 从串行总线线程连接到主线程上的适当对象来完成。
Qt 知道您何时使用这样的连接跨越了线程边界,并将post它以某种方式到达主线程,以确保它以单线程方式得到处理。