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.
34 from salome.kernel import termcolor
35 from salome.kernel.logger import Logger
39 logger = Logger("salome.kernel.studyedit", color=termcolor.PURPLE)
42 _DEFAULT_CONTAINER = "FactoryServer"
44 ## Return the ID of the active study. In GUI mode, this function is equivalent
45 # to salome.sg.getActiveStudyId(). Outside GUI, it returns <b> salome.myStudyId </b>
48 def getActiveStudyId():
50 Return the ID of the active study. In GUI mode, this function is equivalent
51 to ``salome.sg.getActiveStudyId()``. Outside GUI, it returns
52 ``salome.myStudyId`` variable.
55 # Warning: we don't use salome.getActiveStudy() here because it doesn't
56 # work properly when called from Salome modules (multi-study interpreter
58 if salome.hasDesktop():
59 return salome.sg.getActiveStudyId()
61 return salome.myStudyId
64 return getStudyFromStudyId(getActiveStudyId())
66 def getStudyFromStudyId(studyId):
68 study = salome.myStudyManager.GetStudyByID(studyId)
71 def getStudyIdFromStudy(study):
72 studyId = study._get_StudyId()
75 ## Return a \b StudyEditor instance to edit the study with ID studyId.
76 # If \b studyId is \b None, return an editor for the current study.
78 def getStudyEditor(studyId = None):
80 Return a :class:`StudyEditor` instance to edit the study with ID
81 `studyId`. If `studyId` is :const:`None`, return an editor for the current
85 studyId = getActiveStudyId()
86 if studyId not in _editors:
87 _editors[studyId] = StudyEditor(studyId)
88 return _editors[studyId]
90 ## This class provides utility methods to complement \b Study and
91 # \b StudyBuilder classes. Those methods may be moved in those classes
92 # in the future. The parameter \b studyId defines the ID of the study to
93 # edit. If it is \em None, the edited study will be the current study.
94 # The preferred way to get a StudyEditor object is through the method
95 # \b getStudyEditor which allows to reuse existing instances.
97 # \param studyId This instance attribute contains the ID of the edited study.
98 # This attribute should not be modified.
100 # \param study This instance attribute contains the underlying \b Study object.
101 # It can be used to access the study but the attribute itself should not
104 # \param builder This instance attribute contains the underlying \b StudyBuilder
105 # object. It can be used to edit the study but the attribute itself
106 # should not be modified.
110 This class provides utility methods to complement :class:`Study` and
111 :class:`StudyBuilder` classes. Those methods may be moved in those classes
112 in the future. The parameter `studyId` defines the ID of the study to
113 edit. If it is :const:`None`, the edited study will be the current study.
114 The preferred way to get a StudyEditor object is through the method
115 :meth:`getStudyEditor` which allows to reuse existing instances.
117 .. attribute:: studyId
119 This instance attribute contains the ID of the edited study. This
120 attribute should not be modified.
124 This instance attribute contains the underlying :class:`Study` object.
125 It can be used to access the study but the attribute itself should not
128 .. attribute:: builder
130 This instance attribute contains the underlying :class:`StudyBuilder`
131 object. It can be used to edit the study but the attribute itself
132 should not be modified.
135 def __init__(self, studyId = None):
138 studyId = getActiveStudyId()
139 self.studyId = studyId
140 self.study = salome.myStudyManager.GetStudyByID(studyId)
141 if self.study is None:
142 raise Exception("Can't create StudyEditor object: "
143 "Study %d doesn't exist" % studyId)
144 self.builder = self.study.NewBuilder()
146 ## Find a component corresponding to the Salome module \b moduleName in
147 # the study. If none is found, create a new component and associate it
148 # with the corresponding engine (i.e. the engine named \b moduleName).
149 # Note that in Salome 5, the module name and engine name must be
150 # identical (every module must provide an engine with the same name).
151 # In Salome 6 it will be possible to define a different name for the
154 # \param moduleName (string) name of the module corresponding to the component
155 # (the module name is the string value in the
156 # attribute "AttributeComment" of the component)
158 # \param componentName (string) name of the new component if created.
159 # If \b None, use \b moduleName instead.
161 # \param icon (string) icon for the new component (attribute "AttributePixMap").
163 # \param containerName (string) name of the container in which the engine should be
166 # \return the SComponent found or created.
167 def findOrCreateComponent(self, moduleName, componentName = None,
168 icon = None, containerName = _DEFAULT_CONTAINER):
170 Find a component corresponding to the Salome module `moduleName` in
171 the study. If none is found, create a new component and associate it
172 with the corresponding engine (i.e. the engine named `moduleName`).
173 Note that in Salome 5, the module name and engine name must be
174 identical (every module must provide an engine with the same name).
175 In Salome 6 it will be possible to define a different name for the
178 :type moduleName: string
179 :param moduleName: name of the module corresponding to the component
180 (the module name is the string value in the
181 attribute "AttributeComment" of the component)
183 :type componentName: string
184 :param componentName: name of the new component if created. If
185 :const:`None`, use `moduleName` instead.
188 :param icon: icon for the new component (attribute "AttributePixMap").
190 :type containerName: string
191 :param containerName: name of the container in which the engine should be
194 :return: the SComponent found or created.
197 sComponent = self.study.FindComponent(moduleName)
198 if sComponent is None:
199 sComponent = self.builder.NewComponent(moduleName)
200 # Note that the NewComponent method set the "comment" attribute to the
201 # value of its argument (moduleName here)
202 if componentName is None:
203 componentName = moduleName
204 self.builder.SetName(sComponent, componentName)
206 # _MEM_ : This will be effective if and only if "moduleName"
207 # really corresponds to the module name (as specified in the
209 self.setIcon(sComponent, icon)
211 # This part will stay inactive until Salome 6. In Salome 6, the
212 # engine name will be stored separately from the module name.
213 # An internal convention (in this class) is to store the name of the
214 # associated engine in the parameter attribute of the scomponent (so that
215 # it could be retrieved in a future usage of this scomponent, for example,
216 # for the need of the function loadComponentEngine). The comment attribute
217 # SHOULD NOT be used for this purpose because it's used by the SALOME
218 # resources manager to identify the SALOME module and then localized
220 #attr = self.builder.FindOrCreateAttribute( sComponent, "AttributeParameter" )
221 #attr.SetString( "ENGINE_NAME", engineName )
223 engine = salome.lcc.FindOrLoadComponent(containerName, moduleName)
225 raise Exception("Cannot load engine %s in container %s. See "
226 "logs of container %s for more details." %
227 (moduleName, containerName, containerName))
228 self.builder.DefineComponentInstance(sComponent, engine)
232 ## Load the engine corresponding to \b sComponent in the container
233 # \b containerName, associate the engine with the component and load the
234 # CORBA objects of this component in the study.
235 def loadComponentEngine(self, sComponent,
236 containerName = _DEFAULT_CONTAINER):
238 Load the engine corresponding to `sComponent` in the container
239 `containerName`, associate the engine with the component and load the
240 CORBA objects of this component in the study.
242 # This part will stay inactive until Salome 6. In Salome 6, the
243 # engine name will be stored separately from the module name.
244 #attr = self.builder.FindOrCreateAttribute( sComponent, "AttributeParameter" )
245 #engineName = attr.GetString( "ENGINE_NAME" )
246 engine = salome.lcc.FindOrLoadComponent(containerName,
247 sComponent.GetComment())
249 raise Exception("Cannot load component %s in container %s. See "
250 "logs of container %s for more details." %
251 (sComponent.GetComment(), containerName,
253 self.builder.LoadWith(sComponent, engine)
255 ## Get the CORBA object associated with the SObject \b item, eventually by
256 # first loading it with the corresponding engine.
257 def getOrLoadObject(self, item):
259 Get the CORBA object associated with the SObject `item`, eventually by
260 first loading it with the corresponding engine.
262 object = item.GetObject()
263 if object is None: # the engine has not been loaded yet
264 sComponent = item.GetFatherComponent()
265 self.loadComponentEngine(sComponent)
266 object = item.GetObject()
269 ## Find an object under \b fatherItem in the study with the given
270 # attributes. Return the first one found if at least one exists,
271 # otherwise create a new one with the given attributes and return it.
273 # See \b setItem() for the description of the parameters.
274 def findOrCreateItem(self, fatherItem, name, fileType = None,
275 fileName = None, comment = None, icon = None,
276 IOR = None, typeId = None):
278 Find an object under `fatherItem` in the study with the given
279 attributes. Return the first one found if at least one exists,
280 otherwise create a new one with the given attributes and return it.
282 See :meth:`setItem` for the description of the parameters.
284 sObject = self.findItem(fatherItem, name, fileType, fileName, comment,
287 sObject = self.createItem(fatherItem, name, fileType, fileName,
288 comment, icon, IOR, typeId)
291 ## Find an item with given attributes under \b fatherItem in the study. If
292 # none is found, return \b None. If several items correspond to
293 # the parameters, only the first one is returned. The search is made
294 # only on given parameters (i.e. not \b None). To look explicitly
295 # for an empty attribute, use an empty string in the corresponding
298 # See \b setItem() for the description of the parameters.
299 def findItem(self, fatherItem, name = None, fileType = None,
300 fileName = None, comment = None, icon = None, IOR = None,
303 Find an item with given attributes under `fatherItem` in the study. If
304 none is found, return :const:`None`. If several items correspond to
305 the parameters, only the first one is returned. The search is made
306 only on given parameters (i.e. not :const:`None`). To look explicitly
307 for an empty attribute, use an empty string in the corresponding
310 See :meth:`setItem` for the description of the parameters.
313 childIterator = self.study.NewChildIterator(fatherItem)
314 while childIterator.More() and foundItem is None:
315 childItem = childIterator.Value()
317 (name is None or self.getName(childItem) == name) and \
318 (fileType is None or \
319 self.getFileType(childItem) == fileType) and \
320 (fileName is None or \
321 self.getFileName(childItem) == fileName) and \
322 (comment is None or self.getComment(childItem) == comment) and \
324 self.getIcon(childItem) == icon) and \
325 (IOR is None or childItem.GetIOR() == IOR) and \
327 self.getTypeId(childItem) == typeId):
328 foundItem = childItem
332 ## Create a new object named \b name under \b fatherItem in the study, with
333 # the given attributes. If an object named \b name already exists under
334 # the father object, the new object is created with a new name \b name_X
335 # where X is the first available index.
337 # param fatherItem (SObject) item under which the new item will be added.
338 # \return new SObject created in the study.
340 # See \b setItem() for the description of the other parameters.
341 def createItem(self, fatherItem, name, fileType=None, fileName=None,
342 comment=None, icon=None, IOR=None, typeId=None):
344 Create a new object named `name` under `fatherItem` in the study, with
345 the given attributes. If an object named `name` already exists under
346 the father object, the new object is created with a new name `name_X`
347 where X is the first available index.
349 :type fatherItem: SObject
350 :param fatherItem: item under which the new item will be added.
352 :return: new SObject created in the study
354 See :meth:`setItem` for the description of the other parameters.
356 aSObject = self.builder.NewObject(fatherItem)
358 aChildIterator = self.study.NewChildIterator(fatherItem)
362 anIdRE = re.compile("^" + aDelim + "[0-9]+")
363 aNameRE = re.compile("^" + name)
364 while aChildIterator.More():
365 aSObj = aChildIterator.Value()
366 aChildIterator.Next()
367 aName = aSObj.GetName()
368 if re.match(aNameRE, aName):
369 aTmp = aName[aLength:]
370 if re.match(anIdRE, aTmp):
385 aName = aName + aDelim + str(aMaxId)
388 self.setItem(aSObject, aName, fileType, fileName, comment, icon,
393 ## Modify the attributes of an item in the study. Unspecified attributes
394 # (i.e. those set to \b None) are left unchanged.
396 # \param item (SObject) item to modify.
398 # \param name (string or unicode) item name (attribute \b AttributeName).
400 # \param fileType (string or unicode) item file type (attribute \b AttributeFileType).
402 # \param fileName (string or unicode) item file name (attribute \b AttributeExternalFileDef).
404 # \param comment (string or unicode) item comment (attribute \b AttributeComment). Note that
405 # this attribute will appear in the \b Value column in the object browser.
407 # \param icon (string or unicode) item icon name (attribute \b AttributePixMap).
409 # \param IOR (string) IOR of a CORBA object associated with the item
410 # (attribute \b AttributeIOR).
412 # \param typeId (integer) item type (attribute \b AttributeLocalID).
413 def setItem(self, item, name = None, fileType = None, fileName = None,
414 comment = None, icon = None, IOR = None, typeId = None):
416 Modify the attributes of an item in the study. Unspecified attributes
417 (i.e. those set to :const:`None`) are left unchanged.
420 :param item: item to modify.
422 :type name: string or unicode
423 :param name: item name (attribute 'AttributeName').
425 :type fileType: string or unicode
426 :param fileType: item file type (attribute 'AttributeFileType').
428 :type fileName: string or unicode
429 :param fileName: item file name (attribute
430 'AttributeExternalFileDef').
432 :type comment: string or unicode
433 :param comment: item comment (attribute 'AttributeComment'). Note that
434 this attribute will appear in the 'Value' column in
437 :type icon: string or unicode
438 :param icon: item icon name (attribute 'AttributePixMap').
441 :param IOR: IOR of a CORBA object associated with the item
442 (attribute 'AttributeIOR').
444 :type typeId: integer
445 :param typeId: item type (attribute 'AttributeLocalID').
447 logger.debug("setItem (ID=%s): name=%s, fileType=%s, fileName=%s, "
448 "comment=%s, icon=%s, IOR=%s" %
449 (item.GetID(), name, fileType, fileName, comment,
452 self.setName(item, name)
453 if fileType is not None:
454 self.setFileType(item, fileType)
455 if fileName is not None:
456 self.setFileName(item, fileName)
457 if comment is not None:
458 self.setComment(item, comment)
460 self.setIcon(item, icon)
462 self.builder.SetIOR(item, IOR)
463 if typeId is not None:
464 self.setTypeId(item, typeId)
466 ## Remove the given item from the study. Note that the items are never
467 # really deleted. They just don't appear in the study anymore.
469 # \param item (SObject) the item to be removed
471 # \param withChildren (boolean) if \b True, also remove the children of item
473 # \return \b True if the item was removed successfully, or
474 # \b False if an error happened.
475 def removeItem(self, item, withChildren = False ):
477 Remove the given item from the study. Note that the items are never
478 really deleted. They just don't appear in the study anymore.
481 :param item: the item to be removed
483 :type withChildren: boolean
484 :param withChildren: if :const:`True`, also remove the children of
487 :return: :const:`True` if the item was removed successfully, or
488 :const:`False` if an error happened.
493 self.builder.RemoveObjectWithChildren(item)
495 self.builder.RemoveObject(item)
501 ## Find an item tagged \b tag under \b fatherItem in the study tree or
502 # create it if there is none, then set its attributes.
504 # \param fatherItem (SObject) item under which the tagged item will be looked for
505 # and eventually created.
507 # \param tag integer) tag of the item to look for.
509 # \return the SObject at \b tag if found or created successfully, or
510 # \b None if an error happened.
512 # See \b setItem() for the description of the other parameters.
513 def setItemAtTag(self, fatherItem, tag, name = None, fileType = None,
514 fileName = None, comment = None, icon = None, IOR = None,
517 Find an item tagged `tag` under `fatherItem` in the study tree or
518 create it if there is none, then set its attributes.
520 :type fatherItem: SObject
521 :param fatherItem: item under which the tagged item will be looked for
522 and eventually created.
525 :param tag: tag of the item to look for.
527 :return: the SObject at `tag` if found or created successfully, or
528 :const:`None` if an error happened.
530 See :meth:`setItem` for the description of the other parameters.
532 found, sObj = fatherItem.FindSubObject(tag)
534 sObj = self.builder.NewObjectToTag(fatherItem, tag)
535 self.setItem(sObj, name, fileType, fileName, comment, icon,
539 ## Return the name of the object sObject
540 def getName(self, sObject):
541 return sObject.GetName()
543 ## Set the name of the object sObject
544 def setName(self, sObject, name):
545 self.builder.SetName(sObject, name)
547 ## Return the comment of the object sObject
548 def getComment(self, sObject):
549 return sObject.GetComment()
551 ## Set the comment of the object sObject
552 def setComment(self, sObject, comment):
553 self.builder.SetComment(sObject, comment)
555 ## Return the value of the attribute named \b attributeName on the object
556 # sObject, or \b default if the attribute doesn't exist.
557 def getAttributeValue(self, sObject, attributeName, default = None):
559 Return the value of the attribute named `attributeName` on the object
560 `sObject`, or `default` if the attribute doesn't exist.
563 found, attr = self.builder.FindAttribute(sObject, attributeName)
568 ## Set the value of the attribute named \b attributeName on the object
569 # sObject to the value \b attributeValue.
570 def setAttributeValue(self, sObject, attributeName, attributeValue):
572 Set the value of the attribute named `attributeName` on the object
573 `sObject` to the value `attributeValue`.
575 attr = self.builder.FindOrCreateAttribute(sObject, attributeName)
576 attr.SetValue(attributeValue)
578 ## Return the value of the attribute "AttributeLocalID" of the object
579 # sObject, or \b None if it is not set.
580 def getTypeId(self, sObject):
582 Return the value of the attribute "AttributeLocalID" of the object
583 `sObject`, or :const:`None` if it is not set.
585 return self.getAttributeValue(sObject, "AttributeLocalID")
587 ## Set the attribute "AttributeLocalID" of the object \b sObject to the
589 def setTypeId(self, sObject, value):
591 Set the attribute "AttributeLocalID" of the object `sObject` to the
594 self.setAttributeValue(sObject, "AttributeLocalID", value)
596 ## Return the value of the attribute "AttributeFileType" of the object
597 # sObject, or an empty string if it is not set.
598 def getFileType(self, sObject):
600 Return the value of the attribute "AttributeFileType" of the object
601 `sObject`, or an empty string if it is not set.
603 return self.getAttributeValue(sObject, "AttributeFileType", "")
605 ## Set the attribute "AttributeFileType" of the object sObject to the
607 def setFileType(self, sObject, value):
609 Set the attribute "AttributeFileType" of the object `sObject` to the
612 self.setAttributeValue(sObject, "AttributeFileType",
615 ## Return the value of the attribute "AttributeExternalFileDef" of the
616 # object sObject, or an empty string if it is not set.
617 def getFileName(self, sObject):
619 Return the value of the attribute "AttributeExternalFileDef" of the
620 object `sObject`, or an empty string if it is not set.
622 return self.getAttributeValue(sObject, "AttributeExternalFileDef", "")
624 ## Set the attribute "AttributeExternalFileDef" of the object sObject
625 # to the value value.
626 def setFileName(self, sObject, value):
628 Set the attribute "AttributeExternalFileDef" of the object `sObject`
629 to the value `value`.
631 self.setAttributeValue(sObject, "AttributeExternalFileDef",
634 ## Return the value of the attribute "AttributePixMap" of the object
635 # sObject, or an empty string if it is not set.
636 def getIcon(self, sObject):
638 Return the value of the attribute "AttributePixMap" of the object
639 `sObject`, or an empty string if it is not set.
642 found, attr = self.builder.FindAttribute(sObject, "AttributePixMap")
643 if found and attr.HasPixMap():
644 value = attr.GetPixMap()
647 ## Set the attribute "AttributePixMap" of the object sObject to the
649 def setIcon(self, sObject, value):
651 Set the attribute "AttributePixMap" of the object `sObject` to the
654 attr = self.builder.FindOrCreateAttribute(sObject, "AttributePixMap")
655 attr.SetPixMap(value)