Salome HOME
Issue 0020342: EDF 1025 GUI: Impossible to choose the container for a Python module
[samples/pyhello.git] / src / PYHELLOGUI / PYHELLOGUI.py
1 #  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 #
3 #  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 #  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 #
6 #  This library is free software; you can redistribute it and/or
7 #  modify it under the terms of the GNU Lesser General Public
8 #  License as published by the Free Software Foundation; either
9 #  version 2.1 of the License.
10 #
11 #  This library is distributed in the hope that it will be useful,
12 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 #  Lesser General Public License for more details.
15 #
16 #  You should have received a copy of the GNU Lesser General Public
17 #  License along with this library; if not, write to the Free Software
18 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 #
20 #  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 #
22 # ---
23 # File   : PYHELLOGUI.py
24 # Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
25 # ---
26 #
27 import traceback
28 import os
29 from qt import *
30
31 from omniORB import CORBA
32 from SALOME_NamingServicePy import *
33 from LifeCycleCORBA import *
34 import SALOMEDS
35 import SALOMEDS_Attributes_idl
36
37 import PYHELLO_ORB
38
39 ################################################
40 # GUI context class
41 # Used to store actions, menus, toolbars, etc...
42 ################################################
43
44 class GUIcontext:
45     # module name
46     MODULE_NAME      = "PYHELLO"
47     # module icon
48     MODULE_PIXMAP    = "PYHELLO.png"
49     # data objects IDs
50     MODULE_ID        = 1000
51     OBJECT_ID        = 1010
52     FOREIGN_ID       = -1
53     # menus/toolbars/actions IDs
54     PYHELLO_MENU_ID  = 90
55     HELLO_ID         = 941
56     CREATE_OBJECT_ID = 942
57     OPTIONS_ID       = 943
58     OPTION_1_ID      = 944
59     OPTION_2_ID      = 945
60     OPTION_3_ID      = 946
61     PYHELLO_TB_ID    = 90
62     DELETE_ALL_ID    = 951
63     SHOW_ME_ID       = 952
64     DELETE_ME_ID     = 953
65     RENAME_ME_ID     = 954
66     # default object name
67     DEFAULT_NAME     = "Object"
68
69     # constructor
70     def __init__( self ):
71         # create top-level menu
72         mid = sgPyQt.createMenu( "PyHello", -1, GUIcontext.PYHELLO_MENU_ID, sgPyQt.defaultMenuGroup() )
73         # create toolbar
74         tid = sgPyQt.createTool( "PyHello" )
75         # create actions and fill menu and toolbar with actions
76         a = sgPyQt.createAction( GUIcontext.HELLO_ID, "Hello", "Hello", "Show hello dialog box", "ExecPYHELLO.png" )
77         sgPyQt.createMenu( a, mid )
78         sgPyQt.createTool( a, tid )
79         a = sgPyQt.createSeparator()
80         sgPyQt.createMenu( a, mid )
81         a = sgPyQt.createAction( GUIcontext.CREATE_OBJECT_ID, "Create object", "Create object", "Create object" )
82         sgPyQt.createMenu( a, mid )
83         a = sgPyQt.createSeparator()
84         sgPyQt.createMenu( a, mid )
85         ag = sgPyQt.createActionGroup( GUIcontext.OPTIONS_ID )
86         ag.setText( "Creation mode" )
87         ag.setUsesDropDown( True )
88         a = sgPyQt.createAction( GUIcontext.OPTION_1_ID, "Default name", "Default name", "Use default name for the objects" )
89         a.setToggleAction( True )
90         ag.add( a )
91         a = sgPyQt.createAction( GUIcontext.OPTION_2_ID, "Generate name", "Generate name", "Generate name for the objects" )
92         a.setToggleAction( True )
93         ag.add( a )
94         a = sgPyQt.createAction( GUIcontext.OPTION_3_ID, "Ask name", "Ask name", "Request object name from the user" )
95         a.setToggleAction( True )
96         ag.add( a )
97         sgPyQt.createMenu( ag, mid )
98         sgPyQt.createTool( ag, tid )
99         default_mode = sgPyQt.integerSetting( "PYHELLO", "creation_mode", 0 )
100         sgPyQt.action( GUIcontext.OPTION_1_ID + default_mode ).setOn( True )
101         # the following action are used in context popup
102         a = sgPyQt.createAction( GUIcontext.DELETE_ALL_ID, "Delete all", "Delete all", "Delete all objects" )
103         a = sgPyQt.createAction( GUIcontext.SHOW_ME_ID,    "Show",       "Show",       "Show object name" )
104         a = sgPyQt.createAction( GUIcontext.DELETE_ME_ID,  "Delete",     "Delete",     "Remove object" )
105         a = sgPyQt.createAction( GUIcontext.RENAME_ME_ID,  "Rename",     "Rename",     "Rename object" )
106         pass
107     pass
108
109 ################################################
110 # Global variables
111 ################################################
112
113 # study-to-context map
114 __study2context__   = {}
115 # current context
116 __current_context__ = None
117 # object counter
118 __id__ = 0
119
120 ################################################
121        
122 # Get SALOME PyQt interface
123 import SalomePyQt
124 sgPyQt = SalomePyQt.SalomePyQt()
125
126 # Get SALOME Swig interface
127 import libSALOME_Swig
128 sg = libSALOME_Swig.SALOMEGUI_Swig()
129
130 ################################################
131
132 # init ORB
133 orb = CORBA.ORB_init( [''], CORBA.ORB_ID )
134
135 # create naming service instance
136 naming_service = SALOME_NamingServicePy_i( orb )
137
138 # create life cycle CORBA instance
139 lcc = LifeCycleCORBA( orb )
140
141 # get study manager
142 obj = naming_service.Resolve( '/myStudyManager' )
143 studyManager = obj._narrow( SALOMEDS.StudyManager )
144
145 ################################################
146 # Internal methods
147 ################################################
148
149 ###
150 # Check verbose mode
151 ### 
152 __verbose__ = None
153 def verbose():
154     global __verbose__
155     if __verbose__ is None:
156         try:
157             __verbose__ = int( os.getenv('SALOME_VERBOSE', 0) )
158         except:
159             __verbose__ = 0
160             pass
161         pass
162     return __verbose__
163
164 ###
165 # get PYHELLO engine
166 ###
167 def _getEngine():
168     engine = lcc.FindOrLoadComponent( "FactoryServerPy", GUIcontext.MODULE_NAME )
169     return engine
170
171 ###
172 # get active study ID
173 ###
174 def _getStudyId():
175     return sgPyQt.getStudyId()
176
177 ###
178 # get active study
179 ###
180 def _getStudy():
181     studyId = _getStudyId()
182     study = studyManager.GetStudyByID( studyId )
183     return study
184
185 ###
186 # returns True if object has children
187 ###
188 def _hasChildren( sobj ):
189     if sobj:
190         study = _getStudy()
191         iter  = study.NewChildIterator( sobj )
192         while iter.More():
193             name = iter.Value().GetName()
194             if name:
195                 return True
196             iter.Next()
197             pass
198         pass
199     return False
200
201 ###
202 # finds or creates component object
203 ###
204 def _findOrCreateComponent():
205     study = _getStudy()
206     father = study.FindComponent( GUIcontext.MODULE_NAME )
207     if father is None:
208         builder = study.NewBuilder()
209         father = builder.NewComponent( GUIcontext.MODULE_NAME )
210         attr = builder.FindOrCreateAttribute( father, "AttributeName" )
211         attr.SetValue( GUIcontext.MODULE_NAME )
212         attr = builder.FindOrCreateAttribute( father, "AttributePixMap" )
213         attr.SetPixMap( GUIcontext.MODULE_PIXMAP )
214         attr = builder.FindOrCreateAttribute( father, "AttributeLocalID" )
215         attr.SetValue( GUIcontext.MODULE_ID )
216         try:
217             builder.DefineComponentInstance( father, _getEngine() )
218             pass
219         except:
220             pass
221         pass
222     return father
223
224 ###
225 # get current GUI context
226 ###
227 def _getContext():
228     global __current_context__
229     return __current_context__
230
231 ###
232 # set and return current GUI context
233 # study ID is passed as parameter
234 ###
235 def _setContext( studyID ):
236     global __study2context__, __current_context__
237     if not __study2context__.has_key(studyID):
238         __study2context__[studyID] = GUIcontext()
239         pass
240     __current_context__ = __study2context__[studyID]
241     return __current_context__
242
243 ###
244 # increment object counter in the map
245 ###
246 def _incObjToMap( m, id ):
247     if id not in m: m[id] = 0
248     m[id] += 1
249     pass
250
251 ###
252 # analyse selection
253 ###
254 def _getSelection():
255     selcount = sg.SelectedCount()
256     seltypes = {}
257     study = _getStudy()
258     for i in range( selcount ):
259         entry = sg.getSelected( i )
260         if entry:
261             sobj = study.FindObjectID( entry )
262             if sobj is not None:
263                 test, anAttr = sobj.FindAttribute( "AttributeLocalID" )
264                 if test:
265                     ID = anAttr._narrow( SALOMEDS.AttributeLocalID ).Value()
266                     if ID >= 0:
267                         _incObjToMap( seltypes, ID )
268                         continue
269                     pass
270                 pass
271             pass
272         _incObjToMap( seltypes, GUIcontext.FOREIGN_ID )
273         pass
274     return selcount, seltypes
275
276 ################################################
277 # Callback functions
278 ################################################
279
280 # called when module is initialized
281 # perform initialization actions
282 def initialize():
283     if verbose() : print "PYHELLOGUI.initialize() : study : %d" % _getStudyId()
284     # set default preferences values
285     if not sgPyQt.hasSetting( "PYHELLO", "def_obj_name"):
286         sgPyQt.addSetting( "PYHELLO", "def_obj_name", GUIcontext.DEFAULT_NAME )
287     if not sgPyQt.hasSetting( "PYHELLO", "creation_mode"):
288         sgPyQt.addSetting( "PYHELLO", "creation_mode", 0 )
289     pass
290
291 # called when module is initialized
292 # return map of popup windows to be used by the module
293 def windows():
294     if verbose() : print "PYHELLOGUI.windows() : study : %d" % _getStudyId()
295     wm = {}
296     wm[SalomePyQt.WT_ObjectBrowser] = Qt.DockLeft
297     wm[SalomePyQt.WT_PyConsole]     = Qt.DockBottom
298     return wm
299
300 # called when module is initialized
301 # return list of 2d/3d views to be used ny the module
302 def views():
303     if verbose() : print "PYHELLOGUI.views() : study : %d" % _getStudyId()
304     return []
305
306 # called when module is initialized
307 # export module's preferences
308 def createPreferences():
309     if verbose() : print "PYHELLOGUI.createPreferences() : study : %d" % _getStudyId()
310     gid = sgPyQt.addPreference( "General" )
311     gid = sgPyQt.addPreference( "Object creation", gid )
312     pid = sgPyQt.addPreference( "Default name",  gid, SalomePyQt.PT_String,   "PYHELLO", "def_obj_name" )
313     pid = sgPyQt.addPreference( "Default creation mode", gid, SalomePyQt.PT_Selector, "PYHELLO", "creation_mode" )
314     sgPyQt.addPreferenceProperty( pid, "strings", 0, QVariant( "Default name" ) )
315     sgPyQt.addPreferenceProperty( pid, "indexes", 0, QVariant( 0 ) )
316     sgPyQt.addPreferenceProperty( pid, "strings", 1, QVariant( "Generate name" ) )
317     sgPyQt.addPreferenceProperty( pid, "indexes", 1, QVariant( 1 ) )
318     sgPyQt.addPreferenceProperty( pid, "strings", 2, QVariant( "Ask name" ) )
319     sgPyQt.addPreferenceProperty( pid, "indexes", 2, QVariant( 2 ) )
320     pass
321
322 # called when module is activated
323 # returns True if activating is successfull and False otherwise
324 def activate():
325     if verbose() : print "PYHELLOGUI.activate() : study : %d" % _getStudyId()
326     ctx = _setContext( _getStudyId() )
327     return True
328
329 # called when module is deactivated
330 def deactivate():
331     if verbose() : print "PYHELLOGUI.deactivate() : study : %d" % _getStudyId()
332     pass
333
334 # called when active study is changed
335 # active study ID is passed as parameter
336 def activeStudyChanged( studyID ):
337     if verbose() : print "PYHELLOGUI.activeStudyChanged(): study : %d" % studyID
338     ctx = _setContext( _getStudyId() )
339     pass
340
341 # called when popup menu is invoked
342 # popup menu and menu context are passed as parameters
343 def createPopupMenu( popup, context ):
344     if verbose() : print "PYHELLOGUI.createPopupMenu(): context = %s" % context
345     ctx = _setContext( _getStudyId() )
346     study = _getStudy()
347     selcount, selected = _getSelection()
348     print selcount, selected
349     if selcount == 1:
350         # one object is selected
351         if GUIcontext.MODULE_ID in selected:
352             # menu for component
353             sgPyQt.action( GUIcontext.DELETE_ALL_ID ).addTo( popup )
354         elif GUIcontext.OBJECT_ID in selected:
355             # menu for object
356             sgPyQt.action( GUIcontext.SHOW_ME_ID ).addTo( popup )
357             sgPyQt.action( GUIcontext.RENAME_ME_ID ).addTo( popup )
358             popup.insertSeparator()
359             sgPyQt.action( GUIcontext.DELETE_ME_ID ).addTo( popup )
360             pass
361         pass
362     elif selcount > 1:
363         # several objects are selected
364         if len( selected ) == 1:
365             if GUIcontext.MODULE_ID in selected:
366                 # menu for component
367                 sgPyQt.action( GUIcontext.DELETE_ALL_ID ).addTo( popup )
368             elif GUIcontext.OBJECT_ID in selected:
369                 # menu for list of objects
370                 sgPyQt.action( GUIcontext.DELETE_ME_ID ).addTo( popup )
371                 pass
372             pass
373         pass
374     pass
375
376 # called when GUI action is activated
377 # action ID is passed as parameter
378 def OnGUIEvent( commandID ):
379     if verbose() : print "PYHELLOGUI.OnGUIEvent(): command = %d" % commandID
380     if dict_command.has_key( commandID ):
381         try:
382             dict_command[commandID]()
383         except:
384             traceback.print_exc()
385     else:
386         if verbose() : print "The command is not implemented: %d" % commandID
387     pass
388
389 # called when module's preferences are changed
390 # preference's resources section and setting name are passed as parameters
391 def preferenceChanged( section, setting ):
392     if verbose() : print "PYHELLOGUI.preferenceChanged(): %s / %s" % ( section, setting )
393     pass
394
395 # called when active view is changed
396 # view ID is passed as parameter
397 def activeViewChanged( viewID ):
398     if verbose() : print "PYHELLOGUI.activeViewChanged(): %d" % viewID
399     pass
400
401 # called when active view is cloned
402 # cloned view ID is passed as parameter
403 def viewCloned( viewID ):
404     if verbose() : print "PYHELLOGUI.viewCloned(): %d" % viewID
405     pass
406
407 # called when active view is viewClosed
408 # view ID is passed as parameter
409 def viewClosed( viewID ):
410     if verbose() : print "PYHELLOGUI.viewClosed(): %d" % viewID
411     pass
412
413 # called when study is opened
414 # returns engine IOR
415 def engineIOR():
416     if verbose() : print "PYHELLOGUI.engineIOR()"
417     return orb.object_to_string(_getEngine())
418
419 ################################################
420 # GUI actions implementation
421 ################################################
422
423 ###
424 # 'HELLO' dialog box
425 ###
426 class MyDialog( QDialog ):
427     # constructor
428     def __init__( self, parent = None, modal = 0):
429         QDialog.__init__( self, parent, "MyDialog", modal )
430         self.setCaption( "HELLO!" )
431         vb = QVBoxLayout( self, 8 )
432         vb.setAutoAdd( 1 )
433         hb0 = QHBox( self )
434         label = QLabel( "Prenom: ", hb0 )
435         self.entry = QLineEdit( hb0 )
436         self.entry.setMinimumWidth( 200 )
437         
438         hb1 = QHBox( self )
439         bOk = QPushButton( "&OK", hb1 )
440         self.connect( bOk, SIGNAL( 'clicked()' ), self, SLOT( 'accept()' ) )
441         dummy = QWidget( hb1 )
442         bCancel = QPushButton( "&Cancel", hb1 )
443         self.connect( bCancel, SIGNAL( 'clicked()' ), self, SLOT( 'close()' ) )
444         hb1.setStretchFactor( dummy, 10 )
445         pass
446     
447     # OK button slot
448     def accept( self ):
449         name = str( self.entry.text() )
450         if name != "":
451             banner = _getEngine().makeBanner( name )
452             QMessageBox.information( self, 'Info', banner )
453             self.close()
454         else:
455             QMessageBox.warning( self, 'Error!', 'Please, enter the name!' )
456         pass
457
458 ###
459 # Show 'HELLO' dialog box
460 ###
461 def ShowHELLO():
462     # create dialog box
463     d = MyDialog( sgPyQt.getDesktop(), 1 )
464     # show dialog box
465     d.exec_loop()
466     pass
467
468 ###
469 # Create new object
470 ###
471 def CreateObject():
472     default_name = str( sgPyQt.stringSetting( "PYHELLO", "def_obj_name", GUIcontext.DEFAULT_NAME ).stripWhiteSpace() )
473     if sgPyQt.action( GUIcontext.OPTION_3_ID ).isOn():
474         # request object name from the user
475         name, ok = QInputDialog.getText( "Create Object", "Enter object name:", QLineEdit.Normal, default_name )
476         if not ok: return
477         name = str( name.stripWhiteSpace() )
478     elif sgPyQt.action( GUIcontext.OPTION_2_ID ).isOn():
479         # generate object name
480         global __id__
481         __id__  = __id__ + 1
482         name = "%s %d" % ( default_name, __id__ )
483     else:
484         name = default_name
485         pass
486     if not name: return
487     study   = _getStudy()
488     builder = study.NewBuilder()
489     father  = _findOrCreateComponent()
490     object  = builder.NewObject( father )
491     attr    = builder.FindOrCreateAttribute( object, "AttributeName" )
492     attr.SetValue( name )
493     attr    = builder.FindOrCreateAttribute( object, "AttributeLocalID" )
494     attr.SetValue( GUIcontext.OBJECT_ID )
495     sg.updateObjBrowser( True )
496     pass
497
498 ###
499 # Delete all objects
500 ###
501 def DeleteAll():
502     study = _getStudy()
503     father = study.FindComponent( GUIcontext.MODULE_NAME )
504     if father:
505         iter = study.NewChildIterator( father )
506         builder = study.NewBuilder()
507         while iter.More():
508             sobj = iter.Value()
509             iter.Next()
510             builder.RemoveObjectWithChildren( sobj )
511             pass
512         sg.updateObjBrowser(True)
513         pass
514     pass
515
516 ###
517 # Show object's name
518 ###
519 def ShowMe():
520     study = _getStudy()
521     entry = sg.getSelected( 0 )
522     if entry != '':
523         sobj = study.FindObjectID( entry )
524         if ( sobj ):
525             test, attr = sobj.FindAttribute( "AttributeName" )
526             if test:
527                 QMessageBox.information( sgPyQt.getDesktop(), 'Info', "My name is '%s'" % attr.Value() )
528                 pass
529             pass
530         pass
531     pass
532
533 ###
534 # Delete selected object(s)
535 ###
536 def Delete():
537     study = _getStudy()
538     builder = study.NewBuilder()
539     if sg.SelectedCount() <= 0: return
540     for i in range( sg.SelectedCount() ):
541         entry = sg.getSelected( i )
542         if entry != '':
543             sobj = study.FindObjectID( entry )
544             if ( sobj ):
545                 builder.RemoveObject( sobj )
546                 pass
547             pass
548         pass
549     sg.updateObjBrowser( True )
550     pass
551
552 ###
553 # Rename selected object
554 ###
555 def Rename():
556     study = _getStudy()
557     builder = study.NewBuilder()
558     entry = sg.getSelected( 0 )
559     if entry != '':
560         sobj = study.FindObjectID( entry )
561         if ( sobj ):
562             name, ok = QInputDialog.getText( "Object name", "Enter object name:", QLineEdit.Normal, sobj.GetName() )
563             name = str( name.stripWhiteSpace() )
564             if not ok or not name: return
565             attr = builder.FindOrCreateAttribute( sobj, "AttributeName" )
566             attr.SetValue( name )
567             sg.updateObjBrowser( True )
568             pass
569         pass
570     pass
571
572 ###
573 # Commands dictionary
574 ###
575 dict_command = {
576     GUIcontext.HELLO_ID         : ShowHELLO,
577     GUIcontext.CREATE_OBJECT_ID : CreateObject,
578     GUIcontext.DELETE_ALL_ID    : DeleteAll,
579     GUIcontext.SHOW_ME_ID       : ShowMe,
580     GUIcontext.DELETE_ME_ID     : Delete,
581     GUIcontext.RENAME_ME_ID     : Rename,
582     }