更改线程中的QObject样式表时出错 [英] Error while changing QObject stylesheet in a Thread

查看:61
本文介绍了更改线程中的QObject样式表时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用python构建QObject动画.例如,我尝试对QLineEdit对象的背景进行动画处理,以便在输入错误时产生红色闪烁".该函数正在运行,线程开始运行,我看到了动画,但是当线程结束时,应用程序崩溃,而没有错误回溯.我只会

I want to build QObject animations in python. For example, I tried animating the background of a QLineEdit object in order to make a "red flash" when a something wrong is entered. The function is working, the thread starts and I see the animation, but when the thread ends, the app collapses without error trace-back. I only get

exit code -1073740940

我在互联网上找不到的那个

Which I didn't find on the internet.

这里是我为了让您仅用一个文件即可重现此错误而制作的mwe.您会注意到代码的重要部分在LoginDialog类中.

Here's a mwe that I made in order for you to reproduce this error with only one file. You will notice that the important part of the code is inside LoginDialog class.

from PyQt5.QtWidgets import QDialog, QLineEdit, QVBoxLayout, QApplication
from threading import Thread
import time
import sys


class Ui_LoginUi(object):
    def setupUi(self, Ui_LoginUi):
        Ui_LoginUi.setObjectName("LoginUi")
        Ui_LoginUi.resize(293, 105)
        self.layout = QVBoxLayout(Ui_LoginUi)
        self.le_test = QLineEdit(Ui_LoginUi)
        self.layout.addWidget(self.le_test)


class LoginDialog(QDialog, Ui_LoginUi):

    def __init__(self):
        super(LoginDialog, self).__init__()
        self.setupUi(self)
        self.le_test.textChanged.connect(self.redFlashThreader)

    def redFlashThreader(self):
        self.redFlashTread1 = Thread(target=self.lineEdit_redFlash, args=[self.le_test])
        self.redFlashTread1.start()

    def lineEdit_redFlash(self, *args):
        inital_r = 255
        initial_g = 127
        initial_b = 127

        for i in range(64):
            initial_g += 2
            initial_b += 2
            time.sleep(0.005)
            args[0].setStyleSheet("background-color: rgb(255,{},{})".format(initial_g, initial_b))

        args[0].setStyleSheet("background-color: rgb(255,255,255")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    dialog = LoginDialog()
    dialog.show()
    sys.exit(app.exec_())

结果

如果单击多次,则该应用程序将冻结并崩溃.我想理解为什么,但是没有追溯,我觉得很难.有时,它是在第一次点击后发生的.我以为这是线程冲突问题,但是由于仅在运行第一个线程时发生,所以我不太确定.任何人都可以向我指出正确的方向或向我解释发生了什么事?

Results

If you click multiple times, the app will freeze and crash. I would like to understand why, but without trace-back, I find that quite hard. Sometimes, it happens after the first click. I thought it would be a thread conflict problem, but since it happens with only the first thread running, I'm not so sure. Anyone could point me in the right direction or explain to me what is happening?

推荐答案

您的问题允许分析以下方面:

Your question allows to analyze the following aspects:

GUI的绘制是在主线程中完成的,因此GUI在任何情况下都不允许修改涉及从另一个线程进行绘制的任何属性,因此,如果开发人员这样做,则不能保证在这种情况下能正常工作怎么了.有关更多信息,请阅读 GUI线程和工作器线程.

The painting of the GUI is done in the main thread so the GUI do not allow in any case to modify any property that involves painting from another thread, so if the developer does it there is no guarantee that works as in this case what's wrong. For more information read GUI Thread and Worker Thread.

对于Qt,如果要从另一个线程更新某些GUI元素,则应通过某种方式(信号,QEvent,QMetaObject :: invokeMethod()等)将信息发送到主线程,并且在主线程中进行更新.

In the case of Qt if you want to update some GUI element from another thread what you should do is send by some means (signals, QEvent, QMetaObject::invokeMethod(), etc) the information to the main thread, and in the main thread do the update.

因此,考虑到上述情况,使用信号的可能解决方案如下:

So considering the above, a possible solution using signals is the following:

import sys
import time
from threading import Thread
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_LoginUi(object):
    def setupUi(self, Ui_LoginUi):
        Ui_LoginUi.setObjectName("LoginUi")
        Ui_LoginUi.resize(293, 105)
        layout = QtWidgets.QVBoxLayout(Ui_LoginUi)
        self.le_test = QtWidgets.QLineEdit(Ui_LoginUi)
        layout.addWidget(self.le_test)


