NOTICE

 任何跟文章無關的閒聊,請愛用 留言板(Guestbook)

 想要快速瀏覽主題,請點選單 目錄 標籤。

 停止更新ing,請見諒。 <(_ _)>


顯示具有 筆記 標籤的文章。 顯示所有文章
顯示具有 筆記 標籤的文章。 顯示所有文章

3月 13, 2010

【筆記】Create a Verilog Project

@
  安裝好 Verilog 的撰寫環境--Quartus II 之後,我們還需要建置好一個專案(project)才能正式開始。所以,這裡簡單的描述一下建立專案,以及編譯 Verilog 原始碼的流程。



  首先,點選選單的 File/New Project Wizard。就會跳出一個專案的建置精靈:



  在「What is the working directory for this project?」中輸入你專案的擺放路徑,並在「What is the name of this project?」中輸入專案的名稱(註1)。

  若是專案路徑的資料夾不存在,會跳出訊息問你是否要建立它:



  確定要建立,就選擇「是」。



  按下 Next 之後,我們可以加入一些現有的檔案到專案裡頭:



  你可以利用 File name 後方的「...」按鈕開啟檔案對話方塊,選擇要加入的檔案,再按下 Add 按鈕加入專案。也可以直接按下 Add All 將目錄下的檔案都加到專案中。

  不過,這裡我們還沒有任何現存的檔案可以加入,所以直接按下 Finish 完成專案建置(註2)。



  建立好專案之後,我們還需要建立一個 Verilog 的原始碼檔案。按下選單的 File/New,就會出現對話框詢問你新增的檔案類型:



  由於我們要是 Verilog 的原始碼檔案,所以選擇「Verilog HDL File」。

  新增完成之後,就可以直接開始撰寫程式碼(下圖中的程式碼為先前 Verilog Module 解釋的 AndGate Module):



  接下來將檔案存檔:



  請記得,Quartus II 的主要模組一定要跟檔案名稱相同



  接下來,就可以按下選單的 Precessing/Start Compilation 開始編譯的動作。假如跳出「Full Compilation was successful」的對話框,就代表編譯成功了(註3):




註1. 建議將專案放在獨立的地方,並為專案取個有意義的名稱,方便以後重複使用專案。
註2. 實際上後面還有一些設定燒錄晶片跟相關工具的部份,不過這裡我們目前都用不到。
註3. 這裡會看到其實編譯過程中會有些警告訊息。雖然這裡我們並不在意它,不過你也可以去看看這些警告訊息,查查到底為什麼。

【筆記】Verilog Module

@
  在之前的簡介中提到,Verilog 以模組(module)作為描述的基本單位。模組如同程式語言中的函式(function),可以將一部份的程式碼封裝起來,並提供對外溝通的介面(interface)。如此一來,我們便可以重複利用這些定義好的模組,以構成較大、較複雜的系統。



  我們可以將模組看成兩個部份:連接埠(port)的宣告,以及模組的主體(body)

  其中,連接埠類似於程式語言中函式的參數(parameter),提供了對外溝通的介面。包含了輸入埠(input)輸出埠(output)、或是兼具輸出入的雙向埠(inout)。簡單來說的話,其實可以把連接埠想成一顆 IC 上面的接腳(pin)。

  而模組主體則類似於程式語言中的函式主體,表達了模組的結構,或是模組的功能與行為



  以下是一個 Verilog 的模組,代表一個 and 邏輯閘:

module AndGate(out, a, b);
    input   a, b;
    output  out;

    and(out, a, b);
endmodule

  接著,讓我們一步一步理解這個簡易的模組。



module AndGate(out, a, b);

  這裡的「module」為 Verilog 語法的關鍵字,代表一個模組宣告的開頭;「AndGate」為這個模組的名稱(註1);而括號中的 out、a、跟 b,則是這個模組的連接埠(註2)。

  不過,在這裡並沒有指明連接埠的類型(input、output、或是 inout)。所以我們將在下面兩行宣告它們:

    input   a, b;
    output  out;

  我們宣告 a 跟 b 為 AndGate 的輸入埠,且 out 為 AndGate 的輸出埠。



  而模組主體的部份,我們只做了一個簡單的動作:

    and(out, a, b);

  「and」為 Verilog 提供的內建邏輯閘。這裡的動作是將輸入埠 a 跟 b 進行 and 運算,並以輸出埠 out 作為運算後的結果(註3)。



  最後,我們以「endmodule」關鍵字結束模組的宣告:

