使用QSignalMapper处理信号

接上篇文章,使用网格管理布局后,每个子Widget都是相同的,也就是有相同的信号与槽。对于上层的SpaceContainerWidget来说,需要根据区别接收信号的控件,从而发送不同的信息给不同的控件。

具体来说,多个子控件具有相同的信号,上层父控件需要根据其在网格中的位置执行相应的动作,但子控件不知道自己在网格中的位置,所以子空间发出的信号不具有位置信息。我们可以在父控件中将不同子空间与不同的槽连接到一起,但这样使代码简单重复。因为子控件除了位置信息外,其他信息都相同,所以一个只有一个位置参数的函数即可处理所有子控件。我们需要将子控件的无参数信号和父控件有位置参数的槽相连。但Qt中的连接以信号的参数为准,槽中多余的参数被忽略掉。换一种方式,将子控件的无参数信号转换为父控件中有位置参数的信号,这样父控件中连接信号、槽即可。
举个例子,设置某个子控件为当前控件。

过程与上段描述类似。
详细流程

某动作激活某子控件为当前控件,调用WorkSpaceWidget的槽slotSetCurrent,该函数发送信号signalSeleted,告诉父控件“我是当前控件”。

父控件接收到某个子控件发送的singalSeleted信号,发现该信号与QSingalMapper相连,信号映射器中已经建立了子控件与某个以int为参数的信号的映射关系,所以父控件发送信号signalSetCurrentWorkSpaceIndex(int)。该信号激活父控件的槽slotSetCurrentWorkSpaceIndex(int),在槽内调用非index的子控件的slotSetNoncurrent()函数设置其他子控件为非激活状态。
下面介绍以上任务中如何使用Qt提供专门处理信号映射的类——QSignalMapper
每次更新网格布局时,会重新建立信号映射关系。现将每个子控件的信号与QSignalMapper的map()槽相连,并设置相应的映射关系。再将QSignalMapper的mapped信号与另一个信号相连。
[cpp]
void SpaceContainerWidget::connectWorkSpaceSelectionSignalMapper()
{
// TODO: we need disconnect previous signals before.
for(int i=0;i<work_space_widgets_.size();i++) { connect(work_space_widgets_[i],SIGNAL(signalSelected()),work_space_selection_signal_mapper_,SLOT(map())); work_space_selection_signal_mapper_->setMapping(work_space_widgets_[i],i);
}
connect(work_space_selection_signal_mapper_,SIGNAL(mapped(int)),
this,SIGNAL(signalSetCurrentWorkSpaceIndex(int)));
}
[/cpp]
某动作激活子控件的signalSelected信号。
[cpp]
void ImageViewer::WorkSpaceWidget::on_current_radio_toggled(bool checked)
{
// TODO: error?
if(checked)
{
slotSetCurrent();
emit signalSelected();
}
else
{
;
}
}
[/cpp]
父控件接收本身的signalSetCurrentWorkSpaceIndex信号。
[cpp]
connect(this,SIGNAL(signalSetCurrentWorkSpaceIndex(int)),this,SLOT(slotSetCurrentWorkSpaceIndex(int)));
[/cpp]