QT - How to get values from a single row in QTableView - qt

I have a QTableView with few records, a single row contains four columns.
I need to get these 4 index values (name, surname, age, username) in order to delete them in SQLite, so I need these four values to put in the deletion query. I expect to click on an every index of THAT row and get back all the 4 values.
How can I do it?
Thanks

I don't see a problem. With QModelIndex you can get any data relative to given model index.
void GuiClass::onTableCellClicked(const QModelIndex &index)
{
int row = index.row();
QString name = index.sibling(row, 0).data().toString();
QString surname = index.sibling(row, 1).data().toString();
int age = index.sibling(row, 2).data().toInt();
QString username = index.sibling(row, 3).data().toString();
...
}

First you need to handle clicks on your table view. For that purpose you can handle QAbstractItemView::clicked(const QModelIndex &index) signal and connect it to the appropriate slot. For example:
void GuiClass::onTableCellClicked(const QModelIndex &index)
{
QString cellText = index.data().toString();
[..]
}

Related

Creating a QModelIndex

I have spent the last week struggling to create a QModelIndex given a row and a column.
Alternatively, I would settle for changing the value of row() in an already existing QModelIndex.
Any help would be appreciated.
Edit:
QModelIndex nIndex = QAbstractItemModel::createIndex(1, 2);
int b = nIndex.row();
qInfo() << "b" << b;
Fails with error:
cannot call member function ‘QModelIndex QAbstractItemModel::createIndex(int, int, void*) const’ without object
QModelIndex nIndex = QAbstractItemModel::createIndex(1, 2);
^
The goal at hand is this:
I have a function:
void MyClass::doStuff(QModelIndex index)
Inside that class, I essentially do the following:
if (index.column() != 1)
{
int a=index.row();
}
So my goal is to call that function from a different class and pass it a QModelIndex, but for that index to have been created with a row/column I specify.
I'm not sure this is what you want, but you can just create a QModelIndex with the method QAbstractItemModel::index(row, column) ( http://doc.qt.io/qt-5/qabstractitemmodel.html#index )!? On the other hand that seems to be to simple for you to struggle with it for so long, maybe explain a little bit more.
Example:
QAbstractTableModel *model = ...;
// then you can do something like
QModelIndex nIndex = model->index(1,2);
int b = nIndex.row();
qInfo() << "b" << b;
You can get a new index from the appropriate model, using its index() method.
If you already have an index from the model, with the same parent as your desired index, then you can get another index using the sibling() method of that index:
void MyClass::doStuff(const QModelIndex& index)
{
// get the value at row zero, same column
const QModelIndex header = index.sibling(0, index.column());
}
The index itself is immutable once created - you can't change its row, column or parent (other than by invalidating it with changes to the model behind its back).

How to add different types of delegates in QTreeView

