10 import wx.propgrid as wxpg
15 property_descriptor = []
25 class IntProperty2(wxpg.PyProperty):
27 This is a simple re-implementation of wxIntProperty.
29 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=0):
30 wxpg.PyProperty.__init__(self, label, name)
33 def GetClassName(self):
35 This is not 100% necessary and in future is probably going to be
36 automated to return class name.
43 def GetValueAsString(self, flags):
44 return str(self.GetValue())
46 def PyStringToValue(self, s, flags):
49 if self.GetValue() != v:
52 if flags & wxpg.PG_REPORT_ERROR:
53 wx.MessageBox("Cannot convert '%s' into a number."%s, "Error")
56 def PyIntToValue(self, v, flags):
57 if (self.GetValue() != v):
61 class PyFilesProperty(wxpg.PyArrayStringProperty):
62 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=[]):
63 wxpg.PyArrayStringProperty.__init__(self, label, name, value)
66 def OnSetValue(self, v):
68 self.display = ', '.join(self.value)
70 def GetValueAsString(self, argFlags):
73 def PyStringToValue(self, s, flags):
74 return [a.strip() for a in text.split(',')]
76 def OnEvent(self, propgrid, ctrl, event):
77 if event.GetEventType() == wx.wxEVT_COMMAND_BUTTON_CLICKED:
78 # Show dialog to select a string, call DoSetValue and
79 # return True, if value changed.
85 class PyObjectPropertyValue:
87 Value type of our sample PyObjectProperty. We keep a simple dash-delimited
88 list of string given as argument to constructor.
90 def __init__(self, s=None):
92 self.ls = [a.strip() for a in s.split('-')]
97 return ' - '.join(self.ls)
101 class PyObjectProperty(wxpg.PyProperty):
103 Another simple example. This time our value is a PyObject (NOTE: we can't
104 return an arbitrary python object in DoGetValue. It cannot be a simple
105 type such as int, bool, double, or string, nor an array or wxObject based.
106 Dictionary, None, or any user-specified Python object is allowed).
108 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=None):
109 wxpg.PyProperty.__init__(self, label, name)
112 def GetClassName(self):
113 return self.__class__.__name__
118 def GetValueAsString(self, flags):
119 return repr(self.GetValue())
121 def PyStringToValue(self, s, flags):
122 return PyObjectPropertyValue(s)
125 class ShapeProperty(wxpg.PyEnumProperty):
127 Demonstrates use of OnCustomPaint method.
129 def __init__(self, label, name = wxpg.LABEL_AS_NAME, value=-1):
130 wxpg.PyEnumProperty.__init__(self, label, name, ['Line','Circle','Rectangle'], [0,1,2], value)
132 def OnMeasureImage(self, index):
133 return wxpg.DEFAULT_IMAGE_SIZE
135 def OnCustomPaint(self, dc, rect, paint_data):
137 paint_data.m_choiceItem is -1 if we are painting the control,
138 in which case we need to get the drawn item using DoGetValue.
140 item = paint_data.m_choiceItem
142 item = self.DoGetValue()
144 dc.SetPen(wx.Pen(wx.BLACK))
145 dc.SetBrush(wx.Brush(wx.BLACK))
148 dc.DrawLine(rect.x,rect.y,rect.x+rect.width,rect.y+rect.height)
150 half_width = rect.width / 2
151 dc.DrawCircle(rect.x+half_width,rect.y+half_width,half_width-3)
153 dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height)
156 class LargeImagePickerCtrl(wx.Window):
158 Control created and used by LargeImageEditor.
164 def Create(self, parent, id_, pos, size, style = 0):
165 wx.Window.Create(self, parent, id_, pos, size, style | wx.BORDER_SIMPLE)
167 self.tc = wx.TextCtrl(self, -1, "", (img_spc,0), (2048,size[1]), wx.BORDER_NONE)
168 self.SetBackgroundColour(wx.WHITE)
169 self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
172 self.Bind(wx.EVT_PAINT, self.OnPaint)
174 def OnPaint(self, event):
175 dc = wx.BufferedPaintDC(self)
177 whiteBrush = wx.Brush(wx.WHITE)
178 dc.SetBackground(whiteBrush)
183 dc.DrawBitmap(bmp, 2, 2)
185 dc.SetPen(wx.Pen(wx.BLACK))
186 dc.SetBrush(whiteBrush)
187 dc.DrawRectangle(2, 2, 64, 64)
189 def RefreshThumbnail(self):
191 We use here very simple image scaling code.
193 if not self.property:
197 path = self.property.DoGetValue()
199 if not os.path.isfile(path):
203 image = wx.Image(path)
204 image.Rescale(64, 64)
205 self.bmp = wx.BitmapFromImage(image)
207 def SetProperty(self, property):
208 self.property = property
209 self.tc.SetValue(property.GetDisplayedString())
210 self.RefreshThumbnail()
212 def SetValue(self, s):
213 self.RefreshThumbnail()
216 def GetLastPosition(self):
217 return self.tc.GetLastPosition()
220 class LargeImageEditor(wxpg.PyEditor):
222 Double-height text-editor with image in front.
225 wxpg.PyEditor.__init__(self)
227 def CreateControls(self, propgrid, property, pos, sz):
230 x = propgrid.GetSplitterPosition()
231 x2 = propgrid.GetClientSize().x
232 bw = propgrid.GetRowHeight()
233 lipc = LargeImagePickerCtrl()
234 if sys.platform == 'win32':
236 lipc.Create(propgrid, wxpg.PG_SUBID1, (x,pos[1]), (x2-x-bw,h))
237 lipc.SetProperty(property)
238 # Hmmm.. how to have two-stage creation without subclassing?
239 #btn = wx.PreButton()
240 #pre = wx.PreWindow()
241 #self.PostCreate(pre)
242 #if sys.platform == 'win32':
244 #btn.Create(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
245 btn = wx.Button(propgrid, wxpg.PG_SUBID2, '...', (x2-bw,pos[1]), (bw,h), wx.WANTS_CHARS)
249 print traceback.print_exc()
251 def UpdateControl(self, property, ctrl):
252 ctrl.SetValue(property.GetDisplayedString())
254 def DrawValue(self, dc, property, rect):
255 if not (property.GetFlags() & wxpg.PG_PROP_UNSPECIFIED):
256 dc.DrawText( property.GetDisplayedString(), rect.x+5, rect.y );
258 def OnEvent(self, propgrid, ctrl, event):
262 evtType = event.GetEventType()
264 if evtType == wx.wxEVT_COMMAND_TEXT_ENTER:
265 if propgrid.IsEditorsValueModified():
268 elif evtType == wx.wxEVT_COMMAND_TEXT_UPDATED:
269 if not property.HasFlag(wxpg.PG_PROP_UNSPECIFIED) or not ctrl or \
270 ctrl.GetLastPosition() > 0:
272 # We must check this since an 'empty' text event
273 # may be triggered when creating the property.
274 PG_FL_IN_SELECT_PROPERTY = 0x00100000
275 if not (propgrid.GetInternalFlags() & PG_FL_IN_SELECT_PROPERTY):
277 event.SetId(propGrid.GetId());
279 propgrid.EditorsValueWasModified();
284 def CopyValueFromControl(self, property, ctrl):
286 res = property.SetValueFromString(tc.GetValue(),0)
287 # Changing unspecified always causes event (returning
288 # true here should be enough to trigger it).
289 if not res and property.IsFlagSet(wxpg.PG_PROP_UNSPECIFIED):
294 def SetValueToUnspecified(self, ctrl):
295 ctrl.tc.Remove(0,len(ctrl.tc.GetValue()));
297 def SetControlStringValue(self, ctrl, txt):
300 def OnFocus(self, property, ctrl):
301 ctrl.tc.SetSelection(-1,-1)
305 class PropertyEditor(wx.Panel):
307 def __init__(self, parent):
308 # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
309 wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS, size=(160, 200))
311 sizer = wx.BoxSizer(wx.VERTICAL)
313 self.pg = wxpg.PropertyGrid(self, style=wxpg.PG_SPLITTER_AUTO_CENTER|wxpg.PG_AUTO_SORT)
315 # Show help as tooltips
316 self.pg.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS)
318 #pg.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange)
319 #pg.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect)
320 #self.pg.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick)
322 # Needed by custom image editor
323 wx.InitAllImageHandlers()
326 # Let's create a simple custom editor
328 # NOTE: Editor must be registered *before* adding a property that uses it.
329 self.pg.RegisterEditor(LargeImageEditor)
336 pg.Append( wxpg.PropertyCategory("1 - Basic Properties") )
337 pg.Append( wxpg.StringProperty("String",value="Some Text") )
338 pg.Append( wxpg.IntProperty("Int",value=100) )
339 pg.Append( wxpg.FloatProperty("Float",value=100.0) )
340 pg.Append( wxpg.BoolProperty("Bool",value=True) )
341 pg.Append( wxpg.BoolProperty("Bool_with_Checkbox",value=True) )
342 pg.SetPropertyAttribute("Bool_with_Checkbox", "UseCheckbox", True)
344 pg.Append( wxpg.PropertyCategory("2 - More Properties") )
345 pg.Append( wxpg.LongStringProperty("LongString",value="This is a\\nmulti-line string\\nwith\\ttabs\\nmixed\\tin.") )
346 pg.Append( wxpg.DirProperty("Dir",value="C:\\Windows") )
347 pg.Append( wxpg.FileProperty("File",value="C:\\Windows\\system.ini") )
348 pg.Append( wxpg.ArrayStringProperty("ArrayString",value=['A','B','C']) )
350 pg.Append( wxpg.EnumProperty("Enum","Enum",
351 ['wxPython Rules','wxPython Rocks','wxPython Is The Best'],
353 pg.Append( wxpg.EditEnumProperty("EditEnum","EditEnumProperty",['A','B','C'],[0,1,2],"Text Not in List") )
355 pg.Append( wxpg.PropertyCategory("3 - Advanced Properties") )
356 pg.Append( wxpg.DateProperty("Date",value=wx.DateTime_Now()) )
357 pg.Append( wxpg.FontProperty("Font",value=self.GetFont()) )
358 pg.Append( wxpg.ColourProperty("Colour",value=self.GetBackgroundColour()) )
359 pg.Append( wxpg.SystemColourProperty("SystemColour") )
360 pg.Append( wxpg.ImageFileProperty("ImageFile") )
361 pg.Append( wxpg.MultiChoiceProperty("MultiChoice",choices=['wxWidgets','QT','GTK+']) )
363 pg.Append( wxpg.PropertyCategory("4 - Additional Properties") )
364 pg.Append( wxpg.PointProperty("Point",value=self.GetPosition()) )
365 pg.Append( wxpg.SizeProperty("Size",value=self.GetSize()) )
366 pg.Append( wxpg.FontDataProperty("FontData") )
367 pg.Append( wxpg.IntProperty("IntWithSpin",value=256) )
368 pg.SetPropertyEditor("IntWithSpin","SpinCtrl")
369 pg.Append( wxpg.DirsProperty("Dirs",value=['C:/Lib','C:/Bin']) )
370 pg.SetPropertyHelpString( "String", "String Property help string!" )
371 pg.SetPropertyHelpString( "Dirs", "Dirs Property help string!" )
373 pg.SetPropertyAttribute( "File", wxpg.PG_FILE_SHOW_FULL_PATH, 0 )
374 pg.SetPropertyAttribute( "File", wxpg.PG_FILE_INITIAL_PATH, "C:\\Program Files\\Internet Explorer" )
375 pg.SetPropertyAttribute( "Date", wxpg.PG_DATE_PICKER_STYLE, wx.DP_DROPDOWN|wx.DP_SHOWCENTURY )
377 pg.Append( wxpg.PropertyCategory("5 - Custom Properties") )
378 pg.Append( IntProperty2("IntProperty2", value=1024) )
380 pg.Append( ShapeProperty("ShapeProperty", value=0) )
381 pg.Append( PyObjectProperty("PyObjectProperty") )
383 pg.Append( wxpg.ImageFileProperty("ImageFileWithLargeEditor") )
384 pg.SetPropertyEditor("ImageFileWithLargeEditor", "LargeImageEditor")
387 pg.SetPropertyClientData( "Point", 1234 )
388 if pg.GetPropertyClientData( "Point" ) != 1234:
389 raise ValueError("Set/GetPropertyClientData() failed")
391 # Test setting unicode string
392 pg.GetPropertyByName("String").SetValue(u"Some Unicode Text")
395 # Test some code that *should* fail (but not crash)
397 #a_ = pg.GetPropertyValue( "NotARealProperty" )
398 #pg.EnableProperty( "NotAtAllRealProperty", False )
399 #pg.SetPropertyHelpString( "AgaintNotARealProperty", "Dummy Help String" )
405 sizer.Add(self.pg, 1, wx.EXPAND)
407 sizer.SetSizeHints(self)
409 self.SelectedTreeItem = None
411 def GetPropertyValues(self):
412 #return self.pg.GetPropertyValues(as_strings=True)
413 return self.pg.GetPropertyValues()
416 def Initialize(self, properties):
421 for element in properties:
422 if element[1]['type'] == 'integer':
423 if 'value' in element[1]:
424 property_value = element[1].as_int('value')
426 property_value = element[1].as_int('default')
427 property_control = wxpg.IntProperty(element[0], value=property_value)
428 if 'maximum' in element[1]:
429 property_control.SetAttribute('Max', element[1].as_int('maximum'))
430 if 'minimum' in element[1]:
431 property_control.SetAttribute('Min', element[1].as_int('minimum'))
432 property_control.SetAttribute('Wrap', True)
433 pg.Append(property_control)
434 pg.SetPropertyEditor(element[0], 'SpinCtrl')
436 if element[1]['type'] == 'float':
437 if 'value' in element[1]:
438 property_value = element[1].as_float('value')
440 property_value = element[1].as_float('default')
441 property_control = wxpg.FloatProperty(element[0], value=property_value)
442 if 'maximum' in element[1]:
443 property_control.SetAttribute('Max', element[1].as_float('maximum'))
444 if 'minimum' in element[1]:
445 property_control.SetAttribute('Min', element[1].as_float('minimum'))
446 property_control.SetAttribute('Wrap', True)
447 pg.Append(property_control)
448 pg.SetPropertyEditor(element[0], 'SpinCtrl')
450 if element[1]['type'] == 'boolean':
451 if 'value' in element[1]:
452 property_value = element[1].as_bool('value')
454 property_value = element[1].as_bool('default')
455 property_control = wxpg.BoolProperty(element[0], value=property_value)
456 pg.Append(property_control)
457 pg.SetPropertyAttribute(element[0], 'UseCheckbox', True)
458 if element[1]['type'] == 'string':
459 if 'value' in element[1]:
460 property_value = element[1]['value']
462 property_value = element[1]['default']
463 pg.Append(wxpg.StringProperty(element[0], value=property_value))
464 if element[1]['type'] == 'folder':
465 if 'value' in element[1]:
466 property_value = element[1]['value']
468 property_value = element[1]['default']
469 pg.Append(wxpg.DirProperty(element[0], value=property_value))
470 if element[1]['type'] == 'filename':
471 if 'value' in element[1]:
472 property_value = element[1]['value']
474 property_value = element[1]['default']
475 pg.Append(wxpg.FileProperty(element[0], value=property_value))
476 #if element[0] == 'category':
477 #pg.Append(wxpg.PropertyCategory(element[1]))
478 #if element[0] == 'folder':
479 #pg.Append(wxpg.DirProperty(element[1], value=element[2]))
480 #if element[0] == 'string':
481 #pg.Append(wxpg.StringProperty(element[1], value=element[2]))
485 def OnReserved(self, event):
489 #---------------------------------------------------------------------------
492 class MemoDialog(wx.Dialog):
494 Dialog for multi-line text editing.
496 def __init__(self, parent=None, title='', text='', pos=None, size=(500,500)):
497 wx.Dialog.__init__(self, parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
499 sizer = wx.BoxSizer(wx.VERTICAL)
501 tc = wx.TextCtrl(self, 11, text, style=wx.TE_MULTILINE)
503 topsizer.Add(tc,1,wx.EXPAND|wx.ALL,8)
505 rowsizer = wx.BoxSizer( wx.HORIZONTAL )
506 rowsizer.Add(wx.Button(self,wx.ID_OK,'Ok'),0,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
507 rowsizer.Add((0,0),1,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
508 rowsizer.Add(wx.Button(self,wx.ID_CANCEL,'Cancel'),0,wx.ALIGN_RIGHT|wx.ALIGN_CENTRE_VERTICAL,8)
509 topsizer.Add(rowsizer,0,wx.EXPAND|wx.ALL,8)
511 self.SetSizer( topsizer )
516 self.CenterOnScreen()
521 #---------------------------------------------------------------------------