PDA

View Full Version : How to find which button was pressed (PyQt5)



RickTee
27th January 2020, 11:26
Hello fellow qt users. :)

In the process of learning python I created a tictactoe game and use the PyQt5 to create a gui. Simple 3x3 grid of buttons created in a loop with a view to a 4x4 grid later. Ran into a problem, how to identify the button pressed? Code below shows ugly solution used. Putting clicked.connect statements in the loop fails as it seems the variables i and j are passed by reference so they all contain 2, 2 when the loop exits. Full code here: https://github.com/RickTee/tictacQt. I've probably missed something some were in the docs.



GRID_SIZE = 3
# Create our board of 9 buttons and put them in a grid
for i in range(0, GRID_SIZE ):
for j in range(0, GRID_SIZE ):
self.button[i][j] = QPushButton()
self.button[i][j].setMinimumSize(25, 25)
self.button[i][j].setMaximumSize(25, 25)
grid.addWidget(self.button[i][j], i, j)

vbox.addLayout(grid)
# Add the warnings label
vbox.addWidget(self.labelWarn)
vbox.addStretch(0)
hbox.addLayout(vbox)
hbox.addStretch(0)
self.setLayout(hbox)

self.button[0][0].clicked.connect(lambda: self.on_button_clicked(0, 0))
self.button[0][1].clicked.connect(lambda: self.on_button_clicked(0, 1))
self.button[0][2].clicked.connect(lambda: self.on_button_clicked(0, 2))
self.button[1][0].clicked.connect(lambda: self.on_button_clicked(1, 0))
self.button[1][1].clicked.connect(lambda: self.on_button_clicked(1, 1))
self.button[1][2].clicked.connect(lambda: self.on_button_clicked(1, 2))
self.button[2][0].clicked.connect(lambda: self.on_button_clicked(2, 0))
self.button[2][1].clicked.connect(lambda: self.on_button_clicked(2, 1))
self.button[2][2].clicked.connect(lambda: self.on_button_clicked(2, 2))

Lesiok
27th January 2020, 12:34
I don't know how in Python but in C++ it is a method QObject::sender().

d_stranz
27th January 2020, 18:03
self.button[0][0].clicked.connect(lambda: self.on_button_clicked(0, 0))

And I don't know if Python allows you to do tricks that C++ cannot, but in C++, the slot that is connected to a signal must have the same signature (usually) as the signal: the clicked() signal has no arguments, so the on_clicked() slot cannot have any either. Even when the slot is a lambda, the arguments will be stripped off when the slot is called, if it compiles at all.

The exception to the rule is a signal that -does- pass arguments can be connected to a slot defined with fewer arguments (in which case the extra arguments are simply ignored).

INeedADollar
2nd March 2020, 15:30
If you didn't find any solution, you can add all buttons to a QButtonGroup and then when a button is clicked the button group will emit buttonClicked signal which has as parameter the ID of the button or the button itself for overload version.
But use it in the last instance when you don't find how to solve it because the button group is for checkable buttons. Better call QObject::sender() in your slot to get the button.

ChrisW67
3rd March 2020, 11:27
QSignalMapper is definite option. Connect all button's clicked() to a mapper's map(). Map each button to an integer 0..(GRID_SIZE^2-1). In the slot connected to mapped(int) derive the row/column from the integer.