2
# Purpose: base component classes
3
# Author: Roman Rolinsky <rolinsky@femagsoft.com>
5
# RCS-ID: $Id: component.py 64107 2010-04-22 14:05:36Z ROL $
8
Component plugin classes.
10
This module defines base classes and for deriving component plugin
11
classes and contains some global variables used to register components
14
Components are objects of Component class or one of the derived
15
classes used to define specialized components (such as sizers). After
16
a component object is constructed and configured it can be registered
17
using the Manager global object.
26
from model import Model
27
from attribute import *
32
DEFAULT_POS = (1000,1000)
34
# Group compatibility specifications.
35
# Key is the parent group, value is the list of child groups.
36
# !value means named main group is excluded from possible children.
37
# "root" is a special group for the tree root
39
'root': ['top_level', 'component'], # top-level objects
40
'frame': ['toolbar', 'menubar', 'statusbar'],
41
'mdi_parent_frame': ['toolbar', 'menubar', 'statusbar', 'mdi_child_frame'],
42
'mdi_child_frame': ['toolbar', 'menubar', 'statusbar',
43
'control', 'window', 'sizer', 'btnsizer',
44
'!frame', '!mdi_child_frame'],
45
'wizard': ['wizard_page'],
46
'window': ['control', 'window', 'sizer', 'btnsizer', '!frame', '!mdi_child_frame'],
47
'sizer': ['control', 'sizer', 'btnsizer', 'spacer'],
48
'book': ['control', 'window', '!sizer', '!btnsizer'],
49
'btnsizer': ['stdbtn'],
51
'toolbar': ['tool', 'separator'],
52
'menu': ['menu', 'menu_item', 'separator'],
55
Definition of compatibility of component groups as I{key}:I{group_list} pairs, where
56
I{key} is a parent group name and I{group_list} is a list of children group names or
57
group names prefixed with '!' character to exclude components having corresponding
58
primary group. This dictionary can be modified by component plugins directly
59
(some care must be taken not to redefine existing relations or breake them).
63
class Component(object):
64
'''Base class for component plugins.'''
65
# Common window attributes
66
windowAttributes = ['fg', 'bg', 'font', 'tooltip', 'help',
67
'enabled', 'focused', 'hidden']
68
'''Default window attributes for window-like components.'''
70
'wxSIMPLE_BORDER', 'wxSUNKEN_BORDER', 'wxDOUBLE_BORDER',
71
'wxRAISED_BORDER', 'wxSTATIC_BORDER', 'wxNO_BORDER',
72
'wxCLIP_CHILDREN', 'wxTRANSPARENT_WINDOW', 'wxWANTS_CHARS',
73
'wxNO_FULL_REPAINT_ON_RESIZE', 'wxFULL_REPAINT_ON_RESIZE'
75
'''Default generic styles.'''
77
'wxWS_EX_VALIDATE_RECURSIVELY',
78
'wxWS_EX_BLOCK_EVENTS',
79
# 'wxWS_EX_TRANSIENT', # not processed by XRC (yet?)
80
'wxWS_EX_PROCESS_IDLE',
81
'wxWS_EX_PROCESS_UI_UPDATES'
83
'''Default generic extended styles.'''
85
'EVT_WINDOW_CREATE', 'EVT_WINDOW_DESTROY',
86
'EVT_MOVE', 'EVT_SIZE',
87
'EVT_MOUSE_EVENTS', 'EVT_MOTION',
88
'EVT_LEFT_DOWN', 'EVT_LEFT_DCLICK',
89
'EVT_MIDDLE_DOWN', 'EVT_MIDDLE_DCLICK',
90
'EVT_RIGHT_DOWN', 'EVT_RIGHT_DCLICK', 'EVT_MOUSEWHEEL',
91
'EVT_ENTER_WINDOW', 'EVT_LEAVE_WINDOW',
92
'EVT_KEY_DOWN', 'EVT_KEY_UP', 'EVT_CHAR',
93
'EVT_PAINT', 'EVT_ERASE_BACKGROUND',
94
'EVT_CONTEXT_MENU', 'EVT_HELP',
95
'EVT_SET_FOCUS', 'EVT_KILL_FOCUS', 'EVT_CHILD_FOCUS',
96
'EVT_UPDATE_UI', 'EVT_IDLE',
98
'''Default generic events.'''
99
hasName = True # most elements have XRC IDs
100
'''True if component has an XRC ID attribute.'''
101
isTopLevel = False # if can be created as top level window
102
'''True if component can be a top-level object in XML tree.'''
104
'''Dictionary of I{old_name}:I{new_name} for renaming some attributes
105
in the Attribute Panel.'''
107
'''True if component can generate code.'''
109
def __init__(self, klass, groups, attributes, **kargs):
111
Construct a new Component object.
113
@param klass: Interface element class name (e.g. C{'wxButton'}).
114
@param groups: List of group names to which this component belongs.
115
First group is considered to be the I{primary group}.
116
@param attributes: List of XRC attribute names.
118
B{Supported keyword parameters:}
120
@keyword defaults: Dictionary of default attribute values for creating
122
@keyword specials: Dictionary of I{attribute_name}:I{attribute_class} pairs
123
for specifying special attribute classes for some attributes, instead of
124
using default Attribute class.
125
@keyword params: Dictionary of pairs I{attribute_name}:I{param_class} where
126
I{param_class} is a attribute interface class (one of classes in
127
params.py or a custom class). If a param class is not specified, a default
128
value defined by C{paramDict} dictionary is used.
129
@keyword image,images: C{wx.Image} object or a list of C{wx.Image} objects
131
@keyword events: List of event names for code generation panel.
135
self.attributes = attributes
138
self.defaults = kargs.pop('defaults', {})
139
# Special Attribute classes if required
140
self.specials = kargs.pop('specials', {})
141
# Some default special attributes
142
self.specials['font'] = FontAttribute
143
self.specials['XRCED'] = CodeAttribute
144
# Special Param classes if required
145
self.params = kargs.pop('params', {})
147
if 'images' in kargs:
148
self.images = kargs.pop('images')
149
elif 'image' in kargs:
150
self.images = [kargs.pop('image')]
151
elif not 'image' in self.__dict__:
153
# Code generation data
154
self.events = kargs.pop('events', [])
156
def addStyles(self, *styles):
157
'''Add more styles.'''
158
self.styles.extend(styles)
160
def addExStyles(self, *styles):
161
'''Add more extra styles.'''
162
self.exStyles.extend(styles)
164
def setSpecial(self, attrName, attrClass):
165
'''Set special attribute class for processing XML.
167
@param attrName: Attribute name.
168
@param attrClass: Attribute class.
170
self.specials[attrName] = attrClass
172
def setParamClass(self, attrName, paramClass):
173
'''Set special attribute panel class for editing attribute value.
175
@param attrName: Attribute name.
176
@param paramClass: Param class.
178
self.params[attrName] = paramClass
180
def getTreeImageId(self, node):
182
return self.images[0].Id
184
if self.isContainer():
188
def getTreeText(self, node):
189
if node.nodeType == node.COMMENT_NODE:
191
if node.tagName == 'object_ref':
192
ref = node.getAttribute('ref')
193
label = 'ref: %s' % ref
195
label = node.getAttribute('subclass')
197
label = node.getAttribute('class')
199
name = node.getAttribute('name')
200
if name: label += ' "%s"' % name
203
def addEvents(self, *events):
204
'''Add more events.'''
205
self.events.extend(events)
207
# Order components having same index by group and klass
208
def __cmp__(self, other):
209
if self.groups < other.groups: return -1
210
elif self.groups == other.groups:
211
if self.klass < other.klass: return -1
212
elif self.klass == other.klass: return 0
217
return "Component('%s', %s)" % (self.klass, self.attributes)
219
def canHaveChild(self, component):
220
'''True if the current component can have child of given type.
222
This function is redefined by container classes.'''
225
def canBeReplaced(self, component):
226
'''True if the current component can be replaced by component.
228
This function can be redefined by derived classes.'''
229
return component.groups == groups
231
def isContainer(self):
232
'''True if component is a container (can have child nodes).'''
233
return isinstance(self, Container)
235
def getAttribute(self, node, attribute):
236
attrClass = self.specials.get(attribute, Attribute)
237
# 'object' means attribute is a text node without element tag,
238
if attribute == 'object':
239
return attrClass.get(node)
240
elif issubclass(attrClass, AttributeAttribute):
241
return attrClass.getAA(node, attribute)
242
for n in node.childNodes:
243
if n.nodeType == node.ELEMENT_NODE and n.tagName == attribute:
244
return attrClass.get(n)
245
return attrClass.get(None)
247
def addAttribute(self, node, attribute, value):
248
'''Add attribute element.'''
249
attrClass = self.specials.get(attribute, Attribute)
250
attrClass.add(node, attribute, value)
252
def makeTestWin(self, res, name):
253
'''Method can be overrided by derived classes to create custom test view.
255
@param res: C{wx.xrc.XmlResource} object with current test resource.
256
@param name: XRC ID of tested object.
258
if not self.hasName: raise NotImplementedError
260
testWin = view.testWin
262
# Top-level window creates frame itself
264
object = res.LoadObject(view.frame, STD_NAME, self.klass)
266
testWin.size = object.GetSize()
268
# Create MiniFrame to hold selected subtree
269
frame = testWin.frame
271
frame = wx.MiniFrame(view.frame, -1, '%s: %s' % (self.klass, name), name=STD_NAME,
272
style=wx.CAPTION|wx.CLOSE_BOX|wx.RESIZE_BORDER)
273
frame.panel = wx.Panel(frame)
274
object = res.LoadObject(frame.panel, STD_NAME, self.klass)
275
if not object: raise NotImplementedError
276
if not isinstance(object, wx.Window): raise NotImplementedError
277
object.SetPosition((10,10))
278
if g.conf.fitTestWin:
281
frame.SetClientSize(object.GetSize()+(20,20))
282
testWin.size = frame.GetSize()
285
def getRect(self, obj):
286
'''Return bounding box coordinates for C{obj}.'''
287
# Object's rect must be relative to testWin.object
288
if isinstance(obj, wx.Window):
289
return [obj.GetRect()]
290
elif isinstance(obj, wx.Rect): # spacer
295
def copyAttributes(self, srcNode, dstNode):
296
'''Copy relevant attribute nodes from srcNode to dstNode.'''
297
dstComp = Manager.getNodeComp(dstNode)
299
dstNode.setAttribute('name', srcNode.getAttribute('name'))
300
for n in srcNode.childNodes:
301
if n.nodeType == n.ELEMENT_NODE:
303
# Check if attributes are compatible
304
srcAttrClass = self.specials.get(a, Attribute)
305
dstAttrClass = dstComp.specials.get(a, Attribute)
306
if srcAttrClass is not dstAttrClass: continue
307
srcParamClass = self.params.get(a, params.paramDict.get(a, params.ParamText))
308
dstParamClass = dstComp.params.get(a, params.paramDict.get(a, params.ParamText))
309
if srcParamClass is not dstParamClass: continue
310
# Style and exstyle are not in attributes and can be treated specially
312
styles = self.getAttribute(srcNode, a).split('|')
313
allStyles = dstComp.styles + self.genericStyles
314
dstStyles = [s for s in styles if s.strip() in allStyles]
316
dstComp.addAttribute(dstNode, a, '|'.join(dstStyles))
318
styles = self.getAttribute(srcNode, a).split('|')
319
allStyles = dstComp.exStyles + self.genericExStyles
320
dstStyles = [s for s in styles if s.strip() in allStyles]
322
dstComp.addAttribute(dstNode, a, '|'.join(dstStyles))
323
elif a in dstComp.attributes:
324
value = self.getAttribute(srcNode, a)
325
dstComp.addAttribute(dstNode, a, value)
327
def copyImplicitAttributes(self, srcNode, dstNode, dstComp):
328
'''Copy relevant implicit attribute nodes from srcNode to dstNode.'''
329
for n in srcNode.childNodes:
330
if n.nodeType == n.ELEMENT_NODE and not is_object(n):
332
if a in dstComp.implicitAttributes:
333
value = self.getAttribute(srcNode, a)
334
dstComp.addAttribute(dstNode, a, value)
337
class SimpleComponent(Component):
338
'''Component without window attributes and styles.'''
339
windowAttributes = []
340
genericStyles = genericExStyles = []
343
class Container(Component):
344
'''Base class for containers.'''
345
def canHaveChild(self, component):
346
# Test exclusion first
347
for g in self.groups:
348
if '!'+component.groups[0] in parentChildGroups.get(g, []): return False
349
# Test for any possible parent-child
350
groups = Set(component.groups)
351
for g in self.groups:
352
if groups.intersection(parentChildGroups.get(g, [])):
357
'''If this container manages children positions and sizes.'''
360
def requireImplicit(self, node):
361
'''If there are implicit nodes for this particular node.'''
364
def getTreeNode(self, node):
365
'''Some containers may hide some internal elements.'''
368
def getTreeOrImplicitNode(self, node):
369
'''Return topmost child (implicit if exists).'''
372
def appendChild(self, parentNode, node):
373
'''Append child node. Can be overriden to create implicit nodes.'''
374
parentNode.appendChild(node)
376
def insertBefore(self, parentNode, node, nextNode):
377
'''Insert node before nextNode. Can be overriden to create implicit nodes.'''
378
parentNode.insertBefore(node, nextNode)
380
def insertAfter(self, parentNode, node, prevNode):
381
'''Insert node after prevNode. Can be overriden to create implicit nodes.'''
382
parentNode.insertBefore(node, prevNode.nextSibling)
384
def removeChild(self, parentNode, node):
386
Remove node and the implicit node (if present). Return
387
top-level removed child.
389
return parentNode.removeChild(node)
391
def copyObjects(self, srcNode, dstNode):
392
# Copy child objects only for the same group
393
dstComp = Manager.getNodeComp(dstNode)
394
if self.groups[0] != dstComp.groups[0]: return
396
for n in filter(is_object, srcNode.childNodes):
397
n = self.getTreeNode(n)
398
if dstComp.canHaveChild(Manager.getNodeComp(n)):
399
dstComp.appendChild(dstNode, n)
401
def replaceChild(self, parentNode, newNode, oldNode):
402
'''Replace oldNode by newNode keeping relevant attributes.'''
403
# Keep compatible children
404
oldComp = Manager.getNodeComp(oldNode)
405
oldComp.copyAttributes(oldNode, newNode)
406
if oldComp.isContainer():
407
oldComp.copyObjects(oldNode, newNode)
408
parentNode.replaceChild(newNode, oldNode)
410
def getChildObject(self, node, obj, index):
411
"""Get index'th child of a tested interface element."""
412
if isinstance(obj, wx.Window) and obj.GetSizer():
413
return obj.GetSizer()
415
return obj.GetChildren()[index]
420
class SimpleContainer(Container):
421
'''Container without window attributes and styles.'''
422
windowAttributes = []
423
genericStyles = genericExStyles = []
426
class RootComponent(Container):
427
'''Special root component.'''
428
windowAttributes = []
429
genericStyles = genericExStyles = []
434
class SmartContainer(Container):
435
'''Base class for containers with implicit nodes.'''
436
implicitRenameDict = {}
437
def __init__(self, klass, groups, attributes, **kargs):
438
Container.__init__(self, klass, groups, attributes, **kargs)
439
self.implicitKlass = kargs.pop('implicit_klass')
440
self.implicitPageName = kargs.pop('implicit_page')
441
self.implicitAttributes = kargs.pop('implicit_attributes')
443
self.implicitParams = kargs.pop('implicit_params', {})
445
def getTreeNode(self, node):
446
if node.getAttribute('class') == self.implicitKlass:
447
for n in node.childNodes: # find first object
448
if is_object(n): return n
449
# Maybe some children are not implicit
452
def getTreeOrImplicitNode(self, node):
453
'''Return topmost child (implicit if exists).'''
454
if node.parentNode.getAttribute('class') == self.implicitKlass:
455
return node.parentNode
459
def appendChild(self, parentNode, node):
460
if self.requireImplicit(node):
461
elem = Model.createObjectNode(self.implicitKlass)
462
elem.appendChild(node)
463
parentNode.appendChild(elem)
465
parentNode.appendChild(node)
467
def insertBefore(self, parentNode, node, nextNode):
468
if self.requireImplicit(nextNode):
469
nextNode = nextNode.parentNode
470
if self.requireImplicit(node):
471
elem = Model.createObjectNode(self.implicitKlass)
472
elem.appendChild(node)
473
parentNode.insertBefore(elem, nextNode)
475
parentNode.insertBefore(node, nextNode)
477
def insertAfter(self, parentNode, node, prevNode):
478
if self.requireImplicit(prevNode):
479
nextNode = prevNode.parentNode.nextSibling
481
nextNode = prevNode.nextSibling
482
if self.requireImplicit(node):
483
elem = Model.createObjectNode(self.implicitKlass)
484
elem.appendChild(node)
485
parentNode.insertBefore(elem, nextNode)
487
parentNode.insertBefore(node, nextNode)
489
def removeChild(self, parentNode, node):
490
if self.requireImplicit(node):
491
implicitNode = node.parentNode
492
return parentNode.removeChild(implicitNode)
494
return parentNode.removeChild(node)
496
def replaceChild(self, parentNode, newNode, oldNode):
497
# Do similarly to Container for object child nodes
498
oldComp = Manager.getNodeComp(oldNode)
499
oldComp.copyAttributes(oldNode, newNode)
500
if oldComp.isContainer():
501
oldComp.copyObjects(oldNode, newNode)
502
# Special treatment for implicit nodes
503
if self.requireImplicit(oldNode):
504
implicitNode = oldNode.parentNode
505
if self.requireImplicit(newNode):
506
implicitNode.replaceChild(newNode, oldNode)
508
parentNode.replaceChild(newNode, implicitNode)
510
if self.requireImplicit(newNode):
511
elem = Model.createObjectNode(self.implicitKlass)
512
elem.appendChild(newNode)
513
parentNode.replaceChild(elem, oldNode)
515
parentNode.replaceChild(newNode, oldNode)
517
def requireImplicit(self, node):
518
# SmartContainer by default requires implicit
521
def setImplicitParamClass(self, attrName, paramClass):
522
'''Set special Param class.'''
523
self.implicitParams[attrName] = paramClass
526
class Sizer(SmartContainer):
527
'''Sizers are not windows and have common implicit node.'''
528
windowAttributes = []
532
renameDict = {'orient':'orientation'}
533
implicitRenameDict = {'option':'proportion'}
534
def __init__(self, klass, groups, attributes, **kargs):
535
kargs.setdefault('implicit_klass', 'sizeritem')
536
kargs.setdefault('implicit_page', 'SizerItem')
537
kargs.setdefault('implicit_attributes', ['option', 'flag', 'border', 'minsize', 'ratio'])
538
kargs.setdefault('implicit_params', {'option': params.ParamInt,
539
'minsize': params.ParamPosSize,
540
'ratio': params.ParamPosSize})
541
SmartContainer.__init__(self, klass, groups, attributes, **kargs)
546
def requireImplicit(self, node):
547
return node.getAttribute('class') != 'spacer'
549
def getChildObject(self, node, obj, index):
550
obj = obj.GetChildren()[index]
552
return obj.GetSizer()
554
return obj.GetWindow()
557
return None # normally this is an error
559
def getRect(self, obj):
560
rects = [wx.RectPS(obj.GetPosition(), obj.GetSize())]
561
for sizerItem in obj.GetChildren():
562
rect = sizerItem.GetRect()
564
# Add lines to show borders
565
flag = sizerItem.GetFlag()
566
border = sizerItem.GetBorder()
567
if border == 0: continue
568
x = (rect.GetLeft() + rect.GetRight()) / 2
570
rects.append(wx.Rect(x, rect.GetTop() - border, 0, border))
572
rects.append(wx.Rect(x, rect.GetBottom() + 1, 0, border))
573
y = (rect.GetTop() + rect.GetBottom()) / 2
575
rects.append(wx.Rect(rect.GetLeft() - border, y, border, 0))
577
rects.append(wx.Rect(rect.GetRight() + 1, y, border, 0))
581
class BoxSizer(Sizer):
582
'''Sizers are not windows and have common implicit node.'''
584
def __init__(self, klass, groups, attributes, **kargs):
585
Sizer.__init__(self, klass, groups, attributes, **kargs)
587
def getTreeImageId(self, node):
588
if self.getAttribute(node, 'orient') == 'wxVERTICAL':
589
return self.images[0].Id
591
return self.images[1].Id
593
def getRect(self, obj):
594
rects = Sizer.getRect(self, obj)
595
for sizerItem in obj.GetChildren():
596
rect = sizerItem.GetRect()
597
flag = sizerItem.GetFlag()
599
if obj.GetOrientation() == wx.VERTICAL:
600
y = (rect.GetTop() + rect.GetBottom()) / 2
601
rects.append(wx.Rect(rect.x, y, rect.width, 0))
603
x = (rect.GetLeft() + rect.GetRight()) / 2
604
rects.append(wx.Rect(x, rect.y, 0, rect.height))
607
def setDefaults(self, node):
608
if node.tagName == 'object_ref': return
609
if self.requireImplicit(node):
610
comp = Manager.getNodeComp(node)
611
sizerItem = self.getTreeOrImplicitNode(node)
612
if comp.isContainer():
613
for a,v in g.conf.defaultsContainer.items():
614
self.addAttribute(sizerItem, a, v)
616
for a,v in g.conf.defaultsControl.items():
617
self.addAttribute(sizerItem, a, v)
619
def appendChild(self, parentNode, node):
620
Sizer.appendChild(self, parentNode, node)
621
self.setDefaults(node)
623
def insertBefore(self, parentNode, node, nextNode):
624
Sizer.insertBefore(self, parentNode, node, nextNode)
625
self.setDefaults(node)
627
def insertAfter(self, parentNode, node, prevNode):
628
Sizer.insertAfter(self, parentNode, node, prevNode)
629
self.setDefaults(node)
631
################################################################################
633
class _ComponentManager:
634
'''Component manager used to register component plugins.'''
636
self.rootComponent = RootComponent('root', ['root'], ['encoding'],
637
specials={'encoding': EncodingAttribute},
638
params={'encoding': params.ParamEncoding})
641
self.firstId = self.lastId = -1
644
self.menuNames = ['TOP_LEVEL', 'ROOT', 'bar', 'control', 'button', 'box',
645
'container', 'sizer', 'custom']
646
self.panelNames = ['Windows', 'Panels', 'Controls', 'Sizers', 'Menus',
648
self.panelImages = {}
651
self.firstId = self.lastId = wx.NewId()
652
self.external = [] # external resources
653
self.handlers = [] # registered XmlHandlers
655
def register(self, component):
656
'''Register component object.'''
657
TRACE('register %s' % component.klass)
658
self.components[component.klass] = component
659
# unique wx ID for event handling
660
component.id = self.lastId = wx.NewId()
661
self.ids[component.id] = component
663
def forget(self, klass):
664
'''Remove registered component.'''
665
del self.components[klass]
666
for menu,iclh in self.menus.items():
667
if iclh[1].klass == klass:
668
self.menus[menu].remove(iclh)
669
for panel,icb in self.panels.items():
670
if icb[1].klass == klass:
671
self.panels[panel].remove(icb)
673
def getNodeComp(self, node, defaultClass='unknown'):
674
# For ref nodes without class name, need to find ref element
675
if node is Model.mainNode:
676
return self.rootComponent
677
elif node.nodeType == node.COMMENT_NODE:
678
return self.components['comment']
679
cls = node.getAttribute('class')
680
if node.tagName == 'object_ref':
682
refNode = Model.findResource(node.getAttribute('ref'))
684
cls = refNode.getAttribute('class')
687
if defaultClass and cls not in self.components:
689
return self.components[cls]
691
def getMenuData(self, menu):
692
return self.menus.get(menu, None)
694
def setMenu(self, component, menu, label, help, index=999999):
695
'''Set pulldown menu data.'''
696
if menu not in self.menuNames: self.menuNames.append(menu)
697
if menu not in self.menus: self.menus[menu] = []
698
bisect.insort_left(self.menus[menu], (index, component, label, help))
700
def getPanelData(self, panel):
701
return self.panels.get(panel, None)
703
def setTool(self, component, panel, bitmap=None,
704
pos=DEFAULT_POS, span=(1,1)):
705
'''Set toolpanel data.'''
706
if panel not in self.panelNames: self.panelNames.append(panel)
707
if panel not in self.panels: self.panels[panel] = []
708
# Auto-select bitmap if not provided
710
bmpPath = os.path.join('bitmaps', component.klass + '.png')
711
if os.path.exists(bmpPath):
712
bitmap = wx.Bitmap(bmpPath)
714
bitmap = images.ToolDefault.GetBitmap()
715
if g.conf.toolIconScale != 100:
716
im = bitmap.ConvertToImage().Scale(
717
bitmap.GetWidth() * g.conf.toolIconScale / 100,
718
bitmap.GetHeight() * g.conf.toolIconScale / 100)
719
bitmap = im.ConvertToBitmap()
720
bisect.insort_left(self.panels[panel], (pos, span, component, bitmap))
722
def addXmlHandler(self, h):
724
Add an XML resource handler. h must be a class derived from
725
XmlResourceHandler or a function loaded from a dynamic library
728
self.handlers.append(h)
730
def findById(self, id):
733
def addXmlHandlers(self, res):
734
'''Register XML handlers before creating a test window.'''
735
for h in self.handlers:
736
TRACE('registering Xml handler %s', h)
737
if g._CFuncPtr and isinstance(h, g._CFuncPtr):
741
logger.exception('error calling DL func "%s"', h)
742
wx.LogError('error calling DL func "%s"' % h)
743
else: # assume a python class handler
745
res.AddHandler(apply(h, ()))
747
logger.exception('error adding XmlHandler "%s"', h)
748
wx.LogError('error adding XmlHandler "%s"' % h)
750
def addExternal(self, f):
751
'''Add an external resource file f to the list of preloaded
753
self.external.append(f)
756
def preload(self, res):
757
'''Preload external resources.'''
758
for f in self.external:
759
TRACE('Loading external resources: %s', f)
764
Manager = _ComponentManager()
765
'''Singleton global object of L{_ComponentManager} class.'''
767
c = SimpleComponent('comment', ['top_level', 'control'], ['comment'],
768
specials={'comment': CommentAttribute},
769
image=images.TreeComment.GetImage())
772
c.setParamClass('comment', params.ParamMultilineText)