Presetting currentIndex in QML TreeView - qt

I'm using QML TreeView to list some categorized options for user to select.
Using property TreeView.selection I have assigned SlectionModel to the treeview.
I have a problem with preselecting the item. Using
treeView.selection.setCurrentIndex(idx,3)
I only set the properties of the selection model(the item gets correctly selected/higlighted), but treeView.currentIndex is still invalid. And when using key up/down, it will jump to the first item.
What am I missing?
ItemSelectionModel {
id: treeViewSelectionModel
objectName: "treeViewSelectionModel"
model: myModel
onCurrentChanged:{console.log("Selectio - current changed from ",previous, " to ", current)}
}
TreeView {
focus: true
id: treeView
objectName: "treeView"
headerVisible: false //to hide the header
TableViewColumn {
title: "Name"
role: "name"
}
model: myModel
selectionMode: SelectionMode.SingleSelection
selection: treeViewSelectionModel
Component.onCompleted: {
var idx = treeView.model.getPreselected();
console.log("preselected",idx);
treeView.selection.setCurrentIndex(idx,ItemSelectionModel.Select);
treeView.selection = treeView.selection
//These logged current indexes does not match
console.log("treeView.currentIndex",treeView.currentIndex);
console.log("treeView.selection.currentIndex",treeView.selection.currentIndex);
updateGuiSize();
treeView.forceActiveFocus();
}
}

The problem is that the treeView.currentIndex and treeView.selection.currentIndex are not the same. If you go to set the value of the treeView.currentIndex, it is accessible only through the hidden variable __listView.
__listView.currentIndex = idx.row
EDIT:
I found another option
treeView.__currentRow = idx.row

Related

How to programmatically append text to a QML TextArea?

I am trying to pass log data to my QML front end, one line at a time, and have it append to the end of a TextArea. I've considered several approaches. The following is the most promising. I have created a QAbstractListModel (in Python) and pass this model into a repeater where it arrives as a single item (rowCount =1) which I append to the TextArea using the line
text: terminal_text.text + display
This works but I get this warning everytime the text is updated.
file://.../TextArea.qml:728:9: QML QQuickTextEdit*: Binding loop detected for property "text"
See below for the code of the repeater.
Repeater {
model: TerminalFeed { }
delegate: TextArea {
id: terminal_text
font.family: "Courier"
width: parent.width
height: parent.height
readOnly: true
selectByMouse: true
wrapMode: TextEdit.NoWrap
horizontalScrollBarPolicy: Qt.ScrollBarAsNeeded
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
text: terminal_text.text + display
}
}
How can I stop this happening? Alternatively does anyone have a better way of achieving the same result?
Technically, that is indeed a binding loop because text is dependent on its own value. If QML didn't detect it and break it, an infinite loop of updating would result.
Instead of using a binding, you can do something like this:
Repeater {
model: TerminalFeed { }
delegate: TextArea {
id: terminal_text
font.family: "Courier"
width: parent.width
height: parent.height
readOnly: true
selectByMouse: true
wrapMode: TextEdit.NoWrap
horizontalScrollBarPolicy: Qt.ScrollBarAsNeeded
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
onDisplayChanged: {
text = text + display;
}
}
}
With the original binding approach, it will try and update whenever either display or text changes. With this approach, it will only try and update whenever display changes – which is what you really want.
I had a similar problem where I wanted to show logged data in a QML window.
I use the insert() method, which is inherited from QML TextField. The insertion position is the length of the TextArea.
TextArea {
id: outputTextArea
}
Component.onCompleted: {
data = "dummyString"
outputTextArea.insert(outputTextArea.length, data)
}

Qt QML Notify ListView when items in the model are added or removed

My ListView should always scroll its currently active item to the top between preferredHighlightBegin and preferredHighlightEnd. That works while clicking an item. However when a new item is added to my AbsctractListModel (And its not just added on the end but could be added anywhere in the middle), the ListView shows the new item, but it is not scrolled to the top. This is most likely because list.currentIndex = index is not set for new added items. I want to set it but did not find a way yet to do so. Does anyone have a hint?
I tried different signals such as onCurrentItemChanged but did not find the proper one yet which also gives me the correct index.
ListView {
id: list
anchors.fill: parent
clip: true
spacing: 11
focus: true
highlightMoveDuration: 400
snapMode: ListView.SnapToItem
preferredHighlightBegin: 0
preferredHighlightEnd: 100
highlightRangeMode: ListView.StrictlyEnforceRange
model: myAbstractListModel
delegate: ListItem {
height: 56
width: list.width
color: "red"
onListItemClicked: {
list.currentIndex = index
}
}
}
The expected result would be a ListView which scrolls a programmatically new added item of my AbstractListModel to the top (between preferredHighlightBegin and preferredHighlightEnd).
Adding this to your ListView will change the current index to the last one whenever the number of items in the model changes, you might want to keep track of the previous items count if you want to scroll only on insertion.
onCountChanged: {
if (count > 0) {
currentIndex = count - 1;
}
}
If the new items might be inserted anywhere in the model you could connect directly to it using the Connections item inside your ListView and connect it directly to the rowsInserted signal of QAbstractItemModel.
Connections {
target: model
onRowsInserted: {
currentIndex = first + 1;
}
}
If your model emits only the dataChanged signal you can try this.
Connections {
target: model
onDataChanged: {
currentIndex = topLeft.row() + 1;
}
}

