日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Signals Slots(Qt5)

發布時間:2025/3/8 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Signals Slots(Qt5) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

>Signal-Slot的作用是對象間的通信; Signals-Slots機制是Qt的核心特性, 也可能是Qt和其他大多數框架提供的特性不同的部分;

介紹

>GUI編程中, 當我們改變了一個widget,經常希望另一個widget能被通知到; 通常我們希望各種對象間能互相通信. Example: 用戶點擊了CLOSE按鈕, 我們會想要讓window的close()函數被調用;

>老一點的toolkit包使用callback機制實現通信. callback是指向函數的指針, 如果你希望一個processing function能在一些事件上通知你, 需要傳遞一個函數指針到那個processing function; processing function會在何時的時候調用callback; Callbacks有兩個基本瑕疵: 第一, 不是類型安全的type-safe. 我們永遠不能確定processing function會用正確的參數來調用callback; 第二, callback和processing function有很強的耦合, processing function必須知道要調用哪一個callback;

Signals and Slots
>Qt使用Signals-Slots代替callback技術; signal在一個特定事件發生時被發出; Qt的widgets有很多預定義的signals, 我們可以自定義subclass來添加自己的signals; slot是一個函數, 接收到對應的signal時會被調用; 同樣, Qt有預定義的slots, 我們也可以自定義slots來處理相關的signals;

>signals-slots機制是type-safe的: signal的原型signature必須和接收信號的slot的signature一致;(slot的signature會短一些, 因為它可以忽略多余的參數) signature和編譯器兼容, 所以編譯器可以進行類型匹配; signal和slot是松耦合的: 一個類發出一個signal, 它不會去關心哪個slot接受到; 對于相關聯的signal和slot, Qt的signal-slot機制保證了slot會在合適的時間接收到signal的參數并被調用; Signal-slot可以傳遞任意個數和類型的參數; 完全type safe;

>所有繼承于QObject或Object的subclass的類都可以包含signal和slot; Signal在對象改變狀態時被發送, 對這個事件感興趣的對象可以處理這個Signal; 我們不知道也不關心是否有對象接收到這個發送的信號; 這是真正的信息封裝, 保證對象被當作軟件的組件來使用;

>Slots可以接收Signals, 他們也可以被當作普通的成員函數; 和Signal一樣, Slot也不用知道它是否和Signal連接起來了. 這樣保證了Qt能創建真正獨立的組件;

>你可以將任意多的signals連接到一個slot, 也可以將一個signal連接到任意多的slots; 甚至可以將一個signal連接到另一個signal(當第一個signal被發出時第二個signal會立即被發出)

Small Example

>基于QObject的類能發出signal告訴外界它的狀態改變了, valueChanged(), 同時有一個slot可以接收其他對象的signal; 所有包含signal-slot的類必須在類聲明的開始聲明Q_OBJECT宏, 并且(直接或間接)繼承自QObject類;

class Counter : public QObject {Q_OBJECT public:Counter(QObject *parent = 0) { m_value = 0; }int value() const { return m_value; } public slots:void setValue(int value); signals:void valueChanged(int newValue); private:int m_value; };

?

//Slot由程序員實現 void Counter::setValue(int value) {if (value != m_value) {m_value = value;//發出signal,傳遞新的參數值emit valueChanged(value);} }

>下面的代碼段創建2個Counter對象, 把第一個對象的valueChanged() signal關聯到第二個對象的setValue() slot;

Counter a, b; QObject::connect(&a, &Counter::valueChanged,&b, &Counter::setValue); a.setValue(12); // a.value() == 12, b.value() == 12 b.setValue(48); // a.value() == 12, b.value() == 48

>對象a調用setValue(12), 會發送一個signal valueChanged(12), 對象b會在slot setVaule()接收到, 并調用這個slot; 對象b同樣會發出signal valueChanged(), 但是沒有slot和對象b的valueChanged()連接, signal會被忽略;

