Может ли FastBlur размывать все, что за этим стоит? - PullRequest
0 голосов
/ 28 февраля 2020

У меня есть этот код, 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 может сделать это. если есть другие варианты, дайте мне знать

Могу ли я это сделать?

image

1 Ответ

0 голосов
/ 28 февраля 2020

Я обновляю этот ответ, так как вопрос был частично очищен в комментариях, но я оставлю исходный ответ в конце, так как он все еще может быть полезен.

Кроме того, концепция на основа остается прежней: к объекту применяется графический эффект, который изменяет внешний вид этого объекта , а не внешний вид объектов. Если вы хотите применить этот эффект к нескольким объектам, они должны быть children от общего родителя, и эффект должен быть установлен для этого parent, но для всего, что ниже этот родитель (а не его дочерний элемент) будет только частично затронут результатом эффекта.
Представьте эффект размытия как фильтр, применяемый к реальной фотографии, которая частично прозрачный: в то время как изображение на фотографии размыто, то, что вы видите за ним, будет не размыто.

Подкласс графического эффекта

QGraphicsEffects не обеспечивает возможность ограничивать степень их обработки, так как они обычно изменяют весь «ограничивающий прямоугольник» объекта, для которого они установлены.
Для этого необходимо создать подклассы, а draw() метод должен быть переопределен, поскольку именно он отвечает за фактическое рисование.

Я собираюсь предположить, что весь интерфейс будет затронут эффект в некотором роде: даже если некоторые объекты находятся «вне» прямоугольника эффекта, они все еще являются частью одного и того же родителя, поэтому мы собираемся сделать следующее:

  • create основной виджет, который выступает в качестве контейнера для интерфейса full
  • , добавьте основной макет для интерфейса main (тот, который обычно отображается)
  • create вспомогательный виджет, который содержит интерфейс main , установите для него макет и добавьте в него все, что вам нужно
  • установите эффект подкласса графики для виджета sub
  • создать виджет для меню, у которого основной виджет является родительским, поэтому он не будет частью основного макета; он будет иметь свой собственный макет со своими кнопками, надписями и т. д. c.
  • добавить систему, которая изменяет графический эффект в соответствии с геометрией меню, и всякий раз, когда это изменяется, эффект будет применен к только эта геометрия

a very very cool menu!

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
    }
}

resulting full blur effect

...