0
votes

I have two qml components sitting in one module.

components
  |- Edge.qml
  |- Class.qml
  |- qmdir
main
  |- main.qml
main.py

with main.qml

import urmelgraph.components 1.0

import CustomGeometry 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 240
    title: qsTr("Test")
    color: "#2C3E50"
}

and qmdir

module urmelgraph.components
Class 1.0 Class.qml
Edge 1.0 Edge.qml

I am loading both inside my python main.py file.

from PyQt5.QtGui import QGuiApplication, QColor, QSurfaceFormat
from PyQt5.QtQml import QQmlApplicationEngine, QQmlComponent, QQmlContext, qmlRegisterType, QQmlProperty
from PyQt5.QtQuick import QQuickItem, QQuickView, QSGGeometryNode, QSGGeometry, QSGNode, QSGFlatColorMaterial
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty, QUrl, QPointF, QSizeF

from  pathlib import Path

class StraightLine(QQuickItem):
    p1_Changed = pyqtSignal()
    p2_Changed = pyqtSignal()
    segment_count_Changed = pyqtSignal()

    def __init__(self, parent: QQuickItem, p1: QPointF = QPointF(0,0), p2: QPointF = QPointF(1,1), segment_count: int = 2):
        super().__init__(parent)
        self._p1 = p1
        self._p2 = p2
        self._segment_count = segment_count
        self.setFlag(QQuickItem.ItemHasContents, True)

    @pyqtProperty("QPointF", notify = p1_Changed)
    def p1(self):
        return self._p1

    @p1.setter
    def p1(self, p1: QPointF):
        if p1 == self._p1:
            return

        self._p1 = p1
        self.p1_Changed.emit()
        self.update()

    @pyqtProperty("QPointF", notify = p2_Changed)
    def p2(self):
        return self._p2

    @p2.setter
    def p2(self, p2: QPointF):
        if p2 == self._p2:
            return

        self._p2 = p2
        self.p2_Changed.emit()
        self.update()

    @pyqtProperty(int, notify = segment_count_Changed)
    def segment_count(self):
        return self._segment_count

    @segment_count.setter
    def segment_count(self, count: int):
        if count == self._segment_count:
            return

        self._segment_count = count
        self.segment_count_Changed.emit()
        self.update()

    def updatePaintNode(self, oldNode: QSGGeometryNode, _):
        if oldNode == None:
            node = QSGGeometryNode()
            geometry = QSGGeometry(QSGGeometry.defaultAttributes_Point2D(), self._segment_count)
            geometry.setLineWidth(3)
            geometry.setDrawingMode(QSGGeometry.DrawLineStrip)
            node.setGeometry(geometry)
            node.setFlag(QSGNode.OwnsGeometry)

            material = QSGFlatColorMaterial()
            material.setColor(QColor(45, 100, 120))
            node.setMaterial(material)
            node.setFlag(QSGNode.OwnsMaterial)
        else:
            node = oldNode
            geometry = node.geometry()
            geometry.allocate(self._segment_count)

        itemSize = self.size()
        vertices = geometry.vertexDataAsPoint2D()

        x1 = self._p1.x()
        y1 = self._p1.y()
        vertices[0].set(x1, y1)

        x2 = self._p2.x()
        y2 = self._p2.y()
        vertices[1].set(x2, y2)

        print(vertices[1].x)

        node.markDirty(QSGNode.DirtyGeometry)

        return node

if __name__ == "__main__":
    import sys

    path = Path("..")
    resolved_path = path.resolve()

    # Create an instance of the application
    app = QGuiApplication(sys.argv)

    # Set antialising 4 samples
    format = QSurfaceFormat()
    format.setSamples(4)
    QSurfaceFormat.setDefaultFormat(format)

    # register custom types
    qmlRegisterType(StraightLine, "CustomGeometry", 1, 0, "StraightLine")

    # Create QML engine
    engine = QQmlApplicationEngine()

    # Load the qml file into the engine
    engine.addImportPath(str(resolved_path))
    engine.load("main/main.qml")

    # load the components
    component = QQmlComponent(engine)
    component.loadUrl(QUrl("components/Class.qml"))
    line_component = QQmlComponent(engine)
    line_component.loadUrl(QUrl("components/Edge.qml"))
    # check for component creation errors
    for error in component.errors():
        print(error.toString())
    # check for component creation errors
    for error in line_component.errors():
        print(error.toString())

    classes = []
    for index, class_name in enumerate(["Person", "Home"]):
        # create a new instance of the component
        cclass = component.create()
        # set the class name property of the component
        cclass.setProperty("className", class_name)
        cclass.setX((cclass.width() + 50) * index)
        cclass.setParentItem(engine.rootObjects()[0].findChild(QQuickItem))
        classes.append(cclass)

    line = line_component.beginCreate(engine.rootContext())
    line.setProperty("anchor1", classes[0])
    line.setProperty("anchor2", classes[1])
    line_component.completeCreate()

    # check for object creation errors
    for error in line_component.errors():
        print(error.toString())
    for error in component.errors():
        print(error.toString())

    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

