一、可按比例拖拽窗体大小的无边框窗口

前几天接到一个需求,就是视频广播的窗体画面要可以拖拽,修改成了可以拖拽全屏的窗口之后,又有一个问题:视频画面也被拉伸了。

由于视频画面是有比例的,所以我们最好也能保证窗口画面也保持一定的比例,所以这里我就改了一下之前的无边框窗口方案如下:

优点:改造过的无边框方案,可以让无边框的窗体变换大小的时候保持一定比例,保证窗体中的画面不变形

缺点:1.不够自由,不能自由拖动

2.如果拖动的是左边,因为拖动事件实际上是先变换大小后修改坐标,那么右边就会有明显抖动

3.因为要检测当前的鼠标状态,所以是挺浪费系统资源的,较差的机型上可能会有一些意想不到的问题。

.h文件下:

enum {
TOPLEFT = 11,
TOP = 12,
TOPRIGHT = 13,
LEFT = 21,
CENTER = 22,
RIGHT = 23,
BUTTOMLEFT = 31,
BUTTOM = 32,
BUTTOMRIGHT = 33
};
#define FRAMESHAPE 15
const double default_rate = 4.00 / 3.00; //画面比例参数
public:
int CalCursorCol(QPoint pt); //计算鼠标X的位置
int CalCursorPos(QPoint pt, int colPos); //计算鼠标的位置
void setCursorShape(int CalPos); //设置鼠标对应位置的形状
protected:
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
int m_iCalCursorPos;
bool m_bLeftPress;
QRect m_rtPreGeometry;
QPoint m_ptViewMousePos;

.cpp文件:

//注,要在构造函数写上setMouseTracking(true),要从表面的控件->控件的父控件 ->...->this都要写上
void Mengban::mouseMoveEvent(QMouseEvent * event)
{
//窗体不是最大的话就改变鼠标的形状
if (Qt::WindowMaximized != windowState())
{
setCursorShape(CalCursorPos(event->pos(), CalCursorCol(event->pos())));
}
//获取当前的点,这个点是全局的
QPoint ptCurrentPos = QCursor::pos();
//计算出移动的位置,当前点 - 鼠标左键按下的点
QPoint ptMoveSize = ptCurrentPos - m_ptViewMousePos;
QRect rtTempGeometry = m_rtPreGeometry;
if (m_bLeftPress)
{
switch (m_iCalCursorPos)
{
//case TOPLEFT:
// rtTempGeometry.setTopLeft(m_rtPreGeometry.topLeft() + ptMoveSize);
// break;
case TOP:
rtTempGeometry.setTop(m_rtPreGeometry.top() + ptMoveSize.y());
rtTempGeometry.setRight(this->x() + this->height() * default_rate);
break;
//case TOPRIGHT:
// rtTempGeometry.setTopRight(m_rtPreGeometry.topRight() + ptMoveSize);
// break;
case LEFT:
rtTempGeometry.setLeft(m_rtPreGeometry.left() + ptMoveSize.x());
rtTempGeometry.setTop(this->y() - this->width() / default_rate + this->height());
break;
case RIGHT:
rtTempGeometry.setRight(m_rtPreGeometry.right() + ptMoveSize.x());
rtTempGeometry.setBottom(this->y() + this->width() / default_rate);
break;
//case BUTTOMLEFT:
// rtTempGeometry.setBottomLeft(m_rtPreGeometry.bottomLeft() + ptMoveSize);
// break;
case BUTTOM:
rtTempGeometry.setBottom(m_rtPreGeometry.bottom() + ptMoveSize.y());
rtTempGeometry.setLeft(this->x() - this->height() * default_rate+this->width());
break;
//case BUTTOMRIGHT:
// rtTempGeometry.setBottomRight(m_rtPreGeometry.bottomRight() + ptMoveSize);
// break;
case CENTER:
rtTempGeometry.moveTo(rtTempGeometry.x() + ptMoveSize.x(), rtTempGeometry.y() + ptMoveSize.y());
default:
break;
}
//移动窗体,如果比最小窗体大,就移动
if (rtTempGeometry.width() >= 360 && rtTempGeometry.height() >= 270)
setGeometry(rtTempGeometry); }
} void Mengban::mousePressEvent(QMouseEvent * event)
{
m_iCalCursorPos = CalCursorPos(event->pos(), CalCursorCol(event->pos()));
if (event->button() == Qt::LeftButton /*&& Qt::WindowMaximized != windowState()*/)
{
//if (m_iCalCursorPos != CENTER)
//{
m_bLeftPress = true;
//}
}
m_rtPreGeometry = geometry();
m_ptViewMousePos = event->globalPos();
} void Mengban::mouseReleaseEvent(QMouseEvent * event)
{
m_bLeftPress = false;
QApplication::restoreOverrideCursor();
} int Mengban::CalCursorCol(QPoint pt)
{
return (pt.x() < FRAMESHAPE ? 1 : ((pt.x() > this->width() - FRAMESHAPE) ? 3 : 2));
} int Mengban::CalCursorPos(QPoint pt, int colPos)
{
return ((pt.y() < FRAMESHAPE ? 10 : ((pt.y() > this->height() - FRAMESHAPE) ? 30 : 20)) + colPos);
} void Mengban::setCursorShape(int CalPos)
{
Qt::CursorShape cursor;
switch (CalPos)
{
//case TOPLEFT:
//case BUTTOMRIGHT:
// cursor = Qt::SizeFDiagCursor;
// break;
//case TOPRIGHT:
//case BUTTOMLEFT:
// cursor = Qt::SizeBDiagCursor;
// break;
case TOP:
case BUTTOM:
cursor = Qt::SizeVerCursor;
break;
case LEFT:
case RIGHT:
cursor = Qt::SizeHorCursor;
break;
default:
cursor = Qt::ArrowCursor;
break;
}
//qDebug() << "fresh!!";
//qDebug() << "current cursor" << cursor;
this->setCursor(cursor); }

