FastBlur 可以模糊它背后的一切吗?

Can FastBlur blur everything behind it?

我有这个代码, qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0
ApplicationWindow {
    property var theme: String("#ffffff")
    property var focusColor: String('transparent')
    id: applicationWindow
    visible: false                                      
    width: 600
    height:600
    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;}
    }
}
    ShaderEffectSource {
        id: effectSource
        sourceItem: image_bug
        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
    }
}

PyQT5 或 Pyside2

import sys
import os                                                            # +++
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QVBoxLayout, QLabel
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtCore import (QCoreApplication, QMetaObject, QObject, QPoint,
    QRect, QSize, QUrl, Qt)
    '''
    from PySide2.QtCore import Qt, QUrl
    from PySide2.QtWidgets import QApplication, QWidget, QMainWindow, QVBoxLayout, QLabel
    from PySide2.QtQml import QQmlApplicationEngine
    from PySide2.QtCore import (QCoreApplication, QMetaObject, QObject, QPoint,
        QRect, QSize, QUrl, Qt)
    '''
DIR_PATH = os.path.dirname(os.path.realpath(__file__))
class GUI_MainWindow(QMainWindow):
    def __init__(self, widget, parent=None):
        QMainWindow.__init__(self, parent)
        self.setWindowTitle('GUI_MainWindow')
        self.resize(600, 600)
        self.widget = widget
        centralWidget = QWidget()
        self.setCentralWidget(centralWidget)    
        self.widget_test_2 = QLabel("<h1>Hello World !</h1>", alignment=Qt.AlignCenter)
        self.widget_test_2.setObjectName(u"widget_test_2")
        self.widget_test_2.setGeometry(QRect(180, 40, 151, 181))
        self.widget_test_2.raise_()
        layout = QVBoxLayout(centralWidget)
        layout.addWidget(self.widget_test_2)
        layout.addWidget(self.widget, stretch=1)#blur box
if __name__ == "__main__":
    myApp = QApplication(sys.argv)
    file = os.path.join(DIR_PATH, "qml_window.qml")                     
    url = QUrl.fromLocalFile(file)                                      
    engine = QQmlApplicationEngine()
    context = engine.rootContext()
    context.setContextProperty("main", engine)
    engine.load(url)
    if not engine.rootObjects():
        sys.exit(-1)
    widget = QWidget.createWindowContainer(engine.rootObjects()[0])
    window = GUI_MainWindow(widget)
    window.show()

    sys.exit(myApp.exec_())     

并且我希望 ShaderEffectSource 模糊其背后的所有内容,

甚至是 PyQt5 或 PySide2 创建的小部件。 移动或停留在原地时

小部件后面的所有内容都应该是模糊的。

我已经尝试为此使用 QGraphicsBlurEffect 效果,但这并没有给我想要的结果。 我希望 FastBlur 可以做到。 如果还有其他选择,请告诉我

我可以吗?

我正在更新这个答案,因为问题已经在评论中被部分清除,但我会在最后留下原来的答案,因为它可能仍然有用。

除此之外,基础概念保持不变:图形效果应用于对象,它修改对象外观,而不是底层对象的显示方式。如果您想将该效果应用于多个对象,它们必须是共同父对象的 children,并且必须为 that 设置效果parent,但是 below 父项(而不是它的子项)的所有内容都只会部分受到 result 效果的影响。
将模糊效果想象成应用于部分透明的真实生活照片的滤镜:虽然照片中的图像是模糊的,但您在其后面看到的内容 不会 模糊。

子类图形效果

QGraphicsEffects 不提供限制其处理范围的能力,因为它们通常会修改为其设置的对象的整个 "bounding rect"。
为了实现这一点,子类化是必要的,并且 draw() 方法必须被覆盖,因为它是负责实际绘图的方法。

我假设整个界面都会以某种方式受到效果的影响:即使某些对象是 "outside" 效果的矩形,它们仍然是效果的一部分家长,这就是我们要做的:

  • 创建一个主窗口小部件作为 完整 界面
  • 的容器
  • main 界面添加主布局(正常显示的界面)
  • 创建一个包含 main 界面的子小部件,为其设置布局并向该布局添加您需要的任何内容
  • 将子类图​​形效果设置为 sub 小部件
  • 为菜单创建一个小部件,将 主小部件 作为父级,因此它不会成为主布局的一部分;它将有自己的布局,包括按钮、标签等。
  • 添加一个系统,根据菜单的几何形状改变图形效果,每当改变时,效果将只应用于该几何形状

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())

上一个回答

既然您想将效果应用于底层对象,我认为解决方案是使用 "container" 嵌入它们,然后对其应用模糊效果。同样的概念也适用于 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
    }
}