Qt - Replacing a row in the QAbstractItemModel (Tied to a QTableView) - qt

I had a problem with my QTableView showing an extra empty row whenever I'd change data in a row using :
beginInsertRows(QModelIndex(), rowCount(), rowCount());
listOfObjects.replace(i, *object);
endInsertRows();
I changed it to this, which works but seems very hacky.
beginRemoveRows(QModelIndex(), rowCount(), rowCount());
beginInsertRows(QModelIndex(), rowCount(), rowCount());
listOfObjects.replace(i, *object);
endInsertRows();
endRemoveRows();
Is there a better way to accomplish this?
Thanks

Here's a simple exmaple.
Let's create a custom table model:
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit MyModel(int rows, int cols, QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
void updateRow(int row);
private:
int m_rows, m_cols;
QMap<int, QMap<int, QString> > updatedItems;
};
It has an updateRow function that change items text from 'clean' to 'update' and a QMap of updated items.
MyModel::MyModel(int rows, int cols, QObject *parent) :
QAbstractTableModel(parent),
m_rows(rows),
m_cols(cols)
{
}
int MyModel::rowCount(const QModelIndex &parent) const
{
return m_rows;
}
int MyModel::columnCount(const QModelIndex &parent) const
{
return m_cols;
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
QVariant res;
if (role == Qt::DisplayRole)
{
int row = index.row();
int col = index.column();
QString text;
if (updatedItems.contains(row))
{
QMap<int, QString> colData = updatedItems.value(row);
if (colData.contains(col))
{
text = colData.value(col);
}
}
if (text.isEmpty())
{
text = QString("clean %1 - %2").arg(row).arg(col);
}
res = text;
}
return res;
}
void
MyModel::updateRow(int row)
{
if (updatedItems.contains(row) == false)
{
updatedItems[row] = QMap<int, QString>();
}
for (int col = 0; col < m_cols; ++col)
{
QString text = QString("update %1 - %2").arg(row).arg(col);
updatedItems[row].insert(col, text);
}
QModelIndex index1 = index(row, 0);
QModelIndex index2 = index(row, m_cols - 1);
emit dataChanged(index1, index2);
}
Check how the dataChanged signal is emitted from the last function.
And here is how you can use that model:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
QPushButton *btn = new QPushButton("update");
view = new QTableView;
model = new MyModel(4, 5, view);
view->setModel(model);
layout->addWidget(btn);
layout->addWidget(view);
connect(btn, SIGNAL(clicked()), this, SLOT(updateRowData()));
resize(400, 300);
}
void Widget::updateRowData()
{
QModelIndex index = view->currentIndex();
if (index.isValid())
{
int row = index.row();
model->updateRow(row);
}
}

Related

qcombobox do not display current text when choose item

