]> SALOME platform Git repositories - modules/kernel.git/blob - src/KERNEL_PY/kernel/studyedit.py
Salome HOME
0023299: [CEA] Finalize multi-study removal
[modules/kernel.git] / src / KERNEL_PY / kernel / studyedit.py
1 # Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 #
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.
7 #
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.
12 #
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
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20 ## \defgroup studyedit studyedit
21 #  \{ 
22 #  \details
23 #  This module provides a new class \bStudyEditor to complement \bStudy
24 #  and \bStudyBuilder classes.
25 #  \}
26
27 """
28 This module provides a new class :class:`StudyEditor` to complement
29 :class:`Study` and :class:`StudyBuilder` classes.
30 """
31
32 import re
33
34 import salome
35 from salome.kernel.logger import Logger
36 from salome.kernel import termcolor
37 logger = Logger("salome.kernel.studyedit", color = termcolor.PURPLE)
38
39 _editor = None
40 _DEFAULT_CONTAINER = "FactoryServer"
41
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"
44
45 def getStudy():
46     return salome.myStudy
47
48 ## Return a \b StudyEditor instance to edit the study. 
49 #  \ingroup studyedit
50 def getStudyEditor():
51     """
52     Return a :class:`StudyEditor` instance to edit the study.
53     """
54     if _editor is None:
55         _editor = StudyEditor()
56     return _editor
57
58 ## This class provides utility methods to complement \b Study and
59 #  \b StudyBuilder classes. Those methods may be moved in those classes
60 #  in the future.
61 #  The preferred way to get a StudyEditor object is through the method
62 #  \b getStudyEditor which allows to reuse existing instances.
63 #
64 #  \param study This instance attribute contains the underlying \b Study object.
65 #  It can be used to access the study but the attribute itself should not
66 #  be modified.
67 #
68 #  \param builder This instance attribute contains the underlying \b StudyBuilder
69 #  object. It can be used to edit the study but the attribute itself
70 #  should not be modified.
71 #  \ingroup studyedit
72 class StudyEditor:
73     """
74     This class provides utility methods to complement :class:`Study` and
75     :class:`StudyBuilder` classes. Those methods may be moved in those classes
76     in the future.
77     The preferred way to get a StudyEditor object is through the method
78     :meth:`getStudyEditor` which allows to reuse existing instances.
79
80     .. attribute:: study
81     
82        This instance attribute contains the underlying :class:`Study` object.
83        It can be used to access the study but the attribute itself should not
84        be modified.
85
86     .. attribute:: builder
87
88        This instance attribute contains the underlying :class:`StudyBuilder`
89        object. It can be used to edit the study but the attribute itself
90        should not be modified.
91
92     """
93     def __init__(self):
94         salome.salome_init()
95         self.study = salome.myStudy
96         if self.study is None:
97             raise Exception("Can't create StudyEditor object: "
98                             "Study doesn't exist")
99         self.builder = self.study.NewBuilder()
100
101     ## Find a component corresponding to the Salome module \b moduleName in
102     #  the study. If none is found, create a new component and associate it
103     #  with the corresponding engine (i.e. the engine named \b moduleName).
104     #  Note that in Salome 5, the module name and engine name must be
105     #  identical (every module must provide an engine with the same name).
106     #  In Salome 6 it will be possible to define a different name for the
107     #  engine.
108     #
109     #  \param moduleName (string) name of the module corresponding to the component
110     #  (the module name is the string value in the
111     #  attribute "AttributeComment" of the component)
112     #
113     #  \param componentName (string) name of the new component if created. 
114     #  If \b None, use \b moduleName instead.
115     #
116     #  \param icon (string) icon for the new component (attribute "AttributePixMap").
117     #
118     #  \param containerName (string) name of the container in which the engine should be
119     #  loaded.
120     #
121     #  \return the SComponent found or created.
122     def findOrCreateComponent(self, moduleName, componentName = None,
123                               icon = None, containerName = _DEFAULT_CONTAINER):
124         """
125         Find a component corresponding to the Salome module `moduleName` in
126         the study. If none is found, create a new component and associate it
127         with the corresponding engine (i.e. the engine named `moduleName`).
128         Note that in Salome 5, the module name and engine name must be
129         identical (every module must provide an engine with the same name).
130         In Salome 6 it will be possible to define a different name for the
131         engine.
132
133         :type  moduleName: string
134         :param moduleName: name of the module corresponding to the component
135                            (the module name is the string value in the
136                            attribute "AttributeComment" of the component)
137
138         :type  componentName: string
139         :param componentName: name of the new component if created. If
140                               :const:`None`, use `moduleName` instead.
141
142         :type  icon: string
143         :param icon: icon for the new component (attribute "AttributePixMap").
144
145         :type  containerName: string
146         :param containerName: name of the container in which the engine should be
147                               loaded.
148
149         :return: the SComponent found or created.
150
151         """
152         sComponent = self.study.FindComponent(moduleName)
153         if sComponent is None:
154             sComponent = self.builder.NewComponent(moduleName)
155             # Note that the NewComponent method set the "comment" attribute to the
156             # value of its argument (moduleName here)
157             if componentName is None:
158                 componentName = moduleName
159             self.builder.SetName(sComponent, componentName)
160             if icon is not None:
161                 # _MEM_ : This will be effective if and only if "moduleName"
162                 # really corresponds to the module name (as specified in the
163                 # SalomeApp.xml)
164                 self.setIcon(sComponent, icon)
165
166             # This part will stay inactive until Salome 6. In Salome 6, the
167             # engine name will be stored separately from the module name.
168             # An internal convention (in this class) is to store the name of the
169             # associated engine in the parameter attribute of the scomponent (so that
170             # it could be retrieved in a future usage of this scomponent, for example,
171             # for the need of the function loadComponentEngine). The comment attribute
172             # SHOULD NOT be used for this purpose  because it's used by the SALOME
173             # resources manager to identify the SALOME module and then localized
174             # the resource files
175             #attr = self.builder.FindOrCreateAttribute( sComponent, "AttributeParameter" )
176             #attr.SetString( "ENGINE_NAME", engineName )
177
178             engine = salome.lcc.FindOrLoadComponent(containerName, moduleName)
179             if engine is None:
180                 raise Exception("Cannot load engine %s in container %s. See "
181                                 "logs of container %s for more details." %
182                                 (moduleName, containerName, containerName))
183             self.builder.DefineComponentInstance(sComponent, engine)
184
185         return sComponent
186
187     ## Load the engine corresponding to \b sComponent in the container
188     #  \b containerName, associate the engine with the component and load the
189     #  CORBA objects of this component in the study.
190     def loadComponentEngine(self, sComponent,
191                             containerName = _DEFAULT_CONTAINER):
192         """
193         Load the engine corresponding to `sComponent` in the container
194         `containerName`, associate the engine with the component and load the
195         CORBA objects of this component in the study.
196         """
197         # This part will stay inactive until Salome 6. In Salome 6, the
198         # engine name will be stored separately from the module name.
199         #attr = self.builder.FindOrCreateAttribute( sComponent, "AttributeParameter" )
200         #engineName = attr.GetString( "ENGINE_NAME" )
201         engine = salome.lcc.FindOrLoadComponent(containerName,
202                                                 sComponent.GetComment())
203         if engine is None:
204             raise Exception("Cannot load component %s in container %s. See "
205                             "logs of container %s for more details." %
206                             (sComponent.GetComment(), containerName,
207                              containerName))
208         self.builder.LoadWith(sComponent, engine)
209
210     ## Get the CORBA object associated with the SObject \b item, eventually by
211     #  first loading it with the corresponding engine.
212     def getOrLoadObject(self, item):
213         """
214         Get the CORBA object associated with the SObject `item`, eventually by
215         first loading it with the corresponding engine.
216         """
217         object = item.GetObject()
218         if object is None: # the engine has not been loaded yet
219             sComponent = item.GetFatherComponent()
220             self.loadComponentEngine(sComponent)
221             object = item.GetObject()
222         return object
223
224     ## Find an object under \b fatherItem in the study with the given
225     #  attributes. Return the first one found if at least one exists,
226     #  otherwise create a new one with the given attributes and return it.
227     #
228     #  See \b setItem() for the description of the parameters.
229     def findOrCreateItem(self, fatherItem, name, fileType = None,
230                          fileName = None, comment = None, icon = None,
231                          IOR = None, typeId = None):
232         """
233         Find an object under `fatherItem` in the study with the given
234         attributes. Return the first one found if at least one exists,
235         otherwise create a new one with the given attributes and return it.
236         
237         See :meth:`setItem` for the description of the parameters.
238         """
239         sObject = self.findItem(fatherItem, name, fileType, fileName, comment,
240                                 icon, IOR, typeId)
241         if sObject is None:
242             sObject = self.createItem(fatherItem, name, fileType, fileName,
243                                       comment, icon, IOR, typeId)
244         return sObject
245
246     ## Find an item with given attributes under \b fatherItem in the study. If
247     #  none is found, return \b None. If several items correspond to
248     #  the parameters, only the first one is returned. The search is made
249     #  only on given parameters (i.e. not \b None). To look explicitly
250     #  for an empty attribute, use an empty string in the corresponding
251     #  parameter.
252     #    
253     #  See \b setItem() for the description of the parameters.
254     def findItem(self, fatherItem, name = None, fileType = None,
255                  fileName = None, comment = None, icon = None, IOR = None,
256                  typeId = None):
257         """
258         Find an item with given attributes under `fatherItem` in the study. If
259         none is found, return :const:`None`. If several items correspond to
260         the parameters, only the first one is returned. The search is made
261         only on given parameters (i.e. not :const:`None`). To look explicitly
262         for an empty attribute, use an empty string in the corresponding
263         parameter.
264         
265         See :meth:`setItem` for the description of the parameters.
266         """
267         foundItem = None;
268         childIterator = self.study.NewChildIterator(fatherItem)
269         while childIterator.More() and foundItem is None:
270             childItem = childIterator.Value()
271             if childItem and \
272                (name is None or self.getName(childItem) == name) and \
273                (fileType is None or \
274                 self.getFileType(childItem) == fileType) and \
275                (fileName is None or \
276                 self.getFileName(childItem) == fileName) and \
277                (comment is None or self.getComment(childItem) == comment) and \
278                (icon is None or \
279                 self.getIcon(childItem) == icon) and \
280                (IOR is None or childItem.GetIOR() == IOR) and \
281                (typeId is None or \
282                 self.getTypeId(childItem) == typeId):
283                 foundItem = childItem
284             childIterator.Next()
285         return foundItem
286
287     ## Create a new object named \b name under \b fatherItem in the study, with
288     #  the given attributes. If an object named \b name already exists under
289     #  the father object, the new object is created with a new name \b name_X
290     #  where X is the first available index.
291     #
292     #  param fatherItem (SObject) item under which the new item will be added.
293     #  \return new SObject created in the study.
294     #
295     #  See \b setItem() for the description of the other parameters.
296     def createItem(self, fatherItem, name, fileType = None, fileName = None,
297                    comment = None, icon = None, IOR = None, typeId = None):
298         """
299         Create a new object named `name` under `fatherItem` in the study, with
300         the given attributes. If an object named `name` already exists under
301         the father object, the new object is created with a new name `name_X`
302         where X is the first available index.
303         
304         :type  fatherItem: SObject
305         :param fatherItem: item under which the new item will be added.
306                 
307         :return: new SObject created in the study
308         
309         See :meth:`setItem` for the description of the other parameters.
310         """
311         aSObject = self.builder.NewObject(fatherItem)
312
313         aChildIterator = self.study.NewChildIterator(fatherItem)
314         aMaxId = -1
315         aLength = len(name)
316         aDelim = "_"
317         anIdRE = re.compile("^" + aDelim + "[0-9]+")
318         aNameRE = re.compile("^" + name)
319         while aChildIterator.More():
320             aSObj = aChildIterator.Value()
321             aChildIterator.Next()
322             aName = aSObj.GetName()
323             if re.match(aNameRE,aName):
324                 aTmp = aName[aLength:]
325                 if re.match(anIdRE,aTmp):
326                     import string
327                     anId = string.atol(aTmp[1:])
328                     if aMaxId < anId:
329                         aMaxId = anId
330                         pass
331                     pass
332                 elif aMaxId < 0:
333                     aMaxId = 0
334                     pass
335                 pass
336             pass
337         
338         aMaxId = aMaxId + 1
339         aName = name
340         if aMaxId > 0:
341             aName = aName + aDelim + str(aMaxId)
342             pass
343         
344         self.setItem(aSObject, aName, fileType, fileName, comment, icon,
345                      IOR, typeId)
346     
347         return aSObject
348
349     ## Modify the attributes of an item in the study. Unspecified attributes
350     #  (i.e. those set to \b None) are left unchanged.
351     #
352     #  \param item (SObject) item to modify.
353     #
354     #  \param name (string or unicode) item name (attribute \b AttributeName).
355     #
356     #  \param fileType (string or unicode) item file type (attribute \b AttributeFileType).
357     #
358     #  \param fileName (string or unicode) item file name (attribute \b AttributeExternalFileDef).
359     #
360     #  \param comment (string or unicode) item comment (attribute \b AttributeComment). Note that
361     #  this attribute will appear in the \b Value column in the object browser.
362     #
363     #  \param icon (string or unicode) item icon name (attribute \b AttributePixMap).
364     #
365     #  \param IOR (string) IOR of a CORBA object associated with the item
366     #  (attribute \b AttributeIOR).
367     #
368     #  \param typeId (integer) item type (attribute \b AttributeLocalID).
369     def setItem(self, item, name = None, fileType = None, fileName = None,
370                 comment = None, icon = None, IOR = None, typeId = None):
371         """
372         Modify the attributes of an item in the study. Unspecified attributes
373         (i.e. those set to :const:`None`) are left unchanged.
374
375         :type  item: SObject
376         :param item: item to modify.
377
378         :type  name: string or unicode
379         :param name: item name (attribute 'AttributeName').
380
381         :type  fileType: string or unicode
382         :param fileType: item file type (attribute 'AttributeFileType').
383
384         :type  fileName: string or unicode
385         :param fileName: item file name (attribute
386                          'AttributeExternalFileDef').
387
388         :type  comment: string or unicode
389         :param comment: item comment (attribute 'AttributeComment'). Note that
390                         this attribute will appear in the 'Value' column in
391                         the object browser.
392
393         :type  icon: string or unicode
394         :param icon: item icon name (attribute 'AttributePixMap').
395
396         :type  IOR: string
397         :param IOR: IOR of a CORBA object associated with the item
398                     (attribute 'AttributeIOR').
399
400         :type  typeId: integer
401         :param typeId: item type (attribute 'AttributeLocalID').
402         """
403         logger.debug("setItem (ID=%s): name=%s, fileType=%s, fileName=%s, "
404                      "comment=%s, icon=%s, IOR=%s" %
405                      (item.GetID(), name, fileType, fileName, comment,
406                       icon, IOR))
407         if name is not None:
408             self.setName(item, name)
409         if fileType is not None:
410             self.setFileType(item, fileType)
411         if fileName is not None:
412             self.setFileName(item, fileName)
413         if comment is not None:
414             self.setComment(item, comment)
415         if icon is not None:
416             self.setIcon(item, icon)
417         if IOR is not None:
418             self.builder.SetIOR(item, IOR)
419         if typeId is not None:
420             self.setTypeId(item, typeId)
421
422     ## Remove the given item from the study. Note that the items are never
423     #  really deleted. They just don't appear in the study anymore.
424     #
425     #  \param item (SObject) the item to be removed
426     #
427     #  \param withChildren (boolean) if \b True, also remove the children of item
428     #
429     #  \return \b True if the item was removed successfully, or 
430     #  \b False if an error happened.
431     def removeItem(self, item, withChildren = False ):
432         """
433         Remove the given item from the study. Note that the items are never
434         really deleted. They just don't appear in the study anymore.
435
436         :type  item: SObject
437         :param item: the item to be removed
438
439         :type  withChildren: boolean
440         :param withChildren: if :const:`True`, also remove the children of
441                              `item`
442
443         :return: :const:`True` if the item was removed successfully, or
444                  :const:`False` if an error happened.
445         """
446         ok = False
447         try:
448             if withChildren:
449                 self.builder.RemoveObjectWithChildren(item)
450             else:
451                 self.builder.RemoveObject(item)
452             ok = True
453         except:
454             ok = False
455         return ok
456
457     ## Find an item tagged \b tag under \b fatherItem in the study tree or
458     #  create it if there is none, then set its attributes.
459     #
460     #  \param fatherItem (SObject) item under which the tagged item will be looked for
461     #  and eventually created.
462     #
463     #  \param tag integer) tag of the item to look for.
464     #
465     #  \return the SObject at \b tag if found or created successfully, or
466     #  \b None if an error happened.
467     #    
468     #  See \b setItem() for the description of the other parameters.
469     def setItemAtTag(self, fatherItem, tag, name = None, fileType = None,
470                      fileName = None, comment = None, icon = None, IOR = None,
471                      typeId = None):
472         """
473         Find an item tagged `tag` under `fatherItem` in the study tree or
474         create it if there is none, then set its attributes.
475         
476         :type  fatherItem: SObject
477         :param fatherItem: item under which the tagged item will be looked for
478                            and eventually created.
479
480         :type  tag: integer
481         :param tag: tag of the item to look for.
482
483         :return: the SObject at `tag` if found or created successfully, or
484                  :const:`None` if an error happened.
485         
486         See :meth:`setItem` for the description of the other parameters.
487         """
488         found, sObj = fatherItem.FindSubObject(tag)
489         if not found:
490             sObj = self.builder.NewObjectToTag(fatherItem, tag)
491         self.setItem(sObj, name, fileType, fileName, comment, icon,
492                      IOR, typeId)
493         return sObj
494
495     ## Return the name of the object sObject
496     def getName(self, sObject):
497         val = sObject.GetName()
498         return unicode(val, ENCODING_FOR_SALOME_STUDY)
499
500     ## Set the name of the object sObject
501     def setName(self, sObject, name):
502         self.builder.SetName(sObject, name.encode(ENCODING_FOR_SALOME_STUDY))
503
504     ## Return the comment of the object sObject
505     def getComment(self, sObject):
506         val = sObject.GetComment()
507         return unicode(val, ENCODING_FOR_SALOME_STUDY)
508
509     ## Set the comment of the object sObject
510     def setComment(self, sObject, comment):
511         self.builder.SetComment(sObject, comment.encode(ENCODING_FOR_SALOME_STUDY))
512
513     ## Return the value of the attribute named \b attributeName on the object
514     #  sObject, or \b default if the attribute doesn't exist.
515     def getAttributeValue(self, sObject, attributeName, default = None):
516         """
517         Return the value of the attribute named `attributeName` on the object
518         `sObject`, or `default` if the attribute doesn't exist.
519         """
520         value = default
521         found, attr = self.builder.FindAttribute(sObject, attributeName)
522         if found:
523             value = attr.Value()
524         return value
525
526     ## Set the value of the attribute named \b attributeName on the object
527     #  sObject to the value \b attributeValue.
528     def setAttributeValue(self, sObject, attributeName, attributeValue):
529         """
530         Set the value of the attribute named `attributeName` on the object
531         `sObject` to the value `attributeValue`.
532         """        
533         attr = self.builder.FindOrCreateAttribute(sObject, attributeName)
534         attr.SetValue(attributeValue)
535
536     ## Return the value of the attribute "AttributeLocalID" of the object
537     #  sObject, or \b None if it is not set.
538     def getTypeId(self, sObject):
539         """
540         Return the value of the attribute "AttributeLocalID" of the object
541         `sObject`, or :const:`None` if it is not set.
542         """
543         return self.getAttributeValue(sObject, "AttributeLocalID")
544
545     ## Set the attribute "AttributeLocalID" of the object \b sObject to the
546     #  value \b value.
547     def setTypeId(self, sObject, value):
548         """
549         Set the attribute "AttributeLocalID" of the object `sObject` to the
550         value `value`.
551         """
552         self.setAttributeValue(sObject, "AttributeLocalID", value)
553
554     ## Return the value of the attribute "AttributeFileType" of the object
555     #  sObject, or an empty string if it is not set.
556     def getFileType(self, sObject):
557         """
558         Return the value of the attribute "AttributeFileType" of the object
559         `sObject`, or an empty string if it is not set.
560         """
561         val = self.getAttributeValue(sObject, "AttributeFileType", "")
562         return unicode(val, ENCODING_FOR_SALOME_STUDY)
563
564     ## Set the attribute "AttributeFileType" of the object sObject to the
565     #  value value.
566     def setFileType(self, sObject, value):
567         """
568         Set the attribute "AttributeFileType" of the object `sObject` to the
569         value `value`.
570         """
571         self.setAttributeValue(sObject, "AttributeFileType",
572                                value.encode(ENCODING_FOR_SALOME_STUDY))
573
574     ## Return the value of the attribute "AttributeExternalFileDef" of the
575     #  object sObject, or an empty string if it is not set.
576     def getFileName(self, sObject):
577         """
578         Return the value of the attribute "AttributeExternalFileDef" of the
579         object `sObject`, or an empty string if it is not set.
580         """
581         val = self.getAttributeValue(sObject, "AttributeExternalFileDef", "")
582         return unicode(val, ENCODING_FOR_SALOME_STUDY)
583
584     ## Set the attribute "AttributeExternalFileDef" of the object sObject
585     #  to the value value.
586     def setFileName(self, sObject, value):
587         """
588         Set the attribute "AttributeExternalFileDef" of the object `sObject`
589         to the value `value`.
590         """
591         self.setAttributeValue(sObject, "AttributeExternalFileDef",
592                                value.encode(ENCODING_FOR_SALOME_STUDY))
593
594     ## Return the value of the attribute "AttributePixMap" of the object
595     #  sObject, or an empty string if it is not set.
596     def getIcon(self, sObject):
597         """
598         Return the value of the attribute "AttributePixMap" of the object
599         `sObject`, or an empty string if it is not set.
600         """
601         value = ""
602         found, attr = self.builder.FindAttribute(sObject, "AttributePixMap")
603         if found and attr.HasPixMap():
604             value = attr.GetPixMap()
605         return unicode(value, ENCODING_FOR_SALOME_STUDY)
606
607     ## Set the attribute "AttributePixMap" of the object sObject to the
608     #  value value.
609     def setIcon(self, sObject, value):
610         """
611         Set the attribute "AttributePixMap" of the object `sObject` to the
612         value `value`.
613         """
614         attr = self.builder.FindOrCreateAttribute(sObject, "AttributePixMap")
615         attr.SetPixMap(value.encode(ENCODING_FOR_SALOME_STUDY))