python – PyQt:当使用partial()作为插槽时,moveToThread不起作用
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了python – PyQt:当使用partial()作为插槽时,moveToThread不起作用,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5506字,纯文字阅读大概需要8分钟。
内容图文
![python – PyQt:当使用partial()作为插槽时,moveToThread不起作用](/upload/InfoBanner/zyjiaocheng/709/7e8440f7d82f42dc80ac8717646e0033.jpg)
我正在构建一个运行生产者(工作者)的小型GUI应用程序,GUI按需消耗输出并绘制它(使用pyqtgraph).
由于生产者是一个阻塞函数(运行需要一段时间),我(据说)将它移动到自己的线程.
从生产者调用QThread.currentThreadId()时,它输出与主GUI线程相同的编号.因此,首先执行worker,然后执行所有绘图函数调用(因为它们在同一线程的事件队列中排队).我怎样才能解决这个问题?
示例运行部分:
gui thread id 140665453623104
worker thread id: 140665453623104
这是我的完整代码:
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import pyqtSignal
import pyqtgraph as pg
import numpy as np
from functools import partial
from Queue import Queue
import math
import sys
import time
class 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):
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.moveToThread(self.workerThread)
self.worker.termino.connect(self.setTermino)
# 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.btn.clicked.connect(self.graficar)
self.workerThread.start()
self.show()
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:
QtCore.QTimer.singleShot(1, self.graficar)
if __name__ == '__main__':
app = QtGui.QApplication([])
window = MainWindow()
QtCore.QTimer.singleShot(0, window.run);
sys.exit(app.exec_())
解决方法:
问题是Qt尝试根据插槽所在的线程选择连接类型(当你调用signal.connect(slot)时).因为你已将QThread中的插槽包裹为部分,所以你要连接的插槽驻留在MainThread(GUI线程)中.您可以覆盖连接类型(作为connect()的第二个参数,但这没有帮助,因为partial创建的方法将始终存在于MainThread中,因此将连接类型设置为Qt.QueuedConnection无济于事.
我能看到的唯一方法就是建立一个中继信号,其唯一目的是有效地将没有参数的发射信号(例如来自按钮的点击信号)更改为带有一个参数的信号(你的m)参数).这样您就不需要使用partial()将插槽包装在QThread中.
代码如下.我在主windows类中创建了一个带有一个名为’relay’的参数(一个int)的信号.单击按钮的信号连接到主窗口类中的方法,此方法有一行代码,用于发出我创建的自定义信号.您可以扩展此方法(relay_signal())以获取整数以m(本例中为500)传递给QThread,从您喜欢的地方!
所以这是代码:
from functools import partial
from Queue import Queue
import math
import sys
import time
class 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方法以继续绘图(即使在线程终止后).我想这可能就是为什么你需要QThread中的time.sleep,现在也被删除了.
另外关于你在代码中关于moveToThread放置位置的注释,它现在在哪里是正确的.它应该在将QThread插槽连接到信号的调用之前,并在此堆栈溢出帖子中讨论其原因:PyQt: Connecting a signal to a slot to start a background operation
内容总结
以上是互联网集市为您收集整理的python – PyQt:当使用partial()作为插槽时,moveToThread不起作用全部内容,希望文章能够帮你解决python – PyQt:当使用partial()作为插槽时,moveToThread不起作用所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。