Connecting a signal to slot yields (nullptr)::slot() error

Viewed 184

I want to connect a signal from my custom QWidget class (WidgetClass) to the slot of DerivedController. I try to connect the signal after I instantiate both classes.

However, I get the following error on the connect() statement:

QObject::connect: Cannot connect WidgetClass::mySignal(double) to (nullptr)::mySlot(double)

I'm not quite sure why this is happening. If I understand correctly, you can only connect signals to slots between instances of classes. Below is a pseudo implementation of what I'm writing (it produces the same error in ipython):

from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication, QWidget

class BaseController():
    def __init__(self, widget1, widget2):
        self.widget1 = widget1
        self.widget2 = widget2

    #@pyqtSlot(float)
    def mySlot(self, value):
        print(f"BaseClass got {value}.")


class DerivedController(BaseController):
    def __init__(self, widget1, widget2):
        super().__init__(widget1, widget2)

    @pyqtSlot(float)
    def mySlot(self, value):
        #super().mySlot(value)
        print(f"Derived class got {value}!")

    def _setupConnections(self):
        self.widget1.mySignal.connect(
            self.mySlot)
        self.widget2.mySignal.connect(
            self.mySlot)

class WidgetClass(QWidget):
    mySignal = pyqtSignal(float)

    def emitSignal(self):
        self.mySignal.emit(1.8)

def myfunc():
    w1 = WidgetClass()
    w2 = WidgetClass()
    controller = DerivedController(w1, w2)
    controller._setupConnections()
    controller.widget1.emitSignal()

def main():
    app = QApplication(["test"])
    return app.exec(myfunc())

if __name__ == "__main__":
    main()
3 Answers

The solution proposed by the OP is incorrect since the cause of the error is different:

If you inherit from a class P then you have to call the constructor of class P, in your case BaseController that inherits from QObject does not invoke the constructor of QObject.

Considering the above, the error message of the question is no longer obtained, but there are still other problems:

  • The scope of a function is limited, and the objects created within that function are destroyed when the function is executed, in this case "controller" is destroyed and may cause the problem.

  • On the other hand, the QApplication exec() method should not receive any parameters.

Considering the above, the solution is:

from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication, QWidget


class BaseController(QObject):
    def __init__(self, widget1, widget2):
        super().__init__()
        self.widget1 = widget1
        self.widget2 = widget2

    # @pyqtSlot(float)
    def mySlot(self, value):
        print(f"BaseClass got {value}.")


class DerivedController(BaseController):
    def __init__(self, widget1, widget2):
        super().__init__(widget1, widget2)

    @pyqtSlot(float)
    def mySlot(self, value):
        # super().mySlot(value)
        print(f"Derived class got {value}!")

    def _setupConnections(self):
        self.widget1.mySignal.connect(self.mySlot)
        self.widget2.mySignal.connect(self.mySlot)


class WidgetClass(QWidget):
    mySignal = pyqtSignal(float)

    def emitSignal(self):
        self.mySignal.emit(1.8)


def myfunc():
    w1 = WidgetClass()
    w2 = WidgetClass()
    controller = DerivedController(w1, w2)
    controller._setupConnections()
    controller.widget1.emitSignal()

    return controller


def main():
    app = QApplication(["test"])
    controller = myfunc()
    return app.exec()


if __name__ == "__main__":
    main()

On the other, what the OP points out in its answer: I think this is because we are not binding to the slot of the instance, but the slot of the class itself is false, the connection is between objects.

You forgot the super().__init__() call in BaseController

I figured out that the following part is wrong:

self.widget1.mySignal.connect(
    self.mySlot)
self.widget2.mySignal.connect(
    self.mySlot)

And should be rewritten as such:

self.widget1.mySignal.connect(
    DerivedController.mySlot)
self.widget2.mySignal.connect(
    DerivedController.mySlot)

Which seems to fix the problem. I think this is because we are not binding to the slot of the instance, but the slot of the class itself, just like the signal which is declared as part of the class definition, not the class instantiation (I hope that makes sense).

Related