首页 Qt 学习之路 2 Qt 学习之路 2(2):Qt 简介

Qt 学习之路 2(2):Qt 简介

46 4.2K

Qt 是一个著名的 C++ 应用程序框架。你并不能说它只是一个 GUI 库,因为 Qt 十分庞大,并不仅仅是 GUI 组件。使用 Qt,在一定程度上你获得的是一个“一站式”的解决方案:不再需要研究 STL,不再需要 C++ 的<string>,不再需要到处去找解析 XML、连接数据库、访问网络的各种第三方库,因为 Qt 自己内置了这些技术。

Qt 是一个跨平台的框架。跨平台 GUI 通常有三种实现策略:

  1. API 映射:API 映射是说,界面库使用同一套 API,将其映射到不同的底层平台上面。大体相当于将不同平台的 API 提取公共部分。比如说,将 Windows 平台上的按钮控件和 Mac OS 上的按钮组件都取名为 Button。当你使用 Button 时,如果在 Windows 平台上,则编译成按钮控件;如果在 Mac OS 上,则编译成按钮组件。这么做的好处是,所有组件都是原始平台自有的,外观和原生平台一致;缺点是,编写库代码的时候需要大量工作用于适配不同平台,并且,只能提取相同部分的 API。比如 Mac OS 的文本框自带拼写检测,但是 Windows 上面没有,则不能提供该功能。这种策略的典型代表是 wxWidgets。这也是一个标准的 C++ 库,和 Qt 一样庞大。它的语法看上去和 MFC 类似,有大量的宏。据说,一个 MFC 程序员可以很容易的转换到 wxWidgets 上面来。
  2. API 模拟:前面提到,API 映射会“缺失”不同平台的特定功能,而 API 模拟则是解决这一问题。不同平台的有差异 API,将使用工具库自己的代码用于模拟出来。按照前面的例子,Mac OS 上的文本框有拼写检测,但是 Windows 的没有。那么,工具库自己提供一个拼写检测算法,让 Windows 的文本框也有相同的功能。API 模拟的典型代表是 wine —— 一个 Linux 上面的 Windows 模拟器。它将大部分 Win32 API 在 Linux 上面模拟了出来,让 Linux 可以通过 wine 运行 Windows 程序。由此可以看出,API 模拟最大优点是,应用程序无需重新编译,即可运行到特定平台上。另外一个例子是微软提供的 DirectX,这个开发库将屏蔽掉不同显卡硬件所提供的具体功能。使用这个库,你无需担心硬件之间的差异,如果有的显卡没有提供该种功能,SDK 会使用软件的方式加以实现。(关于举例,可以参考文末一段精彩的讨论。)
  3. GUI 模拟:任何平台都提供了图形绘制函数,例如画点、画线、画面等。有些工具库利用这些基本函数,在不同绘制出自己的组件,这就是 GUI 模拟。GUI 模拟的工作量无疑是很大的,因为需要使用最基本的绘图函数将所有组件画出来;并且这种绘制很难保证和原生组件一模一样。但是,这一代价带来的优势是,可以很方便的修改组件的外观——只要修改组件绘制函数即可。很多跨平台的 GUI 库都是使用的这种策略,例如 gtk+(这是一个 C 语言的图形界面库。使用 C 语言很优雅地实现了面向对象程序设计。不过,这也同样带来了一个问题——使用大量的类型转换的宏来模拟多态,并且它的函数名一般都比较长,使用下划线分割单词,看上去和 Linux 如出一辙。gtk+ 并不是模拟的原生界面,而有它自己的风格,所以有时候就会和操作系统的界面格格不入。),Swing 以及我们的 Qt。

Qt 和 wxWidgets 一样,也是一个标准的 C++ 库。但是它的语法类似于 Java 的 Swing,十分清晰,而且使用信号槽(signal/slot)机制,让程序看起来很明白——这也是很多人优先选择 Qt 的一个很重要的原因。不过,所谓“成也萧何,败也萧何”。这种机制虽然很清楚,但是它所带来的后果是你需要使用 Qt 的 moc 对程序进行预处理,才能够再使用标准的 make 或者 nmake 进行正常的编译,并且信号槽的调用要比普通的函数调用慢大约一个数量级(Qt 4 文档中说明该数据,但 Qt 5 尚未有官方说明)。Qt 的界面也不是原生风格的,尽管 Qt 使用 style 机制十分巧妙地模拟了原生界面。另外值得一提的是,Qt 不仅仅能够运行在桌面环境中,还可以运行在嵌入式平台以及手机平台。

