Qt——信号槽

信号和槽(slot)

类似于OS中的信号回调机制,一旦发射了信号就会触发(执行)槽函数**。在Qt中,信号看起来是函数的样子(之所以说看起来像,是因为实际上Qt暴露给我们的信号或者我们在Qt上自己创建的信号,都会被Qt的MOC处理成另外一种样子),而槽本质上就是一个函数。**

大部分信号都是Qt提供给我们的,但是我们也可以自定义信号。Qt也提供一些槽,但我们经常自己定义槽。对于Qt给我我们的,我们要多去查阅文档,从子类到父类向上寻找

在Qt中,信号和槽的关系是多对多关系,即一个槽可以被多个信号关联,一个信号可以关联多个槽。作为Qt的使用者,我们主要做的就是将信号源,信号,槽,接受对象关联起来,目的是使得用户操作后得到反馈。当信号源发射后信号后就会查找信号和槽的关联关系,执行与之关联的所有槽。


关联信号和槽

关联信号和槽有多种方式,我们下面逐一介绍

Connect函数方式

connect是QObject类(也就是Qt中所有类的祖宗)中的一个静态方法,因此所有类中都可以直接调用它。它常使用(connect有多个重载)的重载形式如下:

cpp 复制代码
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
                                     const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
                                     Qt::ConnectionType type = Qt::AutoConnection)
  • sender:发送信号的控件对象的地址,即设置信号源。
  • signal:要关联的信号,以函数地址的方式传入。
  • receiver:要对信号做出反应的控件对象,以对象指针的方式传入。
  • slot:槽函数,以函数指针的方式传入。
  • type:基本不会用到,暂时不做解释

接下来我们来使用connect:

cpp 复制代码
/*只要点击按钮,按钮上就会出现"哈哈"*/

void Widget::handler()
{
    button->setText("哈哈");
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    button = new QPushButton(this);//创建按钮对象,button是widget的成员属性
    button->move(100,100);//将button安置到某位置

    //建立关联关系,&QPushButton::clicked是QAbstractButton的给我们提供的信号,
    //继承这个类的其他类型的按钮也有这个信号
    connect(button,&QPushButton::clicked,this,&Widget::handler);//建立关联关系
}
cpp 复制代码
/*只要点击按钮,窗口就会关闭*/

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    button = new QPushButton(this);//创建按钮对象,button是widget的成员属性
    button->move(100,100);//将button安置到某位置

    //建立关联关系,&QPushButton::clicked是QAbstractButton的给我们提供的信号,
    //继承这个类的其他类型的按钮也有这个信号
    //&Widget::close是widget提供给我们的槽
    connect(button,&QPushButton::clicked,this,&Widget::close);//建立关联关系
}

顺便提一下,以前connect函数的第二和第四个参数都是char*类型的,因此每次传参都要把实参用宏强转成char*,但是现在重载了更好用的版本。

自动关联方式

右击选中想要设置信号和槽的控件,然后选择'转到槽':

然后我们选择一个按钮发射的信号:

然后我们发现widget的成员函数多了一个(头文件和源文件都写好了):

接下来我么只要在该函数编写信号的处理过程即可,该函数就是pushbutton按钮发出发射信号后所执行的槽函数。并且我们不需要手动关联信号源,信号,接收对象,槽,而是Qt通过命名方式自动关联的:

接下来我们使用一下这种方式:

cpp 复制代码
/*点击按钮,按钮上出现"哈哈"*/

void Widget::handler()
{
    button->setText("哈哈");
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

}

Widget::~Widget()
{
    delete ui;
}
void Widget::on_pushButton_clicked()
{
    ui->pushButton->setText("哈哈");
}

当我们查看ui文件生成的类,可以发现:

这个函数就是开启名字匹配的方式。


disconnect解除关联

diconnect函数和connect用法一样,只不过他是把关联关系解除


自定义槽

上面已经使用过自定义槽和Qt提供的槽(如close)。自定义槽其实就是编写一个成员函数然后传参给connect即可,但是在早期,Qt中自定义槽需要在声明时加**"slot"关键字****来帮助Qt进行元编程(现在不需要了,但是加上也可以),比如下面这样:**

槽还可以是一个lambda表达式,调用connect的时候传入即可**:**

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
     ui->setupUi(this);
     button = new QPushButton(this);
     connect(button,&QPushButton::clicked,this,[this]()->void{this->setWindowTitle("haha");});
}
Widget::~Widget()
{
    delete ui;
}

自定义信号

自定义信号只需要写出函数声明,并告诉Qt这是一个信号(使用signals关键字)即可,但是要保证信号的返回值必须是void。例如这样:

定义信号过后,就可以像正常信号一样关联使用了:

上面程序的意思就是打开窗口后Widget发送信号,然后将Widget内的按钮文本替换成"哈哈"。emit也是Qt添加的关键字,表示发射一个信号,但实际上现在不加emit也可以。


信号和槽的参数

如果要使用参数,信号和槽的对应参数的类型必须一致,但是在个数上信号的参数可以比槽更多(不可以更少),这样可以让槽被更多信号复用,关联。直接举一个使用参数的例子:

在这个例子中,我们实现了按不同按钮就设置不同标题的功能,但是设置标题的槽函数只有一个。


注意

一个类想要定义信号和槽,必须在类声明最开始的地方,写下Q_OBJECT宏:

如果不写这个宏,那么不能定义信号槽,但是使用Qt库提供给我们的信号槽是可以的

相关推荐
用户8055336980321 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript