在99.996%的情况下,我们弄 Qt 应用都会使用 QApplication 类和 QWidget 类,即直接用 Widgets 库中的组件/控件。为了方便开发人员自己造轮子,Qt 也提供了一套基础的 GUI 组件。这些组件位于 Gui 库中。

实际上,Widgets 也是在 Gui 库上实现的,算是官方默认为咱们实现的图形组件库。若是我们自己也想实现一套图形组件库,就得从 Gui 库入手。当然,此行为需要决心、恒心、耐心、信心、专心、勇气、朝气、力气、努力、神力、洪荒之力。毕竟是一项大工程,没有上述精神元素是很难做成的。因此,许多时候,我们压根儿不会去弄,就算要写个自定义的可视化组件,那一般也是从 QWidget 派生。

尽管不怎么用,但还得了解一下的。QGuiAppliction 类用于管理整个应用程序的消息循环,初始化完各类组件后,开启主消息循环,直到循环结束,程序归西。QApplication 类就是它的子类。

QWindow 类是各种窗口部件的基类。它不仅指窗体,也包括窗口上的控件什么的。控件会被视为子窗口(就是没有了标题栏和边框这些)。所以,从 QWindow 类派生既能实现窗体逻辑,也能自定义控件。不过,咱们一般不会直接从 QWindow 类派生,而是使用以下两个家伙:

1、QRasterWindow :这个很好使,就是我们最最最的绘图方式来画控件的可视化部分。Raster 是“光栅”的意思。

2、QOpenGLWindow :看名字也猜到,该类使用 OpenGL 来绘制控件的可视化部分。

下面老周弄一个简单的演示,实现一个 MyCustWindow 类,派生自 QRasterWindow。里面也没啥逻辑,就是先把窗口的背景刷成红色,显得异常地喜庆和活泼。然后当鼠标移到窗口上时,会在窗口上画一个饼……不,是一个圆,这个圆会跟着鼠标指针走——其实,圆并不会动,只不过在鼠标移动后不断地将窗口重新绘制,以制造出圆会动的效果。

总体的代码文件有四个,其实就两个,C/C++都有头文件,于是就四个了。

/* CMakeLists.txt */

project(MyApp LANGUAGES CXX)

find_package(Qt6 REQUIRED COMPONENTS Core Gui)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(MyApp WIN32
app.h
app.cpp
MyCustWindow.h
MyCustWindow.cpp
) target_link_libraries(MyApp PRIVATE
Qt6::Core
Qt6::Gui)

app.cpp 是写 main 的,自定义窗口是 MyCustWindow.h 和 MyCustWindow.cpp。老周有个习惯,写C++代码通常会新建个头文件,把要用到的所有头文件一次性弄上去,然后项目中其他地方包含这个头文件就行了。比如这里的 app.h。

#ifndef _APP_H_
#define _APP_H_ #include <QGuiApplication>
#include <QRasterWindow>
#include <QRect>
#include <QSize>
#include <QPainter>
#include <QColor>
#include <QEvent>
#include <QMouseEvent> #endif

Qt 这厮基本就是这样的,每个类一个头文件,所以用到哪些类,直接上名字。_APP_H_ 只是个宏,为了防止多次包含此头文件时被重复定义,没有别的用途。这里老周没有用 #pragma once,据说这个不太通用。当然也没说不能用,只要不报错啥也能上。

接下来是自定义窗口类的定义。

/* MyCustWindow.h */

#include "app.h"

class MyCustWindow : public QRasterWindow {
Q_OBJECT
public:
explicit MyCustWindow(QWindow* parent = nullptr);
~MyCustWindow();
protected:
void paintEvent(QPaintEvent *event) override;
bool event(QEvent *event) override; private:
QPoint _curr_pos;
};

用在Qt对象树上的类型(说白了是 QObject 的直接或间接子类)都要加个宏 Q_OBJECT,信号和 Cao 需要它。不知道是啥也没事,照搬就行了。

这里,_curr_pos 是私有的成员,主要用来记录当前鼠标指针的坐标,以便在 paintEvent 中画图用。这个示例需要重写两个成员:

1、paintEvent:绘制窗口内容。

2、event:处理事件,捕捉鼠标指针移动事件—— MouseMove。这里其实可以用 eventFilter,也能起来相同的效果,不过,直接重写 event 最划算。

下面看 event 方法的实现。

