官术网_书友最值得收藏!

  • Qt 5.12實戰(zhàn)
  • 朱晨冰 李建英
  • 2351字
  • 2021-03-26 21:56:41

3.12 信號

3.12.1 基本概念

Qt為了方便一些事件的處理,引入了信號(Signal)的概念,封裝了一些事件操作的標準預(yù)處理,使得用戶不必去處理底層事件,只需要處理信號即可。Qt還定義了一些預(yù)定義信號。在某些事件處理函數(shù)中會發(fā)送預(yù)定義信號,如果用戶添加了與該信號相連的信號處理函數(shù)(也叫槽函數(shù)),則調(diào)用該槽函數(shù)。當然,并不是所有事件處理函數(shù)都會有信號發(fā)送。除了預(yù)定義信號外,用戶也可以自己發(fā)送自定義信號。

信號與槽(Slot)其實都是函數(shù)。當特定事件被觸發(fā)時(如在編輯框輸入了字符)將發(fā)送一個信號,與之連接的槽則可以接收到并做出響應(yīng)。

信號類似Windows編程中的消息,槽類似消息處理函數(shù)。比如,鼠標的按鈕被單擊,就會發(fā)出名為clicked的信號,如果該信號連接了槽(函數(shù)),就會調(diào)用這個函數(shù)來進行處理。這種發(fā)出是沒有目的的,類似廣播。如果有對象對這個信號感興趣,它就會使用連接(connect)函數(shù),意思是將想要處理的信號和自己的一個函數(shù)(槽)綁定來進行處理。也就是說,當信號發(fā)出時,被連接的槽函數(shù)會自動被回調(diào)。

信號和槽是Qt特有的信息傳輸機制,是Qt程序設(shè)計的重要基礎(chǔ),可以讓互不干擾的對象建立一種聯(lián)系。

槽的本質(zhì)是類的成員函數(shù),它的參數(shù)可以是任意類型,和普通C++成員函數(shù)幾乎沒有區(qū)別,可以是虛函數(shù),可以被重載,可以是公有的、保護的、私有的,也可以被其他C++成員函數(shù)調(diào)用。唯一的區(qū)別是:槽可以與信號連接在一起,每當和槽連接的信號被發(fā)出時,就會調(diào)用這個槽。

信號和槽是多對多的關(guān)系。一個信號可以連接多個槽,一個槽也可以監(jiān)聽多個信號。

信號可以有附加信息。例如,窗口關(guān)閉的時候可能發(fā)出windowClosing信號,這個信號可以包含窗口的句柄,用來表明究竟是哪個窗口發(fā)出的;一個滑塊在滑動時可能發(fā)出一個信號,包含滑塊的具體位置或者新的值等。我們可以把信號和槽理解成函數(shù)簽名。信號只能同具有相同簽名的槽連接起來。也可以把信號看成是底層事件一個形象的名字,比如windowClosing信號就是窗口關(guān)閉事件發(fā)生時會發(fā)出的信號。

信號和槽的機制實際是與語言無關(guān)的,有很多方法都可以實現(xiàn)信號和槽的機制,不同的實現(xiàn)機制會導(dǎo)致信號和槽的差別很大。信號和槽這一術(shù)語最初來自Trolltech(奇趣)公司的Qt庫(后來被Nokia收購)。1994年,Qt的第一個版本發(fā)布后,為我們帶來了信號和槽的概念。這一概念立刻引起計算機科學界的注意,提出了多種不同的實現(xiàn)。如今,信號和槽依然是Qt庫的核心之一。其他許多庫也提供了類似的實現(xiàn),甚至出現(xiàn)了一些專門提供這一機制的工具庫。

3.12.2 信號和槽的連接

這里的連接是關(guān)聯(lián)的意思。信號和槽是通過系統(tǒng)函數(shù)connect()關(guān)聯(lián)起來的。該函數(shù)是信號和槽里最重要的函數(shù),它將信號發(fā)送者sender對象中的信號signal與接收者receiver中的member槽函數(shù)聯(lián)系起來。

需要注意的是,connect()函數(shù)只能在QObject類和QObject派生類中使用,在自己新建的類(基類不是QObject類和QObject派生類)中使用connect()函數(shù)是無效的,編譯時會一直報錯。我們新建的項目(比如widget、mainwindow、dialog)都是QObject類的派生類,所以可以直接調(diào)用connect()函數(shù),實現(xiàn)信號與槽的機制。該函數(shù)的原型聲明如下:

    QMetaObject::Connection QObject::connect(const QObject *sender, const char
*signal, const QObject *receiver, const char *method, Qt::ConnectionType type =
Qt::AutoConnection)