Note?setValue()只有在value != m_value的時候才會發出signal. 這樣可以防止signal-slot環形關聯導致無限循環的情況; (e.g. b.vauleChanged() 和 a.setVaule()互相關聯)

>默認情況下, 你創建一個信號關聯就應該有一個signal發出; 重復的關聯會有兩個或以上的signal發出; 你可以調用一個disconnect()打斷這些關聯; 使用Qt::UniqueConnect類型參數, 這樣只有在不重復的情況下, 信號關聯才會建立; 如果已經有了一個重復的關聯(同樣的object上的完全一樣的signal和完全一樣的slot), connect()會失敗并且返回false;

>這個例子說明對象間不需要知道對方的任何信息, 照樣可以互相通信; 為了實現通信機制我們只需要將對象互相關聯, 調用簡單的QObject::connect(), 或者使用uic的自動關聯特性(名字關聯 QMetaObject::connectSlotsByName(this)); ??

構建Example

>C++預處理會改變或者移除signal-slot和emit關鍵字, 這樣編譯器可以按照標準C++來處理代碼;

>對于包含signal-slot的類定義, 進行moc會產生一個C++源文件, 這個文件需要和其他的object文件一同編譯和鏈接; 如果你使用qmake, makefile規則中自動調用moc的部分會加到你工程中的makefile;

Signals

>Signals會在對象的內部狀態改變的時候被發出, 狀態的改變可能被對象的client或owner所注意; Signals是public的函數, 可以在任何地方被發出, 但是我們推薦只在定義signal的類或者子類中發signal;

>當一個signal被發出, 相關聯的slot一般會立即被執行, 就像普通函數的調用; 這個情況下, signal-slot機制完全獨立于任GUI的事件循環event loop; 在emit代碼段后面的代碼會在所有的slots都返回了以后被執行; 當使用queued connections的時候情況稍有不同, queued情況下在emit關鍵字后面的代碼還會繼續立即執行, slots則會在稍后執行;

>如果多個slots關聯到了一個signal上, 這些slots會一個接一個地被執行, 先后次序是按照它們被connected的次序來排列;

>Signals會被moc自動生成, 并且不能在cpp文件中實現. Signal不可以有返回值.(e.g. 使用void)

Note:?關于參數, 如果signal-slot不用特殊類型的參數, 他們可以更多地被重用; e.g. 如果QScrollBar::valueChanged()試圖使用一個特別類型, 假設是QScrollBar::Ranger, 那么它只能被關聯到特別為QScrollBar設計的slots上了, 想要關聯到其他的input widget上基本不可能;

Slots

>Slot會在關聯的signal被發送的時候被調用; Slot是普通的C++函數, 可以被正常調用; 它們的特別之處只是可以和signal關聯起來;

>直接調用Slot時它就是普通的函數, 遵循一般的C++規則. 不過, 作為Slot, 忽略權限級別的話, 它可以被任何組件通過signal-slot關聯來激發. 這表示隨意一個類的實例發送了一個signal, 都可能激發其他不相關的類的實例的私有slot.

>Slot也可以定義成virtual的, 在實際使用中很有用;

>雖然在實際的應用程序中區別很小, 但是和callback相比, signal-slot會稍微慢一些, 因為它提供了更多的靈活性. 不考慮虛函數調用時, 普遍來說, 發出一個和一些slots相關聯的signal, 大約比直接調用接收函數receivers慢10倍. 主要的消耗是在: 安全地遍歷所有的connections, 鎖定關聯的對象(檢查后續的receivers沒有在發送的過程中被銷毀), 然后按照通用的方式安置參數; 如果有10個非虛函數被調用, 聽起來好像很多, 實際的損耗比任何new或delete操作要小很多. e.g. 當你在操作一個string, vector或list的時候, 如果需要new或delete, signal-slot的消耗只占了整個函數花費的效率很小的一個比例; 如果你調用系統函數或者間接調用10個函數, 情況是類似的; 簡單和靈活是signal-slot機制值得那一點小小消耗的理由;

