有些情况下,我们需要实现不规则窗口,比如在软件的加载窗口,可以使用背景透明的图片填充窗口,从而创建炫酷的不规则窗口。下面介绍使用背景透明图片设计不规则窗口以及实现鼠标拖拽不规则窗口的方法。
首先,我们需要创建一个C++类(),这个类继承于QWidget(当然也可以使用设计师界面类,具体看个人需求),接下来的代码均是基于这个派生类。

1、设置窗口无边框且背景透明

首先,设置窗口的属性:

MyWidget::MyWidget(QWidget *parent): QWidget(parent)
{//设置窗口风格为无边框this->setWindowFlag(Qt::FramelessWindowHint);//设置背景为透明()this->setAttribute(Qt::WA_TranslucentBackground);
}

2、使用背景透明图片填充窗口

首先,需要重写QPaintEvent()事件,用于绘制窗口背景,将其写在类的保护成员里:

//声明(mywidget.h中)
protected://绘图事件void paintEvent(QPaintEvent*);
//定义(mywidget.cpp中)
void MyWidget::paintEvent(QPaintEvent* )
{QPainter p(this);p.drawPixmap(0,0,this->width(),this->height(),QPixmap("E:\image\sunny.png"));
}

其中,图片sunny.png是一张背景透明的图片而不是说图片的背景为白色。如下图所示:

而我们使用
QWidget::setAttribute(Qt::WA_TranslucentBackground);
设置窗口为背景透明则指的是窗口开启了阿尔法通道,关于Qt::WA_TranslucentBackground属性,QT文档的官方文档的说明如下:


Qt::WA_TranslucentBackground 120
Indicates that the widget should have a translucent background, i.e., any non-opaque regions of the widgets will be translucent because the widget will have an alpha channel. Setting this flag causes WA_NoSystemBackground to be set. On Windows the widget also needs the Qt::FramelessWindowHint window flag to be set. This flag is set or cleared by the widget’s author.


到目前为止,我们已经完成了不规则窗口的创建,运行程序可以看到,显示效果如下所示:

但是此时的窗口还无法拖动,接下来从数学上证明如何拖动窗口,并给出现相关代码。

3、鼠标按下拖拽不规则窗口证明

上面的窗口已经做到了无边框且背景透明,但是正是由于无边框,我们无法拖动也无法关闭它(除非在任务栏右键关闭),下面通过重写鼠标按下事件使得窗口可拖动。先说明证明窗口拖动的原理。

3.1 鼠标移动与鼠标追踪

鼠标追踪是相对于mouseMoveEvent()所属的父组件而言,比如我们在MyWidget中声明了一个鼠标移动事件MyWidget::mouseMoveEvent(),那么鼠标追踪就是指的当鼠标移动进此窗口时,是否会被追踪。
关闭鼠标追踪: 鼠标如果没有按下,则在MyWidget移动时不会触发鼠标点击事件(即不会调用mouseMoveEvent()),鼠标追踪是默认关闭的。
开启鼠标追踪: 鼠标只要在窗口内移动,就会触发鼠标移动事件。
开启方式是在窗口的构造函数中使QWidget::setMouseTracking()参数为true,即:

    //关闭鼠标追踪this->setMouseTracking(false);//开启鼠标追踪this->setMouseTracking(true);

如果开启了鼠标追踪,则只要鼠标在窗口内移动,就会触发mouseMoveEvent(),而我们希望只是在鼠标按下的时候才触发鼠标移动事件,因此我们选择将此窗口的鼠标追踪关闭:

    //关闭鼠标追踪this->setMouseTracking(false);

3.2 窗口移动原理以证明

对于桌面系统,其坐标系如下图所示:

