QStandardItemModel & binding to custom object

╄→尐↘猪︶ㄣ 提交于 2020-01-24 20:49:06

问题


I tried to use the cities-standarditem of the Qt exemple and adapt it to my exemple. I have a strange result:

Here is my User class:

class User{
public:
User();

QString getFirstname() const;
void setFirstname(const QString &value);

QString getLastname() const;
void setLastname(const QString &value);

int getAge() const;
void setAge(int value);

private:
QString firstname;
QString lastname;
int age;
};

and i have declared a usermodel.h:

class UserModel: public QStandardItemModel
{
    Q_OBJECT
public:
    UserModel(QList<User> users, QObject *parent = Q_NULLPTR);

    QHash<int, QByteArray> roleNames() const;
};

And here is the implementations of the constructor and the roleNames functions:

enum ItemRoles {
    FirstnameRole,
    LastnameRole,
    AgeRole,
};


UserModel::UserModel(QList<User> users, QObject *parent) : QStandardItemModel(parent)
{    

    //this->setItemRoleNames(roleNames());
    this->setColumnCount(3);
    for (auto user: users) {
        QStandardItem *item = new QStandardItem();
        item->setData(user.getFirstname(), FirstnameRole);
        item->setData(user.getLastname(), LastnameRole);
        item->setData(user.getAge(), AgeRole);
        appendRow(item);
    }

    setSortRole(FirstnameRole);
}


QHash<int, QByteArray> UserModel::roleNames() const
{
    QHash<int, QByteArray> mapping = QStandardItemModel::roleNames();

    mapping[FirstnameRole] = "firstname";
    mapping[LastnameRole] = "lastname";
    mapping[AgeRole] = "age";

    return mapping;
}

My table view only show the last Role added with the function: item->setData(user.getFirstname(), FirstnameRole);

If its the age lastly added, its the age showed... Any clues ?


回答1:


Let's say you really need a custom model and want to extend an existing one. Since your data is in tabular fashion, I'd suggest to use QAbstractTableModel as the base class.

So let's have this class:

class UserModel: public QAbstractTableModel
{
    Q_OBJECT
    QList<User> _users;
public:
    UserModel(QList<User> users, QObject *parent = Q_NULLPTR) : QAbstractTableModel(parent), _users(users){}

As you can see, the class itself stores a list of users, given at construction time. The constructor itself does nothing but copy-initializing the list.

Then you need to supply these implementation, at least:

int rowCount(const QModelIndex &) const override
{
    return _users.size();
}
int columnCount(const QModelIndex &) const override
{
    return 3;
}
QVariant data(const QModelIndex &index, int role) const override
{
    if(role == Qt::DisplayRole)
    {
        User user = _users.at(index.row());
        QVariant data[] = { user.getFirstname(), user.getLastname() , user.getAge() };
        return data[index.column()];
    }
    return {};
}

While columnCount is constant and always returns 3, rowCount will return the number of items in the list of users. In data implementation, the index passed is inspected and a value is returned, according to the index row and column, and the passed role. It's important to understand that when the view calls data passing a role equal to Qt::DisplayRole, the function should return the very data that will be shown in the cell at (index.row(), index.column()), in our case: one of the three User's data members.

It's quite useful to reimplement the sort function as well, i.e.

void sort(int column, Qt::SortOrder order) override
{
    auto fnSort = [](const User & u1, const User & u2){ return u1.getFirstname() < u2.getFirstname(); };
    auto lnSort = [](const User & u1, const User & u2){ return u1.getLastname() < u2.getLastname(); };
    auto agSort = [](const User & u1, const User & u2){ return u1.getAge() < u2.getAge(); };

    std::function<bool (const User &, const User &)>  sortFn[] = {fnSort, lnSort, agSort};
    std::sort(_users.begin(), _users.end(), sortFn[column]);

    if(order == Qt::DescendingOrder)
    {
        std::reverse(_users.begin(), _users.end());
    }
}

This way you let the user sort by column, as expected:

myTableView->setModel(new UserModel(list));
myTableView->model()->sort(2, Qt::DescendingOrder); //sort by first age, in descending order

If, for some reason, you want to use custom roles, please have your enum be like this:

enum ItemRoles {
    FirstnameRole = Qt::UserRole,
    LastnameRole,
    AgeRole,
};

Starting from Qt::UserRole (which is there for this very purpose) ensures that your roles don't clash whit builtin roles.

Please notice that the above code is meant to suggest a possible solution and it's not the solution itself (and lacks many important features like bounds checking and memory management).



来源:https://stackoverflow.com/questions/59369248/qstandarditemmodel-binding-to-custom-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!