Search code examples
pythoneventswxpythonobjectlistview

Python create an event to an ObjectListView check box


How would I create an event for when a box is checked/unchecked? For this example I just want it to print the data about the object that was checked

NOTE: This code was modified from http://www.blog.pythonlibrary.org/2009/12/23/wxpython-using-objectlistview-instead-of-a-listctrl/ as a learning exercise

import wx
from ObjectListView import ObjectListView, ColumnDefn

########################################################################
class Book(object):
    """
    Model of the Book object

    Contains the following attributes:
    'ISBN', 'Author', 'Manufacturer', 'Title'
    """
    #----------------------------------------------------------------------
    def __init__(self, title, author, isbn, mfg):
        self.isbn = isbn
        self.author = author
        self.mfg = mfg
        self.title = title


########################################################################
class MainPanel(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
        self.products = [Book("wxPython in Action", "Robin Dunn",
                              "1932394621", "Manning"),
                         Book("Hello World", "Warren and Carter Sande",
                              "1933988495", "Manning")
                         ]

        self.dataOlv = ObjectListView(self, wx.ID_ANY, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.setBooks()
        self.dataOlv.CreateCheckStateColumn()        

        # Allow the cell values to be edited when double-clicked
        self.dataOlv.cellEditMode = ObjectListView.CELLEDIT_SINGLECLICK

        # create an update button
        updateBtn = wx.Button(self, wx.ID_ANY, "Update OLV")
        updateBtn.Bind(wx.EVT_BUTTON, self.updateControl)

        # Create some sizers
        mainSizer = wx.BoxSizer(wx.VERTICAL)

        mainSizer.Add(self.dataOlv, 1, wx.ALL|wx.EXPAND, 5)
        mainSizer.Add(updateBtn, 0, wx.ALL|wx.CENTER, 5)
        self.SetSizer(mainSizer)

    #----------------------------------------------------------------------
    def updateControl(self, event):
        """

        """
        print "updating..."
        product_dict = [Book("Core Python Programming", "Wesley Chun",
                         "0132269937", "Prentice Hall"),
                        Book("Python Programming for the Absolute Beginner",
                         "Michael Dawson", "1598631128", "Course Technology"),
                        Book("Learning Python", "Mark Lutz",
                         "0596513984", "O'Reilly")
                        ]
        data = self.products + product_dict
        self.dataOlv.SetObjects(data)

    #----------------------------------------------------------------------
    def setBooks(self, data=None):
        self.dataOlv.SetColumns([
            ColumnDefn("Title", "left", 220, "title"),
            ColumnDefn("Author", "left", 200, "author"),
            ColumnDefn("ISBN", "right", 100, "isbn"),
            ColumnDefn("Mfg", "left", 180, "mfg")
        ])

        self.dataOlv.SetObjects(self.products)

########################################################################
class MainFrame(wx.Frame):
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, parent=None, id=wx.ID_ANY,
                          title="ObjectListView Demo", size=(800,600))
        panel = MainPanel(self)

########################################################################
class GenApp(wx.App):

    #----------------------------------------------------------------------
    def __init__(self, redirect=False, filename=None):
        wx.App.__init__(self, redirect, filename)

    #----------------------------------------------------------------------
    def OnInit(self):
        # create frame here
        frame = MainFrame()
        frame.Show()
        return True

#----------------------------------------------------------------------
def main():
    """
    Run the demo
    """
    app = GenApp()
    app.MainLoop()

if __name__ == "__main__":
    main()

Solution

  • The class ObjectListView does not seem to contain that functionality. After digging in the code for a while I have decided to extend it.

    You can derive your own class from ObjectListView and force the event in. You have to override _HandleLeftDownOnImage and SetCheckState methods. Or you can just change the ObjectListView code if you like. I have derived a new class:

    import  wx.lib.newevent
    
    OvlCheckEvent, EVT_OVL_CHECK_EVENT = wx.lib.newevent.NewEvent()
    
    class MyOvl(ObjectListView):  
        def SetCheckState(self, modelObject, state):
            """
            This is the same code, just added the event inside
            """
            if self.checkStateColumn is None:
                return None
            else:
                r = self.checkStateColumn.SetCheckState(modelObject, state)
    
                # Just added the event here ===================================
                e = OvlCheckEvent(object=modelObject, value=state)
                wx.PostEvent(self, e)
                # =============================================================
    
                return r
    
        def _HandleLeftDownOnImage(self, rowIndex, subItemIndex):
            """
            This is the same code, just added the event inside
            """
            column = self.columns[subItemIndex]
            if not column.HasCheckState():
                return
    
            self._PossibleFinishCellEdit()
            modelObject = self.GetObjectAt(rowIndex)
            if modelObject is not None:
                column.SetCheckState(modelObject, not column.GetCheckState(modelObject))
    
                # Just added the event here ===================================
                e = OvlCheckEvent(object=modelObject, value=column.GetCheckState(modelObject))
                wx.PostEvent(self, e)
                # =============================================================
    
                self.RefreshIndex(rowIndex, modelObject)
    

    Then I used that class instead of ObjectListView:

    self.dataOlv = MyOvl(self, wx.ID_ANY, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
    

    Bound the event:

    self.dataOlv.Bind(EVT_OVL_CHECK_EVENT, self.HandleCheckbox)
    

    And wrote the handler:

    def HandleCheckbox(self, e):
        print(e.object.title, e.value)
    

    I am certain that this is not the best way how to do it, but it is simple and effective hack :-D.


    Edit: Complete example

    import wx
    import  wx.lib.newevent
    from ObjectListView import ObjectListView, ColumnDefn, OLVEvent
    
    OvlCheckEvent, EVT_OVL_CHECK_EVENT = wx.lib.newevent.NewEvent()
    
    class MyOvl(ObjectListView):  
        def SetCheckState(self, modelObject, state):
            """
            This is the same code, just added the event inside
            """
            if self.checkStateColumn is None:
                return None
            else:
                r = self.checkStateColumn.SetCheckState(modelObject, state)
    
                # Just added the event here ===================================
                e = OvlCheckEvent(object=modelObject, value=state)
                wx.PostEvent(self, e)
                # =============================================================
    
                return r
    
        def _HandleLeftDownOnImage(self, rowIndex, subItemIndex):
            """
            This is the same code, just added the event inside
            """
            column = self.columns[subItemIndex]
            if not column.HasCheckState():
                return
    
            self._PossibleFinishCellEdit()
            modelObject = self.GetObjectAt(rowIndex)
            if modelObject is not None:
                column.SetCheckState(modelObject, not column.GetCheckState(modelObject))
    
                # Just added the event here ===================================
                e = OvlCheckEvent(object=modelObject, value=column.GetCheckState(modelObject))
                wx.PostEvent(self, e)
                # =============================================================
    
                self.RefreshIndex(rowIndex, modelObject)
    
    ########################################################################
    class Book(object):
        """
        Model of the Book object
    
        Contains the following attributes:
        'ISBN', 'Author', 'Manufacturer', 'Title'
        """
        #----------------------------------------------------------------------
        def __init__(self, title, author, isbn, mfg):
            self.isbn = isbn
            self.author = author
            self.mfg = mfg
            self.title = title
    
    ########################################################################
    class MainPanel(wx.Panel):
        #----------------------------------------------------------------------
        def __init__(self, parent):
            wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
            self.products = [Book("wxPython in Action", "Robin Dunn",
                                  "1932394621", "Manning"),
                             Book("Hello World", "Warren and Carter Sande",
                                  "1933988495", "Manning")
                             ]
    
            self.dataOlv = MyOvl(self, wx.ID_ANY, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
            self.setBooks()
            self.dataOlv.CreateCheckStateColumn()
            self.dataOlv.Bind(EVT_OVL_CHECK_EVENT, self.HandleCheckbox)
    
            # Allow the cell values to be edited when double-clicked
            self.dataOlv.cellEditMode = ObjectListView.CELLEDIT_SINGLECLICK
    
            # create an update button
            updateBtn = wx.Button(self, wx.ID_ANY, "Update OLV")
            updateBtn.Bind(wx.EVT_BUTTON, self.updateControl)
    
            # Create some sizers
            mainSizer = wx.BoxSizer(wx.VERTICAL)
    
            mainSizer.Add(self.dataOlv, 1, wx.ALL|wx.EXPAND, 5)
            mainSizer.Add(updateBtn, 0, wx.ALL|wx.CENTER, 5)
            self.SetSizer(mainSizer)
    
        def HandleCheckbox(self, e):
            print(e.object.title, e.value)
    
        #----------------------------------------------------------------------
        def updateControl(self, event):
            """
    
            """
            print "updating..."
            product_dict = [Book("Core Python Programming", "Wesley Chun",
                             "0132269937", "Prentice Hall"),
                            Book("Python Programming for the Absolute Beginner",
                             "Michael Dawson", "1598631128", "Course Technology"),
                            Book("Learning Python", "Mark Lutz",
                             "0596513984", "O'Reilly")
                            ]
            data = self.products + product_dict
            self.dataOlv.SetObjects(data)
    
        #----------------------------------------------------------------------
        def setBooks(self, data=None):
            self.dataOlv.SetColumns([
                ColumnDefn("Title", "left", 220, "title"),
                ColumnDefn("Author", "left", 200, "author"),
                ColumnDefn("ISBN", "right", 100, "isbn"),
                ColumnDefn("Mfg", "left", 180, "mfg")
            ])
    
            self.dataOlv.SetObjects(self.products)
    
    ########################################################################
    class MainFrame(wx.Frame):
        #----------------------------------------------------------------------
        def __init__(self):
            wx.Frame.__init__(self, parent=None, id=wx.ID_ANY,
                              title="ObjectListView Demo", size=(800,600))
            panel = MainPanel(self)
    
    ########################################################################
    class GenApp(wx.App):
    
        #----------------------------------------------------------------------
        def __init__(self, redirect=False, filename=None):
            wx.App.__init__(self, redirect, filename)
    
        #----------------------------------------------------------------------
        def OnInit(self):
            # create frame here
            frame = MainFrame()
            frame.Show()
            return True
    
    #----------------------------------------------------------------------
    def main():
        """
        Run the demo
        """
        app = GenApp()
        app.MainLoop()
    
    if __name__ == "__main__":
        main()