【问题标题】:Sending signals to Thread function in PyQt5 leads to TypeError of decorated slot在 PyQt5 中向 Thread 函数发送信号会导致装饰槽的 TypeError
【发布时间】:2019-01-24 10:05:08
【问题描述】:

我目前正在研究一个 Gui,它会一直计数直到达到给定的输入数字。应该可以在计数期间停止 while 循环,然后使用按钮重新启动它,而无需结束 Gui 主线程。这已经有效,但前提是目标计数在线程工作函数中是固定的,例如n = 10 -> 计数为 10 并且不能更改为 20 或任何其他数字,除非关闭 Gui 并更改主代码中的数字。我想对数字输入使用行编辑并将所需的目标数字发送到线程,以便在我开始计数时它计数到给定的数字。但是,当启动 Gui 时(在实现通常需要发送和获取输入值的信号之后)在行编辑中键入数字并按下开始按钮,我得到一个 TypeError,指出'装饰插槽没有签名兼容started()' ('started' 应该连接线程和工作函数)。有没有人偶然知道我在哪里犯了错误或必须更改某些内容和/或是否有解决此错误的方法?

我很绝望(我已经尝试解决这个问题很长一段时间了,但在网上找不到任何提示......),非常感谢任何帮助!最好的问候和感谢!

这是带有 cmets 的完整代码:

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。

【问题讨论】:

标签: python multithreading pyqt5 signals-slots qthread


【解决方案1】:

替换这一行:

self.thread.started.connect(self.worker.work)

用这个:

self.thread.started.connect(lambda: self.worker.work(10))

您可以将10 替换为文本框中的值。请记住先将其转换为int

结帐this article以获得更详细的解释。

【讨论】:

  • 有趣,我通常使用partial 来代替 lambdas。 from functools import partialself.thread.started.connect(partial(self.worker.work, input=10))
  • @Guimute 我最近才发现partial。我一直都是 lambda 表达式
  • 部分解决方案对我有用!不知何故,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())))
  • 完美,很高兴它有帮助! partial 在您想强制 some 命名输入特别是让其他人为空时很方便。装饰器和默认值可能会填充其他输入。
猜你喜欢
  • 2016-07-27
  • 1970-01-01
  • 2019-04-15
  • 1970-01-01
  • 2016-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-02
相关资源
最近更新 更多