But now I want to connect the first point of the Edge E to a Class component A and the second point of the Edge E to a Class component B.

For that I have created properties in the Edge.qml.

import QtQuick 2.11
import CustomGeometry 1.0
import urmelgraph.components 1.0

StraightLine {
    property Class anchor1
    property Class anchor2

    anchors.fill: parent

    Component.onCompleted: {
        console.log(anchor1)
        console.log(anchor2)
    }

    p2: Qt.point(anchor2.x + (anchor2.width/2), anchor2.y + (anchor2.height/2))
    p1: Qt.point(anchor1.x + (anchor1.width/2), anchor1.y + (anchor1.height/2))
}

This is my Class.qml

import QtQuick 2.11
import QtQuick.Layouts 1.11

Rectangle {
    width: 50
    height: 20
    color: "#2980B9"

    border.color: "#ECF0F1"

    property string className

    Drag.active: dragArea.drag.active

    MouseArea {
        id: dragArea
        anchors.fill: parent

        drag.target: parent
        // disable delay when moved
        drag.threshold: 0
    }
    Text {
        text: className
    }
}

In my main.py I have a classes list with all generated Class components but trying to connect the first class with the second class via Edge (line), for example, doesn't work:

line = line_component.beginCreate(engine.rootContext())
line.setProperty("anchor1", classes[0])
line.setProperty("anchor2", classes[1])
line_component.completeCreate()

However, if I create two Rectangles with id rect1 and rect2 within the main.qml file. using the QQuickItem StraightLine this code is working:

StraightLine {
    anchors.fill: parent

    p1: Qt.point(rect2.x + (rect2.width/2), rect2.y + (rect2.height/2))
    p2: Qt.point(rect1.x + (rect1.width/2), rect1.y + (rect1.height/2))
}

Rectangle {
    id: rect1
    width: 10
    height: 10
    color: "red"
    radius: width*0.5

    Drag.active: dragArea.drag.active

    MouseArea {
        id: dragArea
        anchors.fill: parent

        drag.target: parent
        // disable delay when moved
        drag.threshold: 0
    }
}

Rectangle {
    id: rect2
    width: 10
    height: 10
    color: "blue"
    radius: width*0.5

    Drag.active: dragArea2.drag.active

    MouseArea {
        id: dragArea2
        anchors.fill: parent

        drag.target: parent
        // disable delay when moved
        drag.threshold: 0
    }
}

How can I pass the reference from those class components to my edge components to properly setup a binding for x,y,width,height?

1
whats are Edge.qml and Class.qml?eyllanesc
with "property Class anchor1" you are creating a variable that will store an object of type "Class", so anchor2.Some_property will throw an error, please provide a decent minimal reproducible exampleeyllanesc
I edited my answer. Edge.qml is a wrapper for the StraightLine class. Class.qml is just a qml component. With text. 2 Class components should be connected to one edge.Sens4

1 Answers

1
votes

The solution was to establish the data type of the property anchor1 and anchor2 to var.

Edge.qml

import QtQuick 2.11
import CustomGeometry 1.0

StraightLine {
    property var anchor1
    property var anchor2

    anchors.fill: parent

    Component.onCompleted: {
        console.log(anchor1)
        console.log(anchor2)
    }

    p2: Qt.point(anchor2.x + (anchor2.width/2), anchor2.y + (anchor2.height/2))
    p1: Qt.point(anchor1.x + (anchor1.width/2), anchor1.y + (anchor1.height/2))
}

On the other hand, I have not included the QtQuick.Controls 1.4 import to recognize the ApplicationWindow in the main.qml:

import QtQuick.Controls 1.4

import urmelgraph.components 1.0

import CustomGeometry 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 240
    title: qsTr("Test")
    color: "#2C3E50"
}

In the following link you will find the complete code