#(1):Hello, world!
想要學習 Qt 開發,首先要搭建 Qt 開發環境。好在現在搭建 Qt 開發環境還是比較簡單的。我們可以到?[Qt 官方網站](http://www.qt-project.org/)找到最新版本的 Qt。在?[Downloads](http://qt-project.org/downloads)?頁面,可以看到有幾個版本的 Qt:Qt SDK、Qt Library、Qt Creator 等等。它們分別是:
* Qt SDK:包含了 Qt 庫以及 Qt 的開發工具(IDE、i18n 等工具),是一套完整的開發環境。當然,這個的體積也是最大的(Windows 平臺大約 1.7G,其它平臺大約 780M)。如果僅僅為開發 Qt,建議選擇這一項下載安裝。安裝方法很簡單,同普通程序沒有什么區別。所需注意的是,安裝過程中可能能夠提供選擇是否安裝源代碼,是否安裝 mingw 編譯器(Windows),這個就按照需要進行選擇即可。另外值得說明的是,Qt SDK 通常比單獨的 Qt 庫版本要舊一些。比如現在 Qt 正式版是 4.8.2,但是 Qt SDK 的最新版 1.2.1 中包含的 Qt 是 4.8.1。
* Qt Library:僅包含 Qt 庫。如果您已經安裝了 Qt 開發環境,為了升級一下 SDK 中提供的 Qt 庫版本,就可以安裝這一個。安裝過之后,應該需要在 IDE 中配置安裝路徑,以便找到最新版本的 Qt(如果不是覆蓋安裝的話)。
* Qt Creator:基于 Qt 構建的一個輕量級 IDE,現在最新版是 2.5.2,還是比較好用的,建議使用 Qt Creator 進行開發。當然,如果你已經習慣了 VS2010 這樣的工具,可以在頁面最下方找到相應的 Addin。很多朋友希望閱讀 Qt 代碼以提高自己的開發水平。當然,Qt 的經典代碼是 KDE,不過這個項目不大適合初學者閱讀。此時,我們就可以選擇閱讀 Qt Creator 的代碼,它的代碼還是比較清晰的。
當我們安裝完成 Qt 開發環境之后,就可以使用 Qt Creator 進行開發。在本系列中,豆子會一直使用這個 IDE 進行講解。至于編譯器,豆子一般會使用 mingw 或者 gcc。為了編譯 Qt 5 的程序,你應該使用 gcc 4.5 以上的版本,這意味著,如果你是使用 Qt SDK 自帶的 mingw,是不能編譯 Qt 5 的程序的(因為這個自帶的版本是 4.4),你應該升級 mingw 為 4.5 以上版本。
至此,我們已經有了 Qt 4 的完整開發環境。如果你想要開發 Qt 5,由于現在(2012 年 8 月) Qt 5 還處于測試階段,并沒有提供二進制庫,所以我們需要使用 git 自己獲取 Qt 5 的源代碼自己編譯(一般需要幾個小時時間)。豆子非常不建議在 Windows 上編譯 Qt 5,因為可能會出很多問題。如果你想嘗試,可以參考[這里](http://qt-project.org/wiki/Building_Qt_5_from_Git)。豆子提一句,在 Windows 上編譯 Qt 5,需要安裝 perl(并且要安裝 GetOpt::Long 模塊)、python 和 git,并且需要找到彼此路徑。相比而言,Linux 上面就會簡單很多。豆子建議,如果你想在 Windows 上嘗試 Qt 5,可以考慮安裝一個虛擬機,使用 Linux 平臺;或者自己試著直接在 Windows 本地編譯。豆子的環境是使用 openSUSE。openSUSE 的?[Qt 5.0 Development Snapshots](http://download.opensuse.org/repositories/KDE:/Qt50/openSUSE_12.1/)?已經提供了 Qt 5 二進制版本,免去了編譯的過程。*基于此,本文的 Qt 4 版本將在 Windows 平臺上使用 mingw 進行測試;Qt 5 版本將在 openSUSE 上使用 gcc 4.6 進行測試。在未來官方推出 Qt 5 Windows 平臺的二進制版本,也不排除在 Windows 上面測試 Qt 5 代碼。*
在 Qt Creator 中,我們可以在菜單欄的工具-選項-構建和運行的“Qt 版本”和“工具鏈”這兩個選項卡中配置 Qt Creator 所使用的 Qt 版本和編譯器。這或許是最重要的步驟,包括添加新的 Qt 版本以及以后的切換編譯器或者 Qt 升級等。
下面嘗試開發第一個 Qt 項目:HelloWorld。在 Qt Creator 中新建一個工程:
[](http://files.devbean.net/images/2012/08/qtcreator-new-project.png)
點擊這個“新建文件或工程”,在左側選擇項目-Applications,中間選擇 Qt Gui 應用,然后點擊“選擇…”:
[](http://files.devbean.net/images/2012/08/qtcreator-gui-project.png)
在彈出的對話框中填寫名稱、創建路徑等信息:
[](http://files.devbean.net/images/2012/08/qtcreator-proj-name.png)
點擊“下一步”,選擇該工程的編譯器。這里我們只選擇 mingw 調試即可(在以后的項目中,根據自己的需要選擇。)Shadow Build 的含義是“影子構建”,即將構建生成的文件不放在源代碼文件夾下。這樣可以最大地保持源代碼文件夾的整潔。
[](http://files.devbean.net/images/2012/08/qtcreator-proj-compiler.png)
點擊“下一步”,可以選擇生成的主窗口文件。不過在我們的簡單示例中是不需要這么復雜的窗口的,因此我們盡可能簡單地選擇,將“創建界面”的選擇去除:
[](http://files.devbean.net/images/2012/08/qtcreator-proj-mainwindow.png)
終于到了最后一步。這里是在詢問我們是否添加版本控制。對于我們的小項目當然是不需要的,所以選擇“無”,然后點擊“完成”即可:
[](http://files.devbean.net/images/2012/08/qtcreator-proj-vc.png)
可以看到,Qt Creator 幫助我們在 HelloWorld 項目文件夾下生成了四個文件:main.cpp,mainwindow.cpp,mainwindow.h 和 HelloWorld.pro。pro 文件就是 Qt 工程文件(project file),由 qmake 處理,生成 make 程序所需要的 makefile;main.cpp 里面就是一個main函數,作為應用程序的入口函數;其他兩個文件就是先前我們曾經指定的文件名的文件。
我們將 main.cpp 修改如下:
~~~
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel label("Hello, world");
label.show();
return app.exec();
}
~~~
點擊 Qt Creater 左側下面的綠色三角按鈕即可運行(這里一共有三個按鈕,從上到下分別是“運行”、“調試”和“構建”)。如果沒有錯誤的話,就會看到運行結果:
[](http://files.devbean.net/images/2012/08/qt-hello-world.png)
這個程序有這么幾行。我們解釋一下。
前兩行是 C++ 的 include 語句,這里我們引入的是`QApplication`以及`QLabel`這兩個類。`main()`函數中第一句是創建一個`QApplication`類的實例。對于 Qt 程序來說,`main()`函數一般以創建 application 對象(GUI 程序是`QApplication`,非 GUI 程序是`QCoreApplication`。`QApplication`實際上是`QCoreApplication`的子類。)開始,后面才是實際業務的代碼。這個對象用于管理 Qt 程序的生命周期,開啟事件循環,這一切都是必不可少的。在我們創建了`QApplication`對象之后,直接創建一個`QLabel`對象,構造函數賦值“Hello, world”,當然就是能夠在`QLabel`上面顯示這行文本。最后調用`QLabel`的`show()`函數將其顯示出來。`main()`函數最后,調用`app.exec()`,開啟事件循環。我們現在可以簡單地將事件循環理解成一段無限循環。正因為如此,我們在棧上構建了`QLabel`對象,卻能夠一直顯示在那里(試想,如果不是無限循環,`main()`函數立刻會退出,`QLabel`對象當然也就直接析構了)。
示例程序我們已經講解完畢。下面再說一點。我們可以將上面的程序改寫成下面的代碼嗎?
~~~
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel *label = new QLabel("Hello, world");
label->show();
return app.exec();
}
~~~
答案是,**不~~可以~~建議這樣做!**
首先,按照標準 C++ 來看這段程序。這里存在著內存泄露。當`exec()`退出時(也就是事件循環結束的時候。窗口關閉,事件循環就會結束),label 是沒辦法 delete 的。這就造成了內存泄露。當然,由于程序結束,操作系統會負責回收內存,所以這個問題不會很嚴重。即便你這樣修改了代碼再運行,也不會有任何錯誤。
早期版本的 Qt 可能會有問題(詳見本文最后帶有刪除線的部分,不過豆子也沒有測試,只是看到有文章這樣介紹),不過在新版本的 Qt 基本不存在問題。在新版本的 Qt 中,`app.exec()`的實現機制確定,當最后一個可視組件關閉之后,主事件循環(也就是`app.exec()`)才會退出,`main()`函數結束(此時會銷毀`app`)。這意味著,所有可視元素已經都關閉了,也就不存在后文提到的,`QPaintDevice`沒有`QApplication`實例這種情況。另外,如果你是顯式關閉了`QApplication`實例,例如調用了`qApp->quit()`之類的函數,`QApplication`的最后一個動作將會是關閉所有窗口。所以,即便在這種情況下,也不會出現類這種問題。由于是在`main()`函數中,當`main()`函數結束時,操作系統會回收進程所占用的資源,相當于沒有內存泄露。不過,這里有一個潛在的問題:操作系統只會粗暴地釋放掉所占內存,并不會調用對象的析構函數(這與調用`delete`運算符是不同的),所以,很有可能有些資源占用不會被“正確”釋放。事實上,在最新版的 Sailfish OS 上面就有這樣的代碼:
~~~
#include <QApplication>
int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(new QApplication(argc, argv));
QScopedPointer<QQuickView> view(new QQuickView);
view->setSource("/path/to/main.qml");
...
return app->exec();
}
~~~
這段代碼不僅在堆上創建組件實例,更是把`QApplication`本身創建在了堆上。不過,注意,它使用了智能指針,因此我們不需要考慮操作系統直接釋放內存導致的資源占用的問題。
當然,允許使用并不一定意味著我們建議這樣使用。畢竟,這是種不好的用法(就像我們不推薦利用異常控制業務邏輯一樣),因為存在內存泄露。而且對程序維護者也是不好的。所以,我們還是推薦在棧上創建組件。因為要靠人工管理`new`和`delete`的出錯概率要遠大于在棧上的自動控制。除此之外,在堆上和在棧上創建已經沒有任何區別。
如果你必須在堆上創建對象,不妨增加一句:
~~~
label->setAttribute(Qt::WA_DeleteOnClose);
~~~
這點提示足夠告訴程序維護者,你已經考慮到內存問題了。
- (1)序
- (2)Qt 簡介
- (3)Hello, world!
- (4)信號槽
- (5)自定義信號槽
- (6)Qt 模塊簡介
- (7)MainWindow 簡介
- (8)添加動作
- (9)資源文件
- (10)對象模型
- (11)布局管理器
- (12)菜單欄、工具欄和狀態欄
- (13)對話框簡介
- (14)對話框數據傳遞
- (15)標準對話框 QMessageBox
- (16)深入 Qt5 信號槽新語法
- (17)文件對話框
- (18)事件
- (19)事件的接受與忽略
- (21)事件過濾器
- (22)事件總結
- (23)自定義事件
- (24)Qt 繪制系統簡介
- (25)畫刷和畫筆
- (26)反走樣
- (27)漸變
- (28)坐標系統
- (29)繪制設備
- (30)Graphics View Framework
- (31)貪吃蛇游戲(1)
- (32)貪吃蛇游戲(2)
- (33)貪吃蛇游戲(3)
- (34)貪吃蛇游戲(4)
- (35)文件
- (36)二進制文件讀寫
- (37)文本文件讀寫
- (38)存儲容器
- (39)遍歷容器
- (40)隱式數據共享
- (41)model/view 架構
- (42)QListWidget、QTreeWidget 和 QTableWidget
- (43)QStringListModel
- (44)QFileSystemModel
- (45)模型
- (46)視圖和委托
- (47)視圖選擇
- (48)QSortFilterProxyModel
- (49)自定義只讀模型
- (50)自定義可編輯模型
- (51)布爾表達式樹模型
- (52)使用拖放
- (53)自定義拖放數據
- (54)剪貼板
- (55)數據庫操作
- (56)使用模型操作數據庫
- (57)可視化顯示數據庫數據
- (58)編輯數據庫外鍵
- (59)使用流處理 XML
- (60)使用 DOM 處理 XML
- (61)使用 SAX 處理 XML
- (62)保存 XML
- (63)使用 QJson 處理 JSON
- (64)使用 QJsonDocument 處理 JSON
- (65)訪問網絡(1)
- (66)訪問網絡(2)
- (67)訪問網絡(3)
- (68)訪問網絡(4)
- (69)進程
- (70)進程間通信
- (71)線程簡介
- (72)線程和事件循環
- (73)Qt 線程相關類
- (74)線程和 QObject
- (75)線程總結
- (76)QML 和 QtQuick 2
- (77)QML 語法
- (78)QML 基本元素
- (79)QML 組件
- (80)定位器
- (81)元素布局
- (82)輸入元素
- (83)Qt Quick Controls
- (84)Repeater
- (85)動態視圖
- (86)視圖代理
- (87)模型-視圖高級技術
- (88)Canvas
- (89)Canvas(續)