栅格窗口示例
本示例展示了如何使用 QWindow 和 QPainter 渲染来创建一个最小化的基于窗口的应用程序。
应用程序入口点
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() 来立即渲染窗口。
© 2024 Qt 公司有限。本文件包含的文档贡献是各自所有者的版权。本文件提供的文档是根据自由软件基金会发布的 GNU 自由文档许可 1.3 版本 许可的。Qt 和相应的徽标是芬兰的 Qt 公司及其在全球的子公司和附属公司的商标。所有其他商标均为各自所有者的财产。