I've made myModel using inheritance (QSqlQueryModel) and I
insert column into it. I want insert data of type QString, but I don't know how to do.
QSQlQueryModel is read-only model. To make it read-write, you must subclass it and reimplement setData() and flags().
I want to override these two methods and here is what I've done so far:
dialog.cpp:
this->model = new myModel(this);
model->setQuery("SELECT * lists");
model->insertColumn(3);
model->setHeaderData(3, Qt::Horizontal, QObject::tr("string"));
qDebug() << model->setData(model->index(0,3),"string",Qt::EditRole);
// column add, column set data using setdata()
ui->tvGameList->setModel(model);
myModel.h:
class myModel : public QSqlQueryModel
{
Q_OBJECT
public:
myModel(QObject *parent = 0);
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
bool setData(const QModelIndex &index, const QVariant &value, int role) Q_DECL_OVERRIDE;
};
myModel.cpp:
#include "mymodel.h"
#include <QSqlQuery>
myModel::myModel(QObject *parent): QSqlQueryModel(parent)
{
}
Qt::ItemFlags myModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QSqlQueryModel::flags(index);
if (index.column() == 1 || index.column() == 2)
flags |= Qt::ItemIsEditable;
return flags;
}
bool myModel::setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
{
if (index.isValid() && role == Qt::EditRole)
{
// I don't know here
emit dataChanged(index, index);
return true;
}
return false;
}
See Qt examples, setData must call SQL UPDATE statment as usually SQL.
Qt::ItemFlags EditableSqlModel::flags(
const QModelIndex &index) const
{
Qt::ItemFlags flags = QSqlQueryModel::flags(index);
if (index.column() == 1 || index.column() == 2)
flags |= Qt::ItemIsEditable;
return flags;
}
bool EditableSqlModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
if (index.column() < 1 || index.column() > 2)
return false;
QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);
int id = data(primaryKeyIndex).toInt();
clear();
bool ok;
if (index.column() == 1) {
ok = setFirstName(id, value.toString());
} else {
ok = setLastName(id, value.toString());
}
refresh();
return ok;
}
bool EditableSqlModel::setFirstName(int personId, const QString &firstName)
{
QSqlQuery query;
query.prepare("update person set firstname = ? where id = ?");
query.addBindValue(firstName);
query.addBindValue(personId);
return query.exec();
}
Related
I am trying to add large sets of data in QTableView. As there is huge set of data, in order to avoid freezing the application, I am trying to use a QAbstractTableModel to not showthe whole dataset immediately, but only what is necessary. Based on the QT example for this (HERE), I have the following class:
FileListModel.cpp
#include "filelistmodel.h"
#include <QGuiApplication>
#include <QDir>
#include <QPalette>
#include "qdebug.h"
FileListModel::FileListModel(QObject *parent)
: QAbstractTableModel(parent), fileCount(0) //edit
{}
int FileListModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : fileCount;
}
QVariant FileListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (index.row() >= fileList.size() || index.row() < 0)
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
return fileList.at(index.row());
}
return QVariant();
}
bool FileListModel::canFetchMore(const QModelIndex &parent) const
{
if (parent.isValid())
{
return false;
}
return (fileCount < fileList.size());
}
int FileListModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : colCount;
}
void FileListModel::fetchMore(const QModelIndex &parent)
{
if (parent.isValid())
{
return;
}
int remainder = fileList.size() - fileCount;
int itemsToFetch = qMin(100, remainder);
if (itemsToFetch <= 0)
{
return;
}
beginInsertRows(QModelIndex(), fileCount, fileCount + itemsToFetch - 1);
qDebug()<< "Qmodelindex "<< QModelIndex()<< "filecount "<< fileCount <<"filecount + itemtofetch "<<fileCount + itemsToFetch - 1;
fileCount += itemsToFetch;
endInsertRows();
}
void FileListModel::setColumnNumber(const int x) //edit
{
colCount = x;
}
void FileListModel::setDataToList(const QList<float> &data)
{
beginResetModel();
fileList = data;
fileCount = 0;
endResetModel();
}
FileListModel.h
#ifndef FILELISTMODEL_H
#define FILELISTMODEL_H
#include <QAbstractTableModel>
#include <QStringList>
class FileListModel : public QAbstractTableModel //edit
{
Q_OBJECT
public:
FileListModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override; //edit
void setColumnNumber(const int );
public slots:
void setDataToList(const QList<float>&);
protected:
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
private:
QList<float> fileList;
int fileCount;
int colCount;//edit
};
#endif // FILELISTMODEL_H
In my window, I have a QTableView and 5 QList<float>, each representing a column.
I proceed this way to add the data in my QTableView:
FileListModel *model=new FileListModel(this);
model->setColumnNumber(5); //edit
model->setDataToList(list1);
model->setDataToList(list2);
model->setDataToList(list3);
model->setDataToList(list4);
model->setDataToList(list5);
tableview->setModel(model)
However, the last list added (list5) replace the previous one and I have only the same text in all the columns. I am aware that I need to write the needed code to add columns. But to be honest, I am not knowing very well the QAbstract classes, and have no idea how to proceed. I would be grateful if you could provide me some hints or an example on how to modify my code in order to add columns in my model.
I found how to proceed by storing my data in a QList>.
Please find below a working code that can probably be improved:
CPP FILE
#include "filelistmodel.h"
#include <QGuiApplication>
#include <QDir>
#include <QPalette>
#include "qdebug.h"
FileListModel::FileListModel(QObject *parent)
: QAbstractTableModel(parent), rowNumber(0)
{}
int FileListModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : rowNumber;
}
int FileListModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : colNumber;
}
void FileListModel::setColumnNumber(const int x)
{
colNumber = x;
}
QVariant FileListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (index.row() >= fileList[0].size() || index.row() < 0)
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
return fileList[index.column()][index.row()];
}
return QVariant();
}
bool FileListModel::canFetchMore(const QModelIndex &parent) const
{
if (parent.isValid())
{
return false;
}
return (rowNumber < fileList[0].size());
}
void FileListModel::fetchMore(const QModelIndex &parent)
{
if (parent.isValid())
{
return;
}
int remainder = fileList[0].size() - rowNumber;
int itemsToFetch = qMin(100, remainder);
if (itemsToFetch <= 0)
{
return;
}
beginInsertRows(QModelIndex(), rowNumber, rowNumber + itemsToFetch - 1);
rowNumber += itemsToFetch;
endInsertRows();
}
void FileListModel::setDataToTable(const QList<QList<float>> &data)
{
beginResetModel();
fileList = data;
rowNumber = 0;
endResetModel();
}
H FILE
#ifndef FILELISTMODEL_H
#define FILELISTMODEL_H
#include <QAbstractListModel>
#include <QStringList>
class FileListModel : public QAbstractTableModel
{
Q_OBJECT
public:
FileListModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void setColumnNumber(const int );
void setDataToTable(const QList<QList<float>>&);
protected:
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
private:
QList<QList<float>> fileList;
int rowNumber;
int colNumber;
};
#endif // FILELISTMODEL_H
BUILDING MODEL
FileListModel *pModel =new FileListModel(this);
QList<QList<float>> a;
pModel->setColumnNumber(5);
a.append(qlist1);
a.append(qlist2);
a.append(qlist3);
a.append(qlist4);
a.append(qlist5);
pModel->setDataToTable(a);
I am trying to view a list of c++ models in qml by subclassing QAbstractListModel following this tutorial Models and Views: AbstractItemModel Example
Here is my implementation:
Item model class
class User
{
private:
QString m_macAddr;
QString m_firstName;
QString m_lastName;
public:
User();
User(const QString &mac);
QString macAddr() const;
void setMacAddr(QString &mac);
};
User::User()
{}
User::User(const QString &mac){
m_macAddr = mac;
}
QString User::macAddr() const{
return m_macAddr;
}
void User::setMacAddr(QString &mac){
if(mac == m_macAddr)
return;
m_macAddr = mac;
// emit macAddrChanged();
}
QAbstractListModel subclass
class UserListModel : public QAbstractListModel
{
Q_OBJECT
public:
// User model roles
enum roles{
macRole = Qt::UserRole + 1
};
UserListModel(QObject *parent = nullptr);
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
QModelIndex getIndex(int r, int c, void *p);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void addUser(const User &user);
private:
QList<User> m_users;
protected:
QHash<int, QByteArray> rolesNames() const;
};
// Class inplementation.
UserListModel::UserListModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void UserListModel::addUser(const User &user){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_users << user;
endInsertRows();
}
int UserListModel::rowCount(const QModelIndex &parent) const
{
return m_users.count();
}
QVariant UserListModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= rowCount())
return QVariant();
const User &user = m_users[index.row()];
if(role == macRole){
return user.macAddr();
}
return QVariant();
}
QModelIndex UserListModel::index(int row, int column, const QModelIndex &parent) const
{
return parent;
}
QModelIndex UserListModel::parent(const QModelIndex &index) const
{
return index;
}
QModelIndex UserListModel::getIndex(int r, int c, void *p) {
return this->createIndex(r, c, p);
}
QHash<int, QByteArray> UserListModel::rolesNames() const {
QHash<int, QByteArray> roles;
roles[macRole] = "mac";
return roles;
}
main.cpp
QQmlApplicationEngine engine;
QString mac = "12:3e:3w:4r:33";
userlistModel->addUser(User(mac));
userlistModel->addUser(User(mac));
userlistModel->addUser(User(mac));
userlistModel->addUser(User(mac));
engine.rootContext()->setContextProperty("usersModel", userlistModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QModelIndex id = userlistModel->getIndex(1, 0, 0);
QVariant v1 = userlistModel->data(id, Qt::UserRole + 1);
qDebug() << "===========" << v1.toString();
controller::DatabaseService<SqliteDB> *sqliteDb = new controller::DatabaseService<SqliteDB>();
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
QML
ListView{
id: listView
anchors.fill: parent
model:usersModel
delegate: Component { HorizontalListItem{mac: mac}}
}
The Problem is :
In main function I am adding 4 models to the list model but, They are shown in qml without data. althought, I am trying to view mac role.
what I tried
I tried to add a breakpoint inside methodes rolesNames() and data()
but, It seems that the compiler is not getting inside anyone of them.
I tried to invoke data() method in main function and it returns the desired data.
It is not necessary that you override the index, or parent methods, nor do you create the getIndex method. In your case you need to implement the roleNames method:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QAbstractListModel>
#include <QDebug>
class User
{
QString m_macAddr;
public:
User(){}
User(const QString &mac): m_macAddr(mac){}
QString macAddr() const{return m_macAddr;}
void setMacAddr(QString &mac){m_macAddr = mac;}
};
class UserListModel : public QAbstractListModel
{
public:
enum roles{
macRole = Qt::UserRole + 1
};
explicit UserListModel(QObject *parent = nullptr)
: QAbstractListModel(parent){}
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
if (parent.isValid())
return 0;
return m_users.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
if (!index.isValid())
return QVariant();
if (index.row() < 0 || index.row() >= rowCount())
return QVariant();
const User &user = m_users[index.row()];
if(role == macRole)
return user.macAddr();
return QVariant();
}
QHash<int, QByteArray> roleNames() const override{
QHash<int, QByteArray> roles;
roles[macRole] = "mac";
return roles;
}
void addUser(const User &user){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_users << user;
endInsertRows();
}
private:
QList<User> m_users;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
UserListModel userlistModel;
QString mac = "12:3e:3w:4r:33";
userlistModel.addUser(User(mac));
userlistModel.addUser(User(mac));
userlistModel.addUser(User(mac));
userlistModel.addUser(User(mac));
QModelIndex ix = userlistModel.index(1, 0);
QVariant v = userlistModel.data(ix, UserListModel::macRole);
qDebug() << "===========" << v.toString();
engine.rootContext()->setContextProperty("usersModel", &userlistModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListView{
anchors.fill: parent
model:usersModel
delegate: Component
{
Text{
text: model.mac
}
}
}
}
I have a qt-application with QListView, a custom model and a class inherited from QStyledItemDelegate to show custom items. Unfortunately it takes to long (5 seconds) when I add approximately 6000 items. Do you have a hint/solution for my problem?
Below you see my implementation:
The Model:
class AddressItemData : public QObject {
Q_OBJECT
public:
AddressItemData (QObject * parent = 0) : QObject(parent) {}
QString address;
QString name;
bool inserted;
};
class AddressModel : public QAbstractListModel
{
Q_OBJECT
public:
AddressModel (QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const {
return addressItems.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
if (!index.isValid())
return QVariant();
if (index.row() >= addressItems.size() || index.row() < 0)
return QVariant();
if (Qt::DisplayRole == role) {
return QVariant::fromValue<AddressItemData*>(addressItems.at(index.row()));
}
return QVariant();
}
void addAddressItem(AddressItemData* addressItem){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
addressItems.append(addressItem);
endInsertRows();
}
void addAddressItems(QList<AddressItemData*> addressItems){
beginInsertRows(QModelIndex(), rowCount(), rowCount() + addressItems.size() - 1);
this->addressItems.append(addressItems);
endInsertRows();
}
void removeAddressItem(AddressItemData* addressItem);
void deleteAllAddressItems(void){
beginRemoveRows(QModelIndex(), rowCount(), rowCount());
for (int i = 0; i < addressItems.size(); ++i) {
delete addressItems.at(i);
}
addressItems.clear();
endRemoveRows();
}
private:
QList<AddressItemData*> addressItems;
};
For the model I created a custom add and delete method, which will be called from the delegate.
The ItemViewDelegate:
class AddressViewDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
AddressViewDelegate (QWidget *parent = 0) : QStyledItemDelegate(parent) {}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const {
if(!index.isValid())
return;
painter->save();
QStyleOptionViewItemV4 opt = option;
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
QRect addressRect = opt.rect;
AddressItemData* d = index.data().value<AddressItemData*>();
painter->setPen(Qt::black);
painter->drawText(QRect(addressRect.left(), addressRect.top(), addressRect.width(), addressRect.height()/2),
opt.displayAlignment, d->address);
}
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const {
if(!index.isValid())
return QSize();
QSize result = QStyledItemDelegate::sizeHint(option, index);
result.setHeight(result.height()*2);
return result;
}
};
For test purposes, the application displays only the address. I want to show also the other information next to address in one line. Later I also want to add signal/slot-behavior for each row, but that's still a long way off.
The main:
AddressModel* model = new AddressModel;
AddressViewDelegate* viewDelegate = new AddressViewDelegate;
ui->listView->setItemDelegate(viewDelegate);
ui->listView->setModel(model);
ui->listView->setUniformItemSizes(true);
ui->listView->setLayoutMode(QListView::Batched);
QList<AddressItemData *> addressItems;
... for loop to add AddressItemData:
AddressItemData* data = new AddressItemData;
data->address = anotherTable.at(index);
addressItems.append(data);
// ...
model->addAddressItems(addressItems);
In the main, I call addAddressItems but also with the single-call-variant it is slow. I hope my code is clear enough and I can help others as well.
Do you have an idea, why the adding-procedure is so slow?
I have a classes.
dataitem
datamodel
interface
networkconnector
In dataitem is specified item properties.
class MDataItem : public QObject
{
Q_OBJECT
Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged)
Q_PROPERTY(QString service READ service WRITE setService NOTIFY serviceChanged)
Q_PROPERTY(QString status READ status WRITE setStatus NOTIFY statusChanged)
Q_PROPERTY(QString lastchck READ lastchck WRITE setLastChck NOTIFY lastchckChanged)
Q_PROPERTY(QString duration READ duration WRITE setDuration NOTIFY durationChanged)
Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged)
public:
explicit MDataItem(QObject *parent = 0);
QString host() const;
void setHost(const QString &newhost);
QString service() const;
void setService(const QString &newservice);
QString status() const;
void setStatus(const QString &newstatus);
QString lastchck() const;
void setLastChck(const QString &newlastchck);
QString duration() const;
void setDuration(const QString &newduration);
QString info() const;
void setInfo(const QString &newinfo);
signals:
void hostChanged();
void serviceChanged();
void statusChanged();
void lastchckChanged();
void durationChanged();
void infoChanged();
private:
QString m_host;
QString m_service;
QString m_status;
QString m_lastchck;
QString m_duration;
QString m_info;
};
In model is model specification
class MDataModel : public QAbstractTableModel
{
public:
enum Columns{
Host = Qt::UserRole,
Service = Qt::UserRole + 1,
Status = Qt::UserRole + 2,
Duration = Qt::UserRole + 3,
Info = Qt::UserRole + 4
};
MDataModel(QObject *parent = 0);
QHash<int, QByteArray> roleNames() const;
void fillData(QNetworkReply *r);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
private:
QString dataAt(int offset) const;
QList<MDataItem*> items_;
public:
void prepareDataFinished(QNetworkReply *r);
};
The network get some data from the web.
class MNetworkConnector : public QObject
{
Q_OBJECT
public:
MNetworkConnector(QObject *parent=0);
private:
QNetworkAccessManager *manager;
void getData();
private slots:
void replyFinished(QNetworkReply *r);
void requireAuth(QNetworkReply *r, QAuthenticator *a);
signals:
void dataWasChanged(QNetworkReply *r);
};
and in the interface the network fill data in the model:
class MInterface : public QObject
{
Q_OBJECT
public:
MInterface();
MDataModel *mainModel;
MNetworkConnector *newConnection;
MDataModel* getModel();
public slots:
void dataWasPrepared(QNetworkReply *r);
};
At this point everything is Ok. In the debuger I see that data are in the model, in the correct MDataItem format.
But when I add model to the QML listView - application show nothing.
Where is the problem? Can you push me on the right way?
Main.cpp looks this:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
MInterface *myIface = new MInterface();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("MainModel", myIface->getModel());
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Main qml file:
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQml 2.0
Window {
visible: true
width: Screen.width / 2
height: Screen.height / 2
title: qsTr("webinfo")
ListView
{
id: myList
visible: true
header: Header{}
model: MainModel
delegate: Item
{
Rectangle
{
Text { text: host }
}
}
}
}
When I set debuger breakpoint into my modedata class and in the function which should return the data, debuger not stop there. It looks that program never go inside this function.
Maybe problem is how I set model? Function getmodel() return pointer only.
main.cpp:
engine.rootContext()->setContextProperty("MainModel", myIface->getModel());
minterface.cpp:
MDataModel* MInterface::getModel()
{
return this->mainModel;
}
There is MDataModel.cpp
MDataModel::MDataModel(QObject *parent) : QAbstractTableModel(parent)
{
}
int MDataModel::rowCount(const QModelIndex & /* parent */) const
{
return items_.count();
}
int MDataModel::columnCount(const QModelIndex & /* parent */) const
{
return 5;
}
QVariant MDataModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
switch(index.column()){
case Columns::Host:
return items_.value(index.row())->host();
case Columns::Service:
return items_.value(index.row())->service();
case Columns::Status:
return items_.value(index.row())->status();
case Columns::Duration:
return items_.value(index.row())->duration();
case Columns::Info:
return items_.value(index.row())->info();
}
}
return QVariant();
}
QVariant MDataModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch(section){
case Columns::Host:
return tr("Host");
case Columns::Service:
return tr("Service");
case Columns::Status:
return tr("Status");
case Columns::Duration:
return tr("Duration");
case Columns::Info:
return tr("Info");
}
}
return QVariant();
}
void MDataModel::prepareDataFinished(QNetworkReply *r)
{
QList<MDataItem*> newItems;
MDataItem *item = new MDataItem();
QString data;
while(r->canReadLine())
{
data = r->readLine();
item = new MDataItem();
if(data.contains("warning", Qt::CaseInsensitive))
{
item->setStatus("WARNING");
}
else if(data.contains("critical", Qt::CaseInsensitive))
{
item->setStatus("CRITICAL");
}
else if(data.contains("unknown", Qt::CaseInsensitive))
{
item->setStatus("UNKNOWN");
}
item->setHost(reg.cap(0).replace("host=","").toUpper());
...
...
}
}
items_ = newItems;
qDebug() << items_;
}
QHash<int, QByteArray> MDataModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Columns::Host] = "host";
roles[Columns::Service] = "service";
roles[Columns::Status] = "status";
roles[Columns::Duration] = "duration";
roles[Columns::Info] = "info";
return roles;
}
Your main problems are:
MDataModel inherits QAbstractTableModel instead of QAbstractListModel. This is not an absolute problem since you could use a QAbstractTableModel, but since you are using a ListView in QML you should prefer using QAbstractListModel.
You are mixing roles and columns. This is a consequence of the first point since QAbstractListModel hides the concept of column.
You do not signal the model (and therefore the view/QML) you changed the data. Whenever changes happen to the underlying data you must tell the model by calling functions by pair, depending on what the change is: beginResetModel()/endResetModel(), beginInsertRows()/endInsertRows(), beginRemoveRows()/endRemoveRows(), etc. There are also 2 unpaired signals for that purpose: dataChanged() and modelReset(). Note that you can get away by always calling modelReset(), but this is not a good practice and you should favor using the most sepcific pair of function.
You should also read Qt documentation:
Using C++ Models with Qt Quick Views > QAbstractItemModel subclass
QAbstractItemModel Class > Protected Functions
And here is you fixed model. I removed columnCount() and headerData() as you do not need them to make it work.
MDataModel.h
class MDataModel : public QAbstractListModel
{
public:
enum Role{
Host = Qt::UserRole,
Service,
Status,
Duration,
Info
};
MDataModel(QObject *parent = 0);
QHash<int, QByteArray> roleNames() const;
void fillData(QNetworkReply *r);
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
private:
QString dataAt(int offset) const;
QList<MDataItem*> items_;
public:
void prepareDataFinished(QNetworkReply *r);
};
MDataModel.cpp
MDataModel::MDataModel(QObject *parent) : QAbstractListModel(parent)
{
}
int MDataModel::rowCount(const QModelIndex & /* parent */) const
{
return items_.count();
}
QVariant MDataModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
switch(role){
// For C++ views
case Qt::DisplayRole:
return items_.value(index.row())->host();
// For QML views
case Host:
return items_.value(index.row())->host();
case Service:
return items_.value(index.row())->service();
case Status:
return items_.value(index.row())->status();
case Duration:
return items_.value(index.row())->duration();
case Info:
return items_.value(index.row())->info();
}
return QVariant();
}
void MDataModel::prepareDataFinished(QNetworkReply *r)
{
QList<MDataItem*> newItems;
MDataItem *item = new MDataItem();
QString data;
while(r->canReadLine())
{
data = r->readLine();
item = new MDataItem();
if(data.contains("warning", Qt::CaseInsensitive))
{
item->setStatus("WARNING");
}
else if(data.contains("critical", Qt::CaseInsensitive))
{
item->setStatus("CRITICAL");
}
else if(data.contains("unknown", Qt::CaseInsensitive))
{
item->setStatus("UNKNOWN");
}
item->setHost(reg.cap(0).replace("host=","").toUpper());
...
...
}
}
// Call beginResetModel() and endResetModel() to tell the model
// and the view the data changed.
beginResetModel();
items_ = newItems;
endResetModel();
qDebug() << items_;
}
QHash<int, QByteArray> MDataModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Host] = "host";
roles[Service] = "service";
roles[Status] = "status";
roles[Duration] = "duration";
roles[Info] = "info";
// Add displayRole so that you can get what C++ views displays in QML
// e.g Text { text: host + ' ' + status + ' (' + displayRole +')' }
roles[Qt::DisplayRole] = "displayRole";
return roles;
}
QML fix
delegate: Component
{
Rectangle
{
width: 250
height: textItem.height
Text {
id: textItem
text: host + ' ' + status + ' (' + displayRole +')'
}
}
}
At this moment, my MDataModel.h and MDataModel.cpp looks like this:
MDataModel.h
class MDataModel : public QAbstractListModel
{
public:
enum icingaRoles{
host,
service,
status,
duration,
info
};
MDataModel(QObject *parent = 0);
QHash<int, QByteArray> roleNames() const;
void fillData(QNetworkReply *r);
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
private:
QString dataAt(int offset) const;
QList<MDataItem*> items_;
public:
void prepareDataFinished(QNetworkReply *r);
};
MDataModel.cpp
MDataModel::MDataModel(QObject *parent) : QAbstractListModel(parent)
{
}
int MDataModel::rowCount(const QModelIndex & /* parent */) const
{
return items_.count();
}
QVariant MDataModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role == Qt::DisplayRole)
{
switch(index.column()){
case host:
return items_.value(index.row())->host();
case service:
return items_.value(index.row())->service();
case status:
return items_.value(index.row())->status();
case duration:
return items_.value(index.row())->duration();
case info:
return items_.value(index.row())->info();
}
}
return QVariant();
}
void MDataModel::prepareDataFinished(QNetworkReply *r)
{
QList<MDataItem*> newItems;
MDataItem *item = new MDataItem();
QString data;
while(r->canReadLine())
{
data = r->readLine();
QRegExp reg;
if(data.contains("statusbgwarning'><a href", Qt::CaseInsensitive) || data.contains("statusbgcritical'><a href", Qt::CaseInsensitive) || data.contains("statusbgunknown'><a href", Qt::CaseInsensitive))
{
item = new MDataItem();
newItems.append(item);
if(data.contains("warning", Qt::CaseInsensitive))
{
item->setStatus("WARNING");
}
else if(data.contains("critical", Qt::CaseInsensitive))
{
item->setStatus("CRITICAL");
}
else if(data.contains("unknown", Qt::CaseInsensitive))
{
item->setStatus("UNKNOWN");
}
...
...
}
// Call beginResetModel() and endResetModel() to tell the model
// and the view the data changed.
beginResetModel();
items_ = newItems;
endResetModel();
}
QHash<int, QByteArray> MDataModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[host] = "host";
roles[service] = "service";
roles[status] = "status";
roles[duration] = "duration";
roles[info] = "info";
return roles;
}
I use a QTreeView with a TreeModel (that inherits QAbstractItemModel). The TreeModel::flags() function returns Qt::ItemIsUserCheckable and therefore a checkbox is displayed besides every item in the view.
What I need to do is display the checkbox as disabled when the parent item is not checked but the user must be able to interact with it normally (the checkbox must be enabled).
I tried to implement a custom QStyledItemDelegate and draw the checkbox myself. However I don't know how to use custom images when drawing the checkbox at the draw() function of the delegate. I don't even know if this is the right way to do it.
I also thought of using stylesheets somehow, but the display of the checkbox depends on the model data (if the parent is checked or not).
Any Ideas?
This is my ItemDelegate::paint() function.
void ItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::DisplayRole).toInt();
QStyleOptionButton check_box_style_option;
check_box_style_option.state |= QStyle::State_Enabled;
if (value == 1)
check_box_style_option.state |= QStyle::State_On;
else
check_box_style_option.state |= QStyle::State_Off;
check_box_style_option.rect = option.rect;
QApplication::style()->drawControl(QStyle::CE_CheckBox,
&check_box_style_option, painter);
}
[Edit] TreeModel code. The tree model is based on the "simpletreemodel" example. I changed flags() to also return Qt::ItemIsUserCheckable and data(), setData() to enable the checkboxes to be checked and unchecked.
#include <QtGui>
#include <QDebug>
#include "treeitem.h"
#include "treemodel.h"
TreeModel::TreeModel(Container *data, QObject *parent)
: QAbstractItemModel(parent)
{
root_item_ = new TreeItem("");
SetupModelData(data, root_item_);
}
TreeModel::~TreeModel()
{
delete root_item_;
}
int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
else
return root_item_->columnCount();
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(role == Qt::CheckStateRole && index.column() == 0)
return static_cast<int>(item->checked()) ? Qt::Checked : Qt::Unchecked;
if(role != Qt::DisplayRole)
return QVariant();
return item->data(index.column());
}
bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(role == Qt::CheckStateRole)
item->set_checked(!item->checked());
if(role == Qt::EditRole)
{
qDebug() << "value:" << value.toString();
item->set_data(value.toString());
}
emit dataChanged(index, index);
return true;
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return root_item_->data(section);
return QVariant();
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
TreeItem *parentItem;
if (!parent.isValid())
parentItem = root_item_;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
TreeItem *parentItem = childItem->parent();
if (parentItem == root_item_)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = root_item_;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
return parentItem->childCount();
}
void TreeModel::SetupModelData(Container *data, TreeItem *parent)
{
TreeItem *new_item = new TreeItem(data->id(), parent);
data->set_tree_item(new_item);
parent->appendChild(new_item);
foreach(Container *child, data->children())
SetupModelData(child, new_item);
}