11

I have written a custom data model to be displayed with several QTableViews.

Technically, everything works fine: my views show the changes made from my model. My data model is editable, and the setData() method does emit the dataChanged() signal and returns true on successful edition.

However, my problem is that I have to move my mouse over a QTableView for it to show the actual change, while I would like all views to show the changes when they were made, without needing to interact with the views in order for them to be updated.

Any idea? Thanks,


It may be relevant to mention that I do not use the default Qt::EditRole role to edit data, but rather a custom enum value (named ActiveRole).

Here is what I am seeking: my data model contains properties on how to display data, used to generate style sheets that are fed to the viewS.

Thus, when altering the model, for each view, all its items are impacted, which is why the dataChanged() signal is sent with indices covering all cells.

I also tried to emit layoutChanged(), but it does not seem to change the behavior in my case.

Here is an excerpt of the setData() method:

bool DataModel::setData(QModelIndex const& idx, QVariant const& value, int role)
{
  if (ActiveRole == role)
  {
    // Update data...

    QModelIndex topLeft = index(0, 0);
    QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1);

    emit dataChanged(topLeft, bottomRight);
    emit layoutChanged();

    return true;
  }
  return false;
}

Here is a sample of the data() method:

QVariant DataModel::data(QModelIndex const& idx, int role) const
{
  if (ActiveRole == role)
  {
    boost::uuids::uuid id;

    return qVariantFromValue(id);
  }

  return QVariant();
}

And flags() does indicate an editable model:

Qt::ItemFlags DataModel::flags(QModelIndex const& idx) const
{
  if (false == idx.isValid())
  {
    return Qt::ItemIsEditable;
  }

  return QAbstractTableModel::flags(idx) | Qt::ItemIsEditable;
}

I have a custom delegate, which relies heavily on this SO thread for overriding the paint and the sizeHint methods in order to draw a QTextDocument. Also, it provides the content of the ActiveRole to the editor in setEditorData, and calls DataMode::setData in setModelData:

void DataModelDelegate::setEditorData(QWidget* editor, QModelIndex const& idx) const
{
  auto active = qVariantValue<boost::uuids::uuid>(idx.data(ActiveRole));
  static_cast<DataModelEditor*>(editor)->setActive(active);
}

void DataModelDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, QModelIndex const& idx) const
{
  auto active = static_cast<DataModelEditor*>(editor)->getActive();
  model->setData(idx, qVariantFromValue(active), ActiveRole);
}

In the createEditor(), I plug a signal from the editor to a slot of my delegate for committing data:

QWidget* DataModelDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& option, QModelIndex const& idx) const
{
  auto editor = new DataModelEditor(parent);
  connect(editor, SIGNAL(activeItem()), this, SLOT(commitEditorData()));
  return editor;
}

When clicking on an item, the editor triggers the activeItem signal; the connected slot commitEditorData in turn raises the commitData signal with the editor in argument.

So all my views use these custom delegate, editor, and data model. The view that I am interacting with does show the change immediately, but the other views need to have the mouse hovering over them to show changes as well.

Community
  • 1
  • 1
piwi
  • 5,136
  • 2
  • 24
  • 48
  • Could you show the code of the `setData()` function ? – alexisdm Oct 15 '12 at 18:13
  • So your setData works on custom **ActiveRole** instead of standard **EditRole/DisplayRole**, but what role are you setting to the items? Are you overriding also `data()`? Do you have custom rendering delegate for your view? – Pavel Zdenek Oct 16 '12 at 14:21
  • I updated my question with some implementation of data, setData, createEditor and such. Hope this helps! – piwi Oct 16 '12 at 15:20

3 Answers3

6

I actually found the problem, which was that my other view was not properly notified of the data changes: my views each showed different portions of my data, so the other views needed to be notified of the dataChanged(), but for their own, proper, indices.

On a side note, I also had the problem of updating my views while my Qt application was not the active window in my window manager. The solution was to call repaint() on the main window.

piwi
  • 5,136
  • 2
  • 24
  • 48
  • 5
    The solution (calling `repaint()`) didn't work for me. Furthermore, IMO, the answer is not complete. Should describe or give example on how to use `dataChanged()` in the code to achive the expected effect. – Paulo Carvalho Jun 24 '20 at 14:41
6

I have met the same problem, and let me add a detailed explanation to the piwi's answers. If you change the data,and what to update the single or several columns(or rows,depending on your requirement), you should emit a set of index for topleft to bottomright.For example,if you have a table like below:

enter image description here

and, now you have changed some data, and want to update the cell row 1, column 1-2, then you should emit signal dataChange

emit datachange(index(1,1),index(1,2));
  • 2
    How can I do it from outside the model? That is, without the `QModelIndex`es? – Paulo Carvalho Jun 24 '20 at 14:55
  • I am too facing same problem, my original table is a non Qt library, and I use it through a qt model and there is no way to get the exact indices of the changed data. – Keshav Sahu May 18 '22 at 12:03
-1

Are you calling the setData()? Is the dataChanged() signal really emitted? Connect some debug logging slot to it. I dare to speculate that this is very similar problem to yours:

http://www.qtcentre.org/threads/18388-Refreshing-a-QTableView-when-QAbstractTableModel-changes?s=fd88b7c4e59f4487a5457db551f3df2c

Pavel Zdenek
  • 7,146
  • 1
  • 23
  • 38
  • Data is properly changed, `dataChanged` is emitted when needed, within `setData`; changes do show up immediately on the view I am interacting with. The sole problem is that the _other_ view showing same data needs to have the mouse over it to show the changes. I'm prospecting with your link! – piwi Oct 15 '12 at 15:36
  • About the link, the problem is that the data model is altered but not through Qt means, thus not emitting the `dataChanged` signal; not my case though :-/ – piwi Oct 16 '12 at 09:05
  • How can I do it from outside the model? That is, without the `QModelIndex`es? I can't make `QModelIndex`es from integer indexes because the constructor is private. – Paulo Carvalho Jun 24 '20 at 15:01