1. ホーム
  2. c++

[解決済み] QStandardItemModelとカスタムオブジェクトへのバインディング

2022-02-15 20:05:18

質問

Qtのexampleにあるcities-standarditemを自分のexampleに適応させようとしたのですが、どうすればいいですか?おかしな結果になっています。

以下は私のUserクラスです。

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

で、usermodel.hを宣言しています。

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

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

そして、コンストラクタとroleNames関数の実装は以下の通りです。

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

私のテーブルビューには、関数で追加された最後のRoleだけが表示されます。 item->setData(user.getFirstname(), FirstnameRole);

最後に追加された年齢が表示されている場合、その年齢が表示されます。 何か手がかりはありますか?

解決方法は?

例えば、次のような場合です。 <強い 本当に はカスタムモデルが必要で、既存のモデルを拡張したい。あなたのデータは表形式なので、私は QAbstractTableModel を基本クラスとします。

では、このクラスを用意しましょう。

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

見ての通り、このクラスは構築時に指定されたユーザーのリストを保存します。コンストラクタ自体は、リストをコピーして初期化する以外には何もしません。

そうすると、少なくともこれらの実装を用意する必要があります。

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 {};
}

一方 columnCount は一定で常に3を返します。 rowCount は、ユーザーのリストに含まれる項目の数を返します。 で data の実装では、渡されたインデックスが検査され、そのインデックスに応じた値が返されます。 と、渡された 役割 . ここで重要なのは、ビューが data を渡すと role と等しい Qt::DisplayRole のセルに表示されるデータそのものを返す必要があります。 (index.row(), index.column()) この例では、3つの User のデータメンバです。

を再実装すると、かなり便利です。 sort 関数も同様に、すなわち

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());
    }
}

こうすることで、期待通り、ユーザーに列でソートさせることができます。

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

もし、何らかの理由で カスタム のロールは、このようなenumにしてください。

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

からスタートします。 Qt::UserRole (これはまさにこの目的のためにあるのですが) あなたのロールが組み込みのロールと衝突しないようにするためです。

上記のコードは、次のような意味であることに注意してください。 提案 可能な解決策であり、それは その の解決策そのものです(境界チェックやメモリ管理など、多くの重要な機能が欠けています)。