Qt 5.12 QML,在javascript中通过var选择QML对象

Posted

技术标签:

【中文标题】Qt 5.12 QML,在javascript中通过var选择QML对象【英文标题】:Qt 5.12 QML, select QML object by var in javascript 【发布时间】:2019-02-20 10:17:41 【问题描述】:

我想知道是否有一种方法可以通过 var 在 javascript 函数中选择具有特定 idobjectName 的已创建 QML 对象(var 是对应于 QML 对象 ID 或名称的字符串参数)。 例如:

// main.qml

ApplicationWindow 

    RoundButton 
        id: btn1
    

    RoundButton 
        id: btn2
    

    RoundButton 
        id: btn3
    

    // ...

    function foo(qmlObjectNameOrId) 
        qmlObjectNameOrId.text = "qmlObjectNameOrId is already in the document and has a property text that I want to set";

        // Qt.findQmlObject(id) would have been great !
    

Qt.createQmlObject 不是解决方案,因为我想使用已经创建的 QML 对象。

在 C++ 中,实现这一点的方法是使用QQmlApplicationEngine 对象,然后使用 QML 根对象通过 QML 对象名称执行选择:

int main(int argc, char *argv[])

    QQmlApplicationEngine engine;

    QString objectName = someFunction();

    QObject* qmlObject = engine.rootObjects()[0]->findChild<QObject*>(objectName);

    // use your qmlObject ...

javascript 函数将在 C++ 中调用QMetaObject::invokeMethod(appWindow, "foo", Q_ARG(QVariant, "bar"));

谢谢

[编辑 1] 尝试 Roman Sverdlov 回答:

ApplicationWindow 
    id: appWindow
    objectName: "appWindow"
    // ...

    Pane 
        id: buttonsContainer
        objectName: "buttonsContainer"

        property string  disposition         : "circular_1"
        property int     btnWidth            : 130
        property int     btnHeight           : 130
        property int     btnIconWidth        : 40
        property int     btnIconHeight       : 40
        property int     btnRadius           : btnWidth / 2
        property int     btnMargin           : 40
        property int     btnZ                : 4
        property int     btnPressedBackground: Material.Purple

        anchors.right: parent.right
        anchors.left: parent.left
        anchors.top: instructionContainer.bottom
        anchors.bottom: parent.bottom
        visible: false

        Image 
            id: background
            height: buttonsContainer.height - 100
            x: (buttonsContainer.width - width) / 2
            y: (buttonsContainer.height - height) / 2
            source: "qrc:///images/circle_background.png"
            horizontalAlignment: Image.AlignHCenter
            verticalAlignment: Image.AlignVCenter
            fillMode: Image.PreserveAspectFit
        

        Rectangle 
            id: topLeft
            color: Material.color(Material.Red)
            x: 0
            z: 1
            height: parent.height / 2
            width: parent.width / 2
        

        Rectangle 
            id: topRight
            color: Material.color(Material.Green)
            x: parent.width / 2
            z: 1
            anchors.top: parent.top
            height: parent.height / 2
            width: parent.width / 2
        

        Label 
            id: backgroundTextTop
            text: "MONTER"
            font.pixelSize: 40
            x: (parent.width - width) / 2
            y: (parent.height - height) / 2 - 50
            z: 2
        

        Rectangle 
            id: bottomLeft
            color: Material.color(Material.Green)
            x: 0
            z: 1
            anchors.bottom: parent.bottom
            height: parent.height / 2
            width: parent.width / 2
        

        Rectangle 
            id: bottomRight
            color: Material.color(Material.Red)
            x: parent.width / 2
            z: 1
            anchors.bottom: parent.bottom
            height: parent.height / 2
            width: parent.width / 2
        

        Label 
            id: backgroundTextBottom
            text: "PLONGER"
            font.pixelSize: 40
            x: (parent.width - width) / 2
            y: (parent.height - height) / 2 + 200
            z: 2
        

        RoundButton 
            id: btnB1MB
            objectName: "btnB1MB"
            width: buttonsContainer.btnWidth
            height: buttonsContainer.btnHeight
            radius: buttonsContainer.btnRadius
            z: buttonsContainer.btnZ
            display: AbstractButton.TextUnderIcon
            onPressed: 
                TestsRun.pressButton(Buttons.B1MB);
            
            onReleased: 
                TestsRun.releaseButton(Buttons.B1MB);
            
        

        RoundButton 
            id: btnB4MT
            objectName: "btnB4MT"
            width: buttonsContainer.btnWidth
            height: buttonsContainer.btnHeight
            radius: buttonsContainer.btnRadius
            z: buttonsContainer.btnZ
            display: AbstractButton.TextUnderIcon
            onPressed: 
                TestsRun.pressButton(Buttons.B4MT);
            
            onReleased: 
                TestsRun.releaseButton(Buttons.B4MT);
            
        

        RoundButton 
            id: btnB3MB
            objectName: "btnB3MB"
            width: buttonsContainer.btnWidth
            height: buttonsContainer.btnHeight
            radius: buttonsContainer.btnRadius
            z: buttonsContainer.btnZ
            display: AbstractButton.TextUnderIcon
            onPressed: 
                TestsRun.pressButton(Buttons.B3MB);
            
            onReleased: 
                TestsRun.releaseButton(Buttons.B3MB);
            
        

        RoundButton 
            id: btnB4PB
            objectName: "btnB4PB"
            width: buttonsContainer.btnWidth
            height: buttonsContainer.btnHeight
            radius: buttonsContainer.btnRadius
            z: buttonsContainer.btnZ
            display: AbstractButton.TextUnderIcon
            onPressed: 
                TestsRun.pressButton(Buttons.B4PB);
            
            onReleased: 
                TestsRun.releaseButton(Buttons.B4PB);
            
        

        RoundButton 
            id: btnB2MT
            objectName: "btnB2MT"
            width: buttonsContainer.btnWidth
            height: buttonsContainer.btnHeight
            radius: buttonsContainer.btnRadius
            z: buttonsContainer.btnZ
            display: AbstractButton.TextUnderIcon
            onPressed: 
                TestsRun.pressButton(Buttons.B2MT);
            
            onReleased: 
                TestsRun.releaseButton(Buttons.B2MT);
            
        

        RoundButton 
            id: btnB1PT
            objectName: "btnB1PT"
            width: buttonsContainer.btnWidth
            height: buttonsContainer.btnHeight
            radius: buttonsContainer.btnRadius
            z: buttonsContainer.btnZ
            display: AbstractButton.TextUnderIcon
            onPressed: 
                TestsRun.pressButton(Buttons.B1PT);
            
            onReleased: 
                TestsRun.releaseButton(Buttons.B1PT);
            
        

        RoundButton 
            id: btnB2PB
            objectName: "btnB2PB"
            width: buttonsContainer.btnWidth
            height: buttonsContainer.btnHeight
            radius: buttonsContainer.btnRadius
            z: buttonsContainer.btnZ
            display: AbstractButton.TextUnderIcon
            onPressed: 
                TestsRun.pressButton(Buttons.B2PB);
            
            onReleased: 
                TestsRun.releaseButton(Buttons.B2PB);
            
        

        RoundButton 
            id: btnB3PT
            objectName: "btnB3PT"
            width: buttonsContainer.btnWidth
            height: buttonsContainer.btnHeight
            radius: buttonsContainer.btnRadius
            z: buttonsContainer.btnZ
            display: AbstractButton.TextUnderIcon
            onPressed: 
                TestsRun.pressButton(Buttons.B3PT);
            
            onReleased: 
                TestsRun.releaseButton(Buttons.B3PT);
            
        

        Component.onCompleted: 
            setButtonsPosition(buttonsContainer.disposition);
        
    

    /**
     * Get a QML element by objectName property
     *
     * @todo Not working
     *
     * @param String objectName - The QML object name to get
     */
    function getQmlObjectByName(objectName) 
        console.log("buttonsContainer.children.length", buttonsContainer.children.length);
        return getQmlObjectByNameRecursive(buttonsContainer, objectName)
    

    /**
     * Get a QML element by objectName property
     *
     * @todo Not working
     *
     * @param Object object - The QML object to find the QML element in
     * @param String objectName - The QML object name to get
     */
    function getQmlObjectByNameRecursive(object, objectName) 
        for (let child in object.children) 
            console.log(object.children[child].objectName);

            if (object.children[child].objectName === objectName) 
                console.log('found');
                return object.children[child];
            

            if (typeof object.children[child].children !== 'undefined') 
                console.log('children', object.children[child]);
                return getQmlObjectByNameRecursive(object.children[child], objectName);
            
        
    

