Ну, в конце концов я решил, что это весело :-)
ВАЖНО. Учтите, что это какой-то взлом, потому что он использует частную и недокументированную функцию Qt (та же самая, что используется в QGraphicsBlurEffect), и это не гарантируется. будет работать везде.
Мне удалось добиться этого, позаимствовав некоторый код из средства просмотра прямых трансляций, известного как Атропин, самое интересное в эффектах. py источник.
Обратите внимание, что аналогичный эффект, вероятно, может быть достигнут с помощью «абстрактного» рисования частной QGraphicsScene внутри самого QGraphicsEffect, но это будет намного медленнее (поскольку вам придется создавать новые QGraphicsPixmapItems каждый раз, когда метод draw()
эффекта называется) и, вероятно, будет иметь некоторые побочные эффекты.
Хитрость заключалась в том, чтобы получить имя функции через ctypes, я смог найти экспортированные имена функций только в Linux и Windows (но я не смог проверить это там):
# Linux:
$ nm -D /usr/lib/libQt5Widgets.so |grep qt_blurImage
004adc30 T _Z12qt_blurImageP8QPainterR6QImagedbbi
004ae0e0 T _Z12qt_blurImageR6QImagedbi
# Windows (through Mingw):
> objdump -p /QtGui4.dll |grep blurImage
[8695] ?qt_blurImage@@YAXAAVQImage@@N_NH@Z
[8696] ?qt_blurImage@@YAXPAVQPainter@@AAVQImage@@N_N2H@Z
Я не могу проводить тестирование для MacO, но думаю, что это должна быть та же командная строка, что и в Linux.
Обратите внимание, что имена этих функций кажутся статичными в одной и той же версии операционной системы и Qt: я запускаю ту же команду nn
в старом файле libQtGui.so
для Qt4, и она дает тот же результат.
Итак, вот ваш красивый неоновый эффект...
![прекрасный неоновый эффект!](https://i.stack.imgur.com/DAFDp.png)
И вот код, я добавил пример программы, чтобы проверить его:
import sys
import sip
import ctypes
from PyQt5 import QtCore, QtGui, QtWidgets
if sys.platform == 'win32':
# the exported function name has illegal characters on Windows, let's use
# getattr to access it
_qt_blurImage = getattr(ctypes.CDLL('QtGui5.dll'),
'?qt_blurImage@@YAXPAVQPainter@@AAVQImage@@N_N2H@Z')
else:
try:
qtgui = ctypes.CDLL('libQt5Widgets.so')
except:
qtgui = ctypes.CDLL('libQt5Widgets.so.5')
_qt_blurImage = qtgui._Z12qt_blurImageP8QPainterR6QImagedbbi
class NeonEffect(QtWidgets.QGraphicsColorizeEffect):
_blurRadius = 5.
_glow = 2
def glow(self):
return self._glow
@QtCore.pyqtSlot(int)
def setGlow(self, glow):
if glow == self._glow:
return
self._glow = max(1, min(glow, 10))
self.update()
def blurRadius(self):
return self._blurRadius
@QtCore.pyqtSlot(int)
@QtCore.pyqtSlot(float)
def setBlurRadius(self, radius):
if radius == self._blurRadius:
return
self._blurRadius = max(1., float(radius))
self.update()
def applyBlurEffect(self, blurImage, radius, quality, alphaOnly, transposed=0, qp=None):
blurImage = ctypes.c_void_p(sip.unwrapinstance(blurImage))
radius = ctypes.c_double(radius)
quality = ctypes.c_bool(quality)
alphaOnly = ctypes.c_bool(alphaOnly)
transposed = ctypes.c_int(transposed)
if qp:
qp = ctypes.c_void_p(sip.unwrapinstance(qp))
_qt_blurImage(qp, blurImage, radius, quality, alphaOnly, transposed)
def draw(self, qp):
pm, offset = self.sourcePixmap(QtCore.Qt.LogicalCoordinates, self.PadToEffectiveBoundingRect)
if pm.isNull():
return
# use a double sized image to increase the blur factor
scaledSize = QtCore.QSize(pm.width() * 2, pm.height() * 2)
blurImage = QtGui.QImage(scaledSize, QtGui.QImage.Format_ARGB32_Premultiplied)
blurImage.fill(0)
blurPainter = QtGui.QPainter(blurImage)
blurPainter.drawPixmap(0, 0, pm.scaled(scaledSize,
QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
blurPainter.end()
# apply the blurred effect on the image
self.applyBlurEffect(blurImage, 1 * self._blurRadius, True, False)
# start the painter that will use the previous image as alpha
tmpPainter = QtGui.QPainter(blurImage)
# using SourceIn composition mode we use the existing alpha values
# to paint over
tmpPainter.setCompositionMode(tmpPainter.CompositionMode_SourceIn)
color = QtGui.QColor(self.color())
color.setAlpha(color.alpha() * self.strength())
# fill using the color
tmpPainter.fillRect(pm.rect(), color)
tmpPainter.end()
# repeat the effect which will make it more "glowing"
for g in range(self._glow):
qp.drawImage(0, 0, blurImage.scaled(pm.size(),
QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
super().draw(qp)
class NeonTest(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
palette = self.palette()
palette.setColor(palette.Window, QtCore.Qt.black)
palette.setColor(palette.WindowText, QtCore.Qt.white)
self.setPalette(palette)
self.label = QtWidgets.QLabel('NEON EFFECT')
layout.addWidget(self.label, 0, 0, 1, 2)
self.label.setPalette(QtWidgets.QApplication.palette())
self.label.setContentsMargins(20, 20, 20, 20)
f = self.font()
f.setPointSizeF(48)
f.setBold(True)
self.label.setFont(f)
self.effect = NeonEffect(color=QtGui.QColor(152, 255, 250))
self.label.setGraphicsEffect(self.effect)
self.effect.setBlurRadius(40)
layout.addWidget(QtWidgets.QLabel('blur radius'))
radiusSpin = QtWidgets.QDoubleSpinBox(minimum=1, maximum=100, singleStep=5)
layout.addWidget(radiusSpin, 1, 1)
radiusSpin.setValue(self.effect.blurRadius())
radiusSpin.valueChanged.connect(self.effect.setBlurRadius)
layout.addWidget(QtWidgets.QLabel('glow factor'))
glowSpin = QtWidgets.QSpinBox(minimum=1, maximum=10)
layout.addWidget(glowSpin, 2, 1)
glowSpin.setValue(self.effect.glow())
glowSpin.valueChanged.connect(self.effect.setGlow)
layout.addWidget(QtWidgets.QLabel('color strength'))
strengthSpin = QtWidgets.QDoubleSpinBox(minimum=0, maximum=1, singleStep=.05)
strengthSpin.setValue(1)
layout.addWidget(strengthSpin, 3, 1)
strengthSpin.valueChanged.connect(self.effect.setStrength)
colorBtn = QtWidgets.QPushButton('color')
layout.addWidget(colorBtn, 4, 0)
colorBtn.clicked.connect(self.setColor)
self.aniBtn = QtWidgets.QPushButton('play animation')
layout.addWidget(self.aniBtn, 4, 1)
self.aniBtn.setCheckable(True)
self.glowAni = QtCore.QVariantAnimation(duration=250)
self.glowAni.setStartValue(1)
self.glowAni.setEndValue(5)
self.glowAni.setEasingCurve(QtCore.QEasingCurve.InQuad)
self.glowAni.valueChanged.connect(glowSpin.setValue)
self.glowAni.finished.connect(self.animationFinished)
self.aniBtn.toggled.connect(self.glowAni.start)
def animationFinished(self):
if self.aniBtn.isChecked():
self.glowAni.setDirection(not self.glowAni.direction())
self.glowAni.start()
def setColor(self):
d = QtWidgets.QColorDialog(self.effect.color(), self)
if d.exec_():
self.effect.setColor(d.currentColor())
Обратите внимание, что, основываясь на моих тестах, использование коэффициента свечения выше 1 с радиусом размытия менее 4 может привести к некоторым артефактам рисования.
Кроме того, с палитрой нужно быть осторожным. Если вы хотите, например, применить эффект к QLineEdit, вы, вероятно, также захотите сделать QPalette.Base
прозрачным:
![редактировать светящуюся строку!](https://i.stack.imgur.com/wtlj6.png)
Я смог успешно протестировать его на двух машинах Linux (с Qt 5.7 и 5.12), если кто-то захочет прокомментировать тестирование на других платформах, я буду рад соответствующим образом обновить этот ответ.
person
musicamante
schedule
27.03.2020
if
, просто создайте функцию, которая возвращает все значения в правильном порядке (чтобы вы не нужно делатьlower()
каждый раз). Кроме того, это выглядит плохо, потому что QGraphicsBlurEffect был создан не для этого, поэтому вы просто создаете композицию, которая напоминает то, чего вы хотите достичь. Боюсь, что единственной альтернативой является создание собственного подкласса QGraphicsEffect, возможно, по размеру эффекта и/или изменению цвета размытия. - person musicamante   schedule 27.03.2020