How to position and anchor text in a QGraphicsScene?

Viewed 642

so i have this little code snippet:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

from PyQt5 import (QtWidgets as qtw, QtCore as qtc, QtGui as qtg)
import qdarkstyle
style = qdarkstyle.load_stylesheet_pyqt5()

class Scene(qtw.QGraphicsScene):
    """
    contains data
    """

    gridSize = (10, 10)
    rectSize = 30

    def __init__(self, parent, ):

        super(Scene, self).__init__(parent)
        self.setSceneRect(0, 0, 600, 400)
        self.createGrid(*self.gridSize)

    def createGrid(self, height, width):
        rectSize = self.rectSize

        self.fields = [
                [self.addRect(
                    rectSize*(1+x), rectSize*(1+y), rectSize, rectSize)
                 for x in range(width)]
                for y in range(height)]

        self.column_ids = [
                self.addText(letter) for letter in
                [chr(65 + i) for i in range(self.gridSize[1])]]
        [x.setPos(rectSize*(1.5 + i), rectSize*.5)
         for i, x in enumerate(self.column_ids)]

        self.row_ids = [
                self.addText(letter) for letter in
                [str(i) for i in range(1, self.gridSize[0])]]
        [x.setPos(rectSize*.5, rectSize*(1.5+i))
         for i, x in enumerate(self.row_ids)]


class View(qtw.QGraphicsView):
    """
    displays data
    """

    def __init__(self, parent=None, scene=None):

        super(View, self).__init__(parent)

        self.setScene(Scene(self))
        self.setMouseTracking(True)


if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)
    app.setStyleSheet(style)
    widget = qtw.QWidget()
    playerView = View(widget)
    widget.setGeometry(300, 300, 400, 300)
    widget.show()

    sys.exit(app.exec_())

It creates a QGraphicScene, adds some rectangles so they form a grid and then writes some text to id the rows and columns of the grid.

When i set the position of the row_id and column_id it seems to be the position of the upper left corner of the bounding box of the text.

What i want is that the center of the textboxes is aligned with the center of the rectangles in a row/column of the grid.

i.e. the result should look something like this:

___|_A_|_B_|_C_|_D_|_E_|___
   |   |   |   |   |   |
 1 |   |   |   |   |   |
___|___|___|___|___|___|___
   |   |   |   |   |   |
 2 |   |   |   |   |   |
___|___|___|___|___|___|___

But what i get at the moment is more like this:

___|__A|__B|__C|__D|__E|___
   |   |   |   |   |   |
   |   |   |   |   |   |
__1|___|___|___|___|___|___
   |   |   |   |   |   |
   |   |   |   |   |   |
__3|___|___|___|___|___|___

I searched the documentation for QGraphicsScene.addText and QTextItem to find a way to set the anchor for the setPos method to center or something alike but couldn't find anything.

I probably could just get the size of the bounding box of the textitems and subtract half of the position to get the right result but this seems like a hack and i would prefer to directly position the center of the TextItems.

1 Answers

Setting the position of a text item by subtracting the center is not a hack, it's the only (or, at least, the simplest) solution.

As the documentation shows, the item is always positioned with the coordinates set for the top-left corner, as it is for all QGraphicsItems (and, actually, all QWidgets).

text item coordinate system

So, the correct way to position them is to subtract the boundingRect center from the position you want to put the items. I suggest you to avoid list comprehensions like you did: there's no performance benefit, and it makes your code less readable.

    self.column_ids = []
    for i in range(self.gridSize[1]):
        letter = chr(65 + i)
        pos = qtc.QPointF(rectSize * (1.5 + i), rectSize * .5)
        item = self.addText(letter)
        item.setPos(pos - item.boundingRect().center())
        self.column_ids.append(item)

    self.row_ids = []
    for i in range(self.gridSize[0]):
        pos = qtc.QPointF(rectSize * .5, rectSize * (1.5 + i))
        item = self.addText(str(i + 1))
        item.setPos(pos - item.boundingRect().center())
        self.row_ids.append(item)

Consider that you could also create a subclass of QGraphicsTextItem and override its setPos method or (better) create your own method for centering it.

class CenteredTextItem(qtw.QGraphicsTextItem):
    def centerAt(self, pos):
        self.setPos(pos - self.boundingRect().center())

class Scene(qtw.QGraphicsScene):
    def createGrid(self, height, width):
        # ...
        for i in range(self.gridSize[1]):
            letter = chr(65 + i)
            pos = qtc.QPointF(rectSize * (1.5 + i), rectSize * .5)
            item = CenteredTextItem(letter)
            self.addItem(item)
            item.centerAt(pos)
            self.column_ids.append(item)

        self.row_ids = []
        for i in range(self.gridSize[0]):
            pos = qtc.QPointF(rectSize * .5, rectSize * (1.5 + i))
            item = CenteredTextItem(str(i + 1))
            self.addItem(item)
            item.centerAt(pos)
            self.row_ids.append(item)
Related