I'm making guitar tablature software where, when playback is started, the graphics update every 16th note (say, by moving a cursor to the right one space). I'm having trouble figuring out how to update QGraphicsItems from a thread without problems. In the example below (simplified from the original program), I have a QThread do the playback and redraw a QGraphicsRectItem to the right every 0.02 seconds. The problem is that the rectangle often freezes and remains frozen even after playback has been stopped.
Could anyone let me know what a better way to update a QGraphicsView from a thread would be?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import random
import sys
import threading
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        # add TablatureWindow
        self.tabWidget = QtGui.QTabWidget()
        self.setCentralWidget(self.tabWidget)
        self.setWindowTitle('Tablature Editor')    
        self.tablatureWindow = TablatureWindow(self)
        self.tabWidget.removeTab(0)
        self.tabWidget.addTab(self.tablatureWindow, 'Untitled')
        self.tablatureWindow.setFocus()
        self.positionWindow()       # center and enlargen window
        self.show()
    def positionWindow(self):
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        width = QtGui.QDesktopWidget().availableGeometry().width() - 100
        height = QtGui.QDesktopWidget().availableGeometry().height() - 100
        self.resize(width, height)
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())  
class TablatureWindow(QtGui.QGraphicsView):    
    def __init__(*args, **kwargs):
        start_time = time.time()
        self = args[0] 
        self._parent = args[1]
        QtGui.QGraphicsView.__init__(self)    
        self.scene = QtGui.QGraphicsScene(self)
        self.setScene(self.scene)
        self.scene.setSceneRect(QtCore.QRectF(0, 0, 20000, 2000))
        self.cursorItem = QtGui.QGraphicsRectItem(100, 100, 20, 20)
        self.cursorItem.setBrush(QtCore.Qt.black)
        self.scene.addItem(self.cursorItem)
        self.centerOn(0,0)
        self.isPlaying = False
    def keyPressEvent(self, e):
        key = e.key()
        # "p" starts or stops playback
        if key == QtCore.Qt.Key_P:
            if self.isPlaying == False:
                self.beginPlayback()
            else:
                self.stopPlayback()
    def beginPlayback(self):
        print('begin')
        self.isPlaying = True
        self.playbackThread = PlaybackThread(self)
        self.playbackThread.start()
    def stopPlayback(self):
        print('stop')
        self.isPlaying = False
        self.playbackThread.stopPlayback()
class PlaybackThread(QtCore.QThread):
    def __init__(self, parent):
        QtCore.QThread.__init__(self)
        self._parent = parent
        self.doStopThread = False
    def run(self):
        self.startTime = time.time()
        dt = 0.02    # move every dt seconds
        for i in range(0, 2000):            # keep going right for 1000 spaces
            if not self.doStopThread:
                x = self._parent.cursorItem.rect().x()
                y = self._parent.cursorItem.rect().y()
                w = self._parent.cursorItem.rect().width()
                h = self._parent.cursorItem.rect().height()
                self._parent.cursorItem.setRect(x+10, y, w, h)
                ideal_dt = (i+1) * dt + self.startTime
                dt2 = ideal_dt-time.time()
                time.sleep(dt2)             # sleep for remaining time
    def stopPlayback(self):
        self.doStopThread = True
    def __del__(self):
        self.wait()
def main():
    app = QtGui.QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec_())
if __name__ == '__main__':
    main() 
 
     
    