【Qt6】嵌套 QWindow

在上个世纪的文章中,老周简单介绍了 QWindow 类的基本使用——包括从 QWindow 类派生和从 QRasterWindow 类派生。

其实,QWindow 类并不是只能充当主窗口用,它也可以嵌套到父级窗口中,变成子级对象。咱们一般称之为【控件】。F 话不多讲,下面咱们用实际案例来说明。

这个例子中老周定义了两个类:

MyControl:子窗口对象,充当控件角色。这里实现一个类似开关的控件。【关闭】状态下,控件的背景呈现为灰色,金色方块位于最左侧;当控件处于【开启】状态下,控件背景为红色,金色方块位于最右侧。
MyWindow:作为窗口使用,里面包含 MyControl 对象。
先看 MyControl 类。
class MyControl : public QRasterWindow
{
    Q_OBJECT

public:
    MyControl(QWindow *parent = nullptr);

private:
    // “开启”状态时的背景色
    QColor _on_bgcolor;
    // “关闭”状态时的背景色
    QColor _off_bgcolor;
    // 当前状态
    bool _state;

signals:
    // 信号
    void stateChanged(bool isOn);

public:
    // 获取状态
    inline bool state() const { return _state; }
    // 修改状态
    inline void setState(bool s)
    {
        _state = s;
        // 发出信号
        emit stateChanged(_state);
    }

protected:
    // 重写方法
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent* evebt) override;
};

在私有成员中,两个 QColor 类型的变量分别表示控件处于【开】或【关】状态时的背景色。_state 是一个布尔值,true就是【开】,false就是【关】,用来存储控件的当前状态。

公共方法 state 获取当前状态,setState 方法用来修改当前状态,同时会发出 stateChanged 信号。信号成员咱们先放一下,后文再叙。

两个虚函数的重写。paintEvent 负责画出控件的模样;mousePressEvent 当鼠标左键按下时改变控件的状态。实现点击一下开启,再点击一下关闭的功能。

接下来是实现各个成员。先是构造函数。

MyControl::MyControl(QWindow* parent)
    :QRasterWindow::QRasterWindow(parent),
     _state(false),
     _on_bgcolor(QColor("red")),
     _off_bgcolor(QColor("gray"))
{

}

构造函数主要用来初始化几个私有成员。下面代码实现鼠标左键按下后更改状态。

void MyControl::mousePressEvent(QMouseEvent *event)
{
    if(! (event->buttons() & Qt::MouseButton::LeftButton))
        return;  // 如果按的不是左键就 PASS
    
    this->setState(!this->state());
    update();
}

每次点击后控件的状态都会取反(开变关,关变开),为了反映改变必须重新绘制控件,所以要调用 update 方法。

paintEvent 中实现绘制的过程。

void MyControl::paintEvent(QPaintEvent* event)
{
    // 如果当前为不可见状态,就不绘图了
    if(!isExposed())
        return;
    // 要绘制的区域
    QRect rect = event -> rect();

    QPainter painter;
    painter.begin(this);
    // 根据状态填充背景
    QBrush bgbrush;
    bgbrush.setStyle(Qt::SolidPattern);
    if(_state)
    {
        bgbrush.setColor(_on_bgcolor);
    }
    else
    {
        bgbrush.setColor(_off_bgcolor);
    }
    painter.fillRect(rect, bgbrush);

    QRect rectSq;
    // 如果是“开”的状态,绿色矩形在右侧
    if(_state)
    {
        rectSq.setX(rect.width() / 3 * 2);
        rectSq.setWidth(rect.width() / 3);
    }
    // 如果为“关”的状态,绿色矩形在左侧
    else{
        rectSq.setWidth(rect.width() / 3);
    }
    rectSq.setHeight(rect.height());
    painter.fillRect(rectSq, QColor("gold"));
    painter.end();
}

绘制分两步走。第一步是填充背景矩形,如果【开】就填充红色,如果【关】就填充灰色。此处用到了 QBrush 对象。根据不同颜色调用 setColor 方法来设置。这里要注意 QBrush 对象要调用 setStyle 方法设置画刷样式为 SolidPattern。这是因为 QBrush 类默认的 style 是 NoBrush,因此要手动设置一下,不然看不到绘制。

第二步是绘制小方块。当控件状态为【开】时方块在右边,状态为【关】时方块在左边。小方块的宽度是控件宽度的 1/3,高度与控件相同。要显式调用 setHeight 方法设置矩形高度(因为它默认为0)。记得小方块的左上角的 X 坐标也要调整的。

 

下面是窗口 MyWindow 的成员。