二、可拖拽大小的windows系统方案:

这个方案拖拽窗体大小是通过系统api实现的,具体怎么实现的我也不是很清楚,之后有时间系统看一下windows高级编程再回来聊聊这个技术点。这里只谈优缺点

优点:

1.windows自带api,效率更高,消耗资源更少

2.没有限制,自由拖拽大小(当然了,窗体会受到最大最小窗口大小的限制)

3.代码简单,分析方便

缺点:

1.不可修改,这部分就像个黑盒,native只是告诉你现在修改的时候那个地方是锚点,仅此而已,基本上没法修改,当然了,我什么都不知道 :<

2.因为是windows的api,所以该功能仅限windows平台

  #ifndef MBASEWIDGET_H

  #define MBASEWIDGET_H

  #include <QtWidgets/QWidget>

  #include "windows.h"

  class MBaseWidget : public QWidget

  {

      Q_OBJECT

  public:

      MBaseWidget(QWidget *parent);

      ~MBaseWidget();

      void setMarginWidth(const int &);           //设置鼠标可以在界面边缘多大范围内拖动改变界面大小

      void serResizable(bool);                    //设置是否可以拖动改变大小

  protected:

      bool nativeEvent(const QByteArray & eventType, void * message, long * result);

  private:

      int m_iMarginWidth;

      bool m_bCanResize;

  };

  #endif // MBASEWIDGET_H

  #include "MBaseWidget.h"

  #include "windowsx.h"

  MBaseWidget::MBaseWidget(QWidget *parent)

      : QWidget(parent)

  {

      m_iMarginWidth = 3;
m_bCanResize = true;
setWindowFlags(Qt::FramelessWindowHint); } MBaseWidget::~MBaseWidget() {
} void MBaseWidget::setMarginWidth(const int &iWidth) { m_iMarginWidth = iWidth; } void MBaseWidget::serResizable(bool bCanResize) { m_bCanResize = bCanResize; } bool MBaseWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
if (!m_bCanResize)
{
return QWidget::nativeEvent(eventType,message,result);
} MSG* msg = (MSG*)message;
switch(msg->message)
{
case WM_NCHITTEST: int xPos = GET_X_LPARAM(msg->lParam) - this->frameGeometry().x();
int yPos = GET_Y_LPARAM(msg->lParam) - this->frameGeometry().y();
if(xPos < m_iMarginWidth && yPos<m_iMarginWidth) //左上角
*result = HTTOPLEFT;
else if(xPos>=width()-m_iMarginWidth&&yPos<m_iMarginWidth) //右上角
*result = HTTOPRIGHT;
else if(xPos<m_iMarginWidth&&yPos>=height()-m_iMarginWidth) //左下角
*result = HTBOTTOMLEFT;
else if(xPos>=width()-m_iMarginWidth&&yPos>=height()-m_iMarginWidth)//右下角
*result = HTBOTTOMRIGHT;
else if(xPos < m_iMarginWidth) //左边
*result = HTLEFT;
else if(xPos>=width()-m_iMarginWidth) //右边
*result = HTRIGHT;
else if(yPos<m_iMarginWidth) //上边
*result = HTTOP;
else if(yPos>=height()-m_iMarginWidth) //下边
*result = HTBOTTOM;
else //其他部分不做处理,返回false,留给其他事件处理器处理
return false;
return true;
}
return false; //此处返回false,留给其他事件处理器处理 }

