【问题标题】:Append Class to QStandardItemModel将类附加到 QStandardItemModel
【发布时间】:2019-04-22 10:57:46
【问题描述】:

如何将我的 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()
{

}

【问题讨论】:

    标签: c++ qt qt5 qstandarditemmodel


    【解决方案1】:

    实现它的简单版本是从QStandardItem 类继承。

    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 的 ctor 中调用 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&amp;)QModelIndex(从槽传递索引)作为参数并返回指向QStandardItem 的指针。您只需将此指针强制转换为BundleItem,然后您就可以访问您班级的其他成员。

    【讨论】:

    • 关于如何解决 OP 的问题,您的回答是 100% 正确的。我将分享另一种观点,这并不意味着质疑您的答案是否正确,而是是否需要将 QStandardItem 子类化。
    • @scopchanov 伟大的解决方案拉菲。但是我很想看到 scopchanov 的回答。理想情况下,如果我不需要的话,我不想让我的类包必须从 qstandarditem 模型继承。但是 j 确实喜欢这个建议作为一个选项。我从来没有想过。
    • scopchanov 是的,我同意你的观点,QStandardItem 作为基类不需要解决 OP 问题。 @JokerMartini,eyllanesc 将解决方案放在不使用继承的地方。我不知道他为什么删除了他的答案。
    • 是的,我从未见过他的回答。不过感谢您提供的信息。
    【解决方案2】:

    不必使用指针,您只能保存一份,但必须可以将其转换为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 &amp;,这是我将其作为任务留给您的原因。

    【讨论】:

    • 重新实现QDebug operator&lt;&lt; 是锦上添花。非常漂亮整洁。
    【解决方案3】:

    解决方案

    @rafix07 和 @eyllanesc 的答案都是 100% 正确的,并且解决了您所询问的问题。

    但是,我允许自己在您的设计中提出不同的方向,因为:

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

    QStandardItem 本身提供了足够的功能来满足您的需求。只需使用QStandardItem::dataQStandardItem::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();
    }
    

    【讨论】:

    • 我使用结构的原因是因为我最终要实现它,这样用户就可以双击和项目并编辑各种属性,如昵称和团队名称。
    • @JokerMartini,你也可以用这种方法做到这一点。只需使用代表。发布另一个关于如何显示和编辑 QStandardItem 的数据的问题,我会尝试给你一个解决方案。或者有人可以提出更好的建议。如您所见,人们愿意提供帮助。
    • 好的,我可能会这样做。如果我寻求帮助,我将先做一些研究并发布。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-03
    • 1970-01-01
    • 2010-10-19
    • 2011-09-12
    • 2019-03-25
    • 1970-01-01
    相关资源
    最近更新 更多