1 //=============================================================================
2 // File : SALOME_PYQT_Module.cxx
4 // Author : Vadim SANDLER
6 // Copyright : 2003-2005 CEA/DEN, EDF R&D
8 //=============================================================================
10 #include "SALOME_PYQT_Module.h"
12 #include "PyInterp_Dispatcher.h"
13 #include "SUIT_ResourceMgr.h"
14 #include "STD_MDIDesktop.h"
15 #include "STD_TabDesktop.h"
16 #include "SalomeApp_Application.h"
17 #include "SalomeApp_Study.h"
19 #include "QtxWorkstack.h"
20 #include <SALOME_LifeCycleCORBA.hxx>
24 #include <qworkspace.h>
25 #include <qpopupmenu.h>
27 #include "SALOME_PYQT_SipDefs.h"
28 #if defined(SIP_VERS_v4_old) || defined(SIP_VERS_v4_new)
29 #include "sipAPISalomePyQtGUI.h"
31 #include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
34 #include <sipqtQWidget.h>
35 #include <sipqtQPopupMenu.h>
41 ///////////////////////////////////////////////////////////////////////////////
42 // Default name of the module, replaced at the moment of module creation
43 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
45 ///////////////////////////////////////////////////////////////////////////////
46 // If __CALL_OLD_METHODS__ macro is not defined the invoking of obsolete Python
47 // module's methods like setSetting(), definePopup(), etc. is blocked.
48 // This macro is defined by default (in Makefile)
49 #ifdef __CALL_OLD_METHODS__
50 const bool IsCallOldMethods = true;
52 const bool IsCallOldMethods = false;
55 ///////////////////////////////////////////////////////////////////////////////
56 // NB: Python requests.
57 // General rule for Python requests created by SALOME_PYQT_Module:
58 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
59 // However, it is obligatory that ANY Python call is wrapped with a request object,
60 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
61 ///////////////////////////////////////////////////////////////////////////////
63 //=============================================================================
64 // The class for parsing of the XML resource files.
65 // Used for backward compatibility with existing Python modules.
66 //=============================================================================
67 class SALOME_PYQT_XmlHandler
70 SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
72 void createPopup ( QPopupMenu* menu,
73 const QString& context,
74 const QString& parent,
75 const QString& object );
78 void createToolBar ( QDomNode& parentNode );
79 void createMenu ( QDomNode& parentNode,
80 const int parentMenuId = -1 );
82 void insertPopupItems( QDomNode& parentNode,
86 SALOME_PYQT_Module* myModule;
90 //=============================================================================
91 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
92 // all Python-based SALOME module
93 //=============================================================================
95 // While the SalomePyQtGUI library is not imported in Python it's initialization function
96 // should be called manually (and only once) in order to initialize global sip data
97 #if defined(SIP_STATIC_MODULE)
98 extern "C" void initSalomePyQtGUI();
100 PyMODINIT_FUNC initSalomePyQtGUI();
104 * This function creates an instance of SALOME_PYQT_Module object by request
105 * of and application object when the module is loaded.
108 SALOME_PYQT_EXPORT CAM_Module* createModule() {
109 static bool alreadyInitialized = false;
110 if ( !alreadyInitialized ) {
111 // call only once (see above) !
113 alreadyInitialized = !alreadyInitialized;
115 return new SALOME_PYQT_Module();
120 * Static variables definition
122 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
123 SALOME_PYQT_Module* SALOME_PYQT_Module::myInitModule = 0;
126 * Little trick : provide an access to being activated Python module from outside;
127 * needed by the SalomePyQt library :(
129 SALOME_PYQT_Module* SALOME_PYQT_Module::getInitModule()
137 SALOME_PYQT_Module::SALOME_PYQT_Module() :
138 SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
140 myMenuActionList.setAutoDelete( false );
141 myPopupActionList.setAutoDelete( false );
142 myToolbarActionList.setAutoDelete( false );
148 SALOME_PYQT_Module::~SALOME_PYQT_Module()
150 myMenuActionList.clear();
151 myPopupActionList.clear();
152 myToolbarActionList.clear();
158 * Initialization of the module.
159 * Inherited from CAM_Module.
161 * This method is used for creation of the menus, toolbars and other staff.
162 * There are two ways:
163 * - for obsolete modules this method first tries to read <module>_<language>.xml
164 * resource file which contains a menu, toolbars and popup menus description.
165 * - new modules can create menus by by calling the corresponding methods of SalomePyQt
166 * Python API in the Python module's initialize() method which is called from here.
167 * NOTE: if postponed modules loading is not used, the active study might be not defined
168 * yet at this stage, so initialize() method should not perform any study-based initialization.
170 void SALOME_PYQT_Module::initialize( CAM_Application* app )
172 SalomeApp_Module::initialize( app );
174 // Try to get XML resource file name
175 SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
176 QString aLang = aResMgr->stringValue( "language", "language", QString::null );
177 if ( aLang.isEmpty() ) aLang = QString( "en" );
178 QString aName = name( "" );
179 QString aFileName = aName + "_" + aLang + ".xml";
180 aFileName = aResMgr->path( "resources", aName, aFileName );
182 // parse XML file if it is found and create actions
183 if ( !myXmlHandler && !aFileName.isEmpty() ) {
184 myXmlHandler = new SALOME_PYQT_XmlHandler( this, aFileName );
185 myXmlHandler->createActions();
188 // perform internal initialization and call module's initialize() method
189 // InitializeReq: request class for internal init() operation
190 class InitializeReq : public PyInterp_Request
193 InitializeReq( CAM_Application* _app,
194 SALOME_PYQT_Module* _obj )
195 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
200 virtual void execute()
202 myObj->init( myApp );
206 CAM_Application* myApp;
207 SALOME_PYQT_Module* myObj;
210 // Posting the request
211 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
215 * Activation of the module.
216 * Inherited from CAM_Module.
218 bool SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
220 MESSAGE( "SALOME_PYQT_Module::activateModule" );
222 bool res = SalomeApp_Module::activateModule( theStudy );
227 // ActivateReq: request class for internal activate() operation
228 class ActivateReq : public PyInterp_Request
231 ActivateReq( SUIT_Study* _study,
232 SALOME_PYQT_Module* _obj )
233 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
238 virtual void execute()
240 myObj->activate( myStudy );
245 SALOME_PYQT_Module* myObj;
248 // Posting the request
249 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
251 // activate menus, toolbars, etc
252 setMenuShown( true );
253 setToolShown( true );
259 * Deactivation of the module.
260 * Inherited from CAM_Module.
262 bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
264 MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
266 bool res = SalomeApp_Module::deactivateModule( theStudy );
268 // deactivate menus, toolbars, etc
269 setMenuShown( false );
270 setToolShown( false );
272 // DeactivateReq: request class for internal deactivate() operation
273 class DeactivateReq : public PyInterp_LockRequest
276 DeactivateReq( PyInterp_base* _py_interp,
278 SALOME_PYQT_Module* _obj )
279 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
284 virtual void execute()
286 myObj->deactivate( myStudy );
291 SALOME_PYQT_Module* myObj;
294 // Posting the request
295 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
301 * Processes GUI action (from main menu, toolbar or context popup menu)
303 void SALOME_PYQT_Module::onGUIEvent()
306 const QObject* obj = sender();
307 if ( !obj || !obj->inherits( "QAction" ) )
309 QAction* action = (QAction*)obj;
312 int id = actionId( action );
313 if ( myMenuActionList.contains( action ) )
314 id -= PYQT_ACTION_MENU;
315 if ( myToolbarActionList.contains( action ) )
316 id -= PYQT_ACTION_TOOLBAL;
317 if ( myPopupActionList.contains( action ) )
318 id -= PYQT_ACTION_POPUP;
319 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
321 // perform synchronous request to Python event dispatcher
322 class GUIEvent : public PyInterp_LockRequest
325 GUIEvent( PyInterp_base* _py_interp,
326 SALOME_PYQT_Module* _obj,
328 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
333 virtual void execute()
335 myObj->guiEvent( myId );
340 SALOME_PYQT_Module* myObj;
343 // Posting the request
344 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
348 Context popup menu request.
349 Called when user activates popup menu in some window (view, object browser, etc).
351 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
353 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
354 // perform synchronous request to Python event dispatcher
355 class PopupMenuEvent : public PyInterp_LockRequest
358 PopupMenuEvent( PyInterp_base* _py_interp,
359 SALOME_PYQT_Module* _obj,
360 const QString& _context,
362 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
363 myContext( _context ),
368 virtual void execute()
370 myObj->contextMenu( myContext, myPopup );
374 SALOME_PYQT_Module* myObj;
379 // Posting the request only if dispatcher is not busy!
380 // Executing the request synchronously
381 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
382 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
386 * Defines the dockable window associated with the module.
387 * To fill the list of windows the correspondind Python module's windows()
388 * method is called from SALOME_PYQT_Module::init() method.
389 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
391 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
393 // First clear the output parameters
394 QMap<int, int>::ConstIterator it;
395 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
396 mappa[ it.key() ] = it.data();
401 * Defines the compatible views which should be opened on module activation.
402 * To fill the list of views the correspondind Python module's views()
403 * method is called from SALOME_PYQT_Module::init() method.
404 * By default, the list is empty.
406 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
408 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
409 listik.append( *it );
414 * Performs internal initialization
415 * - initializes/gets the Python interpreter (one per study)
416 * - imports the Python module
417 * - passes the workspace widget to the Python module
418 * - calls Python module's initialize() method
419 * - calls Python module's windows() method
420 * - calls Python module's views() method
422 void SALOME_PYQT_Module::init( CAM_Application* app )
424 // reset interpreter to NULL
428 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
432 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
435 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
437 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
438 initInterp( aStudyId );
442 // import Python GUI module
449 if ( IsCallOldMethods ) { // __CALL_OLD_METHODS__
450 // call Python module's setWorkspace() method
452 } //__CALL_OLD_METHODS__
454 // then call Python module's initialize() method
455 // ... first get python lock
456 PyLockWrapper aLock = myInterp->GetLockWrapper();
457 // ... (the Python module is already imported)
458 // ... finally call Python module's initialize() method
459 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
461 // VSR: this method may not be implemented in Python module
465 // get the windows list from the Python module by calling windows() method
466 // ... first put default values
467 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
468 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
469 // VSR: LogWindow is not yet implemented
470 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
472 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
474 // VSR: this method may not be implemented in Python module
478 myWindowsMap.clear();
479 if ( PyDict_Check( res1 ) ) {
483 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
484 // parse the return value
485 // it should be a map: {integer:integer}
487 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
488 aKey = PyInt_AsLong( key );
489 aValue = PyInt_AsLong( value );
490 myWindowsMap[ aKey ] = aValue;
495 // get the windows list from the Python module by calling views() method
496 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
498 // VSR: this method may not be implemented in Python module
502 // parse the return value
503 // result can be one string...
504 if ( PyString_Check( res2 ) ) {
505 myViewMgrList.append( PyString_AsString( res2 ) );
507 // ... or list of strings
508 else if ( PyList_Check( res2 ) ) {
509 int size = PyList_Size( res2 );
510 for ( int i = 0; i < size; i++ ) {
511 PyObject* value = PyList_GetItem( res2, i );
512 if( value && PyString_Check( value ) ) {
513 myViewMgrList.append( PyString_AsString( value ) );
522 * Performs internal activation:
523 * - initializes/gets the Python interpreter (one per study)
524 * - imports the Python GUI module
525 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
526 * or activate() method (for new modules)
528 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
531 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
532 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
534 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
535 initInterp( aStudyId );
539 // import Python GUI module
545 PyLockWrapper aLock = myInterp->GetLockWrapper();
547 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
548 // call Python module's setSettings() method (obsolete)
549 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
551 // VSR: this method may not be implemented in Python module
554 } //__CALL_OLD_METHODS__
556 // call Python module's activate() method (for the new modules)
557 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
559 // VSR: this method may not be implemented in Python module
565 * Performs internal deactivation:
566 * - calls Python module's deactivate() method
568 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
570 // check if the subinterpreter is initialized and Python module is imported
571 if ( !myInterp || !myModule ) {
572 // Error! Python subinterpreter should be initialized and module should be imported first!
575 // then call Python module's deactivate() method
576 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
578 // VSR: this method may not be implemented in Python module
584 * Called when active the study is actived (user brings its desktop to top)
585 * - initializes/gets the Python interpreter (one per study)
586 * - imports the Python GUI module
587 * - calls Python module's activeStudyChanged() method
589 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
592 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
593 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
595 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
596 initInterp( aStudyId );
600 // import Python GUI module
606 PyLockWrapper aLock = myInterp->GetLockWrapper();
608 // call Python module's activeStudyChanged() method
609 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
611 // VSR: this method may not be implemented in Python module
617 * Get module engine, returns nil var if engine is not found in LifeCycleCORBA
619 Engines::Component_var SALOME_PYQT_Module::getEngine() const
621 Engines::Component_var comp;
622 // temporary solution
624 comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name( "" ) );
626 catch (CORBA::Exception&) {
632 * Get module engine IOR, returns empty string if engine is not found in LifeCycleCORBA
634 QString SALOME_PYQT_Module::engineIOR() const
636 if ( !CORBA::is_nil( getEngine() ) )
637 return QString( getApp()->orb()->object_to_string( getEngine() ) );
638 return QString( "" );
642 * Called when study desktop is activated.
643 * Used for notifying about changing of the active study.
645 void SALOME_PYQT_Module::studyActivated()
647 // StudyChangedReq: request class for internal studyChanged() operation
648 class StudyChangedReq : public PyInterp_Request
651 StudyChangedReq( SUIT_Study* _study,
652 SALOME_PYQT_Module* _obj )
653 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
658 virtual void execute()
660 myObj->studyChanged( myStudy );
665 SALOME_PYQT_Module* myObj;
668 // Posting the request
669 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
673 * Processes context popup menu request
674 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
675 * to define the popup menu context
676 * - parses XML resourses file (if exists) and fills the popup menu with the items)
677 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
678 * to allow module to customize the popup menu
679 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
680 * by using insertItem(...) Qt functions.
682 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
684 // Python interpreter should be initialized and Python module should be
686 if ( !myInterp || !myModule )
689 QString aContext( theContext ), aObject( "" ), aParent( "" );
691 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
692 // call definePopup() Python module's function
693 // this is obsolete function, used only for compatibility reasons
694 PyObjWrapper res(PyObject_CallMethod( myModule,
699 aParent.latin1() ) );
701 // VSR: this method may not be implemented in Python module
705 // parse return value
707 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
713 } //__CALL_OLD_METHODS__
715 // first try to create menu via XML parser:
716 // we create popup menus without help of QtxPopupMgr
718 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
720 PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QPopupMenu ) );
722 // then call Python module's createPopupMenu() method (for new modules)
723 PyObjWrapper res1( PyObject_CallMethod( myModule,
727 aContext.latin1() ) );
729 // VSR: this method may not be implemented in Python module
733 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
734 // call customPopup() Python module's function
735 // this is obsolete function, used only for compatibility reasons
736 PyObjWrapper res2( PyObject_CallMethod( myModule,
742 aParent.latin1() ) );
744 // VSR: this method may not be implemented in Python module
747 } //__CALL_OLD_METHODS__
751 * Processes GUI event
752 * - calls Python module's OnGUIEvent() method
754 void SALOME_PYQT_Module::guiEvent( const int theId )
756 // Python interpreter should be initialized and Python module should be
758 if ( !myInterp || !myModule )
761 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
769 * Initialises python subinterpreter (one per study)
771 void SALOME_PYQT_Module::initInterp( int theStudyId )
775 // Error! Study Id must not be 0!
779 // try to find the subinterpreter
780 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
782 myInterp = myInterpMap[ theStudyId ];
785 // not found - create a new one!
786 ///////////////////////////////////////////////////////////////////
787 // Attention: the creation of Python interpretor must be protected
788 // by a C++ Lock because of C threads
789 ///////////////////////////////////////////////////////////////////
790 myInterp = new SALOME_PYQT_PyInterp();
791 myInterp->initialize();
792 myInterpMap[ theStudyId ] = myInterp;
794 // import 'salome' module and call 'salome_init' method;
795 // do it only once on interpreter creation
796 // ... first get python lock
797 PyLockWrapper aLock = myInterp->GetLockWrapper();
798 // ... then import a module
799 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
805 // ... then call a method
806 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
815 * Imports Python GUI module and remember the reference to the module
816 * !!! initInterp() should be called first!!!
818 void SALOME_PYQT_Module::importModule()
820 // check if the subinterpreter is initialized
822 // Error! Python subinterpreter should be initialized first!
826 // import Python GUI module and puts it in <myModule> attribute
827 // ... first get python lock
828 PyLockWrapper aLock = myInterp->GetLockWrapper();
829 // ... then import a module
830 QString aMod = QString( name("") ) + "GUI";
831 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
840 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
842 * !!! initInterp() and importModule() should be called first!!!
844 void SALOME_PYQT_Module::setWorkSpace()
846 // check if the subinterpreter is initialized and Python module is imported
847 if ( !myInterp || !myModule ) {
848 // Error! Python subinterpreter should be initialized and module should be imported first!
852 // call setWorkspace() method
853 // ... first get python lock
854 PyLockWrapper aLock = myInterp->GetLockWrapper();
856 // ... then try to import SalomePyQt module. If it's not possible don't go on.
857 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
864 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
865 // ... then get workspace object
866 QWidget* aWorkspace = 0;
867 if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
868 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
870 aWorkspace = aDesktop->workspace();
872 else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
873 STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
875 aWorkspace = aDesktop->workstack();
877 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget ) );
878 // ... and finally call Python module's setWorkspace() method (obsolete)
879 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
881 // VSR: this method may not be implemented in Python module
885 } //__CALL_OLD_METHODS__
889 * Adds an action into private action list [internal usage]
891 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
894 case PYQT_ACTION_MENU:
895 myMenuActionList.append( action );
897 case PYQT_ACTION_TOOLBAL:
898 myToolbarActionList.append( action );
900 case PYQT_ACTION_POPUP:
901 myPopupActionList.append( action );
908 * The next methods just call the parent implementation.
909 * This is done to open protected methods from CAM_Module class.
911 int SALOME_PYQT_Module::createTool( const QString& name )
913 return SalomeApp_Module::createTool( name );
915 int SALOME_PYQT_Module::createTool( const int id, const int tBar, const int idx )
917 return SalomeApp_Module::createTool( id, tBar, idx );
919 int SALOME_PYQT_Module::createTool( const int id, const QString& tBar, const int idx )
921 return SalomeApp_Module::createTool( id, tBar, idx );
923 int SALOME_PYQT_Module::createTool( QAction* a, const int tBar, const int id, const int idx )
925 return SalomeApp_Module::createTool( a, tBar, id, idx );
927 int SALOME_PYQT_Module::createTool( QAction* a, const QString& tBar, const int id, const int idx )
929 return SalomeApp_Module::createTool( a, tBar, id, idx );
931 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
933 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
935 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
937 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
939 int SALOME_PYQT_Module::createMenu( const int id, const int menu, const int group, const int idx )
941 return SalomeApp_Module::createMenu( id, menu, group, idx );
943 int SALOME_PYQT_Module::createMenu( const int id, const QString& menu, const int group, const int idx )
945 return SalomeApp_Module::createMenu( id, menu, group, idx );
947 int SALOME_PYQT_Module::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
949 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
951 int SALOME_PYQT_Module::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
953 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
955 QAction* SALOME_PYQT_Module::createSeparator()
957 return SalomeApp_Module::separator();
959 QAction* SALOME_PYQT_Module::action( const int id ) const
961 return SalomeApp_Module::action( id );
963 int SALOME_PYQT_Module::actionId( const QAction* a ) const
965 return SalomeApp_Module::actionId( a );
967 QAction* SALOME_PYQT_Module::createAction( const int id, const QString& text, const QString& icon,
968 const QString& menu, const QString& tip, const int key,
972 if ( !icon.isEmpty() ) {
973 QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(""), tr( icon ) );
974 if ( !pixmap.isNull() )
975 anIcon = QIconSet( pixmap );
977 return SalomeApp_Module::createAction( id, text, anIcon, menu, tip, key, getApp()->desktop(), toggle, this, SLOT( onGUIEvent() ) );
981 //=============================================================================
982 // SALOME_PYQT_XmlHandler class implementation
983 //=============================================================================
985 // gets an tag name for the dom element [ static ]
986 // returns an empty string if the element does not have tag name
987 static QString tagName( const QDomElement& element ) {
988 return element.tagName().stripWhiteSpace();
991 // gets an attribute by it's name for the dom element [ static ]
992 // returns an empty string if the element does not have such attribute
993 static QString attribute( const QDomElement& element, const QString& attName ) {
994 return element.attribute( attName ).stripWhiteSpace();
997 // checks the given value for the boolean value [ static ]
998 // returns TRUE if string is "true", "yes" or "1"
999 static bool checkBool( const QString& value ) {
1000 return ( value == "true" || value == "yes" || value == "1" );
1003 // checks the given value for the integer value [ static ]
1004 // returns -1 if item is empty or presents and invalid number
1005 static int checkInt( const QString& value )
1007 return value.isEmpty() ? -1 : value.toInt();
1013 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
1014 : myModule( module )
1016 QFile aFile( fileName );
1017 if ( !aFile.open( IO_ReadOnly ) )
1019 if ( !myDoc.setContent( &aFile ) ) {
1027 Called by SALOME_PYQT_Module::initialize() in order to create actions
1028 (menus, toolbars, popup menus)
1030 void SALOME_PYQT_XmlHandler::createActions()
1032 // get document element
1033 QDomElement aDocElem = myDoc.documentElement();
1035 // get main menu actions
1036 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
1037 for ( int i = 0; i < aMenuList.count(); i++ ) {
1038 QDomNode n = aMenuList.item( i );
1042 // create toolbars actions
1043 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
1044 for ( int i = 0; i < aToolsList.count(); i++ ) {
1045 QDomNode n = aToolsList.item( i );
1051 * Creates popup menu
1053 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
1054 const QString& context,
1055 const QString& parent,
1056 const QString& object )
1058 // get document element
1059 QDomElement aDocElem = myDoc.documentElement();
1061 // get popup menus actions
1062 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
1063 for ( int i = 0; i < aPopupList.count(); i++ ) {
1064 QDomNode n = aPopupList.item( i );
1065 if ( !n.isNull() && n.isElement() ) {
1066 QDomElement e = n.toElement();
1067 QString lab = attribute( e, "label-id" );
1068 QString ctx = attribute( e, "context-id" );
1069 QString prt = attribute( e, "parent-id" );
1070 QString obj = attribute( e, "object-id" );
1071 if ( ctx == context && prt == parent && obj == object ) {
1072 insertPopupItems( n, menu );
1080 Create main menu with child actions
1082 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
1087 if ( parentNode.isNull() )
1090 QDomElement parentElement = parentNode.toElement();
1091 if ( !parentElement.isNull() ) {
1092 QString plabel = attribute( parentElement, "label-id" );
1093 int pid = checkInt( attribute( parentElement, "item-id" ) );
1094 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
1095 if ( !plabel.isEmpty() ) {
1097 int menuId = myModule->createMenu( plabel, // label
1098 parentMenuId, // parent menu ID, should be -1 for main menu
1102 QDomNode node = parentNode.firstChild();
1103 while ( !node.isNull() ) {
1104 if ( node.isElement() ) {
1105 QDomElement elem = node.toElement();
1106 QString aTagName = tagName( elem );
1107 if ( aTagName == "popup-item" ) {
1108 int id = checkInt( attribute( elem, "item-id" ) );
1109 int pos = checkInt( attribute( elem, "pos-id" ) );
1110 QString label = attribute( elem, "label-id" );
1111 QString icon = attribute( elem, "icon-id" );
1112 QString tooltip = attribute( elem, "tooltip-id" );
1113 QString accel = attribute( elem, "accel-id" );
1114 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1115 ////QString execute = attribute( elem, "execute-action" ); // not used
1117 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1118 // also check if the action with given ID is already created
1119 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1120 // little trick to have several actions with same ID for menus and toolbars
1121 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1122 // create menu action
1123 QAction* action = myModule->createAction( id, // ID
1127 tooltip, // status-bar text
1128 QKeySequence( accel ), // keyboard accelerator
1129 toggle ); // toogled action
1130 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1131 myModule->createMenu( action, menuId, -1, 80, pos );
1134 else if ( aTagName == "submenu" ) {
1136 createMenu( node, menuId );
1138 else if ( aTagName == "separator" ) {
1139 // create menu separator
1140 int pos = checkInt( attribute( elem, "pos-id" ) );
1141 QAction* action = myModule->createSeparator();
1142 myModule->createMenu( action, menuId, -1, 80, pos );
1145 node = node.nextSibling();
1152 Create a toolbar with child actions
1154 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1159 if ( parentNode.isNull() )
1162 QDomElement parentElement = parentNode.toElement();
1163 if ( !parentElement.isNull() ) {
1164 QString aLabel = attribute( parentElement, "label-id" );
1165 if ( !aLabel.isEmpty() ) {
1167 int tbId = myModule->createTool( aLabel );
1168 QDomNode node = parentNode.firstChild();
1169 while ( !node.isNull() ) {
1170 if ( node.isElement() ) {
1171 QDomElement elem = node.toElement();
1172 QString aTagName = tagName( elem );
1173 if ( aTagName == "toolbutton-item" ) {
1174 int id = checkInt( attribute( elem, "item-id" ) );
1175 int pos = checkInt( attribute( elem, "pos-id" ) );
1176 QString label = attribute( elem, "label-id" );
1177 QString icon = attribute( elem, "icon-id" );
1178 QString tooltip = attribute( elem, "tooltip-id" );
1179 QString accel = attribute( elem, "accel-id" );
1180 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1181 ////QString execute = attribute( elem, "execute-action" ); // not used
1183 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1184 // also check if the action with given ID is already created
1185 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1186 // little trick to have several actions with same ID for menus and toolbars
1187 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1188 // create toolbar action
1189 QAction* action = myModule->createAction( id, // ID
1193 tooltip, // status-bar text
1194 QKeySequence( accel ), // keyboard accelerator
1195 toggle ); // toogled action
1196 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1197 myModule->createTool( action, tbId, -1, pos );
1200 else if ( aTagName == "separatorTB" ) {
1201 // create toolbar separator
1202 int pos = checkInt( attribute( elem, "pos-id" ) );
1203 QAction* action = myModule->createSeparator();
1204 myModule->createTool( action, tbId, -1, pos );
1207 node = node.nextSibling();
1213 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1218 if ( parentNode.isNull() )
1221 // we create popup menus without help of QtxPopupMgr
1222 QDomNode node = parentNode.firstChild();
1223 while ( !node.isNull() ) {
1224 if ( node.isElement() ) {
1225 QDomElement elem = node.toElement();
1226 QString aTagName = tagName( elem );
1227 if ( aTagName == "popup-item" ) {
1228 // insert a command item
1229 int id = checkInt( attribute( elem, "item-id" ) );
1230 int pos = checkInt( attribute( elem, "pos-id" ) );
1231 QString label = attribute( elem, "label-id" );
1232 QString icon = attribute( elem, "icon-id" );
1233 /////QString tooltip = attribute( elem, "tooltip-id" ); // not used
1234 QString accel = attribute( elem, "accel-id" );
1235 /////bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1236 /////QString execute = attribute( elem, "execute-action" ); // not used
1239 if ( !icon.isEmpty() ) {
1240 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1241 if ( !pixmap.isNull() )
1242 anIcon = QIconSet( pixmap );
1245 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1246 // also check if the action with given ID is already created
1248 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent() ), QKeySequence( accel ), id, pos );
1251 else if ( aTagName == "submenu" ) {
1253 int id = checkInt( attribute( elem, "item-id" ) );
1254 int pos = checkInt( attribute( elem, "pos-id" ) );
1255 QString label = attribute( elem, "label-id" );
1256 QString icon = attribute( elem, "icon-id" );
1259 if ( !icon.isEmpty() ) {
1260 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1261 if ( !pixmap.isNull() )
1262 anIcon = QIconSet( pixmap );
1265 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1266 menu->insertItem( anIcon, label, newPopup, id, pos );
1267 insertPopupItems( node, newPopup );
1269 else if ( aTagName == "separator" ) {
1270 // create menu separator
1271 int pos = checkInt( attribute( elem, "pos-id" ) );
1272 menu->insertSeparator( pos );
1275 node = node.nextSibling();