前几天有人私信我,说机器上装的是 Visual Studio 2015,老师又要求交一个带图形界面的 C++ 小作业,问我能不能用 VS2015 配 Qt 来做。当然能,而且 VS2015 + Qt 这套组合,是国内很多老项目、教材和实验课上绕不开的一条路。这篇就把从零跑通图形化 Hello World 的每一步讲透:版本怎么配、插件怎么装、项目怎么建、报错怎么治,最后再顺手做一个能输入名字的小交互。适合刚接触 Qt,或者平时只在命令行里写过 C++ 的朋友。
1. 版本搭配先过关:VS2015该配哪个Qt
1.1 先搞懂Qt安装包的命名密码
很多人一上来就栽在下载环节:去 Qt 官网看到一堆目录,msvc2015、msvc2017、mingw73_64……直接懵了。
其实 Qt 官方安装包的名字已经把答案写脸上了:
msvc2015表示它用 VS2015 的编译器(MSVC v140 工具集)编译;msvc2015_64表示 64 位版本;mingw53_32这种带 MinGW 的是另一套编译器体系,跟 VS 是两路人。
Qt 是靠底层库分发的,你用msvc2015编译出来的库,配合 VS2015 的 v140 运行时,兼容性才是最稳的。如果你强行装一个msvc2017的 Qt 包,再用 VS2015 去链接,最常见的后果就是编译阶段报一堆头文件缺失、链接阶段报LNK2019,或者即使编译过了,运行时弹 "0xc000007b",非常劝退。
1.2 我的推荐组合与理由
VS2015 对应的 Qt 版本,我一般推荐Qt 5.11.2或者Qt 5.9(LTS 长期版)。原因很简单:这两个版本是官方最后一波还提供msvc2015预编译包的版本。到 Qt 5.12 以后,官方预编译包的主打编译器换成了 msvc2017,VS2015 用户就得自己折腾源码编译,那个复杂度对新手来说完全是灾难。
另外提醒一句:如果选的是 MinGW 版本,就要搭配 Qt Creator 使用,而不是 VS。所以用 VS2015 做开发,安装 Qt 时一定要勾选 MSVC 系列的包,别顺手勾了 MinGW,装完发现模板里面啥都没有,又得回头折腾。
如果你只是练手,32 位和 64 位怎么选?我的建议是:第一次做就先装 32 位(msvc2015),VS 里用默认的 Win32 平台,少一层位数不匹配的烦恼。64 位后面会了再补也不迟。
2. 安装与配置:组件勾选、插件注册、Qt Versions指路
2.1 Qt在线安装器里到底要勾什么
安装 Qt 时,我建议用离线安装包,省去注册账号和网络的等待。安装路径建议用D:\Qt这种短路径,尽量不要带空格和中文,比如C:\Program Files (x86)\Qt。虽然 Qt 也能处理,但后面你用命令行工具、拼环境变量路径的时候,空格经常会给你整出意想不到的幺蛾子。
进入组件选择页面之后,展开Qt 5.11.2这一项,勾上msvc2015或msvc2015_64(看你前面决定装哪个)。下面那一长串 Qt Charts、Qt 3D、Qt WebEngine 之类,Hello World 阶段一律不勾,装了纯属占硬盘。等你真的需要某个模块,以后单独维护也是可以的。
工具链那块,除非你想顺便试试 Qt Creator,否则不用额外装。装完后你会在D:\Qt\5.11.2\msvc2015_64下面看到 include、lib、bin 这些目录,这就算成了。
这里要习惯性地做一个动作:设置环境变量。
- 新建
QTDIR,值填D:\Qt\5.11.2\msvc2015_64; - 把
%QTDIR%\bin追加到Path变量里。
这步不是必须,但做了之后,后面在命令行里跑windeployqt、或者排查“找不到 Qt5Widgets.dll”时会省事很多。
2.2 VS2015侧的Qt插件与Qt Versions设置
VS 本身不认识 Qt,它俩之间需要一个插件来桥接。VS2015 上一般装的是 Qt Visual Studio Tools,老一些的资料里叫 Qt Add-in。打开 VS2015,进工具 -> 扩展和更新 -> 联机,搜索 “Qt Visual Studio Tools”,下载安装后重启 VS。
装完插件,菜单栏会多出一个Qt VS Tools(或老版本的Qt)菜单。进去找到Qt VS Tools -> Qt Options,左侧选Qt Versions,点Add按钮,名称随便填,路径选到D:\Qt\5.11.2\msvc2015_64。填完设成默认。
这步千万别跳过。插件靠这个路径去定位头文件、库文件,还要调用moc、uic、windeployqt这些 Qt 自带工具。路径填错或者没填,后面新建工程后一编译就是一堆“工具找不到”的错。
3. 第一次新建Qt Widgets项目:每个人的第一个带界面的C++程序
3.1 选对模板之后,项目里这几个文件是干嘛的
打开 VS2015,文件 -> 新建 -> 项目,左侧选 Visual C++,如果插件正常,你会看到Qt Widgets Application(旧版插件可能叫Qt Application)。给项目起名HelloWorld,点确定。
系统会生成一个 Qt 项目,核心文件就这几样:
HelloWorld.pro:qmake 工程文件,以后你想脱离 VS、单纯用命令行构建,它就有用了;main.cpp:整个程序的入口,创建应用对象和主窗口,进入事件循环;mainwindow.h/mainwindow.cpp:主窗口类的声明和实现;mainwindow.ui:用 Qt Designer 可视编辑的界面文件。
新手容易犯的错是把mainwindow.ui当普通文本乱改,或者去翻生成的ui_mainwindow.h。这个文件是在编译前由uic工具根据.ui自动生成的,你改它没有任何意义,下次重新生成又会覆盖。
main.cpp 默认长这样,先混个脸熟:
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }a.exec()是事件循环,别问为什么窗口能一直挂着不退出,就是这个函数在持续接收鼠标、键盘、绘制等消息。这是 Qt 应用生命的核心。
3.2 在Qt Designer里拖出按钮和标签
双击项目里的mainwindow.ui,会打开 Qt Designer 的可视化编辑界面。左边是一堆控件面板,右侧是属性编辑器。
先拖一个QLabel(标签)和一个QPushButton(按钮)到窗体上。拖完第一件事,改对象名。选中控件,在右侧属性编辑器的最上面找到objectName:
- 标签改成
labelHello; - 按钮改成
btnSayHello; - 按钮的
text属性改成“点我试试”这类文字。
对象名这件事一定别偷懒。默认的label_2、pushButton_3这种名字,写代码的时候根本分不清谁是谁,等到项目稍微大一点,那就是给自己埋雷。
再把两个控件都选中,在窗体空白处右键,选栅格布局。不布局的后果是,窗口一拉伸,控件原地不动,非常丑。布局是 Qt 界面开发里最容易被忽略的习惯,早点养成。
3.3 用信号槽让按钮真正干一件事
界面拖好了,接下来才是 Qt 的精髓——信号槽。
打开mainwindow.cpp,在构造函数里补一段:
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); connect(ui->btnSayHello, &QPushButton::clicked, this, [this]() { ui->labelHello->setText("Hello World"); }); } MainWindow::~MainWindow() { delete ui; }connect的意思就是:当按钮的clicked信号发射时,执行后面 lambda 里的代码。你点按钮,标签的文字就变成 “Hello World”。
这里我刻意用了 Qt5 推荐的函数指针写法,而不是老的SIGNAL/SLOT宏。差别在于函数指针写法在编译期就能检查信号和槽是否存在、参数是否匹配,老写法跑起来炸了才提示你,排查半天,对新人非常不友好。
快捷键Ctrl+Shift+B生成解决方案,再按F5启动调试。弹出一个窗口,点一下按钮,标签显示 Hello World。到这里,你的第一个图形化 C++ 程序就跑通了。
顺便说一嘴,Qt 还有一种“自动连接”的写法:如果槽函数名刚好叫on_btnSayHello_clicked(),setupUi会自动帮你连上,不用写 connect。这个技巧项目里很常见,但我更建议新手先从手动 connect 学起,看代码时逻辑更直白。
4. 连点三次“调试”的完整排错链路:从报错到能跑
4.1 常见编译错误的排查链路
头一次跑,报错一两回很正常。我把最常见的几类按症状整理了排查顺序:
| 症状 | 常见原因 | 最先排查的地方 |
|---|---|---|
| fatal error C1083:找不到 QMainWindow.h | 插件没注册 Qt 版本,或包含目录缺失 | Qt VS Tools -> Qt Options -> Qt Versions里的路径 |
| LNK2019 无法解析的外部符号 | Qt 库没链接,或模块没勾全 | 确认项目是 Qt 模板创建;Debug/Release 附加依赖项是否正确 |
| 报错找不到 ui_mainwindow.h | uic没跑,或项目不是 Qt 工程 | 删除后重新用 Qt 模板新建,不要从空项目硬改 |
| 运行时弹“0xc000007b” | 32/64 位不匹配 | 检查 VS 平台(Win32/x64)与 Qt 包位数是否一致 |
如果你真的遇到LNK2019,可以这样检查:打开项目属性 -> 配置属性 -> 链接器 -> 输入 -> 附加依赖项。Qt 插件的正常工程里会自动加Qt5Widgetsd.lib(Debug 版带 d,Release 版不带)。如果你是从普通 C++ 项目改的,这里十有八九是空的。但我不推荐手搓附加依赖项来解决,最干净的办法是重新用 Qt 模板建一个项目,把代码复制进去。Qt 模板已经把所有自定义生成规则都配置好了,手搓很容易顾此失彼。
4.2 windeployqt:告别“缺少Qt5Widgets.dll”
过了编译关,还有一关是部署。
清完解决方案,你到 Debug 目录双击HelloWorld.exe,如果本机没装 Qt,大概率弹窗说找不到Qt5Widgets.dll。这是正常的,因为 Qt 是动态库,exe 运行时要到处找依赖。
Qt 官方提供了一个工具叫windeployqt,专门解决这事。从开始菜单打开Qt 5.11.2 (MSVC 2015 64-bit)这个命令行环境,然后进到 exe 所在目录,执行:
windeployqt HelloWorld.exe它会自动把 Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll 以及 platforms 目录下的 qwindows.dll 等一堆依赖拷到 exe 旁边。这一步做完,把整个目录拷到别的 Windows 机器上就能直接运行。
如果你生成的是 Debug 版 exe,记得加参数:
windeployqt HelloWorld.exe --debug不加的话,它默认按 Release 处理,Debug 程序运行时还是会缺带 d 的Qt5Cored.dll之类。
4.3 中文文本乱码的根源与一次性修法
最后一个高频坑是中文乱码。
现象是:标签显示的不是“你好”,而是一串生僻字。原因是 VS2015 把源码文件默认存成了 GBK 编码,而 Qt5 认为源码是 UTF-8,两边各说各话,文字就乱了。
修法很简单:在 VS2015 里打开源文件,文件 -> 高级保存选项,把编码选成Unicode (UTF-8 带签名) - 代码页 65001,保存后重新编译。
还有土办法是在字符串外面包一层:
ui->labelHello->setText(QString::fromLocal8Bit("你好"));这招在中文 Windows 上能临时救急,但换到别的语言环境立马又出问题,不建议长期用。我后来几乎所有项目都统一把源文件存成 UTF-8,一劳永逸。
5. 把Hello World换成你的名字:一个能互动的扩展练习
5.1 加一个QLineEdit,把输入框联动起来
跑通 Hello World 之后,你可以顺手把它扩展成一个能互动的小程序,顺便多练两个控件。
回到 Qt Designer,再拖一个QLineEdit(单行输入框)到窗体,把objectName改成editName。然后回mainwindow.cpp,把构造函数里的 connect 改成:
connect(ui->btnSayHello, &QPushButton::clicked, this, [this]() { QString name = ui->editName->text(); if (name.isEmpty()) { ui->labelHello->setText(tr("Hello World")); } else { ui->labelHello->setText(tr("Hello, ") + name); } });text()返回输入框里的内容;isEmpty()是防空的判断。现在你在输入框里填个名字,点按钮,标签就会显示 “Hello, 名字”。这一步走完,你对“界面控件 + 代码逻辑 + 信号槽”三者之间的配合,就有感觉了。
5.2 一个小习惯让以后的Qt代码更好维护
最后分享一个我在多个项目里都坚持的习惯:字符串一律用tr()包起来。
tr()是 Qt 给多语言和编码管理预留的通道。现在的 Hello World 看不出差别,但以后项目要国际化,或者团队里有人要改文案,tr()包过的字符串能统一提取、统一替换,不会散落在代码各处。用它,就是在为未来的自己少踩坑。
另外一个习惯是,lambda 内部只做简单逻辑。如果槽函数里要做的事超过三行,就别硬塞在 lambda 里,拉出来写成一个普通成员函数,再connect到那个函数。代码一大,可读性的差距就出来了。
这套环境搭好之后,其实你以后做很多 Qt 小工具都会沿用同一套流程。我第一次跑通图形化 Hello World 的时候,以为还要写不少底层代码,结果发现 Qt 把窗口管理、事件分发、消息循环这些都封装好了,作为使用者真的只需要关注业务逻辑本身。后面再遇到“窗口卡死”“界面控件找不到”“dll 缺失”这类问题,先回头检查版本匹配和插件路径,八成都是这些基础环节出了岔子。