Lighthouse 是 QPA(Qt Platform Abstraction) 项目的名字,它使得将Qt移植到新的平台变得比较简单。尽管现在它已经完全融入到了Qt主干代码中,lighthouse作为独立项目已经不复存在了,但本文中,我们继续使用这个名字(虽然已不太恰当)。
Lighthouse 是什么东西?一直不太清楚...
第二次注意到它是 看到cuteqt博客中的转载的一篇关于 Qt Lighthouse & Wayland 的博客
事不过三,...
Qt 是一个夸平台的库(其座右铭是"Qt Everywhere"?),但是Qt底层不是夸平台的。
比如:Qt中Gui部件最核心的类是QWidget,该类除了qwidget.h 和 qwidget.cpp两个原文件外,还有
在源代码中,还有随处可见的
#if defined(Q_WS_X11) ... #elif defined(Q_WS_MAC) ... #elif defined(Q_WS_WIN) ... #endif
而这一切都使得将 Qt 移植到一个新的窗口系统变的不太容易。
Lighthouse 是Qt Platform Abstraction 项目的名字,它使得将Qt移植到新的平台变得比容易。
Lighthouse 是如何做的呢?在现阶段:Lighthouse是QtGui的一个Window System Agnostic移植,它和X11、MAC、WIN在代码上处于同等地位(这样才不会影响现有代码):
kernel/qwidget_qpa.cpp
qpa 即:Qt Platform Abstraction
#if defined(Q_WS_X11) ... #elif defined(Q_WS_QPA) ...
Lighthouse 是一个插件结构,要移植Qt到新的窗口系统,只要编写相应的插件就可以了。比如现在的插件(在$QTDIR/src/plugins/platform下):
有问题不是,我们使用Qt4.8在linux X11桌面系统下编写程序:
这是暂时的,等Qt5发布时,代码中应该就没有 Q_WS_X11 这些东西了吧。
在刚过去的5月份最后一天,Paul Olav Tvete在blog中宣布:Lighthouse has grown up now。这距离他关于Lighthouse的第一篇博客:Introducing New Port of Qt to Your Favourite Platform 刚刚满20个月。
这意味着:
为了避免有人不小心使用Lighthouse原有项目仓库的代码,开发人员在qglobal.h文件中加入了 #error 这一预处理指令。
既然QPA已经正式包含在Qt仓库代码中了,如何看到运行效果呢?
比如
git clone git://gitorious.org/qt/qtbase.git
运行 configure --help我们可以关注选项:
-qpa ................ This will enable the QPA build. QPA is a window system agnostic implementation of Qt.
恩,加上它就够了,我自己用的参数
./configure -developer-build -qpa -opensource -nomake examples -nomake demos -nomake tests
然后运行make,等它完成
默认情况下,它似乎只编译了一个 minimal 插件,我们可以切换到(注,我在linux环境下)
src/plugins/platforms/xlib
目录下,运行make来生成xlib的插件
编译完成,可以看个例子了,先前一直没搞清楚:为什么其他人都用 examples/widgets/wiggly 这个例子来演示,直到我使用了 examples/widgets/groupbox 这个例子。
和往常完全一样,qmake 然后 make,然后运行:
$ ./groupbox Failed to load platform plugin "". Available platforms are: Minimal Xlib XlibGL Aborted
效果出来了,运行失败!告诉我们有3个platform可用,恩,听它的
$ ./groupbox -platform Xlib
这样以来我们熟悉的界面就出来了
换一个试试,
$ ./groupbox -platform Minimal
呵呵,这次看看不到界面了,因为没有渲染到屏幕上,而是到图片中了,打开生成的output0000.png即可看到我们熟悉的界面。
由于Minimal不响应键鼠操作,而我们的例子groupbox只渲染一次,所以只有一个图片。如果选择wiggly就不同了,它是个动画,所以会生成一系列图片。
http://developer.qt.nokia.com/wiki/Getting_Started_With_Lighthouse
http://labs.qt.nokia.com/2009/10/02/introducing-new-port-of-qt-to-your-favourite-platform/
http://labs.qt.nokia.com/2010/04/06/remodelling-the-lighthouse/
http://labs.qt.nokia.com/2010/06/25/a-lighthouse-by-any-other-name/
http://labs.qt.nokia.com/2010/10/06/there-is-a-light-that-never-goes-out/
http://labs.qt.nokia.com/2010/10/29/lighthouse-is-integrated/
http://labs.qt.nokia.com/2011/03/11/new-proof-of-concept-uikit-based-lighthouse-platform/
http://labs.qt.nokia.com/2011/05/31/lighthouse-has-grown-up-now/
http://www.cuteqt.com/blog/?p=2125
不妨看看QPA前后,有何不同:
考虑一下,传统的Qt是如何实现图形界面的夸平台:
针对不同的窗口系统(WS)定义相应的宏: Q_WS_*
Q_WS_X11 Q_WS_MAC Q_WS_QWS Q_WS_WIN Q_WS_S60
#if defined(Q_WS_X11) ... #elif defined(Q_WS_MAC) ... #elif defined(Q_WS_WIN) ... #endif
qapplication_x11.cpp qapplication_win.cpp qapplication_s60.cpp qapplication_mac.mm qapplication_qws.cpp ... qwidget_win.cpp qwidget_qws.cpp qwidget_mac.cpp qwidget_x11.cpp ...
win32 { ... } symbian { ... } unix:x11 { ... }
这一切这意味这什么??
如果我们想在这个基础上支持一个新的窗口系统,比如wayland,需要
添加平台相关的宏,代码中针对该窗口再扩充 #if #elif #endif
总之,需要对Qt的代码进行大量的修改。这一切使得将Qt移植到新的窗口系统中,变得不是那么容易。
QPA 定义了一套接口,而后,将窗口系统相关的代码放到插件中:
这时,如果我们想支持一个新的窗口系统,怎么办?只需要编写一个新的插件,而Qt自身的代码则不需要任何改变。
(当然,编写插件本身还是很有难度的,哈...)
为了使插件能供工作,Qt中需要提供有相应的加载接口,在Qt源码中搜索*_qpa.h、*_qpa.cpp、 *_qpa_p.h 即可找到所有(fixme)和 qpa有关的代码:
可以看到代码集中在 gui/kernel 部分;除此外,和字体相关gui/text,和绘图相关gui/painting、gui/image、gui/egl,和opengl相关
这个应该算是 QPA 的核心了,
class Q_GUI_EXPORT QApplicationPrivate : public QCoreApplicationPrivate { ... static QPlatformIntegration *platform_integration; ...
QApplication::QApplication() QApplicationPrivate::construct() qt_init() init_platform() QPlatfromIntegrationFactory::create()
class Q_GUI_EXPORT QPlatformIntegration { public:
GraphicsSystem functions |
|
virtual QPixmapData *createPixmapData() |
|
virtual QPlatformWindow *createPlatformWindow() |
|
virtual QWindowSurface *createWindowSurface() |
|
Window System functions |
|
virtual QList<QPlatformScreen *> screens() |
|
virtual void moveToScreen() |
|
virtual bool isVirtualDesktop() |
|
virtual QPixmap grabWindow() |
|
Deeper window system integrations |
|
virtual QPlatformFontDatabase *fontDatabase() |
|
virtual QPlatformClipboard *clipboard() |
|
... |
这样一来:
当你在程序中 |
它会向QPlatformIntegration请求 |
使用QWidget时 |
给我一个窗口(QPlatformWindow)及绘图区域(QWindowSurface) |
使用QPixmap时 |
给我一个位图的后端(QPixmapData) |
使用QFont时 |
给我字体数据信息(QPlatformFontDatabase) |
使用QGLWidget时 |
给我一个窗口 |
... |
... |
QPlatformWindow |
窗口 |
QWindowSurface |
窗口绘图区域(drawing area of a window) |
相对而言,QPlatformWindow 出现的比较晚一点(见Say hello to QPlatformWindow一文)之所以。之所以分离开来,原因见Remodelling the Lighthouse。
class Q_GUI_EXPORT QPlatformScreen : public QObject { ... virtual QRect geometry() const = 0; virtual QRect availableGeometry() const {return geometry();} virtual int depth() const = 0; virtual QImage::Format format() const = 0; virtual QSize physicalSize() const;
为什么要有这个东西?
通常我们似乎都不怎么区分QImage和QPixmap,尽管在Manual中很大的篇幅在描述这二者的区别。
设计目的和用途(一个是IO和像素操作,一个是屏幕显示):
典型的用途:
与QImage不同,QPixmap 是平台相关的
于是它的后端需要由各个窗口系统来提供也就不足为奇了。
提供字体信息
详见Fonts in Lighthouse一文。
同前面几个一样,从名字上容易看出是做什么的。
http://labs.qt.nokia.com/2010/04/06/remodelling-the-lighthouse/
http://labs.qt.nokia.com/2010/04/21/say-hello-to-qplatformwindow/
http://labs.qt.nokia.com/2010/09/02/fonts-in-lighthouse