Search code examples
pythonpython-multithreadingpyside2qtstylesheets

Program randomly crash when background-color of Qwidget changes


To set the background-color is fine, but my interface has a Qwidget that changes its background-color at certain point. That will cause the program to crash, and the crash stops when I comment out the color changing code.

This code below will certainly crash ,after running a little while.

import sys
from PySide2.QtWidgets import QWidget,QPushButton,QApplication,QListWidget,QGridLayout,\
QLabel,QMainWindow,QLineEdit,QScrollArea,QVBoxLayout,QMessageBox
from PySide2.QtCore import QTimer,QDateTime,QSize,Qt
from PySide2.QtGui import *
import requests,json
import configparser #
import os
import time
import threading #
from pynput import keyboard #
import requests,json
from math import * 
from functools import partial

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.resize(320,100)    
        self.setWindowTitle('aaaa')
        self.setStyleSheet("QMainWindow\
        {\
            background-color:white;\
            color:white;\
            margin:0px\
        }")
        self.InitializeWindow()
        self.i=0

    def InitializeWindow(self):
        self.statebar=QWidget(self)
        self.statebar.setGeometry(0, 40, 320, 54)
        print(sys._getframe().f_lineno)
        self.statebar.setStyleSheet("QWidget{background-color:#1EC5CD}")
        self.th_countdown=threading.Thread(target=self.countdown,args=())
        self.th_countdown.setDaemon(True)
        self.th_countdown.start()

        self.message_main=QLabel(self.statebar)
        self.message_main.setGeometry(150,7,40,37)
        self.message_main.setStyleSheet("background:transparent;color:white;margin:0px;font-size:45px;font-weight:bold")
        self.message_main.setWordWrap(True)
        self.message_main.hide()
        self.message_main.setAlignment(Qt.AlignTop|Qt.AlignLeft)
        self.message_signature=QLabel(self.statebar)
        self.message_signature.setGeometry(50,6,42,42)
        self.message_signature.setScaledContents(True)
        self.message_signature.setStyleSheet("background:transparent")

        self.message_result=QLabel(self.statebar)
        self.message_result.setGeometry(150,6,160,25)
        self.message_result.setStyleSheet("background:transparent;color:white;margin:0px;font-size:24px;font-weight:bold")
        self.message_result.setWordWrap(True)
        self.message_result.hide()
        self.message_detail=QLabel(self.statebar)
        self.message_detail.setGeometry(150,34,160,15)
        self.message_detail.setStyleSheet("background:transparent;color:white;margin:0px;font-size:14px")
        self.message_detail.setWordWrap(True)
        self.message_detail.hide()

    def bind(self, ret):
        self.message_result.show()
        self.message_detail.show()
        self.message_main.hide()
        if(ret%2==0):
            self.statebar.setStyleSheet("QWidget{background-color:#1EC5CD}") ##FA6400
            self.message_result.setText(str(ret))
            self.message_detail.setText(str(ret))
        else:
            self.statebar.setStyleSheet("QWidget{background-color:#FA6400}") ##FA6400
            self.message_result.setText(str(ret))
            self.message_detail.setText(str(ret))

    def countdown(self):
        while(1):
            time.sleep(0.2)
            self.i=self.i+1
            self.bind(self.i)

if __name__ == '__main__':
    app = 0
    app = QApplication(sys.argv)
    win = MainWindow()
    #icon = QIcon()
    #icon.addPixmap(QPixmap("./res/mdm.ico"),QIcon.Normal, QIcon.Off)
    #win.setWindowIcon(icon)
    win.show()

    sys.exit(app.exec_())
    pass

sometimes python throw out a error message "Could not parse stylesheet of object...", sometimes it just crash with no message.


Solution

  • The countdown method is running on a thread other than the GUI where you are modifying the GUI, and that action is not secure since 2 threads cannot access the same resource causing the problem you are pointing out. The solution is not to modify the GUI directly from another thread but through signals that are thread-safe:

    import sys
    import time
    import threading
    
    from PySide2.QtCore import Qt, Signal, Slot
    from PySide2.QtWidgets import QApplication, QLabel, QMainWindow, QWidget
    
    
    class MainWindow(QMainWindow):
        valueChanged = Signal(int)
    
        def __init__(self):
            super(MainWindow, self).__init__()
            self.resize(320, 100)
            self.setWindowTitle("aaaa")
            self.setStyleSheet(
                """
                QMainWindow
                {
                    background-color:white;
                    color:white;
                    margin:0px
                }
                """
            )
            self.InitializeWindow()
            self.i = 0
    
            self.valueChanged.connect(self.bind)
    
        def InitializeWindow(self):
            self.statebar = QWidget(self)
            self.statebar.setGeometry(0, 40, 320, 54)
            print(sys._getframe().f_lineno)
            self.statebar.setStyleSheet("QWidget{background-color:#1EC5CD}")
            self.th_countdown = threading.Thread(target=self.countdown)
            self.th_countdown.setDaemon(True)
            self.th_countdown.start()
    
            self.message_main = QLabel(self.statebar)
            self.message_main.setGeometry(150, 7, 40, 37)
            self.message_main.setStyleSheet(
                "background:transparent;color:white;margin:0px;font-size:45px;font-weight:bold"
            )
            self.message_main.setWordWrap(True)
            self.message_main.hide()
            self.message_main.setAlignment(Qt.AlignTop | Qt.AlignLeft)
            self.message_signature = QLabel(self.statebar)
            self.message_signature.setGeometry(50, 6, 42, 42)
            self.message_signature.setScaledContents(True)
            self.message_signature.setStyleSheet("background:transparent")
    
            self.message_result = QLabel(self.statebar)
            self.message_result.setGeometry(150, 6, 160, 25)
            self.message_result.setStyleSheet(
                "background:transparent;color:white;margin:0px;font-size:24px;font-weight:bold"
            )
            self.message_result.setWordWrap(True)
            self.message_result.hide()
            self.message_detail = QLabel(self.statebar)
            self.message_detail.setGeometry(150, 34, 160, 15)
            self.message_detail.setStyleSheet(
                "background:transparent;color:white;margin:0px;font-size:14px"
            )
            self.message_detail.setWordWrap(True)
            self.message_detail.hide()
    
        @Slot(int)
        def bind(self, ret):
            self.message_result.show()
            self.message_detail.show()
            self.message_main.hide()
            qss = "QWidget{background-color:%s}" % (
                "#1EC5CD" if ret % 2 == 0 else "#FA6400"
            )
            self.statebar.setStyleSheet(qss)
            self.message_result.setNum(ret)
            self.message_detail.setNum(ret)
    
        def countdown(self):
            while True:
                time.sleep(0.2)
                self.i += 1
                self.valueChanged.emit(self.i)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        win = MainWindow()
        win.show()
        sys.exit(app.exec_())