class MyWindow : public QRasterWindow
{
    Q_OBJECT

public:
    MyWindow(QWindow *parent = nullptr);

private:
    MyControl *_control;
    void initUI();

protected:
    void paintEvent(QPaintEvent *event) override;
};

重写的 paintEvent 方法负责画窗口的背景。私有成员 initUI 用于初始化子窗口(MyControl对象)。initUI 方法在构造函数中调用。

MyWindow::MyWindow(QWindow *parent)
    :QRasterWindow::QRasterWindow(parent)
{
    initUI();
}

void MyWindow::initUI()
{
    // 初始化窗口
    setTitle("示例程序");
    resize(550, 450);
    setMinimumSize(QSize(340, 200));
    _control = new MyControl(this);
    // 初始化子窗口
    _control->setPosition(35, 40);
    _control->resize(120, 35);
    _control->setVisible(true);
}

setVisible 方法使用控件变为可见,只有可见的对象才能被看到。

paintEvent 方法只负责画窗口背景。

void MyWindow::paintEvent(QPaintEvent *event)
{
    // 只填充窗口
    QPainter painter(this);
    painter.fillRect(event->rect(), QColor("blue"));
    painter.end();
}

 

最后写 main 函数。

int main(int argc, char** argv)
{
    QGuiApplication app(argc, argv);
    MyWindow wind;
    wind.show();
    return QGuiApplication::exec();
}

 

运行后,点一下子窗口,它就会改变颜色。

【Qt6】嵌套 QWindow

 

 关于控件周围有白边(或黑边)的问题:

这个问题比较不确定,不同平台好像表现不一样。Windows 下在控件周围会多出白色区域(也可能是黑色);在 Debian 下没有出现白边。所以,为了尽可能地避免跨平台差异导致的问题,可以用 mask 让控件区域之外的地方变为透明,这样白边黑边就会消失。

调用 setMask 方法要在重写的 resizeEvent 方法中进行,这是为了响应控件大小改变后,及时调整要透明的区域。

void MyControl::resizeEvent(QResizeEvent *event)
{
    setMask(QRect(QPoint(), event->size()));
}

 

刚才,咱们 MyControl 类定义了 stateChanged 信号,并在修改控件状态后发出。接下来我们用一个 lambda 表达式来充当 slot,当控件状态改变后输出调试信息(使用 qDebug 宏)。

connect(_control, &MyControl::stateChanged, this, [](bool st){
    qDebug() << "当前状态:" << st;
});

再次运行程序,每点击一下控件,控制台就会输出一条调试信息。

【Qt6】嵌套 QWindow

 

 setMask 还有一个经典用途——制作透明窗口。下面咱们顺便讨论一下如何做不规则形状的窗口。

a、调用 setFlags 方法设置 WindowType::FramelessWindowHint 标志,去掉窗口的边框;

b、调用 setMask 方法时需要传递一个 QRegion 对象,它表示一个形状区域。这个区域可以是矩形,也可以是圆形,当然也可以是多边形。设置 mask 后,指定区域外的内容变成透明,并且不会响应用户的输入事件(鼠标、键盘等)。QRegion 对象可以进行 and、or 等运算,多个区域可以进行交集或并集处理,形成各种不规则图形。

 

咱位做个例子。

#ifndef APP_H
#define APP_H
#include <QColor>
#include <QSize>
#include <QWindow>
#include <QRasterWindow>
#include <QPainter>
#include <QRect>
#include <QPaintEvent>
#include <QRegion>
#include <QList>
#include <QPolygon>
#include <QPoint>
#include <QResizeEvent>

class CustWindow : public QRasterWindow
{
    Q_OBJECT

public:
    CustWindow(QWindow *parent = nullptr);

protected:
    void paintEvent(QPaintEvent* event) override;
    void resizeEvent(QResizeEvent* event) override;

    // 窗口没了边框无法用常规操作,于是实现双击关闭窗口
    void mouseDoubleClickEvent(QMouseEvent *event) override;
};
#endif
CustWindow::CustWindow(QWindow* parent)
    :QRasterWindow::QRasterWindow(parent)
{
    setFlags(Qt::WindowType::FramelessWindowHint|Qt::WindowType::BypassWindowManagerHint);
    // 窗口位置和大小
    setGeometry(600, 500, 400, 400);
}

void CustWindow::paintEvent(QPaintEvent* event)
{
    QPainter p(this);
    p.fillRect(event->rect(), QColor("deeppink"));
    p.end();
}

