Avoiding memory leaks in wxPython 4.0.1 when using AssociateModel for DataViewModel?

Hi everybody,

I am using DataViewModel and DataViewCtrl extensively. After upgrading to wxPython 4.0.1, I now have objects that don’t get automatically garbage collected, causing memory leaks.

I went through the documentation, in particular wx.dataview.DataViewModel — wxPython Phoenix 4.2.3a1 documentation

I noticed the section about avoiding memory leaks (… you need to decrease the reference count after associating the model with a control…) and the first recommended approach:

musicCtrl = wx.dataview.DataViewCtrl(self, wx.ID_ANY)

musicModel = MyMusicModel()

musicCtrl.AssociateModel(musicModel)

musicModel.DecRef() # avoid memory leak !!

My original code, which I had copied from demo examples, did not include the call to DecRef(). However, my code crashes when I add it.

The second recommended approach (A potentially better way to avoid memory leaks…) is to add a call to get():

musicCtrl = wx.dataview.DataViewCtrl(self, wx.ID_ANY)

musicModel = MyMusicModel()

musicCtrl.AssociateModel(musicModel.get())

When I try this approach, I get an error message: object … has no method get().

Could somebody please shed some light on the correct approach?

Many thanks in advance for your help

AC

I’ve added an issue for this at Possible ref-counting issue with DataViewCtrl.AssociateModel · Issue #794 · wxWidgets/Phoenix · GitHub so it doesn’t get forgotten.

···

Robin

On Thursday, March 15, 2018 at 11:10:36 PM UTC-7, AC_cla wrote:

Hi everybody,

I am using DataViewModel and DataViewCtrl extensively. After upgrading to wxPython 4.0.1, I now have objects that don’t get automatically garbage collected, causing memory leaks.

I went through the documentation, in particular https://wxpython.org/Phoenix/docs/html/wx.dataview.DataViewModel.html#wx.dataview.DataViewModel

I noticed the section about avoiding memory leaks (… you need to decrease the reference count after associating the model with a control…) and the first recommended approach:

musicCtrl = wx.dataview.DataViewCtrl(self, wx.ID_ANY)

musicModel = MyMusicModel()

musicCtrl.AssociateModel(musicModel)

musicModel.DecRef() # avoid memory leak !!

My original code, which I had copied from demo examples, did not include the call to DecRef(). However, my code crashes when I add it.

The second recommended approach (A potentially better way to avoid memory leaks…) is to add a call to get():

musicCtrl = wx.dataview.DataViewCtrl(self, wx.ID_ANY)

musicModel = MyMusicModel()

musicCtrl.AssociateModel(musicModel.get())

When I try this approach, I get an error message: object … has no method get().

Could somebody please shed some light on the correct approach?

Many thanks in advance for your help

AC

That second example does not translate well from C++ to Python. The C++ version is showing how to use the model with a shared pointer template that is not available in Python. I’ve removed that code snippet.

Are you sharing the models across more than one view? That’s the only way I was able duplicate the crash. For me the problem was that you only need to DecRef when you’ve created the instance, if it already existed then don’t. In other words, when you create the instance, you have a refcount of 1. When you associate it with a view then the view will also call IncRef to ensure that the model continues to exist as long as the view needs it. Now the refcount is 2. At this point the extra ref from the creation of the instance is no longer needed so you can call DecRef bringing the count down to 1 and with 1 view using the model. If you reuse the model in a 2nd view, it calls IncRef so it can reserve the model for itself, bringing the count up to 2. If you ever need to hold on to a model when there are no views then you’ll need to IncRef (or don’t do the DecRef after you created it) but then you’ll need to call DecRef when you don’t need to hold it any more.

I’m changing the DVC_DataViewModel sample in the demo to handle it like this:

    # Create an instance of our model...

    if model is None:

        self.model = MyTreeListModel(data, log)

        newModel = True # it's a new instance so we need to decref it below

    else:

        self.model = model

        newModel = False

    # Tell the DVC to use the model

    self.dvc.AssociateModel(self.model)

    if newModel:

        self.model.DecRef()

If doing something similar in your project doesn’t help then please post more details and a small sample application I can use to replicate the crash.

···

Robin

On Thursday, March 15, 2018 at 11:10:36 PM UTC-7, AC_cla wrote:

Hi everybody,

