Qt Qml 连接到 Context 属性 的 QObject 属性 的信号
Qt Qml connect to a signal of a QObject property of a Context Property
所以这看起来像是一个奇怪的设置。为了简单起见,我有一个继承自 QObject 的 C++ 对象,称为 "MasterGuiLogic"。它是用指向另一个名为 "MainEventBroker" 的对象的指针创建的,您可能猜到它处理我所有的应用程序事件。 MasterGuiLogic 对象作为上下文 属性 在 qml 中注册,因此它的属性可以在我的 qml 中的任何地方使用。所以 main.cpp 看起来像这样:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
MasterEventBroker *MainEventBroker = new MasterEventBroker();
MasterGuiLogic *MainGuiLogic = new MasterGuiLogic(*MainEventBroker);
qmlRegisterUncreatableType<MasterGuiLogic>("GrblCom", 1, 0, "MasterGuiLogic", "");
qmlRegisterUncreatableType<GuiLogic_SerialCom>("GrblCom", 1, 0, "GuiLogic_SerialCom", "");
QQmlApplicationEngine engine;
QQmlContext* context = engine.rootContext();
context->setContextProperty("MasterGuiLogic", &(*MainGuiLogic));
engine.load(QUrl(QLatin1String("qrc:/QmlGui/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
MasterGuiLogic 创建另一个名为 SerialCom 的 class 的实例,它被设置为 Q_PROPERTY 以便它的属性和 public 插槽可以通过 MasterGuiLogic 属性.
MasterGuiLogic.h:
class MasterGuiLogic : public QObject
{
Q_OBJECT
Q_PROPERTY(GuiLogic_SerialCom* serialCom READ serialCom CONSTANT)
public:
MasterEventBroker *eventBroker;
explicit MasterGuiLogic(MasterEventBroker &ev, QObject *parent = nullptr);
GuiLogic_SerialCom* serialCom() const {
return Gui_SerialCom;
}
private:
GuiLogic_SerialCom *Gui_SerialCom;
MasterGuiLogic.cpp:
MasterGuiLogic::MasterGuiLogic(MasterEventBroker &ev, QObject *parent) : QObject(parent)
{
this->eventBroker = &ev;
this->Gui_SerialCom = new GuiLogic_SerialCom(this);
}
SerialCom.h:
//Forward Declare our parent
class MasterGuiLogic;
class GuiLogic_SerialCom : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList portNames READ portNames NOTIFY portNamesChanged)
Q_PROPERTY(bool connectedToPort READ connectedToPort NOTIFY connectedToPortChanged)
public:
MasterGuiLogic *parent;
explicit GuiLogic_SerialCom(MasterGuiLogic *parent = nullptr);
std::map<QString, QSerialPortInfo> portsMap;
QStringList portNames() {
return _portNames;
}
bool connectedToPort() {
return _connectedToPort;
}
private:
QStringList _portNames;
bool _connectedToPort = false;
signals:
void portNamesChanged(const QStringList &);
void connectedToPortChanged(const bool &);
public slots:
void connectToPort(const QString portName);
void disconnectFromPort(const QString portName);
};
SerialCom.cpp:
GuiLogic_SerialCom::GuiLogic_SerialCom(MasterGuiLogic *parent) : QObject(qobject_cast<QObject *>(parent))
{
this->parent = parent;
QList<QSerialPortInfo> allPorts = QSerialPortInfo::availablePorts();
for (int i = 0; i < allPorts.size(); ++i) {
this->_portNames.append(allPorts.at(i).portName());
this->portsMap[allPorts.at(i).portName()] = allPorts.at(i);
}
emit portNamesChanged(_portNames);
}
void GuiLogic_SerialCom::connectToPort(const QString portName) {
//TODO: Connect To Port Logic Here;
//Set Connected
this->_connectedToPort = true;
emit connectedToPortChanged(this->_connectedToPort);
qDebug() << portName;
}
void GuiLogic_SerialCom::disconnectFromPort(const QString portName) {
//TODO: DisConnect To Port Logic Here;
//Set DisConnected
this->_connectedToPort = false;
emit connectedToPortChanged(this->_connectedToPort);
qDebug() << portName;
}
所以从 qml 中读取这些属性中的任何一个都非常容易,甚至可以将信号从 qml 发送到 c++
例如,这很好用:
connectCom.onClicked: {
if (MasterGuiLogic.serialCom.connectedToPort === false) {
MasterGuiLogic.serialCom.connectToPort(comPort.currentText);
} else {
MasterGuiLogic.serialCom.disconnectFromPort(comPort.currentText);
}
}
问题是,我似乎无法找到连接到从 SerialCom 发出的信号的方法。我以为我可以做这样的事情:
Connections: {
target: MasterGuiLogic.serialCom;
onConnectedToPortChanged: {
if (MasterGuiLogic.serialCom.connectedToPort === false) {
connectCom.text = "Disconnect";
comPort.enabled = false;
} else {
connectCom.text = "Connect";
comPort.enabled = true;
}
}
}
这应该听 SerialCom 上的布尔值 属性 来改变,但是我得到以下错误:
QQmlApplicationEngine failed to load component
qrc:/QmlGui/main.qml:21 Type Page1 unavailable
qrc:/QmlGui/Page1.qml:49 Invalid attached object assignment
这只是意味着我无法 "connect" 使用上面的目标行。有没有其他方法可以连接到来自 ContextProperty 中 QObject 类型的 Q_PROPERTY 的信号?
首先&(*MainGuiLogic)
是什么意思?
您正在取消引用并再次引用 MainGuiLogic
?为什么?
context->setContextProperty("MasterGuiLogic", MainGuiLogic);
就够了。
但是将 MasterGuiLogic
注册为 Type 并添加名为 MasterGuiLogic
的 Object 可以在 QML 世界中覆盖自己.
将其设置为 context->setContextProperty("MyGuiLogic", MainGuiLogic);
以消除此行为。
也不要在 C++ 和 QML 世界之间传递引用,例如:
void connectedToPortChanged(**const bool &**);
。
只需使用原子类型和值(const bool)
;
并给它一个名字,以便能够在 QML 中将其用作 命名值 :
void connectedToPortChanged(bool connected)
这是一个与您的结构相似的示例,它可以正常工作。只需单击 window 并查看输出控制台。
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
class GuiLogic_SerialCom : public QObject
{
Q_OBJECT
public:
GuiLogic_SerialCom(){}
signals:
void connectedToPortChanged(bool connected);
public slots:
void connectToPort(const QString & portName);
};
class MasterGuiLogic : public QObject
{
Q_OBJECT
public:
MasterGuiLogic();
Q_PROPERTY(GuiLogic_SerialCom * serialCom READ serialCom CONSTANT)
GuiLogic_SerialCom* serialCom() const {return test;}
Q_INVOKABLE void generate_signal();
private:
GuiLogic_SerialCom * test;
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <QDebug>
MasterGuiLogic::MasterGuiLogic()
{
this->test = new GuiLogic_SerialCom();
}
void MasterGuiLogic::generate_signal()
{
qDebug() << __FUNCTION__ << "Calling serialcom to gen signal";
this->test->connectToPort("88");
}
void GuiLogic_SerialCom::connectToPort(const QString &portName)
{
qDebug() << __FUNCTION__ << "got signal" << portName;
emit this->connectedToPortChanged(true);
}
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterUncreatableType<MasterGuiLogic>("GrblCom", 1, 0, "MasterGuiLogic", "");
qmlRegisterUncreatableType<GuiLogic_SerialCom>("GrblCom", 1, 0, "GuiLogic_SerialCom", "");
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("Test"), new MasterGuiLogic());
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import GrblCom 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea
{
anchors.fill: parent
onClicked:
{
Test.generate_signal();
}
}
Connections
{
target: Test.serialCom
onConnectedToPortChanged:
{
console.log("Got signal from SerialCom in QML. passed bool value is: " + connected);
}
}
}
好的,我找到了问题...所提供的答案虽然出于其他原因确实很有帮助,但并不是正确的解决方案。在查看@Xplatforms 的代码后,我无法弄清楚我所做的和他所做的之间有什么区别....直到我在自己的代码中看到这一点:
Connections: {
target: MasterGuiLogic.serialCom;
onConnectedToPortChanged: {
...
}
}
那里不应该有冒号 (:)...
Connections {
target: MasterGuiLogic.serialCom;
onConnectedToPortChanged: {
...
}
}
千万不要在困倦的时候尝试编程...哈哈
所以这看起来像是一个奇怪的设置。为了简单起见,我有一个继承自 QObject 的 C++ 对象,称为 "MasterGuiLogic"。它是用指向另一个名为 "MainEventBroker" 的对象的指针创建的,您可能猜到它处理我所有的应用程序事件。 MasterGuiLogic 对象作为上下文 属性 在 qml 中注册,因此它的属性可以在我的 qml 中的任何地方使用。所以 main.cpp 看起来像这样:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
MasterEventBroker *MainEventBroker = new MasterEventBroker();
MasterGuiLogic *MainGuiLogic = new MasterGuiLogic(*MainEventBroker);
qmlRegisterUncreatableType<MasterGuiLogic>("GrblCom", 1, 0, "MasterGuiLogic", "");
qmlRegisterUncreatableType<GuiLogic_SerialCom>("GrblCom", 1, 0, "GuiLogic_SerialCom", "");
QQmlApplicationEngine engine;
QQmlContext* context = engine.rootContext();
context->setContextProperty("MasterGuiLogic", &(*MainGuiLogic));
engine.load(QUrl(QLatin1String("qrc:/QmlGui/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
MasterGuiLogic 创建另一个名为 SerialCom 的 class 的实例,它被设置为 Q_PROPERTY 以便它的属性和 public 插槽可以通过 MasterGuiLogic 属性.
MasterGuiLogic.h:
class MasterGuiLogic : public QObject
{
Q_OBJECT
Q_PROPERTY(GuiLogic_SerialCom* serialCom READ serialCom CONSTANT)
public:
MasterEventBroker *eventBroker;
explicit MasterGuiLogic(MasterEventBroker &ev, QObject *parent = nullptr);
GuiLogic_SerialCom* serialCom() const {
return Gui_SerialCom;
}
private:
GuiLogic_SerialCom *Gui_SerialCom;
MasterGuiLogic.cpp:
MasterGuiLogic::MasterGuiLogic(MasterEventBroker &ev, QObject *parent) : QObject(parent)
{
this->eventBroker = &ev;
this->Gui_SerialCom = new GuiLogic_SerialCom(this);
}
SerialCom.h:
//Forward Declare our parent
class MasterGuiLogic;
class GuiLogic_SerialCom : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList portNames READ portNames NOTIFY portNamesChanged)
Q_PROPERTY(bool connectedToPort READ connectedToPort NOTIFY connectedToPortChanged)
public:
MasterGuiLogic *parent;
explicit GuiLogic_SerialCom(MasterGuiLogic *parent = nullptr);
std::map<QString, QSerialPortInfo> portsMap;
QStringList portNames() {
return _portNames;
}
bool connectedToPort() {
return _connectedToPort;
}
private:
QStringList _portNames;
bool _connectedToPort = false;
signals:
void portNamesChanged(const QStringList &);
void connectedToPortChanged(const bool &);
public slots:
void connectToPort(const QString portName);
void disconnectFromPort(const QString portName);
};
SerialCom.cpp:
GuiLogic_SerialCom::GuiLogic_SerialCom(MasterGuiLogic *parent) : QObject(qobject_cast<QObject *>(parent))
{
this->parent = parent;
QList<QSerialPortInfo> allPorts = QSerialPortInfo::availablePorts();
for (int i = 0; i < allPorts.size(); ++i) {
this->_portNames.append(allPorts.at(i).portName());
this->portsMap[allPorts.at(i).portName()] = allPorts.at(i);
}
emit portNamesChanged(_portNames);
}
void GuiLogic_SerialCom::connectToPort(const QString portName) {
//TODO: Connect To Port Logic Here;
//Set Connected
this->_connectedToPort = true;
emit connectedToPortChanged(this->_connectedToPort);
qDebug() << portName;
}
void GuiLogic_SerialCom::disconnectFromPort(const QString portName) {
//TODO: DisConnect To Port Logic Here;
//Set DisConnected
this->_connectedToPort = false;
emit connectedToPortChanged(this->_connectedToPort);
qDebug() << portName;
}
所以从 qml 中读取这些属性中的任何一个都非常容易,甚至可以将信号从 qml 发送到 c++
例如,这很好用:
connectCom.onClicked: {
if (MasterGuiLogic.serialCom.connectedToPort === false) {
MasterGuiLogic.serialCom.connectToPort(comPort.currentText);
} else {
MasterGuiLogic.serialCom.disconnectFromPort(comPort.currentText);
}
}
问题是,我似乎无法找到连接到从 SerialCom 发出的信号的方法。我以为我可以做这样的事情:
Connections: {
target: MasterGuiLogic.serialCom;
onConnectedToPortChanged: {
if (MasterGuiLogic.serialCom.connectedToPort === false) {
connectCom.text = "Disconnect";
comPort.enabled = false;
} else {
connectCom.text = "Connect";
comPort.enabled = true;
}
}
}
这应该听 SerialCom 上的布尔值 属性 来改变,但是我得到以下错误:
QQmlApplicationEngine failed to load component
qrc:/QmlGui/main.qml:21 Type Page1 unavailable
qrc:/QmlGui/Page1.qml:49 Invalid attached object assignment
这只是意味着我无法 "connect" 使用上面的目标行。有没有其他方法可以连接到来自 ContextProperty 中 QObject 类型的 Q_PROPERTY 的信号?
首先&(*MainGuiLogic)
是什么意思?
您正在取消引用并再次引用 MainGuiLogic
?为什么?
context->setContextProperty("MasterGuiLogic", MainGuiLogic);
就够了。
但是将 MasterGuiLogic
注册为 Type 并添加名为 MasterGuiLogic
的 Object 可以在 QML 世界中覆盖自己.
将其设置为 context->setContextProperty("MyGuiLogic", MainGuiLogic);
以消除此行为。
也不要在 C++ 和 QML 世界之间传递引用,例如:
void connectedToPortChanged(**const bool &**);
。
只需使用原子类型和值(const bool)
;
并给它一个名字,以便能够在 QML 中将其用作 命名值 :
void connectedToPortChanged(bool connected)
这是一个与您的结构相似的示例,它可以正常工作。只需单击 window 并查看输出控制台。
test.h:
#ifndef TEST_H
#define TEST_H
#include <QObject>
class GuiLogic_SerialCom : public QObject
{
Q_OBJECT
public:
GuiLogic_SerialCom(){}
signals:
void connectedToPortChanged(bool connected);
public slots:
void connectToPort(const QString & portName);
};
class MasterGuiLogic : public QObject
{
Q_OBJECT
public:
MasterGuiLogic();
Q_PROPERTY(GuiLogic_SerialCom * serialCom READ serialCom CONSTANT)
GuiLogic_SerialCom* serialCom() const {return test;}
Q_INVOKABLE void generate_signal();
private:
GuiLogic_SerialCom * test;
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <QDebug>
MasterGuiLogic::MasterGuiLogic()
{
this->test = new GuiLogic_SerialCom();
}
void MasterGuiLogic::generate_signal()
{
qDebug() << __FUNCTION__ << "Calling serialcom to gen signal";
this->test->connectToPort("88");
}
void GuiLogic_SerialCom::connectToPort(const QString &portName)
{
qDebug() << __FUNCTION__ << "got signal" << portName;
emit this->connectedToPortChanged(true);
}
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "test.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterUncreatableType<MasterGuiLogic>("GrblCom", 1, 0, "MasterGuiLogic", "");
qmlRegisterUncreatableType<GuiLogic_SerialCom>("GrblCom", 1, 0, "GuiLogic_SerialCom", "");
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("Test"), new MasterGuiLogic());
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import GrblCom 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea
{
anchors.fill: parent
onClicked:
{
Test.generate_signal();
}
}
Connections
{
target: Test.serialCom
onConnectedToPortChanged:
{
console.log("Got signal from SerialCom in QML. passed bool value is: " + connected);
}
}
}
好的,我找到了问题...所提供的答案虽然出于其他原因确实很有帮助,但并不是正确的解决方案。在查看@Xplatforms 的代码后,我无法弄清楚我所做的和他所做的之间有什么区别....直到我在自己的代码中看到这一点:
Connections: {
target: MasterGuiLogic.serialCom;
onConnectedToPortChanged: {
...
}
}
那里不应该有冒号 (:)...
Connections {
target: MasterGuiLogic.serialCom;
onConnectedToPortChanged: {
...
}
}
千万不要在困倦的时候尝试编程...哈哈