我正在制作吉他指法软件,当开始播放时,图形会每16个音符更新一次(例如,将光标移动到右侧的一个空格)。我无法弄清楚如何从线程更新QGraphicsItems没有问题。在下面的示例中(从原始程序简化),我有一个QThread进行播放并每0.02秒重新绘制一个QGraphicsRectItem。问题是矩形经常冻结并且即使在停止播放后仍然保持冻结状态。
有谁能告诉我从线程更新QGraphicsView的更好方法是什么?
#!/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()
答案 0 :(得分:0)
我想我已经通过用QTimer替换线程找到了部分答案:
#!/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.playback = Playback(self)
def stopPlayback(self):
print('stop')
self.isPlaying = False
self.playback.stopPlayback()
class Playback:
def __init__(self, parent):
self._parent = parent
self.timer = QtCore.QTimer()
QtCore.QObject.connect(
self.timer,
QtCore.SIGNAL("timeout()"),
self.doStuff)
self.timer.start(20) # 20 ms
def doStuff(self):
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)
self._parent.update()
def stopPlayback(self):
self.timer.stop()
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
我不再看到冻结(但是,无论如何),虽然我确实看到一些图形故障,例如当我滚动窗口时从移动矩形留下的小矩形。
答案 1 :(得分:0)
您无法使用非主线程中的GUI对象。我对Cannot send posted events for objects in another thread的回答完全适用于此。