Essential Qt 第十章 事件

               在記事本程序ReadMe中,爲了將查詢對話框更好的安裝到主程序中,修改了對話框的關閉事件,使得用於關閉對話框的時候,對話框只是隱藏,而不是真正的關閉。這只是對Qt中有關事件的運用的一小部分,Qt的事件(類)都繼承自QEvent,查詢Qt助手可以在文檔發現,這個類提供了超過一百種Qt事件的支持,查詢對話框中使用到的關閉時間也包含其中,這一章將通過幾個例子詳細說明事件的使用等。
                首先,我們定義一個按鈕,暫且命名爲ClickButton,這是一個繼承自QPushButton,這個類有一個功能,當點擊這個按鈕的時候,他會在按鈕中顯示一段文字信息,要實現這樣的功能,必須修改QPushButton的鼠標點擊事件,首先看下ClickButton.h文件
#ifndef CLICKBUTTON_H__
#define CLICKBUTTON_H__
#include<QPushButton>
#include<QMouseEvent>//註釋1
class ClickButton:public QPushButton
{
  public:
    ClickButton(QWidget* parent = 0);
  protected:
    void mousePressEvent(QMouseEvent* event);註釋2
};
#endif
註釋1: 這個頭文件用於事件函數的參數,稍後還看到QMouseEvent還提供了鼠標點擊時間中有關硬件的支持
註釋2: 這個是QPushButton的鼠標點擊函數,在ReadMe程序中修改對話框關閉事件時就已經說過,Qt的事件函數都是保護函數
                這個類構造函數沒有任何代碼,唯一的改動就是鼠標點擊事件函數,具體實現爲
void ClickButton::mousePressEvent(QMouseEvent* event)
{
  if(event->button() == Qt::LeftButton)//註釋1
    setText("This Is A");
}

註釋1: 鼠標點擊時間QMouseEvent有個成員函數button(),他返回鼠標點擊的硬件類型,究竟左鍵還是右鍵,或者是中鍵,通過這個函數的返回值可以判斷究竟是鼠標的哪個鍵點擊了按鈕,這裏設定爲左鍵點擊後顯示文本,所有判斷返回值爲Qt::LeftButton,當然,如果你希望是點擊右鍵,也可以把返回值判斷爲Qt:RightButton,關於硬件的類型,查詢中關於Qt:MouseButton的文檔可以找到完整的支持列表。
              到這裏已經完成了對鼠標點擊事件的修改,把這個ClickButton類作爲一個單獨的程序顯示出來,鼠標左鍵點擊這個按鈕,按鈕上就會顯示"This Is A"

              當然,這裏只是簡單的修改了一下按鈕的一個鼠標點擊事件,事實上上面的例子中存在一個嚴重的問題,爲了顯示這個問題,這裏再設計一個按鈕,這個按鈕想對於他的基類QPushButton來說,多了一個槽,這個槽的功能也很簡單,在按鈕上顯示一個信息,我們把這個繼承子QPushButton的類成爲ShowNameButton,這個類添加的槽也很簡單
void ShowNameButton::ShowName()
{
  setText("This Is B");
}
               然後設計一個對話框TestEvent,這個對話框上有兩個並列的按鈕,分別是ClickButton和ShowNameButton,這兩個按鈕通過佈局管理器QHBoxLayout安裝到對話框中,然後他們之間有個信號與槽的連接
connect(ButtonA_ClickButton,SIGNAL(clicked()),ButtonB_ShowNameButton,SLOT(ShowName()));
              從這個信號與槽的連接可以看出這個對話框的功能也很簡單,點擊左邊的按鈕(ButtonA),左邊的按鈕顯示信息"This Is A",然後右邊的按鈕(ButtonB)接受到了信號,會調用自身的ShowName()槽函數,所以此時右邊的按鈕也會顯示信息"This Is B".這是這個程序的設計初衷,我們希望程序運行後點擊左邊的按鈕後是這個樣子
Essential Qt 第十章 事件 - Vim - 我只是個路人甲,別在意我
但這個程序實際運行後,鼠標點擊左邊的按鈕後卻是這個樣子
Essential Qt 第十章 事件 - Vim - 我只是個路人甲,別在意我
 
               很顯然,右邊的按鈕沒有顯示他應該顯示的信息,那問題處在哪裏呢?問題處在ClickButton的鼠標點擊事件中,我們重新寫了按鈕的鼠標點擊函數,而在基類QPushButton的鼠標點擊事件函數中,有一個重要的功能就是發射clicked()信號,換言之,在QPushButton的mousePressEvent()函數中有一行類似下面的代碼。
