I have class called List to print a list
class List : public QAbstractListModel
{
Q_OBJECT
Q_ENUMS(Roles)
public:
enum Roles {
address = Qt::UserRole + 1,
name
};
DeviceList(QObject *parent = 0);
void addrows(const Manager &client);
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< Manager > m_client;
};
And another class Manager as
class Manager : public QObject
{
Q_OBJECT
Q_PROPERTY(List* List READ getList CONSTANT)
public:
Manager(const QString &address, const QString &name);
QString address() const;
QString name() const;
virtual List* getList() = 0;
private:
QString m_address;
QString m_name;
};
Now i am trying to addrows in manager.cpp as
void List::addrows(const Manager &client)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_client << client; // **i am getting error here**
endInsertRows();
}
My intention is to implement getlist in manager.cpp file
List* Manager :: getList()
{
List* list = new List();
list->addrows(Manager("street1","John"));
list->addrows(Manager("street2:","Tim"));
list->addrows(Manager("street3","Roberrt"));
return list;
}
In order to use the operator << you have to override the operator = in the class Manager. Instead of doing that use List and handle list elements as pointer.
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'm trying to achieve a filter model that has a source model inside QML. Both of my models are c++ based and registered as modules.
Assuming I have:
ListView {
id: autocomplete
anchors.top: field.bottom
model: MyTreeModelCompleter {
separator: "."
model: MyTreeModel{}
}
}
The c++ of MyTreeModelCompleter:
class TreeModelCompleter : public QCompleter
{
Q_OBJECT
Q_PROPERTY(QString separator READ separator WRITE setSeparator)
Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel)
public:
explicit TreeModelCompleter(QObject *parent = Q_NULLPTR);
explicit TreeModelCompleter(QAbstractItemModel *model, QObject *parent = Q_NULLPTR);
QString separator() const;
QAbstractItemModel* model();
public slots:
void setSeparator(const QString&);
void setModel(QAbstractItemModel*);
protected:
QStringList splitPath(const QString &path) const override;
QString pathFromIndex(const QModelIndex &index) const override;
private:
QString m_sep;
QAbstractItemModel *m_model;
};
MyTreeModel c++:
class MyTreeModel : public QAbstractItemModel
{
Q_OBJECT
...
};
MyTreeModel QML:
MyTreeElement {
property bool check
property string name: "name1"
property string description: "desc of name1"
MyTreeElement {
property bool check
property string name: "name2"
property string description: "desc of name2"
}
MyTreeElement {
property bool check
property string name: "name3"
property string description: "desc of name3"
MyTreeElement {
property bool check
property string name: "name 4"
property string description: "desc of name4"
MyTreeElement {
property bool check
property string name: "name 5"
property string description: "desc of name5"
}
}
}
}
MyTreeelement:
class MyTreeNode : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QQmlListProperty<MyTreeNode> nodes READ nodes)
Q_CLASSINFO("DefaultProperty", "nodes")
MyTreeNode(QObject *parent = Q_NULLPTR);
void setParentNode(MyTreeNode *parent);
Q_INVOKABLE MyTreeNode *parentNode() const;
bool insertNode(MyTreeNode *node, int pos = (-1));
QQmlListProperty<MyTreeNode> nodes();
MyTreeNode *childNode(int index) const;
void clear();
Q_INVOKABLE int pos() const;
Q_INVOKABLE int count() const;
private:
QList<MyTreeNode *> m_nodes;
MyTreeNode *m_parentNode;
};
My main issue is including MyTreeModel inside the completer model MyTreeModelCompleter. The issue is generated when I try to bind them, the compiler complains that the values don't match in types as 1 is QAbstractItemModel* and the other is QAbstractItemModel.
Is there a way to get this model binding to work?
This is by no means a working code, because I think the issue is in the embedding of the Models in one another and not in the actual code.
The error:
Cannot assign object of type "MyTreeModel" to property of type
"QAbstractItemModel*" as the former is neither the same as the latter
nor a sub-class of it.
A way to handle the problem is to use QObject * instead of QAbstractItemModel * for the model property. Here is an example :
class TreeModelCompleter : public QCompleter
{
Q_OBJECT
Q_PROPERTY(QString separator READ separator WRITE setSeparator)
Q_PROPERTY(QObject* model READ model WRITE setModel)
public:
explicit TreeModelCompleter(QObject *parent = Q_NULLPTR);
explicit TreeModelCompleter(QAbstractItemModel *model, QObject *parent = Q_NULLPTR);
QString separator() const;
QObject* model();
public slots:
void setSeparator(const QString&);
void setModel(QObject*);
protected:
QStringList splitPath(const QString &path) const override;
QString pathFromIndex(const QModelIndex &index) const override;
private:
QString m_sep;
QAbstractItemModel *m_model;
};
I have a QT QML project. (still very small)
I started by binding a listview on my UScenario model, by subclassing QAbstractListModel and it worked fined.
Now, each UScenario has a list of UTask, which also have a list of UCondition (so, Utask also subclasses QAbstractListModel). But then, QT Creator gives me an error:
Core/Tasks/utask.h:6: erreur : base class 'QAbstractListModel' has private copy constructor
class UTask: public QAbstractListModel
^
So I'm not sure where is my problem. I tried reading the doc about QAbstractListModel vs QAbstractItemModel, but I have no clue.
I also tried to see if I ever constructed a UTask in a wrong way; I think not.
// USCENARIO.h
#ifndef USCENARIO_H
#define USCENARIO_H
#include <QAbstractListModel>
#include "../Tasks/utask.h"
class UScenario : public QAbstractListModel
{
Q_OBJECT
public slots:
void cppSlot() { // Used to test the insertion from UI
this->addTask(UTask());
}
public:
enum TaskRoles {
IdRole = Qt::UserRole + 1
};
UScenario(QObject *parent = 0);
private:
QList<UTask> m_tasks;
public:
void addTask(const UTask &task);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QHash<int, QByteArray> roleNames() const;
};
#endif // USCENARIO_H
// USCENARIO.CPP
#include "uscenario.h"
UScenario::UScenario(QObject *parent)
: QAbstractListModel(parent)
{
}
void UScenario::addTask(const UTask &task)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_tasks.append(task);
endInsertRows();
}
int UScenario::rowCount(const QModelIndex & parent) const {
return m_tasks.count();
}
QVariant UScenario::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_tasks.count())
return QVariant();
const UTask &task = m_tasks[index.row()];
if (role == IdRole)
return task.id();
return QVariant();
}
QHash<int, QByteArray> UScenario::roleNames() const {
QHash<int, QByteArray> roles;
roles[IdRole] = "id";
return roles;
}
// UTASK.H
#ifndef UTASK_H
#define UTASK_H
#include <QAbstractListModel>
#include "../Conditions/ucondition.h"
class UTask: public QAbstractListModel
{
Q_OBJECT
public:
enum TaskRoles {
typeRole = Qt::UserRole + 1
};
UTask(QObject *parent = 0);//:m_id(0){}
int id() const{return m_id;}
private:
int m_id;
QList<UCondition> m_conditions;
// QAbstractItemModel interface
public:
void addCondition(const UCondition &cond);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QHash<int, QByteArray> roleNames() const;
};
#endif // UTASK_H
// UTASK.cpp
#include "utask.h"
UTask::UTask(QObject *parent):
QAbstractListModel(parent), m_id(0)
{
}
void UTask::addCondition(const UCondition &cond)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_conditions.append(cond);
endInsertRows();
}
int UTask::rowCount(const QModelIndex &parent) const
{
return m_conditions.count();
}
QVariant UTask::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_conditions.count())
return QVariant();
const UCondition &cond = m_conditions[index.row()];
if (role == typeRole)
return cond.type();
return QVariant();
}
QHash<int, QByteArray> UTask::roleNames() const
{
QHash<int, QByteArray> roles;
roles[typeRole] = "type";
return roles;
}
// MAIN
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <qqmlengine.h>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include "../uCtrlCore/Scenario/uscenario.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
UScenario scenarioModel;
scenarioModel.addTask(UTask());
scenarioModel.addTask(UTask());
scenarioModel.addTask(UTask());
QtQuick2ApplicationViewer viewer;
QQmlContext *ctxt = viewer.rootContext();
ctxt->setContextProperty("myScenarioModel", &scenarioModel);
viewer.setMainQmlFile(QStringLiteral("qml/uCtrlDesktopQml/main.qml"));
QObject *item = viewer.rootObject()->findChild<QObject*>("btn");
QObject::connect(item, SIGNAL(qmlSignal()), &scenarioModel, SLOT(cppSlot()));
viewer.showExpanded();
return app.exec();
}
There problem is with how you're storing the UTask objects in your UScenario class
QList<UTask> m_tasks
In simple terms when you call m_tasks.append it is attempting to allocate a new UTask object in the QList by copying the source UTask object via the default copy constructor. In the case of QAbstractListModel it is private. This is why you're at getting the error.
A simply solution is to change the storage type to a list of UTask pointers, QList< UTask* > along with the supporting code to properly release the memory when your UScenario object is destroyed.
For example here are some but not all of the changes but should point you in the right direction. Just make sure to change m_tasks to QList< UTask* > first:
int main(int argc, char *argv[])
{
...
UScenario scenarioModel;
scenarioModel.addTask( new UTask() );
scenarioModel.addTask( new UTask() );
scenarioModel.addTask( new UTask() );
...
return app.exec();
}
void UScenario::cppSlot()
{
// Used to test the insertion from UI
this->addTask( new UTask() );
}
// Change the signature to take a pointer
void UScenario::addTask( UTask* task )
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_tasks.append(task);
endInsertRows();
}
// Make sure you define a destructor for UScenario
UScenario::~UScenario()
{
QList< UTask* >::iterator task = m_tasks.begin();
while( m_tasks.end() != task )
{
// Release the memory associated with the task.
delete (*task);
++task;
}
m_tasks.clear();
}
I just want to display elements from list using QML, but not using item roles.
For ex. I want to call a method getName() that returns the name of the item to be displayed.
Is it possible? I didn't find nothing clear reffering to this.
You can use one special role to return the whole item as you can see below:
template<typename T>
class List : public QAbstractListModel
{
public:
explicit List(const QString &itemRoleName, QObject *parent = 0)
: QAbstractListModel(parent)
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole] = QByteArray(itemRoleName.toAscii());
setRoleNames(roles);
}
void insert(int where, T *item) {
Q_ASSERT(item);
if (!item) return;
// This is very important to prevent items to be garbage collected in JS!!!
QDeclarativeEngine::setObjectOwnership(item, QDeclarativeEngine::CppOwnership);
item->setParent(this);
beginInsertRows(QModelIndex(), where, where);
items_.insert(where, item);
endInsertRows();
}
public: // QAbstractItemModel
int rowCount(const QModelIndex &parent = QModelIndex()) const {
Q_UNUSED(parent);
return items_.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const {
if (index.row() < 0 || index.row() >= items_.count()) {
return QVariant();
}
if (Qt::UserRole == role) {
QObject *item = items_[index.row()];
return QVariant::fromValue(item);
}
return QVariant();
}
protected:
QList<T*> items_;
};
Do not forget to use QDeclarativeEngine::setObjectOwnership in all your insert methods. Otherwise all your objects returned from data method will be garbage collected on QML side.
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"