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 "SalomeApp_Application.h"
16 #include "SalomeApp_Study.h"
17 #include "SalomeApp_DataModel.h"
21 #include <qworkspace.h>
22 #include <qpopupmenu.h>
24 #if QT_VERSION > 0x030005
25 #include "sipAPISalomePyQtGUI.h"
27 #include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
30 #include <sipqtQWidget.h>
31 #include <sipqtQPopupMenu.h>
35 // Default name of the module, should be replaced at the moment
37 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
38 // Comment this define to block invoking of obsolete Python module's
39 // methods like setSetting(), definePopup(), etc.
40 #define __CALL_OLD_METHODS__
42 //=============================================================================
43 // General rule for Python requests created by SALOME_PYQT_Module:
44 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
45 // However, it is obligatory that ANY Python call is wrapped with a request object,
46 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
47 //=============================================================================
50 //=============================================================================
51 // The default PyQt module data model.
52 // Reuses common data model from SalomeApp.
53 //=============================================================================
54 class SALOME_PYQT_DataModel: public SalomeApp_DataModel
57 SALOME_PYQT_DataModel( CAM_Module* theModule ) : SalomeApp_DataModel( theModule ) {}
58 bool isModified() const { return false; }
59 bool isSaved() const { return false; }
62 //=============================================================================
63 // The class for parsing of the XML resource files.
64 // Used for backward compatibility with existing Python modules.
65 //=============================================================================
66 class SALOME_PYQT_XmlHandler
69 SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
71 void createPopup ( QPopupMenu* menu,
72 const QString& context,
73 const QString& parent,
74 const QString& object );
77 void createToolBar ( QDomNode& parentNode );
78 void createMenu ( QDomNode& parentNode,
79 const int parentMenuId = -1 );
81 void insertPopupItems( QDomNode& parentNode,
85 SALOME_PYQT_Module* myModule;
89 //=============================================================================
90 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
91 // all Python-based SALOME module
92 //=============================================================================
95 * This function creates an instance of SALOME_PYQT_Module object by request
96 * of and application object when the module is loaded.
99 SALOME_PYQT_EXPORT CAM_Module* createModule() {
100 return new SALOME_PYQT_Module();
105 * Static variables definition
107 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
112 SALOME_PYQT_Module::SALOME_PYQT_Module() :
113 SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
115 myMenuActionList.setAutoDelete( false );
116 myPopupActionList.setAutoDelete( false );
117 myToolbarActionList.setAutoDelete( false );
123 SALOME_PYQT_Module::~SALOME_PYQT_Module()
125 myMenuActionList.clear();
126 myPopupActionList.clear();
127 myToolbarActionList.clear();
133 * Creates data model for the module.
134 * Reimplemented from CAM_Module.
136 CAM_DataModel* SALOME_PYQT_Module::createDataModel()
138 // VSR: this is a temporary solution :
139 // should reuse default data model from SalomeApp
140 return new SALOME_PYQT_DataModel( this );
144 * Initialization of the module.
145 * Inherited from CAM_Module.
147 * This method is used for creation of the menus, toolbars and other staff.
148 * There are two ways:
149 * - for obsolete modules this method first tries to read <module>_<language>.xml
150 * resource file which contains a menu, toolbars and popup menus description.
151 * - new modules can create menus by by calling the corresponding methods of SalomePyQt
152 * Python API in the Python module's initialize() method which is called from here.
153 * NOTE: if postponed modules loading is not used, the active study might be not defined
154 * yet at this stage, so initialize() method should not perform any study-based initialization.
156 void SALOME_PYQT_Module::initialize( CAM_Application* app )
158 SalomeApp_Module::initialize( app );
160 // Try to get XML resource file name
161 SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
162 QString aLang = aResMgr->stringValue( "language", "language", QString::null );
163 if ( aLang.isEmpty() ) aLang = QString( "en" );
164 QString aName = name( "" );
165 QString aFileName = aName + "_" + aLang + ".xml";
166 aFileName = aResMgr->path( "resources", aName, aFileName );
168 // parse XML file if it is found and create actions
169 if ( !myXmlHandler && !aFileName.isEmpty() ) {
170 myXmlHandler = new SALOME_PYQT_XmlHandler( this, aFileName );
171 myXmlHandler->createActions();
174 // perform internal initialization and call module's initialize() method
175 // InitializeReq: request class for internal init() operation
176 class InitializeReq : public PyInterp_Request
179 InitializeReq( CAM_Application* _app,
180 SALOME_PYQT_Module* _obj )
181 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
186 virtual void execute()
188 myObj->init( myApp );
192 CAM_Application* myApp;
193 SALOME_PYQT_Module* myObj;
196 // Posting the request
197 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
201 * Activation of the module.
202 * Inherited from CAM_Module.
204 void SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
206 MESSAGE( "SALOME_PYQT_Module::activateModule" );
208 SalomeApp_Module::activateModule( theStudy );
210 // activate menus, toolbars, etc
211 setMenuShown( true );
212 setToolShown( true );
214 // ActivateReq: request class for internal activate() operation
215 class ActivateReq : public PyInterp_Request
218 ActivateReq( SUIT_Study* _study,
219 SALOME_PYQT_Module* _obj )
220 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
225 virtual void execute()
227 myObj->activate( myStudy );
232 SALOME_PYQT_Module* myObj;
235 // Posting the request
236 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
238 // connect desktop activation signal
239 connect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
243 * Deactivation of the module.
244 * Inherited from CAM_Module.
246 void SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
248 MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
250 SalomeApp_Module::deactivateModule( theStudy );
252 // deactivate menus, toolbars, etc
253 setMenuShown( false );
254 setToolShown( false );
256 // DeactivateReq: request class for internal deactivate() operation
257 class DeactivateReq : public PyInterp_LockRequest
260 DeactivateReq( PyInterp_base* _py_interp,
262 SALOME_PYQT_Module* _obj )
263 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
268 virtual void execute()
270 myObj->deactivate( myStudy );
275 SALOME_PYQT_Module* myObj;
278 // Posting the request
279 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
281 // disconnect desktop activation signal
282 disconnect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
286 * Processes GUI action (from main menu, toolbar or context popup menu)
288 void SALOME_PYQT_Module::onGUIEvent()
291 const QObject* obj = sender();
292 if ( !obj || !obj->inherits( "QAction" ) )
294 QAction* action = (QAction*)obj;
297 int id = actionId( action );
298 if ( myMenuActionList.contains( action ) )
299 id -= PYQT_ACTION_MENU;
300 if ( myToolbarActionList.contains( action ) )
301 id -= PYQT_ACTION_TOOLBAL;
302 if ( myPopupActionList.contains( action ) )
303 id -= PYQT_ACTION_POPUP;
304 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
306 // perform synchronous request to Python event dispatcher
307 class GUIEvent : public PyInterp_LockRequest
310 GUIEvent( PyInterp_base* _py_interp,
311 SALOME_PYQT_Module* _obj,
313 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
318 virtual void execute()
320 myObj->guiEvent( myId );
325 SALOME_PYQT_Module* myObj;
328 // Posting the request
329 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
333 * Desktop activation slot. Used for notifying about changing of the active study.
335 void SALOME_PYQT_Module::onDesktopActivated()
337 // StudyChangedReq: request class for internal studyChanged() operation
338 class StudyChangedReq : public PyInterp_Request
341 StudyChangedReq( SUIT_Study* _study,
342 SALOME_PYQT_Module* _obj )
343 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
348 virtual void execute()
350 myObj->studyChanged( myStudy );
355 SALOME_PYQT_Module* myObj;
358 // Posting the request
359 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
363 Context popup menu request.
364 Called when user activates popup menu in some window (view, object browser, etc).
366 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
368 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
369 // perform synchronous request to Python event dispatcher
370 class PopupMenuEvent : public PyInterp_LockRequest
373 PopupMenuEvent( PyInterp_base* _py_interp,
374 SALOME_PYQT_Module* _obj,
375 const QString& _context,
377 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
378 myContext( _context ),
383 virtual void execute()
385 myObj->contextMenu( myContext, myPopup );
389 SALOME_PYQT_Module* myObj;
394 // Posting the request only if dispatcher is not busy!
395 // Executing the request synchronously
396 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
397 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
401 * Defines the dockable window associated with the module.
402 * To fill the list of windows the correspondind Python module's windows()
403 * method is called from SALOME_PYQT_Module::init() method.
404 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
406 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
408 // First clear the output parameters
409 QMap<int, int>::ConstIterator it;
410 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
411 mappa[ it.key() ] = it.data();
416 * Defines the compatible views which should be opened on module activation.
417 * To fill the list of views the correspondind Python module's views()
418 * method is called from SALOME_PYQT_Module::init() method.
419 * By default, the list is empty.
421 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
423 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
424 listik.append( *it );
429 * Performs internal initialization
430 * - initializes/gets the Python interpreter (one per study)
431 * - imports the Python module
432 * - passes the workspace widget to the Python module
433 * - calls Python module's initialize() method
434 * - calls Python module's windows() method
435 * - calls Python module's views() method
437 void SALOME_PYQT_Module::init( CAM_Application* app )
439 // reset interpreter to NULL
443 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
447 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
450 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
452 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
453 initInterp( aStudyId );
457 // import Python GUI module
462 #ifdef __CALL_OLD_METHODS__
463 // call Python module's setWorkspace() method
465 #endif // __CALL_OLD_METHODS__
467 // then call Python module's initialize() method
468 // ... first get python lock
469 PyLockWrapper aLock = myInterp->GetLockWrapper();
470 // ... (the Python module is already imported)
471 // ... finally call Python module's initialize() method
472 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
474 // VSR: this method may not be implemented in Python module
478 // get the windows list from the Python module by calling windows() method
479 // ... first put default values
480 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
481 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
482 // VSR: LogWindow is not yet implemented
483 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
485 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
487 // VSR: this method may not be implemented in Python module
491 myWindowsMap.clear();
492 if ( PyDict_Check( res1 ) ) {
496 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
497 // parse the return value
498 // it should be a map: {integer:integer}
500 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
501 aKey = PyInt_AsLong( key );
502 aValue = PyInt_AsLong( value );
503 myWindowsMap[ aKey ] = aValue;
508 // get the windows list from the Python module by calling views() method
509 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
511 // VSR: this method may not be implemented in Python module
515 // parse the return value
516 // result can be one string...
517 if ( PyString_Check( res2 ) ) {
518 myViewMgrList.append( PyString_AsString( res2 ) );
520 // ... or list of strings
521 else if ( PyList_Check( res2 ) ) {
522 int size = PyList_Size( res2 );
523 for ( int i = 0; i < size; i++ ) {
524 PyObject* value = PyList_GetItem( res2, i );
525 if( value && PyString_Check( value ) ) {
526 myViewMgrList.append( PyString_AsString( value ) );
534 * Performs internal activation:
535 * - initializes/gets the Python interpreter (one per study)
536 * - imports the Python GUI module
537 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
538 * or activate() method (for new modules)
540 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
543 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
544 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
546 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
547 initInterp( aStudyId );
551 // import Python GUI module
557 PyLockWrapper aLock = myInterp->GetLockWrapper();
559 #ifdef __CALL_OLD_METHODS__
560 // call Python module's setSettings() method (obsolete)
561 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
563 // VSR: this method may not be implemented in Python module
566 #endif // __CALL_OLD_METHODS__
568 // call Python module's activate() method (for the new modules)
569 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
571 // VSR: this method may not be implemented in Python module
577 * Performs internal deactivation:
578 * - calls Python module's deactivate() method
580 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
582 // check if the subinterpreter is initialized and Python module is imported
583 if ( !myInterp || !myModule ) {
584 // Error! Python subinterpreter should be initialized and module should be imported first!
587 // then call Python module's deactivate() method
588 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
590 // VSR: this method may not be implemented in Python module
596 * Called when active the study is actived (user brings its desktop to top)
597 * - initializes/gets the Python interpreter (one per study)
598 * - imports the Python GUI module
599 * - calls Python module's activeStudyChanged() method
601 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
604 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
605 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
607 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
608 initInterp( aStudyId );
612 // import Python GUI module
618 PyLockWrapper aLock = myInterp->GetLockWrapper();
620 // call Python module's activeStudyChanged() method
621 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
623 // VSR: this method may not be implemented in Python module
628 QString SALOME_PYQT_Module::engineIOR() const
630 return QString::null;
634 * Processes context popup menu request
635 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
636 * to define the popup menu context
637 * - parses XML resourses file (if exists) and fills the popup menu with the items)
638 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
639 * to allow module to customize the popup menu
640 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
641 * by using insertItem(...) Qt functions.
643 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
645 // Python interpreter should be initialized and Python module should be
647 if ( !myInterp || !myModule )
650 QString aContext( theContext ), aObject( "" ), aParent( "" );
651 #ifdef __CALL_OLD_METHODS__
652 // call definePopup() Python module's function
653 // this is obsolete function, used only for compatibility reasons
654 PyObjWrapper res(PyObject_CallMethod( myModule,
659 aParent.latin1() ) );
661 // VSR: this method may not be implemented in Python module
665 // parse return value
667 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
673 #endif // __CALL_OLD_METHODS__
675 // first try to create menu via XML parser:
676 // we create popup menus without help of QtxPopupMgr
678 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
680 PyObjWrapper sipPopup( sipMapCppToSelf( thePopupMenu, sipClass_QPopupMenu ) );
682 // then call Python module's createPopupMenu() method (for new modules)
683 PyObjWrapper res1( PyObject_CallMethod( myModule,
687 aContext.latin1() ) );
689 // VSR: this method may not be implemented in Python module
693 #ifdef __CALL_OLD_METHODS__
694 // call customPopup() Python module's function
695 // this is obsolete function, used only for compatibility reasons
696 PyObjWrapper res2( PyObject_CallMethod( myModule,
702 aParent.latin1() ) );
704 // VSR: this method may not be implemented in Python module
707 #endif // __CALL_OLD_METHODS__
711 * Processes GUI event
712 * - calls Python module's OnGUIEvent() method
714 void SALOME_PYQT_Module::guiEvent( const int theId )
716 // Python interpreter should be initialized and Python module should be
718 if ( !myInterp || !myModule )
721 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
729 * Initialises python subinterpreter (one per study)
731 void SALOME_PYQT_Module::initInterp( int theStudyId )
735 // Error! Study Id must not be 0!
739 // try to find the subinterpreter
740 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
742 myInterp = myInterpMap[ theStudyId ];
745 // not found - create a new one!
746 ///////////////////////////////////////////////////////////////////
747 // Attention: the creation of Python interpretor must be protected
748 // by a C++ Lock because of C threads
749 ///////////////////////////////////////////////////////////////////
750 myInterp = new SALOME_PYQT_PyInterp();
751 myInterp->initialize();
752 myInterpMap[ theStudyId ] = myInterp;
754 // import 'salome' module and call 'salome_init' method;
755 // do it only once on interpreter creation
756 // ... first get python lock
757 PyLockWrapper aLock = myInterp->GetLockWrapper();
758 // ... then import a module
759 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
765 // ... then call a method
766 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
775 * Imports Python GUI module and remember the reference to the module
776 * !!! initInterp() should be called first!!!
778 void SALOME_PYQT_Module::importModule()
780 // check if the subinterpreter is initialized
782 // Error! Python subinterpreter should be initialized first!
786 // import Python GUI module and puts it in <myModule> attribute
787 // ... first get python lock
788 PyLockWrapper aLock = myInterp->GetLockWrapper();
789 // ... then import a module
790 QString aMod = QString( name("") ) + "GUI";
791 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
800 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
802 * !!! initInterp() and importModule() should be called first!!!
804 void SALOME_PYQT_Module::setWorkSpace()
806 // check if the subinterpreter is initialized and Python module is imported
807 if ( !myInterp || !myModule ) {
808 // Error! Python subinterpreter should be initialized and module should be imported first!
812 // call setWorkspace() method
813 // ... first get python lock
814 PyLockWrapper aLock = myInterp->GetLockWrapper();
816 // ... then try to import SalomePyQt module. If it's not possible don't go on.
817 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
823 #ifdef __CALL_OLD_METHODS__
824 // ... then get workspace object
825 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
826 if ( aDesktop && aDesktop->workspace() ) {
827 QWidget* aWorkspace = aDesktop->workspace();
828 PyObjWrapper pyws( sipMapCppToSelfSubClass( aWorkspace, sipClass_QWidget ) );
829 // ... and finally call Python module's setWorkspace() method (obsolete)
830 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
832 // VSR: this method may not be implemented in Python module
837 #endif // __CALL_OLD_METHODS__
841 * Adds an action into private action list [internal usage]
843 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
846 case PYQT_ACTION_MENU:
847 myMenuActionList.append( action );
849 case PYQT_ACTION_TOOLBAL:
850 myToolbarActionList.append( action );
852 case PYQT_ACTION_POPUP:
853 myPopupActionList.append( action );
858 //=============================================================================
859 // SALOME_PYQT_XmlHandler class implementation
860 //=============================================================================
862 // gets an tag name for the dom element [ static ]
863 // returns an empty string if the element does not have tag name
864 static QString tagName( const QDomElement& element ) {
865 return element.tagName().stripWhiteSpace();
868 // gets an attribute by it's name for the dom element [ static ]
869 // returns an empty string if the element does not have such attribute
870 static QString attribute( const QDomElement& element, const QString& attName ) {
871 return element.attribute( attName ).stripWhiteSpace();
874 // checks the given value for the boolean value [ static ]
875 // returns TRUE if string is "true", "yes" or "1"
876 static bool checkBool( const QString& value ) {
877 return ( value == "true" || value == "yes" || value == "1" );
880 // checks the given value for the integer value [ static ]
881 // returns -1 if item is empty or presents and invalid number
882 static int checkInt( const QString& value )
884 return value.isEmpty() ? -1 : value.toInt();
890 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
893 QFile aFile( fileName );
894 if ( !aFile.open( IO_ReadOnly ) )
896 if ( !myDoc.setContent( &aFile ) ) {
904 Called by SALOME_PYQT_Module::initialize() in order to create actions
905 (menus, toolbars, popup menus)
907 void SALOME_PYQT_XmlHandler::createActions()
909 // get document element
910 QDomElement aDocElem = myDoc.documentElement();
912 // get main menu actions
913 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
914 for ( int i = 0; i < aMenuList.count(); i++ ) {
915 QDomNode n = aMenuList.item( i );
919 // create toolbars actions
920 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
921 for ( int i = 0; i < aToolsList.count(); i++ ) {
922 QDomNode n = aToolsList.item( i );
930 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
931 const QString& context,
932 const QString& parent,
933 const QString& object )
935 // get document element
936 QDomElement aDocElem = myDoc.documentElement();
938 // get popup menus actions
939 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
940 for ( int i = 0; i < aPopupList.count(); i++ ) {
941 QDomNode n = aPopupList.item( i );
942 if ( !n.isNull() && n.isElement() ) {
943 QDomElement e = n.toElement();
944 QString lab = attribute( e, "label-id" );
945 QString ctx = attribute( e, "context-id" );
946 QString prt = attribute( e, "parent-id" );
947 QString obj = attribute( e, "object-id" );
948 if ( ctx == context && prt == parent && obj == object ) {
949 insertPopupItems( n, menu );
957 Create main menu with child actions
959 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
964 if ( parentNode.isNull() )
967 QDomElement parentElement = parentNode.toElement();
968 if ( !parentElement.isNull() ) {
969 QString plabel = attribute( parentElement, "label-id" );
970 int pid = checkInt( attribute( parentElement, "item-id" ) );
971 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
972 if ( !plabel.isEmpty() ) {
974 int menuId = myModule->createMenu( plabel, // label
975 parentMenuId, // parent menu ID, should be -1 for main menu
979 QDomNode node = parentNode.firstChild();
980 while ( !node.isNull() ) {
981 if ( node.isElement() ) {
982 QDomElement elem = node.toElement();
983 QString aTagName = tagName( elem );
984 if ( aTagName == "popup-item" ) {
985 int id = checkInt( attribute( elem, "item-id" ) );
986 int pos = checkInt( attribute( elem, "pos-id" ) );
987 QString label = attribute( elem, "label-id" );
988 QString icon = attribute( elem, "icon-id" );
989 QString tooltip = attribute( elem, "tooltip-id" );
990 QString accel = attribute( elem, "accel-id" );
991 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
992 QString execute = attribute( elem, "execute-action" ); // not used
994 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
996 if ( !pixmap.isNull() )
997 anIcon = QIconSet( pixmap );
999 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1000 // also check if the action with given ID is already created
1001 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1002 // little trick to have several actions with same ID for menus and toolbars
1003 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1004 // create menu action
1005 QAction* action = myModule->createAction( id, // ID
1009 tooltip, // status-bar text
1010 QKeySequence( accel ), // keyboard accelerator
1011 myModule->getApp()->desktop(), // desktop
1012 toggle, // toogled action
1013 myModule, // receiver
1014 SLOT( onGUIEvent() ) ); // slot
1015 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1016 myModule->createMenu( action, menuId, -1, 100, pos );
1019 else if ( aTagName == "submenu" ) {
1021 createMenu( node, menuId );
1023 else if ( aTagName == "separator" ) {
1024 // create menu separator
1025 int pos = checkInt( attribute( elem, "pos-id" ) );
1026 QAction* action = myModule->separator();
1027 myModule->createMenu( action, menuId, -1, 100, pos );
1030 node = node.nextSibling();
1037 Create a toolbar with child actions
1039 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1044 if ( parentNode.isNull() )
1047 QDomElement parentElement = parentNode.toElement();
1048 if ( !parentElement.isNull() ) {
1049 QString aLabel = attribute( parentElement, "label-id" );
1050 if ( !aLabel.isEmpty() ) {
1052 int tbId = myModule->createTool( aLabel );
1053 QDomNode node = parentNode.firstChild();
1054 while ( !node.isNull() ) {
1055 if ( node.isElement() ) {
1056 QDomElement elem = node.toElement();
1057 QString aTagName = tagName( elem );
1058 if ( aTagName == "toolbutton-item" ) {
1059 int id = checkInt( attribute( elem, "item-id" ) );
1060 int pos = checkInt( attribute( elem, "pos-id" ) );
1061 QString label = attribute( elem, "label-id" );
1062 QString icon = attribute( elem, "icon-id" );
1063 QString tooltip = attribute( elem, "tooltip-id" );
1064 QString accel = attribute( elem, "accel-id" );
1065 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1066 QString execute = attribute( elem, "execute-action" ); // not used
1068 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1070 if ( !pixmap.isNull() )
1071 anIcon = QIconSet( pixmap );
1073 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1074 // also check if the action with given ID is already created
1075 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1076 // little trick to have several actions with same ID for menus and toolbars
1077 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1078 // create toolbar action
1079 QAction* action = myModule->createAction( id, // ID
1083 tooltip, // status-bar text
1084 QKeySequence( accel ), // keyboard accelerator
1085 myModule->getApp()->desktop(), // desktop
1086 toggle, // toogled action
1087 myModule, // receiver
1088 SLOT( onGUIEvent() ) ); // slot
1089 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1090 myModule->createTool( action, tbId, -1, pos );
1093 else if ( aTagName == "separatorTB" ) {
1094 // create toolbar separator
1095 int pos = checkInt( attribute( elem, "pos-id" ) );
1096 QAction* action = myModule->separator();
1097 myModule->createTool( action, tbId, -1, pos );
1100 node = node.nextSibling();
1106 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1111 if ( parentNode.isNull() )
1114 // we create popup menus without help of QtxPopupMgr
1115 QDomNode node = parentNode.firstChild();
1116 while ( !node.isNull() ) {
1117 if ( node.isElement() ) {
1118 QDomElement elem = node.toElement();
1119 QString aTagName = tagName( elem );
1120 if ( aTagName == "popup-item" ) {
1121 // insert a command item
1122 int id = checkInt( attribute( elem, "item-id" ) );
1123 int pos = checkInt( attribute( elem, "pos-id" ) );
1124 QString label = attribute( elem, "label-id" );
1125 QString icon = attribute( elem, "icon-id" );
1126 QString tooltip = attribute( elem, "tooltip-id" ); // not used
1127 QString accel = attribute( elem, "accel-id" );
1128 bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1129 QString execute = attribute( elem, "execute-action" ); // not used
1131 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1133 if ( !pixmap.isNull() )
1134 anIcon = QIconSet( pixmap );
1136 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1137 // also check if the action with given ID is already created
1139 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent() ), QKeySequence( accel ), id, pos );
1142 else if ( aTagName == "submenu" ) {
1144 int id = checkInt( attribute( elem, "item-id" ) );
1145 int pos = checkInt( attribute( elem, "pos-id" ) );
1146 QString label = attribute( elem, "label-id" );
1147 QString icon = attribute( elem, "icon-id" );
1149 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1151 if ( !pixmap.isNull() )
1152 anIcon = QIconSet( pixmap );
1154 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1155 menu->insertItem( anIcon, label, newPopup, id, pos );
1156 insertPopupItems( node, newPopup );
1158 else if ( aTagName == "separator" ) {
1159 // create menu separator
1160 int pos = checkInt( attribute( elem, "pos-id" ) );
1161 menu->insertSeparator( pos );
1164 node = node.nextSibling();