【发布时间】:2018-01-26 09:52:37
【问题描述】:
我正在为支持多个通道的信号生成设备开发应用程序的 GUI,其呈现方式类似于控制台混音器的通道样式。在菜单栏中,用户应该能够在 3 种信号类型之间配置通道的使用。每种类型将在主窗口的不同选项卡中包含相应通道的总和。 "Configuration.qml" 包含信号处理程序,"TabArea.qml" 包含 javascript 函数 create_PWM(countPWM)。
简而言之,我的 mainWindow 由组件 Devices 、 MenuBar 、 Tools 和 TabArea 组成。在 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