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!
-
@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.clickedsignal (e.g. https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QAbstractButton.html#PySide2.QtWidgets.PySide2.QtWidgets.QAbstractButton.clicked) passescheckedas 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. -
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 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 wherebuttonis 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!
-
@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.clickedsignal (e.g. https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QAbstractButton.html#PySide2.QtWidgets.PySide2.QtWidgets.QAbstractButton.clicked) passescheckedas 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.