【问题标题】:qt qml TypeError: Cannot call method 'create_PWM' of undefinedqt qml TypeError:无法调用未定义的方法“create_PWM”
【发布时间】:2018-01-26 09:52:37
【问题描述】:

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

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

\\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 在信号处理程序中不“可见”。但这是为什么呢?如上所述,即使我键入 TabArea.channelsPWM. 等,QtCreator 编辑器也会向我显示可用选项,这意味着此 id 已被当前范围接受。

我还尝试通过将 create_PWM 放在一个单独的 js 文件中来“绕过”这个问题,并且当激活()信号被省略时,从那里调用它。在这种情况下,我没有得到 TypeError BUT 所需的通道没有在所需的位置创建。

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

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

提前谢谢你。

【问题讨论】:

  • 你要创建一个TabArea,不创建实例就不能使用,例如:TabArea{id: t_area},然后使用t_area.channelsPWM.create_PWM(countPWM)
  • @eyllanesc 我已经尝试过了,是的,频道是在新的父级下创建的 BUT ,例如t_area。因此放置在与期望完全不同的地方。我希望它们被实例化为父 tabAreaRoot 的子代,以便放在主窗口的相应选项卡中。我是否应该考虑在忽略 Accepted() 信号时创建整个选项卡部分作为更好的选择?
  • 我无法帮助您,因为您的代码无法复制,因此我对您的项目没有完整的愿景,因此我无法再提供建议。我之所以这么说,是因为这是最容易看到的错误。如果您希望我们帮助您,您必须提供minimal reproducible example
  • @eyllanesc 感谢您的回答并提示您纠正示例。我提供了所有必要的代码。有些部分可以省略,但它们会再次让您全面了解问题。

标签: javascript qml qtquick2 qtquickcontrols2


【解决方案1】:

问题解决了。

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

因此,我必须在 MenuBar.qml 中创建一个方法,并将其连接到 Configuration.qml 中的自定义信号,如 here 所述。这样,我将countPWM 参数传递给父项configurationButton,然后我可以从那里调用create_PWM(countPWM),使用在中实例化的tabs 对象>MainWindow.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);
}

【讨论】:

    猜你喜欢
    • 2015-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-14
    相关资源
    最近更新 更多