Raster Window Example

Application Entry Point


  int main(int argc, char **argv)
  {
      QGuiApplication app(argc, argv);

      RasterWindow window;
      window.show();

      return app.exec();
  }

The entry point for a QWindow based application is the QGuiApplication class. It manages the GUI application's control flow and main settings. We pass the command line arguments which can be used to pick up certain system wide options.

From there, we go on to create our window instance and then call the QWindow::show() function to tell the windowing system that this window should now be made visible on screen.

Once this is done, we enter the application's event loop so the application can run.

RasterWindow Declaration


  #include <QtGui>

  class RasterWindow : public QWindow
  {
      Q_OBJECT
  public:
      explicit RasterWindow(QWindow *parent = 0);

      virtual void render(QPainter *painter);

  public slots:
      void renderLater();
      void renderNow();

  protected:
      bool event(QEvent *event) Q_DECL_OVERRIDE;

      void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
      void exposeEvent(QExposeEvent *event) Q_DECL_OVERRIDE;

  private:
      QBackingStore *m_backingStore;
      bool m_update_pending;
  };

We first start by including the <QtGui> header. This means we can use all classes in the Qt GUI module. Classes can also be included individually if that is preferred.

The RasterWindow class subclasses QWindow directly and provides a constructor which allows the window to be a sub-window of another QWindow. Parent-less QWindows show up in the windowing system as top-level windows.

The class declares a QBackingStore which is what we use to manage the window's back buffer for QPainter based graphics.

The raster window is also reused in a few other examples and adds a few helper functions, like renderLater().

RasterWindow Implementation


  RasterWindow::RasterWindow(QWindow *parent)
      : QWindow(parent)
      , m_update_pending(false)
  {
      create();
      m_backingStore = new QBackingStore(this);

      setGeometry(100, 100, 300, 200);

  }

The constructor first of all calls QWindow::create(). This will create the window in the windowing system. Without calling create, the window will not get events and will not be visible in the windowing system. The call to create does not show the window. We then set the geometry to be something reasonable.

Then we create the backingstore and pass it the window instance it is supposed to manage.


  void RasterWindow::exposeEvent(QExposeEvent *)
  {
      if (isExposed()) {
          renderNow();
      }
  }

Shortly after calling QWindow::show() on a created window, the virtual function QWindow::exposeEvent() will be called to notify us that the window's exposure in the windowing system has changed. The event contains the exposed sub-region, but since we will anyway draw the entire window every time, we do not make use of that.

The function QWindow::isExposed() will tell us if the window is showing or not. We need this as the exposeEvent is called also when the window becomes obscured in the windowing system. If the window is showing, we call renderNow() to draw the window immediately. We want to draw right away so we can present the system with some visual content.


  void RasterWindow::resizeEvent(QResizeEvent *resizeEvent)
  {
      m_backingStore->resize(resizeEvent->size());
      if (isExposed())
          renderNow();
  }

The resize event is guaranteed to be called prior to the window being shown on screen and will also be called whenever the window is resized while on screen. We use this to resize the back buffer and call renderNow() if we are visible to immediately update the visual representation of the window on screen.


  void RasterWindow::renderNow()
  {
      if (!isExposed())
          return;

      QRect rect(0, 0, width(), height());
      m_backingStore->beginPaint(rect);

      QPaintDevice *device = m_backingStore->paintDevice();
      QPainter painter(device);

      painter.fillRect(0, 0, width(), height(), Qt::white);
      render(&painter);

      m_backingStore->endPaint();
      m_backingStore->flush(rect);
  }

The renderNow function sets up what is needed for a QWindow to render its content using QPainter. As obscured windows have will not be visible, we abort if the window is not exposed in the windowing system. This can for instance happen when another window fully obscures this window.

We start the drawing by calling QBackingStore::beginPaint() on the region we want to draw. Then we get the QPaintDevice of the back buffer and create a QPainter to render to that paint device.

To void leaving traces from the previous rendering and start with a clean buffer, we fill the entire buffer with the color white. Then we call the virtual render() function which does the actual drawing of this window.

After drawing is complete, we call endPaint() to signal that we are done rendering and present the contents in the back buffer using QBackingStore::flush().


  void RasterWindow::render(QPainter *painter)
  {
      painter->drawText(QRectF(0, 0, width(), height()), Qt::AlignCenter, QStringLiteral("QWindow"));
  }

The render function contains the drawing code for the window. In this minial example, we only draw the string "QWindow" in the center.

Rendering Asynchronously


  void RasterWindow::renderLater()
  {
      if (!m_update_pending) {
          m_update_pending = true;
          QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
      }
  }

We went through a few places where the window needed to repainted immediately. There are some cases where this is not desierable, but rather let the application return to the event loop and later. We acheive this by posting an even to ourself which will then be delivered when the application returns to the QGuiApplication event loop. To avoid posting new requests when one is already pending, we store this state in the m_update_pending variable.


  bool RasterWindow::event(QEvent *event)
  {
      if (event->type() == QEvent::UpdateRequest) {
          m_update_pending = false;
          renderNow();
          return true;
      }
      return QWindow::event(event);
  }

We reimplement the virtual QObject::event() function to handle the update event we posted to ourselves. When the event comes in we reset the pending update flag and call renderNow() to render the window right away.

Files: