網頁

2014年6月13日 星期五

Qt: A Very Quick Overview實作

          

    看完QT: A Very Quick Overview後來實作一個例子,目標是像下圖一樣,有個人物能夠在畫面中上下左右移動,並有兩個Qt的元件:LCDNumber來顯示人物的x坐標和y坐標,因此可以從本例子中學到Qt 圖片載入,更新畫面並畫出圖,鍵盤控制,signal和slot,Qt Creator表單。


    首先開啟Qt Creator後,新增專案->應用程式->選'Qt Widgets Application',然後一直按下一步。從最簡單的開始,左邊有個表單,選擇裡面的mainwindow.ui,這是Qt的設計模式,可以直接拖拉Widget。在設計模式中,先將視窗拉到適當大小(我是800x600),然後左邊Display Widgets裡面有LCD Number,拉兩個放到視窗裡,你會看到右邊的物件多了兩個名字'lcdNumber'和'lcdNumber_2',等等在寫MainWindow的slot的時候會用到。


    完成GUI設計後,先幫這兩個LCD Number設定Slot,在mainwondow.h加入:
public slots:
    void ChangeLCD(int x, int y);

然後mainwindow.cpp加入:
void MainWindow::ChangeLCD(int x, int y)
{
    ui->lcdNumber->display(x);
    ui->lcdNumber_2->display(y);
}
以上是當人物改變坐標的時候,會傳signal到這個slot,我們就讓這兩個LCD顯示該坐標位置。


    接下來我們還要2個class:
  • class Game: 用來掌控整個Game的過程
  • class Man: 人物的class


    先從main.cpp開始:
    因為我們有用到設計模式,所以就讓MainWindow當我們的主視窗,Game是MainWindow的child,也就是說建立Game的時候,要設MainWindow為parent(Line 10,等等在Game.cpp有constructor的寫法),下一行setFocus()是讓Game被focus,你可以在完成的時候試試去掉這行,會發現按鍵盤沒有反應,原因在於程式一開始是focus在MainWindow。



    接下來看Game.h和Game.cpp:
    class Game繼承自QWidget。在Constructor可以看到設定parent的方式(Game.cpp Line 4),在我們程式是要把MainWindow設成Game的parent。

    在Contructor內建立man和timer兩個物件,class Man有個PositionChange()的signal,將它connect到MainWindow的ChangeLCD();timer則設定每(1000/60)ms發出一個timeout()的signal,連結到class Game的RreshFrame(),也就是一秒刷新畫面60次。

    RefreshFrame()就是更新人物的位置,並repaint(),repaint()這個function會呼叫paintEvent(...)。

    因此我們在paintEvent(...)裡面畫圖,我是用QPainter(記得要把QPainter的parent設成Game),因為它可以畫出圖片。建立QPainter後,我把painter丟給man這個物件,讓man用painter來畫出它自己的圖片,因此如果有很多個物件,只要把同個painter丟給這些物件讓他們自己畫出自己的圖。

    然後看到keyPressEvent(...)和keyReleaseEvent(...),一個是你按下按鍵會呼叫;另一個是放開某個按鍵會呼叫。因此很簡單的就把上下左右鍵分別設定man的速度,放開按鍵就讓man的速度歸0。



    最後,把class Man完成:
 
    用QPixmap來存圖片,在constructor可以看到QPixmap的用法,基本上就是載入該路徑的圖片並存下來,路徑建議用相對路徑,至於該路徑在哪,如果在桌面建立project,你按執行(CTRL+R)的時候會在桌面產生"build-xxxxxxx.......-Debug"的資料夾,相對路徑就預設在這個資料夾。

    PaintMan(...)用pass by reference來接受從Game傳進來的painter,並用這個painter來畫出圖片。

    還記得剛剛class Game的Constructor我們將class Man connect to class MainWindow,因此我們要寫個signal,signal不用definition,只要declaration即可。注意到UpdatePosition()裡面Line 21我們發出(emit)PositionChange(x,y)這個signal,因此每次更新位置,Mainwindow那邊就會接收到signal,LCD就會同步顯示。到此完成了所有code,可以編譯並執行。


  • 如果你碰到undefined reference to 'vtable for xxx'的問題,可以試試在project的名稱上按右鍵->qmake,讓qt重新qmake。
  • 如果發現按方向鍵還是沒效果,那麼main.cpp裡面改成
    g->setFocusPolicy(Qt::StrongFocus);