用VS Code搞Qt6:編譯源程式碼與基本配置

先說明一下,本水文老周僅討論新版的 Qt 6,舊版的 Qt 不討論。

儘管 Qt 有自己的開發環境,但老周必須說句不裝逼的話:真的不好用。說起寫程式碼,當然了,用記事本也能寫。但是,有個高逼格的工具,寫起來不僅效率高,而且舒服。

Qt 應用程式本質上就是 C++ 開發的程式碼,所以,不用官方工具是沒有問題的。老周第一個想到的,不用猜,必是 VS。在 Windows 上,用 VS 也是沒問題的。

安裝的時候,工作負載可以選「使用C++的桌面開發」,這個其實不選也可以的,老周已做過實驗,不選這個也能編譯。所以,可以在單個組件中安裝以下各項:

1、C++核心功能;

2、C++ 2022 可再發行程式更新包;

3、MSBuild(這個裝上好一點,不裝也沒報錯);

4、MSVC 143 C++ 生成工具(這個是重點,要裝,要裝);

5、用於 Windows 的 CMake 工具(這個也必須裝上);

6、Windows SDK 任選一個版本,建議越新越好(自己編譯源程式碼時必須)。

7、其他的組件自己看心情。

如果你不用 VS,但在 Windows 上也要裝 MSVC 生成工具。在 Windows 上還是建議用微軟的編譯器,不容易出現莫名其妙的錯誤。老周實驗過,用 Windows 版的 g++ 編譯失敗。

當然本文講的  VS Code,但如果是 Windows 平台,也要裝 VS 生成工具的。

———————————————————————————————————————–

編譯源程式碼

Qt 現在也開始裝X了,編譯好的內褲只支援在線安裝,十有八九慢到你要裝幾天才能裝好,而且體積比 VS 還要大幾倍。想離線安裝只能下載源程式碼自己編譯。

編譯源程式碼需要以下工具:

1、Windows SDK,否則會找不到相關 .lib 文件而出錯。

2、Python,3.x 後隨便找個版本。可以下載嵌入版,不用安裝,解壓後,把Python可執行文件所在的目錄路徑添加到 PATH 變數中。

3、Perl。可以下載綠色版壓縮包,解壓到某目錄,將包含 perl.exe 的目錄添加到 PATH 環境變數中,一般位於 <解壓後目錄>\Perl\perl\bin。

 

下載源碼可以用中國鏡像,如清華大學的:Index of /qt/archive/qt/ | 清華大學開源軟體鏡像站 | Tsinghua Open Source Mirror

最新的是 6.3,進去之後,不要下載整個源碼包,而是找到 submodules 目錄。核心組件是 qtbase-everywhere-6.xxx。

qtbase 是基礎包,只編譯這個模組的程式碼也能寫 Qt 程式。解壓之後,放在一個路徑無空格無特殊符號的目錄下,如 E:\SDK\Qt。然後執行一下 configure 腳本,Windows 上是有後綴 .bat 的,Linux 腳本無後綴。

命令行參數可參考幫助文檔,實際上我們只關心一個參數 -prefix。這個參數指定在編譯成功後,複製(安裝)動態庫的目錄。因為編譯時會產生許多後期沒用的文件,所以才要指定這個路徑。操作方法:

打開 「Visual Studio 2022」 — “x64 Native Tools Command Prompt for VS 2022″。

不要用 Developer Command Prompt for VS 2022,因為用這個你要指定 CPU 架構,若不指定默認編譯出來的庫是 32 位的。

【注意:下面輸入命令這一大段,你先別著急跟著輸入。請你先看完了再輸入。因為這 Qt 6 在編譯前的配置有些雷區。即在「+++++++++++」與「+++++++++++」之間這一大段內容】

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

先看最保險的操作,全按官方文檔的來,基本保持默認。

打開 x64 Native Tools Command Prompt for VS 2022 工具的命令提示窗口,輸入:

cd <源碼路徑> 
configure -prefix F:\Qt6\Libs

這樣配置之後,將來在執行安裝時會把生成的 .dll 複製到這個目錄下。這廝好像重複執行會報錯。老周一般是懶得去找錯誤,有時候就算找到錯誤在哪個 cmake 文件也解決不了問題,最直接有效的方法是全刪掉,再從下載的壓縮包里重新解壓。官方文檔說刪除 CMakeCache.txt 文件可以,但老周嘗試過,也是會報錯的。

一切順利之後,直接交給 CMake 解決。編譯:

cmake --build .

. 是源碼所在目錄,因為上面我們已經 cd 到源碼目錄了,所以這裡用個「.」表示當前目錄。

運氣好的話,一次通過。這時候電腦的 CPU 風扇會發瘋,請做好心理準備。因為只編譯一個基礎模組,所以花的時間會少一些。

編譯成功後,還要執行一下安裝操作:

cmake --install .

後面的「.」依然指的是當前目錄(源碼目錄),編譯後的二進位文件(重點是那些 .dll)會複製到你剛才用 -prefix 參數配置的路徑下。如剛才配置的是 F:\Qt6\Libs。

