3 """Property editor panel for Hooke.
\r
10 import wx.propgrid as wxpg
\r
12 # There are many comments and code fragments in here from the demo app.
\r
13 # They should come in handy to expand the functionality in the future.
\r
15 class Display (object):
\r
16 property_descriptor = []
\r
20 class ValueObject (object):
\r
25 class IntProperty2 (wxpg.PyProperty):
\r
26 """This is a simple re-implementation of wxIntProperty.
\r
28 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=0):
\r
29 wxpg.PyProperty.__init__(self, label, name)
\r
30 self.SetValue(value)
\r
32 def GetClassName(self):
\r
33 return "IntProperty2"
\r
35 def GetEditor(self):
\r
38 def GetValueAsString(self, flags):
\r
39 return str(self.GetValue())
\r
41 def PyStringToValue(self, s, flags):
\r
44 if self.GetValue() != v:
\r
47 if flags & wxpg.PG_REPORT_ERROR:
\r
48 wx.MessageBox("Cannot convert '%s' into a number."%s, "Error")
\r
51 def PyIntToValue(self, v, flags):
\r
52 if (self.GetValue() != v):
\r
56 class PyFilesProperty(wxpg.PyArrayStringProperty):
\r
57 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=[]):
\r
58 wxpg.PyArrayStringProperty.__init__(self, label, name, value)
\r
59 self.SetValue(value)
\r
61 def OnSetValue(self, v):
\r
63 self.display = ', '.join(self.value)
\r
65 def GetValueAsString(self, argFlags):
\r
68 def PyStringToValue(self, s, flags):
\r
69 return [a.strip() for a in s.split(',')]
\r
71 def OnEvent(self, propgrid, ctrl, event):
\r
72 if event.GetEventType() == wx.wxEVT_COMMAND_BUTTON_CLICKED:
\r
73 # Show dialog to select a string, call DoSetValue and
\r
74 # return True, if value changed.
\r
80 class PyObjectPropertyValue:
\r
82 Value type of our sample PyObjectProperty. We keep a simple dash-delimited
\r
83 list of string given as argument to constructor.
\r
85 def __init__(self, s=None):
\r
87 self.ls = [a.strip() for a in s.split('-')]
\r
92 return ' - '.join(self.ls)
\r
95 class PyObjectProperty(wxpg.PyProperty):
\r
97 Another simple example. This time our value is a PyObject (NOTE: we can't
\r
98 return an arbitrary python object in DoGetValue. It cannot be a simple
\r
99 type such as int, bool, double, or string, nor an array or wxObject based.
\r
100 Dictionary, None, or any user-specified Python object is allowed).
\r
102 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=None):
\r
103 wxpg.PyProperty.__init__(self, label, name)
\r
104 self.SetValue(value)
\r
106 def GetClassName(self):
\r
107 return self.__class__.__name__
\r
109 def GetEditor(self):
\r
112 def GetValueAsString(self, flags):
\r
113 return repr(self.GetValue())
\r
115 def PyStringToValue(self, s, flags):
\r
116 return PyObjectPropertyValue(s)
\r
119 class ShapeProperty(wxpg.PyEnumProperty):
\r
121 Demonstrates use of OnCustomPaint method.
\r
123 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=-1):
\r
124 wxpg.PyEnumProperty.__init__(self, label, name, ['Line','Circle','Rectangle'], [0,1,2], value)
\r
126 def OnMeasureImage(self, index):
\r
127 return wxpg.DEFAULT_IMAGE_SIZE
\r
129 def OnCustomPaint(self, dc, rect, paint_data):
\r
131 paint_data.m_choiceItem is -1 if we are painting the control,
\r
132 in which case we need to get the drawn item using DoGetValue.
\r
134 item = paint_data.m_choiceItem
\r
136 item = self.DoGetValue()
\r
138 dc.SetPen(wx.Pen(wx.BLACK))
\r
139 dc.SetBrush(wx.Brush(wx.BLACK))
\r
142 dc.DrawLine(rect.x,rect.y,rect.x+rect.width,rect.y+rect.height)
\r
144 half_width = rect.width / 2
\r
145 dc.DrawCircle(rect.x+half_width,rect.y+half_width,half_width-3)
\r
147 dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
\r
150 class LargeImagePickerCtrl(wx.Window):
\r
152 Control created and used by LargeImageEditor.
\r
154 def __init__(self):
\r
155 pre = wx.PreWindow()
\r
156 self.PostCreate(pre)
\r
158 def Create(self, parent, id_, pos, size, style = 0):
\r
159 wx.Window.Create(self, parent, id_, pos, size, style | wx.BORDER_SIMPLE)
\r
161 self.tc = wx.TextCtrl(self, -1, "", (img_spc,0), (2048,size[1]), wx.BORDER_NONE)
\r
162 self.SetBackgroundColour(wx.WHITE)
\r
163 self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
\r
164 self.property = None
\r
166 self.Bind(wx.EVT_PAINT, self.OnPaint)
\r
168 def OnPaint(self, event):
\r
169 dc = wx.BufferedPaintDC(self)
\r
171 whiteBrush = wx.Brush(wx.WHITE)
\r
172 dc.SetBackground(whiteBrush)
\r
177 dc.DrawBitmap(bmp, 2, 2)
\r
179 dc.SetPen(wx.Pen(wx.BLACK))
\r
180 dc.SetBrush(whiteBrush)
\r
181 dc.DrawRectangle(2, 2, 64, 64)
\r
183 def RefreshThumbnail(self):
\r
185 We use here very simple image scaling code.
\r
187 if not self.property:
\r
191 path = self.property.DoGetValue()
\r
193 if not os.path.isfile(path):
\r
197 image = wx.Image(path)
\r
198 image.Rescale(64, 64)
\r
199 self.bmp = wx.BitmapFromImage(image)
\r
201 def SetProperty(self, property):
\r
202 self.property = property
\r
203 self.tc.SetValue(property.GetDisplayedString())
\r
204 self.RefreshThumbnail()
\r
206 def SetValue(self, s):
\r
207 self.RefreshThumbnail()
\r
208 self.tc.SetValue(s)
\r
210 def GetLastPosition(self):
\r
211 return self.tc.GetLastPosition()
\r
214 class LargeImageEditor(wxpg.PyEditor):
\r
216 Double-height text-editor with image in front.
\r
218 def __init__(self):
\r
219 wxpg.PyEditor.__init__(self)
\r
221 def CreateControls(self, propgrid, property, pos, sz):
\r
224 x = propgrid.GetSplitterPosition()
\r
225 x2 = propgrid.GetClientSize().x
\r
226 bw = propgrid.GetRowHeight()
\r
227 lipc = LargeImagePickerCtrl()
\r
228 if sys.platform == 'win32':
\r
230 lipc.Create(propgrid, wxpg.PG_SUBID1, (x,pos[1]), (x2-x-bw,h))
\r
231 lipc.SetProperty(property)
\r
232 # Hmmm.. how to have two-stage creation without subclassing?
\r
233 #btn = wx.PreButton()
\r
234 #pre = wx.PreWindow()
\r
235 #self.PostCreate(pre)
\r
236 #if sys.platform == 'win32':
\r
238 #btn.Create(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
\r
239 btn = wx.Button(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
\r
243 print traceback.print_exc()
\r
245 def UpdateControl(self, property, ctrl):
\r
246 ctrl.SetValue(property.GetDisplayedString())
\r
248 def DrawValue(self, dc, property, rect):
\r
249 if not (property.GetFlags() & wxpg.PG_PROP_AUTO_UNSPECIFIED):
\r
250 dc.DrawText( property.GetDisplayedString(), rect.x+5, rect.y );
\r
252 def OnEvent(self, propgrid, ctrl, event):
\r
256 evtType = event.GetEventType()
\r
258 if evtType == wx.wxEVT_COMMAND_TEXT_ENTER:
\r
259 if propgrid.IsEditorsValueModified():
\r
262 elif evtType == wx.wxEVT_COMMAND_TEXT_UPDATED:
\r
263 if not property.HasFlag(wxpg.PG_PROP_AUTO_UNSPECIFIED) or not ctrl or \
\r
264 ctrl.GetLastPosition() > 0:
\r
266 # We must check this since an 'empty' text event
\r
267 # may be triggered when creating the property.
\r
268 PG_FL_IN_SELECT_PROPERTY = 0x00100000
\r
269 if not (propgrid.GetInternalFlags() & PG_FL_IN_SELECT_PROPERTY):
\r
271 event.SetId(propgrid.GetId());
\r
273 propgrid.EditorsValueWasModified();
\r
278 def CopyValueFromControl(self, property, ctrl):
\r
280 res = property.SetValueFromString(tc.GetValue(),0)
\r
281 # Changing unspecified always causes event (returning
\r
282 # true here should be enough to trigger it).
\r
283 if not res and property.IsFlagSet(wxpg.PG_PROP_AUTO_UNSPECIFIED):
\r
288 def SetValueToUnspecified(self, ctrl):
\r
289 ctrl.tc.Remove(0,len(ctrl.tc.GetValue()));
\r
291 def SetControlStringValue(self, ctrl, txt):
\r
294 def OnFocus(self, property, ctrl):
\r
295 ctrl.tc.SetSelection(-1,-1)
\r
299 class PropertyEditor(wx.Panel):
\r
301 def __init__(self, parent):
\r
302 # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
\r
303 wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(160, 200))
\r
305 sizer = wx.BoxSizer(wx.VERTICAL)
\r
307 self.pg = wxpg.PropertyGrid(self, style=wxpg.PG_SPLITTER_AUTO_CENTER|wxpg.PG_AUTO_SORT)
\r
309 # Show help as tooltips
\r
310 self.pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS)
\r
312 #pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange)
\r
313 #pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)
\r
314 #self.pg.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick)
\r
316 # Needed by custom image editor
\r
317 wx.InitAllImageHandlers()
\r
320 # Let's create a simple custom editor
\r
322 # NOTE: Editor must be registered *before* adding a property that uses it.
\r
323 self.pg.RegisterEditor(LargeImageEditor)
\r
330 pg.Append( wxpg.PropertyCategory("1 - Basic Properties") )
\r
331 pg.Append( wxpg.StringProperty("String",value="Some Text") )
\r
332 pg.Append( wxpg.IntProperty("Int",value=100) )
\r
333 pg.Append( wxpg.FloatProperty("Float",value=100.0) )
\r
334 pg.Append( wxpg.BoolProperty("Bool",value=True) )
\r
335 pg.Append( wxpg.BoolProperty("Bool_with_Checkbox",value=True) )
\r
336 pg.SetPropertyAttribute("Bool_with_Checkbox", "UseCheckbox", True)
\r
338 pg.Append( wxpg.PropertyCategory("2 - More Properties") )
\r
339 pg.Append( wxpg.LongStringProperty("LongString",value="This is a\\nmulti-line string\\nwith\\ttabs\\nmixed\\tin.") )
\r
340 pg.Append( wxpg.DirProperty("Dir",value="C:\\Windows") )
\r
341 pg.Append( wxpg.FileProperty("File",value="C:\\Windows\\system.ini") )
\r
342 pg.Append( wxpg.ArrayStringProperty("ArrayString",value=['A','B','C']) )
\r
344 pg.Append( wxpg.EnumProperty("Enum","Enum",
\r
345 ['wxPython Rules','wxPython Rocks','wxPython Is The Best'],
\r
347 pg.Append( wxpg.EditEnumProperty("EditEnum","EditEnumProperty",['A','B','C'],[0,1,2],"Text Not in List") )
\r
349 pg.Append( wxpg.PropertyCategory("3 - Advanced Properties") )
\r
350 pg.Append( wxpg.DateProperty("Date",value=wx.DateTime_Now()) )
\r
351 pg.Append( wxpg.FontProperty("Font",value=self.GetFont()) )
\r
352 pg.Append( wxpg.ColourProperty("Colour",value=self.GetBackgroundColour()) )
\r
353 pg.Append( wxpg.SystemColourProperty("SystemColour") )
\r
354 pg.Append( wxpg.ImageFileProperty("ImageFile") )
\r
355 pg.Append( wxpg.MultiChoiceProperty("MultiChoice",choices=['wxWidgets','QT','GTK+']) )
\r
357 pg.Append( wxpg.PropertyCategory("4 - Additional Properties") )
\r
358 pg.Append( wxpg.PointProperty("Point",value=self.GetPosition()) )
\r
359 pg.Append( wxpg.SizeProperty("Size",value=self.GetSize()) )
\r
360 pg.Append( wxpg.FontDataProperty("FontData") )
\r
361 pg.Append( wxpg.IntProperty("IntWithSpin",value=256) )
\r
362 pg.SetPropertyEditor("IntWithSpin","SpinCtrl")
\r
363 pg.Append( wxpg.DirsProperty("Dirs",value=['C:/Lib','C:/Bin']) )
\r
364 pg.SetPropertyHelpString( "String", "String Property help string!" )
\r
365 pg.SetPropertyHelpString( "Dirs", "Dirs Property help string!" )
\r
367 pg.SetPropertyAttribute( "File", wxpg.PG_FILE_SHOW_FULL_PATH, 0 )
\r
368 pg.SetPropertyAttribute( "File", wxpg.PG_FILE_INITIAL_PATH, "C:\\Program Files\\Internet Explorer" )
\r
369 pg.SetPropertyAttribute( "Date", wxpg.PG_DATE_PICKER_STYLE, wx.DP_DROPDOWN|wx.DP_SHOWCENTURY )
\r
371 pg.Append( wxpg.PropertyCategory("5 - Custom Properties") )
\r
372 pg.Append( IntProperty2("IntProperty2", value=1024) )
\r
374 pg.Append( ShapeProperty("ShapeProperty", value=0) )
\r
375 pg.Append( PyObjectProperty("PyObjectProperty") )
\r
377 pg.Append( wxpg.ImageFileProperty("ImageFileWithLargeEditor") )
\r
378 pg.SetPropertyEditor("ImageFileWithLargeEditor", "LargeImageEditor")
\r
381 pg.SetPropertyClientData( "Point", 1234 )
\r
382 if pg.GetPropertyClientData( "Point" ) != 1234:
\r
383 raise ValueError("Set/GetPropertyClientData() failed")
\r
385 # Test setting unicode string
\r
386 pg.GetPropertyByName("String").SetValue(u"Some Unicode Text")
\r
389 # Test some code that *should* fail (but not crash)
\r
391 #a_ = pg.GetPropertyValue( "NotARealProperty" )
\r
392 #pg.EnableProperty( "NotAtAllRealProperty", False )
\r
393 #pg.SetPropertyHelpString( "AgaintNotARealProperty", "Dummy Help String" )
\r
399 sizer.Add(self.pg, 1, wx.EXPAND)
\r
400 self.SetSizer(sizer)
\r
401 sizer.SetSizeHints(self)
\r
403 self.SelectedTreeItem = None
\r
405 def GetPropertyValues(self):
\r
406 return self.pg.GetPropertyValues()
\r
408 def Initialize(self, properties):
\r
413 for element in properties:
\r
414 if element[1]['type'] == 'arraystring':
\r
415 elements = element[1]['elements']
\r
416 if 'value' in element[1]:
\r
417 property_value = element[1]['value']
\r
419 property_value = element[1]['default']
\r
420 #retrieve individual strings
\r
421 property_value = split(property_value, ' ')
\r
422 #remove " delimiters
\r
423 values = [value.strip('"') for value in property_value]
\r
424 pg.Append(wxpg.ArrayStringProperty(element[0], value=values))
\r
426 if element[1]['type'] == 'boolean':
\r
427 if 'value' in element[1]:
\r
428 property_value = element[1].as_bool('value')
\r
430 property_value = element[1].as_bool('default')
\r
431 property_control = wxpg.BoolProperty(element[0], value=property_value)
\r
432 pg.Append(property_control)
\r
433 pg.SetPropertyAttribute(element[0], 'UseCheckbox', True)
\r
435 #if element[0] == 'category':
\r
436 #pg.Append(wxpg.PropertyCategory(element[1]))
\r
438 if element[1]['type'] == 'color':
\r
439 if 'value' in element[1]:
\r
440 property_value = element[1]['value']
\r
442 property_value = element[1]['default']
\r
443 property_value = eval(property_value)
\r
444 pg.Append(wxpg.ColourProperty(element[0], value=property_value))
\r
446 if element[1]['type'] == 'enum':
\r
447 elements = element[1]['elements']
\r
448 if 'value' in element[1]:
\r
449 property_value = element[1]['value']
\r
451 property_value = element[1]['default']
\r
452 pg.Append(wxpg.EnumProperty(element[0], element[0], elements, [], elements.index(property_value)))
\r
454 if element[1]['type'] == 'filename':
\r
455 if 'value' in element[1]:
\r
456 property_value = element[1]['value']
\r
458 property_value = element[1]['default']
\r
459 pg.Append(wxpg.FileProperty(element[0], value=property_value))
\r
461 if element[1]['type'] == 'float':
\r
462 if 'value' in element[1]:
\r
463 property_value = element[1].as_float('value')
\r
465 property_value = element[1].as_float('default')
\r
466 property_control = wxpg.FloatProperty(element[0], value=property_value)
\r
467 pg.Append(property_control)
\r
469 if element[1]['type'] == 'folder':
\r
470 if 'value' in element[1]:
\r
471 property_value = element[1]['value']
\r
473 property_value = element[1]['default']
\r
474 pg.Append(wxpg.DirProperty(element[0], value=property_value))
\r
476 if element[1]['type'] == 'integer':
\r
477 if 'value' in element[1]:
\r
478 property_value = element[1].as_int('value')
\r
480 property_value = element[1].as_int('default')
\r
481 property_control = wxpg.IntProperty(element[0], value=property_value)
\r
482 if 'maximum' in element[1]:
\r
483 property_control.SetAttribute('Max', element[1].as_int('maximum'))
\r
484 if 'minimum' in element[1]:
\r
485 property_control.SetAttribute('Min', element[1].as_int('minimum'))
\r
486 property_control.SetAttribute('Wrap', True)
\r
487 pg.Append(property_control)
\r
488 pg.SetPropertyEditor(element[0], 'SpinCtrl')
\r
490 if element[1]['type'] == 'string':
\r
491 if 'value' in element[1]:
\r
492 property_value = element[1]['value']
\r
494 property_value = element[1]['default']
\r
495 pg.Append(wxpg.StringProperty(element[0], value=property_value))
\r
499 def OnReserved(self, event):
\r