I have this code
import sys # +++
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtWidgets import QApplicat
You can't. Only one effect can be applied at once on a widget (and after that, no effect can be applied on any of its children or parents), at least for QWidgets.
From QWidget.setGraphicsEffect():
If there already is an effect installed on this widget, QWidget will delete the existing effect before installing the new effect.
What happens is that as soon as you apply self.effect2
on subWidget, self.effect
is removed from it and actually deleted. In PyQt terms, it means that the python object still exists, but not its C++ counterpart.
It seems that you still don't understand how a QGraphicsEffect works.
The effect is NOT applied on the widgets you see with the blurred background. It is applied on the underlying widget (subWidget
, in this case), and only on the rectangle(s) specified using the geometries of the widgets. You could even set the effectRect
to any rect you want, even without any other widgets other than subWidget
.
If you need to apply the effect to more than one rectangle, then you should use setClipRegion
and use a composite QRegion with it.
Assuming you will always use QWidgets as a reference for the effect, and that the effect will always be applied to a widget that occupies the whole area of the window, you can use a "watch list" of widgets that need to be tracked, and update the effect whenever their geometry change.
class BlurEffect(QtWidgets.QGraphicsBlurEffect):
shouldEnable = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.watched = []
def watchWidget(self, widget):
widget.installEventFilter(self)
self.watched.append(widget)
def unwatchWidget(self, widget):
if widget in self.watched:
self.watched.remove(widget)
self.update()
def setEnabled(self, enabled):
# in case you want to manually disable the effect, keep track of
# the selected behavior
self.shouldEnable = enabled
super().setEnabled(enabled)
def draw(self, qp):
rects = []
for widget in self.watched:
if widget.isVisible():
rect = widget.rect()
if rect.isNull():
continue
# map the widget geometry to the window
rect.translate(
widget.mapTo(widget.window(), QtCore.QPoint()))
rects.append(rect)
if not self.isEnabled() and self.shouldEnable:
super().setEnabled(True)
if not rects:
# no valid rect to be used, disable the effect if we should
if not self.shouldEnable:
super().setEnabled(False)
# otherwise, keep drawing the source with the effect applied
# to the whole area of the widget
else:
self.drawSource(qp)
else:
qp.save()
# create a region that includes all rects
rectRegion = QtGui.QRegion()
for rect in rects:
rectRegion |= QtGui.QRegion(rect)
# clip the effect painting to the region
qp.setClipRegion(rectRegion)
# call the default implementation, which will draw the effect
super().draw(qp)
# get the full region that should be painted
fullRegion = QtGui.QRegion(qp.viewport())
# and subtract the effect rectangle used before
fullRegion -= rectRegion
qp.setClipRegion(fullRegion)
# draw the *source*, which has no effect applied
self.drawSource(qp)
qp.restore()
def eventFilter(self, source, event):
# update the effect whenever a widget changes its geometry or
# becomes visible
if event.type() in (QtCore.QEvent.Resize, QtCore.QEvent.Move,
QtCore.QEvent.Show) and source.isVisible():
super().setEnabled(True)
self.update()
# if a widget is going to be deleted, remove it from the list
# of watched list; this is **VERY** important
elif event.type() == QtCore.QEvent.DeferredDelete:
self.unwatchWidget(source)
return super().eventFilter(source, event)
Important notes:
watchWidget
for any widget for which you want to see the effect, including the topMenu; again, this doesn't mean that the effect is applied to those widget, but that their geometry is used for that;setEffectRect
anymore;self.effect.setEnabled()
anymore;Finally, I strongly suggest you to carefully study this code (and the previous) and the documentation about both QGraphicsEffect and QPainter (including the clipping section and all the related pages), and create some simple tests and examples by yourself to better understand how they work, before attempting to do what you're trying to achieve.