2

I am trying to write my own QQuickItems that use the SceneGraph to draw shapes as if it the parent Item was a Canvas. I am using PySide6 and Python. During my attempts I found a bug in PySide6, which was earlier reported. I downloaded the patch that fixed it and it seems to be ok now (https://bugreports.qt.io/browse/PYSIDE-1345).

My program now compiles and runs, but the node is not painted. If I understand the documentation correctly, I need to do three things to have a custom QQuickItem painted:

  1. Inherit from QQuickItem
  2. Override updatePaintNode
  3. Set the ItemHasContents flag

I did all that, registered my type, added it in QML and made sure it has non-zero dimensions. Unfortunately, it still does not get paited. I do not know what to do next.

Here's the minimal working example: main.py

# 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


class JustItem(QQuickItem):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFlag(QQuickItem.ItemHasContents)

    def updatePaintNode(self, node, update_data):
        if node is None:
            node = QSGGeometryNode()

        geometry = QSGGeometry(QSGGeometry.defaultAttributes_Point2D(), 4)
        geometry.setDrawingMode(QSGGeometry.DrawTriangles)
        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)

        material = QSGFlatColorMaterial()
        material.setColor(QColor(255, 0, 0, 127))

        node.setGeometry(geometry)
        node.setMaterial(material)

        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 QML:

import QtQuick
import QtQuick.Window
import PythonTypes 1.0

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    JustItem {

    }
}

The result is an empty white window. When I resize it, it segfaults.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
WhiteStork
  • 385
  • 1
  • 15
  • 1
    at least you have to set the items size. Did you try to use a debugger to locate the error? – folibis Jul 11 '21 at 15:35
  • You need to give your Item a width and height at least. Or use anchors to fill the parent. – David K. Hess Jul 11 '21 at 17:08
  • I tried both. Width & Height do not change the situation. anchors.fill causes the program to immediately freeze then segfault – WhiteStork Jul 12 '21 at 16:17
  • I run the debugger with the anchors.fill variant. I could see that JustItem was a child of Window and inherited it's width and height. If I stepped forward in the debugger, it would segfault. – WhiteStork Jul 12 '21 at 16:18

1 Answers1

2

This is a bug in PySide 2. Currently it is not possible to draw your custom QQuickItems in PySide 2.

The bug is discussed in more detail here: https://forum.qt.io/topic/116585/qsggeometry-does-not-work-on-pyside2/16

There's also a bug report, which has a proposed fix. The fix causes segmentation fault: https://bugreports.qt.io/browse/PYSIDE-1345

Unfortunately, if you want to draw custom QQuickItems you either need to write them in C++ or use another GUI framework entirely.

WhiteStork
  • 385
  • 1
  • 15