Bind CheckBox checked-state in TableView to custom model attribute - c++

I've got a QML-application containing a TableView with two columns. One of them is a CheckBox. Now I created a model derived from QAbstractTableModel. Reading data for the ordinary text-column already works but how do I sync the checked-property for my CheckBox with the model?
Currently I can't even set it checked via model. You find the relevant code below.
tablemodel.cpp
TableModel::TableModel(QObject *parent) :
QAbstractTableModel(parent)
{
list.append("test1");
list.append("test2");
}
int TableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return list.count();
}
int TableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 2;
}
QVariant TableModel::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= list.count())
return QVariant();
if (role == NameRole)
return list[index.row()]
else if (role== EnabledRole){
//list is not QList<QString>, its a custom class saving a String and a boolean for the checked state
return list[index.row()].isEnabled();
}
else {
return QVariant();
}
}
QHash<int, QByteArray> TableModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[EnabledRole] = "enabled";
return roles;
}
tablemodel.hpp
class TableModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Roles {
NameRole = Qt::UserRole + 1,
EnabledRole
};
explicit TableModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex & parent = QModelIndex()) const;
Q_INVOKABLE QVariant data (const QModelIndex & index, int role) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<QString> list;
main.qml
TableView {
id: Table
model: TableModel
TableViewColumn {
role: "name"
}
TableViewColumn {
role: "enabled"
delegate: CheckBox {
//how to get the right state from the model
//checked: ??
}
}
}
main.cpp
QQmlApplicationEngine engine;
QQmlContext * context = new QQmlContext(engine.rootContext());
TableModel tableModel;
context->setContextProperty("tableModel",&tableModel);
QQmlComponent component(&engine, QUrl("qrc:///main.qml"));
QQuickWindow * window = qobject_cast<QQuickWindow*>(component.create(context));
window->show();

You can emit signal from qml, when clicked on checkbox; connect this signal to your model slot and do something
main.qml
TableView {
id: table
objectName: "myTable"
signal checkedChanged(int index, bool cheked)
TableViewColumn {
role: "enabled"
delegate: CheckBox {
onClicked: table.checkedChanged(styleData.row, checked);
}
}
main.cpp
QQuickItem *obj = engine.rootObjects().at(0)->findChild<QQuickItem*>(QStringLiteral("myTable"));
QObject::connect(obj,SIGNAL(checkedChanged(int,bool)),tableModel,SLOT(mySlot(int,bool)));

Related

How to unite several models into one?

I've created two models and now it's need to be united in one.
Both of them are inheriting QAbstractListModel and contains only roles and simple data as QVector<QMap<..., ...>> backing; what I pass to data() function
There's any way to unite them into one model and pass to Qml page through delegate?
Here my models:
ExtraModel
#include "extramodel.h"
ExtraModel::ExtraModel(QObject *parent) : QAbstractListModel(parent)
{
QMap<QString, QString> element;
element.insert("SimpleText", "Hello, world!");
backing.append(element);
element.clear();
}
QHash<int, QByteArray> ExtraModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[SimpleTextRole] = "SimpleText";
return roles;
}
QVariant ExtraModel::data(const QModelIndex &index, int role) const
{
if(role == SimpleTextRole)
return backing[index.row()].value("SimpleText");
}
MainModel (here I am trying to pass ExtraModel to MainModel)
#include <mainmodel.h>
#include <extramodel.h>
MainModel::MainModel(QObject *parent) : QAbstractListModel(parent)
{
ExtraModel *extra;
QMap<QString, QHash<int, QByteArray>> element;
element.insert("Role", extra->roleNames());
backing.append(element);
element.clear();
}
QHash<int, QByteArray> MainModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[ModelRole] = "Role";
return roles;
}
QVariant MainModel::data(const QModelIndex &index, int role) const
{
if (role == ModelRole)
return backing[index.row()].value("Role");
return QVariant();
}
That's how I want it to be in .qml file
import QtQuick 2.6
import Sailfish.Silica 1.0
import MainModel 1.0
Page {
SilicaListView {
model: MainModel {
id: _mainModel
}
delegate: Rectangle {
Text {
id: _simpleText
text: _mainModel.[someRoleNameFromExtraModel].SimpleText
}
}
}
}
You are probably better of writing a plain class with Q_PROPERTY to ExtraModel
class MainModel : public QObject {
Q_OBJECT
Q_PROPERTY(ExtraModel* extra READ extraModel CONSTANT)
public:
MainModel();
ExtraModel* extraModel() const { return extraModel_; }
private:
ExtraModel* extraModel_ = nullptr;
};
cpp file:
MainModel::MainModel(QObject *parent)
: QObject(parent)
, extraModel_(new ExtraModel(this))
{
}
Then your QML will look like this:
import QtQuick 2.6
import Sailfish.Silica 1.0
import MainModel 1.0
Page {
MainModel {
id: _mainModel
}
SilicaListView {
id: _mainModelView
model: _mainModel.extra
delegate: Rectangle {
Text {
id: _simpleText
text: simpleText
}
}
}
}
BTW, you should also change the rolenames to be uncapitalized for the first letter:
QHash<int, QByteArray> ExtraModel::roleNames() const
{
return {
{SimpleTextRole, "simpleText"},
};
}

