[Qt6]QWindow类可以做什么

科技资讯 投稿 5500 0 评论

[Qt6]QWindow类可以做什么

创建应用程序界面的时候,我们一般会选用 QWidget 以及其子类的。不过,在 Gui 模块中,有一个 QWindow 类,干吗用的呢?写个程序试试看。

#include <QGuiApplication> #include <QWindow> int main(int argc, char** argv { // 一定要先创建应用程序对象 QGuiApplication app(argc, argv; // 创建窗口实例 QWindow win; // create方法其实可以不调用 win.create(; // 调整窗口的大小 win.resize(300, 250; // 设置标题栏文本 win.setTitle("番薯联盟"; // 显示窗口 win.show(; // exec进入事件(消息)循环 return QGuiApplication::exec(; }

这里说明一下,QWindow 类有个 create 方法,它的作用是创建平台相关的资源的,对应的是 destroy 方法,用来销毁这些平台相关的资源。这些平台相关的资源是为了实现跨平台的类型,如 QPlatformWindow、QPlatformSurfaceEvent 之类的。Windows 平台有单独的实现,Linux 平台也单独地实现。像 qwindowsguieventdispatcher、qunixeventdispatcher 这些也是。总之,QWindow 类可能会用到它们,于是,这些平台相关的资源,其生命周期始于 create 方法,终于 destroy 方法。

咱们为上述代码写一个 CMakeLists.txt。

