3

I tried to make a simple example to help understand how the concept of QSignalMapping works in PySide. I would like to dynamically create a series of buttons by iterating through a loop, and when the user pushes one of the buttons, I can activate a method that returns the appropriate label for the button that was pressed.

from PySide2 import QtWidgets,QtCore,QtGui

fruit_list = ["apples","oranges","pears"]

def fruit_button_event():
    print "this is the pressed button's label"

def main():
    for fruit in fruit_list:
        fruit_button = QtWidgets.QPushButton(fruit)
        fruit_button.clicked.connect(lambda:fruit_button_event())
main()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
winteralfs
  • 459
  • 6
  • 25

1 Answers1

6

In the next part I show an example how to use QSignalMapper:

from PySide2 import QtCore, QtWidgets


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        lay = QtWidgets.QVBoxLayout(self)

        fruit_list = ["apples","oranges","pears"]
        mapper =  QtCore.QSignalMapper(self)
        mapper.mapped[str].connect(self.fruit_button_event)

        for fruit in fruit_list:
            btn = QtWidgets.QPushButton(fruit)
            btn.clicked.connect(mapper.map)
            mapper.setMapping(btn, fruit)
            lay.addWidget(btn)

    @QtCore.Slot(str)
    def fruit_button_event(self, text):
        print("this is the pressed button's label", text)


if __name__ == '__main__':
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

Remember that from Qt 5.10 QSignalMapper is deprecated:

This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.


The same functionality in python can be obtained with functools.partial(...):

from PySide2 import QtCore, QtWidgets
from functools import partial


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        lay = QtWidgets.QVBoxLayout(self)

        fruit_list = ["apples","oranges","pears"]

        for fruit in fruit_list:
            btn = QtWidgets.QPushButton(fruit)
            btn.clicked.connect(partial(self.fruit_button_event, fruit))
            lay.addWidget(btn)

    @QtCore.Slot(str)
    def fruit_button_event(self, text):
        print("this is the pressed button's label", text)

Or with lambda:

btn.clicked.connect(lambda text=fruit: self.fruit_button_event(text))

Or QButtonGroup:

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        lay = QtWidgets.QVBoxLayout(self)

        fruit_list = ["apples","oranges","pears"]
        group = QtWidgets.QButtonGroup(self)
        group.buttonClicked.connect(self.OnButtonClicked)

        for fruit in fruit_list:
            btn = QtWidgets.QPushButton(fruit)
            group.addButton(btn)
            lay.addWidget(btn)

    @QtCore.Slot(QtWidgets.QAbstractButton)
    def OnButtonClicked(self, btn):
        print("this is the pressed button's label", btn.text())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thank you for the example and the advise, what is the alternative? The correct approach for Pyside2 ? – winteralfs Sep 12 '18 at 01:12
  • @winteralfs in C++ it is recommended to use lambdas in python I recommend using functools.partial(), I'll show you an example. On the other hand, if my answer helps you, do not forget to mark it as correct, if you do not know how to do it, review the [tour], that is the best way to thank. – eyllanesc Sep 12 '18 at 01:14
  • In your Python example, is there a way that it would work without using a decorator on the fruit_button_event method? – winteralfs Sep 12 '18 at 01:49
  • 1
    @winteralfs you can remove it, the advantage of the decorator is that the connection is faster since the connection is given in C ++ since less resources are used so I recommend using them although it is not mandatory. – eyllanesc Sep 12 '18 at 01:55
  • If i remove it, do I have to add any other code to replace it? – winteralfs Sep 12 '18 at 01:59
  • @winteralfs no, you do not have to add anything I recommend you try, experiment before asking. – eyllanesc Sep 12 '18 at 02:01