I am using DataViewModel and DataViewCtrl extensively. After upgrading to wxPython 4.0.1, I now have objects that don’t get automatically garbage collected, causing memory leaks.

I went through the documentation, in particular https://wxpython.org/Phoenix/docs/html/wx.dataview.DataViewModel.html#wx.dataview.DataViewModel

I noticed the section about avoiding memory leaks (… you need to decrease the reference count after associating the model with a control…) and the first recommended approach:

musicCtrl = wx.dataview.DataViewCtrl(self, wx.ID_ANY)

musicModel = MyMusicModel()

musicCtrl.AssociateModel(musicModel)

musicModel.DecRef() # avoid memory leak !!

My original code, which I had copied from demo examples, did not include the call to DecRef(). However, my code crashes when I add it.

The second recommended approach (A potentially better way to avoid memory leaks…) is to add a call to get():

musicCtrl = wx.dataview.DataViewCtrl(self, wx.ID_ANY)

musicModel = MyMusicModel()

musicCtrl.AssociateModel(musicModel.get())

When I try this approach, I get an error message: object … has no method get().

Could somebody please shed some light on the correct approach?

Many thanks in advance for your help

AC

Dear Robin,

Sorry for a late reply!

I now better understand how one should use DecRef, thanks! I must admit that I had been confused by the documentation, but now it makes sense. I will change my code according to your recommended approach.

Regarding your question: the model causing difficulties was indeed shared between two views.

Many thanks for taking the time to answer my query, and for your great work on wxPython!

AC

···

On Wednesday, March 21, 2018 at 1:34:40 AM UTC+1, Robin Dunn wrote:

That second example does not translate well from C++ to Python. The C++ version is showing how to use the model with a shared pointer template that is not available in Python. I’ve removed that code snippet.

Are you sharing the models across more than one view? That’s the only way I was able duplicate the crash. For me the problem was that you only need to DecRef when you’ve created the instance, if it already existed then don’t. In other words, when you create the instance, you have a refcount of 1. When you associate it with a view then the view will also call IncRef to ensure that the model continues to exist as long as the view needs it. Now the refcount is 2. At this point the extra ref from the creation of the instance is no longer needed so you can call DecRef bringing the count down to 1 and with 1 view using the model. If you reuse the model in a 2nd view, it calls IncRef so it can reserve the model for itself, bringing the count up to 2. If you ever need to hold on to a model when there are no views then you’ll need to IncRef (or don’t do the DecRef after you created it) but then you’ll need to call DecRef when you don’t need to hold it any more.

I’m changing the DVC_DataViewModel sample in the demo to handle it like this:

    # Create an instance of our model...
    if model is None:
        self.model = MyTreeListModel(data, log)
        newModel = True # it's a new instance so we need to decref it below
    else:
        self.model = model
        newModel = False
    # Tell the DVC to use the model
    self.dvc.AssociateModel(self.model)
    if newModel:
        self.model.DecRef()

If doing something similar in your project doesn’t help then please post more details and a small sample application I can use to replicate the crash.

Robin

On Thursday, March 15, 2018 at 11:10:36 PM UTC-7, AC_cla wrote:

Hi everybody,

I am using DataViewModel and DataViewCtrl extensively. After upgrading to wxPython 4.0.1, I now have objects that don’t get automatically garbage collected, causing memory leaks.

I went through the documentation, in particular https://wxpython.org/Phoenix/docs/html/wx.dataview.DataViewModel.html#wx.dataview.DataViewModel

I noticed the section about avoiding memory leaks (… you need to decrease the reference count after associating the model with a control…) and the first recommended approach:

musicCtrl = wx.dataview.DataViewCtrl(self, wx.ID_ANY)

musicModel = MyMusicModel()

musicCtrl.AssociateModel(musicModel)

musicModel.DecRef() # avoid memory leak !!

My original code, which I had copied from demo examples, did not include the call to DecRef(). However, my code crashes when I add it.

The second recommended approach (A potentially better way to avoid memory leaks…) is to add a call to get():

musicCtrl = wx.dataview.DataViewCtrl(self, wx.ID_ANY)

musicModel = MyMusicModel()

musicCtrl.AssociateModel(musicModel.get())

When I try this approach, I get an error message: object … has no method get().

Could somebody please shed some light on the correct approach?

Many thanks in advance for your help

AC