I have a QComboBox connect to model and display list of text item from model, but when I choose one of its item, QComboBox display item text, but when I click on other widget (QComboBox lost focus), current text become empty
cboProfiles->setModel(QComboBoxModel);
cboProfiles->setModelColumn(0);
cboProfiles->setEditable(true);
follow is my model QComboBoxModel.h
#ifndef QCOMBOBOXMODEL_H
#define QCOMBOBOXMODEL_H
#include <QModelIndex>
class QComboBoxModel : public QAbstractListModel
{
public:
QComboBoxModel(QObject *parent=nullptr);
int rowCount(const QModelIndex &) const;
QVariant data(const QModelIndex &index, int role) const;
void populate(const QList<QPair<int,QString>> &values);
void append(int index, QString value);
private:
QList<QPair<int,QString>> values;
};
#endif // QCOMBOBOXMODEL_H
here is QComboBoxModel.cpp
#include "qcomboboxmodel.h"
#include <QModelIndex>
QComboBoxModel::QComboBoxModel(QObject *parent)
:QAbstractListModel(parent)
{
}
int QComboBoxModel::rowCount(const QModelIndex &) const
{
return values.count();
}
QVariant QComboBoxModel::data( const QModelIndex &index, int role ) const
{
QVariant value;
switch ( role )
{
case Qt::DisplayRole: //string
{
value = this->values.value(index.row()).second;
}
break;
case Qt::UserRole: //data
{
value = this->values.value(index.row()).first;
}
break;
default:
break;
}
return value;
}
void QComboBoxModel::populate(const QList<QPair<int,QString>> &values)
{
this->values = values;
}
void QComboBoxModel::append(int index, QString value)
{
int newRow = this->values.count();
this->beginInsertRows(QModelIndex(), newRow, newRow);
values.append(QPair<int, QString>(index, value));
endInsertRows();
}
here is my use code:
QComboBox* combobox = new QComboBox;
QList<QPair<int, QString>> values;
values.append(QPair<int, QString>(-1, "Select item"));
values.append(QPair<int, QString>(10, "item1(0)"));
values.append(QPair<int, QString>(11, "item1(1)"));
values.append(QPair<int, QString>(21, "item1(2)"));
values.append(QPair<int, QString>(32, "item1(3)"));
values.append(QPair<int, QString>(44, "item1(4)"));
QComboBoxModel *model = new QComboBoxModel();
model->populate(values);
combobox->setModel(model);
combobox->setEditable(true);

Creating listview from QQuickItem in C++

I am trying to make a listview component using QQuickItem and load its model using QAbstractListModel. Below are the steps which i tried.
listviewComponent.qml
ListView {
required model
delegate: Text {
required property string type
required property string size
text: type + ", " + size
}
}
Model.h
class Model
{
public:
Model(const QString& type, const QString& size);
QString type() const;
QString size() const;
private:
QString m_type;
QString m_size;
};
class CustomModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Roles {
TypeRole = Qt::UserRole + 1,
SizeRole
};
CustomModel(QObject* parent = 0);
void addElement(const Model& pElement);
int rowCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<Model> m_list;
};
Model.cpp
Model::Model(const QString& type, const QString& size)
: m_type(type), m_size(size)
{
}
QString Model::type() const
{
return m_type;
}
QString Model::size() const
{
return m_size;
}
CustomModel::CustomModel(QObject* parent)
: QAbstractListModel(parent)
{
}
void CustomModel::addElement(const Model &pElement)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_list << pElement;
endInsertRows();
}
int CustomModel::rowCount(const QModelIndex& parent) const {
Q_UNUSED(parent);
return m_list.count();
}
QVariant CustomModel::data(const QModelIndex& index, int role) const {
if (index.row() < 0 || index.row() >= m_list.count())
return QVariant();
const Model& mod = m_list[index.row()];
if (role == TypeRole)
return mod.type();
else if (role == SizeRole)
return mod.size();
return QVariant();
}
QHash<int, QByteArray> CustomModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[TypeRole] = "type";
roles[SizeRole] = "size";
return roles;
}
Myclass.h
class myclass : public QObject
{
Q_OBJECT
public:
myclass();
inline int createUI(QQmlApplicationEngine &engine){
QQuickWindow *window = qobject_cast<QQuickWindow*>(engine.rootObjects().at(0));
if (!window) {
qFatal("Error: Your root item has to be a window.");
return -1;
}
QQuickItem *root = window->contentItem();
window->setWidth(600);
window->setHeight(500);
QQmlComponent listviewComp(&engine, QUrl(QStringLiteral("qrc:/listviewComponent.qml")));
CustomModel model;
model.addElement(Model("Wolf", "Medium"));
model.addElement(Model("Polar bear", "Large"));
model.addElement(Model("Quoll", "Small"));
QQuickItem *listview = qobject_cast<QQuickItem*>(listviewComp.createWithInitialProperties({ {"model", QVariant::fromValue(&model)} }));
QQmlEngine::setObjectOwnership(listview, QQmlEngine::CppOwnership);
listview->setParentItem(root);
listview->setParent(&engine);
listview->setProperty("objectName", "lv");
listview->setWidth(200);
listview->setHeight(100);
listview->setX(250);
listview->setY(30);
window->show();
return 0;
}
};
main.cpp
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.load(url);
QQmlContext *item = engine.rootContext();
myclass myClass;
item->setContextProperty("_myClass", &myClass);
myClass.createUI(engine);
return app.exec();
}
Problem: Listview is getting displayed but with only one row, but there are 3 rows added in CustomModel. I am assuming some problem with createWithInitialProperties, but couldnt able to crack it.
The problem is caused because "model" is a local variable that will be destroyed as soon as createUI is finished executing, and what you see as the first row is just a cache, you shouldn't actually see a row (it looks like a bug). The solution is to use the heap memory:
// ...
CustomModel *model = new CustomModel(window);
model->addElement(Model("Wolf", "Medium"));
model->addElement(Model("Polar bear", "Large"));
model->addElement(Model("Quoll", "Small"));
QQuickItem *listview = qobject_cast<QQuickItem*>(listviewComp.createWithInitialProperties({ {"model", QVariant::fromValue(model)} }));
// ...