endmodule



  這個模組的示意圖大致如下:




註1. 你可以根據模組的功能,自行取一個有意義的名稱。
註2. 一般來說,我們習慣將 output 寫在前面、將 input 寫在後面。
註3. 或許想成「將 a 跟 b 接到 and gate 的 input 接腳,並將 out 接到 and gate 的 output 接腳」會比較好理解。

3月 12, 2010

【筆記】Quartus II Installation

@
  在實際開始撰寫 Verilog 之前,我們需要先安裝好撰寫執行 Verilog 程式的環境工具。而這裡我們採用的工具,是 Altera 公司的 Quartus II 這套軟體。

  Quartus II 是一套用以撰寫 Verilog、VHDL、AHDL 等 HDL 的開發工具,除了可以進行程式的編輯與編譯之外,也可以用以產生邏輯圖、狀態圖、波形圖等等,在功能上算是相當齊全。



  現行的 Quartus II 有兩個不同的版本:Web Edition 與 Subscription Edition。

  其中 Subscription Edition 有提供 30 天的試用期。超過試用期之後,是需要經過授權才可以繼續使用的。而 Web Edition 則是免費的版本,可以直接下載來使用。

  這裡我們要使用的是 Web Edition



  要取得 Quartus II Web Edition,需要先連到其下載頁面



  然後,選擇下載「Quartus® II Web Edition Software v9.1 Service Pack 1」(請依照當前最新版本自行尋找)。



  接著會進到 Account Sign-In 的畫面。假設各位都沒有 Altera 的帳號,可以選擇「Get One-Time Access」並在下面的文字框輸入你的 E-mail,就能夠不經由註冊的步驟繼續下載。

  當然,若是你真的想註冊一個帳號,選擇「Create Your myAltera Account」並完成註冊步驟也是可以的。



  最後,拉到頁面下方,點選「Click to Download Your File Now」,就可以進行下載,並開始安裝(因為安裝過程只需要選擇安裝路徑,這裡就不花版面贅述了)。

  由於檔案大小高達 1.5GB,下載跟安裝的動作比較耗時,所以可能需要耐心等待一下子。



  安裝完成之後,就可以開始使用 Quartus II 這套功能強大的工具囉:

【筆記】Introduction to Verilog

@
  因為系上這學期有一門要寫 Verilog 的必修課,所以想說乾脆直接在 blog 上做個筆記整理內容,並供一起上課的同學們作為參考。不過,其中的許多內容都不是我非常瞭解的,假如有任何錯誤或問題,都歡迎各位提出來。



  說到 Verilog 就不能不先提到 HDL(Hardware Description Language,硬體描述語言)

  所謂 HDL,即是利用一般高階語言的程式撰寫法,以達成數位電路功能或結構的設計、驗證與模擬。利用 HDL,我們便可以不必透過傳統的手繪方式設計電路,再在麵包板上接出電路來進行驗證與模擬。而 Verilog 則是主流的兩種 HDL 之一(註1)。

  Verilog 在設計時採用了近似於 C 語言的語法,以類似於函式(function)的模組(module)作為描述的基本單位。對於熟悉 C 語言的程式設計師來說,Verilog 還算是相當容易學習。



  在實際的設計上,Verilog 則提供了四種層級的描述方式,供設計者在不同層次的觀點上設計電路。分別為:開關層級(Switch Level)邏輯閘層級(Gate Level)資料流層級(Dataflow Level)行為層級(Behavioral Level)

  開關層級是 Verilog 所提供的最低層級。在這個層級上,設計者需要瞭解電路的開關--電晶體(transistor)的元件特性,並以此進行電路的設計。

  在邏輯閘層級中,設計者的觀點從一個個的電晶體,轉到較高層的邏輯閘(logic gate)。以此觀點,設計者可以直接利用如 and、or、not、xor 等邏輯閘,與將之連接的線路來進行設計。

  在資料流層級中,我們關注的則是資料如何經由硬體元件進行處理、儲存、與流向;而在最高層級的行為層級上,設計者只需要知道模組與函式間的功能,而不去考慮硬體實作的細節。




註1. 另一種常用的硬體描述語言是 VHDL(Very-High-Speed Integrated Circuit Hardware Description Language)

