ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Qt/QML入门:用Qt Creator创建第一个Hello World工程

Qt/QML入门:用Qt Creator创建第一个Hello World工程 Hello World大概是每个程序员绕不开的第一课。从大一写C语言那个黑乎乎的终端里蹦出两行字开始到后来接触各种GUI框架每个新环境的第一件事几乎都是确认“Hello World能不能跑起来”。放到Qt/QML这套技术栈里这件事的意义就更实在了——它不只是验证环境装没装好更是理解“声明式UI”这个核心思想的入口。我最早是拿QWidgets写界面的后来转到QML说实话刚开始很不适应。习惯了一行行new控件、设置属性的写法突然改成“描述界面长什么样”总觉得心里没底。但写了几轮带状态切换、动画过渡的界面之后才真正体会到声明式的好处界面代码和业务逻辑可以拆得更干净UI表达力强改起来也快。这篇内容就是为准备跨进Qt/QML门槛的朋友准备的从用QtCreator10创建第一个QML工程开始把Hello World每一行代码的含义、工程里每个文件的职责、以及我踩过的那些低级坑完整地捋一遍。不管你是刚装完Qt还没建过程序的新手还是从Widgets转过来的老手应该都能从中找到点对路的东西。1. 内容整体设计与思路拆解1.1 为什么是QtCreator10而不是直接写代码Qt本身是一个跨平台的C框架但真正让开发效率提起来的是它配套的IDE——QtCreator。版本到10这个节点经历了很多代的积淀功能已经非常成熟。我之所以推荐用它来创建和管理QML工程是因为它对QML语言的支持做得很到位彩色高亮、语法补全、属性提示、还有那个实时预览功能都能减少很多“低级错误”。有人可能会问QML文件本质上是纯文本用VS Code配个插件不也能写吗确实能写但工程依赖和编译流程会比较折腾。QtCreator10的最大优势在于“一站式”新建工程、管理.pro或CMakeLists、选择构建套件Kit、编译运行、调试全都在一个界面里闭环。尤其是QML那种运行时才报错的语言特性配合QtCreator的调试器和控制台输出排查问题会直观很多。还有一个细节我很喜欢QtCreator10的QML编辑器支持Ctrl空格快速补全输入属性名的时候会自动列出候选值比如写font.pixelSize它会提示像素值、还有字号上下文的参考。这种体验对初学者特别友好至少不用记全那些青涩的属性拼写。所以我一直觉得初学QML老老实实用QtCreator是性价比最高的选择等熟悉到一定程度你再去折腾VSCode或者命令行构建也不迟。1.2 第一个程序为什么选QML而不是QWidgets现在Qt官方主推的界面方案有两种一种是老牌的QWidgets一种是基于QML的Qt Quick。很多刚接触Qt的人会纠结到底学哪个我的观点很直接——如果是新项目、需要漂亮界面、希望前后端分离优先学QML如果是在维护老系统、或者做纯桌面工具类软件、追求极致稳定QWidgets也完全可以。但对于入门第一个程序我强烈推荐QML。原因不复杂。QML的思维方式是“描述界面长什么样而不是一步一步画出界面”。比如你想放一行文字在QML里就是写一个Text元素设置它的text属性、位置属性、字体属性界面会自动跟着变化。而QWidgets的方式是用代码new一个QLabel设置字体、设置几何、添加到布局里中间还得考虑布局策略、尺寸策略这些东西。对新手来说QML的声明式写法更贴近“人类描述界面”的自然方式理解成本低得多。另外就是QML和C的混合编程非常灵活。界面上用QML描述业务逻辑用C处理两者通过信号槽和上下文属性交互。这种模式在QtCreator的工程模板里已经预设好了Hello World跑通之后你再往上叠加C逻辑路径非常清晰。这也是为什么很多实际项目选择QML的原因——它不排斥C反而把C放在它该在的地方。1.3 这篇内容能帮你解决什么问题如果你是个零基础新手跟着这串操作走完至少能得到三个明确的产出第一电脑上装好一套能用的QtCreator10开发环境知道该勾选哪些组件第二成功创建一个QML工程并用内置模板跑出第一个界面第三完完整整理解那几十行Hello World代码知道每个文件存在的意义、每行代码在干什么。除此之外我还会把从工程创建到运行过程中最隐蔽的“坑”挖出来比如为什么新建工程后运行没反应为什么QML文件改了但界面不刷新为什么明明写了Text却看不到字以及那个让无数人头疼的“Qt的.qml文件用记事本打开看到中文会乱码”问题。这些都遇到过也一个个解决过写出来算是给大家省点时间。2. 环境准备与创建QML工程2.1 下载安装与组件选择的取舍工欲善其事必先利其器。装Qt Creator这件事最关键的其实不是下载安装包本身而是选择安装哪些模块。很多人装完才发现缺少QML模块工程编译时直接报“Unknown module(s) in QT: qml”类似的错误一脸懵。这通常是安装时图省事或者不理解组件含义导致的。我拿Qt 5.15.2这个经典版本来举例虽然Qt6也已经很成熟但5.15.2仍然有大量工程在用。安装器运行后会让你选择组件这里有几个必选项Qt 5.15.2 分类下根据自己的编译器选一套比如MSVC 2019 64-bit或者MinGW 8.1.0 64-bit。这两个分别对应不同的编译工具链MSVC需要电脑装了对应版本的Visual Studio Build ToolsMinGW则是Qt自带的GCC编译器。新手我更推荐MinGW省去配置VS的麻烦。Qt 5.15.2 分类下一般默认就会带上Qt Quick和QML模块但最好检查一下“Qt Quick 3D”之类可以暂时不选普通2D界面用不到。Developer and Designer Tools 分类下记得选上Qt Creator本身的组件如果是绿色版或者在线安装器一般会包含还有Qt Debugger调试器这个在后续排错时非常有用。安装路径尽量不要有中文和空格我早年吃过这个亏。有次装到一个带空格的目录结果好几个插件加载失败查了很久才反应过来是路径问题。2.2 新建工程从向导到第一个可运行界面打开QtCreator10之后第一步操作是左上角“文件”菜单选择“新建项目或文件”或者直接点击欢迎页的“New Project”按钮。这一步其实是个向导QtCreator会引导你选择工程类型。在模板列表里选择“Application”分类下的“Qt Quick Application - Empty”。这里注意别选错有的模板带了StackView、SwipeView这些导航骨架对新手来说内容太多反而干扰。Empty模板比较干净生成出来的代码最少最适合从头讲解。接着填项目名称比如HelloWorld选择保存路径。然后会让你选择构建系统老版本默认是qmake.pro文件新版本默认是CMake。说实话两个都能用qmake更贴近Qt自身的传统配置简单CMake则更通用是现在整个C社区的主流。我个人的建议是如果你未来可能要碰大型跨平台项目选CMake如果只是想快速验证一个QML程序选qmake也无妨它生成的.pro文件看着确实清楚。选择构建套件Kit时确认你选中的Kit和安装Qt时选的编译器对应。比如你装的是MinGW那Kit就选带MinGW的那个千万别选MSVC的Kit否则编译时找不到编译器报错会让你怀疑人生。完成之后QtCreator会自动生成工程文件并打开这个界面就是你的第一个QML工程了。2.3 工程文件结构每个文件都不是白给的工程生成之后你会在左侧的“项目”面板里看到这几个关键文件HelloWorld.pro或CMakeLists.txt工程配置文件描述这个工程依赖哪些Qt模块、源文件有哪些。qmake的.pro文件简单直接几行字CMakeLists.txt则会多一些但整体也不难理解。初次接触时看个大概就行理解不了细节不影响你跑通程序。main.cpp整个程序的C入口。它的职责是创建QQmlApplicationEngine加载你的QML文件。这个文件用到的代码很固定几乎每个QML工程长得都一样。qml.qrc资源文件把QML文件打包进程序里。看起来像XML格式里面记录了qml文件的路径。如果你手动加了新的QML文件记得往里面注册一下否则运行时找不到。Main.qml界面描述文件这是整个Hello World的主角。整体逻辑就像一场舞台剧main.cpp是幕后导演负责启动引擎、把舞台搭好Main.qml是剧本描述舞台上的布景和演员.pro或者说CMakeLists.txt是剧务负责准备道具链接Qt模块qml.qrc是道具清单告诉引擎去哪里找剧本。理解了这层关系后面无论工程做得多大你都不会迷路。我曾经见过一个新人往工程里手动添加了一个QML文件但忘记更新qrc结果界面一直加载不出来查了一整天最后发现是资源清单里没有那一项。这就是典型的“文件结构没吃透”。3. Hello World逐行拆解3.1 先看C入口main.cpp做了什么QtCreator生成的main.cpp通常长这样#include QGuiApplication #include QQmlApplicationEngine int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral(qrc:/Main.qml)); QObject::connect(engine, QQmlApplicationEngine::objectCreated, app, [url](QObject *obj, const QUrl objUrl) { if (!obj url objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }别被这些代码吓到其实拆开看就几件事。QGuiApplication是QML程序的事件循环对象相当于整个程序的“心跳”所有界面事件、鼠标键盘消息都靠它分发给对应的QML元素。QQmlApplicationEngine是加载QML文件的引擎它读入qrc:/Main.qml这个URL解析QML语法创建对应的界面对象。中间那段QObject::connect是做一个加载失败检查如果QML引擎没能成功创建顶层对象程序就直接退出也就是说如果你的QML文件写错了程序会静默退出不弹任何窗口这时候去抓编译期日志往往抓不到有用信息。最后app.exec()进入事件循环程序开始跑起来直到你关掉窗口。这段代码在几乎所有QML工程里都一模一样你照着用就行不用每行都重新理解。但有一个坑要提醒那个URL——qrc:/Main.qml是大小写敏感的。如果你把文件改成了main.qml但这里还是Main.qml运行时就会加载失败又没任何提示。我遇到过好几个朋友栽在这上面。3.2 QML界面声明式思想的第一次直观感受接下来是真正的重点——Main.qml。工程模板默认生成的代码差不多是这样import QtQuick 2.15 import QtQuick.Window 2.15 Window { visible: true width: 640 height: 480 title: qsTr(Hello World) color: white Text { anchors.centerIn: parent text: qsTr(Hello World!) font.pixelSize: 32 color: #333333 } }第一次看到这种代码的人通常会有两个反应。一个觉得“这怎么像JSON但又不像”另一个是“这居然不需要写main函数吗”。其实QML的核心思想就在这儿你不再需要写window.show()之类的命令式调用了你只需要声明——这是一个Window它的宽高是多少标题是什么里面放了一个Text内容是什么。至于怎么把窗口显示出来QML引擎会在加载文件时自动处理。import QtQuick 2.15和import QtQuick.Window 2.15是引入模块类似C里的#include。QtQuick模块提供基础可视化元素Text、Rectangle、Image等QtQuick.Window则提供Window类型。这两行必须写在文件开头版本号要和你的Qt版本匹配。如果版本不匹配最常见的错误是“module QtQuick version 2.15 is not installed”那时候你就得回头看看自己装的是Qt5还是Qt6。Window是这个界面树的根节点。visible: true表示窗口创建后直接显示出来别忘了这个属性——如果忘了写或者写成false程序运行后啥都看不到但进程还在后台跑着。title不用说了就是标题栏的文字。这里用qsTr()包了一层是为了将来做国际化i18n做准备中文环境显示中文切到英文环境显示英文。不建议直接硬编码裸字符串虽然现在看不出区别但项目大了之后再改麻烦。Text元素里面的anchors.centerIn: parent是QML布局的精髓。它表示让这个Text元素在父元素这里是Window里水平垂直居中。font.pixelSize设置字号的像素值color设置文字颜色。这里温馨提示一下QML的颜色既支持英文单词如red也支持十六进制色值如#333333还支持Qt.rgba(r,g,b,a)这种RGBA函数。3.3 运行起来看到第一个窗口的仪式感代码就这些现在点击左下角的绿色三角形按钮或者按CtrlRQtCreator会先调用编译工具链编译C部分其实C部分几乎没改动所以编译很快然后启动程序。如果你是第一次运行可能会稍微慢一点毕竟需要链接Qt的库。看到一个新窗口弹出来标题栏写着“Hello World”中间显示着“Hello World!”字符串这就说明整个链路已经通了QtCreator成功编译了C入口QML引擎正确加载了界面文件Window和Text元素都被创建了出来。这时候你可能觉得“就这么点东西”但请相信我从零到这里的完整链路比什么都重要。它证明你的开发环境、你的工程配置、你的代码逻辑全都正确之后你在这个基础上添加任何功能都是在有了一个稳固地基的房子里装修。4. 从Hello World延伸的QML核心机制4.1 对象树与属性绑定QML最强大的底层逻辑Hello World只展示了一个Window套Text的简单结构但它背后体现的是QML的理论基石——对象树和属性绑定。在QML里每个元素可视或不可视都是对象它们按层级组织子元素可以访问父元素的属性还可以通过id互相引用。这种嵌套关系顺着main.qml一展开就是一棵清晰的树。属性绑定则是QML最让我上瘾的功能。它表示一个属性可以绑定到另一个属性或其计算结果上被依赖的属性一旦变化绑定它的属性会自动更新。比如你在Text上面放一个Rectangle作为它的背景Rectangle { width: textItem.implicitWidth 20 height: textItem.implicitHeight 20 color: lightgray Text { id: textItem anchors.centerIn: parent text: Hello World } }这里width和height并不是在赋值时算一次就完了而是一直保持绑定关系。如果text的内容变了implicitWidth会跟着变然后外层的Rectangle宽度也自动更新了。这种机制写UI效率极高你不需要在C代码里到处写“updateUI”函数界面自身的逻辑就在声明里自动完成了。不过属性绑定也有个坑如果你在JavaScript代码块里给一个属性做了赋值例如rect.width 200这个赋值会破坏原有的绑定关系。以后就算被绑定的源变了rect.width也不会再自动更新。这种问题在项目变大之后特别隐蔽排查起来让人头秃。我的经验是尽量避免命令式赋值如果需要动态计算就使用Qt.binding()或者把计算逻辑放到函数里。4.2 信号与槽用户操作怎么响应Hello World里没有涉及交互但QML里最常用的交互模式就是信号与槽。比如你想在用户点击某个区域时改变文字内容可以这样写Rectangle { width: 200 height: 100 color: lightblue Text { id: label anchors.centerIn: parent text: Click me } MouseArea { anchors.fill: parent onClicked: { label.text You clicked! } } }MouseArea是一个不可见的鼠标交互区域anchors.fill: parent让它覆盖整个Rectangle。当用户点击它时onClicked信号处理器会被触发里面写JavaScript代码或调用QML函数都行。这种“信号处理器”模式本质上体现了Qt信号槽机制在QML层的延伸。实际项目里界面上的点击最终往往要传到C层去处理。QML和C交互的经典方式之一是在C里定义一个继承自QObject的类通过qmlRegisterType注册给QML然后在QML里直接new出来用或者把C实例通过engine.rootContext()-setContextProperty()设置为上下文属性。这些属于进阶内容但如果你从Hello World往下一个台阶走马上就会碰到。4.3 无边框窗口与动画QML的拿手好戏看过QML的Hello World后很多人会继续做两个特别常见的尝试自定义无边框窗口以及加动画效果。这两个方向也是QML被大家喜欢的重要原因。无边框窗口怎么实现其实核心就是把Window的flags设为Qt.FramelessWindowHint然后自己实现拖拽移动和边缘缩放。拖拽移动通常写法是MouseArea { anchors.fill: parent property point clickPos: 0,0 onPressed: (mouse) { clickPos Qt.point(mouse.x, mouse.y) } onPositionChanged: (mouse) { var delta Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) window.x delta.x window.y delta.y } }注意这里clickPos用了property point来保存按下时的坐标然后每次移动时计算偏移量更新窗口的x和y。这里有个经验教训不要直接设置窗口坐标等于鼠标全局坐标否则窗口会跳动。原因很简单鼠标在窗口内的位置不是固定的每次移动窗口鼠标相对窗口的位置就变了必须用“偏移量”来增量移动。动画也是QML的强项。想在Hello World上加个“文字淡入”效果你只需要Text { text: Hello World opacity: 0 NumberAnimation on opacity { from: 0 to: 1 duration: 1000 } }声明式动画的优点在于你不需要手动管理定时器、不用写循环更新逻辑只需声明起始值、结束值和时长QtQuick的动画框架会自动驱动属性的插值变化。这在传统Widgets里可能要几十行代码在QML里就这几行。很多刚接触QML的人都会觉得“哇原来做动画这么容易”这种体感在后面的开发中会一直伴随你。4.4 qsTr与国际化为多语言界面做的第一手准备在Hello World里用qsTr()包住字符串很多新手不理解觉得没必要。但国际化i18n是很多Qt/QML项目绕不开的功能。qsTr()是QML提供给每个元素的翻译函数配合Qt的语言家Linguist工具可以后续把界面翻译成多种语言而不需要改代码。具体用法很简单你写界面文案时直接qsTr(Hello World)。将来准备翻译时运行lupdate工具扫描QML文件生成.ts翻译文件用QtLinguist打开翻译再运行lrelease生成.qm文件在main.cpp里通过QTranslator加载对应的语言文件即可。整个过程界面代码不用改一行。有人可能会问不写qsTr()就不能国际化吗也能但你得把所有字符串抽出来放到单独的资源文件里用索引去引用维护麻烦得多。既然Qt已经提供了qsTr()这么自然的方式何乐而不为呢。5. 常见问题与排查技巧实录5.1 编译错误类问题速查新手最先遇到的往往不是业务逻辑而是环境或语法造成的编译错误。我在学习Qt/QML的过程里踩过不少坑这里列一张表给大家参考都是我实际遇到过的不是从文档里抄出来的错误现象常见原因解决办法错误信息里出现“Unknown module(s) in QT: qml”安装Qt时没有勾选QML/Quick模块重新运行Qt安装器勾选Qt Quick和QML相关组件或者直接换Qt版本安装编译报错找不到qml头文件QQmlApplicationEngine等.pro或CMakeLists.txt中未引入Qt模块qmake在.pro里加QT qml quickCMake加上find_package(Qt6 COMPONENTS Qml Quick)运行时程序秒退无任何窗口QML文件加载失败或存在语法错误打开QtCreator的“编译输出”面板查看“QML Debugger”或控制台日志也可以临时在main.cpp里加QObject::connect打印错误QML代码里写错了属性名程序没报错QML对部分错误是运行时才触发的不会在编译期暴露在QtCreator的“QML/JS调试”视图中查看错误信息或者打开“工具-QML/JS编辑器-语法检查”这些错误里最让人头疼的其实不是编译期报错而是运行时静默失败。比如QML文件路径写错、文件没有添加进qrc这些都不会直接报“文件不存在”的错误而是窗口根本不显示。这种时候我的排查思路是先在main.cpp里加打印信息然后用engine.load(url)之后检查engine.rootObjects()是否为空再不行就看看QtCreator底部的“General Messages”面板。5.2 编辑与预览阶段的坑QtCreator10自带的QML Designer提供了可视化设计界面可以直接拖拽控件。但我个人实际体验是复杂的QML元素树一旦多了之后设计器会卡而且它生成的代码不一定符合你的预期甚至会把你的手工排版搞乱。所以我一直建议新手直接用代码写QML配合实时预览功能来做微调——在QtCreator里按CtrlShiftP可以快速打开QML预览面板代码一改预览就更新效果一目了然。还有一个特别常见的坑是在QML文件里改了代码但运行出来的界面没变化。这通常是因为QtCreator的构建缓存没刷新或者程序还跑在旧进程上。解决办法是先停止程序点红色方块再重新运行。如果还是不行执行“构建-清理所有”再重新构建。另外有些QML改动是纯QML层面其实不需要重新编译C但QtCreator的机制是每次运行都会重新构建一次速度慢一点但不容易出现旧版本残留。5.3 中文显示与文本乱码问题中文乱码这个问题几乎每个用Qt/QML的人都遇到过它分两种情况。一种情况是在QML文件里直接写中文比如text: 你好世界运行后发现显示成乱码或者在QtCreator里看着好好的换台电脑运行就乱了。这往往是文件编码不是UTF-8导致的。QtCreator默认会以UTF-8保存文件但如果你用记事本、或者某些编辑器改过文件编码就可能变成GBK。解决办法很直接在QtCreator里打开文件点击右下角的编码信息比如“UTF-8”选择“按编码重新加载”再保存为UTF-8格式。另一种情况是程序运行起来后中文字符显示成方块豆腐块。这是字体的问题而不是编码问题。Qt在某些Linux发行版上不默认带中文字体Windows或macOS上一般没事。解决办法是在你的QML里显式指定一个中文字体比如font.family: Microsoft YaHei或者为了保证跨平台用Qt.application.font.family做基础同时在系统中安装好中文字体。这个坑我在Ubuntu上踩到过那时候我还以为是自己代码的问题排查了好久才发现是系统没有安装中文字体。5.4 工程结构与资源管理问题最后聊一个工程层面的问题手动添加QML文件时经常会遇到“新增了文件但运行时找不到”。前面提过这大概率是qrc资源系统的问题。在QtCreator里右键点击.qrc文件选择“添加现有文件”把新的QML文件加进去路径填相对路径然后重新构建即可。另外还要注意QML里加载图片或字体等资源时路径也是相对于qrc根目录的。比如你在qrc里加了一个images/logo.png在QML里应该写source: qrc:/images/logo.png或者至少写images/logo.png。有不少人在这里写绝对路径一换电脑或者一打包发布就找不到资源了。这个习惯要从Hello World阶段就养成所有资源都通过qrc来管理用相对路径引用发布时才不会话乱。6. 后续学习建议个人经验向Hello World跑通了接下来怎么走根据我带过的新人和自己的经历给你一条相对平滑的路线就当参考。第一步先折腾界面布局。把Rectangle、Text、Image这几个基础元素玩熟配合anchors、Row、Column、Grid这些布局方式做一两个带真实内容的静态页面。这个阶段的关键是熟悉QML的属性系统不用碰C逻辑。第二步加交互。给界面添上MouseArea、Button、TextField这些可交互元素用信号处理器更新界面状态配合state和Transition做简单的状态切换。这里你会体会到QML状态机描述UI有多方便也能理解为什么说“状态驱动界面”。第三步接C。在工程里添加自定义的C类用Q_PROPERTY声明属性注册到QML让界面和业务逻辑真正分离。这时候你再去搜“qml与C交互”“qml与C混合编程详解”理解起来就非常顺了。别忘了把main.cpp里那段加载代码看懂这是QML和C之间的“桥”。第四步根据兴趣选方向。喜欢视觉效果去研究动画框架PropertyAnimation、Behavior、Transition和自定义控件绘制Canvas想做工具型软件去研究TableView、ListView和模型-视图架构QAbstractListModel和QML的ListView对接想搞数据可视化Qt官方有Qt Charts模块配合QML可以做交互性很强的图表界面。这个顺序里每走一步都会踩新坑也会解锁更多乐趣。我个人最大的体会是QML这套技术栈的上手门槛其实不高但它的天花板非常高。从最简单的Hello World到复杂的大型项目核心思路始终是统一的UI靠声明描述逻辑靠C承载两者通过Qt这个成熟的框架无缝衔接。只要第一课的地基打牢了后面的路自然会越走越顺。
返回列表