现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

从QWindow到QWidget

2015-05-25 07:21 工业·编程 ⁄ 共 2826字 ⁄ 字号 暂无评论

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

给我留言

留言无头像?