2月 07, 2009

【筆記】Installing Qt with MinGW

@
  由於安裝 Qt 感覺起來還滿麻煩的,所以在這裡,我使用 Windows XP + MinGW 的環境,紀錄一下安裝 Qt 的步驟與過程。




  首先,請先到 Qt for Open Source C++ development on Windows 的下載頁中,選擇一個載點下載當前最新版的 "qt-win-opensource-4.4.3-mingw.exe"(請自己選擇當前最新版)。



  下載完成,開始安裝之後,安裝程式會詢問你要不要將 .ui 副檔名跟 Qt 的 GUI 編輯器(Qt Designer)作檔案關聯。

  基本上不用更改直接繼續就可以了。



  接著選擇安裝 Qt 的目錄。



  在這裡,假如你已經安裝過 MinGW,請直接選擇 MinGW 所在的目錄。

  (要瞭解如何安裝 MinGW,請見這篇文章。)



  此時,若你的 MinGW 是新版(我的是 5.1.4 版),Qt 的安裝程式會對 w32api.h 版本不合提出警告。

  但是,因為此時我的 w32api.h 的版本為 3.12,照理說是比 Qt 需求的 3.2 版還要新的。既然我的版本比 Qt 需求的版本還新,安裝程式還對此提出警告,實在是有些怪異。

  無論如何,在此我們先選擇「是」,忽略這個警告繼續進行安裝。




  而若是電腦裡並沒有預先安裝 MinGW,則可以勾選上面的 "Download ans install minimal MinGW installition",再選擇欲安裝 MinGW 的目錄。



  接著它會要你選擇下載 MinGW 的鏡像點(mirror)。

  其實也沒什麼好選的,因為站點只有一個 Norway。

  至於下方的 "Download MinGW source code" 則是可以順便將 MinGW 的原始碼下載下來。沒必要的話就別勾選吧。



  之後就會開始下載 MinGW 的動作(由於鏡像點相當遠,下載可能會等很久)。





  等一段時間,就能安裝完成了。


  不過,這樣還不能夠藉由 C++ 來使用 Qt。我們還需要將 Qt 函式庫進行編譯才能夠使用。

  首先,請到系統內容中,將 MinGW 目錄下的 bin 資料夾與 Qt 目錄下的 bin 資料夾加入到 PATH 環境變數中。例如 C:\MinGW\binC:\Qt\4.4.3\bin。(不知道如何設定環境變數,可以參考這篇文章。)

  然後,執行開始功能表中 Qt 資料夾(預設名稱為 "Qt by Nokia v4.4.3 (OpenSource)")裡的 "Qt 4.4.3 (Build Debug Libraries)",以建立 Debug Library。

  剛開始,應該會看到以下這段文字:

 This is the Qt for Windows Open Source Edition.

 You are licensed to use this software under the terms of
 the GNU General Public License (GPL) version 2 or 3.

 Type '2' to view the GNU General Public License version 2 (GPLv2).
 Type '3' to view the GNU General Public License version 3 (GPLv3).
 Type 'y' to accept this license offer.
 Type 'n' to decline this license offer.

 Do you accept the terms of the license?

  這裡是在詢問你同不同意 GNU GPL 的條款。若是要詳細瞭解條款內容,就輸入 23 (GPL的版本),再按下 Enter

  若是同意接受 GNU GPL 條款,就輸入 y 並按下 Enter 繼續安裝的程序吧。


  但是,假如你在之前選擇 MinGW 的地方,也碰上了 w32api 的警告,在這個過程中將會兩項錯誤(若是選擇由鏡像點下載並安裝 MinGW 則不會出現此問題)。

  所幸的是,這兩個問題都已經在網路上找到解決的方法。你可以到這裡閱讀相關的討論。


  第一個錯誤是 MinGW 目錄下 "\include\sspi.h" 的 "error: `UNICODE_STRING' does not name a type"。根據查到的結果,"UNICODE_STRING" 是被定義在 <subauth.h> 標頭檔中的。所以要修正這個問題,請開啟 sspi.h 找到以下程式碼:

#ifndef _SSPI_H
#define _SSPI_H
#if __GNUC__ >=3
#pragma GCC system_header
#endif

