Qt5下,QWidget系列从QtGui中被剥离出去,成为单独的QtWidget模块。随着Qt Quick2的引入,QtDeclarative也逐渐和QWidget系列也脱离关系。
最终:在Qt5下的GUI编程,有两套不同的东西
-
QtWidget (使用一个被称为 BackingStore 的东西)
-
QtQuick (使用一个被称为 Scene Graph 的东西)
这两个是什么东西(我还真说不清)?但我们,
不妨先,从根本的QtGui中的QWindow这个东西开始看起,看看QWindow是如何配合QWidget进行工作的
QWindow
只有QWindow,不用我们熟悉的QWidget,该如何得到一个窗口呢?
例子一
跟着感觉走,我就这样写了,行不行?
#include <QtGui> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QWindow w; w.setGeometry(100, 100, 200, 300); w.show(); return app.exec(); }
恩,看起来没什么不妥,编译运行,就可以得到一个窗口。大小可拖动,可以关闭。
还可以继续跟着直觉走,尝试子类化一下 QWindow
class Window:public QWindow { public: Window(QWindow *parent=0) :QWindow(parent) { } protected: bool event(QEvent *evt) { if (evt->type()==QEvent::Expose || evt->type()==QEvent::Resize) { // What can we do here ?? } return QWindow::event(evt); } };
同样,这种风格是我们很熟悉的东西,
可是,到了这儿,我们如何绘制我们想要的东西呢?
注:同QWidget一样,QWindow下也有
- keyPressEvent()
- resizeEvent()
- showEvent()
- ...
这堆东西,但我们这儿直接用最根本的event()函数。
例子二
在QWidget中,我们有熟悉的QPainter类,它可以在QPaintDevice上进行绘制操作。
现在,我还是想用QPainter,于是,需要认识一个新朋友:QBackingStore
#include <QtGui> class Window:public QWindow { public: Window(QWindow *parent=0) :QWindow(parent), m_backingStore(this) { } protected: bool event(QEvent *evt) { if (evt->type()==QEvent::Expose || evt->type()==QEvent::Resize) { QRect rect(QPoint(), geometry().size()); m_backingStore.resize(rect.size()); m_backingStore.beginPaint(rect); QPainter p(m_backingStore.paintDevice()); p.setBrush(Qt::blue); p.drawEllipse(rect); m_backingStore.endPaint(); m_backingStore.flush(rect); } return QWindow::event(evt); } private: QBackingStore m_backingStore; };
- 创建了一个QBackingStore的实例
- 当收到重绘(Expose)或大小改变(Resize)的事件时,进行重绘
- 将 backingStore 调整和合适的尺寸。(我们此处和窗口一样大)
- 获取相应的 painteDevice()。(比如后台提供的一个QImage图片)
- 用熟悉的QPainter进行熟悉的绘图操作
- 绘图...
- 将绘制的东西送入显存。
QWidget
在Qt5下,我们仍然可以使用QWidget,和Qt4下没有区别。
例子三
QWidget的例子没必要列了(略)
class Widget:public QWidget { public: Widget(){} protected: void paintEvent(QPaintEvent*){...} };
只是,这个东西和前面的QWindow如何关联上呢?
派生类?
QWidgt是QWindow的派生类么?如果是那就简单了,可是
class QWidget : public QObject, public QPaintDevice { ... };
在QWidget之Alien与Native小记一文中,我们知道QWidget有alien和native之分,那么对这个结果也就不惊奇了。因为:
- 大部分QWidget默认都是alien的,而QWindow天生就是native的。
什么关系?
对于每一个native的QWidget的,其私有成员QWidgetPrivate中有一个:
d->extra->topextra->window = new QWidgetWindow(q);
其中,QWidgetWindow是QWindow的派生类。
这样一来,问题就解决了:
-
EventDispatcher将从窗口系统中获得的事件,通过Q派发到QWindow(此处即QWidgetWindow)的event()函数中。
- QWidgetWindow将这些事件稍加处理,最终大量事件将派发到对应的QWidget::event()函数中
- 注意:因为这个QWidgetWindow所依附的native QWidget中一般会有大量的alien的QWidget,事件最终派发到某个QWidget(不一定是它依附的native QWidget)。
太乱了?
看了例子,
- 当窗口大小改变时,程序将收到窗口系统提供的RESIZE事件通知
-
EventDispatcher负责将事件派发到QWindow,此处是它的派生类QWidgetWindow
- QWidgetWindow的event()收到QEvent::Resize事件,处理该事件
-
发现某些部分dirty,需要重绘,调用QWidgetPrivate::drawWidget()向QBackingStore对应的paintDevice()中进行绘制
- drawWidget()生成QPaintEvent事件
- 事件派发到QWidget::event(),进而paintEvent()函数被调用
-
- 所有东西都绘制到QBackingStore对应的paintDevice()中,然后调用系统api,将其送入显存。
-
在Windows下,GetDC()/!BitBlt()/ReleaseDC()
- 在Linux(xcb)下,xcb_create_gc()/xcb_image_put()/xcb_flush()/...
-
作者:dbzhang800