I want to create same kind of QTreeView (not QTreeWidget) structure as shown in attached figure.. This is Property Editor of QT.
I am using QT-4.6
On 2nd column, depending on different condition, I can have either a spin box, or a drop down or a checkbox or text edit... and so on...
Please guide me on how to set different delegates in different cells of a particular column.
From docs, it is evident that there is no straight away API for setting delegate on a cell (rather is available for full widget or a row or a column).
All QAbstractItemDelegate methods, like createEditor or paint, have a model index as one of their parameters. You can access model data using that index and create an appropriate delegate widget. When you create your model you should set some value to every item that will be used to distinguish its type.
An example:
enum DelegateType
{
DT_Text,
DT_Checkbox,
DT_Combo
}
const int MyTypeRole = Qt::UserRole + 1;
QStandardItemModel* createModel()
{
QStandardItemModel *model = new QStandardItemModel;
QStandardItem *item = new QStandardItem;
item->setText("Hello!");
item->setData(DT_Checkbox, MyTypeRole);
model->appendRow(item);
return model;
}
QWidget* MyDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
int type = index.data(MyTypeRole).toInt();
// this is a simplified example
switch (type)
{
case DT_Text:
return new QLinedEdit;
case DT_Checkbox:
return new QCheckBox;
case DT_Combo:
return new QComboBox;
default:
return QItemDelegate::createEditor(parent, option, index);
}
}
#hank This is in response to your last comment... Do you see any flaw in it ?
MyItem* item2 = new MyItem(second);
item2->setData(delType, **MyTypeRole**);
if(delType == DT_Combo)
{
QString str1, str2, str3;
QStringList abc ;
abc << ("1" + str1.setNum(counter) ) << ("2" + str2.setNum(counter) )<< ( "3" + str3.setNum(counter) );
item2->setData(abc, MyTypeRole1);
}
QWidget* MyDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
int type = index.data(MyTypeRole).toInt();
// this is a simplified example
switch (type)
{
case DT_Text:
return new QLinedEdit;
case DT_Combo:
{
QComboBox* cb = new QComboBox(parent);
QStringList entries - index.data(MyTypeRole1).toStringList();
cb->addItems(entries)
return cb;
}
On different item2, I dynamically create entries with a counter variable that is different everytime it comes here...
Here, different combo boxes display different entries.
Does the approach looks fine to you ?

Qt does not update several QTableView cells when editing in the code

I have a simple logic, that basically goes like this: a few entries are filtered from source model (my custom QAbstractTableModel) and presented to the user using QSortFilterProxyModel, which does have only modified filterAcceptsRow function. This presentation is done using simple dialog. User selects desired entries from filtered ones and those selected entries from model must be updated (actually two fields have to be modified). So simplified code goes like this:
QModelIndexList selectedRows = myProxyModel->selectionModel()->selectedRows();
for (int i = 0; i < selectedRows.count(); i++) {
myProxyModel->setData(myProxyModel->index(selectedRows.at(i).row(), (int) LoanStatusCol, QModelIndex()), (int) ReturnedLoan, Qt::EditRole);
myProxyModel->setData(myProxyModel->index(selectedRows.at(i).row(), (int) LoanRetEntriesCol, QModelIndex()), (lastEntryNo + 1), Qt::EditRole);
}
However, this does not work. And each time behavour is quite weird. What I noticed is that, when it gets to second selected row in this cycle and when it reaches setData() code in the model:
bool TransactionModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (!index.isValid()) {
return false;
}
it returns invalid index. However, when I swaped these two setData() code lines, one row was updated, but second row was not - due to invalid index. I do not know, whether I explained that correctly, but probably this should be my silly mistake, because I am new at this.
UPDATE:
Since model consists of QList data, where Transaction is a custom class that defines fields of entry, I created a function, that updated underlying entry by column number (so to say...). I use function setValueByColumnNo. I just could not find a better way to do that, when working with lists of custom classes.
bool TransactionModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (!index.isValid()) {
return false;
}
if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) {
transactionData[index.row()].setValueByColumnNo(index.column(), value);
emit dataChanged(index, index);
return true;
}
return false;
}
Any ideas?
Thanks.

emit dataChanged(createIndex(1,1),createIndex(1,1)) results in many ::data invocations

