Ticket #1140: gGridEditors-v2.py

File gGridEditors-v2.py, 13.0 kB (added by kweinert, 4 years ago)

cleaned up, has still some issues, see inside the code.

Line 
1 """Grid Editors
2
3 20080131 - GNT
4 """
5
6 import wx
7 import dabo
8 import datetime
9 import time
10 import locale
11 import sys
12 dabo.ui.loadUI("wx")
13 from dabo.dBug import loggit
14
15 # There is an issue with this renderer class where a black highlight remains around changed cells.  This is caused
16 # purely by the use of this renderer.
17
18 # todo: sometimes the column is formated bold, not always
19 # todo: if the row/column is selected and a diffent cell then the gDropdownList-Column, then the background is
20 #       not displayed properly.       
21 # todo: if in editmode and the header is click to sort, the dropdownlist is not updated
22 # todo: if started, the keys are not found, i.e. Draw is called before getLookupDict
23 # todo: sorts by keys, not values.
24
25 # TODO: If this becomes included within dabo, it is recommended to remove the need for
26 # getKeys, getChoices, getValueMode and set these arguements in the dGrid._updateEditor function as with the existing List Editor
27 class gGridDropdownListRenderer(wx.grid.PyGridCellRenderer):
28     """Display the Lookup value of a combo or list box instead of the actual value.
29     You must inherit this class and override the getKeys, getChoices and getValueMode Functions."""
30     def __init__(self, *args, **kwargs):
31         self.__valueLookupDict = self.getLookupDict()
32         super(gGridDropdownListRenderer, self).__init__(*args, **kwargs)
33
34     # These functions must be overriden
35     @loggit
36     def getKeys(self):
37         """Override this function to return a list of stored keys stored in this datafield."""
38         return [2, 4, 6, 8]
39     def getChoices(self):
40         """Override this function to return a list of values to be displayed for this datafield."""
41         return ["The Ants", "Go Marching", "Two by Two", "Hoorah, Hoorah"]
42     def getValueMode(self):
43         """Override this Function to return the ValueMode for the Combo or List box."""
44         return 'Key'
45        
46     def getLookupDict(self):
47         """Override this function to return a dictionary of values to display.  The dictionary key is the stored value."""
48         if self.getValueMode() == 'Key':
49             keys = self.getKeys()
50             choices = self.getChoices()
51             return dict(zip(keys,choices))
52    
53         # This is the function overriden from the parent class.
54     def Draw(self, grid, attr, dc, rect, row, col, isSelected):
55         """Customisation Point: Draw the data from grid in the rectangle with attributes using the dc"""
56         self.clip(dc, rect)
57         # We use our custom attr, not the one wx passes:
58         attr = grid._Table.GetAttr(row, col)
59         try:
60             txt = self.getValueText(grid, row, col)
61             dc.Clear()
62             super(gGridDropdownListRenderer, self).Draw(grid, attr, dc, rect, row, col, isSelected)
63             self.drawText(txt, attr, dc, rect)
64             #super(gGridDropdownListRenderer, self).Draw(grid, attr, dc, rect, row, col, isSelected)
65         finally:
66             self.unclip(dc)
67     @loggit           
68     def getValueText(self, grid, row, col):
69         """Returns the text to display in the grid.  If the key does not exist, then the
70         key will be displayed."""
71         key = grid._Table.GetValue(row, col)
72         return self.__valueLookupDict.get(key, "key not found")
73
74     def drawText(self, txt, attr, dc, rect):
75         dc.DrawText(txt, rect.x, rect.y)
76     def clip(self, dc, rect):
77         """Setup the clipping rectangle"""
78         dc.SetClippingRegion(rect.x, rect.y, rect.width, rect.height)
79     def unclip(self, dc):
80         """Destroy the clipping rectangle"""
81         dc.DestroyClippingRegion()
82
83 class gGridDropdownListDebug(dabo.ui.dDropdownList):
84     def onDestroy(self, evt):
85         dabo.infoLog.write("gGridDropdownList: OnDestroy")
86         #super(gGridDropdownListEditor, self).__init__(*args, **kwargs)
87     def onCreate(self, evt):
88         dabo.infoLog.write("gGridDropdownList: OnCreate ")
89     def onMouseEvent(self, evt):
90         dabo.infoLog.write("gGridDropdownList: MouseEvent ")
91     def onGotFocus(self, evt):
92         dabo.infoLog.write("gGridDropdownList: GotFocus")
93     def onLostFocus(self, evt):
94         dabo.infoLog.write("gGridDropdownList: LostFocus")
95     def onHit(self, evt):
96         dabo.infoLog.write("gGridDropdownList: Hit")
97         evt.Continue = True
98         #print "New Key, ", evt.EventData['index']
99         #print "New Choice, ", evt.EventObject
100         #evt.EventObject.SetFocus()
101         args = self.endEditArgs
102         kwargs = self.endEditKwargs
103         self.endEdit(*args, **kwargs)
104     def onValueChanged(self, evt):
105         dabo.infoLog.write("gGridDropdownList: ValueChanged")
106     def setEndEdit(self, endEdit, *args, **kwargs):
107         self.endEdit = endEdit
108         self.endEditArgs = args
109         self.endEditKwargs = kwargs
110
111 class gGridDropdownList(gGridDropdownListDebug):
112     """Due to an issue with PyGridCellEditor calling EndEdit on control
113        lostFocus (and the DropdownList/wx.Choice looses focus a little early),
114        this class has been created with the intention of still capturing user input."""
115     # I suspect that wx.grid.GridCellChoiceEditor is designed specifically
116     # not to loose focus during editing. It might be worth overriding
117     # some of the functionality of dDropdownList / wx.Choice in a new subclass to behave in a similiar manner.
118     def onHit(self, evt):
119         args = self.endEditArgs
120         kwargs = self.endEditKwargs
121         self.endEdit(*args, **kwargs)
122
123     def setEndEdit(self, endEdit, *args, **kwargs):
124         self.endEdit = endEdit
125         self.endEditArgs = args
126         self.endEditKwargs = kwargs
127
128 # TODO: If this becomes included within dabo, it is recommended to remove the need for
129 # getKeys, getChoices, getValueMode and set these arguements in the dGrid._updateEditor function as with the existing List Editor
130 class gGridDropdownListEditor(wx.grid.PyGridCellEditor):
131     """GridComboListEditor embeds a (dabo) combo box or (dabo) list box within a (dabo) grid.
132        This class must be inherited and have the getKeys, getChoices and getValueMode methods overriden."""
133
134     # This function must be overriden
135     def getKeys(self):
136         """Override this function to return a list of stored keys stored in this datafield."""
137         return [2, 4, 6, 8]
138     def getChoices(self):
139         """Override this function to return a list of values to be displayed for this datafield."""
140         return ["The Ants", "Go Marching", "Two by Two", "Hoorah, Hoorah"]
141     def getValueMode(self):
142         """Override this Function to return the ValueMode for the Combo or List box."""
143         return 'Key'
144        
145     def Create(self, parent, id, evtHandler, *args, **kwargs):
146         control = gGridDropdownList(parent=parent, id=id, Keys=self.getKeys(), Choices=self.getChoices(), ValueMode=self.getValueMode())
147         self.control = control
148         self.SetControl(self.control)
149         if evtHandler:
150             self.control.PushEventHandler(evtHandler)
151         # This Control does not appear to be receiving mouse events properly... I had this problem with the DateTextBox
152         # Once the drop down list is selected, the control looses focus and then the grid will no longer be updated.
153         # This is because EndEdit is called after the object has lost focus (I think). Quick Fix is to catch event in
154         # above class (gGridDropdownList)
155         super(gGridDropdownListEditor, self).Create(parent, id, evtHandler)
156          
157     def Clone(self):
158         return self.__class__()
159
160     def SetParameters(self, paramStr):
161         dabo.infoLog.write("gGridDropdownListEditor: SetParameters: %s" % paramStr)
162         #self.control.Choices = eval(paramStr)
163
164     def BeginEdit(self, row, col, grid):
165         self.value = grid.GetTable().GetValue(row, col)
166         # todo: adapt for choice mode
167         if self.value in self.control.Keys:
168             self.control.Value = self.value
169         else:
170             dabo.infoLog.write("gGridDropdownListEditor.BeginEdit: Value not in Choices")
171         self.control.setEndEdit(self.EndEdit, row, col, grid)
172         self.control.SetFocus()
173
174     def EndEdit(self, row, col, grid):
175         changed = False
176         v = self.control.Value
177         if v != self.value and v is not None:
178             changed = True
179             grid.GetTable().SetValue(row, col, v)
180             self.value = ""
181         self.control.Value = self.value
182         return changed
183        
184 #   def SetSize(self, rectorig):
185 #       dabo.infoLog.write("GridListEditor: SetSize: %s" % rectorig)
186 #       dabo.infoLog.write("GridListEditor: type of rectorig: %s" % type(rectorig))
187 #       rect = wx.Rect(rectorig.X, rectorig.Y, rectorig.Width+10, rectorig.Height+10)
188 #       #rect = rectorig
189 #       dabo.infoLog.write("GridListEditor RECT: %s" % rect)
190 #       super(gGridDropdownListEditor, self).SetSize(rect)
191 #       #self.control.fitToSizer()
192
193     def Reset(self):
194         self.control.Value = self.value
195     def IsAcceptedKey(self, key):
196         return True
197        
198 if __name__ == '__main__':
199     class _dGrid_test(dabo.ui.dGrid):
200         def initProperties(self):
201             self.DataSet = [
202                     {"name" : "2", "age" : 49, "coder" :  True, "color": "orange"},
203                     {"name" : "4", "age" : 37, "coder" :  True, "color": "yellow"},
204                     {"name" : "6", "age" : 48, "coder" :  True, "color": "red"},
205                     {"name" : "8", "age": 32 , "coder" :  False, "color": "white"}]
206             self.Width = 360
207             self.Height = 150
208             self.Editable = True
209             #self.Sortable = False
210             #self.Searchable = False
211
212         def afterInit(self):
213             self.super()
214
215             self.addColumn(Name="Geek", DataField="coder", Caption="Geek?",
216                     Order=10, DataType="bool", Width=60, Sortable=False,
217                     Searchable=False, Editable=True, HeaderFontBold=False)
218
219             col = dabo.ui.dColumn(self, Name="Person", Order=20, DataField="name",
220                     DataType="string", Width=200, Caption="Celebrity Name",
221                     Sortable=True, Searchable=True, Editable=True, Expand=False)
222             self.addColumn(col)
223
224             col.HeaderFontItalic = True
225             col.HeaderBackColor = "orange"
226             col.HeaderVerticalAlignment = "Top"
227             col.HeaderHorizontalAlignment = "Left"
228             #col.ListEditorChoices = dabo.dColors.colors
229             #col.CustomEditorClass = GridListEditor
230
231             col.CustomEditorClass = gGridDropdownListEditor
232             col.CustomRendererClass = gGridDropdownListRenderer #this calls getLookupDict
233             #col.CustomRendererClass = wx.grid.GridCellStringRenderer
234
235             self.addColumn(Name="Age", Order=30, DataField="age",
236                     DataType="integer", Width=40, Caption="Age",
237                     Sortable=True, Searchable=True, Editable=True)
238
239             col = dabo.ui.dColumn(self, Name="Color", Order=40, DataField="color",
240                     DataType="string", Width=40, Caption="Favorite Color",
241                     Sortable=True, Searchable=True, Editable=True, Expand=False)
242             self.addColumn(col)
243
244             col.ListEditorChoices = dabo.dColors.colors
245             col.CustomEditorClass = col.listEditorClass
246             #col.CustomEditorClass = dabo.ui.dGrid.GridListEditor
247             #col.CustomEditorClass = gGridDropdownListEditor
248
249             col.HeaderVerticalAlignment = "Bottom"
250             col.HeaderHorizontalAlignment = "Right"
251             col.HeaderForeColor = "brown"
252
253             self.RowLabels = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
254             #self.ShowRowLabels = True
255
256     class TestForm(dabo.ui.dForm):
257         def afterInit(self):
258             self.BackColor = "khaki"
259             g = self.grid = _dGrid_test(self, RegID="sampleGrid")
260             self.Sizer.append(g, 1, "x", border=40, borderSides="all")
261             self.Sizer.appendSpacer(10)
262             gsz = dabo.ui.dGridSizer(HGap=50)
263
264             chk = dabo.ui.dCheckBox(self, Caption="Edit Table", RegID="geekEdit",
265                     DataSource="sampleGrid", DataField="Editable")
266             chk.refresh()
267             gsz.append(chk, row=0, col=0)
268
269             chk = dabo.ui.dCheckBox(self, Caption="Show Row Labels",
270                     RegID="showRowLabels", DataSource="sampleGrid",
271                     DataField="ShowRowLabels")
272             gsz.append(chk, row=1, col=0)
273             chk.refresh()
274
275             chk = dabo.ui.dCheckBox(self, Caption="Allow Multiple Selection",
276                     RegID="multiSelect", DataSource="sampleGrid",
277                     DataField="MultipleSelection")
278             chk.refresh()
279             gsz.append(chk, row=2, col=0)
280
281             radSelect = dabo.ui.dRadioList(self, Choices=["Row", "Col", "Cell"],
282                     ValueMode="string", Caption="Sel Mode", BackColor=self.BackColor,
283                     DataSource="sampleGrid", DataField="SelectionMode", RegID="radSelect")
284             radSelect.refresh()
285             gsz.append(radSelect, row=0, col=1, rowSpan=3)
286
287             self.Sizer.append(gsz, halign="Center", border=10)
288             gsz.setColExpand(True, 1)
289             self.layout()
290
291             self.fitToSizer(20,20)
292
293
294     app = dabo.dApp(MainFormClass=TestForm)
295     app.setup()
296     app.MainForm.radSelect.setFocus()
297     app.start()
298    
299 # GridCellChoice