qt qml TypeError: Cannot call method 'create_PWM' of undefined

qt qml TypeError: Cannot call method 'create_PWM' of undefined

我正在为信号生成设备开发应用程序的 GUI,该设备将支持多个通道,呈现方式类似于控制台混音器的通道样式。在菜单栏中,用户应该能够配置通道在 3 种信号类型之间的使用。每种类型都将在主 window 的不同选项卡中包含各个通道的总和。 "Configuration.qml" 包含信号处理程序,"TabArea.qml" 包含 javascript 函数 create_PWM(countPWM)

简而言之,我的主窗口由组件 DevicesMenuBarTools标签区域。在 MenuBar 中,我动态创建了一个名为 Configuration 的对话框组件。其中存在一个 table 从 table 模型 中检索默认信息。用户可以修改模型,当单击“保存”按钮时,我希望考虑到模型数据,在相应的选项卡上描述通道。

\MainWindow.qml
Item {
id: mainWindow
width: 1200
height: 800

Rectangle {
    id: background
    color: "#d7dfda"
    anchors.fill: parent

    MenuBar {
        id: menuBar
        anchors {
            left: parent.left
            top: parent.top
        }
        height: 40
    }
    Tools {
        id: toolBar
        y: menuBar.height
        height: implicitHeight
        anchors {
            left: parent.left
            right: parent.right
            top: menuBar.bottom
            bottom: devices.top
        }
        channelsPWM: tabs.channelsPWM
        channelsFrequency: tabs.channelsFrequency
    }

    Devices {
        id: devices
        anchors {
            //                top: menuBar.down
            top: toolBar.bottom
            left: parent.left
            right: tabs.left
        }
        height: 3 * parent.height / 8
        //            y: menuBar.height
        y: toolBar.height
    }

    TabArea {
        id: tabs
        //            y: menuBar.height
        y: toolBar.height
        x: parent.width / 8
        anchors {
            //                top: menuBar.bottom
            top: toolBar.bottom
        }
        width: 3 * parent.width / 4
        height: 3 * parent.height / 4
    }
}
}