Qt 第一版于 1991 年由 Trolltech (奇趣科技)发布。后来在 2008 年,Nokia 斥资 1.5 亿美元收购 TrollTech,将 Qt 应用于 Symbian 程序开发。2012 年 8 月 9 日,Nokia 将 Qt 以 400 万欧元的价格出售给 Digia。

伴随着 Qt,一直有两种授权协议:商业授权以及开源授权。在 Qt 的早期版本,商业授权包含一些开源授权不提供的组件,但是在近期版本则不存在这个问题。以往人们对 Qt 的开源授权多有诟病。早期版本的 Qt 使用与 GPL 不兼容的协议授权,这直接导致了 KDE 与 GNOME 的战争(由于 Linux 使用 GPL 协议发布,GPL 协议具有传染性,作为 Linux 桌面环境的 KDE 却是基于与 GPL 不兼容的 Qt 开发,这就不遵守 GPL 协议)。不过,现在 Qt 的开源版本使用的是 GPLv3 以及 LGPL 协议。这意味着,你可以将 Qt 作为一个库连接到一个闭源软件里面。可以说,Qt 协议的争议已经不存在了。

46 评论

YorArch 2012年8月21日 - 16:50

并不同意Qt采用GUI模拟的方法实现。大部分Widget还是使用API映射的方法实现的,当目标平台无法提供此功能时且此功能必要时,就用自己的代码来模拟。

例如,在Windows下面,创建一个Widget最终调用的是CreateWindowEx (\src\gui\kernel\qwidget_win.cpp),而不是用于画线的GDI函数。

Style机制同样利用了平台的API,而且,当平台无法提供此功能时,就不能提供。例如WindowXPStyle大量使用uxtheme.dll中的API函数,等等。

回复
DevBean 2012年8月21日 - 17:10

这个我觉得是一个理解的问题。因为 Qt 不是 DirectUI,所以底层肯定是要调用 CreateWindowEx 的,这是普通的 GUI 程序不可避免的。即便是最典型的 Swing(我觉得这个没有异议的是 GUI 模拟),创建 JFrame 也是调用的 CreateWindowEx 函数。至于 style,新版本的 Qt 显然也是要考虑到性能。就像你不能说现在的哪一个操作系统完全按照操作系统教程实现的,这里也只是举一个例子,因为现在业界公认的 Qt 是使用 GUI 模拟的策略。当然,这也是一个理论说法而已,具体还是看自己的理解。我是这样认为的。

回复
YorArch 2012年8月21日 - 18:11

"任何平台都提供了图形绘制函数,例如画点、画线、画面等。有些工具库利用这些基本函数,在不同绘制出自己的组件,这就是 GUI 模拟。" 如果按照这个概念严格界定的话,最典型的GUI模拟库是FLTK,它在Windows下面调用的是GDI而没有使用CreateWindowEx,其事件系统也是自己模拟的,而Qt与之明显不同。从这个角度说,Qt并不符合本文中定义的“GUI 模拟”的概念。

回复
DevBean 2012年8月21日 - 21:34

FLTK 没有仔细研究,大致看了下,类似于 DirectUI 的理念,这与 Qt 这类传统 GUI 库有所区别。我这里说的 GUI 模拟的概念也不是一个准确的定义,事实上,业界也没有这么一种说法,是在另外的文档上面看到的介绍。我认为,这里所说的“API 模拟”,更多的强调对缺失功能的模拟,比如 wine。“API 模拟”和“GUI 模拟”是跨平台策略,强调的是对跨平台实现上的方式,而不是具体实现细节上面是不是调用了某个特定的函数。我觉得这是一种偷换概念。

回复
YorArch 2012年8月22日 - 14:11

个人认为若欲说明“API模拟”这个概念,似乎以下两个例子更适合:1) DirectX,其对应平台为各种显卡硬件环境。当某种渲染功能硬件不能够直接实现的时候,就用软件来模拟。使用DXSDK的人不必care某个功能是否真正在目标平台上实现;2) Qt的某些模块。例如QFileDialog的实现。默认情况下,Windows平台使用Shell32.dll中的API、WinCE平台使用Ceshell.dll的API、MAC平台使用Cocoa的NSOpenPanel和NSSavePanel,其他平台使用Qt自带的对话框qfiledialog.ui作为模拟。这样,开发者只需要调用 QfileDialog 而不必关心目标平台的细节。wine的话更像一个虚拟机,Windows API 似乎不能理解为 *nix 平台所“缺失”的功能。

DevBean 2012年8月22日 - 14:23

我同意你的说法。不过我觉得所谓“缺失”,是填补一些不存在的功能,并不是真正意义上的“缺失”。

大伟 2015年2月27日 - 05:11