上面的方案是保證出錯概率最低的做法,但是,生成文件會和源碼混在一起,想手動清理它們估計會累死人。如果想三個基本目錄相互隔離,就要用接下來的方法。這三個目錄是:

1、源碼。

2、build 輸出目錄。

3、安裝目錄。

我們來假設一下:

1、源程式碼:G:\Kits\Qt6\src\qtbase

2、build 輸出目錄:G:\Kits\Qt6\build

3、安裝目錄:G:\Kits\Qt6\installed

其實,這些目錄都在 G:\Kits\Qt6 下面。

廢話一下,我現在的想法是:保留源程式碼目錄不變,想留著將來重複用;把生成/編譯輸出的東東放在 build 目錄,編譯好後的二進位文件安裝到 installed 目錄。下面開始操作。

【警告:以下操作出現靈異事件的概率大,請鼓起勇氣嘗試。世事難料,各自珍重】

首先,打開 x64 Native Tools Command Prompt fo VS 20XX,定位到 G:\Kits\Qt6\build,這個路徑要根據你實際的路徑寫。

cd /d G:\kits\Qt6\build

命令工具窗口當前目錄一般是 C 盤,要跨分區 CD 的話,要加上 /d 參數。

保持 build 目錄為當前目錄不要改變,在 build 目錄中執行 configure 腳本。

..\src\qtbase\configure -prefix ..\installed

老周這裡用的是相對路徑,你也可以用絕對路徑。注意 -prefix 參數是一個短橫線的,不能寫成 –prefix,會出錯。如果看到下面這一行,說明你運氣好,第一關算是過了,接下來的編譯成功率很高。

Build files have been written to: G:/Kits/Qt6/build

接下來的操作就和前面的一樣了。保持當前目錄在 build 不變,依次執行:

cmake --build .
cmake --install .

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

大約會編譯 1700 多個目標。這過程中很有可能會出錯退出。可以嘗試重新執行 cmake –build . ,一般會成功的。 在 installed\bin 目錄下你會看到許多 .dll,這說明一切順利。

編譯默認是生成動態庫的,所以在 configure 時我們不用改。建議用動態庫,若編譯為靜態庫,做項目時會涉及授權問題,也就是說你要購買。

 

配置 VS Code

編譯完畢後,就是配置 VS Code 了,其實就是安裝插件罷了。這幾個插件少不了:

1、C/C++(微軟官方的)

2、CMake Tools(也是微軟官方的)

3、CMake Language Support(有了這個,寫CMake時有智慧提示)

有以上這幾個就夠玩了。另外,微軟有個集合包,叫做 C/C++ Extension Pack,會一次安裝一堆 C++ 有關的插件。覺得有必要可以裝裝,覺得沒必要就當它透明。

打開 VS Code 的設置頁,找到擴展中的 CMake Tools,滾動到 Environment,點「添加項」設置一個環境變數。

PATH = G:\Kits\Qt6\installed;G:\Kits\Qt6\installed\bin;%PATH%

【鍵】是「PATH」,【值】是 = 後面那串。路徑就是你剛才 Qt6 安裝的目錄,包括安裝目錄,以及安裝目錄下的 bin。

保存退出,收工。

 

開始裝逼

 如果上面各步驟都成功,我們現在可以開寫玩了。

在 VS Code 中打開一個目錄作為工作區。然後按 【ctl + shift + P】,輸入「cmake」,找到「CMake 配置」命令,執行。

 

 編譯器選擇 amd64 的,如果你要 32 位的就選 x86。

 

 【注】如果你要寫 32 位的,那前面在編譯源程式碼時也要編譯一份 32 位的動態庫。這個你懂的,32 位和 64 位的二進位文件是不同混用的。

 

這時,提示還沒創建 CMakeLists.txt ,那就點創建唄。但是,生成的 CMakeLists.txt 文件中的東東很多不是我們所需要的。凡是有 enable_testing,CTest 什麼的,全部幹掉。   

大致內容如下:

cmake_minimum_required(VERSION 3.15.0)

# 項目名稱
project(testApp LANGUAGES CXX)

# 設置變數
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)

# 引用需要的庫
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Gui)
# 添加源程式碼文件
add_executable(testApp WIN32 main.cpp)
# 與相關的庫鏈接
target_link_libraries(testApp PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui)

1

cmake_minimum_required(VERSION 3.15.0)
設置要使用的 CMake 最小版本號,這個你看情況寫吧,我這裡寫 3.15。
 
2:設置項目的名稱,這個也是自己定義的,比如你的項目叫 KillDog。
project(KillDog LANGUAGES CXX)
CXX 表示 C++ 語言。

3:設置變數。

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)

CMAKE_CXX_STANDARD = 17     C++版本號 ,不要低於17。

CMAKE_CXX_STANDARD_REQUIRED = on 或 yes 或 1,和上面一起,必須符合C++版本。