#ifdef __cplusplus
extern "C" {
#endif

  並在這段程式碼下面加上這一行:

#include <subauth.h>

  然後存檔關閉,這樣就可以了。


  而碰到的第二個錯誤,則是 Qt 目錄下的 "\src\corelib\arch\qatomic_windows.h" 與 MinGW 目錄下的 "\include\winbase.h" 中有部分函式發生了原型衝突(prototype conflict),也就是相同的函式被重複定義了兩次。

  解決的方法是,開啟 qatomic_windows.h 後,找到以下程式碼:
extern "C" {
    __declspec(dllimport) long __stdcall InterlockedCompareExchange(long *, long, long);
    __declspec(dllimport) long __stdcall InterlockedIncrement(long *);
    __declspec(dllimport) long __stdcall InterlockedDecrement(long *);
    __declspec(dllimport) long __stdcall InterlockedExchange(long *, long);
    __declspec(dllimport) long __stdcall InterlockedExchangeAdd(long *, long);
}

  並將它修改成:

#ifndef __INTERLOCKED_DECLARED
#define __INTERLOCKED_DECLARED
extern "C" {
    __declspec(dllimport) long __stdcall InterlockedCompareExchange(long *, long, long);
    __declspec(dllimport) long __stdcall InterlockedIncrement(long *);
    __declspec(dllimport) long __stdcall InterlockedDecrement(long *);
    __declspec(dllimport) long __stdcall InterlockedExchange(long *, long);
    __declspec(dllimport) long __stdcall InterlockedExchangeAdd(long *, long);
}
#endif

  以巨集的方式避開重複定義,這樣就沒有問題了。


  話說回來,這個建立 Debug Library 的過程其實相當久(約兩個小時)。

  確認沒有問題之後,建議就別盯著發呆,開個即時通聊天也好


  完成建置工作之後,請點選「開始」中的「執行」,並輸入 "cmd" 以開啟命令列提示工具。

  接著,我們要將所在目錄移到 Qt 的根目錄中。請輸入 "cd" 加上 Qt 的所在目錄,例如:

cd C:\Qt\4.4.3

  然後,再輸入以下命令:

make

  這一個步驟也相當耗時。想睡的,甚至可以回去補個眠了(無誤)。


  等到睡醒了,發現 make 的步驟也順利完成了。

  最後,我們需要將一些不必要的檔案清除,請輸入以下命令:

make clean

  安裝到這裡,就大功告成囉!

9月 16, 2008

【筆記】Using Qt Designer

@
  既然 Qt (大概)多少會用了,這次我要來試試其附帶的所見即所得編輯器,也就是 Qt Designer。說實在這個我還不是很會用,所以這裡只做個很簡單的測試:如何將 Qt Designer 製作出來的檔案(.ui 檔)與實際編寫的 C++ 程式結合的方式。




  首先,打開 Qt Designer 會出現這樣一個畫面:選擇一個樣版或是元件作為「頂層」的視窗。看過網路上的範例,滿多人都是選擇 "Widget",所以這裡我也先照著做了。




  因為目前都還不會寫到元件功能,所以就隨便亂做畫面了。哈哈,偽月曆一份!




  完成之後,按下存檔,存在我預先建立的 Qt Designer 的資料夾裡。


  接著就是程式的部份了:

#include <QApplication>
#include "ui_design.h"

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    QWidget *widget = new QWidget;

    Ui::Form ui;
    ui.setupUi(widget);

    widget->show();

    return app.exec();
}

  首先,比較要注意的是第二行,需要引括由 .ui 檔建立的標頭檔。

  不過其實,現在這個標頭檔還是不存在的。這個檔案會在編譯時,由 qmake 根據 Qt Designer 所建立的 .ui 檔產生。其中就包含了剛剛我所建立元件擺設。

  另外呢,還有中間 "Ui::Form ui" 的那兩行,是利用 Ui_Form::setupUi() 函式將我自行建立的 widget 作為包含了我剛剛建立的元件以及擺放位置的類別實體。有了實體之後,我們就可以簡單的利用 QWidget::show() 函式來顯示它了。


  接著,使用以下命令編譯程式:

qmake -project
qmake
make

  執行之後的結果:




  總之,好像又是個滿無聊的小測試。所以之後,再來試看看怎麼寫各個元件的處理程式吧。

9月 14, 2008

【筆記】Creating a Event Label

