I have a ListModel which elements have information about a data type. I'm using this in a Repeater to show a label and a component according to the datatype, but I don't know how to change that TextField (in the case of "TEXT" datatype) with a; e.g. a button to call a file dialog in the case of "IMAGE"; or a TextField with a mask for double in the case I get datatype "REAL". How can I make this?
Here is my code:
Repeater {
id: r2
model: ListModel {
ListElement {
nombreCampo: "Name"
datoValor: "John Doe"
tipoDato: "TEXT"
}
ListElement {
nombreCampo: "Birth Date"
datoValor: "19910101"
tipoDato: "DATE"
}
ListElement {
nombreCampo: "Photo"
datoValor: "whatever.jpg"
tipoDato: "IMAGE"
}
ListElement {
nombreCampo: "Height"
datoValor: "1.55"
tipoDato: "REAL"
}
}
Text {
text: nombreCampo
}
// this would go well with "TEXT" but not with "DATE" (where I'd prefer a datepicker)
// or with "IMAGE" (where I'd prefer a button to call a file dialog).
// so this is the part where I need to generate it according to the case
TextField {
text: datoValor
placeholderText: nombreCampo
onTextChanged: {
r2.model.get( index ).datoValor = this.text;
}
}
}
Thanks in advance.
In Qt 5.12 you can use DelegateChooser + DelegateChoice:
import QtQuick 2.7
import QtQuick.Controls 2.3
import Qt.labs.qmlmodels 1.0
ApplicationWindow {
width: 500
height: 500
visible: true
Column {
anchors.fill: parent
Repeater {
id: r2
model: ListModel {
ListElement {
nombreCampo: "Name"
datoValor: "John Doe"
tipoDato: "TEXT"
}
ListElement {
nombreCampo: "Birth Date"
datoValor: "19910101"
tipoDato: "DATE"
}
ListElement {
nombreCampo: "Photo"
datoValor: "whatever.jpg"
tipoDato: "IMAGE"
}
ListElement {
nombreCampo: "Height"
datoValor: "1.55"
tipoDato: "REAL"
}
}
delegate: DelegateChooser {
role: "tipoDato"
DelegateChoice {
roleValue: "DATE"
// delegate: SomeDatePicker {
// date: datoValor
// }
}
DelegateChoice {
roleValue: "IMAGE"
delegate: Button {
text: "Select image"
}
}
DelegateChoice {
roleValue: "TEXT"
delegate: TextField {
text: model.datoValor
}
}
DelegateChoice {
roleValue: "REAL"
delegate: TextField {
text: model.datoValor
// inputMask: ...
}
}
}
}
}
}
In earlier versions, you can use Loader, and choose the source/sourceComponent based on the model data.
I would use a loader inside your delegate.
Column{
Repeater{
model: // your model
delegate: Loader{
anchors{
right: parent.right
left: parent.left
}
sourceComponent: {
if(tipoDato=== "TEXT") {return textFieldComponent;}
else if(tipoDato=== "DATE") {return datePickerComponent;}
else if(tipoDato=== "IMAGE"){return imagePickerPickerComponent;}
else if(tipoDato=== "REAL") {return floatPickerComponent;}
}
}
}
}
// Component definition
Component{
id: textFieldComponent
// Define your component here
}
Component{
id: datePickerComponent
// Define your component here
}
Component{
id: imagePickerPickerComponent
// Define your component here
}
Component{
id: floatPickerComponent
// Define your component here
}
If you use this example take into account that each component has to have their height defined and the top parent column has to have their width defined in order to render all the items correctly.
Related
I am new to Qt. Wondering if there a possibility to make an item "unselectable" in ListView.
I see there are a lot of other things, e.g: collapsing , expanding, etc.
**
I have not find any simple example for this problem. **
Can you provide some minimalistic examples to make a specific item in the list unselectable?
I have the following minimalistic example. How can I set list item index 2 to be unselectable?
Window {
id: mainWindow
width: 130
height: 240
visible: truetitle: qsTr("Hello")
Rectangle {
id: bg
color: "#ffffff"
anchors.fill: parent
ListModel {
id: nameModel
ListElement { name: "Alice" }
ListElement { name: "Bob" }
ListElement { name: "Jay" }
ListElement { name: "Kate" }
}
Component {
id: nameDelegate
Text {
text: model.name
font.pixelSize: 24
}
}
ListView {
anchors.fill: parent
model: nameModel
delegate: nameDelegate
clip: true
highlight: Rectangle {
anchors { left: parent.left; right: parent.right }
//height: parent.height
color: "lightgrey"
}
}
}
}
I found numerous issues with your code snippet, so I attempted to address them all:
I made use of Page and set Page.background instead of declaring an outer Rectangle. This removes a level of indentation
I refactored NameComponent.qml to reduce the complexity of your main program
I change the delegate from Text to ItemDelegate so that it is clickable, and, it being clickable, I can (1) make the ListView have active focus so that it can receive keyboard events, (2) change the current item in the ListView => I think this achieves your criteria of being able to select a different item
I removed unnecessary anchoring from your highlight - your highlight will default anchor to your selected item
I set the width of your delegate to listView.width - I also made use of the ListView.view attached property so that your delegate and access properties from the ListView
Finally, I added a 20 pixel width vertical ScrollBar
import QtQuick
import QtQuick.Controls
Page {
background: Rectangle { color: "#ffffff" }
ListModel {
id: nameModel
ListElement { name: "Alice" }
ListElement { name: "Bob" }
ListElement { name: "Jay" }
ListElement { name: "Kate" }
}
ListView {
anchors.fill: parent
model: nameModel
delegate: NameDelegate { }
clip: true
highlight: Rectangle {
color: "lightgrey"
}
ScrollBar.vertical: ScrollBar {
width: 20
policy: ScrollBar.AlwaysOn
}
}
}
// NameDelegate.qml
import QtQuick
import QtQuick.Controls
ItemDelegate {
property ListView listView: ListView.view
width: listView.width - 20
text: model.name
font.pixelSize: 24
onClicked: {
listView.forceActiveFocus();
if (listView.currentIndex === index) {
listView.currentIndex = -1;
} else {
listView.currentIndex = index;
}
}
}
You can Try it Online!
I have ListView with some text input fields inside it:
Window {
visible: true
ListModel {
id: textModel
ListElement {
text: "Bill Smith"
}
ListElement {
text: "John Brown"
}
ListElement {
text: "Sam Wise"
}
}
ListView {
width: 180; height: 200
focus: true
model: textModel
delegate: RowLayout{
id: layout
Label {
text: model.text
}
TextField {
text: model.text
}
}
}
}
And I want to set input focus to the first TextField in the list. How can I do this? If I add focus: true in ListView it does not help.
You have to activate the focus of the TextField using the ListView.isCurrentItem property:
ListView {
id: view
width: 180; height: 200
focus: true
model: textModel
delegate: RowLayout{
id: layout
Label {
text: model.text
}
TextField {
focus: layout.ListView.isCurrentItem
text: model.text
}
}
// Component.onCompleted: view.currentIndex = 0
}
import QtQuick 2.7
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "dog" }
}
TableView {
id: tableView
anchors.fill: parent
model: tstModel
headerDelegate: Item{
height: 50
width: animalColumn.width
TextField{
text: styleData.value
anchors.fill: parent
}
}
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 50
resizable: false
movable: false
}
}
}
This code is not acting as I expect. Textfield gets focus only when I am clicking the header with right button. And there is a Textfield works fine next to the first column header, but it's not what I want.
How to add a editable TableView header in qml?
I find this headerDelegate to be rather buggy or not supposed to be used in this scenario. I would rather implemnt a header from scrath to use for your use-case. Try this as a starting point:
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
}
TextField{
id: tableViewCustomHeader
property int curRow: tableView.currentRow
property bool isActual: curRow >= 0
function reloadText() { text = tstModel.get(curRow).animal }
function saveText() { tstModel.setProperty(curRow, "animal", text); tableView.model = tstModel; }
width: animalColumn.width
height: isActual ? 20 : 0
onCurRowChanged: {
if ( isActual >= 0 ) reloadText();
focus = true;
}
onTextChanged: if ( isActual >= 0 ) saveText()
}
TableView {
id: tableView
anchors {
top: tableViewCustomHeader.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
model: tstModel
headerVisible: false
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 200
resizable: false
movable: false
}
}
}
You can edit this anyway you want afterwards.
If it's a strict requirement to go with headerDelegate - this what I've end up with:
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "dog" }
ListElement { animal: "cat" }
}
TableView {
id: tableView
anchors.fill: parent
model: tstModel
headerDelegate:
TextField{
property int curRow: tableView.currentRow
function reloadText() { text = tstModel.get(curRow).animal }
function saveText() { tstModel.setProperty(curRow, "animal", text); tableView.model = tstModel; }
onCurRowChanged: {
if ( curRow >= 0 ) reloadText()
}
onTextChanged: saveText()
width: parent.width
}
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 200
resizable: false
movable: false
}
}
}
But, again, it works in a very strange way once you are adding TextField to it, so I would vote against it.
I would like to know if it's possible to use (several) different delegates for a QML ListView.
Depending on the individual object in the ListView model, I would like to visualize the objects with different delegates.
This piece of code explains what I want to achieve:
main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
ListModel {
id: contactsModel
ListElement {
name: "Bill Smith"
position: "Engineer"
}
ListElement {
name: "John Brown"
position: "Engineer"
}
ListElement {
name: "Sam Wise"
position: "Manager"
}
}
ListView {
id: contactsView
anchors.left: parent.left
anchors.top: parent.top
width: parent.width
height: parent.height
orientation: Qt.Vertical
spacing: 10
model: contactsModel
delegate: {
if (position == "Engineer") return Employee; //<--- depending on condition, load Contact{}
else if (position == "Manager") return Manager; //<--- depending on condition, load Person{}
}
}
}
Employee.qml (One possible Component which I would like to use as a delegate)
import QtQuick 2.4
Rectangle{
width: 200
height: 50
color: ListView.isCurrentItem ? "#003366" : "#585858"
border.color: "gray"
border.width: 1
Text{
anchors.centerIn: parent
color: "white"
text: name
}
}
Manager.qml (other Component I would like to use as a delegate)
import QtQuick 2.4
Rectangle{
width: 200
height: 50
color: "red"
border.color: "blue"
border.width: 1
Text{
anchors.centerIn: parent
color: "white"
text: name
}
}
I would appreciate any advice!
Thanks!
I've had the same problem, the Qt documentation is providing a pretty good answer: http://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate
The easiest solution is an inline Component with a Loader to set a source file:
ListView {
id: contactsView
anchors.left: parent.left
anchors.top: parent.top
width: parent.width
height: parent.height
orientation: Qt.Vertical
spacing: 10
model: contactsModel
delegate: Component {
Loader {
source: switch(position) {
case "Engineer": return "Employee.qml"
case "Manager": return "Manager.qml"
}
}
}
}
Any attempt to use Loader.srcComponent will result in missing any variable from the model (including index). The only way for the variables to be present is the children Component to be inside the main Component, but then only one can be present, so it is useless.
I believe it would be better to implement one base delegate for all kind of position which loads concrete implementation depending on position or any other data properties using Loader
BaseDelegate {
property var position
Loader {
sourceComponent: {
switch(position) {
case "Engineer": return engineerDelegate
}
}
}
Component {
id: engineerDelegate
Rectangle {
Text { }
}
}
}
I implemented it as follow:
ListView {
id: iranCitiesList
model: sampleModel
delegate: Loader {
height: childrenRect.height
width: parent.width
sourceComponent: {
switch(itemType) {
case "image" :
return imageDel;
case "video":
return videoDel;
}
}
}
ImageDelegate { id: imageDel }
VideoDelegate { id: videoDel }
}
ImageDelegate.qml
Component {
Image { /*...*/ }
}
VideoDelegate.qml
Component {
Item { /*....*/ }
}
Last note, check width and height of delegates. In my case, I had to set width and height of my delegate in Loader again.
Good luck - Mousavi
The simplest way to do this now is using DelegateChooser. This also allows you to edit the properties of the delegates, which is something that is more difficult to do with Loader!
Example inspired from the docs:
import QtQuick 2.14
import QtQuick.Controls 2.14
import Qt.labs.qmlmodels 1.0
ListView {
width: 640; height: 480
ListModel {
id: contactsModel
ListElement {
name: "Bill Smith"
position: "Engineer"
}
ListElement {
name: "John Brown"
position: "Engineer"
}
ListElement {
name: "Sam Wise"
position: "Manager"
}
}
DelegateChooser {
id: chooser
role: "position"
DelegateChoice { roleValue: "Manager"; Manager { ... } }
DelegateChoice { roleValue: "Employee"; Employee { ... } }
}
model: contractsModel
delegate: chooser
}
Sure, it's possible. ListView.delegate is a kind of pointer to a Component which will draw the items so you can change it.
For example:
Employee { id: delegateEmployee }
Manager { id: delegateManager}
...
ListView {
property string position
delegate: position == "Engineer" ? delegateEmployee : delegateManager
}
As far as you have only two types, the following code is as easy to maintain as easy to understand:
delegate: Item {
Employee { visible = position === "Engineer" }
Manager { visible = position === "Manager" }
}
In case the number of types will grow, it is not a suitable solution for it easily leads to an hell of if statement.
Because position is either "Manager" or "Engineer" and the delegates are saved in Manager.qml or Engineer.qml we can use a clever expression for Loader.source:
Loader {
source: position + ".qml"
}
Here's the full source:
import QtQuick
import QtQuick.Controls
Page {
ListModel {
id: contactsModel
ListElement { name: "Bill Smith"; position: "Engineer" }
ListElement { name: "John Brown"; position: "Engineer" }
ListElement { name: "Sam Wise"; position: "Manager" }
}
ListView {
id: listView
anchors.fill: parent
model: contactsModel
delegate: Loader {
width: ListView.view.width
source: position + ".qml"
}
}
}
//Engineer.qml
import QtQuick
import QtQuick.Controls
Rectangle {
property bool isCurrentItem: listView.currentIndex === index
height: 50
color: isCurrentItem ? "#0033cc" : "#585858"
border.color: "gray"
border.width: 1
Text {
anchors.centerIn: parent
color: "white"
text: name
}
}
//Manager.qml
import QtQuick
import QtQuick.Controls
Rectangle {
property bool isCurrentItem: listView.currentIndex === index
height: 50
color: isCurrentItem ? "#cc3300" : "#661100"
border.color: "blue"
border.width: 1
Text {
anchors.centerIn: parent
color: "white"
text: name
}
}
You can Try it Online!
can someone say me how to set up a icon mode for my listViewe in qml?
like this:
I know that this mode is available in the c++ version of the listview (setListMode)but in qml?
Greetings
I don't think there is a default component for this in QML but you can easily do one yourself using the Gridview Component and creating the delegate yourself.
ListModel {
id: modelIcons
ListElement { iconSource: "icon1.jpg"; iconText: "Train1" }
ListElement { iconSource: "icon2.jpg"; iconText: "Train2" }
ListElement { iconSource: "icon3.jpg"; iconText: "Train3" }
ListElement { iconSource: "icon4.jpg"; iconText: "Train4" }
ListElement { iconSource: "icon5.jpg"; iconText: "Train5" }
ListElement { iconSource: "icon6.jpg"; iconText: "Train6" }
ListElement { iconSource: "icon7.jpg"; iconText: "Train7" }
}
Component {
id: delegateListElement
Item {
width: 80
height: width
Column {
Image {
height: 50
width: 50
source: iconSource
}
Text {
text: iconText
}
}
}
}
GridView {
anchors.fill: parent
model: modelIcons
delegate: delegateListElement
focus: true
}