Search code examples
c++qtqt-signalsslot

SIGNAL: QStandarditemmodel::datachanged(...) does not emit roles when using QStandarditemmodel::setData(...))


my Problem is, when using

QStandarditemmodel::setData(const QModelIndex &index, const QVariant &value, int role)

it seems that the role is never emitted.

Example:

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{

QStandardItemModel *model = new QStandardItemModel();

QList<QStandardItem*> itemList;
itemList.append(new QStandardItem());

model->appendRow(itemList);

connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(myslot_dataChanged(QModelIndex,QModelIndex,QVector<int>)));

model->item(0,0)->setData("supercool value", Qt::DisplayRole); // // should emit 1 role, but 0 are arriving
model->item(0,0)->setData("another supercool value", Qt::UserRole); //  // should emit 1 role, but 0 are arriving

QVector<int> roles;
roles.append(Qt::DisplayRole); 
roles.append(Qt::UserRole);
emit model->dataChanged(model->index(0,0), model->index(0,0), roles); // works. roles.count() == 2
}

MainWindow::myslot_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
    qDebug() << roles.count();
}

given Output:

0
0
2

expected Output:

1
1
2

I know that the roles QVector in the signal is optional. Could it somehow be possible that Qt omit the roles und just uses the default ones? Maybe I have to use the right signal (NOT the ones where you can omit the roles...) explicitely, but i dont know how.

Sorry for bad english, and thank you for help! ;)

Edit 1: Further Investigations:

I've crawled through the Qt sources and found this in qstandarditemmodel.cpp

void QStandardItem::setData(const QVariant &value, int role)
{
    Q_D(QStandardItem);
    role = (role == Qt::EditRole) ? Qt::DisplayRole : role;
    QVector<QStandardItemData>::iterator it;
    for (it = d->values.begin(); it != d->values.end(); ++it) {
        if ((*it).role == role) {
            if (value.isValid()) {
                if ((*it).value.type() == value.type() && (*it).value == value)
                    return;
                (*it).value = value;
            } else {
                d->values.erase(it);
            }
            if (d->model)
                d->model->d_func()->itemChanged(this);
            return;
        }
    }
    d->values.append(QStandardItemData(role, value));
    if (d->model)
        d->model->d_func()->itemChanged(this);
}

When using setData the function itemChanged is used which does not care about the roles. It seems that the roles parameter in dataChanged is just optional for you and never used by native Qt functions.

Edit 2: even more investigations...

I stepped through the setData function. Here is the snippet when setData calls itemChanged():

void QStandardItemModelPrivate::itemChanged(QStandardItem *item)
{
    Q_Q(QStandardItemModel);
    Q_ASSERT(item);
    if (item->d_func()->parent == 0) {
        // Header item
        int idx = columnHeaderItems.indexOf(item);
        if (idx != -1) {
            emit q->headerDataChanged(Qt::Horizontal, idx, idx);
        } else {
            idx = rowHeaderItems.indexOf(item);
            if (idx != -1)
                emit q->headerDataChanged(Qt::Vertical, idx, idx);
        }
    } else {
        // Normal item
        QModelIndex index = q->indexFromItem(item);
        emit q->dataChanged(index, index);
    }
}

As mentioned in the previous investigations setData calls itemChanged which in turn calls emit q->dataChanged(index, index); There we see dataChanged omits the role parameter. This means my previous guess, that Qt native functions dont use the roles, seems to be confirmed.


Solution

  • From the docs of the signal QAbstractItemModel::dataChanged() (emphasis by me):

    void QAbstractItemModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = ...)
    [...]
    The optional roles argument can be used to specify which data roles have actually been modified. An empty vector in the roles argument means that all roles should be considered modified.

    As you stated, Qt's implementation of QStandardItemModel doesn't care to specify for which roles data changed, but instead specifies that all data should be updated.

    The answer to the question (you didn't ask) is:

    If your slot is called with a specific set of roles, you can optimize for that. If roles is empty, you need to fetch data for all roles that are relevant to your view.