栅格窗口示例

本示例展示了如何使用 QWindowQPainter 渲染来创建一个最小化的基于窗口的应用程序。

应用程序入口点

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

    RasterWindow window;
    window.show();

    return app.exec();
}

基于窗口的应用程序的入口点是 QGuiApplication 类。它管理 GUI 应用程序的流程和主设置。我们传递命令行参数,这些参数可以用于选择某些系统级别的选项。

从这里,我们继续创建窗口实例,然后调用 QWindow::show() 函数,告诉窗口系统这个窗口现在应该显示在屏幕上。

完成后,我们进入应用程序的事件循环,以便应用程序可以运行。

栅格窗口声明

#include <QtGui>
#include <QScopedPointer>

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

    virtual void render(QPainter *painter);

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

protected:
    bool event(QEvent *event) override;

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

private:
    QScopedPointer<QBackingStore> m_backingStore;
};

我们首先包含 <QtGui> 头文件。这意味着我们可以使用 Qt GUI 模块中的所有类。如果需要,也可以单独包含类。

RasterWindow 类直接从 QWindow 继承,并提供了一个构造函数,允许窗口作为另一个 QWindow 的子窗口。没有父窗口的 QWindow 在窗口系统中显示为顶级窗口。

类声明了一个 QBackingStore,这是我们用于管理窗口后缓冲区以便进行基于 QPainter 的图形。

栅格窗口在其他几个示例中也得到复用,并增加了一些辅助函数,例如 renderLater()。

栅格窗口实现

RasterWindow::RasterWindow(QWindow *parent)
    : QWindow(parent)
    , m_backingStore(new QBackingStore(this))
{
    setGeometry(100, 100, 300, 200);
}

在构造函数中,我们创建 backingstore 并将其传递给它应该管理的窗口实例。我们还设置了窗口的初始几何形状。

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

在创建的窗口上调用 QWindow::show() 之前,虚拟函数 QWindow::exposeEvent() 将被调用来通知我们窗口在窗口系统中的暴露状态已更改。事件包含暴露的子区域,但由于我们每次都绘制整个窗口,所以我们没有使用它。

函数 QWindow::isExposed() 将告诉我们窗口是否显示。我们需要这个信息,因为当窗口在窗口系统中被遮挡时,也会调用 exposeEvent。如果窗口正在显示,我们调用 renderNow() 立即绘制窗口。我们希望立即绘图,以便向系统提供一些视觉内容。

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

窗口大小调整事件确保在窗口显示在屏幕上之前将被调用,并且在窗口在屏幕上调整大小时也会被调用。我们使用它来调整后缓冲区的大小,并将渲染推迟到相应的后续 expose 事件。

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(), QGradient::NightFade);
    render(&painter);
    painter.end();

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

renderNow 函数设置 QWindow 使用 QPainter 渲染其内容的所需条件。由于被遮挡的窗口将不可见,如果窗口在窗口系统中未暴露,我们将终止。例如,当另一个窗口完全遮挡此窗口时,这种情况可能发生。

我们通过在要绘制的区域上调用 QBackingStore::beginPaint() 来开始绘制。然后我们获取后缓冲区的 QPaintDevice,并创建一个用于渲染到该绘图设备的 QPainter

为了防止留下之前渲染的痕迹并从干净的缓冲区开始,我们将整个缓冲区填充为白色。然后我们调用虚拟的 render() 函数,它负责绘制此窗口。

绘制完成后,我们调用 endPaint() 来表示我们已经完成渲染,并使用 QBackingStore::flush() 在后缓冲区中呈现内容。

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

render 函数包含窗口的绘制代码。在这个简单的示例中,我们只在中心绘制字符串 "QWindow"。

异步渲染

void RasterWindow::renderLater()
{
    requestUpdate();
}

我们讨论了几个需要立即重绘窗口的地方。有些情况下,我们不希望这样,而是要让应用程序返回事件循环并计划稍后重绘。我们通过请求更新,即使用 QWindow::requestUpdate() 来实现这一点,它将在系统准备好重绘时传递。

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

我们重新实现了虚拟的 QObject::event() 函数来处理更新事件。当事件来临时,我们调用 renderNow() 来立即渲染窗口。

示例项目 @ code.qt.io

© 2024 Qt 公司有限。本文件包含的文档贡献是各自所有者的版权。本文件提供的文档是根据自由软件基金会发布的 GNU 自由文档许可 1.3 版本 许可的。Qt 和相应的徽标是芬兰的 Qt 公司及其在全球的子公司和附属公司的商标。所有其他商标均为各自所有者的财产。