怎样才能做出漂亮的霓虹灯效果呢?
问题描述
我想做一个漂亮多汁的霓虹灯效果,能够控制光线的力量。为此,我构建了这样的代码
import sys
from PyQt5.QtWidgets import (QRadioButton, QHBoxLayout, QButtonGroup,
QApplication, QGraphicsScene,QGraphicsView, QGraphicsLinearLayout, QGraphicsWidget, QWidget, QLabel)
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtCore import QSize, QPoint,Qt
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QPainter
class Window(QWidget):
def __init__(self):
super().__init__()
self.resize(800, 800)
self.setStyleSheet('background:black;')
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.setContentsMargins(0, 0, 0, 0)
color_1 = '162, 162, 162,'
color_2 = '255, 255, 255,'
color_3 = '0, 255, 255,'
d_ = 1
power = int(255/100*d_)
for x in range(6):
label = QLabel(self)
color_L = color_1
glass_L = 255
size_L = 60
blut_L = 0
label.raise_()
if x < 1 :
color_L = color_1
elif x < 2 :
color_L = color_3
glass_L = power
elif x < 3 :
color_L = color_2
blut_L = 6
glass_L = power
elif x < 4:
color_L = color_2
blut_L = 40
glass_L = power
elif x < 5 :
label.lower()
color_L = color_3
blut_L = 40
size_L = 70
glass_L = power
elif x < 6 :
label.lower()
color_L = color_3
blut_L = 150
size_L = 70
glass_L = power
label.setText('test')
label.setStyleSheet('background:rgba(0, 0, 0, 0);color:rgba({} {}); font-size:{}px;'.format(color_L, glass_L,size_L))
label.resize(self.width(), self.height())
label.setAlignment(Qt.AlignCenter)
self.effect = QtWidgets.QGraphicsBlurEffect(blurRadius=blut_L)
label.setGraphicsEffect(self.effect)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
但是代码太繁琐了。结果灯光太不自然了,
如果您指示光线强度较弱,则看起来特别糟糕。
有没有更好的方法来制作霓虹灯效果?或者为什么他看起来这么糟糕?
解决方案
嗯,我最终觉得做这件事很有趣:-)
重要信息:请考虑这是某种黑客攻击,因为它使用了私有的和未记录的Qt函数(与QGraphicsBlurEffect使用的函数相同),并且不能保证它在任何地方都可以工作。
我从一个叫做Atropine的直播电视观众那里借用了一些代码来实现它,有趣的部分在effects.py源代码中。
请注意,通过在QGraphicsEffect本身内使用私有QGraphicsScene的"抽象"绘制,可能会达到类似的效果,但速度会慢得多(因为每次调用效果的draw()
方法时都必须创建新的QGraphicsPixmapItems),并且可能会有一些副作用。
诀窍是通过ctype获取函数名,我只能在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
我无法为MacOS进行测试,但我认为它应该与Linux上的命令行相同。
请注意,这些函数名称似乎是同一操作系统和Qt版本中的静电:我对Qt4的旧libQtGui.so
文件运行相同的nn
命令,结果相同。
那么,这就是您的漂亮的霓虹灯效果.
下面是代码,我添加了一个示例程序来测试它:
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
设置为透明:
我已经在两台Linux计算机(Qt 5.7和5.12)上成功测试了它,如果有人愿意对在其他平台上的测试发表意见,我很乐意相应地更新此答案。
相关文章