如何将动态创建的 qmlcomponent 对象绑定到另一个动态创建的 qmlcomponent 对象的属性?

Posted

技术标签:

【中文标题】如何将动态创建的 qmlcomponent 对象绑定到另一个动态创建的 qmlcomponent 对象的属性?【英文标题】:How do I bind a dynamically created qmlcomponent object to the property of another dynamic created qmlcomponent object? 【发布时间】:2018-07-26 18:30:12 【问题描述】:

我在一个模块中有两个 qml 组件。

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

main.qml

import urmelgraph.components 1.0

import CustomGeometry 1.0

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

qmdir

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

我正在我的 python main.py 文件中加载这两个文件。

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_())

但现在我想将边 E 的第一个点连接到 Class 组件 A 和边 E 的第二个点到一个类组件B

为此,我在 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))

这是我的 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
    

在我的 main.py 中,我有一个 classes 列表,其中包含所有生成的 Class 组件,但试图通过 Edge(线)连接第一个类和第二个类,例如,不起作用:

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

但是,如果我在 main.qml 文件中创建两个 id 为 rect1 和 rect2 的矩形。使用 QQuickItem StraightLine 此代码有效:

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
    

如何将这些 class 组件的引用传递给我的 edge 组件,以正确设置 x、y、width、height 的绑定?

【问题讨论】:

Edge.qml 和 Class.qml 是什么? 用“property Class anchor1”你正在创建一个变量来存储一个“Class”类型的对象,所以anchor2.Some_property会抛出一个错误,请提供一个像样的minimal reproducible example 我编辑了我的答案。 Edge.qml 是 StraightLine 类的包装器。 Class.qml 只是一个 qml 组件。带文字。 2 类组件应该连接到一个边缘。 【参考方案1】:

解决方法是将属性anchor1anchor2的数据类型建立为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))

另一方面,我没有包含 QtQuick.Controls 1.4 导入来识别 main.qml 中的 ApplicationWindow:

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"

在下面的link你会找到完整的代码

【讨论】:

以上是关于如何将动态创建的 qmlcomponent 对象绑定到另一个动态创建的 qmlcomponent 对象的属性?的主要内容,如果未能解决你的问题,请参考以下文章

VB.NET ReportViewer 动态绑RDLC

在 TreeTable 绑定上添加动态过滤器

WPF 如何将主题应用于使用 MVVM 动态创建的对象

[C++11]可调用对象绑定器

如何使用绑定变量使整个 PL/SQL 代码块动态化?

如何使用 WPF 按钮的参数或绑定来更改 XAML 样式中的 fa 图标