bool MyCustWindow::event(QEvent *event)
{
// 分析一下事件类型
if(event -> type() == QEvent::MouseMove)
{
QMouseEvent* msevent = dynamic_cast<QMouseEvent*>(event);
// 拿到当前鼠标指针的位置
_curr_pos = msevent->pos(); // 重新绘制窗口
update();
// 已处理,返回true
return true;
} // 剩下的留给基类默认处理
return QRasterWindow::event(event);
}

如果是鼠标事件,那么,event 参数的指针实际指向的是 QMouseEvent 对象,所以我这里用 dynamic_cast 转换了一下,这个运算符虽然不太安全,但指针之间转换比较好用。这里其实不会有啥问题,因为如 event type 确定为 MouseMove,那么 event 参数就是 QMouseEvent* 类型。接着,访问 pos() 获得当前坐标,赋值给 _curr_pos。最后一步是调用 update 方法,这个一定要调用,这样才能强制窗口重新绘制,那个圆才会跟着鼠标走。

当然,最后一行也少不了,毕竟我们这里只关心鼠标事件,可能还有很多事件,于是这些我们不感兴趣的事件交给基类处理。

return QRasterWindow::event(event);

下一步,我们画窗口内容。

void MyCustWindow :: paintEvent(QPaintEvent *event)
{
QColor bkColor(255,0,0); // 背景色
QColor ptColor(255,255,0); // 跟随鼠标指针的颜色 // 开始表演
QPainter painter(this);
// 设置默认背景色
painter.setBackgroundMode(Qt::OpaqueMode);
painter.setBackground(QBrush(bkColor));
painter.setBrush(QBrush(ptColor));
// 擦除画布
painter.eraseRect(0, 0, width(), height());
// 画圆
painter.drawEllipse(_curr_pos, 20, 20); //event -> accept();
}

最后一行的 accept 方法调用,此处可以有也可以省略。accept 后事件就不再传播到父对象了,不是父类,是对象树上的父对象。窗口算是这里的顶层对象了,所以传不传上去无所谓。

这里有坑,有不少同志说,调用 setBackground 方法无法设置背景,全是黑的。这里要注意两点:

1、setBackgroundMode 方法将模式设置为 OpaqueMode 时才能上背景,如果是 TransparentMode,表明是透明背景,此时设置不了背景色的。程序默认就是 OpaqueMode,所以 setBackgroundMode 一行可以省略。

2、设置背景色后,在绘制前一定要清空画布——调用 eraseRect 方法,本示例中要擦除整个窗口的区域,所以,矩形的大小和窗口大小相同。

调用 drawEllipse 方法直接就可以画圆了,我们这里画的不是椭圆,而是正圆,只要让 x 和 y 轴方向上的半径相等(都是 20)就可以了。

回到 app.cpp,写 main 入口函数。

#include "app.h"
#include "MyCustWindow.h" int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv); // 实例化窗口
MyCustWindow window;
// 标题
window.setTitle("喜狼狼和灰太羊");
// 大小
window.resize(300, 270);
// 位置
window.setPosition(670,364);
// 显示窗口
window.show(); return app.exec();
}

app.exec 开启主消息循环,要在所有初始化工作完成再调用,因为它不会马上返回,而是等消息循环结束才返回。

末了,完工,咱们看看效果。

通过这个演示,大伙伴们对 QWindow 的用处,想必有所了解了。若某天你发现你要干自造轮子的大活,那就得这样弄了。