上图中,绿色区域表示电脑桌面,蓝色区域表示被背景透明的图片填充的窗口,A点为鼠标按下的点,B点为鼠标按下并移动后的点(注意A点相对于窗口的位置是没有的变的,鼠标从A点移动到B指的是相对桌面从A点移动到B点)。
我们不设置鼠标追踪,保证只有在鼠标按下的时候移动才会触发鼠标移动事件。
●首先在鼠标按下事件mousePressEvent()中获取到鼠标按下位置相对于窗口左上角的位置A:QPoint mousePositon
●假设鼠标按下后移动到桌面B点,那么我们需要做的是将原窗口从相对桌面的A点移动到相对桌面的B点。而要移动窗口,需要一个函数:
QWidget::move(QPoint newPosition)
这个函数的作用是:以窗口的左上角顶点坐标为参考点,将窗口移动到newPositon点,move()函数使用的坐标系统是桌面坐标系,即全局坐标系。
所以我们需要将鼠标的位置量转换为窗口左上角顶点的位移。
下面做一个推导:
假设鼠标按下并从桌面A移动到桌面B点,则我们希望鼠标拖动从A移动到B时,窗口也能按相同的轨迹移动到B,我们用GB(global)代表全局坐标,用LC(local)代表局部坐标则,窗口左上角的全局坐标为O,则:
窗口移动到A点时左上角全局坐标:(XW1GB,YW1GB)\left( X_{{W1}_{GB}}, Y_{{W1}_{GB}} \right)(XW1GB​​,YW1GB​​)
点A的本地坐标:(xALC,yALC)\left( x_{A_{LC}}, y_{A_{LC}} \right)(xALC​​,yALC​​)
点A的全局坐标为:
(xAGB,yAGB)=(XWGB,YWGB)+(xALC,yALC)=(XWGB+xALC,YWGB+yALC)\left( {x_{A_{GB}},y_{A_{GB}}} \right) = \left( {X_{W_{GB}},Y_{W_{GB}}} \right) + \left( {x_{A_{LC}},y_{A_{LC}}} \right) = \left( {X_{W_{GB}} + x_{A_{LC}},Y_{W_{GB}} + y_{A_{LC}}} \right) (xAGB​​,yAGB​​)=(XWGB​​,YWGB​​)+(xALC​​,yALC​​)=(XWGB​​+xALC​​,YWGB​​+yALC​​)
点B的全局坐标为:(xBGB,yBGB)\left( {x_{B_{GB}},y_{B_{GB}}} \right)(xBGB​​,yBGB​​)
设窗口移动到B点时左上角全局坐标:XW2GB,YW2GB)\left. X_{{W2}_{GB}},Y_{{W2}_{GB}} \right)XW2GB​​,YW2GB​​)
由于点B的本地坐标没有变,即为点A的本地坐标。
点B的本地坐标为:(xALC,yALC)\left( {x_{A_{LC}},y_{A_{LC}}} \right)(xALC​​,yALC​​)
则点B的全局坐标可以表示为:
(xBGB,yBGB)=(XW2GB,YW2GB)+(xALC,yALC)\left( {x_{B_{GB}},y_{B_{GB}}} \right) = \left( {X_{{W2}_{GB}},Y_{{W2}_{GB}}} \right) + \left( {x_{A_{LC}},y_{A_{LC}}} \right) (xBGB​​,yBGB​​)=(XW2GB​​,YW2GB​​)+(xALC​​,yALC​​)
从而,当窗口移动到点B时其左上角的坐标为:
(XW2GB,YW2GB)=(xBGB,yBGB)−(xALC,yALC)\left( {X_{{W2}_{GB}},Y_{{W2}_{GB}}} \right) = \left( {x_{B_{GB}},y_{B_{GB}}} \right) - \left( {x_{A_{LC}},y_{A_{LC}}} \right) (XW2GB​​,YW2GB​​)=(xBGB​​,yBGB​​)−(xALC​​,yALC​​)
而这个 (XW2GB,YW2GB)\left( {X_{{W2}_{GB}},Y_{{W2}_{GB}}} \right)(XW2GB​​,YW2GB​​) 正是我们move()函数需要的参数。
实际上,我们可以使用向量可以反映这个问题的本质,下面用向量来说明窗口移动原理:

