本文索引:

需求

我们在显示一些模态对话框的时候,往往需要将对话框的背景颜色调暗以达到突出当前对话框的效果,例如:
![突出对话框](https://img2018.cnblogs.com/blog/1434464/201901/1434464-20190114154416771-1203159655.png)
对话框的父窗口除了标题栏以外的部分都变暗了,在父窗口的对比下对话框的显示效果就得到了强调。

这种设计多见于web页面,当用户点击诸如购买之类的按钮后页面会弹出一个购物清单确认对话框,并将对话框以外的内容用类似图中的效果处理,使用户可以将注意力集中在对话框本身。

今天我们也将使用Qt来实现这一效果。

原理

在介绍具体做法前我想先介绍一点预备知识——“亮盒效果”。这是一个摄影技术的名词,大意是指将背景暗化以便突出照片的主体,因为往往使用一个黑色的“盒子”来罩住需要拍摄的主体,所以被称为亮盒。而这与我们想实现的效果不谋而合。

所以想要实现让对话框的父窗口变暗的效果,最常见的手段就是使用一个半透明遮罩控件将父窗口组件整个遮住。

可能有人会问,既然只需要将背景暗化,那为何不直接修改父窗口的QSS,而要使用一个遮罩组件呢?原因也很简单,因为父控件的background属性是少数几个能被子控件继承的属性,当我们修改了父窗口的QSS那么我们的对话框也将不可避免的遭受影响,虽然可以使用setStyleSheet('')去除这些额外的影响,但是这样做将会引入许多不必要的复杂性,显然是与我们的设计初衷相违背的。

所以我们选择使用遮罩控件。回顾一下QWidget的特性,当除了QDialog以外的控件设置了非None的parent时,该控件就会绘制在parent控件上。布局管理器只是帮助我们设置了parent并自动指定了一个合适的位置和尺寸来绘制控件,所以我们完全可以自己指定控件的大小和需要绘制的区域。

绘制区域使用的是QWidget的逻辑坐标。与painter使用的坐标系统一致。所以我们只需要设置遮罩组件的parent为父窗口,然后获取父窗口的高度和宽度,并设置遮罩组件的大小与父窗口一致,最后从父窗口逻辑坐标系的(0, 0)出开始绘制控件即可保证遮罩控件可以完整的遮盖住父窗口实现遮罩效果。

注意,如果子控件的绘制区域或者大小超过了父控件,超过的部分将会被截断,也就是说不会显示出来。不过不用担心,Qt为我们提供了geometrysetGeometry接口,通过它们就可以方便的控制widgets的形状和位置而不用担心出错。

下面就让我们看一下python3实现的遮罩控件。

实现遮罩控件

先看代码:
```python
class MaskWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlag(Qt.FramelessWindowHint, True)
self.setAttribute(Qt.WA_StyledBackground)
self.setStyleSheet('background:rgba(0,0,0,102);')
self.setAttribute(Qt.WA_DeleteOnClose)

def show(self):
"""重写show,设置遮罩大小与parent一致
"""
if self.parent() is None:
return parent_rect = self.parent().geometry()
self.setGeometry(0, 0, parent_rect.width(), parent_rect.height())
super().show()
遮罩控件的实现相当简单,只需要注意一些细节。

遮罩控件的初始化和普通的自定义控件的过程一样,不过需要注意的是`self.setAttribute(Qt.WA_StyledBackground)`这一行,自定义控件只有设置该属性后才能正常设置背景。

随后我们还设置了无边框窗口和deleteOnClose,遮罩不需要显示任何边框,不过这里的deleteOnClose可以不用设置,因为python使用的pyqt可以完美地配合gc,当控件不在被使用时可以自动释放资源,不过我还是养成了显示释放的习惯,明确对资源的处理永远都不是坏事。

第一个重点在于那句QSS。QSS中也可以设置rgba颜色,不过与css相比有一些区别。最后的alpha参数,css中通常是0-1的实数或者一个百分数,而在QSS中它是一个0-255的整数值,而我们想要实现半透明的黑色遮罩,就需要指定控件背景色透明度为40%,也就是`255 * 0.4 = 102`,最终的结果就是`rgba(255, 0, 0, 102)`,设置完成后控件就拥有了半透明效果。

第二个重点在重写的`show`方法上。光设置了颜色和透明度还不够,我们还要让控件正确地遮盖住parent。为了达到这一目的,我们先获取parent的geometry,然后使用`self.setGeometry(0, 0, parent_rect.width(), parent_rect.height())`将控件设置到与parent重合(原理参考上一节内容)。而如果我们没有给控件设置parent,那么控件什么也不会做,因为控件本身需要依赖于parent,如果没有的话也就没法正常显示了。之后再使用`QWidget.show()`就可以显示我们的遮罩效果了。

<h2 id="using">遮罩的使用</h2>
使用遮罩也相当简单:
```python
class MyWidget(QWidget):
"""测试遮罩的显示效果
"""
def __init__(self):
super().__init__()
# 设置白色背景,方便显示出遮罩
self.setStyleSheet('background:white;')
main_layout = QVBoxLayout()
button = QPushButton('点击显示对话框')
button.clicked.connect(self.show_dialog)
main_layout.addStretch(5)
main_layout.addWidget(button, 1, Qt.AlignCenter)
self.setLayout(main_layout)
self.show() def show_dialog(self):
dialog = QDialog(self)
dialog.setModal(True)
dialog_layout = QVBoxLayout()
dialog_layout.addWidget(QLabel('<font color="red">mask test</font>'))
dialog.setLayout(dialog_layout)
mask = MaskWidget(self)
mask.show()
dialog.exec()
mask.close() if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.show()
app.exec_()

遮罩的使用分为如下个步骤:

  1. 根据需要遮盖的控件创建MaskWidget
  2. 显示遮罩
  3. 在模态对话框关闭后调用close()清除遮罩

之所以要在对话框显示之前先显示遮罩,是因为显示模态对话框后父窗口的事件循环被阻塞,这时所有对父窗口的操作都是被阻塞的,而对话框关闭后遮罩就被close了,父窗口的事件循环会将多次绘制事件智能的合并,所以遮罩可能根本不会被显示出来,因此我们必须在对话框前显示遮罩。(如果你好奇的话可以把两行代码的顺序对调,看看是否能正常显示遮罩控件)

这样我们的遮罩控件就完成了,运行程序:

Qt实现半透明遮罩效果的更多相关文章

  1. jquery 半透明遮罩效果 小结

    最近偏离学术的道路越来越远了!! 今天要小结的是实现一个半透明遮罩效果.点击页面上的一个按钮,立即在屏幕的正中央显示某个部件,并且在这个部件之外的区域像是蒙上了一层半透明的遮罩.点击遮罩区域,该正中央 ...

  2. JS+CSS实现弹出全屏灰黑色透明遮罩效果的方法

    本文实例讲述了js+CSS实现弹出一个全屏灰黑色透明遮罩效果的方法.分享给大家供大家参考.具体分析如下: 在众多的网站都有这样的效果,当进行一定的操作之后,会弹出一个灰黑色的半透明的遮罩,在上面可以操 ...

  3. CSS遮罩效果和毛玻璃效果

    前面的话 本文将详细介绍CSS遮罩效果和毛玻璃效果 遮罩效果 普通遮罩 一般地,处理全屏遮罩的方法是使用额外标签 <style>.overlay{ position:fixed; top: ...

  4. C# Winform 实现自定义半透明遮罩层介绍

    在网页中通过div+css实现半透明效果不难,今天我们看看一种在winfrom中实现的方法: 效果图如下,正常时: 显示遮罩层时: 自定义遮罩层控件的源码如下: View Row Code 1 usi ...

  5. LPL Ban/Pick 选人阶段的遮罩效果是如何实现的?

    最近 S11 LPL 春季赛开赛,在看比赛的过程中,我发现新赛季的 Ban/Pick 选人阶段,出现了一种新的,有意思的遮罩效果,如下图所示: 当然,它是一个动态的效果,当选人的过程中,会有一种呼吸的 ...

  6. [读码][js,css3]能感知鼠标方向的图片遮罩效果

    效果图: 无意间看到过去流行的一个效果:[能感知鼠标方向的图片遮罩效果]近来不忙,就仔细的看了一看看到后来发现,网上有好多版本,谁是原著者似乎已经无法考证.读码就要读比较全面的,读像是原著的代码.代码 ...

  7. 【原】使用Xfermode正确的绘制出遮罩效果

    以前写as3的时候,遮罩效果一个mask属性就搞定了,真是方便. 转到android上以后,发现要实现类似的效果,可以使用Xfermode,android一共提供了三种: AvoidXfermode; ...

  8. ext.ajax.request请求时带有遮罩效果

    ajax请求时有时需要操作大量的数据,反应有时会很慢,这时我们想要来一个遮罩效果,具体步骤如下 1.定义一个遮罩 var myMask = new Ext.LoadMask(Ext.getBody() ...

  9. jquery制作弹出层带遮罩效果,点击阴影部分层消失

    jquery制作弹出层带遮罩效果,点击阴影部分层消失. 整体还是比较简单的. HTML代码很简单 <a href="#" class="big-link" ...

随机推荐

  1. 在 Vim 中优雅地查找和替换(转)

    总有人问我 Vim 中能不能查找,当然能!而且是超级强的查找! 这篇文章来详细介绍 Vim 中查找相关的设置和使用方法. 包括查找与替换.查找光标所在词.高亮前景/背景色.切换高亮状态.大小写敏感查找 ...

  2. docker 设计原理

    自从上次更新博客截至目前已经8个多月之久,在这大半年里面,我自己经历了好多,换了工作,换了定位,从之前的小运维,到现在负责整个运维部的工作,需要自己协调的事情更多了,最大的成长是可以通过自己的见解对公 ...

  3. Python函数式编程之闭包

    -------------------------函数式编程之*******闭包------------------------ Note: 一:简介 函数式编程不是程序必须要的,但是对于简化程序有很 ...

  4. 【react】---手动封装一个简易版的redux

    export let createStore = (reducer)=>{ //定义默认的state let state; //定义默认的action let actionTypes = &qu ...

  5. 构建一个 预装 pm2 的 node 项目 docker 底包

    Dockerfile: 创建 dockerfile 文件, 命名为 dockerfile-yourProject-node.8.12.0-pm2 # MAGE: yourGroup/yourProje ...

  6. 前端基础之BOM和DOM

    关于网页交互:BOM和DOM javaScript分为ECMAScript,DOM,BOM . BOM(Browser  object  Model)是指浏览器对象模型,它使JavaScript有能力 ...

  7. 别以为真懂Openstack: 虚拟机创建的50个步骤和100个知识点(5)

    八.KVM 这一步,像virsh start命令一样,将虚拟机启动起来了.虚拟机启动之后,还有很多的步骤需要完成. 步骤38:从DHCP Server获取IP 有时候往往数据库里面,VM已经有了IP, ...

  8. 如何在Linux下查看版本信息

    Linux下如何查看版本信息, 包括位数.版本信息以及CPU内核信息.CPU具体型号等等,整个CPU信息一目了然.   1.# uname -a   (Linux查看版本当前操作系统内核信息)   L ...

  9. Array.find()和Array.findIndex()

    ES6新增的两个方法,根据回调函数返回作为判断依据,按照数组顺序进行遍历,符合条件(为真)时find()返回该值.findIndex()返回下标. 1.语法 arr.find(callback[, t ...

  10. [Swift]LeetCode12. 整数转罗马数字 | Integer to Roman

    Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. Symbol Value I 1 ...