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)。
简而言之,我的主窗口由组件 Devices 、 MenuBar 、 Tools 和标签区域。在 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)
,使用 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);
}
我正在为信号生成设备开发应用程序的 GUI,该设备将支持多个通道,呈现方式类似于控制台混音器的通道样式。在菜单栏中,用户应该能够配置通道在 3 种信号类型之间的使用。每种类型都将在主 window 的不同选项卡中包含各个通道的总和。 "Configuration.qml" 包含信号处理程序,"TabArea.qml" 包含 javascript 函数 create_PWM(countPWM)。
简而言之,我的主窗口由组件 Devices 、 MenuBar 、 Tools 和标签区域。在 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)
,使用 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);
}