Dynamic grid with pushbuttons
-
Hi all,
I would like to create a dynamic grid (i.e. with a dynamic size say n) of pushbuttons on a grid. When I click on the button a gridpoint-specific event should occur let's say the name of the button shall be displayed. Here is my code:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
from PyQt5.QtWidgets import *
from os import environdef suppress_qt_warnings():
environ["QT_DEVICE_PIXEL_RATIO"] = "0"
environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
environ["QT_SCREEN_SCALE_FACTORS"] = "1"
environ["QT_SCALE_FACTOR"] = "1"if name == "main":
suppress_qt_warnings()class MainWindow(QMainWindow):
def __init__(self,n): super().__init__() self.initUI(n) def initUI(self, n): self.lengthDF = n self.widthDF = n self.scroll = QScrollArea() self.widget = QWidget() self.grid = QGridLayout() self.grid.setVerticalSpacing(0) self.grid.setHorizontalSpacing(0) for i in range(0, self.widthDF): for j in range(0, self.lengthDF): button = QPushButton() button.setText(str(i)+","+str(j)) button.clicked.connect(lambda: self.clickme(str(button.text()))) self.grid.addWidget(button, j, i) self.widget.setLayout(self.grid) self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.scroll.setWidgetResizable(True) self.scroll.setWidget(self.widget) self.setCentralWidget(self.scroll) self.setGeometry(100, 100, 600, 500) self.setWindowTitle('TestGrid') self.show() return def clickme(self, text): print(text)
def main(n = 5):
app = QApplication(sys.argv)
main = MainWindow(n)
sys.exit(app.exec_())main()
However, when I click a button always the text of the button (4,4) is displayed. Does anybody know how to fix that problem?
Regards!
-
@Sebastian-Egger
Yes, well done for adding the first parameter. I hadn't noticed that theQPushButton.clicked
signal (e.g. https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QAbstractButton.html#PySide2.QtWidgets.PySide2.QtWidgets.QAbstractButton.clicked) passeschecked
as a parameter. Any extra named arguments must come after all unnamed arguments are specified, so if you need to add a named argument you need to pass any/all the existing, non-named arguments too. -
@Sebastian-Egger said in Dynamic grid with pushbuttons:
button.clicked.connect(lambda: self.clickme(str(button.text())))
Hi and welcome.
I don't know how you are supposed to know this, but as I discovered long ago Python lambda parameters in a loop don't work like you'd think they do! This ends up connecting each button with a lambda using
button.text()
in its body wherebutton
is always the value it had the last time through the loop, not its value at the time eachbutton.clicked.connect()
was executed.You have to pass a changing-variable-in-the-loop via a named parameter to the lambda [I remove the
str()
as unnecessary]:button.clicked.connect(lambda btn=button: self.clickme(btn.text()))
Trust me! ;-)
-
@JonB said in Dynamic grid with pushbuttons:
changing-variable-in-the-loop
Thanks a lot, JonB,
That was the essential step! Only one thing: I had to use (notice the dummy variable state)
button.clicked.connect(lambda state, btn = button: self.clickme(btn.text()))
otherwise I get an error
AttributeError: 'bool' object has no attribute 'text'
there is a link to this problem
https://stackoverflow.com/questions/35819538/using-lambda-expression-to-connect-slots-in-pyqt
I guess what the problem is but at the moment my knowledge about lambdas is too restrictive to see it clearly...
Thanks again for your help!
-
@Sebastian-Egger
Yes, well done for adding the first parameter. I hadn't noticed that theQPushButton.clicked
signal (e.g. https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QAbstractButton.html#PySide2.QtWidgets.PySide2.QtWidgets.QAbstractButton.clicked) passeschecked
as a parameter. Any extra named arguments must come after all unnamed arguments are specified, so if you need to add a named argument you need to pass any/all the existing, non-named arguments too.