非常有用的学习资料,看了你的网站应该是用CMS做的,能不能给所有这些article加个list,排序下每一篇文章,因为瀑布流loading到第一篇很慢,UX不好

回复
豆子 2015年3月9日 - 14:11

导航栏中就是各个系列的目录 🙂

回复
nihao 2012年8月21日 - 17:48

你好,请问学习qt是否能取代学习mfc?

回复
DevBean 2012年8月21日 - 21:15

MFC 是 Windows 平台上面的界面库,如果你的程序是 Windows 独占,考虑 MFC 可能会更好一些,毕竟是自家产品。如果要求跨平台,MFC 显然不可以。

回复
Chinahugg 2012年8月22日 - 15:46

更新的挺快的,加油,DevBean,等着看呢 !技术好,文笔也没得说!

回复
DevBean 2012年8月22日 - 17:34

一定一定,呵呵

回复
qiang 2012年8月24日 - 14:28

这学期就看你的文章来学习Qt啦哈哈 希望不要断更呀

回复
DevBean 2012年8月24日 - 14:40

一定一定!

回复
大米 2012年9月1日 - 21:22

请教一个问题。。我在做一个按位操作的需求时。用了STL里的bitset。请问Qt里有否替代的类库。感谢

回复
DevBean 2012年9月2日 - 07:29

可以看看 QbitArray;如果是标记位的话,Qt 是使用的 QFlag。

回复
大米 2012年9月2日 - 19:23

谢谢bean兄指点。 😀

回复
anonymous 2012年9月24日 - 08:07

非常感谢博主提供如此好的系列教程,无以为报,只能帮忙挑两个错别字bug啦 🙂 :
1.“并且信号槽的调用要比普通的函数调用蛮大约一个数量级”,应该是“慢”吧;
2.“不在,现在 Qt 的开源版本使用的是 GPLv3 以及 LGPL 协议”,应该是“不过”吧。

回复
DevBean 2012年9月24日 - 09:09

感谢指出哦~已经修改过了~谢谢

回复
wdds 2012年11月12日 - 23:03

经常有人说qt做出来的软件,外观总是和原生平台十分相似,是一种优势.
事实上,对于外观这东西,我从来不觉得有需要和原生平台一致.
随便找一个window下的软件,毒霸,360,chrome,qq,播放器等等.
他们这些软件的界面或者说外观是不是都和原生平台一致?
很明显稍微有一点规模的软件都会使用自己的ui,自己的ui风格,
很少会使用原生界面.

不过有一件事叫习惯,例如对话框的按纽分布位置:
win 确认or取消
linux 取消or确认
qt在这方面就做得很好

回复
DevBean 2012年11月13日 - 13:00

外观一致给人一种专业的感觉。毒霸、360 这样的界面并不能说不一致,因为它们使用的是 DirectUI 技术,本身就是 Windows 支持的一种技术,而不是模拟其它平台的样式。微软自己的 Office 2007 之后的版本同样使用了这个技术。所谓不一致,可以参考 iTunes 和 Windows 版的 Safari,这些被业界认为是典型的不一致的界面——标题居中显示,配色等完全类似 Mac 的风格。因此,个人认为,这里所说“不一致”,是说像 Safari 这种不同平台的混用,而不是 QQ 这种形成了自己独特的风格。

回复
radon 2013年12月31日 - 18:03

博主这么说的情况下,如果就是想要类似毒霸、360、QQ软件管家之类工具的“非操作系统原生长相”的UI风格,就只能是采用 DirectUI 的思路,而 Qt 就无门了吗?
如果 Qt 可以支持,那么是不是 Qt 通过 QML 的描述,去调用 GDI (or DirectX、openGL 等库)来“从零绘制出UI来”的呢?可不可说这个也是属于 DirectUI 的范畴?

回复
小小 2013年1月25日 - 13:03

博主,
diagia打錯囉, 是 digia 吧.

回复
豆子 2013年1月25日 - 14:16

感谢指出,已经修改过了

回复
恒古炎 2013年9月5日 - 21:25

豆子哥的学习之路真的把我带进Qt的大门了~ 才看了几章学习之路1 突然找到了2~ 感谢~
顺便问问豆子哥:
我在用QObject::tr();的过程中,每次打上超过两个字的中文就会报错,说什么有换行符之类的。但是两个中文以下就没事…… 是在搞不懂为什么。 求解~~

回复
豆子 2013年9月6日 - 09:47

这个一般是编译器和编码的问题,注意把文件编码设置为 UTF-8 试试(Windows 默认是 GBK)

回复
恒古炎 2013年9月6日 - 11:17

