I have a QTableWidget, where one column is filled with custom checkboxes. Also, I have implemented an undo mechanic so that every change to the table can be undone. For the other item columns, where only text is stored, I basically achieve it in the following way:
Every time an item in the table is pressed (calling the itemPressed signal), I store the table data before the item editing starts using a function called saveOldState. After editing (and triggering an itemChanged signal), I push the actual widget content together with the old content onto a QUndoStack instance using a function called pushOnUndoStack.
Now I want to achieve a similar thing for the cell widgets. However, changing the checkbox state does not trigger itemChanged. Thus, I have to connect to the checkbox's stateChanged signal to save the new state:
QObject::connect(checkBox, &QCheckBox::stateChanged, this, [checkBox] {
pushOnUndoStack();
});
So, getting the newest table data is not that hard. However, I am struggling to find the right moment to save the data before the checkbox is set, because there is no similar variant for an itemPressed signal in case of a cell widget.
My question is: Is there a good alternative way to store the checkbox state immediately before the state is actually set? Currently, my only idea is to implement a custom mouse move event filter for the cell widget, which calls saveOldState the moment a user moves the mouse inside the cell widget's boundaries. But is there maybe a better way?
Chapter 1: the solution you cannot use
What I think is the correct way to address your question is a proxy model in charge of maintaining the undo stack. It does so by saving the model's data right before changing it.
Header:
Source:
Note that we use
QPersistentModelIndexes in a modified version ofpushOnUndoStack(that I let you implement yourself). Also, I did not write how the stacked undo/redo commands should be processed, apart from callingrestoreData. As long as you get the idea...Chapter 2: where it fails for you
The above solution works regarless of the actual class of the source model ... except if working with
QTableWidgetandQTreeWidget.What blocks this solution in the case of e.g.
QTableWidgetis its internal model (QTableModel).QTableWidgetto useMyUndoModelinstead.If you try, you will very quickly see your application crash.
QTableModelto perform the above substitution but I advise against it.Sample:
myTableWidget->QTableView::setModel(new MyQTableModel);QTableModelis a private class in Qt and should not be used directly. I wish I knew why it was done this way.Chapter 3:The alternative solution
Alternatively, subclassing
QStyledItemDelegatecould work for you. The design is not as clean, there are more ways to make a mistake when using it in your window but it essentially follows the same logic as the above proxy model.The magic is in
setModelData.I kept the version with
index(my habit) but you could use the pointers toQTableItemof course.To be used (most likely in the constructor of your window):
You will have to implement:
pushOnUndoStack(once).roleFromEditorandvalueFromEditor(for every subclass).Edit to address your comment.
I am going to assume you know how
QAbstractIdemModeland subclasses work in a generic manner. To manipulate a checkState in the model of aQTableWidget, I recommend you create aUndoCheckboxDelegatesubclass to implement/override the additional methods this way:Header:
Source:
It may be only a starting point for you. Make sure it correctly fills the undo stack first; after that, you can tweak the behavior a bit.