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.

wxGridCellChoiceEditor2 (last edited 2013-12-09 19:50:24 by machine126)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.