\MenuBar.qml
Item {
id: menuToolBar
Button {
    id: fileButton
    text: "File"
    anchors.left: parent.left
    anchors.top: parent.top
    height: 40
    onClicked: fileMenu.open()
    onHoveredChanged: hovered? fileButton.font.bold = true : fileButton.font.bold = false
    Menu {
        id: fileMenu
        y: parent.height
        MenuItem {
            text: "New Project      Ctrl+N"
            onTriggered: newDialog.open()

            }
        MenuItem {
            text: "Open Project     Ctrl+O"
            onTriggered: openDialog.open()
        }
    }
}

Button {
    id: editButton
    y: 0
    text: "Edit"
    anchors.left: fileButton.right
    anchors.leftMargin: 0
    anchors.top: parent.top
    height: fileButton.height
    onHoveredChanged: hovered? editButton.font.bold = true : editButton.font.bold = false
    onClicked: editMenu.open()
    Menu {
        id: editMenu
        y: parent.height
        MenuItem {
            text: "Undo             Ctrl+Z"
        }
        MenuItem {
            text: "Cut              Ctrl+X"
        }
        MenuItem {
            text: "Copy             Ctrl+C"
        }
        MenuItem {
            text: "Paste            Ctrl+V"
        }
    }
}

Button {
    id: configurationButton
    text: "Configuration"
    anchors.left: editButton.right
    anchors.top: parent.top
    height: editButton.height
    onHoveredChanged: hovered? configurationButton.font.bold = true : configurationButton.font.bold = false
    onClicked: {
        var component = Qt.createComponent("Configuration.qml");
        if( component.status !== Component.Ready )
        {
            if( component.status === Component.Error )
                console.debug("Error:"+ component.errorString() );
            return; // or maybe throw
        }
        var dialog =component.createObject(configurationButton);
        dialog.channelConfigDialog.open();
    }
}

Button {
    id: helpButton
    y: 0
    text: "Help"
    anchors.left: configurationButton.right
    anchors.leftMargin: 0
    anchors.top: parent.top
    height: fileButton.height
    onHoveredChanged: hovered? helpButton.font.bold = true : helpButton.font.bold = false
    onClicked: helpMenu.open()
    Menu {
        id: helpMenu
        y: parent.height
        MenuItem {
            text: "Contents"
        }
        MenuItem {
            text: "Index"
        }
        MenuItem {
            text: "Context Help             F1"
        }

    }
}

/**
  More Buttons for Menu implementation can be added here
  **/

FileDialog {
    id: openDialog
    title: "Please choose a Project."
    /**
      The backend behavior needs to be added here in order to determine
      what the application's actions will be for the selected files etc.
      e.g. onAccepted: {}
            onRejected: {}
      **/

}}
\Configuration.qml
Item{
property alias channelConfigDialog: channelConfigDialog
Dialog {
    id: channelConfigDialog
    modality: Qt.WindowModal
    title: "Channel Configuration."
    height: 500
    width: 500
    standardButtons: Dialog.Save | Dialog.Cancel
    property int totalChannels: 30

    ListModel {
        id: tableModel
        Component.onCompleted: {
            for (var i=0;i<channelConfigDialog.totalChannels;i++){
                append({"name": "Channel"+(i+1), "use": "PWM", "data": "Raw", "conversion": ""});
            }
        }
    }

    Component {
        id: usageComboDel
        Item{
            anchors.fill: parent
            ComboBox {
                id: usageCombo
                model:
                    ListModel{
                        id: usageModel
                        ListElement {
                            text: "PWM"
                        }
                        ListElement {
                            text: "Frequency"
                        }
                        ListElement {
                            text: "BLDC"
                        }
                        }
                currentIndex: 0
                height: 16
                anchors.fill: parent
                onCurrentTextChanged: {
                    tableModel.setProperty(styleData.row,"use",currentText);

                }

    }


    Component{
        id: dataTypeComboDel
        Item{
            id: itemDataTypeComboDel
            anchors.fill: parent
            ComboBox {
                id: dataTypeCombo
                model: ["Raw", "Phys"]
                currentIndex: 0
                height: 16
                anchors.fill: parent
                onCurrentTextChanged: {
                    tableModel.setProperty(styleData.row,"data",currentText);
                    sample()
                }
                }
            function sample(){
                for (var i=0;i<tableConfig.rowCount;i++){
                    var temp = tableModel.get(i).name;
                    console.log(temp);
                    console.log(tableModel.get(i).use + ", " + tableModel.get(i).data);
                    }
            }
                }
    }
    Component{
        id: conversionRuleComboDel
        Item{
            id: itemConversionRuleComboDel
            anchors.fill: parent
            ComboBox {
                id: conversionRuleCombo
                model: ["","Linear", "Ranges", "Quadtratic", "Logarithmic", "Mathematical function"]
                currentIndex: -1
                height: 16
                anchors.fill: parent
                onCurrentTextChanged: {
                    tableModel.setProperty(styleData.row,"conversion",currentText);
                }
                }

        }

    }


    TableView {
        id: tableConfig
        model: tableModel
        anchors.fill: parent


        TableViewColumn{
            role: "name"
            title: "Channels"
            width: tableConfig.width/ tableConfig.columnCount

        }
        TableViewColumn{
            id: usageCol
            property alias delagata: usageComboDel
            title: "Usage"
            delegate: usageComboDel
            width: tableConfig.width/tableConfig.columnCount
        }
        TableViewColumn{
            title: "Data Type"
            delegate: dataTypeComboDel
            width: tableConfig.width/tableConfig.columnCount
        }
        TableViewColumn{
            id: conversionRuleClmn
            title: "Coversion Rule"
            delegate: conversionRuleComboDel
            width: tableConfig.width/tableConfig.columnCount
        }
    }

    onAccepted: {
        var countPWM = 0;
        var countFrequency = 0;
        for (var i=0; i<tableModel.count; i++){
            if ( tableModel.get(i).use === "PWM" ){
                countPWM++;
            }
            else if (tableModel.get(i).use === "Frequency"){
                countFrequency++;
            }
        }
        TabArea.channelsPWM.create_PWM(countPWM);

        }
}
}
\TabArea.qml
Item{
id: tabAreaRoot
property alias channelsPWM: channelsPWM
property alias channelsFrequency: channelsFrequency

TabBar {
    id: tabBar
    TabButton {
        text: qsTr("PWM Output")
        width: implicitWidth
    }
    TabButton {
        text: qsTr("Frequency Output")
        width: implicitWidth
    }
    TabButton {
        text: qsTr("BLDC Emulation")
        width: implicitWidth
    }
}

StackLayout {
    id: tabLayout
    anchors.top: tabBar.bottom
    currentIndex: tabBar.currentIndex
    width: parent.width
    height: parent.height
    Rectangle {
        color: background.color
        border.width: 2
        ScrollView{
            id: scrollPWM
            anchors.fill: parent
            contentItem: channelsPWM.children
            horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOn
            RowLayout{
                id: channelsPWM
                spacing: 0
                Layout.fillHeight: true
                Layout.fillWidth: true


                function create_PWM(countPWM){
                    for (var i=0;i<countPWM;i++){
                        var component = Qt.createComponent("PWM.qml");
                        if( component.status !== Component.Ready )
                        {
                            if( component.status === Component.Error )
                                console.debug("Error:"+ component.errorString() );
                            return; // or maybe throw
                        }
                        var channels =component.createObject(channelsPWM, { "id": "channelPWM"+(i+1), "channelText.text": "Channel"+(i+1)});
                    }
                    }
            }
        }

    }/* Each tab will be a row layout containing column positioners for each channel */
    Rectangle {
        color: background.color
        border.width: 2
        ScrollView{
            id: scrollFrequency
            anchors.fill: parent
            contentItem: channelsFrequency.children
            horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOn
            RowLayout{
                id: channelsFrequency
                spacing: 0
                Layout.fillHeight: true
                Layout.fillWidth: true
                function create_Frequency(countFrequency){
                    for (var i=0;i<countFrequency;i++){
                        var component = Qt.createComponent("Frequency.qml");
                        if( component.status !== Component.Ready )
                        {
                            if( component.status === Component.Error )
                                console.debug("Error:"+ component.errorString() );
                            return; // or maybe throw
                        }
                        var channels =component.createObject(channelsFrequency, { "id": "channelFrequency"+(i+1), "channelText.text": "Channel"+(i+1)});
                    }
                    }
                }
        }

    }
    Rectangle {
        color: background.color
        border.width: 2


    }
}

}

两个 qml 文件都在同一目录中声明。因此,TabArea 组件在 onAccepted 处理程序中可见。此外,id: channelsPWM 被声明为 tabAreaRoot 项的 属性,因此也可在组件外部调用。

问题是,当我尝试在 onActivated 处理程序中调用 create_PWM 时,我得到以下信息:

qrc:/Configuration.qml:181: TypeError: Cannot call method 'create_PWM' of undefined

我知道这是因为信号处理程序中的 channelsPWM 不是 "visible"。但这是为什么呢?如上所述,即使我键入 TabArea.channelsPWM. 等,QtCreator 编辑器也会向我显示可用选项,这意味着该 ID 已被当前范围接受。

我也试图通过将 create_PWM 放在一个单独的 js 文件中来 [=​​75=] 这个问题,当 activated() 信号被省略时,调用它从那里。在这种情况下,我没有得到 TypeError BUT 所需的通道未在所需位置创建。

我还检查了 ID 为 channelsPWM 的对象在处理程序内部调用之前没有被销毁。 (认为​​无论如何检查都会很好)

事情可能会变得有点混乱,因为我希望根据用户的配置动态创建这些频道。因此,如果我明白了这一点,创建它们的函数需要与通道的 RowLayout 父级放在同一个 qml 中。

提前谢谢你。

问题已解决。

似乎我无法直接从动态创建的对象与任何其他 qml 文件交换数据除了它是父主 qml 文件。

所以,我必须在 MenuBar.qml 中创建一个方法,并将其连接到 Configuration.qml[ 中的自定义信号=27=] 如所述 here。这样,我将 countPWM 参数传递给父项目 configurationButton,然后我可以从那里调用 create_PWM(countPWM),使用 tabsMainWindow.qml 中实例化的对象。因此,新创建的 PWM 通道将成为现有对象的子对象并放置在正确的选项卡中。

\MenuBar.qml
(rest of code)
function sendParams(counter){
        tabs.channelsPWM.create_PWM(counter);
    }


\Configuration.qml
(rest of code)
signal saved(int counter)
onSaved: {
        console.log("I just sent the counter");
}
onAccepted: {
        var countPWM = 0;
        var countFrequency = 0;
        for (var i=0; i<tableModel.count; i++){
            if ( tableModel.get(i).use === "PWM" ){
                countPWM++;
            }
            else if (tableModel.get(i).use === "Frequency"){
                countFrequency++;
            }
        }
        saved.connect(parent.sendParams);
        saved(countPWM);
}