cmake_minimum_required(VERSION 3.0.0 project(TestApp VERSION 0.1.0 find_package(Qt6 REQUIRED COMPONENTS Core Gui add_executable(TestApp main.cpp target_link_libraries(TestApp PRIVATE Qt6::Core Qt6::Gui

这里我们不到“铁三角”库,只用 core 和 gui 就够了,不需要 widgets。

哦,直接实例化 QWindow 类会呈现一个空白窗口,而且这个窗口很诡异,你拖动一下改变它的大小后,就会变成这样。

QWindow 类虽然定义了 paintEvent 方法,但是,它实现了个寂寞。

void QWindow::paintEvent(QPaintEvent *ev { ev->ignore(; }

从源代码中你会看到,默认的实现是直接把 paint 事件忽略了。

cmake_minimum_required(VERSION 3.0.0
project(TestApp VERSION 1.2.3

find_package(Qt6 REQUIRED COMPONENTS Core Gui
set(CMAKE_AUTOMOC ON
set(CMAKE_CXX_STANDARD 20
set(CMAKE_CXX_STANDARD_REQUIRED ON

add_executable(TestApp MyWindow.h MyWindow.cpp main.cpp

target_link_libraries(TestApp PRIVATE Qt6::Core Qt6::Gui

 

1 #include <QWindow> 2 #include <QPaintEvent> 3 #include <QBackingStore> 4 5 #ifndef __MYWINDOW_H__ 6 #define __MYWINDOW_H__ 7 class MyWindow : public QWindow 8 { 9 Q_OBJECT 10 public: 11 // 构造函数 12 explicit MyWindow(QWindow* parent = nullptr; 13 protected: 14 // 重写事件 15 void paintEvent(QPaintEvent *ev override; 16 private: 17 // 绘制窗口内容需要这个类 18 QBackingStore* m_backstore; 19 }; 20 #endif

要在窗口上涂鸦,需要用到 QBackingStore 类。这是由于 QPainter 类需要一个 QPaintDevice 指针才能完成绘图。QBackingStore类可以通过 paintDevice 方法返回一个 QPaintDevice 类的指针。

private:
    // 绘制窗口内容需要这个类
    QScopedPointer<QBackingStore> m_backstore;

当超出成员作用域时会自动删除指针。

#include "MyWindow.h"
#include <QPaintDevice>
#include <QPainter>
#include <QColor>
#include <QRect>
#include <QtDebug>

MyWindow::MyWindow(QWindow* parent
    : QWindow(parent, 
      m_backstore(new QBackingStore(this
{
    // 设置当前窗口的位置和大小
    setGeometry(799, 304, 425, 385;
    // 设置绘画设备画布大小
    m_backstore -> resize(QSize(400, 300;
    // 设置窗口标题
    setTitle("红红火火";
}

void MyWindow::paintEvent(QPaintEvent* ev
{
    // 要进行绘图的区域
    QRect rect = ev->rect(;
    // 开始
    m_backstore->beginPaint(rect;
    QPaintDevice* dev = m_backstore -> paintDevice(;
    // 创建painter实例
    QPainter painter;
    painter.begin(dev;
    // 填充矩形
    painter.fillRect(rect, QColor("red";
    painter.end(;
    // 结束
    m_backstore->endPaint(;
    // 把绘图输出到窗口上
    m_backstore->flush(rect;
}

QBackingStore 类的构造函数需要一个 QWindow 类或子类的指针,一般是当前窗口类。这里注意的是,QBackingStore 对象不能使用默认大小(程序会闪退),一定要调用 resize 方法设置画布的大小(或者说你能看到的视窗大小)。

    m_backstore->beginPaint(rect;
    QPaintDevice* dev = m_backstore -> paintDevice(;
    // 创建painter实例
    QPainter painter;
    painter.begin(dev;
    // 填充矩形
    painter.fillRect(rect, QColor("red";
    painter.end(;
    // 结束
    m_backstore->endPaint(;
    // 把绘图输出到窗口上
    m_backstore->flush(rect;

QBackingStore.paintDevice 方法所返回的 QPaintDevice 指针只在 beginPaint 和 endPaint 方法之间有效。QPaintDevice 是一个虚拟设备,用于构建二维坐标空间,然后才能在上面绘图。绘图用到 QPainter 类。这个类在实例化后,调用 begin 方法开始绘图,前面获取的 QPaintDevice 指针就在这里传递。绘制完后调用 end 方法结束。如果实例化 QPainter 类时向构造函数传递了 QPaintDevice 指针,那就不需要调用 begin 方法了。

QPainter painter(dev; //painter.begin(dev; // 填充矩形 painter.fillRect(rect, QColor("red"; painter.end(;

最后,main 函数中实例化 MyWindow,并显示它。

int main(int argc, char** argv { // 一定要先创建应用程序对象 QGuiApplication app(argc, argv; // 创建窗口实例 MyWindow win; // 显示窗口 win.show(; // exec进入事件(消息)循环 return QGuiApplication::exec(; }

运行程序,看到红红的一块,就说明通正确运行了。

为了完善一下,我们还要重写 resizeEvent 函数,在窗口的大小被调整后,手动修改 QBackingStore 的画布大小。

class MyWindow : public QWindow { Q_OBJECT …… protected: …… // 调整窗口大小后发生 void resizeEvent(QResizeEvent* ev override; …… };

void MyWindow::resizeEvent(QResizeEvent *ev
{
    this->m_backstore->resize(ev->size(;
}

这样处理之后,窗口的背景色就能正常绘制了,哪怕你调整了窗口大小。

直接从 QWindow 类继承还是不太方便的,内部还要使用 QBackingStore 类。于是,我们可以考虑用 QWindow 的派生类。比如 QRasterWindow。这个类是用于创建基于像素呈现的窗口——相对应的是 QOpenGLWindow。两者用法差不多,只是绘制方式不同罢了。

接下来咱们演示一下。先编好 CMakeLists.txt 文件。

cmake_minimum_required(VERSION 3.8 project(HelloApp VERSION 1.0.0 LANGUAGES CXX # Qt内裤包 find_package(Qt6 REQUIRED COMPONENTS Core Gui # 开启MOC等选项 set(CMAKE_CXX_STANDARD 20 set(CMAKE_CXX_STANDARD_REQUIRED YES set(CMAKE_AUTOMOC YES # 代码目录 file(GLOB SRCS src/*.cpp includes/*.h # 添加可执行代码 add_executable(HelloApp ${SRCS} # 链接Qt内裤 target_link_libraries(HelloApp PRIVATE Qt6::Core Qt6::Gui

这里老周学会了偷懒,用 file 指令找出 includes 目录下所有扩展名为 .h 的文件,以及 src 目录下所有扩展名为 .cpp 的文件。然后把结果存到 SRCS 变量中,在 add_executable 命令执行时直接把 SRCS 传给它。这样做的好处是不用每新建一个文件都要手动添加一次了。当 IDE 提示找不到头文件时,执行一次 CMake 配置就会触发 file 命令。项目的目录结构大致长这样:

CustWindow 类派生自 QRasterWindow 类,重写 paintEvent 方法,自行绘制窗口内容。

#include <QRasterWindow> #include <QPaintEvent> #ifndef __CUSTWINDOW_H__ #define __CUSTWINDOW_H__ class CustWindow : public QRasterWindow { Q_OBJECT protected: void paintEvent(QPaintEvent* event override; }; #endif

下面是实现代码。

#include "../includes/CustWindow.h" #include <QPainter> void CustWindow::paintEvent(QPaintEvent *event { QPainter painter; painter.begin(this; // 要绘制的区域 QRect rect = event->rect(; // 先刷刷墙壁 painter.fillRect(rect, QColor("blue"; // 刷累了画个大饼充饥 // 换支笔 QPen pen(QColor("yellow", 3.0f; painter.setPen(pen; rect.adjust(50, 50, -50, -50; painter.drawEllipse(rect; // 收工 painter.end(; }

在实例化 QPainter 时,可以把当前窗口指针 this 传递给 QPainter 的构造函数;或者先调用无参构造函数,然后调用 begin 方法传递 this。前面说过,QRasterWindow 类的父类中有 QPaintDevice,所以咱们的窗口类自然就能直接传给 QPainter 对象了。

QWindow、QPaintDevice => QPaintDeviceWindow => QRasterWindow => CustWindow

app.cpp 文件中写 main 函数。

#include "../includes/CustWindow.h" #include <QGuiApplication> int main(int argc, char* argv[] { QGuiApplication app(argc, argv; // 实例化窗口 CustWindow window; // 设置标题和大小 window.setTitle("Bug App"; window.resize(450, 450; // 显示窗口 window.show(; return app.exec(; }

运行一下,看看咱们画的大饼,又大又黄。

看到这里,相信大伙伴们都了解 QWindow 怎么玩了。于是,咱们回归标题,这个类到底干吗呢?与 QWidget 类比如何?

2、可是,它也不是没用的。QWidget 测重组件化,封装得好,开柜即用,方便组装。而 QWindow 更抽象,更高级,更灵活,用来装逼直接爆表。比如你有一个窗口只用来画一个图表,告诉用户,他最近抑郁症发作的频率和趋势,以及预测什么时候无可救药。这种情形就很适合使用 QWindow 来创建窗口。

QWindow 对象也可以嵌套使用的,这个老周会在下一篇水文中介绍。故,QWindow 类不仅能灵活的创建窗口,也能自制许多控件。

 

编程笔记 » [Qt6]QWindow类可以做什么

赞同 (28) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