I have a vertical Listview and when i click an item i will display horizontal Listview. The question is that when i click 2 nd item i should display 2nd item on horizontal Listview. Here it is my implementation:
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
StackView {
id: rootStackView
anchors.fill: parent
initialItem: verListView
}
VListview {
id: verListView
onListItemClicked: {
rootStackView.push(horListView)
horListView.init(index)
}
}
HListView {
id: horListView
visible: false
}
}
VListview.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
Page {
id: root
signal listItemClicked(var index)
ListView {
id: listView
anchors.fill: parent
Layout.fillWidth: true
model: ListModel {
ListElement {
name: "Bill Smith"
number: "555 3264"
}
ListElement {
name: "John Brown"
number: "555 8426"
}
ListElement {
name: "Sam Wise"
number: "555 0473"
}
}
delegate: Component {
id: contactDelegate
Item {
MouseArea {
anchors.fill: parent
onClicked: {
console.log("aaa")
root.listItemClicked(index)
}
}
width: listView.width; height: 40
Column {
Text { text: '<b>Name:</b> ' + name }
Text { text: '<b>Number:</b> ' + number }
}
}
}
}
}
HListview.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
Page {
function init(index) {
console.log(index)
horizontalListView.currentIndex = index;
horizontalListView.positionViewAtIndex(index, ListView.Beginning)
}
ListView {
id: horizontalListView
anchors.fill: parent
orientation: ListView.Horizontal
model: ListModel {
ListElement {
name: "Bill Smith"
number: "555 3264"
}
ListElement {
name: "John Brown"
number: "555 8426"
}
ListElement {
name: "Sam Wise"
number: "555 0473"
}
}
snapMode: ListView.SnapToItem
delegate: Component {
id: contactDelegate
Item {
width: horizontalListView.width; height: 40
Column {
Text { text: '<b>Name:</b> ' + name }
Text { text: '<b>Number:</b> ' + number }
}
}
}
}
}
HorizontalListView
I am clicking 2nd and 3 rd items but it display always first item. Anybody faced the issue? Thanks in advance
The width of horizontalListView is wrongly set.
HListview.qml
ListView {
id: horizontalListView
// anchors.fill: parent
height: 40 <== fix the height
width: root.width <== width of the hview must be same size as that of root window.
orientation: ListView.Horizontal
...
}
EDIT
Explanation: If positioning the view at index would cause empty space to be displayed at the beginning or end of the view, the view will be positioned at the boundary.
Related
I'm trying to make a list of buttons, where the user can select one of the options. I can't seem to display the text from the listmodel, but I can't figure out why. It seems like even the button is not correctly placed. Does anyone have ideas as to how to fix this? Thanks!
import QtQuick 2.15
import QtQuick.Window 2.2
import QtQuick.Controls 2.15
Window {
id: window
visible: true
height: 400
width: 400
Rectangle {
id: rect
width: 400; height: 400
ListView {
id: listview
anchors.fill: parent
model: timeList
delegate: Item {
anchors.top: parent.top
height: 30
Button {
anchors.top: parent.top
anchors.left: parent.left
id: textButton
text: text
width: parent.width
height: 30
flat: true
onClicked: {
console.log(value)
}
}
Rectangle {
height: (index === timeList.count - 1 ? 0 : 1)
color: '#E1E6E9'
width: 400
anchors {
left: textButton.left
top: textButton.bottom
}
}
}
ListModel {
id: timeList
ListElement { text: "15 minutes"; value: 15 }
ListElement { text: "1 hour"; value: 60 }
ListElement { text: "3 hours"; value: 180 }
ListElement { text: "6 hours"; value: 360 }
ListElement { text: "12 hours"; value: 720 }
}
}
}
}
There are several issues here:
don't name ListElement properties using keywords, text: text looks confusing.
don't wrap everything with Rectangle/Item with no need, use Layout instead if needed
never use anchors for delegates
pay attention that all containers have a size, the delegate's Item has no with for example (or probably 0)
actually, that should work:
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
id: window
visible: true
height: 400
width: 400
ListView {
anchors.fill: parent
model: timeList
delegate: Button {
id: textButton
text: name
width: parent.width
height: 30
flat: true
onClicked: {
console.log(value)
}
}
ListModel {
id: timeList
ListElement { name: "15 minutes"; value: 15 }
ListElement { name: "1 hour"; value: 60 }
ListElement { name: "3 hours"; value: 180 }
ListElement { name: "6 hours"; value: 360 }
ListElement { name: "12 hours"; value: 720 }
}
}
}
I am trying to create a component in QML as shown in the attached screenshot
AFAIK, TableView is the component that I should use to create something like this using QML. Looking at the example here it seems that it can support multiple columns and the style is configurable. However, I am not sure how to add the checkbox control and an image element in the columns.
You can start from here:
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Window {
visible: true
width:1000; height: 500
ListModel {
id: mymodel
ListElement {
title: "my_name.mp4"
check: true
img: "1450465860217s.jpg" //your own img url here
filesize: "1.5GB"
lenght: "20:00"
lastMod: "12/02/2014"
}
ListElement {
title: "my_nam2.mp4"
check: false
img: "1450465860217s.jpg" //your own img url here
filesize: "400MB"
lenght: "8:00"
lastMod: "01/01/2015"
}
ListElement {
title: "my_nam2.mp4"
check: false
img: "1450465860217s.jpg" //your own img url here
filesize: "1.5GB"
lenght: "1:20:00"
lastMod: "12/13/2016"
}
}
TableView {
width: 1000; height: 500
anchors.centerIn: parent
TableViewColumn {
role: "title"
title: "Title"
width: 200
}
TableViewColumn {
role: "filesize"
title: "FileSize"
}
TableViewColumn {
role: "lenght"
title: "Lenght"
}
TableViewColumn {
role: "lastMod"
title: "Last Modified"
}
model: mymodel
rowDelegate: Rectangle{
color: "white"
height: 40
}
itemDelegate: RowLayout {
width: parent == null? 0 : parent.width
Loader{
sourceComponent: styleData.column == 0 ?
things : null
}
Component {
id: things
RowLayout{
height: 30
CheckBox{
id: itemCheckBox
checked: mymodel.get(styleData.row).check
}
Image{
Layout.preferredWidth: 80
Layout.preferredHeight: 40
source: mymodel.get(styleData.row).img
}
}
}
Text {
//anchors.centerIn: parent
text: styleData.value
}
}
}
}
You'll need to code your model in c++ and polish the interface, but its a good starting point.
I have a Listmodel where the delegates get selected/highlighted when they are clicked on. However when I click on a Combobox, which is part of the delegate, the delegate does not get selected.
Is there something like propagateComposedEvents which could propagate the click to the MouseArea of the delegate?
What would be the best way to also select the delegate when I click on it's containing Combobox?
Here is a screenshot
Here is the example code
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"
}
ListElement {
name: "John Brown"
}
ListElement {
name: "Sam Wise"
}
}
ListModel {
id: roleModel
ListElement {
text: "Employee"
}
ListElement {
text: "Manager"
}
ListElement {
text: "Big Boss"
}
}
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: contactsDelegate
}
Component{
id: contactsDelegate
Rectangle{
width: 200
height: 50
color: ListView.isCurrentItem ? "#003366" : "#585858"
border.color: "gray"
border.width: 1
MouseArea{
anchors.fill: parent
onClicked: {
contactsView.currentIndex = index;
}
}
Column{
Text {
color: "white"
text: name
}
ComboBox{
currentIndex: 0
model: roleModel
}
}
}
}
}
ComboBox{
currentIndex: 0
model: roleModel
onPressedChanged: if (pressed) contactsView.currentIndex = index
}
It is not exactly propagating, but it does the trick.
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!
Does anybody knows, how to prevent scrolling down QML ListView when I prepend some elements to its head? I want to update ListView in a Twitter-like manner, when it always keeps its position and only explicit flick is possible.
Actually the insert function did the job. You can insert it at the top or any desired position like this modelName.insert(0,{object});. A working example is here.
import QtQuick 1.0
Rectangle {
id: rectangle1
width: 320
height: 480
ListModel {
id: cModel
ListElement {
name: "Bill Smith"
number: "555 3264"
}
ListElement {
name: "John Brown"
number: "555 8426"
}
ListElement {
name: "Sam Wise"
number: "555 0473"
}
}
ListView {
id: list_view1
width: rectangle1.width
height: rectangle1.height - 40
anchors.horizontalCenter: parent.horizontalCenter
delegate: Text {
text: name + ": " + number
}
model: cModel
}
Rectangle {
id: rectangle2
width: 320
height: 40
color: "#ffffff"
anchors.top: list_view1.bottom
Text {
id: text1
text: qsTr("Click to add!")
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 16
MouseArea {
id: mouse_area1
anchors.fill: parent
onClicked: addNewItemTop();
}
}
}
function addNewItemTop()
{ var i = Math.random();
cModel.insert(0,{"name" : "New Number", "number":i.toString()});
}
}