【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.11 小案例-无边框窗口

news2024/11/16 15:59:49

本节对应的视频讲解:B_站_链_接

【QT开发笔记-基础篇】 第4章 事件 4.11 小案例-无边框窗口


本章要实现的整体效果如下:

本章整体效果


本节课,来实现一个非常精简的无边框窗口,支持拖动4个边和4个角来缩放窗口

整体效果如下:

小案例-无边框窗口

整体效果说明:

  • 当鼠标位于 12346789 区域时,鼠标形状如上,可以进行窗口缩放;

  • 当鼠标位于 5 区域时,鼠标恢复正常形状如上,可以进行窗口的移动;

边界说明


1. 新建工程,去掉边框

1.1 新建工程

首先,从零新建一个基于 “Qt Widgets Application” 的项目:

类信息

项目创建完毕,如下:

项目目录

此时运行,是一个空白窗口,如下:

运行效果


1.2 添加按钮,设置背景色等

在空白界面上,添加两个水平布局的按钮,并设置窗体的最小大小,设置窗体的背景色,如下:

#include <QPushButton>
#include <QHBoxLayout>
#include <QDebug>

FramelessWidget::FramelessWidget(QWidget* parent) : QWidget(parent)
{
    // 设置最小宽高
    this->setMinimumWidth(500);
    this->setMinimumHeight(300);

    // 设置背景色
    this->setStyleSheet("background:#303030");

    QHBoxLayout* layout = new QHBoxLayout(this);
    layout->setSpacing(10);
    layout->setContentsMargins(10, 10, 10, 10);

    QPushButton* btn1 = new QPushButton("确定");
    QPushButton* btn2 = new QPushButton("取消");

    layout->addWidget(btn1);
    layout->addWidget(btn2);

    QString style = R"(
        QPushButton {
            background-color: rgb(64, 64, 64);
            font:16px "Microsoft YaHei";
            color:rgb(200,200,200);
            border: 1px solid #707070;
            border-radius: 5px;
            padding: 5px;
        }
        QPushButton:hover {
            background-color: rgb(40, 40, 40);
        }
        QPushButton:pressed {
            background-color: rgb(64, 64, 64);
        }
     )";

    btn1->setStyleSheet(style);
    btn2->setStyleSheet(style);

    connect(btn1, &QPushButton::clicked, this, [=] { qDebug() << btn1->text(); });

    connect(btn2, &QPushButton::clicked, this, [=] { qDebug() << btn2->text(); });
}

此时运行,效果如下:

此时效果


1.3 去除标题栏

自带的标题栏很丑,在构造中,添加一条语句即可去除,如下:

FramelessWidget::FramelessWidget(QWidget* parent) : QWidget(parent)
{
	// ...
    
    // 去除标题栏
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
}

1.4 右键退出

没有了标题栏,也就没有了右上角的 “关闭” 按钮,就无法退出程序了

接下来增加一个右键退出的功能

首先, 在 framelesswidget.h 中,声明虚函数 mousePressEvent(),如下:

class FramelessWidget : public QWidget
{
protected:
    void mouseReleaseEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event);
    void mousePressEvent(QMouseEvent* event);
};

然后,在 framelesswidget.cpp 中,实现 mousePressEvent(),如下:

#include <QMouseEvent>

void FramelessWidget::mousePressEvent(QMouseEvent* event)
{
    switch ( event->button() ) {
        case Qt::RightButton:
            this->close();
            break;
    }
}

此时,点击右键就可以直接退出程序,如下:

右键退出


2. 拖动窗口

由于没有了标题栏,无法移动窗口。接下来就实现窗口的移动

首先,在 framelesswidget.h 中,声明需要的成员变量和成员函数:

// 针对9个区域,定义了一个枚举变量;
enum Location { 
    TOP, 
    BOTTOM, 
    LEFT, 
    RIGHT, 
    TOP_LEFT, 
    TOP_RIGHT, 
    BOTTOM_LEFT, 
    BOTTOM_RIGHT, 
    CENTER 
};

class FramelessWidget : public QWidget
{
private:
    bool isLeftPressed; // 鼠标左键是否按下
    QPoint mousePos;	// 记录鼠标按下的位置
    Location location;
};

然后,修改鼠标按下、移动和释放的 3 个事件处理函数:

FramelessWidget::FramelessWidget(QWidget* parent) : QWidget(parent)
{
    // ...
    isLeftPressed = false; // 在构造中,将左键按下初始化为 false
}

void FramelessWidget::mouseReleaseEvent(QMouseEvent* event)
{
    if ( event->button() == Qt::LeftButton ) {
        isLeftPressed = false;
    }
}

void FramelessWidget::mouseMoveEvent(QMouseEvent* event)
{
    QPoint globalPos = event->globalPos();

	if ( isLeftPressed ) {
        move(globalPos - mousePos);
        event->accept();
    }
}

void FramelessWidget::mousePressEvent(QMouseEvent* event)
{
    switch ( event->button() ) {
        case Qt::LeftButton:
            isLeftPressed = true;
            mousePos = event->globalPos() - this->frameGeometry().topLeft();
            break;
        case Qt::RightButton:
            this->close();
            break;
        default:
            break;
    }
}

此时运行效果如下:

拖动窗口


3. 边界位置,改变鼠标形状

接下来实现鼠标在边界处按下时,可以缩放窗口的功能。

不过本节先来实现当鼠标移动到边界时,改变鼠标的形状的功能。

首先,在 framelesswidget.h 的构造中,开启鼠标跟踪 :

FramelessWidget::FramelessWidget(QWidget* parent) : QWidget(parent)
{
    //...
    
    this->setMouseTracking(true); // 开启鼠标跟踪
}

然后,修改 mouseMoveEvent()

void Dialog::mouseMoveEvent(QMouseEvent* event)
{
    QPoint globalPos = event->globalPos();

    // 1. 鼠标未按下
    if ( !isLeftPressed ) {
        this->setCursorShape(globalPos);
        return;
    }

    // 2. 鼠标在 CENTER 位置按下
    if ( location == CENTER ) {
        move(globalPos - mousePos);
        event->accept();
        return;
    }
}

然后,在 framelesswidget.h 声明 setCursorShape() 函数

// 定义一个间距
#define PADDING 5

class FramelessWidget : public QWidget
{
private:
    void setCursorShape(const QPoint& cursorPoint);
}

并在 framelesswidget.cpp 中实现它:

void Dialog::setCursorShape(const QPoint& point)
{
    QRect rect = this->rect();
    QPoint topLeft = mapToGlobal(rect.topLeft());
    QPoint bottomRight = mapToGlobal(rect.bottomRight());

    int x = point.x();
    int y = point.y();

    if ( x >= topLeft.x() && x <= topLeft.x() + PADDING && y >= topLeft.y() && y <= topLeft.y() + PADDING ) {
        // 左上角
        location = TOP_LEFT;
        this->setCursor(QCursor(Qt::SizeFDiagCursor));
    } else if ( x <= bottomRight.x() && x >= bottomRight.x() - PADDING && y <= bottomRight.y() && y >= bottomRight.y() - PADDING ) {
        // 右下角
        location = BOTTOM_RIGHT;
        this->setCursor(QCursor(Qt::SizeFDiagCursor));
    } else if ( x >= topLeft.x() && x <= topLeft.x() + PADDING && y <= bottomRight.y() && y >= bottomRight.y() - PADDING ) {
        //左下角
        location = BOTTOM_LEFT;
        this->setCursor(QCursor(Qt::SizeBDiagCursor));
    } else if ( x <= bottomRight.x() && x >= bottomRight.x() - PADDING && y >= topLeft.y() && y <= topLeft.y() + PADDING ) {
        // 右上角
        location = TOP_RIGHT;
        this->setCursor(QCursor(Qt::SizeBDiagCursor));
    } else if ( x >= topLeft.x() && x <= topLeft.x() + PADDING ) {
        // 左边
        location = LEFT;
        this->setCursor(QCursor(Qt::SizeHorCursor));
    } else if ( x <= bottomRight.x() && x >= bottomRight.x() - PADDING ) {
        // 右边
        location = RIGHT;
        this->setCursor(QCursor(Qt::SizeHorCursor));
    } else if ( y >= topLeft.y() && y <= topLeft.y() + PADDING ) {
        // 上边
        location = TOP;
        this->setCursor(QCursor(Qt::SizeVerCursor));
    } else if ( y <= bottomRight.y() && y >= bottomRight.y() - PADDING ) {
        // 下边
        location = BOTTOM;
        this->setCursor(QCursor(Qt::SizeVerCursor));
    } else {
        // 默认
        location = CENTER;
        this->setCursor(QCursor(Qt::ArrowCursor));
    }
}