其中,O(0,0)为电脑桌面零点,W1为鼠标在A点时的窗口左上角全局坐标,W2为鼠标在B点时的窗口左上角全局坐标,A为鼠标位于点A时的全局坐标,B为鼠标位于B点时全局坐标,则move()函数需要的量为W2的坐标,下满进行推导:
由于鼠标按下的点相对窗口的位置不变,所以W和A的相对位置不会变,即:

由于O点的坐标坐标为(0,0),W1点的局部坐标为(0,0),所以上式表明:
窗口新位置的全局坐标W2=鼠标新位置的全局坐标B – 鼠标原位置的全局坐标A
至此,已经说明了如何进行窗口移动。

4、完整代码

下面附上完整代码,供大家参考:

//mywidget.h 头文件
#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget>class MyWidget : public QWidget
{Q_OBJECTpublic:MyWidget(QWidget *parent = nullptr);~MyWidget();
protected://绘图事件void paintEvent(QPaintEvent*);//鼠标按下事件void mousePressEvent(QMouseEvent* e);//鼠标移动事件void mouseMoveEvent(QMouseEvent* e);
private:QPoint mouseLocalPos;
};
#endif // MYWIDGET_H
//mywidget.cpp 源文件
#include "mywidget.h"
#include<QPainter>
#include<QPixmap>
#include<QMouseEvent>
#include<QDebug>MyWidget::MyWidget(QWidget *parent): QWidget(parent)
{this->setWindowFlag(Qt::FramelessWindowHint);//设置背景为透明this->setAttribute(Qt::WA_TranslucentBackground);//关闭鼠标追踪(默认为关闭)this->setMouseTracking(false);
}MyWidget::~MyWidget()
{}void MyWidget::paintEvent(QPaintEvent* )
{QPainter p(this);p.drawPixmap(0,0,this->width(),this->height(),QPixmap("E:/image/sunny.png"));
}void MyWidget::mousePressEvent(QMouseEvent* e)
{//如果是右键,关闭窗口if(e->button()==Qt::RightButton){this->close();}//如果是左键,拖动窗口if(e->button()==Qt::LeftButton){//求坐标差值//鼠标相对窗口左上角的位置=当前坐标(相对屏幕)-窗口左上角坐标(相对屏幕)mouseLocalPos=e->pos();//或者//mouseLocalPos=e->globalPos()-this->frameGeometry().topLeft();qDebug()<<"点击本地坐标:("<<mouseLocalPos.x()<<","<<mouseLocalPos.y()<<")";}
}//鼠标追踪默认是关闭的,只有在鼠标按下且移动的时候才会触发鼠标移动事件
void MyWidget::mouseMoveEvent(QMouseEvent* e)
{QPoint newLeftPos;  //窗口移动距离newLeftPos=e->globalPos()-mouseLocalPos;this->move(newLeftPos);qDebug()<<"窗口左上角全局坐标:("<<newLeftPos.x()<<","<<newLeftPos.y()<<")";
}
//main()函数
#include "mywidget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MyWidget w;w.show();return a.exec();
}