感谢
又有一个问题不明白,做头文件中类的前向声明时,我看到有QT_HEAD_NAMESPACE
QT_END_NAMESPACE
这样的用法,这种前向声明和没有这种方式的前向声明有什么不同吗?

回复
豆子 2013年9月6日 - 15:57

QT_HEAD_NAMESPACE 没有见过,倒是有 QT_BEGIN_NAMESPACE。Qt 在编译时允许添加 namespace(如果安装预编译版本是没有命名空间的),当存在命名空间时,这两个宏会替换成用户自定义的命名空间,否则的话则为空。所以这是 Qt 兼容性的一种实现。

回复
恒古炎 2013年9月6日 - 17:46

确实是QT_BEGIN_NAMESPACE 记错了 OTL
多谢解疑~

yue 2013年10月5日 - 22:32

"但是它所带来的后果是你需要使用 Qt 的 qmake 对程序进行预处理"

是使用moc预处理,qmake只是个build工具,不是必须的。

回复
豆子 2013年10月8日 - 13:22

的确是这样的,感谢指出

回复
loadomain 2014年5月15日 - 23:34

继续关注!!支持楼主!

回复
豆子 2014年5月16日 - 08:58

多谢支持!

回复
orgiz 2014年5月18日 - 17:08

感谢豆子啊!!!在网上找过好多关于QT的学习材料,最后找到这里,感觉一下就是“山穷水尽疑无路,柳暗花明又一村”

回复
goagain 2014年7月4日 - 15:14

豆子大神写的东西真是好,只不过每一个文章下面没有上一篇下一篇的超链接总是感觉好别扭Orz

回复
nixiaoya 2014年7月25日 - 12:47

支持楼主,文笔很好,准备看楼主文章入门

回复
gaoqiang 2015年11月21日 - 10:39

请教楼主一个tabwidget的问题,就是tabposition设置为west的时候,有没有什么简单的办法,使得text文字从左到右显示,而不是从上至下,谢谢楼主。

回复
豆子 2015年11月22日 - 11:33

QTabWidget 没有提供任何 API 达到这一目的,这需要你自己实现。可以继承 QTabBar,重写其 paintEvent() 函数,或者尝试使用 QProxyStyle 类。参考 这一链接,可以有一些思路。

回复
ccppaa 2016年9月24日 - 14:52

大神,弹出新的dialog是,show()和setFocus()的区别是什么,之前练习的时候,是在弹出的界面基础之上,再弹出,使用show()的时候,弹出之后,按ESC键就直接返回到第一层界面了,使用setFocus()可以正常返回他的上一层也就是第二层界面,这是什么原因呢

回复
zx 2017年1月24日 - 15:16

支持

回复
letsdiff 2017年6月11日 - 23:02

这句话不对吧:
。。。。
由于 Linux 使用 GPL 协议发布,GPL 协议具有传染性,作为 Linux 桌面环境的 KDE 却是基于与 GPL 不兼容的 Qt 开发,这就不遵守 GPL 协议
。。。。
Linux的GPL作用范围仅限于内核空间,不涉及用户空间应用程序(因为GPL的传染性不跨越地址空间),所以别说桌面环境这种一个系统可有可无的东西,就连和内核关系密切的libc都可以不与GPL兼容,你用个和GPL不兼容甚至闭源的libc打造一款发行版(至少在法律上)都是完全可以的
另外如果我没记错的话,gnome与kde之争应该不是qt与GPL不兼容,而是当初qt根本就不开源(用RMS的话就是根本就不是自由软件,是专有的产品,是强加于用户的不义)所以kde用qt导致GNU的支持者的不满(当然反对GNU思想体系的黑客们也不会高兴,黑客们没人愿意使用封闭源码的软件),这导致了gnome的开发

回复
name 2017年7月31日 - 09:55

大神 QT升级到了5,8 编译之前的项目时提示Error while building/deploying project untitled6 (kit: Desktop Qt 5.8.0 MSVC2015_64bit)When executing step "qmake",请问该怎么解决

回复
豆子 2017年8月1日 - 19:37

这个信息太少,看不出来是什么错误。重新创建一下看看是不是还会这样

回复
无垠 2019年5月25日 - 15:12

为什么是倒着排序啊?对于初学者来说真的挺不方便的,看一次还要翻到最下面。

回复
豆子 2019年5月26日 - 16:53

什么倒着排序?如果是文章的话,在页面最上方的导航栏有目录页面

回复
xffish 2019年11月26日 - 13:33

意思是点了导航栏的“QT学习之路2”,出来的是本系列的文章,然而是倒序的!第1篇在最底下,非常麻烦。我的方法是用右边的搜索框搜索指定第几篇。

回复

回复 YorArch 取消回复

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2