巧用QSignalMapper分類有序地處理大量信號(含源碼)

QSignalMapper這個類並不是個新鮮概念, 早在Qt2裏就已經存在, 而且它的功能也是始終如一。 不過由於宣傳力度不夠(例子裏涉及到它的很少)瞭解這個類人可能還不是很多, 所以特此撰文介紹此類的功能和用法。

簡單的理解,可以把SignalMapper這個類看成是信號的翻譯和轉發器, 它可以把一個無參數的信號翻譯成帶int參數、QString參數、QObject*參數或者QWidget*參數的信號, 並將之轉發。 這麼一說大家有沒有聯想到該類的適用範圍呢? 呵呵, 是不是一下就想到了如果我有一堆的button, 可以把clicked事件放在一個函數裏處理, 只要給button編個號或者給button起個名就行了, 這樣就不用給每個button寫一個slot了,豈不是很方便?

下面這段代碼就實現了該功能:

//mainwin.h
class MainWin : public QWidget
{
Q_OBJECT
public:
MainWin(QWidget *parent = 0);
private slots:
void doClicked(const QString & btnname);//處理最終信號的槽
private:
QSignalMapper *signalMapper;
};

//mainwin.cpp
MainWin::MainWin(QStringList texts, QWidget *parent) : QWidget(parent)
{
QString buttontext = "btn1,btn2,btn3,btn4,btn5,btn6,btn7,btn8,btn9,btn10";//10個button
QStringList texts = buttontext.split(",");
signalMapper = new QSignalMapper(this);
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < texts.size(); ++i)
{
QPushButton *button = new QPushButton(texts[i]);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map ()));
//原始信號傳遞給signalmapper
signalMapper->setMapping (button, texts[i]);
//設置signalmapper的轉發規則, 轉發爲參數爲QString類型的信號,
// 並把texts[i]的內容作爲實參傳遞。
gridLayout->addWidget(button, i / 3, i % 3);
}

connect(signalMapper, SIGNAL(mapped (const QString &)),
this, SLOT(doClicked(const QString &)));//將轉發的信號連接到最終的槽函數

setLayout(gridLayout);
}

void MainWin::doClicked(const QString& btnname)
{
QMessageBox::information(this, "Clicked", btnname + " is clicked!");//顯示被按下的btn名稱。
}

從這個例子來看QSignalMapper的用法是非常簡單的, 也很容易理解。

*首先把原始不帶參數的信號連接到signalmapper的map槽函數, 這樣signalmapper能在第一時間接收到原始信號;
*其次調用setMapper方法告訴signalmapper怎樣去處理原始信號。 在這個例子中是把原始信號轉化爲一個帶QString參數的信號
*最後接收轉化後的帶參數信號, 這裏所把轉化後的信號與槽函數連接, 在槽函數中獲得需要的數據。

QSignalMapper類的功能核心是要建立一個從發出原始信號的object到需要的數據的映射(setMapper函數), 如果你的程序恰巧需要這樣的功能,那麼當然QSignalMapper就是當仁不讓的最佳選擇。 除了上述最常見的用法, 我們也來開動腦筋想想還有什麼別的場合適合使用這個類呢?

在Qt的examples裏僅有一個例子用到了QSignalMapper這個類, 在examples/mainwindow/mdi/mainwindow.cpp裏。 它的用法是將QWidget指針作爲參數, 然後菜單選中的信號映射到子Window的指針, 最終由QMainWindow來處理, 用於實現子窗口的切換。 這個用法很有些意思, 可以說是QSignalMapper的最佳用例, 建議大家學習一下。 這裏截取其中最核心的代碼, 有註釋應該很好看懂吧, 如果還是有問題就留言:

//主類從QMainWindow派生
MainWindow::MainWindow()
{
//...
windowMapper = new QSignalMapper(this);
connect(windowMapper, SIGNAL(mapped (QWidget *)),
this, SLOT(setActiveSubWindow(QWidget *)));
//轉發的信號直接連接到QMainWindow的setActiveSubWindow槽
//...
}

void MainWindow::updateWindowMenu()
{
//...
QList windows = mdiArea->subWindowList();
separatorAct->setVisible(!windows.isEmpty());
for (int i = 0; i < windows.size(); ++i)
{
MdiChild *child = qobject_cast(windows.at(i)->widget());
QString text;
if (i < 9)
{
text = tr("&%1 %2").arg(i + 1) .arg(child->userFriendlyCurrentFile());
}
else
{
text = tr("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile());
}
QAction *action = windowMenu->addAction(text);
action->setCheckable(true);
action ->setChecked(child == activeMdiChild());
connect(action, SIGNAL(triggered()), windowMapper, SLOT(map ()));//監控action的triggered信號
windowMapper->setMapping (action, windows.at(i));//建立action指針到QWidget*的映射
//...
}



來源:http://www.cuteqt.com/blog/?p=512
發佈了43 篇原創文章 · 獲贊 11 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章