จะสร้างเอฟเฟกต์นีออนที่สวยงามได้อย่างไร?

ฉันต้องการสร้างเอฟเฟกต์นีออนที่สวยงามและชุ่มฉ่ำพร้อมความสามารถในการควบคุมพลังของแสง เพื่อสิ่งนี้ ฉันจึงสร้างโค้ดดังกล่าวขึ้นมา

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

แต่รหัสยุ่งยากเกินไป และแสงกลับกลายเป็นว่าไม่เป็นธรรมชาติเกินไป

ป้อนคำอธิบายรูปภาพที่นี่

มันดูแย่เป็นพิเศษหากคุณระบุว่ามีความเข้มของแสงน้อย

มีตัวเลือกอื่นที่ดีกว่าในการสร้างเอฟเฟกต์นีออนหรือไม่? หรือทำไมเขาดูแย่มาก?


person askqest    schedule 27.03.2020    source แหล่งที่มา
comment
โค้ดสามารถปรับปรุงได้เล็กน้อย (แต่เป็นเพียงเกี่ยวกับความสามารถในการอ่านและการใช้งาน ไม่ใช่การเพิ่มประสิทธิภาพที่แท้จริง): ไม่จำเป็นต้องทำคำสั่ง if ทั้งหมดเหล่านั้น เพียงแค่สร้างฟังก์ชันที่คืนค่าทั้งหมดตามลำดับที่ถูกต้อง (เพื่อที่คุณจะได้ไม่ต้อง ต้องทำ lower() ทุกครั้ง) นอกจากนั้น มันดูแย่เพราะนี่ไม่ใช่สิ่งที่ QGraphicsBlurEffect ถูกสร้างขึ้นมา ดังนั้นคุณแค่สร้างองค์ประกอบที่คล้ายกับสิ่งที่คุณต้องการบรรลุ ฉันเกรงว่าทางเลือกเดียวคือสร้างคลาสย่อย QGraphicsEffect ของคุณเอง ซึ่งอาจเป็นไปตามขนาดของเอฟเฟกต์และ/หรือการเปลี่ยนสีเบลอ   -  person musicamante    schedule 27.03.2020


คำตอบ (2)


ในที่สุดฉันก็ตัดสินใจว่ามันสนุกที่จะทำ :-)

สำคัญ: พิจารณาว่านี่เป็นการแฮ็กบางประเภท เนื่องจากใช้ฟังก์ชัน ส่วนตัว และไม่มีเอกสารของ Qt (ซึ่งเป็นแบบเดียวกับที่ใช้โดย QGraphicsBlurEffect) และไม่รับประกันว่าจะเป็นเช่นนั้น จะทำงานได้ทุกที่
ฉันสามารถบรรลุเป้าหมายนี้ได้ด้วยการยืมโค้ดบางส่วนจากโปรแกรมดูรายการทีวีสดที่เรียกว่า Atropine ส่วนที่น่าสนใจอยู่ใน เอฟเฟกต์ ไพแหล่งที่มา

โปรดทราบว่าเอฟเฟกต์ที่คล้ายกันอาจเกิดขึ้นได้โดยใช้ภาพวาด "นามธรรม" ของ 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 และให้ผลลัพธ์เดียวกัน

นี่คือเอฟเฟกต์นีออนที่สวยงามของคุณ...

เอฟเฟกต์แสงนีออนที่ยอดเยี่ยม!

และนี่คือโค้ด ฉันได้เพิ่มโปรแกรมตัวอย่างเพื่อทดสอบ:

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) หากใครสนใจแสดงความคิดเห็นเกี่ยวกับการทดสอบบนแพลตฟอร์มอื่น ฉันยินดีที่จะอัปเดตคำตอบนี้ตามนั้น