Note?如果有其他庫定義的變量調用了signals和slots, 可能會在同時編譯Qt-based應用的時候導致編譯器的warnning和error. 解決的辦法是 #undef掉這些offending預編譯符號;

Meta-Object信息

>meta-object compiler(moc)會在一個C++文件中解析類的聲明并且生成C++代碼, 初始化meta-object. meta-object包含了所有signal和slot成員的名字, 還有指向這些函數的指針;

>meta-object還包含其他信息: 比如對象的類名. 你可以檢查對象是否繼承自某個特定的類; example:

?

if (widget->inherits("QAbstractButton")) {QAbstractButton *button = static_cast<QAbstractButton *>(widget);button->toggle(); }

>meta-object信息也可以被qobject_cast<T>()使用, 和QObject::inherits()類似, 但是更加安全less error-prone;

?

if (QAbstractButton *button = qobject_cast<QAbstractButton *>(widget))button->toggle();

實際例子

>LcdNumber繼承自QObject, 通過QFrame和QWidget具備signal-slot機制; 某種程度上和內建的QLcdNumber類似;

>Q_OBJECT宏會被預編譯展開來去聲明多個被moc實現的成員函數; 如果你在編譯時遇到"undefined reference to vtable for LcdNumber"的錯誤, 你可能忘了先要運行moc命令, 把moc命令輸出的moc文件Include進來.

?

//略過moc不關心的一些析構函數和成員函數 class LcdNumber : public QFrame {Q_OBJECT public:LcdNumber(QWidget *parent = 0); signals:void overflow(); public slots:void display(int num);void display(double num);void display(const QString &str);void setHexMode();void setDecMode();void setOctMode();void setBinMode();void setSmallDecimalPoint(bool point); };

>如果你繼承自QWidget, 基本上肯定需要在構造函數中加上parent參數, 把它傳遞給基類的;

>當LcdNumber被要求顯示一些非法的值時, 會發送overflow() signal;

>如果你不關心溢出, 或者知道不可能發生溢出, 你可以忽略overflow() signal; 可以不把它關聯到任何slot上;

>相反如果你想要在溢出時調用兩個不同的錯誤處理函數, 簡單地關聯到兩個不同的slots就行; Qt會按照關聯的次序調用兩個函數;

>Slot是被用來獲得其他widget狀態改變的信息的接收函數; 在示例代碼中, LcdNumber使用它去設置顯示的數字; 因為display()是類的一個接口, 所以這個slot設置為public;

>多個示例程序關聯QScrollBar的valueChanged() signal到了dispaly() slot, 因此LCD數字會不停得在scrollbar上顯示;

Note?display()被重載overload了; 當你把一個signal和一個slot關聯起來, Qt會選擇適合的版本; 如果是使用callback, 你將不得不自己來找出5個不同的函數名字并且控制不同的類型;

具有默認參數的Signals和Slots

>signal和slot的原型可能包含了參數, 參數可能有默認值. 考慮QObject::destroyed():

void destroyed(QObject* = 0);

>當一個QObject被刪除, 它會發出QObject::destoryed() signal. 我們想要捕獲這個signal, 不論在哪我們可能有一個dangling reference指向被刪除的QObject, 這樣我們可以清除它; 一個合適的slot原型可能是:

void objectDestroyed(QObject* obj = 0);

>有多種方式使用QObject::connect()來關聯signal-slot, 第一種是以函數指針:

connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed);

>使用函數指針有很多優點. 1) 允許編譯器檢查signal的參數是否和slot的參數兼容; 需要的話參數也能被編譯器隱式地轉換;

>你也可以使用C++11 lamdas表達式:

connect(sender, &QObject::destroyed, [=](){ this->m_objects.remove(sender); });