@
  接續前一篇筆記的內容。目前的程式有點普通,所以乾脆順便來寫一個(鍵盤或滑鼠)事件處理器。


  因為需要一個能夠接受事件的標籤元件,所以我定義了一個繼承自 QLabel 的物件:

class QEventLabel : public QLabel
{
    public:
        explicit QEventLabel(QWidget *parent = 0, Qt::WindowFlags f = 0);
        explicit QEventLabel(const QString &text, QWidget *parent = 0,
                                Qt::WindowFlags f = 0);
        ~QEventLabel();

    protected:
        void mouseMoveEvent(QMouseEvent *event);
        void mousePressEvent(QMouseEvent *event);
        void mouseReleaseEvent(QMouseEvent *event);
        void keyPressEvent(QKeyEvent *event);
};


QEventLabel::QEventLabel(QWidget *parent, Qt::WindowFlags f) : QLabel(parent, f)
{
}

QEventLabel::QEventLabel(const QString &text, QWidget *parent,
                            Qt::WindowFlags f) : QLabel(text, parent, f)
{
}

QEventLabel::~QEventLabel()
{
}

  建構子與解構子基本上都不需要做更動。


void QEventLabel::mouseMoveEvent(QMouseEvent *event)
{
    QString msg;
    msg.sprintf("<center>%d, %d</center>", event->x(), event->y());
    this->setText(msg);
}

void QEventLabel::mousePressEvent(QMouseEvent *event)
{
    QString msg;
    msg.sprintf("<center>Press</center>");
    this->setText(msg);
}

void QEventLabel::mouseReleaseEvent(QMouseEvent *event)
{
    QString msg;
    msg.sprintf("<center>Release</center>");
    this->setText(msg);
}

  在這裡的滑鼠是利用 QString 物件搭配 QString::sprintf() 函式來建立字串,以顯示標籤接收到的事件。

  當滑鼠移動,標籤會顯示滑鼠的座標;當滑鼠按鍵被按下,標籤會顯示"Press";當滑鼠按鍵被釋放,標籤會顯示"Release"。

  其中,滑鼠的座標可以由 QMouseEvent::x() 與 QMouseEvent::y() 取得。


void QEventLabel::keyPressEvent(QKeyEvent *event)
{
    QString msg;

    if (event->key() == '<')
    {
        msg = "<center>Type: &lt;</center>";
    }
    else if (event->key() >= Qt::Key_Space && event->key() <= Qt::Key_AsciiTilde)
    {
        msg = "<center>Type: " + event->text() + "</center>";
    }
    else
    {
        msg.sprintf("<center>Type: <font color=red>%d</font></center>", event->key());
    }

    this->setText(msg);
}

  這裡的鍵盤處理器也與滑鼠處理器相同,利用了 QString 物件與 QString::sprintf() 函式來顯示接收到的事件。

  當鍵盤被按下時,若是可以直接印出的字元('D'、' '、'!'、'3'等符號),我利用了 QKeyEvent::text() 取得鍵盤代號所對應的符號,並將之顯示在標籤上;而若是不能印出的功能鍵(Enter、Shift、Home 等),我則利用 QKeyEvent::key() 直接在標籤上顯示鍵盤代號。

  要特別注意的是,若鍵盤代號對應的是上角括號'<',直接利用 QKeyEvent::text() 會被判定為 html 標籤而不顯示。為了避免這種情況,我就將之改成直接輸出"&lt;",也就是'<'的字元實體(character entity)

  然後,別忘了在之前建立 QLabel 實體的地方做一點更動:
    QEventLabel *label = new QEventLabel("<center>No Event</center>", window);

  簡單來說,就是把 QLabel 改成 QEventLabel,並將標籤文字改成"No Event"就可以了。


  這樣應該完成了吧?試著先編譯執行看看。

  當滑鼠在標籤上移動,標籤成功的顯示滑鼠所在的座標。試著按看看滑鼠左右鍵,這個功能也正常運作了,看來一切完美。最後再按一按鍵盤......。等一等,為什麼按下鍵盤卻沒有反應呢?

  這個問題讓我修改又測試了一陣子,我猜想大概是標籤並沒有取得鍵盤的焦點(focus)。有了如此推測之後,又翻了一下官方的函式庫,發現這個 QWidget::setFocus() 函式似乎是我所需要的解答。

    label->setFocus();

  將這段程式碼加進去之後,編譯執行試看看。Bingo!現在標籤也能成功取得鍵盤事件囉。


  完整的程式碼如下:

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QDesktopWidget>
#include <QMouseEvent>
#include <QKeyEvent>

