Reputation: 14615
I am trying to get listview and tableview working together.
The listview must be used for display, the tableview must be used for editing data. The tableview is created on demand in a popup widget (and it may never be needed).
I populate the listview, on start, from a text file - each row a line, with 2 entries separated by a tab. Easy.
The tableview will have to edit 2 columns separately... also, on listview click, I must be able to retrieve the first part of the split...
I have created a model subclass of QStringListModel
.
QListView *m_myView = new QListView();
StringList *m_myList = new StringList();
QTextStream in(&myFile);
while (!in.atEnd())
{
QString temp = in.readLine();
if(!temp.isEmpty())
m_myList->append(temp);
}
myFile.close();
m_myView->setModel(m_myList);
where
class StringList : public QStringListModel
{
public:
void append (const QString& string)
{
insertRows(rowCount(), 1);
QModelIndex m = index(rowCount() - 1);
setData(m, string, Qt::EditRole);
QStringList splist = string.split('\t');
setData(m, splist.at(0), Qt::ToolTipRole);
if(splist.size() > 1)
setData(m, splist.at(1), Qt::StatusTipRole);
else
setData(m, "", Qt::StatusTipRole);
qDebug() << data(m, Qt::EditRole).toString()
<< data(m, Qt::ToolTipRole).toString()
<< data(m, Qt::StatusTipRole).toString();
}
};
The EditRole
displays correctly, the others display empty strings.
It seems that QStringListModel
is incapable of storing anything except EditRole
.
So I am left with 2 options - either do the split on each selection, and also when creating the table view, or - what I would like to try but don't know how - create a QStandardItemModel
that can act as data sources for both the listview and the tableview, and can easily retrieve the partial data I require on click.
I thought I can simply use it to set the data on listview (like they do here).
QListView *m_myView = new QListView();
QStandardItemModel *m_myList = new QStandardItemModel();
QTextStream in(&myFile);
int r = 0;
while (!in.atEnd())
{
QString temp = in.readLine();
if(!temp.isEmpty())
{
QStringList splist = temp.split('\t'); // assume I know there will be 2 strings always
QStandardItem *item = new QStandardItem(splist.at(0));
m_myList->setItem(r, 0, item);
QStandardItem *item1 = new QStandardItem(splist.at(1));
m_myList->setItem(r, 1, item1);
++r;
}
}
myFile.close();
m_myView->setModel(m_myList);
connect(m_myListView, SIGNAL(clicked(QModelIndex)),
this, SLOT(listChangedSlot(QModelIndex)));
But this will only set the first string in the listview, I really need both, and I still don't know how to retrieve the data
void listChangedSlot(QModelIndex index)
{
qDebug() << m_myList->item(index.row(), 0)->data().toString()
<< m_myList->item(index.row(), 1)->data().toString();
} // shows empty strings
Or...
In the loading section, try:
if(!temp.isEmpty())
{
QStringList splist = temp.split('\t');
splist.append(QString()); // so I don't need to test
QStandardItem *item = new QStandardItem(temp);
m_myList->setItem(r, 0, item);
QModelIndex idx = m_myList->index(r, 0);
QMap<int, QVariant> roles;
roles.insert(Qt::UserRole + 1, QVariant(splist.at(0)));
roles.insert(Qt::UserRole + 2, QVariant(splist.at(1)));
roles.insert(Qt::DisplayRole, QVariant(temp));
m_myList->setItemData(idx, roles);
++r;
}
This works - displays fine and gets the correct content on click - but seems to be unusable for the tableview.
(And seems I am doing twice the work, with setItem
and setItemData
- so technically storing the content 3 times).
How can I get my listview have a datasource with 2 string items, show both, be able to set it on a tableview, and be able to retrieve the first item on click ?
Upvotes: 2
Views: 1514
Reputation: 1050
In case you cannot or don't want to modify the original model you could use a QIdentityProxyModel
. When you override the data
function it will work as a converter between your original model and the expected one for your other view.
Let's say our original model is a derived class from QAbstractTableModel
with FirstName
and LastName
columns.
We want to feed a QListView with that model, combining the first and last name (with a space in between).
Then our proxy would look like something:
class RegistrationListModel : public QIdentityProxyModel {
public:
QVariant data(const QModelIndex &index, int role) const override {
if(role == Qt::DisplayRole) {
return QString("%1 %2").arg(
sourceModel()->data({index.row(), CLM_FIRST_NAME}, role),
sourceModel()->data({index.row(), CLM_LAST_NAME}, role));
}
return sourceModel()->data(index, role);
};
};
For the shake of the clarity, I simplified the 'get data' call. It would be something sourceModel()->data(sourceModel()->index(index.row(), CLM_FIRST_NAME), role).toString()
because we cannot create an index on our own.
Using such a proxy in your widget (assume proxyModel
is a member pointer of MyWidget
):
MyWidget::MyWidget(QWidget* parent) :
QWidget(parent)
{
proxyModel = new RegistrationListModel();
auto listView = new QListView(this);
listView->setModel(proxyModel);
}
void MyWidget::setSharedModel(QAbstractItemModel *model)
{
proxyModel->setSourceModel(model);
}
The only thing left is to call setSharedModel
on our widget with the appropriate QAbstractTableModel
.
Upvotes: 1
Reputation: 14615
I figured out a way to share the data source between the listview and tableview:
I create 3 columns - column 0 with the combined components, and columns 1 and 2 with the separated components.
The listview will display only column 0. For tableview, I will hide column 0.
The data I require for processing will be stored in columns 1 and 2. user edit of the data in tableview will require, upon accepting changes, the edit of corresponding column 0.
Upvotes: 0