原来的水文标题是“用 VS Code 搞 Qt6”,想想还是直接改为“Qt6”,反正这个用不用 VS Code 也能搞。虽然我知道大伙伴们都很讨厌 CMake,但毕竟这厮几乎成了 C++ 的玩家规范了。Qt 也算识大体,支持用 CMake 来构建程序。所以,只要你用的是能写 C++ 的工具,理论上都能搞 Qt。
创建应用程序界面的时候,我们一般会选用 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 方法。
不过,create 方法这里其实可以不调用的,因为 show 方法会调用;destroy 方法也不可以不调用,它在 QWindow 类的析构函数中被调用。
咱们为上述代码写一个 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 事件忽略了。
所以,我们只能从 QWindow 类派生,并重写 paintEvent 方法,绘制我们所需要的内容。
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 类的指针。
m_backstore 成员也可以用 QScopedPointer 封装,防止内存泄漏。
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 = e