I have a QTableView and a corresponding instance of a child of QAbtractTableModel.
I was surprised to see that if my table model instance emits a dataChanged naming a single cell, the Qt framework will then issue a large number of calls to my table model's ::data() member function. The row/column range of those calls appears to cover the entire range of what is on the screen + some extra.
This is more than I expected. I would have thought that a dataChanged() that names a single cell would only result in ::data() calls requesting data for that cell. After all, that's the only cell that my table model said was changed. But the Qt framework appears to be very gregarious and inquires about all the cells.
I clearly have a broken understanding of the intent of the dataChanged() signal.
Is there a way to tell the QTableView to update one cell and one cell only without all the extra chatter sent to my table model?
UPDATE: Including code sample
The example here is a header, source, and a chunk of code to create the table. For me, the table displays with 12 columns and 29 rows. After the "issueEmit" invocation at the end, ::data will be invoked 1044 times all because of a dataChanged() signal for a single cell.
// Declaration
#include <QAbstractTableModel>
class SimpleModel : public QAbstractTableModel
{
Q_OBJECT
private:
bool _post_emit;
public:
explicit SimpleModel(QObject *parent=0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
void issueEmit();
};
// Implementation
#include <stdlib.h>
#include <stdio.h>
#include "simplemodel.h"
SimpleModel::SimpleModel(QObject *parent) : QAbstractTableModel(parent), _post_emit(false) { }
int SimpleModel::rowCount(const QModelIndex &parent) const {
return 100;
}
int SimpleModel::columnCount(const QModelIndex &parent) const {
return 100;
}
QVariant SimpleModel::data(const QModelIndex &index, int role) const {
if (role==Qt::DisplayRole) {
if (_post_emit) {
static unsigned s_calls=0;
s_calls++;
printf("Data calls: %d\n",s_calls);
}
return ((rand()%10000)/1000.00);
}
return QVariant();
}
void SimpleModel::issueEmit() {
_post_emit=true;
emit dataChanged(createIndex(1,1),createIndex(1,1));
}
// Usage
QTableView *table=new QTableView;
table->setMinimumSize(1200,900);
SimpleModel *model=new SimpleModel;
table->setModel(model);
table->show();
model->issueEmit();
QVariant QStandardItem::data ( int role = Qt::UserRole + 1 ) const [virtual]
Returns the item's data for the given role, or an invalid QVariant if there is no data for the role.
The argument is really the interesting thing here. Each item in a model holds a number of QVariants, these QVariants maintain different information about the item.
These Variants are all assigned roles. Any time you emit that data has changed the model must redraw the item. To redraw the item it must look at many different pieces of data (small excerpt included below)
Roles describing appearance and meta data (with associated types):
Constant Value Description
Qt::FontRole 6 The font used for items rendered with the default delegate. (QFont)
Qt::TextAlignmentRole 7 The alignment of the text for items rendered with the default delegate. (Qt::AlignmentFlag)
Qt::BackgroundRole 8 The background brush used for items rendered with the default delegate. (QBrush)

How to associate QModelIndex with a new row?

I've cooked up a QAbstractListModel whose model indexes contain a pointer I absolutely needed in order to process data. I add the data like so:
void PointListModel::addPoint(int frameNumber, QPoint const& pos)
{
PointItem *pointItem = new PointItem( frameNumber, pos );
QModelIndex newRow = this->createIndex( m_points.count(), 0, pointItem );
qDebug() << newRow.internalPointer();
beginInsertRows( newRow, m_points.count(), m_points.count() );
m_points.insert( m_points.count( ), pointItem );
endInsertRows();
emit pointAdded( pointItem, pos );
}
It was only later that I realized that the argument to beginInsertRows is asking for the parent model index of the new row, not the new row's actual model index.
So, at this point in time, Qt has given me no way of supplying a QModelIndex to associate with this particular row. How do I create my own model index for this new row?
Okay, I'm rewriting my answer as after some research I've found out that I got it wrong.
You shouldn't do anything special to create a new index when you add new data. You code should look like this:
PointItem *pointItem = new PointItem( frameNumber, pos );
// assume you insert a top level row, having no parent
beginInsertRows( QModelIndex(), m_points.count(), m_points.count() );
m_points.insert( m_points.count( ), pointItem );
endInsertRows();
Then you should implement the index() method which will create indexes on demand and the parent() method which will determine the parent of some index, but since you have a list model, it should probably always just return QModelIndex(). Here is a good article about creating custom models.
Here is a complete example of a working QAbstractListModel:
class MyModel: public QAbstractListModel {
Q_OBJECT
public:
virtual QModelIndex index(int row, int column = 0,
const QModelIndex &parent = QModelIndex()) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
void add(int i);
private:
QList<int> list;
};
void MyModel::add(int i)
{
beginInsertRows(QModelIndex(), list.size(), list.size());
list.append(i);
endInsertRows();
}
QModelIndex MyModel::index(int row, int column,
const QModelIndex &parent) const
{
return hasIndex(row, column, parent) ? createIndex(row, column, (void*)&list[row])
: QModelIndex();
}
int MyModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return list.size();
}
QVariant MyModel::data(const QModelIndex &index,
int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
return QVariant(QString::number(*static_cast<int*>(index.internalPointer())));
}
I've cooked up a QAbstractListModel whose model indexes contain a pointer I absolutely needed in order to process data.
If you start with wrong requirements, you end up with wrong solutions :)
A list model is simple enough so that you don't need more than the QModelIndex's row() to uniquely define the data the index addresses.
So, given a QModelIndex mi, when you before did
PointItem * item = static_cast<PointItem*>(mi.internalPointer());
you can instead do
PointItem * item = plm->pointItemFromIndex(mi);
where plm is your PointListModel. If you don't have a pointer to it lying around when you need to access the PointItem, you can reconstruct it like this:
PointItemModel * plm = qobject_cast<PointItemModel*>(mi.model());
// check for !plm here (!mi.isValid() || qobject_cast fails)
In turn, PointListMode::pointItemFromIndex() would do the actual work:
PointItem * PointListMode::pointItemFromindex(const QModelIndex &mi) const {
return mi.isValid() ? m_points[mi.row()] : 0 ;
}
This is the most important thing to realize when working with QAbstractListModel in Qt: Mentally replace QModelIndex with int row, ignore everything else it has (an invalid QModelIndex has row() == -1).
Same for QAbstractTableModel: mentally reduce the QModelIndex to int row, int column. Forget everything else.
The only time you need the full QModelIndex (including its internalPointer() or internalId() is when you implement a tree model (QAbstractItemModel).

Resources