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 if ( myMenuActionList.contains( action ) )
318 id -= PYQT_ACTION_MENU;
319 if ( myToolbarActionList.contains( action ) )
320 id -= PYQT_ACTION_TOOLBAL;
321 if ( myPopupActionList.contains( action ) )
322 id -= PYQT_ACTION_POPUP;
323 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
325 // perform synchronous request to Python event dispatcher
326 class GUIEvent : public PyInterp_LockRequest
329 GUIEvent( PyInterp_base* _py_interp,
330 SALOME_PYQT_Module* _obj,
332 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
337 virtual void execute()
339 myObj->guiEvent( myId );
344 SALOME_PYQT_Module* myObj;
347 // Posting the request
348 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
352 * Processes GUI action (from context popup menu, only for XML-based actions!)
354 void SALOME_PYQT_Module::onGUIEvent( int id )
356 // perform synchronous request to Python event dispatcher
357 class GUIEvent : public PyInterp_LockRequest
360 GUIEvent( PyInterp_base* _py_interp,
361 SALOME_PYQT_Module* _obj,
363 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
368 virtual void execute()
370 myObj->guiEvent( myId );
375 SALOME_PYQT_Module* myObj;
378 // Posting the request
379 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
383 Context popup menu request.
384 Called when user activates popup menu in some window (view, object browser, etc).
386 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
388 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
389 // perform synchronous request to Python event dispatcher
390 class PopupMenuEvent : public PyInterp_LockRequest
393 PopupMenuEvent( PyInterp_base* _py_interp,
394 SALOME_PYQT_Module* _obj,
395 const QString& _context,
397 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
398 myContext( _context ),
403 virtual void execute()
405 myObj->contextMenu( myContext, myPopup );
409 SALOME_PYQT_Module* myObj;
414 // Posting the request only if dispatcher is not busy!
415 // Executing the request synchronously
416 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
417 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
421 * Defines the dockable window associated with the module.
422 * To fill the list of windows the correspondind Python module's windows()
423 * method is called from SALOME_PYQT_Module::init() method.
424 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
426 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
428 // First clear the output parameters
429 QMap<int, int>::ConstIterator it;
430 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
431 mappa[ it.key() ] = it.data();
436 * Defines the compatible views which should be opened on module activation.
437 * To fill the list of views the correspondind Python module's views()
438 * method is called from SALOME_PYQT_Module::init() method.
439 * By default, the list is empty.
441 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
443 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
444 listik.append( *it );
449 * Performs internal initialization
450 * - initializes/gets the Python interpreter (one per study)
451 * - imports the Python module
452 * - passes the workspace widget to the Python module
453 * - calls Python module's initialize() method
454 * - calls Python module's windows() method
455 * - calls Python module's views() method
457 void SALOME_PYQT_Module::init( CAM_Application* app )
459 // reset interpreter to NULL
463 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
467 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
470 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
472 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
473 initInterp( aStudyId );
477 // import Python GUI module
484 if ( IsCallOldMethods ) { // __CALL_OLD_METHODS__
485 // call Python module's setWorkspace() method
487 } //__CALL_OLD_METHODS__
489 // then call Python module's initialize() method
490 // ... first get python lock
491 PyLockWrapper aLock = myInterp->GetLockWrapper();
492 // ... (the Python module is already imported)
493 // ... finally call Python module's initialize() method
494 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
496 // VSR: this method may not be implemented in Python module
500 // get the windows list from the Python module by calling windows() method
501 // ... first put default values
502 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
503 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
504 // VSR: LogWindow is not yet implemented
505 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
507 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
509 // VSR: this method may not be implemented in Python module
513 myWindowsMap.clear();
514 if ( PyDict_Check( res1 ) ) {
518 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
519 // parse the return value
520 // it should be a map: {integer:integer}
522 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
523 aKey = PyInt_AsLong( key );
524 aValue = PyInt_AsLong( value );
525 myWindowsMap[ aKey ] = aValue;
530 // get the windows list from the Python module by calling views() method
531 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
533 // VSR: this method may not be implemented in Python module
537 // parse the return value
538 // result can be one string...
539 if ( PyString_Check( res2 ) ) {
540 myViewMgrList.append( PyString_AsString( res2 ) );
542 // ... or list of strings
543 else if ( PyList_Check( res2 ) ) {
544 int size = PyList_Size( res2 );
545 for ( int i = 0; i < size; i++ ) {
546 PyObject* value = PyList_GetItem( res2, i );
547 if( value && PyString_Check( value ) ) {
548 myViewMgrList.append( PyString_AsString( value ) );
557 * Performs internal activation:
558 * - initializes/gets the Python interpreter (one per study)
559 * - imports the Python GUI module
560 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
561 * or activate() method (for new modules)
563 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
566 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
567 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
569 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
570 initInterp( aStudyId );
574 // import Python GUI module
580 PyLockWrapper aLock = myInterp->GetLockWrapper();
582 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
583 // call Python module's setSettings() method (obsolete)
584 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
586 // VSR: this method may not be implemented in Python module
589 } //__CALL_OLD_METHODS__
591 // call Python module's activate() method (for the new modules)
592 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
594 // VSR: this method may not be implemented in Python module
600 * Performs internal deactivation:
601 * - calls Python module's deactivate() method
603 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
605 // check if the subinterpreter is initialized and Python module is imported
606 if ( !myInterp || !myModule ) {
607 // Error! Python subinterpreter should be initialized and module should be imported first!
610 // then call Python module's deactivate() method
611 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
613 // 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
652 * Get module engine, returns nil var if engine is not found in LifeCycleCORBA
654 Engines::Component_var SALOME_PYQT_Module::getEngine() const
656 Engines::Component_var comp;
657 // temporary solution
659 comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name( "" ) );
661 catch (CORBA::Exception&) {
667 * Get module engine IOR, returns empty string if engine is not found in LifeCycleCORBA
669 QString SALOME_PYQT_Module::engineIOR() const
671 if ( !CORBA::is_nil( getEngine() ) )
672 return QString( getApp()->orb()->object_to_string( getEngine() ) );
673 return QString( "" );
677 * Called when study desktop is activated.
678 * Used for notifying about changing of the active study.
680 void SALOME_PYQT_Module::studyActivated()
682 // StudyChangedReq: request class for internal studyChanged() operation
683 class StudyChangedReq : public PyInterp_Request
686 StudyChangedReq( SUIT_Study* _study,
687 SALOME_PYQT_Module* _obj )
688 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
693 virtual void execute()
695 myObj->studyChanged( myStudy );
700 SALOME_PYQT_Module* myObj;
703 // Posting the request
704 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
708 * Processes context popup menu request
709 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
710 * to define the popup menu context
711 * - parses XML resourses file (if exists) and fills the popup menu with the items)
712 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
713 * to allow module to customize the popup menu
714 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
715 * by using insertItem(...) Qt functions.
717 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
719 // Python interpreter should be initialized and Python module should be
721 if ( !myInterp || !myModule )
724 QString aContext( theContext ), aObject( "" ), aParent( "" );
726 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
727 // call definePopup() Python module's function
728 // this is obsolete function, used only for compatibility reasons
729 PyObjWrapper res(PyObject_CallMethod( myModule,
734 aParent.latin1() ) );
736 // VSR: this method may not be implemented in Python module
740 // parse return value
742 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
748 } //__CALL_OLD_METHODS__
750 // first try to create menu via XML parser:
751 // we create popup menus without help of QtxPopupMgr
753 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
755 PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QPopupMenu ) );
757 // then call Python module's createPopupMenu() method (for new modules)
758 PyObjWrapper res1( PyObject_CallMethod( myModule,
762 aContext.latin1() ) );
764 // VSR: this method may not be implemented in Python module
768 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
769 // call customPopup() Python module's function
770 // this is obsolete function, used only for compatibility reasons
771 PyObjWrapper res2( PyObject_CallMethod( myModule,
777 aParent.latin1() ) );
779 // VSR: this method may not be implemented in Python module
782 } //__CALL_OLD_METHODS__
786 * Processes GUI event
787 * - calls Python module's OnGUIEvent() method
789 void SALOME_PYQT_Module::guiEvent( const int theId )
791 // Python interpreter should be initialized and Python module should be
793 if ( !myInterp || !myModule )
796 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
804 * Initialises python subinterpreter (one per study)
806 void SALOME_PYQT_Module::initInterp( int theStudyId )
810 // Error! Study Id must not be 0!
814 // try to find the subinterpreter
815 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
817 myInterp = myInterpMap[ theStudyId ];
820 // not found - create a new one!
821 ///////////////////////////////////////////////////////////////////
822 // Attention: the creation of Python interpretor must be protected
823 // by a C++ Lock because of C threads
824 ///////////////////////////////////////////////////////////////////
825 myInterp = new SALOME_PYQT_PyInterp();
826 myInterp->initialize();
827 myInterpMap[ theStudyId ] = myInterp;
829 // import 'salome' module and call 'salome_init' method;
830 // do it only once on interpreter creation
831 // ... first get python lock
832 PyLockWrapper aLock = myInterp->GetLockWrapper();
833 // ... then import a module
834 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
840 // ... then call a method
841 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
850 * Imports Python GUI module and remember the reference to the module
851 * !!! initInterp() should be called first!!!
853 void SALOME_PYQT_Module::importModule()
855 // check if the subinterpreter is initialized
857 // Error! Python subinterpreter should be initialized first!
861 // import Python GUI module and puts it in <myModule> attribute
862 // ... first get python lock
863 PyLockWrapper aLock = myInterp->GetLockWrapper();
864 // ... then import a module
865 QString aMod = QString( name("") ) + "GUI";
866 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
875 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
877 * !!! initInterp() and importModule() should be called first!!!
879 void SALOME_PYQT_Module::setWorkSpace()
881 // check if the subinterpreter is initialized and Python module is imported
882 if ( !myInterp || !myModule ) {
883 // Error! Python subinterpreter should be initialized and module should be imported first!
887 // call setWorkspace() method
888 // ... first get python lock
889 PyLockWrapper aLock = myInterp->GetLockWrapper();
891 // ... then try to import SalomePyQt module. If it's not possible don't go on.
892 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
899 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
900 // ... then get workspace object
901 QWidget* aWorkspace = 0;
902 if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
903 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
905 aWorkspace = aDesktop->workspace();
907 else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
908 STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
910 aWorkspace = aDesktop->workstack();
912 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget ) );
913 // ... and finally call Python module's setWorkspace() method (obsolete)
914 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
916 // VSR: this method may not be implemented in Python module
920 } //__CALL_OLD_METHODS__
924 * Adds an action into private action list [internal usage]
926 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
929 case PYQT_ACTION_MENU:
930 myMenuActionList.append( action );
932 case PYQT_ACTION_TOOLBAL:
933 myToolbarActionList.append( action );
935 case PYQT_ACTION_POPUP:
936 myPopupActionList.append( action );
943 * The next methods just call the parent implementation.
944 * This is done to open protected methods from CAM_Module class.
946 int SALOME_PYQT_Module::createTool( const QString& name )
948 return SalomeApp_Module::createTool( name );
950 int SALOME_PYQT_Module::createTool( const int id, const int tBar, const int idx )
952 return SalomeApp_Module::createTool( id, tBar, idx );
954 int SALOME_PYQT_Module::createTool( const int id, const QString& tBar, const int idx )
956 return SalomeApp_Module::createTool( id, tBar, idx );
958 int SALOME_PYQT_Module::createTool( QAction* a, const int tBar, const int id, const int idx )
960 return SalomeApp_Module::createTool( a, tBar, id, idx );
962 int SALOME_PYQT_Module::createTool( QAction* a, const QString& tBar, const int id, const int idx )
964 return SalomeApp_Module::createTool( a, tBar, id, idx );
966 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
968 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
970 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
972 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
974 int SALOME_PYQT_Module::createMenu( const int id, const int menu, const int group, const int idx )
976 return SalomeApp_Module::createMenu( id, menu, group, idx );
978 int SALOME_PYQT_Module::createMenu( const int id, const QString& menu, const int group, const int idx )
980 return SalomeApp_Module::createMenu( id, menu, group, idx );
982 int SALOME_PYQT_Module::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
984 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
986 int SALOME_PYQT_Module::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
988 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
990 QAction* SALOME_PYQT_Module::createSeparator()
992 return SalomeApp_Module::separator();
994 QAction* SALOME_PYQT_Module::action( const int id ) const
996 return SalomeApp_Module::action( id );
998 int SALOME_PYQT_Module::actionId( const QAction* a ) const
1000 return SalomeApp_Module::actionId( a );
1002 QAction* SALOME_PYQT_Module::createAction( const int id, const QString& text, const QString& icon,
1003 const QString& menu, const QString& tip, const int key,
1007 if ( !icon.isEmpty() ) {
1008 QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(""), tr( icon ) );
1009 if ( !pixmap.isNull() )
1010 anIcon = QIconSet( pixmap );
1012 return SalomeApp_Module::createAction( id, text, anIcon, menu, tip, key, getApp()->desktop(), toggle, this, SLOT( onGUIEvent() ) );
1016 //=============================================================================
1017 // SALOME_PYQT_XmlHandler class implementation
1018 //=============================================================================
1020 // gets an tag name for the dom element [ static ]
1021 // returns an empty string if the element does not have tag name
1022 static QString tagName( const QDomElement& element ) {
1023 return element.tagName().stripWhiteSpace();
1026 // gets an attribute by it's name for the dom element [ static ]
1027 // returns an empty string if the element does not have such attribute
1028 static QString attribute( const QDomElement& element, const QString& attName ) {
1029 return element.attribute( attName ).stripWhiteSpace();
1032 // checks the given value for the boolean value [ static ]
1033 // returns TRUE if string is "true", "yes" or "1"
1034 static bool checkBool( const QString& value ) {
1035 return ( value == "true" || value == "yes" || value == "1" );
1038 // checks the given value for the integer value [ static ]
1039 // returns -1 if item is empty or presents and invalid number
1040 static int checkInt( const QString& value )
1042 return value.isEmpty() ? -1 : value.toInt();
1048 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
1049 : myModule( module )
1051 QFile aFile( fileName );
1052 if ( !aFile.open( IO_ReadOnly ) )
1054 if ( !myDoc.setContent( &aFile ) ) {
1062 Called by SALOME_PYQT_Module::initialize() in order to create actions
1063 (menus, toolbars, popup menus)
1065 void SALOME_PYQT_XmlHandler::createActions()
1067 // get document element
1068 QDomElement aDocElem = myDoc.documentElement();
1070 // get main menu actions
1071 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
1072 for ( int i = 0; i < aMenuList.count(); i++ ) {
1073 QDomNode n = aMenuList.item( i );
1077 // create toolbars actions
1078 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
1079 for ( int i = 0; i < aToolsList.count(); i++ ) {
1080 QDomNode n = aToolsList.item( i );
1086 * Creates popup menu
1088 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
1089 const QString& context,
1090 const QString& parent,
1091 const QString& object )
1093 // get document element
1094 QDomElement aDocElem = myDoc.documentElement();
1096 // get popup menus actions
1097 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
1098 for ( int i = 0; i < aPopupList.count(); i++ ) {
1099 QDomNode n = aPopupList.item( i );
1100 if ( !n.isNull() && n.isElement() ) {
1101 QDomElement e = n.toElement();
1102 QString lab = attribute( e, "label-id" );
1103 QString ctx = attribute( e, "context-id" );
1104 QString prt = attribute( e, "parent-id" );
1105 QString obj = attribute( e, "object-id" );
1106 if ( ctx == context && prt == parent && obj == object ) {
1107 insertPopupItems( n, menu );
1115 Create main menu with child actions
1117 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
1122 if ( parentNode.isNull() )
1125 QDomElement parentElement = parentNode.toElement();
1126 if ( !parentElement.isNull() ) {
1127 QString plabel = attribute( parentElement, "label-id" );
1128 int pid = checkInt( attribute( parentElement, "item-id" ) );
1129 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
1130 if ( !plabel.isEmpty() ) {
1132 int menuId = myModule->createMenu( plabel, // label
1133 parentMenuId, // parent menu ID, should be -1 for main menu
1137 QDomNode node = parentNode.firstChild();
1138 while ( !node.isNull() ) {
1139 if ( node.isElement() ) {
1140 QDomElement elem = node.toElement();
1141 QString aTagName = tagName( elem );
1142 if ( aTagName == "popup-item" ) {
1143 int id = checkInt( attribute( elem, "item-id" ) );
1144 int pos = checkInt( attribute( elem, "pos-id" ) );
1145 QString label = attribute( elem, "label-id" );
1146 QString icon = attribute( elem, "icon-id" );
1147 QString tooltip = attribute( elem, "tooltip-id" );
1148 QString accel = attribute( elem, "accel-id" );
1149 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1150 ////QString execute = attribute( elem, "execute-action" ); // not used
1152 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1153 // also check if the action with given ID is already created
1154 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1155 // little trick to have several actions with same ID for menus and toolbars
1156 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1157 // create menu action
1158 QAction* action = myModule->createAction( id, // ID
1162 tooltip, // status-bar text
1163 QKeySequence( accel ), // keyboard accelerator
1164 toggle ); // toogled action
1165 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1166 myModule->createMenu( action, menuId, -1, 80, pos );
1169 else if ( aTagName == "submenu" ) {
1171 createMenu( node, menuId );
1173 else if ( aTagName == "separator" ) {
1174 // create menu separator
1175 int pos = checkInt( attribute( elem, "pos-id" ) );
1176 QAction* action = myModule->createSeparator();
1177 myModule->createMenu( action, menuId, -1, 80, pos );
1180 node = node.nextSibling();
1187 Create a toolbar with child actions
1189 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1194 if ( parentNode.isNull() )
1197 QDomElement parentElement = parentNode.toElement();
1198 if ( !parentElement.isNull() ) {
1199 QString aLabel = attribute( parentElement, "label-id" );
1200 if ( !aLabel.isEmpty() ) {
1202 int tbId = myModule->createTool( aLabel );
1203 QDomNode node = parentNode.firstChild();
1204 while ( !node.isNull() ) {
1205 if ( node.isElement() ) {
1206 QDomElement elem = node.toElement();
1207 QString aTagName = tagName( elem );
1208 if ( aTagName == "toolbutton-item" ) {
1209 int id = checkInt( attribute( elem, "item-id" ) );
1210 int pos = checkInt( attribute( elem, "pos-id" ) );
1211 QString label = attribute( elem, "label-id" );
1212 QString icon = attribute( elem, "icon-id" );
1213 QString tooltip = attribute( elem, "tooltip-id" );
1214 QString accel = attribute( elem, "accel-id" );
1215 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1216 ////QString execute = attribute( elem, "execute-action" ); // not used
1218 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1219 // also check if the action with given ID is already created
1220 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1221 // little trick to have several actions with same ID for menus and toolbars
1222 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1223 // create toolbar action
1224 QAction* action = myModule->createAction( id, // ID
1228 tooltip, // status-bar text
1229 QKeySequence( accel ), // keyboard accelerator
1230 toggle ); // toogled action
1231 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1232 myModule->createTool( action, tbId, -1, pos );
1235 else if ( aTagName == "separatorTB" ) {
1236 // create toolbar separator
1237 int pos = checkInt( attribute( elem, "pos-id" ) );
1238 QAction* action = myModule->createSeparator();
1239 myModule->createTool( action, tbId, -1, pos );
1242 node = node.nextSibling();
1248 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1253 if ( parentNode.isNull() )
1256 // we create popup menus without help of QtxPopupMgr
1257 QDomNode node = parentNode.firstChild();
1258 while ( !node.isNull() ) {
1259 if ( node.isElement() ) {
1260 QDomElement elem = node.toElement();
1261 QString aTagName = tagName( elem );
1262 if ( aTagName == "popup-item" ) {
1263 // insert a command item
1264 int id = checkInt( attribute( elem, "item-id" ) );
1265 int pos = checkInt( attribute( elem, "pos-id" ) );
1266 QString label = attribute( elem, "label-id" );
1267 QString icon = attribute( elem, "icon-id" );
1268 /////QString tooltip = attribute( elem, "tooltip-id" ); // not used
1269 QString accel = attribute( elem, "accel-id" );
1270 /////bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1271 /////QString execute = attribute( elem, "execute-action" ); // not used
1274 if ( !icon.isEmpty() ) {
1275 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1276 if ( !pixmap.isNull() )
1277 anIcon = QIconSet( pixmap );
1280 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1281 // also check if the action with given ID is already created
1283 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent(int) ), QKeySequence( accel ), id, pos );
1286 else if ( aTagName == "submenu" ) {
1288 int id = checkInt( attribute( elem, "item-id" ) );
1289 int pos = checkInt( attribute( elem, "pos-id" ) );
1290 QString label = attribute( elem, "label-id" );
1291 QString icon = attribute( elem, "icon-id" );
1294 if ( !icon.isEmpty() ) {
1295 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1296 if ( !pixmap.isNull() )
1297 anIcon = QIconSet( pixmap );
1300 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1301 menu->insertItem( anIcon, label, newPopup, id, pos );
1302 insertPopupItems( node, newPopup );
1304 else if ( aTagName == "separator" ) {
1305 // create menu separator
1306 int pos = checkInt( attribute( elem, "pos-id" ) );
1307 menu->insertSeparator( pos );
1310 node = node.nextSibling();