this is my code
devicemodel.h
class Device
{
public:
Device(const int &nodeId ,const QString &type, const int &lampVoltage);
//![0]
QString type() const;
int lampVoltage() const;
int nodeId() const;
public:
QString m_type;
int m_lampVoltage;
int m_nodeId;
//![1]
};
class DeviceModel : public QAbstractListModel
{
Q_OBJECT
public:
enum DeviceRoles {
NodeIdRole = Qt::UserRole + 1,
TypeRole ,
LampVoltageRole
};
DeviceModel(QObject *parent = 0);
//![1]
void addDevice(const Device &Device);
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<Device> m_Devices;
//![2]
public slots :
void callFromQml(int index);
};
devicemodel.cpp
Device::Device(const int &nodeId ,const QString &type, const int &lampVoltage)
: m_nodeId(nodeId) , m_type(type), m_lampVoltage(lampVoltage)
{
}
QString Device::type() const
{
return m_type;
}
int Device::nodeId() const
{
return m_nodeId;
}
int Device::lampVoltage() const
{
return m_lampVoltage;
}
DeviceModel::DeviceModel(QObject *parent)
: QAbstractListModel(parent)
{
}
void DeviceModel::callFromQml(int index){
Device k= m_Devices.at(index);
qDebug() << k.type() << k.lampVoltage();
k.m_lampVoltage = 5;
emit dataChanged(createIndex(index,0), createIndex(index,0), {0,1,2} );
}
void DeviceModel::addDevice(const Device &Device)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_Devices << Device;
endInsertRows();
}
int DeviceModel::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_Devices.count();
}
QVariant DeviceModel::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_Devices.count())
return QVariant();
const Device &Device = m_Devices[index.row()];
if (role == NodeIdRole)
return Device.nodeId();
else if (role == TypeRole)
return Device.type();
else if (role == LampVoltageRole)
return Device.lampVoltage();
return QVariant();
}
//![0]
QHash<int, QByteArray> DeviceModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[NodeIdRole] = "nodeId";
roles[TypeRole] = "type";
roles[LampVoltageRole] = "lampVoltage";
return roles;
}
main.qml
ListView {
width: 200; height: 250
spacing: 10
model: myModel
delegate:
Text { text: "Animal: " + type + ", " + lampVoltage
MouseArea {
anchors.fill: parent
onClicked: {
console.log("egwegweg");
myModel.callFromQml(index);
//lampVoltage = 10;
// myModel.setData(index , 10 , 2);
}
}
}
}
main.cpp
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
DeviceModel model;
model.addDevice(Device(1, "Medium" , 200));
model.addDevice(Device(1, "Medium" , 200));
model.addDevice(Device(1, "Medium" , 200));
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("myModel", &model);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
model.addDevice(Device(1, "Medium" , 200));
model.addDevice(Device(1, "Medium" , 200));
model.addDevice(Device(1, "Medium" , 200));
return app.exec();
i want modify my item values from Qml
i wrote a slot named callFromQml and pass index of item from qml to c++ code and want update the values from it
but can not do it
i don not know emit signal works or not and do not know pass index to it correctly or not and don not
Thanks for #MarkCh for pointing out, that you have not reimplemented the setData()-method.
The signature is:
bool QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
It returns true if some data was set successfully, and false if not.
Further, uppon success, before returning, it is necessary to emit dataChanged(...) so any views will be aware of the change.
Let's do it:
Add the appropriate declaration to the header file
Add the implementation to the .cpp-file. This might look like this:
bool QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.row() < 0 || index.row() >= m_Devices.count())
return false;
const Device &Device = m_Devices[index.row()];
if (role == NodeIdRole) {
Device.m_nodeId = value.toInt();
emit dataChanged(index, index);
return true;
}
else if (role == TypeRole) {
Device.m_type = value.toString();
emit dataChanged(index, index);
return true;
}
else if (role == LampVoltageRole) {
Device.m_lampVoltage = value.toInt();
emit dataChanged(index, index);
return true;
}
return false;
}
Make DeviceModel a friend of Device or change the access of the private fields to usage of getters. As you like.
Now you should be able to set the roles in the delegate as if they were properties... Like:
onClicked: model.nodeId = 100;
The code above is not tested, but should include the most relevant details.
Related
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)} }));
// ...
I have a QAbstractItemModel tree model for a Qt3D Entity tree:
qt3dentitytreemodel.h:
#define QT3DENTITYTREEMODEL_H
#include <QAbstractItemModel>
#include <Qt3DCore/QEntity>
class Qt3DEntityTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
Q_PROPERTY(Qt3DCore::QEntity * rootEntity READ rootEntity WRITE setRootEntity NOTIFY rootEntityChanged)
explicit Qt3DEntityTreeModel(QObject *parent = nullptr);
void setRootEntity(Qt3DCore::QEntity *rootEntity);
Qt3DCore::QEntity * rootEntity() const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
signals:
void rootEntityChanged(Qt3DCore::QEntity *rootEntity);
private:
Qt3DCore::QEntity *rootEntity_;
};
#endif // QT3DENTITYTREEMODEL_H
qt3dentitytreemodel.cpp:
#include "qt3dentitytreemodel.h"
Qt3DEntityTreeModel::Qt3DEntityTreeModel(QObject *parent)
: QAbstractItemModel(parent)
{
}
void Qt3DEntityTreeModel::setRootEntity(Qt3DCore::QEntity *rootEntity)
{
qDebug() << "Qt3DEntityTreeModel::setRootEntity" << rootEntity;
auto old = rootEntity_;
rootEntity_ = rootEntity;
if(rootEntity_ != old)
emit rootEntityChanged(rootEntity);
}
Qt3DCore::QEntity * Qt3DEntityTreeModel::rootEntity() const
{
return rootEntity_;
}
QModelIndex Qt3DEntityTreeModel::index(int row, int column, const QModelIndex &parent) const
{
if(!rootEntity_) return {};
if(column != 0) return {};
if(!parent.isValid())
{
if(row == 0) return createIndex(0, 0, rootEntity_);
}
else
{
auto entity = reinterpret_cast<Qt3DCore::QEntity*>(parent.internalPointer());
if(!entity) return {};
return createIndex(row, column, entity->childNodes().at(row));
}
return {};
}
QModelIndex Qt3DEntityTreeModel::parent(const QModelIndex &child) const
{
if(!rootEntity_) return {};
if(!child.isValid()) return {};
auto entity = reinterpret_cast<Qt3DCore::QEntity*>(child.internalPointer());
if(!entity) return {};
auto parent = entity->parentNode();
if(!parent) return {};
auto grandParent = parent->parentNode();
if(!grandParent) return createIndex(0, 0, parent);
return createIndex(grandParent->childNodes().indexOf(parent), 0, parent);
}
QVariant Qt3DEntityTreeModel::data(const QModelIndex &index, int role) const
{
if(!rootEntity_) return {};
if(!index.isValid()) return {};
if(role != Qt::DisplayRole && role != DisplayRole) return {};
auto entity = reinterpret_cast<Qt3DCore::QEntity*>(index.internalPointer());
if(!entity) return {};
QString data = entity->metaObject()->className();
if(!entity->objectName().isEmpty())
data += QString(" \"%1\"").arg(entity->objectName());
return data;
}
QVariant Qt3DEntityTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
return {};
}
int Qt3DEntityTreeModel::rowCount(const QModelIndex &parent) const
{
if(!parent.isValid()) return 1;
auto entity = reinterpret_cast<Qt3DCore::QEntity*>(parent.internalPointer());
if(!entity) return 0;
return entity->childNodes().count();
}
int Qt3DEntityTreeModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
Qt::ItemFlags Qt3DEntityTreeModel::flags(const QModelIndex &index) const
{
return {};
}
which works fine when displayed in a QTreeView (QtWidgets):
auto treeView = new QTreeView();
auto treeModel = new Qt3DEntityTreeModel(treeView);
treeModel->setRootEntity(rootEntity);
treeView->setModel(treeModel);
treeView->resize(640, 480);
treeView->show();
however when used in a QtQuick Controls TreeView, I cannot see the item text, and I cannot even expand the root tree item:
Item {
...
Qt3DEntityTreeModel {
id: entityTreeModel
rootEntity: root
}
TreeView {
model: entityTreeModel
TableViewColumn {
title: "Name"
width: 200
}
}
Scene3D {
id: scene3d
Entity {
id: root
...
}
}
}
I also tried to explicitly define a role in Qt3DEntityTreeModel:
qt3dentitytreemodel.h:
enum Roles {
DisplayRole = Qt::UserRole + 1
};
QHash<int, QByteArray> roleNames() const override;
qt3dentitytreemodel.cpp:
QVariant Qt3DEntityTreeModel::data(const QModelIndex &index, int role) const
{
if(!rootEntity_) return {};
if(!index.isValid()) return {};
if(role != Qt::DisplayRole && role != DisplayRole) return {};
...
}
QHash<int, QByteArray> Qt3DEntityTreeModel::roleNames() const
{
QHash<int, QByteArray> result;
result.insert(DisplayRole, QByteArrayLiteral("display"));
return result;
}
and define the respective role property in the column of the QtQuick TreeView:
TreeView {
model: entityTreeModel
TableViewColumn {
title: "Name"
role: "display"
width: 200
}
}
but still shows nothing.
Is there something else to do to be able to use a custom abstract item model with QtQuick's TreeView?
I think I found the explanation: the model is created before the Qt3D entity tree is "ready".
If I create the model in C++ after the view is loaded, then set the model as a context property, it displays correctly in the QML's TreeView:
QQuickView view;
view.resize(500, 500);
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:/main.qml"));
auto rootObject = view.rootObject();
auto rootEntity = rootObject->findChild<Qt3DCore::QEntity*>("theRootEntity");
qDebug() << "rootEntity:" << rootEntity;
auto treeModel = new Qt3DEntityTreeModel(&app);
treeModel->setRootEntity(rootEntity);
view.rootContext()->setContextProperty("theModel", treeModel);
view.show();
The fundamental problem is that my model doesn't monitor updates to the Qt3D entity tree, and it does not reflect such updates.
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.
I am learning how to integrate qml with c++.
I've implemented a custom model class StringListModel, which inherits QAbstratListModel.
And, I have a main.qml to use StringListModel.
QML view can show initial values correctly.
I have another thread to change model periodically.
I do use beginResetModel() and endResetModel() to indicate model changed.
However, while the model keeps been changed, the view didn't update.
Here is my source code.
Please teach me what went wrong.
THANKS!
=== main.qml ===
Rectangle {
width: 360
height: 360
Grid {
id: gridview
columns: 2
spacing: 20
Repeater {
id: repeater
model: StringListModel {
id:myclass
}
delegate: Text {
text: model.title
}
}
}
=== custom class.h ===
class StringListModel : public QAbstractListModel{
Q_OBJECT
public:
StringListModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QHash<int, QByteArray> roleNames() const;
void newItem();
private:
QStringList stringList;
};
class UpdateThread : public QThread {
Q_OBJECT
StringListModel *mMyClass;
public:
UpdateThread(StringListModel * myClass);
protected:
void run();
};
=== custom class.cpp ===
StringListModel::StringListModel(QObject *parent) : QAbstractListModel(parent)
{
stringList << "one" << "two" << "three";
QThread *thread = new UpdateThread(this);
thread->start();
}
int StringListModel::rowCount(const QModelIndex &parent) const
{
return stringList.count();
}
QVariant StringListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= stringList.size())
return QVariant();
if (role == Qt::UserRole + 1)
return stringList.at(index.row());
else
return QVariant();
}
QHash<int, QByteArray> StringListModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole + 1] = "title";
return roles;
}
void StringListModel::newItem()
{
qDebug() << "size: " << stringList.size();
beginResetModel();
stringList << "new";
endResetModel();
}
UpdateThread::UpdateThread(StringListModel * myClass)
{
mMyClass = myClass;
}
void UpdateThread::run()
{
while (true) {
mMyClass->newItem();
msleep(1000);
}
}
You're dutifully ignoring the matters of synchronizing access to the model. When you have any object that's accessed from more than one thread (even something as "simple" as a raw pointer), you must deal with the ramifications of such access.
I suggest you don't mess with threads unless you have measurements showing that you'll benefit.
this works:
/*Just copy and paste*/
#include <QApplication>
#include <QtGui>
#include <QDebug>
#include <QAbstractProxyModel>
class File_List_Proxy : public QAbstractProxyModel
{
public:
virtual QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const
{
return sourceModel()->index(sourceIndex.row(),sourceIndex.column());
}
virtual QModelIndex mapToSource ( const QModelIndex & proxyIndex ) const
{
return sourceModel()->index(proxyIndex.row(),proxyIndex.column());
}
virtual QModelIndex index(int row, int column, const QModelIndex&) const
{
return createIndex(row,column);
}
virtual QModelIndex parent(const QModelIndex&) const
{
return QModelIndex();
}
virtual int rowCount(const QModelIndex&) const
{
return sourceModel()->rowCount();
}
virtual int columnCount(const QModelIndex&) const
{
return sourceModel()->columnCount();
}
};
class File_List_Model : public QAbstractItemModel
{
private:
QStringList data_;
public:
File_List_Model(/*const QStringList& value*/)//:QStringListModel(value)
{
}
QVariant data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole){
/*QVariant t = data_.at(index.row());
qDebug() << "index.row(): " << index.row();
qDebug() << "data_.at(index.row()): " << data_.at(index.row());*/
return data_.at(index.row());
}
else
{
return QVariant();
}
}
bool set_entries(const QStringList& entries)
{
beginInsertRows(createIndex(0,0),0,entries.count());
data_ = entries;
endInsertRows();
emit dataChanged(createIndex(0,0),createIndex(0,entries.count()));
return true;
}
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole)
{
switch(role)
{
case Qt::DisplayRole:
data_ = value.toStringList();
emit dataChanged(index,index);
return true;
}
return false;
}
virtual QModelIndex index(int row, int column, const QModelIndex&) const
{
return createIndex(row,column);
}
virtual QModelIndex parent(const QModelIndex&) const
{
return QModelIndex();
}
virtual int rowCount(const QModelIndex&) const
{
return data_.size();
}
virtual int columnCount(const QModelIndex&) const
{
return 1;
}
};
int main(int argc,char** argv)
{
QApplication app(argc,argv);
QDir dir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
File_List_Model* model = new File_List_Model;//(dir.entryList());
bool t = model->set_entries(dir.entryList());
File_List_Proxy* proxy = new File_List_Proxy;
proxy->setSourceModel(model);
QListView* view = new QListView;
view->setModel(proxy);
//new ModelTest(model);
view->show();
return app.exec();
}
/*End of copy*/
This on the contrary from a different project where File_List_Model and File_List_Proxy ARE COPIED AND NOT CHANGED from the code above doesn't work:
Line_Counter::Line_Counter(QWidget *parent) :
QDialog(parent), model_(new File_List_Model),
proxy_model_(new File_List_Proxy)
{
setupUi(this);
setup_mvc_();
QDir dir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
File_List_Model* model = new File_List_Model;//(dir.entryList());
bool t = model->set_entries(dir.entryList());
}
void Line_Counter::setup_mvc_()
{
proxy_model_->setSourceModel(model_);
listView->setModel(proxy_model_);
}
//Line counter
class Line_Counter : public QDialog, private Ui::Line_Counter
{
Q_OBJECT
private:
File_List_Model* model_;
//QStringListModel* model_;
File_List_Proxy* proxy_model_;
};
What's going on here?!
You call the setup_mvc_ before the model creation. The model_ in this case it a default constructed model where the set_entries has not been called. On the other hand you call the set_entries on the model which you do not set to a view.
This will work:
Line_Counter::Line_Counter(QWidget *parent) :
QDialog(parent), model_(new File_List_Model),
proxy_model_(new File_List_Proxy)
{
setupUi(this);
QDir dir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
bool t = model_->set_entries(dir.entryList());
setup_mvc_();
}