开发环境:win10+vs2015+qt5.9.1

背景:开发过程中,一般很少会使用系统提供的标题栏和边框;往往都是自定义一个自己设计的方案。这时候在QWidget中需要加上flag:Qt::FramelessWindowHint(实现方式很容易百度就不再赘述)。但是这样带来的问题就是系统自带的标题栏边框提供的拖拽移动和拖拽修改窗口大小的功能被废弃掉。这样就需要自己实现一个方案来提供这个功能。

实现:拖拽移动在很多文章中都应该有介绍了,代码也非常简单,不再赘述,主要讲一下拖拽修改窗口大小的方式;方案有两个,一个是借用系统平台自己的处理逻辑来完成,这里贴上windows平台的方案(Linux的没有尝试但应该也是有对应方案适应的)

/*
* Description: 无边框可拖拽大小窗口
* Author: 公子开明 KaiMing Prince
* Detail: 边框的拖拽大小是windows默认实现的效果,为了美化窗口经常需要去掉默认边框自定义标题栏和边框等部分,这样拖拽效果需要自己实现
* Class: FrameLessDragResizeWidget
* Implement: 本类实现了无边框的QWidget拖拽大小效果
*/
#ifndef _FRAMELESS_DRAG_RESIZE_WIDGET_H__
#define _FRAMELESS_DRAG_RESIZE_WIDGET_H__
#include <QWidget> class FrameLessDragResizeWidget : public QWidget
{
public:
FrameLessDragResizeWidget(QWidget *parent=Q_NULLPTR);
~FrameLessDragResizeWidget(); protected:
//第一种方案是走windows自己的消息机制
virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result);
};
#endif //_FRAMELESS_DRAG_RESIZE_WIDGET_H__

可以看到qt自己提供了一个nativeEvent事件负责处理系统事件,在windows平台则是窗口的消息机制,所以我们只要在我们认为适合的地方回馈给系统本身的拖拽修改消息就可以实现

#include "FrameLessDragResizeWidget.h"
#include <QMouseEvent> #include <qt_windows.h> const int g_nBorder = ; FrameLessDragResizeWidget::FrameLessDragResizeWidget(QWidget *parent)
: QWidget(parent, Qt::FramelessWindowHint)
{
setMouseTracking(true);
} FrameLessDragResizeWidget::~FrameLessDragResizeWidget()
{
} bool FrameLessDragResizeWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
MSG* pMsg = (MSG*)message;
switch (pMsg->message)
{
case WM_NCHITTEST:
{
QPoint pos = mapFromGlobal(QPoint(LOWORD(pMsg->lParam), HIWORD(pMsg->lParam)));
bool bHorLeft = pos.x() < g_nBorder;
bool bHorRight = pos.x() > width() - g_nBorder;
bool bVertTop = pos.y() < g_nBorder;
bool bVertBottom = pos.y() > height() - g_nBorder;
if (bHorLeft && bVertTop)
{
*result = HTTOPLEFT;
}
else if (bHorLeft && bVertBottom)
{
*result = HTBOTTOMLEFT;
}
else if (bHorRight && bVertTop)
{
*result = HTTOPRIGHT;
}
else if (bHorRight && bVertBottom)
{
*result = HTBOTTOMRIGHT;
}
else if (bHorLeft)
{
*result = HTLEFT;
}
else if (bHorRight)
{
*result = HTRIGHT;
}
else if (bVertTop)
{
*result = HTTOP;
}
else if (bVertBottom)
{
*result = HTBOTTOM;
}
else
{
return false;
}
return true;
}
break;
default:
break;
}
return QWidget::nativeEvent(eventType, message, result);
}

自定义了一个边框宽度为4,在这个范围内视为可拖拽区域;当然也可以自定义其它数值;主要处理看nativeEvent内部处理,在适当的判断下返回HTXX这种事件给系统,让系统来处理修改窗口大小的工作

方案2:方案1的问题在于,只适用于windows平台;如果修改平台就需要再一份代码处理。所以还是应该考虑用qt本身的处理方式来解决,避免多余实现;既然是qt实现,就要考虑鼠标的各个事件处理了,比如移动到边缘的光标状态改变,在可拖拽的范围内按下拖拽的移动等。。具体贴代码再分析(代码里还是保留了方案1,不需要的可以自己剔除)