person musicamante    schedule 27.03.2020
comment
ขออภัยสำหรับคำถามโง่ ๆ แต่ import sip คืออะไร ฉันได้ตั้งค่า pip install sip และ pip ​​install PyQt5-sip แต่ paython ไม่พบโมดูล SIP - person askqest; 28.03.2020
comment
หากคุณมี PyQt คุณควรจิบแล้ว อาจตรวจสอบการติดตั้ง PyQt5 และค้นหาที่นี่ใน StackOverflow เกี่ยวกับข้อผิดพลาดที่คล้ายกัน - person musicamante; 28.03.2020
comment
สิ่งเดียวที่ฉันพบคือคำถามใน PYQT4 แต่ตามที่ฉันเข้าใจ พวกเขาแนะนำให้ติดตั้ง sip แยกต่างหาก และคำถามนี้จะไม่ช่วยฉัน ในโฟลเดอร์ PyQt5 มี sip.cp37-win_amd64.pyd file. ไฟล์นี้รับผิดชอบในการจิบหรือไม่ - person askqest; 28.03.2020
comment
ตอนนี้โค้ดแสดงข้อผิดพลาด Traceback (most recent call last): File "C:\Users\user\Desktop\neon.py", line 35, in <module> _qt_blurImage = getattr(ctypes.CDLL('QtGui5.dll'), File "C:\Python37\lib\ctypes\__init__.py", line 364, in __init__ self._handle = _dlopen(self._name, mode) OSError: [WinError 126] The specified module was not found - person askqest; 29.03.2020
comment
อาจเป็น Qt5Gui.dll หรือ Qt5Widgets.dll เพียงแค่มองหามัน อย่างที่ฉันบอกไปแล้ว ฉันไม่สามารถทดสอบบน Windows ได้ - person musicamante; 29.03.2020
comment
ฉันไม่เข้าใจว่าเกิดอะไรขึ้นกับไฟล์เหล่านี้ (Qt5Gui.dll และ Qt5Widgets.dll) ฉันควรค้นหาอะไรบนอินเทอร์เน็ต - person askqest; 29.03.2020
comment
คุณหมายถึงอะไรว่ามีอะไรผิดปกติกับไฟล์เหล่านี้? คุณมีพวกเขาไหม? คุณได้ค้นหา บนคอมพิวเตอร์ของคุณ แล้วหรือยัง? ข้อผิดพลาดจะเหมือนกับความคิดเห็นก่อนหน้าของคุณเสมอโดยใช้ชื่ออื่นหรือไม่ ตามที่ฉันได้บอกคุณไปแล้ว โปรดอย่าให้ข้อมูลเป็นชิ้นๆ ทำให้ชัดเจนมากขึ้น - person musicamante; 29.03.2020
comment
ฉันมีไฟล์เหล่านี้ มันอาจดูแปลกสำหรับฉันหากไม่มีไฟล์เหล่านี้ Qt5Gui และ Qt5Widgets เป็นไฟล์หลักที่โปรแกรม Qt พักอยู่ ขออภัยที่ฉันเข้าใจผิด - person askqest; 31.03.2020
comment
ฉันรู้ว่าไฟล์เหล่านั้นมีไว้เพื่ออะไร แต่เนื่องจากฉันไม่ได้ใช้ windows ฉันจึงไม่แน่ใจเกี่ยวกับชื่อไฟล์เหล่านั้น และฉันไม่ได้ขอให้คุณอธิบายเรื่องนั้น อีกครั้ง และหวังว่าครั้งสุดท้าย ปัญหาคืออะไร มันยังบอกว่าไม่พบโมดูลหรือไม่? หรือมันเป็นข้อผิดพลาดอื่น? คุณต้องตอบทุกคำถามที่เราถามคุณ!!! - person musicamante; 31.03.2020
comment
ฉันพบปัญหาอะไร สคริปต์บอกว่า (QtGui5.dll) และไฟล์ของฉันชื่อ (Qt5Gui.dll) ฉันไม่ได้สังเกตเห็นสิ่งนี้เพราะฉันกำลังมองหา QtGui แต่ตอนนี้โค้ดได้นำปัญหาใหม่ออกมาแล้ว Traceback (most recent call last):File"C:\neon.py",line36,in<module>'qt_blurImage@@YAXPAVQPainter@@AAVQImage@@N_N2H@Z')File"C:\Python37\lib\ctypes\__init__.py",line377,in__getattr__func=self.__getitem__(name)File"C:\Python37\lib\ctypes\__init__.py",line382,in__getitem__func=self._FuncPtr((name_or_ordinal,self))AttributeError:function'qt_blurImage@@YAXPAVQPainter@@AAVQImage@@N_N2H@Z' not found - person askqest; 02.04.2020
comment
?qt_blurImage@@YAXAAVQImage@@N_NH@Z และ ?qt_blurImage@@YAXPAVQPainter@@AAVQImage@@N_N2H@Z ไม่ทำงาน - person askqest; 02.04.2020
comment
@askqest ฉันได้รับปัญหาเดียวกันใน window10 ฉันจะยอมแพ้แล้ว รหัสที่ให้มาแปลกมาก - person Luk Aron; 11.06.2020
comment
ฉันใส่คำเตือนนั้นด้วยเหตุผลเฉพาะ: พิจารณาว่านี่เป็นแฮ็คบางประเภท เนื่องจากใช้ฟังก์ชันส่วนตัวและไม่มีเอกสารของ Qt (ซึ่งใช้โดย QGraphicsBlurEffect แบบเดียวกับที่ใช้) และไม่รับประกันว่าจะทำงานได้ทุกที่ - person musicamante; 11.06.2020
comment
ว้าว ฉันกำลังมองหาแผนภูมิเรืองแสงใน qtwidgets (qml มีประเภทเรืองแสง: qt.io/blog/2017/02/07/glowing-qt-charts) สิ่งนี้ใช้ได้กับชาร์ตด้วย คุณเจ๋งมาก! - person Muhammed B. Aydemir; 02.09.2020

