Qt World Summit: Submit your Presentation


QSGGeometry does not work on PySide2



  • I am trying to use QSGGeometry from PySide2. In the C++ implementation vertexDataAsPoint2D() return a pointer to the 2d points. Not sure how to access the points from geometry.vertexDataAsPoint2D() in PySide2. In PyQt, the following works,

    vertices = geometry.vertexDataAsPoint2D()
    vertices[1].set(x2, y2)
    

    But in PySide2 this gives an error,
    TypeError: 'PySide2.QtQuick.QSGGeometry.Point2D' object is not subscriptable


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    From the error it seems that you directly get a Point2D object.

    Check the type of the return value of that method.



  • @SGaist , you are right, vertexDataAsPoint2D() returns Point2D. Not sure how this works with PyQt and not on PySide2. From the documentation I understand this function should return a pointer. But pointer in python?



  • @Advig
    Just read it as returning an object/instance in Python.


  • Lifetime Qt Champion

    @Advig it's a binding issue. You should check the bug report system to see if there's already something related. If not please open a new issue. The best would be if you can provided a minimal project that shows the issue.



  • @SGaist
    Thanks. Please find attached the file. Also creating a bug report as advised.
    trying to create Scene Graph - Custom Geometry (https://doc.qt.io/qt-5/qtquick-scenegraph-customgeometry-example.html) in Pyside2, but getting following error :
    File "/BezierCurveProj/main.py", line 119, in updatePaintNode
    vertices[i].set(x, y)
    TypeError: 'PySide2.QtQuick.QSGGeometry.Point2D' object is not subscriptable

    # This Python file uses the following encoding: utf-8
    import sys
    from PySide2.QtWidgets import QApplication
    from PySide2.QtQuick import QQuickView, QQuickItem, QSGNode, QSGGeometryNode, QSGGeometry, QSGMaterialType
    from PySide2.QtCore import Signal, QUrl, Property, QPointF
    from PySide2.QtGui import QColor
    from PySide2.QtQml import qmlRegisterType
    
    
    class BezierCurve(QQuickItem):
        def __init__(self):
            QQuickItem.__init__(self)
            self.m_p1 = QPointF(0, 0)
            self.m_p2 = QPointF(0, 1)
            self.m_p3 = QPointF(1, 0)
            self.m_p4 = QPointF(1, 1)
            self.m_segmentCount = 32
    
            self.node = None
            self.geometry = None
            self.setFlag(QQuickItem.ItemHasContents, True)
    
        @Signal
        def p1Changed(self):
            pass
    
        @Signal
        def p2Changed(self):
            pass
    
        @Signal
        def p3Changed(self):
            pass
    
        @Signal
        def p4Changed(self):
            pass
    
        @Signal
        def segmentCountChanged(self):
            pass
    
        def p1(self):
            return self.m_p1
    
        def p2(self):
            return self.m_p2
    
        def p3(self):
            return self.m_p3
    
        def p4(self):
            return self.m_p4
    
        def segmentCount(self):
            return self.m_segmentCount
    
        def setP1(self, p):
            if (p == self.m_p1):
                return
            self.m_p1 = p
            self.p1Changed.emit()
            self.update()
    
        def setP2(self, p):
            if (p == self.m_p2):
                return
            self.m_p2 = p
            self.p2Changed.emit()
            self.update()
    
        def setP3(self, p):
            if (p == self.m_p3):
                return
            self.m_p3 = p
            self.p3Changed.emit()
            self.update()
    
        def setP4(self, p):
            if (p == self.m_p4):
                return
            self.m_p4 = p
            self.p4Changed.emit()
            self.update()
    
        def setSegmentCount(self, p):
            if (p == self.m_segmentCount):
                return
            self.m_segmentCount = p
            self.segmentCountChanged.emit()
            self.update()
    
        def updatePaintNode(self, oldNode, updatePaintNodeData):
            self.node = oldNode
            if(self.node is None):
                self.node = QSGGeometryNode()
                self.geometry = QSGGeometry(QSGGeometry.defaultAttributes_Point2D(), self.m_segmentCount)
                self.geometry.setLineWidth(2)
                self.geometry.setDrawingMode(QSGGeometry.DrawLineStrip)
                self.node.setGeometry(self.geometry)
                self.node.setFlag = QSGNode.OwnsGeometry
                material = QSGMaterialType()
                material.setColor = QColor(255, 0, 0)
                self.node.setMaterial = material
                self.node.setFlag = QSGNode.OwnsMaterial
                self.geometry.allocate(self.m_segmentCount)
            else:
                self.geometry = self.node.geometry()
                self.geometry.allocate(self.m_segmentCount)
    
            itemSize = self.size()
            vertices = self.geometry.vertexDataAsPoint2D()
            for i in range(self.m_segmentCount):
                t = i / (self.m_segmentCount - 1)
                invt = 1 - t
                pos = (invt * invt * invt * self.m_p1) + (3 * invt * invt * t * self.m_p2) + (3 * invt * t * t * self.m_p3) + (t * t * t * self.m_p4)
                x = pos.x() * itemSize.width()
                y = pos.y() * itemSize.height()
                vertices[i].set(x, y)
    
            self.node.markDirty(QSGNode.DirtyGeometry)
            return self.node
    
        p1 = Property('QPointF', p1, setP1, notify=p1Changed)
        p2 = Property('QPointF', p2, setP2, notify=p2Changed)
        p3 = Property('QPointF', p3, setP3, notify=p3Changed)
        p4 = Property('QPointF', p4, setP4, notify=p4Changed)
        segmentCount = Property('int', segmentCount, setSegmentCount, notify=segmentCountChanged)
    
    
    if __name__ == "__main__":
        app = QApplication([])
        view = QQuickView()
        view.setResizeMode(QQuickView.SizeRootObjectToView)
        qmlRegisterType(BezierCurve, 'BezierCurve', 1, 0, 'BezierCurve')
        url = QUrl("main.qml")
        view.setSource(url)
        view.show()
        sys.exit(app.exec_())
    

  • Lifetime Qt Champion

    Did you open an issue on the bug tracker ?



  • @SGaist . Thanks. Yes. PYSIDE-1345


  • Lifetime Qt Champion

    Thank you !



  • @SGaist : Do you know any easy way I could write a patch for this. I am not sure if I will get a solution soon from bug tracking system.



  • I am facing the same issue. This basically makes writing custom QQuickItems in Python impossible.

    Also, I am hitting this issue with PySide6.

    The bug that was reported is stuck nowhere.



  • Quick update. I reviewed the ticket again and realized there is a patch there. I recomplied PySide6 with the patch applied and I can say that vertexDataAsPoint2D() now does return a list of Point2D instead of just Point2D. In the ticket the developer states that the fix crashes submitted example. I am yet to confirm whether this causes any issues on my side.



  • Ok, I have the same problem as mentioned in the ticket - it segfaults.

    To summarize, you cannot write custom QQuickItems in Python at this time.


  • Lifetime Qt Champion

    Thanks for the feedback !

    Can you upload your minimal example on the report ?
    It can help fix this issue as well.



  • Hi SGaist,

    Here's the code. It's really basic:

    # This Python file uses the following encoding: utf-8
    import os
    from pathlib import Path
    import sys
    
    from PySide6.QtGui import QGuiApplication, QColor
    from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterType
    from PySide6.QtQuick import QQuickItem, QSGGeometryNode, QSGGeometry, QSGFlatColorMaterial, QSGNode
    
    
    class JustItem(QQuickItem):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setFlag(QQuickItem.ItemHasContents, enabled=True)
    
    
        def updatePaintNode(self, node, update_data):
            if node is None:
                node = QSGGeometryNode()
                geometry = QSGGeometry(QSGGeometry.defaultAttributes_Point2D(), 4)
                geometry.setLineWidth(1)
                geometry.setDrawingMode(QSGGeometry.DrawTriangles)
    
    
                material = QSGFlatColorMaterial()
                material.setColor(QColor(255, 0, 0, 127))
    
                node.setGeometry(geometry)
                node.setMaterial(material)
                node.setFlag(QSGNode.OwnsGeometry)
                node.setFlag(QSGNode.OwnsMaterial)
            else:
                geometry = node
    
            vertex_data = geometry.vertexDataAsPoint2D()
    
            vertex_data[0].set(10, 10)
            vertex_data[1].set(100, 10)
            vertex_data[2].set(100, 100)
            vertex_data[3].set(10, 100)
            node.markDirty(QSGNode.DirtyGeometry)
    
            return node
    
    
    if __name__ == "__main__":
        qmlRegisterType(JustItem, "PythonTypes", 1, 0, "JustItem")
    
        app = QGuiApplication(sys.argv)
        engine = QQmlApplicationEngine()
        engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
    
    
        if not engine.rootObjects():
            sys.exit(-1)
        sys.exit(app.exec())
    

    and main.qml:

    import QtQuick
    import QtQuick.Window
    import PythonTypes 1.0
    
    Window {
        width: 640
        height: 480
        visible: true
        title: qsTr("Hello World")
    
        JustItem {
    
        }
    }
    

    If you run it without the patch, it will complain that Point2D is not subscriptible (because Point2D and not a list of Point2Ds is returned). If you run it with the patch applied, it will segfault.


  • Lifetime Qt Champion

    Thanks for posting it here but you should add it to the bug report as well. It will be easier to find over there.


Log in to reply