Salome HOME
CMake: set the flag Boost_NO_BOOST_CMAKE to avoid that the *standard* FindBoost.cmake
[modules/kernel.git] / src / KERNEL_PY / kernel / studyedit.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 #
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21 """
22 This module provides a new class :class:`StudyEditor` to complement
23 :class:`Study` and :class:`StudyBuilder` classes.
24 """
25
26 import re
27
28 import salome
29 from salome.kernel.logger import Logger
30 from salome.kernel import termcolor
31 logger = Logger("salome.kernel.studyedit", color = termcolor.PURPLE)
32
33 _editors = {}
34 _DEFAULT_CONTAINER = "FactoryServer"
35
36 def getActiveStudyId():
37     """
38     Return the ID of the active study. In GUI mode, this function is equivalent
39     to ``salome.sg.getActiveStudyId()``. Outside GUI, it returns
40     ``salome.myStudyId`` variable.
41     """
42     salome.salome_init()
43     # Warning: we don't use salome.getActiveStudy() here because it doesn't
44     # work properly when called from Salome modules (multi-study interpreter
45     # issue)
46     if salome.hasDesktop():
47         return salome.sg.getActiveStudyId()
48     else:
49         return salome.myStudyId
50
51 def getActiveStudy():
52     return getStudyFromStudyId(getActiveStudyId())
53
54 def getStudyFromStudyId(studyId):
55     salome.salome_init()
56     study = salome.myStudyManager.GetStudyByID(studyId)
57     return study
58
59 def getStudyIdFromStudy(study):
60     studyId = study._get_StudyId()
61     return studyId
62
63 def getStudyEditor(studyId = None):
64     """
65     Return a :class:`StudyEditor` instance to edit the study with ID
66     `studyId`. If `studyId` is :const:`None`, return an editor for the current
67     study.
68     """
69     if studyId is None:
70         studyId = getActiveStudyId()
71     if not _editors.has_key(studyId):
72         _editors[studyId] = StudyEditor(studyId)
73     return _editors[studyId]
74
75 class StudyEditor:
76     """
77     This class provides utility methods to complement :class:`Study` and
78     :class:`StudyBuilder` classes. Those methods may be moved in those classes
79     in the future. The parameter `studyId` defines the ID of the study to
80     edit. If it is :const:`None`, the edited study will be the current study.
81     The preferred way to get a StudyEditor object is through the method
82     :meth:`getStudyEditor` which allows to reuse existing instances.
83
84     .. attribute:: studyId
85     
86        This instance attribute contains the ID of the edited study. This
87        attribute should not be modified.
88
89     .. attribute:: study
90     
91        This instance attribute contains the underlying :class:`Study` object.
92        It can be used to access the study but the attribute itself should not
93        be modified.
94
95     .. attribute:: builder
96
97        This instance attribute contains the underlying :class:`StudyBuilder`
98        object. It can be used to edit the study but the attribute itself
99        should not be modified.
100
101     """
102     def __init__(self, studyId = None):
103         salome.salome_init()
104         if studyId is None:
105             studyId = getActiveStudyId()
106         self.studyId = studyId
107         self.study = salome.myStudyManager.GetStudyByID(studyId)
108         if self.study is None:
109             raise Exception("Can't create StudyEditor object: "
110                             "Study %d doesn't exist" % studyId)
111         self.builder = self.study.NewBuilder()
112
113     def findOrCreateComponent(self, moduleName, componentName = None,
114                               icon = None, containerName = _DEFAULT_CONTAINER):
115         """
116         Find a component corresponding to the Salome module `moduleName` in
117         the study. If none is found, create a new component and associate it
118         with the corresponding engine (i.e. the engine named `moduleName`).
119         Note that in Salome 5, the module name and engine name must be
120         identical (every module must provide an engine with the same name).
121         In Salome 6 it will be possible to define a different name for the
122         engine.
123
124         :type  moduleName: string
125         :param moduleName: name of the module corresponding to the component
126                            (the module name is the string value in the
127                            attribute "AttributeComment" of the component)
128
129         :type  componentName: string
130         :param componentName: name of the new component if created. If
131                               :const:`None`, use `moduleName` instead.
132
133         :type  icon: string
134         :param icon: icon for the new component (attribute "AttributePixMap").
135
136         :type  containerName: string
137         :param containerName: name of the container in which the engine should be
138                               loaded.
139
140         :return: the SComponent found or created.
141
142         """
143         sComponent = self.study.FindComponent(moduleName)
144         if sComponent is None:
145             sComponent = self.builder.NewComponent(moduleName)
146             # Note that the NewComponent method set the "comment" attribute to the
147             # value of its argument (moduleName here)
148             if componentName is None:
149                 componentName = moduleName
150             self.builder.SetName(sComponent, componentName)
151             if icon is not None:
152                 # _MEM_ : This will be effective if and only if "moduleName"
153                 # really corresponds to the module name (as specified in the
154                 # SalomeApp.xml)
155                 self.setIcon(sComponent, icon)
156
157             # This part will stay inactive until Salome 6. In Salome 6, the
158             # engine name will be stored separately from the module name.
159             # An internal convention (in this class) is to store the name of the
160             # associated engine in the parameter attribute of the scomponent (so that
161             # it could be retrieved in a future usage of this scomponent, for example,
162             # for the need of the function loadComponentEngine). The comment attribute
163             # SHOULD NOT be used for this purpose  because it's used by the SALOME
164             # resources manager to identify the SALOME module and then localized
165             # the resource files
166             #attr = self.builder.FindOrCreateAttribute( sComponent, "AttributeParameter" )
167             #attr.SetString( "ENGINE_NAME", engineName )
168
169             engine = salome.lcc.FindOrLoadComponent(containerName, moduleName)
170             if engine is None:
171                 raise Exception("Cannot load engine %s in container %s. See "
172                                 "logs of container %s for more details." %
173                                 (moduleName, containerName, containerName))
174             self.builder.DefineComponentInstance(sComponent, engine)
175
176         return sComponent
177
178     def loadComponentEngine(self, sComponent,
179                             containerName = _DEFAULT_CONTAINER):
180         """
181         Load the engine corresponding to `sComponent` in the container
182         `containerName`, associate the engine with the component and load the
183         CORBA objects of this component in the study.
184         """
185         # This part will stay inactive until Salome 6. In Salome 6, the
186         # engine name will be stored separately from the module name.
187         #attr = self.builder.FindOrCreateAttribute( sComponent, "AttributeParameter" )
188         #engineName = attr.GetString( "ENGINE_NAME" )
189         engine = salome.lcc.FindOrLoadComponent(containerName,
190                                                 sComponent.GetComment())
191         if engine is None:
192             raise Exception("Cannot load component %s in container %s. See "
193                             "logs of container %s for more details." %
194                             (sComponent.GetComment(), containerName,
195                              containerName))
196         self.builder.LoadWith(sComponent, engine)
197
198     def getOrLoadObject(self, item):
199         """
200         Get the CORBA object associated with the SObject `item`, eventually by
201         first loading it with the corresponding engine.
202         """
203         object = item.GetObject()
204         if object is None: # the engine has not been loaded yet
205             sComponent = item.GetFatherComponent()
206             self.loadComponentEngine(sComponent)
207             object = item.GetObject()
208         return object
209
210     def findOrCreateItem(self, fatherItem, name, fileType = None,
211                          fileName = None, comment = None, icon = None,
212                          IOR = None, typeId = None):
213         """
214         Find an object under `fatherItem` in the study with the given
215         attributes. Return the first one found if at least one exists,
216         otherwise create a new one with the given attributes and return it.
217         
218         See :meth:`setItem` for the description of the parameters.
219         """
220         sObject = self.findItem(fatherItem, name, fileType, fileName, comment,
221                                 icon, IOR, typeId)
222         if sObject is None:
223             sObject = self.createItem(fatherItem, name, fileType, fileName,
224                                       comment, icon, IOR, typeId)
225         return sObject
226
227     def findItem(self, fatherItem, name = None, fileType = None,
228                  fileName = None, comment = None, icon = None, IOR = None,
229                  typeId = None):
230         """
231         Find an item with given attributes under `fatherItem` in the study. If
232         none is found, return :const:`None`. If several items correspond to
233         the parameters, only the first one is returned. The search is made
234         only on given parameters (i.e. not :const:`None`). To look explicitly
235         for an empty attribute, use an empty string in the corresponding
236         parameter.
237         
238         See :meth:`setItem` for the description of the parameters.
239         """
240         foundItem = None;
241         childIterator = self.study.NewChildIterator(fatherItem)
242         while childIterator.More() and foundItem is None:
243             childItem = childIterator.Value()
244             if childItem and \
245                (name is None or childItem.GetName() == name) and \
246                (fileType is None or \
247                 self.getFileType(childItem) == fileType) and \
248                (fileName is None or \
249                 self.getFileName(childItem) == fileName) and \
250                (comment is None or childItem.GetComment() == comment) and \
251                (icon is None or \
252                 self.getIcon(childItem) == icon) and \
253                (IOR is None or childItem.GetIOR() == IOR) and \
254                (typeId is None or \
255                 self.getTypeId(childItem) == typeId):
256                 foundItem = childItem
257             childIterator.Next()
258         return foundItem
259
260     def createItem(self, fatherItem, name, fileType = None, fileName = None,
261                    comment = None, icon = None, IOR = None, typeId = None):
262         """
263         Create a new object named `name` under `fatherItem` in the study, with
264         the given attributes. If an object named `name` already exists under
265         the father object, the new object is created with a new name `name_X`
266         where X is the first available index.
267         
268         :type  fatherItem: SObject
269         :param fatherItem: item under which the new item will be added.
270                 
271         :return: new SObject created in the study
272         
273         See :meth:`setItem` for the description of the other parameters.
274         """
275         aSObject = self.builder.NewObject(fatherItem)
276
277         aChildIterator = self.study.NewChildIterator(fatherItem)
278         aMaxId = -1
279         aLength = len(name)
280         aDelim = "_"
281         anIdRE = re.compile("^" + aDelim + "[0-9]+")
282         aNameRE = re.compile("^" + name)
283         while aChildIterator.More():
284             aSObj = aChildIterator.Value()
285             aChildIterator.Next()
286             aName = aSObj.GetName()
287             if re.match(aNameRE,aName):
288                 aTmp = aName[aLength:]
289                 if re.match(anIdRE,aTmp):
290                     import string
291                     anId = string.atol(aTmp[1:])
292                     if aMaxId < anId:
293                         aMaxId = anId
294                         pass
295                     pass
296                 elif aMaxId < 0:
297                     aMaxId = 0
298                     pass
299                 pass
300             pass
301         
302         aMaxId = aMaxId + 1
303         aName = name
304         if aMaxId > 0:
305             aName = aName + aDelim + str(aMaxId)
306             pass
307         
308         self.setItem(aSObject, aName, fileType, fileName, comment, icon,
309                      IOR, typeId)
310     
311         return aSObject
312
313     def setItem(self, item, name = None, fileType = None, fileName = None,
314                 comment = None, icon = None, IOR = None, typeId = None):
315         """
316         Modify the attributes of an item in the study. Unspecified attributes
317         (i.e. those set to :const:`None`) are left unchanged.
318
319         :type  item: SObject
320         :param item: item to modify.
321
322         :type  name: string
323         :param name: item name (attribute 'AttributeName').
324
325         :type  fileType: string
326         :param fileType: item file type (attribute 'AttributeFileType').
327
328         :type  fileName: string
329         :param fileName: item file name (attribute
330                          'AttributeExternalFileDef').
331
332         :type  comment: string
333         :param comment: item comment (attribute 'AttributeComment'). Note that
334                         this attribute will appear in the 'Value' column in
335                         the object browser.
336
337         :type  icon: string
338         :param icon: item icon name (attribute 'AttributePixMap').
339
340         :type  IOR: string
341         :param IOR: IOR of a CORBA object associated with the item
342                     (attribute 'AttributeIOR').
343
344         :type  typeId: integer
345         :param typeId: item type (attribute 'AttributeLocalID').
346         """
347         logger.debug("setItem (ID=%s): name=%s, fileType=%s, fileName=%s, "
348                      "comment=%s, icon=%s, IOR=%s" %
349                      (item.GetID(), name, fileType, fileName, comment,
350                       icon, IOR))
351         # Explicit cast is necessary for unicode to string conversion
352         if name is not None:
353             self.builder.SetName(item, str(name))
354         if fileType is not None:
355             self.setFileType(item, fileType)
356         if fileName is not None:
357             self.setFileName(item, fileName)
358         if comment is not None:
359             self.builder.SetComment(item, str(comment))
360         if icon is not None:
361             self.setIcon(item, icon)
362         if IOR is not None:
363             self.builder.SetIOR(item, str(IOR))
364         if typeId is not None:
365             self.setTypeId(item, typeId)
366
367     def removeItem(self, item, withChildren = False ):
368         """
369         Remove the given item from the study. Note that the items are never
370         really deleted. They just don't appear in the study anymore.
371
372         :type  item: SObject
373         :param item: the item to be removed
374
375         :type  withChildren: boolean
376         :param withChildren: if :const:`True`, also remove the children of
377                              `item`
378
379         :return: :const:`True` if the item was removed successfully, or
380                  :const:`False` if an error happened.
381         """
382         ok = False
383         try:
384             if withChildren:
385                 self.builder.RemoveObjectWithChildren(item)
386             else:
387                 self.builder.RemoveObject(item)
388             ok = True
389         except:
390             ok = False
391         return ok
392
393     def setItemAtTag(self, fatherItem, tag, name = None, fileType = None,
394                      fileName = None, comment = None, icon = None, IOR = None,
395                      typeId = None):
396         """
397         Find an item tagged `tag` under `fatherItem` in the study tree or
398         create it if there is none, then set its attributes.
399         
400         :type  fatherItem: SObject
401         :param fatherItem: item under which the tagged item will be looked for
402                            and eventually created.
403
404         :type  tag: integer
405         :param tag: tag of the item to look for.
406
407         :return: the SObject at `tag` if found or created successfully, or
408                  :const:`None` if an error happened.
409         
410         See :meth:`setItem` for the description of the other parameters.
411         """
412         found, sObj = fatherItem.FindSubObject(tag)
413         if not found:
414             sObj = self.builder.NewObjectToTag(fatherItem, tag)
415         self.setItem(sObj, name, fileType, fileName, comment, icon,
416                      IOR, typeId)
417         return sObj
418
419     def getAttributeValue(self, sObject, attributeName, default = None):
420         """
421         Return the value of the attribute named `attributeName` on the object
422         `sObject`, or `default` if the attribute doesn't exist.
423         """
424         value = default
425         found, attr = self.builder.FindAttribute(sObject, attributeName)
426         if found:
427             value = attr.Value()
428         return value
429
430     def setAttributeValue(self, sObject, attributeName, attributeValue):
431         """
432         Set the value of the attribute named `attributeName` on the object
433         `sObject` to the value `attributeValue`.
434         """        
435         attr = self.builder.FindOrCreateAttribute(sObject, attributeName)
436         attr.SetValue(attributeValue)
437
438     def getTypeId(self, sObject):
439         """
440         Return the value of the attribute "AttributeLocalID" of the object
441         `sObject`, or :const:`None` if it is not set.
442         """
443         return self.getAttributeValue(sObject, "AttributeLocalID")
444
445     def setTypeId(self, sObject, value):
446         """
447         Set the attribute "AttributeLocalID" of the object `sObject` to the
448         value `value`.
449         """
450         self.setAttributeValue(sObject, "AttributeLocalID", value)
451
452     def getFileType(self, sObject):
453         """
454         Return the value of the attribute "AttributeFileType" of the object
455         `sObject`, or an empty string if it is not set.
456         """
457         return self.getAttributeValue(sObject, "AttributeFileType", "")
458
459     def setFileType(self, sObject, value):
460         """
461         Set the attribute "AttributeFileType" of the object `sObject` to the
462         value `value`.
463         """
464         # Explicit cast is necessary for unicode to string conversion
465         self.setAttributeValue(sObject, "AttributeFileType", str(value))
466
467     def getFileName(self, sObject):
468         """
469         Return the value of the attribute "AttributeExternalFileDef" of the
470         object `sObject`, or an empty string if it is not set.
471         """
472         return self.getAttributeValue(sObject, "AttributeExternalFileDef", "")
473
474     def setFileName(self, sObject, value):
475         """
476         Set the attribute "AttributeExternalFileDef" of the object `sObject`
477         to the value `value`.
478         """
479         # Explicit cast is necessary for unicode to string conversion
480         self.setAttributeValue(sObject, "AttributeExternalFileDef",
481                                str(value))
482
483     def getIcon(self, sObject):
484         """
485         Return the value of the attribute "AttributePixMap" of the object
486         `sObject`, or an empty string if it is not set.
487         """
488         value = ""
489         found, attr = self.builder.FindAttribute(sObject, "AttributePixMap")
490         if found and attr.HasPixMap():
491             value = attr.GetPixMap()
492         return value
493
494     def setIcon(self, sObject, value):
495         """
496         Set the attribute "AttributePixMap" of the object `sObject` to the
497         value `value`.
498         """
499         attr = self.builder.FindOrCreateAttribute(sObject, "AttributePixMap")
500         # Explicit cast is necessary for unicode to string conversion
501         attr.SetPixMap(str(value))