简介
设想有这样一种应用场景,我们用Qt写一个多标签页(QTabWidget)的文本编辑器,该文本编辑器有一个窗口菜单栏,当我们每点击该菜单栏的子菜单栏时就会将相应的标签页置为活跃状态,如下图所示:
一般我们想到的做法是,利用connect关联点击事件到设置活跃窗口的槽函数上去,但是槽函数如何知道是谁发来的信号呢?其实,Qt为我们提供了信号映射器类QSignalMapper,该类可以识别出信号是由哪个对象发射出来的,它可以收集一系列无参信号并为这些信号带上参数重新发射出去,可以带的参数为整型、字符串或者窗口部件。既然可以带参数,那么,针对上述例子,槽函数就知道是谁发来的信号了,同理也就知道该将哪一个标签页置为活跃状态了。
具体实现
样例代码如下:
try {
QTabWidget * fileTab = new QTabWidget(this);
// 1、实例化QSignalMapper
QSignalMapper * pwinMapper = new QSignalMapper(this);
// 2、关联信号与槽,这里信号所携带的参数为所设置标签页的序号,
// 槽函数setActiveTab的功能为设置指定的标签页为活跃状态
// 注意这个槽函数是我自己写的可不是Qt自带的,其实现核心语句为fileTab->setCurrentIndex(index);
connect(pwinMapper, SIGNAL(mapped(int)), this, SLOT(setActiveTab(int)));
}
catch(std::bad_alloc &memExc) {
qDebug() << memExc.what();
}
void MainWindow::updateWinMenus(void)
{
// 遍历所有标签
for (int i = 0; i < fileTab->count(); i++) {
// 获取指定标签页的窗口部件,MyChild是我自己实现的
MyChild *myChild = qobject_cast<MyChild *>(fileTab->widget(i));
// 获取指定文件的文件名,这个方法是我自己实现的
QString subName = tr("&%1 %2").arg(i + 1).arg(myChild->pureCurrentFile());
// 添加菜单动作,将文件名subName和其关联
QAction *subWinAct = pwindowMenu->addAction(subName);
// 关联菜单点击动作和QSignalMapper的槽map
connect(subWinAct, SIGNAL(triggered()), pwinMapper, SLOT(map()));
// 将指定的对象subWinAct和指定的整型变量i做映射,这样一来点击指定的菜单动作就会发射指定整型变量给槽setActiveTab
pwinMapper->setMapping(subWinAct, i);
}
}
总结
关于信号映射器的使用方法我们可以总结如下:
1、实例化一个信号映射器;
2、关联信号映射器信号mapped和我们所指定的槽函数;
3、关联我们想要捕获的信号和信号映射器的槽函数map;
4、绑定发射信号的实例化对象何其希望传递的参数(可以为整型、字符串或者窗口部件)。
其工作流程简述如下:
我们实例化的对象发射信号给槽map而map发射信号mapped并且携带setMapping()指定的参数给我们指定的槽函数,简言之信号与槽的级联。
补充
上述代码摘自我自己用Qt写的一个多标签页文本编辑器也就是动态图里那个演示的编辑器,github链接:https://github.com/JS-Cao/MultiDocuEditot 大家可以参考一下,别忘了点个星哈,谢谢啦。
另外其实信号映射器有一个替代方案就是lambda,针对上述例子我们可以用lambda的捕获列表捕获每一个指定的参数i,样例代码如下:
connect(subWinAct, &QAction::triggered, [=] { setActiveTab(i); });
还是lambda表达式方便啊。