[PyQt] General question about Proxy Model

John F Sturtz john at sturtz.org
Mon May 20 17:51:37 BST 2019


:head slap:

Wow.  Thanks again Maurizio!

On the negative side:  That was kind of a dumb mistake.  Imagine the problem being that simple.  I definitely thought it was something darker and more sinister.

On the positive side:  My understanding wasn't as far off as I thought!

Appreciate the help, as always.  Learning all the time ...

/John
On 5/19/2019 6:28:49 PM, Maurizio Berti <maurizio.berti at gmail.com> wrote:
Hi John,
I think that the issue comes from your implementation of the closeEditor slot.

Since you're calling super() *after* changing the model structure, the view's default closeEditor() method tries to interact with what it thought it was the "current" index, but you just deleted it: the proxy model isn't able to map the index, since it doesn't exist anymore, then it crashes (although, in some tests I did it justs reports inconsistencies, while in other cases it just ignored as the index is invalid or just froze the main loop).
After moving the super() at the beginning of the method, you'll see that everything works as expected.
The only issue with this is that the EndEditHint will be ignored, but I think that you can manage that.
Also remember to implement the setEditorData() with the current index.data (but you can do that within createEditor() too).

Maurizio


Il giorno dom 19 mag 2019 alle ore 18:09 John F Sturtz <john at sturtz.org [mailto:john at sturtz.org]> ha scritto:

Hi again Maurizio.  Thanks as always for your help.

Minimal example is attached.  Here is a brief summary of what it is trying to do:

This is code to manage a list of categories and subcategories.  It creates a dialog (Dlg, which is loaded from the attached dlg.ui file) that uses a QTreeView to display a list of main categories, each of which can contain zero or more subcategories.  The dialog has buttons to create a new main category, create a new subcategory, and delete either type.

The actual data for a main category or subcategory is represented by a CategoryNodeobject.  A tree structure of these nodes is the data structure that underlies the source model (CategoryTreeModel).  The source model manipulates the data tree using the methods defined in the CategoryNode class.

The QTreeView (TreeView) uses a QSortFilterProxyModel proxy model to maintain a sorted view of the data.  Here's how I maintain the sorted view (not sure this is correct, but it's what I gleaned from such documentation as I could find):  The proxy model overrides setData(), so when an item in the view is edited (either an existing one is changed, or a new one is added), the proxy's setData() gets called.  It first calls the source model's setData() to update the appropriate node in the underlyingCategoryNode tree.  Then it calls sort() and invalidate() on itself to sort the view.

All this seems to work pretty well.  I've ensured that the model is implemented correctly at least to this extent:
* I've run the model through the pytest qmodeltester and not gotten any warnings.
* I've spent quite a bit of time going about adding, deleting and changing the descriptions of the items in the view using the buttons in the dialog, and they seem to work properly.
The problem comes when I try to do this:  When you add either a main category or subcategory, it creates a new node in the tree with a blank value, then opens a QLineEdit delegate editor to enter the description.  If the user hits Escape, or hits Enter without typing in a value, I want to just delete the newly-added node and throw it away.

To that end, I override closeEditor() in the TreeView.  I get the view's currentIndex() (which should be an index in the proxy model, I think?).  If the value is empty, I get a reference to the source model, map the current index to the corresponding index in the source model (mapToSource()), and then call the source model's removeRows() method to delete it (lines 277-282).

This consistently crashes or produces a warning.

On my machine, the following consistently crashes:
* Start the app
* Click on Reimbursible at the bottom of the list
* Click the Sub button to add a new empty subcategory
* Hit Escape
This consistently produces the message QSortFilterProxyModel: inconsistent changes reported by source model (but doesn't crash):
* Start the app
* Click on Healthcare
* Click the Sub button to add a new empty subcategory
* Hit Escape
I thought I was doing all this correctly (don't we usually?), but clearly I must misunderstand some part of it.  If you (or anyone) spots a problem, I'd be grateful to hear it.

Thanks!

/John
On 5/17/2019 4:37:47 PM, Maurizio Berti <maurizio.berti at gmail.com [mailto:maurizio.berti at gmail.com]> wrote:
Since you're implementing your own model, implementing removeRows() in the source model should be enough, but you have to ensure that its implementation responds as expected: removeRows has to return a bool, and the parent *must* be checked, expecially if you have a tree structure. Also ensure that all basic model functions (index, parent, rowCount, columnCount and data) are correctly implemented.

I'd suggest you to give us a minimal example anyway, you might even find out what is wrong in your case.

Maurizio

Il giorno mar 14 mag 2019 alle ore 21:38 John F Sturtz <john at sturtz.org [mailto:john at sturtz.org]> ha scritto:

Hi again.

I'm the one who started the QTreeView + sort + delete = crash thread on May 8th.  In response to assistance from Kyle and Florian, I've re-implemented to use a QAbstractItemModel with associated QSortFilterProxyModel.

On the whole, that seems to be working pretty well, but there's one thing (well, at least one) I haven't been clear on despite poring over what documentation I can find.  I can post the test code again if it will help, but I think this is more of a general question:

If I have both a source model and also a proxy, and I'm modifying the view in a way that changes the structure (deleting a row, in the current case I'm working on), should I be overriding removeRows() for both the source model and the proxy, and calling beginRemoveRows()/endRemoveRows() in both cases?

I guess that seems to make sense to me, and my initial testing suggests it's so, as it seems to work (whereas if I don't do that, I either get a crash, or a message something like QSortFilterProxyModel: inconsistent changes reported by source model).

Same for inserting rows?  Just checking to see if I'm thinking right here.

Thanks!

/John
_______________________________________________
PyQt mailing list    PyQt at riverbankcomputing.com [mailto:PyQt at riverbankcomputing.com]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt [https://www.riverbankcomputing.com/mailman/listinfo/pyqt]



--

È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net [http://www.jidesk.net]
_______________________________________________
PyQt mailing list    PyQt at riverbankcomputing.com [mailto:PyQt at riverbankcomputing.com]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt [https://www.riverbankcomputing.com/mailman/listinfo/pyqt]



--

È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net [http://www.jidesk.net]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190520/ab6ad7f7/attachment.html>


More information about the PyQt mailing list