/*
* Description: 无边框可拖拽大小窗口
* Author: 公子开明 KaiMing Prince
* Detail: 边框的拖拽大小是windows默认实现的效果,为了美化窗口经常需要去掉默认边框自定义标题栏和边框等部分,这样拖拽效果需要自己实现
* Class: FrameLessDragResizeWidget
* Implement: 本类实现了无边框的QWidget拖拽大小效果
*/
#ifndef _FRAMELESS_DRAG_RESIZE_WIDGET_H__
#define _FRAMELESS_DRAG_RESIZE_WIDGET_H__
#include <QWidget> enum DRAG_ABSOLUTE_POSITION
{
DRAG_KEEP=, //保持不动
DRAG_LOCK_LEFT, //锁定左边坐标
DRAG_LOCK_TOP=, //锁定上边坐标
DRAG_LOCK_RIGHT, //锁定右边坐标
DRAG_LOCK_BOTTOM= //锁定下边坐标
}; class FrameLessDragResizeWidget : public QWidget
{
public:
FrameLessDragResizeWidget(QWidget *parent=Q_NULLPTR);
~FrameLessDragResizeWidget(); protected:
//第一种方案是走windows自己的消息机制
virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result); //第二种方案是通过鼠标事件自己判断处理
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event); private:
bool   m_bCanDragResize;  //当前窗口是否处于可拖拽窗口大小状态
QPoint m_start_drag_point; //拖拽开始的起始位置
DRAG_ABSOLUTE_POSITION m_emHorizontal; //横向的移动范围
DRAG_ABSOLUTE_POSITION m_emVertical;  //纵向的移动范围
};
#endif //_FRAMELESS_DRAG_RESIZE_WIDGET_H__

枚举DRAG_ABSOLUTE_POSITION表示拖拽的一种状态,左右和上下的拖拽情况,肯定有一个保持不动的状态,我们可以通过这个状态来确认窗口的改变是怎样的。比如,我们在左上角拖拽的时候,右下角这个点肯定是保持原来的位置不变,而修改的是左上角的位置。这样在拖拽的时候就能判断geometry如何修改

#include "FrameLessDragResizeWidget.h"

#include <QMouseEvent>

//#define _NATIVE_EVENT_