#define WIDTH   290
#define HEIGHT  140

/**************************************
    創建一個能接收事件的 Label 類別
**************************************/
class QEventLabel : public QLabel
{
    public:
        explicit QEventLabel(QWidget *parent = 0, Qt::WindowFlags f = 0);
        explicit QEventLabel(const QString &text, QWidget *parent = 0,
                                Qt::WindowFlags f = 0);
        ~QEventLabel();

    protected:
        void mouseMoveEvent(QMouseEvent *event);
        void mousePressEvent(QMouseEvent *event);
        void mouseReleaseEvent(QMouseEvent *event);
        void keyPressEvent(QKeyEvent *event);
};

/**************************************
    QEventLabel 建構子
**************************************/
QEventLabel::QEventLabel(QWidget *parent, Qt::WindowFlags f) : QLabel(parent, f)
{
}

QEventLabel::QEventLabel(const QString &text, QWidget *parent,
                            Qt::WindowFlags f) : QLabel(text, parent, f)
{
}

QEventLabel::~QEventLabel()
{
}

/**************************************
    QEventLabel 事件處理
**************************************/
void QEventLabel::mouseMoveEvent(QMouseEvent *event)
{
    QString msg;
    msg.sprintf("<center>%d, %d</center>", event->x(), event->y());
    this->setText(msg);
}

void QEventLabel::mousePressEvent(QMouseEvent *event)
{
    QString msg;
    msg.sprintf("<center>Press</center>");
    this->setText(msg);
}

void QEventLabel::mouseReleaseEvent(QMouseEvent *event)
{
    QString msg;
    msg.sprintf("<center>Release</center>");
    this->setText(msg);
}

void QEventLabel::keyPressEvent(QKeyEvent *event)
{
    QString msg;

    if (event->key() == '<')
    {
        // 避免上角括號被當作標籤而不顯示
        msg = "<center>Type: <</center>";
    }
    else if (event->key() >= Qt::Key_Space && event->key() <= Qt::Key_AsciiTilde)
    {
        msg = "<center>Type: " + event->text() + "</center>";
    }
    else
    {
        msg.sprintf("<center>Type: <font color=red>%d</font></center>", event->key());
    }

    this->setText(msg);
}

/**************************************
    Main 主函式
**************************************/
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget *window = new QWidget;
    window->setWindowTitle("Hello, Qt!");
    window->setMinimumSize(WIDTH, HEIGHT);
    window->setMaximumSize(WIDTH, HEIGHT);

    // 調整視窗至螢幕中央
    QSize size = app.desktop()->size();
    window->move((size.width() - WIDTH) / 2, (size.height() - HEIGHT) / 2);
 
    // 建立並設定元件
    QEventLabel *label = new QEventLabel("<center>No Event</center>", window);
    label->setFont(QFont("Courier New", 25, QFont::Bold));
    label->setGeometry(0, 0, 290, 70);

    QPushButton *quit = new QPushButton("&Quit", window);
    quit->setFont(QFont("Monotype Corsiva", 25, QFont::Bold));
    quit->setGeometry(5, 75, 280, 60);

    QObject::connect(quit, SIGNAL(clicked()), &app, SLOT(quit()));

    // 使 Label 變為焦點
    label->setFocus();

    window->show();

    return app.exec();
}

  編譯執行之後的結果:










  編譯出來的程式,我試著轉移到沒有裝 Qt 的電腦執行。沒想到還需要附帶 Qt 目錄下 lib 資料夾的 QtCore4.dll、QtGui4.dll 等動態函式庫。而這些動態函式庫的檔案也都不小,光一個 QtGui4.dll 就要將近 10 MB。

  不過怎麼說,還算是滿好學、好用的,有時間再寫些別的吧!或許來試用一下 Qt Designer。

【筆記】Small Test of Qt

