Qt C++入门:环境搭建与Hello World窗口程序实现

📅 发布时间:2026/10/19 8:41:21
Qt C++入门:环境搭建与Hello World窗口程序实现
第一次接触 C Qt 开发我对着 Qt Creator 的新建向导愣了半天。工具链、套件、qmake、shadow build——这些词单个看都认识连在一起就完全不知道什么意思。如果你也卡在 Qt 开发第一天想从最基础的 Hello World 开始这篇文章会很实用。我会从环境准备说起一步步把一个最简单的窗口程序跑起来再把背后“为什么会这样”的机制讲明白。文章不追求覆盖所有 Qt 功能只做一件事让你写完代码以后真正清楚自己在写什么。1. 第一天先别急着写代码Qt 到底解决了什么问题1.1 不绕弯子Qt 是什么Qt 本质上是一套基于 C 的跨平台应用程序开发框架确切点说它不只管界面还管网络、文件、数据库、多线程、XML 解析这些底层能力。我们今天接触的部分只是其中最直观的一块Qt Widgets 模块用来创建传统桌面窗口程序。平时大家常把 Qt 和 C 混着说比如“用 C 写界面”但严格来讲C 标准库本身没有提供任何窗口按钮控件你只能在控制台里打印字符。Qt 填补的正是“C 语言”和“操作系统原生窗口”之间的鸿沟。它把 Windows、Linux、macOS 上不同的窗口 API 封装成一套统一的类你写一份代码理论上就能编译到三个平台这就是它最核心的价值。在这套框架里QApplication 负责整个程序的管理QWidget 是所有窗口控件的基类QLabel 只是显示文本的小控件。后面所有花哨的功能都是从这几个最基本的类延伸出去的。1.2 为什么选 Qt几种写界面的方式对比第一天选技术栈最容易纠结的就是“我到底该学哪个”。我在学习过程中接触过几种主流方案简单对比一下你就知道 Qt 的定位在哪里了方案核心语言跨平台能力上手难度适合场景Win32 API / MFCC仅 Windows高大量样板代码老项目维护Qt WidgetsCWindows / Linux / macOS中文档完善桌面工具、行业软件QML / QtQuickQML C同上中偏向移动/触摸动画丰富的界面ElectronJavaScript三平台低偏 Web 技术栈的桌面应用我的建议很简单你的目标是扎实搞 C或者要做科学计算、嵌入式上位机、工业控制这类偏底层软件直接选 Qt Widgets。Qt 的成熟度非常高二十年积累下来的坑基本都写在文档和示例里了第一天遇到的问题前人几乎都问过一遍。1.3 授权问题个人学习阶段不用太焦虑很多人一上来会问“Qt 是不是要收费”。这个问题值得了解但不必成为第一天的障碍。Qt 采用开源和商业双授权模式。个人学习、开发开源软件使用 LGPL 开源协议的开源版本完全可以但要注意动态链接和保留相关声明。如果你只是自己练手、写内部小工具先装上开源版跑起来完全没问题等真正到了要商用闭源分发那天再评估商业授权也不迟。2. 环境搭建的取舍Qt 版本、编译器与安装组件怎么选2.1 版本选择不必追求最新安装 Qt 的第一步不是下载而是选版本。这里给新手一个非常明确的结论在主版本上第一优先选 LTS 长期支持版。以目前的时间点来说Qt 6.5 和新的 Qt 6.8 都是 LTSQt 5.15 也已进入长期维护状态。对学习而言选其中任何一个都行但建议直接奔 Qt 6毕竟 Qt 6 是当前主线资料和教程也在陆续迁移过去。在 Qt 大版本下面还要考虑编译器的选择。Windows 上最让新手犯晕的就是Qt 提供了一个“使用微软 Visual Studio 编译器MSVC”的版本也提供了“使用 MinGW 编译器”的版本。MinGW 相当于一个可以独立运行的 GCC 改版编译器套件不依赖 Visual Studio安装更省事与 Qt Creator 自带工具链配合也最顺畅。第一天建议直接用 MinGW等以后需要特定 MSVC 版本调试时再切不迟。2.2 安装步骤与组件选择从 Qt 官网下载在线安装器安装时留意以下几个组件选择在版本列表里展开你要的 Qt 版本比如 Qt 6.5.x选择对应平台下的 MinGW 编译器工具链例如 MinGW 11.2.0 64-bit勾选 Qt Creator 这个 IDE 组件如果磁盘空间充足“Sources”和“Qt Debug Symbols”不要嫌大以后调试时很有用尤其是你想深入看 Qt 源码内部逻辑时我反复跟身边的新手强调安装界面里组件很多但第一天最关键的只有三样Qt 版本本身、编译器工具链、Qt Creator。即使你漏装了某个模块后面在 Qt Creator 的“Qt 维护工具”里也能补装不用重装。2.3 Kit 与编译器路径最容易卡住的地方安装完成后Qt Creator 里有个概念叫 Kit可以理解为一套“编译方案”配置。一个 Kit 把编译器、Qt 版本、调试器、CMake 或 qmake 打包在一起。新建项目时它让你选 Kit如果列表是空的项目就没法编译。遇到这种情况先检查 Qt Creator 里“工具→选项→Kits”页面看编译器是否有正常的自动检测结果再看“Qt Versions”页面里是否有 Qt 的安装路径。多数没有 Kit 的问题都是因为安装时漏选了 MinGW 组件或者杀毒软件拦截了安装器导致的。解决方法也很粗暴卸载重装或者用维护工具补装对应编译器组件。2.4 用两条命令验证环境装完之后别急着写代码。先在命令行里确认两个东西编译器在不在、Qt 工具链在不在。如果你安装的是 MinGW 版 Qt会有一个专门的命令行入口启动菜单里通常有“Qt 6.x (MinGW) Command Prompt”之类的快捷方式。在这个终端里分别执行g --version qmake --version第一条输出 GCC 版本号第二条输出 qmake 版本和 Qt 路径。都能看到说明环境已经通了。这一步花不了两分钟却能把“环境问题”和“代码问题”彻底分开后面写代码时能少掉很多不必要的自我怀疑。3. 新建工程之后别急着跑先拆一拆 Qt 生成的骨架3.1 用模板创建一个窗口工程现在正式进入 Hello World。打开 Qt Creator点击“文件→新建项目或文件”在模板里选择“应用程序Application→Qt Widgets Application”。工程名字可以叫 HelloWorld路径选一个不带中文和空格的目录这一步很关键Qt 对中文路径的支持虽然一直在改善但第一天没必要给自己挖这个坑。紧接着会让你选 Kit挑刚才确认过的那个 MinGW Kit 就行。再往下会默认生成一个带菜单栏、工具栏和状态栏的主窗口模板。你可以直接点“构建”按钮看到窗口弹出来的时候你会发现 Qt 的第一印象没那么神秘——它就是一个普通窗口程序。但我的建议是看完模板跑通之后回到起点重新用“空 qmake 工程”再走一遍。因为默认模板自动生成了 mainwindow.h、mainwindow.cpp、mainwindow.ui 三个文件这些来自图形界面设计器的文件会在编译时自动生成一堆额外代码。第一天上来就面对这些很容易把主次搞混。空工程只有一个 .pro 文件和 main.cpp所有逻辑一目了然。3.2 模板生成了哪些文件如果用默认模板工程目录大概是这样的HelloWorld.proqmake 的工程描述文件告诉构建系统这个工程依赖哪些 Qt 模块、生成什么名字的目标main.cpp程序入口mainwindow.h / mainwindow.cpp自定义主窗口类的头文件和实现mainwindow.ui界面设计器生成的 XML 文件后面用 Qt Designer 拖控件时改的就是它.pro 文件内容大概是这样QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET HelloWorld TEMPLATE app DEFINES QT_DEPRECATED_WARNINGSQT widgets表示链接 Qt Widgets 模块。TEMPLATE app表示生成的是可执行程序不是库。它和代码是分离的qmake 会读取这个文件来生成 Makefile。Qt 6 里那行greaterThan判断已经不需要了直接QT widgets就行。3.3 空工程里写一个最简窗口新建空 qmake 工程后手动在 .pro 里加上一行QT widgets然后在 main.cpp 里写这个#include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel window(Hello World这是我的第一个 Qt 窗口); window.resize(360, 120); window.show(); return app.exec(); }按一下构建运行按钮屏幕上会出现一个居中的小窗口上面显示这行文字。如果你能到这一步恭喜你Qt 开发的大门已经打开了。接下来我们要做的就是把这五行代码每一个字都拆开看明白。4. Hello World 五行代码背后的窗口生命期与事件循环4.1 头文件的命名规则代码首行是#include QApplication第二行是#include QLabel。Qt 的头文件命名有一个非常友好的约定类名叫什么头文件名就是什么。想用 QPushButton 就包含 QPushButton想用 QTimer 就包含 QTimer。这和标准库里的vector、string完全不是一个路数你不需要查表记“哪个头文件包含哪个类”基本能猜出来。当你第一次使用某个 Qt 类时编译器提示找不到头文件第一反应也应该是是不是缺了对应的#include而不是去怀疑类不存在。4.2 QApplication每个 Qt 窗口程序都必须有的管家QApplication app(argc, argv);这一行做的不是创建一个窗口而是创建一个“应用对象”。这个对象负责整个程序的资源管理、全局设置和事件分发。它可以是 QApplication也可以根据你的需要换成 QGuiApplication 或 QCoreApplication三个类的层级关系是 QApplication 继承 QGuiApplication再往上继承 QCoreApplication。关键点在于一个 Qt GUI 程序的生命周期里只能存在一个 QApplication 对象。它必须在创建任何窗口控件之前创建。试着把它的创建顺序理解成开饭馆时先找好大堂经理再让服务员上桌干活顺序反了整个店都会乱套。4.3 QLabel所有控件的基础行为都从这三个方法开始QLabel window(Hello World)创建了一个 QLabel 控件。QLabel 继承自 QFrame再往上继承 QWidget。QWidget 是 Qt 里所有可视化控件的共同祖先而所有能作为独立窗口显示控件的公共行为也都在 QWidget 里定义。第一行创建出来时这个控件默认是不可见的也默认没有大小。resize(360, 120)给它设置了像素尺寸show()让它显示出来。这个三件套——创建、设置大小、显示——是 Qt 每个控件的通用模式。哪怕后续你用代码创建一个 QPushButton 或 QTextEdit也逃不出这个流程。有个细节值得注意因为 QLabel 没有指定父对象它就成了顶级窗口。如果给它指定了父控件它就不再是独立窗口而会嵌套显示在父控件里面。这个“父对象决定归属位置”的机制是理解 Qt 布局系统的一把钥匙。4.4 exec() 与事件循环窗口为什么不会一闪而过严格来说这几行代码真正的灵魂是最后一句return app.exec();。QApplication 对象创建好之后程序并没有立刻进入运行状态它需要进入一个事件循环。事件循环可以这样理解操作系统不停地把鼠标点击、按键、窗口重绘、定时器触发等事件打包送进来QApplication 收到后再分发给对应控件去处理。exec()会开启这个循环并且程序会一直停留在这里直到某个事件触发了退出。假如没有这一行main 函数执行到 return 就直接退出了窗口可能只在屏幕上闪现一瞬就消失。这大约也是初学 Qt 时最经典的一个现象“我的窗口为什么闪一下就没了”原因十有八九就是漏了exec()。事件循环的引入也改变了我们对“程序暂停”的理解。在纯 C 的循环里程序执行到while (1)会占满 CPU但在 Qt 的事件循环里没有事件时它是空闲等待的不空转。你可以把exec()想成餐厅的叫号系统顾客不来服务员就站着等来了才动。5. 构建、运行和第一天最容易踩的四个坑5.1 影子构建编译产物到底去了哪里第一次构建 Hello World 时Qt Creator 会在工程目录平级的地方生成一个 build-HelloWorld-... 的文件夹里面摆满乱七八糟的 Makefile、对象文件、可执行文件。这叫影子构建Shadow Build意思是把源码目录和构建目录分开。这样做的理由很实在你可以在不同目录里用不同的 Kit 构建同一个工程互不干扰也不会因为垃圾文件污染源码目录Git 管理起来干净。如果你在工程目录里找不到编译好的 exe不要慌到 build 目录里的 debug 子目录去找。Qt Creator 默认构建的是 Debug 版本这是为了方便断点调试。Release 版本需要手动切换构建套件或构建配置顺便说一句想看真机性能时别用 Debug 版它会慢很多。5.2 脱离 Qt Creator 后提示缺 DLL在 Qt Creator 里点击运行一切正常。但直接跑到 build 目录里双击那个 exeWindows 立刻弹窗说缺 Qt6Widgets.dll 或 Qt6Core.dll。这是第一天极其普遍的“鬼故事”。原理不复杂Qt Creator 运行时自动把 Qt 安装目录里的 bin 目录加进了 PATH所以程序的动态链接库能找到你手动双击时系统找 DLL 的范围里没有 Qt 的安装目录。解决办法有两种。临时验证可以手动把 Qt 的 bin 目录放进 PATH或者直接把 DLL 复制到 exe 旁边。正规做法是用 Qt 自带部署工具 windeployqt在命令行里执行windeployqt --release C:\build\HelloWorld\release\HelloWorld.exe它会自动分析 exe 依赖哪些 Qt 库把需要的 DLL 和插件目录比如 platforms全部复制到 exe 同级目录。这一步是以后做软件发布的必修课第一天知道这个概念就够了。5.3 中文乱码Hello World 如果写成英文还好一旦写中文部分环境下会显示成一堆乱码。这其实涉及源码文件编码和编译器对字符串字面量的处理。Qt 6 里 QString 默认假设源代码是 UTF-8所以只要你把源文件存成 UTF-8Qt Creator 默认就是一般没太大问题。如果还在用 Qt 5或者使用 MSVC 编译器就需要特别注意。保守而稳妥的做法是涉及中文文本时统一用 Qt 推荐的显式字符串转换例如QLabel label(QStringLiteral(中文内容));或者用QString::fromUtf8(中文内容)。虽然看起来啰嗦但能屏蔽掉大部分因为编译器默认代码页不同导致的乱码问题。第一天踩过一次后你会对这个习惯铭记于心。5.4 高频报错速查表根据我自己以及周围同学第一周的反馈下面这几个错误是出现频率最高的我把错误特征和修复方向整理成一张表错误信息特征通常原因处理方向cannot find -lQt5Widgets / -lQt6Widgets.pro 文件里少了 widgets 模块在 .pro 里加QT widgets后重新构建No suitable kits found编译器工具链没装或没配置检查 Kit 页面的自动检测结果补装 MinGWundefined reference to vtable for MainWindow工程缓存和代码不同步删除 build 目录重新 qmake / 重新构建Project ERROR: Unknown module(s) in QT安装时没勾选对应模块用维护工具补装对应 Qt 组件窗口一闪而过main 里没有调用 exec()补上return app.exec();遇到错误时先看完整错误文本不要只看第一行。编译器给出的信息往往已经告诉你缺了哪个库、哪一行出了问题。第一天不需要会修所有错误但需要学会“读懂错误在指向什么”这比背修复答案重要得多。6. 第二天路线图从静态文本到按钮与信号槽6.1 Hello World 只是起点信号与槽才是 Qt 的灵魂如果你第一天把上面全跑通第二个自然动作就是让窗口“动起来”。Qt 最标志性的机制叫信号与槽。简单说某个事件发生时会发出一个信号你可以把一个槽函数连接到这个信号上信号一旦触发槽函数就会执行。它是 Qt 对“事件回调”的封装比传统 C 语言风格的回调函数安全得多因为类型不匹配时编译器会直接报错。举个例子我想让窗口里放一个按钮点它就能退掉整个程序代码可以改成这样#include QApplication #include QPushButton int main(int argc, char *argv[]) { QApplication app(argc, argv); QPushButton button(点我关闭窗口); QObject::connect(button, QPushButton::clicked, app, QApplication::quit); button.show(); return app.exec(); }注意connect这行它把按钮的 clicked 信号连接到了 app 的 quit 槽。clicked 是在按钮被点击的瞬间发出的quit 是 QApplication 提供的一个会让事件循环结束的槽。整个程序的本质变成了初始化界面 → 进入事件循环 → 用户点按钮 → 发出信号 → 因信号调用 quit → 事件循环结束 → 程序退出。学习 Qt 从第一天到以后很长一段时间你都会和 connect 打交道所以越早适应越好。6.2 给第一天新手的三个务实建议我自己的体会是学 Qt 最容易产生的错觉是“我要把所有控件都学会”。实际上 Qt 控件有上百个硬背根本记不住真正有经验的人也是到了具体需求才去查控件。建议第一每天只掌握一个概念。比如第一天只搞明白 QApplication 和 QWidget 的关系第二天只学信号槽第三天只学布局管理器。一步一个脚印比看十篇大而全的教程都有效。建议第二重视自带的官方示例。Qt 安装目录里有一堆 examples这些是官方筛选过的黄金案例。遇到不懂的类直接搜索该类对应的 example比在搜索引擎里扒过时帖子靠谱得多。建议第三多动手改参数。就算照着我的代码写出来了也建议把数字改一改、把字符串改一改比如把按钮文字改成自己的名字看看有没有哪里报错。实际练习中踩过的小坑往往比顺利跑通过的代码更能帮你建立直觉。第一天不求做出多复杂的界面跑稳一个窗口理解它为什么活着就已经走在正确的路上了。