PyQt QDialogButtonBox signals

Hi all, I have some questions on PyQt.

I created 2 sets of QDialogButtonBox, each with 3 buttons - ‘Add’, ‘Remove’, ‘Clear’.
Each set is connected/ for with a QListWidget, and since both buttons sets are working the same way except to different list, I created 3 separate methods, so that it is easier to maintain as I do not see the need to have a new method for each button.

Since I have used rejected for the ‘Remove’ button, I used clicked for the ‘Clear’ button since I am unable to see if there are any other signals that I can used for it.

However while it works for the ‘Add’ and ‘Remove’ buttons, it is not working ‘Clear’.
If either ‘Add’ or ‘Remove’ is clicked onto, the command for ‘Clear’ is called as well which ends up emptying the list everytime I tried to remove or add new item(s).

This is some portions of my code:

# List widget 01
self.widget_01_btns = QtGui.QDialogButtonBox()
self.widget_01_btns.addButton('Add', QtGui.QDialogButtonBox.AcceptRole)
self.widget_01_btns.addButton('Remove', QtGui.QDialogButtonBox.RejectRole)
self.widget_01_btns.addButton('Clear', QtGui.QDialogButtonBox.ResetRole)

# List widget 02
self.widget_02_btns = QtGui.QDialogButtonBox()
self.widget_02_btns.addButton('Add', QtGui.QDialogButtonBox.AcceptRole)
self.widget_02_btns.addButton('Remove', QtGui.QDialogButtonBox.RejectRole)
self.widget_02_btns.addButton('Clear', QtGui.QDialogButtonBox.ResetRole)

# Button Connections

# For widget 01
self.widget_01_btns.accepted.connect(lambda:self.add_objects(self.widget01_list))
self.widget_01_btns.rejected.connect(lambda:self.remove_objects(self.widget01_list))
# This is called in conjunction whenever either 'Add' or 'Remove' button is clicked
self.widget_01_btns.clicked.connect(lambda:self.clear_objects(self.widget01_list))

# For widget 02
self.widget_02_btns.accepted.connect(lambda:self.add_objects(self.widget02_list))
self.widget_02_btns.rejected.connect(lambda:self.remove_objects(self.widget02_list))
# This is called in conjunction whenever either 'Add' or 'Remove' button is clicked
self.widget_02_btns.clicked.connect(lambda:self.clear_objects(self.widget02_list))

I tried another method for the ‘Clear’ button that I found online, but upon using it, I got this error instead - AttributeError: 'NoneType' object has no attribute 'clicked'

self.widget_01_btns.button(QtGui.QDialogButtonBox.Reset).clicked.connect(lambda:self.clear_objects(self.widget01_list))

Any pointers for me?

My advice would be not to use QDialogButtonBox at all and instead just use several QPushButtons. QDialogButtonBox is designed for actions that affect the entire dialog, like closing it or clearing all the data in the dialog. You are trying to affect only certain parts of the dialog.

If there is some reason you want to avoid individual QPushButtons, please post it.

In this specific scenario, I would suggest you connect your methods to QActions, and connect those actions to your buttons or toolbar/toolbuttons.

https://srinikom.github.io/pyside-docs/PySide/QtGui/QAction.html

[QUOTE=RFlannery;31266]My advice would be not to use QDialogButtonBox at all and instead just use several QPushButtons. QDialogButtonBox is designed for actions that affect the entire dialog, like closing it or clearing all the data in the dialog. You are trying to affect only certain parts of the dialog.

If there is some reason you want to avoid individual QPushButtons, please post it.[/QUOTE]

I used QDialogButtonBox because I am trying to cater/ fit it into my ui to make it ‘pretty’ due to its layout - 1 button on the left-hand side, while 2 buttons to the far right…
And with the use of buttonRoles, it seems to fit my cause well and hence I used it. But all seems to work well until the use of resetRole which is causing me quite some issue.

By the way, I replaced reset to using of helpRequested, it works but does not seems to make sense as my button is not a Help but a Reset button.

[QUOTE=Claudio A;31267]In this specific scenario, I would suggest you connect your methods to QActions, and connect those actions to your buttons or toolbar/toolbuttons.

https://srinikom.github.io/pyside-docs/PySide/QtGui/QAction.html[/QUOTE]

How do I do that? Could you give me an example?

Okay, I see now why you want to use the dialog button box. You could try something like the following code. It uses the “clicked” signal only. Then it figures out which button was clicked and calls the correct function. (Warning: This is untested.)

from functools import partial

    self.widget_01_btns.clicked.connect(partial(self.doTheThing, self.widget01_list))
    self.widget_02_btns.clicked.connect(partial(self.doTheThing, self.widget02_list))

def doTheThing(self, listWidget, buttonClicked):
    role = buttonClicked.buttonRole()
    if role == QtGui.QDialogButtonBox.AcceptRole:
        self.add_objects(listWidget)
    if role == QtGui.QDialogButtonBox.Reject:
        self.remove_objects(listWidget)
    if role == QtGui.QDialogButtonBox.Reset:
        self.clear_objects(listWidget)

[QUOTE=RFlannery;31270]Okay, I see now why you want to use the dialog button box. You could try something like the following code. It uses the “clicked” signal only. Then it figures out which button was clicked and calls the correct function. (Warning: This is untested.)