ฉันไม่สามารถแสดงความคิดเห็นและแก้ไขโพสต์ของ musicamante ได้ ทำให้ฉันมีข้อผิดพลาดเกี่ยวกับรูปแบบโค้ด ดังนั้นฉันจึงโพสต์ไว้ที่นี่

เมื่อพิจารณาว่า QGraphicsEffect ถูกนำเข้าจาก QtWidgets แทนที่จะเป็น QtGui ดังนั้นฉันจึง objdump Qt5Widgets.dll และนี่คือผลลัพธ์:

.\objdump.exe -p /Qt5Widgets.dll | findstr "blurImage"
        [5154] ?qt_blurImage@@YAXAEAVQImage@@N_NH@Z
        [5155] ?qt_blurImage@@YAXPEAVQPainter@@AEAVQImage@@N_N2H@Z

ดังนั้นบน Windows ควรเป็น:

_qt_blurImage = getattr(ctypes.CDLL('Qt5Widgets.dll'),
        '?qt_blurImage@@YAXPEAVQPainter@@AEAVQImage@@N_N2H@Z')

นี่คือภาพหน้าจอ:

ป้อนคำอธิบายรูปภาพที่นี่

person closer_ex    schedule 09.07.2020
comment
ตัวเลือกนี้ไม่ทำให้เกิดข้อผิดพลาด แต่ตอนนี้โค้ดถามอีกครั้ง sip ฉันยังไม่เข้าใจว่าเหตุใดฉันจึงไม่มี ฉันติดตั้ง PyQT5 หลายครั้ง - person askqest; 09.07.2020
comment
@askqest ลอง from PyQt5 import sip - person closer_ex; 09.07.2020
comment
มันได้ผล!! คุณผสมสีขาวและสีน้ำเงินได้อย่างไร? - person askqest; 10.07.2020
comment
@askqest รหัสเหมือนกับของ musicamante แต่การแสดงผลแบบอักษรจะแตกต่างกันบน windows ฉันเดาซึ่งส่งผลให้เกิดเอฟเฟกต์ที่แตกต่างกัน โดยพื้นฐานแล้วมันเป็นเพียงป้ายกำกับสีขาวที่มีภาพเบลอสีน้ำเงิน เคล็ดลับคือการทำให้ภาพเบลอเหมาะกับข้อความมากขึ้น - person closer_ex; 11.07.2020
comment
ฉันพยายามเปลี่ยนสีใน setStyleSheet('color:red') แต่ไม่มีผลใดๆ ฉันสงสัยว่าเป็นไปได้หรือไม่ที่จะเพิ่ม NeonEffect เข้าไปข้างใน กล่าวอีกนัยหนึ่ง กลับด้าน setBlurRadius() เพื่อให้สีขาวบางลง ชอบ ที่นี่ - person askqest; 11.07.2020
comment
@askqest อาจอ่านเกี่ยวกับ CompositionMode เพื่อเรียนรู้วิธีการผสมผสานเอฟเฟกต์กับป้ายกำกับ ด้วยเหตุนี้ สีข้อความในโหมด SourceIn จะเหมือนกับสีเบลอเสมอ การเปลี่ยนไปใช้โหมด DestinationIn จะเปลี่ยนสีข้อความและสีเบลอของคุณ แต่ถ้าคุณหมายถึงการวาดเส้นสีขาวภายในตัวอักษร ฉันคิดว่าเอฟเฟกต์เบลอไม่ใช่สิ่งที่คุณควรมองหา - person closer_ex; 11.07.2020