Skip to main content

Qt 5中信号和槽的新语法

Comments

参考原文:woboq: Olivier Goffart - Signals and Slots in Qt5

Qt 5 Alpha已经发布。我曾经工作过的一个新特性就是信号和槽的新语法。本篇博客将会为您讲解这一内容。

Qt 5之前的语法

这里是我们如何把信号和槽连接起来的方法:

connect(sender, SIGNAL(valueChanged(QString, QString)),
receiver, SLOT(updateValue(QString)));

幕后的实际情况是SIGNALSLOT这两个宏会把它们的参数转换成字符串。然后QObject::connect()将会把这些字符串和moc工具所收集的内省(introspection)数据进行比较。

Qt 5之前信号和槽语法的问题

虽然通常情况下都可以正常工作,我们还是发现了如下问题:

  • 没有编译期检查:因为函数名被处理成字符串,所有的检查都是在运行时完成的。这就是为什么有时会发生编译通过了,但槽并没有被调用。此时,你就应该去检查标准输出,看看有没有什么警告说明这个连接没有成功。
  • 因为处理的是字符串,所以槽函数中的类型名字必须与信号的完全一致,而且在头文件中的和实际的connect()语句中的也必须一致。也就是说,如果你使用了typedef或者命名空间,那么这个连接就不能正常工作。

新语法:使用函数指针

在即将到来的Qt 5中提供了一套新的语法。之前的语法依然可以使用,但是现在,我们有了全新的方式:

connect(sender, &Sender::valueChanged,
receiver, &Receiver::updateValue);

哪一种方式更酷可能取决于个人喜好。但大家会更快的适应新方式。

现在让我们先远离美学视角,来看看新语法有什么好处:

编译期检查

如果信号或者槽的名字的拼写发生了错误,或者槽函数的参数与信号的不一致,你会在编译期就得到一个错误。

这肯定会在重构、或者修改信号或槽函数的名字时节省很多时间。

另一个好处是,如果我们少了Q_OBJECT宏,可以利用static_cast返回更友好的错误信息。

参数的自动类型转换

现在,我们不仅可以更好地使用typedef或者命名空间,而且可以利用隐式类型转换。

在下面的例子中,我们的信号有一个QString参数,而槽函数需要的是QVariant。它可以正常工作,是因为QVariant有一个可以使用QString的隐式构造函数。

class Test : public QObject
{
Q_OBJECT
public:
Test() {
connect(this, &Test::someSignal, this, &Test::someSlot);
}
signals:
void someSignal(const QString &);
public:
void someSlot(const QVariant &);
};

连接到任意函数

如果你留心上面的例子,就会发现,我们的信号被连接到了一个槽,但是它的声明只有public,没有slot。Qt的新语法通过函数指针直接调用函数,而不再需要moc的内省(但是信号依然需要)。

更进一步,我们可以将信号连接到任意函数或者函数对象(functor):

static void someFunction() {
qDebug() << "pressed";
}

// ... 然后其它地方
QObject::connect(button, &QPushButton::clicked, someFunction);

这样处理,就可以让你很方便的同boost或者tr1::bind进行协作。

C++11 Lambda表达式

至此之前,我们所有的示例都是基于C++98标准的。但是,如果你的编译器支持C++11,我还是强烈建议你使用一些这个语言的新特性。现在,Lambda表达式至少被MSVC 2010GCC 4.5clang 3.1这几个编译器支持。不过对于后面两个编译器,你需要在编译时加上-std=c++0x参数。

然后我们就可以这样写代码了:

void MyWindow::saveDocumentAs() {
QFileDialog *dlg = new QFileDialog();
dlg->open();
QObject::connect(dlg, &QDialog::finished, [=](int result) {
if (result) {
QFile file(dlg->selectedFiles().first());
// ... 在这里保存文档 ...
}
dlg->deleteLater();
});
}

这种语法允许我们更方便地编写异步代码。

更新:您还可以看看Qt 5中提供的其它C++11特性

现在该干什么呢?

让我们来尝试一下吧。下载Qt 5 Alpha,开始玩吧。不要忘记报告Bug呀。

(译者注:感谢齐亮对本篇博客全文进行了修正。)

Comments

Subscribe to our blog

Try Qt 6.11 Now!

Download the latest release here: www.qt.io/download

Qt 6.11 is now available, with new features and improvements for application developers and device creators.

We're Hiring

Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.