调用QQmlPropertyMap child child slot?
Invoke slot of child of child of QQmlPropertyMap?
在将此标记为“无法在 QQmlPropertyMap 的子 class 中从 QML 调用插槽或 Q_INVOKABLE”的副本之前,请阅读最终回复 ()。
我正在设计一个从 QQmlPropertyMap 继承的 class(称之为 MyBaseClass
)。它为我的系统实现了一些额外的行为,这些行为随后被从 MyBaseClass
继承的其他 classes 使用(例如 ChildClass
)。
我希望从 QML (QtQuick) 调用 ChildClass
的槽 (mySlot()
)。当我尝试时,我在应用程序输出中遇到以下问题(提到 MyBaseClass
),即使我只使用 ChildClass
.
qrc:/main.qml:17: TypeError: Property 'mySlot' of object MyBaseClass(0x55f88ef86b50) is not a function
qrc:/main.qml:24: TypeError: Property 'mySlot' of object MyBaseClass(0x55f88ef86b50) is not a function
我也尝试过从 QML 发送信号,但我在尝试绑定到 QML 发出的信号时遇到了类似的问题。 (我没有添加那个例子。)
对于那些建议我将 QQmlPropertyMap 继承到 ChildClass 中的人(除了 MyBaseClass 之外,它已经通过受保护的方法对其进行了适当的继承和初始化),结果并不理想:
/home/jwerner/Projects/InheritanceTest-simple/ChildClass.h:8: warning: direct base 'QQmlPropertyMap' is inaccessible due to ambiguity:
class ChildClass -> class MyBaseClass -> class QQmlPropertyMap
class ChildClass -> class QQmlPropertyMap
我的“直觉”说问题是我没有按照“Qt 方式”做事,但我不知道它是什么。
这里是演示问题的最小示例代码。 (我省略了 .pro 文件,因为制作起来很简单。)
MyBaseClass.h
#include <QQmlPropertyMap>
class MyBaseClass : public QQmlPropertyMap
{
Q_OBJECT
public:
explicit MyBaseClass(QObject *parent = nullptr)
: QQmlPropertyMap(this, parent)
{}
};
ChildClass.h
#include "MyBaseClass.h"
#include <QObject>
#include <QDebug>
class ChildClass : public MyBaseClass
{
Q_OBJECT
public:
ChildClass(QObject *parent = nullptr)
: MyBaseClass(parent) {}
public slots:
void mySlot() {
qDebug() << "This is mySlot";
};
};
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: theMainWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property QtObject cData: childData;
onCDataChanged: {
childData.mySlot();
}
Timer { // wait a bit before calling
repeat: false
interval: 2000
running: true
onTriggered: childData.mySlot();
}
}
main.cpp
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
auto childData = new ChildClass();
engine.rootContext()->setContextProperties({
QQmlContext::PropertyPair{"childData", QVariant::fromValue(childData)},
});
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
观察到 2-argument QQmlPropertyMap
构造函数是一个 template 构造函数,它需要 [most] derived class 的类型(文档可能更清楚一点,但这是使其与自动注册一起工作的唯一方法)。它看到的唯一类型是 MyBaseClass
.
您需要将派生的类型 class 传递给该构造函数,例如通过制作一个受保护的 MyBaseClass
构造函数 templated 并赋予它与 QQmlPropertyMap
相同的签名,然后从 MyChildClass
调用它。使 MyBaseClass
成为 CRTP 模板 class 的替代方案仅在 class 不添加任何属性、信号或插槽时才有效。
因此:
#include <QQmlPropertyMap>
class MyBaseClass : public QQmlPropertyMap
{
Q_OBJECT
protected:
template <typename Derived>
explicit MyBaseClass(Derived *derived, QObject *parent = nullptr)
: QQmlPropertyMap(derived, parent)
{}
};
构造函数应该受到保护,因为我认为 MyBaseClass
不能单独使用。如果你愿意,你总是可以添加一个单参数 public 构造函数:
public:
MyBaseClass(QObject *parent = nullptr) : QQmlPropertyMap(this, parent) {}
然后:
#include "MyBaseClass.h"
#include <QObject>
#include <QDebug>
class ChildClass /* final, or see below */ : public MyBaseClass
{
Q_OBJECT
public:
ChildClass(QObject *parent = nullptr)
: MyBaseClass(this, parent) {} // notice the additional argument
protected:
// if this class is not final, then add:
template <typename Derived>
ChildClass(Derived *derived, QObject *parent = nullptr)
: MyBaseClass(derived, parent) {}
public slots:
void mySlot() { qDebug() << "This is mySlot"; };
};
请注意,必须存在以下 之一:
将 class ChildClass
声明为最终 class、 或
模板化的受保护的 2 参数构造函数。
两者都没有或两者都没有是设计错误。
我测试了这个并且它有效,但我不能说我推荐这个作为解决方案。如果它必须知道其派生类型,那么它有点否定了拥有基数 class 的目的。
// Forward-declare the derived type so it can be passed to the constructor
class ChildClass;
class MyBaseClass : public QQmlPropertyMap
{
Q_OBJECT
public:
explicit MyBaseClass(ChildClass *derivedObj, QObject *parent = nullptr)
: QQmlPropertyMap(derivedObj, parent)
{}
};
在将此标记为“无法在 QQmlPropertyMap 的子 class 中从 QML 调用插槽或 Q_INVOKABLE”的副本之前,请阅读最终回复 ()。
我正在设计一个从 QQmlPropertyMap 继承的 class(称之为 MyBaseClass
)。它为我的系统实现了一些额外的行为,这些行为随后被从 MyBaseClass
继承的其他 classes 使用(例如 ChildClass
)。
我希望从 QML (QtQuick) 调用 ChildClass
的槽 (mySlot()
)。当我尝试时,我在应用程序输出中遇到以下问题(提到 MyBaseClass
),即使我只使用 ChildClass
.
qrc:/main.qml:17: TypeError: Property 'mySlot' of object MyBaseClass(0x55f88ef86b50) is not a function
qrc:/main.qml:24: TypeError: Property 'mySlot' of object MyBaseClass(0x55f88ef86b50) is not a function
我也尝试过从 QML 发送信号,但我在尝试绑定到 QML 发出的信号时遇到了类似的问题。 (我没有添加那个例子。)
对于那些建议我将 QQmlPropertyMap 继承到 ChildClass 中的人(除了 MyBaseClass 之外,它已经通过受保护的方法对其进行了适当的继承和初始化),结果并不理想:
/home/jwerner/Projects/InheritanceTest-simple/ChildClass.h:8: warning: direct base 'QQmlPropertyMap' is inaccessible due to ambiguity:
class ChildClass -> class MyBaseClass -> class QQmlPropertyMap
class ChildClass -> class QQmlPropertyMap
我的“直觉”说问题是我没有按照“Qt 方式”做事,但我不知道它是什么。
这里是演示问题的最小示例代码。 (我省略了 .pro 文件,因为制作起来很简单。)
MyBaseClass.h
#include <QQmlPropertyMap>
class MyBaseClass : public QQmlPropertyMap
{
Q_OBJECT
public:
explicit MyBaseClass(QObject *parent = nullptr)
: QQmlPropertyMap(this, parent)
{}
};
ChildClass.h
#include "MyBaseClass.h"
#include <QObject>
#include <QDebug>
class ChildClass : public MyBaseClass
{
Q_OBJECT
public:
ChildClass(QObject *parent = nullptr)
: MyBaseClass(parent) {}
public slots:
void mySlot() {
qDebug() << "This is mySlot";
};
};
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: theMainWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property QtObject cData: childData;
onCDataChanged: {
childData.mySlot();
}
Timer { // wait a bit before calling
repeat: false
interval: 2000
running: true
onTriggered: childData.mySlot();
}
}
main.cpp
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
auto childData = new ChildClass();
engine.rootContext()->setContextProperties({
QQmlContext::PropertyPair{"childData", QVariant::fromValue(childData)},
});
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
观察到 2-argument QQmlPropertyMap
构造函数是一个 template 构造函数,它需要 [most] derived class 的类型(文档可能更清楚一点,但这是使其与自动注册一起工作的唯一方法)。它看到的唯一类型是 MyBaseClass
.
您需要将派生的类型 class 传递给该构造函数,例如通过制作一个受保护的 MyBaseClass
构造函数 templated 并赋予它与 QQmlPropertyMap
相同的签名,然后从 MyChildClass
调用它。使 MyBaseClass
成为 CRTP 模板 class 的替代方案仅在 class 不添加任何属性、信号或插槽时才有效。
因此:
#include <QQmlPropertyMap>
class MyBaseClass : public QQmlPropertyMap
{
Q_OBJECT
protected:
template <typename Derived>
explicit MyBaseClass(Derived *derived, QObject *parent = nullptr)
: QQmlPropertyMap(derived, parent)
{}
};
构造函数应该受到保护,因为我认为 MyBaseClass
不能单独使用。如果你愿意,你总是可以添加一个单参数 public 构造函数:
public:
MyBaseClass(QObject *parent = nullptr) : QQmlPropertyMap(this, parent) {}
然后:
#include "MyBaseClass.h"
#include <QObject>
#include <QDebug>
class ChildClass /* final, or see below */ : public MyBaseClass
{
Q_OBJECT
public:
ChildClass(QObject *parent = nullptr)
: MyBaseClass(this, parent) {} // notice the additional argument
protected:
// if this class is not final, then add:
template <typename Derived>
ChildClass(Derived *derived, QObject *parent = nullptr)
: MyBaseClass(derived, parent) {}
public slots:
void mySlot() { qDebug() << "This is mySlot"; };
};
请注意,必须存在以下 之一:
将
class ChildClass
声明为最终 class、 或模板化的受保护的 2 参数构造函数。
两者都没有或两者都没有是设计错误。
我测试了这个并且它有效,但我不能说我推荐这个作为解决方案。如果它必须知道其派生类型,那么它有点否定了拥有基数 class 的目的。
// Forward-declare the derived type so it can be passed to the constructor
class ChildClass;
class MyBaseClass : public QQmlPropertyMap
{
Q_OBJECT
public:
explicit MyBaseClass(ChildClass *derivedObj, QObject *parent = nullptr)
: QQmlPropertyMap(derivedObj, parent)
{}
};