4
__version__ = "$Revision: 1.2 $"
5
__date__ = "$Date: 2004/12/31 20:44:29 $"
8
from PythonCard import dialog, font, model, registry, util
9
from PythonCard.event import ChangeListener
17
# KEA this is a load of dingos' kidneys and needs to be rewritten
19
# now I'm compounding the problem by porting from the original
20
# Property Editor to a PythonCard background
21
class PropertyEditor(model.Background, ChangeListener):
23
def on_initialize(self, event):
24
self._parent = self.GetParent()
25
self._comp = self._parent.components
26
self._updatingComponent = 0
27
self.autoAttributeUpdate = True
28
##self.components.addChangeEventListener(self)
29
self._comp.addChangeEventListener(self)
31
self.checkItems = ['enabled', 'visible', 'editable', 'checked', 'default', \
32
'rules', 'labels', 'ticks', 'horizontalScrollbar']
33
self.popItems = ['layout', 'border', 'style', 'alignment', 'stringSelection']
34
self.cantModify = ['id', 'name', 'alignment', 'layout', 'style', 'border', \
35
'horizontalScrollbar', 'min', 'max', 'columns', 'rules', 'labels', 'ticks']
37
self.editItems = [self.components.wField, self.components.wColor,
38
self.components.wFont, self.components.wTextArea,
39
self.components.wChecked, self.components.wPop,
40
self.components.wFile,]
43
# this was causing an assertion error with the hybrid wxPython
44
#self.components.wComponentList.SetSelection(0)
45
if self.components.wComponentList.stringSelection == "":
48
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
49
self.setValidProps(wClass)
51
#self.displayComponents(self.components)
52
self.displayComponents(self._comp)
56
# support updating of attributes without the need
57
# for clicking the Update button
58
def on_wField_closeField(self, event):
59
if self.autoAttributeUpdate:
60
self.updateComponent()
62
def on_wTextArea_closeField(self, event):
63
if self.autoAttributeUpdate:
64
self.updateComponent()
66
def on_wChecked_mouseClick(self, event):
67
if self.autoAttributeUpdate:
68
self.updateComponent()
70
def on_wPop_select(self, event):
71
if self.autoAttributeUpdate:
72
self.updateComponent()
74
def on_wColor_mouseClick(self, event):
75
result = dialog.colorDialog(self, color=util.colorFromString(self.components.wField.text))
77
self.components.wField.text = str(result.color)
78
if self.autoAttributeUpdate:
79
self.updateComponent()
81
def on_wFont_mouseClick(self, event):
82
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
83
##widget = self.components[wName]
84
widget = self._comp[wName]
87
desc = font.fontDescription(widget.GetFont())
89
result = dialog.fontDialog(self, f)
91
#color = dlg.getColor()
93
#self.components.wField.SetValue("%s;%s" % (f, color))
94
self.components.wField.text = "%s" % f
95
if self.autoAttributeUpdate:
96
self.updateComponent()
98
def on_wFile_mouseClick(self, event):
99
path, filename = os.path.split(self.components.wField.text)
100
result = dialog.openFileDialog(self, directory=path, filename=filename)
102
self.components.wField.text = util.relativePath(self._parent.filename, result.paths[0])
103
if self.autoAttributeUpdate:
104
self.updateComponent()
107
def addWidgetToComponentList(self, widget):
110
# just use __name__, the other code must have been something from wxPython 2.3
111
#wClass = str(widget.__class__).split('.')
112
#self.components.wComponentList.Append(wName + " : " + wClass[len(wClass) - 1])
113
wClass = widget.__class__.__name__
114
self.components.wComponentList.Append(wName + " : " + wClass)
117
# need to redo the logic below to avoid asserts in hybrid
118
# versions of wxPython, but also be cleaner
119
def deleteWidgetFromComponentList(self, wName, wClass):
120
i = self.components.wComponentList.GetSelection()
121
j = self.components.wComponentList.FindString(wName + " : " + wClass)
122
if i == -1 or i != j:
124
self.components.wComponentList.Delete(j)
127
self.components.wComponentList.SetSelection(j - 1)
129
self.components.wComponentList.Delete(j)
130
if self.components.wComponentList.GetSelection() == -1:
131
self.setValidProps("")
132
self.hideAllBut(self.components.wField)
134
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
135
# deselect the name from properties list
136
self.setValidProps(wClass)
137
propName = self.components.wPropertyList.stringSelection
140
self.components.wPropertyList.stringSelection = "name"
141
self.displayProperty(wName, wClass, propName)
143
def selectComponentList(self, wName, wClass):
144
self.components.wComponentList.stringSelection = wName + " : " + wClass
145
self.setValidProps(wClass)
146
propName = self.components.wPropertyList.stringSelection
150
self.components.wPropertyList.stringSelection = "name"
151
self.displayProperty(wName, wClass, propName)
152
c = self._parent.components[wName]
153
if hasattr(c, 'position'):
155
# Don't set a tooltip pos for components that don't have a pos - they aren't displayed.
156
self._parent.setToolTipDrag(wName, c.position, c.size)
158
def changed(self, event):
159
##comp = self.components
160
if self._updatingComponent:
162
# hack to speed up updates in place
166
wName, wClass = event.getOldValue().split(",")
169
self.addWidgetToComponentList(comp[wName])
172
self.deleteWidgetFromComponentList(wName, wClass)
175
def on_wCopy_mouseClick(self, event):
176
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
177
# what needs to happen here is to have a method for the Widget class that
178
# will provide a valid resource description, each subclass of widget would
179
# override the method to deal with their specific resource attributes
180
# the Widget class should provide some ordering so that 'type',
181
# 'position', 'size' comes before less commonly used items, the actual
182
# ordering could just be defined in a list, so it is easy to change
183
# also, if the current values match the defaults for a widget attribute
184
# then that attribute should not be provided as part of the output
185
print "this is just a placeholder method right now,"
186
print "the resource is not actually copied to the clipboard yet"
187
pprint.pprint(self._comp[wName])
190
def on_wUpdate_mouseClick(self, event):
191
self.updateComponent()
193
def updateComponent(self):
194
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
195
propName = self.components.wPropertyList.stringSelection
197
if propName in self.checkItems:
198
value = self.components.wChecked.checked
199
elif propName in self.popItems:
200
value = self.components.wPop.stringSelection
201
elif propName in ('items', 'userdata') or (wClass == 'TextArea' and propName == 'text'):
202
value = self.components.wTextArea.text
204
#value = self.components.wField.GetValue()
205
value = self.components.wField.text
207
if propName not in ['label', 'stringSelection', 'text', 'toolTip', 'userdata']:
214
# need to figure out where to stick validation code
215
# but for now just need to make sure that if we're changing the name
216
# attribute that it is valid, but similar checks will be necessary for
217
# integer fields, a list of items, etc.
218
# also maybe each attribute should have a doc or help string displayed
219
# saying what the attribute does, example values, etc.
220
if propName == 'name':
222
# if it isn't valid then display an alert and exit
223
# must start with a letter and only contain alphanumeric characters
224
if value == "" or value[0] not in string.ascii_letters:
227
alphanumeric = string.ascii_letters + string.digits
229
if c not in alphanumeric:
233
dialog.alertDialog(None, "Name must start with a letter and only contain letters and numbers.", 'Error: Name is invalid')
234
self.components.wField.setFocus()
235
self.components.wField.setSelection(-1, -1)
237
# check for duplicate names is done below
239
##widget = self.components[wName]
240
widget = self._comp[wName]
243
# I can't remember why this is actually necessary
244
if propName == 'size':
245
width, height = value
246
if wClass not in ['BitmapCanvas', 'HtmlWindow']:
247
bestWidth, bestHeight = widget.GetBestSize()
252
widget.size = (width, height)
253
#setattr(widget, propName, (width, height))
254
#print widget.size, propName, width, height
256
if (propName in self.cantModify) or \
257
(propName == 'items' and wClass == 'RadioGroup'):
258
order = self._comp.order.index(wName)
259
desc = resourceOutput.widgetAttributes(self._parent, widget)
260
if desc.endswith(',\n'):
261
desc = eval(desc[:-2])
265
if propName == 'name':
267
# user didn't actually change the name
269
elif value in self._comp:
270
# we already have a component with that name
271
dialog.alertDialog(self, 'Another component already exists with the name ' + value,
272
'Error: unable to rename component')
275
desc[propName] = 'none'
276
elif propName in ['min', 'max']:
277
desc[propName] = int(value)
279
desc[propName] = value
281
# need to experiment with freeze and thaw to avoid
282
# a lot of update events
283
startTime = time.time()
285
# this is going to trigger a changed event
286
# as we delete the old component
287
self._updatingComponent = True
288
del self._comp[wName]
289
if propName == 'name':
291
# this is going to trigger another changed event
292
# as we create a new component with the changed attribute
293
self._comp[wName] = desc
294
c = self._comp[wName]
295
wx.EVT_LEFT_DOWN(c, self._parent.on_mouseDown)
296
wx.EVT_LEFT_UP(c, self._parent.on_mouseUp)
297
wx.EVT_MOTION(c, self._parent.on_mouseDrag)
300
# now restore the order of the component
301
# have to update the startName in case the name was updated
302
if propName == 'name':
303
self._parent.startName = wName
304
self._comp.order.remove(wName)
305
self._comp.order.insert(order, wName)
307
self._parent.fixComponentOrder(wName)
309
self._updatingComponent = False
311
endTime = time.time()
312
#print "attribute change took:", endTime - startTime
314
if wClass in ['Image', 'ImageButton'] and propName == 'file':
317
os.chdir(self._parent.filename)
320
setattr(widget, propName, value)
323
setattr(widget, propName, value)
324
#print propName, value
326
self._parent.showSizingHandles(wName)
328
def setValidProps(self, wClass):
329
#print "setValidProps", wClass
330
oldProp = self.components.wPropertyList.stringSelection
332
self.components.wPropertyList.Clear()
334
##props = self.propList + self.optionalProps[wClass]
336
##print "props", props
337
# get the property (attribute) list from the spec
338
klass = registry.Registry.getInstance().getComponentClass(wClass)
339
props = klass._spec.getAttributes().keys()
342
# only show the 'id' attribute for Button
343
# and only when displaying dialog properties
344
if not (self._parent.editingDialog and wClass == 'Button'):
346
# The id attrib is optional, and may not be present. All widgets will have
347
# IDs, but Components may not. If it is not present, then we don't need to remove it.
352
# Don't show the action bindings at all. Edit them only via the action binding dialog.
353
if 'actionBindings' in props:
354
props.remove('actionBindings')
356
##print "spec props", specProps
358
self.components.wPropertyList.Clear()
359
self.components.wPropertyList.InsertItems(props, 0)
361
self.components.wPropertyList.stringSelection = oldProp
363
def hideAllBut(self, widget):
364
for w in self.editItems:
365
if widget.id != w.id:
368
def displayProperty(self, wName, wClass, propName):
369
self.components.wName.text = propName + ":"
370
##widget = self.components[wName]
371
widget = self._comp[wName]
372
if propName in ['label', 'stringSelection', 'text', 'toolTip'] or propName in self.checkItems:
373
value = getattr(widget, propName)
375
value = str(getattr(widget, propName))
377
if propName in self.checkItems:
378
self.hideAllBut(self.components.wChecked)
379
self.components.wChecked.visible = True
380
self.components.wChecked.checked = value
381
elif propName in self.popItems:
382
self.hideAllBut(self.components.wPop)
383
self.components.wPop.visible = True
384
self.components.wPop.Clear()
385
if propName == 'stringSelection':
386
for v in widget.items:
387
self.components.wPop.Append(v)
389
for v in widget._spec.getAttributes()[propName].values:
390
self.components.wPop.Append(v)
392
self.components.wPop.stringSelection = value
394
# if value is empty or doesn't already exist
396
elif propName in ('items', 'userdata') or (wClass == 'TextArea' and propName == 'text'):
397
#print 'displaying TextArea'
398
self.hideAllBut(self.components.wTextArea)
399
self.components.wTextArea.visible = True
400
self.components.wTextArea.text = value
402
self.hideAllBut(self.components.wField)
403
self.components.wField.visible = True
404
if propName == 'foregroundColor' or propName == 'backgroundColor':
405
self.components.wColor.visible = True
406
elif propName == 'font':
407
self.components.wFont.visible = True
408
elif propName == 'file':
409
self.components.wFile.visible = True
410
self.components.wName.text = propName + ":"
413
# I can't remember why this is actually necessary
414
if propName == 'size':
415
width, height = getattr(widget, propName)
416
if wClass not in ['BitmapCanvas', 'HtmlWindow']:
417
bestWidth, bestHeight = widget.GetBestSize()
418
if width == bestWidth:
420
if height == bestHeight:
422
size = (width, height)
425
self.components.wField.text = value
426
# this should only display if the attribute is settable
427
# so name, alignment, and others are read-only
429
# wUpdate is always visible now
430
self.components.wUpdate.visible = True
432
def on_wComponentList_select(self, event):
433
#print 'selectComponentListEvent: %s\n' % event.GetString()
434
wName, wClass = event.GetString().split(" : ")
435
# change the wPropertiesList to only show relevant properties
436
# either display the name by default or try and preserve the
437
# wPropertiesList selection and display that item, so for example
438
# you could look at size for all widgets, simply by going up and down
439
# the components list
440
self.setValidProps(wClass)
441
propName = self.components.wPropertyList.stringSelection
445
self.components.wPropertyList.stringSelection = "name"
446
self.displayProperty(wName, wClass, propName)
447
c = self._parent.components[wName]
448
if hasattr(c, 'position'):
449
self._parent.showSizingHandles(wName)
450
self._parent.setToolTipDrag(wName, c.position, c.size)
452
def on_wPropertyList_select(self, event):
453
propName = event.GetString()
454
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
456
self.displayProperty(wName, wClass, propName)
458
def clearComponentList(self):
459
self.components.wComponentList.Clear()
460
self.statusBar.text = ''
462
def clearPropertyList(self):
463
self.components.wPropertyList.Clear()
465
def displayComponents(self, components):
466
self.components.wComponentList.Freeze()
467
self.components.wComponentList.Clear()
468
self._comp = components
469
for c in components.order:
470
if c not in self._parent.sizingHandleNames:
471
self.addWidgetToComponentList(components[c])
472
self.components.wComponentList.Thaw()
473
self.components.wComponentList.Refresh()
474
self.components.wComponentList.Update()
476
def on_close(self, event):
478
parent = self.GetParent()
479
parent.menuBar.setChecked('menuViewPropertyEditor', 0)