三、之前用的老的frameless方案:

最开始在网上找的frameless的方案,没什么好说的,详情见一个关于如何将widget变成一个像商用软件那样 不可拖拽大小、没有开启、关闭等按钮的固定窗口

讲道理,我发现莫名其妙的其实在这里就包含了前两种写法,但是我莫名其妙地一点都没用过。所以说抄代码也要看源码啊

内容如下:

在程序中定义Padding 为2,并同时定义枚举类型。

    #define PADDING 2
enum Direction { UP=0, DOWN=1, LEFT, RIGHT, LEFTTOP, LEFTBOTTOM, RIGHTBOTTOM, RIGHTTOP, NONE };

在.cpp中设置当前窗口

    this->setWindowFlags(Qt::FramelessWindowHint);                //取消标题栏
// 去掉标题栏,去掉工具栏,窗口置顶
setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);
setWindowOpacity(0.7); //设置窗体透明度
重写mouseMoveEvent,mousePressEvent,mouseReleaseEvent

MainWindow.h

    public:
void region(const QPoint &currentGlobalPoint); //鼠标的位置,改变光标
protected:
//鼠标按下移动及释放事件
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event); private:
QPoint m_movePoint; //鼠标的位置
bool isLeftPressDown; // 判断左键是否按下
Direction dir; // 窗口大小改变时,记录改变方向

MainWindow.cpp

