I have model inherited from QAbstractItemModel:
class Plugin: public QObject
{
Q_OBJECT
public:
explicit Plugin(QObject *parent=0);
~Plugin();
const QIcon icon();
void setIcon(const QString &ico);
private:
QIcon mIcon;
};
class PluginsModel: public QAbstractItemModel
{
Q_OBJECT
public:
explicit PluginsModel(QObject *parent=0);
~PluginsModel();
protected:
QList<Plugin*> mList;
};
I want to create QIcon instance only once and in data just do:
QVariant PluginsModel::data(const QModelIndex &index, int role) const
{
if (mList.isEmpty())
return QVariant();
Plugin *it = static_cast<Plugin*>(index.internalPointer());
switch (role ) {
case Qt::DecorationRole:
switch (index.column()) {
case 0:
return it->icon();
break;
default:
break;
}
}
return QVariant();
}
So setIcon function looks like:
const QIcon Plugin::icon()
{
return mIcon;
}
void Plugin::setIcon(const QString &ico)
{
mIcon = QIcon(ico);
}
And usage:
Plugin *p = new Plugin(this);
p->setIcon(":/images/16/amarok.png");
mPlugins->add(p);
The problem is that it doesn't work (icon is not drawed). But if I change setIcon to:
void Plugin::setIcon(QIcon &ico)
{
mIcon = ico;
}
... and usage to:
Plugin *p = new Plugin(this);
QIcon i(":/images/16/amarok.png");
p->setIcon(i);
mPlugins->add(p);
... then it is working fine. Anyone can tell me what is a difference between passing QIcon reference from outside and creating reference inside class from image path argument?
Related
I am trying to pass a c++ user model to qml and get an error that I don't understand.
I use a manager class that should reads in the users and fills the listmodel.
The list model itself should be pass to qml via Q_PROPERTY.
The manager class is known in the qml context.
In the public method of the manager class, the compiler tells me that private m_listModel is a deleted function.
UserManager.h
#pragma once
#include <QObject>
#include <QMap>
#include "UserListModel.h"
class UserManager : public QObject
{
Q_OBJECT
public:
explicit UserManager(QObject* parent = nullptr);
Q_PROPERTY(UserListModel model READ getModel)
UserListModel getModel() { return m_listModel; }
private:
UserListModel m_listModel;
};
UserListModel getModel() { return m_listModel; } gives the error on m_listModel
Error is this
error C2280: "UserListModel::UserListModel(const UserListModel &)" : Es wurde versucht, auf eine gelöschte Funktion zu verweisen
UserManager.cpp
#include "UserManager.h"
UserManager::UserManager(QObject* parent)
{
}
void UserManager::initialize(QMap<QString, QString> args)
{
m_listModel.addModel(UserModel("user1", "0000"));
m_listModel.addModel(UserModel("user2", "9999"));
}
UserListModel.h
#pragma once
#include <QObject>
#include <QAbstractListModel>
struct UserModel {
UserModel() {}
UserModel(const QString name, const QString password) : name(name), password(password) {}
QString name;
QString password;
};
class UserListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit UserListModel(QObject* parent = 0);
enum Roles { NameRole = Qt::UserRole, PasswordRole };
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
void addModel(const UserModel model);
private:
QList<UserModel> m_models;
};
UserListModel.cpp
#include "UserListModel.h"
UserListModel::UserListModel(QObject* parent)
{
}
int UserListModel::rowCount(const QModelIndex& parent) const
{
return m_models.count();
}
QVariant UserListModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid()) return QVariant();
const UserModel user = m_models.at(index.row());
if (role == NameRole) return user.name;
else if (role == PasswordRole) return user.password;
else return QVariant();
}
QHash<int, QByteArray> UserListModel::roleNames() const
{
static QHash<int, QByteArray> mapping{ {NameRole, "name"}, {PasswordRole, "password"} };
return mapping;
}
void UserListModel::addModel(const UserModel model)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_models << model;
endInsertRows();
}
Sorry if it is a beginner's mistake, I'm just starting to learn qt and c++.
I have got a custom structure defined in C++, which contains several variables among them is a QList variable. I can now add a new structure dynamically in Qml listview, but my question is I also want to add a new item into QList inside the structure, I can do this in the background but fail to update the listview.
I think the error appears in the connect() function in C++. As I understand in order to add a new row need to call beginInsertRows() and endInsertRows(), but should be sender and receiver be the same in these two scenarios?
So I have got the following structure in C++ along with some signals and functions used to insert new rows.
"todolist.h":
struct ToDoItem
{
bool done;
QString description;
QList<int> list;
ToDoItem(){
done = false;
description = "text";
QList<int> mylist;
mylist.append(1);
list = mylist;
}
};
signals:
void preItemAppended();
void postItemAppended();
void preListAppended();
void postListAppended();
public slots:
void appendItem();
void appendList();
void ToDoList::appendItem()
{
emit preItemAppended();
mItems.append(ToDoItem());
emit postItemAppended();
}
void ToDoList::appendList()
{
emit preListAppended();
mItems[0].list.append(1);
emit postListAppended();
}
"todomodel.h"
void ToDoModel::setList(ToDoList *list)
{
beginResetModel();
if (mList)
mList->disconnect(this);
mList = list;
if (mList) {
connect(mList, &ToDoList::preItemAppended, this, [=]() {
const int index = mList->items().size();
beginInsertRows(QModelIndex(), index, index);
});
connect(mList, &ToDoList::postItemAppended, this, [=]() {
endInsertRows();
});
connect(mList, &ToDoList::preListAppended, this, [=]() {
const int index = mList->lists().size();
beginInsertRows(QModelIndex(), index, index);
});
connect(mList, &ToDoList::postListAppended, this, [=]() {
endInsertRows();
});
}
endResetModel();
}
The first two connect function works fine which are used to insert a new item but the second two connect functions fail.
It's not a good idea to connect signals to beginInsertRows and endInsertRows.
You should abstract that and rather connect to add or remove methods.
Assume you have your model class which implements QAbstractListModel:
class MyListModel : public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(int size READ size NOTIFY sizeChanged)
public:
explicit myListModel(QObject *parent = nullptr);
~myListModel() override;
int rowCount(const QModelIndex &p) const override;
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
int size() const;
public slots:
void add(QObject *o);
QObject *remove(int i);
signals:
void sizeChanged();
private:
QList<QObject *> m_data;
};
The implementation of add and remove wouldbe as follows:
void MyListModel::add(QObject* o) {
int i = m_data.size();
beginInsertRows(QModelIndex(), i, i);
m_data.append(o);
o->setParent(this);
sizeChanged();
endInsertRows();
}
QObject * MyListModel::take(int i) {
if ((i > -1) && (i < m_data.size())) {
beginRemoveRows(QModelIndex(), i, i);
QObject * o = m_data.takeAt(i);
o->setParent(nullptr);
sizeChanged();
endRemoveRows();
return o;
}
return nullptr;
}
In this case, you can call add on newly added items, or if you're repopulating your list, you can clear it and loop over your list and add them 1 by 1.
ADDING CUSTOM OBJECT EXAMPLE:
class ToDoItem : public QObject {
Q_OBJECT
Q_PROPERTY(bool done READ done WRITE setDone NOTIFY doneChanged)
public:
explicit ToDoItem(QObject* parent = nullptr);
~ToDoItem() override;
bool done() const { return m_done_; }
void setDone(bool done) { m_done_ = done; emit doneChanged(done); }
signals:
void doneChanged(bool);
private:
bool m_done_;
};
I have a very simple class with 2 properties; key and value:
KeyValue.h:
class KeyValue : public QObject
{
Q_OBJECT
Q_PROPERTY(QString key READ getKey WRITE setKey NOTIFY keyChanged)
Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)
public:
KeyValue(const QString& key, const QString& value, QObject* parent = 0);
signals:
void keyChanged();
void valueChanged();
private:
QString _key;
QString _value;
QString getKey() const;
QString getValue() const;
void setKey(const QString& key);
void setValue(const QString& value);
};
Q_DECLARE_METATYPE(KeyValue)
In another class I would like a property containing a list of KeyValue objects, so I can use this list as a model in QML.
Controller.h
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<KeyValue*> items READ getItems NOTIFY itemsChanged)
public:
explicit Controller(QObject* parent = 0);
signals:
void itemsChanged();
private:
QList<KeyValue*> getItems() const;
};
I want to be able to use this in QML the following way:
import QtQuick 2.7
import customqml 1.0
Item{
Controller{
id: controller
}
Repeater{
model: controller.items
Text{
text: modelData.key + ": " + modelData.value
}
}
}
Both classes are registered in my main.cpp file:
qmlRegisterType<KeyValue>("customqml", 1, 0, "KeyValue");
qmlRegisterType<Controller>("customqml", 1, 0, "Controller");
The above code does not work, bacause I apparently can't expose a QList to QML directly. I have tried using QAbstractItemModel and QQmlListProperty, but I was unable to get it to work. Can anyone point me in the right direction?
My primary issues are the type of the items property in the Controller class and the return value of the getItems method.
I'm using Qt 5.9 if that makes any difference.
Note:
The getters and setters are generally public except for exceptions so move it to the public part
The classes that inherit from QObject do not need QMetaType because when you want to transfer data of that class the pointers are used.
Not all data types are supported by QML through Q_PROPERTY, so a possible solution is to export through known classes such as
QList<QObject *>:
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject *> items READ getItems NOTIFY itemsChanged)
public:
explicit Controller(QObject *parent = nullptr);
QList<QObject *> getItems() const;
signals:
void itemsChanged();
private:
QList<KeyValue *>key_values_list;
};
...
QList<QObject *> Controller::getItems() const
{
QObjectList l;
for(auto e: key_values_list)
l << e;
return l;
}
QVariantList:
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList items READ getItems NOTIFY itemsChanged)
public:
explicit Controller(QObject *parent = nullptr);
QVariantList getItems() const;
signals:
void itemsChanged();
private:
QList<KeyValue *>key_values_list;
};
...
QVariantList Controller::getItems() const
{
QVariantList l;
for(auto e: key_values_list)
l.append(QVariant::fromValue(e));
return l;
}
Other options is to implement a model, the following example shows only a read-only model:
keyvaluemodel.h
#ifndef KEYVALUEMODEL_H
#define KEYVALUEMODEL_H
#include "keyvalue.h"
#include <QAbstractListModel>
class KeyValueModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit KeyValueModel(QObject *parent = nullptr)
: QAbstractListModel(parent)
{
key_values_list = {new KeyValue{"k", "v"}, new KeyValue{"k2", "v2"}};
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
if (parent.isValid())
return 0;
return key_values_list.length();
}
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::fromValue(key_values_list[index.row()]);
return QVariant();
}
private:
QList<KeyValue* >key_values_list;
};
#endif // KEYVALUEMODEL_H
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(KeyValueModel* items READ getItems NOTIFY itemsChanged)
public:
explicit Controller(QObject *parent = nullptr);
KeyValueModel* getItems() const;
signals:
void itemsChanged();
private:
KeyValueModel *model;
};
...
Text{
text: display.key + ": " + display.value
}
...
And in a similar way you can implement a QQmlListProperty, in the docs there are many examples.
If you want a sophisticated model with adding/deleting objects and altering data you should look into subclassing QAbstractListModel.
As a simple but less flexible way you can use a QVariantList and make your Controller class a value type. You need:
The macro Q_DECLARE_METATYPE(Controller) at the end of the Controller header file.
A copy constructor for Controller
A default constructor for Controller
Q_PROPERTY Type and return value are then QVariantList.
There is a class named customSortFilterProxyModel inheriting from QSortFilterProxyModel. And one protected function filterAcceptsRow is override.
But, filterAcceptsRow is not called at all. what is the promblem?
thanks.
customSortFilterProxyModel.h
class customSortFilterProxyModel: public QSortFilterProxyModel
{
Q_OBJECT
public:
customSortFilterProxyModel(QObject *parent);
~customSortFilterProxyModel();
protected:
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
};
//customSortFilterProxyModel.cpp
customSortFilterProxyModel::customSortFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
customSortFilterProxyModel::~customSortFilterProxyModel()
{
}
bool customSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
return true;
}
Test code using this proxy model
QStringListModel *newModel = new QStringListModel;
QStringList strList;
strList << "1" << "2" << "3" << "4";
newModel->setStringList(strList);
customSortFilterProxyModel *m_customSortFilterProxyModel = new customSortFilterProxyModel(this);
m_customSortFilterProxyModel->setSourceModel(newModel);
call this function for sort column
m_customSortFilterProxyModel->sort(0);
I force my customSortFilterProxyModel to reload the source model by
setSourceModel. It works. But I am not sure is it the right solution or not?
How can I expose QMap from C++ to QML?
Using QList I can use QDeclarativeListProperty.
You could convert or change it into a QVariantMap. In QML you can use the QVariantMap instance as a normal Javascript object.
class MyClass : public QObject {
Q_OBJECT
public:
Q_INVOKABLE QVariantMap getIntMap() {
QVariantMap rval;
foreach (QString key, m_intMap.keys()) {
// int has an implicit conversion to QVariant
rval[key] = m_intMap[key];
}
return rval;
}
Q_INVOKABLE QVariantMap getObjMap() {
QVariantMap rval;
foreach (QString key, m_objMap.keys()) {
// TODO: make sure all QObject subclasses are exported to QML
rval[key] = QVariant::fromValue<QObject*>(m_objMap[key]);
}
return rval;
}
private:
QMap<QString, int> m_intMap;
QMap<QString, QObject*> m_objMap;
}