问题是Qt尝试
signal.connect(slot)根据
slot存在的线程来选择连接类型(当您调用时)。因为您已经
partial用来将QThread封装在插槽中,所以要连接的插槽位于MainThread(GUI线程)中。
)。您可以覆盖连接类型(作为的第二个参数,
connect()但这没有帮助,因为by创建的方法
partial将始终存在于MainThread中,因此将连接类型设置为by
Qt.QueuedConnection无效。
我可以看到的唯一解决方法是设置一个中继信号,其唯一目的是将没有参数的发射信号(例如,按钮的单击信号)有效地更改为一个参数(您的
m参数)的信号。)。这样,您就不需要使用来包装QThread中的插槽
partial()。
代码如下。我已经在主Windows类中创建了一个带有称为“
relay”的参数(一个int)的信号。按钮
clicked信号连接到主窗口类中的一个方法,该方法有一行代码,可发出我创建的自定义信号。您可以扩展此方法(
relay_signal()),以从任意位置获取要传递给QThread的整数
m(本例中为500)!
所以这是代码:
from functools import partialfrom Queue import Queueimport mathimport sysimport timeclass Worker(QtCore.QObject): termino = pyqtSignal() def __init__(self, q=None, parent=None): super(Worker, self).__init__(parent) self.q = q def run(self, m=30000): print('worker thread id: {}'.format(QtCore.QThread.currentThreadId())) for x in xrange(m): #y = math.sin(x) y = x**2 #time.sleep(0.001) # Weird, plotting stops if this is not present... self.q.put((x,y,y)) print('Worker finished') self.termino.emit()class MainWindow(QtGui.QWidget): relay = pyqtSignal(int) def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.q = Queue() self.termino = False self.worker = Worker(self.q) self.workerThread = None self.btn = QtGui.QPushButton('Start worker') self.pw = pg.PlotWidget(self) pi = self.pw.getPlotItem() pi.enableAutoRange('x', True) pi.enableAutoRange('y', True) self.ge1 = pi.plot(pen='y') self.xs = [] self.ys = [] layout = QtGui.QVBoxLayout(self) layout.addWidget(self.pw) layout.addWidget(self.btn) self.resize(400, 400) def run(self): self.workerThread = QtCore.QThread() self.worker.termino.connect(self.setTermino) self.worker.moveToThread(self.workerThread) # moveToThread doesn't work here # self.btn.clicked.connect(partial(self.worker.run, 30000)) # moveToThread will work here # assume def worker.run(self): instead of def worker.run(self, m=30000) #self.btn.clicked.connect(self.worker.run) self.relay.connect(self.worker.run) self.btn.clicked.connect(self.relay_signal) self.btn.clicked.connect(self.graficar) self.workerThread.start() self.show() def relay_signal(self): self.relay.emit(500) def setTermino(self): self.termino = True def graficar(self): if not self.q.empty(): e1,e2,ciclos = self.q.get() self.xs.append(ciclos) self.ys.append(e1) self.ge1.setData(y=self.ys, x=self.xs) if not self.termino or not self.q.empty(): QtCore.QTimer.singleShot(1, self.graficar)if __name__ == '__main__': app = QtGui.QApplication([]) window = MainWindow() QtCore.QTimer.singleShot(0, window.run); sys.exit(app.exec_())graficar如果队列中仍然有数据,我还修改了该方法以继续绘制(即使在线程终止之后)。我认为这可能就是为什么您需要
time.sleepQThread中的原因,该问题现在也已删除。
另外,关于代码中有关放置
moveToThread位置(现在 正确)的 注释 也是正确的
。应该在将QThread插槽连接到信号的调用之前,并且在此堆栈溢出文章中讨论了这样做的原因:PyQt:将信号连接到插槽以启动后台操作