void MainWindow::region(const QPoint &currentGlobalPoint)
{
// 获取窗体在屏幕上的位置区域,topLeft为坐上角点,rightButton为右下角点
QRect rect = this->rect(); QPoint topLeft = this->mapToGlobal(rect.topLeft()); //将左上角的(0,0)转化为全局坐标
QPoint rightButton = this->mapToGlobal(rect.bottomRight()); int x = currentGlobalPoint.x(); //当前鼠标的坐标
int y = currentGlobalPoint.y(); if(((topLeft.x() + PADDING >= x) && (topLeft.x() <= x))
&& ((topLeft.y() + PADDING >= y) && (topLeft.y() <= y)))
{
// 左上角
dir = LEFTTOP;
this->setCursor(QCursor(Qt::SizeFDiagCursor)); // 设置光标形状
}else if(((x >= rightButton.x() - PADDING) && (x <= rightButton.x()))
&& ((y >= rightButton.y() - PADDING) && (y <= rightButton.y())))
{
// 右下角
dir = RIGHTBOTTOM;
this->setCursor(QCursor(Qt::SizeFDiagCursor));
}else if(((x <= topLeft.x() + PADDING) && (x >= topLeft.x()))
&& ((y >= rightButton.y() - PADDING) && (y <= rightButton.y())))
{
//左下角
dir = LEFTBOTTOM;
this->setCursor(QCursor(Qt::SizeBDiagCursor));
}else if(((x <= rightButton.x()) && (x >= rightButton.x() - PADDING))
&& ((y >= topLeft.y()) && (y <= topLeft.y() + PADDING)))
{
// 右上角
dir = RIGHTTOP;
this->setCursor(QCursor(Qt::SizeBDiagCursor));
}else if((x <= topLeft.x() + PADDING) && (x >= topLeft.x()))
{
// 左边
dir = LEFT;
this->setCursor(QCursor(Qt::SizeHorCursor));
}else if((x <= rightButton.x()) && (x >= rightButton.x() - PADDING))
{
// 右边
dir = RIGHT;
this->setCursor(QCursor(Qt::SizeHorCursor));
}else if((y >= topLeft.y()) && (y <= topLeft.y() + PADDING))
{
// 上边
dir = UP;
this->setCursor(QCursor(Qt::SizeVerCursor));
}else if((y <= rightButton.y()) && (y >= rightButton.y() - PADDING))
{
// 下边
dir = DOWN;
this->setCursor(QCursor(Qt::SizeVerCursor));
}else
{
// 默认
dir = NONE;
this->setCursor(QCursor(Qt::ArrowCursor));
} }
//三个鼠标事件的重写 //鼠标按下事件 void MainWindow::mousePressEvent(QMouseEvent *event)
{
switch(event->button())
{
case Qt::LeftButton:
isLeftPressDown = true; if(dir != NONE)
{
this->mouseGrabber(); //返回当前抓取鼠标输入的窗口
}
else
{
m_movePoint = event->globalPos() - this->frameGeometry().topLeft();
//globalPos()鼠标位置,topLeft()窗口左上角的位置
}
break;
case Qt::RightButton:
this->setWindowState(Qt::WindowMinimized);
break;
default:
MainWindow::mousePressEvent(event);
} }
//鼠标移动事件 void MainWindow::mouseMoveEvent(QMouseEvent *event) { QPoint globalPoint = event->globalPos(); //鼠标全局坐标 QRect rect = this->rect(); //rect == QRect(0,0 1280x720)
QPoint topLeft = mapToGlobal(rect.topLeft());
QPoint bottomRight = mapToGlobal(rect.bottomRight()); if (this->windowState() != Qt::WindowMaximized)
{
if(!isLeftPressDown) //没有按下左键时
{
this->region(globalPoint); //窗口大小的改变——判断鼠标位置,改变光标形状
}
else
{
if(dir != NONE)
{
QRect newRect(topLeft, bottomRight); //定义一个矩形 拖动后最大1000*1618 switch(dir)
{
case LEFT: if(bottomRight.x() - globalPoint.x() <= this->minimumWidth())
{
newRect.setLeft(topLeft.x()); //小于界面的最小宽度时,设置为左上角横坐标为窗口x
//只改变左边界
}
else
{
newRect.setLeft(globalPoint.x());
}
break;
case RIGHT:
newRect.setWidth(globalPoint.x() - topLeft.x()); //只能改变右边界
break;
case UP:
if(bottomRight.y() - globalPoint.y() <= this->minimumHeight())
{
newRect.setY(topLeft.y());
}
else
{
newRect.setY(globalPoint.y());
}
break;
case DOWN:
newRect.setHeight(globalPoint.y() - topLeft.y());
break;
case LEFTTOP:
if(bottomRight.x() - globalPoint.x() <= this->minimumWidth())
{
newRect.setX(topLeft.x());
}
else
{
newRect.setX(globalPoint.x());
} if(bottomRight.y() - globalPoint.y() <= this->minimumHeight())
{
newRect.setY(topLeft.y());
}
else
{
newRect.setY(globalPoint.y());
}
break;
case RIGHTTOP:
if (globalPoint.x() - topLeft.x() >= this->minimumWidth())
{
newRect.setWidth(globalPoint.x() - topLeft.x());
}
else
{
newRect.setWidth(bottomRight.x() - topLeft.x());
}
if (bottomRight.y() - globalPoint.y() >= this->minimumHeight())
{
newRect.setY(globalPoint.y());
}
else
{
newRect.setY(topLeft.y());
}
break;
case LEFTBOTTOM:
if (bottomRight.x() - globalPoint.x() >= this->minimumWidth())
{
newRect.setX(globalPoint.x());
}
else
{
newRect.setX(topLeft.x());
}
if (globalPoint.y() - topLeft.y() >= this->minimumHeight())
{
newRect.setHeight(globalPoint.y() - topLeft.y());
}
else
{
newRect.setHeight(bottomRight.y() - topLeft.y());
}
break;
case RIGHTBOTTOM:
newRect.setWidth(globalPoint.x() - topLeft.x());
newRect.setHeight(globalPoint.y() - topLeft.y());
break;
default:
break;
}
this->setGeometry(newRect);
}
else
{
move(event->globalPos() - m_movePoint); //移动窗口
event->accept();
}
}
} } //鼠标释放事件 void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
isLeftPressDown = false;
if (dir != NONE)
{
this->releaseMouse(); //释放鼠标抓取
this->setCursor(QCursor(Qt::ArrowCursor));
dir = NONE; //热心网友指正
}
} }

