Introduction
This is a second sample for GridCellChoiceEditor use. It is similar to the GridCustEditor.py example provided in the demo.
Code Sample
1 import wx
2 import wx.grid
3
4 import string
5 #---------------------------------------------------------------------------
6 class MyCellEditor(wx.grid.PyGridCellEditor):
7 #class MyCellEditor(wx.grid.GridCellFloatEditor):
8 """
9 This is a sample GridCellEditor that shows you how to make your own custom
10 grid editors. All the methods that can be overridden are show here. The
11 ones that must be overridden are marked with "*Must Override*" in the
12 docstring.
13 The sample custom editor is a choice editor.
14
15 Notice that in order to call the base class version of these special
16 methods we use the method name preceded by "base_". This is because these
17 methods are "virtual" in C++ so if we try to call wx.grid.GridCellEditor. Create
18 for example, then when the wx.Python extension module tries to call
19 ptr->Create(...) then it actually calls the derived class version which
20 looks up the method in this class and calls it, causing a recursion loop.
21 If you don't understand any of this, don't worry, just call the "base_"
22 version instead.
23
24 NB: It comes from GridCustEditor.py available in the demo.
25 """
26 def __init__(self, log):
27 self.log = log
28 self.log.write("MyCellEditor ctor\n")
29 wx.grid.PyGridCellEditor.__init__(self)
30
31
32 def Create(self, parent, id, evtHandler):
33 """
34 Called to create the control, which must derive from wxControl.
35 *Must Override*
36 """
37 self.log.write("MyCellEditor: Create\n")
38 sampleList = ['zero', 'one', 'two', 'three', 'four', 'five','six', 'seven', 'eight']
39 self._tc = wx.Choice(parent, id, (100, 50), choices = sampleList)
40
41 self.SetControl(self._tc)
42 if evtHandler:
43 self._tc.PushEventHandler(evtHandler)
44
45
46 def SetSize(self, rect):
47 """
48 Called to position/size the edit control within the cell rectangle.
49 If you don't fill the cell (the rect) then be sure to override
50 PaintBackground and do something meaningful there.
51 """
52 self.log.write("MyCellEditor: SetSize %s\n" % rect)
53 self._tc.SetDimensions(rect.x, rect.y, rect.width+2, rect.height+2,
54 wx.SIZE_ALLOW_MINUS_ONE)
55
56
57 def Show(self, show, attr):
58 """
59 Show or hide the edit control. You can use the attr (if not None)
60 to set colours or fonts for the control.
61 """
62 self.log.write("MyCellEditor: Show(self, %s, %s)\n" % (show, attr))
63 self.base_Show(show, attr)
64
65
66 def PaintBackground(self, rect, attr):
67 """
68 Draws the part of the cell not occupied by the edit control. The
69 base class version just fills it with background colour from the
70 attribute. In this class the edit control fills the whole cell so
71 don't do anything at all in order to reduce flicker.
72 """
73 self.log.write("MyCellEditor: PaintBackground\n")
74
75
76 def BeginEdit(self, row, col, grid):
77 """
78 Fetch the value from the table and prepare the edit control
79 to begin editing. Set the focus to the edit control.
80 *Must Override*
81 """
82 self.log.write("MyCellEditor: BeginEdit (%d,%d)\n" % (row, col))
83 self.startValue = grid.GetTable().GetValue(row, col)
84 self._tc.SetStringSelection(self.startValue)
85 self._tc.SetFocus()
86
87
88 def EndEdit(self, row, col, grid):
89 """
90 Complete the editing of the current cell. Returns True if the value
91 has changed. If necessary, the control may be destroyed.
92 *Must Override*
93 """
94 self.log.write("MyCellEditor: EndEdit (%d,%d)\n" % (row, col))
95 changed = False
96
97 val = self._tc.GetStringSelection()
98 if val != self.startValue:
99 changed = True
100 grid.GetTable().SetValue(row, col, val) # update the table
101
102 self.startValue = 'zero'
103 self._tc.SetStringSelection('zero')
104 return changed
105
106
107 def Reset(self):
108 """
109 Reset the value in the control back to its starting value.
110 *Must Override*
111 """
112 self.log.write("MyCellEditor: Reset\n")
113 self._tc.SetStringSelection(self.startValue)
114 #self._tc.SetInsertionPointEnd()
115
116
117 def IsAcceptedKey(self, evt):
118 """
119 Return True to allow the given key to start editing: the base class
120 version only checks that the event has no modifiers. F2 is special
121 and will always start the editor.
122 """
123 self.log.write("MyCellEditor: IsAcceptedKey: %d\n" % (evt.GetKeyCode()))
124
125 ## Oops, there's a bug here, we'll have to do it ourself..
126 ##return self.base_IsAcceptedKey(evt)
127
128 return (not (evt.ControlDown() or evt.AltDown()) and
129 evt.GetKeyCode() != wx.WXK_SHIFT)
130
131
132 def StartingKey(self, evt):
133 """
134 If the editor is enabled by pressing keys on the grid, this will be
135 called to let the editor do something about that first key if desired.
136 """
137 self.log.write("MyCellEditor: StartingKey %d\n" % evt.GetKeyCode())
138 key = evt.GetKeyCode()
139 ch = None
140 if key in [wx.WXK_NUMPAD0, wx.WXK_NUMPAD1, wx.WXK_NUMPAD2, wx.WXK_NUMPAD3, wx.WXK_NUMPAD4,
141 wx.WXK_NUMPAD5, wx.WXK_NUMPAD6, wx.WXK_NUMPAD7, wx.WXK_NUMPAD8, wx.WXK_NUMPAD9]:
142 ch = ch = chr(ord('0') + key - wx.WXK_NUMPAD0)
143
144 elif key < 256 and key >= 0 and chr(key) in string.printable:
145 ch = chr(key)
146 if not evt.ShiftDown():
147 ch = ch.lower()
148
149 if ch is not None:
150 self._tc.SetStringSelection(ch)
151 else:
152 evt.Skip()
153
154
155 def StartingClick(self):
156 """
157 If the editor is enabled by clicking on the cell, this method will be
158 called to allow the editor to simulate the click on the control if
159 needed.
160 """
161 self.log.write("MyCellEditor: StartingClick\n")
162
163
164 def Destroy(self):
165 """final cleanup"""
166 self.log.write("MyCellEditor: Destroy\n")
167 self.base_Destroy()
168
169
170 def Clone(self):
171 """
172 Create a new object which is the copy of this one
173 *Must Override*
174 """
175 self.log.write("MyCellEditor: Clone\n")
176 return MyCellEditor(self.log)
177
178
179 #---------------------------------------------------------------------------
180 class GridEditorTest(wx.grid.Grid):
181 def __init__(self, parent, log):
182 wx.grid.Grid.__init__(self, parent, -1)
183 self.log = log
184
185 self.CreateGrid(10, 3)
186
187 # Somebody changed the grid so the type registry takes precedence
188 # over the default attribute set for editors and renderers, so we
189 # have to set null handlers for the type registry before the
190 # default editor will get used otherwise...
191 #self.RegisterDataType(wx.GRID_VALUE_STRING, None, None)
192 #self.SetDefaultEditor(MyCellEditor(self.log))
193
194 # Or we could just do it like this:
195 #self.RegisterDataType(wx.GRID_VALUE_STRING,
196 # wx.GridCellStringRenderer(),
197 # MyCellEditor(self.log))
198
199 # but for this example, we'll just set the custom editor on one cell
200 self.SetCellEditor(1, 0, MyCellEditor(self.log))
201 self.SetCellValue(1, 0, "11")
202
203 # and on a column
204 attr = wx.grid.GridCellAttr()
205 attr.SetEditor(MyCellEditor(self.log))
206 self.SetColAttr(2, attr)
207 self.SetCellValue(1, 2, "two")
208
209 self.SetColSize(0, 150)
210 self.SetColSize(1, 150)
211 self.SetColSize(2, 75)
212
213
214 #---------------------------------------------------------------------------
215
216 class TestFrame(wx.Frame):
217 def __init__(self, parent, log):
218 wx.Frame.__init__(self, parent, -1, "Custom Grid Cell Editor Test",
219 size=(640,480))
220 grid = GridEditorTest(self, log)
221
222 #---------------------------------------------------------------------------
223
224 if __name__ == '__main__':
225 import sys
226 app = wx.App(False)
227 frame = TestFrame(None, sys.stdout)
228 frame.Show(True)
229 app.MainLoop()
Comments
If you want to use it, it's probably a good idea to pick out the MyCellEditor class, and to use it as in the __init__ method of GridEditorTest
Edits
I've found that on Linux the Choice box just doesn't work. Seems that when you click on the cell and start editing it, you have to click on it again to show the wxChoice. The evtHandler passes that up the event chain and calls EndEdit, before it even shows the list of choices! Hence no changes. The solution to this is to modify the create function to look like this
1 def Create(self, parent, id, evtHandler):
2 """
3 Called to create the control, which must derive from wxControl.
4 *Must Override*
5 """
6 self.log.write("MyCellEditor: Create\n")
7 sampleList = ['zero', 'one', 'two', 'three', 'four', 'five','six', 'seven', 'eight']
8 self._tc = wx.Choice(parent, id, (100, 50), choices = sampleList)
9
10 self.SetControl(self._tc)
11 if evtHandler:
12 self._tc.PushEventHandler(evtHandler)
13 evtHandler.SetEvtHandlerEnabled(False) # Add this line
Turns out you HAVE to have the evtHandler in there, otherwise on close it SegFaults, but you just don't want it to do anything. Also on my system setting the extra window style WS_EX_BLOCK_EVENTS had no affect.