内容参考于《Qt Creator快速入门》(第三版)
目的:使用c++,不是为了深入学习c++也不是为了学习QT
二.信号-槽
1. 基本示例
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
mydialog.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
private:
Ui::MyWidget *ui;
// 声明this中的槽(可以直接理解为处理函数handler)
public slots:
void showChildDialog();
};
#endif // MYWIDGET_H
mydialog.c
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QDialog>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
// 创建一个信号(showChildButton的clicked信号)-槽(this的showChildDialog槽)
connect(ui->showChildButton, &QPushButton::clicked,
this, &MyWidget::showChildDialog);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::showChildDialog()
{
// 弹出一个对话框
QDialog *dialog = new QDialog(this);
// 模态对话框(不处理就无法操作其他)
dialog->setModal(true);
dialog->show();
}
自动生成的ui_mywidget.h
/********************************************************************************
** Form generated from reading UI file 'mywidget.ui'
**
** Created by: Qt User Interface Compiler version 5.12.0
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_MYWIDGET_H
#define UI_MYWIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MyWidget
{
public:
QPushButton *showChildButton;
QLabel *label;
void setupUi(QWidget *MyWidget)
{
if (MyWidget->objectName().isEmpty())
MyWidget->setObjectName(QString::fromUtf8("MyWidget"));
MyWidget->resize(400, 300);
// 按钮控件
showChildButton = new QPushButton(MyWidget);
showChildButton->setObjectName(QString::fromUtf8("showChildButton"));
showChildButton->setGeometry(QRect(150, 210, 75, 23));
label = new QLabel(MyWidget);
label->setObjectName(QString::fromUtf8("label"));
label->setGeometry(QRect(80, 80, 141, 41));
retranslateUi(MyWidget);
QMetaObject::connectSlotsByName(MyWidget);
} // setupUi
void retranslateUi(QWidget *MyWidget)
{
MyWidget->setWindowTitle(QApplication::translate("MyWidget", "MyWidget", nullptr));
showChildButton->setText(QApplication::translate("MyWidget", "\346\230\276\347\244\272\345\255\220\347\252\227\345\217\243", nullptr));
label->setText(QApplication::translate("MyWidget", "\346\210\221\346\230\257\344\270\273\347\225\214\351\235\242\357\274\201", nullptr));
} // retranslateUi
};
namespace Ui {
class MyWidget: public Ui_MyWidget {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_MYWIDGET_H
2. 原理
class Demo : public QObject
{
Q_OBJECT
public:
Demo();
int value() const { return val; };
public slots:
void setValue(int);
signals:
void valueChanged(int);
private:
int val;
};
// 由样例可看到,类的定义中有两个关键字slots和signals,还有一个宏Q_OBJECT。
// 在Qt的程序中如果使用了信号与反应槽就必须在类的定义中声明这个宏,
// 不过如果你声明了该宏但在程序中并没有信号与反应槽,对程序也不会有任何影响,
// 所以建议大家在用Qt写程序时不妨都把这个宏加上。使用slots定义的就是信号的功能实现,即反应槽,例如:
void Demo::setValue(int v)
{
if (v != val)
{
val = v;
emit valueChanged(v);
}
}
// 这段程序表明当setValue执行时它将释放出valueChanged这个信号。
// 以下程序示范了不同对象间信号与反应槽的连接。
Demo a, b;
connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
b.setValue(11);
a.setValue(79);
b.value(); // b的值将是79而不是原先设的11
// 在以上程序中,一旦信号与反应槽连接,当执行a.setValue(79)时就会释放出一个valueChanged(int)的信号,
// 对象b将会收到这个信号并触发setValue(int)这个函数。当b在执行setValue(int)这个函数时,
// 它也将释放valueChanged(int)这个信号,当然b的信号无人接收,因此就什么也没干。
由于在样例中使用了Qt特有的关键字,必须用Qt的中间编译工具moc.exe把该段代码转换为无专用关键字的C++代码才能为这些编译程序所解析、编译与链接。
连接信号和槽:
connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
发出信号:
emit f(param);
3. 通过函数名自动关联
on_showChildButton_clicked 由on+对象+信号名组成。这种机制的实现基本是通过.ui文件自动生成,前提条件不限止于依赖于setObjectName指定对象名。
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
private:
Ui::MyWidget *ui;
public slots:
void on_showChildButton_clicked();
};
#endif // MYWIDGET_H
mywidget.c
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QDialog>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::on_showChildButton_clicked()
{
QDialog *dialog = new QDialog(this);
dialog->setModal(true);
dialog->show();
}