Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Cannot make QGraphicsTextItem both in center of the scene and with supported word wrap
Forum Updated to NodeBB v4.3 + New Features

Cannot make QGraphicsTextItem both in center of the scene and with supported word wrap

Scheduled Pinned Locked Moved Unsolved General and Desktop
5 Posts 3 Posters 234 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    sapvi
    wrote on last edited by
    #1

    Hi all, I always seem to find more questions about some minor things... :)

    (Also, it is really interesting why this is flagged as spam... I literally needed to add this text here just so that that automatic anti-spam lets me in. I know my questions are annoying but they are not really spam :D)

    This time I cannot get QGraphicsTextItem to be both in the center of its corresponding QGraphicsScene and wrappable.

    In MRE below, if I leave self._text.document().adjustSize() commented out, then the QGraphicsTextItem reacts to the change of the window size (i.e. wraps properly) but it is not centered and seems to be located rather randomly on the scene. If I uncomment self._text.document().adjustSize(), then the text is indeed in center but the wrap is fixed and is not adapting to the size of the scene: i.e. even if there is enough space for the text, it will be wrapper for now reason and if, on the contrary, there is not enough space, the wrap will not change.

    I have seen several similar questions but they pretty much advised to use setPos, which is what I am doing. So, what am I doing wrong? How to make both properties available at the same time? Should I use something else instead of QGraphicsTextItem?

    import sys
    from PySide6.QtCore import Qt, QRectF
    from PySide6.QtGui import QColor, QFont
    from PySide6.QtWidgets import (
        QApplication,
        QGraphicsView,
        QGraphicsScene,
        QGraphicsTextItem,
        QGraphicsPixmapItem,
        QVBoxLayout,
        QWidget,
    )
    
    
    class ImageViewer(QGraphicsView):
        def __init__(self, text="", parent=None):
            super().__init__(parent)
    
            self._scene = QGraphicsScene(self)
            self._photo = QGraphicsPixmapItem()
            self._photo.setShapeMode(QGraphicsPixmapItem.ShapeMode.BoundingRectShape)
            self._scene.addItem(self._photo)
    
            self._text = QGraphicsTextItem(text)
            self._text.setDefaultTextColor(QColor(255, 255, 255))
            self._text.document().setTextWidth(self.viewport().width() - 20)
            font = QFont()
            font.setPointSize(20)
            self._text.setFont(font)
            self._scene.addItem(self._text)
    
            self.setScene(self._scene)
            self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
            self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
            self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
            self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
            self.setBackgroundBrush(QColor(0, 0, 0))
            self.setFrameShape(QGraphicsView.Shape.NoFrame)
    
            self._center_text()
    
        def _center_text(self):
            max_width = self.viewport().width() - 20
            self._text.document().setTextWidth(max_width)
            # self._text.document().adjustSize()
            text_rect = self._text.boundingRect()
            scene_rect = self._scene.sceneRect()
            center_x = scene_rect.center().x() - text_rect.width() / 2
            center_y = scene_rect.center().y() - text_rect.height() / 2
            self._text.setPos(center_x, center_y)
    
        def resizeEvent(self, event):
            super().resizeEvent(event)
            self._center_text()
    
    
    class MainWindow(QWidget):
        def __init__(self):
            super().__init__()
            layout = QVBoxLayout(self)
            self.viewer = ImageViewer(
                "This is a centered text. Resize to test the wrapping behavior."
            )
            layout.addWidget(self.viewer)
            self.setLayout(layout)
            self.setWindowTitle("QGraphicsTextItem Example")
            self.resize(800, 600)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec())
    
    JonBJ 1 Reply Last reply
    0
    • S sapvi

      Hi all, I always seem to find more questions about some minor things... :)

      (Also, it is really interesting why this is flagged as spam... I literally needed to add this text here just so that that automatic anti-spam lets me in. I know my questions are annoying but they are not really spam :D)

      This time I cannot get QGraphicsTextItem to be both in the center of its corresponding QGraphicsScene and wrappable.

      In MRE below, if I leave self._text.document().adjustSize() commented out, then the QGraphicsTextItem reacts to the change of the window size (i.e. wraps properly) but it is not centered and seems to be located rather randomly on the scene. If I uncomment self._text.document().adjustSize(), then the text is indeed in center but the wrap is fixed and is not adapting to the size of the scene: i.e. even if there is enough space for the text, it will be wrapper for now reason and if, on the contrary, there is not enough space, the wrap will not change.

      I have seen several similar questions but they pretty much advised to use setPos, which is what I am doing. So, what am I doing wrong? How to make both properties available at the same time? Should I use something else instead of QGraphicsTextItem?

      import sys
      from PySide6.QtCore import Qt, QRectF
      from PySide6.QtGui import QColor, QFont
      from PySide6.QtWidgets import (
          QApplication,
          QGraphicsView,
          QGraphicsScene,
          QGraphicsTextItem,
          QGraphicsPixmapItem,
          QVBoxLayout,
          QWidget,
      )
      
      
      class ImageViewer(QGraphicsView):
          def __init__(self, text="", parent=None):
              super().__init__(parent)
      
              self._scene = QGraphicsScene(self)
              self._photo = QGraphicsPixmapItem()
              self._photo.setShapeMode(QGraphicsPixmapItem.ShapeMode.BoundingRectShape)
              self._scene.addItem(self._photo)
      
              self._text = QGraphicsTextItem(text)
              self._text.setDefaultTextColor(QColor(255, 255, 255))
              self._text.document().setTextWidth(self.viewport().width() - 20)
              font = QFont()
              font.setPointSize(20)
              self._text.setFont(font)
              self._scene.addItem(self._text)
      
              self.setScene(self._scene)
              self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
              self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
              self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
              self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
              self.setBackgroundBrush(QColor(0, 0, 0))
              self.setFrameShape(QGraphicsView.Shape.NoFrame)
      
              self._center_text()
      
          def _center_text(self):
              max_width = self.viewport().width() - 20
              self._text.document().setTextWidth(max_width)
              # self._text.document().adjustSize()
              text_rect = self._text.boundingRect()
              scene_rect = self._scene.sceneRect()
              center_x = scene_rect.center().x() - text_rect.width() / 2
              center_y = scene_rect.center().y() - text_rect.height() / 2
              self._text.setPos(center_x, center_y)
      
          def resizeEvent(self, event):
              super().resizeEvent(event)
              self._center_text()
      
      
      class MainWindow(QWidget):
          def __init__(self):
              super().__init__()
              layout = QVBoxLayout(self)
              self.viewer = ImageViewer(
                  "This is a centered text. Resize to test the wrapping behavior."
              )
              layout.addWidget(self.viewer)
              self.setLayout(layout)
              self.setWindowTitle("QGraphicsTextItem Example")
              self.resize(800, 600)
      
      
      if __name__ == "__main__":
          app = QApplication(sys.argv)
          window = MainWindow()
          window.show()
          sys.exit(app.exec())
      
      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #2

      @sapvi
      Only suggestions.
      Presumably you want to get the text wrapping/centering/whatever correct before you worry about the item being centered on the scene/view. Unless they interfere/interact with each other, which I wouldn't know.
      You call QTextDocument::adjustSize(). Does it improve any if you call QGraphicsTextItem::adjustSize() instead/as well? E.g. maybe that is required for QGraphicsTextItem::boundingRect()?

      S 1 Reply Last reply
      0
      • JonBJ JonB

        @sapvi
        Only suggestions.
        Presumably you want to get the text wrapping/centering/whatever correct before you worry about the item being centered on the scene/view. Unless they interfere/interact with each other, which I wouldn't know.
        You call QTextDocument::adjustSize(). Does it improve any if you call QGraphicsTextItem::adjustSize() instead/as well? E.g. maybe that is required for QGraphicsTextItem::boundingRect()?

        S Offline
        S Offline
        sapvi
        wrote on last edited by
        #3

        @JonB hello, thanks for the answer.

        I've tried also calling QGraphicsTextItem::adjustSize() (i.e. self._text.adjustSize()). I've tried both QTextDocument::adjustSize() and QGraphicsTextItem::adjustSize() in different configurations: before setting max width / after setting max width, together or separately. It did not seem to influence result at all: if I use adjustSize, I get text centered but too narrow, if I do not use it, I get good wrap but in wrong position.

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          Hi,

          Might be a silly question but what about QGraphicsView::centerOn ?

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          S 1 Reply Last reply
          0
          • SGaistS SGaist

            Hi,

            Might be a silly question but what about QGraphicsView::centerOn ?

            S Offline
            S Offline
            sapvi
            wrote on last edited by
            #5

            @SGaist hi, I managed to fix it but my solution looks very over-engineered to me... I've also tried your suggestion but by itself it does not really work as intended.

            def _center_text(self):
                    width = max(0, self.viewport().width() - 20)
                    doc = self._text.document()
                    doc.setTextWidth(width)
                    self.centerOn(self._text)
            

            This does not really work because the problem is that the text itself is not centered. So, the text item is occupying width of whole viewport but text is on the left anyway, though wrap works correctly.

            def _center_text(self):
                    width = max(0, self.viewport().width() - 20)
                    doc = self._text.document()
                    doc.setTextWidth(-1)
                    natural_width = doc.idealWidth()
                    text_width = min(natural_width, width)
                    doc.setTextWidth(text_width)
                    self.centerOn(self._text)
            

            This works almost as intended, but the wrapped text is again on the left. So the text is in the center when it has only one line, the effect is broken when wrap is introduced.

            def _center_text(self):
                    width = max(0, self.viewport().width() - 20)
                    doc = self._text.document()
                    doc.setTextWidth(-1)
                    natural_width = doc.idealWidth()
                    text_width = min(natural_width, width)
                    doc.setTextWidth(text_width)
                    self.centerOn(self._text)
                    cursor = QTextCursor(doc)
                    block_format = QTextBlockFormat()
                    block_format.setAlignment(Qt.AlignCenter)
                    cursor.select(QTextCursor.Document)
                    cursor.mergeBlockFormat(block_format)
            

            These crazy additional lines which I barely found make the centered text even for the wrapped version.

            However, I still had some weird effect in my real app when first position of the text was wrong, but after any resize it would normalize. This is most likely because the widget there is not visible by default so some sizes are not properly calculated. I am not sure how to solve this with centerOn. Tried several things but problem persisted or the location broke completely. So I returned to setPos and even had to add setSceneRect on top of that. With this solution I get correct behavior at all time: different sizes, hiding/showing widget, switching between tabs... Amount of code required just for centering the text is crazy though, so if someone has improvement ideas, they are welcome here.

            def _center_text(self):
                    width = max(0, self.viewport().width() - 20)
                    height = max(0, self.viewport().height() - 20)
                    self._scene.setSceneRect(QRectF(0, 0, width, height))
                    doc = self._text.document()
                    doc.setTextWidth(-1)
                    natural_width = doc.idealWidth()
                    text_width = min(natural_width, width)
                    doc.setTextWidth(text_width)
                    cursor = QTextCursor(doc)
                    block_format = QTextBlockFormat()
                    block_format.setAlignment(Qt.AlignCenter)
                    cursor.select(QTextCursor.Document)
                    cursor.mergeBlockFormat(block_format)
                    text_rect = self._text.boundingRect()
                    scene_rect = self._scene.sceneRect()
                    center_x = scene_rect.center().x() - text_rect.width() / 2
                    center_y = scene_rect.center().y() - text_rect.height() / 2
                    self._text.setPos(center_x, center_y)
            
            1 Reply Last reply
            0

            • Login

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved