Python为ObjectListView复选框创建事件
我该如何创建一个事件,当一个框被选中或取消选中时触发?在这个例子中,我只是想打印出被选中的对象的数据。
注意:这段代码是从 http://www.blog.pythonlibrary.org/2009/12/23/wxpython-using-objectlistview-instead-of-a-listctrl/ 修改而来的,目的是为了学习。
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()
2 个回答
ObjectListView的文档里似乎没有关于这个主题的详细信息,只有以下两个链接:
http://objectlistview.sourceforge.net/python/recipes.html#data-based-checkboxes
第一个链接可能会通过checkStateGetter这个参数对你有帮助。我猜ObjectListView里可能包含了CheckListCtrlMixin。如果是这样的话,你可以创建一个自己的ObjectListView类,并重写OnCheckItem这个方法,就像在wxPython的CheckListCtrlMixin示例中那样。
我最后想到的是,当你选择某一行时,可能需要进行临时事件绑定。我的意思是,在选择行的事件中,尝试获取事件对象(希望是一个ListItem实例),然后将这个对象绑定到EVT_CHECKBOX事件上。
如果这些方法都不行,那就去官方的wxPython邮件列表问问吧。那里聚集了大多数wxPython的用户。
这个 ObjectListView
类好像没有我需要的功能。经过一段时间的代码研究,我决定自己扩展一下它。
你可以从 ObjectListView
类派生出自己的类,并强制添加事件。你需要重写 _HandleLeftDownOnImage
和 SetCheckState
这两个方法。或者,如果你愿意,也可以直接修改 ObjectListView
的代码。我创建了一个新的类:
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)
然后我用这个新类替代了 ObjectListView
:
self.dataOlv = MyOvl(self, wx.ID_ANY, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
绑定事件:
self.dataOlv.Bind(EVT_OVL_CHECK_EVENT, self.HandleCheckbox)
并写了事件处理函数:
def HandleCheckbox(self, e):
print(e.object.title, e.value)
我知道这可能不是最好的做法,但这个方法简单有效,算是一种小技巧 :-D。
补充: 完整示例
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()