#ifdef _NATIVE_EVENT_
#include <qt_windows.h>
#endif const int g_nBorder = ; FrameLessDragResizeWidget::FrameLessDragResizeWidget(QWidget *parent)
: QWidget(parent, Qt::FramelessWindowHint),
m_bCanDragResize(false)
{
setMouseTracking(true);
} FrameLessDragResizeWidget::~FrameLessDragResizeWidget()
{
} bool FrameLessDragResizeWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
#ifdef _NATIVE_EVENT_
MSG* pMsg = (MSG*)message;
switch (pMsg->message)
{
case WM_NCHITTEST:
{
QPoint pos = mapFromGlobal(QPoint(LOWORD(pMsg->lParam), HIWORD(pMsg->lParam)));
bool bHorLeft = pos.x() < g_nBorder;
bool bHorRight = pos.x() > width() - g_nBorder;
bool bVertTop = pos.y() < g_nBorder;
bool bVertBottom = pos.y() > height() - g_nBorder;
if (bHorLeft && bVertTop)
{
*result = HTTOPLEFT;
}
else if (bHorLeft && bVertBottom)
{
*result = HTBOTTOMLEFT;
}
else if (bHorRight && bVertTop)
{
*result = HTTOPRIGHT;
}
else if (bHorRight && bVertBottom)
{
*result = HTBOTTOMRIGHT;
}
else if (bHorLeft)
{
*result = HTLEFT;
}
else if (bHorRight)
{
*result = HTRIGHT;
}
else if (bVertTop)
{
*result = HTTOP;
}
else if (bVertBottom)
{
*result = HTBOTTOM;
}
else
{
return false;
}
return true;
}
break;
default:
break;
}
#endif
return QWidget::nativeEvent(eventType, message, result);
} void FrameLessDragResizeWidget::mousePressEvent(QMouseEvent *event)
{
#ifndef _NATIVE_EVENT_
if (Qt::LeftButton == event->button() && m_bCanDragResize)
{
m_start_drag_point = event->globalPos();
}
#endif
QWidget::mousePressEvent(event);
} void FrameLessDragResizeWidget::mouseMoveEvent(QMouseEvent *event)
{
#ifndef _NATIVE_EVENT_
if (Qt::LeftButton & event->buttons())
{
if (m_bCanDragResize) //如果左键按住移动且在拖拽状态
{
const QPoint point_offset = event->globalPos() - m_start_drag_point;
m_start_drag_point = event->globalPos();
int nOffsetX1 = DRAG_LOCK_RIGHT == m_emHorizontal ? point_offset.x() : ;
int nOffsetY1 = DRAG_LOCK_BOTTOM == m_emVertical ? point_offset.y() : ;
int nOffsetX2 = DRAG_LOCK_LEFT == m_emHorizontal ? point_offset.x() : ;
int nOffsetY2 = DRAG_LOCK_TOP == m_emVertical ? point_offset.y() : ;
setGeometry(geometry().adjusted(nOffsetX1, nOffsetY1, nOffsetX2, nOffsetY2));
}
}
else if (Qt::NoButton == event->button())
{
//先判断是否光标在可拖拽大小的窗口位置
const QPoint& pos = event->pos();
bool bHorLeft = pos.x() < g_nBorder;
bool bHorRight = pos.x() > rect().right() - g_nBorder;
bool bVertTop = pos.y() < g_nBorder;
bool bVertBottom = pos.y() > rect().bottom() - g_nBorder;
if (bHorLeft || bHorRight || bVertTop || bVertBottom)
{
m_bCanDragResize = true;
if (bHorLeft && bVertTop) //左上角拖拽
{
setCursor(Qt::SizeFDiagCursor);
m_emHorizontal = DRAG_LOCK_RIGHT;
m_emVertical = DRAG_LOCK_BOTTOM;
}
else if (bHorLeft && bVertBottom) //左下角拖拽
{
setCursor(Qt::SizeBDiagCursor);
m_emHorizontal = DRAG_LOCK_RIGHT;
m_emVertical = DRAG_LOCK_TOP;
}
else if (bHorRight && bVertTop) //右上角拖拽
{
setCursor(Qt::SizeBDiagCursor);
m_emHorizontal = DRAG_LOCK_LEFT;
m_emVertical = DRAG_LOCK_BOTTOM;
}
else if (bHorRight && bVertBottom) //右下角拖拽
{
setCursor(Qt::SizeFDiagCursor);
m_emHorizontal = DRAG_LOCK_LEFT;
m_emVertical = DRAG_LOCK_TOP;
}
else if (bHorLeft) //左边框拖拽
{
setCursor(Qt::SizeHorCursor);
m_emHorizontal = DRAG_LOCK_RIGHT;
m_emVertical = DRAG_KEEP;
}
else if (bHorRight) //右边框拖拽
{
setCursor(Qt::SizeHorCursor);
m_emHorizontal = DRAG_LOCK_LEFT;
m_emVertical = DRAG_KEEP;
}
else if (bVertTop) //上边框拖拽
{
setCursor(Qt::SizeVerCursor);
m_emHorizontal = DRAG_KEEP;
m_emVertical = DRAG_LOCK_BOTTOM;
}
else if (bVertBottom) //下边框拖拽
{
setCursor(Qt::SizeVerCursor);
m_emHorizontal = DRAG_KEEP;
m_emVertical = DRAG_LOCK_TOP;
}
}
else if (m_bCanDragResize)
{
m_bCanDragResize = false;
setCursor(Qt::ArrowCursor); //如果上一次判断修改了光标,不再是拖拽的状态把光标改回来
}
}
#endif
QWidget::mouseMoveEvent(event);
} void FrameLessDragResizeWidget::mouseReleaseEvent(QMouseEvent *event)
{
QWidget::mouseReleaseEvent(event);
}

首先看mousemove的nobutton情况,在边缘范围修改光标状态并记录边缘位置状态;然后根据是否拖拽的标识判断执行窗口大小的修改,根据鼠标位置偏移来确定改变大小数值,状态来确认adjusted的参数填充,并不断更新起始坐标位置。

效果图就不录制上传了,各位有兴趣可以直接copy代码尝试;下一篇谈谈拖拽移动和拖拽大小的合并处理。

