I'm trying to implement a grid of images with titles under them, with a drop shadow on 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. 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. 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)
.
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).
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:
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)
update()
on those widgets: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.
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)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.