此时运行,当鼠标移动到边界时,就可以改变形状了,如下:

边界光标形状


4. 实现窗口缩放

本节来实现,当鼠标在边界处按下并拖动时,缩放窗口的功能。

首先,当鼠标按下时,只有在 CENTER 区域,才移动窗口,所以修改 mousePressEvent()

void Dialog::mousePressEvent(QMouseEvent* event)
{
    switch ( event->button() ) {
        case Qt::LeftButton:
            isLeftPressed = true;
            if ( location == CENTER ) { // 增加这个 if 判断
                mousePos = event->globalPos() - this->frameGeometry().topLeft();
            }
            break;
    }
}

然后,修改 mouseMoveEvent(),增加以下代码:

void FramelessWidget::mouseMoveEvent(QMouseEvent* event)
{
    QPoint globalPos = event->globalPos();

    QRect rect = this->rect();
    QPoint topLeft = mapToGlobal(rect.topLeft());
    QPoint bottomRight = mapToGlobal(rect.bottomRight());

    // ...

    // 3. 鼠标在 边缘 位置按下
    QRect rMove(topLeft, bottomRight);

    switch ( location ) {
        case TOP:
            // 如果不加if判断,则窗口高度达到最小高度后,会被鼠标 "向下推走"
            if ( bottomRight.y() - globalPos.y() > this->minimumHeight() ) {
                rMove.setY(globalPos.y());
            }
            break;
        case BOTTOM:
            rMove.setHeight(globalPos.y() - topLeft.y());
            break;
        case LEFT:
            // 如果不加if判断,则窗口高度达到最小宽度后,会被鼠标 "向右推走"
            if ( bottomRight.x() - globalPos.x() > this->minimumWidth() ) {
                rMove.setX(globalPos.x());
            }
            break;
        case RIGHT:
            rMove.setWidth(globalPos.x() - topLeft.x());
            break;
        case TOP_LEFT:
            // 如果不加if判断,则窗口达到最小宽高后,会被鼠标 "向右下推走"
            if ( bottomRight.x() - globalPos.x() > this->minimumWidth() ) {
                rMove.setX(globalPos.x());
            }
            if ( bottomRight.y() - globalPos.y() > this->minimumHeight() ) {
                rMove.setY(globalPos.y());
            }
            break;
        case TOP_RIGHT:
            rMove.setWidth(globalPos.x() - topLeft.x());
            // 如果不加if判断,则窗口达到最小宽高后,会被鼠标 "向下推走"
            if ( bottomRight.y() - globalPos.y() > this->minimumHeight() ) {
                rMove.setY(globalPos.y());
            }
            break;
        case BOTTOM_LEFT:
            // 如果不加if判断,则窗口达到最小宽高后,会被鼠标 "向右推走"
            if ( bottomRight.x() - globalPos.x() > this->minimumWidth() ) {
                rMove.setX(globalPos.x());
            }
            rMove.setHeight(globalPos.y() - topLeft.y());
            break;
        case BOTTOM_RIGHT:
            rMove.setWidth(globalPos.x() - topLeft.x());
            rMove.setHeight(globalPos.y() - topLeft.y());
            break;
        default:
            break;
    }
    this->setGeometry(rMove);
}

此时运行效果如下:

窗口缩放

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1156323.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑实用增效工具

