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>
21 #include <Container_init_python.hxx>
25 #include <qworkspace.h>
26 #include <qpopupmenu.h>
28 #include "SALOME_PYQT_SipDefs.h"
29 #if defined(SIP_VERS_v4_old) || defined(SIP_VERS_v4_new)
30 #include "sipAPISalomePyQtGUI.h"
32 #include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
35 #include <sipqtQWidget.h>
36 #include <sipqtQPopupMenu.h>
42 ///////////////////////////////////////////////////////////////////////////////
43 // Default name of the module, replaced at the moment of module creation
44 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
46 ///////////////////////////////////////////////////////////////////////////////
47 // If __CALL_OLD_METHODS__ macro is not defined the invoking of obsolete Python
48 // module's methods like setSetting(), definePopup(), etc. is blocked.
49 // This macro is defined by default (in Makefile)
50 #ifdef __CALL_OLD_METHODS__
51 const bool IsCallOldMethods = true;
53 const bool IsCallOldMethods = false;
56 ///////////////////////////////////////////////////////////////////////////////
57 // NB: Python requests.
58 // General rule for Python requests created by SALOME_PYQT_Module:
59 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
60 // However, it is obligatory that ANY Python call is wrapped with a request object,
61 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
62 ///////////////////////////////////////////////////////////////////////////////
64 //=============================================================================
65 // The class for parsing of the XML resource files.
66 // Used for backward compatibility with existing Python modules.
67 //=============================================================================
68 class SALOME_PYQT_XmlHandler
71 SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
73 void createPopup ( QPopupMenu* menu,
74 const QString& context,
75 const QString& parent,
76 const QString& object );
79 void createToolBar ( QDomNode& parentNode );
80 void createMenu ( QDomNode& parentNode,
81 const int parentMenuId = -1 );
83 void insertPopupItems( QDomNode& parentNode,
87 SALOME_PYQT_Module* myModule;
91 //=============================================================================
92 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
93 // all Python-based SALOME module
94 //=============================================================================
96 // While the SalomePyQtGUI library is not imported in Python it's initialization function
97 // should be called manually (and only once) in order to initialize global sip data
98 // and to get C API from sip : sipBuildResult for example
99 #if defined(SIP_STATIC_MODULE)
100 extern "C" void initSalomePyQtGUI();
102 PyMODINIT_FUNC initSalomePyQtGUI();
106 * This function creates an instance of SALOME_PYQT_Module object by request
107 * of and application object when the module is loaded.
110 SALOME_PYQT_EXPORT CAM_Module* createModule() {
111 static bool alreadyInitialized = false;
112 if ( !alreadyInitialized ) {
113 // call only once (see above) !
114 PyEval_RestoreThread( KERNEL_PYTHON::_gtstate );
116 PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
117 alreadyInitialized = !alreadyInitialized;
119 return new SALOME_PYQT_Module();
124 * Static variables definition
126 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
127 SALOME_PYQT_Module* SALOME_PYQT_Module::myInitModule = 0;
130 * Little trick : provide an access to being activated Python module from outside;
131 * needed by the SalomePyQt library :(
133 SALOME_PYQT_Module* SALOME_PYQT_Module::getInitModule()
141 SALOME_PYQT_Module::SALOME_PYQT_Module() :
142 SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
144 myMenuActionList.setAutoDelete( false );
145 myPopupActionList.setAutoDelete( false );
146 myToolbarActionList.setAutoDelete( false );
152 SALOME_PYQT_Module::~SALOME_PYQT_Module()
154 myMenuActionList.clear();
155 myPopupActionList.clear();
156 myToolbarActionList.clear();
162 * Initialization of the module.
163 * Inherited from CAM_Module.
165 * This method is used for creation of the menus, toolbars and other staff.
166 * There are two ways:
167 * - for obsolete modules this method first tries to read <module>_<language>.xml
168 * resource file which contains a menu, toolbars and popup menus description.
169 * - new modules can create menus by by calling the corresponding methods of SalomePyQt
170 * Python API in the Python module's initialize() method which is called from here.
171 * NOTE: if postponed modules loading is not used, the active study might be not defined
172 * yet at this stage, so initialize() method should not perform any study-based initialization.
174 void SALOME_PYQT_Module::initialize( CAM_Application* app )
176 SalomeApp_Module::initialize( app );
178 // Try to get XML resource file name
179 SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
180 QString aLang = aResMgr->stringValue( "language", "language", QString::null );
181 if ( aLang.isEmpty() ) aLang = QString( "en" );
182 QString aName = name( "" );
183 QString aFileName = aName + "_" + aLang + ".xml";
184 aFileName = aResMgr->path( "resources", aName, aFileName );
186 // parse XML file if it is found and create actions
187 if ( !myXmlHandler && !aFileName.isEmpty() ) {
188 myXmlHandler = new SALOME_PYQT_XmlHandler( this, aFileName );
189 myXmlHandler->createActions();
192 // perform internal initialization and call module's initialize() method
193 // InitializeReq: request class for internal init() operation
194 class InitializeReq : public PyInterp_Request
197 InitializeReq( CAM_Application* _app,
198 SALOME_PYQT_Module* _obj )
199 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
204 virtual void execute()
206 myObj->init( myApp );
210 CAM_Application* myApp;
211 SALOME_PYQT_Module* myObj;
214 // Posting the request
215 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
219 * Activation of the module.
220 * Inherited from CAM_Module.
222 bool SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
224 MESSAGE( "SALOME_PYQT_Module::activateModule" );
226 bool res = SalomeApp_Module::activateModule( theStudy );
231 // ActivateReq: request class for internal activate() operation
232 class ActivateReq : public PyInterp_Request
235 ActivateReq( SUIT_Study* _study,
236 SALOME_PYQT_Module* _obj )
237 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
242 virtual void execute()
244 myObj->activate( myStudy );
249 SALOME_PYQT_Module* myObj;
252 // Posting the request
253 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
255 // activate menus, toolbars, etc
256 setMenuShown( true );
257 setToolShown( true );
263 * Deactivation of the module.
264 * Inherited from CAM_Module.
266 bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
268 MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
270 bool res = SalomeApp_Module::deactivateModule( theStudy );
272 // deactivate menus, toolbars, etc
273 setMenuShown( false );
274 setToolShown( false );
276 // DeactivateReq: request class for internal deactivate() operation
277 class DeactivateReq : public PyInterp_LockRequest
280 DeactivateReq( PyInterp_base* _py_interp,
282 SALOME_PYQT_Module* _obj )
283 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
288 virtual void execute()
290 myObj->deactivate( myStudy );
295 SALOME_PYQT_Module* myObj;
298 // Posting the request
299 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
305 * Processes GUI action (from main menu, toolbar or context popup menu)
307 void SALOME_PYQT_Module::onGUIEvent()
310 const QObject* obj = sender();
311 if ( !obj || !obj->inherits( "QAction" ) )
313 QAction* action = (QAction*)obj;
316 int id = actionId( action );
317 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
319 // perform synchronous request to Python event dispatcher
320 class GUIEvent : public PyInterp_LockRequest
323 GUIEvent( PyInterp_base* _py_interp,
324 SALOME_PYQT_Module* _obj,
326 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
331 virtual void execute()
333 myObj->guiEvent( myId );
338 SALOME_PYQT_Module* myObj;
341 // Posting the request
342 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
346 * Processes GUI action (from context popup menu, only for XML-based actions!)
348 void SALOME_PYQT_Module::onGUIEvent( int id )
350 // perform synchronous request to Python event dispatcher
351 class GUIEvent : public PyInterp_LockRequest
354 GUIEvent( PyInterp_base* _py_interp,
355 SALOME_PYQT_Module* _obj,
357 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
362 virtual void execute()
364 myObj->guiEvent( myId );
369 SALOME_PYQT_Module* myObj;
372 // Posting the request
373 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
377 Context popup menu request.
378 Called when user activates popup menu in some window (view, object browser, etc).
380 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
382 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
383 // perform synchronous request to Python event dispatcher
384 class PopupMenuEvent : public PyInterp_LockRequest
387 PopupMenuEvent( PyInterp_base* _py_interp,
388 SALOME_PYQT_Module* _obj,
389 const QString& _context,
391 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
392 myContext( _context ),
397 virtual void execute()
399 myObj->contextMenu( myContext, myPopup );
403 SALOME_PYQT_Module* myObj;
408 // Posting the request only if dispatcher is not busy!
409 // Executing the request synchronously
410 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
411 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
415 * Defines the dockable window associated with the module.
416 * To fill the list of windows the correspondind Python module's windows()
417 * method is called from SALOME_PYQT_Module::init() method.
418 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
420 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
422 // First clear the output parameters
423 QMap<int, int>::ConstIterator it;
424 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
425 mappa[ it.key() ] = it.data();
430 * Defines the compatible views which should be opened on module activation.
431 * To fill the list of views the correspondind Python module's views()
432 * method is called from SALOME_PYQT_Module::init() method.
433 * By default, the list is empty.
435 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
437 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
438 listik.append( *it );
443 * Performs internal initialization
444 * - initializes/gets the Python interpreter (one per study)
445 * - imports the Python module
446 * - passes the workspace widget to the Python module
447 * - calls Python module's initialize() method
448 * - calls Python module's windows() method
449 * - calls Python module's views() method
451 void SALOME_PYQT_Module::init( CAM_Application* app )
453 // reset interpreter to NULL
457 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
461 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
464 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
466 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
467 initInterp( aStudyId );
471 // import Python GUI module
478 if ( IsCallOldMethods ) { // __CALL_OLD_METHODS__
479 // call Python module's setWorkspace() method
481 } //__CALL_OLD_METHODS__
483 // then call Python module's initialize() method
484 // ... first get python lock
485 PyLockWrapper aLock = myInterp->GetLockWrapper();
486 // ... (the Python module is already imported)
487 // ... finally call Python module's initialize() method
488 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
490 // VSR: this method may not be implemented in Python module
495 // get the windows list from the Python module by calling windows() method
496 // ... first put default values
497 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
498 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
499 // VSR: LogWindow is not yet implemented
500 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
502 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
504 // VSR: this method may not be implemented in Python module
509 myWindowsMap.clear();
510 if ( PyDict_Check( res1 ) ) {
514 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
515 // parse the return value
516 // it should be a map: {integer:integer}
518 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
519 aKey = PyInt_AsLong( key );
520 aValue = PyInt_AsLong( value );
521 myWindowsMap[ aKey ] = aValue;
526 // get the windows list from the Python module by calling views() method
527 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
529 // VSR: this method may not be implemented in Python module
534 // parse the return value
535 // result can be one string...
536 if ( PyString_Check( res2 ) ) {
537 myViewMgrList.append( PyString_AsString( res2 ) );
539 // ... or list of strings
540 else if ( PyList_Check( res2 ) ) {
541 int size = PyList_Size( res2 );
542 for ( int i = 0; i < size; i++ ) {
543 PyObject* value = PyList_GetItem( res2, i );
544 if( value && PyString_Check( value ) ) {
545 myViewMgrList.append( PyString_AsString( value ) );
554 * Performs internal activation:
555 * - initializes/gets the Python interpreter (one per study)
556 * - imports the Python GUI module
557 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
558 * or activate() method (for new modules)
560 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
563 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
564 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
566 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
567 initInterp( aStudyId );
571 // import Python GUI module
577 PyLockWrapper aLock = myInterp->GetLockWrapper();
579 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
580 // call Python module's setSettings() method (obsolete)
581 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
583 // VSR: this method may not be implemented in Python module
587 } //__CALL_OLD_METHODS__
589 // call Python module's activate() method (for the new modules)
590 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
592 // VSR: this method may not be implemented in Python module
599 * Performs internal deactivation:
600 * - calls Python module's deactivate() method
602 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
604 // check if the subinterpreter is initialized and Python module is imported
605 if ( !myInterp || !myModule ) {
606 // Error! Python subinterpreter should be initialized and module should be imported first!
609 // then call Python module's deactivate() method
610 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
612 // VSR: this method may not be implemented in Python module
619 * Called when active the study is actived (user brings its desktop to top)
620 * - initializes/gets the Python interpreter (one per study)
621 * - imports the Python GUI module
622 * - calls Python module's activeStudyChanged() method
624 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
627 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
628 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
630 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
631 initInterp( aStudyId );
635 // import Python GUI module
641 PyLockWrapper aLock = myInterp->GetLockWrapper();
643 // call Python module's activeStudyChanged() method
644 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
646 // VSR: this method may not be implemented in Python module
653 * Get module engine, returns nil var if engine is not found in LifeCycleCORBA
655 Engines::Component_var SALOME_PYQT_Module::getEngine() const
657 Engines::Component_var comp;
658 // temporary solution
660 comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name( "" ) );
662 catch (CORBA::Exception&) {
668 * Get module engine IOR, returns empty string if engine is not found in LifeCycleCORBA
670 QString SALOME_PYQT_Module::engineIOR() const
672 if ( !CORBA::is_nil( getEngine() ) )
673 return QString( getApp()->orb()->object_to_string( getEngine() ) );
674 return QString( "" );
678 * Called when study desktop is activated.
679 * Used for notifying about changing of the active study.
681 void SALOME_PYQT_Module::studyActivated()
683 // StudyChangedReq: request class for internal studyChanged() operation
684 class StudyChangedReq : public PyInterp_Request
687 StudyChangedReq( SUIT_Study* _study,
688 SALOME_PYQT_Module* _obj )
689 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
694 virtual void execute()
696 myObj->studyChanged( myStudy );
701 SALOME_PYQT_Module* myObj;
704 // Posting the request
705 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
709 * Processes context popup menu request
710 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
711 * to define the popup menu context
712 * - parses XML resourses file (if exists) and fills the popup menu with the items)
713 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
714 * to allow module to customize the popup menu
715 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
716 * by using insertItem(...) Qt functions.
718 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
720 // Python interpreter should be initialized and Python module should be
722 if ( !myInterp || !myModule )
725 QString aContext( theContext ), aObject( "" ), aParent( "" );
727 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
728 // call definePopup() Python module's function
729 // this is obsolete function, used only for compatibility reasons
730 PyObjWrapper res(PyObject_CallMethod( myModule,
735 aParent.latin1() ) );
737 // VSR: this method may not be implemented in Python module
742 // parse return value
744 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
750 } //__CALL_OLD_METHODS__
752 // first try to create menu via XML parser:
753 // we create popup menus without help of QtxPopupMgr
755 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
757 PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QPopupMenu ) );
759 // then call Python module's createPopupMenu() method (for new modules)
760 PyObjWrapper res1( PyObject_CallMethod( myModule,
764 aContext.latin1() ) );
766 // VSR: this method may not be implemented in Python module
771 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
772 // call customPopup() Python module's function
773 // this is obsolete function, used only for compatibility reasons
774 PyObjWrapper res2( PyObject_CallMethod( myModule,
780 aParent.latin1() ) );
782 // VSR: this method may not be implemented in Python module
786 } //__CALL_OLD_METHODS__
790 * Processes GUI event
791 * - calls Python module's OnGUIEvent() method
793 void SALOME_PYQT_Module::guiEvent( const int theId )
795 // Python interpreter should be initialized and Python module should be
797 if ( !myInterp || !myModule )
800 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
802 // VSR: this method may not be implemented in Python module
809 * Initialises python subinterpreter (one per study)
811 void SALOME_PYQT_Module::initInterp( int theStudyId )
815 // Error! Study Id must not be 0!
819 // try to find the subinterpreter
820 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
822 myInterp = myInterpMap[ theStudyId ];
825 // not found - create a new one!
826 ///////////////////////////////////////////////////////////////////
827 // Attention: the creation of Python interpretor must be protected
828 // by a C++ Lock because of C threads
829 ///////////////////////////////////////////////////////////////////
830 myInterp = new SALOME_PYQT_PyInterp();
831 myInterp->initialize();
832 myInterpMap[ theStudyId ] = myInterp;
834 // import 'salome' module and call 'salome_init' method;
835 // do it only once on interpreter creation
836 // ... first get python lock
837 PyLockWrapper aLock = myInterp->GetLockWrapper();
838 // ... then import a module
839 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
845 // ... then call a method
846 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
855 * Imports Python GUI module and remember the reference to the module
856 * !!! initInterp() should be called first!!!
858 void SALOME_PYQT_Module::importModule()
860 // check if the subinterpreter is initialized
862 // Error! Python subinterpreter should be initialized first!
866 // import Python GUI module and puts it in <myModule> attribute
867 // ... first get python lock
868 PyLockWrapper aLock = myInterp->GetLockWrapper();
869 // ... then import a module
870 QString aMod = QString( name("") ) + "GUI";
871 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
880 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
882 * !!! initInterp() and importModule() should be called first!!!
884 void SALOME_PYQT_Module::setWorkSpace()
886 // check if the subinterpreter is initialized and Python module is imported
887 if ( !myInterp || !myModule ) {
888 // Error! Python subinterpreter should be initialized and module should be imported first!
892 // call setWorkspace() method
893 // ... first get python lock
894 PyLockWrapper aLock = myInterp->GetLockWrapper();
896 // ... then try to import SalomePyQt module. If it's not possible don't go on.
897 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
904 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
905 // ... then get workspace object
906 QWidget* aWorkspace = 0;
907 if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
908 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
910 aWorkspace = aDesktop->workspace();
912 else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
913 STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
915 aWorkspace = aDesktop->workstack();
917 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget ) );
918 // ... and finally call Python module's setWorkspace() method (obsolete)
919 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
921 // VSR: this method may not be implemented in Python module
925 } //__CALL_OLD_METHODS__
929 * Adds an action into private action list [internal usage]
931 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
934 case PYQT_ACTION_MENU:
935 myMenuActionList.append( action );
937 case PYQT_ACTION_TOOLBAL:
938 myToolbarActionList.append( action );
940 case PYQT_ACTION_POPUP:
941 myPopupActionList.append( action );
948 * The next methods just call the parent implementation.
949 * This is done to open protected methods from CAM_Module class.
951 int SALOME_PYQT_Module::createTool( const QString& name )
953 return SalomeApp_Module::createTool( name );
955 int SALOME_PYQT_Module::createTool( const int id, const int tBar, const int idx )
957 return SalomeApp_Module::createTool( id, tBar, idx );
959 int SALOME_PYQT_Module::createTool( const int id, const QString& tBar, const int idx )
961 return SalomeApp_Module::createTool( id, tBar, idx );
963 int SALOME_PYQT_Module::createTool( QAction* a, const int tBar, const int id, const int idx )
965 return SalomeApp_Module::createTool( a, tBar, id, idx );
967 int SALOME_PYQT_Module::createTool( QAction* a, const QString& tBar, const int id, const int idx )
969 return SalomeApp_Module::createTool( a, tBar, id, idx );
971 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
973 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
975 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
977 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
979 int SALOME_PYQT_Module::createMenu( const int id, const int menu, const int group, const int idx )
981 return SalomeApp_Module::createMenu( id, menu, group, idx );
983 int SALOME_PYQT_Module::createMenu( const int id, const QString& menu, const int group, const int idx )
985 return SalomeApp_Module::createMenu( id, menu, group, idx );
987 int SALOME_PYQT_Module::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
989 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
991 int SALOME_PYQT_Module::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
993 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
995 QAction* SALOME_PYQT_Module::createSeparator()
997 return SalomeApp_Module::separator();
999 QAction* SALOME_PYQT_Module::action( const int id ) const
1001 QAction* a = SalomeApp_Module::action( id );
1002 if ( !a ) // try own action map for menu items
1003 a = SalomeApp_Module::action( id + PYQT_ACTION_MENU );
1004 if ( !a ) // try own action map for toolbar items
1005 a = SalomeApp_Module::action( id + PYQT_ACTION_TOOLBAL );
1006 if ( !a ) // try own action map for popup items
1007 a = SalomeApp_Module::action( id + PYQT_ACTION_POPUP );
1010 int SALOME_PYQT_Module::actionId( const QAction* a ) const
1012 int id = SalomeApp_Module::actionId( a );
1013 if ( myMenuActionList.contains( a ) ) // check own action map for menu items
1014 id -= PYQT_ACTION_MENU;
1015 if ( myToolbarActionList.contains( a ) ) // check own action map for toolbar items
1016 id -= PYQT_ACTION_TOOLBAL;
1017 if ( myPopupActionList.contains( a ) ) // check own action map for popup items
1018 id -= PYQT_ACTION_POPUP;
1021 QAction* SALOME_PYQT_Module::createAction( const int id, const QString& text, const QString& icon,
1022 const QString& menu, const QString& tip, const int key,
1026 if ( !icon.isEmpty() ) {
1027 QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(""), tr( icon ) );
1028 if ( !pixmap.isNull() )
1029 anIcon = QIconSet( pixmap );
1031 return SalomeApp_Module::createAction( id, text, anIcon, menu, tip, key, getApp()->desktop(), toggle, this, SLOT( onGUIEvent() ) );
1035 //=============================================================================
1036 // SALOME_PYQT_XmlHandler class implementation
1037 //=============================================================================
1039 // gets an tag name for the dom element [ static ]
1040 // returns an empty string if the element does not have tag name
1041 static QString tagName( const QDomElement& element ) {
1042 return element.tagName().stripWhiteSpace();
1045 // gets an attribute by it's name for the dom element [ static ]
1046 // returns an empty string if the element does not have such attribute
1047 static QString attribute( const QDomElement& element, const QString& attName ) {
1048 return element.attribute( attName ).stripWhiteSpace();
1051 // checks the given value for the boolean value [ static ]
1052 // returns TRUE if string is "true", "yes" or "1"
1053 static bool checkBool( const QString& value ) {
1054 return ( value == "true" || value == "yes" || value == "1" );
1057 // checks the given value for the integer value [ static ]
1058 // returns -1 if item is empty or presents and invalid number
1059 static int checkInt( const QString& value )
1061 return value.isEmpty() ? -1 : value.toInt();
1067 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
1068 : myModule( module )
1070 QFile aFile( fileName );
1071 if ( !aFile.open( IO_ReadOnly ) )
1073 if ( !myDoc.setContent( &aFile ) ) {
1081 Called by SALOME_PYQT_Module::initialize() in order to create actions
1082 (menus, toolbars, popup menus)
1084 void SALOME_PYQT_XmlHandler::createActions()
1086 // get document element
1087 QDomElement aDocElem = myDoc.documentElement();
1089 // get main menu actions
1090 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
1091 for ( int i = 0; i < aMenuList.count(); i++ ) {
1092 QDomNode n = aMenuList.item( i );
1096 // create toolbars actions
1097 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
1098 for ( int i = 0; i < aToolsList.count(); i++ ) {
1099 QDomNode n = aToolsList.item( i );
1105 * Creates popup menu
1107 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
1108 const QString& context,
1109 const QString& parent,
1110 const QString& object )
1112 // get document element
1113 QDomElement aDocElem = myDoc.documentElement();
1115 // get popup menus actions
1116 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
1117 for ( int i = 0; i < aPopupList.count(); i++ ) {
1118 QDomNode n = aPopupList.item( i );
1119 if ( !n.isNull() && n.isElement() ) {
1120 QDomElement e = n.toElement();
1121 QString lab = attribute( e, "label-id" );
1122 QString ctx = attribute( e, "context-id" );
1123 QString prt = attribute( e, "parent-id" );
1124 QString obj = attribute( e, "object-id" );
1125 if ( ctx == context && prt == parent && obj == object ) {
1126 insertPopupItems( n, menu );
1134 Create main menu with child actions
1136 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
1141 if ( parentNode.isNull() )
1144 QDomElement parentElement = parentNode.toElement();
1145 if ( !parentElement.isNull() ) {
1146 QString plabel = attribute( parentElement, "label-id" );
1147 int pid = checkInt( attribute( parentElement, "item-id" ) );
1148 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
1149 if ( !plabel.isEmpty() ) {
1151 int menuId = myModule->createMenu( plabel, // label
1152 parentMenuId, // parent menu ID, should be -1 for main menu
1156 QDomNode node = parentNode.firstChild();
1157 while ( !node.isNull() ) {
1158 if ( node.isElement() ) {
1159 QDomElement elem = node.toElement();
1160 QString aTagName = tagName( elem );
1161 if ( aTagName == "popup-item" ) {
1162 int id = checkInt( attribute( elem, "item-id" ) );
1163 int pos = checkInt( attribute( elem, "pos-id" ) );
1164 QString label = attribute( elem, "label-id" );
1165 QString icon = attribute( elem, "icon-id" );
1166 QString tooltip = attribute( elem, "tooltip-id" );
1167 QString accel = attribute( elem, "accel-id" );
1168 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1169 ////QString execute = attribute( elem, "execute-action" ); // not used
1171 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1172 // also check if the action with given ID is already created
1173 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1174 // little trick to have several actions with same ID for menus and toolbars
1175 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1176 // create menu action
1177 QAction* action = myModule->createAction( id, // ID
1181 tooltip, // status-bar text
1182 QKeySequence( accel ), // keyboard accelerator
1183 toggle ); // toogled action
1184 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1185 myModule->createMenu( action, menuId, -1, 80, pos );
1188 else if ( aTagName == "submenu" ) {
1190 createMenu( node, menuId );
1192 else if ( aTagName == "separator" ) {
1193 // create menu separator
1194 int pos = checkInt( attribute( elem, "pos-id" ) );
1195 QAction* action = myModule->createSeparator();
1196 myModule->createMenu( action, menuId, -1, 80, pos );
1199 node = node.nextSibling();
1206 Create a toolbar with child actions
1208 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1213 if ( parentNode.isNull() )
1216 QDomElement parentElement = parentNode.toElement();
1217 if ( !parentElement.isNull() ) {
1218 QString aLabel = attribute( parentElement, "label-id" );
1219 if ( !aLabel.isEmpty() ) {
1221 int tbId = myModule->createTool( aLabel );
1222 QDomNode node = parentNode.firstChild();
1223 while ( !node.isNull() ) {
1224 if ( node.isElement() ) {
1225 QDomElement elem = node.toElement();
1226 QString aTagName = tagName( elem );
1227 if ( aTagName == "toolbutton-item" ) {
1228 int id = checkInt( attribute( elem, "item-id" ) );
1229 int pos = checkInt( attribute( elem, "pos-id" ) );
1230 QString label = attribute( elem, "label-id" );
1231 QString icon = attribute( elem, "icon-id" );
1232 QString tooltip = attribute( elem, "tooltip-id" );
1233 QString accel = attribute( elem, "accel-id" );
1234 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1235 ////QString execute = attribute( elem, "execute-action" ); // not used
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
1239 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1240 // little trick to have several actions with same ID for menus and toolbars
1241 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1242 // create toolbar action
1243 QAction* action = myModule->createAction( id, // ID
1247 tooltip, // status-bar text
1248 QKeySequence( accel ), // keyboard accelerator
1249 toggle ); // toogled action
1250 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1251 myModule->createTool( action, tbId, -1, pos );
1254 else if ( aTagName == "separatorTB" ) {
1255 // create toolbar separator
1256 int pos = checkInt( attribute( elem, "pos-id" ) );
1257 QAction* action = myModule->createSeparator();
1258 myModule->createTool( action, tbId, -1, pos );
1261 node = node.nextSibling();
1267 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1272 if ( parentNode.isNull() )
1275 // we create popup menus without help of QtxPopupMgr
1276 QDomNode node = parentNode.firstChild();
1277 while ( !node.isNull() ) {
1278 if ( node.isElement() ) {
1279 QDomElement elem = node.toElement();
1280 QString aTagName = tagName( elem );
1281 if ( aTagName == "popup-item" ) {
1282 // insert a command item
1283 int id = checkInt( attribute( elem, "item-id" ) );
1284 int pos = checkInt( attribute( elem, "pos-id" ) );
1285 QString label = attribute( elem, "label-id" );
1286 QString icon = attribute( elem, "icon-id" );
1287 /////QString tooltip = attribute( elem, "tooltip-id" ); // not used
1288 QString accel = attribute( elem, "accel-id" );
1289 /////bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1290 /////QString execute = attribute( elem, "execute-action" ); // not used
1293 if ( !icon.isEmpty() ) {
1294 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1295 if ( !pixmap.isNull() )
1296 anIcon = QIconSet( pixmap );
1299 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1300 // also check if the action with given ID is already created
1302 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent(int) ), QKeySequence( accel ), id, pos );
1305 else if ( aTagName == "submenu" ) {
1307 int id = checkInt( attribute( elem, "item-id" ) );
1308 int pos = checkInt( attribute( elem, "pos-id" ) );
1309 QString label = attribute( elem, "label-id" );
1310 QString icon = attribute( elem, "icon-id" );
1313 if ( !icon.isEmpty() ) {
1314 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1315 if ( !pixmap.isNull() )
1316 anIcon = QIconSet( pixmap );
1319 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1320 menu->insertItem( anIcon, label, newPopup, id, pos );
1321 insertPopupItems( node, newPopup );
1323 else if ( aTagName == "separator" ) {
1324 // create menu separator
1325 int pos = checkInt( attribute( elem, "pos-id" ) );
1326 menu->insertSeparator( pos );
1329 node = node.nextSibling();