mottosso / Qt.py

Minimal Python 2 & 3 shim around all Qt bindings - PySide, PySide2, PyQt4 and PyQt5.
MIT License
896 stars 252 forks source link

stubs missing signal.connect, disconnect and emit #389

Closed p0las closed 4 months ago

p0las commented 7 months ago

here is a minimalistic example:

from Qt import QtWidgets, QtCore

class Foo(QtWidgets.QWidget):
    my_signal = QtCore.Signal()

    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        self.my_signal.connect(self.doSomething)

        self.my_signal.emit()

    def doSomething(self):
        print("signal emitted")

app = QtWidgets.QApplication([])
f = Foo()        
app.exec_()

and what I get in pycharm: image

pycharm is not able to resolve SignaInstance stubs and is looking for the definition in the Signal stub. I'm not sure if this is pycharm issue but I also do not understand why we need two stubs for the same class. Shouldn't we merge SignalInstance stub to Signal? is there any other way to get pycharm to resolve it? I'm getting a lot of false positives in the IDE.

Warning:(88, 17) Unresolved attribute reference 'connect' for class 'Signal' Warning:(107, 64) Unresolved attribute reference 'emit' for class 'Signal'

mottosso commented 7 months ago

Ping @chadrik, the author of these stubs.

chadrik commented 7 months ago

Hi all, thanks for flagging me.

This is unfortunately an issue with PyCharm. mypy correctly understands that the type of self.my_signal is SignalInstance.

I added a reveal_type statement and ran mypy 1.7 on this code:

from Qt import QtWidgets, QtCore

class Foo(QtWidgets.QWidget):
    my_signal = QtCore.Signal()

    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        reveal_type(self.my_signal)
        self.my_signal.connect(self.doSomething)

        self.my_signal.emit()

    def doSomething(self):
        print("signal emitted")

mypy prints the expected type:

tests/test_general.py:8: note: Revealed type is "PySide2.QtCore.SignalInstance"

This appears to be an issue with PyCharm's comprehension of python data descriptors (__get__).

What version of PyCharm are you using?

p0las commented 7 months ago

I use whatever is the latest one (pro). this issue has been bugging me for a while and I tried a lot of things to fix it recently. I can obviously edit the stubs and make pycharm happy but I wanted to fix it properly and I don't really understand the idea of SignalInstance in the stubs. what is the purpose of this distinction?

chadrik commented 7 months ago

I think this is the relevant PyCharm ticket: https://youtrack.jetbrains.com/issue/PY-47698/PyCharm-does-not-know-recognize-return-types-of-custom-descriptors

I made these stubs as accurate as possible, so that they would work with advanced code-bases using mypy. It's possible to dumb them down and make them less accurate so that they work with PyCharm. For example, I think removing SignalInstance from QtCore.pyi and editing Signal to the code below would solve your problem:

class Signal:
    def __init__(self, *args, **kwargs) -> None: ...
    def __call__(self, *args, **kwargs): ...
    def connect(self, slot: typing.Callable, type: typing.Union[type, None] = ...) -> bool: ...
    def disconnect(self, slot: typing.Union[typing.Callable, None] = ...) -> None: ...
    def emit(self, *args: typing.Any) -> None: ...
    def __call__(self, *args, **kwargs): ...
    def __getitem__(self, index) -> Signal: ...

I'd rather not have to distribute two sets of stubs -- one for accurate type-checkers and one for broken ones -- but I recognize that PyCharm is much more widely used and relied on than mypy. I'm not sure what a good solution is for this.

p0las commented 7 months ago

I will report it to pycharm folks. thank you for looking into it.

mottosso commented 4 months ago

Closing this as it seems unrelated to Qt.py