I'm updating the qml Listview from c++ like;
in main.cpp;
listeci frmlisteci;
engine.rootContext()->setContextProperty("listeElemanlar", &frmlisteci.listeElemanlar);
in Liste.qml;
ListView{
id: listeciElemanlar
anchors.fill: parent
model: listeElemanlar
delegate: Text{
text: display
verticalAlignment: Text.AlignBottom
width : parent.width
color: textRengi
font.pointSize: 30
leftPadding: 20
}}
in listeci.h;
public:
QStringListModel listeElemanlar;
private:
QStringList userAyarlari;
in listeci.cpp;
listeci::listeci(QObject *parent) : QObject(parent)
{
// Kullanıcı ayarları elemanları
userAyarlari.append("Kullanıcı ayarı 01");
userAyarlari.append("Kullanıcı ayarı 02");
userAyarlari.append("Kullanıcı ayarı 03");
userAyarlari.append("Kullanıcı ayarı 04");
userAyarlari.append("Kullanıcı ayarı 05");
userAyarlari.append("Kullanıcı ayarı 06");
userAyarlari.append("Kullanıcı ayarı 07");
userAyarlari.append("Kullanıcı ayarı 08");
userAyarlari.append("Kullanıcı ayarı 09");
userAyarlari.append("Kullanıcı ayarı 10");
userAyarlari.append("Kullanıcı ayarı 11");
userAyarlari.append("Kullanıcı ayarı 12");
userAyarlari.append("Kullanıcı ayarı 13");
userAyarlari.append("Kullanıcı ayarı 14");
userAyarlari.append("Kullanıcı ayarı 15");
userAyarlari.append("Kullanıcı ayarı 16");
userAyarlari.append("Kullanıcı ayarı 17");
userAyarlari.append("Kullanıcı ayarı 18");
userAyarlari.append("Kullanıcı ayarı 19");
}
void listeci::listeGuncelle()
{
listeElemanlar.setStringList(userAyarlari);
}
with these lines, I can successfully create a list and show on qml Listview, when I clicked on a button on qml side by calling
frmlisteci.listeGuncelle()
But when I do this procedure after a mount,( we can say refreshing the list) the code becoming slow, and waiting on this line:
listeElemanlar.setStringList(userAyarlari);
What can be the reason ?
I discovered that the problem is causing by stackview....
I'm navigating between the pages by stackview.pop.
I changed the pop to replace and the problem solved....
Related
I have a virtual large table, and an area that is real drawn. Depending on the position of the content, a certain part of the virtual table is displayed.
And now I'm trying in different ways to synchronize the content and the scrollbar.
In this case, the scrollbar jumps with the content.
AreaScrollBar.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
ScrollBar {
id: root
/// Virtual content size
/// 50000
property real virtualContentSize: 0
/// Real content size
/// 2000
property real contentSize: 0
/// Visible content size
/// 500
property real visibleSize: orientation === Qt.Horizontal ? width : height
/// Position offset step.
/// 100
property real stepAreaPosition: 1
/// The position of the component area [0 .. virtualContentSize - contentSize]
property real areaPosition: 0
/// [0 .. contentSize - visibleSize]
property real positionInArea: 0
property QtObject priv: QtObject {
/// В запасе после перестроения
readonly property real buffer: 0.2 * contentSize - (0.2 * contentSize) % stepAreaPosition
}
size: (orientation === Qt.Horizontal ? width : height) / virtualContentSize
policy: ScrollBar.AlwaysOn
onPositionChanged: {
console.log('onPosChanged', position)
let arP = -1
let pia = -1
if (position < 0) {
position = 0
} else if (position + size > 1) {
position = 1 - size
}
if (position * virtualContentSize + visibleSize
> areaPosition + 0.9 * contentSize) {
pia = priv.buffer
} else if (position * virtualContentSize - areaPosition < 0.1 * contentSize) {
pia = contentSize - (priv.buffer + visibleSize)
} else {
positionInArea = position * virtualContentSize - areaPosition
return
}
arP = position * virtualContentSize - pia
let arp2 = 0
if (arP < 0) {
arp2 = 0
} else if (arP > virtualContentSize - contentSize) {
arp2 = virtualContentSize - contentSize
} else {
arp2 = arP - (arP % stepAreaPosition)
}
positionInArea = position * virtualContentSize - arp2
areaPosition = arp2
}
}
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Window {
width: 600
height: 600
visible: true
title: qsTr("Hello World")
Column {
focus: true
Keys.onDownPressed: vSb.position += 10 / vSb.virtualContentSize
Keys.onUpPressed: vSb.position -= 10 / vSb.virtualContentSize
Keys.onLeftPressed: hSb.position -= 10 / hSb.virtualContentSize
Keys.onRightPressed: hSb.position += 10 / hSb.virtualContentSize
Row {
Column {
Text { text: qsTr("vSb.position %1").arg(vSb.position) }
Text { text: qsTr("vSb.areaPosition %1").arg(vSb.areaPosition) }
Text { text: qsTr("vSb.positionInArea %1").arg(vSb.positionInArea) }
}
Item {
width: 100
height: 1
}
Column {
Text { text: qsTr("hSb.position %1").arg(hSb.position) }
Text { text: qsTr("hSb.areaPosition %1").arg(hSb.areaPosition) }
Text { text: qsTr("hSb.positionInArea %1").arg(hSb.positionInArea) }
}
}
Rectangle {
id: mainArea
width: 500
height: 500
clip: true
color: "gray"
border.color: "darkgray"
border.width: 3
property int _rows: 50
property int _columns: 10
property int _virtualRows: 500
property int _virtualColumns: 50
property int _rectWidth: 90
property int _rectHeight: 90
property int _spacing: 10
property int _virtualContentWidth: _virtualColumns * (_rectWidth + _spacing)
property int _virtualContentHeight: _virtualRows * (_rectHeight + _spacing)
Flickable {
id: _flick
anchors.fill: parent
contentWidth: mainArea._columns * (mainArea._rectWidth + mainArea._spacing)
contentHeight: mainArea._rows * (mainArea._rectHeight + mainArea._spacing)
Column {
spacing: mainArea._spacing
Repeater {
model: mainArea._rows
Row {
property int rowInd: index
spacing: mainArea._spacing
Repeater {
model: mainArea._columns
Rectangle {
id: rect
property int columnInd: index
width: mainArea._rectWidth
height: mainArea._rectHeight
color: "#3c324a"
Column {
Repeater {
model: rect.height / 10
Rectangle {
width: rect.width
height: 10
color: Qt.lighter(rect.color, 1 + index * 0.1)
}
}
}
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignTop
text: '[%2, %3]'.arg(rowInd).arg(columnInd)
font.pointSize: 7
}
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: ('content\n[%1, %2]'
.arg(vSb.areaPosition / vSb.stepAreaPosition + rowInd)
.arg(hSb.areaPosition / hSb.stepAreaPosition + columnInd))
font.pixelSize: 15
}
}
}
}
}
}
interactive: true
Binding on contentX {
value: {
console.log('_flick.contentX', _flick.contentX, hSb.positionInArea)
return hSb.positionInArea
}
}
Binding on contentY {
value: {
console.log('_flick.contentY', _flick.contentY, vSb.positionInArea)
return vSb.positionInArea
}
}
}
AreaScrollBar {
id: vSb
orientation: Qt.Vertical
stepAreaPosition: mainArea._rectHeight + mainArea._spacing
virtualContentSize: mainArea._virtualRows * stepAreaPosition
contentSize: mainArea._rows * stepAreaPosition
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: hSb.top
Binding on position {
value: {
console.log('vSb.position', _flick.contentY,
vSb.areaPosition, vSb.virtualContentSize)
return (_flick.contentY + vSb.areaPosition) / vSb.virtualContentSize
}
}
}
AreaScrollBar {
id: hSb
orientation: Qt.Horizontal
stepAreaPosition: mainArea._rectWidth + mainArea._spacing
virtualContentSize: mainArea._virtualColumns * stepAreaPosition
visibleSize: mainArea.width
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: vSb.left
Binding on position {
value: {
console.log('hSb.position', _flick.contentX,
hSb.areaPosition, hSb.virtualContentSize)
return (_flick.contentX + hSb.areaPosition) / hSb.virtualContentSize
}
}
}
}
}
}
Error when I try to drag the canvas
This was always the problem when Qt project added QML. People try to skip learning C++ and do everything in JavaScript.
You need to read the TableView documentation. A TableView is already Flickable and provides a rectangle view into a TableModel. No need to try re-inventing the wheel.
Then look at this for adding scrollbars to TableView.
The correct solution is always create all data in C++, use QML for display only.
As soon as people start trying to implement logic in QML/JavaScript the wheels come off the cart.
Please test this code, I use some parts of your code but I didn't complicate it with the position I just use Flickable and Scrollbar, and the Repeaters that you use for creating your matrix.
I test it and in this way, scrollbars didn't jump.
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
id:mainArea
visible: true
width: 640
height: 480
title: qsTr("Flickable and Scrollbar ")
property int _rows: 50
property int _columns: 10
property int _virtualRows: 500
property int _virtualColumns: 50
property int _rectWidth: 90
property int _rectHeight: 90
property int _spacing: 10
property int _virtualContentWidth: _virtualColumns * (_rectWidth + _spacing)
property int _virtualContentHeight: _virtualRows * (_rectHeight + _spacing)
Flickable{
width: parent.width
height: parent.height
contentHeight: mColumnId.implicitHeight
contentWidth: mColumnId.implicitWidth
Column{
id : mColumnId
anchors.fill: parent
spacing: mainArea._spacing
Repeater {
model: mainArea._rows
Row {
property int rowInd: index
spacing: mainArea._spacing
Repeater {
model: mainArea._columns
Rectangle {
id: rect
property int columnInd: index
width: mainArea._rectWidth
height: mainArea._rectHeight
color: "#3c324a"
Column {
Repeater {
model: rect.height / 10
Rectangle {
width: rect.width
height: 10
color: Qt.lighter(rect.color, 1 + index * 0.1)
}
}
}
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignTop
text: '[%2, %3]'.arg(rowInd).arg(columnInd)
font.pointSize: 7
}
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: "Parisa"
font.pixelSize: 15
}
}
}
}
}
}
ScrollBar.vertical: ScrollBar{}
ScrollBar.horizontal: ScrollBar{}
}
}
Item {
Component.onComplete: {
for (var i=0;i < 10;i++) {
myModel.append({"myTxt": "SomeThing"+i});
}
}
ListModel {
id: myModel
}
ListView {
id: listView
width: parent.width
height: parent.height
model: myModel
delegate: Rectangle {
property string propItemState: 0;
MyListItem {
id: test
itemText: myText
itemState: propItemState;
}
}
}
}
I want to update the propItemState once the list is displayed.
For that, I have tried the below method but am getting an undefined error.
I am calling this method once the list model is updated.
function updateListItems() {
for (var index=0;index < listView.count;index++) {
console.log("propItemState: "+listView.contentItem.children[index].propItemState);
listView.contentItem.children[index].propItemState = 2;
}
}
You can bind propItemState to something outside of your delegate. Or you can add signal handler (Connections), which listens to your model (for example; or to some another class) and changes state when your conditions met.
Example:
Item {
Component.onComplete: {
for (var i=0;i < 10;i++) {
myModel.append({"myTxt": "SomeThing"+i});
}
internal.state = "1";
}
ListModel {
id: myModel
}
QtObject {
id: internal
property string state: "0"
}
ListView {
id: listView
width: parent.width
height: parent.height
model: myModel
delegate: Rectangle {
property string propItemState: internal.state
MyListItem {
id: test
itemText: myText
itemState: propItemState;
}
}
}
}
In my ListView I've these:
ListView{
id: listView
Layout.fillHeight: true
Layout.fillWidth: true
model: addContext.newLease.receivables
delegate: ItemDelegate{
width: parent.width
topPadding: 0
bottomPadding: 0
contentItem: RowLayout{
Text{
text: modelData.headId //mainContext.receivableHeads....name
Layout.preferredWidth: parent.width / 2
}
Text{
text: modelData.amount
horizontalAlignment: Text.AlignRight
}
PathButton{
pathData: C.minusIcon
Layout.alignment: Qt.AlignRight
hasToolTip: false
onClicked: addContext.removeReceivable(listView.currentIndex)
}
}
}
}
With this I've some Layout issue BUT I'll ask about that later. Here in the contentItem's first Text, I've a number headId and it presents that number in the ListView with that modelData.headId BUT I want to present the name instead. The model, addContext.newLease.receivables, doesn't contain the name, it only has these properties:
class Receivable : public QObject{
Q_OBJECT
Property(int, leaseId)
Property(int, headId)
Property(int, amount)
};
name corresponding to the headId is in mainContext.receivableHeads which has these properties:
class Head : public QObject{
Q_OBJECT
Property(int, id)
Property(int, controlId)
Property(QString, name)
Property(QString, description)
};
I want to get the name from mainContext.receivableHeads by matching the headId of modelData to the id of mainContext.receivableHeads.
EDIT
This:
Q_INVOKABLE QString headName(int headId){
foreach(auto head, mvm->receivableHeads()){
if(head->id() == headId)
return head->name();
}
}
along with this:
text: addContext.headName(modelData.headId) //modelData.headId //mainContext.receivableHeads.indexOf(modelData).name
as suggested by Thomenson, works. My receivableHeads is a tiny list, so it's a good candidate for javascript, is there any built-in javascript function that does this?
Well, I didn't know that javascript function like this:
text: {
for(var i =0; i < mainContext.receivableHeads.length; i++){
if(mainContext.receivableHeads[i].id === modelData.headId)
return mainContext.receivableHeads[i].name
}
}
also works!
I have the following Virtual Keyboard which sets the y of a flickable to a value to not hide text. the settings variable is containts the IntSettingsRow below.
//Administration.qml
K_VirtualKeyBoard{
id: keyboard
z:100
onVisibleChanged: {
if(visible){
if(settings.posY - keyboard.height > 0){
flickable.contentY = settings.posY - keyboard.height + 20
}
else{
flickable.contentY = 0
}
}
if(!visible){
settings.settingsRow.textField.focus = false
flickable.contentY = 0
}
}
}
And i have a IntSettingsRow which is a Textfield to insert Some text.
//GeneralSettings.qml
IntSettingsRow{
id: touchDeactivationTimedTime
height: itemHeight
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 8
validator: IntValidator{bottom: 10; top: 300}
title: qsTr("deactivate time")+":"
value: currentSettings?currentSettings.deactivationTime:0
onValueChanged:{
var val = parseInt(value)
if (!isNaN(val)){
currentSettings?currentSettings.deactivationTime = val:{}
}
}
onEditingFinished: {
if (value === "") textField.text = currentSettings.deactivationTime
}
onFocusChanged: {
settingsRow = touchDeactivationTimedTime
if(!textField.focus){
editingFinished()
posY = 0
}else{
posY = mapToItem(parent, x, y).y
}
}
}
Somehow on the first click into the Textfield the virtual keyboard pops up as expected and the flickable shifts. i hide the keyboard and try it again it doesn't work. So the virtual keyboard pops up but the flickable does not change.
I found a solution with editing the flickable directly from the onfocus in my intsettingsrow. now it works
So i changed my K_VirtualKeyboard to:
K_VirtualKeyBoard{
id: keyboard
z:100
onVisibleChanged: {
if(!visible){
flickable.contentY = 0
}
}
}
And added this to the textfield that triggered the Keyboard
textField.onReleased: {
var keyBoardY
if(keyboard.visible)
keyBoardY = keyboard.y
else
keyBoardY = keyboard.y - keyboard.height
if(keyBoardY < (mapToItem(root.parent.parent,0,0).y + height*2))
flickable.contentY = (mapToItem(root.parent.parent,0,0).y + height*2) - keyBoardY + 8
}
I use a ListView and I need to fill my Window with the last Item that was inserted inside it.
In this case, I set the ListView property: contentY with the y value of myItem
My problem is that when you insert a new Item, I don't know how to know the y value of the Item because it is not yet set.
This is what I try:
ScrollView {
id: scroll
ListView {
model: DelegateModel {
id: visualModel
model: myModel //Model is set in the cpp
delegate: Rectangle {
id: rectangleDelegate
Component.onCompleted: {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
console.log(rectangleDelegate.y ) //Not set yet (y = 0)
}
Button {
onClicked {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
console.log(rectangleDelegate.y ) //Now it is ok (y = rightValue)
}
}
}
}
}
}
How could I proceed ?
Thanks to #derM, here is a simple example that works:
ScrollView {
id: scroll
ListView {
model: DelegateModel {
id: visualModel
model: myModel //Model is set in the cpp
delegate: Rectangle {
id: rectangleDelegate
onYchanged: {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
}
}
}
}
}