Ticket #1138: gGridEditors.3.py

File gGridEditors.3.py, 12.8 kB (added by gary, 6 months ago)

This one is now working. I have modified some of the dDateTextBox to make it less flexible but more consistent in the use of datetime classes.

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 #Todo: I still haven't got the Tool Tip showing for the control.
14
15 # I'm unsure of how to import this as dabo.ui.dDateTextBox.CalPanel is not available.  Coppied and Pasted here, with some minor changes:
16 class CalPanel(dabo.ui.dPanel):
17     def __init__(self, parent, pos=None, dt=None, ctrl=None ):
18         if dt is None:
19             self.date = datetime.date.today()
20         else:
21             self.date = dt
22         self.ctrl = ctrl
23         super(CalPanel, self).__init__(parent, pos=pos)
24        
25    
26     def afterInit(self):
27         """ Create the calendar control, and resize this panel
28         to the calendar's size.
29         """
30         self.cal = dabo.ui.dCalendar(self, Position=(5, 5))
31         self.cal.Date = self.date
32         self.cal.bindEvent(dabo.dEvents.Hit, self.onCalSelection)
33         self.cal.bindEvent(dabo.dEvents.KeyChar, self.onCalKey)
34         wd, ht = self.cal.Size
35         self.Size = (wd+10, ht+10)
36         self.BackColor = (192, 192, 0)
37         self.cal.Visible = True
38        
39        
40     def onCalSelection(self, evt):
41         if self.ctrl is not None:
42             self.ctrl.setDate(self.cal.Date)
43             self.ctrl.setFocus()
44         self.Visible = False
45    
46    
47     def onCalKey(self, evt):
48         if evt.keyCode == wx.WXK_ESCAPE:
49             evt.Continue = False
50             if self.ctrl is not None:
51                 self.ctrl.setFocus()
52             self.Visible = False
53
54
55
56 class GridDateTextBox(dabo.ui.dDateTextBox):
57         def showCalendar(self):
58         if self.ReadOnly:
59             # ignore
60             return
61         availHt = self.Parent.Parent.Bottom - self.Bottom
62         try:
63             self.calPanel.cal.Date = self.Value
64         except:
65             self.calPanel = CalPanel(self.Parent, dt=self.Value, ctrl=self)
66         cp = self.calPanel
67         cp.Position = (self.Left, self.Bottom)
68         if self.Bottom + cp.Height > self.Parent.Parent.Bottom:
69             # Maybe we should move it above
70             if cp.Height <= self.Top:
71                 cp.Bottom = self.Top
72             else:
73                 # We can't fit it cleanly, so try to fit as much as possible
74                 cp.Top = max(0, (self.Parent.Parent.Height - cp.Height) )
75         if self.Left + cp.Width > self.Parent.Parent.Right:
76             # Try moving it to the left
77             cp.Left = max(0, (self.Parent.Parent.Width - cp.Width) )
78         cp.Visible = True
79         cp.bringToFront()
80         # Commented out so that the grid control will receive the update from the Calendar
81         # For some reaons the application crashes when the Cal losses focus and the focus
82         #  doesn't return to the control.  I might need to execute this line but pass to the
83         #  control the "End Edit" function from the GridDataEditor.
84         #  Now it appears this doesn't work at all? Correction:  Seems to die when editing
85         #  a bizobj DataSource?  It turns out the crash is just once the control is selected...
86         # Crash is a segfault within wx libraries
87         #cp.setFocus()
88        
89     def initEvents(self):
90         self.bindEvent(dabo.dEvents.KeyChar, self.__onChar)
91         self.bindEvent(dabo.dEvents.LostFocus, self.__onLostFocus)
92         self.bindEvent(dabo.dEvents.MouseLeftDoubleClick, self.__onDblClick)
93    
94    
95     def __onDblClick(self, evt):
96         """ Display a calendar to allow users to select dates."""
97         self.showCalendar()
98        
99        
100     def __onChar(self, evt):
101         """ If a shortcut key was pressed, process that. Otherwise, eat
102         inappropriate characters.
103         """
104         try:
105             key = evt.keyChar.lower()
106             ctrl = evt.controlDown
107             shift = evt.shiftDown
108            
109             if ctrl:
110                 if shift and self.Application.Platform == "GTK":
111                     # Linux reads keys differently depending on the Shift key
112                     key = {72: "h", 77: "m", 83: "s"}[evt.keyCode]
113                 else:
114                     key = {8: "h", 13: "m", 19: "s"}[evt.keyCode]
115         except:
116             # spurious key event; ignore
117             return
118        
119         shortcutKeys = "t+-mhsyrc[]"
120         dateEntryKeys = "0123456789/- :."
121         if self.ampm:
122             dateEntryKeys + "apm"
123        
124         if key in shortcutKeys and not self.ReadOnly:
125             # There is a conflict if the key, such as '-', is used in both the
126             # date formatting and as a shortcut. So let's check the text
127             # of this field to see if it is a full date or if the user is typing in
128             # a value.
129             adjust = True
130             val = self.Value
131            
132             if val:
133                     if isinstance(val, str) or isinstance(val, unicode):
134                         valDt = self.strToDate(str(val), testing=True)
135                     else:
136                             valDt = val
137                            
138                 if valDt is None:
139                     adjust = False
140                 else:
141                     # They've just finished typing a new date, or are just
142                     # positioned on the field. Either way, update the stored
143                     # date to make sure they are in sync.
144                     self.Value = valDt
145                     evt.Continue = False
146                    
147                 if adjust:
148                     self.adjustDate(key, ctrl, shift)
149    
150         elif key in dateEntryKeys:
151             # key can be used for date entry: allow
152             pass
153         elif evt.keyCode in range(32, 129):
154             # key is in ascii range, but isn't one of the above
155             # allowed key sets. Disallow.
156             evt.stop()
157         else:
158             # Pass the key up the chain to process - perhaps a Tab, Enter, or Backspace...
159             pass
160
161    
162     def __onLostFocus(self, evt):
163         val = self.Value
164         try:
165             newVal = self.strToDate(self.GetValue())
166             if newVal != val:
167                 self.Value = newVal
168         except: pass
169        
170 class GridDateEditor(wx.grid.PyGridCellEditor):
171     def __init__(self, *args, **kwargs):
172         dabo.infoLog.write("GridDateEditor: Init ")
173         dabo.infoLog.write(str(args))
174         dabo.infoLog.write(str(kwargs))
175
176                 super(GridDateEditor, self).__init__(*args, **kwargs)
177
178     def Create(self, parent, id, evtHandler, *args, **kwargs):
179         dabo.infoLog.write("GridDateEditor: Create")
180         dabo.infoLog.write(str(args))
181         dabo.infoLog.write(str(kwargs))
182         control = GridDateTextBox(parent=parent, id=id)
183         self.control = control
184         self.SetControl(self.control)
185         if evtHandler:
186             self.control.PushEventHandler(evtHandler)
187         #super(GridDateEditor, self).Create(parent, id, evtHandler)
188        
189
190     def Clone(self):
191         return self.__class__()
192
193     def SetParameters(self, paramStr):
194         dabo.infoLog.write("GridDateEditor: SetParameters: %s" % paramStr)
195         #self.control.Choices = eval(paramStr)
196
197     def BeginEdit(self, row, col, grid):
198         dabo.infoLog.write("GridDateEditor: BeginEdit (%d,%d)" % (row, col))
199         self.value = grid.GetTable().GetValue(row, col)
200         update = True
201                 #datetime.datetime.fromtimestamp(time.mktime(time.strptime(mytime, time_format)))
202                
203         try:
204                 try:
205                         timeValue = time.strptime(self.value, self.DateFormat)
206                     except ValueError:
207                             dabo.infoLog.write("GridDateEditor: Control Text does not Match Date Format")
208                             #Take our best Guess
209                             dabo.infoLog.write("GridDateEditor: Guessing Time Format")
210                             try:
211                                     timeValue = self.control.strToDate(self.value).timetuple()
212                             except:
213                                     # Still No Luck, default to today
214                                     dabo.infoLog.write("GridDateEditor: %s" %(str(sys.exc_info()[0])))
215                                     dabo.infoLog.write("GridDateEditor: Setting Unknown Control Value to Today")
216                                     update = dabo.ui.areYouSure(title='Error in Existing Date Format', message='Do you want to reset this date field?',
217                                                              cancelButton=False)
218                                         timeValue = datetime.date.today().timetuple()
219                    
220                     if update:     
221                     dateValue = datetime.date(timeValue[0], timeValue[1], timeValue[2])
222                     self.control.Value = dateValue
223                     #self.control.Value = self.value
224
225         except ValueError, vError:
226             dabo.infoLog.write("GridDateEditor: ValueError in BeginEdit: " + str(vError))
227
228                 if not update:
229                         self.Reset()
230                    
231         self.control.SetFocus()
232
233     def EndEdit(self, row, col, grid):
234         changed = False
235         v = self.control.Value.strftime(self.DateFormat)
236         #v = self.control.Value
237        
238         if v != self.value:
239             changed = True
240         if changed:
241             grid.GetTable().SetValue(row, col, v)
242             # I don't think the DateTextBox handles an empty value?
243         #self.value = ""
244         #self.control.Value = self.value
245         return changed
246
247     def Reset(self):
248         self.control.Value = self.value
249
250     def SetSize(self, rectorig):
251         rect = rectorig
252         self.control.SetDimensions(rect.x+3, rect.y+3, rect.width-2, rect.height-2)
253
254     def IsAcceptedKey(self, key):
255         return True
256
257         #Properties
258     def _getDateFormat(self):
259                 # Get the date format - Make it up if none is given using the system locale
260                 if hasattr(self, "_dateFormat"):
261                 return self._dateFormat
262         else:
263                         #dateFormat = locale.nl_langinfo(locale.D_FMT) # %d/%m/%y
264                         #pos = dateFormat.find("%")+1
265                         #date1 = dateFormat[pos]
266                         #pos = dateFormat.find("%", pos)+1
267                         #date2 = dateFormat[pos]
268                         #pos = dateFormat.find("%", pos)+1
269                         #date3 = dateFormat[pos]
270                         #return "%" + date1 + "-%" + date2 + "-%" + date3
271                         return "%Y-%m-%d"
272        
273     def _setDateFormat(self, val):
274             #Todo: Check format is valid
275         self._dateFormat = val
276     DateFormat = property(_getDateFormat, _setDateFormat, None,
277             "Get or Set the Date Format String for this Control.")
278
279
280 #gbd info:
281 #Program received signal SIGSEGV, Segmentation fault.
282 #[Switching to Thread 0xb7c9b6c0 (LWP 6096)]
283 #---Type <return> to continue, or q <return> to quit---
284 #0xb733a91e in wxGridCellEditor::Show () from /usr/lib/libwx_gtk2u_adv-2.8.so.0
285
286
287
288
289 class _dGrid_test(dabo.ui.dGrid):
290     def initProperties(self):
291         self.DataSet = [
292                 {"name" : "Ed Leafe", "age" : 49, "coder" :  True, "color": "2008-10-10"},
293                 {"name" : "Paul McNett", "age" : 37, "coder" :  True, "color": "11-10-2008"},
294                 {"name" : "Ted Roche", "age" : 48, "coder" :  True, "color": "2008-10-12"},
295                 {"name" : "Derek Jeter", "age": 32 , "coder" :  False, "color": "white"},
296                 {"name" : "Halle Berry", "age" : 38, "coder" :  False, "color": "orange"},
297                 {"name" : "Steve Wozniak", "age" : 56, "coder" :  True, "color": "yellow"},
298                 {"name" : "LeBron James", "age" : 22, "coder" :  False, "color": "gold"},
299                 {"name" : "Madeline Albright", "age" : 69, "coder" :  False, "color": "red"}]
300         self.Width = 360
301         self.Height = 150
302         self.Editable = True
303         #self.Sortable = False
304         #self.Searchable = False
305
306
307     def afterInit(self):
308         self.super()
309
310         self.addColumn(Name="Geek", DataField="coder", Caption="Geek?",
311                 Order=10, DataType="bool", Width=60, Sortable=False,
312                 Searchable=False, Editable=True, HeaderFontBold=False)
313
314         col = dabo.ui.dColumn(self, Name="Person", Order=20, DataField="name",
315                 DataType="string", Width=200, Caption="Celebrity Name",
316                 Sortable=True, Searchable=True, Editable=True, Expand=False)
317         self.addColumn(col)
318
319         col.HeaderFontItalic = True
320         col.HeaderBackColor = "orange"
321         col.HeaderVerticalAlignment = "Top"
322         col.HeaderHorizontalAlignment = "Left"
323
324         self.addColumn(Name="Age", Order=30, DataField="age",
325                 DataType="integer", Width=40, Caption="Age",
326                 Sortable=True, Searchable=True, Editable=True)
327
328         col = dabo.ui.dColumn(self, Name="Color", Order=40, DataField="color",
329                 DataType="string", Width=40, Caption="Favorite Color",
330                 Sortable=True, Searchable=True, Editable=True, Expand=False)
331         self.addColumn(col)
332
333         #col.ListEditorChoices = dabo.dColors.colors
334         #col.CustomEditorClass = gGridEditors.GridListEditor
335         col.CustomEditorClass = GridDateEditor
336
337         col.HeaderVerticalAlignment = "Bottom"
338         col.HeaderHorizontalAlignment = "Right"
339         col.HeaderForeColor = "brown"
340
341         self.RowLabels = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
342         #self.ShowRowLabels = True
343
344 if __name__ == '__main__':
345     class TestForm(dabo.ui.dForm):
346         def afterInit(self):
347             self.BackColor = "khaki"
348             g = self.grid = _dGrid_test(self, RegID="sampleGrid")
349             self.Sizer.append(g, 1, "x", border=40, borderSides="all")
350             self.Sizer.appendSpacer(10)
351             gsz = dabo.ui.dGridSizer(HGap=50)
352
353             chk = dabo.ui.dCheckBox(self, Caption="Edit Table", RegID="geekEdit",
354                     DataSource="sampleGrid", DataField="Editable")
355             chk.refresh()
356             gsz.append(chk, row=0, col=0)
357
358             chk = dabo.ui.dCheckBox(self, Caption="Show Row Labels",
359                     RegID="showRowLabels", DataSource="sampleGrid",
360                     DataField="ShowRowLabels")
361             gsz.append(chk, row=1, col=0)
362             chk.refresh()
363
364             chk = dabo.ui.dCheckBox(self, Caption="Allow Multiple Selection",
365                     RegID="multiSelect", DataSource="sampleGrid",
366                     DataField="MultipleSelection")
367             chk.refresh()
368             gsz.append(chk, row=2, col=0)
369
370             radSelect = dabo.ui.dRadioList(self, Choices=["Row", "Col", "Cell"],
371                     ValueMode="string", Caption="Sel Mode", BackColor=self.BackColor,
372                     DataSource="sampleGrid", DataField="SelectionMode", RegID="radSelect")
373             radSelect.refresh()
374             gsz.append(radSelect, row=0, col=1, rowSpan=3)
375
376             self.Sizer.append(gsz, halign="Center", border=10)
377             gsz.setColExpand(True, 1)
378             self.layout()
379
380             self.fitToSizer(20,20)
381
382
383     app = dabo.dApp(MainFormClass=TestForm)
384     app.setup()
385     app.MainForm.radSelect.setFocus()
386     app.start()