1 // Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
8 // This library is distributed in the hope that it will be useful
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/
19 //=============================================================================
20 // File : SALOME_PYQT_Module.cxx
22 // Author : Vadim SANDLER
24 // Copyright : 2003-2005 CEA/DEN, EDF R&D
26 //=============================================================================
28 #include "SALOME_PYQT_Module.h"
30 #include "PyInterp_Dispatcher.h"
31 #include "SUIT_ResourceMgr.h"
32 #include "STD_MDIDesktop.h"
33 #include "STD_TabDesktop.h"
34 #include "SalomeApp_Application.h"
35 #include "SalomeApp_Study.h"
37 #include "QtxWorkstack.h"
38 #include <SALOME_LifeCycleCORBA.hxx>
39 #include <Container_init_python.hxx>
43 #include <qworkspace.h>
44 #include <qpopupmenu.h>
46 #include "SALOME_PYQT_SipDefs.h"
47 #if defined(SIP_VERS_v4_old) || defined(SIP_VERS_v4_new)
48 #include "sipAPISalomePyQtGUI.h"
50 #include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
53 #include <sipqtQWidget.h>
54 #include <sipqtQPopupMenu.h>
60 ///////////////////////////////////////////////////////////////////////////////
61 // Default name of the module, replaced at the moment of module creation
62 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
64 ///////////////////////////////////////////////////////////////////////////////
65 // If __CALL_OLD_METHODS__ macro is not defined the invoking of obsolete Python
66 // module's methods like setSetting(), definePopup(), etc. is blocked.
67 // This macro is defined by default (in Makefile)
68 #ifdef __CALL_OLD_METHODS__
69 const bool IsCallOldMethods = true;
71 const bool IsCallOldMethods = false;
74 ///////////////////////////////////////////////////////////////////////////////
75 // NB: Python requests.
76 // General rule for Python requests created by SALOME_PYQT_Module:
77 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
78 // However, it is obligatory that ANY Python call is wrapped with a request object,
79 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
80 ///////////////////////////////////////////////////////////////////////////////
82 //=============================================================================
83 // The class for parsing of the XML resource files.
84 // Used for backward compatibility with existing Python modules.
85 //=============================================================================
86 class SALOME_PYQT_XmlHandler
89 SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
91 void createPopup ( QPopupMenu* menu,
92 const QString& context,
93 const QString& parent,
94 const QString& object );
97 void createToolBar ( QDomNode& parentNode );
98 void createMenu ( QDomNode& parentNode,
99 const int parentMenuId = -1 );
101 void insertPopupItems( QDomNode& parentNode,
105 SALOME_PYQT_Module* myModule;
109 //=============================================================================
110 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
111 // all Python-based SALOME module
112 //=============================================================================
114 // While the SalomePyQtGUI library is not imported in Python it's initialization function
115 // should be called manually (and only once) in order to initialize global sip data
116 // and to get C API from sip : sipBuildResult for example
117 #if defined(SIP_STATIC_MODULE)
118 extern "C" void initSalomePyQtGUI();
120 PyMODINIT_FUNC initSalomePyQtGUI();
124 * This function creates an instance of SALOME_PYQT_Module object by request
125 * of and application object when the module is loaded.
128 SALOME_PYQT_EXPORT CAM_Module* createModule() {
129 static bool alreadyInitialized = false;
130 if ( !alreadyInitialized ) {
131 // call only once (see above) !
132 PyEval_RestoreThread( KERNEL_PYTHON::_gtstate );
134 PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
135 alreadyInitialized = !alreadyInitialized;
137 return new SALOME_PYQT_Module();
142 * Static variables definition
144 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
145 SALOME_PYQT_Module* SALOME_PYQT_Module::myInitModule = 0;
148 * Little trick : provide an access to being activated Python module from outside;
149 * needed by the SalomePyQt library :(
151 SALOME_PYQT_Module* SALOME_PYQT_Module::getInitModule()
159 SALOME_PYQT_Module::SALOME_PYQT_Module() :
160 SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
162 myMenuActionList.setAutoDelete( false );
163 myPopupActionList.setAutoDelete( false );
164 myToolbarActionList.setAutoDelete( false );
170 SALOME_PYQT_Module::~SALOME_PYQT_Module()
172 myMenuActionList.clear();
173 myPopupActionList.clear();
174 myToolbarActionList.clear();
180 * Initialization of the module.
181 * Inherited from CAM_Module.
183 * This method is used for creation of the menus, toolbars and other staff.
184 * There are two ways:
185 * - for obsolete modules this method first tries to read <module>_<language>.xml
186 * resource file which contains a menu, toolbars and popup menus description.
187 * - new modules can create menus by by calling the corresponding methods of SalomePyQt
188 * Python API in the Python module's initialize() method which is called from here.
189 * NOTE: if postponed modules loading is not used, the active study might be not defined
190 * yet at this stage, so initialize() method should not perform any study-based initialization.
192 void SALOME_PYQT_Module::initialize( CAM_Application* app )
194 SalomeApp_Module::initialize( app );
196 // Try to get XML resource file name
197 SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
198 QString aLang = aResMgr->stringValue( "language", "language", QString::null );
199 if ( aLang.isEmpty() ) aLang = QString( "en" );
200 QString aName = name( "" );
201 QString aFileName = aName + "_" + aLang + ".xml";
202 aFileName = aResMgr->path( "resources", aName, aFileName );
204 // parse XML file if it is found and create actions
205 if ( !myXmlHandler && !aFileName.isEmpty() ) {
206 myXmlHandler = new SALOME_PYQT_XmlHandler( this, aFileName );
207 myXmlHandler->createActions();
210 // perform internal initialization and call module's initialize() method
211 // InitializeReq: request class for internal init() operation
212 class InitializeReq : public PyInterp_Request
215 InitializeReq( CAM_Application* _app,
216 SALOME_PYQT_Module* _obj )
217 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
222 virtual void execute()
224 myObj->init( myApp );
228 CAM_Application* myApp;
229 SALOME_PYQT_Module* myObj;
232 // Posting the request
233 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
237 * Activation of the module.
238 * Inherited from CAM_Module.
240 bool SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
242 MESSAGE( "SALOME_PYQT_Module::activateModule" );
244 bool res = SalomeApp_Module::activateModule( theStudy );
249 // ActivateReq: request class for internal activate() operation
250 class ActivateReq : public PyInterp_Request
253 ActivateReq( SUIT_Study* _study,
254 SALOME_PYQT_Module* _obj )
255 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
260 virtual void execute()
262 myObj->activate( myStudy );
267 SALOME_PYQT_Module* myObj;
270 // Posting the request
271 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
273 // activate menus, toolbars, etc
274 setMenuShown( true );
275 setToolShown( true );
281 * Deactivation of the module.
282 * Inherited from CAM_Module.
284 bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
286 MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
288 bool res = SalomeApp_Module::deactivateModule( theStudy );
290 // deactivate menus, toolbars, etc
291 setMenuShown( false );
292 setToolShown( false );
294 // DeactivateReq: request class for internal deactivate() operation
295 class DeactivateReq : public PyInterp_LockRequest
298 DeactivateReq( PyInterp_base* _py_interp,
300 SALOME_PYQT_Module* _obj )
301 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
306 virtual void execute()
308 myObj->deactivate( myStudy );
313 SALOME_PYQT_Module* myObj;
316 // Posting the request
317 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
323 * Processes GUI action (from main menu, toolbar or context popup menu)
325 void SALOME_PYQT_Module::onGUIEvent()
328 const QObject* obj = sender();
329 if ( !obj || !obj->inherits( "QAction" ) )
331 QAction* action = (QAction*)obj;
334 int id = actionId( action );
335 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
337 // perform synchronous request to Python event dispatcher
338 class GUIEvent : public PyInterp_LockRequest
341 GUIEvent( PyInterp_base* _py_interp,
342 SALOME_PYQT_Module* _obj,
344 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
349 virtual void execute()
351 myObj->guiEvent( myId );
356 SALOME_PYQT_Module* myObj;
359 // Posting the request
360 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
364 * Processes GUI action (from context popup menu, only for XML-based actions!)
366 void SALOME_PYQT_Module::onGUIEvent( int id )
368 // perform synchronous request to Python event dispatcher
369 class GUIEvent : public PyInterp_LockRequest
372 GUIEvent( PyInterp_base* _py_interp,
373 SALOME_PYQT_Module* _obj,
375 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
380 virtual void execute()
382 myObj->guiEvent( myId );
387 SALOME_PYQT_Module* myObj;
390 // Posting the request
391 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
395 Context popup menu request.
396 Called when user activates popup menu in some window (view, object browser, etc).
398 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
400 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
401 // perform synchronous request to Python event dispatcher
402 class PopupMenuEvent : public PyInterp_LockRequest
405 PopupMenuEvent( PyInterp_base* _py_interp,
406 SALOME_PYQT_Module* _obj,
407 const QString& _context,
409 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
410 myContext( _context ),
415 virtual void execute()
417 myObj->contextMenu( myContext, myPopup );
421 SALOME_PYQT_Module* myObj;
426 // Posting the request only if dispatcher is not busy!
427 // Executing the request synchronously
428 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
429 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
433 * Defines the dockable window associated with the module.
434 * To fill the list of windows the correspondind Python module's windows()
435 * method is called from SALOME_PYQT_Module::init() method.
436 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
438 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
440 // First clear the output parameters
441 QMap<int, int>::ConstIterator it;
442 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
443 mappa[ it.key() ] = it.data();
448 * Defines the compatible views which should be opened on module activation.
449 * To fill the list of views the correspondind Python module's views()
450 * method is called from SALOME_PYQT_Module::init() method.
451 * By default, the list is empty.
453 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
455 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
456 listik.append( *it );
461 * Performs internal initialization
462 * - initializes/gets the Python interpreter (one per study)
463 * - imports the Python module
464 * - passes the workspace widget to the Python module
465 * - calls Python module's initialize() method
466 * - calls Python module's windows() method
467 * - calls Python module's views() method
469 void SALOME_PYQT_Module::init( CAM_Application* app )
471 // reset interpreter to NULL
475 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
479 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
482 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
484 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
485 initInterp( aStudyId );
489 // import Python GUI module
496 if ( IsCallOldMethods ) { // __CALL_OLD_METHODS__
497 // call Python module's setWorkspace() method
499 } //__CALL_OLD_METHODS__
501 // then call Python module's initialize() method
502 // ... first get python lock
503 PyLockWrapper aLock = myInterp->GetLockWrapper();
504 // ... (the Python module is already imported)
505 // ... finally call Python module's initialize() method
506 if(PyObject_HasAttrString(myModule , "initialize")){
507 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
513 // get the windows list from the Python module by calling windows() method
514 // ... first put default values
515 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
516 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
517 // VSR: LogWindow is not yet implemented
518 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
520 if(PyObject_HasAttrString(myModule , "windows")){
521 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
526 myWindowsMap.clear();
527 if ( PyDict_Check( res1 ) ) {
531 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
532 // parse the return value
533 // it should be a map: {integer:integer}
535 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
536 aKey = PyInt_AsLong( key );
537 aValue = PyInt_AsLong( value );
538 myWindowsMap[ aKey ] = aValue;
544 // get the windows list from the Python module by calling views() method
545 if(PyObject_HasAttrString(myModule , "views")){
546 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
551 // parse the return value
552 // result can be one string...
553 if ( PyString_Check( res2 ) ) {
554 myViewMgrList.append( PyString_AsString( res2 ) );
556 // ... or list of strings
557 else if ( PyList_Check( res2 ) ) {
558 int size = PyList_Size( res2 );
559 for ( int i = 0; i < size; i++ ) {
560 PyObject* value = PyList_GetItem( res2, i );
561 if( value && PyString_Check( value ) ) {
562 myViewMgrList.append( PyString_AsString( value ) );
572 * Performs internal activation:
573 * - initializes/gets the Python interpreter (one per study)
574 * - imports the Python GUI module
575 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
576 * or activate() method (for new modules)
578 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
581 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
582 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
584 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
585 initInterp( aStudyId );
589 // import Python GUI module
595 PyLockWrapper aLock = myInterp->GetLockWrapper();
597 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
598 // call Python module's setSettings() method (obsolete)
599 if(PyObject_HasAttrString(myModule , "setSettings")){
600 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
605 } //__CALL_OLD_METHODS__
607 // call Python module's activate() method (for the new modules)
608 if(PyObject_HasAttrString(myModule , "activate")){
609 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
617 * Performs internal deactivation:
618 * - calls Python module's deactivate() method
620 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
622 // check if the subinterpreter is initialized and Python module is imported
623 if ( !myInterp || !myModule ) {
624 // Error! Python subinterpreter should be initialized and module should be imported first!
627 // then call Python module's deactivate() method
628 if(PyObject_HasAttrString(myModule , "deactivate")){
629 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
637 * Called when active the study is actived (user brings its desktop to top)
638 * - initializes/gets the Python interpreter (one per study)
639 * - imports the Python GUI module
640 * - calls Python module's activeStudyChanged() method
642 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
645 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
646 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
648 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
649 initInterp( aStudyId );
653 // import Python GUI module
659 PyLockWrapper aLock = myInterp->GetLockWrapper();
661 // call Python module's activeStudyChanged() method
662 if(PyObject_HasAttrString(myModule , "activeStudyChanged")){
663 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
671 * Get module engine, returns nil var if engine is not found in LifeCycleCORBA
673 Engines::Component_var SALOME_PYQT_Module::getEngine() const
675 Engines::Component_var comp;
676 // temporary solution
678 comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name( "" ) );
680 catch (CORBA::Exception&) {
686 * Get module engine IOR, returns empty string if engine is not found in LifeCycleCORBA
688 QString SALOME_PYQT_Module::engineIOR() const
690 if ( !CORBA::is_nil( getEngine() ) )
691 return QString( getApp()->orb()->object_to_string( getEngine() ) );
692 return QString( "" );
696 * Called when study desktop is activated.
697 * Used for notifying about changing of the active study.
699 void SALOME_PYQT_Module::studyActivated()
701 // StudyChangedReq: request class for internal studyChanged() operation
702 class StudyChangedReq : public PyInterp_Request
705 StudyChangedReq( SUIT_Study* _study,
706 SALOME_PYQT_Module* _obj )
707 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
712 virtual void execute()
714 myObj->studyChanged( myStudy );
719 SALOME_PYQT_Module* myObj;
722 // Posting the request
723 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
727 * Processes context popup menu request
728 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
729 * to define the popup menu context
730 * - parses XML resourses file (if exists) and fills the popup menu with the items)
731 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
732 * to allow module to customize the popup menu
733 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
734 * by using insertItem(...) Qt functions.
736 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
738 // Python interpreter should be initialized and Python module should be
740 if ( !myInterp || !myModule )
743 QString aContext( theContext ), aObject( "" ), aParent( "" );
745 if ( IsCallOldMethods && PyObject_HasAttrString(myModule , "definePopup") ) { //__CALL_OLD_METHODS__
746 // call definePopup() Python module's function
747 // this is obsolete function, used only for compatibility reasons
748 PyObjWrapper res(PyObject_CallMethod( myModule,
753 aParent.latin1() ) );
758 // parse return value
760 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
766 } //__CALL_OLD_METHODS__
768 // first try to create menu via XML parser:
769 // we create popup menus without help of QtxPopupMgr
771 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
773 PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QPopupMenu ) );
775 // then call Python module's createPopupMenu() method (for new modules)
776 if ( PyObject_HasAttrString(myModule , "createPopupMenu") ) {
777 PyObjWrapper res1( PyObject_CallMethod( myModule,
781 aContext.latin1() ) );
787 if ( IsCallOldMethods && PyObject_HasAttrString(myModule , "customPopup") ) { //__CALL_OLD_METHODS__
788 // call customPopup() Python module's function
789 // this is obsolete function, used only for compatibility reasons
790 PyObjWrapper res2( PyObject_CallMethod( myModule,
796 aParent.latin1() ) );
800 } //__CALL_OLD_METHODS__
804 * Processes GUI event
805 * - calls Python module's OnGUIEvent() method
807 void SALOME_PYQT_Module::guiEvent( const int theId )
809 // Python interpreter should be initialized and Python module should be
811 if ( !myInterp || !myModule )
814 if ( PyObject_HasAttrString(myModule , "OnGUIEvent") ) {
815 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
823 * Initialises python subinterpreter (one per study)
825 void SALOME_PYQT_Module::initInterp( int theStudyId )
829 // Error! Study Id must not be 0!
833 // try to find the subinterpreter
834 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
836 myInterp = myInterpMap[ theStudyId ];
839 // not found - create a new one!
840 ///////////////////////////////////////////////////////////////////
841 // Attention: the creation of Python interpretor must be protected
842 // by a C++ Lock because of C threads
843 ///////////////////////////////////////////////////////////////////
844 myInterp = new SALOME_PYQT_PyInterp();
845 myInterp->initialize();
846 myInterpMap[ theStudyId ] = myInterp;
848 // import 'salome' module and call 'salome_init' method;
849 // do it only once on interpreter creation
850 // ... first get python lock
851 PyLockWrapper aLock = myInterp->GetLockWrapper();
852 // ... then import a module
853 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
859 // ... then call a method
860 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
869 * Imports Python GUI module and remember the reference to the module
870 * !!! initInterp() should be called first!!!
872 void SALOME_PYQT_Module::importModule()
874 // check if the subinterpreter is initialized
876 // Error! Python subinterpreter should be initialized first!
880 // import Python GUI module and puts it in <myModule> attribute
881 // ... first get python lock
882 PyLockWrapper aLock = myInterp->GetLockWrapper();
883 // ... then import a module
884 QString aMod = QString( name("") ) + "GUI";
885 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
894 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
896 * !!! initInterp() and importModule() should be called first!!!
898 void SALOME_PYQT_Module::setWorkSpace()
900 // check if the subinterpreter is initialized and Python module is imported
901 if ( !myInterp || !myModule ) {
902 // Error! Python subinterpreter should be initialized and module should be imported first!
906 // call setWorkspace() method
907 // ... first get python lock
908 PyLockWrapper aLock = myInterp->GetLockWrapper();
910 // ... then try to import SalomePyQt module. If it's not possible don't go on.
911 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
918 if ( IsCallOldMethods ) { //__CALL_OLD_METHODS__
919 // ... then get workspace object
920 QWidget* aWorkspace = 0;
921 if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
922 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
924 aWorkspace = aDesktop->workspace();
926 else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
927 STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
929 aWorkspace = aDesktop->workstack();
931 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget ) );
932 // ... and finally call Python module's setWorkspace() method (obsolete)
933 if ( PyObject_HasAttrString(myModule , "setWorkSpace") ) {
934 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
939 } //__CALL_OLD_METHODS__
943 * Adds an action into private action list [internal usage]
945 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
948 case PYQT_ACTION_MENU:
949 myMenuActionList.append( action );
951 case PYQT_ACTION_TOOLBAL:
952 myToolbarActionList.append( action );
954 case PYQT_ACTION_POPUP:
955 myPopupActionList.append( action );
962 * The next methods just call the parent implementation.
963 * This is done to open protected methods from CAM_Module class.
965 int SALOME_PYQT_Module::createTool( const QString& name )
967 return SalomeApp_Module::createTool( name );
969 int SALOME_PYQT_Module::createTool( const int id, const int tBar, const int idx )
971 return SalomeApp_Module::createTool( id, tBar, idx );
973 int SALOME_PYQT_Module::createTool( const int id, const QString& tBar, const int idx )
975 return SalomeApp_Module::createTool( id, tBar, idx );
977 int SALOME_PYQT_Module::createTool( QAction* a, const int tBar, const int id, const int idx )
979 return SalomeApp_Module::createTool( a, tBar, id, idx );
981 int SALOME_PYQT_Module::createTool( QAction* a, const QString& tBar, const int id, const int idx )
983 return SalomeApp_Module::createTool( a, tBar, id, idx );
985 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
987 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
989 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
991 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
993 int SALOME_PYQT_Module::createMenu( const int id, const int menu, const int group, const int idx )
995 return SalomeApp_Module::createMenu( id, menu, group, idx );
997 int SALOME_PYQT_Module::createMenu( const int id, const QString& menu, const int group, const int idx )
999 return SalomeApp_Module::createMenu( id, menu, group, idx );
1001 int SALOME_PYQT_Module::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
1003 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
1005 int SALOME_PYQT_Module::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
1007 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
1009 QAction* SALOME_PYQT_Module::createSeparator()
1011 return SalomeApp_Module::separator();
1013 QAction* SALOME_PYQT_Module::action( const int id ) const
1015 QAction* a = SalomeApp_Module::action( id );
1016 if ( !a ) // try own action map for menu items
1017 a = SalomeApp_Module::action( id + PYQT_ACTION_MENU );
1018 if ( !a ) // try own action map for toolbar items
1019 a = SalomeApp_Module::action( id + PYQT_ACTION_TOOLBAL );
1020 if ( !a ) // try own action map for popup items
1021 a = SalomeApp_Module::action( id + PYQT_ACTION_POPUP );
1024 int SALOME_PYQT_Module::actionId( const QAction* a ) const
1026 int id = SalomeApp_Module::actionId( a );
1027 if ( myMenuActionList.contains( a ) ) // check own action map for menu items
1028 id -= PYQT_ACTION_MENU;
1029 if ( myToolbarActionList.contains( a ) ) // check own action map for toolbar items
1030 id -= PYQT_ACTION_TOOLBAL;
1031 if ( myPopupActionList.contains( a ) ) // check own action map for popup items
1032 id -= PYQT_ACTION_POPUP;
1035 QAction* SALOME_PYQT_Module::createAction( const int id, const QString& text, const QString& icon,
1036 const QString& menu, const QString& tip, const int key,
1040 if ( !icon.isEmpty() ) {
1041 QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(""), tr( icon ) );
1042 if ( !pixmap.isNull() )
1043 anIcon = QIconSet( pixmap );
1045 return SalomeApp_Module::createAction( id, text, anIcon, menu, tip, key, getApp()->desktop(), toggle, this, SLOT( onGUIEvent() ) );
1049 //=============================================================================
1050 // SALOME_PYQT_XmlHandler class implementation
1051 //=============================================================================
1053 // gets an tag name for the dom element [ static ]
1054 // returns an empty string if the element does not have tag name
1055 static QString tagName( const QDomElement& element ) {
1056 return element.tagName().stripWhiteSpace();
1059 // gets an attribute by it's name for the dom element [ static ]
1060 // returns an empty string if the element does not have such attribute
1061 static QString attribute( const QDomElement& element, const QString& attName ) {
1062 return element.attribute( attName ).stripWhiteSpace();
1065 // checks the given value for the boolean value [ static ]
1066 // returns TRUE if string is "true", "yes" or "1"
1067 static bool checkBool( const QString& value ) {
1068 return ( value == "true" || value == "yes" || value == "1" );
1071 // checks the given value for the integer value [ static ]
1072 // returns -1 if item is empty or presents and invalid number
1073 static int checkInt( const QString& value )
1075 return value.isEmpty() ? -1 : value.toInt();
1081 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
1082 : myModule( module )
1084 QFile aFile( fileName );
1085 if ( !aFile.open( IO_ReadOnly ) )
1087 if ( !myDoc.setContent( &aFile ) ) {
1095 Called by SALOME_PYQT_Module::initialize() in order to create actions
1096 (menus, toolbars, popup menus)
1098 void SALOME_PYQT_XmlHandler::createActions()
1100 // get document element
1101 QDomElement aDocElem = myDoc.documentElement();
1103 // get main menu actions
1104 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
1105 for ( int i = 0; i < aMenuList.count(); i++ ) {
1106 QDomNode n = aMenuList.item( i );
1110 // create toolbars actions
1111 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
1112 for ( int i = 0; i < aToolsList.count(); i++ ) {
1113 QDomNode n = aToolsList.item( i );
1119 * Creates popup menu
1121 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
1122 const QString& context,
1123 const QString& parent,
1124 const QString& object )
1126 // get document element
1127 QDomElement aDocElem = myDoc.documentElement();
1129 // get popup menus actions
1130 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
1131 for ( int i = 0; i < aPopupList.count(); i++ ) {
1132 QDomNode n = aPopupList.item( i );
1133 if ( !n.isNull() && n.isElement() ) {
1134 QDomElement e = n.toElement();
1135 QString lab = attribute( e, "label-id" );
1136 QString ctx = attribute( e, "context-id" );
1137 QString prt = attribute( e, "parent-id" );
1138 QString obj = attribute( e, "object-id" );
1139 if ( ctx == context && prt == parent && obj == object ) {
1140 insertPopupItems( n, menu );
1148 Create main menu with child actions
1150 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
1155 if ( parentNode.isNull() )
1158 QDomElement parentElement = parentNode.toElement();
1159 if ( !parentElement.isNull() ) {
1160 QString plabel = attribute( parentElement, "label-id" );
1161 int pid = checkInt( attribute( parentElement, "item-id" ) );
1162 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
1163 if ( !plabel.isEmpty() ) {
1165 int menuId = myModule->createMenu( plabel, // label
1166 parentMenuId, // parent menu ID, should be -1 for main menu
1170 QDomNode node = parentNode.firstChild();
1171 while ( !node.isNull() ) {
1172 if ( node.isElement() ) {
1173 QDomElement elem = node.toElement();
1174 QString aTagName = tagName( elem );
1175 if ( aTagName == "popup-item" ) {
1176 int id = checkInt( attribute( elem, "item-id" ) );
1177 int pos = checkInt( attribute( elem, "pos-id" ) );
1178 QString label = attribute( elem, "label-id" );
1179 QString icon = attribute( elem, "icon-id" );
1180 QString tooltip = attribute( elem, "tooltip-id" );
1181 QString accel = attribute( elem, "accel-id" );
1182 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1183 ////QString execute = attribute( elem, "execute-action" ); // not used
1185 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1186 // also check if the action with given ID is already created
1187 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1188 // little trick to have several actions with same ID for menus and toolbars
1189 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1190 // create menu action
1191 QAction* action = myModule->createAction( id, // ID
1195 tooltip, // status-bar text
1196 QKeySequence( accel ), // keyboard accelerator
1197 toggle ); // toogled action
1198 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1199 myModule->createMenu( action, menuId, -1, 80, pos );
1202 else if ( aTagName == "submenu" ) {
1204 createMenu( node, menuId );
1206 else if ( aTagName == "separator" ) {
1207 // create menu separator
1208 int pos = checkInt( attribute( elem, "pos-id" ) );
1209 QAction* action = myModule->createSeparator();
1210 myModule->createMenu( action, menuId, -1, 80, pos );
1213 node = node.nextSibling();
1220 Create a toolbar with child actions
1222 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1227 if ( parentNode.isNull() )
1230 QDomElement parentElement = parentNode.toElement();
1231 if ( !parentElement.isNull() ) {
1232 QString aLabel = attribute( parentElement, "label-id" );
1233 if ( !aLabel.isEmpty() ) {
1235 int tbId = myModule->createTool( aLabel );
1236 QDomNode node = parentNode.firstChild();
1237 while ( !node.isNull() ) {
1238 if ( node.isElement() ) {
1239 QDomElement elem = node.toElement();
1240 QString aTagName = tagName( elem );
1241 if ( aTagName == "toolbutton-item" ) {
1242 int id = checkInt( attribute( elem, "item-id" ) );
1243 int pos = checkInt( attribute( elem, "pos-id" ) );
1244 QString label = attribute( elem, "label-id" );
1245 QString icon = attribute( elem, "icon-id" );
1246 QString tooltip = attribute( elem, "tooltip-id" );
1247 QString accel = attribute( elem, "accel-id" );
1248 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1249 ////QString execute = attribute( elem, "execute-action" ); // not used
1251 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1252 // also check if the action with given ID is already created
1253 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1254 // little trick to have several actions with same ID for menus and toolbars
1255 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1256 // create toolbar action
1257 QAction* action = myModule->createAction( id, // ID
1261 tooltip, // status-bar text
1262 QKeySequence( accel ), // keyboard accelerator
1263 toggle ); // toogled action
1264 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1265 myModule->createTool( action, tbId, -1, pos );
1268 else if ( aTagName == "separatorTB" ) {
1269 // create toolbar separator
1270 int pos = checkInt( attribute( elem, "pos-id" ) );
1271 QAction* action = myModule->createSeparator();
1272 myModule->createTool( action, tbId, -1, pos );
1275 node = node.nextSibling();
1281 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1286 if ( parentNode.isNull() )
1289 // we create popup menus without help of QtxPopupMgr
1290 QDomNode node = parentNode.firstChild();
1291 while ( !node.isNull() ) {
1292 if ( node.isElement() ) {
1293 QDomElement elem = node.toElement();
1294 QString aTagName = tagName( elem );
1295 if ( aTagName == "popup-item" ) {
1296 // insert a command item
1297 int id = checkInt( attribute( elem, "item-id" ) );
1298 int pos = checkInt( attribute( elem, "pos-id" ) );
1299 QString label = attribute( elem, "label-id" );
1300 QString icon = attribute( elem, "icon-id" );
1301 /////QString tooltip = attribute( elem, "tooltip-id" ); // not used
1302 QString accel = attribute( elem, "accel-id" );
1303 /////bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1304 /////QString execute = attribute( elem, "execute-action" ); // not used
1307 if ( !icon.isEmpty() ) {
1308 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1309 if ( !pixmap.isNull() )
1310 anIcon = QIconSet( pixmap );
1313 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1314 // also check if the action with given ID is already created
1316 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent(int) ), QKeySequence( accel ), id, pos );
1319 else if ( aTagName == "submenu" ) {
1321 int id = checkInt( attribute( elem, "item-id" ) );
1322 int pos = checkInt( attribute( elem, "pos-id" ) );
1323 QString label = attribute( elem, "label-id" );
1324 QString icon = attribute( elem, "icon-id" );
1327 if ( !icon.isEmpty() ) {
1328 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1329 if ( !pixmap.isNull() )
1330 anIcon = QIconSet( pixmap );
1333 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1334 menu->insertItem( anIcon, label, newPopup, id, pos );
1335 insertPopupItems( node, newPopup );
1337 else if ( aTagName == "separator" ) {
1338 // create menu separator
1339 int pos = checkInt( attribute( elem, "pos-id" ) );
1340 menu->insertSeparator( pos );
1343 node = node.nextSibling();