本站所有文章由本站和原作者保留一切權力,僅在保留本著作權資訊、原文連結、原文作者的情況下允許轉載,轉載請勿刪改原文內容, 並不得用於商業用途。 謝謝合作。原文連結:巧用QSignalMapper分類有序地處理大量訊號(含源碼)
QSignalMapper這個類並不是個新鮮概念, 早在Qt2裡就已經存在, 而且它的功能也是始終如一。 不過由於宣傳力度不夠(例子裡涉及到它的很少)瞭解這個類人可能還不是很多, 所以特此撰文介紹此類的功能和用法。
簡單的理解,可以把SignalMapper這個類看成是訊號的翻譯和轉寄站, 它可以把一個無參數的訊號翻譯成帶int參數、QString參數、QObject*參數或者QWidget*參數的訊號, 並將之轉寄。 這麼一說大家有沒有聯想到該類的適用範圍呢? 呵呵, 是不是一下就想到了如果我有一堆的button, 可以把clicked事件放在一個函數裡處理, 只要給button編個號或者給button起個名就行了, 這樣就不用給每個button寫一個slot了,豈不是很方便?
下面這段代碼就實現了該功能:
//mainwin.hclass MainWin : public QWidget{Q_OBJECTpublic: MainWin(QWidget *parent = 0);private slots: void doClicked(const QString & btnname);//處理最終訊號的槽private: QSignalMapper *signalMapper;};//mainwin.cppMainWin::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*的映射 //...}