when deleting Tree item destructor not being called

when i am deleting Treeitem from the the tree model the destructor for the tree item is not being called.
This is my code for deleting the tree item from the model.
void TreeModel::removeItem(TreeItem *node)
{
const int row = node->row();
QModelIndex idx = createIndex(row, 0, node);
TreeItem* itm = getItem(idx);
beginRemoveRows(idx.parent(), row, row);
node->parent()->removeChild(row);
endRemoveRows();
}
The code for Treeitem RemoveChild.
void TreeItem::removeChild(int row)
{
childItems.removeAt(row);
}
The code for tree item header file.
#include <QList>
#include <QVariant>
#include <QVector>
#include "Container.h"
class TreeItem
{
public:
explicit TreeItem( Container *data , TreeItem *parent = 0 );
~TreeItem();
TreeItem *parent();
void appendChild(TreeItem *child);
TreeItem *child(int iNumber);
int childCount() const;
int childNumber() const;
Container data() const ;
Container* GetContainer();
bool setData(Container* data , QVariant value);
void setContainer( Container* data);
bool insertChildren(int position, int count );
bool removeChildren( int position , int count );
void removeChild(int row);
void removeChild(TreeItem* itm);
std::string getChildName(int row);
std::string getName();
int row() const;
void insertChild(int pos, TreeItem *child);
private:
QList<TreeItem*> childItems;
Container* itemData;
TreeItem* parentItem;
};
The code for the tree item Cpp file.
/////////////////////////////////////////////
//////////////////////////////////////////////////////
TreeItem::TreeItem( Container *data, TreeItem *parent )
{
parentItem = parent;
itemData = new Container;
*itemData = *data;
}
TreeItem::~TreeItem()
{
qDebug() << itemData->GetName().c_str();
if (itemData != nullptr)
{
delete itemData;
qDebug() << "deleting Item Data";
}
qDeleteAll(childItems);
}
TreeItem *TreeItem::parent()
{
return parentItem;
}
TreeItem *TreeItem::child(int iNumber)
{
return childItems.value(iNumber);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*> (this));
return 0;
}
Container TreeItem::data() const
{
return *itemData;
}
bool TreeItem::setData( Container* data , QVariant value )
{
//*itemData = *data; // Do Not !!!! uncomment this as it will set the
value of default container constructor.
itemData->SetName(value.toString().toStdString() );
return true;
}
bool TreeItem::insertChildren(int position, int count)
{
if (position < 0 || position > childItems.count())
return false;
Container cont;
TreeItem *item = new TreeItem(&cont, this);
childItems.insert(position, item);
return true;
}
bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position > childItems.count())
return false;
for (int row = 0; row < count; ++row)
{
delete childItems.takeAt(position);
}
return true;
}
void TreeItem::setContainer( Container* cont)
{
*itemData = *cont;
}
void TreeItem::appendChild(TreeItem *node)
{
childItems.append( node );
}
int TreeItem::row() const
{
if (parentItem)
return parentItem->childItems.indexOf( const_cast<TreeItem*>(this) );
return 0;
}
void TreeItem::removeChild(int row)
{
childItems.removeAt(row);
}
void TreeItem::insertChild(int pos, TreeItem *child)
{
childItems.insert(pos, child);
child->parentItem = this;
}
void TreeItem::removeChild(TreeItem* itm)
{
childItems.removeOne(itm);
}
std::string TreeItem::getChildName(int row)
{
return childItems.value(row)->getName();
}
std::string TreeItem::getName()
{
return itemData->GetName();
}
Container* TreeItem::GetContainer()
{
return itemData;
}
The Header file for the TreeModel Class///////////////////////////////////
#pragma once
#include <QAbstractItemModel>
#include <QString>
#include <QMimedata.h>
#include <Qdatastream.h>
class TreeItem;
class Container;
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
TreeModel(const QString &header, Container *data, QObject *parent = 0);
~TreeModel();
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role = Qt::EditRole) override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &parent) const override;
bool insertRows(int position, int rows, const QModelIndex &parent);
// bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Container* GetContainer(const QModelIndex &index);
void SetContainer(const QModelIndex &index, Container* cont);
////////////////////// Drag And Drop Actions ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Qt::DropActions supportedDropActions() const override;
Qt::DropActions supportedDragActions() const override;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
void setupModelData(const QStringList &lines, TreeItem *parent);
void removeItem(TreeItem *item);
bool FindChild(std::string stdstrChildName);
TreeItem *getItem(const QModelIndex &index) const;
TreeItem *getRoot();
private:
//void setupModelData(const Container &cont, TreeItem *parent);
TreeItem *rootItem;
};
The Cpp file for the TreeModel
#include "TreeModel.h"
#include "TreeItem.h"
#include <qcoreapplication.h>
#include <qdebug.h>
#include "Container.h"
TreeItem *TreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (item)
return item;
}
return rootItem;
}
TreeModel::TreeModel(const QString &header, Container *data, QObject
*parent) : QAbstractItemModel(parent)
{
qDebug() << "First level done";
rootItem = new TreeItem( data);
}
TreeModel::~TreeModel()
{
delete rootItem;
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole && role != Qt::EditRole)
return QVariant();
TreeItem *item = getItem(index);
return QString::fromStdString(item->data().GetName());
//return QVariant::fromValue(item->data());
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return QVariant::fromValue(rootItem->data());
return QVariant();
}
bool TreeModel::setData(const QModelIndex &index, const QVariant &val, int
role)
{
if (role != Qt::EditRole)
return false;
Container c = val.value<Container>();
TreeItem *item = getItem(index);
bool result = true;
item->setData(&c, val);
if (result)
emit dataChanged(index, index, { role });
return result;
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
TreeItem *parentItem = getItem(parent);
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 = getItem(index);
TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
bool TreeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &val, int role)
{
if (role != Qt::EditRole || orientation != Qt::Horizontal)
return false;
Container c = val.value<Container>();
bool result = rootItem->setData(&c, val);
if (result)
emit headerDataChanged(orientation, section, section);
return result;
}
bool TreeModel::insertRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success;
beginInsertRows(parent, position, position + rows - 1);
success = parentItem->insertChildren(position, rows);
endInsertRows();
return success;
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsDropEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 1;
TreeItem *parentItem = getItem(parent);
//qDebug() << "the child count = " << parentItem->childCount() << parentItem->data().GetName().c_str();
return parentItem->childCount();
}
int TreeModel::columnCount(const QModelIndex & /* parent */) const
{
return 1;
}
Container* TreeModel::GetContainer(const QModelIndex &index)
{
TreeItem *item = getItem(index);
return item->GetContainer();
}
void TreeModel::SetContainer(const QModelIndex &index, Container* Cont)
{
TreeItem *item = getItem(index);
item->setContainer(Cont);
}
static const char s_treeNodeMimeType[] = "application/x-treenode";
QStringList TreeModel::mimeTypes() const
{
return QStringList() << s_treeNodeMimeType;
}
QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData;
QByteArray data; //a kind of RAW format for datas
//QDataStream is independant on the OS or proc architecture
//serialization of C++'s basic data types, like char, short, int, char *, etc.
//Serialization of more complex data is accomplished
//by breaking up the data into primitive units.
QDataStream stream(&data, QIODevice::WriteOnly);
QList<TreeItem *> nodes;
//
foreach(const QModelIndex &index, indexes) {
TreeItem *node = getItem(index);
if (!nodes.contains(node))
nodes << node;
}
stream << QCoreApplication::applicationPid();
stream << nodes.count();
foreach(TreeItem *node, nodes) {
stream << reinterpret_cast<qlonglong>(node);
}
mimeData->setData(s_treeNodeMimeType, data);
return mimeData;
}
bool TreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction
action, int row, int column, const QModelIndex &parent)
{
//Q_ASSERT(action == Qt::MoveAction);
//Q_UNUSED(column);
//test if the data type is the good one
if (!mimeData->hasFormat(s_treeNodeMimeType)) {
return false;
}
QByteArray data = mimeData->data(s_treeNodeMimeType);
QDataStream stream(&data, QIODevice::ReadOnly);
qint64 senderPid;
stream >> senderPid;
if (senderPid != QCoreApplication::applicationPid()) {
// Let's not cast pointers that come from another process...
return false;
}
TreeItem *parentNode = getItem(parent);
// Q_ASSERT(parentNode);
int count;
stream >> count;
if (row == -1) {
// valid index means: drop onto item. I chose that this should insert
// a child item, because this is the only way to create the first child
of an item...
// This explains why Qt calls it parent: unless you just support
replacing, this
// is really the future parent of the dropped items.
if (parent.isValid())
row = 0;
else
// invalid index means: append at bottom, after last toplevel
row = rowCount(parent);
}
//qDebug() << "The row" << row << parentNode->data().GetName().c_str() ;
for (int i = 0; i < count; ++i) {
// Decode data from the QMimeData
qlonglong nodePtr;
stream >> nodePtr;
TreeItem *node = reinterpret_cast<TreeItem *>(nodePtr);
// Adjust destination row for the case of moving an item
// within the same parent, to a position further down.
// Its own removal will reduce the final row number by one.
if (node->row() < row && parentNode == node->parent())
--row;
// Remove from old position
// qDebug() << "The remove item " << node->data().GetName().c_str();
removeItem(node);
// Insert at new position
//qDebug() << "Inserting into" << parent << row;
beginInsertRows(parent, row, row);
parentNode->insertChild(row, node);
endInsertRows();
++row;
}
return true;
}
void TreeModel::removeItem(TreeItem *node)
{
const int row = node->row();
QModelIndex idx = createIndex(row, 0, node);
TreeItem* itm = getItem(idx);
beginRemoveRows(idx.parent(), row, row);
node->parent()->removeChild(row);
endRemoveRows();
}
Qt::DropActions TreeModel::supportedDropActions() const
{
return Qt::MoveAction;
}
Qt::DropActions TreeModel::supportedDragActions() const
{
return Qt::MoveAction;
}
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
{
QList<TreeItem*> parents;
QList<int> indentations;
parents << parent;
indentations << 0;
int number = 0;
while (number < lines.count()) {
int position = 0;
while (position < lines[number].length()) {
if (lines[number].mid(position, 1) != " ")
break;
position++;
}
QString lineData = lines[number].mid(position).trimmed();
if (!lineData.isEmpty()) {
// Read the column data from the rest of the line.
QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
QList<QVariant> columnData;
for (int column = 0; column < columnStrings.count(); ++column)
columnData << columnStrings[column];
if (position > indentations.last()) {
// The last child of the current parent is now the new parent
// unless the current parent has no children.
if (parents.last()->childCount() > 0) {
parents << parents.last()->child(parents.last()->childCount() - 1);
indentations << position;
}
}
else {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}
Container c;
// Append a new node to the current parent's list of children.
parents.last()->appendChild(new TreeItem(&c, parents.last()));
}
++number;
}
}
TreeItem *TreeModel::getRoot()
{
return rootItem;
}
childItems.removeOne(itm); and childItems.removeAt(row); just remove TreeItem * from your QList<TreeItem*> childItems; so memory is not freed.
You should explicitly call delete, once they are removed as you did with delete childItems.takeAt(position); for example.

