18

I am using Python 3.4 with Pyside 1.2.4 and PyQt 4.8.7 and when I try to connect a Signal to a Slot it says:

'PySide.QtCore.Signal' object has no attribute 'connect'

Im am using MVC:

Model:

from PySide.QtCore import Signal
class Model(object):

    def __init__(self):
        self.updateProgress = Signal(int)

Controller:

class Controller(QWidget):
"""
MVC Pattern: Represents the controller class

"""
def __init__(self, parent=None):
    super().__init__(parent)
    self.model = Model()
    self.model.updateProgress.connect(self.setProgress)

When I look up the Class in Pycharm, by holding CTRL and click on the Signal class it looks like the following:

class Signal(object):
""" Signal """
def __call__(self, *args, **kwargs): # real signature unknown
    """ Call self as a function. """
    pass

def __getitem__(self, *args, **kwargs): # real signature unknown
    """ Return self[key]. """
    pass

def __init__(self, *args, **kwargs): # real signature unknown
    pass

@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
    """ Create and return a new object.  See help(type) for accurate signature. """
    pass

def __str__(self, *args, **kwargs): # real signature unknown
    """ Return str(self). """
    pass

... while there are actually supposed to be the methods connect, disconnect and emit according to the PySide documentation, available at:

https://srinikom.github.io/pyside-docs/PySide/QtCore/Signal.html#PySide.QtCore.Signal.connect

Thanks for your help in advance

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
LimitX
  • 595
  • 3
  • 7
  • 23

3 Answers3

33

The signal must be defined on the class, not the instance. The class must be a subclass of QObject, or be a mixin of such a class. So either of:

class Model(QtCore.QObject):
    updateProgress = Signal(int)

or:

class Mixin(object):
    updateProgress = Signal(int)

class Model(Mixin, QtCore.QObject):
    pass
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • 1
    Making the class inheriting from QObject AND defining the signal on the class made it. Thx – LimitX Apr 12 '16 at 10:53
  • 2
    Can you explain why it must be defined on the class? – Diego Palacios Mar 20 '21 at 15:53
  • @DiegoPalacios It's an implementation detail. You'd need to look at the source code to understand it fully, but essentially it uses the same mechanism that python uses to create bound instance methods from class functions (i.e. signal objects are factory functions that return descriptors). Doing things this way ensures that signals can be introspected using the QMetaObject API (amongst other things). – ekhumoro Mar 20 '21 at 17:42
10

In addition to ekhumoro's answer, the class with the signal also needs to call super().__init__(). Forgetting to do so can lead to the same error.

class Model(QtCore.QObject):
    updateProgress = Signal(int)

    def __init__(self):
        super().__init__()  # This is required!
        # Other initialization...
freidrichen
  • 2,237
  • 1
  • 19
  • 23
0

Concrete working example:

class QWorker(QRunnable, QObject):
    started = Signal()
    finished = Signal()
    failed = Signal()

    def __init__(self, fn, *args, **kwargs):
        QObject.__init__(self)
        QRunnable.__init__(self)

        self.fn = fn
        self.args = args
        self.kwargs = kwargs

    def run(self):
        self.started.emit()
        try:
            self.fn(*self.args, **self.kwargs)
        except BaseException as e:
            self.failed.emit()
        else:
            self.finished.emit()

The interesting thing is that, you must execute QObject.__init__(self)
instead of super(QObject, self).__init__(), then it works

RedEyed
  • 2,062
  • 1
  • 23
  • 26