Linux下應用程式開發:QT的內部程式通訊(轉)

BSDLite發表於2007-08-12
Linux下應用程式開發:QT的內部程式通訊(轉)[@more@]  Qt 作為一種跨平臺的基於 C++ 的 GUI 系統,能夠提供給使用者構造圖形使用者介面的強大功能。自從 1996 年 Qt 被 Trolltech 公司釋出以來,該系統成為世界上很多成功的圖形使用者應用所使用的主要系統。更為重要的是,Linux 作業系統的桌面環境系統 KDE 也是基於 Qt 構造的。目前,Qt 已經提供了對包括 MS/Windows、Unix/X11 和嵌入式平臺的支援,得到了越來越廣泛的應用。
  
  在 Qt 系統中,不僅有著構造完善的系統結構,而且為了滿足使用者對編寫圖形使用者介面應用的種種需求,它還建立了許多新的系統機制,其中 Qt 所特有的內部程式通訊機制尤其值得一提。 本文分析了基於 QT 的應用程式之間通訊常用的三種機制:QCOP 協議,Signal-Slot 機制和 FIFO 機制。給出了各自的使用方法,並指出了各自的使用場合。
  
  1、 QCOP協議
  QCOP 是 Qt 內部的一種通訊協議,這種協議用於不同的客戶之間在同一地址空間內部或者不同的程式之間的通訊。目前,這種機制還只在 Qt 的嵌入式版本中提供。
  
  為實現這種通訊機制,Qt 中包括了由 QObject 類繼承而來的 QCopChannel 類,該類提供了諸如 send()、isRegistered() 等靜態函式,它們可以在脫離物件的情況下使用。為了在 channel 中接收通訊資料,使用者需要構造一個 QCopChannel 的子類並提供 receive() 函式的過載函式,或者利用 connect() 函式與接收到的訊號相聯絡。
  
  值得一提的是,在 Qt 系統中,只提供了 QCOP 協議機制和用於接收訊息的類,而如何傳送訊息則沒有提供相應的類供使用者使用。
  
  在基於 Qt 的桌面系統 Qtopia(QPE)中,則提供了相應的傳送類:QCopEnvelope。使用者可以透過該類利用 channel 向其他程式傳送訊息。該類將透過 QCopChannel 傳送 QCop 訊息的過程進行了封裝,使用者只需要呼叫該類中的相關函式就可以方便地實現程式之間的通訊過程。一方面,QCop 訊息的傳送要利用 QCopEnvelope 類,另一方面,接收訊息則是透過與一個 QCopChannel 相關聯。
  
  在傳送訊息時,將利用如下的協議機制:
  
  QCopEnvelope e(channelname, messagename);
  
  對於需要攜帶引數的訊息,必須使用"<  
  e << parameter1 << parameter2 << ...;
  
  對於不帶引數的訊息,只需要利用:
  
  QCopEnvelope e(channelname, messagename);
  
  在Qtopia中,所有的channels名都以"QPE/"開始,而messagename則是一個函式的識別符號。
  
  在接收訊息時,通常只需要利用在應用程式中預先定義好的QPE/Application/{appname}管道,當然,也可以根據需要自己定義管道,並將其與一個slot函式相關聯:
  
  myChannel = new QCopChannel( "QPE/FooBar", this );
  connect( myChannel, SIGNAL(received(const QCString &, const QByteArray &)),
       this, SLOT(fooBarMessage( const QCString &, const QByteArray &)) );
  
  下面將具體的通訊過程舉例如下:
  
  在需要接收訊息的類(如Window1)中定義管道:
  
   QCopChannel *doChannel = new QCopChannel("QPE/Do", this);
   connect(doChannel, SIGNAL(received(const QCString &, const QByteArray &)), this, SLOT(doMessage(const QCString &, const QByteArray &)));
  
  同時,需要在該類中定義相應的訊息處理函式doMessage,
  
  void Window1::doMessage(const QCString &msg, const QByteArray &args)
  {
   QDataStream stream(args, IO_ReadOnly);
   if(msg == "Message1(QString)")
   {
   QString text;
   stream >> text;
   button->setText(text);
   }
   else if(msg == "Message2()")
   {
   close();
   }
  }
  
  其中的Message1(QString)和Message2(QString)都是使用者自己定義的訊息,該函式中分別對這些訊息進行了相應的處理。在該例中當收到帶有引數的Message1訊息時,將該字串引數stream顯示在按鈕button上;當收到Message2訊息時,將執行關閉Window1視窗的動作,當然使用者可以根據需要自行編寫相應的處理過程。
  
  另一方面,在類Class2中需要發出訊息的函式function中利用QCopEnvelope傳送訊息:
  
  
  void Class2::function()
  {  QCopEnvelope e("QPE/Do", "Message1(QString)");
    e << param; }
  
  這裡發出了Message1訊息,並將需要攜帶的引數param傳送到管道中。
  
  透過這樣的過程,使用者可以很方便地實現不同物件、不同程式之間通訊過程,而且可以根據需要在通訊過程中任意傳遞引數。
  
  2、 訊號-槽(Signal-Slot)機制
  在Qt中,有一種用於物件之間的通訊:訊號-槽機制,這種機制是Qt的核心機制,也是它區別於其他GUI工具的最主要的特徵。在大多數GUI工具中,通常為可能觸發的每種行為定義一個回撥函式,這個回撥函式是一個指向函式的指標。在Qt中,訊號-槽機制取代了這種繁雜的函式指標,能夠實現同樣的功能。訊號-槽機制可以攜帶任意型別、任意數量的引數,而且完全是安全的,不會引起系統的崩潰。
  
  所有由QObject類繼承而來的類,或者是它的一個子類,都可以包括訊號-槽機制。訊號通常是當物件改變他們的狀態時發出的,這就是一個物件在需要與其他物件通訊時所需要做的一切,它並不知道是否有其他物件在另一端接收該訊號。從這個意義上來說,這種機制實現了真正的資訊封裝,確保了物件可以被當作一個獨立的軟體構件來使用。
  
  而槽可以被用於接收訊號,它們通常是類中的成員函式。一個槽並不知曉是否有一個訊號與自己相聯絡,同樣,包含有槽函式的物件也對通訊機制一無所知,它們也可以作為一個獨立的軟體構件。
  
  使用者可以按照需要將許多訊號與一個單獨的槽函式相聯絡,一個訊號也可以按需要被聯絡到很多不同的槽函式。甚至還可以將一個訊號直接與另一個訊號相聯絡,這樣當第一個訊號被髮出時立刻發出第二個訊號。
  
  這樣,訊號-槽相結合就產生了一種功能強大的程式設計機制。
  
  例如:
  
  button = new QAction(tr("button"), QIconSet(QPixmap("button.png")), 0, 0, this);
  connect(button, SIGNAL(activated()), this, SLOT(slotButton()));
  
  程式中定義了一個按鈕,並利用connect()函式將該按鈕button的activated()訊號與slotButton()函式相關聯,當使用者觸發按鈕時,就會執行相應的槽函式。當然,這裡的訊號是QAction類中預先定義好的訊號,使用者在使用該機制時,可以根據需要自行定義訊號,同時在適當的時候利用emit語句發出該訊號。另外,在訊號和相應的槽函式之間還可以傳遞任意引數,如:
  
  emit signal(parameter);
  
  3、 FIFO機制
  當然,除了 Qt 內部所特有的通訊機制之外,一般作業系統中常用的程式間通訊機制同樣可以用於 Qt 系統內部不同程式之間的通訊。如訊息佇列、共享記憶體、訊號量、管道等機制,其中有些機制,如訊號量,在 Qt 中重新進行了封裝;有些機制則可以直接呼叫作業系統的系統呼叫來實現。這裡,有名管道是一種簡單實用的通訊機制,使用者在對Qt內部機制
  
  不甚瞭解的情況下,同樣可以使用這種方法實現物件程式之間的通訊。下面就對利用這種機制實現Qt內部程式之間的通訊過程進行介紹。
  
  首先,需要建立 FIFO,這個過程類似於建立檔案,在系統中可以利用 mkfifo 命令來建立,這樣就可以用 open 函式開啟它,同時,一般的檔案 I/O函式(close、read、write)都可以用於 FIFO。
  
  在基於 Qt 的應用中,有很多應用採用了一種客戶機-伺服器模式,這時就可以利用 FIFO 在客戶機和伺服器之間傳遞資料。例如,有一個伺服器,它負責接收底層程式發來的訊息,同時,它與很多客戶機有關,伺服器需要將收到的不同訊息傳送到不同的客戶機,而每個客戶機也有請求需要發給伺服器,進而發給底層程式。
  
  下面是伺服器端的程式示例:(架設已有客戶端程式為讀而開啟/dev/fifoclient1和/dev/fifoclient1)
  
  fd = open("/dev/fifoserver", O_NONBLOCK|O_RDONLY);
    file = fdopen(fd, "r");
  ret = fgets(buf, MAX_LINE, file );
   if(buf[0] == '0')
   {
    QFile fd_file("/dev/fifoclient1");
    QString temp(buf);
    if(fd_file.open(IO_WriteOnly|IO_Append)) {
       QTextStream t(&fd_file);
   t<< temp;
    fd_file.close();
   }
   else if(buf[0] == '1')
   {
    QFile fd_file("/dev/fifoclient2");
    QString temp(buf);
    if(fd_file.open(IO_WriteOnly|IO_Append)) {
       QTextStream t(&fd_file);
   t<< temp;
    fd_file.close();
   }
  ……
  
  在該程式中,伺服器接收底層發來的資訊(這裡假設也是由 FIFO 管道傳來),然後根據收到的資訊內容,如第一個位元組的內容,將資訊發到不同客戶端的管道中,實現對資訊的正確分發。
  
  客戶端程式示例如下:(假設伺服器端已經為讀而開啟 /dev/fifo 管道)
  
  QFile out_file("/dev/fifo");
   if(out_file.open(IO_WriteOnly|IO_Append)) {
   QTextStream t(&out_file); 
  t << text << " "; }

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617542/viewspace-949515/,如需轉載,請註明出處,否則將追究法律責任。

相關文章