How to catch the model update signal in ListView - qt

Is there any way to catch the model update signal in qml.
here is my sample program. i have a rectangle on top of that there is listview.
on mouse i am updating the listmodel.
code:
Rectangle{
id: root
anchors.fill: parent
ListModel {
id: fruitModel
ListElement {
name: "Apple"
cost: 2.45
}
ListElement {
name: "Orange"
cost: 3.25
}
ListElement {
name: "Banana"
cost: 1.95
}
}
Component {
id: fruitDelegate
Row {
spacing: 10
Text { text: name }
Text { text: '$' + cost }
}
}
ListView {
id: list
anchors.fill: parent
model: fruitModel
delegate: fruitDelegate
onModelChanged: {
console.log("hi heloooo")
}
}
MouseArea{
anchors.fill: parent
onClicked: {
fruitModel.append({"cost": 5.95, "name":"Pizza"})//added new
fruitModel.remove(1) // deleted old. so count still same
}
}
}
On mouse click i am updating the model, i just want catch when ever there is a change in model.

What does it mean that you change the model? If you are interested in items added or removed, you can bind a listener to the onCountChanged signal of the ListView.

Related

How to create different component base on ListModel in QML

There are 9 parameters that I need to use TextField1 to input value.
So I use
ListModel lstPara {
ListElement{
text:"A";value:"123"
}...(9 Elements)
}
Grid{
id: grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
delegate: TextField1 {
}
}
}
But now there is a parameter that i need to use in another QML type to set the value, all others are used in TextField1.
I tried to define ListModel like this
ListModel lstPara{
ListElement {
text: "A";
type: 1";
value: "123"
}
ListElement {
text: "B";
type: 2";
value: "321"
}
...(9 Elements)
}
Grid{
id: grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
(some code : like this)
delegate: {
return type === 1 ?
TextField1 {
}
:
another QML type {
}
}
}
}
The code above can not run.
And I don`t want to write 8 TextField1 and 1 another QML type.
So, is there a way to use ListModel?
You can't directly use an Item declaration in a conditional expression like that, but you can do it with a Component. Here's an example of how to do it using a Loader as your delegate, and choosing which Component to load based on the model:
ListModel {
id: lstPara
ListElement {
text: "A"
type: 1
value: "123"
}
ListElement {
text: "B"
type: 2
value: "321"
}
}
Grid {
id:grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
delegate: Loader {
sourceComponent: type === 1 ? someText : otherText
onLoaded: {
item.text = text
item.value = value
}
}
}
Component {
id: someText
Text {
property string value
color: "blue"
}
}
Component {
id: otherText
Text {
property string value
color: "red"
}
}
}

Receive signal in newly added listview entry

I am trying to execute a function in a newly added entry to my ListView.
After adding an entry via button I want to execute the just added delegate's onNewEntry function. But only the old delegates execute it.
Minimal example:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.0
Window {
id:root
width: 640
height: 480
visible: true
signal newEntry(int new_row)
property var someProperty
ListModel {
id:listmodel
ListElement {
name: "Bill Smith"
}
ListElement {
name: "John Brown"
}
ListElement {
name: "Sam Wise"
}
}
ListView{
id: listView
width:100
height:200
model: listmodel
delegate: ItemDelegate{
id:delegateId
Text{
text:name
}
Connections { //ISSUE!!!: the new delegate doesnt execute this, just the old ones
target: root
function onNewEntry(new_row){
console.debug(index)
console.debug(new_row)
if(new_row==index){ //doesnt get true
listView.currentIndex = index
setProductData()
}
function setProductData(){
root.someProperty=name
}
}
}
}
}
Button {
anchors.top: listView.bottom
id: btnAdd
text:"+"
onClicked:{
listmodel.append({"name":"Joe Black"})
newEntry(listView.count-1) //emit signal newEntry
}
}
}
output:
qml: 0
qml: 3
qml: 1
qml: 3
qml: 2
qml: 3
My already mentioned workaround is using ListView.onAdd in the delegate:
delegate: ItemDelegate{
ListView.onAdd: {
setProductData()
}
My question is: Why does the newly added entry doesnt listen to the newEntry signal. Thank you
It would be easier to just set the currentIndex after inserting the new row:
Button {
id: btnAdd
onClicked: {
sqlTableModel.insertNewEmptyRow()
listView.currentIndex = listView.count - 1
}
}
I found a better, probably more efficient solution:
delegate: ItemDelegate{
ListView.onAdd: {
listView.currentIndex=index
setProductData()
}
only the newly added delegate receives onAdd not all, like in the approach before.

qml delegate cannot create binding with ListModel when ListModel isEmpty and append an empty jsobject at first time

I have a repeater like this:
Repeater {
id: idRepeater
model: fruitModel
delegate: Rectangle {
id:idRect
width:20
height:20
color:sColor
}
}
My model has nothing, like this:
ListModel {
id: fruitModel
}
All I want is that I can add element by ListModel's append(jsobject dict) API.
I write code below:
fruitModel.append({});
fruitModel.append({"sColor":"yellow"});
But the second rectangle is not yellow. why?
You should refer to model data like:
ListModel {
id: fruitModel
dynamicRoles: true
}
Repeater {
id: idRepeater
model: fruitModel
delegate: Rectangle {
id:idRect
width:20
height:20
color: model.sColor
}
}
You should also set dynamicRoles to true for your list model when members of each object are different.

How to fetch the ListElement associated with the row which is clicked in a TableView in QML?

I have a TableView defined in my QML which will obviously have multiple rows populated by a ListModel.
I want to fetch the ListElement associated with the row which is double clicked.
I have my rowDelegate of the table view defined as such:
rowDelegate: Rectangle {
color: "#D3D3D3"
height: 30
MouseArea {
anchors.fill: parent
onDoubleClicked: {
console.log("table view row clicked...")
// How to fetch the ListElement associated with the row
// and return it for use by another module?
}
}
}
My comment pretty much emphasises what I'm looking for.
You probably don't even need a MouseArea to handle the click in your delegate.
The TableView already has a doubleClicked signal that you can use to retrieve the model data from the clicked row index:
TableView {
model: ListModel {
ListElement {
name: "name 1"
}
ListElement {
name: "name 2"
}
}
TableViewColumn {
title: "name"
delegate: Text {
text: model.name
}
}
rowDelegate: Rectangle {
color: "#D3D3D3"
height: 30
// no MouseArea
}
// handle the click directly in TableView
onDoubleClicked: {
const element = model.get(row)
console.error("doubleClicked on", element.name)
}
}
In scope of your delegate you can use model pseudo-property to fetch associated ListElement (or any other piece of data which is displayed via that delegate). You may think of it as a reference to original data item. It has all properties of ListElement (for example text or color or whatever) and also index property (index of item in your ListModel or any other model).

How do I correctly handle mouse events in a QML TableView with overlapping mouse areas?

I've got a delegate attached to my TableViewColumn that contains a MouseArea. I use the MouseArea to detect double clicks on individual cells in the table, which allows me to show a TextField for editing purposes.
The problem is the delegate MouseArea blocks mouse events from propagating through to TableView. This means that the selection behaviour of TableView no longer works. Specifically, I have SelectionMode.ExtendedSelection enabled.
The MouseArea child item is simple and originally looked like this:
MouseArea{
id: mousearea
anchors.fill: parent
onDoubleClicked: {
showTextField()
}
}
After consulting the documentation, it looked like this should work:
MouseArea{
id: mousearea
anchors.fill: parent
propagateComposedEvents: true // new
onDoubleClicked: {
showTextField()
}
onPressed: mouse.accepted = false // new
}
Which it does, except now I cannot pick up double click events anymore (in MouseArea)! Which makes sense, as it states later in the documentation:
pressed(MouseEvent mouse)
When handling this signal, use the accepted property of the mouse parameter to control whether this MouseArea handles the press and all future mouse events until release. The default is to accept the event and not allow other MouseAreas beneath this one to handle the event. If accepted is set to false, no further events will be sent to this MouseArea until the button is next pressed.
There does not seem to be a way to capture mouse events for individual cells at the TableView level. It's my first day playing around with QML, so I might have missed something obvious here, but what are my options? Note I'm using PyQt.
If it is only the the selection you want to achive you can set the selection manually:
TableView {
id: tv
itemDelegate: Item {
Text {
anchors.centerIn: parent
color: styleData.textColor
elide: styleData.elideMode
text: styleData.value
}
MouseArea {
id: ma
anchors.fill: parent
onPressed: {
tv.currentRow = styleData.row
tv.selection.select(styleData.row) // <-- select here.
}
onClicked: {
console.log(styleData.value)
}
}
}
TableViewColumn {
role: 'c1'
title: 'hey'
width: 100
}
TableViewColumn {
role: 'c2'
title: 'tschau'
width: 100
}
model: lm
}
Right now I only select. But you can write your very own selection/deselection-logic.
You might also map from the TableView.__mouseArea to the delegate.
import QtQuick 2.7
import QtQuick.Controls 1.4
ApplicationWindow {
id: appWindow
width: 1024
height: 800
visible: true
ListModel {
id: lm
ListElement { c1: 'hallo1'; c2: 'bye' }
ListElement { c1: 'hallo2'; c2: 'bye' }
ListElement { c1: 'hallo3'; c2: 'bye' }
ListElement { c1: 'hallo4'; c2: 'bye' }
ListElement { c1: 'hallo5'; c2: 'bye' }
ListElement { c1: 'hallo6'; c2: 'bye' }
ListElement { c1: 'hallo7'; c2: 'bye' }
ListElement { c1: 'hallo8'; c2: 'bye' }
ListElement { c1: 'hallo9'; c2: 'bye' }
}
TableView {
id: tv
itemDelegate: Item {
id: mydelegate
signal doubleclicked()
onDoubleclicked: console.log(styleData.value)
Text {
anchors.centerIn: parent
color: styleData.textColor
elide: styleData.elideMode
text: styleData.value
}
Connections {
target: tv.__mouseArea
onDoubleClicked: {
// Map to the clickposition to the delegate
var pos = mydelegate.mapFromItem(tv.__mouseArea, mouse.x, mouse.y)
// Check whether the click was within the delegate
if (mydelegate.contains(pos)) mydelegate.doubleclicked()
}
}
}
TableViewColumn {
role: 'c1'
title: 'hey'
width: 100
}
TableViewColumn {
role: 'c2'
title: 'tschau'
width: 100
}
model: lm
}
}

Resources