Subclassing QAbstractTableModel

I have subclassed a QAbstractTableModel to represent data from a QMap. This QMap has QLists of QSqlRecords and this map is modified by some other part of my code. I want to use this model with a QTableView to display the sql records in this map for each key. Here is my code.
//mymodel.h
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
MyModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
void setRecordMap(QMap<int, QList<QSqlRecord>> *map);
void setSelectedSerMsgIndex(QModelIndex *index);
private:
QMap<int, QList<QSqlRecord>> *recordMap;
QModelIndex *selectedSerendibMsgIndex;
};
//mymodel.cpp
MyModel::MyModel(QObject *parent) : QAbstractTableModel(parent)
{
}
int MyModel::rowCount(const QModelIndex &parent) const
{
if(recordMap->isEmpty())
return 0;
int row = selectedSerendibMsgIndex->row();
return recordMap->value(row).size();
}
int MyModel::columnCount(const QModelIndex &parent) const
{
if(recordMap->isEmpty())
return 0;
int row = selectedSerendibMsgIndex->row();
return recordMap->value(row).at(0).count();
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if(recordMap->isEmpty())
return QVariant();
if (!index.isValid())
return QVariant();
int row = selectedSerendibMsgIndex->row();
if (index.row() >= recordMap->value(row).size())
return QVariant();
if (role == Qt::DisplayRole)
{
return recordMap->value(row).value(index.row()).value(index.column()); /* QVariant("hello");*/
}
else
{
return QVariant();
}
}
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
recordMap = map;
}
void MyModel::setSelectedSerMsgIndex(QModelIndex *index)
{
selectedSerendibMsgIndex = index;
}
Sorry for the huge post. But the problem is, I cannot see the data from the map. I am guessing it is because there's something wrong with my implementation of the data() method. But I can't figure out what it is. Please be kind enough to help me. Thank you.
try changing this:
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
recordMap = map;
}
to this:
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
beginResetModel();
recordMap = map;
endResetModel();
}