QT不规则窗口的移动原理和证明/QT窗口背景透明(附代码实现)相关推荐

  1. ctr 平滑_CTR平滑的原理,包懂!!!附代码

    为什么需要平滑? 某个物品CTR(click-Through-Rate)定义为"物品被点击的概率".CTR是某个物品在其他条件保持不变下自身的属性.但是概率我们不好确定,能确定的是 ...

  2. Qt 事件机制,底层实现原理

    [1]事件 事件是可以被控件识别的操作.如按下确定按钮.选择某个单选按钮或复选框. 每种控件有自己可识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件等等. 事件就是用户对窗口 ...

  3. 深入探索 Qt WebEngineCore:从基础原理到高级应用与技巧

    深入探索 Qt WebEngineCore:从基础原理到高级应用与技巧 Diving into Qt WebEngineCore: From Basic Principles to Advanced ...

  4. Python Qt GUI设计:QMainWindow、QWidget和QDialog窗口类(基础篇—10)

    目录 1. QMainWindow窗口 2.QWidget窗口 3.QDialog窗口 3.1.QMessageBox窗口 3.2.QInputDialog窗口 3.3.QFontDialog窗口 3 ...

  5. 【Qt】在Qt中使用opencv,不要使用opencv创建窗口

    问题描述 在ubuntu14.04.5 Qt5.6中使用opencv创建窗口显示摄像头时,报错: (:1103): Gtk-WARNING **: gtk_disable_setlocale() mu ...

  6. Qt中消息的机制原理

    参考文章:https://blog.csdn.net/perfectguyipeng/article/details/78082360 原理基础介绍 Qt中通过object类定义了connect函数, ...

  7. QT学习笔记(六):Qt5主窗口框架示例

    QT学习笔记(五):Qt5主窗口框架代码示例 一.添加编辑菜单:并在下拉菜单和工具栏中添加"打开文件"动作菜单 #include <QToolButton> #incl ...

  8. 【QT】QT从零入门教程(七):图像适应窗口

      在第5节里,我们讲解了初始化图像框QLabel的方法,用于显示图像.在第6节里,运用QDockWidget实现了基本窗口布局.在显示图像时,如果打开比QLabel大的图像,会自动出现滚动条.这样能 ...

  9. 【QT】QT从零入门教程(六):QDockWidget停靠窗口

    QDockWidget   上节我们引出了QDockWidget的概念,这节进行讲解并加以引用.   常用函数:   1.addDockWidget:添加停靠控件,用于指定或更改停靠控件的位置以及方向 ...

最新文章

  1. Spark任务调度流程及调度策略分析
  2. 【机器学习】机器学习实践中的 7 种常见错误
  3. TTL传输中过期的解决办法
  4. java获取正在执行的timer_Java线程与并行编程(一)
  5. Flask redirect
  6. python语言源程序文件类型_浅谈Python的文件类型
  7. 许可证编译器 (Lc.exe)
  8. pandas python groupby_python – pandas groupby方法实际上是如何工作的?
  9. BZOJ2286 [Sdoi2011]消耗战 【虚树 + 树形Dp】
  10. 实用软件工程复习课重点
  11. 数学建模之向量自回归模型
  12. Python数据处理 | 批量提取文件夹下的csv文件,每个csv文件根据列索引提取特定几列,并将提取后的数据保存到新建的一个文件夹
  13. MVVM框架理解及其原理实现
  14. Kopernio插件+SCI-HUB最新可用网址
  15. 洛谷-P1428-小鱼比可爱
  16. 小米手机android目录在哪里设置字体,在哪里修改小米手机的字体大小
  17. 任务计划服务程序在哪里
  18. android实现多画面播放,快手同框怎么弄?怎样制作多个视频同框?安卓手机上制作三个不同的视频同框播放...
  19. 云栖大会 mysql_2019云栖大会 数据库
  20. 股票移动平均线matlab,matlab – 计算移动平均线

热门文章

  1. c# winform调用POS热敏打印机打印小票
  2. 为什么计算机连不上无线网络,为什么无线网络连接上却不能上网,教您电脑连上无线网却不能上网怎么解决...
  3. chage(charger)
  4. (十)学生课程表查询
  5. 调试winddows程序(windbg 和 Debug Diagnostic Tool)
  6. Echarts实现省级地图的两种方法(以浙江省为例)
  7. J-LinK-OB改造版 仿真/调试器 使用说明
  8. Python 量化金融库最全汇总!
  9. 如何基于微信开放接口开发企业的微信CRM
  10. 最全的Go资料汇总和最走心的学Go建议