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 Tools,MinGW则是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 运行起来:看到第一个窗口的仪式感
代码就这些,现在点击左下角的绿色三角形按钮,或者按Ctrl+R,QtCreator会先调用编译工具链编译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 quick;CMake加上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里按Ctrl+Shift+P可以快速打开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这个成熟的框架无缝衔接。只要第一课的地基打牢了,后面的路自然会越走越顺。