python - FastBlur 可以模糊它背后的一切吗?
问题描述
我有这个代码,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 可以做到。如果还有其他选择,请告诉我
我可以做吗?
解决方案
我正在更新此答案,因为问题已在评论中部分清除,但我将在最后保留原始答案,因为它可能仍然有用。
除此之外,基本概念保持不变:图形效果应用于对象,它修改对象的外观,而不是底层对象的外观。如果您想将该效果应用于多个对象,它们必须是共同父对象的子对象,并且必须为该父对象设置效果,但该父对象下方的所有内容(并且不是其子对象)将只是部分影响效果的结果。
将模糊效果想象为应用于部分透明的真实照片的滤镜:虽然照片中的图像是模糊的,但您在它后面看到的东西不会模糊。
子类化图形效果
QGraphicsEffects 不提供限制其处理范围的能力,因为它们通常会修改为其设置的对象的整个“边界矩形”。
为了实现这一点,子类化是必要的,并且draw()
必须重写该方法,因为它是负责实际绘图的方法。
我将假设整个界面会以某种方式受到效果的影响:即使某些对象在效果的矩形“外部”,它们仍然是同一个父对象的一部分,所以这就是我们'要做:
- 创建一个主小部件,作为完整界面的容器
- 为主界面添加主布局(通常显示的那个)
- 创建一个包含主界面的子小部件,为其设置布局并将您需要的任何内容添加到该布局
- 将子类图形效果设置为子小部件
- 为菜单创建一个小部件,将主小部件作为父小部件,因此它不会成为主布局的一部分;它将有自己的布局,包括按钮、标签等。
- 添加一个根据菜单的几何形状更改图形效果的系统,并且每当更改时,效果将仅应用于该几何形状
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
Rectangle {
id: container
anchors.fill: parent
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: container
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
}
}
推荐阅读
- podspec - 使用 cocoapods 进行源代码和二进制分发的单个 Podspec
- python-3.x - ARIMA 预测 - 多个模型
- r - R:将二进制文件转换为光栅文件
- php - 错误:内部服务器错误 (500)?WordPress
- react-native - FlatList 是否强制 json 响应应始终以 [] 开头?
- c# - 如何使后台工作人员与延迟或计时器一起工作
- javascript - 禁用按钮 .Net Core MVC 视图
- javascript - 如何在jQuery中点击取消事件
- powershell - check_nrpe 将返回码 1 视为 OK
- android - Firebase和Android以及云功能等平台