@
  看完官方的 Qt Tutorial 文件之後,想自己實際寫一次 Qt 的程式。在這裡,我想盡量依照我寫的過程將程式碼列出來,所以內文的順序並不是實際程式碼的順序。建議可以對照著完整原始碼來看。


 #define WIDTH   290
 #define HEIGHT  140

  首先,為了方便起見,我直接使用前置處理器定義了程式的寬度(WIDTH = 290)與高度(HEIGHT = 140)。


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
            .
            .
            .
    return app.exec();
}

  接著,建立一個 QApplication,並傳遞 main() 函式的 argc 與 argv 引數作為其建構子參數。

  然後在程式的最後,利用 QApplication::exec() 函式將 main() 函式的主控權交給 Qt,並將執行結果作為返回值。

    QWidget *window = new QWidget;
    window->setWindowTitle("Hello, Qt!");
    window->setMinimumSize(WIDTH, HEIGHT);
    window->setMaximumSize(WIDTH, HEIGHT);

  現在需要一個頂層元件來作為一個視窗。

  在這裡,我直接使用 QWidget 作為頂層元件。同時,指定其標題為"Hello, Qt!",並使用 setMinimumSize() 與 setMaximumSize 固定其寬度與高度。

  而這裡使用的 WIDTH 與 HEIGHT 就是在前面提到,使用前置處理器定義的。


  空空的視窗還滿無聊的。所以我加入了一個 QLabel 元件。

    QLabel *label = new QLabel("<center>Test</center>", window);
    label->setFont(QFont("Courier New", 25, QFont::Bold));
    label->setGeometry(0, 0, 290, 70);

  沒什麼作用,這個 Label 只是擺好看的。

  文字內容是"Test",並使用 HTML 標籤來作置中。然後使用 QWidget::setFont() 設定文字的字體,並使用 QWidget::setGeometry() 來設定標籤的位置。


    QPushButton *quit = new QPushButton("&Quit", window);
    quit->setFont(QFont("Monotype Corsiva", 25, QFont::Bold));
    quit->setGeometry(5, 75, 280, 60);

    QObject::connect(quit, SIGNAL(clicked()), &app, SLOT(quit()));

  這是模仿 Qt Tutorial 文件加入的 Quit 按鈕。同樣是設定好字體及標籤位置之後,使用 QObject::connect() 將 quit 的 clicked() signal 連結到 app 的 quit() slot。


     window->show();

  接著將把視窗設為可見。


  這樣差不多了吧?不過我想,只有一個沒用的標籤跟一個 Quit 按鈕的程式實在太無聊。所以現在,我還要對這個視窗做一點小手腳:

    QSize size = app.desktop()->size();
    window->move((size.width() - WIDTH) / 2, (size.height() - HEIGHT) / 2);

  首先,我藉由 QApplication::desktop() 取得一個 QDesktopWidget 元件,其提供了程式所在螢幕的相關資訊。接著就可以直接利用 QWidget::size() 取得螢幕的大小。

  需要注意的是,為了要使用 QApplication::desktop(),在這裡我們需要引入 <QDesktopWidget> 這個標頭檔。

  根據螢幕及視窗大小,計算出擺放位置後,再使用 QWidget::move() 做移動。這樣每次啟動程式時,視窗就會自動移動到螢幕的正中央了。


  實際的原始碼如下:

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QDesktopWidget>

#define WIDTH   290
#define HEIGHT  140

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget *window = new QWidget;
    window->setWindowTitle("Hello, Qt!");
    window->setMinimumSize(WIDTH, HEIGHT);
    window->setMaximumSize(WIDTH, HEIGHT);

    // 調整視窗至螢幕中央
    QSize size = app.desktop()->size();
    window->move((size.width() - WIDTH) / 2, (size.height() - HEIGHT) / 2);
 
    // 建立並設定元件
    QLabel *label = new QLabel("<center>test</center>", window);
    label->setFont(QFont("Courier New", 25, QFont::Bold));
    label->setGeometry(0, 0, 290, 70);

    QPushButton *quit = new QPushButton("&Quit", window);
    quit->setFont(QFont("Monotype Corsiva", 25, QFont::Bold));
    quit->setGeometry(5, 75, 280, 60);

    QObject::connect(quit, SIGNAL(clicked()), &app, SLOT(quit()));

    window->show();

    return app.exec();
}

  編譯執行之後的結果:




  有實際編譯出來的人可以試看看改變視窗大小,或是按下最大化按鈕。

  雖說到目前為止,這程式應該還滿無聊的。不過自己實際寫一遍,還是比看著範例打來得好一點?