Qt::QWidget 无默认标题栏边框的拖拽修改大小方式的更多相关文章

  1. qt 拖拽 修改大小(二)

    最近项目需要实现windows下橡皮筋的效果,所以对此做了一些了解,特此记录. 首先windows系统是支持橡皮筋效果的,需要使用win32方 法:SystemParametersInfo(SPI_S ...

  2. qt 拖拽 修改大小

    写次篇文章之前,qt窗口的放大缩小和拖拽我都是通过setGeometry方法实现的,但是作为windows程序,windows支持橡 皮筋式(拖拽时有一个虚框)拖拽和拉伸.通过setGeometry方 ...

  3. qt 拖拽 修改大小(使用了nativeEvent和winEvent)

    http://www.cnblogs.com/swarmbees/p/5621543.html http://blog.sina.com.cn/s/blog_9e59cf590102w3r6.html

  4. Qt之股票组件-自选股--列表可以拖拽、右键常用菜单

    目录 一.开头嘴一嘴 二.效果展示 三.自选股列表 1.列表初始化 2.添加Item 3.右键菜单 4.拖拽Item 5.刷新数据 四.相关文章 原文链接:Qt之股票组件-自选股--列表可以拖拽.右键 ...

  5. WPF实现无边框窗体拖拽右下角▲ 改变窗体大小【framwork4.0】 谢谢大家关注

    效果图:(右下角拖拽改变窗体大小) 第一步:添加xaml代码: <Border Name="ResizeBottomRight" MouseMove="Resize ...

  6. day23—JavaScript实现DIV盒子拖拽(原生方式)

    转行学开发,代码100天——2018-04-08 <!doctype html> <html> <head> <meta charset="utf- ...

  7. 解决Delphi图形化界面的TEdit、TLable等组件手动拖拽固定大小,但是编译之后显示有差别的情况

    经常遇到这样的情况,在我们使用Delphi的可视化工具进行UI设计的时候,我们拖拽TEdit或者Label组件,并且在可视化界面上设置它们的长.宽 但是当我们编译和运行程序的时候,却发现真正显示出来的 ...

  8. react之每日一更(实现canvas拖拽,增、删、改拖拽模块大小功能)

    效果图: import React, { Component } from 'react'; import scaleImage from './images/scale.png'; import c ...

  9. QT 标题栏隐藏可拖拽

    这个也是我网上找到了 为了方便,记录一下 void mousePressEvent(QMouseEvent *e); void mouseMoveEvent(QMouseEvent *e); void ...

随机推荐

  1. Climbing Stairs爬楼梯——动态规划

    题目描写叙述: 初阶:有n层的台阶,一開始你站在第0层,每次能够爬两层或者一层. 请问爬到第n层有多少种不同的方法? 进阶:假设每次能够爬两层.和倒退一层,同一个位置不能反复走,请问爬到第n层有多少种 ...

  2. perl File::Spec 模块

    File::Spec 模块提供了很多的功能,这里只列举几个常用的函数 rel2abs : 返回一个文件的绝对路径, 常见用法,返回当前运行的perl脚本的绝对路径 代码示例: my $prog = F ...

  3. Nginx的启动与停止,重启

    1.先确定nginx所在的文件位置 如: 重启 1.验证nginx配置文件是否正确 方法一:进入nginx安装目录sbin下,输入命令./nginx -t 2.重启Nginx服务 方法一:进入ngin ...

  4. Hbase1.1.0.1配置集群

    参考链接 http://wuyudong.com/archives/119?utm_source=tuicool 参考链接 http://www.cnblogs.com/archimedes/p/45 ...

  5. SQL Server 2008 收缩日志(log)文件

    USE TestDB; GO ALTER DATABASE TestDB SET RECOVERY SIMPLE; --设置简单恢复模式 GO ); GO ALTER DATABASE TestDB ...

  6. 使用Visual Studio将C#生成DLL文件的方法

    1.命令方式 打开Visual Studio安装目录下的开发人员命令提示 译 File.cs 以产生 File.exe csc File.cs 编译 File.cs 以产生 File.dll csc ...

  7. linux常用命令-tar,scp,du

    tar 打包排除指定目录 tar -zcvf afish.tar.gz * --exclude=file1 --exclude=dir1 排除目录注意: 1.--exclude=file1 而不是 - ...

  8. js继承摘要

    对象的构造函数是指向创建对象的类的原型对象的构造函数. 类是一个Function, Function都有原型对象,原型对象的构造函数指向类的声明. function Person(){ } Perso ...

  9. 【java】 java 设计模式概述

    一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...

  10. QlikView报表显示连续若干个月内活跃用户的数量

    之前有朋友提到了这样一个需求,要计算三年中每年都有销售记录的客户量,仅仅有近期两年有销售纪录的客户量(假如某个用户2012年和2014年都有记录,在2013年没有则不计算在内).以及近期一年的新增客户 ...