from functools import partial

    self.widget_01_btns.clicked.connect(partial(self.doTheThing, self.widget01_list))
    self.widget_02_btns.clicked.connect(partial(self.doTheThing, self.widget02_list))

def doTheThing(self, listWidget, buttonClicked):
    role = buttonClicked.buttonRole()
    if role == QtGui.QDialogButtonBox.AcceptRole:
        self.add_objects(listWidget)
    if role == QtGui.QDialogButtonBox.Reject:
        self.remove_objects(listWidget)
    if role == QtGui.QDialogButtonBox.Reset:
        self.clear_objects(listWidget)

[/QUOTE]

Thanks for getting back.
Tried the code you have provided, and there is an error - # AttributeError: 'PySide.QtGui.QPushButton' object has no attribute 'buttonRole'

Oops. Here’s another try. (Again, this is untested and not very elegant. Feel free to clean it up):

from functools import partial

    self.widget_01_btns.clicked.connect(partial(self.doTheThing, self.widget01_list))
    self.widget_02_btns.clicked.connect(partial(self.doTheThing, self.widget02_list))

def doTheThing(self, listWidget, buttonClicked):
    buttonBox = self.sender()
    role = buttonBox.buttonRole(buttonClicked)
    if role == QtGui.QDialogButtonBox.AcceptRole:
        self.add_objects(listWidget)
    if role == QtGui.QDialogButtonBox.Reject:
        self.remove_objects(listWidget)
    if role == QtGui.QDialogButtonBox.Reset:
        self.clear_objects(listWidget)

[QUOTE=RFlannery;31272]Oops. Here’s another try. (Again, this is untested and not very elegant. Feel free to clean it up):

from functools import partial

    self.widget_01_btns.clicked.connect(partial(self.doTheThing, self.widget01_list))
    self.widget_02_btns.clicked.connect(partial(self.doTheThing, self.widget02_list))

def doTheThing(self, listWidget, buttonClicked):
    buttonBox = self.sender()
    role = buttonBox.buttonRole(buttonClicked)
    if role == QtGui.QDialogButtonBox.AcceptRole:
        self.add_objects(listWidget)
    if role == QtGui.QDialogButtonBox.Reject:
        self.remove_objects(listWidget)
    if role == QtGui.QDialogButtonBox.Reset:
        self.clear_objects(listWidget)

[/QUOTE]

Hmm, actually it is not working as well.

Got the same error, erroing at the btn_role.
Error is: AttributeError: 'NoneType' object has no attribute 'buttonRole'

And buttonBox is registering as None

Fascinating. It looks like both “partial” and “lambda” cause self.sender() to return None.

Looks like you will have to pass both the button box and the list widget to your function.

from functools import partial

        self.buttonBox1.clicked.connect(partial(self.doTheThing, self.buttonBox1, self.listWidget1))
        self.buttonBox2.clicked.connect(partial(self.doTheThing, self.buttonBox2, self.listWidget2))
    
    def doTheThing(self, sender, listWidget, buttonClicked):
        role = sender.buttonRole(buttonClicked)
        if role == QtGui.QDialogButtonBox.AcceptRole:
            self.add_objects(listWidget)
        elif role == QtGui.QDialogButtonBox.RejectRole:
            self.remove_objects(listWidget)
        elif role == QtGui.QDialogButtonBox.ResetRole:
            self.clear_objects(listWidget)

[QUOTE=RFlannery;31281]Looks like you will have to pass both the button box and the list widget to your function.

from functools import partial

        self.buttonBox1.clicked.connect(partial(self.doTheThing, self.buttonBox1, self.listWidget1))
        self.buttonBox2.clicked.connect(partial(self.doTheThing, self.buttonBox2, self.listWidget2))
    
    def doTheThing(self, sender, listWidget, buttonClicked):
        role = sender.buttonRole(buttonClicked)
        if role == QtGui.QDialogButtonBox.AcceptRole:
            self.add_objects(listWidget)
        elif role == QtGui.QDialogButtonBox.RejectRole:
            self.remove_objects(listWidget)
        elif role == QtGui.QDialogButtonBox.ResetRole:
            self.clear_objects(listWidget)

[/QUOTE]

This does works.

But right now I am curious - is there no exact signal when using ResetRole?
From online searches, I have and only been seeing that ‘accepted’ is used for AcceptRole, while ‘rejected’ is for RejectRole and ‘helpRequested’ is for helpRole.

[QUOTE=salik89;31282]This does works.

But right now I am curious - is there no exact signal when using ResetRole?
From online searches, I have and only been seeing that ‘accepted’ is used for AcceptRole, while ‘rejected’ is for RejectRole and ‘helpRequested’ is for helpRole.[/QUOTE]

That is correct. The “clicked” signal will be emitted no matter what button is pressed. The “AcceptRole”, “RejectRole”, and “HelpRole” are the only three roles with specific signals tied to them. If you want your buttons to have any other role, you must use the “clicked” signal.

Okay, it just makes it seems lame? From a beginner pov, or at least for me, I would have thought the ‘clicked’ signal will not be associated with the only 3 roles above as it has its own specific signals to it.

Not to mention that in my initial code, it was merely a one-liner for the AcceptRole and RejectRole using either lambda or partial without the need to create another function. Just saying.