Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Centered text in QGraphicsView

Centered text in QGraphicsView

Scheduled Pinned Locked Moved Unsolved Qt for Python
3 Posts 2 Posters 48 Views
  • 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 last edited by
    #1

    So, I thought I've already solved it but no... Something so simple as just having centered text in QGraphicsView is seriously testing my sanity.

    What I want:
    Have text in QGraphicsView IN THE CENTER. That's all. The text should be wrappable and kept in center of the QGraphicsView even on resize of the window. User can load the image, in which case the text becomes hidden. User can remove the image, and then the scene returns to its original state.

    Now, I've tried endless options to make this but something was always off.

    I apologize for a bit long MRE: I really cannot pinpoint the issue to some more specific location. In MRE, I can load dummy image and remove it, texts appears/disappears, but it's location seems pretty much random to me due to some Qt intrinsics.

    I am on PySide6.

    import sys
    from PySide6.QtWidgets import (
        QApplication,
        QGraphicsView,
        QGraphicsScene,
        QGraphicsTextItem,
        QGraphicsPixmapItem,
        QVBoxLayout,
        QWidget,
        QPushButton,
    )
    from PySide6.QtGui import (
        QPixmap,
        QColor,
        QBrush,
        QFont,
        QTransform,
        QTextCursor,
        QTextBlockFormat,
    )
    from PySide6.QtCore import Qt, QRectF
    
    
    class ImageViewer(QGraphicsView):
        SCALE_FACTOR = 1.25
    
        def __init__(self, text="", parent=None):
            super().__init__(parent)
            self._zoom = 0
            self._pinned = False
            self._empty = True
            self._placeholder_str = text
            self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
            self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
            self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
            self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
            self.setBackgroundBrush(QBrush(QColor(0, 0, 0)))
            self.setFrameShape(QGraphicsView.Shape.NoFrame)
            self.setCacheMode(QGraphicsView.CacheModeFlag.CacheNone)
            self._create_empty_scene()
            self.interaction_allowed = False
    
        def _create_empty_scene(self):
            self._scene = QGraphicsScene(self)
            self.setScene(self._scene)
            self.setSceneRect(0, 0, self.viewport().width(), self.viewport().height())
            self._scene.setSceneRect(
                0, 0, self.viewport().width(), self.viewport().height()
            )
            self._photo = QGraphicsPixmapItem()
            self._photo.setShapeMode(QGraphicsPixmapItem.ShapeMode.BoundingRectShape)
            self._scene.addItem(self._photo)
            self._text = QGraphicsTextItem(self._placeholder_str)
            self._text.setVisible(True)
            self._text.setDefaultTextColor(QColor(255, 255, 255))
            self._scene.addItem(self._text)
            font = QFont()
            font.setPointSize(18)
            self._text.setFont(font)
            self._center_text()
    
        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)
    
        def setPixmap(self, pixmap=None):
            if pixmap and not pixmap.isNull():
                self._empty = False
                self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
                self._text.setVisible(False)
                self.setBackgroundBrush(Qt.NoBrush)
                prev_rect = self._photo.pixmap().rect()
                new_rect = pixmap.rect()
                prev_transform = self.transform()
                self._photo.setPixmap(pixmap)
                if not prev_rect.isNull() and not new_rect.isNull():
                    prev_size = prev_rect.size()
                    new_size = new_rect.size()
                    scale_x = new_size.width() / prev_size.width()
                    scale_y = new_size.height() / prev_size.height()
                    self.setTransform(prev_transform)
                    self.scale(1 / scale_x, 1 / scale_y)
                self.interaction_allowed = True
            else:
                self._empty = True
                self.interaction_allowed = False
                self._create_empty_scene()
                self.setTransform(QTransform())
                self.setZoomPinned(False)
                self.setDragMode(QGraphicsView.DragMode.NoDrag)
                self.setBackgroundBrush(QBrush(QColor(0, 0, 0)))
            self.resetView(ImageViewer.SCALE_FACTOR**self._zoom)
    
        def resetView(self, scale=1):
            rect = QRectF(self._photo.pixmap().rect())
            if not rect.isNull():
                self.setSceneRect(rect)
                if (scale := max(1, scale)) == 1:
                    self._zoom = 0
                if self.hasPhoto():
                    unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
                    self.scale(1 / unity.width(), 1 / unity.height())
                    viewrect = self.viewport().rect()
                    scenerect = self.transform().mapRect(rect)
                    factor = (
                        min(
                            viewrect.width() / scenerect.width(),
                            viewrect.height() / scenerect.height(),
                        )
                        * scale
                    )
                    self.scale(factor, factor)
                    if not self.zoomPinned():
                        self.centerOn(self._photo)
    
        def zoom(self, step):
            zoom = max(0, self._zoom + (step := int(step)))
            if zoom != self._zoom:
                self._zoom = zoom
                if self._zoom > 0:
                    if step > 0:
                        factor = ImageViewer.SCALE_FACTOR**step
                    else:
                        factor = 1 / ImageViewer.SCALE_FACTOR ** abs(step)
                    self.scale(factor, factor)
                else:
                    self.resetView()
    
        def wheelEvent(self, event):
            if self.interaction_allowed:
                delta = event.angleDelta().y()
                self.zoom(delta and delta // abs(delta))
    
        def resizeEvent(self, event):
            super().resizeEvent(event)
            self.resetView()
            self._center_text()
    
        def toggleDragMode(self):
            if self.dragMode() == QGraphicsView.DragMode.ScrollHandDrag:
                self.setDragMode(QGraphicsView.DragMode.NoDrag)
            elif not self._photo.pixmap().isNull():
                self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
    
        def hasPhoto(self):
            return not self._empty
    
        def zoomPinned(self):
            return self._pinned
    
        def setZoomPinned(self, enable):
            self._pinned = bool(enable)
    
    
    class MainWindow(QWidget):
        def __init__(self):
            super().__init__()
            self.viewer = ImageViewer(
                "Placeholder Text Text Text Text Text Text Text Text Text Text"
            )
            self.viewer.setMinimumSize(800, 600)
            self.load_button = QPushButton("Load Image")
            self.load_button.clicked.connect(self.load_image)
            self.unload_button = QPushButton("Remove Image")
            self.unload_button.clicked.connect(self.remove_image)
            layout = QVBoxLayout()
            layout.addWidget(self.viewer)
            layout.addWidget(self.load_button)
            layout.addWidget(self.unload_button)
            self.setLayout(layout)
            self.setWindowTitle("Qt Image Viewer")
    
        def load_image(self):
            pixmap = QPixmap(10000, 5000)
            pixmap.fill(QColor(255, 0, 0))
            self.viewer.setPixmap(pixmap)
    
        def remove_image(self):
            self.viewer.setPixmap(None)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec())
    
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote last edited by
      #2

      Hi,

      You are missing

      self.setSceneRect(0, 0, self.viewport().width(), self.viewport().height())
      

      before calling self._center_text() in resizeEvent.

      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
      1
      • SGaistS SGaist

        Hi,

        You are missing

        self.setSceneRect(0, 0, self.viewport().width(), self.viewport().height())
        

        before calling self._center_text() in resizeEvent.

        S Offline
        S Offline
        sapvi
        wrote last edited by
        #3

        @SGaist said in Centered text in QGraphicsView:

        self.setSceneRect(0, 0, self.viewport().width(), self.viewport().height())

        Thank you. Yes, that is indeed what was missing. I had this line before, but probably at the wrong place, so I discarded it.

        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