最近尝试学习 Qt,觉得还挺有趣的。信号(Signal)和槽(Slot)可能是 Qt 最重要也是最独特的特性了,有了这个机制,Qt 才能变得如此强大!
顺便打一个广告,刚入门 Qt 就开始写的 Notepanda (opens new window) 小项目,一个很 simple 的 notepad 啦~
事实证明,用边写边学的方式学习 Qt 是对的。这篇文章中的例子全都来自于我编写上面这个 Notepanda 的过程。
# 简介
Signal 和 Slot 用于对象之间的通信,举个例子:我想要在点击一个 open 按钮时,自动调用 Open()
函数。使用 Signal 和 Slot 就可以简单地实现这样的功能,通过一个 connect()
函数就能绑定一对 Signal & Slot:
connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::open);
下面是一张说明 Signal & Slot 如何工作的示意图:
# 实现一个 Slot 函数
先引用一段来自 Qt 官方文档的文字进一步说明 Signal & Slot 机制
::: theorem Signal&Slot Signal & Slot 机制是类型安全的: Signal 的签名必须与接收槽的签名相匹配。(事实上,一个 Slot 的签名可能比它接收到的 Signal 更短,因为它可以忽略额外的参数)由于签名是兼容的,当使用基于函数指针的语法时,编译器可以帮助我们检测类型不匹配。基于字符串的 SIGNAL 和 SLOT 语法将在运行时检测类型不匹配。Signal 和 Slot 是松散耦合的: 发出信号的类既不知道也不关心哪些插槽接收信号。Qt 的 Signal 和 Slot 机制确保了如果您将 Signal 连接到 Slot,Slot 将在正确的时间用 Signal 的参数调用。Signal 和 Slot 可以接受任意类型的任意数量的参数。 :::
看不懂?没关系,我举一个带有参数的 Signal & Slot 例子。
我有一个 TextEditor
(其实是一个 QPlainTextEdit
) 对象,叫做 plainTextEdit
。我要实现一个 undo 操作,我想在 undo 操作不可用时 Disable 掉 undo 的按钮。但我不知道现在能否在编辑器里进行 undo,QPlainTextEdit
提供了一个 undoAvailable(bool available)
的 Signal,如果不可用,它会将 available
设成 false
。否则为 true
。所以现在我要实现一个 Slot。
首先定义一个 slot 函数:
class MainWindow : public QMainWindow
{
Q_OBJECT
private slots:
void setActUndoState(bool available);
};
这样就定义好了一个 setActUndoState(bool available)
的 Slot 函数。接着我要实现它:
void MainWindow::setActUndoState(bool available)
{
ui->actionUndo->setDisabled(!available);
}
最后 connect 这一对 Signal & Slot。
connect(plainTextEdit, &TextEditor::undoAvailable, this, &MainWindow::setActUndoState);
看上去很简单吧!其实,setActUndoState(bool available)
函数里的 available
参数就是在 Signal undoAvailable(bool available)
里传送过来的。
# 实现一个 Signal
先介绍一下场景,我想要当 QPlainTextEdit
打开的文件 title 改变时,随之改变窗口的 title。我要在 TextEditor
端实现一个 Signal,然后在 UI 层的 MainWindow
修改 title。
定义 Signal:
class TextEditor : public QPlainTextEdit
{
Q_OBJECT
signals:
void changeTitle();
};
提醒:
Signal 函数必须是
void
类型的。
然后,我要在改动文件 title 时发送这个 changeTitle
Signal。可以这样写:
...
emit changeTitle();
...
这样就发送好了。继续在 MainWindow
定义一个 Slot 函数:
class MainWindow : public QMainWindow
{
Q_OBJECT
private slots:
void changeWindowTitle();
};
然后实现 changeWindowTitle
:
void MainWindow::changeWindowTitle()
{
setWindowTitle(plainTextEdit->currentFile.split("/").last() + " - Notepanda");
}
最后写 connect:
connect(plainTextEdit, &TextEditor::changeTitle, this, &MainWindow::changeWindowTitle);
这样就实现啦!怎么样,是不是很简单!对,就这么简单的东西我摸索了一早上才写对(逃
Qt 酱还是很有趣的!我越来越喜欢她啦~