how to catch Spinbox event of change value in QML?

Subj
I show my data on QML' Listview with delegated text, labels, and spinboxvalue elements of item. When user change the value of spinbox I need to know it and make some deals. But MouseArea works beyond of Spinbox, not on spinned buttons, onClicked event doesn't exists.
SpinBox {
id: goodsCountSpinBox
leftPadding: 1
rightPadding: 1
value: model.goodsCount
implicitWidth: 100
anchors.right: dataRow.right
MouseArea {
anchors.fill: parent
onClicked: {
listView.currentIndex = index; //Change the current selected item to the clicked item
console.log("SpinBox::MouseArea::onClicked")
}
}
By the way, and how to out current datetime in console log?
I found onValueChanged in examples but I don't find it in QML manuals.
SpinBox {
id: fontSizeSpinBox
activeFocusOnPress: false
implicitWidth: 50
value: 0
property bool valueGuard: true
onValueChanged: if (valueGuard) document.fontSize = value
}
I found to print current timestamp
Qt.formatTime(new Date()
If you want to just use the current value of the spinbox, just use its value property in a binding.
If you want to react to the value change, use the handler for the property's change signal, i.e. onValueChanged.

Open QML ComboBox drop down menu from the current item position

I have choose an item in combobox. Item's position is 300 for example. If I want to choose new element from combobox . Popup shows from beginning. I want to popup opened from current item position.
ComboBox {
id: control
model: ["First", "Second", "Third","MERHABA","NASILSIN","SELAM","IYIMISIN","DOSTUM","SUAN","BIR","DENEME YAPILIYOR"]
//width: 350
//font.pixelSize: 20
delegate: ItemDelegate {
width: 350
text: modelData
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
font.pixelSize: 30
highlighted: control.highlightedIndex == index
}
For QtQuick.Controls 2, There is a 'popup' property, so we can set the popup position with it's own property 'y', like this:
ComboBox {
model: ["First", "Second", "Third"]
delegate: ItemDelegate {
text: modelData
}
popup.y: y + height // popup just below ComboBox
}
The ComboBox will work the way you want it if the conditions allow for it, that is, if you have enough elements to fill the entire drop down list after the current index item it will show from that item rather than the beginning.
The stock ComboBox however doesn't seem to allow you to specify the drop down list height, so it will take significantly more elements than you have in your example. Or significantly taller elements.
Furthermore, if the current index is the last element, how do you imagine this will show? The list would show only the last element plus a whole bunch of nothing, which is not even possible, the last item cannot move up from the end of the list.
If you really want that behavior, you will have to implement your own combo box element from scratch.
I was facing the same issue and found out that if you place the popup onOpened, it works perfectly:
ComboBox {
id: yearDropdown
model: yearModel
onActivated: updateVisibleDate()
popup: Popup {
id: comboPopup
clip: true
contentItem: ListView {
id: listView
implicitHeight: contentHeight
model: yearDropdown.popup.visible ? yearDropdown.delegateModel : null
onModelChanged: if(model) positionViewAtIndex(yearDropdown.currentIndex, ListView.Center);
ScrollIndicator.vertical: ScrollIndicator { }
}
onOpened: {
x = yearDropdown.x //Set the position you want
y = yearDropdown.y + yearDropdown.implicitHeight //Set the position you want
}
}
}

ListView model function

I am just getting started in Qt, and trying to make function which operates ListView model's elements.
I have custom made button in "myButton.qml" which has states like "normal", "pressed", "selected", etc.
ListView is in "main.qml". Structure is like this:
ListView{
//...
model: nameModel
delegate: myButton {
//...
}
}
So here is my goal: this list of buttons should act like group of radiobuttons - only one can have selected state and selected state is when you press button. I think that I should have click handler and a function that calls on button click. Function should check the list of buttons and if one button was selected before function just changes its state to "Normal".
So I have no idea of how to write this func and where should I place it. I read Qt docs but still no idea.
A possible easy way to solve this problem is by exploiting ExclusiveGroup. As discussed in the documentation, support to this type can be added to any type:
It is possible to add support for ExclusiveGroup for an object or control. It should have a checked property, and either a checkedChanged, toggled(), or toggled(bool) signal. It also needs to be bound with ExclusiveGroup::bindCheckable() when its ExclusiveGroup typed property is set.
You can define an ExclusiveGroup at the ListView level and implement the required logic in the ListView delegate. By binding the delegate ExclusiveGroup property to the ExclusiveGroup of the ListView you should achieve what you want, without the need of a function that crawls the model.
Final toy example to demonstrate the usage:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
id: root
visible: true
width: 200
height: 500
ListView {
anchors.fill: parent
model: 10
spacing: 20
ExclusiveGroup { id: ex } // the group for all the delegate
delegate: Rectangle {
id: delegate
width: ListView.view.width
height: 30
color: checked ? "yellow" : "steelblue"
// code to have exclusive behaviour
property bool checked: false
property ExclusiveGroup exclusiveGroup: ex
onExclusiveGroupChanged: {
if (exclusiveGroup)
exclusiveGroup.bindCheckable(delegate)
}
// mouse area to trigger the property change
MouseArea {
anchors.fill: parent
onClicked: checked = true
}
}
}
}

Resources