用VS Code搞Qt 6:Gui基础类型——QGuiApplication和QWindow的更多相关文章

  1. Qt入门之基础篇 ( 一 ) :Qt4及Qt5的下载与安装

    转载请注明出处:CN_Simo. 导语: Qt是一个跨平台的C++图形界面应用程序框架.它提供给开发者建立图形用户界面所需的功能,广泛用于开发GUI程序,也可用于开发非GUI程序.Qt很容易扩展,并且 ...

  2. Unity3D入门之GUI基础以及常用GUI控件使用(2)

    1.GUI基础 (1)GUI部分是每帧擦除重绘的,只应该在OnGUI中绘制GUI,按钮:GUILayout.Button(“Hello”); 只读标签:GUILayout.Label() (2)修改控 ...

  3. Qt入门之基础篇 ( 二 ) :Qt项目建立、编译、运行和发布过程解析

    转载请注明出处:CN_Simo. 题解: 本篇内容主讲Qt应用从创建到发布的整个过程,旨在帮助读者能够快速走进Qt的世界. 本来计划是讲解Qt源码静态编译,如此的话读者可能并不能清楚地知道为何要静态编 ...

  4. Qt入门之基础篇(三):掌握Qt4的静态编译基本方法

    转载载请注明出处:CN_Simo. 导语: 前两章都提到过“静态编译”(Static Compilation),在Windows下一次静态编译差不多需要长达三个小时才能完成,而且还非常容易由于各种原因 ...

  5. 第4天 | 12天搞定Python,基础语法(下)

    为了方便你的学习,减轻负重,我特意将基础语法分成上下两部分.希望你喜欢这种方式,如果不喜欢,你可以跟我说,反正我是不会改的,哈哈~~. 如果上部分,你还没看的话,先去看<第4天 | 12天搞定P ...

  6. Java基础类型与其二进制表示

    Java中的基础类型有:byte.short.int.long.float.double.char和boolean. 它们可被分为四种类型,整型.浮点型.char型和boolean型. 整型:byte ...

  7. go - 内置基础类型

    Go 语言中包括以下内置基础类型: 布尔型:bool 整型:int int64 int32 int16 int8 uint8(byte) uint16 uint32 uint64 uint 浮点型:f ...

  8. Typescript学习笔记(一)基础类型

    为了面向ng2和前端未来,开始搞向ts,ts是微软出的一枚语言,作为es6的超集,他出的一些特性还是蛮好用的(略坑).对于我等纯前端(从开始就接触javascript)的人来说,真想说,这特么什么鬼. ...

  9. Go的基础类型

    1. 命名 Go语言中的函数名.变量名.常量名.类型名.语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母.数字或 ...

随机推荐

  1. Redis三种模式——主从复制,哨兵模式,集群

    一.Redis主从复制作用 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复:实际上是一种服务的冗余. 负 ...

  2. python使用技巧

    当存在一个列表你需要将列表中的元素转为对应字典时,如何操作? 例如将["a", 1]变成{"a": 1} data = ["a", 1] d ...

  3. composer常用命令(部分摘抄)

    1. 仅更新单个库 composer update foo/bar 2. 不编辑composer.json的情况下安装库 composer require "foo/bar:1.0.0&qu ...

  4. Apache DolphinScheduler新一代分布式工作流任务调度平台实战-中

    @ 目录 架构设计 总体架构 启动流程图 架构设计思想简述 负载均衡 缓存 实战使用 参数 参数优先级 内置参数 基础内置参数 衍生内置参数 本地参数和全局参数 工作流传参 数据源管理 支持数据源 创 ...

  5. 6.8 NOI 模拟

    \(T1\ edge\) 考虑\(O(q\times n\times \log n)\)的暴力 暴力二分,直接树上差分 #define Eternal_Battle ZXK #include<b ...

  6. Auto.js 特殊定位控件方法 不能在ui线程执行阻塞操作,请使用setTimeout代替

    本文所有教程及源码.软件仅为技术研究.不涉及计算机信息系统功能的删除.修改.增加.干扰,更不会影响计算机信息系统的正常运行.不得将代码用于非法用途,如侵立删! Auto.js 特殊定位控件方法 操作环 ...

  7. Python 爬取途虎养车 全系车型 轮胎 保养 数据

    Python 爬取途虎养车 全系车型 轮胎 保养 数据 2021.7.27 更新 增加标题.发布时间参数 demo文末自行下载,需要完整数据私聊我 2021.2.19 更新 增加大保养数据 2020. ...

  8. 使用 for 循环 打印 9X9乘法表

    C 语言自学之99乘法表 请使用for循环,倒序打印9*9乘法表 1 #include <stdio.h> 2 3 int main() 4 { 5 int i,j,result;//定义 ...

  9. ASP.NET Core 6框架揭秘实例演示[32]:错误页面的集中呈现方式

    由于ASP.NET是一个同时处理多个请求的Web应用框架,所以在处理某个请求过程中出现异常并不会导致整个应用的中止.出于安全方面的考量,为了避免敏感信息外泄,客户端在默认情况下并不会得到详细的出错信息 ...

  10. Apache DolphinScheduler&ShenYu(Incubating) 联合 Meetup,暖春 3 月与你相约!

    云霞出海曙,梅柳渡江春. 2022 年的早春在疫情中显得格外生机勃勃,虽然接下来寒流仍有可能造访国内部分地区,但开源的世界,早已热闹非凡! 2022 年 3 月 26 日(星期六), Apache D ...