Search code examples
python-2.7pyqt4

pyqt marquee text hangs after few hours


I am using following code for creating scrolling text using pyqt. Also I am creating rectangular box in my UI using QPainter. The problem is that the code as such works fine but after sometime it hangs. The message stops scrolling while other part UI is functioning. I used code from earlier stack overflow question for scrolling text Marquee effect. Following is the some part of my code

class MarqueeLabel(QtGui.QLabel):
    def __init__(self, parent=None):
        QtGui.QLabel.__init__(self, parent)
        self.px = 0
        self.py = 15
        self._direction = Qt.RightToLeft #Qt.LeftToRight
        self.setWordWrap(True)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update)
        self.timer.start(20)
        self._speed = 2
        self.textLength = 0
        self.fontPointSize = 0
        self.setAlignment(Qt.AlignVCenter)
        self.setFixedHeight(self.fontMetrics().height())

    def setFont(self, font, size):
        newfont = QtGui.QFont(font, size, QtGui.QFont.Bold)
        QtGui.QLabel.setFont(self, newfont)
        self.setFixedHeight(self.fontMetrics().height())

    def updateCoordinates(self):
        align = self.alignment()
        if align == Qt.AlignTop:
            self.py = 10
        elif align == Qt.AlignBottom:
            self.py = self.height() - 10
        elif align == Qt.AlignVCenter:
            self.py = self.height() / 2
        self.fontPointSize = self.font().pointSize() / 2
        self.textLength = self.fontMetrics().width(self.text())

    def setAlignment(self, alignment):
        self.updateCoordinates()
        QtGui.QLabel.setAlignment(self, alignment)

    def resizeEvent(self, event):
        self.updateCoordinates()
        QtGui.QLabel.resizeEvent(self, event)

    def paintEvent(self, event):
        painter = QPainter(self)
        if self._direction == Qt.RightToLeft:
            self.px -= self.speed()
            if self.px <= -self.textLength:
                self.px = self.width()
        else:
            self.px += self.speed()
            if self.px >= self.width():
                self.px = -self.textLength
        painter.drawText(self.px, self.py + self.fontPointSize, self.text())
        painter.translate(self.px, 0)

    def speed(self):
        return self._speed

    def setSpeed(self, speed):
        self._speed = speed

    def setDirection(self, direction):
        self._direction = direction
        if self._direction == Qt.RightToLeft:
            self.px = self.width() - self.textLength
        else:
            self.px = 0
        self.update()

    def pause(self):
        self.timer.stop()

    def unpause(self):
        self.timer.start()

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        self.signalUpdateUI()
    def signalUpdateUI(self):
        updateUIThread = Thread(target=self.updateUI)
        updateUIThread.daemon = True
        updateUIThread.start()
    def updateUI(self):
        while(1==1):
            #update some text color and contents
            #update marquee label contents
            self.update()
            sleep(2)
    def paintEvent(self, event):
        QWidget.paintEvent(self, event)
        painter = QPainter(self)
        pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)
        painter.setPen(pen)
        painter.drawLine(0,190, 1920,190)
        painter.drawLine(0,300, 1920,300)
        painter.drawLine(0,840, 1920,840)
        painter.drawLine(0,930, 1920,930)

        # Reactangle Box Settings
        painter.setPen(self._rectBoxLine_color)
        painter.setBrush(self._rectBox_color)
        rect = QRect(1430,300,470,535)

        painter.drawRect(rect)
        painter.setFont(QtGui.QFont('Consolas', 60, QtGui.QFont.Bold))
        painter.setPen(QtGui.QColor(0, 0, 0))      
        painter.drawText(QRect(1430,300,470,535), QtCore.Qt.AlignCenter, self._rect_text)

I don't know why the scrolling message stops after few hours even though the other part UI is functioning.


