| 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 |
class GridDateEditor(wx.grid.PyGridCellEditor): |
|---|
| 90 |
def __init__(self, *args, **kwargs): |
|---|
| 91 |
dabo.infoLog.write("GridDateEditor: Init ") |
|---|
| 92 |
dabo.infoLog.write(str(args)) |
|---|
| 93 |
dabo.infoLog.write(str(kwargs)) |
|---|
| 94 |
|
|---|
| 95 |
super(GridDateEditor, self).__init__(*args, **kwargs) |
|---|
| 96 |
|
|---|
| 97 |
def Create(self, parent, id, evtHandler, *args, **kwargs): |
|---|
| 98 |
dabo.infoLog.write("GridDateEditor: Create") |
|---|
| 99 |
dabo.infoLog.write(str(args)) |
|---|
| 100 |
dabo.infoLog.write(str(kwargs)) |
|---|
| 101 |
control = GridDateTextBox(parent=parent, id=id) |
|---|
| 102 |
self.control = control |
|---|
| 103 |
|
|---|
| 104 |
self.SetControl(self.control) |
|---|
| 105 |
if evtHandler: |
|---|
| 106 |
self.control.PushEventHandler(evtHandler) |
|---|
| 107 |
# super(GridDateEditor, self).Create(parent, id, evtHandler) |
|---|
| 108 |
|
|---|
| 109 |
def Clone(self): |
|---|
| 110 |
return self.__class__() |
|---|
| 111 |
|
|---|
| 112 |
def SetParameters(self, paramStr): |
|---|
| 113 |
dabo.infoLog.write("GridDateEditor: SetParameters: %s" % paramStr) |
|---|
| 114 |
#self.control.Choices = eval(paramStr) |
|---|
| 115 |
|
|---|
| 116 |
def BeginEdit(self, row, col, grid): |
|---|
| 117 |
dabo.infoLog.write("GridDateEditor: BeginEdit (%d,%d)" % (row, col)) |
|---|
| 118 |
self.value = grid.GetTable().GetValue(row, col) |
|---|
| 119 |
|
|---|
| 120 |
#datetime.datetime.fromtimestamp(time.mktime(time.strptime(mytime, time_format))) |
|---|
| 121 |
|
|---|
| 122 |
try: |
|---|
| 123 |
try: |
|---|
| 124 |
timeValue = time.strptime(self.value, self.DateFormat) |
|---|
| 125 |
except ValueError: |
|---|
| 126 |
dabo.infoLog.write("GridDateEditor: Control Text does not Match Date Format") |
|---|
| 127 |
#Take our best Guess |
|---|
| 128 |
dabo.infoLog.write("GridDateEditor: Guessing Time Format") |
|---|
| 129 |
try: |
|---|
| 130 |
timeValue = self.control.strToDate(self.value).timetuple() |
|---|
| 131 |
except: |
|---|
| 132 |
# Still No Luck, default to today |
|---|
| 133 |
dabo.infoLog.write("GridDateEditor: %s" %(str(sys.exc_info()[0]))) |
|---|
| 134 |
dabo.infoLog.write("GridDateEditor: Setting Unknown Control Value to Today") |
|---|
| 135 |
dlg = dabo.ui.areYouSure(title='Error in Existing Date Format', message='Do you want to reset this date field?' |
|---|
| 136 |
cancelButton=False) |
|---|
| 137 |
if dlg: |
|---|
| 138 |
timeValue = datetime.date.today().timetuple() |
|---|
| 139 |
|
|---|
| 140 |
dateValue = datetime.date(timeValue[0], timeValue[1], timeValue[2]) |
|---|
| 141 |
self.control.Value = dateValue |
|---|
| 142 |
|
|---|
| 143 |
except ValueError, vError: |
|---|
| 144 |
dabo.infoLog.write("GridDateEditor: ValueError in BeginEdit: " + str(vError)) |
|---|
| 145 |
|
|---|
| 146 |
self.control.SetFocus() |
|---|
| 147 |
|
|---|
| 148 |
def EndEdit(self, row, col, grid): |
|---|
| 149 |
print "End Control Focus" |
|---|
| 150 |
changed = False |
|---|
| 151 |
v = self.control.Value.strftime(self.DateFormat) |
|---|
| 152 |
|
|---|
| 153 |
if v != self.value: |
|---|
| 154 |
changed = True |
|---|
| 155 |
if changed: |
|---|
| 156 |
grid.GetTable().SetValue(row, col, v) |
|---|
| 157 |
# I don't think the DateTextBox handles an empty value? |
|---|
| 158 |
#self.value = "" |
|---|
| 159 |
#self.control.Value = self.value |
|---|
| 160 |
return changed |
|---|
| 161 |
|
|---|
| 162 |
def Reset(self): |
|---|
| 163 |
self.control.Value = self.value |
|---|
| 164 |
|
|---|
| 165 |
# def SetSize(self, rectorig): |
|---|
| 166 |
# dabo.infoLog.write("GridDateEditor: SetSize: %s" % rectorig) |
|---|
| 167 |
# dabo.infoLog.write("GridDateEditor: type of rectorig: %s" % type(rectorig)) |
|---|
| 168 |
# # rect = wx.Rect(rectorig) |
|---|
| 169 |
# # dabo.infoLog.write("GridDateEditor RECT: %s" % rect) |
|---|
| 170 |
# super(GridDateEditor, self).SetSize(rectorig) |
|---|
| 171 |
|
|---|
| 172 |
def IsAcceptedKey(self, key): |
|---|
| 173 |
return true |
|---|
| 174 |
|
|---|
| 175 |
#Properties |
|---|
| 176 |
def _getDateFormat(self): |
|---|
| 177 |
# Get the date format - Make it up if none is given using the system locale |
|---|
| 178 |
if hasattr(self, "_dateFormat"): |
|---|
| 179 |
return self._dateFormat |
|---|
| 180 |
else: |
|---|
| 181 |
#dateFormat = locale.nl_langinfo(locale.D_FMT) # %d/%m/%y |
|---|
| 182 |
#pos = dateFormat.find("%")+1 |
|---|
| 183 |
#date1 = dateFormat[pos] |
|---|
| 184 |
#pos = dateFormat.find("%", pos)+1 |
|---|
| 185 |
#date2 = dateFormat[pos] |
|---|
| 186 |
#pos = dateFormat.find("%", pos)+1 |
|---|
| 187 |
#date3 = dateFormat[pos] |
|---|
| 188 |
#return "%" + date1 + "-%" + date2 + "-%" + date3 |
|---|
| 189 |
return "%Y-%m-%d" |
|---|
| 190 |
|
|---|
| 191 |
def _setDateFormat(self, val): |
|---|
| 192 |
#Todo: Check format is valid |
|---|
| 193 |
self._dateFormat = val |
|---|
| 194 |
DateFormat = property(_getDateFormat, _setDateFormat, None, |
|---|
| 195 |
"Get or Set the Date Format String for this Control.") |
|---|
| 196 |
|
|---|
| 197 |
|
|---|
| 198 |
#gbd info: |
|---|
| 199 |
#Program received signal SIGSEGV, Segmentation fault. |
|---|
| 200 |
#[Switching to Thread 0xb7c9b6c0 (LWP 6096)] |
|---|
| 201 |
#---Type <return> to continue, or q <return> to quit--- |
|---|
| 202 |
#0xb733a91e in wxGridCellEditor::Show () from /usr/lib/libwx_gtk2u_adv-2.8.so.0 |
|---|
| 203 |
|
|---|
| 204 |
|
|---|
| 205 |
|
|---|
| 206 |
|
|---|
| 207 |
class _dGrid_test(dabo.ui.dGrid): |
|---|
| 208 |
def initProperties(self): |
|---|
| 209 |
self.DataSet = [ |
|---|
| 210 |
{"name" : "Ed Leafe", "age" : 49, "coder" : True, "color": "2008-10-10"}, |
|---|
| 211 |
{"name" : "Paul McNett", "age" : 37, "coder" : True, "color": "11-10-2008"}, |
|---|
| 212 |
{"name" : "Ted Roche", "age" : 48, "coder" : True, "color": "2008-10-12"}, |
|---|
| 213 |
{"name" : "Derek Jeter", "age": 32 , "coder" : False, "color": "white"}, |
|---|
| 214 |
{"name" : "Halle Berry", "age" : 38, "coder" : False, "color": "orange"}, |
|---|
| 215 |
{"name" : "Steve Wozniak", "age" : 56, "coder" : True, "color": "yellow"}, |
|---|
| 216 |
{"name" : "LeBron James", "age" : 22, "coder" : False, "color": "gold"}, |
|---|
| 217 |
{"name" : "Madeline Albright", "age" : 69, "coder" : False, "color": "red"}] |
|---|
| 218 |
self.Width = 360 |
|---|
| 219 |
self.Height = 150 |
|---|
| 220 |
self.Editable = True |
|---|
| 221 |
#self.Sortable = False |
|---|
| 222 |
#self.Searchable = False |
|---|
| 223 |
|
|---|
| 224 |
|
|---|
| 225 |
def afterInit(self): |
|---|
| 226 |
self.super() |
|---|
| 227 |
|
|---|
| 228 |
self.addColumn(Name="Geek", DataField="coder", Caption="Geek?", |
|---|
| 229 |
Order=10, DataType="bool", Width=60, Sortable=False, |
|---|
| 230 |
Searchable=False, Editable=True, HeaderFontBold=False) |
|---|
| 231 |
|
|---|
| 232 |
col = dabo.ui.dColumn(self, Name="Person", Order=20, DataField="name", |
|---|
| 233 |
DataType="string", Width=200, Caption="Celebrity Name", |
|---|
| 234 |
Sortable=True, Searchable=True, Editable=True, Expand=False) |
|---|
| 235 |
self.addColumn(col) |
|---|
| 236 |
|
|---|
| 237 |
col.HeaderFontItalic = True |
|---|
| 238 |
col.HeaderBackColor = "orange" |
|---|
| 239 |
col.HeaderVerticalAlignment = "Top" |
|---|
| 240 |
col.HeaderHorizontalAlignment = "Left" |
|---|
| 241 |
|
|---|
| 242 |
self.addColumn(Name="Age", Order=30, DataField="age", |
|---|
| 243 |
DataType="integer", Width=40, Caption="Age", |
|---|
| 244 |
Sortable=True, Searchable=True, Editable=True) |
|---|
| 245 |
|
|---|
| 246 |
col = dabo.ui.dColumn(self, Name="Color", Order=40, DataField="color", |
|---|
| 247 |
DataType="string", Width=40, Caption="Favorite Color", |
|---|
| 248 |
Sortable=True, Searchable=True, Editable=True, Expand=False) |
|---|
| 249 |
self.addColumn(col) |
|---|
| 250 |
|
|---|
| 251 |
#col.ListEditorChoices = dabo.dColors.colors |
|---|
| 252 |
#col.CustomEditorClass = gGridEditors.GridListEditor |
|---|
| 253 |
col.CustomEditorClass = GridDateEditor |
|---|
| 254 |
|
|---|
| 255 |
col.HeaderVerticalAlignment = "Bottom" |
|---|
| 256 |
col.HeaderHorizontalAlignment = "Right" |
|---|
| 257 |
col.HeaderForeColor = "brown" |
|---|
| 258 |
|
|---|
| 259 |
self.RowLabels = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] |
|---|
| 260 |
#self.ShowRowLabels = True |
|---|
| 261 |
|
|---|
| 262 |
if __name__ == '__main__': |
|---|
| 263 |
class TestForm(dabo.ui.dForm): |
|---|
| 264 |
def afterInit(self): |
|---|
| 265 |
self.BackColor = "khaki" |
|---|
| 266 |
g = self.grid = _dGrid_test(self, RegID="sampleGrid") |
|---|
| 267 |
self.Sizer.append(g, 1, "x", border=40, borderSides="all") |
|---|
| 268 |
self.Sizer.appendSpacer(10) |
|---|
| 269 |
gsz = dabo.ui.dGridSizer(HGap=50) |
|---|
| 270 |
|
|---|
| 271 |
chk = dabo.ui.dCheckBox(self, Caption="Edit Table", RegID="geekEdit", |
|---|
| 272 |
DataSource="sampleGrid", DataField="Editable") |
|---|
| 273 |
chk.refresh() |
|---|
| 274 |
gsz.append(chk, row=0, col=0) |
|---|
| 275 |
|
|---|
| 276 |
chk = dabo.ui.dCheckBox(self, Caption="Show Row Labels", |
|---|
| 277 |
RegID="showRowLabels", DataSource="sampleGrid", |
|---|
| 278 |
DataField="ShowRowLabels") |
|---|
| 279 |
gsz.append(chk, row=1, col=0) |
|---|
| 280 |
chk.refresh() |
|---|
| 281 |
|
|---|
| 282 |
chk = dabo.ui.dCheckBox(self, Caption="Allow Multiple Selection", |
|---|
| 283 |
RegID="multiSelect", DataSource="sampleGrid", |
|---|
| 284 |
DataField="MultipleSelection") |
|---|
| 285 |
chk.refresh() |
|---|
| 286 |
gsz.append(chk, row=2, col=0) |
|---|
| 287 |
|
|---|
| 288 |
radSelect = dabo.ui.dRadioList(self, Choices=["Row", "Col", "Cell"], |
|---|
| 289 |
ValueMode="string", Caption="Sel Mode", BackColor=self.BackColor, |
|---|
| 290 |
DataSource="sampleGrid", DataField="SelectionMode", RegID="radSelect") |
|---|
| 291 |
radSelect.refresh() |
|---|
| 292 |
gsz.append(radSelect, row=0, col=1, rowSpan=3) |
|---|
| 293 |
|
|---|
| 294 |
self.Sizer.append(gsz, halign="Center", border=10) |
|---|
| 295 |
gsz.setColExpand(True, 1) |
|---|
| 296 |
self.layout() |
|---|
| 297 |
|
|---|
| 298 |
self.fitToSizer(20,20) |
|---|
| 299 |
|
|---|
| 300 |
|
|---|
| 301 |
app = dabo.dApp(MainFormClass=TestForm) |
|---|
| 302 |
app.setup() |
|---|
| 303 |
app.MainForm.radSelect.setFocus() |
|---|
| 304 |
app.start() |
|---|