Introduction
wxPython includes a wx.GridCellBoolEditor/Renderer, which looks like a wx.CheckBox
Unfortunately it does not behave exactly like a wx.CheckBox, for two reasons -
1, You have to click it twice with a mouse to change it - once to create the Editor, a second time to change the value
2. There is no obvious way to detect that the value has changed until the Editor is closed
What Objects are Involved
wx.grid.Grid
wx.grid.GridCellBoolEditor
wx.grid.GridCellBoolRenderer
Process Overview
The example assumes that you want all cells in Column 1 to act like a wx.CheckBox.
The grid's init() method sets up the Editor and Renderer for Column 1 in a Column Attribute.
To solve problem 1 above, when a mouse click is detected in Column 1, wx.CallLater is used to call a method to toggle the value, with a delay of 100 (1/10 sec). This gives enough time for the Editor to be created first.
To solve problem 2 above, an EVT_GRID_EDITOR_CREATED handler is used to obtain and save a reference to the underlying wx.CheckBox, and set up an EVT_CHECKBOX handler for it.
A method called afterCheckBox() is used to respond to any changes to the wx.CheckBox. It is called both from the initial mouse click and from EVT_CHECKBOX.
Code Sample
1 import wx, wx.grid as grd
2
3 class MyGrid(grd.Grid):
4 def __init__(self, parent):
5 grd.Grid.__init__(self, parent, -1, pos=(10,40), size=(420,95))
6
7 self.CreateGrid(3,3)
8 self.RowLabelSize = 0
9 self.ColLabelSize = 20
10
11 attr = grd.GridCellAttr()
12 attr.SetEditor(grd.GridCellBoolEditor())
13 attr.SetRenderer(grd.GridCellBoolRenderer())
14 self.SetColAttr(1,attr)
15 self.SetColSize(1,20)
16
17 self.Bind(grd.EVT_GRID_CELL_LEFT_CLICK,self.onMouse)
18 self.Bind(grd.EVT_GRID_SELECT_CELL,self.onCellSelected)
19 self.Bind(grd.EVT_GRID_EDITOR_CREATED, self.onEditorCreated)
20
21 def onMouse(self,evt):
22 if evt.Col == 1:
23 wx.CallLater(100,self.toggleCheckBox)
24 evt.Skip()
25
26 def toggleCheckBox(self):
27 self.cb.Value = not self.cb.Value
28 self.afterCheckBox(self.cb.Value)
29
30 def onCellSelected(self,evt):
31 if evt.Col == 1:
32 wx.CallAfter(self.EnableCellEditControl)
33 evt.Skip()
34
35 def onEditorCreated(self,evt):
36 if evt.Col == 1:
37 self.cb = evt.Control
38 self.cb.WindowStyle |= wx.WANTS_CHARS
39 self.cb.Bind(wx.EVT_KEY_DOWN,self.onKeyDown)
40 self.cb.Bind(wx.EVT_CHECKBOX,self.onCheckBox)
41 evt.Skip()
42
43 def onKeyDown(self,evt):
44 if evt.KeyCode == wx.WXK_UP:
45 if self.GridCursorRow > 0:
46 self.DisableCellEditControl()
47 self.MoveCursorUp(False)
48 elif evt.KeyCode == wx.WXK_DOWN:
49 if self.GridCursorRow < (self.NumberRows-1):
50 self.DisableCellEditControl()
51 self.MoveCursorDown(False)
52 elif evt.KeyCode == wx.WXK_LEFT:
53 if self.GridCursorCol > 0:
54 self.DisableCellEditControl()
55 self.MoveCursorLeft(False)
56 elif evt.KeyCode == wx.WXK_RIGHT:
57 if self.GridCursorCol < (self.NumberCols-1):
58 self.DisableCellEditControl()
59 self.MoveCursorRight(False)
60 else:
61 evt.Skip()
62
63 def onCheckBox(self,evt):
64 self.afterCheckBox(evt.IsChecked())
65
66 def afterCheckBox(self,isChecked):
67 print 'afterCheckBox',self.GridCursorRow,isChecked
68
69 class TestFrame(wx.Frame):
70 def __init__(self, parent):
71 wx.Frame.__init__(self, parent, -1, "Custom cell editor test", size=(250,200))
72 panel = wx.Panel(self,style=0)
73 grid = MyGrid(panel)
74 grid.SetFocus()
75 self.CentreOnScreen()
76
77 class MyApp(wx.App):
78 def OnInit(self):
79 frame = TestFrame(None)
80 frame.Show(True)
81 self.SetTopWindow(frame)
82 return True
83
84 MyApp(0).MainLoop()
Comments
If anything is not clear, ask questions here and I will do my best to answer them.
Frank Millman