关于Qt – connect的使用, 首先推荐大家两篇我之前写的两篇文章
Qt5 connect使用之“传说中的第五参数”
Qt5 connect 使用之“重载信号和槽“
从Qt5.0开始,Qt提供了两种不同的方法用来实现“信号–槽 connect”, 一种是之前版本就有的基于字符串的connect, 另一种是Qt5.0新增的基于方法指针的connect。两种不同方法各有优缺点, 这里做一个简单的总结:
基于字符串 | 基于函数指针 | |
---|---|---|
类型检查时机 | 运行时检查 | 编译期检查 |
支持类型隐式转换 | √ | |
支持lambda表达式 | √ | |
信号参数个数比槽函数多的connect(使用默认参数) | √ | |
C++与QML之间的connect | √ |
一、类型检查与隐式类型转换
基于字符串的connect 在运行时才会进行类型检查,这样做相对于基于函数指针的connect会有以下劣势:
- 程序运行时才能检测到connect类型错误
- 不支持在信号与槽函数参数的隐式转换
- typedef 和 namespace 无法解析
因为C++无法比较字符串中的类型信息,所以基于字符串的connect依赖于字符串的精确匹配,而基于函数指针的connect可以在编译期进行类型检查,捕获类型错误,进行隐式转换,识别相同类型的不同名称。
以下为两个示例:
// 1.函数类型隐式转换
QSlider *slider = new QSlider;
QDoubleSpinBox *doubleSpinBox = new QDoubleSpinBox;
// ok. 可以正常connect成功
connect(slider, &QSlider::valueChanged, doubleSpinBox, &QDoubleSpinBox::setValue);
// error.connect失败. int类型和double类型不匹配
connect(slider, SIGNAL(valueChanged(int)), doubleSpinBox, SLOT(setValue(double)));
// 2.相同类型的不同名称
auto audioInput = new QAudioInput(QAudioFormat(), this);
auto widget = new QWidget(this);
// OK
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), widget, SLOT(show()));
// error.字符串 "State" 和 "QAudio::State"不匹配
using namespace QAudio;
connect(audioInput, SIGNAL(stateChanged(State)), widget, SLOT(show()));
二、信号参数个数比槽函数多的connect
通常,槽函数具有与信号相同(或更少)的参数并且所有参数类型兼容的情况下,才能connect成功。
但是, 基于字符串的connect 如果槽函数有默认参数,信号则可以省略这些参数。当发出信号的参数少于槽函数的参数时,Qt将使用默认参数值进行connect。
// .h
class Test : public QWidget {
Q_OBJECT
public:
Test(QWidget * parent = nullptr);
public slots:
void testSlot(int number = 42, const QString& str = "test") {
qDebug() << number << str;
}
};
// .cpp
Test::Test(QWidget * parent)
{
QPushButton *btn = new QPushButton(this);
// error.编译失败
connect(btn, &QPushButton::clicked, this, &Test::testSlot);
// ok
connect(btn, SIGNAL(clicked()), this, SLOT(testSlot()));
}
三、C++与QML之间的connect
基于字符串的connect 可以绑定C++对象QML对象之前的信号槽,但是基于函数指针的connect不可以。因为QML类型是在运行时解析的, 基于函数指针的connect无法做到这一点。
下面这个案例, 单机QML对象, C++对象会打印一条消息,反之亦然。
当C++中的QPushButton点击, 控制台会打印 QML received: “Hello from C++!”
当点击QML中的Rectangle, 控制台会打印 ''C++ received: "Hello from QML!
// QML.QmlGui.qml
Rectangle {
width: 100; height: 100
signal qmlSignal(string sentMsg)
function qmlSlot(receivedMsg) {
console.log("QML received: " + receivedMsg)
}
MouseArea {
anchors.fill: parent
onClicked: qmlSignal("Hello from QML!")
}
}
// cpp. CppGui.cpp
class CppGui : public QWidget {
Q_OBJECT
QPushButton *button;
signals:
void cppSignal(const QVariant& sentMsg) const;
public slots:
void cppSlot(const QString& receivedMsg) const {
qDebug() << "C++ received:" << receivedMsg;
}
public:
CppGui(QWidget *parent = nullptr) : QWidget(parent) {
button = new QPushButton("Click Me!", this);
connect(button, &QPushButton::clicked, [=] {
emit cppSignal("Hello from C++!");
});
}
};
二者进行connect
auto cppObj = new CppGui(this);
auto quickWidget = new QQuickWidget(QUrl("QmlGui.qml"), this);
auto qmlObj = quickWidget->rootObject();
// Connect QML signal to C++ slot
connect(qmlObj, SIGNAL(qmlSignal(QString)), cppObj, SLOT(cppSlot(QString)));
// Connect C++ signal to QML slot
connect(cppObj, SIGNAL(cppSignal(QVariant)), qmlObj, SLOT(qmlSlot(QVariant)));
Note: QML中JavaScript中使用的 var 类型, 对应C++中的QVariant 类型。