1、腾讯电脑管家 强力卸载特别有用。本来挺排斥这类软件&#xff0c;但真正用一段时间后发现&#xff0c;已经能做到无骚扰&#xff0c;只需在关键时刻排上用场 2、Snipaste 桌面贴图软件&#xff0c;高效好用 3、DeskPins 置顶工具&#xff0c;可自定义快捷键 4、向日葵远程…

【红蓝攻防鸿篇巨著】ATTCK视角下的红蓝对抗实战指南

【文末送书】今天推荐一本网安领域优质书籍《ATT&CK视角下的红蓝对抗实战指南》&#xff0c;本文将从其亮点与内容出发&#xff0c;详细阐发其对于网安从业人员的重要性与益处。 文章目录 背景简介内容文末送书 背景 根据中国互联网络信息中心&#xff08;CNNIC&#xff0…

“2024杭州智慧城市展“汇集全球领先的智慧城市解决方案和前沿技术

2024杭州智慧城市展览会&#xff0c;将于2024年4月份在杭州国际博览中心盛大召开。此次展览会以智慧城市为主题&#xff0c;涵盖了智慧城市、信息安全、数据中心与通信、人工智能、公共安全、会议广播视讯、智慧社区与智能家居、智慧停车等八个模块&#xff0c;旨在推动互联网、…

基于英伟达NVIDIA Jetson Xavier nx的Ubuntu系统安装nginx,mysql, java8

记录一下基于英伟达NVIDIA Jetson Xavier nx的Ubuntu系统安装nginx&#xff0c;mysql&#xff0c; java8(非docker方式) nx系统主要用于开发ai边缘人工智能视觉计算&#xff0c;人脸识别&#xff0c;车辆识别等&#xff0c;同样的也可以部署一些常见的程序应用 nx系统界面 …

x3daudio1 7.dll丢失怎么修复?多种x3daudio1 7.dll修复方法对比

x3daudio1_7.dll是Windows操作系统中的一个动态链接库文件&#xff0c;它主要负责处理音频相关的功能。当这个文件缺失或损坏时&#xff0c;可能会导致一些音频播放问题&#xff0c;如无声、杂音等。那么&#xff0c;x3daudio1_7.dll缺失的原因是什么呢&#xff1f;又该如何修复…

JavaScript从入门到精通系列第二十六篇:详解JavaScript中的Math对象

大神链接&#xff1a;作者有幸结识技术大神孙哥为好友&#xff0c;获益匪浅。现在把孙哥视频分享给大家。 孙哥连接&#xff1a;孙哥个人主页 作者简介&#xff1a;一个颜值99分&#xff0c;只比孙哥差一点的程序员 本专栏简介&#xff1a;话不多说&#xff0c;让我们一起干翻J…

sitespeedio.io 前端页面监控安装部署接入influxdb 到grafana

1.docker部署influxdb,部署1.8一下&#xff0c;不然语法有变化后面用不了grafana模板 docker run -d -p 8086:8086 --name influxdb -v $PWD/influxdb-data:/var/lib/influxdb influxdb:1.7.11-alpine docker exec -it influxdb_id bash #influx create user admin with pass…

基于供需算法的无人机航迹规划-附代码

基于供需算法的无人机航迹规划 文章目录 基于供需算法的无人机航迹规划1.供需搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用供需算法来优化无人机航迹规划。 1.供需搜索算法 …

kubernetes实验挑战三(| Vote App | Redis | Postgresql DB | Deployment | service)

Deploy the given architecture to vote namespace. 1、Create a new namespace: name ‘vote’ kubectl create ns vote2、 Create new deployment. name: ‘worker’image: ‘kodekloud/examplevotingapp_worker’status: ‘Running’ kubectl create deployment worker…

感觉嵌入式嵌入式单片机太难了,该不该放弃?

今日话题&#xff0c;感觉嵌入式单片机太难了&#xff0c;该不该放弃&#xff1f;嵌入式和单片机开发可能是一项具有挑战性但也非常有前景的领域。虽然它的学习曲线可能陡峭&#xff0c;但只要你克服了一开始的难度&#xff0c;你可能会发现它非常有趣且有价值。不要轻易放弃&a…

