Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looping QProgressBar gives error >> QObject::installEventFilter: Cannot filter events for objects in a different thread

This question seems to have been asked many times in many different forms but I haven't managed to find one with a -relevant to my code- solution.

When I run the program it shows

QObject::installEventFilter: Cannot filter events for objects in a different thread.

Despite this the code works initially but after a while it bombs and python gives an error saying its stopped working.

My code is as follows:

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from xml.etree import ElementTree as ET
import os , time

class LayoutCreator(QDialog):
    def __init__(self , parent=None):
        super(LayoutCreator, self).__init__(parent)
        self.Cameras_Update()


    def Cameras_Update( self ):                                             # Get all shots with camera plots and add them to the cameras_tree
        busyBar = sqrl_QtTools.BusyBar( text = "Gathering Camera Data" )    # Looping progress bar
        busyBar.start()

        # loop through folder structure storing data                

        busyBar.Kill()                                                      # Close looping progress bar    


class BusyBar(QThread):                     # Looping progress bar
    def __init__(self, text = "" ):
        QThread.__init__(self)
        self.text = text
        self.stop = False

    def run( self ):
        self.proBar = QProgressBar()
        self.proBar.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.SplashScreen )
        self.proBar.setMinimum( 0 )
        self.proBar.setMaximum( 100 )
        self.proBar.setTextVisible( True )
        self.proBar.setFormat( self.text )
        self.proBar.setValue( 0 )
        self.proBar.setFixedSize( 500 , 50 )
        self.proBar.setAlignment(Qt.AlignCenter)
        self.proBar.show()
        while not self.stop:                # keep looping while self is visible
            # Loop sending mail 
            for i in range(100):
                progress = self.proBar.value()
                progress = progress + 1
                self.proBar.setValue( progress )

                time.sleep(0.05)
            self.proBar.setValue( 0 )
        self.proBar.hide()

    def Kill(self):
        self.stop = True
like image 787
Jay Avatar asked Nov 04 '11 10:11

Jay


1 Answers

You can't create or access any QWidget outside the main thread.

You can use signals and slots to indirectly access widgets from the other thread:

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, time

class BusyBar(QThread):                     # Looping progress bar
    # create the signal that the thread will emit
    changeValue = pyqtSignal(int)
    def __init__(self, text = "" ):
        QThread.__init__(self)
        self.text = text
        self.stop = False
        self.proBar = QProgressBar()
        self.proBar.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.SplashScreen )
        self.proBar.setRange( 0, 100 )
        self.proBar.setTextVisible( True )
        self.proBar.setFormat( self.text )
        self.proBar.setValue( 0 )
        self.proBar.setFixedSize( 500 , 50 )
        self.proBar.setAlignment(Qt.AlignCenter)
        self.proBar.show()

        self.changeValue.connect(self.proBar.setValue, Qt.QueuedConnection)
        # Make the Busybar delete itself and the QProgressBar when done        
        self.finished.connect(self.onFinished)

    def run( self ):
        while not self.stop:                # keep looping while self is visible
            # Loop sending mail 
            for i in range(100):
                # emit the signal instead of calling setValue
                # also we can't read the progress bar value from the thread
                self.changeValue.emit( i )
                time.sleep(0.05)
            self.changeValue.emit( 0 )

    def onFinished(self):
        self.proBar.deleteLater()
        self.deleteLater()

    def Kill(self):
        self.stop = True

class LayoutCreator(QDialog):
    def __init__(self , parent=None):
        super(LayoutCreator, self).__init__(parent)
        self.Cameras_Update()

    def Cameras_Update( self ):                                       
        # Looping progress bar 
        self.busyBar = BusyBar( text = "Gathering Camera Data" )
        self.busyBar.start()

        # loop through folder structure storing data

        # Simulate async activity that doesn't block the GUI event loop
        # (if you do everything without giving control back to 
        # the event loop, you have to call QApplication.processEvents()
        # to allow the GUI to update itself )
        QTimer.singleShot(10000, self.stopBar)

    def stopBar(self):
        self.busyBar.Kill()                        # Close looping progress bar    

app = QApplication(sys.argv)
win = LayoutCreator()
win.show();
sys.exit(app.exec_())

Or

If you only want a busy indicator, you can simply set both the minimum and maximum of the QProgressBar to 0, and you won't need a thread, as indicated in the documentation.

like image 169
alexisdm Avatar answered Sep 16 '22 22:09

alexisdm