输出

qml: buttonsContainer.children.length 2
qml: Pane
qml: children QQuickContentItem(0x55c4e2a145d0, "Pane")
qml: 
qml: children QQuickImage(0x55c4e2a1cc80)
qml: undefined

【问题讨论】:

嗨,我不能在 js 中使用直接的 QML id,因为 id 是使用 C++ 中调用的 js 函数给出的参数构建的。 您好,很抱歉误解了您的问题。我已经清理了你的问题。 (也感谢您添加更多上下文。)此外,我发现了这些相关帖子:Find a QML Object by ID。 Find a QML Object by Object Name. 谢谢,我不认为有解决这个问题的办法,它被认为是 Qt 中的“反模式”。我只是想通过一些元编程使我的 QML 更加紧凑和简约,但 Qt 似乎不允许这样做 【参考方案1】:

如果您为对象设置了objectName 属性,那么您可以使用类似

ApplicationWindow 
id: appWin
RoundButton 
    id: btn1
    objectName: "btn1"


RoundButton 
    id: btn2
    objectName: "btn2"


// ...

function foo(objectName) 
    for(var child in appWin.children) 
        if(appWin.children[child].objectName === objectName) 
            appWin.children[child].text = "qmlObjectNameOrId is already in the document and has a property text that I want to set";
            break
        
    

当然它看起来很丑,但是如果你非常需要它......

【讨论】:

没那么难看,我不知道我可以像 DOM 一样使用 javascript 在 html 中遍历 QML。如果可行,我将对此进行测试并更新问题。 appWin 没有属性children, console.log(appWin.children) === undefined 那么我认为只有Item可以有孩子。所以试着把你的按钮放在某个 item 里面,例如列布局和重复 似乎容器的 children 属性中没有所有子项...我将使用问题中的代码向您发布尝试解决方案【参考方案2】:

通过遍历 QML 容器解决了问题,我想通过 objectName 属性在其中找到 QML 对象。

感谢 Roman Sverdlov,“DOM 遍历”是使用 contentChildren 属性而不是 children 属性完成的。

    /**
     * Get a QML element by objectName property in buttonsContainer container
     *
     * @param String objectName - The QML object name to get
     */
    function getQmlObjectByName(objectName) 
        for (let child in buttonsContainer.contentChildren) 
            if (buttonsContainer.contentChildren[child].objectName === objectName) 
                return buttonsContainer.contentChildren[child];
            
        
    

【讨论】:

哇,这个框架设计太糟糕了……

以上是关于Qt 5.12 QML,在javascript中通过var选择QML对象的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Qt 5.12 中的 QJSEngine 获取标准输出?

QT 5.12安装

涛哥:Qt安卓-5.12环境搭建

QT开发(五十二)———QML语言

QML/QT 如何将对象从 C++ 转换为 QML?

如何完全禁用 Qt Creator 中的 QML 调试器?