gct
11th March 2008, 03:56
OK so I've been having some unexpected async reply errors in my GUI, and I've pinned most of them down to doing stupid stuff like updating widgets directly from other threads. I thought I'd gotten rid of it all by moving to custom events, but apparently now. Now I've tracked one down to a widget I have, but it looks to me like it's obeying all the rules of threaded Qt programming, but no matter what I do, when I let the threaded loop run, I can cause an async reply by maximizing/minimizing the GUI rapidly. If I take the loop out, no errors. Can anyone see anything obvious I'm doing wrong:
I've trimmed out some unimportant stuff, but this is basically a widget to source a motion jpeg stream and display it on screen.
Update: I should elaborate that the postCustomEvent call causes the event thread to call the update() method on the widget, thus displaying new frames of video.
class QCameraView(QFrame):
def startMainLoop(self):
# We have a valid HW connection
self.hwConnection = True
# Set pixmap
self.curPixmap = None
# Start up reading thread
self.running = 1
thread.start_new_thread(self.mainLoop, ())
################################################## ################
def __init__(self, parent, cameraHost, testMode=0):
# Initialize parent, send WNoAutoErase to solve flickering problems
QFrame.__init__(self, parent, "none", Qt.WNoAutoErase )
# Make frame shape flat
self.setFrameShape(QFrame.MenuBarPanel)
self.setFrameShadow(QFrame.Plain)
# Open up an image for when there's no connection
self.noConnectPM = QPixmap("Images/nocamera.bmp")
# Return if we don't have physical hardware lying around
if (testMode):
self.curPixmap = self.noConnectPM
self.update()
return
# Save camera host
self.cameraHost = cameraHost
# Set current pixmap
self.curPixmap = None
# Try to open HTTP socket, start ping thread if there's an error
try:
self.inputSocket = urlopen("http://%s/mjpg/video.mjpg" % cameraHost)
except:
self.startPingThread()
else:
if (not self.inputSocket):
self.startPingThread()
else:
self.startMainLoop()
################################################## ################
# Simple destructor, closes down main thread and cleans up sockets
def __del__(self):
self.stop()
def stop(self):
print "[ Shutting down camera connection ]"
self.running = 0
time.sleep(.5)
self.inputSocket.close()
################################################## ################
# Called whenever the widget needs to paint itself
# Simple does a bit-blit if there's a valid pixmap available
def paintEvent(self, event):
if(self.curPixmap):
bitBlt(self, 0, 0, self.curPixmap)
################################################## ################
# Responsible for decoding the motion JPEG stream from the camera
# Basically multiple frames of JPEG data with some frame markers injected
# In the event of a read error, this thread is closed and the ping thread is started
def mainLoop(self):
<some arbitrary pre-processing stuff goes here>
data = read(self.inputSocket, contentSize)
if (data == None):
startPingThread()
return
# Create a QImage
videoImage = QImage()
status = videoImage.loadFromData(data, "JPEG")
# Create a pixmap
self.curPixmap = QPixmap(videoImage)
# Update canvas
postCustomEvent(CameraFrameUpdate())
# Sleep
time.sleep(1.0/float(FRAMERATE))
I've trimmed out some unimportant stuff, but this is basically a widget to source a motion jpeg stream and display it on screen.
Update: I should elaborate that the postCustomEvent call causes the event thread to call the update() method on the widget, thus displaying new frames of video.
class QCameraView(QFrame):
def startMainLoop(self):
# We have a valid HW connection
self.hwConnection = True
# Set pixmap
self.curPixmap = None
# Start up reading thread
self.running = 1
thread.start_new_thread(self.mainLoop, ())
################################################## ################
def __init__(self, parent, cameraHost, testMode=0):
# Initialize parent, send WNoAutoErase to solve flickering problems
QFrame.__init__(self, parent, "none", Qt.WNoAutoErase )
# Make frame shape flat
self.setFrameShape(QFrame.MenuBarPanel)
self.setFrameShadow(QFrame.Plain)
# Open up an image for when there's no connection
self.noConnectPM = QPixmap("Images/nocamera.bmp")
# Return if we don't have physical hardware lying around
if (testMode):
self.curPixmap = self.noConnectPM
self.update()
return
# Save camera host
self.cameraHost = cameraHost
# Set current pixmap
self.curPixmap = None
# Try to open HTTP socket, start ping thread if there's an error
try:
self.inputSocket = urlopen("http://%s/mjpg/video.mjpg" % cameraHost)
except:
self.startPingThread()
else:
if (not self.inputSocket):
self.startPingThread()
else:
self.startMainLoop()
################################################## ################
# Simple destructor, closes down main thread and cleans up sockets
def __del__(self):
self.stop()
def stop(self):
print "[ Shutting down camera connection ]"
self.running = 0
time.sleep(.5)
self.inputSocket.close()
################################################## ################
# Called whenever the widget needs to paint itself
# Simple does a bit-blit if there's a valid pixmap available
def paintEvent(self, event):
if(self.curPixmap):
bitBlt(self, 0, 0, self.curPixmap)
################################################## ################
# Responsible for decoding the motion JPEG stream from the camera
# Basically multiple frames of JPEG data with some frame markers injected
# In the event of a read error, this thread is closed and the ping thread is started
def mainLoop(self):
<some arbitrary pre-processing stuff goes here>
data = read(self.inputSocket, contentSize)
if (data == None):
startPingThread()
return
# Create a QImage
videoImage = QImage()
status = videoImage.loadFromData(data, "JPEG")
# Create a pixmap
self.curPixmap = QPixmap(videoImage)
# Update canvas
postCustomEvent(CameraFrameUpdate())
# Sleep
time.sleep(1.0/float(FRAMERATE))