法2: 重写nativeEvent事件: MainWindow.h

    protected:
bool nativeEvent(const QByteArray &eventType, void *message, long *result);

MainWindow.cpp

    bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
MSG* msg = (MSG*)message;
switch(msg->message)
{
case WM_NCHITTEST:
int xPos = GET_X_LPARAM(msg->lParam) - this->frameGeometry().x();
int yPos = GET_Y_LPARAM(msg->lParam) - this->frameGeometry().y();
if(this->childAt(xPos,yPos) == 0)
{
*result = HTCAPTION;
}else{
return false;
}
if(xPos > 0 && xPos < 8)
*result = HTLEFT;
if(xPos > (this->width() - 8) && xPos < (this->width() - 0))
*result = HTRIGHT;
if(yPos > 0 && yPos < 8)
*result = HTTOP;
if(yPos > (this->height() - 8) && yPos < (this->height() - 0))
*result = HTBOTTOM;
if(xPos > 18 && xPos < 22 && yPos > 18 && yPos < 22)
*result = HTTOPLEFT;
if(xPos > (this->width() - 22) && xPos < (this->width() - 18) && yPos > 18 && yPos < 22)
*result = HTTOPRIGHT;
if(xPos > 18 && xPos < 22 && yPos > (this->height() - 22) && yPos < (this->height() - 18))
*result = HTBOTTOMLEFT;
if(xPos > (this->width() - 22) && xPos < (this->width() - 18) && yPos > (this->height() - 22) && yPos < (this->height() - 18))
*result = HTBOTTOMRIGHT;
return true;
}
return false;
}

