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"
18 #include "SalomeApp_DataModel.h"
20 #include "QtxWorkstack.h"
21 #include <SALOME_LifeCycleCORBA.hxx>
25 #include <qworkspace.h>
26 #include <qpopupmenu.h>
28 #if QT_VERSION > 0x030005
29 #include "sipAPISalomePyQtGUI.h"
31 #include "sipSalomePyQtGUIDeclSalomePyQtGUI.h"
34 #include <sipqtQWidget.h>
35 #include <sipqtQPopupMenu.h>
39 // Default name of the module, should be replaced at the moment
41 #define __DEFAULT_NAME__ "SALOME_PYQT_Module"
42 // Comment this define to block invoking of obsolete Python module's
43 // methods like setSetting(), definePopup(), etc.
44 #define __CALL_OLD_METHODS__
46 //=============================================================================
47 // General rule for Python requests created by SALOME_PYQT_Module:
48 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
49 // However, it is obligatory that ANY Python call is wrapped with a request object,
50 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
51 //=============================================================================
54 //=============================================================================
55 // The default PyQt module data model.
56 // Reuses common data model from SalomeApp.
57 //=============================================================================
58 class SALOME_PYQT_DataModel: public SalomeApp_DataModel
61 SALOME_PYQT_DataModel( CAM_Module* theModule ) : SalomeApp_DataModel( theModule ) {}
62 bool isModified() const { return false; }
63 bool isSaved() const { return false; }
66 //=============================================================================
67 // The class for parsing of the XML resource files.
68 // Used for backward compatibility with existing Python modules.
69 //=============================================================================
70 class SALOME_PYQT_XmlHandler
73 SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
75 void createPopup ( QPopupMenu* menu,
76 const QString& context,
77 const QString& parent,
78 const QString& object );
81 void createToolBar ( QDomNode& parentNode );
82 void createMenu ( QDomNode& parentNode,
83 const int parentMenuId = -1 );
85 void insertPopupItems( QDomNode& parentNode,
89 SALOME_PYQT_Module* myModule;
93 //=============================================================================
94 // SALOME_PYQT_Module class implementation (implements CAM_Module API for
95 // all Python-based SALOME module
96 //=============================================================================
98 // While the SalomePyQtGUI library is not imported in Python it's initialization function
99 // should be called manually (and only once) in order to initialize global sip data
100 #if defined(SIP_STATIC_MODULE)
101 extern "C" void initSalomePyQtGUI();
103 PyMODINIT_FUNC initSalomePyQtGUI();
107 * This function creates an instance of SALOME_PYQT_Module object by request
108 * of and application object when the module is loaded.
111 SALOME_PYQT_EXPORT CAM_Module* createModule() {
112 static bool alreadyInitialized = false;
113 if ( !alreadyInitialized ) {
114 // call only once (see above) !
116 alreadyInitialized = !alreadyInitialized;
118 return new SALOME_PYQT_Module();
123 * Static variables definition
125 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
130 SALOME_PYQT_Module::SALOME_PYQT_Module() :
131 SalomeApp_Module( __DEFAULT_NAME__ ), myModule( 0 ), myXmlHandler ( 0 )
133 myMenuActionList.setAutoDelete( false );
134 myPopupActionList.setAutoDelete( false );
135 myToolbarActionList.setAutoDelete( false );
141 SALOME_PYQT_Module::~SALOME_PYQT_Module()
143 myMenuActionList.clear();
144 myPopupActionList.clear();
145 myToolbarActionList.clear();
151 * Creates data model for the module.
152 * Reimplemented from CAM_Module.
154 CAM_DataModel* SALOME_PYQT_Module::createDataModel()
156 // VSR: this is a temporary solution :
157 // should reuse default data model from SalomeApp
158 return new SALOME_PYQT_DataModel( this );
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 // activate menus, toolbars, etc
232 setMenuShown( true );
233 setToolShown( true );
235 // ActivateReq: request class for internal activate() operation
236 class ActivateReq : public PyInterp_Request
239 ActivateReq( SUIT_Study* _study,
240 SALOME_PYQT_Module* _obj )
241 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
246 virtual void execute()
248 myObj->activate( myStudy );
253 SALOME_PYQT_Module* myObj;
256 // Posting the request
257 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
259 // connect desktop activation signal
260 connect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
266 * Deactivation of the module.
267 * Inherited from CAM_Module.
269 bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
271 MESSAGE( "SALOME_PYQT_Module::deactivateModule" );
273 bool res = SalomeApp_Module::deactivateModule( theStudy );
275 // deactivate menus, toolbars, etc
276 setMenuShown( false );
277 setToolShown( false );
279 // DeactivateReq: request class for internal deactivate() operation
280 class DeactivateReq : public PyInterp_LockRequest
283 DeactivateReq( PyInterp_base* _py_interp,
285 SALOME_PYQT_Module* _obj )
286 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
291 virtual void execute()
293 myObj->deactivate( myStudy );
298 SALOME_PYQT_Module* myObj;
301 // Posting the request
302 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
304 // disconnect desktop activation signal
305 disconnect( application()->desktop(), SIGNAL( activated() ), this, SLOT( onDesktopActivated() ) );
311 * Processes GUI action (from main menu, toolbar or context popup menu)
313 void SALOME_PYQT_Module::onGUIEvent()
316 const QObject* obj = sender();
317 if ( !obj || !obj->inherits( "QAction" ) )
319 QAction* action = (QAction*)obj;
322 int id = actionId( action );
323 if ( myMenuActionList.contains( action ) )
324 id -= PYQT_ACTION_MENU;
325 if ( myToolbarActionList.contains( action ) )
326 id -= PYQT_ACTION_TOOLBAL;
327 if ( myPopupActionList.contains( action ) )
328 id -= PYQT_ACTION_POPUP;
329 MESSAGE( "SALOME_PYQT_Module::onGUIEvent: id = " << id );
331 // perform synchronous request to Python event dispatcher
332 class GUIEvent : public PyInterp_LockRequest
335 GUIEvent( PyInterp_base* _py_interp,
336 SALOME_PYQT_Module* _obj,
338 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
343 virtual void execute()
345 myObj->guiEvent( myId );
350 SALOME_PYQT_Module* myObj;
353 // Posting the request
354 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
358 * Desktop activation slot. Used for notifying about changing of the active study.
360 void SALOME_PYQT_Module::onDesktopActivated()
362 // StudyChangedReq: request class for internal studyChanged() operation
363 class StudyChangedReq : public PyInterp_Request
366 StudyChangedReq( SUIT_Study* _study,
367 SALOME_PYQT_Module* _obj )
368 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
373 virtual void execute()
375 myObj->studyChanged( myStudy );
380 SALOME_PYQT_Module* myObj;
383 // Posting the request
384 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
388 Context popup menu request.
389 Called when user activates popup menu in some window (view, object browser, etc).
391 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext, QPopupMenu* thePopupMenu, QString& /*title*/ )
393 MESSAGE( "SALOME_PYQT_Module::contextMenuPopup : " << theContext.latin1() );
394 // perform synchronous request to Python event dispatcher
395 class PopupMenuEvent : public PyInterp_LockRequest
398 PopupMenuEvent( PyInterp_base* _py_interp,
399 SALOME_PYQT_Module* _obj,
400 const QString& _context,
402 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
403 myContext( _context ),
408 virtual void execute()
410 myObj->contextMenu( myContext, myPopup );
414 SALOME_PYQT_Module* myObj;
419 // Posting the request only if dispatcher is not busy!
420 // Executing the request synchronously
421 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
422 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
426 * Defines the dockable window associated with the module.
427 * To fill the list of windows the correspondind Python module's windows()
428 * method is called from SALOME_PYQT_Module::init() method.
429 * By default, ObjectBrowser, PythonConsole and LogWindow are provided.
431 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
433 // First clear the output parameters
434 QMap<int, int>::ConstIterator it;
435 for ( it = myWindowsMap.begin(); it != myWindowsMap.end(); ++it ) {
436 mappa[ it.key() ] = it.data();
441 * Defines the compatible views which should be opened on module activation.
442 * To fill the list of views the correspondind Python module's views()
443 * method is called from SALOME_PYQT_Module::init() method.
444 * By default, the list is empty.
446 void SALOME_PYQT_Module::viewManagers( QStringList& listik ) const
448 for ( QStringList::ConstIterator it = myViewMgrList.begin(); it != myViewMgrList.end(); ++it ) {
449 listik.append( *it );
454 * Performs internal initialization
455 * - initializes/gets the Python interpreter (one per study)
456 * - imports the Python module
457 * - passes the workspace widget to the Python module
458 * - calls Python module's initialize() method
459 * - calls Python module's windows() method
460 * - calls Python module's views() method
462 void SALOME_PYQT_Module::init( CAM_Application* app )
464 // reset interpreter to NULL
468 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
472 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
475 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
477 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
478 initInterp( aStudyId );
482 // import Python GUI module
487 #ifdef __CALL_OLD_METHODS__
488 // call Python module's setWorkspace() method
490 #endif // __CALL_OLD_METHODS__
492 // then call Python module's initialize() method
493 // ... first get python lock
494 PyLockWrapper aLock = myInterp->GetLockWrapper();
495 // ... (the Python module is already imported)
496 // ... finally call Python module's initialize() method
497 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
499 // VSR: this method may not be implemented in Python module
503 // get the windows list from the Python module by calling windows() method
504 // ... first put default values
505 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::DockLeft );
506 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::DockBottom );
507 // VSR: LogWindow is not yet implemented
508 // myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::DockBottom );
510 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
512 // VSR: this method may not be implemented in Python module
516 myWindowsMap.clear();
517 if ( PyDict_Check( res1 ) ) {
521 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
522 // parse the return value
523 // it should be a map: {integer:integer}
525 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
526 aKey = PyInt_AsLong( key );
527 aValue = PyInt_AsLong( value );
528 myWindowsMap[ aKey ] = aValue;
533 // get the windows list from the Python module by calling views() method
534 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
536 // VSR: this method may not be implemented in Python module
540 // parse the return value
541 // result can be one string...
542 if ( PyString_Check( res2 ) ) {
543 myViewMgrList.append( PyString_AsString( res2 ) );
545 // ... or list of strings
546 else if ( PyList_Check( res2 ) ) {
547 int size = PyList_Size( res2 );
548 for ( int i = 0; i < size; i++ ) {
549 PyObject* value = PyList_GetItem( res2, i );
550 if( value && PyString_Check( value ) ) {
551 myViewMgrList.append( PyString_AsString( value ) );
559 * Performs internal activation:
560 * - initializes/gets the Python interpreter (one per study)
561 * - imports the Python GUI module
562 * - calls Python module's setSettings() method (obsolete function, used for compatibility with old code)
563 * or activate() method (for new modules)
565 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
568 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
569 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
571 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
572 initInterp( aStudyId );
576 // import Python GUI module
582 PyLockWrapper aLock = myInterp->GetLockWrapper();
584 #ifdef __CALL_OLD_METHODS__
585 // call Python module's setSettings() method (obsolete)
586 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
588 // VSR: this method may not be implemented in Python module
591 #endif // __CALL_OLD_METHODS__
593 // call Python module's activate() method (for the new modules)
594 PyObjWrapper res1( PyObject_CallMethod( myModule, "activate", "" ) );
596 // VSR: this method may not be implemented in Python module
602 * Performs internal deactivation:
603 * - calls Python module's deactivate() method
605 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
607 // check if the subinterpreter is initialized and Python module is imported
608 if ( !myInterp || !myModule ) {
609 // Error! Python subinterpreter should be initialized and module should be imported first!
612 // then call Python module's deactivate() method
613 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
615 // VSR: this method may not be implemented in Python module
621 * Called when active the study is actived (user brings its desktop to top)
622 * - initializes/gets the Python interpreter (one per study)
623 * - imports the Python GUI module
624 * - calls Python module's activeStudyChanged() method
626 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
629 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
630 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
632 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
633 initInterp( aStudyId );
637 // import Python GUI module
643 PyLockWrapper aLock = myInterp->GetLockWrapper();
645 // call Python module's activeStudyChanged() method
646 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
648 // VSR: this method may not be implemented in Python module
654 * Get module engine, returns nil var if engine is not found in LifeCycleCORBA
656 Engines::Component_var SALOME_PYQT_Module::getEngine() const
658 Engines::Component_var comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name( "" ) );
663 * Get module engine IOR, returns empty string if engine is not found in LifeCycleCORBA
665 QString SALOME_PYQT_Module::engineIOR() const
667 if ( !CORBA::is_nil( getEngine() ) )
668 return QString( getApp()->orb()->object_to_string( getEngine() ) );
669 return QString( "" );
673 * Processes context popup menu request
674 * - calls Python module's definePopup(...) method (obsolete function, used for compatibility with old code)
675 * to define the popup menu context
676 * - parses XML resourses file (if exists) and fills the popup menu with the items)
677 * - calls Python module's customPopup(...) method (obsolete function, used for compatibility with old code)
678 * to allow module to customize the popup menu
679 * - for new modules calls createPopupMenu() function to allow the modules to build the popup menu
680 * by using insertItem(...) Qt functions.
682 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QPopupMenu* thePopupMenu )
684 // Python interpreter should be initialized and Python module should be
686 if ( !myInterp || !myModule )
689 QString aContext( theContext ), aObject( "" ), aParent( "" );
690 #ifdef __CALL_OLD_METHODS__
691 // call definePopup() Python module's function
692 // this is obsolete function, used only for compatibility reasons
693 PyObjWrapper res(PyObject_CallMethod( myModule,
698 aParent.latin1() ) );
700 // VSR: this method may not be implemented in Python module
704 // parse return value
706 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
712 #endif // __CALL_OLD_METHODS__
714 // first try to create menu via XML parser:
715 // we create popup menus without help of QtxPopupMgr
717 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
719 PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QPopupMenu ) );
720 //PyObjWrapper sipPopup( sipMapCppToSelf( thePopupMenu, sipClass_QPopupMenu ) );
722 // then call Python module's createPopupMenu() method (for new modules)
723 PyObjWrapper res1( PyObject_CallMethod( myModule,
727 aContext.latin1() ) );
729 // VSR: this method may not be implemented in Python module
733 #ifdef __CALL_OLD_METHODS__
734 // call customPopup() Python module's function
735 // this is obsolete function, used only for compatibility reasons
736 PyObjWrapper res2( PyObject_CallMethod( myModule,
742 aParent.latin1() ) );
744 // VSR: this method may not be implemented in Python module
747 #endif // __CALL_OLD_METHODS__
751 * Processes GUI event
752 * - calls Python module's OnGUIEvent() method
754 void SALOME_PYQT_Module::guiEvent( const int theId )
756 // Python interpreter should be initialized and Python module should be
758 if ( !myInterp || !myModule )
761 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
769 * Initialises python subinterpreter (one per study)
771 void SALOME_PYQT_Module::initInterp( int theStudyId )
775 // Error! Study Id must not be 0!
779 // try to find the subinterpreter
780 if( myInterpMap.find( theStudyId ) != myInterpMap.end() ) {
782 myInterp = myInterpMap[ theStudyId ];
785 // not found - create a new one!
786 ///////////////////////////////////////////////////////////////////
787 // Attention: the creation of Python interpretor must be protected
788 // by a C++ Lock because of C threads
789 ///////////////////////////////////////////////////////////////////
790 myInterp = new SALOME_PYQT_PyInterp();
791 myInterp->initialize();
792 myInterpMap[ theStudyId ] = myInterp;
794 // import 'salome' module and call 'salome_init' method;
795 // do it only once on interpreter creation
796 // ... first get python lock
797 PyLockWrapper aLock = myInterp->GetLockWrapper();
798 // ... then import a module
799 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
805 // ... then call a method
806 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "" ) );
815 * Imports Python GUI module and remember the reference to the module
816 * !!! initInterp() should be called first!!!
818 void SALOME_PYQT_Module::importModule()
820 // check if the subinterpreter is initialized
822 // Error! Python subinterpreter should be initialized first!
826 // import Python GUI module and puts it in <myModule> attribute
827 // ... first get python lock
828 PyLockWrapper aLock = myInterp->GetLockWrapper();
829 // ... then import a module
830 QString aMod = QString( name("") ) + "GUI";
831 myModule = PyImport_ImportModule( (char*)( aMod.latin1() ) );
840 * Calls <module>.setWorkSpace() method with PyQt QWidget object to use with
842 * !!! initInterp() and importModule() should be called first!!!
844 void SALOME_PYQT_Module::setWorkSpace()
846 // check if the subinterpreter is initialized and Python module is imported
847 if ( !myInterp || !myModule ) {
848 // Error! Python subinterpreter should be initialized and module should be imported first!
852 // call setWorkspace() method
853 // ... first get python lock
854 PyLockWrapper aLock = myInterp->GetLockWrapper();
856 // ... then try to import SalomePyQt module. If it's not possible don't go on.
857 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
863 #ifdef __CALL_OLD_METHODS__
864 // ... then get workspace object
865 QWidget* aWorkspace = 0;
866 if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
867 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
869 aWorkspace = aDesktop->workspace();
871 else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
872 STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
874 aWorkspace = aDesktop->workstack();
876 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget ) );
877 //PyObjWrapper pyws( sipMapCppToSelfSubClass( aWorkspace, sipClass_QWidget ) );
878 // ... and finally call Python module's setWorkspace() method (obsolete)
879 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
881 // VSR: this method may not be implemented in Python module
885 #endif // __CALL_OLD_METHODS__
889 * Adds an action into private action list [internal usage]
891 void SALOME_PYQT_Module::addAction( const PyQtGUIAction type, QAction* action )
894 case PYQT_ACTION_MENU:
895 myMenuActionList.append( action );
897 case PYQT_ACTION_TOOLBAL:
898 myToolbarActionList.append( action );
900 case PYQT_ACTION_POPUP:
901 myPopupActionList.append( action );
906 //=============================================================================
907 // SALOME_PYQT_XmlHandler class implementation
908 //=============================================================================
910 // gets an tag name for the dom element [ static ]
911 // returns an empty string if the element does not have tag name
912 static QString tagName( const QDomElement& element ) {
913 return element.tagName().stripWhiteSpace();
916 // gets an attribute by it's name for the dom element [ static ]
917 // returns an empty string if the element does not have such attribute
918 static QString attribute( const QDomElement& element, const QString& attName ) {
919 return element.attribute( attName ).stripWhiteSpace();
922 // checks the given value for the boolean value [ static ]
923 // returns TRUE if string is "true", "yes" or "1"
924 static bool checkBool( const QString& value ) {
925 return ( value == "true" || value == "yes" || value == "1" );
928 // checks the given value for the integer value [ static ]
929 // returns -1 if item is empty or presents and invalid number
930 static int checkInt( const QString& value )
932 return value.isEmpty() ? -1 : value.toInt();
938 SALOME_PYQT_XmlHandler::SALOME_PYQT_XmlHandler( SALOME_PYQT_Module* module, const QString& fileName )
941 QFile aFile( fileName );
942 if ( !aFile.open( IO_ReadOnly ) )
944 if ( !myDoc.setContent( &aFile ) ) {
952 Called by SALOME_PYQT_Module::initialize() in order to create actions
953 (menus, toolbars, popup menus)
955 void SALOME_PYQT_XmlHandler::createActions()
957 // get document element
958 QDomElement aDocElem = myDoc.documentElement();
960 // get main menu actions
961 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
962 for ( int i = 0; i < aMenuList.count(); i++ ) {
963 QDomNode n = aMenuList.item( i );
967 // create toolbars actions
968 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
969 for ( int i = 0; i < aToolsList.count(); i++ ) {
970 QDomNode n = aToolsList.item( i );
978 void SALOME_PYQT_XmlHandler::createPopup( QPopupMenu* menu,
979 const QString& context,
980 const QString& parent,
981 const QString& object )
983 // get document element
984 QDomElement aDocElem = myDoc.documentElement();
986 // get popup menus actions
987 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
988 for ( int i = 0; i < aPopupList.count(); i++ ) {
989 QDomNode n = aPopupList.item( i );
990 if ( !n.isNull() && n.isElement() ) {
991 QDomElement e = n.toElement();
992 QString lab = attribute( e, "label-id" );
993 QString ctx = attribute( e, "context-id" );
994 QString prt = attribute( e, "parent-id" );
995 QString obj = attribute( e, "object-id" );
996 if ( ctx == context && prt == parent && obj == object ) {
997 insertPopupItems( n, menu );
1005 Create main menu with child actions
1007 void SALOME_PYQT_XmlHandler::createMenu( QDomNode& parentNode, const int parentMenuId )
1012 if ( parentNode.isNull() )
1015 QDomElement parentElement = parentNode.toElement();
1016 if ( !parentElement.isNull() ) {
1017 QString plabel = attribute( parentElement, "label-id" );
1018 int pid = checkInt( attribute( parentElement, "item-id" ) );
1019 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
1020 if ( !plabel.isEmpty() ) {
1022 int menuId = myModule->createMenu( plabel, // label
1023 parentMenuId, // parent menu ID, should be -1 for main menu
1027 QDomNode node = parentNode.firstChild();
1028 while ( !node.isNull() ) {
1029 if ( node.isElement() ) {
1030 QDomElement elem = node.toElement();
1031 QString aTagName = tagName( elem );
1032 if ( aTagName == "popup-item" ) {
1033 int id = checkInt( attribute( elem, "item-id" ) );
1034 int pos = checkInt( attribute( elem, "pos-id" ) );
1035 QString label = attribute( elem, "label-id" );
1036 QString icon = attribute( elem, "icon-id" );
1037 QString tooltip = attribute( elem, "tooltip-id" );
1038 QString accel = attribute( elem, "accel-id" );
1039 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1040 QString execute = attribute( elem, "execute-action" ); // not used
1043 if ( !icon.isEmpty() ) {
1044 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1045 if ( !pixmap.isNull() )
1046 anIcon = QIconSet( pixmap );
1049 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1050 // also check if the action with given ID is already created
1051 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_MENU + id ) ) {
1052 // little trick to have several actions with same ID for menus and toolbars
1053 id = SALOME_PYQT_Module::PYQT_ACTION_MENU + id;
1054 // create menu action
1055 QAction* action = myModule->createAction( id, // ID
1059 tooltip, // status-bar text
1060 QKeySequence( accel ), // keyboard accelerator
1061 myModule->getApp()->desktop(), // desktop
1062 toggle, // toogled action
1063 myModule, // receiver
1064 SLOT( onGUIEvent() ) ); // slot
1065 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_MENU, action );
1066 myModule->createMenu( action, menuId, -1, 100, pos );
1069 else if ( aTagName == "submenu" ) {
1071 createMenu( node, menuId );
1073 else if ( aTagName == "separator" ) {
1074 // create menu separator
1075 int pos = checkInt( attribute( elem, "pos-id" ) );
1076 QAction* action = myModule->separator();
1077 myModule->createMenu( action, menuId, -1, 100, pos );
1080 node = node.nextSibling();
1087 Create a toolbar with child actions
1089 void SALOME_PYQT_XmlHandler::createToolBar( QDomNode& parentNode )
1094 if ( parentNode.isNull() )
1097 QDomElement parentElement = parentNode.toElement();
1098 if ( !parentElement.isNull() ) {
1099 QString aLabel = attribute( parentElement, "label-id" );
1100 if ( !aLabel.isEmpty() ) {
1102 int tbId = myModule->createTool( aLabel );
1103 QDomNode node = parentNode.firstChild();
1104 while ( !node.isNull() ) {
1105 if ( node.isElement() ) {
1106 QDomElement elem = node.toElement();
1107 QString aTagName = tagName( elem );
1108 if ( aTagName == "toolbutton-item" ) {
1109 int id = checkInt( attribute( elem, "item-id" ) );
1110 int pos = checkInt( attribute( elem, "pos-id" ) );
1111 QString label = attribute( elem, "label-id" );
1112 QString icon = attribute( elem, "icon-id" );
1113 QString tooltip = attribute( elem, "tooltip-id" );
1114 QString accel = attribute( elem, "accel-id" );
1115 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
1116 QString execute = attribute( elem, "execute-action" ); // not used
1119 if ( !icon.isEmpty() ) {
1120 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1121 if ( !pixmap.isNull() )
1122 anIcon = QIconSet( pixmap );
1125 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1126 // also check if the action with given ID is already created
1127 if ( id != -1 && !myModule->action( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id ) ) {
1128 // little trick to have several actions with same ID for menus and toolbars
1129 id = SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL + id;
1130 // create toolbar action
1131 QAction* action = myModule->createAction( id, // ID
1135 tooltip, // status-bar text
1136 QKeySequence( accel ), // keyboard accelerator
1137 myModule->getApp()->desktop(), // desktop
1138 toggle, // toogled action
1139 myModule, // receiver
1140 SLOT( onGUIEvent() ) ); // slot
1141 myModule->addAction( SALOME_PYQT_Module::PYQT_ACTION_TOOLBAL, action );
1142 myModule->createTool( action, tbId, -1, pos );
1145 else if ( aTagName == "separatorTB" ) {
1146 // create toolbar separator
1147 int pos = checkInt( attribute( elem, "pos-id" ) );
1148 QAction* action = myModule->separator();
1149 myModule->createTool( action, tbId, -1, pos );
1152 node = node.nextSibling();
1158 void SALOME_PYQT_XmlHandler::insertPopupItems( QDomNode& parentNode, QPopupMenu* menu )
1163 if ( parentNode.isNull() )
1166 // we create popup menus without help of QtxPopupMgr
1167 QDomNode node = parentNode.firstChild();
1168 while ( !node.isNull() ) {
1169 if ( node.isElement() ) {
1170 QDomElement elem = node.toElement();
1171 QString aTagName = tagName( elem );
1172 if ( aTagName == "popup-item" ) {
1173 // insert a command item
1174 int id = checkInt( attribute( elem, "item-id" ) );
1175 int pos = checkInt( attribute( elem, "pos-id" ) );
1176 QString label = attribute( elem, "label-id" );
1177 QString icon = attribute( elem, "icon-id" );
1178 QString tooltip = attribute( elem, "tooltip-id" ); // not used
1179 QString accel = attribute( elem, "accel-id" );
1180 bool toggle = checkBool( attribute( elem, "toggle-id" ) ); // not used
1181 QString execute = attribute( elem, "execute-action" ); // not used
1184 if ( !icon.isEmpty() ) {
1185 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1186 if ( !pixmap.isNull() )
1187 anIcon = QIconSet( pixmap );
1190 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
1191 // also check if the action with given ID is already created
1193 menu->insertItem( anIcon, label, myModule, SLOT( onGUIEvent() ), QKeySequence( accel ), id, pos );
1196 else if ( aTagName == "submenu" ) {
1198 int id = checkInt( attribute( elem, "item-id" ) );
1199 int pos = checkInt( attribute( elem, "pos-id" ) );
1200 QString label = attribute( elem, "label-id" );
1201 QString icon = attribute( elem, "icon-id" );
1204 if ( !icon.isEmpty() ) {
1205 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(""), icon );
1206 if ( !pixmap.isNull() )
1207 anIcon = QIconSet( pixmap );
1210 QPopupMenu* newPopup = new QPopupMenu( menu, label );
1211 menu->insertItem( anIcon, label, newPopup, id, pos );
1212 insertPopupItems( node, newPopup );
1214 else if ( aTagName == "separator" ) {
1215 // create menu separator
1216 int pos = checkInt( attribute( elem, "pos-id" ) );
1217 menu->insertSeparator( pos );
1220 node = node.nextSibling();