目錄
前言
最近在使用VisualStudio搭建Qt開發的時候遇到了一些問題,故寫此文章記錄一下解決問題的過程。問題主要有以下幾點:
1.如何在VisualStudio中使用Qt
2.Q_OBJECT引起的編譯失敗
對於問題1,主要是如何正確的搭建QT開發環境了,這裏我沒有使用Qt VisualStudio Tools,具體的配置過程將在下文提到
對於問題2,涉及到Qt的meta-object system,在本文中會幫助大家認識Qt中一個非常重要的工具:MOC
本文的重心在於快速的創建一個工程復現我所遇到的問題,給出我的一些解決方法供大家參考。
搭建QT開發環境
準備工作
IDE: VisualStudio 2019
Qt版本:Qt 5.13.0
VisualStudio軟件以及Qt的下載這裏不再詳細介紹,本文使用的Qt版本是開源版本
Qt安裝完成後找到下面的文件目錄:
- 你的Qt安裝根目錄/5.13.0/msvc2017
比如我的該目錄位於:F:\Qt\5.13.0\msvc2017
我們把這個路徑命名爲QTDIR,記住這個路徑後面會用到。
新建工程
我這裏新建了一個空項目Project2,爲其創建main.cpp
並添加如下代碼:
#include <iostream>
int main(int argc, char* argv[]) {
return 0;
}
這是一個空的主函數,爲了能夠使用Qt的各種類,我們需要把Qt的代碼庫包含到項目中來。
工程配置
我們要在VisualStudio中創建一個用戶宏,以方便我們添加對Qt代碼庫的引用
- 打開屬性管理器(View->Other Windows->Property Manager)
- 雙擊Microsoft.Cpp.Win32.user,選擇AddMacro,創建一個名爲QTDIR的宏,然後把上文中的QTDIR路徑添加上去,點擊確定即可,如下圖所示:
- 這麼搞有一個好處是,以後新建的項目都可以使用這個宏,不用每次重複創建了,一勞永逸。
接下來把Qt的include目錄包含到項目中
- 打開解決方案瀏覽器(Solution Explorer),右鍵單擊項目名稱,單擊Properties打開屬性設置面板
- 選擇C/C++->General,編輯Additional Include Directories,添加如下目錄:
- $(QTDIR)\include;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtWidgets
- 添加鏈接庫
- 同樣在屬性設置界面,打開Linker->General 編輯Additional Library Directories, 添加如下目錄
- $(QTDIR)\lib
- 打開Linker->Input, 編輯Additional Dependencies,添加我們所需要用到的靜態鏈接庫
- qtmain.lib;Qt5Core.lib;Qt5Widgets.lib;Qt5Gui.lib;
- 同樣在屬性設置界面,打開Linker->General 編輯Additional Library Directories, 添加如下目錄
代碼實驗
按照上面的步驟配置完成後,我們可以迴歸我們的代碼,可以嘗試添加一些Qt的內容:
#include <iostream>
#include <QApplication>
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
return a.exec();
}
編譯通過,運行時報如下錯誤:
彈出缺少一系列dll,不要慌張,我們到qt的bin目錄裏,找到這些dll,把它放到我們的編譯輸出的Debug目錄下就行了,例如本文中在如下位置F:\Qt\5.13.0\msvc2017\bin)
拷貝過去後再次編譯運行則不再報錯。
到了這裏我們似乎已經可以開始Qt的開發了,嘗試着創建一個窗口來,我們新建一個類,然後繼承自QMainWindow
#pragma once
#include <QMainWindow>
class MyWindow :
public QMainWindow
{
public:
MyWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0);
~MyWindow();
};
接下來在main.cpp中顯示該窗口
#include <iostream>
#include <QApplication>
#include "MyWindow.h"
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
MyWindow w;
w.show();
return a.exec();
}
編譯運行,發現確實創建了一個小窗口
走到這一步似乎我們搭建Qt開發環境的任務就大功告成了,但我們似乎遺漏了一點:在使用QtCreator進行開發時,創建出來的窗體類中都包含了一個叫Q_OBJECT的宏,我們乾脆也把這個宏加上好了,這樣顯得更完整了:
#pragma once
#include <QMainWindow>
class MyWindow :
public QMainWindow
{
Q_OBJECT
public:
MyWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0);
~MyWindow();
};
再次編譯,報錯
爲什麼會報錯呢,代碼看上去並沒有問題,要明白爲什麼報錯還是需要對Q_OBJECT宏有一個更深入的認識
Q_OBJECT
我們知道在使用QtCreater創建的窗體頭文件中都會被標上Q_OBJECT這樣一個宏定義,對於剛接觸Qt的開發者可能會感覺很奇怪,爲啥頭文件裏都要加一個這玩意兒,不加不行麼?
我們先看一下官方的說明:
The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt’s meta-object system
大概意思就是如果你要在類中聲明信號/槽或者使用到了一些Qt meta-object系統提供的某些服務的時候你就得在這個類頭文件的私有域添加Q_OBJECT宏,換言之,如果你不打算使用信號/槽或meta-object相關服務的時候,不加這個宏也是可以的
就比如上面提到的例子,沒有添加Q_OBJECT依然不妨礙代碼的正常編譯和窗口的生成。
(當然最好是不管用不用得到信號和槽,都把Q_OBJECT加上,雖然可能會增加一點點編譯時間)。
在官方文檔中對信號和槽的說明中,也提到了所有包含signals或slots的類都必須在他們聲明的最頂部寫上Q_OBJECT,而且這些類必須繼承自QObject
我們在代碼中使用這個宏,就是爲了能夠使用信號/槽這種機制,那麼Qt是如何處理Q_OBJECT的呢?答案就是:moc,在介紹moc之前我們先介紹一下Qt的meta-object system
Meta-Object System
在前文中提及了QT的meta-object system,meta-object system包含了信號/槽的通信機制,運行時類型信息以及動態屬性。
meta-object system建立在另外三種東西之上,分別爲:
- QObject,爲對象提供了一種基類,使之能夠利用到meta-object system
- Q_OBJECT,在類聲明的私有域中添加Q_OBJECT宏可以讓該類能夠使用到meta-object system的特性,比如動態屬性和信號槽
- MOC(meta-object compiler),爲每一個QObject的子類創建必要的代碼以便其使用meta-object system的特性
MOC (Meta-Object Compiler)
認識了Qt的meta-object system之後,我們着眼看一下MOC。MOC是一個工具,它的exe文件可以在QTDIR\bin下面找到。
它做了哪些事情呢?moc會讀取一個c++源文件,moc會檢查該文件中定義的類聲明中是否包含Q_OBJECT宏,如果包含的話,就會爲其生成一份cpp源文件,並在該文件中處理Q_OBJECT的實現。我們需要把這個新生成的源文件同樣包含進項目中參與編譯才行。
下面我們可以具體操作一下:
- 進入Qt的bin目錄下,在該目錄下調出命令行
- 輸入moc "yourfilename.h" –o "moc_yourfilename.cpp", 需要注意的是由於此處是在Qt的bin目錄下開啓的命令行,所以在輸入文件名和輸出文件名的時候還要寫全文件的路徑
- 按下回車,然後到輸出目錄查看,可以找到生成的moc開頭的cpp文件
我們到VisualStudio中把該moc開頭的源文件包含進去,再次編譯,不再報錯。
上面的例子使用命令行窗口調用moc來生成Q_OBJECT所需要的額外實現代碼,顯得有點麻煩,這裏介紹一下如何在VisualStudio進行一些配置來減少我們的工作量
VisualStudio相關設置
對於那些繼承自QObject且代碼中使用到Q_OBJECT宏的類,我們可以右鍵其頭文件,單擊Properties,打開屬性界面
將ItemType改爲Custom Build Tool
單擊 “應用”,此時左側會刷新出來CustomBuildTool的配置選項,
選中CustomBuildTool,在Command Line中填入:
"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(Configuration)\moc_%(Filename).cpp"
在Outputs中填入:
.\GeneratedFiles\$(Configuration)\moc_%(Filename).cpp
這樣當我們第一次編譯的時候,VS就會調用moc並在我們指定的目錄下生成一系列meta-object code
我們在只需要把這些生成的代碼include到我們的項目中再次編譯就不會報錯了
上面的配置中會把生成的代碼放到GeneratedFiles文件夾裏