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>
36 static int MYDEBUG = 0;
38 static int MYDEBUG = 0;
41 // Default name of the module, should be replaced at the moment
43 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
44 // Comment this define to block invoking of obsolete Python module's
45 // methods like setSetting(), definePopup(), etc.
46 #define __CALL_OLD_METHODS__
48 //=============================================================================
49 // General rule for Python requests created by SALOME_PYQT_Module:
50 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
51 // However, it is obligatory that ANY Python call is wrapped with a request object,
52 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
53 //=============================================================================
56 //=============================================================================
57 // The default PyQt module data model.
58 // Reuses common data model from SalomeApp.
59 //=============================================================================
60 class SALOME_PYQT_DataModel: public SalomeApp_DataModel
63 SALOME_PYQT_DataModel( CAM_Module* theModule ) : SalomeApp_DataModel( theModule ) {}
64 bool isModified() const { return false; }
65 bool isSaved() const { return false; }
68 //=============================================================================
69 // The class for parsing of the XML resource files.
70 // Used for backward compatibility with existing Python modules.
71 //=============================================================================
72 class SALOME_PYQT_XmlHandler
75 SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
77 void createPopup ( QPopupMenu* menu,
78 const QString& context,
79 const QString& parent,
80 const QString& object );
83 void createToolBar ( QDomNode& parentNode );
84 void createMenu ( QDomNode& parentNode,
85 const int parentMenuId = -1 );
87 void insertPopupItems( QDomNode& parentNode,
91 SALOME_PYQT_Module* myModule;
95 //=============================================================================
96 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
97 // all Python-based SALOME module
98 //=============================================================================
101 * This function creates an instance of SALOME_PYQT_Module object by request
102 * of and application object when the module is loaded.
105 SALOME_PYQT_EXPORT CAM_Module* createModule() {
106 return new SALOME_PYQT_Module();
111 * Static variables definition
113 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
118 SALOME_PYQT_Module::SALOME_PYQT_Module() :
119 SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
121 myMenuActionList.setAutoDelete( false );
122 myPopupActionList.setAutoDelete( false );
123 myToolbarActionList.setAutoDelete( false );
129 SALOME_PYQT_Module::~SALOME_PYQT_Module()
131 myMenuActionList.clear();
132 myPopupActionList.clear();
133 myToolbarActionList.clear();
139 * Creates data model for the module.
140 * Reimplemented from CAM_Module.
142 CAM_DataModel* SALOME_PYQT_Module::createDataModel()
144 // VSR: this is a temporary solution :
145 // should reuse default data model from SalomeApp
146 return new SALOME_PYQT_DataModel( this );
150 * Initialization of the module.
151 * Inherited from CAM_Module.
153 * This method is used for creation of the menus, toolbars and other staff.
154 * There are two ways:
155 * - for obsolete modules this method first tries to read <module>_<language>.xml
156 * resource file which contains a menu, toolbars and popup menus description.
157 * - new modules can create menus by by calling the corresponding methods of SalomePyQt
158 * Python API in the Python module's initialize() method which is called from here.
159 * NOTE: if postponed modules loading is not used, the active study might be not defined
160 * yet at this stage, so initialize() method should not perform any study-based initialization.
162 void SALOME_PYQT_Module::initialize( CAM_Application* app )
164 SalomeApp_Module::initialize( app );
166 // Try to get XML resource file name
167 SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
168 QString aLang = aResMgr->stringValue( "language", "language", QString::null );
169 if ( aLang.isEmpty() ) aLang = QString( "en" );
170 QString aName = name( "" );
171 QString aFileName = aName + "_" + aLang + ".xml";
172 aFileName = aResMgr->path( "resources", aName, aFileName );
174 // parse XML file if it is found and create actions
175 if ( !myXmlHandler && !aFileName.isEmpty() ) {
176 myXmlHandler = new SALOME_PYQT_XmlHandler( this, aFileName );
177 myXmlHandler->createActions();
180 // perform internal initialization and call module's initialize() method
181 // InitializeReq: request class for internal init() operation
182 class InitializeReq : public PyInterp_Request
185 InitializeReq( CAM_Application* _app,
186 SALOME_PYQT_Module* _obj )
187 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
192 virtual void execute()
194 myObj->init( myApp );
198 CAM_Application* myApp;
199 SALOME_PYQT_Module* myObj;
202 // Posting the request
203 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
207 * Activation of the module.
208 * Inherited from CAM_Module.
210 void SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
212 MESSAGE( "SALOME_PYQT_Module::activateModule" );
214 SalomeApp_Module::activateModule( theStudy );
216 // activate menus, toolbars, etc
217 setMenuShown( true );
218 setToolShown( true );
220 // ActivateReq: request class for internal activate() operation
221 class ActivateReq : public PyInterp_Request
224 ActivateReq( SUIT_Study* _study,
225 SALOME_PYQT_Module* _obj )
226 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
231 virtual void execute()
233 myObj->activate( myStudy );
238 SALOME_PYQT_Module* myObj;
241 // Posting the request
242 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
244 // connect desktop activation signal
245 connect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
249 * Deactivation of the module.
250 * Inherited from CAM_Module.
252 void SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
254 MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
256 SalomeApp_Module::deactivateModule( theStudy );
258 // deactivate menus, toolbars, etc
259 setMenuShown( false );
260 setToolShown( false );
262 // DeactivateReq: request class for internal deactivate() operation
263 class DeactivateReq : public PyInterp_LockRequest
266 DeactivateReq( PyInterp_base* _py_interp,
268 SALOME_PYQT_Module* _obj )
269 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
274 virtual void execute()
276 myObj->deactivate( myStudy );
281 SALOME_PYQT_Module* myObj;
284 // Posting the request
285 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
287 // disconnect desktop activation signal
288 disconnect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
292 * Processes GUI action (from main menu, toolbar or context popup menu)
294 void SALOME_PYQT_Module::onGUIEvent()
297 const QObject* obj = sender();
298 if ( !obj || !obj->inherits( "QAction" ) )
300 QAction* action = (QAction*)obj;
303 int id = actionId( action );
304 if ( myMenuActionList.contains( action ) )
305 id -= PYQT_ACTION_MENU;
306 if ( myToolbarActionList.contains( action ) )
307 id -= PYQT_ACTION_TOOLBAL;
308 if ( myPopupActionList.contains( action ) )
309 id -= PYQT_ACTION_POPUP;
310 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
312 // perform synchronous request to Python event dispatcher
313 class GUIEvent : public PyInterp_LockRequest
316 GUIEvent( PyInterp_base* _py_interp,
317 SALOME_PYQT_Module* _obj,
319 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
324 virtual void execute()
326 myObj->guiEvent( myId );
331 SALOME_PYQT_Module* myObj;
334 // Posting the request
335 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
339 * Desktop activation slot. Used for notifying about changing of the active study.
341 void SALOME_PYQT_Module::onDesktopActivated()
343 // StudyChangedReq: request class for internal studyChanged() operation
344 class StudyChangedReq : public PyInterp_Request
347 StudyChangedReq( SUIT_Study* _study,
348 SALOME_PYQT_Module* _obj )
349 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
354 virtual void execute()
356 myObj->studyChanged( myStudy );
361 SALOME_PYQT_Module* myObj;
364 // Posting the request
365 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
369 Context popup menu request.
370 Called when user activates popup menu in some window (view, object browser, etc).
372 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
374 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
375 // perform synchronous request to Python event dispatcher
376 class PopupMenuEvent : public PyInterp_LockRequest
379 PopupMenuEvent( PyInterp_base* _py_interp,
380 SALOME_PYQT_Module* _obj,
381 const QString& _context,
383 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
384 myContext( _context ),
389 virtual void execute()
391 myObj->contextMenu( myContext, myPopup );
395 SALOME_PYQT_Module* myObj;
400 // Posting the request only if dispatcher is not busy!
401 // Executing the request synchronously
402 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
403 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
407 * Defines the dockable window associated with the module.
408 * To fill the list of windows the correspondind Python module's windows()
409 * method is called from SALOME_PYQT_Module::init() method.
410 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
412 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
414 // First clear the output parameters
415 QMap<int, int>::ConstIterator it;
416 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
417 mappa[ it.key() ] = it.data();
422 * Defines the compatible views which should be opened on module activation.
423 * To fill the list of views the correspondind Python module's views()
424 * method is called from SALOME_PYQT_Module::init() method.
425 * By default, the list is empty.
427 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
429 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
430 listik.append( *it );
435 * Performs internal initialization
436 * - initializes/gets the Python interpreter (one per study)
437 * - imports the Python module
438 * - passes the workspace widget to the Python module
439 * - calls Python module's initialize() method
440 * - calls Python module's windows() method
441 * - calls Python module's views() method
443 void SALOME_PYQT_Module::init( CAM_Application* app )
445 // reset interpreter to NULL
449 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
453 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
456 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
458 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
459 initInterp( aStudyId );
463 // import Python GUI module
468 #ifdef __CALL_OLD_METHODS__
469 // call Python module's setWorkspace() method
471 #endif // __CALL_OLD_METHODS__
473 // then call Python module's initialize() method
474 // ... first get python lock
475 PyLockWrapper aLock = myInterp->GetLockWrapper();
476 // ... (the Python module is already imported)
477 // ... finally call Python module's initialize() method
478 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
480 // VSR: this method may not be implemented in Python module
484 // get the windows list from the Python module by calling windows() method
485 // ... first put default values
486 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
487 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
488 // VSR: LogWindow is not yet implemented
489 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
491 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
493 // VSR: this method may not be implemented in Python module
497 myWindowsMap.clear();
498 if ( PyDict_Check( res1 ) ) {
502 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
503 // parse the return value
504 // it should be a map: {integer:integer}
506 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
507 aKey = PyInt_AsLong( key );
508 aValue = PyInt_AsLong( value );
509 myWindowsMap[ aKey ] = aValue;
514 // get the windows list from the Python module by calling views() method
515 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
517 // VSR: this method may not be implemented in Python module
521 // parse the return value
522 // result can be one string...
523 if ( PyString_Check( res2 ) ) {
524 myViewMgrList.append( PyString_AsString( res2 ) );
526 // ... or list of strings
527 else if ( PyList_Check( res2 ) ) {
528 int size = PyList_Size( res2 );
529 for ( int i = 0; i < size; i++ ) {
530 PyObject* value = PyList_GetItem( res2, i );
531 if( value && PyString_Check( value ) ) {
532 myViewMgrList.append( PyString_AsString( value ) );
540 * Performs internal activation:
541 * - initializes/gets the Python interpreter (one per study)
542 * - imports the Python GUI module
543 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
544 * or activate() method (for new modules)
546 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
549 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
550 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
552 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
553 initInterp( aStudyId );
557 // import Python GUI module
563 PyLockWrapper aLock = myInterp->GetLockWrapper();
565 #ifdef __CALL_OLD_METHODS__
566 // call Python module's setSettings() method (obsolete)
567 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
569 // VSR: this method may not be implemented in Python module
572 #endif // __CALL_OLD_METHODS__
574 // call Python module's activate() method (for the new modules)
575 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
577 // VSR: this method may not be implemented in Python module
583 * Performs internal deactivation:
584 * - calls Python module's deactivate() method
586 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
588 // check if the subinterpreter is initialized and Python module is imported
589 if ( !myInterp || !myModule ) {
590 // Error! Python subinterpreter should be initialized and module should be imported first!
593 // then call Python module's deactivate() method
594 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
596 // VSR: this method may not be implemented in Python module
602 * Called when active the study is actived (user brings its desktop to top)
603 * - initializes/gets the Python interpreter (one per study)
604 * - imports the Python GUI module
605 * - calls Python module's activeStudyChanged() method
607 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
610 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
611 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
613 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
614 initInterp( aStudyId );
618 // import Python GUI module
624 PyLockWrapper aLock = myInterp->GetLockWrapper();
626 // call Python module's activeStudyChanged() method
627 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
629 // VSR: this method may not be implemented in Python module
635 * Processes context popup menu request
636 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
637 * to define the popup menu context
638 * - parses XML resourses file (if exists) and fills the popup menu with the items)
639 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
640 * to allow module to customize the popup menu
641 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
642 * by using insertItem(...) Qt functions.
644 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
646 // Python interpreter should be initialized and Python module should be
648 if ( !myInterp || !myModule )
651 QString aContext( theContext ), aObject( "" ), aParent( "" );
652 #ifdef __CALL_OLD_METHODS__
653 // call definePopup() Python module's function
654 // this is obsolete function, used only for compatibility reasons
655 PyObjWrapper res(PyObject_CallMethod( myModule,
660 aParent.latin1() ) );
662 // VSR: this method may not be implemented in Python module
666 // parse return value
668 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
674 #endif // __CALL_OLD_METHODS__
676 // first try to create menu via XML parser:
677 // we create popup menus without help of QtxPopupMgr
679 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
681 PyObjWrapper sipPopup( sipMapCppToSelf( thePopupMenu, sipClass_QPopupMenu ) );
683 // then call Python module's createPopupMenu() method (for new modules)
684 PyObjWrapper res1( PyObject_CallMethod( myModule,
688 aContext.latin1() ) );
690 // VSR: this method may not be implemented in Python module
694 #ifdef __CALL_OLD_METHODS__
695 // call customPopup() Python module's function
696 // this is obsolete function, used only for compatibility reasons
697 PyObjWrapper res2( PyObject_CallMethod( myModule,
703 aParent.latin1() ) );
705 // VSR: this method may not be implemented in Python module
708 #endif // __CALL_OLD_METHODS__
712 * Processes GUI event
713 * - calls Python module's OnGUIEvent() method
715 void SALOME_PYQT_Module::guiEvent( const int theId )
717 // Python interpreter should be initialized and Python module should be
719 if ( !myInterp || !myModule )
722 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
730 * Initialises python subinterpreter (one per study)
732 void SALOME_PYQT_Module::initInterp( int theStudyId )
736 // Error! Study Id must not be 0!
740 // try to find the subinterpreter
741 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
743 myInterp = myInterpMap[ theStudyId ];
746 // not found - create a new one!
747 ///////////////////////////////////////////////////////////////////
748 // Attention: the creation of Python interpretor must be protected
749 // by a C++ Lock because of C threads
750 ///////////////////////////////////////////////////////////////////
751 myInterp = new SALOME_PYQT_PyInterp();
752 myInterp->initialize();
753 myInterpMap[ theStudyId ] = myInterp;
755 // import 'salome' module and call 'salome_init' method;
756 // do it only once on interpreter creation
757 // ... first get python lock
758 PyLockWrapper aLock = myInterp->GetLockWrapper();
759 // ... then import a module
760 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
766 // ... then call a method
767 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
776 * Imports Python GUI module and remember the reference to the module
777 * !!! initInterp() should be called first!!!
779 void SALOME_PYQT_Module::importModule()
781 // check if the subinterpreter is initialized
783 // Error! Python subinterpreter should be initialized first!
787 // import Python GUI module and puts it in <myModule> attribute
788 // ... first get python lock
789 PyLockWrapper aLock = myInterp->GetLockWrapper();
790 // ... then import a module
791 QString aMod = QString( name("") ) + "GUI";
792 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
801 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
803 * !!! initInterp() and importModule() should be called first!!!
805 void SALOME_PYQT_Module::setWorkSpace()
807 // check if the subinterpreter is initialized and Python module is imported
808 if ( !myInterp || !myModule ) {
809 // Error! Python subinterpreter should be initialized and module should be imported first!
813 // call setWorkspace() method
814 // ... first get python lock
815 PyLockWrapper aLock = myInterp->GetLockWrapper();
817 // ... then try to import SalomePyQt module. If it's not possible don't go on.
818 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
824 #ifdef __CALL_OLD_METHODS__
825 // ... then get workspace object
826 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
827 if ( aDesktop && aDesktop->workspace() ) {
828 QWidget* aWorkspace = aDesktop->workspace();
829 PyObjWrapper pyws( sipMapCppToSelfSubClass( aWorkspace, sipClass_QWidget ) );
830 // ... and finally call Python module's setWorkspace() method (obsolete)
831 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
833 // VSR: this method may not be implemented in Python module
838 #endif // __CALL_OLD_METHODS__
842 * Adds an action into private action list [internal usage]
844 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
847 case PYQT_ACTION_MENU:
848 myMenuActionList.append( action );
850 case PYQT_ACTION_TOOLBAL:
851 myToolbarActionList.append( action );
853 case PYQT_ACTION_POPUP:
854 myPopupActionList.append( action );
859 //=============================================================================
860 // SALOME_PYQT_XmlHandler class implementation
861 //=============================================================================
863 // gets an tag name for the dom element [ static ]
864 // returns an empty string if the element does not have tag name
865 static QString tagName( const QDomElement& element ) {
866 return element.tagName().stripWhiteSpace();
869 // gets an attribute by it's name for the dom element [ static ]
870 // returns an empty string if the element does not have such attribute
871 static QString attribute( const QDomElement& element, const QString& attName ) {
872 return element.attribute( attName ).stripWhiteSpace();
875 // checks the given value for the boolean value [ static ]
876 // returns TRUE if string is "true", "yes" or "1"
877 static bool checkBool( const QString& value ) {
878 return ( value == "true" || value == "yes" || value == "1" );
881 // checks the given value for the integer value [ static ]
882 // returns -1 if item is empty or presents and invalid number
883 static int checkInt( const QString& value )
885 return value.isEmpty() ? -1 : value.toInt();
891 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
894 QFile aFile( fileName );
895 if ( !aFile.open( IO_ReadOnly ) )
897 if ( !myDoc.setContent( &aFile ) ) {
905 Called by SALOME_PYQT_Module::initialize() in order to create actions
906 (menus, toolbars, popup menus)
908 void SALOME_PYQT_XmlHandler::createActions()
910 // get document element
911 QDomElement aDocElem = myDoc.documentElement();
913 // get main menu actions
914 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
915 for ( int i = 0; i < aMenuList.count(); i++ ) {
916 QDomNode n = aMenuList.item( i );
920 // create toolbars actions
921 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
922 for ( int i = 0; i < aToolsList.count(); i++ ) {
923 QDomNode n = aToolsList.item( i );
931 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
932 const QString& context,
933 const QString& parent,
934 const QString& object )
936 // get document element
937 QDomElement aDocElem = myDoc.documentElement();
939 // get popup menus actions
940 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
941 for ( int i = 0; i < aPopupList.count(); i++ ) {
942 QDomNode n = aPopupList.item( i );
943 if ( !n.isNull() && n.isElement() ) {
944 QDomElement e = n.toElement();
945 QString lab = attribute( e, "label-id" );
946 QString ctx = attribute( e, "context-id" );
947 QString prt = attribute( e, "parent-id" );
948 QString obj = attribute( e, "object-id" );
949 if ( ctx == context && prt == parent && obj == object ) {
950 insertPopupItems( n, menu );
958 Create main menu with child actions
960 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
965 if ( parentNode.isNull() )
968 QDomElement parentElement = parentNode.toElement();
969 if ( !parentElement.isNull() ) {
970 QString plabel = attribute( parentElement, "label-id" );
971 int pid = checkInt( attribute( parentElement, "item-id" ) );
972 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
973 if ( !plabel.isEmpty() ) {
975 int menuId = myModule->createMenu( plabel, // label
976 parentMenuId, // parent menu ID, should be -1 for main menu
980 QDomNode node = parentNode.firstChild();
981 while ( !node.isNull() ) {
982 if ( node.isElement() ) {
983 QDomElement elem = node.toElement();
984 QString aTagName = tagName( elem );
985 if ( aTagName == "popup-item" ) {
986 int id = checkInt( attribute( elem, "item-id" ) );
987 int pos = checkInt( attribute( elem, "pos-id" ) );
988 QString label = attribute( elem, "label-id" );
989 QString icon = attribute( elem, "icon-id" );
990 QString tooltip = attribute( elem, "tooltip-id" );
991 QString accel = attribute( elem, "accel-id" );
992 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
993 QString execute = attribute( elem, "execute-action" ); // not used
995 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
997 if ( !pixmap.isNull() )
998 anIcon = QIconSet( pixmap );
1000 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1001 // also check if the action with given ID is already created
1002 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1003 // little trick to have several actions with same ID for menus and toolbars
1004 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1005 // create menu action
1006 QAction* action = myModule->createAction( id, // ID
1010 tooltip, // status-bar text
1011 QKeySequence( accel ), // keyboard accelerator
1012 myModule->getApp()->desktop(), // desktop
1013 toggle, // toogled action
1014 myModule, // receiver
1015 SLOT( onGUIEvent() ) ); // slot
1016 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1017 myModule->createMenu( action, menuId, -1, 100, pos );
1020 else if ( aTagName == "submenu" ) {
1022 createMenu( node, menuId );
1024 else if ( aTagName == "separator" ) {
1025 // create menu separator
1026 int pos = checkInt( attribute( elem, "pos-id" ) );
1027 QAction* action = myModule->separator();
1028 myModule->createMenu( action, menuId, -1, 100, pos );
1031 node = node.nextSibling();
1038 Create a toolbar with child actions
1040 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1045 if ( parentNode.isNull() )
1048 QDomElement parentElement = parentNode.toElement();
1049 if ( !parentElement.isNull() ) {
1050 QString aLabel = attribute( parentElement, "label-id" );
1051 if ( !aLabel.isEmpty() ) {
1053 int tbId = myModule->createTool( aLabel );
1054 QDomNode node = parentNode.firstChild();
1055 while ( !node.isNull() ) {
1056 if ( node.isElement() ) {
1057 QDomElement elem = node.toElement();
1058 QString aTagName = tagName( elem );
1059 if ( aTagName == "toolbutton-item" ) {
1060 int id = checkInt( attribute( elem, "item-id" ) );
1061 int pos = checkInt( attribute( elem, "pos-id" ) );
1062 QString label = attribute( elem, "label-id" );
1063 QString icon = attribute( elem, "icon-id" );
1064 QString tooltip = attribute( elem, "tooltip-id" );
1065 QString accel = attribute( elem, "accel-id" );
1066 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1067 QString execute = attribute( elem, "execute-action" ); // not used
1069 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1071 if ( !pixmap.isNull() )
1072 anIcon = QIconSet( pixmap );
1074 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1075 // also check if the action with given ID is already created
1076 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1077 // little trick to have several actions with same ID for menus and toolbars
1078 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1079 // create toolbar action
1080 QAction* action = myModule->createAction( id, // ID
1084 tooltip, // status-bar text
1085 QKeySequence( accel ), // keyboard accelerator
1086 myModule->getApp()->desktop(), // desktop
1087 toggle, // toogled action
1088 myModule, // receiver
1089 SLOT( onGUIEvent() ) ); // slot
1090 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1091 myModule->createTool( action, tbId, -1, pos );
1094 else if ( aTagName == "separatorTB" ) {
1095 // create toolbar separator
1096 int pos = checkInt( attribute( elem, "pos-id" ) );
1097 QAction* action = myModule->separator();
1098 myModule->createTool( action, tbId, -1, pos );
1101 node = node.nextSibling();
1107 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1112 if ( parentNode.isNull() )
1115 // we create popup menus without help of QtxPopupMgr
1116 QDomNode node = parentNode.firstChild();
1117 while ( !node.isNull() ) {
1118 if ( node.isElement() ) {
1119 QDomElement elem = node.toElement();
1120 QString aTagName = tagName( elem );
1121 if ( aTagName == "popup-item" ) {
1122 // insert a command item
1123 int id = checkInt( attribute( elem, "item-id" ) );
1124 int pos = checkInt( attribute( elem, "pos-id" ) );
1125 QString label = attribute( elem, "label-id" );
1126 QString icon = attribute( elem, "icon-id" );
1127 QString tooltip = attribute( elem, "tooltip-id" ); // not used
1128 QString accel = attribute( elem, "accel-id" );
1129 bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1130 QString execute = attribute( elem, "execute-action" ); // not used
1132 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1134 if ( !pixmap.isNull() )
1135 anIcon = QIconSet( pixmap );
1137 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1138 // also check if the action with given ID is already created
1140 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent() ), QKeySequence( accel ), id, pos );
1143 else if ( aTagName == "submenu" ) {
1145 int id = checkInt( attribute( elem, "item-id" ) );
1146 int pos = checkInt( attribute( elem, "pos-id" ) );
1147 QString label = attribute( elem, "label-id" );
1148 QString icon = attribute( elem, "icon-id" );
1150 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1152 if ( !pixmap.isNull() )
1153 anIcon = QIconSet( pixmap );
1155 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1156 menu->insertItem( anIcon, label, newPopup, id, pos );
1157 insertPopupItems( node, newPopup );
1159 else if ( aTagName == "separator" ) {
1160 // create menu separator
1161 int pos = checkInt( attribute( elem, "pos-id" ) );
1162 menu->insertSeparator( pos );
1165 node = node.nextSibling();