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 #if QT_VERSION > 0x030005
28 #include "sipAPISalomePyQtGUI.h"
30 #include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
33 #include <sipqtQWidget.h>
34 #include <sipqtQPopupMenu.h>
40 // Default name of the module, should be replaced at the moment
42 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
43 // Comment this define to block invoking of obsolete Python module's
44 // methods like setSetting(), definePopup(), etc.
45 #define __CALL_OLD_METHODS__
47 //=============================================================================
48 // General rule for Python requests created by SALOME_PYQT_Module:
49 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
50 // However, it is obligatory that ANY Python call is wrapped with a request object,
51 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
52 //=============================================================================
55 //=============================================================================
56 // The class for parsing of the XML resource files.
57 // Used for backward compatibility with existing Python modules.
58 //=============================================================================
59 class SALOME_PYQT_XmlHandler
62 SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
64 void createPopup ( QPopupMenu* menu,
65 const QString& context,
66 const QString& parent,
67 const QString& object );
70 void createToolBar ( QDomNode& parentNode );
71 void createMenu ( QDomNode& parentNode,
72 const int parentMenuId = -1 );
74 void insertPopupItems( QDomNode& parentNode,
78 SALOME_PYQT_Module* myModule;
82 //=============================================================================
83 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
84 // all Python-based SALOME module
85 //=============================================================================
87 // While the SalomePyQtGUI library is not imported in Python it's initialization function
88 // should be called manually (and only once) in order to initialize global sip data
89 #if defined(SIP_STATIC_MODULE)
90 extern "C" void initSalomePyQtGUI();
92 PyMODINIT_FUNC initSalomePyQtGUI();
96 * This function creates an instance of SALOME_PYQT_Module object by request
97 * of and application object when the module is loaded.
100 SALOME_PYQT_EXPORT CAM_Module* createModule() {
101 static bool alreadyInitialized = false;
102 if ( !alreadyInitialized ) {
103 // call only once (see above) !
105 alreadyInitialized = !alreadyInitialized;
107 return new SALOME_PYQT_Module();
112 * Static variables definition
114 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
115 SALOME_PYQT_Module* SALOME_PYQT_Module::myInitModule = 0;
118 * Little trick : provide an access to being activated Python module from outside;
119 * needed by the SalomePyQt library :(
121 SALOME_PYQT_Module* SALOME_PYQT_Module::getInitModule()
129 SALOME_PYQT_Module::SALOME_PYQT_Module() :
130 SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
132 myMenuActionList.setAutoDelete( false );
133 myPopupActionList.setAutoDelete( false );
134 myToolbarActionList.setAutoDelete( false );
140 SALOME_PYQT_Module::~SALOME_PYQT_Module()
142 myMenuActionList.clear();
143 myPopupActionList.clear();
144 myToolbarActionList.clear();
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 bool SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
212 MESSAGE( "SALOME_PYQT_Module::activateModule" );
214 bool res = SalomeApp_Module::activateModule( theStudy );
219 // ActivateReq: request class for internal activate() operation
220 class ActivateReq : public PyInterp_Request
223 ActivateReq( SUIT_Study* _study,
224 SALOME_PYQT_Module* _obj )
225 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
230 virtual void execute()
232 myObj->activate( myStudy );
237 SALOME_PYQT_Module* myObj;
240 // Posting the request
241 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
243 // activate menus, toolbars, etc
244 setMenuShown( true );
245 setToolShown( true );
251 * Deactivation of the module.
252 * Inherited from CAM_Module.
254 bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
256 MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
258 bool res = SalomeApp_Module::deactivateModule( theStudy );
260 // deactivate menus, toolbars, etc
261 setMenuShown( false );
262 setToolShown( false );
264 // DeactivateReq: request class for internal deactivate() operation
265 class DeactivateReq : public PyInterp_LockRequest
268 DeactivateReq( PyInterp_base* _py_interp,
270 SALOME_PYQT_Module* _obj )
271 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
276 virtual void execute()
278 myObj->deactivate( myStudy );
283 SALOME_PYQT_Module* myObj;
286 // Posting the request
287 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
293 * Processes GUI action (from main menu, toolbar or context popup menu)
295 void SALOME_PYQT_Module::onGUIEvent()
298 const QObject* obj = sender();
299 if ( !obj || !obj->inherits( "QAction" ) )
301 QAction* action = (QAction*)obj;
304 int id = actionId( action );
305 if ( myMenuActionList.contains( action ) )
306 id -= PYQT_ACTION_MENU;
307 if ( myToolbarActionList.contains( action ) )
308 id -= PYQT_ACTION_TOOLBAL;
309 if ( myPopupActionList.contains( action ) )
310 id -= PYQT_ACTION_POPUP;
311 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
313 // perform synchronous request to Python event dispatcher
314 class GUIEvent : public PyInterp_LockRequest
317 GUIEvent( PyInterp_base* _py_interp,
318 SALOME_PYQT_Module* _obj,
320 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
325 virtual void execute()
327 myObj->guiEvent( myId );
332 SALOME_PYQT_Module* myObj;
335 // Posting the request
336 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
340 Context popup menu request.
341 Called when user activates popup menu in some window (view, object browser, etc).
343 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
345 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
346 // perform synchronous request to Python event dispatcher
347 class PopupMenuEvent : public PyInterp_LockRequest
350 PopupMenuEvent( PyInterp_base* _py_interp,
351 SALOME_PYQT_Module* _obj,
352 const QString& _context,
354 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
355 myContext( _context ),
360 virtual void execute()
362 myObj->contextMenu( myContext, myPopup );
366 SALOME_PYQT_Module* myObj;
371 // Posting the request only if dispatcher is not busy!
372 // Executing the request synchronously
373 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
374 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
378 * Defines the dockable window associated with the module.
379 * To fill the list of windows the correspondind Python module's windows()
380 * method is called from SALOME_PYQT_Module::init() method.
381 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
383 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
385 // First clear the output parameters
386 QMap<int, int>::ConstIterator it;
387 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
388 mappa[ it.key() ] = it.data();
393 * Defines the compatible views which should be opened on module activation.
394 * To fill the list of views the correspondind Python module's views()
395 * method is called from SALOME_PYQT_Module::init() method.
396 * By default, the list is empty.
398 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
400 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
401 listik.append( *it );
406 * Performs internal initialization
407 * - initializes/gets the Python interpreter (one per study)
408 * - imports the Python module
409 * - passes the workspace widget to the Python module
410 * - calls Python module's initialize() method
411 * - calls Python module's windows() method
412 * - calls Python module's views() method
414 void SALOME_PYQT_Module::init( CAM_Application* app )
416 // reset interpreter to NULL
420 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
424 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
427 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
429 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
430 initInterp( aStudyId );
434 // import Python GUI module
441 #ifdef __CALL_OLD_METHODS__
442 // call Python module's setWorkspace() method
444 #endif // __CALL_OLD_METHODS__
446 // then call Python module's initialize() method
447 // ... first get python lock
448 PyLockWrapper aLock = myInterp->GetLockWrapper();
449 // ... (the Python module is already imported)
450 // ... finally call Python module's initialize() method
451 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
453 // VSR: this method may not be implemented in Python module
457 // get the windows list from the Python module by calling windows() method
458 // ... first put default values
459 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
460 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
461 // VSR: LogWindow is not yet implemented
462 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
464 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
466 // VSR: this method may not be implemented in Python module
470 myWindowsMap.clear();
471 if ( PyDict_Check( res1 ) ) {
475 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
476 // parse the return value
477 // it should be a map: {integer:integer}
479 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
480 aKey = PyInt_AsLong( key );
481 aValue = PyInt_AsLong( value );
482 myWindowsMap[ aKey ] = aValue;
487 // get the windows list from the Python module by calling views() method
488 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
490 // VSR: this method may not be implemented in Python module
494 // parse the return value
495 // result can be one string...
496 if ( PyString_Check( res2 ) ) {
497 myViewMgrList.append( PyString_AsString( res2 ) );
499 // ... or list of strings
500 else if ( PyList_Check( res2 ) ) {
501 int size = PyList_Size( res2 );
502 for ( int i = 0; i < size; i++ ) {
503 PyObject* value = PyList_GetItem( res2, i );
504 if( value && PyString_Check( value ) ) {
505 myViewMgrList.append( PyString_AsString( value ) );
514 * Performs internal activation:
515 * - initializes/gets the Python interpreter (one per study)
516 * - imports the Python GUI module
517 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
518 * or activate() method (for new modules)
520 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
523 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
524 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
526 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
527 initInterp( aStudyId );
531 // import Python GUI module
537 PyLockWrapper aLock = myInterp->GetLockWrapper();
539 #ifdef __CALL_OLD_METHODS__
540 // call Python module's setSettings() method (obsolete)
541 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
543 // VSR: this method may not be implemented in Python module
546 #endif // __CALL_OLD_METHODS__
548 // call Python module's activate() method (for the new modules)
549 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
551 // VSR: this method may not be implemented in Python module
557 * Performs internal deactivation:
558 * - calls Python module's deactivate() method
560 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
562 // check if the subinterpreter is initialized and Python module is imported
563 if ( !myInterp || !myModule ) {
564 // Error! Python subinterpreter should be initialized and module should be imported first!
567 // then call Python module's deactivate() method
568 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
570 // VSR: this method may not be implemented in Python module
576 * Called when active the study is actived (user brings its desktop to top)
577 * - initializes/gets the Python interpreter (one per study)
578 * - imports the Python GUI module
579 * - calls Python module's activeStudyChanged() method
581 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
584 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
585 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
587 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
588 initInterp( aStudyId );
592 // import Python GUI module
598 PyLockWrapper aLock = myInterp->GetLockWrapper();
600 // call Python module's activeStudyChanged() method
601 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
603 // VSR: this method may not be implemented in Python module
609 * Get module engine, returns nil var if engine is not found in LifeCycleCORBA
611 Engines::Component_var SALOME_PYQT_Module::getEngine() const
613 Engines::Component_var comp;
614 // temporary solution
616 comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name( "" ) );
618 catch (CORBA::Exception&) {
624 * Get module engine IOR, returns empty string if engine is not found in LifeCycleCORBA
626 QString SALOME_PYQT_Module::engineIOR() const
628 if ( !CORBA::is_nil( getEngine() ) )
629 return QString( getApp()->orb()->object_to_string( getEngine() ) );
630 return QString( "" );
634 * Called when study desktop is activated.
635 * Used for notifying about changing of the active study.
637 void SALOME_PYQT_Module::studyActivated()
639 // StudyChangedReq: request class for internal studyChanged() operation
640 class StudyChangedReq : public PyInterp_Request
643 StudyChangedReq( SUIT_Study* _study,
644 SALOME_PYQT_Module* _obj )
645 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
650 virtual void execute()
652 myObj->studyChanged( myStudy );
657 SALOME_PYQT_Module* myObj;
660 // Posting the request
661 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
665 * Processes context popup menu request
666 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
667 * to define the popup menu context
668 * - parses XML resourses file (if exists) and fills the popup menu with the items)
669 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
670 * to allow module to customize the popup menu
671 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
672 * by using insertItem(...) Qt functions.
674 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
676 // Python interpreter should be initialized and Python module should be
678 if ( !myInterp || !myModule )
681 QString aContext( theContext ), aObject( "" ), aParent( "" );
682 #ifdef __CALL_OLD_METHODS__
683 // call definePopup() Python module's function
684 // this is obsolete function, used only for compatibility reasons
685 PyObjWrapper res(PyObject_CallMethod( myModule,
690 aParent.latin1() ) );
692 // VSR: this method may not be implemented in Python module
696 // parse return value
698 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
704 #endif // __CALL_OLD_METHODS__
706 // first try to create menu via XML parser:
707 // we create popup menus without help of QtxPopupMgr
709 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
711 PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QPopupMenu ) );
712 //PyObjWrapper sipPopup( sipMapCppToSelf( thePopupMenu, sipClass_QPopupMenu ) );
714 // then call Python module's createPopupMenu() method (for new modules)
715 PyObjWrapper res1( PyObject_CallMethod( myModule,
719 aContext.latin1() ) );
721 // VSR: this method may not be implemented in Python module
725 #ifdef __CALL_OLD_METHODS__
726 // call customPopup() Python module's function
727 // this is obsolete function, used only for compatibility reasons
728 PyObjWrapper res2( PyObject_CallMethod( myModule,
734 aParent.latin1() ) );
736 // VSR: this method may not be implemented in Python module
739 #endif // __CALL_OLD_METHODS__
743 * Processes GUI event
744 * - calls Python module's OnGUIEvent() method
746 void SALOME_PYQT_Module::guiEvent( const int theId )
748 // Python interpreter should be initialized and Python module should be
750 if ( !myInterp || !myModule )
753 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
761 * Initialises python subinterpreter (one per study)
763 void SALOME_PYQT_Module::initInterp( int theStudyId )
767 // Error! Study Id must not be 0!
771 // try to find the subinterpreter
772 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
774 myInterp = myInterpMap[ theStudyId ];
777 // not found - create a new one!
778 ///////////////////////////////////////////////////////////////////
779 // Attention: the creation of Python interpretor must be protected
780 // by a C++ Lock because of C threads
781 ///////////////////////////////////////////////////////////////////
782 myInterp = new SALOME_PYQT_PyInterp();
783 myInterp->initialize();
784 myInterpMap[ theStudyId ] = myInterp;
786 // import 'salome' module and call 'salome_init' method;
787 // do it only once on interpreter creation
788 // ... first get python lock
789 PyLockWrapper aLock = myInterp->GetLockWrapper();
790 // ... then import a module
791 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
797 // ... then call a method
798 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
807 * Imports Python GUI module and remember the reference to the module
808 * !!! initInterp() should be called first!!!
810 void SALOME_PYQT_Module::importModule()
812 // check if the subinterpreter is initialized
814 // Error! Python subinterpreter should be initialized first!
818 // import Python GUI module and puts it in <myModule> attribute
819 // ... first get python lock
820 PyLockWrapper aLock = myInterp->GetLockWrapper();
821 // ... then import a module
822 QString aMod = QString( name("") ) + "GUI";
823 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
832 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
834 * !!! initInterp() and importModule() should be called first!!!
836 void SALOME_PYQT_Module::setWorkSpace()
838 // check if the subinterpreter is initialized and Python module is imported
839 if ( !myInterp || !myModule ) {
840 // Error! Python subinterpreter should be initialized and module should be imported first!
844 // call setWorkspace() method
845 // ... first get python lock
846 PyLockWrapper aLock = myInterp->GetLockWrapper();
848 // ... then try to import SalomePyQt module. If it's not possible don't go on.
849 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
855 #ifdef __CALL_OLD_METHODS__
856 // ... then get workspace object
857 QWidget* aWorkspace = 0;
858 if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
859 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
861 aWorkspace = aDesktop->workspace();
863 else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
864 STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
866 aWorkspace = aDesktop->workstack();
868 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget ) );
869 //PyObjWrapper pyws( sipMapCppToSelfSubClass( aWorkspace, sipClass_QWidget ) );
870 // ... and finally call Python module's setWorkspace() method (obsolete)
871 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
873 // VSR: this method may not be implemented in Python module
877 #endif // __CALL_OLD_METHODS__
881 * Adds an action into private action list [internal usage]
883 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
886 case PYQT_ACTION_MENU:
887 myMenuActionList.append( action );
889 case PYQT_ACTION_TOOLBAL:
890 myToolbarActionList.append( action );
892 case PYQT_ACTION_POPUP:
893 myPopupActionList.append( action );
900 * The next methods just call the parent implementation.
901 * This is done to open protected methods from CAM_Module class.
903 int SALOME_PYQT_Module::createTool( const QString& name )
905 return SalomeApp_Module::createTool( name );
907 int SALOME_PYQT_Module::createTool( const int id, const int tBar, const int idx )
909 return SalomeApp_Module::createTool( id, tBar, idx );
911 int SALOME_PYQT_Module::createTool( const int id, const QString& tBar, const int idx )
913 return SalomeApp_Module::createTool( id, tBar, idx );
915 int SALOME_PYQT_Module::createTool( QAction* a, const int tBar, const int id, const int idx )
917 return SalomeApp_Module::createTool( a, tBar, id, idx );
919 int SALOME_PYQT_Module::createTool( QAction* a, const QString& tBar, const int id, const int idx )
921 return SalomeApp_Module::createTool( a, tBar, id, idx );
923 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
925 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
927 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
929 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
931 int SALOME_PYQT_Module::createMenu( const int id, const int menu, const int group, const int idx )
933 return SalomeApp_Module::createMenu( id, menu, group, idx );
935 int SALOME_PYQT_Module::createMenu( const int id, const QString& menu, const int group, const int idx )
937 return SalomeApp_Module::createMenu( id, menu, group, idx );
939 int SALOME_PYQT_Module::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
941 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
943 int SALOME_PYQT_Module::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
945 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
947 QAction* SALOME_PYQT_Module::createSeparator()
949 return SalomeApp_Module::separator();
951 QAction* SALOME_PYQT_Module::action( const int id ) const
953 return SalomeApp_Module::action( id );
955 int SALOME_PYQT_Module::actionId( const QAction* a ) const
957 return SalomeApp_Module::actionId( a );
959 QAction* SALOME_PYQT_Module::createAction( const int id, const QString& text, const QString& icon,
960 const QString& menu, const QString& tip, const int key,
964 if ( !icon.isEmpty() ) {
965 QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(""), tr( icon ) );
966 if ( !pixmap.isNull() )
967 anIcon = QIconSet( pixmap );
969 return SalomeApp_Module::createAction( id, text, anIcon, menu, tip, key, getApp()->desktop(), toggle, this, SLOT( onGUIEvent() ) );
973 //=============================================================================
974 // SALOME_PYQT_XmlHandler class implementation
975 //=============================================================================
977 // gets an tag name for the dom element [ static ]
978 // returns an empty string if the element does not have tag name
979 static QString tagName( const QDomElement& element ) {
980 return element.tagName().stripWhiteSpace();
983 // gets an attribute by it's name for the dom element [ static ]
984 // returns an empty string if the element does not have such attribute
985 static QString attribute( const QDomElement& element, const QString& attName ) {
986 return element.attribute( attName ).stripWhiteSpace();
989 // checks the given value for the boolean value [ static ]
990 // returns TRUE if string is "true", "yes" or "1"
991 static bool checkBool( const QString& value ) {
992 return ( value == "true" || value == "yes" || value == "1" );
995 // checks the given value for the integer value [ static ]
996 // returns -1 if item is empty or presents and invalid number
997 static int checkInt( const QString& value )
999 return value.isEmpty() ? -1 : value.toInt();
1005 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
1006 : myModule( module )
1008 QFile aFile( fileName );
1009 if ( !aFile.open( IO_ReadOnly ) )
1011 if ( !myDoc.setContent( &aFile ) ) {
1019 Called by SALOME_PYQT_Module::initialize() in order to create actions
1020 (menus, toolbars, popup menus)
1022 void SALOME_PYQT_XmlHandler::createActions()
1024 // get document element
1025 QDomElement aDocElem = myDoc.documentElement();
1027 // get main menu actions
1028 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
1029 for ( int i = 0; i < aMenuList.count(); i++ ) {
1030 QDomNode n = aMenuList.item( i );
1034 // create toolbars actions
1035 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
1036 for ( int i = 0; i < aToolsList.count(); i++ ) {
1037 QDomNode n = aToolsList.item( i );
1043 * Creates popup menu
1045 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
1046 const QString& context,
1047 const QString& parent,
1048 const QString& object )
1050 // get document element
1051 QDomElement aDocElem = myDoc.documentElement();
1053 // get popup menus actions
1054 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
1055 for ( int i = 0; i < aPopupList.count(); i++ ) {
1056 QDomNode n = aPopupList.item( i );
1057 if ( !n.isNull() && n.isElement() ) {
1058 QDomElement e = n.toElement();
1059 QString lab = attribute( e, "label-id" );
1060 QString ctx = attribute( e, "context-id" );
1061 QString prt = attribute( e, "parent-id" );
1062 QString obj = attribute( e, "object-id" );
1063 if ( ctx == context && prt == parent && obj == object ) {
1064 insertPopupItems( n, menu );
1072 Create main menu with child actions
1074 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
1079 if ( parentNode.isNull() )
1082 QDomElement parentElement = parentNode.toElement();
1083 if ( !parentElement.isNull() ) {
1084 QString plabel = attribute( parentElement, "label-id" );
1085 int pid = checkInt( attribute( parentElement, "item-id" ) );
1086 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
1087 if ( !plabel.isEmpty() ) {
1089 int menuId = myModule->createMenu( plabel, // label
1090 parentMenuId, // parent menu ID, should be -1 for main menu
1094 QDomNode node = parentNode.firstChild();
1095 while ( !node.isNull() ) {
1096 if ( node.isElement() ) {
1097 QDomElement elem = node.toElement();
1098 QString aTagName = tagName( elem );
1099 if ( aTagName == "popup-item" ) {
1100 int id = checkInt( attribute( elem, "item-id" ) );
1101 int pos = checkInt( attribute( elem, "pos-id" ) );
1102 QString label = attribute( elem, "label-id" );
1103 QString icon = attribute( elem, "icon-id" );
1104 QString tooltip = attribute( elem, "tooltip-id" );
1105 QString accel = attribute( elem, "accel-id" );
1106 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1107 ////QString execute = attribute( elem, "execute-action" ); // not used
1109 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1110 // also check if the action with given ID is already created
1111 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1112 // little trick to have several actions with same ID for menus and toolbars
1113 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1114 // create menu action
1115 QAction* action = myModule->createAction( id, // ID
1119 tooltip, // status-bar text
1120 QKeySequence( accel ), // keyboard accelerator
1121 toggle ); // toogled action
1122 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1123 myModule->createMenu( action, menuId, -1, 100, pos );
1126 else if ( aTagName == "submenu" ) {
1128 createMenu( node, menuId );
1130 else if ( aTagName == "separator" ) {
1131 // create menu separator
1132 int pos = checkInt( attribute( elem, "pos-id" ) );
1133 QAction* action = myModule->separator();
1134 myModule->createMenu( action, menuId, -1, 100, pos );
1137 node = node.nextSibling();
1144 Create a toolbar with child actions
1146 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1151 if ( parentNode.isNull() )
1154 QDomElement parentElement = parentNode.toElement();
1155 if ( !parentElement.isNull() ) {
1156 QString aLabel = attribute( parentElement, "label-id" );
1157 if ( !aLabel.isEmpty() ) {
1159 int tbId = myModule->createTool( aLabel );
1160 QDomNode node = parentNode.firstChild();
1161 while ( !node.isNull() ) {
1162 if ( node.isElement() ) {
1163 QDomElement elem = node.toElement();
1164 QString aTagName = tagName( elem );
1165 if ( aTagName == "toolbutton-item" ) {
1166 int id = checkInt( attribute( elem, "item-id" ) );
1167 int pos = checkInt( attribute( elem, "pos-id" ) );
1168 QString label = attribute( elem, "label-id" );
1169 QString icon = attribute( elem, "icon-id" );
1170 QString tooltip = attribute( elem, "tooltip-id" );
1171 QString accel = attribute( elem, "accel-id" );
1172 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1173 ////QString execute = attribute( elem, "execute-action" ); // not used
1175 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1176 // also check if the action with given ID is already created
1177 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1178 // little trick to have several actions with same ID for menus and toolbars
1179 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1180 // create toolbar action
1181 QAction* action = myModule->createAction( id, // ID
1185 tooltip, // status-bar text
1186 QKeySequence( accel ), // keyboard accelerator
1187 toggle ); // toogled action
1188 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1189 myModule->createTool( action, tbId, -1, pos );
1192 else if ( aTagName == "separatorTB" ) {
1193 // create toolbar separator
1194 int pos = checkInt( attribute( elem, "pos-id" ) );
1195 QAction* action = myModule->separator();
1196 myModule->createTool( action, tbId, -1, pos );
1199 node = node.nextSibling();
1205 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1210 if ( parentNode.isNull() )
1213 // we create popup menus without help of QtxPopupMgr
1214 QDomNode node = parentNode.firstChild();
1215 while ( !node.isNull() ) {
1216 if ( node.isElement() ) {
1217 QDomElement elem = node.toElement();
1218 QString aTagName = tagName( elem );
1219 if ( aTagName == "popup-item" ) {
1220 // insert a command item
1221 int id = checkInt( attribute( elem, "item-id" ) );
1222 int pos = checkInt( attribute( elem, "pos-id" ) );
1223 QString label = attribute( elem, "label-id" );
1224 QString icon = attribute( elem, "icon-id" );
1225 /////QString tooltip = attribute( elem, "tooltip-id" ); // not used
1226 QString accel = attribute( elem, "accel-id" );
1227 /////bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1228 /////QString execute = attribute( elem, "execute-action" ); // not used
1231 if ( !icon.isEmpty() ) {
1232 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1233 if ( !pixmap.isNull() )
1234 anIcon = QIconSet( pixmap );
1237 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1238 // also check if the action with given ID is already created
1240 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent() ), QKeySequence( accel ), id, pos );
1243 else if ( aTagName == "submenu" ) {
1245 int id = checkInt( attribute( elem, "item-id" ) );
1246 int pos = checkInt( attribute( elem, "pos-id" ) );
1247 QString label = attribute( elem, "label-id" );
1248 QString icon = attribute( elem, "icon-id" );
1251 if ( !icon.isEmpty() ) {
1252 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1253 if ( !pixmap.isNull() )
1254 anIcon = QIconSet( pixmap );
1257 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1258 menu->insertItem( anIcon, label, newPopup, id, pos );
1259 insertPopupItems( node, newPopup );
1261 else if ( aTagName == "separator" ) {
1262 // create menu separator
1263 int pos = checkInt( attribute( elem, "pos-id" ) );
1264 menu->insertSeparator( pos );
1267 node = node.nextSibling();