其中,sender是一個指針,指向信號的發(fā)送對象;signal表示要發(fā)送的信號,具體使用時必須要用宏SIGNAL()將信號轉(zhuǎn)為const char*類型;receiver是一個指針,指向信號的接收對象;method表示槽函數(shù)(信號處理函數(shù)),必須使用SLOT宏將其轉(zhuǎn)換為const char*類型;type表示連接類型,可以取以下5個值:

· Qt::AutoConnection:默認值,使用這個值時連接類型會在信號發(fā)送時決定。如果接收者和發(fā)送者在同一個線程中,則自動使用Qt::DirectConnection類型。如果接收者和發(fā)送者不在同一個線程中,則自動使用Qt::QueuedConnection類型。

· Qt::DirectConnection:槽函數(shù)會在信號發(fā)送的時候直接被調(diào)用,槽函數(shù)運行于信號發(fā)送者所在的線程。效果看上去就像是直接在信號發(fā)送位置調(diào)用了槽函數(shù)。這個在多線程環(huán)境下比較危險,可能會造成系統(tǒng)崩潰。

· Qt::QueuedConnection:槽函數(shù)在控制回到接收者所在線程的事件循環(huán)時被調(diào)用,槽函數(shù)運行于信號接收者所在的線程。發(fā)送信號之后,槽函數(shù)不會立刻被調(diào)用,等到接收者的當前函數(shù)執(zhí)行完,進入事件循環(huán)之后,槽函數(shù)才會被調(diào)用。在多線程環(huán)境下一般用這種連接類型。

· Qt::BlockingQueuedConnection:槽函數(shù)的調(diào)用時機與Qt::QueuedConnection一致,不過發(fā)送完信號后發(fā)送者所在線程會阻塞,直到槽函數(shù)運行完畢。接收者和發(fā)送者絕對不能在同一個線程中,否則會死鎖。在多線程間進行同步的場合可能需要這種類型。

· Qt::UniqueConnection:可以通過按位或(|)運算符來把以上4個結(jié)合在一起使用。使用這種類型,當某個信號和槽已經(jīng)連接時,再進行重復(fù)的連接就會失敗,也就是說避免了重復(fù)連接。

該函數(shù)會返回連接句柄,可用于稍后斷開連接的操作。

值得注意的是,在指定信號和方法時,必須使用SIGNAL()和SLOT()宏。下面的代碼演示了connect()函數(shù)的使用:

    QLabel *label = new QLabel;
    QScrollBar *scrollBar = new QScrollBar;
    QObject::connect(scrollBar, SIGNAL(valueChanged(int)), label,
SLOT(setNum(int)));

這段代碼確保標簽始終顯示當前滾動條的值。注意,信號和槽函數(shù)的參數(shù)不能包含任何變量名,只能包含類型,比如信號valueChanged的參數(shù)是int類型,槽函數(shù)的參數(shù)是int類型。例如,以下用法將不起作用并返回false:

3.12.3 信號和事件的區(qū)別

Qt的事件很容易和信號與槽相混淆。信號由具體對象發(fā)出,然后會馬上交給由connect()函數(shù)連接的槽進行處理。對于事件,Qt使用一個事件隊列對所有發(fā)出的事件進行維護;當新的事件產(chǎn)生時,會被追加到事件隊列的尾部;前一個事件完成后,取出后面的事件接著進行處理。但是,必要的時候,Qt事件也是可以不進入事件隊列而直接進行處理的。事件還可以使用“事件過濾器”進行過濾。比如對于一個按鈕對象,我們只關(guān)心它被按下的信號,至于與這個按鈕相關(guān)的其他信號,我們是不用關(guān)心的。如果我們要重載一個按鈕事件處理函數(shù),就要面對事件觸發(fā)的時機。比如我們可以改變它的行為,讓它在按下鼠標按鈕的時候(mouse press event)就觸發(fā)clicked()信號,而不是通常在釋放鼠標按鈕的時候(mouse release event)才觸發(fā)信號。

總而言之,Qt的事件和Qt中的信號是不一樣的。后者通常用來使用widget,而前者是用來實現(xiàn)widget的。如果是使用系統(tǒng)預(yù)定義的控件,那么我們關(guān)心的是信號;如果使用的是自定義控件,那么我們關(guān)心的是事件。

主站蜘蛛池模板: 远安县| 聊城市| 宜兴市| 营山县| 萍乡市| 西乡县| 哈尔滨市| 高尔夫| 新沂市| 奉贤区| 天等县| 犍为县| 迭部县| 宿迁市| 磐安县| 津市市| 海阳市| 德化县| 太康县| 大冶市| 资源县| 平果县| 梁平县| 赤水市| 重庆市| 水富县| 阳江市| 石门县| 伊金霍洛旗| 措勤县| 天全县| 义马市| 鄂托克旗| 都兰县| 黄浦区| 阳高县| 蒙自县| 政和县| 南川市| 赤峰市| 阿图什市|