Qt的三套无边框窗体的方案:可按比例拖拽窗体大小的无边框窗口和几个常见的无边框实例的更多相关文章

  1. Qt::QWidget 无默认标题栏边框的拖拽修改大小方式

    开发环境:win10+vs2015+qt5.9.1 背景:开发过程中,一般很少会使用系统提供的标题栏和边框:往往都是自定义一个自己设计的方案.这时候在QWidget中需要加上flag:Qt::Fram ...

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

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

  3. WPF 应用 - 拖拽窗体、控件

    1. 拖拽窗体 使用 System.Windows.Window 自带的 DragMove() 方法即可识别窗体拖动. DragMove(); 2. 拖拽控件:复制.移动控件 <Grid> ...

  4. Unity打包PC端各种屏幕适配,无边框,最小化,显示可拖拽部分

    using UnityEngine; using System.Collections; using UnityEngine.EventSystems; //using UnityEngine.Sce ...

  5. qt 拖拽 修改大小

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

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

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

  7. c# 窗体与窗体外的文件互动(拖拽)

    大部分控件都有此事件drag相关. 以picturebox为例: pictureBox1.AllowDrop = true;//此属性需要全打出来,不会自动识别添加 private void pict ...

  8. 戏说 .NET GDI+系列学习教程(三、Graphics类的应用_自定义控件--主要用于画面拖拽效果)

    如题,需求:在某个图片上用户可以手动指定位置. 如下: 中心思想:仿照Visual Studio工具中的控件的做法 如何仿照呢? 1.自定义的控件类继承System.Windows.Forms.Con ...

  9. Qt无边框窗体-最大化时支持拖拽还原

    目录 一.概述 二.效果展示 三.demo制作 1.设计窗体 2.双击放大 四.拖拽 五.相关文章 原文链接:Markdown模板 一.概述 用Qt进行开发界面时,既想要实现友好的用户交互又想界面漂亮 ...

  10. Qt学习-ListView的拖拽

    最近在学习Qt 里面的QML, 使用DropArea和MouseArea实现了ListView的拖拽. 想起了当年用Delphi, 差不多一样的东西, 不过那是2000了. Delphi也是不争气啊, ...

随机推荐

  1. SQL优化步骤

    当生产数据量急剧增长后,很多SQL语句可能会开始暴露出性能问题.当面对一个有SQL性能问题的数据库时,应该从何处入手进行系统的分析,使得能够尽快定位到问题SQL处并尽快解决问题? 第一步:查看SQL执 ...

  2. 已知数据库中存在表tb_book2,通过在图书信息界面填写书本的基本信息,然后提交后写入数据库中的表格中。需要对提交的信息进行修改,信息填入不能为空,为空则则有提示。

    jsp结合SQLSERVER向数据库中的表添加图书信息. 已知数据库中存在表tb_book2,通过在图书信息界面填写书本的基本信息,然后提交后写入数据库中的表格中.需要对提交的信息进行修改,信息填入不 ...

  3. 创建Vue工程常用的命令

    创建一个vue项目的步骤 1.创建一个名称为myapp的工程 vue init webpack myapp 2.进入工程目录 cd myapp 3.安装 vue-router npm install ...

  4. python django超链接

    之前用django框架打了一个简易的博客网站,现在说说怎么用django做超链接. 本文基于之前讲解的博客应用,如果只想学超链接请自行删减代码或评论提问. 首先,在templates文件夹下添加det ...

  5. Oracle数据库的两种授权收费方式介绍!

    首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485212&idx=1 ...

  6. 【MySQL】01_运算符、函数

    运算符 运算符是保留字或主要用于 SQL 语句的 WHERE 子句 中的字符,用于执行操作,例如:比较和算术运算. 这些运算符用于指定 SQL 语句中的条件,并用作语句中多个条件的连词. 常见运算符有 ...

  7. nrf52——DFU升级OTA升级方式详解(基于SDK开发例程)

    在我们开始前,默认你已经安装好了一些基础工具,如nrfutil,如果你没有安装过请根据官方中文博客去安装好这些基础工具,连接如下:Nordic nRF5 SDK开发环境搭建(nRF51/nRF52芯片 ...

  8. 九、Django3的ASGI

    九.Django3的ASGI 9.1.Web应用程序和web服务器 Web应用程序(Web)是一种能完成web业务逻辑,能让用户基于web浏览器访问的应用程序,它可以是一个实现http请求和响应功能的 ...

  9. 部署redis-cluster

    1.环境准备 ☆ 每个Redis 节点采用相同的相同的Redis版本.相同的密码.硬件配置 ☆ 所有Redis服务器必须没有任何数据 #所有主从节点执行: [root@ubuntu2004 ~]#ba ...

  10. Aspose.Cell和NPOI生成Excel文件

    1.使用Aspose.Cell生成Excel文件,Aspose.Cell是.NET组件控件,不依赖COM组件 1首先一点需要使用新建好的空Excel文件做模板,否则容易产生一个多出的警告Sheet 1 ...