#-------------------------------------------------------------------------------# Name: BaseCompanions.py# Purpose: Classes that 'shadow' controls. They implement design time# behaviour and interfaces## Author: Riaan Booysen## Created: 1999# RCS-ID: $Id$# Copyright: (c) 1999 - 2007 Riaan Booysen# Licence: GPL#-------------------------------------------------------------------------------""" Classes that 'shadow' controls.They implement design time behaviour and interfaces. Also used for inspectableobjects """print'importing Companions'importcopyimportwximportPreferences,UtilsfromUtilsimport_fromPropEdit.PropertyEditorsimport*fromConstructorsimportWindowConstrimportRTTI,EventCollectionsimportmethodparse,sourceconst""" Design time classes These are companion classes to wxPython classes that capture design-time behaviour like * Constructor parameters * Events * Property editors XXX Todo XXX * write/wrap many more classes * streaming of properties * xml option * handling of default values * define event and name for popup menu on component * overrideable method or new multi inheritance class for palette creation in container companions,"""classCompanion:""" Default companion, entity with a name and default documentation """def__init__(self,name):self.name=namedefgetPropertyHelp(self,propName):returnpropNameclassCodeCompanion(Companion):passclassDesignTimeCompanion(Companion):""" Base class for all companions participating in the design-time process. """handledConstrParams=()suppressWindowId=Falsedef__init__(self,name,designer):Companion.__init__(self,name)self.parentCompanion=Noneself.designer=designer# Design time window idself.dId=wx.NewId()self.id=None# Property editors for properties whose types cannot be deducedself.editors={'Class':ClassConstrPropEdit}# Enumerated values for the options of a property editorself.options={}# Enumerated display values for the options of a property editor# XXX Change to something less genericself.names={}# Companion methods that must be called when a property changesself.triggers={'Name':self.SetName}# Companions for properties for which a companion can not be deducedself.subCompanions={}# Parsers for special properties, given string value should# return valid wxPython object# The property evaluator should return a tuple of arguments# as customPropEvaluators are also used to initialise multi parameter# propertiesself.customPropEvaluators={}# Properties that should be initialised thru the companion instead of# directly on the control. Usually this applies to 'write-only'# properties whose values cannot otherwise be determined# It's keyed on the property name and if the setter does not start# with Set*, it's keyed on the setter nameself.initPropsThruCompanion=[]# Run time dict of created collection companionsself.collections={}# Can't work, removeself.letClickThru=False# Mutualy depentent props# These props will all be refeshed in the inspector when any one of# them is updatedself.mutualDepProps=[]# Flag for controls which do not process mouse events correctlyself.ctrlDisabled=Falseself.compositeCtrl=False#self.resourceImports=[]# Parse objects for reading in and writing out sourceself.textConstr=Noneself.textPropList=[]self.textEventList=[]self.textCollInitList=[]defdestroy(self):delself.triggersdefconstructor(self):""" This method must be overriden by defining it in in another class and multiply inheriting from this new class and a DesignTimeCompanion derivative. This allows groups of components having the same constructor to be created."""return{}defextraConstrProps(self):return{}# def defaults(self):# return {}defgetPropList(self):propList=RTTI.getPropList(self.control,self)pw=RTTI.PropertyWrapper('Class','CompnRoute',self.GetClass,self.SetClass)propList['constructor'].append(pw)returnpropListdefGetClass(self,dummy=None):""" Used by the Inspector to display the type of the selected object """returnself.textConstr.class_namedefSetClass(self,value):""" Used by the Inspector to display the type of the selected object """self.textConstr.class_name=valuedefproperties(self):""" Properties additional to those gleened thru reflection. Dictionary key=propname : type, val=getter, setter tuple. type = 'CtrlRoute' : Routed to get/setters on the control 'CompnRoute': Routed to get/setters on the companion """return{}defsetConstr(self,constr):""" Define a new constructor for source code, called when a component is parsed from source See also: persistConstr """self.textConstr=constrdefsetProps(self,propList):# XXX Should companion initialise comp props instead of Designer?self.textPropList=propListdefsetCollInits(self,collInitList):self.textCollInitList=collInitListdefsetEvents(self,eventList):self.textEventList=eventListdefgetEvents(self):returnself.textEventListdefgetWinId(self):returnself.iddefhideDesignTime(self):""" Property names of automatically picked up properties that should not be shown in the Inspector. """return[]defdontPersistProps(self):""" Properties are live (i.e. read/write) at design time but whose changes won't be applied to source. This is for cascading type properties like Size vs ClientSize. Updating one will automatically update the other so only one of them has to be stored."""return['Class']defonlyPersistProps(self):""" Properties that should not be applied at design-time and should only be applied to the source """return[]defevents(self):return[]defeditor(self):passdefvetoedMethods(self):return[]## def links(self):## return []# Rename to linksdefdependentProps(self):""" These are properties that depend on other controls already being created. They will be initialised right at the end of the definition block """return[]defapplyRunTime(self):""" Properties whose value will be modifyable at design-time and whose changes will be applied to the source but will not be applied to the controls at design time e.g. Show/Enable. """passdefgetPropEditor(self,prop):ifpropinself.editors:returnself.editors[prop]else:returnNonedefgetPropOptions(self,prop):ifpropinself.options:returnself.options[prop]else:returnNonedefgetPropNames(self,prop):ifpropinself.names:returnself.names[prop]else:returnNonedefevtGetter(self,name):forevtinself.textEventList:ifevt.event_name==name:returnevt.trigger_methreturnNonedefevtSetter(self,name,value):forevtinself.textEventList:ifevt.event_name==name:evt.trigger_meth=valuereturndefpersistConstr(self,className,params):""" Define a new constructor for source code, called when creating a new component from the palette See also: setConstr """paramStrs=[]forparaminparams.keys():paramStrs.append('%s = %s'%(param,params[param]))# XXX Is frame name initialised ???self.textConstr=methodparse.ConstructorParse('self.%s = %s(%s)'%(self.name,className,', '.join(paramStrs)))self.designer.addCtrlToObjectCollection(self.textConstr)defpersistCollInit(self,method,ctrlName,propName,params={}):""" Define a new collection init method for source code, called when creating a new item in CollectionEditor """collInitParse=methodparse.CollectionInitParse(None,ctrlName,method,[],propName)self.parentCompanion.textCollInitList.append(collInitParse)self.designer.addCollToObjectCollection(collInitParse)defcheckTriggers(self,name,oldValue,newValue):#trigger specially handled callbacks for property changes with consequencesifnameinself.triggers:self.triggers[name](oldValue,newValue)defgetCompName(self):ifid(self.control)==id(self.designer):return''else:returnself.namedefpersistProp(self,name,setterName,value):c=self.constructor()#constructorifnameinc:self.textConstr.params[c[name]]=value#propertyelifnamenotinself.dontPersistProps():forpropinself.textPropList:ifprop.prop_setter==setterName:prop.params=[value]returnself.textPropList.append(methodparse.PropertyParse( \
None,self.getCompName(),setterName,[value],name))defpersistedPropVal(self,name,setterName):c=self.constructor()#constructorifnameinc:returnself.textConstr.params[c[name]]#propertyelifnamenotinself.dontPersistProps():forpropinself.textPropList:try:ifprop.prop_setter==setterName:returnprop.paramsexcept:#print 'except in persistprop'raisereturnNonedefpropRevertToDefault(self,name,setterName):""" Removes property methods from source and revert constructor parameters to default values """c=self.constructor()#constructorifnameinc:defVal=self.designTimeSource()[c[name]]self.textConstr.params[c[name]]=defVal#propertyelifnamenotinself.dontPersistProps():idx=0whileidx<len(self.textPropList):prop=self.textPropList[idx]ifprop.prop_setter==setterName:delself.textPropList[idx]else:idx=idx+1defpropIsDefault(self,name,setterName):""" Returns True if no modification has been made to the property or constructor parameter """c=self.constructor()#constructorifnameinc:try:dts=self.designTimeSource()exceptTypeError:returnTrueelse:ifc[name]indts:defVal=self.designTimeSource()[c[name]]returnself.textConstr.params[c[name]]==defValelse:returnTrue#propertyelifnamenotinself.dontPersistProps():forpropinself.textPropList:ifprop.prop_setter==setterName:returnFalsereturnTruedefpersistEvt(self,name,value,wId=None):""" Add a source entry for an event or update the trigger method of am existing event. """forevtinself.textEventList:ifevt.event_name==name:evt.trigger_meth=valuereturnifself.control==self.designeror \
notisinstance(self.control,wx.EvtHandler)or \
isinstance(self.control,wx.Timer):comp_name=''else:comp_name=self.nameself.textEventList.append(methodparse.EventParse(None,comp_name,name,value,wId))defevtName(self):returnself.namedefaddIds(self,lst):ifself.idisnotNone:# generate default win id for list when reserved one is used by controlifself.idinEventCollections.reservedWxIds:wId=Utils.windowIdentifier(self.designer.GetName(),self.name)else:wId=self.idlst.append(wId)defrenameEventListIds(self,wId):forevtinself.textEventList:ifevt.windowidandevt.windowidnotinEventCollections.reservedWxIds:evt.windowid=wIddefsetProp(self,name,value):""" Optional callback companions can override for extra functionality after updating a property e.g. refreshing """passdefSetName(self,oldValue,newValue):""" Triggered when the 'Name' property is changed """ifnewValueinself.designer.objects:wx.LogError(_('There is already an object named %s')%newValue)else:self.name=newValueself.designer.model.renameCtrl(oldValue,newValue)self.designer.renameCtrl(oldValue,newValue)defrenameCtrl(self,oldName,newName):self.textConstr.comp_name=newNameforpropinself.textPropList:prop.comp_name=newNameforcollInitinself.textCollInitList:collInit.renameCompName2(oldName,newName)forevtinself.textEventList:ifevt.comp_name:evt.comp_name=newNamedefrenameCtrlRefs(self,oldName,newName):""" Notification of a the rename of another control, used to fix up references """ifself.textConstr:self.textConstr.renameCompName2(oldName,newName)forpropinself.textPropList:prop.renameCompName2(oldName,newName)defgetPropNameFromSetter(self,setter):props=self.properties()forpropinprops.keys():ifprops[prop][1]andprops[prop][1].__name__==setter:returnpropifsetter[:3]=='Set':returnsetter[3:]else:returnsetterdefeval(self,expr):importPaletteMappingtry:returnPaletteMapping.evalCtrl(expr,self.designer.model.specialAttrs)exceptException,err:print_('Illegal expression: %s')%exprraisedefdefaultAction(self):""" Invoke the default property editor for this component, This can be anything from a custom editor to an event. """passdefnotification(self,compn,action):""" Called after components are added and before they are removed. Used for initialisation or finalisation hooks in other components. """passdefregisterResourceModule(self,name):""" Resource Module name that should be added to the import list """ifnamenotinself.resourceImports:self.resourceImports.append(name)defwriteResourceImports(self):""" Return import line that will be added to module """ifnotself.resourceImports:return''else:return'\n'.join(['import %s'%modformodinself.resourceImports])defwriteImports(self):""" Return import line that will be added to module """return''#---Source writing methods------------------------------------------------------defaddContinuedLine(self,line,output,indent):ifPreferences.cgWrapLines:iflen(line)>Preferences.cgLineWrapWidth:segs=methodparse.safesplitfields(line,',',True,(),())line=sourceconst.bodyIndent+segs[0].lstrip()forseginsegs[1:]:newLine=line+', '+segiflen(newLine)>=Preferences.cgLineWrapWidth:output.append(line+',')line=indent+' '*Preferences.cgContinuedLineIndent+segelse:line=newLineoutput.append(line)defwriteConstructor(self,output,collectionMethod,stripFrmId=''):""" Writes out constructor and parameters for control """# Add constructorifself.textConstr:self.addContinuedLine(sourceconst.bodyIndent+self.textConstr.asText(stripFrmId),output,sourceconst.bodyIndent)nullProps=('None','wx.NullBitmap','wx.NullIcon')defwriteProperties(self,output,ctrlName,definedCtrls,deps,depLinks,stripFrmId=''):""" Write out property setters but postpone dependent properties. """# Add propertiesforpropinself.textPropList:# Skip blanked out propsiflen(prop.params)==1andprop.params[0]inself.nullProps:continue# Postpone dependent propsifself.designer.checkAndAddDepLink(ctrlName,prop,self.dependentProps(),deps,depLinks,definedCtrls):continueself.addContinuedLine(sourceconst.bodyIndent+prop.asText(stripFrmId),output,sourceconst.bodyIndent)defwriteEvents(self,output,module=None,stripFrmId=''):""" Write out EVT_* calls for all events. Optionally For every event definition not defined in source add an empty method declaration to the bottom of the class """forevtinself.textEventList:ifevt.trigger_meth!=_('(delete)'):self.addContinuedLine(sourceconst.bodyIndent+evt.asText(stripFrmId),output,sourceconst.bodyIndent)model=self.designer.model# Either rename the event or add if a new one# The first streamed occurrence will do the rename or addif(evt.prev_trigger_methandmoduleand(evt.prev_trigger_methinmodule.classes[model.main].methods)):module.renameMethod(model.main,evt.prev_trigger_meth,evt.trigger_meth)elifmoduleandnotevt.trigger_methinmodule.classes[model.main].methods:module.addMethod(model.main,evt.trigger_meth,'self, event',[sourceconst.bodyIndent+'event.Skip()'])defwriteCollections(self,output,collDeps,stripFrmId=''):""" Write out collection initialiser methods. """forcollInitinself.textCollInitList:ifcollInit.getPropName()inself.dependentProps():self.addContinuedLine(sourceconst.bodyIndent+collInit.asText(stripFrmId),collDeps,sourceconst.bodyIndent)else:self.addContinuedLine(sourceconst.bodyIndent+collInit.asText(stripFrmId),output,sourceconst.bodyIndent)defwriteDependencies(self,output,ctrlName,depLinks,definedCtrls,stripFrmId=''):""" Write out dependent properties if all the ctrls they reference have been created. """ifctrlNameindepLinks:forprop,otherRefsindepLinks[ctrlName]:foroRfinotherRefs:ifoRfnotindefinedCtrls:# special attrs are not 'reference dependencies'ifnothasattr(self.designer.model.specialAttrs['self'],oRf):breakelse:self.addContinuedLine(sourceconst.bodyIndent+prop.asText(stripFrmId),output,sourceconst.bodyIndent)classNYIDTC(DesignTimeCompanion):""" Blank holder for companions which have not been implemented."""host='Not Implemented'def__init__(self,name,designer,parent,ctrlClass):raiseException,_('Not Implemented')classControlDTC(DesignTimeCompanion):""" Visible controls created on a Frame and defined from _init_ctrls. """handledConstrParams=('id','parent')windowIdName='id'windowParentName='parent'host='Designer'def__init__(self,name,designer,parent,ctrlClass):DesignTimeCompanion.__init__(self,name,designer)self.parent=parentself.ctrlClass=ctrlClassself.generateWindowId()self.container=FalsedefdesignTimeControl(self,position,size,args=None):""" Create and initialise a design-time control """ifargs:self.control=self.ctrlClass(**args)else:self.control=self.ctrlClass(**self.designTimeDefaults(position,size))self.initDesignTimeControl()returnself.controldefdesignTimeDefaults(self,position=wx.DefaultPosition,size=wx.DefaultSize):""" Return a dictionary of parameters for the constructor of a wxPython control. e.g. {'name': 'Frame1', etc) """ifnotposition:position=wx.DefaultPositionifnotsize:size=wx.DefaultSizedts=self.designTimeSource('wx.Point(%s, %s)'%(position.x,position.y),'wx.Size(%s, %s)'%(size.x,size.y))forparamindts.keys():dts[param]=self.eval(dts[param])dts[self.windowParentName]=self.parentifnotself.suppressWindowId:dts[self.windowIdName]=self.dIdreturndtsdefdesignTimeSource(self,position='wx.DefaultPosition',size='wx.DefaultSize'):""" Return a dictionary of parameters for the constructor of a wxPython control's source. 'parent' and 'id' handled automatically """return{}defgenerateWindowId(self):ifself.designer:self.id=Utils.windowIdentifier(self.designer.GetName(),self.name)else:self.id=`wx.NewId()`defSetName(self,oldValue,newValue):DesignTimeCompanion.SetName(self,oldValue,newValue)self.updateWindowIds()ifself.compositeCtrl:forctrlinself.control.GetChildren():ctrl.SetName(newValue)defextraConstrProps(self):return{'Class':'class'}## def GetClass(self, dummy):## return self.control.__class__.__name__#### def SetClass(self, value):## raise 'Cannot change'defupdateWindowIds(self):self.generateWindowId()ifnotself.suppressWindowId:EventCollections.renameCmdIdInDict(self.textConstr.params,self.windowIdName,self.id)self.renameEventListIds(self.id)definitDesignTimeEvents(self,ctrl):# XXX Uncommenting this causes a crash after the first# XXX OnMouseOver event# By pushing the eventhandler, even ctrls# that hook to the Mouse events will still cause# mouse event to fire (theoretically)# ctrl.PushEventHandler(self.designer.ctrlEvtHandler)self.designer.ctrlEvtHandler.connectEvts(ctrl,self.compositeCtrl)definitDesignTimeControl(self):#try to set the nametry:self.control.SetName(self.name)ifself.compositeCtrl:forctrlinself.control.GetChildren():ctrl.SetName(self.name)ctrl._composite_child=1self.control.SetToolTipString(self.name)# Disabled controls do not pass thru mouse clicks to their parents on GTK :(ifwx.Platform!='__WXGTK__'andself.ctrlDisabled:self.control.Enable(False)except:passself.initDesignTimeEvents(self.control)self.popx=self.popy=0self.control.Bind(wx.EVT_RIGHT_DOWN,self.designer.OnRightDown)# for wxMSW# EVT_COMMAND_RIGHT_CLICK(self.control, -1, self.designer.OnRightClick)# for wxGTKself.control.Bind(wx.EVT_RIGHT_UP,self.designer.OnRightClick)defbeforeResize(self):pass#print 'beforeResize'defafterResize(self):pass#print 'afterResize'defupdatePosAndSize(self):ifself.textConstrand'pos'inself.textConstr.params \
and'size'inself.textConstr.params:pos=self.control.GetPosition()size=self.control.GetSize()self.textConstr.params['pos']='wx.Point(%d, %d)'%(pos.x,pos.y)self.textConstr.params['size']='wx.Size(%d, %d)'%(size.x,size.y)defgetDefCtrlSize(self):return'wx.Size(%d, %d)'%(Preferences.dsDefaultControlSize.x,Preferences.dsDefaultControlSize.y)defgetPositionDependentProps(self):return[('constr','Position'),('prop','Position')]defgetSizeDependentProps(self):return[('constr','Size'),('prop','Size'),('prop','ClientSize')]classMultipleSelectionDTC(DesignTimeCompanion):""" Semi mythical class at the moment that will represent a group of selected objects. It's properties should represent the common subset of properties of the selection. Currently only used so the inspector has something to hold on to during multiple selection """# sub properties (Font etc)classHelperDTC(DesignTimeCompanion):""" Helpers are subobjects or enumerations of properties. """def__init__(self,name,designer,ownerCompanion,obj,ownerPropWrap):DesignTimeCompanion.__init__(self,name,designer)self.control=obj# self.obj = objself.ownerCompn=ownerCompanionself.owner=ownerCompanion.controlself.ownerPW=ownerPropWrapself.updateObjFromOwner()defupdateObjFromOwner(self):""" The object to which a sub object is connected may change this method reconnects the property to the current object. """self.obj=self.ownerPW.getValue(self)self.ctrl=self.objself.control=self.objdefupdateOwnerFromObj(self):""" Changes to subobjects do not reflect in their owners automatically they have to be reassigned to their property """self.ownerPW.setValue(self.obj)defpersistProp(self,name,setterName,value):""" When a subobject's property is told to persist, it should persist it's owner This is currently managed by the property editor """pass# non-visual classes (Imagelists, etc)classUtilityDTC(DesignTimeCompanion):""" Utility companions are 'invisible' components that are not owned by the Frame. Utilities are created before the frame and controls and defined in the _init_utils method. """host='Data'def__init__(self,name,designer,objClass):DesignTimeCompanion.__init__(self,name,designer)self.objClass=objClassself.editors['Name']=NameConstrPropEditdefproperties(self):props=DesignTimeCompanion.properties(self)props['Name']=('NoneRoute',None,None)returnpropsdefdesignTimeObject(self,args=None):ifargs:self.control=self.objClass(**args)else:self.control=self.objClass(**self.designTimeDefaults())returnself.controldefdesignTimeDefaults(self):""" Return a dictionary of parameters for the constructor of a wxPython control. e.g. {'name': 'Frame1', etc) """dts=self.designTimeSource()forparamindts.keys():dts[param]=self.eval(dts[param])returndtsdefextraConstrProps(self):return{'Class':'class'}defupdateWindowIds(self):passdefupdatePosAndSize(self):pass# XXX Parents, from constructor or current selected container in designerclassWindowDTC(WindowConstr,ControlDTC):""" Defines the wxWindow interface overloading/defining specialised property editors. """def__init__(self,name,designer,parent,ctrlClass):ControlDTC.__init__(self,name,designer,parent,ctrlClass)self.editors.update({'AutoLayout':BoolPropEdit,'Shown':BoolPropEdit,'Enabled':BoolPropEdit,#'EvtHandlerEnabled': BoolPropEdit,'Style':StyleConstrPropEdit,#'Constraints': CollectionPropEdit,'Name':NamePropEdit,'Anchors':AnchorPropEdit,'Sizer':SizerClassLinkPropEdit,'SizeHints':TuplePropEdit,'Cursor':CursorClassLinkPropEdit,'Centered':EnumPropEdit,'ThemeEnabled':BoolPropEdit,'WindowVariant':EnumPropEdit,'BackgroundStyle':EnumPropEdit,})self.options['Centered']=[None,wx.HORIZONTAL,wx.VERTICAL,wx.BOTH]self.names['Centered']={'None':None,'wx.HORIZONTAL':wx.HORIZONTAL,'wx.VERTICAL':wx.VERTICAL,'wx.BOTH':wx.BOTH}self.options['WindowVariant']=[wx.WINDOW_VARIANT_NORMAL,wx.WINDOW_VARIANT_SMALL,wx.WINDOW_VARIANT_MINI,wx.WINDOW_VARIANT_LARGE]self.names['WindowVariant']={'wx.WINDOW_VARIANT_NORMAL':wx.WINDOW_VARIANT_NORMAL,'wx.WINDOW_VARIANT_SMALL':wx.WINDOW_VARIANT_SMALL,'wx.WINDOW_VARIANT_MINI':wx.WINDOW_VARIANT_MINI,'wx.WINDOW_VARIANT_LARGE':wx.WINDOW_VARIANT_LARGE}self.options['BackgroundStyle']=[wx.BG_STYLE_SYSTEM,wx.BG_STYLE_COLOUR,wx.BG_STYLE_CUSTOM]self.names['BackgroundStyle']={'wx.BG_STYLE_SYSTEM':wx.BG_STYLE_SYSTEM,'wx.BG_STYLE_COLOUR':wx.BG_STYLE_COLOUR,'wx.BG_STYLE_CUSTOM':wx.BG_STYLE_CUSTOM}self.triggers.update({'Size':self.SizeUpdate,'Position':self.PositionUpdate})self.customPropEvaluators.update({'Constraints':self.EvalConstraints,'SizeHints':self.EvalSizeHints,})self.windowStyles=['wx.CAPTION','wx.MINIMIZE_BOX','wx.MAXIMIZE_BOX','wx.THICK_FRAME','wx.SIMPLE_BORDER','wx.DOUBLE_BORDER','wx.SUNKEN_BORDER','wx.RAISED_BORDER','wx.STATIC_BORDER','wx.TRANSPARENT_WINDOW','wx.wxNO_3D','wx.TAB_TRAVERSAL','wx.WANTS_CHARS','wx.NO_FULL_REPAINT_ON_RESIZE','wx.VSCROLL','wx.HSCROLL','wx.CLIP_CHILDREN','wx.NO_BORDER','wx.ALWAYS_SHOW_SB']self.mutualDepProps=['Value','Title','Label']#import UtilCompanions#self.subCompanions['Constraints'] = UtilCompanions.IndividualLayoutConstraintOCDTC#self.subCompanions['SizeHints'] = UtilCompanions.SizeHintsDTCself.anchorSettings=[]self._applyConstraints=Falseself.initPropsThruCompanion=['SizeHints','Cursor','Center','Sizer']self._sizeHints=(-1,-1,-1,-1)self._cursor=wx.NullCursorself._centered=Nonedefproperties(self):return{'Shown':('CompnRoute',self.GetShown,self.Show),'Enabled':('CompnRoute',self.GetEnabled,self.Enable),'ToolTipString':('CompnRoute',self.GetToolTipString,self.SetToolTipString),'Anchors':('CompnRoute',self.GetAnchors,self.SetConstraints),'SizeHints':('CompnRoute',self.GetSizeHints,self.SetSizeHints),'Cursor':('CompnRoute',self.GetCursor,self.SetCursor),'Centered':('CompnRoute',self.GetCentered,self.Center),'Sizer':('CompnRoute',self.GetSizer,self.SetSizer),}defdesignTimeSource(self,position='wx.DefaultPosition',size='wx.DefaultSize'):return{'pos':position,'size':self.getDefCtrlSize(),'name':`self.name`,'style':'0'}defdependentProps(self):return['Cursor']defonlyPersistProps(self):return['Show','Enable']defhideDesignTime(self):return['NextHandler','PreviousHandler','EventHandler','EvtHandlerEnabled','Id','Caret','WindowStyleFlag','ToolTip','Title','Rect','DragTarget','DropTarget','Cursor','VirtualSize','Sizer','ContainingSizer','Constraints','DefaultItem','Validator','WindowStyle','AcceleratorTable','ClientRect','ExtraStyle','LayoutDirection']defdontPersistProps(self):returnControlDTC.dontPersistProps(self)+['ClientSize']defapplyRunTime(self):return['Shown','Enabled','EvtHandlerEnabled']defevents(self):return['MiscEvent','MouseEvent','FocusEvent','KeyEvent','HelpEvent']defnotification(self,compn,action):ifaction=='delete':ifself._cursorand`self._cursor`==`compn.control`:self.propRevertToDefault('Cursor','SetCursor')self.SetCursor(wx.NullCursor)# XXX sizerdefpersistProp(self,name,setterName,value):ifsetterName=='SetSizeHints':minW,minH,maxW,maxH=self.eval(value)newParams=[`minW`,`minH`,`maxW`,`maxH`]# edit if existsforpropinself.textPropList:ifprop.prop_setter==setterName:prop.params=newParamsreturn# add if not definedself.textPropList.append(methodparse.PropertyParse(None,self.getCompName(),setterName,newParams,'SetSizeHints'))elifsetterName=='SetSizer':sizerList=self.designer.getSizerConnectList()ifsizerListisnotNone:forpropinsizerList:ifprop.prop_setter==setterNameand \
prop.comp_name==self.getCompName():ifvalue=='None':sizerList.remove(prop)else:prop.params=[value]returnifvalue!='None':sizerList.append(methodparse.PropertyParse(None,self.getCompName(),setterName,[value],name))else:ControlDTC.persistProp(self,name,setterName,value)defpropIsDefault(self,propName,setterName):ifsetterName=='SetSizer':scl=self.designer.getSizerConnectList()ifscl:forconnPropinself.designer.getSizerConnectList():ifconnProp.comp_name==self.getCompName():returnFalsereturnTrueelse:returnControlDTC.propIsDefault(self,propName,setterName)#---ToolTips--------------------------------------------------------------------defGetToolTipString(self,blah):returnself.control.GetToolTip().GetTip()defSetToolTipString(self,value):self.control.SetToolTipString(value)#---Anchors---------------------------------------------------------------------fromwx.lib.anchorsimportLayoutAnchorsdefwriteImports(self):imports=ControlDTC.writeImports(self)ifself.anchorSettings:return'\n'.join((imports,'from wx.lib.anchors import LayoutAnchors'))else:returnimportsdefGetAnchors(self,compn):ifself.anchorSettings:returnself.LayoutAnchors(*([self.control]+self.anchorSettings))else:returnNone# Named like the wxWindow method to override it when generating codedefSetConstraints(self,value):curVal=self.control.GetConstraints()ifcurVal!=value:self.control.SetConstraints(value)ifself.designer.selection:self.designer.selection.updateAnchors()elifself.designer.multiSelection:forselectioninself.designer.multiSelection:selection.updateAnchors()self.designer.inspector.propertyUpdate('Anchors')defEvalConstraints(self,exprs,objects):ifexprs[0].startswith('LayoutAnchors'):ctrl,left,top,right,bottom= \
methodparse.safesplitfields(exprs[0][len('LayoutAnchors')+1:-1],',')ctrl,left,top,right,bottom=(objects[ctrl],self.eval(left),self.eval(top),self.eval(right),self.eval(bottom))self.anchorSettings=[left,top,right,bottom]return(self.LayoutAnchors(ctrl,left,top,right,bottom),)return(None,)defupdateAnchors(self,flagset,value):ifnotself.anchorSettings:self.defaultAnchors()foridxinrange(4):ifflagset[idx]:self.anchorSettings[idx]=valuedefremoveAnchors(self):self.anchorSettings=[]idx=0whileidx<len(self.textPropList):prop=self.textPropList[idx]ifprop.prop_setter=='SetConstraints'and \
prop.params[0].startswith('LayoutAnchors'):delself.textPropList[idx]breakelse:idx=idx+1defdefaultAnchors(self):self.anchorSettings=[True,True,False,False]defapplyConstraints(self):left,top,right,bottom=self.anchorSettingsself.control.SetConstraints(self.LayoutAnchors(self.control,left,top,right,bottom))defbeforeResize(self):lc=self.control.GetConstraints()self._applyConstraints=lc!=Noneandself.anchorSettingsifself._applyConstraints:self.SetConstraints(None)defafterResize(self):ifself._applyConstraintsandself.anchorSettings:self.applyConstraints()elifself.designer.sizersViewandhasattr(self.control,'_in_sizer'):szr=self.control._in_sizerifszr:forsiinszr.GetChildren():ifsi.IsWindow():ifsi.GetWindow()==self.control:p=self.control.GetPosition()s=self.control.GetSize()#si.SetDimension(p, s)si.SetInitSize(s.width,s.height)#szr.Layout()#self.designer.sizersView.layoutSizers()parent=self.control.GetParent()ifparent:wx.PostEvent(parent,wx.SizeEvent(parent.GetSize(),parent.GetId()))wx.CallAfter(parent.Refresh)#relayoutCtrl(self.designer)#self.designer.model.editor.setStatus('Sizer item update %s, %s'%(p, s))break#---Designer updaters-----------------------------------------------------------defSizeUpdate(self,oldValue,newValue):ifself.designer.selection:self.designer.selection.selectCtrl(self.control,self)self.designer.selection.moveCapture(self.control,self,wx.Point(0,0))defPositionUpdate(self,oldValue,newValue):ifself.designer.selection:self.designer.selection.selectCtrl(self.control,self)self.designer.selection.moveCapture(self.control,self,wx.Point(0,0))#---Size hints------------------------------------------------------------------defGetSizeHints(self,dummy):returnself._sizeHintsdefSetSizeHints(self,value):self._sizeHints=valueself.control.SetSizeHints(value[0],value[1],value[2],value[3])defEvalSizeHints(self,exprs,objects):res=[]forexprinexprs:res.append(self.eval(expr))returntuple(res)#---Cursors---------------------------------------------------------------------defGetCursor(self,x):returnself._cursordefSetCursor(self,value):self._cursor=valueself.control.SetCursor(value)#---Sizers----------------------------------------------------------------------defGetSizer(self,x):returnself.control.GetSizer()defSetSizer(self,value):ifvalueisnotNone:self.control._has_sizer=valuevalue._has_control=self.controlelse:ifhasattr(self.control,'_has_sizer'):szr=self.control._has_sizerifszr:ifhasattr(szr,'_has_control'):delszr._has_controldelself.control._has_sizerself.control.SetSizer(value)ifvalueisnotNone:value.Layout()self.designer.relayoutCtrl(self.control)#-------------------------------------------------------------------------------defGetCentered(self,dummy):returnself._centereddefCenter(self,value):self._centered=valueifvalue:self.control.Center(value)defGetShown(self,x):forpropinself.textPropList:ifprop.prop_setter=='Show':returnint(prop.params[0].lower()=='true')return1defShow(self,value):passdefGetEnabled(self,x):forpropinself.textPropList:ifprop.prop_setter=='Enable':returnint(prop.params[0].lower()=='true')return1defEnable(self,value):passclassChoicedDTC(WindowDTC):def__init__(self,name,designer,parent,ctrlClass):WindowDTC.__init__(self,name,designer,parent,ctrlClass)self.editors['Choices']=ChoicesConstrPropEditclassContainerDTC(WindowDTC):""" Parent for controls that contain/own other controls """def__init__(self,name,designer,parent,ctrlClass):WindowDTC.__init__(self,name,designer,parent,ctrlClass)self.container=TrueclassCollectionDTC(DesignTimeCompanion):""" Companions encapsulating list maintaining behaviour into a single property Maintains an index which points to the currently active item in the collection """propName='undefined'insertionMethod='undefined'deletionMethod='undefined'displayProp='undefined'indexProp='undefined'sourceObjName='parent'additionalMethods={}def__init__(self,name,designer,parentCompanion,ctrl):DesignTimeCompanion.__init__(self,name,designer)fromViews.CollectionEditimportCollectionEditorself.CollEditorFrame=CollectionEditorself.control=ctrlself.setCollectionMethod()self.index=0self.parentCompanion=parentCompaniondefsetCollectionMethod(self):self.collectionMethod='_init_coll_%s_%s'%(self.name,self.propName)defsetIndex(self,index):self.index=indexself.setConstr(self.textConstrLst[index])defsetConstrs(self,constrLst,inits,fins):self.initialisers=initsself.finalisers=finsself.textConstrLst=constrLstdefrenameCtrl(self,oldName,newName):DesignTimeCompanion.renameCtrl(self,oldName,newName)self.setCollectionMethod()defrenameCtrlRefs(self,oldName,newName):# textConstr and textPropList not used in collections# DesignTimeCompanion.renameCtrlRefs(self, oldName, newName)forconstrinself.textConstrLst:constr.renameCompName2(oldName,newName)defgetCount(self):returnlen(self.textConstrLst)defgetDisplayProp(self):tcl=self.textConstrLst[self.index]iftcl.method!=self.insertionMethod:iftcl.methodinself.additionalMethods:displayProp=self.additionalMethods[tcl.method][1]else:return'-'else:displayProp=self.displayPropifdisplayPropintcl.params:propSrc=tcl.params[displayProp]ifpropSrcand(propSrc[0]in("'",'"')orpropSrc[:2]in('u"',"u'")):returnself.eval(propSrc)else:returnpropSrcelse:return'-'definitialiser(self):""" When overriding, append this after derived initialiser """return['']deffinaliser(self):""" When overriding, append this before derived finaliser """return[]defappendItem(self,method=None,srcParams={}):self.index=self.getCount()ifmethodisNone:method=self.insertionMethodsrc=self.designTimeSource(self.index,method)src.update(srcParams)collItemInit=methodparse.CollectionItemInitParse(None,self.sourceObjName,method,src)self.textConstrLst.append(collItemInit)self.setConstr(collItemInit)self.applyDesignTimeDefaults(collItemInit.params,method)returncollItemInitdefdeleteItem(self,idx):# remove from ctrlifself.deletionMethod!='(None)':getattr(self.control,self.deletionMethod)(idx)# renumber items following deleted oneifself.indexProp!='(None)':forconstrinself.textConstrLst[idx:]:constr.params[self.indexProp]=`int(constr.params[self.indexProp]) -1`defmoveItem(self,idx,dir):tc=self.textConstrLst[idx]newIdx=min(max(idx+dir,0),len(self.textConstrLst)-1)ifnewIdx!=idx:delself.textConstrLst[idx]self.textConstrLst.insert(newIdx,tc)ifself.indexProp!='(None)':# swap index property values(self.textConstrLst[idx].params[self.indexProp],self.textConstrLst[newIdx].params[self.indexProp])= \
(self.textConstrLst[newIdx].params[self.indexProp],self.textConstrLst[idx].params[self.indexProp])returnnewIdxdefapplyDesignTimeDefaults(self,params,method=None):ifmethodisNone:method=self.insertionMethodargs=[]kwargs={}paramItems=self.designTimeDefaults(params,method).items()paramItems.sort()fork,vinparamItems:iftype(k)istype(0):args.append(v)else:kwargs[k]=vgetattr(self.control,method)(*args,**kwargs)defSetName(self,oldValue,newValue):self.name=newValueself.setCollectionMethod()defGetClass(self,dummy=None):returnself.propNamedefSetClass(self,value):passdefupdateWindowIds(self):pass## def addIds(self, lst):## """ Iterate over items and extend lst for collections with ids """## passdefdesignTimeDefaults(self,vals,method=None):""" Return a dictionary of parameters for adding an item to the collection """dtd={}forparaminvals.keys():dtd[param]=self.eval(vals[param])returndtddefinitCollection(self):passdefwriteCollectionInitialiser(self,output,stripFrmId=''):output.extend(self.initialiser())defwriteCollectionItems(self,output,stripFrmId=''):forcreatorinself.textConstrLst:self.addContinuedLine(sourceconst.bodyIndent+creator.asText(stripFrmId),output,sourceconst.bodyIndent)defwriteCollectionFinaliser(self,output,stripFrmId=''):output.extend(self.finaliser())defgetPropList(self):""" Returns a dictionary of methods suported by the control The dict has 'properties' and 'methods' keys which contain the getters/setters and undefined methods. """# XXX should use sub objects if available, but properties on collection# XXX items aren't supported yetreturnRTTI.getPropList(None,self)defdefaultAction(self):""" Called when a component is double clicked in a designer """pass## print 'CDTC', self.textConstrLst[self.index]defnotification(self,compn,action):""" Called when other components are deleted. Use it to clear references to components which are being deleted. """# XXX Should use this mechanism to trap renames as wellpass## print 'CollectionDTC.notification', compn, actionclassCollectionIddDTC(CollectionDTC):""" Collections which have window ids and events """windowIdName='id'idProp='(undefined)'idPropNameFrom='(undefined)'def__init__(self,name,designer,parentCompanion,ctrl):CollectionDTC.__init__(self,name,designer,parentCompanion,ctrl)self.editors={'ItemId':ItemIdConstrPropEdit}defproperties(self):props=CollectionDTC.properties(self)props['ItemId']=('CompnRoute',self.GetItemId,self.SetItemId)returnpropsdefevtName(self):# return '%s%s%d' % (self.name, self.propName, self.index)base=self.newWinId('')itemId=self.GetItemId(None)[len(base):].capitalize()return'%s%s'%(self.name,itemId)## def setIndex(self, idx):## CollectionDCT.setIndex(idx)## self.setEvents(defgetEvents(self):evts=[]idxWId=self.getWinId()forevtinself.textEventList:ifevt.windowid==idxWId:evts.append(evt)returnevtsdefgetWinId(self):tcl=self.textConstrLst[self.index]ifself.idPropintcl.params:returntcl.params[self.idProp]else:return-1defgetDesignTimeWinId(self):returnself.control.GetMenuItems()[self.index].GetId()defaddIds(self,lst):forconstrinself.textConstrLst:ifself.idPropinconstr.params:wId=constr.params[self.idProp]ifwIdinEventCollections.reservedWxIds:name,wId=self.newUnusedItemNames(0)lst.append(wId)defappendItem(self,method=None):CollectionDTC.appendItem(self,method)#1 self.generateWindowId(self.index)self.updateWindowIds()defdeleteItemEvents(self,idx):constr=self.textConstrLst[idx]ifself.idPropinconstr.params:wIdStr=constr.params[self.idProp]forevtinself.textEventList[:]:ifevt.windowid==wIdStr:self.textEventList.remove(evt)defdeleteItem(self,idx):self.deleteItemEvents(idx)CollectionDTC.deleteItem(self,idx)self.updateWindowIds()defnewUnusedItemNames(self,wId):while1:newItemName='%s%d'%(self.propName,wId)winId=self.newWinId(newItemName)ifself.isIdUsed(winId):wId=wId+1else:breakreturnnewItemName,winIddefisIdUsed(self,wId):fortcinself.textConstrLst:ifself.idPropintc.paramsandtc.params[self.idProp]==wId:returnTruereturnFalsedefnewWinId(self,itemName):returnUtils.windowIdentifier(self.designer.controllerView.GetName(),self.name+itemName)defgenerateWindowId(self,idx):returndefSetName(self,oldValue,newValue):CollectionDTC.SetName(self,oldValue,newValue)self.updateWindowIds()defevtGetter(self,name):wId=self.getWinId()forevtinself.textEventList:ifevt.event_name==nameandevt.windowid==wId:returnevt.trigger_methreturnNonedefevtSetter(self,name,value):wId=self.getWinId()forevtinself.textEventList:ifevt.event_name==nameandevt.windowid==wId:evt.trigger_meth=valuereturndefpersistEvt(self,name,value,wId=None):""" Add a source entry for an event or update the trigger method of am existing event. """ifwIdisNone:wId=self.getWinId()forevtinself.textEventList:ifevt.event_name==nameandevt.windowid==wId:evt.trigger_meth=valuereturnifself.control==self.designerorwIdisnotNone:comp_name=''else:comp_name=self.nameself.textEventList.append(methodparse.EventParse(None,comp_name,name,value,wId))defupdateWindowIds(self):foridxinrange(len(self.textConstrLst)):# XXX no op?self.generateWindowId(idx)defdesignTimeDefaults(self,vals,method=None):""" Return a dictionary of parameters for the constructor of a wxPython control. e.g. {'name': 'button1', etc) Derived classes should only call this base method if method requires an id parameter. This is usually the case."""values=copy.copy(vals)values[self.idProp]=`wx.NewId()`dts=CollectionDTC.designTimeDefaults(self,values)dts[self.idProp]=wx.NewId()returndtsdefGetItemId(self,x):returnself.textConstrLst[self.index].params[self.idProp]defSetItemId(self,value):oldValue=self.textConstrLst[self.index].params[self.idProp]self.textConstrLst[self.index].params[self.idProp]=valueforevtinself.textEventList:ifevt.windowid==oldValue:evt.windowid=value