原来的水文标题是“用 VS Code 搞 Qt6”,想想还是直接改为“Qt6”,反正这个用不用 VS Code 也能搞。虽然我知道大伙伴们都很讨厌 CMake,但毕竟这厮几乎成了 C++ 的玩家规范了。Qt 也算识大体,支持用 CMake 来构建程序。所以,只要你用的是能写 C++ 的工具,理论上都能搞 Qt。
(相关资料图)
创建应用程序界面的时候,我们一般会选用 QWidget 以及其子类的。不过,在 Gui 模块中,有一个 QWindow 类,干吗用的呢?写个程序试试看。
#include#include 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 #include2 #include 3 #include 4 5 #ifndef __MYWINDOW_H__ 6 #define __MYWINDOW_H__ 7 class MyWindow : public QWindow 8 { 9 Q_OBJECT10 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: // 绘制窗口内容需要这个类 QScopedPointerm_backstore;
当超出成员作用域时会自动删除指针。
下面是实现代码。
#include "MyWindow.h"#include#include #include #include #include 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 方法设置画布的大小(或者说你能看到的视窗大小)。
当窗口需要绘制时会引发 paint 事件,重写 paintEvent 方法自行绘制窗口内容。在上面代码中,只是简单的矩形填充(填充为红色)。
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();}
运行程序,看到红红的一块,就说明通正确运行了。
当然,这个窗口还是有问题的。由于 QBackingStore 对象的画布大小是硬编码的,当调整了窗口大小后,红色矩形只能看到一部分,没看到的那部分仍然是黑乎乎的。
为了完善一下,我们还要重写 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。两者用法差不多,只是绘制方式不同罢了。
QRasterWindow和QOpenGLWindow类都是QPaintDevice 和 QWindow 的子类,所以从 QRasterWindow 派生的自定义窗口不需要定义 QBackingStore成员了,窗口自身的实例就可以传递给 QPainter 对象。
接下来咱们演示一下。先编好 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 命令。项目的目录结构大致长这样:
同理,这里咱们只用到 Core 和 Gui 两个模块,不需要 Widgets。
CustWindow 类派生自 QRasterWindow 类,重写 paintEvent 方法,自行绘制窗口内容。
#include#include #ifndef __CUSTWINDOW_H__#define __CUSTWINDOW_H__class CustWindow : public QRasterWindow{ Q_OBJECTprotected: void paintEvent(QPaintEvent* event) override;};#endif
下面是实现代码。
#include "../includes/CustWindow.h"#includevoid 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
C++ 是可以多继承的,所以 QPaintDevice 能有两个基类。
app.cpp 文件中写 main 函数。
#include "../includes/CustWindow.h"#includeint 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 类比如何?
1、QWindow 比 QWidget 更复杂,更难用,更麻烦,是一盏很浪费油的灯;
2、可是,它也不是没用的。QWidget 测重组件化,封装得好,开柜即用,方便组装。而 QWindow 更抽象,更高级,更灵活,用来装逼直接爆表。比如你有一个窗口只用来画一个图表,告诉用户,他最近抑郁症发作的频率和趋势,以及预测什么时候无可救药。这种情形就很适合使用 QWindow 来创建窗口。
总的来说,QWindow 类能做的事情更多,但需要投入的开发成本更高,代码量更吓人。我们知道,其实窗口上的控件(比如按钮、标签、复选框等)本质上也是窗口对象——只是嵌套在顶层窗口中,成了子窗口罢了。
QWindow 对象也可以嵌套使用的,这个老周会在下一篇水文中介绍。故,QWindow 类不仅能灵活的创建窗口,也能自制许多控件。
-
【Qt6】QWindow类可以做什么原来的水文标题是“用VSCode搞Qt6”,想想还是直接改为“Qt6”,反正这个用不用VSCode也能搞。虽然我知道大
-
数据采集中的6点关键因素数据量就是采集数据的工作量。想要获取的数据越多,工作量也就越大。
-
公关界的泥石流!宝马MINI被冰淇淋击倒,为何说危机才刚刚开始?2023年上海车展,可能是今年汽车行业曝光率最高的一次行业盛会。但很多厂商可能很郁闷,因为从20号开始,外
-
科学家发现多能级量子比特操控新方案中国科学技术大学郭光灿院士团队的教授郭国平、李海欧和龚明等人与纽约州立大学布法罗分校教授胡学东以及本
-
今天到底能不能看到雪山?蓉城雪山指数预报小程序告诉你答案-全球看点封面新闻记者杨霁月4月23日,成德眉资同城化生态环境联防联控联治专项合作组组织召开了成德眉资同城化生态
-
23苏交通SCP010今日发布发行公告23苏交通SCP010发布发行公告
-
豆腐怎么做好吃简单家常菜做法_豆腐怎么做好吃你们好,最近小品发现有诸多的小伙伴们对于豆腐怎么做好吃简单家常菜做法,豆腐怎么做好吃这个问题都颇为感
-
赏电子画展,享游戏乐趣 飞利浦携EVNIA亮相武汉“这里潮好玩“嘉年华-每日讯息4月21日-22日,由ZOL中关村在线举办的“坐标武汉,这里潮好玩”嘉年华活动圆满落幕。本次ZOL潮好玩嘉年华在
-
第二届中国(武汉)文化旅游博览会举办沉浸式文旅产业发展论坛 环球简讯第二届中国(武汉)文化旅游博览会举办沉浸式文旅产业发展论坛2023年4月21日,在第二届中国(武汉)文化旅游
-
怎么幽默回复发错信息的人 别人发错信息怎么幽默回复那是,我可是神探狄仁杰给他发个微笑表情包表情。别回复了,错的是对方又不是你。如果关系好也可以开开玩笑
-
海南省儋州市发布冰雹橙色预警-天天快讯海南省儋州市发布冰雹橙色预警
-
环球快消息!建艺集团: 独立董事对担保等事项的独立意见建艺集团:独立董事对担保等事项的独立意见
-
微资讯!五芯电缆型号大全1、按照电缆类别,电压等级,载流量进行分类。2、例如:YJV-0 6 15*6KVV-5005*1 5
-
螺纹钢或承压调整 防范减仓洗盘期货:螺纹钢1510周四夜盘承压震荡,收2347(+5),总增仓2 5万余手,成交量降低。前十名资金流向:昨日多
-
前沿消息报道:新冠二阳要如何应对 中疾控公共本土新冠最新情况重组毒株大角星已增加到42例一、新冠二阳要如何应对近期,新一波新冠感染潮是否即将来临引起社会关注。不少网友在社交媒体分享自己“二
-
全球头条:海南省琼中县发布雷电黄色预警海南省琼中县发布雷电黄色预警
-
急性阑尾炎是怎么引起的女的_急性阑尾炎是怎么引起的|焦点速讯1、的急性阑尾炎通常是由阑尾腔阻塞引起的,其具体致病原因如下:1 阑尾属于大肠盲端,阑尾腔小。2、排泄物
-
观影需要购买3D眼镜合理吗?昆山市场监管部门提醒需先提供免费类型选择|世界报资讯<p> 本报讯(记者 朱新国 占长孙 通讯员 秦颖 洪姗姗)近日,昆山市市场监管
-
天天快消息!轰73+30+14!湖人三巨头爆发创神迹:浓眉受伤拼杀,詹皇被袭裆轰73+30+14!湖人三巨头爆发创神迹:浓眉受伤拼杀,詹皇被袭裆,湖人,浓眉,詹皇,三巨头,勒布朗詹姆斯,孟菲斯
-
为期一年!江西开展校园食品安全治理提升专项行动中国质量新闻网讯为巩固提升校园食品安全管理水平,江西省市场监管局今年三月起对全省各级各类学校(含幼儿
-
一季度数据不及预期 家电市场复苏尚需时日受疫情抑制的消费市场在2023年一季度迎来了春天。一季度数据显示,GDP同比增长4 5%,超出市场预期,其中消
-
94分钟献绝平?96分钟轰绝杀!东北德比演绎神剧本 天天快资讯体坛加,体坛+,安晓君,亚泰,大连人,中超,94分钟献绝平?96分钟轰绝杀!东北德比演绎神剧本,,足球,中国足球,,
-
践行绿色发展理念 红布林6年减少超5000万千克碳排放-全球信息而实现“双碳”目标是一场广泛而深刻的经济社会系统性变革,需要集聚社会各方力量共同推动。国内首家提出循
-
团灵璧县委开展“爱心传递 悦读相伴”公益捐书活动 全球焦点为深入学习宣传贯彻党的二十大精神,在第28个世界读书日来临之际,团灵璧县委积极动员、汇聚爱心,倡议团员
-
商务部答红星新闻:跨境电商是正在成长的外贸增长点,加快出台相关知识产权保护指南 全球快播↑红星新闻记者提问近日,国务院召开常务会议,研究推动外贸稳规模优结构政策措施。4月23日,国务院新闻办
-
今亮点!【哥伦比亚总统与委内瑞拉反对派会面】哥伦比亚总统佩特罗当地时间22日在其官邸会见委内瑞拉反对派代表,并表示其主要目的是了解委反对派与政府和谈暂停的原因。哥伦比亚外长莱瓦也出席了会议。莱瓦此前表示,哥伦比亚将以“非干涉”的方式促成委国内问题的解决。委政府与反对派的和平谈判几度中断,双方在去年11月于墨西哥城签署了阶段性协议。(央视新闻)【哥伦比亚总统与委内瑞拉反对派会面】哥伦比亚总统佩特罗当地时间22日在其官邸会见委内瑞拉反对派代表,并
-
每日动态!第十五届天津蓟州梨园情旅游文化节开幕中国青年报客户端讯(中青报·中青网记者胡春艳实习生骆嘉仪常馨心)4月的天津蓟州是花的海洋,杏花、海棠
-
天津市中小学劳动技能大赛开赛_天天热资讯2023年4月22日,以 "与生活劳动相伴与生产劳动同学 "为主题的天津市中小学劳动技能大赛在天津市津南区举行
-
世界热议:2023上海车展:努力闪光的“宝石” 静态体验北京现代MUFASA出品:搜狐汽车前两天北京现代刚发布了旗下的全新紧凑级SUV——MUFASA的官图,这上海车展真车就已经迫不及
-
中国罐头“真香”:海外多国热销,出口量保持增长 全球快看点中国罐头“真香”□近期,部分海外国家和地区通胀走高,导致当地食品价格大涨,消费者囤货需求上升,推动我