Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyQt: Why does a QButtonGroup in a method need 'self.' prefix to signal buttonClicked?

I created a QMainWindow GUI that uses a toolbar of radio buttons to select the main display (i.e. which widget of a QStackedWidget is displayed). I finally got a QButtonGroup's signal to be detected, but I don't fully understand why my fix worked.

Here is a minimal working example; the focus is the modelButtonGroup method.

class myGui(QMainWindow):

    def __init__(self, parent=None):

        super(myGui, self).__init__(parent)
        self.setupCentral()
        self.setupButtonToolBar()

    def setupCentral(self):

        self.stackedWidget = QStackedWidget()
        self.setCentralWidget(self.stackedWidget)

        windowA = QWidget()
        windowALayout = QGridLayout()
        windowALayout.addWidget(QLabel('Window A'))

        windowB = QWidget()
        windowBLayout = QGridLayout()
        windowBLayout.addWidget(QLabel('Window B'))

        windowA.setLayout(windowALayout)
        windowB.setLayout(windowBLayout)

        self.stackedWidget.addWidget(windowA)
        self.stackedWidget.addWidget(windowB)
        self.stackedWidget.setCurrentIndex(0)

    def setupButtonToolBar(self):

        buttonBar = QToolBar()
        buttonBar.addWidget(self.modelButtonGroup())
        self.addToolBar(buttonBar)

    def modelButtonGroup(self):

        modelsWidget = QWidget()
        modelsLayout = QVBoxLayout()
        self.ButtonGroup = QButtonGroup()

        windowA_Button = QRadioButton('Window A')
        windowA_Button.setChecked(True)
        self.ButtonGroup.addButton(windowA_Button, 0)

        windowB_Button = QRadioButton('Window B')
        self.ButtonGroup.addButton(windowB_Button, 1)

        self.ButtonGroup.buttonClicked[int].connect(self.switchdisplay)

        modelsLayout.addWidget(windowA_Button)
        modelsLayout.addWidget(windowB_Button)
        modelsWidget.setLayout(modelsLayout)

        return modelsWidget

    def switchdisplay(self, id):
        print('button %d has been pressed' % id)
        self.stackedWidget.setCurrentIndex(id)

My understanding is that, if a name in one method is only referenced within that method, and isn't referred to elsewhere in the class, then self. is not needed as a prefix. However, if ButtonGroup is used instead of self.ButtonGroup, its .buttonClicked signal doesn't get processed by switchdisplay.

While writing this question, I think I determined why this is, but would appreciate confirmation. modelButtonGroup returns only the widget of buttons, and is only called when setting up the GUI. The ButtonGroupis not passed out of the function, and is garbage collected. The 'self.' prefix is required for it to 'live on', even if it's not directly referred to by name.

Am I understanding this correctly?

like image 561
Geoffrey Sametz Avatar asked Apr 09 '17 14:04

Geoffrey Sametz


2 Answers

I see that you already figured it out yourself:

The ButtonGroup is not passed out of the function, and is garbage collected. The 'self.' prefix is required for it to 'live on', even if it's not directly referred to by name.

Am I understanding this correctly?

Yes. That is exactly what is happening here. If you don't retain a reference to ButtonGroup, then it is deleted as soon as it goes out of scope (at the function exit). It will never emit any signals.

I think @eyllanesc's answer might work, too. In that case, the reference to the ButtonGroup object would be retained implicitly, as a child within the parent class.

FWIW, I typically encounter bugs like this when I'm dealing with QMenu objects.

like image 160
Stuart Berg Avatar answered Oct 11 '22 01:10

Stuart Berg


ButtonGroup must be a member of the class, if you do not want to use self.ButtonGroup, you can use ButtonGroup = QButtonGroup(self)

like image 25
eyllanesc Avatar answered Oct 11 '22 01:10

eyllanesc