1 QT信号槽详解
1.1 信号和槽的定义
信号是触发信号,例如按钮的点击触发一个clicked信号,槽是用来接收信号,并处理信号,相当于信号响应函数。一个信号可以关联多个槽函数,信号也可以连接信号。
要使用信号槽,类必须继承与QObject类或者其子类,否则无法识别槽函数错误。在类的定义开头需要添加宏定义Q_OBJECT。如下
class AlarmCenter : public QWidget
{
Q_OBJECT
//用关键字signals定义信号,关键字slots定义槽函数。如下所示:
public slots:
void SlotHideNoPlatPalyerTip(int tip);
signals:
void signalHideNoPlatPalyerTip (int tip);
}
信号和槽函数之间采用connect函数进行连接,采用disconnect函数取消连接。在需要触发信号的地方,采用关键字emit signalHideNoPlatPalyerTip (3);触发信号,调用槽函数,传入参数。
1.2 信号槽的连接方式
1.2.1 按照名称自动关联
自动关联需要响应函数的名称按照格式 on_按钮的名称_信号名称()来书写,并将该函数声明为槽slots。点击按钮的时候就会自动关联该信号到该响应函数。
头文件中:
public slots:
void on_BtnStart_clicked();
源文件中:
void AlarmCenter::on_BtnStart_clicked()
{
int i = 0;
}
1.2.2 Connect连接信号与槽函数
(1)无参数信号槽函数连接
&获取指针需要指定类名称,用宏定义SIGNAL和SLOT可以直接指定函数名称。
QObject::connect(ui.fliterBtn, &QToolButton::clicked, this, &AlarmCenter::DisplayFliterWidget);
connect(ui.fliterBtn, SIGNAL(clicked()), this, SLOT(DisplayFliterWidget ()));
(2)有参数信号槽函数连接
signals:
void valueChanged(int value);
slots:
void AlarmCenter::AlarmListVScrool(int position);
&方式无需添加参数,SIGNAL和SLOT方式则要添加参数,注意使用&方式时,如果槽函数是重载函数,则编译出错,因为没有参数,无法判断是连接哪个槽函数,所以建议采用,SIGNAL和SLOT方式带上参数。多个参数用逗号隔开,只添加形参类型,不加形参实体。
QObject::connect(ui.verticalScrollBarAlarm, &QScrollBar::valueChanged, this, &AlarmCenter::AlarmListVScrool);
QObject::connect(ui.verticalScrollBarAlarm, SIGNAL(valueChanged(int)), this, SLOT(AlarmListVScrool(int)));
对于有重载函数的槽函数,需要用到有参数的连接方式,否则无法判断是哪个槽函数。
(3)自定义结构体参数的信号槽连接
对于自定义的结构体参数,信号槽无法识别参数,导致信号槽连接不起作用。所以需要注册结构体参数。在结构体中声明结束的地方加上结构体注册。
struct DealDetailInfo
{
};
Q_DECLARE_METATYPE(DealDetailInfo);
信号定义
signals:
void signalOnePointUpdateData(QVariant VarDetailInfo);
槽函数定义
void SlotOnePointUpdateData(QVariant VarDealInfo);
信号槽连接
QObject::connect(&m_DealDetail, SIGNAL(signalOnePointUpdateData(QVariant)), this, SLOT(SlotOnePointUpdateData(QVariant)), Qt::QueuedConnection);
发送信号的地方用变量QVariant包装结构体参数
QVariant DataVar;
DataVar.setValue(DetailInfo);
emit signalOnePointUpdateData(DataVar);
接收信号的地方从包装中取出结构体参数
DealDetailInfo DealInfo;
DealInfo= VarDealInfo.value<DealDetailInfo>();
1.2.3 信号与信号连接
当存在复杂的包含关系时,A是B的成员对象,B是C的成员对象。在A中触发的信号需要C中的槽函数响应,则需要连接A中的信号触发B中的信号,B中的信号触发C中的槽函数。例子如下:
Class A
{
signals:
signalChangeColor(int i);
}
Class B
{
signals:
signalChangeColor(int i);
privite:
A a;
}
QObject::connect(&a,SIGNAL(signalChangeColor(int)), this, SIGNAL(signalChangeColor(int)));
Class C
{
public lots:
SlotChangeColor(int i)
privite:
B b;
}
QObject::connect(&b,SIGNAL(signalChangeColor(int)),this, SLOT(SlotChangeColor(int i)));
1.2.4 信号和lambda表达式连接
当槽函数比较简单,没有必要创建一个槽函数去连接,可以直接用一段简短的代码作为信号接收处理。日下所示。
QObject::connect(ui.closeDisPlayImageWndBtn, &QToolButton::clicked, this, [=]()
{
m_imageWidth = 0;
m_imageHeight = 0;
m_iPercent = 100;
m_izoomLevel = 0;
close();
});
或者控件是new出来的,需要关联控件信号到函数
QCheckBox *CheckBox = new QCheckBox(ui.tableWidget);
QSize size(39, 35);
CheckBox->setFixedSize(size);
CheckBox->setCheckState(Qt::Unchecked);
QObject::connect(CheckBox, &QCheckBox::clicked,this, [=]()
{
AlarmTableItemChoosed(step, 0);//成员函数
});
1.2.5 QT Designer界面上连接信号槽
进入QT Designer 界面,点击信号槽编辑按钮,进入信号槽编辑界面,拖动按钮,对按钮的信号槽进行编辑,可以选用已有的槽,或者点击编辑按钮,新增自定义槽。关联之后,在ui_*.h(*表示对话框类名)中会自动添加一行,QObject::connect(BtnStart, SIGNAL(clicked()), AlarmCenterClass, SLOT(sbclslot()));然后在dialog类的文件中实现该槽。
1.3 信号槽的连接响应类型
连接信号与槽的connect()函数原型如下:
bool QObject::connect ( const QObject * sender, const QMetaMethod & signal, const QObject * receiver, const QMetaMethod & method, Qt::ConnectionType type = Qt::AutoConnection );
最后一个参数Qt::ConnectionType是连接类型,不同的连接类型,槽函数的响应策略如下:
Constant |
Value |
Description |
Qt::AutoConnection |
0 |
自动识别。当信号发送者和接收者处于同一线程内时,这个类型等同于DirectConnection,反之等同于QueuedConnection,这个类型也是connect函数的默认连接类型 |
Qt::DirectConnection |
1 |
同步执行。信号一旦发射,与之关联的槽函数立即执行,执行返回后才能执行emit之后的代码,相当于函数调用。 |
Qt::QueuedConnection |
2 |
异步执行。当信号产生,信号会暂时被缓冲到一个消息队列中,不用等待槽函数返回,就会执行后面的代码。等待接收者的事件循环处理去队列中获取消息,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作 |
Qt::BlockingQueuedConnection |
3 |
阻塞执行。这种类型类似于QueuedConnection,但是它只能应用于跨线程操作即发送者和接收者处于不同的线程中的情况,并且信号发送者线程会阻塞等待接收者的槽函数执行结束。如果是同一个线程调用,会造成死锁现象。 |
Qt:: Unique Connection |
0x80 |
作用相当于Qt::AutoConnection。只是防止重复连接。如果当前信号和槽已经连接过了,就不再连接了。 |
1.4 一个信号多个槽函数的调用顺序
存在一个信号连接多个槽函数的情况,槽函数的调用顺序与连接的先后一致,但这只适用于同步调用,异步调用是加入到信号队列中,无法判断调用先后顺序。
1.5 带返回值的信号
大都说Qt信号槽不能使用返回值。Qt5中,信号槽是有返回值的。只是Qt的一个信号可以连接多个槽,还有同步调用和异步调用的问题,没发支持的很好,所以,返回值虽有,但只是鸡肋。同步调用才有返回值,异步调用的返回值永远为返回值类型默认构造函数出来的。连接的多个槽都返回值,那么结果是最后调用(连接)的那个。也就是说对于QueuedConnection连接的信号槽,永远只是返回返回类型的默认构造函数的。对于AutoConnection连接的,如果发出信号的线程和槽函数线程不同亦然。所以只有同步调用或者阻塞调用,才会成功返回值。
bool bReturn;