Segfault when disconnecting a signal before connecting

Viewed 57

The following MWE is showing the problematic behavior:

import sys
from PySide2 import QtCore, QtWidgets


class ExampleMainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(ExampleMainWindow, self).__init__(*args, **kwargs)

        self.setWindowTitle("MWE")

        self.central_widget = QtWidgets.QWidget(self)
        central_grid_layout = QtWidgets.QGridLayout(self.central_widget)
        self.setCentralWidget(self.central_widget)

        self.button_1 = QtWidgets.QPushButton("Push me first")
        self.button_2 = QtWidgets.QPushButton("Push me later")
        self.button_1.clicked.connect(self.on_b1_pressed)

        # REMOVE COMMENT HERE AND IT WON'T FAIL
        # self.button_2.clicked.connect(self.b2_func)

        central_grid_layout.addWidget(self.button_1, 1, 1, 1, 1)
        central_grid_layout.addWidget(self.button_2, 1, 2, 1, 1)

    def on_b1_pressed(self):
        try:
            success = self.button_2.clicked.disconnect(self.b2_func)
            print("disconnect button_2: success is ", success)
        except RuntimeError:
            print("disconnect button_2: RuntimeError")

        print("trying to connect button_2")
        self.button_2.clicked.connect(self.b2_func)

    # COMMENT OUT THE SLOT DECORATOR AND IT WON'T FAIL
    @QtCore.Slot()
    def b2_func(self):
        print("button_2 pressed")

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    example_main_window = ExampleMainWindow()

    example_main_window.show()
    sys.exit(app.exec_())

When button_1 is pressed, a disconnect is called first for button_2, which raises an exception as expected. But then while trying to connect, the program ends with a segfault:

disconnect button_2: RuntimeError
trying to connect button_2
Segmentation fault

This behavior can be fixed by removing the slot decorator (see comment). It is also working when it is connected from the start, so no disconnect is called before connect (see first comment). But the latter might not be intended behavior in a more complex application.

From my point of view, this is not expected behavior. gdb puts out the same backtrace in the segfault case as can be seen in this issue. https://github.com/nucleic/enaml/issues/50#issuecomment-24872564

Am I doing something wrong or is this a bug in PySide?

Python Version 3.9.6, PySide2.__version__ = '5.15.2'

1 Answers

It seems that it is a bug that also reproduces in PySide6 (it would be good to report it). A possible workaround is not to use try-except but to use a boolean flag:

class ExampleMainWindow(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super(ExampleMainWindow, self).__init__(*args, **kwargs)

        self.is_connected = False

        self.setWindowTitle("MWE")

        self.central_widget = QtWidgets.QWidget(self)
        central_grid_layout = QtWidgets.QGridLayout(self.central_widget)
        self.setCentralWidget(self.central_widget)

        self.button_1 = QtWidgets.QPushButton("Push me first")
        self.button_2 = QtWidgets.QPushButton("Push me later")
        self.button_1.clicked.connect(self.on_b1_pressed)

        central_grid_layout.addWidget(self.button_1, 1, 1, 1, 1)
        central_grid_layout.addWidget(self.button_2, 1, 2, 1, 1)

    def on_b1_pressed(self):
        if self.is_connected:
            self.button_2.clicked.disconnect(self.b2_func)
        self.is_connected = self.button_2.clicked.connect(self.b2_func)

    @QtCore.Slot()
    def b2_func(self):
        print("button_2 pressed")

I already reported the bug PYSIDE-1715.

The bug has been resolved and in the next release of PySide6 6.2.2 this problem will no longer be seen.

Related