Scene Graph Painted Item Example¶
Shows how to implement QPainter-based custom scenegraph items.
The Painted Item example shows how to use the QML Scene Graph framework to implement custom scenegraph items using QPainter.

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from argparse import ArgumentParser, RawTextHelpFormatter
from pathlib import Path
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtCore import QCoreApplication
from PySide6.QtQml import QQmlDebuggingEnabler
from PySide6.QtQuick import QQuickView
from TextBalloon.textballoon import TextBalloon # noqa: F401
if __name__ == "__main__":
argument_parser = ArgumentParser(description="Scene Graph Painted Item Example",
formatter_class=RawTextHelpFormatter)
argument_parser.add_argument("-qmljsdebugger", action="store",
help="Enable QML debugging")
options = argument_parser.parse_args()
if options.qmljsdebugger:
QQmlDebuggingEnabler.enableDebugging(True)
app = QGuiApplication(sys.argv)
QCoreApplication.setOrganizationName("QtProject")
QCoreApplication.setOrganizationDomain("qt-project.org")
view = QQuickView()
view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
view.engine().addImportPath(Path(__file__).parent)
view.loadFromModule("painteditemexample", "Main")
if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
exit_code = QCoreApplication.exec()
del view
sys.exit(exit_code)
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import TextBalloon
Item {
height: 480
width: 320
//! [0]
ListModel {
id: balloonModel
ListElement {
balloonWidth: 200
}
ListElement {
balloonWidth: 120
}
}
ListView {
id: balloonView
anchors.bottom: controls.top
anchors.bottomMargin: 2
anchors.top: parent.top
delegate: TextBalloon {
anchors.right: index % 2 !== 0 ? parent?.right : undefined
height: 60
rightAligned: index % 2 !== 0
width: balloonWidth
}
model: balloonModel
spacing: 5
width: parent.width
}
//! [0]
//! [1]
Rectangle {
id: controls
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: 1
anchors.right: parent.right
border.width: 2
color: "white"
height: parent.height * 0.15
Text {
anchors.centerIn: parent
text: qsTr("Add another balloon")
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
balloonModel.append({"balloonWidth": Math.floor(Math.random() * 200 + 100)})
balloonView.positionViewAtIndex(balloonView.count -1, ListView.End)
}
onEntered: {
parent.color = "#8ac953"
}
onExited: {
parent.color = "white"
}
}
}
//! [1]
}
module painteditemexample
Main 1.0 Main.qml
# Copyright (C) 2025 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from PySide6.QtGui import QPainter, QBrush, QColor
from PySide6.QtQml import QmlElement
from PySide6.QtCore import QPointF, Qt, Property, Signal
from PySide6.QtQuick import QQuickPaintedItem
QML_IMPORT_NAME = "TextBalloon"
QML_IMPORT_MAJOR_VERSION = 1
QML_IMPORT_MINOR_VERSION = 0 # Optional
@QmlElement
class TextBalloon(QQuickPaintedItem):
rightAlignedChanged = Signal()
def __init__(self, parent=None):
self._rightAligned = False
super().__init__(parent)
@Property(bool, notify=rightAlignedChanged)
def rightAligned(self):
return self._rightAligned
@rightAligned.setter
def rightAligned(self, value):
self._rightAligned = value
self.rightAlignedChanged.emit()
def paint(self, painter: QPainter):
brush = QBrush(QColor("#007430"))
painter.setBrush(brush)
painter.setPen(Qt.PenStyle.NoPen)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
itemSize = self.size()
painter.drawRoundedRect(0, 0, itemSize.width(), itemSize.height() - 10, 10, 10)
if self.rightAligned:
points = [
QPointF(itemSize.width() - 10.0, itemSize.height() - 10.0),
QPointF(itemSize.width() - 20.0, itemSize.height()),
QPointF(itemSize.width() - 30.0, itemSize.height() - 10.0),
]
else:
points = [
QPointF(10.0, itemSize.height() - 10.0),
QPointF(20.0, itemSize.height()),
QPointF(30.0, itemSize.height() - 10.0),
]
painter.drawConvexPolygon(points)