PDA

View Full Version : Modify c++ model data from QML Repeater



laszlo.gosztola
9th February 2011, 10:49
Hi all!

I would like to create something similar to the corkboard qml example. On a screen I have some items, which I can move, resize, rotate, and I can store these properties.

I created a model inherited from QAbstractListModel on C++ side. It populates data well.

On the QML side I show it in a Repeater, like this:


Repeater {
id: myRepeater
model: dayScreenModel
DayScreenEntry {
x: position.x
y: position.y
text: title
}


position, title, size, rotation comes from the C++ model.

It works fine.

But how can I achieve modification of these data?

I found a way:
in the model, created a public slot:
void updateData(int index, QString role, QVariant value);

and, in the DayScreenEntry.qml I can call like this:
myRepeater.model.updateData(index, "title", "NewTitle")

I can refresh the data in code, and it populates also to the qml side, shows the changes.

But, I have some problems with it:
- it's not 'elegant' - Is it possible somehow to call the 'setData' function of the model?
- on QML side, i have to write the model name in the delegate - I can use it only for this model, have to know the model name.

Do You know any other way to get this stuff working?

wysota
9th February 2011, 20:05
Since the delegate is a component, it should be possible to have it contain an item that allows you to edit the content it displays (like the TextEdit element). If you bind the model's property to a property that is editable by the component, there is a good chance the change will be propagated to the model.

laszlo.gosztola
9th February 2011, 21:53
Hello!

Thank You for your reply. I want expect what you write. But I don't know, how...

I attach a simple example.
The interesting part is in the qml file's mousearea section. I can reach the index, and the title also... But I cannot change the title :(
Maybe only I forgot to set a flag or something like this.
I think, that 'setData' should be called somehow as when the data populates from the model to the view - it's achieved by the 'data' function.
5910

main.cpp


#include <QApplication>
#include <QMessageBox>

#include "mainwidget.h"

int main(int argc, char** argv)
{
QApplication a (argc, argv);

MainWidget* mainWidget=new MainWidget();
mainWidget->show();
int r=0;
try
{
r=a.exec();
}catch (...)
{
QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("Critical error - Unhandled exception in the application!"));
r=-1;
}
delete mainWidget;
return r;
}



mainwidget.h


#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QtDeclarative/QDeclarativeView>
#include "dummymodel.h"


class MainWidget : public QDeclarativeView
{
Q_OBJECT;
public:
MainWidget(QWidget* parent=0);
~MainWidget();
public slots:


private:
QDeclarativeContext* m_context;
DummyModel model;

};

#endif // MAINWIDGET_H


mainwidget.cpp


#include "mainwidget.h"

#include <QtDeclarative>
#include <QDeclarativeContext>

MainWidget::MainWidget(QWidget* parent) : QDeclarativeView(parent)
{
setResizeMode(QDeclarativeView::SizeRootObjectToVi ew);
setMinimumSize(300,300);

m_context=rootContext();
m_context->setContextProperty("dummyModel", &model);
setSource(QUrl("qml/qmlModel/main.qml"));
}

MainWidget::~MainWidget()
{

}



dummymodel.h


#ifndef DUMMYMODEL_H
#define DUMMYMODEL_H

#include <QAbstractListModel>
#include <QStringList>

class DummyModel : public QAbstractListModel
{
Q_OBJECT
public:
enum DummyRoles {
TitleRole=Qt::UserRole+1
};

DummyModel(QObject* parent=NULL);
~DummyModel();

int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);

private:

void fillData();
QStringList m_data;
};

#endif // DUMMYMODEL_H


dummymodel.cpp


#include "dummymodel.h"

#include <QDateTime>

DummyModel::DummyModel(QObject *parent) : QAbstractListModel(parent)
{
m_data.clear();
QHash<int, QByteArray> roles;
roles[TitleRole]="title";
setRoleNames(roles);
fillData();
}

DummyModel::~DummyModel()
{

}

int DummyModel::rowCount(const QModelIndex &parent) const
{
return m_data.count();
}

QVariant DummyModel::data(const QModelIndex &index, int role) const
{
qDebug("dummymodel::data");
qDebug("Role: %d", role);
if (!index.isValid()) return QVariant();
if ((index.row()<0) || (index.row()>=m_data.count())) return QVariant();
if (role==TitleRole)
return m_data.at(index.row());
return QVariant();
}

void DummyModel::fillData()
{
qsrand(QDateTime::currentMSecsSinceEpoch());
beginResetModel();
m_data.clear();
int num=1+qrand()%8;
for (int i=0; i<num; i++)
{
m_data.append(QString("DummyText%1").arg(i+1));
}
endResetModel();
}

bool DummyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
qDebug("SetData called");
return true;
}


and the qml file:
main.qml


import QtQuick 1.0


Rectangle {
id: base
width: 500
height: 360
Repeater {
model: dummyModel
delegate: myDelegate
}

Component {
id: myDelegate
Rectangle {
x: 10
y: index*35+10
width: 200
height: 30
color: "yellow"
TextEdit {
text: title
font.pointSize: 18
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Clicked index:",index);
title="test"
}
}
}
}
}

wysota
9th February 2011, 23:22
Try implementing flags() for your model and return ItemIsEditable.

laszlo.gosztola
10th February 2011, 00:12
Already tried.




Qt::ItemFlags DummyModel::flags(const QModelIndex &index) const
{
qDebug("DummyModel::Flags");
return Qt::ItemIsEditable;
}


But unfortunately it doesn't help, but even the function is not called. (No qDebug output)

wysota
10th February 2011, 00:24
You have to return other flags as well.Like ItemIsEnabled. Make sure the signature fits and verify using QListView that your model is editable.

laszlo.gosztola
10th February 2011, 10:47
It is a good idea to test also with a QListView...

I found that the behavior is a little different.
From QML the flags function never called. In the data function the role is just the roles I have defined with setRoleNames call.

If I use a form with QListView flags is called many times, and role is DisplayRole, and others, so I had to extend the model, to implement the displayrole also.

But from QML, the answer is always: Cannot assign to read-only property 'title'

Any other idea?

Maybe I should use the solution with calling the slots...

Matriarch
19th October 2011, 15:35
I am also trying to use QAbstractItemModel::setData from QML, but it is not possible. QML model-view-controller connection with C++ Qt is build-in hardcoded suck, thats all.