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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With