I’ve set up bi-directional bindings with a ColumnView ( having only recently noticed that they exist ). So in my ‘bind’ handler for each column, I have something like:
grid_row.bind_property( column_name , widget , "text" , GObject.BindingFlags.SYNC_CREATE
| GObject.BindingFlags.BIDIRECTIONAL
, self.bind_transform_to )
This ( using bi-directional binding ) removes some complexity ( having to manually capture changes eg made in GtkEntry widgets and set the corresponding value in the model ). However I’ve noticed that if I populate my model beyond a certain number of rows, and then scroll down, I get 100% CPU usage, and an effective application lockup.
I ran in sysprof to try to see what was happening:
It looks like there’s lots of signals being emitted, and g_object_set_property() happening … so based on this, added some debugging print() statements in places. ( Frustratingly, my python IDE refuses to step into the class I’m using as the model for ColumnViews, probably because it’s being generated at run-time … but this is a side issue ).
Anyway, after adding some debugging print()s, my model has:
def id( self , id ):
print( "In [id].setter() ... current: [{0}] ... new: [{1}]".format( id , self._id ) )
if str( self._id ) != id:
print( "[id] changed" )
self._id = id
self.notify( "id" )
if self.row_state == 'emblem-default':
print( "grid row state ==> changed" )
self.row_state = 'media-playlist-shuffle'
self.notify( "row_state" )
I then run my app ( disabling output buffering in bash ), and watch the output as I scroll. Things look normal until I scroll “too far”, and then I get an endless loop of:
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
In [id].setter() ... current: [308] ... new: [308]
Interestingly, it’s always the same ID it chokes on - I assume this is just the limit at which widgets in the ColumnView are recycled?
Anyway:
-
Why is this happening, and how do I avoid it?
-
Why is this only happening with bi-directional bindings? As noted above, I used to use the default single-directional binding, but this requires more manual code to take input from the widgets and apply them back to the model - and it causes some usability issues ( another side issue at this point ). However I don’t see this infinite loop with single-directional bindings, no matter how many rows I put in the model.