Qt无边框界面添加鼠标事件
在Qt中实现无边框窗口的鼠标事件处理,主要涉及窗口拖动和调整大小功能。以下是分步实现的代码示例:
1. 创建无边框窗口
首先,创建一个继承自QWidget的自定义窗口类,并设置无边框标志:
#include <QWidget>
#include <QMouseEvent>class FramelessWindow : public QWidget {Q_OBJECTpublic:FramelessWindow(QWidget *parent = nullptr) : QWidget(parent) {setWindowFlags(Qt::FramelessWindowHint);setMouseTracking(true); // 启用鼠标追踪}
};
2. 处理窗口拖动
在标题栏区域按下鼠标左键时拖动窗口:
protected:void mousePressEvent(QMouseEvent *event) override {if (event->button() == Qt::LeftButton) {if (isInDragRegion(event->pos())) {m_dragging = true;m_dragStartPos = event->globalPos();m_windowPos = pos();}}QWidget::mousePressEvent(event);}void mouseMoveEvent(QMouseEvent *event) override {if (m_dragging) {QPoint delta = event->globalPos() - m_dragStartPos;move(m_windowPos + delta);}QWidget::mouseMoveEvent(event);}void mouseReleaseEvent(QMouseEvent *event) override {if (event->button() == Qt::LeftButton) {m_dragging = false;}QWidget::mouseReleaseEvent(event);}private:bool m_dragging = false;QPoint m_dragStartPos;QPoint m_windowPos;bool isInDragRegion(const QPoint &pos) const {// 假设标题栏高度为30像素return pos.y() < 30;}
3. 实现调整窗口大小
检测边缘并调整窗口大小:
protected:enum ResizeDirection { None, Left, Right, Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight };ResizeDirection m_resizeDir = None;void checkResizeRegion(const QPoint &pos) {const int borderWidth = 5;QRect rect = this->rect();bool left = pos.x() <= borderWidth;bool right = pos.x() >= rect.width() - borderWidth;bool top = pos.y() <= borderWidth;bool bottom = pos.y() >= rect.height() - borderWidth;if (left && top) m_resizeDir = TopLeft;else if (right && top) m_resizeDir = TopRight;else if (left && bottom) m_resizeDir = BottomLeft;else if (right && bottom) m_resizeDir = BottomRight;else if (left) m_resizeDir = Left;else if (right) m_resizeDir = Right;else if (top) m_resizeDir = Top;else if (bottom) m_resizeDir = Bottom;else m_resizeDir = None;}void mousePressEvent(QMouseEvent *event) override {// ... 原有拖动处理 ...checkResizeRegion(event->pos());if (m_resizeDir != None) {m_resizing = true;m_resizeStartPos = event->globalPos();m_initialGeometry = geometry();}}void handleResize(const QPoint &globalPos) {QRect newGeometry = m_initialGeometry;QPoint delta = globalPos - m_resizeStartPos;switch (m_resizeDir) {case Left:newGeometry.setLeft(newGeometry.left() + delta.x());break;case Right:newGeometry.setRight(newGeometry.right() + delta.x());break;case Top:newGeometry.setTop(newGeometry.top() + delta.y());break;case Bottom:newGeometry.setBottom(newGeometry.bottom() + delta.y());break;case TopLeft:newGeometry.setTopLeft(newGeometry.topLeft() + delta);break;case TopRight:newGeometry.setTopRight(newGeometry.topRight() + delta);break;case BottomLeft:newGeometry.setBottomLeft(newGeometry.bottomLeft() + delta);break;case BottomRight:newGeometry.setBottomRight(newGeometry.bottomRight() + delta);break;}// 限制最小尺寸newGeometry = newGeometry.normalized().adjusted(0, 0, 1, 1);if (newGeometry.width() < minimumWidth() || newGeometry.height() < minimumHeight())return;setGeometry(newGeometry);}
4. 更新光标形状
根据鼠标位置调整光标:
void updateCursorShape(const QPoint &pos) {checkResizeRegion(pos);switch (m_resizeDir) {case Left:case Right:setCursor(Qt::SizeHorCursor);break;case Top:case Bottom:setCursor(Qt::SizeVerCursor);break;case TopLeft:case BottomRight:setCursor(Qt::SizeFDiagCursor);break;case TopRight:case BottomLeft:setCursor(Qt::SizeBDiagCursor);break;default:unsetCursor();}}
5. 双击最大化
双击标题栏切换最大化:
void mouseDoubleClickEvent(QMouseEvent *event) override {if (event->button() == Qt::LeftButton && isInDragRegion(event->pos())) {toggleMaximize();}QWidget::mouseDoubleClickEvent(event);}void toggleMaximize() {isMaximized() ? showNormal() : showMaximized();}
完整示例代码
整合以上代码,完整的无边框窗口类如下:
#include <QWidget>
#include <QMouseEvent>
#include <QCursor>class FramelessWindow : public QWidget {Q_OBJECTpublic:FramelessWindow(QWidget *parent = nullptr) : QWidget(parent) {setWindowFlags(Qt::FramelessWindowHint);setMouseTracking(true);}protected:void mousePressEvent(QMouseEvent *event) override {if (event->button() == Qt::LeftButton) {if (isInDragRegion(event->pos())) {m_dragging = true;m_dragStartPos = event->globalPos();m_windowPos = pos();} else {checkResizeRegion(event->pos());if (m_resizeDir != None) {m_resizing = true;m_resizeStartPos = event->globalPos();m_initialGeometry = geometry();}}}QWidget::mousePressEvent(event);}void mouseMoveEvent(QMouseEvent *event) override {if (!m_resizing && !m_dragging)updateCursorShape(event->pos());if (m_dragging) {QPoint delta = event->globalPos() - m_dragStartPos;move(m_windowPos + delta);} else if (m_resizing) {handleResize(event->globalPos());}QWidget::mouseMoveEvent(event);}void mouseReleaseEvent(QMouseEvent *event) override {if (event->button() == Qt::LeftButton) {m_dragging = false;m_resizing = false;m_resizeDir = None;unsetCursor();}QWidget::mouseReleaseEvent(event);}void mouseDoubleClickEvent(QMouseEvent *event) override {if (event->button() == Qt::LeftButton && isInDragRegion(event->pos())) {toggleMaximize();}QWidget::mouseDoubleClickEvent(event);}private:bool m_dragging = false;QPoint m_dragStartPos;QPoint m_windowPos;bool m_resizing = false;QPoint m_resizeStartPos;QRect m_initialGeometry;enum ResizeDirection {None, Left, Right, Top, Bottom,TopLeft, TopRight, BottomLeft, BottomRight};ResizeDirection m_resizeDir = None;bool isInDragRegion(const QPoint &pos) const {return pos.y() < 30; // 标题栏区域高度}void checkResizeRegion(const QPoint &pos) {const int border = 5;bool left = pos.x() <= border;bool right = pos.x() >= width() - border;bool top = pos.y() <= border;bool bottom = pos.y() >= height() - border;if (left && top) m_resizeDir = TopLeft;else if (right && top) m_resizeDir = TopRight;else if (left && bottom) m_resizeDir = BottomLeft;else if (right && bottom) m_resizeDir = BottomRight;else if (left) m_resizeDir = Left;else if (right) m_resizeDir = Right;else if (top) m_resizeDir = Top;else if (bottom) m_resizeDir = Bottom;else m_resizeDir = None;}void updateCursorShape(const QPoint &pos) {checkResizeRegion(pos);switch (m_resizeDir) {case Left: case Right:setCursor(Qt::SizeHorCursor); break;case Top: case Bottom:setCursor(Qt::SizeVerCursor); break;case TopLeft: case BottomRight:setCursor(Qt::SizeFDiagCursor); break;case TopRight: case BottomLeft:setCursor(Qt::SizeBDiagCursor); break;default:unsetCursor();}}void handleResize(const QPoint &globalPos) {QRect newGeo = m_initialGeometry;int deltaX = globalPos.x() - m_resizeStartPos.x();int deltaY = globalPos.y() - m_resizeStartPos.y();switch (m_resizeDir) {case Left:newGeo.setLeft(newGeo.left() + deltaX);break;case Right:newGeo.setRight(newGeo.right() + deltaX);break;case Top:newGeo.setTop(newGeo.top() + deltaY);break;case Bottom:newGeo.setBottom(newGeo.bottom() + deltaY);break;case TopLeft:newGeo.setTopLeft(newGeo.topLeft() + QPoint(deltaX, deltaY));break;case TopRight:newGeo.setTopRight(newGeo.topRight() + QPoint(deltaX, deltaY));break;case BottomLeft:newGeo.setBottomLeft(newGeo.bottomLeft() + QPoint(deltaX, deltaY));break;case BottomRight:newGeo.setBottomRight(newGeo.bottomRight() + QPoint(deltaX, deltaY));break;}if (newGeo.width() < minimumWidth() || newGeo.height() < minimumHeight())return;setGeometry(newGeo);}void toggleMaximize() {isMaximized() ? showNormal() : showMaximized();}
};
注意事项
- 边缘检测:调整边缘检测的阈值(如
border = 5
)以适应不同需求。 - 最小尺寸:确保窗口不会调整到小于
minimumWidth
和minimumHeight
。 - 最大化处理:在最大化状态下可能需要禁用调整大小或特殊处理拖动行为。
- 性能优化:避免频繁的几何计算影响性能。
通过以上步骤,即可在Qt的无边框窗口中实现完整的鼠标交互功能。
相关文章:
Qt无边框界面添加鼠标事件
在Qt中实现无边框窗口的鼠标事件处理,主要涉及窗口拖动和调整大小功能。以下是分步实现的代码示例: 1. 创建无边框窗口 首先,创建一个继承自QWidget的自定义窗口类,并设置无边框标志: #include <QWidget> #in…...

企业级爬虫进阶开发指南
企业级爬虫进阶开发指南 一、分布式任务调度系统的深度设计 1.1 架构设计原理 图表 1.2 核心代码实现与注释 分布式锁服务 # distributed_lock.py import redis import timeclass DistributedLock:def __init__(self, redis_conn):self.redis = redis_connself.lock_key = …...
Ubuntu ping网络没有问题,但是浏览器无法访问到网络
我这边是尝试清楚DNS缓存然后重新访问就可以了。 使用 resolvectl 刷新 DNS 缓存 在 Ubuntu 20.04 及更高版本中,可以使用以下命令来刷新 DNS 缓存: sudo resolvectl flush-caches 使用 systemd-resolve(适用于旧版本) 如果你…...

网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】
################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…...

数据结构实验10.1:内部排序的基本运算
文章目录 一,实验目的二,实验内容1. 数据生成与初始化2. 排序算法实现(1)直接插入排序(2)二分插入排序(3)希尔排序(4)冒泡排序(5)快速…...
C#:多线程
一.线程常用概念 线程(Thread):操作系统执行程序的最小单位 进程(Process):程序在内存中的运行实例 并发(Concurrency):多个任务交替执行(单核CPU࿰…...
基于Zynq SDK的LWIP UDP组播开发实战指南
一、为什么选择LWIP组播? 在工业控制、智能安防、物联网等领域,一对多的高效数据传输需求日益增长。Zynq-7000系列SoC凭借其ARM+FPGA的独特架构,结合LWIP轻量级网络协议栈,成为嵌入式网络开发的理想选择。本文将带您实现: LWIP组播配置全流程动态组播组切换技术零拷贝数据…...
c#将json字符串转换为对象数组
在C#中,将JSON字符串转换为对象数组是一个常见的需求,特别是在处理来自Web API的响应或需要反序列化本地文件内容时。这可以通过使用Newtonsoft.Json(也称为Json.NET)库或.NET Core内置的System.Text.Json来完成。以下是如何使用这…...
机器学习在智能水泥基复合材料中的应用与实践
“机器学习在智能水泥基复合材料中的应用与实践” 课程 内容 机器学习基础模型与复合材料研究融合 机器学习在复合材料中的应用概述机器学习用于复合材料研究的流程复合材料数据收集与数据预处理 实例:数据的收集和预处理 复合材料机器学习特征工程与选择 实例&a…...

wps编辑技巧
1、编辑模式 2、图片提取方法:右键保存图片 可以直接右键保存下来看看是否是原始图,如果歪着的图,可能保存下来是正的,直接保存试下 3、加批注...

开放世界RPG:无缝地图与动态任务的拓扑学架构
目录 开放世界RPG:无缝地图与动态任务的拓扑学架构引言第一章 地图分块系统1.1 动态加载算法1.2 内存管理模型第二章 任务拓扑网络2.1 任务依赖图2.2 动态可达性分析第三章 NPC行为系统3.1 行为森林架构3.2 日程规划算法第四章 动态事件系统4.1 事件传播模型4.2 玩家影响指标第…...

【图像处理入门】1. 数字图像的本质:从像素到色彩模型
作为图像处理的开篇,本文将带你拆解数字图像的底层逻辑:从模拟图像到数字信号的神奇转换,到像素世界的微观构成,再到彩色图像的编码奥秘。通过 Python 代码实战,你将亲手触摸图像的 “基因”—— 像素值,并…...

(已解决:基于WSL2技术)Windows11家庭中文版(win11家庭版)如何配置和使用Docker Desktop
目录 问题现象: 问题分析: 拓展: 解决方法: 1、使用WSL2技术(亲测有效) 注意: 2、开启Hyper-V功能(未经亲测,待研究) 问题现象: 今天想在本…...

Ubuntu20.04部署KVM
文章目录 一. 环境准备关闭防火墙(UFW)禁用 SELinux更换镜像源检查 CPU 虚拟化支持 二. 安装KVM安装 KVM 及相关组件启动 libvirtd 服务验证安装创建虚拟机 一. 环境准备 4C8G,50G硬盘——VMware Workstation需要给虚拟机开启虚拟化引擎 roo…...

OpenCV CUDA 模块图像过滤------创建一个高斯滤波器函数createGaussianFilter()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::cuda::createGaussianFilter 是 OpenCV CUDA 模块中的一个工厂函数,用于创建一个高斯滤波器。这个滤波器可以用来平滑图像&#…...
计算机视觉与深度学习 | matlab实现ARIMA-WOA-CNN-LSTM时间序列预测(完整源码和数据)
以下是一个基于MATLAB的ARIMA-WOA-CNN-LSTM时间序列预测框架。由于完整代码较长,此处提供核心模块和实现思路,完整源码和数据可通过文末方式获取。 1. 数据准备(示例数据) 使用MATLAB内置的航空乘客数据集: % 加载数据 data = readtable(airline-passengers.csv); data …...

可视化图解算法43:数组中的逆序对
1. 题目 牛客网 面试笔试TOP101 描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007 数据范围&…...

【Python】使用Python实现调用API获取图片存储到本地
使用Python实现调用API获取图片存储到本地 目录 使用Python实现调用API获取图片存储到本地1、项目概述2、核心功能3、环境准备4、代码实现5、结果查看 1、项目概述 开发一个自动化工具,用于从JSON数据源中提取图像ID,通过调用指定API获取未经压缩的原始…...

腾讯2025年校招笔试真题手撕(一)
一、题目 有n 把钥匙,m 个锁,每把锁只能由一把特定的钥匙打开,其他钥匙都无法打开。一把钥匙可能可以打开多把锁,钥匙也可以重复使用。 对于任意一把锁来说,打开它的钥匙是哪一把是等概率的。但你无法事先知道是哪一把…...

Vue3 与 Vue2 区别
一、Vue3 与 Vue2 区别 对于生命周期来说,整体上变化不大,只是大部分生命周期钩子名称上 “on”,功能上是类似的。不过有一点需要注意,组合式API的Vue3 中使用生命周期钩子时需要先引入,而 Vue2 在选项API中可以直接…...
java集合详细讲解
Java 8 集合框架详解 Java集合框架是Java中最重要、最常用的API之一,Java 8对其进行了多项增强。下面我将全面讲解Java 8中的集合框架。 一、集合框架概述 Java集合框架主要分为两大类: Collection - 单列集合 List:有序可重复Set…...

嵌入式学习笔记 - STM32 U(S)ART 模块HAL 库函数总结
一 串口发送方式: ①轮训方式发送,也就是主动发送,这个容易理解,使用如下函数: HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout); ②中断方式发送ÿ…...

【VLNs篇】04:SayNav-为新环境中的动态规划到导航进行大型语言模型的基础构建
栏目内容论文标题SayNav: 为新环境中的动态规划到导航进行大型语言模型的基础构建 (SayNav: Grounding Large Language Models for Dynamic Planning to Navigation in New Environments)研究问题自主代理在未知环境中执行复杂导航任务(如MultiON)时&…...
MySQL中添加一个具有创建数据库权限的用户
要在MySQL中添加一个具有创建数据库权限的用户,可按以下步骤操作: 1. 登录MySQL 使用拥有足够权限(一般是root用户 )的账号登录到MySQL数据库。在命令行输入: mysql -u root -p然后输入对应的密码,即可进…...

oracle使用SPM控制执行计划
一 SPM介绍 Oracle在11G中推出了SPM(SQL Plan management),SPM是一种主动的稳定执行计划的手段,能够保证只有被验证过的执行计划才会被启用,当由于种种原因(比如统计信息的变更)而导致目标SQL产生了新的执…...
[Java实战]Spring Boot整合Seata:分布式事务一致性解决方案(三十一)
[Java实战]Spring Boot整合Seata:分布式事务一致性解决方案(三十一) 引言 在微服务架构中,业务逻辑被拆分为多个独立的服务,每个服务可能拥有独立的数据库。当需要跨服务操作多个数据库时,如何保证数据的…...

Openwrt下使用ffmpeg配合自建RTSP服务器实现推流
目前在Openwrt下时mjpg_streamer实现UVC摄像头转网络摄像头的方案很多,这种方案视频服在路由模组中,在局域网中使用很方便。但是对于需要远程监控管理的情况,mjpg_streamer不适应,因为不在局域网中的播放器无法访问到路由模组中的…...
MySQL 索引的增删改查
MySQL 索引的增删改查 1 建表时创建索引 [UNIQUE|FULLTEXT|SPATIAL] INDEX|KEY [别名] (字段名 [(长度)] [ASC|DESC] )主键直接写: PRIMARY KEY (Id)例如: CREATE TABLE people (id int NOT NULL PRIMARY KEY AUTO_INCREMENT,last_name varchar(10)…...
MySQL Host 被封锁解决方案(全版本适用 + Java 后端优化)
引言 MySQL 中 “Host is blocked because of many connection errors” 是生产环境常见问题,若处理不当会导致服务中断。本文结合 MySQL 官方文档(5.5/8.0)、Java 后端最佳实践及企业级经验,提供从 “快速解封” 到 “根源优化”…...

wifi 如果检查失败,UI 就会出现延迟或缺失打勾的现象。
问题:connectedSsid 的初始化依赖 onCreate 中的状态检查,如果检查失败,UI 就会出现延迟或缺失打勾的现象。 WIFI界面上上的一个标识代表成功连接。重启后出现偶尔不打勾的情况。 原始代码: // if (connectedSsid !…...