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