class LoginDialog(QtWidgets.QDialog, Ui_LoginUi):
    colorChanged = QtCore.pyqtSignal(QtGui.QColor)

    def __init__(self):
        super(LoginDialog, self).__init__()
        self.setupUi(self)
        self.le_test.textChanged.connect(self.redFlashThreader)
        self.colorChanged.connect(self.on_color_change)

    @QtCore.pyqtSlot()
    def redFlashThreader(self):
        self.redFlashTread1 = Thread(
            target=self.lineEdit_redFlash, args=[self.le_test]
        )
        self.redFlashTread1.start()

    def lineEdit_redFlash(self, *args):
        inital_r = 255
        initial_g = 127
        initial_b = 127

        for i in range(64):
            initial_g += 2
            initial_b += 2
            time.sleep(0.005)
            self.colorChanged.emit(QtGui.QColor(255, initial_g, initial_b))
        self.colorChanged.emit(QtGui.QColor(255, 255, 255))

    @QtCore.pyqtSlot(QtGui.QColor)
    def on_color_change(self, color):
        self.setStyleSheet("QLineEdit{background-color: %s}" % (color.name(),))

        """ or
        self.setStyleSheet(
            "QLineEdit{ background-color: rgb(%d, %d, %d)}"
            % (color.red(), color.green(), color.blue())
        )"""

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    dialog = LoginDialog()
    dialog.show()
    sys.exit(app.exec_())

2)不必使用线程在Qt中制作动画,而应使用QVariantAnimation,QPropertyAnimation等.

在GUI中,应该避免使用线程,因为它带来的问题多于好处(例如,使信号队列饱和),因此请尽量使用它.在这种情况下,您可以使用 QVariantAnimation

2) It is not necessary to use threads to make animation in Qt, instead you should use QVariantAnimation, QPropertyAnimation, etc.

In a GUI you should avoid using threading since it can bring more problems than benefits (for example saturate the signal queue), so use it as a last resort. In this case you can use QVariantAnimation or QPropertyAnimation:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_LoginUi(object):
    def setupUi(self, Ui_LoginUi):
        Ui_LoginUi.setObjectName("LoginUi")
        Ui_LoginUi.resize(293, 105)
        layout = QtWidgets.QVBoxLayout(Ui_LoginUi)
        self.le_test = QtWidgets.QLineEdit(Ui_LoginUi)
        layout.addWidget(self.le_test)


class LoginDialog(QtWidgets.QDialog, Ui_LoginUi):
    def __init__(self):
        super(LoginDialog, self).__init__()
        self.setupUi(self)
        self.le_test.textChanged.connect(self.start_animation)

        self.m_animation = QtCore.QVariantAnimation(
            self,
            startValue=QtGui.QColor(255, 127, 127),
            endValue=QtGui.QColor(255, 255, 255),
            duration=1000,
            valueChanged=self.on_color_change,
        )

    @QtCore.pyqtSlot()
    def start_animation(self):
        if self.m_animation.state() == QtCore.QAbstractAnimation.Running:
            self.m_animation.stop()
        self.m_animation.start()

    @QtCore.pyqtSlot(QtCore.QVariant)
    @QtCore.pyqtSlot(QtGui.QColor)
    def on_color_change(self, color):
        self.setStyleSheet("QLineEdit{background-color: %s}" % (color.name(),))

        """ or
        self.setStyleSheet(
            "QLineEdit{ background-color: rgb(%d, %d, %d)}"
            % (color.red(), color.green(), color.blue())
        )"""


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    dialog = LoginDialog()
    dialog.show()
    sys.exit(app.exec_())

2.2)QPropertyAnimation

import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class LineEdit(QtWidgets.QLineEdit):
    backgroundColorChanged = QtCore.pyqtSignal(QtGui.QColor)

    def backgroundColor(self):
        if not hasattr(self, "_background_color"):
            self._background_color = QtGui.QColor()
            self.setBackgroundColor(QtGui.QColor(255, 255, 255))
        return self._background_color

    def setBackgroundColor(self, color):
        if self._background_color != color:
            self._background_color = color
            self.setStyleSheet("background-color: {}".format(color.name()))
            self.backgroundColorChanged.emit(color)

    backgroundColor = QtCore.pyqtProperty(
        QtGui.QColor,
        fget=backgroundColor,
        fset=setBackgroundColor,
        notify=backgroundColorChanged,
    )


class Ui_LoginUi(object):
    def setupUi(self, Ui_LoginUi):
        Ui_LoginUi.setObjectName("LoginUi")
        Ui_LoginUi.resize(293, 105)
        layout = QtWidgets.QVBoxLayout(Ui_LoginUi)
        self.le_test = LineEdit(Ui_LoginUi)
        layout.addWidget(self.le_test)


class LoginDialog(QtWidgets.QDialog, Ui_LoginUi):
    def __init__(self):
        super(LoginDialog, self).__init__()
        self.setupUi(self)
        self.le_test.textChanged.connect(self.start_animation)

        self.m_animation = QtCore.QPropertyAnimation(
            self.le_test,
            b'backgroundColor',
            self,
            startValue=QtGui.QColor(255, 127, 127),
            endValue=QtGui.QColor(255, 255, 255),
            duration=1000,
        )

    @QtCore.pyqtSlot()
    def start_animation(self):
        if self.m_animation.state() == QtCore.QAbstractAnimation.Running:
            self.m_animation.stop()
        self.m_animation.start()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    dialog = LoginDialog()
    dialog.show()
    sys.exit(app.exec_())

这篇关于更改线程中的QObject样式表时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