8 import wx.propgrid as wxpg
13 property_descriptor = []
23 class IntProperty2(wxpg.PyProperty):
25 This is a simple re-implementation of wxIntProperty.
27 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=0):
28 wxpg.PyProperty.__init__(self, label, name)
31 def GetClassName(self):
33 This is not 100% necessary and in future is probably going to be
34 automated to return class name.
41 def GetValueAsString(self, flags):
42 return str(self.GetValue())
44 def PyStringToValue(self, s, flags):
47 if self.GetValue() != v:
50 if flags & wxpg.PG_REPORT_ERROR:
51 wx.MessageBox("Cannot convert '%s' into a number."%s, "Error")
54 def PyIntToValue(self, v, flags):
55 if (self.GetValue() != v):
59 class PyFilesProperty(wxpg.PyArrayStringProperty):
60 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=[]):
61 wxpg.PyArrayStringProperty.__init__(self, label, name, value)
64 def OnSetValue(self, v):
66 self.display = ', '.join(self.value)
68 def GetValueAsString(self, argFlags):
71 def PyStringToValue(self, s, flags):
72 return [a.strip() for a in text.split(',')]
74 def OnEvent(self, propgrid, ctrl, event):
75 if event.GetEventType() == wx.wxEVT_COMMAND_BUTTON_CLICKED:
76 # Show dialog to select a string, call DoSetValue and
77 # return True, if value changed.
83 class PyObjectPropertyValue:
85 Value type of our sample PyObjectProperty. We keep a simple dash-delimited
86 list of string given as argument to constructor.
88 def __init__(self, s=None):
90 self.ls = [a.strip() for a in s.split('-')]
95 return ' - '.join(self.ls)
99 class PyObjectProperty(wxpg.PyProperty):
101 Another simple example. This time our value is a PyObject (NOTE: we can't
102 return an arbitrary python object in DoGetValue. It cannot be a simple
103 type such as int, bool, double, or string, nor an array or wxObject based.
104 Dictionary, None, or any user-specified Python object is allowed).
106 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=None):
107 wxpg.PyProperty.__init__(self, label, name)
110 def GetClassName(self):
111 return self.__class__.__name__
116 def GetValueAsString(self, flags):
117 return repr(self.GetValue())
119 def PyStringToValue(self, s, flags):
120 return PyObjectPropertyValue(s)
123 class ShapeProperty(wxpg.PyEnumProperty):
125 Demonstrates use of OnCustomPaint method.
127 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=-1):
128 wxpg.PyEnumProperty.__init__(self, label, name, ['Line','Circle','Rectangle'], [0,1,2], value)
130 def OnMeasureImage(self, index):
131 return wxpg.DEFAULT_IMAGE_SIZE
133 def OnCustomPaint(self, dc, rect, paint_data):
135 paint_data.m_choiceItem is -1 if we are painting the control,
136 in which case we need to get the drawn item using DoGetValue.
138 item = paint_data.m_choiceItem
140 item = self.DoGetValue()
142 dc.SetPen(wx.Pen(wx.BLACK))
143 dc.SetBrush(wx.Brush(wx.BLACK))
146 dc.DrawLine(rect.x,rect.y,rect.x+rect.width,rect.y+rect.height)
148 half_width = rect.width / 2
149 dc.DrawCircle(rect.x+half_width,rect.y+half_width,half_width-3)
151 dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
154 class LargeImagePickerCtrl(wx.Window):
156 Control created and used by LargeImageEditor.
162 def Create(self, parent, id_, pos, size, style = 0):
163 wx.Window.Create(self, parent, id_, pos, size, style | wx.BORDER_SIMPLE)
165 self.tc = wx.TextCtrl(self, -1, "", (img_spc,0), (2048,size[1]), wx.BORDER_NONE)
166 self.SetBackgroundColour(wx.WHITE)
167 self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
170 self.Bind(wx.EVT_PAINT, self.OnPaint)
172 def OnPaint(self, event):
173 dc = wx.BufferedPaintDC(self)
175 whiteBrush = wx.Brush(wx.WHITE)
176 dc.SetBackground(whiteBrush)
181 dc.DrawBitmap(bmp, 2, 2)
183 dc.SetPen(wx.Pen(wx.BLACK))
184 dc.SetBrush(whiteBrush)
185 dc.DrawRectangle(2, 2, 64, 64)
187 def RefreshThumbnail(self):
189 We use here very simple image scaling code.
191 if not self.property:
195 path = self.property.DoGetValue()
197 if not os.path.isfile(path):
201 image = wx.Image(path)
202 image.Rescale(64, 64)
203 self.bmp = wx.BitmapFromImage(image)
205 def SetProperty(self, property):
206 self.property = property
207 self.tc.SetValue(property.GetDisplayedString())
208 self.RefreshThumbnail()
210 def SetValue(self, s):
211 self.RefreshThumbnail()
214 def GetLastPosition(self):
215 return self.tc.GetLastPosition()
218 class LargeImageEditor(wxpg.PyEditor):
220 Double-height text-editor with image in front.
223 wxpg.PyEditor.__init__(self)
225 def CreateControls(self, propgrid, property, pos, sz):
228 x = propgrid.GetSplitterPosition()
229 x2 = propgrid.GetClientSize().x
230 bw = propgrid.GetRowHeight()
231 lipc = LargeImagePickerCtrl()
232 if sys.platform == 'win32':
234 lipc.Create(propgrid, wxpg.PG_SUBID1, (x,pos[1]), (x2-x-bw,h))
235 lipc.SetProperty(property)
236 # Hmmm.. how to have two-stage creation without subclassing?
237 #btn = wx.PreButton()
238 #pre = wx.PreWindow()
239 #self.PostCreate(pre)
240 #if sys.platform == 'win32':
242 #btn.Create(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
243 btn = wx.Button(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
247 print traceback.print_exc()
249 def UpdateControl(self, property, ctrl):
250 ctrl.SetValue(property.GetDisplayedString())
252 def DrawValue(self, dc, property, rect):
253 if not (property.GetFlags() & wxpg.PG_PROP_UNSPECIFIED):
254 dc.DrawText( property.GetDisplayedString(), rect.x+5, rect.y );
256 def OnEvent(self, propgrid, ctrl, event):
260 evtType = event.GetEventType()
262 if evtType == wx.wxEVT_COMMAND_TEXT_ENTER:
263 if propgrid.IsEditorsValueModified():
266 elif evtType == wx.wxEVT_COMMAND_TEXT_UPDATED:
267 if not property.HasFlag(wxpg.PG_PROP_UNSPECIFIED) or not ctrl or \
268 ctrl.GetLastPosition() > 0:
270 # We must check this since an 'empty' text event
271 # may be triggered when creating the property.
272 PG_FL_IN_SELECT_PROPERTY = 0x00100000
273 if not (propgrid.GetInternalFlags() & PG_FL_IN_SELECT_PROPERTY):
275 event.SetId(propGrid.GetId());
277 propgrid.EditorsValueWasModified();
282 def CopyValueFromControl(self, property, ctrl):
284 res = property.SetValueFromString(tc.GetValue(),0)
285 # Changing unspecified always causes event (returning
286 # true here should be enough to trigger it).
287 if not res and property.IsFlagSet(wxpg.PG_PROP_UNSPECIFIED):
292 def SetValueToUnspecified(self, ctrl):
293 ctrl.tc.Remove(0,len(ctrl.tc.GetValue()));
295 def SetControlStringValue(self, ctrl, txt):
298 def OnFocus(self, property, ctrl):
299 ctrl.tc.SetSelection(-1,-1)
303 class PropertyEditor(wx.Panel):
305 def __init__(self, parent):
306 # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
307 wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(160, 200))
309 sizer = wx.BoxSizer(wx.VERTICAL)
311 self.pg = wxpg.PropertyGrid(self, style=wxpg.PG_SPLITTER_AUTO_CENTER|wxpg.PG_AUTO_SORT)
313 # Show help as tooltips
314 self.pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS)
316 #pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange)
317 #pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)
318 #self.pg.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick)
320 # Needed by custom image editor
321 wx.InitAllImageHandlers()
324 # Let's create a simple custom editor
326 # NOTE: Editor must be registered *before* adding a property that uses it.
327 self.pg.RegisterEditor(LargeImageEditor)
334 pg.Append( wxpg.PropertyCategory("1 - Basic Properties") )
335 pg.Append( wxpg.StringProperty("String",value="Some Text") )
336 pg.Append( wxpg.IntProperty("Int",value=100) )
337 pg.Append( wxpg.FloatProperty("Float",value=100.0) )
338 pg.Append( wxpg.BoolProperty("Bool",value=True) )
339 pg.Append( wxpg.BoolProperty("Bool_with_Checkbox",value=True) )
340 pg.SetPropertyAttribute("Bool_with_Checkbox", "UseCheckbox", True)
342 pg.Append( wxpg.PropertyCategory("2 - More Properties") )
343 pg.Append( wxpg.LongStringProperty("LongString",value="This is a\\nmulti-line string\\nwith\\ttabs\\nmixed\\tin.") )
344 pg.Append( wxpg.DirProperty("Dir",value="C:\\Windows") )
345 pg.Append( wxpg.FileProperty("File",value="C:\\Windows\\system.ini") )
346 pg.Append( wxpg.ArrayStringProperty("ArrayString",value=['A','B','C']) )
348 pg.Append( wxpg.EnumProperty("Enum","Enum",
349 ['wxPython Rules','wxPython Rocks','wxPython Is The Best'],
351 pg.Append( wxpg.EditEnumProperty("EditEnum","EditEnumProperty",['A','B','C'],[0,1,2],"Text Not in List") )
353 pg.Append( wxpg.PropertyCategory("3 - Advanced Properties") )
354 pg.Append( wxpg.DateProperty("Date",value=wx.DateTime_Now()) )
355 pg.Append( wxpg.FontProperty("Font",value=self.GetFont()) )
356 pg.Append( wxpg.ColourProperty("Colour",value=self.GetBackgroundColour()) )
357 pg.Append( wxpg.SystemColourProperty("SystemColour") )
358 pg.Append( wxpg.ImageFileProperty("ImageFile") )
359 pg.Append( wxpg.MultiChoiceProperty("MultiChoice",choices=['wxWidgets','QT','GTK+']) )
361 pg.Append( wxpg.PropertyCategory("4 - Additional Properties") )
362 pg.Append( wxpg.PointProperty("Point",value=self.GetPosition()) )
363 pg.Append( wxpg.SizeProperty("Size",value=self.GetSize()) )
364 pg.Append( wxpg.FontDataProperty("FontData") )
365 pg.Append( wxpg.IntProperty("IntWithSpin",value=256) )
366 pg.SetPropertyEditor("IntWithSpin","SpinCtrl")
367 pg.Append( wxpg.DirsProperty("Dirs",value=['C:/Lib','C:/Bin']) )
368 pg.SetPropertyHelpString( "String", "String Property help string!" )
369 pg.SetPropertyHelpString( "Dirs", "Dirs Property help string!" )
371 pg.SetPropertyAttribute( "File", wxpg.PG_FILE_SHOW_FULL_PATH, 0 )
372 pg.SetPropertyAttribute( "File", wxpg.PG_FILE_INITIAL_PATH, "C:\\Program Files\\Internet Explorer" )
373 pg.SetPropertyAttribute( "Date", wxpg.PG_DATE_PICKER_STYLE, wx.DP_DROPDOWN|wx.DP_SHOWCENTURY )
375 pg.Append( wxpg.PropertyCategory("5 - Custom Properties") )
376 pg.Append( IntProperty2("IntProperty2", value=1024) )
378 pg.Append( ShapeProperty("ShapeProperty", value=0) )
379 pg.Append( PyObjectProperty("PyObjectProperty") )
381 pg.Append( wxpg.ImageFileProperty("ImageFileWithLargeEditor") )
382 pg.SetPropertyEditor("ImageFileWithLargeEditor", "LargeImageEditor")
385 pg.SetPropertyClientData( "Point", 1234 )
386 if pg.GetPropertyClientData( "Point" ) != 1234:
387 raise ValueError("Set/GetPropertyClientData() failed")
389 # Test setting unicode string
390 pg.GetPropertyByName("String").SetValue(u"Some Unicode Text")
393 # Test some code that *should* fail (but not crash)
395 #a_ = pg.GetPropertyValue( "NotARealProperty" )
396 #pg.EnableProperty( "NotAtAllRealProperty", False )
397 #pg.SetPropertyHelpString( "AgaintNotARealProperty", "Dummy Help String" )
403 sizer.Add(self.pg, 1, wx.EXPAND)
405 sizer.SetSizeHints(self)
407 self.SelectedTreeItem = None
409 def GetPropertyValues(self):
410 #return self.pg.GetPropertyValues(as_strings=True)
411 return self.pg.GetPropertyValues()
414 def Initialize(self, properties):
419 for element in properties:
420 if element[1]['type'] == 'integer':
421 if 'value' in element[1]:
422 property_value = element[1].as_int('value')
424 property_value = element[1].as_int('default')
425 property_control = wxpg.IntProperty(element[0], value=property_value)
426 if 'maximum' in element[1]:
427 property_control.SetAttribute('Max', element[1].as_int('maximum'))
428 if 'minimum' in element[1]:
429 property_control.SetAttribute('Min', element[1].as_int('minimum'))
430 property_control.SetAttribute('Wrap', True)
431 pg.Append(property_control)
432 pg.SetPropertyEditor(element[0], 'SpinCtrl')
434 if element[1]['type'] == 'float':
435 if 'value' in element[1]:
436 property_value = element[1].as_float('value')
438 property_value = element[1].as_float('default')
439 property_control = wxpg.FloatProperty(element[0], value=property_value)
440 if 'maximum' in element[1]:
441 property_control.SetAttribute('Max', element[1].as_float('maximum'))
442 if 'minimum' in element[1]:
443 property_control.SetAttribute('Min', element[1].as_float('minimum'))
444 property_control.SetAttribute('Wrap', True)
445 pg.Append(property_control)
446 pg.SetPropertyEditor(element[0], 'SpinCtrl')
448 if element[1]['type'] == 'boolean':
449 if 'value' in element[1]:
450 property_value = element[1].as_bool('value')
452 property_value = element[1].as_bool('default')
453 property_control = wxpg.BoolProperty(element[0], value=property_value)
454 pg.Append(property_control)
455 pg.SetPropertyAttribute(element[0], 'UseCheckbox', True)
456 if element[1]['type'] == 'string':
457 if 'value' in element[1]:
458 property_value = element[1]['value']
460 property_value = element[1]['default']
461 pg.Append(wxpg.StringProperty(element[0], value=property_value))
462 if element[1]['type'] == 'folder':
463 if 'value' in element[1]:
464 property_value = element[1]['value']
466 property_value = element[1]['default']
467 pg.Append(wxpg.DirProperty(element[0], value=property_value))
468 if element[1]['type'] == 'filename':
469 if 'value' in element[1]:
470 property_value = element[1]['value']
472 property_value = element[1]['default']
473 pg.Append(wxpg.FileProperty(element[0], value=property_value))
474 #if element[0] == 'category':
475 #pg.Append(wxpg.PropertyCategory(element[1]))
476 #if element[0] == 'folder':
477 #pg.Append(wxpg.DirProperty(element[1], value=element[2]))
478 #if element[0] == 'string':
479 #pg.Append(wxpg.StringProperty(element[1], value=element[2]))
483 def OnReserved(self, event):
487 #---------------------------------------------------------------------------
490 class MemoDialog(wx.Dialog):
492 Dialog for multi-line text editing.
494 def __init__(self, parent=None, title='', text='', pos=None, size=(500,500)):
495 wx.Dialog.__init__(self, parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
497 sizer = wx.BoxSizer(wx.VERTICAL)
499 tc = wx.TextCtrl(self, 11, text, style=wx.TE_MULTILINE)
501 topsizer.Add(tc,1,wx.EXPAND|wx.ALL,8)
503 rowsizer = wx.BoxSizer( wx.HORIZONTAL )
504 rowsizer.Add(wx.Button(self,wx.ID_OK,'Ok'),0,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
505 rowsizer.Add((0,0),1,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
506 rowsizer.Add(wx.Button(self,wx.ID_CANCEL,'Cancel'),0,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
507 topsizer.Add(rowsizer,0,wx.EXPAND|wx.ALL,8)
509 self.SetSizer( topsizer )
514 self.CenterOnScreen()
519 #---------------------------------------------------------------------------