Reputation: 6147
How can I append my items of class BundleItem to the QListView's QStandardItem model? When they are appended, I only want to use the BundleItem's Name
property to be displayed in the listview. I would like a pointer to the actual item to live in the UserRole of the model so when a user double clicks and item in the list, for now it would just print to the debugger console or something similar.
#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()
{
}
Upvotes: 2
Views: 816
Reputation: 8399
The answers from @rafix07 and @eyllanesc are both 100% correct and solve the problem you have asked about.
However, I will allow myself the liberty to suggest a different direction in your design, because:
In the case you have presented, you do not actually need to create your own structure, i.e. BundleItem
, and subclass QStandardItem
for that matter.
QStandardItem
itself provides enough functionality to suit your needs. Just use QStandardItem::data and QStandardItem::setData.
Note: For your convenience you could create an enum
with the meaning of each data, e.g. to use something like ItemTeam
instead of Qt::UserRole + 1
.
Here is an example I have prepared for you to demonstrate the approach I have suggested:
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();
}
Upvotes: 2
Reputation: 243897
It is not necessary to use a pointer, you can save only one copy but it must be possible to convert it to QVariant
. So that your structure can be converted to QVariant
you must use the Q_DECLARE_METATYPE
macro and have a default constructor. I have also added the possibility of using QDebug
directly with its structure.
*.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;
}
The advantage of using a copy is that you will not have to handle memory and therefore fewer problems. When using a proxy it is not necessary to access the source model, so to access that data you can do it from the proxy model. On the other hand if you are going to pass QString
as an argument of a function or method and it will not modify it better, pass it as const QString &
, the reason I leave it as a task for you.
Upvotes: 2
Reputation: 20918
The easy version to do it is inheritance from 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)
{}
};
Remember to call QStandardItem
constructor in ctor of BundleItem
and passing name as its parameter.
Lines to add your items to ListView are:
model->appendRow (new BundleItem("Kevin", "Kev", "Coyotes"));
model->appendRow (new BundleItem("Michael", "Mike", "Walkers"));
and it is all, you have listview filled by two items : Kevin and Michael.
If you want to handle double-click action, you can use in your slot
handleDoubleClick (const QModelIndex&); // declaration of your slot
method QStandardItemModel::indexFromItem(const QModelIndex&)
which takes QModelIndex
(pass index from slot) as parameter and returns pointer to QStandardItem
. You need only to cast this pointer to BundleItem
then you have access to other members of your class.
Upvotes: 2