Inconsistent view when removing a single row from QSqlTableModel in Qml

What I am trying to do ?
I am trying to create a list of users whoes data come from a database. And I want to use the concept of model/view programming to implement this. Morever I want to apply various operations on this list such as:
Displaying a list
Removing an item from the list
Adding an item to the list
Sorting the list
What is the problem ?
The first operation ( Displaying the list ) was easy but the second ( Removing an item from the list ) seems to impose inconsistency between the model and the view. No matter which item you select for deletion the view always shows two items have been deleted ( when in fact only the selected item has been deleted by the model ). The two items deleted by the view are the selected item and the last item. Why does it always delete the last item ? How would I fix this ?
Here is my code so far:
usermodel.h:
class UserModel : public QAbstractListModel {
Q_OBJECT
public:
UserModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool removeRows(int pos, int row, const QModelIndex &parent = QModelIndex()) override;
Q_INVOKABLE bool del_row(int);
// initialize and setup the database
static bool createConnection() {
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("Test");
if (!db.open()) return false;
QSqlQuery q;
QStringList t = db.tables();
// create table users if there is none
if (!t.contains("users", Qt::CaseInsensitive)){
if (!q.exec("create table users (id int primary key, firstname varchar(20))")){
return false;
}
}
q.exec("select * from users");
// insert new records when there is none
if (!q.first()){
q.exec("insert into users values(1, 'Danny')");
q.exec("insert into users values(2, 'Christine')");
q.exec("insert into users values(3, 'Lars')");
q.exec("insert into users values(4, 'Alex')");
}
}
private:
QSqlTableModel *model; // internal data store for models
static QSqlDatabase db;
};
usermode.cpp:
QSqlDatabase UserModel::db;
UserModel::UserModel(QObject *parent) : QAbstractListModel (parent) {
createConnection();
model = new QSqlTableModel(this, db);
model->setTable("users");
model->select();
}
int UserModel::rowCount(const QModelIndex &/*parent*/) const {
return model->rowCount();
}
QVariant UserModel::data(const QModelIndex &index, int role) const {
// This allows me to return more than one column data when not permitted
QJsonObject u_data;
u_data.insert("id", model->record(index.row()).value(0).toInt());
u_data.insert("name", model->record(index.row()).value(1).toString());
if (role == Qt::DisplayRole)
return u_data;
return QVariant();
}
bool UserModel::del_row(int row){
return removeRows(row, 1);
}
bool UserModel::removeRows(int pos, int rows, const QModelIndex &/*parent*/){
bool response;
int first = pos, last = pos + rows - 1;
beginRemoveRows(QModelIndex(), first, last);
response = model->removeRow(first, QModelIndex());
endRemoveRows();
return response;
}
main.qml:
ApplicationWindow {
visible: true
width: 640
height: 480
ListView {
anchors.fill: parent
delegate: SwipeDelegate {
width: parent.width
height: 50
text: user_model.data(user_model.index(index, 0), 0)["name"]
onClicked: user_model.del_row(model.index)
}
model: UserModel { id: user_model }
}
}
Note: Please don't provide any link in the replay as I may not be able to access them because the Government here has blocked most of the sites and whitelisted few sites ( stackoverflow is one of them ).
I don't see the need to create a model that is a wrapper of another model, so in this first solution I will propose a solution using the QSqlTableModel class directly. On the other hand when using removeRows() the row is not deleted but you must update the database using the select() method:
usermodel.h
#ifndef USERMODEL_H
#define USERMODEL_H
#include <QSqlTableModel>
class UserModel : public QSqlTableModel
{
Q_OBJECT
public:
UserModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
QVariant data(const QModelIndex &index, int role) const;
QHash<int, QByteArray> roleNames() const;
Q_INVOKABLE void removeRow(int row);
};
#endif // USERMODEL_H
usermodel.cpp
#include "usermodel.h"
#include <QSqlRecord>
UserModel::UserModel(QObject *parent, QSqlDatabase db): QSqlTableModel(parent, db){
setTable("users");
select();
}
QVariant UserModel::data(const QModelIndex &index, int role) const{
QVariant value;
if (index.isValid()) {
if (role < Qt::UserRole)
value = QSqlQueryModel::data(index, role);
else {
int columnIdx = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
}
}
return value;
}
QHash<int, QByteArray> UserModel::roleNames() const{
QHash<int, QByteArray> roles;
for (int i = 0; i < record().count(); i ++) {
roles.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
}
return roles;
}
void UserModel::removeRow(int row){
removeRows(row, 1, QModelIndex());
select();
}
main.cpp
#include "usermodel.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSqlQuery>
static bool createConnection() {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("Test");
if (!db.open()) return false;
QSqlQuery q;
QStringList t = db.tables();
// create table users if there is none
if (!q.exec("CREATE TABLE IF NOT EXISTS users (id int primary key, firstname varchar(20))")){
return false;
}
q.exec("insert into users values(1, 'Danny')");
q.exec("insert into users values(2, 'Christine')");
q.exec("insert into users values(3, 'Lars')");
q.exec("insert into users values(4, 'Alex')");
return true;
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<UserModel>("Database", 1, 0, "UserModel");
QGuiApplication app(argc, argv);
if(!createConnection())
return -1;
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.14
import QtQuick.Controls 2.14
import Database 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
ListView {
anchors.fill: parent
delegate: SwipeDelegate {
width: parent.width
height: 50
text: model.firstname
onClicked: user_model.removeRow(model.index)
}
model: UserModel { id: user_model }
}
}

