如何在`QRunnable`类内部使用`pyqtSlot()`装饰器正确地为函数添加类型提示?

huangapple go评论96阅读模式
英文:

How to correctly type-hint a function with `pyqtSlot()` decorator inside `QRunnable` class?

问题

状态

  1. 我正在学习如何在PyQt库中使用并发/线程,教程来源于www.pythonguis.com。以下是该教程中的简化/部分代码。
  2. 我希望在我的代码中使用严格的类型提示。但是我在这段代码中使用mypy时遇到了错误消息(在第二个源代码块中显示)。

问题

  • 在使用pyqtSlot()装饰器时,如何正确地为函数添加类型提示?
  • 在这种情况下,Superclass(返回None)和Subclass(返回str|None)分别是什么?

我尝试解决的问题

  • 我对错误消息感到不满意,这让我很困扰。我浪费了太多时间来解决一行代码的问题。我在互联网上找不到有用的信息。
  • 定义(或者说stub文件)中说pyqtSlot()是这样的:
    def pyqtSlot(*types, name: typing.Optional[str] = ..., result: typing.Optional[str] = ...) -> typing.Callable[..., typing.Optional[str]]: ...,由于我对装饰器没有经验,我无法解释这个定义。
  • stub文件:QtCore.pyi中说Class QRunnablerun方法是这样的:def run(self) -> None: ...
from typing import Callable
from PyQt5.QtCore import QObject, QRunnable, pyqtSignal, pyqtSlot

import traceback
import sys


class WorkerSignal(QObject):
    finished = pyqtSignal()
    error = pyqtSignal(tuple)
    result = pyqtSignal(object)
    progress = pyqtSignal(int)


class Worker(QRunnable):
    def __init__(self, fn: Callable[[], str]) -> None:
        super(Worker, self).__init__()
        self.fn: Callable[[], str] = fn
        self.signals: WorkerSignal = WorkerSignal()

    @pyqtSlot()
    def run(self) -> None:   # <====================== 这一行
        try:
            result: str = self.fn()
        except Exception:
            traceback.print_exc()
            exctype, value = sys.exc_info()[:2]
            self.signals.error.emit((exctype, value, traceback.format_exc()))
        else:
            self.signals.result.emit(result)
        finally:
            self.signals.finished.emit()
$ mypy /tmp/tmp.py
/tmp/tmp.py:22: error: Signature of "run" incompatible with supertype "QRunnable"  [override]
/tmp/tmp.py:22: note:      Superclass:
/tmp/tmp.py:22: note:          def run(self) -> None
/tmp/tmp.py:22: note:      Subclass:
/tmp/tmp.py:22: note:          str | None
Found 1 error in 1 file (checked 1 source file)

编辑

  • 以下猜测是否正确?

    • pyqtSlot()接受返回类型为typing.Optional[str](即str | None)的函数。也许这是subclass
    • QRunnablerun()方法只返回None,也许这是superclass
    • 在这种情况下,正确的问题应该是:如果装饰器的返回值函数的返回值不同,如何使用装饰器?这个问题是否正确?
英文:

Status:

  1. I'm learning how to use concurrency/threads within PyQt library from here www.pythonguis.com. Below is the simplified/part of code from this tutorial.
  2. I want strict type hinting in my code. And I'm getting error messages on this code from mypy. (Shown on the 2-nd source-code block).

Question:

  • How to correctly type-hint the function while using pyqtSlot() decorator?
  • What is Superclass (that return None), and what is Subclass (that returns str|None) in this case?

What I tried to solve:

  • My autistic ass doesn't sit well with error is showing on my text-editor. And I wasted too much time to solve single line. I couldn't find anything useful on the internet.
  • The definition (or should I say stub file) says pyqtSlot() is:
    def pyqtSlot(*types, name: typing.Optional[str] = ..., result: typing.Optional[str] = ...) -&gt; typing.Callable[..., typing.Optional[str]]: ..., I'm unable to interpret this due to no-experience on decorators.
  • stub file: QtCore.pyi says that Class QRunnable's run is: def run(self) -&gt; None: ....
from typing import Callable
from PyQt5.QtCore import QObject, QRunnable, pyqtSignal, pyqtSlot

import traceback
import sys


class WorkerSignal(QObject):
    finished = pyqtSignal()
    error = pyqtSignal(tuple)
    result = pyqtSignal(object)
    progress = pyqtSignal(int)


class Worker(QRunnable):
    def __init__(self, fn: Callable[[], str]) -&gt; None:
        super(Worker, self).__init__()
        self.fn:Callable[[], str] = fn
        self.signals: WorkerSignal = WorkerSignal()

    @pyqtSlot()
    def run(self) -&gt; None:   # &lt;====================== this line
        try:
            result: str = self.fn()
        except Exception:
            traceback.print_exc()
            exctype, value = sys.exc_info()[:2]
            self.signals.error.emit((exctype, value, traceback.format_exc()))
        else:
            self.signals.result.emit(result)
        finally:
            self.signals.finished.emit()
$ mypy /tmp/tmp.py
/tmp/tmp.py:22: error: Signature of &quot;run&quot; incompatible with supertype &quot;QRunnable&quot;  [override]
/tmp/tmp.py:22: note:      Superclass:
/tmp/tmp.py:22: note:          def run(self) -&gt; None
/tmp/tmp.py:22: note:      Subclass:
/tmp/tmp.py:22: note:          str | None
Found 1 error in 1 file (checked 1 source file)

EDIT:

  • Is the following guess correct?

    • pyqtSlot() takes function that returns typing.Optional[str] (i.e. str | None). Perhaps its the subclass?
    • QRunnable's method run() only return None, Perhaps it's the superclass?
    • In this case, correct question would be: How to use decorator, if decorator&#39;s return and function&#39;s return are different? Would this be the correct question?

答案1

得分: 2

除了类型提示之外,你不应该在QRunnable方法中使用pyqtSlot。

pyqtSlot装饰器仅用于继承自QObject的类的方法,而QRunnable不是。我已经查阅了几个教程,它们采用了这种不好的做法,因为它并没有带来任何好处。

英文:

Other than the typehint you should not use pyqtSlot in a QRunnable method.

The pyqtSlot decorator is only used in methods of classes that inherit from QObject and QRunnable is not. I have reviewed several tutorials that resort to this bad practice since it does not bring any benefit.

huangapple
  • 本文由 发表于 2023年8月9日 09:45:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864077.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定