Salome HOME
0013946: Implement QActionGroup functionality for SALOME series 5x
[samples/pyhello.git] / src / PYHELLOGUI / PYHELLOGUI.py
1 # Copyright (C) 2005  CEA/DEN, EDF R&D
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.
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 #
21 # File   : PYHELLOGUI.py
22 # Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
23 #
24 # ---
25
26 import traceback
27 import os
28 from PyQt4.QtGui import *
29 from PyQt4.QtCore 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_small.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         try:
86             ag = sgPyQt.createActionGroup( GUIcontext.OPTIONS_ID )
87             ag.setText( "Creation mode" )
88             ag.setUsesDropDown(True)
89             a = sgPyQt.createAction( GUIcontext.OPTION_1_ID, "Default name", "Default name", "Use default name for the objects" )
90             a.setCheckable( True )
91             ag.add( a )
92             a = sgPyQt.createAction( GUIcontext.OPTION_2_ID, "Generate name", "Generate name", "Generate name for the objects" )
93             a.setCheckable( True )
94             ag.add( a )
95             a = sgPyQt.createAction( GUIcontext.OPTION_3_ID, "Ask name", "Ask name", "Request object name from the user" )
96             a.setCheckable( True )
97             ag.add( a )
98             sgPyQt.createMenu( ag, mid )
99             sgPyQt.createTool( ag, tid )
100             default_mode = sgPyQt.integerSetting( "PYHELLO", "creation_mode", 0 )
101             sgPyQt.action( GUIcontext.OPTION_1_ID + default_mode ).setChecked( True )
102         except:
103             pass
104         # the following action are used in context popup
105         a = sgPyQt.createAction( GUIcontext.DELETE_ALL_ID, "Delete all", "Delete all", "Delete all objects" )
106         a = sgPyQt.createAction( GUIcontext.SHOW_ME_ID,    "Show",       "Show",       "Show object name" )
107         a = sgPyQt.createAction( GUIcontext.DELETE_ME_ID,  "Delete",     "Delete",     "Remove object" )
108         a = sgPyQt.createAction( GUIcontext.RENAME_ME_ID,  "Rename",     "Rename",     "Rename object" )
109         pass
110     pass
111
112 ################################################
113 # Global variables
114 ################################################
115
116 # study-to-context map
117 __study2context__   = {}
118 # current context
119 __current_context__ = None
120 # object counter
121 __id__ = 0
122
123 ################################################
124        
125 # Get SALOME PyQt interface
126 import SalomePyQt
127 sgPyQt = SalomePyQt.SalomePyQt()
128
129 # Get SALOME Swig interface
130 import libSALOME_Swig
131 sg = libSALOME_Swig.SALOMEGUI_Swig()
132
133 ################################################
134
135 # init ORB
136 orb = CORBA.ORB_init( [''], CORBA.ORB_ID )
137
138 # create naming service instance
139 naming_service = SALOME_NamingServicePy_i( orb )
140
141 # create life cycle CORBA instance
142 lcc = LifeCycleCORBA( orb )
143
144 # get study manager
145 obj = naming_service.Resolve( '/myStudyManager' )
146 studyManager = obj._narrow( SALOMEDS.StudyManager )
147
148 ################################################
149 # Internal methods
150 ################################################
151
152 ###
153 # Check verbose mode
154 ### 
155 __verbose__ = None
156 def verbose():
157     global __verbose__
158     if __verbose__ is None:
159         try:
160             __verbose__ = int( os.getenv('SALOME_VERBOSE', 0) )
161         except:
162             __verbose__ = 0
163             pass
164         pass
165     return __verbose__
166
167 ###
168 # get PYHELLO engine
169 ###
170 def _getEngine():
171     engine = lcc.FindOrLoadComponent( "FactoryServerPy", GUIcontext.MODULE_NAME )
172     return engine
173
174 ###
175 # get active study ID
176 ###
177 def _getStudyId():
178     return sgPyQt.getStudyId()
179
180 ###
181 # get active study
182 ###
183 def _getStudy():
184     studyId = _getStudyId()
185     study = studyManager.GetStudyByID( studyId )
186     return study
187
188 ###
189 # returns True if object has children
190 ###
191 def _hasChildren( sobj ):
192     if sobj:
193         study = _getStudy()
194         iter  = study.NewChildIterator( sobj )
195         while iter.More():
196             name = iter.Value().GetName()
197             if name:
198                 return True
199             iter.Next()
200             pass
201         pass
202     return False
203
204 ###
205 # finds or creates component object
206 ###
207 def _findOrCreateComponent():
208     study = _getStudy()
209     father = study.FindComponent( GUIcontext.MODULE_NAME )
210     if father is None:
211         builder = study.NewBuilder()
212         father = builder.NewComponent( GUIcontext.MODULE_NAME )
213         attr = builder.FindOrCreateAttribute( father, "AttributeName" )
214         attr.SetValue( GUIcontext.MODULE_NAME )
215         attr = builder.FindOrCreateAttribute( father, "AttributePixMap" )
216         attr.SetPixMap( GUIcontext.MODULE_PIXMAP )
217         attr = builder.FindOrCreateAttribute( father, "AttributeLocalID" )
218         attr.SetValue( GUIcontext.MODULE_ID )
219         try:
220             builder.DefineComponentInstance( father, _getEngine() )
221             pass
222         except:
223             pass
224         pass
225     return father
226
227 ###
228 # get current GUI context
229 ###
230 def _getContext():
231     global __current_context__
232     return __current_context__
233
234 ###
235 # set and return current GUI context
236 # study ID is passed as parameter
237 ###
238 def _setContext( studyID ):
239     global __study2context__, __current_context__
240     if not __study2context__.has_key(studyID):
241         __study2context__[studyID] = GUIcontext()
242         pass
243     __current_context__ = __study2context__[studyID]
244     return __current_context__
245
246 ###
247 # increment object counter in the map
248 ###
249 def _incObjToMap( m, id ):
250     if id not in m: m[id] = 0
251     m[id] += 1
252     pass
253
254 ###
255 # analyse selection
256 ###
257 def _getSelection():
258     selcount = sg.SelectedCount()
259     seltypes = {}
260     study = _getStudy()
261     for i in range( selcount ):
262         entry = sg.getSelected( i )
263         if entry:
264             sobj = study.FindObjectID( entry )
265             if sobj is not None:
266                 test, anAttr = sobj.FindAttribute( "AttributeLocalID" )
267                 if test:
268                     ID = anAttr._narrow( SALOMEDS.AttributeLocalID ).Value()
269                     if ID >= 0:
270                         _incObjToMap( seltypes, ID )
271                         continue
272                     pass
273                 pass
274             pass
275         _incObjToMap( seltypes, GUIcontext.FOREIGN_ID )
276         pass
277     return selcount, seltypes
278
279 ################################################
280 # Callback functions
281 ################################################
282
283 # called when module is initialized
284 # perform initialization actions
285 def initialize():
286     if verbose() : print "PYHELLOGUI.initialize() : study : %d" % _getStudyId()
287     # set default preferences values
288     if not sgPyQt.hasSetting( "PYHELLO", "def_obj_name"):
289         sgPyQt.addSetting( "PYHELLO", "def_obj_name", GUIcontext.DEFAULT_NAME )
290     if not sgPyQt.hasSetting( "PYHELLO", "creation_mode"):
291         sgPyQt.addSetting( "PYHELLO", "creation_mode", 0 )
292     pass
293
294 # called when module is initialized
295 # return map of popup windows to be used by the module
296 def windows():
297     if verbose() : print "PYHELLOGUI.windows() : study : %d" % _getStudyId()
298     wm = {}
299     wm[SalomePyQt.WT_ObjectBrowser] = Qt.LeftDockWidgetArea
300     wm[SalomePyQt.WT_PyConsole]     = Qt.BottomDockWidgetArea
301     return wm
302
303 # called when module is initialized
304 # return list of 2d/3d views to be used ny the module
305 def views():
306     if verbose() : print "PYHELLOGUI.views() : study : %d" % _getStudyId()
307     return []
308
309 # called when module is initialized
310 # export module's preferences
311 def createPreferences():
312     if verbose() : print "PYHELLOGUI.createPreferences() : study : %d" % _getStudyId()
313     gid = sgPyQt.addPreference( "General" )
314     gid = sgPyQt.addPreference( "Object creation", gid )
315     pid = sgPyQt.addPreference( "Default name",  gid, SalomePyQt.PT_String,   "PYHELLO", "def_obj_name" )
316     pid = sgPyQt.addPreference( "Default creation mode", gid, SalomePyQt.PT_Selector, "PYHELLO", "creation_mode" )
317     strings = QStringList()
318     strings.append( "Default name" )
319     strings.append( "Generate name" )
320     strings.append( "Ask name" )
321     indexes = []
322     indexes.append( QVariant(0) )
323     indexes.append( QVariant(1) )
324     indexes.append( QVariant(2) )
325     sgPyQt.setPreferenceProperty( pid, "strings", QVariant( strings ) )
326     sgPyQt.setPreferenceProperty( pid, "indexes", QVariant( indexes ) )
327     pass
328
329 # called when module is activated
330 # returns True if activating is successfull and False otherwise
331 def activate():
332     if verbose() : print "PYHELLOGUI.activate() : study : %d" % _getStudyId()
333     ctx = _setContext( _getStudyId() )
334     return True
335
336 # called when module is deactivated
337 def deactivate():
338     if verbose() : print "PYHELLOGUI.deactivate() : study : %d" % _getStudyId()
339     pass
340
341 # called when active study is changed
342 # active study ID is passed as parameter
343 def activeStudyChanged( studyID ):
344     if verbose() : print "PYHELLOGUI.activeStudyChanged(): study : %d" % studyID
345     ctx = _setContext( _getStudyId() )
346     pass
347
348 # called when popup menu is invoked
349 # popup menu and menu context are passed as parameters
350 def createPopupMenu( popup, context ):
351     if verbose() : print "PYHELLOGUI.createPopupMenu(): context = %s" % context
352     ctx = _setContext( _getStudyId() )
353     study = _getStudy()
354     selcount, selected = _getSelection()
355     print selcount, selected
356     if selcount == 1:
357         # one object is selected
358         if GUIcontext.MODULE_ID in selected:
359             # menu for component
360             popup.addAction( sgPyQt.action( GUIcontext.DELETE_ALL_ID ) )
361         elif GUIcontext.OBJECT_ID in selected:
362             # menu for object
363             popup.addAction( sgPyQt.action( GUIcontext.SHOW_ME_ID ) )
364             popup.addAction( sgPyQt.action( GUIcontext.RENAME_ME_ID ) )
365             popup.addSeparator()
366             popup.addAction( sgPyQt.action( GUIcontext.DELETE_ME_ID ) )
367             pass
368         pass
369     elif selcount > 1:
370         # several objects are selected
371         if len( selected ) == 1:
372             if GUIcontext.MODULE_ID in selected:
373                 # menu for component
374                 popup.addAction( sgPyQt.action( GUIcontext.DELETE_ALL_ID ) )
375             elif GUIcontext.OBJECT_ID in selected:
376                 # menu for list of objects
377                 popup.addAction( sgPyQt.action( GUIcontext.DELETE_ME_ID ) )
378                 pass
379             pass
380         pass
381     pass
382
383 # called when GUI action is activated
384 # action ID is passed as parameter
385 def OnGUIEvent( commandID ):
386     if verbose() : print "PYHELLOGUI.OnGUIEvent(): command = %d" % commandID
387     if dict_command.has_key( commandID ):
388         try:
389             dict_command[commandID]()
390         except:
391             traceback.print_exc()
392     else:
393         if verbose() : print "The command is not implemented: %d" % commandID
394     pass
395
396 # called when module's preferences are changed
397 # preference's resources section and setting name are passed as parameters
398 def preferenceChanged( section, setting ):
399     if verbose() : print "PYHELLOGUI.preferenceChanged(): %s / %s" % ( section, setting )
400     pass
401
402 # called when active view is changed
403 # view ID is passed as parameter
404 def activeViewChanged( viewID ):
405     if verbose() : print "PYHELLOGUI.activeViewChanged(): %d" % viewID
406     pass
407
408 # called when active view is cloned
409 # cloned view ID is passed as parameter
410 def viewCloned( viewID ):
411     if verbose() : print "PYHELLOGUI.viewCloned(): %d" % viewID
412     pass
413
414 # called when active view is viewClosed
415 # view ID is passed as parameter
416 def viewClosed( viewID ):
417     if verbose() : print "PYHELLOGUI.viewClosed(): %d" % viewID
418     pass
419
420 ################################################
421 # GUI actions implementation
422 ################################################
423
424 ###
425 # 'HELLO' dialog box
426 ###
427 class MyDialog( QDialog ):
428     # constructor
429     def __init__( self, parent = None, modal = 0):
430         QDialog.__init__( self, parent )
431         self.setObjectName( "MyDialog" )
432         self.setModal( modal )
433         self.setWindowTitle( "HELLO!" )
434         vb = QVBoxLayout( self )
435         vb.setMargin( 8 )
436
437         hb0 = QHBoxLayout( self )
438         label = QLabel( "Prenom: ", self )
439         hb0.addWidget( label )
440         self.entry = QLineEdit( self )
441         self.entry.setMinimumWidth( 200 )
442         hb0.addWidget( self.entry )
443         vb.addLayout( hb0 )
444         
445         hb1 = QHBoxLayout( self )
446         bOk = QPushButton( "&OK", self )
447         self.connect( bOk, SIGNAL( 'clicked()' ), self, SLOT( 'accept()' ) )
448         hb1.addWidget( bOk )
449         
450         hb1.addStretch( 10 )
451         
452         bCancel = QPushButton( "&Cancel", self )
453         self.connect( bCancel, SIGNAL( 'clicked()' ), self, SLOT( 'close()' ) )
454         hb1.addWidget( bCancel )
455         
456         vb.addLayout( hb1 )
457         pass
458     
459     # OK button slot
460     def accept( self ):
461         name = str( self.entry.text() )
462         if name != "":
463             banner = _getEngine().makeBanner( name )
464             QMessageBox.information( self, 'Info', banner )
465             self.close()
466         else:
467             QMessageBox.warning( self, 'Error!', 'Please, enter the name!' )
468         pass
469
470 ###
471 # Show 'HELLO' dialog box
472 ###
473 def ShowHELLO():
474     # create dialog box
475     d = MyDialog( sgPyQt.getDesktop(), 1 )
476     # show dialog box
477     d.exec_()
478     pass
479
480 ###
481 # Create new object
482 ###
483 def CreateObject():
484     default_name = str( sgPyQt.stringSetting( "PYHELLO", "def_obj_name", GUIcontext.DEFAULT_NAME ).trimmed() )
485     try:
486         if sgPyQt.action( GUIcontext.OPTION_3_ID ).isChecked():
487             # request object name from the user
488             name, ok = QInputDialog.getText( sgPyQt.getDesktop(),
489                                              "Create Object",
490                                              "Enter object name:",
491                                              QLineEdit.Normal,
492                                              default_name )
493             if not ok: return
494             name = str( name.trimmed() )
495         elif sgPyQt.action( GUIcontext.OPTION_2_ID ).isChecked():
496             # generate object name
497             global __id__
498             __id__  = __id__ + 1
499             name = "%s %d" % ( default_name, __id__ )
500         else:
501             name = default_name
502             pass
503         pass
504     except:
505         # generate object name
506         global __id__
507         __id__  = __id__ + 1
508         name = "%s %d" % ( default_name, __id__ )
509         pass
510     if not name: return
511     study   = _getStudy()
512     builder = study.NewBuilder()
513     father  = _findOrCreateComponent()
514     object  = builder.NewObject( father )
515     attr    = builder.FindOrCreateAttribute( object, "AttributeName" )
516     attr.SetValue( name )
517     attr    = builder.FindOrCreateAttribute( object, "AttributeLocalID" )
518     attr.SetValue( GUIcontext.OBJECT_ID )
519     sg.updateObjBrowser( True )
520     pass
521
522 ###
523 # Delete all objects
524 ###
525 def DeleteAll():
526     study = _getStudy()
527     father = study.FindComponent( GUIcontext.MODULE_NAME )
528     if father:
529         iter = study.NewChildIterator( father )
530         builder = study.NewBuilder()
531         while iter.More():
532             sobj = iter.Value()
533             iter.Next()
534             builder.RemoveObjectWithChildren( sobj )
535             pass
536         sg.updateObjBrowser( True )
537         pass
538     pass
539
540 ###
541 # Show object's name
542 ###
543 def ShowMe():
544     study = _getStudy()
545     entry = sg.getSelected( 0 )
546     if entry != '':
547         sobj = study.FindObjectID( entry )
548         if ( sobj ):
549             test, attr = sobj.FindAttribute( "AttributeName" )
550             if test:
551                 QMessageBox.information( sgPyQt.getDesktop(), 'Info', "My name is '%s'" % attr.Value() )
552                 pass
553             pass
554         pass
555     pass
556
557 ###
558 # Delete selected object(s)
559 ###
560 def Delete():
561     study = _getStudy()
562     builder = study.NewBuilder()
563     if sg.SelectedCount() <= 0: return
564     for i in range( sg.SelectedCount() ):
565         entry = sg.getSelected( i )
566         if entry != '':
567             sobj = study.FindObjectID( entry )
568             if ( sobj ):
569                 builder.RemoveObject( sobj )
570                 pass
571             pass
572         pass
573     sg.updateObjBrowser( True )
574     pass
575
576 ###
577 # Rename selected object
578 ###
579 def Rename():
580     study = _getStudy()
581     builder = study.NewBuilder()
582     entry = sg.getSelected( 0 )
583     if entry != '':
584         sobj = study.FindObjectID( entry )
585         if ( sobj ):
586             name, ok = QInputDialog.getText( sgPyQt.getDesktop(),
587                                              "Object name",
588                                              "Enter object name:",
589                                              QLineEdit.Normal,
590                                              sobj.GetName() )
591             name = str( name.trimmed() )
592             if not ok or not name: return
593             attr = builder.FindOrCreateAttribute( sobj, "AttributeName" )
594             attr.SetValue( name )
595             sg.updateObjBrowser( True )
596             pass
597         pass
598     pass
599
600 ###
601 # Commands dictionary
602 ###
603 dict_command = {
604     GUIcontext.HELLO_ID         : ShowHELLO,
605     GUIcontext.CREATE_OBJECT_ID : CreateObject,
606     GUIcontext.DELETE_ALL_ID    : DeleteAll,
607     GUIcontext.SHOW_ME_ID       : ShowMe,
608     GUIcontext.DELETE_ME_ID     : Delete,
609     GUIcontext.RENAME_ME_ID     : Rename,
610     }