PDA

View Full Version : Creating a nested QAbstractListModel containing a QAbstractListModel



volcano
9th March 2016, 09:36
To create a nested QAbstractListModel containing a QAbstractListModel

I have a structure i.e., Menu containing sub menu
I'm creating the ListModel in Qt subclassing the QAbstractListModel

Here's the code



class MenuItem;

class MenuModel : public QAbstractListModel
{
Q_OBJECT
public:
enum MenuRoles {
NameRole = Qt::UserRole + 1
};

MenuModel(QObject *parent = 0);

void addMenu(const MenuItem &menuitem);

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<MenuItem> m_menuItems;
};

class MenuItem
{
public:
explicit MenuItem();
~MenuItem();
void setName(QString name);
private:
QString m_strName;
MenuModel m_subMenuModel;
};


However I get compile time error saying " error: C2248: 'QAbstractListModel::QAbstractListModel' : cannot access private member declared in class 'QAbstractListModel' "

Is it possible to structure this way? or is there a better way?

anda_skoa
9th March 2016, 11:05
MenuModel is a QObject derived class.
QObject has disabled copy constructor and assignment operator, it cannot be copied.
MenuItem holds a MenuModel as a normal member.
It doesn't have a copy constructor or assignment operator so the default generated ones apply.
Default copy constructor and assignment operator do a member-by-member copy.
MenuItem is therefore not copyable as the m_subMenuModel is not copyable.
QList requires that its value type is copyable.

You could use QList<MenuItem*> or a single tree model

Cheers,
_

volcano
9th March 2016, 13:59
Thanks anda_skoa, the advice really helped me.

volcano
10th March 2016, 01:37
One more doubt. I managed to set the submenu and return using

Declaration


MenuModel *subMenu();


Definition


MenuModel* MenuItem::subMenu()
{
return &m_subMenuModel;
}


Now I need to set the role and return using the function


QVariant MenuModel::data(const QModelIndex & index, int role) const
{
if (index.row() < 0 || index.row() >= m_menuItems.count())
return QVariant();

MenuItem *menu = m_menuItems[index.row()];
switch (role) {
case NameRole:
return menu->menuName();
case SubMenuRole:
return menu->subMenu();
default:
return QVariant();
break;
}
return QVariant();
}



But i get the error on line "return menu->subMenu();" saying "error: C2248: 'QVariant::QVariant' : cannot access private member declared in class 'QVariant'"

Kindly advice what i'm missing

volcano
10th March 2016, 13:58
I managed to export the model to qml using the line



MenuModel* MenuModel::subMenuModel(int index)
{
return m_menuItems.at(index)->subMenu();
}


Is this the right way to do so?

anda_skoa
10th March 2016, 15:14
But i get the error on line "return menu->subMenu();" saying "error: C2248: 'QVariant::QVariant' : cannot access private member declared in class 'QVariant'"


maybe you need a


Q_DECLARE_METATYPE(MenuModel*);

in the menu model header.


I managed to export the model to qml using the line



MenuModel* MenuModel::subMenuModel(int index)
{
return m_menuItems.at(index)->subMenu();
}


Is this the right way to do so?

You mean as a Q_INVOKABLE function?
That is also ok, but you need to make sure that the ownership of the pointer is clear.
Either make sure that each model has a parent, or use QQmlEngine::setObjectOwnership() to use the ownership to C++ on the pointer before returning it.

Otherwise QML will delete/garbage collect the model when it doesn't need it anymore and your C++ code might not like that

Cheers,
_

volcano
11th March 2016, 01:44
That is also ok, but you need to make sure that the ownership of the pointer is clear.
Either make sure that each model has a parent, or use QQmlEngine::setObjectOwnership() to use the ownership to C++ on the pointer before returning it.


Could you elaborate more on how to achieve this?

As currently, i'm using the invokable method to return the model. The model is updated when menu model has changed. After some changes the app crashes with the error "Second Chance Assertion Failed: File f:\dd\vctools\crt_bld\self_x86\crt\src\dbgdel.cpp, Line 52".

This is caused by the problem you mentioned


Otherwise QML will delete/garbage collect the model when it doesn't need it anymore and your C++ code might not like that


Can you suggest how to handle this?

anda_skoa
11th March 2016, 10:53
As I said, use QQmlEngine::setObjectOwnership():


MenuModel* MenuModel::subMenuModel(int index)
{
MenuModel *subMenu = m_menuItems.at(index)->subMenu();
QQmlEngine::setObjectOwnership(subMenu, QQmlEngine::CppOwnership);
return subMenu;
}


Cheers,
_

volcano
11th March 2016, 10:56
Thanks anda_skoa for all the advice and help, it really helped me.