Я обновляю этот ответ, так как вопрос был частично очищен в комментариях, но я оставлю исходный ответ в конце, так как он все еще может быть полезен.
Кроме того, концепция на основа остается прежней: к объекту применяется графический эффект, который изменяет внешний вид этого объекта , а не внешний вид объектов. Если вы хотите применить этот эффект к нескольким объектам, они должны быть children от общего родителя, и эффект должен быть установлен для этого parent, но для всего, что ниже этот родитель (а не его дочерний элемент) будет только частично затронут результатом эффекта.
Представьте эффект размытия как фильтр, применяемый к реальной фотографии, которая частично прозрачный: в то время как изображение на фотографии размыто, то, что вы видите за ним, будет не размыто.
Подкласс графического эффекта
QGraphicsEffects не обеспечивает возможность ограничивать степень их обработки, так как они обычно изменяют весь «ограничивающий прямоугольник» объекта, для которого они установлены.
Для этого необходимо создать подклассы, а draw()
метод должен быть переопределен, поскольку именно он отвечает за фактическое рисование.
Я собираюсь предположить, что весь интерфейс будет затронут эффект в некотором роде: даже если некоторые объекты находятся «вне» прямоугольника эффекта, они все еще являются частью одного и того же родителя, поэтому мы собираемся сделать следующее:
- create основной виджет, который выступает в качестве контейнера для интерфейса full
- , добавьте основной макет для интерфейса main (тот, который обычно отображается)
- create вспомогательный виджет, который содержит интерфейс main , установите для него макет и добавьте в него все, что вам нужно
- установите эффект подкласса графики для виджета sub
- создать виджет для меню, у которого основной виджет является родительским, поэтому он не будет частью основного макета; он будет иметь свой собственный макет со своими кнопками, надписями и т. д. c.
- добавить систему, которая изменяет графический эффект в соответствии с геометрией меню, и всякий раз, когда это изменяется, эффект будет применен к только эта геометрия
class BlurEffect(QtWidgets.QGraphicsBlurEffect):
effectRect = None
def setEffectRect(self, rect):
self.effectRect = rect
self.update()
def draw(self, qp):
if self.effectRect is None or self.effectRect.isNull():
# no valid effect rect to be used, use the default implementation
super().draw(qp)
else:
qp.save()
# clip the drawing so that it's restricted to the effectRect
qp.setClipRect(self.effectRect)
# 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
fullRegion -= QtGui.QRegion(self.effectRect)
qp.setClipRegion(fullRegion)
# draw the *source*, which has no effect applied
self.drawSource(qp)
qp.restore()
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
background = QtGui.QPixmap('background.png')
# apply a background to this widget, note that this only serves for the
# graphics effect to know what's outside the boundaries
p = self.palette()
p.setBrush(p.Window, QtGui.QBrush(background))
self.setPalette(p)
self.resize(background.size())
# this layout is only for the child "sub" widget
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.setContentsMargins(0, 0, 0, 0)
# the "sub" widget, that contains the main interface
self.subWidget = QtWidgets.QWidget()
mainLayout.addWidget(self.subWidget)
# set the background for the subwidget; note that we can't use setPalette()
# because palette and fonts are inherited by children; using ".QWidget"
# we ensure that the background is only applied to the subwidget
self.subWidget.setStyleSheet('''
.QWidget {
background-image: url(background.png);
}
''')
# some random widgets
subLayout = QtWidgets.QGridLayout(self.subWidget)
for row in range(3):
for col in range(3):
btn = QtWidgets.QPushButton()
subLayout.addWidget(btn, row, col)
btn.setText('Open menu')
btn.setFocus()
btn.clicked.connect(self.openMenu)
# create an instance of our effect subclass, and apply it to the subwidget
self.effect = BlurEffect()
self.subWidget.setGraphicsEffect(self.effect)
self.effect.setEnabled(False)
self.effect.setBlurRadius(10)
# create the menu container, that *HAS* to have this main widget as parent
self.topMenu = QtWidgets.QWidget(self)
self.topMenu.setVisible(False)
self.topMenu.setFixedWidth(200)
# move the menu outside the window left margin
self.topMenu.move(-self.topMenu.width(), 0)
menuLayout = QtWidgets.QVBoxLayout(self.topMenu)
menuLayout.addSpacing(20)
for b in range(4):
btn = QtWidgets.QPushButton('Button {}'.format(b + 1))
menuLayout.addWidget(btn)
menuLayout.addSpacing(10)
closeButton = QtWidgets.QPushButton('Close menu')
menuLayout.addWidget(closeButton)
closeButton.clicked.connect(self.closeMenu)
# a stretch to ensure that the items are always aligned on top
menuLayout.addStretch(1)
# an animation that will move the menu laterally
self.menuAnimation = QtCore.QVariantAnimation()
self.menuAnimation.setDuration(500)
self.menuAnimation.setEasingCurve(QtCore.QEasingCurve.OutQuart)
self.menuAnimation.setStartValue(-self.topMenu.width())
self.menuAnimation.setEndValue(0)
self.menuAnimation.valueChanged.connect(self.resizeMenu)
self.menuAnimation.finished.connect(self.animationFinished)
# a simple transparent widget that is used to hide the menu when
# clicking outside it; the event filter is to capture click events
# it may receive
self.clickGrabber = QtWidgets.QWidget(self)
self.clickGrabber.installEventFilter(self)
self.clickGrabber.setVisible(False)
def resizeMenu(self, value):
# move the menu and set its geometry to the effect
self.topMenu.move(value, 0)
self.effect.setEffectRect(self.topMenu.geometry())
def openMenu(self):
if self.topMenu.x() >= 0:
# the menu is already visible
return
# ensure that the menu starts hidden (that is, with its right border
# aligned to the left of the main widget)
self.topMenu.move(-self.topMenu.width(), 0)
self.topMenu.setVisible(True)
self.topMenu.setFocus()
# enable the effect, set the forward direction for the animation, and
# start it; it's important to set the effect rectangle here too, otherwise
# some flickering might show at the beginning
self.effect.setEffectRect(self.topMenu.geometry())
self.effect.setEnabled(True)
self.menuAnimation.setDirection(QtCore.QVariantAnimation.Forward)
self.menuAnimation.start()
# "show" the grabber (it's invisible, but it's there) and resize it
# to cover the whole window area
self.clickGrabber.setGeometry(self.rect())
self.clickGrabber.setVisible(True)
# ensure that it is stacked under the menu and above everything else
self.clickGrabber.stackUnder(self.topMenu)
def closeMenu(self):
# in case that the menu has changed its size, set again the "start" value
# to its negative width, then set the animation direction to backwards
# and start it
self.menuAnimation.setStartValue(-self.topMenu.width())
self.menuAnimation.setDirection(QtCore.QVariantAnimation.Backward)
self.menuAnimation.start()
# hide the click grabber
self.clickGrabber.setVisible(False)
def animationFinished(self):
# if the animation has ended and the direction was backwards it means that
# the menu has been closed, hide it and disable the effect
if self.menuAnimation.direction() == QtCore.QVariantAnimation.Backward:
self.topMenu.hide()
self.effect.setEnabled(False)
def focusNextPrevChild(self, next):
if self.topMenu.isVisible():
# a small hack to prevent tab giving focus to widgets when the
# menu is visible
return False
return super().focusNextPrevChild(next)
def eventFilter(self, source, event):
if source == self.clickGrabber and event.type() == QtCore.QEvent.MouseButtonPress:
# the grabber has been clicked, close the menu
self.closeMenu()
return super().eventFilter(source, event)
def resizeEvent(self, event):
super().resizeEvent(event)
# always set the menu height to that of the window
self.topMenu.setFixedHeight(self.height())
# resize the grabber to the window rectangle, even if it's invisible
self.clickGrabber.setGeometry(self.rect())
if self.topMenu.isVisible():
# resize the effect rectangle
self.effect.setEffectRect(self.topMenu.geometry())
Предыдущий ответ
Поскольку вы хотите применить эффект к лежащие в основе объекты, я считаю, что решение состоит в том, чтобы использовать «контейнер» для встраивания их, а затем применить к нему эффект размытия. То же самое можно применить и к QGraphicsBlurWidget.
ApplicationWindow {
property var theme: String("#ffffff")
property var focusColor: String('transparent')
id: applicationWindow
visible: false
width: 600
height:600
<b>Rectangle {
id: container
anchors.fill: parent</b>
Image {
id: image_bug
anchors.fill: parent
source: "im.png"
}
Rectangle {
width: 100; height: 600
color: "green"
Text {
id: helloText
text: "Hello world!"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: 10; font.bold: true
}
MouseArea {
anchors.fill: parent
onClicked: { effectSource.width = 1200; effectSource.height = 1200;}
}
}
<b>}</b>
ShaderEffectSource {
id: effectSource
sourceItem: <b>container</b>
anchors.centerIn: image_bug
width: 300
height: 300
sourceRect: Qt.rect(x,y, width, height)
}
FastBlur{
id: blur
anchors.fill: effectSource
source: effectSource
radius: 100
}
}