简体   繁体   English

使用 PyQt5,如何在布局的矩形区域添加阴影效果

[英]With PyQt5, how to add drop shadow effect on the rectangle area of a layout

I'm trying to implement a grid of images with titles under them, with a drop shadow on hover.我正在尝试实现一个带有标题的图像网格,在 hover 上有一个阴影。 What I've done so far is to add a drop shadow on the two widgets (the label with the image, and the label with the title), but I would like to have a drop shadow on the rectangular area that contains them.到目前为止,我所做的是在两个小部件上添加阴影(带有图像的 label 和带有标题的 label),但我想在包含它们的矩形区域上添加阴影。 It tried to put them on another widget and apply the effect on this widget, but it still applies to both labels.它试图将它们放在另一个小部件上并将效果应用到这个小部件上,但它仍然适用于两个标签。 Code below.代码如下。

import sys, os
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *



class App(QMainWindow):
    def __init__(self):
        super().__init__()
        self.title = 'PyQt5 layout - pythonspot.com'
        self.left = 100
        self.top = 100
        self.width = 800
        self.height = 600
        
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        content_widget = QWidget()
        self.setCentralWidget(content_widget)
        self._lay = QGridLayout(content_widget)

        self.shadow = QGraphicsDropShadowEffect(self)
        self.shadow.setBlurRadius(5)
        
        nb = 6
        i = 0
        for i in range(0, 12):
            panel=QWidget()
            vbox = QVBoxLayout()
            pixmap = QPixmap(str(i+1)+"jpg")
            pixmap = pixmap.scaled(100, 150, transformMode=Qt.SmoothTransformation)
            img_label = QLabel(pixmap=pixmap)
            vbox.addWidget(img_label)
            
            txt_label = QLabel(str(i+1))
            vbox.addWidget(txt_label)
            vbox.addStretch(1)
            panel.setLayout(vbox)
            self._lay.addWidget(panel , int(i/nb), i%nb)
            panel.installEventFilter(self)
            i = i+1
            
        self.show()
        
    def eventFilter(self, object, event):
        if event.type() == QEvent.Enter:
            object.setGraphicsEffect(self.shadow)
            self.shadow.setEnabled(True)
        elif event.type() == QEvent.Leave:
            print("Mouse is not over the label")
            self.shadow.setEnabled(False)
        return False

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

The problem comes from the fact that a plain QWidget usually doesn't paint anything on its own: it's just a transparent widget.问题来自一个简单的 QWidget 通常不会自己绘制任何东西的事实:它只是一个透明的小部件。 If you apply a graphics effect, it will be the result of what's inside that widget.如果您应用图形效果,它将是该小部件内部的结果。

The solution is to ensure that the widget is opaque , by calling setAutoFillBackground(True) .解决方案是通过调用setAutoFillBackground(True)来确保小部件不透明

Unfortunately, especially in your case, the result won't be very good, because you've lots of other widgets and a certain amount of spacing between them.不幸的是,特别是在您的情况下,结果不会很好,因为您有很多其他小部件并且它们之间有一定的间距。 You'll end up having the shadow behind everything:你最终会在一切背后都有阴影:

一切背后的阴影

The solution would be to call raise_() whenever the graphics effect is set, in order to ensure that the widget is actually above anything else (among the siblings and subchildren of its parent, obviously).解决方案是在设置图形效果时调用raise_() ,以确保小部件实际上高于其他任何东西(显然,在其父级的兄弟姐妹和子孩子中)。
Unfortunately - again - this has a small but important issue, related to your implementation: the first time the effect is removed from a widget because it's set on another, the surrounding widgets don't get updated correctly.不幸的是 - 再次 - 这有一个小但重要的问题,与您的实现有关:第一次从一个小部件中删除效果,因为它设置在另一个小部件上,周围的小部件没有正确更新。

人工制品

This is mostly due to the optimizations of the paint engine and the implementation of the graphics effect.这主要是由于绘画引擎的优化和图形效果的实现。

To avoid this issue, there are two possibilities:为了避免这个问题,有两种可能:

  1. set an unique graphics effect for each widget, disabled upon creation, and then enable it only on the enterEvent :为每个小部件设置独特的图形效果,在创建时禁用,然后仅在enterEvent上启用它:
class App(QMainWindow):
    def __init__(self):
        # ...
        for i in range(0, 12):
            panel = QWidget()
            effect = QGraphicsDropShadowEffect(panel, enabled=False, blurRadius=5)
            panel.setGraphicsEffect(panel)
            # ...

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Enter and obj.graphicsEffect():
            obj.graphicsEffect().setEnabled(True)
        elif event.type() == QEvent.Leave and obj.graphicsEffect():
            obj.graphicsEffect().setEnabled(False)
        return super().eventFilter(obj, event)
  1. alternatively, get the bounding rect of the graphics effect, translate it to the coordinates of the widget, check if any other "sibling" geometry intersects the bounding rect and eventually call update() on those widgets:或者,获取图形效果的边界矩形,将其转换为小部件的坐标,检查是否有任何其他“兄弟”几何与边界矩形相交,并最终在这些小部件上调用update()
class App(QMainWindow):
    def __init__(self):
        # ...
        self.panels = []
        for i in range(0, 12):
            panel = QWidget()
            self.panels.append(panel)
            # ...

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Enter:
            obj.setGraphicsEffect(self.shadow)
            self.shadow.setEnabled(True)
            obj.raise_()
        elif event.type() == QEvent.Leave:
            obj.graphicsEffect().setEnabled(False)
            rect = self.shadow.boundingRect().toRect()
            rect.translate(obj.geometry().topLeft())
            for other in self.panels:
                if other != obj and other.geometry().intersects(rect):
                    other.update()
        return super().eventFilter(obj, event)

PS: object is a built-in type of Python, you should not use it as a variable. PS: object是 Python 的内置类型,不要把它当成变量。

self.shadow = QGraphicsDropShadowEffect(self)
self.shadow.setBlurRadius(15)
self.shadow.setXOffset(0)
self.shadow.setYOffset(0)
self.shadow.setColor(QColor(0, 0, 0, 150))
# add the shadow object to the frame
self.ui.topframe.raise_()
self.ui.topframe.setGraphicsEffect(self.shadow)

enter image description here在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM