Qt无边框界面添加鼠标事件

news2025/5/23 22:14:12

在Qt中实现无边框窗口的鼠标事件处理,主要涉及窗口拖动和调整大小功能。以下是分步实现的代码示例:

1. 创建无边框窗口

首先,创建一个继承自QWidget的自定义窗口类,并设置无边框标志:

#include <QWidget>
#include <QMouseEvent>

class FramelessWindow : public QWidget {
    Q_OBJECT

public:
    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_OBJECT

public:
    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)以适应不同需求。
  • 最小尺寸:确保窗口不会调整到小于minimumWidthminimumHeight
  • 最大化处理:在最大化状态下可能需要禁用调整大小或特殊处理拖动行为。
  • 性能优化:避免频繁的几何计算影响性能。

通过以上步骤,即可在Qt的无边框窗口中实现完整的鼠标交互功能。

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

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

相关文章

企业级爬虫进阶开发指南

企业级爬虫进阶开发指南 一、分布式任务调度系统的深度设计 1.1 架构设计原理 图表 1.2 核心代码实现与注释 分布式锁服务 # distributed_lock.py import redis import timeclass DistributedLock:def __init__(self, redis_conn):self.redis = redis_connself.lock_key = …

网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】

################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…

数据结构实验10.1:内部排序的基本运算

文章目录 一&#xff0c;实验目的二&#xff0c;实验内容1. 数据生成与初始化2. 排序算法实现&#xff08;1&#xff09;直接插入排序&#xff08;2&#xff09;二分插入排序&#xff08;3&#xff09;希尔排序&#xff08;4&#xff09;冒泡排序&#xff08;5&#xff09;快速…

wps编辑技巧

1、编辑模式 2、图片提取方法&#xff1a;右键保存图片 可以直接右键保存下来看看是否是原始图&#xff0c;如果歪着的图&#xff0c;可能保存下来是正的&#xff0c;直接保存试下 3、加批注

开放世界RPG:无缝地图与动态任务的拓扑学架构

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

【图像处理入门】1. 数字图像的本质:从像素到色彩模型

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

(已解决:基于WSL2技术)Windows11家庭中文版(win11家庭版)如何配置和使用Docker Desktop

目录 问题现象&#xff1a; 问题分析&#xff1a; 拓展&#xff1a; 解决方法&#xff1a; 1、使用WSL2技术&#xff08;亲测有效&#xff09; 注意&#xff1a; 2、开启Hyper-V功能&#xff08;未经亲测&#xff0c;待研究&#xff09; 问题现象&#xff1a; 今天想在本…

Ubuntu20.04部署KVM

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

OpenCV CUDA 模块图像过滤------创建一个高斯滤波器函数createGaussianFilter()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::createGaussianFilter 是 OpenCV CUDA 模块中的一个工厂函数&#xff0c;用于创建一个高斯滤波器。这个滤波器可以用来平滑图像&#…

可视化图解算法43:数组中的逆序对

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

【Python】使用Python实现调用API获取图片存储到本地

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

腾讯2025年校招笔试真题手撕(一)

一、题目 有n 把钥匙&#xff0c;m 个锁&#xff0c;每把锁只能由一把特定的钥匙打开&#xff0c;其他钥匙都无法打开。一把钥匙可能可以打开多把锁&#xff0c;钥匙也可以重复使用。 对于任意一把锁来说&#xff0c;打开它的钥匙是哪一把是等概率的。但你无法事先知道是哪一把…

Vue3 与 Vue2 区别

一、Vue3 与 Vue2 区别 对于生命周期来说&#xff0c;整体上变化不大&#xff0c;只是大部分生命周期钩子名称上 “on”&#xff0c;功能上是类似的。不过有一点需要注意&#xff0c;组合式API的Vue3 中使用生命周期钩子时需要先引入&#xff0c;而 Vue2 在选项API中可以直接…

嵌入式学习笔记 - STM32 U(S)ART 模块HAL 库函数总结

一 串口发送方式&#xff1a; ①轮训方式发送&#xff0c;也就是主动发送&#xff0c;这个容易理解&#xff0c;使用如下函数&#xff1a; HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout); ②中断方式发送&#xff…

【VLNs篇】04:SayNav-为新环境中的动态规划到导航进行大型语言模型的基础构建

栏目内容论文标题SayNav: 为新环境中的动态规划到导航进行大型语言模型的基础构建 (SayNav: Grounding Large Language Models for Dynamic Planning to Navigation in New Environments)研究问题自主代理在未知环境中执行复杂导航任务&#xff08;如MultiON&#xff09;时&…

oracle使用SPM控制执行计划

一 SPM介绍 Oracle在11G中推出了SPM&#xff08;SQL Plan management&#xff09;,SPM是一种主动的稳定执行计划的手段&#xff0c;能够保证只有被验证过的执行计划才会被启用&#xff0c;当由于种种原因&#xff08;比如统计信息的变更&#xff09;而导致目标SQL产生了新的执…

Openwrt下使用ffmpeg配合自建RTSP服务器实现推流

目前在Openwrt下时mjpg_streamer实现UVC摄像头转网络摄像头的方案很多&#xff0c;这种方案视频服在路由模组中&#xff0c;在局域网中使用很方便。但是对于需要远程监控管理的情况&#xff0c;mjpg_streamer不适应&#xff0c;因为不在局域网中的播放器无法访问到路由模组中的…

wifi 如果检查失败,UI 就会出现延迟或缺失打勾的现象。

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

点云(point cloud):自动驾驶的“三维扫描图“

点云&#xff08;Point Cloud&#xff09;&#xff1a;就是用很多“点”来表示一个物体或场景的三维形状和结构。&#xff08;用点描绘的3D画&#xff0c;好比素描&#xff0c;但不是用线条勾勒&#xff0c;而是“点点点点”拼出物体形状&#xff09; 观察这幅图像&#xff0c;…

Oracle RAC节点时间差异同步测试

前言&#xff1a; Oracle Real Application Clusters (RAC) 集群依赖于各节点间的心跳检测与缓存融合等机制&#xff0c;这些机制对节点间的时钟同步性有极高的要求。如果集群内不同节点之间存在显著的时间偏差&#xff0c;可能会导致整个集群运行异常。在较早版本的RAC中&…