1

I have a QGridLayout where I'm adding different elements and I need to know the elements and I need to know the coordinates of of a specific element. Is there a way of doing this? I read the documentation and tried using the pos() and geometry() attributes but I couldn't get what I wanted. For example, given the following code:

import sys
from PyQt5.QtWidgets import (QWidget, QGridLayout,QPushButton, QApplication)

class basicWindow(QWidget):
    def __init__(self):
        super().__init__()
        grid_layout = QGridLayout()
        self.setLayout(grid_layout)

        for x in range(3):
            for y in range(3):
                button = QPushButton(str(str(3*x+y)))
                grid_layout.addWidget(button, x, y)

        self.setWindowTitle('Basic Grid Layout')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    windowExample = basicWindow()
    windowExample.show()
    sys.exit(app.exec_())

Is there a way of knowing the coordinates of each button?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Juan
  • 133
  • 1
  • 12

1 Answers1

3

The geometry is updated only when the widget is displayed, so you are probably printing the coordinates before displaying it. In the following example, if you press any button, it will print the coordinates with respect to the window.

import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QWidget, QGridLayout, QPushButton, QApplication


class BasicWindow(QWidget):
    def __init__(self):
        super().__init__()
        grid_layout = QGridLayout(self)

        for x in range(3):
            for y in range(3):
                button = QPushButton(str(3 * x + y))
                button.clicked.connect(self.on_clicked)
                grid_layout.addWidget(button, x, y)

        self.setWindowTitle("Basic Grid Layout")

    @pyqtSlot()
    def on_clicked(self):
        button = self.sender()
        print(button.text(), ":", button.pos(), button.geometry())


if __name__ == "__main__":
    app = QApplication(sys.argv)
    windowExample = BasicWindow()
    windowExample.show()
    sys.exit(app.exec_())

Output:

0 : PyQt5.QtCore.QPoint(10, 10) PyQt5.QtCore.QRect(10, 10, 84, 34)
4 : PyQt5.QtCore.QPoint(100, 50) PyQt5.QtCore.QRect(100, 50, 84, 34)
8 : PyQt5.QtCore.QPoint(190, 90) PyQt5.QtCore.QRect(190, 90, 84, 34)

Update:

If you want to get the position of the widget given the row and column, the first thing is to get the QLayoutItem, and from that QLayoutItem you must get the widget. In the following example, the position of a button is printed a moment after the window is displayed:

import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QWidget, QGridLayout, QPushButton, QApplication


class basicWindow(QWidget):
    def __init__(self):
        super().__init__()
        grid_layout = QGridLayout(self)

        for x in range(3):
            for y in range(3):
                button = QPushButton(str(3 * x + y))
                grid_layout.addWidget(button, x, y)

        self.setWindowTitle("Basic Grid Layout")

        QTimer.singleShot(0, lambda: self.print_coordinates(1, 1))

    def print_coordinates(self, x, y):
        grid_layout = self.layout()
        it = grid_layout.itemAtPosition(x, y)
        w = it.widget()
        print(w.pos())



if __name__ == "__main__":
    app = QApplication(sys.argv)
    windowExample = basicWindow()
    windowExample.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks! This is exactly what I was thinking of. One more thing, what would be a good way to get the coordinates without the on_clicked method? I thought of maybe using `grid_layout.itemAtPosition(x,y).pos()` in line 16 (just after you add the button to the widget) but this doens't work. – Juan Oct 03 '19 at 22:49
  • Thanks, but is there a way to "save" the coordinates in a variable? Something like 'coordinates = (x,y)' where x and y are the corresponding coordinates. I tried to do this but coordinates ends up being a Nonetype object. I want to save the coordinates of each button in different tuples. I don't really know much about QTimer so that's probably the reason on why I'm not being able to do this. – Juan Oct 03 '19 at 23:14
  • @Juan add `coordinates = w.pos().x(), w.pos().y()` on `print_coordinates` but anyway, storing it in a variable has nothing to do with the GUI but with OOP. Although I find it very strange that you want to get the coordinates of a widget, you may have an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – eyllanesc Oct 03 '19 at 23:17
  • I'll explain the whole problem. I'm creating a minigame where I have a character that moves 10 pixels per key pressed (WASD) and there are walls that the character cannot cross. I was thinking of saving the coordinates of the walls and make it so that the class that I created for the "walking" part of the program does not let the character go over these coordinates. The map itself (the one that shows the walls) is a QGridLayout and the character moves over a QLabel that is exactly the same size as the grid. This is probably a complicated way of creating it but I'm just starting with Pyqt. – Juan Oct 03 '19 at 23:38
  • @Juan mmm, as I thought you are going the wrong way. If you want to make a game with Qt then you must use Qt Graphics Framework: https://doc.qt.io/qt-5/graphicsview.html If you want to see examples you must download the source code: https://www.riverbankcomputing.com/software/pyqt/download5 , unzip it and go to the examples/graphicsview folder – eyllanesc Oct 03 '19 at 23:43
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/200367/discussion-between-juan-and-eyllanesc). – Juan Oct 03 '19 at 23:43