Chia´s Small Shop

Chia小鋪,陳列隨手的文字

在VC中利用Qt Designer建立自訂的slot

Qt自己出了個IDE,Qt Creator,支援GNU的編譯器與GDB除錯器
整合度之高,感覺真像是先前使用Borland C++ Builder的經驗一樣
不過,假如已經習慣了VC的編譯環境,就只能望著Qt Creator乾瞪眼嗎?

好在從Qt釋出了Visual Studio add-in (for open-source version)以後
Qt跟VC之間的距離就越來越近了
不用管什麼三部曲、uic與moc
視窗拉一拉,元件放一放,一個可口的GUI就完成了
打造複雜的GUI再也不是夢想!

在設計GUI的過程中,往往會碰到需要自行定義被觸發事件(signal)的處理方法(slot)
signal沒啥問題,通常是按鈕、選單等元件發出(e.g. 滑鼠點擊)
但是slot呢?並不是每個signal都找得到合適的預設slot
比方說,按一個按鈕開啟檔案對話框,然後載入一張圖片
應該找不到一個slot是剛好設定為「開啟檔案對話框」的吧!
這個時候,就需要自訂的slot來解決問題

signal跟slot的宣告,可以依照自己的喜好來設計(不過要照規則唷)
或是由Designer協助建立signal與slot的連結關係
為什麼要由Designer來協助呢?個人認為有兩個好處

1. 由Designer建立signal與slot的連結,不容易出錯

當使用Designer建立連結時,會幫你檢查signal傳送資料的型別與參數量與slot的是否相符
如果兩者的函式簽名對應不起來,你連看到slot的機會都沒有,大大的降低了出錯的可能性

2. 統一管理,程式碼不再亂糟糟

Designer提供了signal/slot編輯器,所有的連結關係都由此編輯器完成
在實做類別中,就不需要再手動寫連結signal/slot的程式碼了

講了一堆,就拿上面提到的例子開始吧~
以下的操作,以VC2008作為編譯環境
Qt版本是4.5.0 (open-source 編譯版),配上Visual Studio add-in 1.0.0
首先,開啟VC,建立一個新的Qt應用程式專案

Open new Qt Application project

Qt Application settings

假設應用程式的class name叫做MyQtApp
設定完成後,點兩下"myqtapp.ui",帶出Qt Designer
就開始畫UI囉

在這個小範例中,需要兩個元件,一個是QLabel,用來顯示影像
另一個就是QPushButton,用來開啟檔案對話框用的
那檔案對話框呢?嗯…必須由程式產生了[註1],所以暫時先不管
最後設計好的視窗看起來會像這樣:

MyQtApp gui design

接著,重點出現了,要設定pushButton的signal/slot連結
按下pushButton,自然會用到clicked()這個signal
但是,要連到哪個slot呢?
利用編輯信號與信號槽的狀態下
發送者選擇pushButton,信號是clicked(bool) [註2]
而對象是我們的應用程式MyQtAppClass,信號槽是空白一片

edit slot

沒關係,按下「編輯」後

signal and slot editor

在信號槽的地方增加一個新的信號槽
這個slot function的名稱隨便你設計,但是簽名要與發送的信號一致
在這裡,引數的型別一定要是bool

new slot

按下「確定」後,原先空白一片的信號槽出現剛剛新增的slot function囉!

connect to the slot

設定連結後,最後可以在信號與信號槽編輯器看到剛剛建立的連結結果

connect result

到這裡,Qt Designer的工作已經完成了
剩下的工作就是coding,定義剛剛新增的slot function

回到VC2008中,開啟myqtapp.h
宣告剛剛新增的slot function,假設放在private好了,因此myqtapp.h看起來就像

class MyQtApp : public QWidget
{
    Q_OBJECT

public:
    MyQtApp(QWidget *parent = 0, Qt::WFlags flags = 0);
    ~MyQtApp();

private:
    Ui::MyQtAppClass ui;

private slots:
    void slotOpenImage( bool );
};

重點就是那第13行,函式名稱要跟剛剛新增的一樣,簽名也要相同
別忘了,slot function一定不可以有返回值(void)
接著,定義slotOpenImage( bool ),就跟尋常的member function沒兩樣了

void MyQtApp::slotOpenImage( bool _b )
{
    QFileDialog file_dialog( this, tr( "Open Image" ), "./", tr( "PNG image (*.png)" ) );
    file_dialog.setFileMode( QFileDialog::ExistingFile );

    QStringList filenames;
    if ( file_dialog.exec() == QDialog::Accepted )
    {
        filenames = file_dialog.selectedFiles();
    }
    else
        return;

    QPixmap pixmap;
    pixmap.load( filenames.front() );

    ui.label->setPixmap( pixmap );
}

其中QFileDialog需要include “QFileDialog"
詳細的用法就不多說了,請翻閱Qt小幫手(assistant)
一切順利的話,執行後按下「Open Image…」
就可以看到選擇影像檔案的對話框跑出來囉

open file dialog

選好影像檔案後開啟,會顯示在QLabel元件上,有沒有很簡單啊!

MyQtApp

其實,在沒有VC add-in介入時,也能用同樣的方法來建立signal/slot之間的連結
只是缺少了「自動」編譯*.ui與建立moc的步驟,感覺就瑣碎了些
幸好,有VC add-in的幫忙,讓整個過程銜接得更順
雖然沒有像原生IDE那樣完整的環境
不過這還是讓VC使用者在使用Qt時,省下許多的時間與減少手動的不便

[註1] 在Qt中有許多元件是沒有「設計時期」的,必須經由寫程式的方式使用該元件
譬如QPushButton,可以讓使用者從元件盒中拖放到設計的視窗
並修改其屬性等等,這就是具有「設計時期」的元件
我倒是比較希望未來Qt也能加強這類僅有「執行時期」的元件
讓程式撰寫更加的簡化
為什麼會這樣想?都是因為曾經習慣了BCB,才會這樣懷念XD

[註2] 關於QPushButton的clicked()信號
在Qt 4.3.4版時(我用的上一版)並不需要bool這個引數
但是這一版(4.5.0)如果不使用含有bool引數的clicked()信號
會發生雖然可以連結signal/自訂的slot,但是卻無法觸發的怪異情形
而,若clicked()信號是連結到原有的slot上(如close()),卻又都正常
不曉得是自己這邊的問題還是怎麼回事,只好都先改用clicked(bool)信號了

五月 19, 2009 - 發文作者 | Qt | , , ,

1 則迴響 »

  1. 写得很好哦..很有用

    迴響 由 LeoOo | 六月 17, 2009


發表迴響

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

連結到 %s

Follow

Get every new post delivered to your Inbox.