将 Class 附加到 QStandardItemModel

Append Class to QStandardItemModel

如何将 class BundleItem 的项附加到 QListView 的 QStandardItem 模型?附加它们时,我只想使用 BundleItem 的 Name 属性 显示在列表视图中。我希望指向实际项目的指针存在于模型的 UserRole 中,因此当用户双击列表中的项目时,现在它只会打印到调试器控制台或类似的东西。

#include "mainwindow.h"
#include <QVBoxLayout>
#include <QListView>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QAbstractItemModel>

struct BundleItem {
  QString name;
  QString nickname;
  QString team;

  // Constructor
  BundleItem(QString name,
             QString nickname,
             QString team):
      name(name), nickname(nickname), team(team)
  {}
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    auto *proxyModel = new QSortFilterProxyModel;
    proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);

    auto *widget = new QWidget(this);
    auto *lay = new QVBoxLayout(widget);
    auto *listview = new QListView();

    auto *model = new QStandardItemModel();
    proxyModel->setSourceModel(model);
    listview->setModel(proxyModel);

    // add Item to list
    BundleItem("Kevin", "Kev", "Coyotes");
    BundleItem("Michael", "Mike", "Walkers");

    lay->addWidget(listview);
    setCentralWidget(widget);
}

MainWindow::~MainWindow()
{

}

最简单的版本是从 QStandardItem class.

继承
struct BundleItem : public QStandardItem {
  QString name;
  QString nickname;
  QString team;

  BundleItem(QString name,
             QString nickname,
             QString team):
      QStandardItem(name), // call constructor of base class - name will be displayed in listview
      name(name), nickname(nickname), team(team)
  {}
};

记得在 BundleItem 的构造函数中调用 QStandardItem 构造函数并将 name 作为参数传递。

将项目添加到 ListView 的行是:

model->appendRow (new BundleItem("Kevin", "Kev", "Coyotes"));
model->appendRow (new BundleItem("Michael", "Mike", "Walkers"));

就是这样,您有两个项目填充的列表视图:KevinMichael.

如果你想处理双击动作,你可以在你的插槽中使用

handleDoubleClick (const QModelIndex&); // declaration of your slot

方法 QStandardItemModel::indexFromItem(const QModelIndex&)QModelIndex(从插槽传递索引)作为参数和 returns 指向 QStandardItem 的指针。您只需将此指针转换为 BundleItem 然后您就可以访问 class.

的其他成员

不一定要用指针,可以只保存一份,但必须可以转换成QVariant。为了使您的结构可以转换为 QVariant,您必须使用 Q_DECLARE_METATYPE 宏并具有默认构造函数。我还添加了直接使用 QDebug 及其结构的可能性。

*.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
class QListView;
class QStandardItemModel;
class QSortFilterProxyModel;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void onDoubleClicked(const QModelIndex & index);
private:
    QListView *listview;
    QStandardItemModel *model;
    QSortFilterProxyModel *proxyModel;
    QWidget *widget;
};

#endif // MAINWINDOW_H

*.cpp

#include "mainwindow.h"

#include <QListView>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QVBoxLayout>

#include <QDebug>

struct BundleItem {
    QString name;
    QString nickname;
    QString team;

    // Constructor
    BundleItem() = default;
    BundleItem(const QString & name,
               const QString & nickname,
               const QString & team):
        name(name), nickname(nickname), team(team)
    {}
};
Q_DECLARE_METATYPE(BundleItem)

QDebug operator<<(QDebug debug, const BundleItem &b)
{
    QDebugStateSaver saver(debug);
    debug.nospace() << '(' << b.name << ", " << b.nickname << ", "<< b.team <<')';
    return debug;
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    proxyModel = new QSortFilterProxyModel(this);
    proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);

    widget = new QWidget(this);
    auto lay = new QVBoxLayout(widget);
    listview = new QListView();

    model = new QStandardItemModel();
    proxyModel->setSourceModel(model);
    listview->setModel(proxyModel);

    // add Item to list
    BundleItem item1("Kevin", "Kev", "Coyotes");
    BundleItem item2("Michael", "Mike", "Walkers");

    for(const BundleItem & e : {item1, item2}){
        QStandardItem *it = new QStandardItem(e.name);
        it->setData(QVariant::fromValue(e));
        model->appendRow(it);
    }
    connect(listview, &QListView::doubleClicked, this, &MainWindow::onDoubleClicked);
    lay->addWidget(listview);
    setCentralWidget(widget);
}

MainWindow::~MainWindow()
{

}

void MainWindow::onDoubleClicked(const QModelIndex &index)
{
    QVariant v = proxyModel->data(index, Qt::UserRole+1);
    BundleItem b = v.value<BundleItem>();
    qDebug()<< b;
}

使用副本的好处是您不必处理内存,因此问题更少。使用代理时,无需访问源模型,因此要访问该数据,您可以从代理模型中进行。另一方面,如果您要将 QString 作为函数或方法的参数传递并且它不会更好地修改它,请将其作为 const QString & 传递,这是我将其作为任务留给您的原因.

解决方案

The answers from @rafix07 and @eyllanesc are both 100% correct and solve the problem you have asked about.

但是,我可以自由地为您的设计建议不同的方向,因为:

在您介绍的情况下,您实际上不需要创建自己的结构,即 BundleItem 和子类 QStandardItem

QStandardItem 本身提供了足够的功能来满足您的需要。只需使用 QStandardItem::data and QStandardItem::setData.

注意:为了您的方便,您可以创建一个 enum 来表示每个数据的含义,例如使用 ItemTeam 而不是 Qt::UserRole + 1.

例子

这是我为您准备的示例,用于演示我建议的方法:

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class QStandardItem;

class MainWindow : public QMainWindow
{
    enum DataType : int {
        ItemName = Qt::DisplayRole,
        ItemNickName = Qt::UserRole,
        ItemTeam
    };

    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);

    QStandardItem *createItem(QString name, QString nickname, QString team);

private slots:
    void onDoubleCLicked(const QModelIndex &index);
};

#endif // MAINWINDOW_H

MainWindow.cpp

#include "MainWindow.h"
#include <QStandardItem>
#include <QSortFilterProxyModel>
#include <QBoxLayout>
#include <QListView>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    auto *proxyModel = new QSortFilterProxyModel;
    proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);

    auto *widget = new QWidget(this);
    auto *lay = new QVBoxLayout(widget);
    auto *listview = new QListView();

    auto *model = new QStandardItemModel();
    proxyModel->setSourceModel(model);
    listview->setModel(proxyModel);

    // add Item to list
    model->appendRow(createItem("Kevin", "Kev", "Coyotes"));
    model->appendRow(createItem("Michael", "Mike", "Walkers"));

    lay->addWidget(listview);
    setCentralWidget(widget);

    connect(listview, &QListView::doubleClicked, this, &MainWindow::onDoubleCLicked);
}

QStandardItem *MainWindow::createItem(QString name, QString nickname, QString team)
{
    auto *item = new QStandardItem(name);

    item->setData(nickname, ItemNickName);
    item->setData(team, ItemTeam);

    return item;
}

void MainWindow::onDoubleCLicked(const QModelIndex &index)
{
    if (index.isValid())
        qDebug() << index.data(ItemName).toString() << index.data(ItemNickName).toString() << index.data(ItemTeam).toString();
}