Solution

  • The implementation of the marqueLabel is efficient so I do not think that is the cause of the error, on the other hand the code that shows is also not executable but what it shows you can notice the misuse of threads for periodic tasks, in the case of the GUI is forbidden to directly modify the GUI from another thread, in addition to the task that is performed is not heavy so you must use a QTimer instead.

    from PyQt4 import QtCore, QtGui
    
    class MarqueeLabel(QtGui.QLabel):
        def __init__(self, parent=None):
            super(MarqueeLabel, self).__init__(parent)
            self.px = 0
            self.py = 15
            self._direction = QtCore.Qt.RightToLeft #Qt.LeftToRight
            self.setWordWrap(True)
            self.timer = QtCore.QTimer(self, interval=20)
            self.timer.timeout.connect(self.update)
            self.timer.start()
            self._speed = 2
            self.textLength = 0
            self.fontPointSize = 0
            self.setAlignment(QtCore.Qt.AlignVCenter)
            self.setFixedHeight(self.fontMetrics().height())
    
        def setFont(self, font, size):
            newfont = QtGui.QFont(font, size, QtGui.QFont.Bold)
            QtGui.QLabel.setFont(self, newfont)
            self.setFixedHeight(self.fontMetrics().height())
    
        def updateCoordinates(self):
            align = self.alignment()
            if align == QtCore.Qt.AlignTop:
                self.py = 10
            elif align == QtCore.Qt.AlignBottom:
                self.py = self.height() - 10
            elif align == QtCore.Qt.AlignVCenter:
                self.py = self.height() / 2
            self.fontPointSize = self.font().pointSize() / 2
            self.textLength = self.fontMetrics().width(self.text())
    
        def setAlignment(self, alignment):
            self.updateCoordinates()
            QtGui.QLabel.setAlignment(self, alignment)
    
        def resizeEvent(self, event):
            self.updateCoordinates()
            QtGui.QLabel.resizeEvent(self, event)
    
        def paintEvent(self, event):
            painter = QtGui.QPainter(self)
            if self._direction == QtCore.Qt.RightToLeft:
                self.px -= self.speed()
                if self.px <= -self.textLength:
                    self.px = self.width()
            else:
                self.px += self.speed()
                if self.px >= self.width():
                    self.px = -self.textLength
            painter.drawText(self.px, self.py + self.fontPointSize, self.text())
            painter.translate(self.px, 0)
    
        def speed(self):
            return self._speed
    
        def setSpeed(self, speed):
            self._speed = speed
    
        def setDirection(self, direction):
            self._direction = direction
            if self._direction == Qt.RightToLeft:
                self.px = self.width() - self.textLength
            else:
                self.px = 0
            self.update()
    
        def pause(self):
            self.timer.stop()
    
        def unpause(self):
            self.timer.start()
    
    class MainWindow(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
            self._rectBoxLine_color = QtGui.QColor(QtCore.Qt.green)
            self._rectBox_color = QtGui.QBrush(QtCore.Qt.blue)
            self._rect_text = "_rect_text"
    
            self.marquee = MarqueeLabel(self)
            self.signalUpdateUI()
    
        def signalUpdateUI(self):
            timer = QtCore.QTimer(self, interval=2000)
            timer.timeout.connect(self.updateUI)
            timer.start()
            self.updateUI()
    
        def updateUI(self):
                #update some text color and contents
                #update marquee label contents
                self.marquee.setText(QtCore.QDateTime.currentDateTime().toString())
                self.update()
    
        def paintEvent(self, event):
            super(MainWindow, self).paintEvent(event)
            painter = QtGui.QPainter(self)
            pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)
            painter.setPen(pen)
            painter.drawLine(0,190, 1920,190)
            painter.drawLine(0,300, 1920,300)
            painter.drawLine(0,840, 1920,840)
            painter.drawLine(0,930, 1920,930)
    
            # Reactangle Box Settings
            painter.setPen(self._rectBoxLine_color)
            painter.setBrush(self._rectBox_color)
            rect = QtCore.QRect(1430,300,470,535)
    
            painter.drawRect(rect)
            painter.setFont(QtGui.QFont('Consolas', 60, QtGui.QFont.Bold))
            painter.setPen(QtGui.QColor(0, 0, 0))      
            painter.drawText(QtCore.QRect(1430,300,470,535), QtCore.Qt.AlignCenter, self._rect_text)
    
    
    if __name__ == '__main__':
        import sys
        app = QtGui.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())