在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. ooday05 JAVA_static final_抽象方法_抽象类

    final:最终的,不可改变的-----------单独应用几率低 修饰变量:变量不能被改变 //演示final修饰变量 class Aoo{ final int num = 5; void show ...

  2. 笔记本Usb接口案例

    笔记本电脑通常具备使用USB设备的功能.在生产的时候,笔记本都预留了可以插入USB设备的USB接口.但具体是什么USB设备,笔记本厂商并不关心,只要符合USB规格的设备都可以. 定义USB接口,具备最 ...

  3. [极客大挑战 2019]BabySQL-1|SQL注入

    1.打开题目之后,查看源代码信息,发现check.php文件,结果如下: 2.那就只能尝试登录,经测试当输入or.by.select.from.and.where等关键字时会被过滤且会被过滤为空(过滤 ...

  4. [BJDCTF2020]Cookie is so stable-1|SSTI注入

    1.打开之后在各个界面查看源代码,未发现很明显的有用信息,仅发现了提示,结果如下: 2.尝试输入数据,结果就是输入什么就回显什么,也未进行过滤,尝试进行sql注入失败,结果如下: 3.那就根据提示抓包 ...

  5. linux 编译式安装apache

    apache的安装需要两个组件,APR对于Tomcat最大的作用就是socket调度 组件下载解压完成 ,讲两个组件解压包移动到apache的类库文件夹内 注:如果系统自带了apr和apr-util可 ...

  6. Mybatis的使用(2)

    1 :模糊查询 #{}占位符和¥{}的区别 #{}占位符:传参大部分用#{}传参,它的底层是PreparedStatement对象,是安全的数据库访问,它能够防止sql注入 1.1:如果parmete ...

  7. Redis系列5:深入分析Cluster 集群模式

    Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) 1 背景 前面我们 ...

  8. JVM学习之 内存结构

    目录 一.引言 1.什么是JVM? 2.学习JVM有什么用 3.常见的JVM 4.学习路线 二.内存结构 1. 程序计数器 1.1 定义 1.2作用 2. 虚拟机栈 2.1定义 2.2栈内存溢出 2. ...

  9. Linux环境监控工具汇总

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. Linux 操作系统有诸多自带和第三方的监控工具,以下从不同维度来整理常用的一些监控工具. CPU top(经典的Linu ...

  10. 万答17,AWS RDS怎么搭建本地同步库

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 背景说明 AWS RDS 权限受限,使用 mysqldump 的时候无法添加 - ...