NodeMCU ESP8266 操作ADC读取外部模拟信号教程详解

文章目录 前言原理介绍准备知识ADC分辨率输入电压范围Analog Pin 电路搭建示例代码结论 前言 NodeMCU ESP8266 上有一个引脚可以快速采集模拟信号&#xff0c;并将其转成数字信号。 这就是我们经常听到的模数转换器&#xff08;ADC, Analog-to-Digital Converter &#xff09…

Simulink的To Workspace

To Workspace模块将Simulink产生的数据存储到matlab的工作区。 用To Workspace模块中的数据进行绘图。 参见Matlab/simulink/simscape multibody-to wotkspace模块使用_to workspace模块_五VV的博客-CSDN博客

数学分析:傅里叶变换(完结撒花)

这里我觉得老师讲的更好。首先我们已经知道了周期函数的傅里叶级数&#xff0c;接下来对于非周期函数&#xff0c;其实可以看成周期无穷大的函数。我们把周期经过一个换元&#xff0c;重新看这个傅里叶级数。 这里要注意&#xff0c;我们发现这个无限周期的傅里叶级数&#xff…

【WinForm详细教程四】WinForm中的ProgressBar 、ImageList和ListView控件

文章目录 1.ProgressBar2. ImageList3.ListView控件 1.ProgressBar 用于显示某个操作的进度。 属性&#xff1a; Value: 表示当前进度条的值&#xff0c;其范围由Min和Max决定。Step: 设置每次调用PerformStep()方法时增加的步长。MarqueeAnimationSpeed: 在Style设置为Marq…

K-means(K-均值)算法

K-means&#xff08;k-均值&#xff0c;也记为kmeans&#xff09;是聚类算法中的一种&#xff0c;由于其原理简单&#xff0c;可解释强&#xff0c;实现方便&#xff0c;收敛速度快&#xff0c;在数据挖掘、聚类分析、数据聚类、模式识别、金融风控、数据科学、智能营销和数据运…

面试题计算器

这篇也是凑数的 ...... 这篇会收录到算法通关村第四关黄金挑战里 计算器 描述 : 给定一个包含正整数、加()、减(-)、乘(*)、除(/)的算数表达式(括号除外)&#xff0c;计算其结果。 表达式仅包含非负整数&#xff0c;&#xff0c; - &#xff0c;*&#xff0c;/ 四种运算符和…

1深度学习李宏毅

目录 机器学习三件事&#xff1a;分类&#xff0c;预测和结构化生成 2、一般会有经常提到什么是标签label&#xff0c;label就是预测值&#xff0c;在机器学习领域的残差就是e和loss​编辑3、一些计算loss的方法&#xff1a;​编辑​编辑 4、可以设置不同的b和w从而控制loss的…

[架构之路-249/创业之路-80]:目标系统 - 纵向分层 - 企业信息化的呈现形态:常见企业信息化软件系统 - 产品(数据)管理

目录 前言&#xff1a; 一、企业信息化的结果&#xff1a;常见企业信息化软件 1.1 产品数据管理 1.1.1 什么是产品数据管理What 1.1.1.1 常见工具 1.1.1.2 软件企业的产品数据管理系统 1.1.2 为什么需要产品数据管理系统Why&#xff1f; 1.1.3 谁需要产品数据管理系统w…

STM32基本定时器中断

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、STM32定时器的结构&#xff1f;1. 51定时器的结构1.1如何实现定时1s的功能&#xff1f; 2. stm32定时器的结构2.1 通用定时器 二、使用步骤1.开启时钟2.初始…

nodejs+vue黄花岗社区核酸检测站-计算机毕业设计python-django-php

对黄花岗社区核酸检测站系统进行大力的研究&#xff0c;主要是因为黄花岗社区核酸检测站系统对于社区的推进有着十分重要的作用&#xff0c; 对于社区的管理来说&#xff0c;黄花岗社区核酸检测站系统是十分有效的一个途径&#xff0c;也正是因为这样的特殊性使得在对社区进行管…