QML Chart using QAbstractTableModel doesn't dynamically update

I have a QAbtractTableModel that is accessible to a QML file with chart and series and XYModelMapper. When trying to update the model, nothing shows up on the chart. I've tried different methods(even trying QStandardItemModel before) and I can't seem to get any charts to update once it's finished loading. Is there something I'm doing wrong using either the model or model mapper? I've included simplified version of the files here.
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
QVariant data(const QModelIndex& index, int role) const override;
int columnCount(const QModelIndex& parent) const override;
int rowCount(const QModelIndex& parent) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
Qt::ItemFlags flags(const QModelIndex & index) const override;
void clear();
void addData(QDateTime time, float temperature, quint32 brightness, quint8 moisture, quint16 conductivity);
private:
struct data {
QDateTime time;
float value;
};
QList<data> tableData;
};
class Obj : public QObject
{
Q_OBJECT
Q_PROPERTY(QAbstractTableModel* model READ getModel NOTIFY updated)
public:
QAbstractTableModel* getModel() {return myModel;}
Q_INVOKABLE void startUpdate(); //updated emitted here eventually as well as addData called on model.
signals:
void updated();
private:
MyModel *myModel = new MyModel();
};
QVariant MyModel::data(const QModelIndex& index, int role) const
{
if(role == Qt::DisplayRole && tableData.size()-1 >= index.row()) {
if(index.column() == 0) {
return QVariant(tableData[index.row()].time.toMSecsSinceEpoch());
} else if(index.column() == 1) {
return QVariant(tableData[index.row()].value);
} else {
return QVariant();
}
} else {
return QVariant();
}
}
int MyModel::columnCount(const QModelIndex& parent) const
{
return 5;
}
int MyModel::rowCount(const QModelIndex& parent) const
{
return 24;
}
void MyModel::addData(QDateTime time, float value)
{
tableData.append(data{time, value}); //Previously tried dynamic row count with begin/end row inserted.
emit dataChanged(createIndex(0,0),createIndex(23,4));
}
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<Obj>("org.eyecreate.testmodel",1,0,"ObjModel");
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
if (engine.rootObjects().isEmpty()) {
return -1;
}
return app.exec();
}
import QtCharts 2.3 as Charts
import org.eyecreate.testmodel 1.0
ObjModel {
id: myObj
}
Charts.ChartView {
antialiasing: true
legend.visible: false
anchors.fill: parent
Charts.LineSeries {
axisX: Charts.DateTimeAxis {
format: "MMM d yyyy ha"
}
axisY: Charts.ValueAxis {
min: 0
max: 50
}
Charts.VXYModelMapper {
model: myObj.model
xColumn: 0
yColumn: 1
}
}
}
Button {
onClicked: {
myObj.startUpdate();
}
}
EDIT: I found code very similar to mine that shows the same issue here: https://bitbucket.org/johnwoltman/chartmodelmappertest/src/master/
It seems this might be a bug: https://bugreports.qt.io/browse/QTBUG-60336
I found out through trying everything that the data points are in the series correctly, just that the graph needed to be adjusted so the points were in view.
Adjusting the min/max for each axis has allowed the lines to show up.