CMAKE_AUTOMOC = on 或 yes 或 1,這個在Qt 項目里一般要開啟,允許一些獨特語法轉譯為C++程式碼。說白了就是 Qt 裡面的訊號和槽,用到特定的語法(其實這些語法是用宏定義的)。

CMAKE_AUTOUIC = on 或 yes 或 1,這個如果用到了 uic 資源時才會開啟,沒用到就可以不寫。

 

4:引入要使用的庫,Qt6 中使用 find_package 命令,格式都是固定,官方文檔中有,可以照著抄。

 

 

 

5:添加源程式碼文件。

add_executable(testApp WIN32 main.cpp)

第一個是目標名,一般和項目名稱相同,你可以自己取名。接著 WIN32 不能少(僅限於 Windows),否則窗口出不來,只出來控制台。main.cpp是程式碼文件,有多個程式碼文件也要寫上,比如 a.cpp b.cpp c.cpp,如果寫漏了就導致有的文件沒有被編譯,那你的程式能運行起來才怪呢。

 

6:鏈接。把目標(上面 add_executable 就是為目標加了源文件)和庫鏈接起來。

target_link_libraries(testApp PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui)

  這個格式也是固定的,照著文檔抄就行了。請看上面 find_package 的截圖。

 

———————————————————————————————————————————-

下面是 main.cpp 中的程式碼,我們來試試前面的配置是否正確。                           ,

 

#include <QApplication>
#include <QWidget>
#include <QHBoxLayout>
#include <QPushButton>
int main(int argc, char** argv) 
{
// 這個 App 和 Widgets 在程式碼上不直接引用 // 但它是必須的,它會開啟主消息循環 QApplication app(argc, argv); // 準備主窗口 QWidget window; // 窗口標題 window.setWindowTitle("窮屌絲應用程式"); // 設置窗口大小 window.resize(400, 300); // 控制項 QPushButton *btn1 = new QPushButton("第一個按鈕"); QPushButton *btn2 = new QPushButton("第二個按鈕"); // 布局 QHBoxLayout *layout = new QHBoxLayout(&window); // 將控制項放入布局中 layout -> addWidget(btn1, 2); layout -> addWidget(btn2, 1); // 顯示窗口 window.show(); // 別忘了這一行,正式啟動程式 return app.exec(); // 調用靜態的也行 //return QApplication::exec(); }

QApplication 類用於啟動應用程式的主消息循環,它不需要顯式引用 Widget 對象。調用 exec 方法正式開始消息循環。這個方法會一直循環,直到應用程式退出(主窗口/根 Widget 被關閉)才會返回。若無錯誤,就返回 0 ,這個返回值可以直接沿著 main 函數返回。

注意,所有初始化(創建窗口,放置控制項等)程式碼必須在 exec 方法之前調用。exec 方法一旦調用,除非應用程式退出,否則是不會返回的。所以不要在 exec 之後寫任何程式碼(清理程式碼除外)。

QWidget 是眾多 UI 元素/控制項的基類,直接使用它可以作為應用程式的窗口——就是一個空白窗口。setWindowTitle 方法用來設置窗口標題欄上的文本;resize 方法調整窗口的大小;show 方法顯示窗口。

QHBoxLayout 是一個布局類,用來布局窗口中控制項的位置。它指的是 UI 元素沿水平方向排列。這個 Layout 類似於 WPF 中的 StackPanel。嗯,相信你也猜到,要垂直布局,就用 QVBoxLayout 類。如果想向 StackPanel 那樣,可以通過設置來控制水平或垂直方向,可以用 QBoxLayout,通過 setDirection 方法可以設置:從左到右、從右到左、從上到下、從下到上。

QPushButton 就是常見的按鈕。

在 Qt 應用程式中,new 出來的指針不一定要 delete / free 的。因為它有個引用樹的概念,會自動釋放指針所引用的東西。前提是這些對象是連接到 QObject 上的。比如窗口是 QWidget ,放在窗口內的控制項元素如果以窗口為父對象,就像上面那個例子,裡面的 QHBoxLayout、QPushButton 等,是連接到 QWidget 上的,所以不需要 delete 指針,它會自動釋放。

我們 build 一下這個項目,然後運行它。

 

 注意看 VS Code 底部的狀態欄,你能找到這兩個按鈕。

 

如果沒有問題,就能看到應用程式窗口了。

 

 

在 Linux 上的編譯和配置與 Windows 上差不多的。注意先安裝 g++,選這個編譯的話可避免各種靈異事件。老周在 Ubuntu 上試驗時,曾經遇到過一次靈異事件,至今無法解釋。編譯 Qt6 源程式碼順利完成,寫一個 app 試了下也能運行起來。但是,系統重啟後就進不了系統了,除了 root 帳戶外,所有帳戶都無許可權登錄。切換到 init 2 登錄也不行,一登錄就會被彈出來。就是除 root 外所有用戶都沒有任何許可權,連登錄的許可權都沒有。這現象無法解釋,因為那次老周成功登入系統後只編譯了 Qt6 源碼,寫了個測試 app,也沒做別的事,重啟後就掛了。