emit clicked();
而上面的例子修改了鼠標點擊事件函數後,很明顯“忘記”了原函數還有這樣一個功能,在最初設計ClickButton的鼠標點擊事件函數的時候“忘記”了他原有的發射信號的功能,導致了這個類功能上的缺陷,爲了彌補這個缺陷,需要在鼠標點擊函數中添加一行 emit clicked();這樣程序就能正常運作了。
              到這裏相信很多人已經發覺問題了,這個程序再添加了發射clicked()的代碼後可以正常運作,但就設計ClickButton類來說他還有缺陷,稍微查詢下文檔就可以知道,鼠標點擊按鈕後不止有一個clicked()信號,這裏只添加了一個clicked().總的來說,我們並不知道基類的鼠標點擊事件函數究竟做了什麼,他究竟發射了什麼信號?也許可以通過查看源代碼來了解鼠標點擊函數究竟做了什麼?但查看源代碼顯然很費事,而且,Qt有一百多種事件,對於不同的類,相同的事件函數功能也不會相同。。。。。
               這個問題解決的最基本方法就是,當需要修改一個類的事件函數時,首先要區分,對於事件來說,究竟是要添加功能還是要修改事件。
               在前面章節的ReadMe程序裏,爲了更好的把查詢對話框FindDialog安裝到主程序中,重寫了他的關閉事件函數,這麼做的目的是因爲程序不需要這個對話框關閉,而只需要他隱藏,所以重新實現了的關閉事件函數,使得窗體的關閉功能變成了隱藏功能,由於不需要原先的關閉事件,所以也無需在乎原來的關閉事件函數的具體功能。
               而在這章的例子ClickButton中,對於鼠標點擊事件,程序需要的是添加一個功能(顯示文本),而原先的功能仍然需要保留,所以對於這樣給事件函數添加功能而不是修改事件的情況,ClickButton的鼠標點擊函數可以這樣寫
void ClickButton::mousePressEvent(QMouseEvent* event)
{
  if(event->button() == Qt::LeftButton)
    setText("This A");//註釋1
  QPushButton::mousePressEvent(event);//註釋2
}
註釋1 這是需要添加的功能
註釋2 調用基類的鼠標點擊函數,這樣基類的鼠標點擊事件所有的功能這個類都會有,包括髮射clicked()信號等

                 在修改事件函數前,區分究竟是添加功能還是修改事件很重要,但這也是指導性建議,並非準則,在實際編程中,很多時候會遇到修改事件時仍需要事件原先的部分功能,就以上面的ClickButton類爲例,有些情況可能需要修改鼠標點擊函數,但仍然需要使用clicked()信號,但並不需要鼠標點擊觸發的其他信號,這樣的情況就需要在修改鼠標點擊事件函數的時候加上這個信號(就像開始做的那樣),實際的編程遇到情況會比較複雜,在修改任何事件函數時都需要注意是否遺漏到他原先的功能。

                 在前面的ReadMe程序中,遺留了一些問題,其中之一就是編輯區域(QTextEdit)的右鍵菜單,上面全是英文的,對於一個程序來說,漢化之個很重要的程序,就Qt而言,可以通過加載.qm文件來實現程序的翻譯   ,這部分內容將在後面的國際化一章中介紹,但機械翻譯機制始終存在一個問題,機器(電腦)翻譯的準確度和精準讀始終無法和人相提並論,以谷歌翻譯爲例,該翻譯就曾把"嫂子"一次翻譯爲"wife".另外,想對於默認的右鍵菜單,自定義的程序可能需要修改菜單上的內容才能滿足需求。      
                  所以對於ReadMe程序來說,子類化QTextEdit來重新實現他的右鍵菜單函數 是一個更好的選擇,這裏定義了一個TextEditCN類,這個類完成後可以替換掉ReadMe程序中的QTextEdit,頭文件定義如下
#ifndef TEXTEDITCN_H__
#define TEXTEDITCN_H__
#include<QTextEdit>
#include<QAction>
#include<QMenu>
#include<QContextMenu>
class TextEditCN:public QTextEdit
{
    Q_OBJETC
  private:
    QMenu* MenuCN_Menu;

    QAction* Redo_Action;
    QAction* Undo_Action;
    QAction* Cut_Action;
    QAction* Copy_Action;
    QAction* Paste_Action;
    QAction* Delete_Action;
    QAction* SelectAll_Action;
  public:
    TextEditCN(QWidget* parent = 0);
  protected:
    void contextMenu(QContextMenu* event); //註釋1
  private slots:
    void DeleteSelectedText();  //註釋2
};
#endif
註釋1: 在Qt文檔中,這個函數被成爲“背景彈出菜單”。。。。請原諒我的鳥語水平一般
註釋2: 由於QTextEdit沒有一共delete功能,需要我們自己實現,在ReadMe中已經實現了這樣一個功能,而在這裏使用的是私有槽,所以這個類替換了ReadMe中QTextEdit也不會有什麼問題,但如果你把這個槽設爲公有槽,那等於這個類帶有刪除功能,ReadMe程序中就不需要再次自定義一個刪除功能的槽,而可以直接調用這個類的槽,如果是這樣,ReadMe中信號與槽的連接也需要做適當的修改,因爲這個槽不在屬於對象this而屬於對象MainEditWindow
             這個類的菜單實現,以及菜單上動作點擊事件和槽的連接相對簡單,具體實現和ReadMe程序中的一樣,這裏就全部放到構造函數中實現,當然也可以使用單獨函數完成,然後在構造函數中調用。最主要的就是“背景彈出菜單”事件函數
void ReadMe::contextMenu(QContextMent* event)
{
  menus->exec(Qcursor::pos());  //註釋1
  event->accept();//註釋2
}
註釋1: Qcursor::pos()返回一個座標,這個座標是鼠標的位置,菜單的調用exec()函數啓動(顯示)
註釋2: 這裏表示接受鼠標右鍵點擊彈出事件

            這個類完成夠可以將ReadMe程序中的QTextEdit替換掉,這樣ReadMe程序的右鍵菜單就是中文的了。


發佈了92 篇原創文章 · 獲贊 19 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章