How can I access QAbstractTableModel Data from QML Repeater

I've been struggling for a few days trying to figure out how to display some pretty simple data in a QML window. I realize there are many ways to accomplish this task, and in this case, I'd prefer to find out how to use QAbstractTableModel.
I have a row of data, each row containing two items, a name and a value (name/value or key/value pair). I've subclassed the QAbstractTableModel to pass this data to the QML. Here is the code I have so far; it is mostly based on the tutorial that can be found here (which is also very old): https://doc.qt.io/archives/4.6/itemviews-addressbook.html.
databridge.h
#include <QObject>
#include <QAbstractTableModel>
#include <QPair>
class DataBridge : public QAbstractTableModel
{
Q_OBJECT
public:
explicit DataBridge();
explicit DataBridge(QList<QPair<QString, QString>> listOfPairs);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
bool insertRows(int row, int count, const QModelIndex &parent);
bool removeRows(int row, int count, const QModelIndex &parent);
Qt::ItemFlags flags(const QModelIndex &index) const;
QList<QPair<QString, QString>> getList();
signals:
public slots:
private:
QList<QPair<QString, QString>> m_listOfPairs;
};
#endif // DATABRIDGE_H
databridge.cpp
#include "databridge.h"
DataBridge::DataBridge()
{}
DataBridge::DataBridge(QList<QPair<QString, QString> > listOfPairs)
{ m_listOfPairs = listOfPairs; }
int DataBridge::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_listOfPairs.size();
}
int DataBridge::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
//Number of columns is always 2 in this kata
return 2;
}
QVariant DataBridge::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(index.row() >= m_listOfPairs.size() || index.row() < 0)
return QVariant();
if(role == Qt::DisplayRole)
{
QPair<QString, QString> pair = m_listOfPairs.at(index.row());
if(index.column() == 0)
return pair.first;
else
return pair.second;
}
return QVariant();
}
bool DataBridge::setData(const QModelIndex &index, const QVariant &value, int role)
{
//TODO
return QVariant();
}
QVariant DataBridge::headerData(int section, Qt::Orientation orientation, int role) const
{
//TODO
return QVariant();
}
bool DataBridge::insertRows(int row, int count, const QModelIndex &parent)
{
//TODO
return true;
}
bool DataBridge::removeRows(int row, int count, const QModelIndex &parent)
{
//TODO
return true;
}
Qt::ItemFlags DataBridge::flags(const QModelIndex &index) const
{
if(!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}
QList<QPair<QString, QString> > DataBridge::getList()
{
return m_listOfPairs;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QPair>
#include "databridge.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QList<QPair<QString, QString>> inserter;
inserter.append(QPair<QString, QString>("0", "zero"));
inserter.append(QPair<QString, QString>("1", "one"));
inserter.append(QPair<QString, QString>("2", "two"));
inserter.append(QPair<QString, QString>("3", "three"));
inserter.append(QPair<QString, QString>("4", "four"));
DataBridge * bridge = new DataBridge(inserter);
engine.rootContext()->setContextProperty("bridge", bridge);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Component{
id: myComponent
Row{
id: thisRow
Text{
text: bridge.data(index, 0)
}
Text{
text: "\t.\t.\t.\t"
}
Text{
text: bridge.data(index, 0)
}
}
}
Column{
id: thisColumn
anchors.horizontalCenter: parent.horizontalCenter
Repeater{
id: myRepeater
delegate: myComponent
model: bridge
}
}
Component.onCompleted: {
console.log("DEBUG main.qml");
}
}
Please ignore the functions with TODO in them in the databridge.cpp. I didn't include the bodies for the sake of brevity.
The first problem I'm facing is that when my repeater in the main.qml calls bridge.data(index, 0), the index is determined to be invalid in the first if statement of the data function (if(!index.isValid()). I'm not sure why this is happening. The second issue I can see, though I haven't gotten that far yet, is how can I tell the data function if I'm calling column 0 or column 1? It checks for this later in the function, and returns the pair relevant to the column requested. I'm guessing that in myComponent, I need something more specific to request the data from whichever column, but I'm not sure what that would be?
Any help with this would be immensely appreciated. Thank you in advance!
QModelIndex Documentation
Another link to the example referenced at the top
QAbstractTableModel Documentation
Qt Role Enum Documentation
I was able to find a solution, but I'm not sure if it's the correct one. I've added a function to the databridge class that takes integer input from the QML, finds a corresponding QModelIndex, and pulls the data from the model that way. This is what the code looks like:
From databridge.cpp
QString DataBridge::getThisItem(int index, int column)
{
QModelIndex currentIndex = QAbstractTableModel::index(index, column);
QString retVal = data(currentIndex, 0).toString();
return retVal;
}
Corresponding QML Repeater Change
Component{
id: myComponent
Row{
id: thisRow
Text{
text: bridge.getThisItem(index, 0)
}
Text{
text: "\t.\t.\t.\t"
}
Text{
text: bridge.getThisItem(index, 1)
}
}
}
This produces the desired output, that being:
I am interested to know if this is a good implementation, or if there is a better way to do this. Thanks again!

QML view wont update when adding a new item to a QAbstractListModel based model

I've figured out how to bind a model derived from QAbstractListModel to a QML view.
But the next thing I tired does not work. If a new Item is added to the model the QML view will not update. Why is that?
DataObject.h
class DataObject {
public:
DataObject(const QString &firstName,
const QString &lastName):
first(firstName),
last(lastName) {}
QString first;
QString last;
};
SimpleListModel.h
class SimpleListModel : public QAbstractListModel
{
Q_OBJECT
enum /*class*/ Roles {
FIRST_NAME = Qt::UserRole,
LAST_NAME
};
public:
SimpleListModel(QObject *parent=0);
QVariant data(const QModelIndex &index, int role) const;
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const;
QHash<int, QByteArray> roleNames() const;
void addName(QString firstName, QString lastName);
private:
Q_DISABLE_COPY(SimpleListModel);
QList<DataObject*> m_items;
};
SimpleListModel.cpp
SimpleListModel::SimpleListModel(QObject *parent) :
QAbstractListModel(parent)
{
DataObject *first = new DataObject(QString("Firstname01"), QString("Lastname01"));
DataObject *second = new DataObject(QString("Firstname02"), QString("Lastname02"));
DataObject *third = new DataObject(QString("Firstname03"), QString("Lastname03"));
m_items.append(first);
m_items.append(second);
m_items.append(third);
}
QHash<int, QByteArray> SimpleListModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[/*Roles::*/FIRST_NAME] = "firstName";
roles[/*Roles::*/LAST_NAME] = "lastName";
return roles;
}
void SimpleListModel::addName(QString firstName, QString lastName)
{
DataObject *dataObject = new DataObject(firstName, lastName);
m_items.append(dataObject);
emit dataChanged(this->index(m_items.size()), this->index(m_items.size()));
}
int SimpleListModel::rowCount(const QModelIndex &) const
{
return m_items.size();
}
QVariant SimpleListModel::data(const QModelIndex &index, int role) const
{
//--- Return Null variant if index is invalid
if(!index.isValid())
return QVariant();
//--- Check bounds
if(index.row() > (m_items.size() - 1))
return QVariant();
DataObject *dobj = m_items.at(index.row());
switch (role)
{
case /*Roles::*/FIRST_NAME:
return QVariant::fromValue(dobj->first);
case /*Roles::*/LAST_NAME:
return QVariant::fromValue(dobj->last);
default:
return QVariant();
}
}
AppCore.h
class AppCore : public QObject
{
Q_OBJECT
Q_PROPERTY(SimpleListModel *simpleListModel READ simpleListModel CONSTANT)
public:
explicit AppCore(QObject *parent = 0);
SimpleListModel *simpleListModel() const;
public slots:
void addName();
private:
SimpleListModel *m_SimpleListModel;
};
AppCore.cpp
AppCore::AppCore(QObject *parent) :
QObject(parent)
{
m_SimpleListModel = new SimpleListModel(this);
}
SimpleListModel *AppCore::simpleListModel() const
{
return m_SimpleListModel;
}
void AppCore::addName()
{
m_SimpleListModel->addName("FirstnameNEW", "LastnameNEW");
}
main.cpp
int main(int argc, char *argv[])
{
QGuiApplication a(argc, argv);
QQuickView *view = new QQuickView();
AppCore *appCore = new AppCore();
qRegisterMetaType<SimpleListModel *>("SimpleListModel");
view->engine()->rootContext()->setContextProperty("appCore", appCore);
view->setSource(QUrl::fromLocalFile("main.qml"));
view->show();
return a.exec();
}
main.qml
// ...
ListView {
id: myListView
anchors.fill: parent
delegate: myDelegate
model: appCore.simpleListModel
}
MouseArea {
anchors.fill: parent
onClicked: {
appCore.addName()
console.log('rowCount: ' + appCore.simpleListModel.rowCount())
}
}
//...
you should call beginInsertRows and endInsertRows instead of emitting the signal
void SimpleListModel::addName(QString firstName, QString lastName)
{
DataObject *dataObject = new DataObject(firstName, lastName);
// tell QT what you will be doing
beginInsertRows(ModelIndex(),m_items.size(),m_items.size());
// do it
m_items.append(dataObject);
// tell QT you are done
endInsertRows();
}
these 2 functions emit all needed signals
You're ignoring the semantics of a QAbstractItemModel. There are two kinds of signals that a model must emit:
data change signals: They must be emitted after the data was changed. A data change is a change of value of an existing item. Other changes to the model are not called data changes - the terminology here has a specific meaning.
structure change signals: They must be emitted before and after any structural change. A structural change is addition or removal of any of the items. The beginXxxYyy and endXxxYyy helper functions emit those signals.