Qt
几乎是 C++ 领域最流行的跨平台桌面端软件开发框架了,
这个框架是两个挪威人在 1995 年创建的,发展至今可以说历史相当悠久,稳定性也很有保障。
很多大公司都在用它做界面比如金山的 WPS。
它内置了自绘引擎,也就是说界面上的一个按钮,一个文本框,都是 Qt 的引擎自己画的,这保证了基于 Qt 开发的软件界面在不同操作系统上看起来是一模一样的。
它提供了大量的与界面无关但与软件开发息息相关的 API,比如、网络、文件系统、剪切板等,而且让这些 API 在不同的操作系统下都有效,这极大的节省了开发人员的时间。
但它也有一些缺点,比如在处理一些特殊需求上很不方便,比如:目前 Qt 有没有比较好解决高分屏下缩放显示的方案?,Qt 没有真正完美的无边框解决方案吗?等,
在一些组件的渲染上也会出一些隐藏的较深的问题(QListItem),一旦遇到,就很难解决。
Qt 近年来不太专一,qml,qtquick 等,搞了很多,而且这些新玩意儿一直不温不火,有些模块做了又废弃了,比如:qt script,搞来搞去,搞的模块繁多且复杂,用起来不是很舒服。
Qt 有界面描述语言(XML 描述界面),可以通过设计器拖拽空间设计界面,编译期界面描述语言被转义成 C++ 代码,性能上没啥损失。
Qt 商业授权不太友好,开发商业应用一定要谨慎,之前听说有公司为此付出了高额的版权费。个人开发者可以免费使用。
Qt 的免费版本不允许静态链接,会有版权上的限制,但开发者还是可以通过一些特殊的编译方法静态连接 Qt 的库的。
除了使用 C++ 开发 Qt 应用外,开发者还可以使用其他语言开发 Qt 应用,
最流行的就是使用 Python 基于 PyQt 做 Qt 应用了,其他语言的绑定不是很成熟,但 PyQt 仍然有版权的问题。
GTK
GTK 是 1997 年创建的,也非常成熟稳定,
是 C 语言开发的,但有很多语言的绑定,比如官方支持的 JavaScript、Rust 等,当然用 C++ 语言操作 GTK 也很方便,
它也有自绘引擎(Cairo),也提供了大量系统相关的 API,
商业授权也非常友好,基于 GTK 开发商业软件不用担心收到律师函的问题,
虽然它是一个跨平台桌面软件,但它似乎只在 Linux 操作系统领域流行,有非常多的 Linux 桌面软件都是基于 GTK 开发的。
这也直接导致 GTK 的维护者很重视 Linux 领域的发展,而忽视 Windows 和 Mac 领域。
这个框架提供的很多 API,只在 Linux 下有,Windows 和 Mac 下没有。这样的 API 数量众多。
甚至在 Windows 下编译一下 GTK 的源码都要比 Linux 下难很多。
而且 GTK 的渲染引擎在 Windows 下性能表现也不如在 Linux 下好。
GTK 在 Windows 上也没办法静态连接,倒不是因为版权的问题,而是它依赖了 MSYS2 的一些库,这个库用于在 Windows 上模拟 Linux 环境,这也是为什么 GTK 在 Windows 上表现不佳的原因之一。
另外,由于 GTK 是 C 语言开发的,所以开发风格也很 C 语言化,这对于部分开发者来说可能觉得繁琐。
wxWidgets
wxWidgets 是 1992 年英国的一个大学教授开创的跨平台 GUI 软件,也非常成熟稳定,商业授权非常友好。
它没有自绘引擎,而是对不同平台下的界面 API 做了整合和封装,
这样开发者在 Windows 下开发的软件看起来就是 Windows 窗口风格、Linux 开发的软件看起来就是 Linux 窗口风格,
这对于某些软件来说,正是他们想要的,但要想搞一些花哨的特效就没那么容易了。它同样也提供了大量的系统相关的 API 供开发者使用。
它是 C++ 开发的,所以对 C++ 开发者非常友好,
除此之外它还支持静态连接,也就是说开发个应用不用分发给用户一大堆 dll。
它会有些小问题,比如我之前提的:wxEVT_NOTIFICATION_MESSAGE_DISMISSED event emit twice,但总体来说还是非常稳的。
除了开发的界面比较死板外,没啥大的问题。
FLTK
FLTK 是 1998 年创建的跨平台开源 GUI 框架,历史悠久,商业授权友好,而且 C++ 之父也用它,
它非常轻量级,支持静态连接,一个简单的应用编译后只有 500K 左右,非常赞,
它有自己的自绘引擎,用的是 OpenGL,
但它的重绘机制是按区域重绘的,如果组件 A 所在的区域上存在组件 B,那么 A 组件重绘时,会把 B 组件的给重绘掉,开发者必须自己写代码处理这种情况。
想象一下,如果你想实现一个 A 组件 fade out 的同时 B 组件 fade in 的效果,就会非常麻烦。
FLTK 提供的一些组件样式都比较刻板,绘图 API 也比较少,
你想实现一个漂亮一点的圆角按钮(它内置圆角按钮的圆角大小是不能改的),必须自己画,而且还得借助一些非常奇葩的手段才行(如果你想知道,可以联系我)
它是 C++ 开发的,但 API 不够现代,用起来总体还算舒服的,
它有 Rust 绑定:fltk-rs。它提供了一些与界面无关的操作系统 API,但非常少,几乎可以忽略。
Duilib
是 2010 年国内一个开发者开发的 GUI 开发框架,
因为底层基于 DirectUI 开发,所以只支持 Windows 平台,不支持跨平台,
开源协议友好,商用没有任何问题(需要附加 Lincence 文件),
国内有很多大厂基于这个技术做桌面端应用,比如网易、腾讯、百度,
这个框架是基于 C++ 开发的,对 C++ 开发者友好。
但框架本身还有一些问题,比如对高分屏支持不佳、特殊控件绘制上也有一些小问题,
除了界面相关的 API 外,几乎没有提供系统级的 API,作者纯粹是用爱发电来开发这个框架,所以更新不是很及时。
相对来说网易基于 Duilib 开发的分支更完善一些:NIM_Duilib_Framework,添加了高分屏支持、多国语言、整合了多线程处理的支持,
但环境搭建相对比较麻烦。如果开发者要用这个框架,一定要用 develop 分支下的代码,master 分支下的代码问题很多,这个框架看上去也是作者一个人努力的成果。
Sciter
Sciter 是 2006 年创建的跨平台闭源 GUI 框架,足够稳定,
它商业授权不友好,但个人开发者可以随便用(只能用动态链接库),一旦公司规模超过 3 人,就得买版权了(也就有权静态连接了)。
它内部封了一个浏览器核心,但对这个浏览器核心做了大量的精简,不像 Electron 和 NW.js 动辄上百兆的体积,它只要 6M 左右就够了。底层的绘制引擎我记得是谷歌的 skia,
开发者可以使用 HTML,CSS,JS 来创建界面,当然由于底层是一个阉割版的浏览器核心,这也意味着有些浏览器特性它是不支持的,
比如 CSS3 的 flex 布局,它就不支持(但它提供了自己的 flex 布局实现方式)。
以前它使用自研的一个脚本语言(和 JavaScript 很像),自从集成了 Fabrice Bellard 大神的 QuickJs 之后,就全面支持 JavaScript 了。
另外,它还对一些特殊的场景做了内置的支持,比如渲染大列表。
它使用 C++ 开发,对 C++ 开发者很友好,有 Rust、go、Python 等语言的绑定,但都是社区提供的,质量堪忧。
有很多知名厂商都用这个库做界面,比如 360、teamviewer、赛门铁克等。
另一个库 RmlUi 和 Sciter 很像,可以看成 Sciter 的替代框架,
但 RmlUi 这个项目有三届作者,一个一个的弃坑不知道新任作者会不会弃坑,目前还不是很成熟,比如我正在尝试帮作者解决的 CJK 输入法的问题,目前还不推荐大家使用这个框架。
CEF
CEF 是 2008 年创立的,基于 Chromium 的跨平台 GUI 框架,稳定且商业授权友好,
国内很多大厂都用的 CEF:比如微信桌面端、网易云音乐桌面端(Win)、QQ 桌面端、微信桌面端、MATLAB、FoxMail、OBS Studio,装机量破亿(过于保守)。
由于它几乎封了一个完整的 Chromium,所以体积非常大,但它支持所有的 HTML\CSS\JS 特性,
它几乎不提供任何与操作系统相关的 API,创建个托盘图标、读写个文件啥的,都要开发者自己完成,
它是 C/C++ 开发完成的,对 C++ 用户非常友好,它有 go\python\java 等语言的绑定,但都是社区提供的,质量值得担忧。
它对 Chromium 封装的很好,避免了开发者直接与 Blink、V8、Chromium 等复杂的代码打交道,
很多功能都有默认实现方式,遵从约定由于配置原则,有经验的 C++ 开发者可以很轻松的驾驭 CEF 框架。
由于 Chromium 是版本弟,所以 CEF 版本发布也非常频繁,很多被标记为稳定的版本,还是会出一些莫名其妙的问题,选一个好的版本非常重要。
与 Electron 一样,它也是分主进程和渲染进程的,所以开发者要非常娴熟的运用跨进程通信的技术,
虽然 CEF 提供了跨进程相关的 API,但复杂度还是有点高的,使用的时候要认真细心。
这是我在掘金写的关于 CEF 的系列课程:https://juejin.cn/book/7075387142121193502
MAUI
这是微软的跨平台 GUI 框架,不仅仅支持桌面端,还支持移动端,但官方并不支持 Linux 的桌面端(黑人问号,感觉与微软近些年向开放、开源的大方针相悖),
这个框架新的狠,至今还没发布稳定版。
它是.NET 平台下的 GUI 框架,有自绘引擎,对 C# 开发者很友好,界面依然是用 XAML 描述的,可能很多人一听到 XAML 就直接弃坑了。
XAML 表现力确实弱一些,我觉得 WPF 没火起来跟 XAML 有直接关系。
使用这个框架开发桌面应用得封一个.NET 框架给用户,当然有了.NET 框架应用程序访问一般的系统级 API 也就不成问题了。
Compose Multiplatform
这是 JetBrains 搞的跨平台 GUI 框架,也非常新,前段时间刚刚推出 1.0.0 版本,
但这个版本还不是很稳,至少比 Flutter Desktop 的第一个稳定版要差很多。几乎没什么人用。
它的自绘引擎用的是 Google 的 skia,这个自绘引擎稳的很,Chrome 和 Flutter 都是用的它,
所以绘制、渲染之类的工作不太会出问题。比 Java 生态圈里的 Swing 和 JavaFx 要好很多。
JetBrains 的东西当然对 Kotlin 开发者友好啦,Java 生态下的很多东西你都能用,访问系统级 API 也没啥大问题,同样也得考虑封一个 JRE 给用户。
flutter-desktop
这是谷歌的跨平台开发框架,开源、免费、文档齐全、投入力度大且持久,
桌面端同样也新的很,Windows 版本刚刚发稳定版,Mac 版本还没稳定。
如果你完全没搞过移动端的 flutter,想用这个框架开发桌面应用,那么意味着你要学的东西还挺多的。好在 dart 和 flutter 入门都不是很难,学习曲线比较平缓。
由于 flutter 在移动端积累了很多年,所以界面上的一些东西在 desktop 端都比较稳(skia 自绘引擎),
与操作系统相关的东西还不成熟,生态也不太好,
比如你想订制一下窗口的标题栏,想访问一下注册表这类工作可能得自己想办法。
不过它有类似 FFI 的支持,跟 C/C++ 语言打交道很方便。
开发者直接使用 Dart 语言描述界面,这会导致众多大括号嵌套在一起的问题,可能很多开发者不习惯。
使用 flutter-desktop 开发的应用程序打包后体积还比较大
webview2
这是微软 Edge 浏览器团队推出的跨平台 GUI 引擎,是闭源的,
目前只支持 Windows,对 C# 和 C++ 开发者友好,
如果使用 C# 开发,就得考虑把.NET 运行时分发给用户,
如果使用 C++ 开发,就得自己处理系统级 API 的操作,webview2 本身是不对系统级 API 做封装的。
这个框架推出有一小段时间了,但很多 API 也还不稳定,
更值得担忧的是这个团队,他们前不久刚刚放弃了自己的浏览器核心转而使用 Chromium 浏览器核心,不知道他们会不会放弃 webview2 这个框架。
它的优势是可以复用系统当中已存在的 webview2 二进制资源,
也就是说它虽然封了一个 Chromium 浏览器核心,但如果你可以确定客户电脑已经存在了基于 webview2 开发的应用,你的安装包体积可以足够小。
它也是多进程架构,甚至比 Electron 还要多一个进程(为了复用二进制资源),资源占用比较多。
更详细的介绍可以看我这篇文章:https://zhuanlan.zhihu.com/p/428560381
webview
这个库使用操作系统的浏览器引擎来达到减小安装包体积的问题,
Mac 上使用 Cocoa/WebKit,Linux 上使用 gtk-webkit2,Windows 10 上使用 Edge(也就是上一个小节里提到的 webview2),
它是不支持 Win7 的。由于不同的 OS 会用不同的浏览器核心,所以开发者使用它开发跨平台应用时要考虑前端代码浏览器兼容的问题。
开源且免费(MIT)有 go、Rust、Python 等语言的绑定,不过官方支持的是 go 语言,C 和 C++,
操作浏览器的 API 非常少,不支持自定义 scheme,更别提系统级 API 了。
TAURI
采用的技术方案与 webview 类似,所以安装包也足够小,非常新,还没发布稳定版,开源免费。webview 框架碰到的问题 TAURI 都有,
使用 Rust 开发,将来会支持 Deno,作者说将来会直接使用 webview 的技术来支持多平台,
NW.js
NW.js 最早把 Chromium 和 Node 绑定到一起,用前端知识做界面,用 Node 技术访问操作系统,
最早叫 node-webkit,在 2012 年创建。
NW.js 基于 MIT 开源,可以无忧使用。
微信小程序开发工具好像是用 NW.js 开发的(几年前调研过)。作者是英特尔的员工,英特尔的一些工具也是用 NW.js 开发的。
除了 Chromium 和 Node 的能力外,NW.js 自己也封装了一些系统级 API,类似托盘图标、剪切板、系统菜单这种,但数量明显比 Electron 要少。
NW.js 可以在多个窗口间共享同一个 Node.js 上下文,而且还可以通过配置让 Node 的上下文和 Dom 上下文混合,这给开发者带来了很多便利。心智负担减少很多。
不像 Electron 要时刻想着进程间通信,哪些模块当前进程不能用这类问题。
NW.js 虽然起步早,但奈何没有杀手级应用,周边的生态和工具链没发展起来。
用的人越来越少,维护的投入也不如 Electron 大,再加上 Chromium 更新非常频繁,导致 NW.js 的有些 API 也不是很稳,恶性循环加剧。
Electron
Electron 的作者曾经在 NW.js 团队工作过(NW.js 项目贡献第二多的人就是 Electron 的作者),
后来辗转到了 github 公司,于 2013 年在创建了 Electron,
也是个开源免费的产品。由于 VSCode、slak 等国际型产品都选择了 Electron,所以从者甚众,
生态和周边工具链也完善的多。虽然开发方式上有点蹩脚的地方(多进程架构及模块归属进程),但瑕不掩瑜。
Electron 每创建一个窗口都会多一个进程,这使 Electron 创建窗口的效率不高(秒级),
NW.js 有复用进程的机制,即使新窗口加载完全不同域的页面也不会创建新的进程(毫秒级)。
这也是为什么很多基于 Electron 开发的应用都使用 Dom 模拟弹窗的原因。
无论是浏览器相关的 API,还是系统级 API,Electron 提供的都比 NW.js 多。
我写过两本关于 Electron 的书《Electron 实战》和《深入浅出 Electron》
ImGui
https://github.com/ocornut/imgui
这个 GUI 框架的实现原理和开发方式可谓独树一帜
它在一个无限循环里不断的重绘整个界面,
别的 GUI 框架都是哪里更新了重绘哪里,它是无论有没有更新,一股脑全部重绘,而且一直在重绘,
这样做对于一些不支持 GPU 的客户端来说 CPU 消耗会略高一些,不过总起来说还算好
它对游戏开发者很友好,很多游戏都集成它来做用户交互(游戏内的一些设置界面、聊天界面之类的)
它支持很多种绘制引擎比如 OpenGL,Directx,Vulkan 等
打包后体积很小,也就几百 K 的样子
也有一些小问题,比如:要用很蛋疼的方法在界面内使用同一种字体的不同的字号
你如果打算使用这个库,我建议你不要选 master 分支,而选 docking 分支(这个分支下有很多 amazing 的特性)
与这个框架类似的还有:https://github.com/Immediate-Mode-UI/Nuklear
总结
我们介绍了这么多框架,可以说各有各的特点,有的成熟稳定,有的运行高效,还有一些框架单凭业务表达能力取胜,开发者在做技术选型时往往会难以抉择。
这里我总结了三个判断桌面软件开发框架是否优秀的底层逻辑,这可以帮助我们开发者认清真相,做出最优选择。
第一,是否具备独立的界面描述语言( UI DSL )。
这非常重要,是一个框架表达业务的重要能力。
类似 WPF 的 XAML、qt ui 文件里的 XML、 HTML + CSS 都属于界面描述语言,这都属于一种通过特化的 XML 来描述界面的方式;
还有一种通过代码来描述界面的方式,flutter、qml 和 Compose Multiplatform 都以类似这样的界面描述语言来描述界面的。
如下伪代码是 Compose Multiplatform 的界面描述形式:
panel {
row { checkBox(...) row { textField(...) } } }
但无论如何,显而易见的是,没有任何一个界面描述语言能比得上 HTML + CSS 组合。
想想看:HTML 里各种五花八门的语义化标签和 Dom 操作技巧、CSS 里的布局方式、伪元素、动画描述等,就会明白这一点。
第二,是否拥有强大的事件处理机制。
作为一个 GUI 应用,与用户的交互、与设备的交互必不可少,
这就涉及到形形色色的事件,比如,与设备有关的鼠标事件、键盘事件、触屏事件、网络状态变更事件等,
与界面元素状态有关的界面加载完成事件、媒体播放结束事件、元素大小改变事件等。
另外,能接收这些事件还远远不够,还得处理事件冒泡、事件捕获、事件分发,等等。
我认为 JavaScirpt 与浏览器核心的结合来处理各种各样的事件也是表现出众。
而且经历了数十年的发展,这套组合的事件系统也相当成熟稳定。
第三,是否拥有强大的异步、并行处理机制。
开发者不能在处理用户业务逻辑的时候,让界面渲染工作阻塞,
这就需要一个强大的异步、并行处理机制,
如果让开发者自己去创建线程并完成这些工作,无疑是又麻烦又会增加开发者的心智负担。
JavaScirpt 虽然是单线程执行的语言,但浏览器核心是多线程的(还是多进程的),
所以 JavaScript 与浏览器核心结合后,开发者既不用为开发多线程应用而苦恼,又不用为没有多线程的支持而手足无措。
从以上三方面的技术需求来看,在桌面 GUI 应用里封装一个浏览器核心还是非常有价值的,
这样开发者就可以用 HTML + CSS 强大的能力来描述界面,
用 JavaScript 强大的事件处理机制和异步处理机制来完成用户交互。
web 相关的技术之所以胜出,并不是这些技术的设计者有多厉害,而是这 20 多年间,有大量的人涌入了这个领域,前赴后继的推动着它前进。
其他任何一个领域都没有这么热火朝天的景象。推荐大家看看我的另一个回答:
用 Web 相关的技术做 GUI 应用的优势是,让开发者可以把大部分精力投注在业务本身上,而不是处理与 GUI 相关的技术细节。
实际上所有的框架,都应该是这个目的,比如 ORM 框架,目的应该是让开发者把大部分精力投注在业务与数据之间的关系上,而不是管理关系型数据的技术细节。
当然这肯定是有损耗的,在性能、稳定性、资源消耗上,都会有所削减。
而且,因为有框架的存在,开发者很难深入到框架内部做一些特殊的事情。
比如,我们该如何修改 HTML 的排版渲染机制呢?
所以,有些框架注重性能,有些框架注重开发效率,开发者做选择题的时候也应该衡量这两个问题,你的应用对哪些方面要求多一些呢?
你如果要开发一个视频监控系统,没多少业务功能,但要 24 小时不间断的记录视频数据,随时调取某一段时间的视频数据,这种应用可能 Qt 是最好的选择。
你如果要开发一个类似飞书的团队协作应用,业务逻辑复杂的一塌糊涂,
而且要在短时间内满足更多用户的需求,占领更多的市场,
那么 Electron 可能是更好的选择(目前飞书已经不再用 Electron 了,他们自己编译了 Chromium 核心,自己封了一个类似 CEF 的框架)
目前微软、谷歌、JetBrains 等公司都非常重视桌面端开发框架,也在推各自的框架产品,说明桌面应用领域并没有没落,反而应该更加受到重视。
虽然移动端应用大行其道,但我认为,只有生活、社交、轻娱乐等方向上的应用在移动端有较好的发展。
文档协作、大型游戏、开发工具、专业管控软件等应用还是在 PC 端发展的更好一些,毕竟 PC 端有更多样的输入输出设备、更广阔的显示和交互的空间,更强的存储和计算能力。
希望桌面软件开发领域的从业者都能获得幸福。