7

if i want to add 10 QPushButton at one time:

NumCount=20
for i in range(NumCount):
  btn=QPushButton("%s %s" %("Button" i+1),self)
  btn.clicked.connect(self.btnclick)

def btnclick(self):
  # here is my question 
  # how to define which button clicked?
  # how to print btn.text?

as stated in the def(btnclick).

Macke
  • 24,812
  • 7
  • 82
  • 118

4 Answers4

3

When you are in a slot, you can use sender() method (just call self.sender()) and you will receive a reference to the object, from which signal was emitted. Here is documentation about it.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
gruszczy
  • 40,948
  • 31
  • 128
  • 181
2

I'd subclass QPushButton and define my own sender and slot. The QObject.sender() method is tempting, but it gives me the heebie-jeebies.

class MyPushButton(QPushButton):
    def __init__(self, text = '', parent = None):
        QPushButton.__init__(self, text, parent)
        self.clicked.connect(self._handle_click)

    my_click = QtCore.pyqtSignal(QObject)

    def _handle_click(self):
        my_click.emit(self)

def btnclick(btn):
    print 'Handle button %s' % btn

for i in xrange(20):
    btn = MyPushButton('%s %s' % ('Button', i + 1), self)
    btn.my_click.connect(btnclick)

A slightly more Pythonic way of doing this could define the behavior within the class, like so:

class MyPushButton(QPushButton):
    def __init__(self, button_number, parent = None):
        QPushButton.__init__(self, '%s %s' % ('Button', button_number), parent)
        self.button_number = button_number
        self.clicked.connect(self._handle_click)

     def _handle_click(self):
        print 'Handle button %s' % self

for i in xrange(20):
    btn = MyPushButton(i + 1, self)
Chris B.
  • 85,731
  • 25
  • 98
  • 139
  • Seriously, there's nothing wrong with sender(). I've used it quite a lot and it works just fine. However, one should use some care. I usually use your method when signalling externally (i.e. to other objects) but sender() when routing signals inside a class. – Macke Feb 18 '10 at 21:28
  • There may not be anything wrong with .sender(), but as the documentation says, this "violates the object-oriented principle of modularity". Which wouldn't be the death of anyone, admittedly. I might be tempted to use it in as a "quick-and-dirty" fix. But Python makes it so easy to do better and cleaner it's probably better just to do it right, in the long run. – Chris B. Feb 18 '10 at 22:44
1

As gruszcsy said, there's self.sender() (in QObject) to get that exact info.

There's also the QSignalMapper class that provides a higher-level mapping from several signal senders to one slot. It helps for the basic cases of many-to-one signal/slot mapping.

Chris B's suggestion about defining a new slot that transmits the sender as a parameter is a bit more complicated, but cleaner in terms of program structure and separation between classes. I tend to use that method when the target slot is in another object. For mapping inside a class's private slot, sender() is both neat and quite appropriate, IMO.

Macke
  • 24,812
  • 7
  • 82
  • 118
0

Here is a small app demonstrating one possible solution:

from PyQt4.QtGui import QPushButton, QWidget
from PyQt4.QtGui import QVBoxLayout, QApplication

def smart_connect(btn, btn_click_slot):
    proxy_slot = lambda checked: btn_click_slot(btn)
    btn.clicked.connect(proxy_slot)

class MyWidget(QWidget):
    btn_count = 4
    def __init__(self):
        super(MyWidget, self).__init__()
        lt = QVBoxLayout(self)
        for i in range(self.btn_count):
            btn = QPushButton("Button %s"%(i+1))
            smart_connect(btn, self.btn_click)
            lt.addWidget(btn)
    def btn_click(self, btn):
        print "Button '%s' was clicked."%btn.text()

app = QApplication([])
wgt = MyWidget()
wgt.show()
app.exec_()

Please enjoy :)

mlvljr
  • 4,066
  • 8
  • 44
  • 61