Reputation: 271
Question is on the title. Let me give an example to make it clear,
let's say I have 2 products and each have different features; therefore, I need to put different items and elements on the Main qml file. So far I have tried controlling elements using Q_PROPERTY
and visible
feature together to check if the parameter has desired value. However, it causes to a total mess, the anchor connections etc. becomes totally uncontrollable because the design also changes, subtly(not critical changes).
So, in short I am looking for a structure that takes a parameter and loads components accordingly.
I also leave an example of what I have done:
In a class,
Q_PROPERTY(int productType READ getproductType WRITE setproductType NOTIFY productType Changed);
And in a qml, I let user to choose which product they have:
Button {
id: id_typeSelector
text: "Type 1"
anchors.left: parent.left
anchors.top: parent.top
onClicked: {
visible: false
object.setproductType(0)
splash.timeout()
}
}
Lastly, on main.qml i determine which component to be showed:
visible: productType === 0
Upvotes: 0
Views: 231
Reputation: 25906
I think what you need is the dynamic loading of a component. One way to do this is through the use of Loaders. The following is an example of how to constraint pipe size input based on the pipe material chosen.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
anchors.fill: parent
ListModel {
id: pipes
ListElement { pipeTypeName: "Wood"; productType: 0 }
ListElement { pipeTypeName: "Iron"; productType: 1 }
ListElement { pipeTypeName: "Steel"; productType: 2 }
}
Frame {
ColumnLayout {
ComboBox {
id: comboBox
model: pipes
textRole: "pipeTypeName"
property int currentProductType: pipes.get(currentIndex).productType ?? -1
}
Loader {
sourceComponent: comboBox.currentProductType === 0 && woodInputComponent
|| comboBox.currentProductType === 1 && ironInputComponent
|| comboBox.currentProductType === 2 && steelInputComponent
|| null
}
}
}
Component {
id: woodInputComponent
RowLayout {
Layout.fillWidth: true
Text { text: qsTr("Wood Pipe Width: ") }
ComboBox { model: [3, 5, 7] }
}
}
Component {
id: ironInputComponent
RowLayout {
Layout.fillWidth: true
TextField { placeholderText: qsTr("Iron Pipe Width") }
}
}
Component {
id: steelInputComponent
RowLayout {
Layout.fillWidth: true
Text { text: qsTr("Steel Pipe Width") }
RangeSlider { from: 3; to: 8 }
}
}
}
You can Try it Online!
In the above example, I used the following way of choosing dynamically loading a component, i.e.
Loader {
sourceComponent: comboBox.currentProductType === 0 && woodInputComponent
|| comboBox.currentProductType === 1 && ironInputComponent
|| comboBox.currentProductType === 2 && steelInputComponent
|| null
}
If your components where declared in their own QML, e.g. WoodInput.qml
, IronInput.qml
and SteelInput.qml
, we can consider refactoring the code to something shorter, e.g.
Loader {
source: pipeTypeName + "Input.qml"
}
The following is a more extended example that uses StackView
instead of Loader
to demonstrate switching between Pages where PipeType and PipeLength may be set. A different PipeLength editor is used subject to the PipeType chosen:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page {
anchors.fill: parent
property int purchaseIndex: -1
property var purchase: purchaseIndex >= 0 ? purchases.get(purchaseIndex) : null
ListModel {
id: purchases
}
StackView {
id: stackView
anchors.fill: parent
initialItem: gallery
}
Component {
id: gallery
Page {
header: Text {
text: qsTr("Gallery (%1 records)").arg(purchases.count)
}
ListView {
anchors.fill: parent
model: purchases
delegate: Frame {
width: ListView.view.width
RowLayout {
width: parent.width
Text {
Layout.fillWidth: true
text: model.index + " " + pipeType + " " + pipeLength
}
Button {
text: qsTr("Edit")
onClicked: {
purchaseIndex = model.index;
stackView.push(edit);
}
}
Button {
text: qsTr("Delete")
onClicked: purchases.remove(model.index)
}
}
}
}
footer: Button {
text: qsTr("New")
onClicked: newPurchase()
}
}
}
Component {
id: edit
Page {
header: Text {
text: qsTr("Edit #%1").arg(purchaseIndex)
}
ColumnLayout {
Text {
text: qsTr("PipeType: %1").arg(purchase.pipeType)
}
Text {
text: qsTr("PipeLength: %1").arg(purchase.pipeLength)
}
Button {
text: qsTr("Change Pipe Type")
onClicked: stackView.push(changePipeType)
}
Button {
text: qsTr("Change Pipe Length")
onClicked: {
let pipeType = purchases.get(purchaseIndex).pipeType;
stackView.push(
pipeType === "Wood" && changePipeLengthWood
|| pipeType === "Iron" && changePipeLengthIron
|| pipeType === "Steel" && changePipeLengthSteel
);
}
}
}
footer: Button {
text: qsTr("Done"); onClicked: stackView.pop()
}
}
}
Component {
id: changePipeType
Page {
header: Text {
text: qsTr("Change PipeType #%1").arg(purchaseIndex)
}
footer: Button {
text: qsTr("Done"); onClicked: stackView.pop()
}
ComboBox {
model: ["Wood", "Iron", "Steel"]
onCurrentTextChanged: {
purchases.setProperty(purchaseIndex, "pipeType", currentText)
}
}
}
}
Component {
id: changePipeLengthWood
Page {
header: Text {
text: qsTr("Wood Pipe Length #%1").arg(purchaseIndex)
}
Frame {
width: parent.width
ComboBox {
model: [3, 5, 7]
onCurrentTextChanged: {
try {
let pipeLength = parseInt(currentText);
purchases.setProperty(purchaseIndex, "pipeLength", pipeLength);
} catch (err) {
console.error(err.message);
}
}
}
}
footer: Button {
text: qsTr("Done"); onClicked: stackView.pop()
}
}
}
Component {
id: changePipeLengthIron
Page {
header: Text {
text: qsTr("Steel Pipe Length #%1").arg(purchaseIndex)
}
TextField {
width: parent.width
text: purchases.get(purchaseIndex).pipeLength
onTextChanged: {
purchases.setProperty(purchaseIndex, "pipeLength", parseInt(text));
}
}
footer: Button {
text: qsTr("Done"); onClicked: stackView.pop()
}
}
}
Component {
id: changePipeLengthSteel
Page {
header: Text {
text: qsTr("Steel Pipe Length #%1").arg(purchaseIndex)
}
Slider {
from: 3
to: 8
stepSize: 0.1
value: purchases.get(purchaseIndex).pipeLength
onValueChanged: {
purchases.setProperty(purchaseIndex, "pipeLength", value);
}
}
footer: Button {
text: qsTr("Done"); onClicked: stackView.pop()
}
}
}
function newPurchase() {
let purchase = {
pipeType: "Wood",
pipeLength: 3.0
};
purchases.append(purchase);
purchaseIndex = purchases.count - 1;
stackView.push(edit);
}
}
You can Try this version Online!
Upvotes: 2