Python 向PyQt5中的线程函数发送信号会导致装饰槽的类型错误
我目前正在开发一个Gui,它会一直计数,直到达到给定的输入数字。应该可以在计数期间停止while循环,并使用按钮重新启动它,而无需结束主Gui线程。这已经起作用了,但仅当目标计数数在线程工作函数中是固定的,例如n=10->counts to 10,并且在不关闭Gui并更改主代码中的数字的情况下,不能更改为20或任何其他数字。我想对数字输入使用行编辑,并将所需的目标数字发送到线程,以便它在我开始计数时计数到给定的数字。但是,当启动Gui(在实现了通常需要发送和获取输入值的信号之后)在行编辑中键入数字并按下开始按钮时,我得到一个TypeError,指出“装饰的插槽没有与started()兼容的签名”(“started”应该连接线程和工作函数)。是否有人碰巧知道我在哪里犯了错误或必须更改某些内容和/或是否有解决此错误的方法 我非常绝望(我已经尝试解决这个问题很长一段时间了,在网上找不到任何提示…),非常感谢您的帮助!致以最良好的问候和感谢 这是带有注释的完整代码:Python 向PyQt5中的线程函数发送信号会导致装饰槽的类型错误,python,multithreading,pyqt5,signals-slots,qthread,Python,Multithreading,Pyqt5,Signals Slots,Qthread,我目前正在开发一个Gui,它会一直计数,直到达到给定的输入数字。应该可以在计数期间停止while循环,并使用按钮重新启动它,而无需结束主Gui线程。这已经起作用了,但仅当目标计数数在线程工作函数中是固定的,例如n=10->counts to 10,并且在不关闭Gui并更改主代码中的数字的情况下,不能更改为20或任何其他数字。我想对数字输入使用行编辑,并将所需的目标数字发送到线程,以便它在我开始计数时计数到给定的数字。但是,当启动Gui(在实现了通常需要发送和获取输入值的信号之后)在行编辑中键入数
import sys
import time
from PyQt5.QtWidgets import QPushButton, QMainWindow, QApplication, QLineEdit
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
class Worker(QObject):
finished = pyqtSignal() # signal out to main thread to alert it that work is completed
def __init__(self):
super(Worker, self).__init__()
self.working = True # flag to control our loop
@pyqtSlot(int) # should take the sended integer n from signal...
def work(self, n):
s = 0
while self.working:
if s != n: # count until input integer is reached (doesn't work so far)
print(s)
s += 1
time.sleep(0.5)
self.finished.emit() # alert gui that the loop stopped
class Window(QMainWindow):
sendnumber = pyqtSignal(int) # send signal (this is how it is usually done...?)
def __init__(self):
super(Window, self).__init__()
self.setGeometry(50, 50, 200, 250)
self.setWindowTitle("Program")
self.inputnumber=QLineEdit(self, placeholderText="number")
self.inputnumber.resize(self.inputnumber.minimumSizeHint())
self.inputnumber.move(50, 50)
self.startbtn = QPushButton("Start", self)
self.startbtn.resize(self.startbtn.minimumSizeHint())
self.startbtn.move(50, 100)
self.stopbtn = QPushButton("Stop", self)
self.stopbtn.resize(self.stopbtn.minimumSizeHint())
self.stopbtn.move(50, 150)
self.thread = None
self.worker = None
self.startbtn.clicked.connect(self.start_loop)
def start_loop(self):
self.thread = QThread() # a new thread to run the background tasks in
self.worker = Worker() # a new worker to perform those tasks
self.worker.moveToThread(self.thread) # move the worker into the thread, do this first before connecting the signals
self.thread.started.connect(self.worker.work) # begin worker object loop when the thread starts running
self.sendnumber.connect(self.worker.work) # connect input number to worker in thread
# this doesn't work so far and gives a TypeError!
self.stopbtn.clicked.connect(self.stop_loop) # stop the loop on the stop button click
self.worker.finished.connect(self.loop_finished) # do something in the gui when the worker loop ends
self.worker.finished.connect(self.thread.quit) # tell the thread it's time to stop running
self.worker.finished.connect(self.worker.deleteLater) # have worker mark itself for deletion
self.thread.finished.connect(self.thread.deleteLater) # have thread mark itself for deletion
# make sure those last two are connected to themselves or you will get random crashes
self.thread.start()
def stop_loop(self):
self.worker.working = False
# when ready to stop the loop, set the working flag to false
@pyqtSlot() # as far as I know you need this Slot to send the input to Slot in thread?
def getnumber(self):
try:
n = int(self.inputnumber.text()) # input for the work function in thread
print('Trying number...')
except:
print('Use an integer!')
return
self.sendnumber.emit(n) # emit integer signal
print('Emitting signal to worker...')
def loop_finished(self):
# received a callback from the thread that it's completed
print('Loop finished.')
if __name__ == '__main__':
def run():
app = QApplication(sys.argv)
gui = Window()
gui.show()
app.exec_()
run()
下面是显示错误的控制台输出:
Traceback (most recent call last):
File "C:/Users/***/WhileLoopInterruptTest2.py", line 63, in start_loop
self.thread.started.connect(self.worker.work)
TypeError: decorated slot has no signature compatible with started()
这就是所有的错误,Gui必须关闭。
我使用的是Python 3.6.1、Spyder 3.3.1和PyQt5.9。替换这一行:
self.thread.started.connect(self.worker.work)
为此:
self.thread.started.connect(lambda: self.worker.work(10))
您可以用文本框中的值替换10
。请记住首先将其转换为int
查看更详细的解释。有趣的是,我通常使用
partial
来代替lambdas<代码>从functools导入部分self.thread.started.connect(部分(self.worker.work,input=10))
@Guimoute我最近发现了部分。我一直在使用lambda表达式,使用partial的解决方案对我有效!不知何故,lambda没有完成这项工作(Gui在为输入设置新值后停止工作)。我还删除了getnumber函数和所有pyqtSlot的东西(不再需要了),因为我现在可以通过文本框控制目标编号。我将self.thread.started.connect(partial(self.worker.work,input=10))更改为self.thread.started.connect(partial(self.worker.work,n=int(self.inputnumber.text())。太好了,很高兴它有帮助<当您想强制某些特定的命名输入,而将其他输入留空时,code>partial非常方便。装饰器和默认值可能会填充其他输入。请参阅示例,其中显示了处理线程的3种不同且简单的方法。