void CustWindow::resizeEvent(QResizeEvent *event)
{
    QRegion rg0(QRect(QPoint(), event->size()));

    QList<QPoint> pt1 = {
        {15,16},
        {180, 300},
        {320, 300},
        {150, 57}
    };
    QPolygon pl1(pt1);
    QRegion rg1(pl1);

    QList<QPoint> pt2 = {
        {315, 20},
        {25, 160},
        {160, 320},
        {240, 150},
        {330, 30}
    };
    QPolygon pl2(pt2);
    QRegion rg2(pl2);

    QRegion rg3 = rg0 & (rg1 | rg2);
    setMask(rg3);
}

void CustWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        this->close();   // 关闭窗口
    }
}

调用setFlags方法时,用到了 WindowType::BypassWindowManagerHint 值。Windows 下没有影响,主要是针对 X11,去除第三方主题产生的窗口阴影。

在 resize 事件处理代码中,QPolygon 类用来构建多边形,通过 QList<QPoint> 对象来指定顶点列表。上面代码中是两个不规则图形并集后,再与窗口的矩形区域取交集。最后把这个区域外的内容都设置成了透明。

运行效果如下。

【Qt6】嵌套 QWindow

 

原文链接:https://www.cnblogs.com/tcjiaan/p/17368021.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:【Qt6】嵌套 QWindow - Python技术站

(0)
上一篇 2023年4月30日
下一篇 2023年5月3日

相关文章

  • 【Visual Leak Detector】使用注意事项

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍使用 VLD 时的注意事项。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 官网文档 2. 注意事项 1. 官网文档 可以在 Using-Visual-Leak-Detector 官方文档里看到如何使用 VLD,里面介绍了如何在 Visual C++ 2003/2005/2…

    C++ 2023年4月17日
    00
  • C++ 并发编程实战 第二章 线程管控

    第二章 线程管控 std::thread 简介 构造和析构函数 /// 默认构造 /// 创建一个线程,什么也不做 thread() noexcept; /// 带参构造 /// 创建一个线程,以 A 为参数执行 F 函数 template <class Fn, class… Args> explicit thread(Fn&&amp…

    C++ 2023年4月17日
    00
  • 【Visual Leak Detector】配置项 TraceInternalFrames

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 配置文件中配置项 TraceInternalFrames 的使用方法。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 配置文件使用说明 2. 设置是否跟踪内部堆栈的调用 2.1 测试代码 2.2 TraceInternalFrames = no 时的输出 2.3 …

    C++ 2023年4月18日
    00
  • 前缀和

      一、什么是前缀和 前缀和是一种预处理,用于降低查询时的时间复杂度。 举个例子:给定 n 个整数,然后进行 m 次询问,每次询问求一个区间内值的和。 如果用暴力写法,那每次询问都需要从区间左端点循环到区间右端点求和,时间复杂度较大。 这种时候就可以预先求出该数组的一维前缀和。 则 ans=s[R]-s[L-1] ,其中 L 和 R 是给定的区间。每次询问可…

    C++ 2023年4月17日
    00
  • C++重载的奥义之运算符重载

    0、引言         重载,顾名思义从字面上理解就是重复装载,打一个不恰当的比方,你可以用一个篮子装蔬菜,也可以装水果或者其它,使用的是同一个篮子,但是可以用篮子重复装载的东西不一样。         正如在之前的文章《重载的奥义之函数重载》中介绍的类似,函数的重载是指利用相同的函数名设计一系列功能相近,但是功能细节不一样的函数接口;因此运算符重载也是指…

    C++ 2023年4月18日
    00
  • 【Visual Leak Detector】核心源码剖析(VLD 1.0)

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇对 VLD 1.0 源码做内存泄漏检测的思路进行剖析。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 源码获取 2. 源码文件概览 3. 源码剖析 3.1 注册自定义 AllocHook 函数 3.2 存储调用堆栈信息 3.3 生成泄漏检测报告 4. 其他问题 4.1 如何区分…

    C++ 2023年4月27日
    00
  • 【Visual Leak Detector】配置项 MaxDataDump

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 配置文件中配置项 MaxDataDump 的使用方法。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 配置文件使用说明 2. 设置每个泄漏块数据显示的最大字节数 2.1 测试代码 2.2 MaxDataDump 为空时的输出 2.3 MaxDataDump = 0…

    C++ 2023年4月18日
    00
  • 用C++编写一个简单的发布者和订阅者

    摘要:节点(Node)是通过 ROS 图进行通信的可执行进程。 本文分享自华为云社区《编写一个简单的发布者和订阅者》,作者: MAVER1CK 。 @[toc] 参考官方文档:Writing a simple publisher and subscriber (C++) 背景 节点(Node)是通过 ROS 图进行通信的可执行进程。 在本教程中,节点将通过话…

    C++ 2023年4月27日
    00
合作推广
合作推广
分享本页
返回顶部