Note?如果你的編譯器不支持C++ 11可變參數模板 variadic templates, 這個語法只能在signal和slot具有小于或等于6個參數的情況下有效;

>還有一個方法是使用SIGNAL和SLOT宏. 關于是否在SIGNAL()和SLOT()宏中引入參數; 如果參數有默認值, 規則是傳到SIGNAL()中的函數原型的參數個數必須少于傳到SLOT()中的函數原型;

//以下這些都能工作 connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(Qbject*))); connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed())); connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));

?

?

//這個無法工作,因為slot預期的是接收一個參數QObject,這個signal不會發出參數, connection會報錯; connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed(QObject*)));

Note?當使用宏的QObject::connect()重載, 編譯器不會檢查signal-slot的參數;

更多Signal-Slot的使用

>如果你想得到發送signal的sender的信息, Qt提供了QObject::sender()函數, 返回一個指向發送signal的對象的指針;

>當遇到很多signals關聯到一個相同的slot的情況, 并且這個slot需要對每個signal作出不同的處理時, 可以用QSignalMapper類;

>假設你有三個push buttons, 用來決定打開哪種文件: Tax File, Accounts File, Report File.

>為了打開正確的文件, 使用QSignalMapper::setMapping()把所有的clicked() signals和QSignalMapper對象map起來; 然后把文件的QPushButton::clicked() signal和QSignalMapper::map() slot關聯起來;

?

signalMapper = new QSignalMapper(this); signalMapper->setMapping(taxFileButton, QString("taxfile.txt")); signalMapper->setMapping(accountFileButton, QString("accountsfile.txt")); signalMapper->setMapping(reportFileButton, QString("reportfile.txt")); connect(taxFileButton, &QPushButton::clicked,signalMapper, &QSignalMapper::map); connect(accountFileButton, &QPushButton::clicked,signalMapper, &QSignalMapper::map); connect(reportFileButton, &QPushButton::clicked,signalMapper, &QSignalMapper::map);

>最后, 把mapped() signal和不同文件打開時調用的readFile() slot關聯起來, 不同的按鈕按下會打開不同的文件;

?

connect(signalMapper, SIGNAL(mapped(QString)),this, SLOT(readFile(QString)));

Qt使用第三方Signals-Slots

>Qt可以使用第三方3rd Party signal-slot機制. 你甚至可以在一個項目里同時使用兩種機制. 需要做的就是把下面一行代碼加到qmake項目文件(.pro)中.

CONFIG += no_keywords

>這行代碼告訴Qt不要去定義moc關鍵字signals, slots, emit, 因為這些名字會被第三方庫使用. e.g, Boost. 在定義了no_keywords標簽的情況下繼續使用Qt, 把源代碼中Qt關鍵字簡單地替換成相應的Qt宏: Q_SIGNALS(or Q_SIGNAL), Q_SLOTS(Q_SLOT)和Q_EMIT;

在Qt5中處理signals-slots的重載

>新的Qt5的語法為了解釋和關聯正確的重載函數會進行顯式地轉換. ClassA定義了兩個重載函數作為signal;

?

class ClassA : public QObject {Q_OBJECT ... signals:void mySignal(double d);void mySignal(QString s); ... };

>假設有個ClassB的實例b, 它的slot有一個QString參數, 那么將ClassA的實例a上重載的第二個signal與其關聯的正確方式是:

?

connect(&a, static_cast<void (ClassA::*)(QString)>(&ClassA::mySignal), &b, &ClassB::mySlot);

>如果有多個重載的slot, 用同樣的方式來connect;

End

<Refer to>?http://qt-project.org/doc/qt-5.0/qtcore/signalsandslots.html#signals-and-slots

轉載于:https://www.cnblogs.com/roymuste/archive/2013/04/18/3029621.html

總結

以上是生活随笔為你收集整理的Signals Slots(Qt5)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。