Qt Model View for a given populated ro QList

i got a godgiven list of xyz (the code says int, just an example) glued into a QList (to big to move anywhere). How can I create a Model View for that? I allready read the Qt doc which tells me, I have to reimplement data, index, parent, rowCount, columnCount functions. But the preprocessor/compiler cries for more reimplemented functions? I allready read a chapter in my Qt Book but it did not help either. Here my hacked away code:
class XModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit XModel(QList<int> *valuelist, QObject *parent = 0);
virtual ~XModel();
int rowCount(const QModelIndex &) const;
int columnCount(const QModelIndex &) const;
QModelIndex index( int row, int column, const QModelIndex & parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
private:
QList<int>* blah;
signals:
public slots:
};
XModel::XModel(QList<int> *valuelist, QObject *parent) :
QAbstractListModel(parent),
blah(valuelist)
{
}
XModel::~XModel()
{
}
int XModel::rowCount(const QModelIndex &) const
{
return blah->size();
}
int XModel::columnCount(const QModelIndex &) const
{
return 1;
}
QModelIndex XModel::index(int row, int column, const QModelIndex &parent) const
{
return createIndex(row, column, (void)&(blah->at(row)));
}
QModelIndex XModel::parent(const QModelIndex &index) const
{
return createIndex(index->row(), index->column(), NULL);
}
QVariant XModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const
{
return QVariant(blah->at(index.row()));
}
Do I even have to use QAbstractItemModel or does QAbstractListModel work the exact same way? How do I give the Model the source of the actual data? Is this only within data function? Please tell me what I am doing wrong, I do not see it and give advice on howto do it properly, (good) howtos welcome.
This is fixed, but...
EDIT:
Widget::Widget(QWidget *parent)
: QWidget(parent),
valuelist(),
xm(&valuelist) //xm = XModel
{
valuelist.append(1);
valuelist.append(2);
valuelist.append(3);
valuelist.append(4);
valuelist.append(5);
valuelist.append(6);
valuelist.append(7);
valuelist.append(8);
valuelist.append(9);
view = new QListView(this);
view->setModel(&xm);
//how to force the XModel to reread the QList`?
view->show();
}
Add to and remove data from XModel and have XModel modify the underlying list (reference?) for you:
Widget::Widget(QWidget *parent)
: QWidget(parent),
valuelist(),
xm(&valuelist) //xm = XModel
{
xm.append(1);
xm.append(2);
xm.append(3);
xm.append(4);
xm.append(5);
xm.append(6);
xm.append(7);
xm.append(8);
xm.append(9);
view = new QListView(this);
view->setModel(&xm);
xm.append(10); // should call beginInsertRows, append to valuelist, and call endInsertRows.
Q_ASSERT(valuelist.contains(10));
view->show();
}
Otherwise, you could perhaps create a mix of QObject and QList that can emit signals to notify XModel of changes, but I think the first approach is better.
Edit:
Here is a silly example. It will create a list-backed model that will append more elements to the list every second:
#include <QtGui/QApplication>
#include <QtGui/QListView>
#include <QtCore/QAbstractListModel>
#include <QtCore/QTimer>
class ListBackedModel : public QAbstractListModel
{
Q_OBJECT
QList<int>* m_list;
public:
ListBackedModel(QList<int>* list, QObject* parent = 0)
: QAbstractListModel(parent)
, m_list(list)
{}
~ListBackedModel()
{}
int rowCount(const QModelIndex &parent = QModelIndex()) const
{
Q_UNUSED(parent);
return m_list->size();
}
QVariant data(const QModelIndex &index, int role) const
{
if (index.row() >= rowCount()) { return QVariant(); }
if (index.row() < 0) { return QVariant(); }
int element = m_list->at(index.row());
if (Qt::DisplayRole == role) {
return QString::number(element);
}
if (Qt::ToolTipRole == role) {
return tr("%1 is element #%2").arg(element).arg(index.row() + 1);
}
return QVariant();
}
void append(int element)
{
/*
First is the new index of the first element that will be inserted.
Last is the new index of the last element that will be inserted.
Since we're appending only one element at the end of the list, the
index of the first and last elements is the same, and is equal to
the current size of the list.
*/
int first = m_list->size();
int last = first;
beginInsertRows(QModelIndex(), first, last);
m_list->append(element);
endInsertRows();
}
void startAddingMoreElements()
{
QTimer::singleShot(1000, this, SLOT(addMoreElements()));
}
private slots:
void addMoreElements()
{
append(qrand() % 100);
startAddingMoreElements();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QList<int> list;
list << 1 << 10 << 34 << 111;
ListBackedModel model(&list);
QListView listView;
listView.setModel(&model);
listView.show();
model.startAddingMoreElements();
return a.exec();
}
#include "main.moc"

Resources