1 # Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # Lesser General Public License for more details.
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 ## \defgroup studyedit studyedit
23 # This module provides a new class \bStudyEditor to complement \bStudy
24 # and \bStudyBuilder classes.
28 This module provides a new class :class:`StudyEditor` to complement
29 :class:`Study` and :class:`StudyBuilder` classes.
35 from salome.kernel.logger import Logger
36 from salome.kernel import termcolor
37 logger = Logger("salome.kernel.studyedit", color = termcolor.PURPLE)
40 _DEFAULT_CONTAINER = "FactoryServer"
42 # The codec to use for strings that are displayed in Salome study tree is Latin-1
43 ENCODING_FOR_SALOME_STUDY = "iso-8859-1"
45 ## Return the ID of the active study. In GUI mode, this function is equivalent
46 # to salome.sg.getActiveStudyId(). Outside GUI, it returns <b> salome.myStudyId </b>
49 def getActiveStudyId():
51 Return the ID of the active study. In GUI mode, this function is equivalent
52 to ``salome.sg.getActiveStudyId()``. Outside GUI, it returns
53 ``salome.myStudyId`` variable.
56 # Warning: we don't use salome.getActiveStudy() here because it doesn't
57 # work properly when called from Salome modules (multi-study interpreter
59 if salome.hasDesktop():
60 return salome.sg.getActiveStudyId()
62 return salome.myStudyId
65 return getStudyFromStudyId(getActiveStudyId())
67 def getStudyFromStudyId(studyId):
69 study = salome.myStudyManager.GetStudyByID(studyId)
72 def getStudyIdFromStudy(study):
73 studyId = study._get_StudyId()
76 ## Return a \b StudyEditor instance to edit the study with ID studyId.
77 # If \b studyId is \b None, return an editor for the current study.
79 def getStudyEditor(studyId = None):
81 Return a :class:`StudyEditor` instance to edit the study with ID
82 `studyId`. If `studyId` is :const:`None`, return an editor for the current
86 studyId = getActiveStudyId()
87 if studyId not in _editors:
88 _editors[studyId] = StudyEditor(studyId)
89 return _editors[studyId]
91 ## This class provides utility methods to complement \b Study and
92 # \b StudyBuilder classes. Those methods may be moved in those classes
93 # in the future. The parameter \b studyId defines the ID of the study to
94 # edit. If it is \em None, the edited study will be the current study.
95 # The preferred way to get a StudyEditor object is through the method
96 # \b getStudyEditor which allows to reuse existing instances.
98 # \param studyId This instance attribute contains the ID of the edited study.
99 # This attribute should not be modified.
101 # \param study This instance attribute contains the underlying \b Study object.
102 # It can be used to access the study but the attribute itself should not
105 # \param builder This instance attribute contains the underlying \b StudyBuilder
106 # object. It can be used to edit the study but the attribute itself
107 # should not be modified.
111 This class provides utility methods to complement :class:`Study` and
112 :class:`StudyBuilder` classes. Those methods may be moved in those classes
113 in the future. The parameter `studyId` defines the ID of the study to
114 edit. If it is :const:`None`, the edited study will be the current study.
115 The preferred way to get a StudyEditor object is through the method
116 :meth:`getStudyEditor` which allows to reuse existing instances.
118 .. attribute:: studyId
120 This instance attribute contains the ID of the edited study. This
121 attribute should not be modified.
125 This instance attribute contains the underlying :class:`Study` object.
126 It can be used to access the study but the attribute itself should not
129 .. attribute:: builder
131 This instance attribute contains the underlying :class:`StudyBuilder`
132 object. It can be used to edit the study but the attribute itself
133 should not be modified.
136 def __init__(self, studyId = None):
139 studyId = getActiveStudyId()
140 self.studyId = studyId
141 self.study = salome.myStudyManager.GetStudyByID(studyId)
142 if self.study is None:
143 raise Exception("Can't create StudyEditor object: "
144 "Study %d doesn't exist" % studyId)
145 self.builder = self.study.NewBuilder()
147 ## Find a component corresponding to the Salome module \b moduleName in
148 # the study. If none is found, create a new component and associate it
149 # with the corresponding engine (i.e. the engine named \b moduleName).
150 # Note that in Salome 5, the module name and engine name must be
151 # identical (every module must provide an engine with the same name).
152 # In Salome 6 it will be possible to define a different name for the
155 # \param moduleName (string) name of the module corresponding to the component
156 # (the module name is the string value in the
157 # attribute "AttributeComment" of the component)
159 # \param componentName (string) name of the new component if created.
160 # If \b None, use \b moduleName instead.
162 # \param icon (string) icon for the new component (attribute "AttributePixMap").
164 # \param containerName (string) name of the container in which the engine should be
167 # \return the SComponent found or created.
168 def findOrCreateComponent(self, moduleName, componentName = None,
169 icon = None, containerName = _DEFAULT_CONTAINER):
171 Find a component corresponding to the Salome module `moduleName` in
172 the study. If none is found, create a new component and associate it
173 with the corresponding engine (i.e. the engine named `moduleName`).
174 Note that in Salome 5, the module name and engine name must be
175 identical (every module must provide an engine with the same name).
176 In Salome 6 it will be possible to define a different name for the
179 :type moduleName: string
180 :param moduleName: name of the module corresponding to the component
181 (the module name is the string value in the
182 attribute "AttributeComment" of the component)
184 :type componentName: string
185 :param componentName: name of the new component if created. If
186 :const:`None`, use `moduleName` instead.
189 :param icon: icon for the new component (attribute "AttributePixMap").
191 :type containerName: string
192 :param containerName: name of the container in which the engine should be
195 :return: the SComponent found or created.
198 sComponent = self.study.FindComponent(moduleName)
199 if sComponent is None:
200 sComponent = self.builder.NewComponent(moduleName)
201 # Note that the NewComponent method set the "comment" attribute to the
202 # value of its argument (moduleName here)
203 if componentName is None:
204 componentName = moduleName
205 self.builder.SetName(sComponent, componentName)
207 # _MEM_ : This will be effective if and only if "moduleName"
208 # really corresponds to the module name (as specified in the
210 self.setIcon(sComponent, icon)
212 # This part will stay inactive until Salome 6. In Salome 6, the
213 # engine name will be stored separately from the module name.
214 # An internal convention (in this class) is to store the name of the
215 # associated engine in the parameter attribute of the scomponent (so that
216 # it could be retrieved in a future usage of this scomponent, for example,
217 # for the need of the function loadComponentEngine). The comment attribute
218 # SHOULD NOT be used for this purpose because it's used by the SALOME
219 # resources manager to identify the SALOME module and then localized
221 #attr = self.builder.FindOrCreateAttribute( sComponent, "AttributeParameter" )
222 #attr.SetString( "ENGINE_NAME", engineName )
224 engine = salome.lcc.FindOrLoadComponent(containerName, moduleName)
226 raise Exception("Cannot load engine %s in container %s. See "
227 "logs of container %s for more details." %
228 (moduleName, containerName, containerName))
229 self.builder.DefineComponentInstance(sComponent, engine)
233 ## Load the engine corresponding to \b sComponent in the container
234 # \b containerName, associate the engine with the component and load the
235 # CORBA objects of this component in the study.
236 def loadComponentEngine(self, sComponent,
237 containerName = _DEFAULT_CONTAINER):
239 Load the engine corresponding to `sComponent` in the container
240 `containerName`, associate the engine with the component and load the
241 CORBA objects of this component in the study.
243 # This part will stay inactive until Salome 6. In Salome 6, the
244 # engine name will be stored separately from the module name.
245 #attr = self.builder.FindOrCreateAttribute( sComponent, "AttributeParameter" )
246 #engineName = attr.GetString( "ENGINE_NAME" )
247 engine = salome.lcc.FindOrLoadComponent(containerName,
248 sComponent.GetComment())
250 raise Exception("Cannot load component %s in container %s. See "
251 "logs of container %s for more details." %
252 (sComponent.GetComment(), containerName,
254 self.builder.LoadWith(sComponent, engine)
256 ## Get the CORBA object associated with the SObject \b item, eventually by
257 # first loading it with the corresponding engine.
258 def getOrLoadObject(self, item):
260 Get the CORBA object associated with the SObject `item`, eventually by
261 first loading it with the corresponding engine.
263 object = item.GetObject()
264 if object is None: # the engine has not been loaded yet
265 sComponent = item.GetFatherComponent()
266 self.loadComponentEngine(sComponent)
267 object = item.GetObject()
270 ## Find an object under \b fatherItem in the study with the given
271 # attributes. Return the first one found if at least one exists,
272 # otherwise create a new one with the given attributes and return it.
274 # See \b setItem() for the description of the parameters.
275 def findOrCreateItem(self, fatherItem, name, fileType = None,
276 fileName = None, comment = None, icon = None,
277 IOR = None, typeId = None):
279 Find an object under `fatherItem` in the study with the given
280 attributes. Return the first one found if at least one exists,
281 otherwise create a new one with the given attributes and return it.
283 See :meth:`setItem` for the description of the parameters.
285 sObject = self.findItem(fatherItem, name, fileType, fileName, comment,
288 sObject = self.createItem(fatherItem, name, fileType, fileName,
289 comment, icon, IOR, typeId)
292 ## Find an item with given attributes under \b fatherItem in the study. If
293 # none is found, return \b None. If several items correspond to
294 # the parameters, only the first one is returned. The search is made
295 # only on given parameters (i.e. not \b None). To look explicitly
296 # for an empty attribute, use an empty string in the corresponding
299 # See \b setItem() for the description of the parameters.
300 def findItem(self, fatherItem, name = None, fileType = None,
301 fileName = None, comment = None, icon = None, IOR = None,
304 Find an item with given attributes under `fatherItem` in the study. If
305 none is found, return :const:`None`. If several items correspond to
306 the parameters, only the first one is returned. The search is made
307 only on given parameters (i.e. not :const:`None`). To look explicitly
308 for an empty attribute, use an empty string in the corresponding
311 See :meth:`setItem` for the description of the parameters.
314 childIterator = self.study.NewChildIterator(fatherItem)
315 while childIterator.More() and foundItem is None:
316 childItem = childIterator.Value()
318 (name is None or self.getName(childItem) == name) and \
319 (fileType is None or \
320 self.getFileType(childItem) == fileType) and \
321 (fileName is None or \
322 self.getFileName(childItem) == fileName) and \
323 (comment is None or self.getComment(childItem) == comment) and \
325 self.getIcon(childItem) == icon) and \
326 (IOR is None or childItem.GetIOR() == IOR) and \
328 self.getTypeId(childItem) == typeId):
329 foundItem = childItem
333 ## Create a new object named \b name under \b fatherItem in the study, with
334 # the given attributes. If an object named \b name already exists under
335 # the father object, the new object is created with a new name \b name_X
336 # where X is the first available index.
338 # param fatherItem (SObject) item under which the new item will be added.
339 # \return new SObject created in the study.
341 # See \b setItem() for the description of the other parameters.
342 def createItem(self, fatherItem, name, fileType = None, fileName = None,
343 comment = None, icon = None, IOR = None, typeId = None):
345 Create a new object named `name` under `fatherItem` in the study, with
346 the given attributes. If an object named `name` already exists under
347 the father object, the new object is created with a new name `name_X`
348 where X is the first available index.
350 :type fatherItem: SObject
351 :param fatherItem: item under which the new item will be added.
353 :return: new SObject created in the study
355 See :meth:`setItem` for the description of the other parameters.
357 aSObject = self.builder.NewObject(fatherItem)
359 aChildIterator = self.study.NewChildIterator(fatherItem)
363 anIdRE = re.compile("^" + aDelim + "[0-9]+")
364 aNameRE = re.compile("^" + name)
365 while aChildIterator.More():
366 aSObj = aChildIterator.Value()
367 aChildIterator.Next()
368 aName = aSObj.GetName()
369 if re.match(aNameRE,aName):
370 aTmp = aName[aLength:]
371 if re.match(anIdRE,aTmp):
373 anId = string.atol(aTmp[1:])
387 aName = aName + aDelim + str(aMaxId)
390 self.setItem(aSObject, aName, fileType, fileName, comment, icon,
395 ## Modify the attributes of an item in the study. Unspecified attributes
396 # (i.e. those set to \b None) are left unchanged.
398 # \param item (SObject) item to modify.
400 # \param name (string or unicode) item name (attribute \b AttributeName).
402 # \param fileType (string or unicode) item file type (attribute \b AttributeFileType).
404 # \param fileName (string or unicode) item file name (attribute \b AttributeExternalFileDef).
406 # \param comment (string or unicode) item comment (attribute \b AttributeComment). Note that
407 # this attribute will appear in the \b Value column in the object browser.
409 # \param icon (string or unicode) item icon name (attribute \b AttributePixMap).
411 # \param IOR (string) IOR of a CORBA object associated with the item
412 # (attribute \b AttributeIOR).
414 # \param typeId (integer) item type (attribute \b AttributeLocalID).
415 def setItem(self, item, name = None, fileType = None, fileName = None,
416 comment = None, icon = None, IOR = None, typeId = None):
418 Modify the attributes of an item in the study. Unspecified attributes
419 (i.e. those set to :const:`None`) are left unchanged.
422 :param item: item to modify.
424 :type name: string or unicode
425 :param name: item name (attribute 'AttributeName').
427 :type fileType: string or unicode
428 :param fileType: item file type (attribute 'AttributeFileType').
430 :type fileName: string or unicode
431 :param fileName: item file name (attribute
432 'AttributeExternalFileDef').
434 :type comment: string or unicode
435 :param comment: item comment (attribute 'AttributeComment'). Note that
436 this attribute will appear in the 'Value' column in
439 :type icon: string or unicode
440 :param icon: item icon name (attribute 'AttributePixMap').
443 :param IOR: IOR of a CORBA object associated with the item
444 (attribute 'AttributeIOR').
446 :type typeId: integer
447 :param typeId: item type (attribute 'AttributeLocalID').
449 logger.debug("setItem (ID=%s): name=%s, fileType=%s, fileName=%s, "
450 "comment=%s, icon=%s, IOR=%s" %
451 (item.GetID(), name, fileType, fileName, comment,
454 self.setName(item, name)
455 if fileType is not None:
456 self.setFileType(item, fileType)
457 if fileName is not None:
458 self.setFileName(item, fileName)
459 if comment is not None:
460 self.setComment(item, comment)
462 self.setIcon(item, icon)
464 self.builder.SetIOR(item, IOR)
465 if typeId is not None:
466 self.setTypeId(item, typeId)
468 ## Remove the given item from the study. Note that the items are never
469 # really deleted. They just don't appear in the study anymore.
471 # \param item (SObject) the item to be removed
473 # \param withChildren (boolean) if \b True, also remove the children of item
475 # \return \b True if the item was removed successfully, or
476 # \b False if an error happened.
477 def removeItem(self, item, withChildren = False ):
479 Remove the given item from the study. Note that the items are never
480 really deleted. They just don't appear in the study anymore.
483 :param item: the item to be removed
485 :type withChildren: boolean
486 :param withChildren: if :const:`True`, also remove the children of
489 :return: :const:`True` if the item was removed successfully, or
490 :const:`False` if an error happened.
495 self.builder.RemoveObjectWithChildren(item)
497 self.builder.RemoveObject(item)
503 ## Find an item tagged \b tag under \b fatherItem in the study tree or
504 # create it if there is none, then set its attributes.
506 # \param fatherItem (SObject) item under which the tagged item will be looked for
507 # and eventually created.
509 # \param tag integer) tag of the item to look for.
511 # \return the SObject at \b tag if found or created successfully, or
512 # \b None if an error happened.
514 # See \b setItem() for the description of the other parameters.
515 def setItemAtTag(self, fatherItem, tag, name = None, fileType = None,
516 fileName = None, comment = None, icon = None, IOR = None,
519 Find an item tagged `tag` under `fatherItem` in the study tree or
520 create it if there is none, then set its attributes.
522 :type fatherItem: SObject
523 :param fatherItem: item under which the tagged item will be looked for
524 and eventually created.
527 :param tag: tag of the item to look for.
529 :return: the SObject at `tag` if found or created successfully, or
530 :const:`None` if an error happened.
532 See :meth:`setItem` for the description of the other parameters.
534 found, sObj = fatherItem.FindSubObject(tag)
536 sObj = self.builder.NewObjectToTag(fatherItem, tag)
537 self.setItem(sObj, name, fileType, fileName, comment, icon,
541 ## Return the name of the object sObject
542 def getName(self, sObject):
543 val = sObject.GetName()
544 return str(val, ENCODING_FOR_SALOME_STUDY)
546 ## Set the name of the object sObject
547 def setName(self, sObject, name):
548 self.builder.SetName(sObject, name.encode(ENCODING_FOR_SALOME_STUDY))
550 ## Return the comment of the object sObject
551 def getComment(self, sObject):
552 val = sObject.GetComment()
553 return str(val, ENCODING_FOR_SALOME_STUDY)
555 ## Set the comment of the object sObject
556 def setComment(self, sObject, comment):
557 self.builder.SetComment(sObject, comment.encode(ENCODING_FOR_SALOME_STUDY))
559 ## Return the value of the attribute named \b attributeName on the object
560 # sObject, or \b default if the attribute doesn't exist.
561 def getAttributeValue(self, sObject, attributeName, default = None):
563 Return the value of the attribute named `attributeName` on the object
564 `sObject`, or `default` if the attribute doesn't exist.
567 found, attr = self.builder.FindAttribute(sObject, attributeName)
572 ## Set the value of the attribute named \b attributeName on the object
573 # sObject to the value \b attributeValue.
574 def setAttributeValue(self, sObject, attributeName, attributeValue):
576 Set the value of the attribute named `attributeName` on the object
577 `sObject` to the value `attributeValue`.
579 attr = self.builder.FindOrCreateAttribute(sObject, attributeName)
580 attr.SetValue(attributeValue)
582 ## Return the value of the attribute "AttributeLocalID" of the object
583 # sObject, or \b None if it is not set.
584 def getTypeId(self, sObject):
586 Return the value of the attribute "AttributeLocalID" of the object
587 `sObject`, or :const:`None` if it is not set.
589 return self.getAttributeValue(sObject, "AttributeLocalID")
591 ## Set the attribute "AttributeLocalID" of the object \b sObject to the
593 def setTypeId(self, sObject, value):
595 Set the attribute "AttributeLocalID" of the object `sObject` to the
598 self.setAttributeValue(sObject, "AttributeLocalID", value)
600 ## Return the value of the attribute "AttributeFileType" of the object
601 # sObject, or an empty string if it is not set.
602 def getFileType(self, sObject):
604 Return the value of the attribute "AttributeFileType" of the object
605 `sObject`, or an empty string if it is not set.
607 val = self.getAttributeValue(sObject, "AttributeFileType", "")
608 return str(val, ENCODING_FOR_SALOME_STUDY)
610 ## Set the attribute "AttributeFileType" of the object sObject to the
612 def setFileType(self, sObject, value):
614 Set the attribute "AttributeFileType" of the object `sObject` to the
617 self.setAttributeValue(sObject, "AttributeFileType",
618 value.encode(ENCODING_FOR_SALOME_STUDY))
620 ## Return the value of the attribute "AttributeExternalFileDef" of the
621 # object sObject, or an empty string if it is not set.
622 def getFileName(self, sObject):
624 Return the value of the attribute "AttributeExternalFileDef" of the
625 object `sObject`, or an empty string if it is not set.
627 val = self.getAttributeValue(sObject, "AttributeExternalFileDef", "")
628 return str(val, ENCODING_FOR_SALOME_STUDY)
630 ## Set the attribute "AttributeExternalFileDef" of the object sObject
631 # to the value value.
632 def setFileName(self, sObject, value):
634 Set the attribute "AttributeExternalFileDef" of the object `sObject`
635 to the value `value`.
637 self.setAttributeValue(sObject, "AttributeExternalFileDef",
638 value.encode(ENCODING_FOR_SALOME_STUDY))
640 ## Return the value of the attribute "AttributePixMap" of the object
641 # sObject, or an empty string if it is not set.
642 def getIcon(self, sObject):
644 Return the value of the attribute "AttributePixMap" of the object
645 `sObject`, or an empty string if it is not set.
648 found, attr = self.builder.FindAttribute(sObject, "AttributePixMap")
649 if found and attr.HasPixMap():
650 value = attr.GetPixMap()
651 return str(value, ENCODING_FOR_SALOME_STUDY)
653 ## Set the attribute "AttributePixMap" of the object sObject to the
655 def setIcon(self, sObject, value):
657 Set the attribute "AttributePixMap" of the object `sObject` to the
660 attr = self.builder.FindOrCreateAttribute(sObject, "AttributePixMap")
661 attr.SetPixMap(value.encode(ENCODING_FOR_SALOME_STUDY))