文章目录
1、概念
Qt中,控件和用户的操作都会发出信号,比如点击按钮,移动鼠标等,信号的处理函数就是槽。Qt可以用connect来把信号和槽关联起来,这样只要信号触发,就自动执行对应的槽函数。槽函数本身也是一个回调函数。对于回调来说,对应的处理方法都是提前准备好的。Qt中必须要先关联好信号和槽,再触发信号。
2、connect
connect是QObject的静态成员函数。Qt的类都是存在继承关系的,比如PushButton,LineEdit的父类都是QWidget,QWidget就是控件,QWidget的父类是QObject,所有Qt的类都直接或间接继承自QObject类,所以Qt中的类都可以直接使用connect。
最后一个参数是默认参数,暂且不考虑。
sender表示信号源,也就是哪个控件发出的信号。
signal表示信号类型。
receiver表示哪个对象接收信号,method就是信号处理函数。
写一个按钮,一点击就关闭窗口。widget.cpp:
cpp
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* button = new QPushButton(this);
button->setText("Exit");
button->move(330, 250);
connect(button, &QPushButton::clicked, this, &Widget::close);
}
connect前两个参数和后两个参数必须是分别匹配的,button是什么类型,那么第二个参数的信号就必须是这个类型内置的信号或者它的父类的信号。
写第二个参数时,写clicked时会出现两个写法,一个是click,一个是clicked,两个前面都有一个符号,click的符号代表槽函数,clicked的符号代表信号类型。
槽函数直接用内置函数。
Qt中很多按钮存在共性内容,这些共性内容放置在QAbstractButton类中,这些按钮类都继承自这个类,clicked信号就在这个父类中。当按钮被激活时,clicked信号就会被发出。
二四两个参数是const char的参数,传的时候需要给信号搭配一个SIGNAL宏,给槽函数搭配一个SLOT宏,都转为char后才能传:
cpp
connect(button, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close));
但Qt5之后给connect做了重载函数,这两个参数就变成了泛型参数,允许传入任意类型的指针了。不过如果参数不匹配也不行。
3、自定义槽函数
在Widget类里写一个自定义的成员函数的声明,widget.cpp中写上实现,我们就可以直接用这个函数了。
widget.h:
cpp
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handleClicked();
private:
Ui::Widget *ui;
};
widget.cpp:
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* button = new QPushButton(this);
button->setText("Button");
button->move(330, 250);
connect(button, &QPushButton::clicked, this, &Widget::close);
}
void Widget::handleClicked()
{
// 点击按钮就修改窗口标题
this->setWindowTitle("Button clicked");
}
更简单的方式是直接操作图形化界面。
将PushButton拖拽过来后,右击选择转到槽
可以看到来到了它的直接的父类QAbstractButton,以及QA的父类QWidget,以及QW的父类QObject。点击click(),就来到widget.cpp文件,并且自动写出了一个函数框架,代码部分则由用户自定义。
此时连接信号和槽就不是connect,而是通过函数名字,也就是选择click()后自动转到的,Qt自动生成的函数名来连接信号和槽。函数名字得符合命名规则,如果把clicked改为click,PushButton并没有对应的函数,那么就不生效,报警告,因为无法连接上。
调用connectSlotsByName就会触发连接,在生产时的实时文件ui_widget.h中会看到该函数。
4、自定义信号
信号本质是一个函数,但和槽函数不同,槽函数在Qt 5以及更高版本中,槽函数和普通成员函数之间已经没什么区别了;但信号特殊,用户只需要写出函数声明,并能让Qt知道这是一个信号,那么函数的定义,Qt会在编译过程中自动生成,而且对于自动生成过程,用户无法进行操作。
作为信号函数,返回值必须为void,参数有无都可,且也可以支持重载。
在写信号时,现在头文件中Widget类的public部分写上signal关键字,这是Qt的关键字,qmake生成代码时扫描到类中包含signals关键字时就会自动把下面的函数声明当作信号,并生成函数定义。
cpp
// widget.h
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handleMySignal();
signals:
void mySignal();
private:
Ui::Widget *ui;
};
// widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this, &Widget::mySignal, this, &Widget::handleMySignal);
// 连接好后用emit来发送信号
emit mySignal();
}
void Widget::handleMySignal()
{
this->setWindowTitle("处理自定义信号");
}
运行后发现窗口标题变为处理自定义信号了。
我们也可以把发送并处理信号放到一个按钮中,按钮的自定义槽函数按照控件方式来写:
cpp
// widget.h
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handleMySignal();
signals:
void mySignal();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
};
// widget.cpp
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
void Widget::handleMySignal()
{
this->setWindowTitle("处理自定义信号");
}
void Widget::on_pushButton_clicked()
{
connect(this, &Widget::mySignal, this, &Widget::handleMySignal);
// 连接好后用emit来发送信号
emit mySignal();
}
在Qt 5以上,不写emit也可以,因为处理signals部分时都会自动发送信号。不过也应该写,可读性更强。
结束。