1 // Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 // File : SALOME_PYQT_Module.cxx
23 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
26 #include "SALOME_PYQT_Module.h"
28 #include <PyInterp_Dispatcher.h>
30 #include <SUIT_ResourceMgr.h>
31 #include <SUIT_Desktop.h>
32 #include <SUIT_ViewModel.h>
33 #include <SUIT_ViewWindow.h>
34 #include <SUIT_ViewManager.h>
35 #include <STD_MDIDesktop.h>
36 #include <STD_TabDesktop.h>
37 #include <LightApp_Preferences.h>
38 #include <SalomeApp_Application.h>
39 #include <SalomeApp_Study.h>
41 #include <QtxWorkstack.h>
42 #include <QtxWorkspace.h>
43 #include <QtxActionGroup.h>
44 #include <QtxActionMenuMgr.h>
45 #include <QtxActionToolMgr.h>
47 #include <SALOME_LifeCycleCORBA.hxx>
48 #include <Container_init_python.hxx>
51 #include <QDomDocument>
53 #include <QDomElement>
58 #include "sipAPISalomePyQtGUI.h"
61 #if SIP_VERSION < 0x040700
62 #include "sipQtGuiQWidget.h"
63 #include "sipQtGuiQMenu.h"
67 \brief Default name of the module, replaced at the moment
71 const char* DEFAULT_NAME = "SALOME_PYQT_Module";
74 \brief Default menu group number.
77 const int DEFAULT_GROUP = 40;
81 \brief Allow calling obsolete callback methods.
84 If the macro CALL_OLD_METHODS is not defined, the invoking
85 of obsolete Python module's methods like setSetting(), definePopup(),
88 CALL_OLD_METHODS macro can be defined for example by adding
89 -DCALL_OLD_METHODS compilation option to the Makefile.
91 #ifdef CALL_OLD_METHODS
92 const bool IsCallOldMethods = true;
94 const bool IsCallOldMethods = false;
97 /* Py_ssize_t for old Pythons */
98 /* This code is as recommended by: */
99 /* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */
100 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
101 typedef int Py_ssize_t;
102 # define PY_SSIZE_T_MAX INT_MAX
103 # define PY_SSIZE_T_MIN INT_MIN
107 // NB: Python requests.
108 // General rule for Python requests created by SALOME_PYQT_Module:
109 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
110 // However, it is obligatory that ANY Python call is wrapped with a request object,
111 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
115 \class SALOME_PYQT_Module::XmlHandler
116 \brief XML resource files parser.
119 This class is used to provide backward compatibility with
120 existing Python modules in which obsolete menu definition system
121 (via XML files) is used.
124 class SALOME_PYQT_Module::XmlHandler
127 XmlHandler( SALOME_PYQT_Module* module, const QString& fileName );
128 void createActions();
129 void createPopup ( QMenu* menu,
130 const QString& context,
131 const QString& parent,
132 const QString& object );
133 void activateMenus( bool );
136 void createToolBar ( QDomNode& parentNode );
137 void createMenu ( QDomNode& parentNode,
138 const int parentMenuId = -1,
139 QMenu* parentPopup = 0 );
141 void insertPopupItems( QDomNode& parentNode,
145 SALOME_PYQT_Module* myModule;
147 QList<int> myMenuItems;
151 // NB: Library initialization
152 // Since the SalomePyQtGUI library is not imported in Python it's initialization function
153 // should be called manually (and only once) in order to initialize global sip data
154 // and to get C API from sip : sipBuildResult for example
156 #define INIT_FUNCTION initSalomePyQtGUI
157 #if defined(SIP_STATIC_MODULE)
158 extern "C" void INIT_FUNCTION();
160 PyMODINIT_FUNC INIT_FUNCTION();
164 \fn CAM_Module* createModule()
165 \brief Module factory function.
168 Creates an instance of SALOME_PYQT_Module object by request
169 of an application when the module is loaded and initialized.
171 \return new module object
175 SALOME_PYQT_EXPORT CAM_Module* createModule() {
176 static bool alreadyInitialized = false;
177 if ( !alreadyInitialized ) {
178 // call only once (see comment above) !
179 PyEval_RestoreThread( KERNEL_PYTHON::_gtstate );
181 PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
182 alreadyInitialized = !alreadyInitialized;
184 return new SALOME_PYQT_Module();
190 \brief Function call in/out tracer.
197 FuncMsg( const QString& funcName )
200 MESSAGE( myName.toLatin1().constData() << " [ begin ]" );
204 MESSAGE( myName.toLatin1().constData() << " [ end ]" );
206 void message( const QString& msg )
208 MESSAGE( myName.toLatin1().constData() << " : " << msg.toLatin1().constData() );
215 \class SALOME_PYQT_Module
216 \brief This class implements module API for all the Python-based
221 // Static variables definition
223 SALOME_PYQT_Module::InterpMap SALOME_PYQT_Module::myInterpMap;
224 SALOME_PYQT_Module* SALOME_PYQT_Module::myInitModule = 0;
227 \brief Get the module being initialized.
229 This is a little trick :) needed to provide an access from Python
230 (SalomePyQt) to the module being currently activated. The problem
231 that during the process of module initialization (initialize()
232 function) it is not yet available via application->activeModule()
235 This method returns valid pointer only if called in scope of
236 initialize() function.
238 \return the module being currently initialized
240 SALOME_PYQT_Module* SALOME_PYQT_Module::getInitModule()
248 SALOME_PYQT_Module::SALOME_PYQT_Module()
249 : SalomeApp_Module( DEFAULT_NAME ),
253 myLastActivateStatus( true )
260 SALOME_PYQT_Module::~SALOME_PYQT_Module()
267 \brief Initialization of the module.
269 This method can be used for creation of the menus, toolbars and
272 There are two ways to do this:
273 - for obsolete modules this method first tries to read
274 <module>_<language>.xml resource file which contains a menu,
275 toolbars and popup menus description;
276 - new modules can create menus by direct calling of the
277 corresponding methods of SalomePyQt Python API in the Python
278 module's initialize() method which is called from here.
280 NOTE: SALOME supports two modes of modules loading:
281 - immediate (all the modules are created and initialized
282 immediately when the application object is created;
283 - postponed modules loading (used currently); in this mode
284 the module is loaded only be request.
285 If postponed modules loading is not used, the active
286 study might be not yet defined at this stage, so initialize()
287 method should not perform any study-based initialization.
289 \param app parent application object
291 void SALOME_PYQT_Module::initialize( CAM_Application* app )
293 FuncMsg fmsg( "SALOME_PYQT_Module::initialize()" );
295 // call base implementation
296 SalomeApp_Module::initialize( app );
298 // try to get XML resource file name
299 SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
300 if ( !myXmlHandler && aResMgr ) {
301 // get current language
302 QString aLang = aResMgr->stringValue( "language", "language", QString() );
303 if ( aLang.isEmpty() )
305 // define resource file name
306 QString aFileName = name() + "_" + aLang + ".xml";
307 aFileName = aResMgr->path( "resources", name(), aFileName );
308 // create XML handler instance
309 if ( !aFileName.isEmpty() && QFile::exists( aFileName ) )
310 myXmlHandler = new SALOME_PYQT_Module::XmlHandler( this, aFileName );
311 // create menus & toolbars from XML file if required
313 myXmlHandler->createActions();
316 // perform internal initialization and call module's initialize() funtion
317 // InitializeReq: request class for internal init() operation
318 class InitializeReq : public PyInterp_Request
321 InitializeReq( CAM_Application* _app,
322 SALOME_PYQT_Module* _obj )
323 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
328 virtual void execute()
330 myObj->init( myApp );
334 CAM_Application* myApp;
335 SALOME_PYQT_Module* myObj;
339 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
343 \brief Activation of the module.
345 This function is usually used in order to show the module's
346 specific menus and toolbars, update actions state and perform
347 other such actions required when the module is activated.
349 Note, that returning \c false in this function prevents the
352 \param theStudy parent study
353 \return \c true if activation is successful and \c false otherwise
355 bool SALOME_PYQT_Module::activateModule( SUIT_Study* theStudy )
357 FuncMsg fmsg( "SALOME_PYQT_Module::activateModule()" );
359 // call base implementation
360 bool res = SalomeApp_Module::activateModule( theStudy );
365 // reset the activation status to the default value
366 myLastActivateStatus = true;
368 // perform internal activation
369 // ActivateReq: request class for internal activate() operation
370 class ActivateReq : public PyInterp_Request
373 ActivateReq( SUIT_Study* _study,
374 SALOME_PYQT_Module* _obj )
375 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
380 virtual void execute()
382 myObj->activate( myStudy );
387 SALOME_PYQT_Module* myObj;
391 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
393 // check activation status (set by activate())
394 if ( !lastActivationStatus() )
397 // activate menus, toolbars, etc
398 if ( myXmlHandler ) myXmlHandler->activateMenus( true );
399 setMenuShown( true );
400 setToolShown( true );
402 // connect preferences changing signal
403 connect( getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
404 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
406 // perform custom activation actions
407 // CustomizeReq: request class for internal customize() operation
408 class CustomizeReq : public PyInterp_Request
411 CustomizeReq( SUIT_Study* _study,
412 SALOME_PYQT_Module* _obj )
413 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
418 virtual void execute()
420 myObj->customize( myStudy );
425 SALOME_PYQT_Module* myObj;
429 PyInterp_Dispatcher::Get()->Exec( new CustomizeReq( theStudy, this ) );
435 \brief Deactivation of the module.
437 This function is usually used in order to hide the module's
438 specific menus and toolbars and perform other such actions
439 required when the module is deactivated.
441 \param theStudy parent study
442 \return \c true if deactivation is successful and \c false otherwise
444 bool SALOME_PYQT_Module::deactivateModule( SUIT_Study* theStudy )
446 FuncMsg fmsg( "SALOME_PYQT_Module::deactivateModule()" );
448 // disconnect preferences changing signal
449 disconnect( getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
450 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
452 // perform internal deactivation
453 // DeactivateReq: request class for internal deactivate() operation
454 class DeactivateReq : public PyInterp_LockRequest
457 DeactivateReq( PyInterp_Interp* _py_interp,
459 SALOME_PYQT_Module* _obj )
460 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
465 virtual void execute()
467 myObj->deactivate( myStudy );
472 SALOME_PYQT_Module* myObj;
476 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
478 // deactivate menus, toolbars, etc
479 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
480 setMenuShown( false );
481 setToolShown( false );
483 // call base implementation
484 return SalomeApp_Module::deactivateModule( theStudy );
488 \brief Get last activation status.
489 \return status of last module activation operation
492 bool SALOME_PYQT_Module::lastActivationStatus() const
494 return myLastActivateStatus;
498 \breif Process application preferences changing.
500 Called when any application setting is changed.
502 \param module preference module
503 \param section preference resource file section
504 \param setting preference resource name
506 void SALOME_PYQT_Module::preferenceChanged( const QString& module,
507 const QString& section,
508 const QString& setting )
510 FuncMsg fmsg( "SALOME_PYQT_Module::preferenceChanged()" );
512 // perform synchronous request to Python event dispatcher
513 class Event : public PyInterp_LockRequest
516 Event( PyInterp_Interp* _py_interp,
517 SALOME_PYQT_Module* _obj,
518 const QString& _section,
519 const QString& _setting )
520 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
522 mySection( _section ),
523 mySetting( _setting ) {}
526 virtual void execute()
528 myObj->prefChanged( mySection, mySetting );
532 SALOME_PYQT_Module* myObj;
533 QString mySection, mySetting;
536 if ( module != moduleName() ) {
537 // module's own preferences are processed by preferencesChanged() method
539 // post the request only if dispatcher is not busy!
540 // execute request synchronously
541 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
542 PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this, section, setting ) );
547 \brief Process study activation.
549 Called when study desktop is activated. Used for notifying the Python
550 module about changing of the active study.
552 void SALOME_PYQT_Module::studyActivated()
554 FuncMsg fmsg( "SALOME_PYQT_Module::studyActivated()" );
556 // StudyChangedReq: request class for internal studyChanged() operation
557 class StudyChangedReq : public PyInterp_Request
560 StudyChangedReq( SUIT_Study* _study,
561 SALOME_PYQT_Module* _obj )
562 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
567 virtual void execute()
569 myObj->studyChanged( myStudy );
574 SALOME_PYQT_Module* myObj;
578 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
582 \brief Process GUI action (from main menu, toolbar or
583 context popup menu action).
585 void SALOME_PYQT_Module::onGUIEvent()
587 FuncMsg fmsg( "SALOME_PYQT_Module::onGUIEvent()" );
590 QAction* action = qobject_cast<QAction*>( sender() );
595 int id = actionId( action );
596 fmsg.message( QString( "action id = %1" ).arg( id ) );
598 // perform synchronous request to Python event dispatcher
599 class GUIEvent : public PyInterp_LockRequest
602 GUIEvent( PyInterp_Interp* _py_interp,
603 SALOME_PYQT_Module* _obj,
605 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
610 virtual void execute()
612 myObj->guiEvent( myId );
617 SALOME_PYQT_Module* myObj;
621 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
625 \brief Process context popup menu request.
627 Called when user activates popup menu in some window
628 (view, object browser, etc).
630 \param theContext popup menu context (e.g. "ObjectBrowser")
631 \param thePopupMenu popup menu
632 \param title popup menu title (not used)
634 void SALOME_PYQT_Module::contextMenuPopup( const QString& theContext,
638 FuncMsg fmsg( "SALOME_PYQT_Module::contextMenuPopup()" );
639 fmsg.message( QString( "context: %1" ).arg( theContext ) );
641 // perform synchronous request to Python event dispatcher
642 class PopupMenuEvent : public PyInterp_LockRequest
645 PopupMenuEvent( PyInterp_Interp* _py_interp,
646 SALOME_PYQT_Module* _obj,
647 const QString& _context,
649 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
650 myContext( _context ),
655 virtual void execute()
657 myObj->contextMenu( myContext, myPopup );
661 SALOME_PYQT_Module* myObj;
666 // post request only if dispatcher is not busy!
667 // execute request synchronously
668 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
669 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
673 \brief Export preferences for the Python module.
675 Called only once when the first instance of the module is created.
677 void SALOME_PYQT_Module::createPreferences()
679 FuncMsg fmsg( "SALOME_PYQT_Module::createPreferences()" );
681 // perform synchronous request to Python event dispatcher
682 class Event : public PyInterp_LockRequest
685 Event( PyInterp_Interp* _py_interp,
686 SALOME_PYQT_Module* _obj )
687 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
691 virtual void execute()
693 myObj->initPreferences();
697 SALOME_PYQT_Module* myObj;
700 // post request only if dispatcher is not busy!
701 // execute request synchronously
702 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
703 PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this ) );
707 \brief Define the dockable windows associated with the module.
709 To fill the list of windows the correspondind Python module's windows()
710 method is called from SALOME_PYQT_Module::init() method.
712 By default, ObjectBrowser, PythonConsole and LogWindow windows are
713 associated to the module.
715 Allowed dockable windows:
716 - SalomeApp_Application::WT_ObjectBrowser : object browser
717 - SalomeApp_Application::WT_PyConsole : python console
718 - SalomeApp_Application::WT_LogWindow : log messages output window
720 Dock area is defined by Qt::DockWidgetArea enumeration:
721 - Qt::TopDockWidgetArea : top dock area
722 - Qt::BottomDockWidgetArea : bottom dock area
723 - Qt::LeftDockWidgetArea : left dock area
724 - Qt::RightDockWidgetArea : right dock area
726 \param mappa map of dockable windows: { <window_type> : <dock_area> }
728 void SALOME_PYQT_Module::windows( QMap<int, int>& mappa ) const
730 FuncMsg fmsg( "SALOME_PYQT_Module::windows()" );
732 mappa = myWindowsMap;
736 \brief Define the compatible view windows associated with the module.
738 The associated view windows are opened automatically when the module
741 To fill the list of views the correspondind Python module's views()
742 method is called from SALOME_PYQT_Module::init() method.
743 By default, the list is empty.
745 \param listik list of view windows types
747 void SALOME_PYQT_Module::viewManagers( QStringList& lst ) const
749 FuncMsg fmsg( "SALOME_PYQT_Module::viewManagers()" );
755 \brief Process module's preferences changing.
757 Called when the module's preferences are changed.
759 \param section setting section
760 \param setting setting name
762 void SALOME_PYQT_Module::preferencesChanged( const QString& section, const QString& setting )
764 FuncMsg fmsg( "SALOME_PYQT_Module::preferencesChanged()" );
766 // perform synchronous request to Python event dispatcher
767 class Event : public PyInterp_LockRequest
770 Event( PyInterp_Interp* _py_interp,
771 SALOME_PYQT_Module* _obj,
772 const QString& _section,
773 const QString& _setting )
774 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
776 mySection( _section ),
777 mySetting( _setting ) {}
780 virtual void execute()
782 myObj->prefChanged( mySection, mySetting );
786 SALOME_PYQT_Module* myObj;
787 QString mySection, mySetting;
790 // post request only if dispatcher is not busy!
791 // execut request synchronously
792 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
793 PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this, section, setting ) );
797 \brief Internal module initialization:
799 Performs the following actions:
800 - initialize or get the Python interpreter (one per study)
801 - import the Python module
802 - pass the workspace widget to the Python module
803 - call Python module's initialize() method
804 - call Python module's windows() method
805 - call Python module's views() method
807 \param app parent application object
809 void SALOME_PYQT_Module::init( CAM_Application* app )
811 FuncMsg fmsg( "SALOME_PYQT_Module::init()" );
813 // reset interpreter to NULL
817 SalomeApp_Application* anApp = dynamic_cast<SalomeApp_Application*>( app );
820 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( app->activeStudy() );
823 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
825 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
826 initInterp( aStudyId );
830 // import Python GUI module
835 // this module is being activated now!
838 // then call Python module's initialize() method
839 // ... first get python lock
840 PyLockWrapper aLock = myInterp->GetLockWrapper();
841 // ... (the Python module is already imported)
842 // ... finally call Python module's initialize() method
843 if ( PyObject_HasAttrString( myModule , "initialize" ) ) {
844 PyObjWrapper res( PyObject_CallMethod( myModule, "initialize", "" ) );
850 // get required dockable windows list from the Python module
851 // by calling windows() method
852 // ... first put default values
853 myWindowsMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
854 myWindowsMap.insert( SalomeApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
855 myWindowsMap.insert( SalomeApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
857 if ( PyObject_HasAttrString( myModule , "windows" ) ) {
858 PyObjWrapper res1( PyObject_CallMethod( myModule, "windows", "" ) );
863 myWindowsMap.clear();
864 if ( PyDict_Check( res1 ) ) {
868 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
869 // parse the return value
870 // it should be a map: {integer:integer}
872 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
873 aKey = PyInt_AsLong( key );
874 aValue = PyInt_AsLong( value );
875 myWindowsMap[ aKey ] = aValue;
882 // get compatible view windows types from the Python module
883 // by calling views() method
884 if ( PyObject_HasAttrString( myModule , "views" ) ) {
885 PyObjWrapper res2( PyObject_CallMethod( myModule, "views", "" ) );
890 // parse the return value
891 // result can be one string...
892 if ( PyString_Check( res2 ) ) {
893 myViewMgrList.append( PyString_AsString( res2 ) );
895 // ... or list of strings
896 else if ( PyList_Check( res2 ) ) {
897 int size = PyList_Size( res2 );
898 for ( int i = 0; i < size; i++ ) {
899 PyObject* value = PyList_GetItem( res2, i );
900 if( value && PyString_Check( value ) ) {
901 myViewMgrList.append( PyString_AsString( value ) );
907 // module is already activated!
912 \brief Internal activation:
914 Performs the following actions:
915 - initialize or get the Python interpreter (one per study)
916 - import the Python GUI module
917 - call Python module's activate() method
919 \param theStudy parent study object
921 void SALOME_PYQT_Module::activate( SUIT_Study* theStudy )
923 FuncMsg fmsg( "SALOME_PYQT_Module::activate()" );
926 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
927 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
929 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
930 initInterp( aStudyId );
934 // import Python GUI module
940 PyLockWrapper aLock = myInterp->GetLockWrapper();
942 // call Python module's activate() method (for the new modules)
943 if ( PyObject_HasAttrString( myModule , "activate" ) ) {
944 PyObject* res1 = PyObject_CallMethod( myModule, "activate", "" );
945 if ( !res1 || !PyBool_Check( res1 ) ) {
947 // always true for old modules (no return value)
948 myLastActivateStatus = true;
951 // detect return status
952 myLastActivateStatus = PyObject_IsTrue( res1 );
956 // Connect the SUIT_Desktop signal windowActivated() to this->onActiveViewChanged()
957 SUIT_Desktop* aDesk = theStudy->application()->desktop();
960 connect( aDesk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
961 this, SLOT( onActiveViewChanged( SUIT_ViewWindow* ) ) );
962 // If a active window exists send activeViewChanged
963 // If a getActiveView() in SalomePyQt available we no longer need this
964 SUIT_ViewWindow* aView = aDesk->activeWindow();
966 activeViewChanged( aView );
968 // get all view currently opened in the study and connect their signals to
969 // the corresponding slots of the class.
970 QList<SUIT_ViewWindow*> wndList = aDesk->windows();
971 SUIT_ViewWindow* wnd;
972 foreach ( wnd, wndList )
978 \brief Additional customization after module is activated:
980 Performs the following actions:
981 - get the Python interpreter (one per study)
982 - import the Python GUI module
983 - call Python module's setSettings() method (obsolete function,
984 used for compatibility with old code)
986 \param theStudy parent study object
988 void SALOME_PYQT_Module::customize( SUIT_Study* theStudy )
990 FuncMsg fmsg( "SALOME_PYQT_Module::customize()" );
993 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
994 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
996 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
997 initInterp( aStudyId );
1001 // import Python GUI module
1006 if ( IsCallOldMethods ) {
1007 // call Python module's setWorkspace() method
1012 PyLockWrapper aLock = myInterp->GetLockWrapper();
1014 if ( IsCallOldMethods ) {
1015 // call Python module's setSettings() method (obsolete)
1016 if ( PyObject_HasAttrString( myModule , "setSettings" ) ) {
1017 PyObjWrapper res( PyObject_CallMethod( myModule, "setSettings", "" ) );
1026 \brief Internal deactivation:
1028 Performs the following actions:
1029 - call Python module's deactivate() method
1031 \param theStudy parent study object
1033 void SALOME_PYQT_Module::deactivate( SUIT_Study* theStudy )
1035 FuncMsg fmsg( "SALOME_PYQT_Module::deactivate()" );
1037 // check if the subinterpreter is initialized and Python module is imported
1038 if ( !myInterp || !myModule ) {
1039 // Error! Python subinterpreter should be initialized and module should be imported first!
1042 // then call Python module's deactivate() method
1043 if ( PyObject_HasAttrString( myModule , "deactivate" ) ) {
1044 PyObjWrapper res( PyObject_CallMethod( myModule, "deactivate", "" ) );
1050 // Disconnect the SUIT_Desktop signal windowActivated()
1051 SUIT_Desktop* aDesk = theStudy->application()->desktop();
1054 disconnect( aDesk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1055 this, SLOT( onActiveViewChanged( SUIT_ViewWindow* ) ) );
1060 \brief Perform internal actions when active study is changed.
1062 Called when active the study is actived (user brings its
1064 - initialize or get the Python interpreter (one per study)
1065 - import the Python GUI module
1066 - call Python module's activeStudyChanged() method
1068 \param theStudy study being activated
1070 void SALOME_PYQT_Module::studyChanged( SUIT_Study* theStudy )
1072 FuncMsg fmsg( "SALOME_PYQT_Module::studyChanged()" );
1075 SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>( theStudy );
1076 int aStudyId = aStudy ? aStudy->studyDS()->StudyId() : 0;
1078 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1079 initInterp( aStudyId );
1083 // import Python GUI module
1088 if ( IsCallOldMethods ) {
1089 // call Python module's setWorkspace() method
1094 PyLockWrapper aLock = myInterp->GetLockWrapper();
1096 // call Python module's activeStudyChanged() method
1097 if ( PyObject_HasAttrString( myModule , "activeStudyChanged" ) ) {
1098 PyObjWrapper res( PyObject_CallMethod( myModule, "activeStudyChanged", "i", aStudyId ) );
1106 \brief Get module engine.
1108 Returns nil var if engine is not found in LifeCycleCORBA.
1110 \return module's engine reference
1112 Engines::Component_var SALOME_PYQT_Module::getEngine() const
1114 FuncMsg fmsg( "SALOME_PYQT_Module::getEngine()" );
1116 Engines::Component_var comp;
1118 comp = getApp()->lcc()->FindOrLoad_Component( "FactoryServerPy", name().toLatin1() );
1120 catch ( CORBA::Exception& ) {
1126 \birief Get module engine IOR.
1128 Returns empty string if engine is not found in LifeCycleCORBA.
1130 \return module's engine IOR
1132 QString SALOME_PYQT_Module::engineIOR() const
1134 FuncMsg fmsg( "SALOME_PYQT_Module::engineIOR()" );
1137 if ( !CORBA::is_nil( getEngine() ) )
1138 anIOR = getApp()->orb()->object_to_string( getEngine() );
1143 \brief Process (internally) context popup menu request.
1145 Performs the following actions:
1146 - calls Python module's definePopup(...) method (obsolete function,
1147 used for compatibility with old code) to define the popup menu context
1148 - parses XML resourses file (if exists) and fills the popup menu with the items)
1149 - calls Python module's customPopup(...) method (obsolete function,
1150 used for compatibility with old code) to allow module to customize the popup menu
1151 - for new modules calls createPopupMenu() function to allow the
1152 modules to build the popup menu by using insertItem(...) Qt functions.
1154 \param theContext popup menu context
1155 \param thePopupMenu popup menu
1157 void SALOME_PYQT_Module::contextMenu( const QString& theContext, QMenu* thePopupMenu )
1159 FuncMsg fmsg( "SALOME_PYQT_Module::contextMenu()" );
1161 // Python interpreter should be initialized and Python module should be
1163 if ( !myInterp || !myModule )
1166 QString aContext( "" ), aObject( "" ), aParent( theContext );
1168 if ( IsCallOldMethods && PyObject_HasAttrString( myModule , "definePopup" ) ) {
1169 // call definePopup() Python module's function
1170 // this is obsolete function, used only for compatibility reasons
1171 PyObjWrapper res( PyObject_CallMethod( myModule,
1174 aContext.toLatin1().constData(),
1175 aObject.toLatin1().constData(),
1176 aParent.toLatin1().constData() ) );
1181 // parse return value
1183 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
1189 } // if ( IsCallOldMethods ... )
1191 // first try to create menu via XML parser:
1192 // we create popup menus without help of QtxPopupMgr
1194 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
1196 PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QMenu ) );
1198 // then call Python module's createPopupMenu() method (for new modules)
1199 if ( PyObject_HasAttrString( myModule , "createPopupMenu" ) ) {
1200 PyObjWrapper res1( PyObject_CallMethod( myModule,
1204 aContext.toLatin1().constData() ) );
1210 if ( IsCallOldMethods && PyObject_HasAttrString( myModule , "customPopup" ) ) {
1211 // call customPopup() Python module's function
1212 // this is obsolete function, used only for compatibility reasons
1213 PyObjWrapper res2( PyObject_CallMethod( myModule,
1217 aContext.toLatin1().constData(),
1218 aObject.toLatin1().constData(),
1219 aParent.toLatin1().constData() ) );
1227 \brief Internal GUI event handling.
1229 Performs the following actions:
1230 - calls Python module's OnGUIEvent() method
1232 \param theId GUI action ID
1234 void SALOME_PYQT_Module::guiEvent( const int theId )
1236 FuncMsg fmsg( "SALOME_PYQT_Module::guiEvent()" );
1238 // Python interpreter should be initialized and Python module should be
1240 if ( !myInterp || !myModule )
1243 if ( PyObject_HasAttrString( myModule , "OnGUIEvent" ) ) {
1244 PyObjWrapper res( PyObject_CallMethod( myModule, "OnGUIEvent", "i", theId ) );
1252 \brief Initialize (internally) preferences for the module.
1254 Performs the following actions:
1255 - calls Python module's createPreferences() method
1257 void SALOME_PYQT_Module::initPreferences()
1259 FuncMsg fmsg( "SALOME_PYQT_Module::initPreferences()" );
1261 // Python interpreter should be initialized and Python module should be
1263 if ( !myInterp || !myModule )
1266 // temporary set myInitModule because createPreferences() method
1267 // might be called during the module intialization process
1268 myInitModule = this;
1270 if ( PyObject_HasAttrString( myModule , "createPreferences" ) ) {
1271 PyObjWrapper res( PyObject_CallMethod( myModule, "createPreferences", "" ) );
1281 \brief Initialize python subinterpreter (one per study).
1282 \param theStudyId study ID
1284 void SALOME_PYQT_Module::initInterp( int theStudyId )
1286 FuncMsg fmsg( "SALOME_PYQT_Module::initInterp()" );
1289 if ( !theStudyId ) {
1290 // Error! Study Id must not be 0!
1294 // try to find the subinterpreter
1295 if( myInterpMap.contains( theStudyId ) ) {
1297 myInterp = myInterpMap[ theStudyId ];
1300 // not found - create a new one!
1301 ///////////////////////////////////////////////////////////////////
1302 // Attention: the creation of Python interpretor must be protected
1303 // by a C++ Lock because of C threads
1304 ///////////////////////////////////////////////////////////////////
1305 myInterp = new SALOME_PYQT_PyInterp();
1306 myInterp->initialize();
1307 myInterpMap[ theStudyId ] = myInterp;
1309 // import 'salome' module and call 'salome_init' method;
1310 // do it only once on interpreter creation
1311 // ... first get python lock
1312 PyLockWrapper aLock = myInterp->GetLockWrapper();
1313 // ... then import a module
1314 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1320 // ... then call a method
1322 PyObjWrapper aRes( PyObject_CallMethod( aMod, "salome_init", "ii", theStudyId, embedded ) );
1331 \brief Import Python GUI module and remember the reference to the module.
1333 Attention! initInterp() should be called first!!!
1335 void SALOME_PYQT_Module::importModule()
1337 FuncMsg fmsg( "SALOME_PYQT_Module::importModule()" );
1339 // check if the subinterpreter is initialized
1341 // Error! Python subinterpreter should be initialized first!
1345 // import Python GUI module and puts it in <myModule> attribute
1346 // ... first get python lock
1347 PyLockWrapper aLock = myInterp->GetLockWrapper();
1348 // ... then import a module
1349 QString aMod = name() + "GUI";
1350 myModule = PyImport_ImportModule( aMod.toLatin1().data() );
1359 \brief Set study workspace to the Python module.
1361 Calls setWorkSpace() method of the Pythohn module with
1362 PyQt QWidget object to use with interpreter.
1364 Attention! initInterp() and importModule() should be called first!!!
1366 void SALOME_PYQT_Module::setWorkSpace()
1368 FuncMsg fmsg( "SALOME_PYQT_Module::setWorkSpace()" );
1370 // check if the subinterpreter is initialized and Python module is imported
1371 if ( !myInterp || !myModule ) {
1372 // Error! Python subinterpreter should be initialized and module should be imported first!
1376 // call setWorkspace() method
1377 // ... first get python lock
1378 PyLockWrapper aLock = myInterp->GetLockWrapper();
1380 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1381 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1388 if ( IsCallOldMethods ) {
1389 // ... then get workspace object
1390 QWidget* aWorkspace = 0;
1391 if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1392 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
1394 aWorkspace = aDesktop->workspace();
1396 else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1397 STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
1399 aWorkspace = aDesktop->workstack();
1401 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget ) );
1402 // ... and finally call Python module's setWorkspace() method (obsolete)
1403 if ( PyObject_HasAttrString( myModule , "setWorkSpace" ) ) {
1404 PyObjWrapper res( PyObject_CallMethod( myModule, "setWorkSpace", "O", pyws.get() ) );
1413 \brief Preference changing callback function (internal).
1415 Performs the following actions:
1416 - call Python module's preferenceChanged() method
1418 \param section setting section name
1419 \param setting setting name
1421 void SALOME_PYQT_Module::prefChanged( const QString& section, const QString& setting )
1423 FuncMsg fmsg( "SALOME_PYQT_Module::prefChanged()" );
1425 // Python interpreter should be initialized and Python module should be
1427 if ( !myInterp || !myModule )
1430 if ( PyObject_HasAttrString( myModule , "preferenceChanged" ) ) {
1431 PyObjWrapper res( PyObject_CallMethod( myModule,
1432 "preferenceChanged",
1434 section.toLatin1().constData(),
1435 setting.toLatin1().constData() ) );
1443 \brief Get default menu group identifier
1444 \return menu group ID (40 by default)
1446 int SALOME_PYQT_Module::defaultMenuGroup()
1448 return DEFAULT_GROUP;
1452 // The next methods call the parent implementation.
1453 // This is done to open protected methods from CAM_Module class.
1457 \brief Create toolbar with specified \a name.
1458 \param name toolbar name
1459 \return toolbar ID or -1 if toolbar creation is failed
1461 int SALOME_PYQT_Module::createTool( const QString& name )
1463 return SalomeApp_Module::createTool( name );
1467 \brief Insert action with specified \a id to the toolbar.
1469 \param tBar toolbar ID
1470 \param idx required index in the toolbar
1471 \return action ID or -1 if action could not be added
1473 int SALOME_PYQT_Module::createTool( const int id, const int tBar, const int idx )
1475 return SalomeApp_Module::createTool( id, tBar, idx );
1479 \brief Insert action with specified \a id to the toolbar.
1481 \param tBar toolbar name
1482 \param idx required index in the toolbar
1483 \return action ID or -1 if action could not be added
1485 int SALOME_PYQT_Module::createTool( const int id, const QString& tBar, const int idx )
1487 return SalomeApp_Module::createTool( id, tBar, idx );
1491 \brief Insert action to the toolbar.
1493 \param tBar toolbar ID
1494 \param id required action ID
1495 \param idx required index in the toolbar
1496 \return action ID or -1 if action could not be added
1498 int SALOME_PYQT_Module::createTool( QAction* a, const int tBar, const int id, const int idx )
1500 return SalomeApp_Module::createTool( a, tBar, id, idx );
1504 \brief Insert action to the toolbar.
1506 \param tBar toolbar name
1507 \param id required action ID
1508 \param idx required index in the toolbar
1509 \return action ID or -1 if action could not be added
1511 int SALOME_PYQT_Module::createTool( QAction* a, const QString& tBar, const int id, const int idx )
1513 return SalomeApp_Module::createTool( a, tBar, id, idx );
1517 \brief Create main menu.
1518 \param subMenu menu name
1519 \param menu parent menu ID
1520 \param id required menu ID
1521 \param group menu group ID
1522 \param idx required index in the menu
1523 \return menu ID or -1 if menu could not be added
1525 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
1527 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
1531 \brief Create main menu.
1532 \param subMenu menu name
1533 \param menu parent menu name (list of menu names separated by "|")
1534 \param id required menu ID
1535 \param group menu group ID
1536 \param idx required index in the menu
1537 \return menu ID or -1 if menu could not be added
1539 int SALOME_PYQT_Module::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
1541 return SalomeApp_Module::createMenu( subMenu, menu, id, group, idx );
1545 \brief Insert action to the main menu.
1547 \param menu parent menu ID
1548 \param group menu group ID
1549 \param idx required index in the menu
1550 \return action ID or -1 if action could not be added
1552 int SALOME_PYQT_Module::createMenu( const int id, const int menu, const int group, const int idx )
1554 return SalomeApp_Module::createMenu( id, menu, group, idx );
1558 \brief Insert action to the main menu.
1560 \param menu parent menu name (list of menu names separated by "|")
1561 \param group menu group ID
1562 \param idx required index in the menu
1563 \return action ID or -1 if action could not be added
1565 int SALOME_PYQT_Module::createMenu( const int id, const QString& menu, const int group, const int idx )
1567 return SalomeApp_Module::createMenu( id, menu, group, idx );
1571 \brief Insert action to the main menu.
1573 \param menu parent menu ID
1574 \param group menu group ID
1575 \param idx required index in the menu
1576 \return action ID or -1 if action could not be added
1578 int SALOME_PYQT_Module::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
1580 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
1584 \brief Insert action to the main menu.
1586 \param menu parent menu name (list of menu names separated by "|")
1587 \param group menu group ID
1588 \param idx required index in the menu
1589 \return action ID or -1 if action could not be added
1591 int SALOME_PYQT_Module::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
1593 return SalomeApp_Module::createMenu( a, menu, id, group, idx );
1597 \brief Create separator action which can be used in the menu or toolbar.
1598 \return new separator action
1600 QAction* SALOME_PYQT_Module::separator()
1602 return SalomeApp_Module::separator();
1606 \brief Get action by specified \a id.
1607 \return action or 0 if it is not found
1609 QAction* SALOME_PYQT_Module::action( const int id ) const
1611 QAction* a = SalomeApp_Module::action( id );
1614 QMenu* m = menuMgr()->findMenu( id );
1615 if ( m ) a = m->menuAction();
1621 \brief Get action identifier.
1622 \return action ID or -1 if action is not registered
1624 int SALOME_PYQT_Module::actionId( const QAction* a ) const
1626 return SalomeApp_Module::actionId( a );
1630 \brief Create new action.
1632 If the action with specified identifier already registered
1633 it is not created, but its attributes are only modified.
1636 \param text tooltip text
1638 \param menu menu text
1639 \param tip status tip
1640 \param key keyboard shortcut
1641 \param toggle \c true for checkable action
1642 \return created action
1644 QAction* SALOME_PYQT_Module::createAction( const int id, const QString& text, const QString& icon,
1645 const QString& menu, const QString& tip, const int key,
1646 const bool toggle, QObject* parent )
1648 QIcon anIcon = loadIcon( icon );
1649 QAction* a = action( id );
1651 if ( a->toolTip().isEmpty() && !text.isEmpty() ) a->setToolTip( text );
1652 if ( a->text().isEmpty() && !menu.isEmpty() ) a->setText( menu );
1653 if ( a->icon().isNull() && !anIcon.isNull() ) a->setIcon( anIcon );
1654 if ( a->statusTip().isEmpty() && !tip.isEmpty() ) a->setStatusTip( tip );
1655 if ( a->shortcut().isEmpty() && key ) a->setShortcut( key );
1656 if ( a->isCheckable() != toggle ) a->setCheckable( toggle );
1657 disconnect( a, SIGNAL( triggered( bool ) ), this, SLOT( onGUIEvent() ) );
1658 connect( a, SIGNAL( triggered( bool ) ), this, SLOT( onGUIEvent() ) );
1661 a = SalomeApp_Module::createAction( id,
1667 parent ? parent : this,
1670 SLOT( onGUIEvent() ) );
1676 \brief Create new action group.
1678 If the action with specified identifier already registered
1679 it is not created, but its attributes are only modified.
1682 \param text tooltip text
1684 \param menu menu text
1685 \param tip status tip
1686 \param key keyboard shortcut
1687 \param toggle \c true for checkable action
1688 \return created action
1690 QtxActionGroup* SALOME_PYQT_Module::createActionGroup(const int id, const bool exclusive)
1692 QtxActionGroup* a = qobject_cast<QtxActionGroup*>( action( id ) );
1694 a = new QtxActionGroup( this );
1695 SalomeApp_Module::registerAction( id, a );
1697 a->setExclusive( exclusive );
1702 \brief Load icon from resource file.
1703 \param fileName icon file name
1704 \return icon (null icon if loading failed)
1706 QIcon SALOME_PYQT_Module::loadIcon( const QString& fileName )
1709 if ( !fileName.isEmpty() ) {
1710 QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(), tr( fileName.toLatin1() ) );
1711 if ( !pixmap.isNull() )
1712 anIcon = QIcon( pixmap );
1718 \brief Add global application preference (for example,
1719 application specific section).
1720 \param label preference name
1721 \return preference ID
1723 int SALOME_PYQT_Module::addGlobalPreference( const QString& label )
1725 LightApp_Preferences* pref = preferences();
1729 return pref->addPreference( label, -1 );
1733 \brief Add preference.
1734 \param label preference name
1735 \return preference ID
1737 int SALOME_PYQT_Module::addPreference( const QString& label )
1739 return SalomeApp_Module::addPreference( label );
1743 \brief Add preference.
1744 \param label preference name
1745 \param pId parent preference ID
1746 \param type preference type
1747 \param section resource file section name
1748 \param param resource file setting name
1749 \return preference ID
1751 int SALOME_PYQT_Module::addPreference( const QString& label,
1752 const int pId, const int type,
1753 const QString& section,
1754 const QString& param )
1756 return SalomeApp_Module::addPreference( label, pId, type, section, param );
1760 \brief Get the preference property.
1761 \param id preference ID
1762 \param prop property name
1763 \return property value (invalid QVariant() if property is not found)
1765 QVariant SALOME_PYQT_Module::preferenceProperty( const int id,
1766 const QString& prop ) const
1768 QVariant v = SalomeApp_Module::preferenceProperty( id, prop );
1773 \brief Set the preference property.
1774 \param id preference ID
1775 \param prop property name
1776 \param var property value
1778 void SALOME_PYQT_Module::setPreferenceProperty( const int id,
1779 const QString& prop,
1780 const QVariant& var )
1782 SalomeApp_Module::setPreferenceProperty( id, prop, var );
1787 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1788 \param pview view being activated
1790 void SALOME_PYQT_Module::onActiveViewChanged( SUIT_ViewWindow* pview )
1792 class ActiveViewChange : public PyInterp_LockRequest
1795 ActiveViewChange( PyInterp_Interp* _py_interp, SALOME_PYQT_Module* _obj, const SUIT_ViewWindow* _pview )
1796 : PyInterp_LockRequest( _py_interp, 0, true ),
1797 myObj(_obj),myView(_pview) {}
1800 virtual void execute()
1802 myObj->activeViewChanged( myView );
1806 SALOME_PYQT_Module* myObj;
1807 const SUIT_ViewWindow * myView;
1810 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChange( myInterp, this, pview ) );
1814 \brief Processes the view changing, calls Python module's activeViewChanged() method
1815 \param pview view being activated
1817 void SALOME_PYQT_Module::activeViewChanged( const SUIT_ViewWindow* pview )
1819 if ( !myInterp || !myModule )
1822 // Do not use SUIT_ViewWindow::closing() signal here. View manager reacts on
1823 // this signal and deletes view. So our slot does not works if it is connected
1824 // on this signal. SUIT_ViewManager::deleteView(SUIT_ViewWindow*) is used here
1826 connectView( pview );
1828 if ( PyObject_HasAttrString( myModule, "activeViewChanged" ) )
1833 PyObjWrapper res( PyObject_CallMethod( myModule, "activeViewChanged", "i" , pview->getId() ) );
1840 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1841 \param pview view being cloned
1843 void SALOME_PYQT_Module::onViewCloned( SUIT_ViewWindow* pview )
1845 class ViewClone : public PyInterp_LockRequest
1848 ViewClone( PyInterp_Interp* _py_interp, SALOME_PYQT_Module* _obj, const SUIT_ViewWindow* _pview )
1849 : PyInterp_LockRequest( _py_interp, 0, true ),
1850 myObj(_obj), myView(_pview) {}
1853 virtual void execute()
1855 myObj->viewCloned( myView );
1859 SALOME_PYQT_Module* myObj;
1860 const SUIT_ViewWindow* myView;
1863 PyInterp_Dispatcher::Get()->Exec( new ViewClone( myInterp, this, pview ) );
1867 \brief Processes the view cloning, calls Python module's activeViewCloned() method
1868 \param pview view being cloned
1870 void SALOME_PYQT_Module::viewCloned( const SUIT_ViewWindow* pview )
1872 if ( !myInterp || !myModule || !pview )
1875 if ( PyObject_HasAttrString( myModule, "viewCloned" ) )
1877 PyObjWrapper res( PyObject_CallMethod( myModule, "viewCloned", "i", pview->getId() ) );
1884 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1885 \param pview view being closed
1887 void SALOME_PYQT_Module::onViewClosed( SUIT_ViewWindow* pview )
1889 class ViewClose : public PyInterp_LockRequest
1892 ViewClose( PyInterp_Interp* _py_interp, SALOME_PYQT_Module* _obj, const SUIT_ViewWindow* _pview )
1893 : PyInterp_LockRequest( _py_interp, 0, true ),
1894 myObj(_obj),myView(_pview) {}
1897 virtual void execute()
1899 myObj->viewClosed( myView );
1903 SALOME_PYQT_Module* myObj;
1904 const SUIT_ViewWindow * myView;
1907 PyInterp_Dispatcher::Get()->Exec( new ViewClose( myInterp, this, pview ) );
1911 \brief Processes the view closing, calls Python module's viewClosed() method
1912 \param pview view being closed
1914 void SALOME_PYQT_Module::viewClosed( const SUIT_ViewWindow* pview )
1916 if ( !myInterp || !myModule )
1919 if ( PyObject_HasAttrString( myModule, "viewClosed" ) )
1921 PyObjWrapper res( PyObject_CallMethod( myModule, "viewClosed", "i", pview->getId() ) );
1930 \brief Connects or disconnects signals about activating and cloning view on the module slots
1931 \param pview view which is connected/disconnected
1933 void SALOME_PYQT_Module::connectView( const SUIT_ViewWindow* pview )
1935 SUIT_ViewManager* viewMgr = pview->getViewManager();
1936 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
1940 disconnect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
1941 this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) );
1943 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
1944 this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) );
1947 // Connect cloneView() signal of an OCC View
1948 if ( pview->inherits( "OCCViewer_ViewWindow" ) )
1950 disconnect( pview, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
1951 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
1952 connect( pview, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
1953 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
1955 // Connect cloneView() signal of Plot2d View manager
1956 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) )
1958 disconnect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
1959 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
1960 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
1961 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
1966 \brief Get tag name for the DOM element.
1967 \param element DOM element
1968 \return empty string if the element does not have tag name
1971 static QString tagName( const QDomElement& element )
1973 return element.tagName().trimmed();
1977 \brief Get DOM element's attribute by its name.
1978 \param element DOM element
1979 \param attName attribute name
1980 \return empty string if the element does not have such attribute
1983 static QString attribute( const QDomElement& element, const QString& attName )
1985 return element.attribute( attName ).trimmed();
1989 \brief Inspect specified string for the boolean value.
1991 This function returns \c true if string represents boolean value:
1992 - "true", "yes" or "1" for \c true
1993 - "false", "no" or "0" for \c false
1994 Second parameter allows to specify what boolean value is expected:
1997 - other value is not taken into account (return represented value)
1999 \param value inspected string
2000 \param check expected boolean value
2001 \return boolean value represented by the string (\a check is not 1 or 0)
2002 or \c true if value correspond to the specified \a check
2004 static bool checkBool( const QString& value, const int check = -1 )
2006 QString v = value.toLower();
2007 if ( ( v == "true" || v == "yes" || v == "1" ) && ( check != 0 ) )
2009 if ( ( v == "false" || v == "no" || v == "0" ) && ( check == 0 ) )
2015 \brief Inspect specified string for the integer value.
2017 This function returns returns -1 if item is empty or represents
2019 \param value inspected string
2020 \param def default value
2021 \param shift shift value (it is added to the integer value to produce shifted result)
2023 static int checkInt( const QString& value, const int def = -1, const int shift = -1 )
2026 int val = value.toInt( &bOk );
2027 if ( !bOk ) val = def;
2028 if ( shift > 0 && bOk && val < 0 )
2036 \param module parent module pointer
2037 \param fileName XML file path
2039 SALOME_PYQT_Module::XmlHandler::XmlHandler( SALOME_PYQT_Module* module,
2040 const QString& fileName )
2041 : myModule( module )
2043 if ( fileName.isEmpty() )
2045 QFile aFile( fileName );
2046 if ( !aFile.open( QIODevice::ReadOnly ) )
2048 myDoc.setContent( &aFile );
2053 \brief Parse XML file and create actions.
2056 Called by SALOME_PYQT_Module::activate() in order to create actions
2059 void SALOME_PYQT_Module::XmlHandler::createActions()
2061 // get document element
2062 QDomElement aDocElem = myDoc.documentElement();
2064 // create main menu actions
2065 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
2066 for ( int i = 0; i < aMenuList.count(); i++ ) {
2067 QDomNode n = aMenuList.item( i );
2071 // create toolbars actions
2072 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
2073 for ( int i = 0; i < aToolsList.count(); i++ ) {
2074 QDomNode n = aToolsList.item( i );
2080 \brief Create popup menu.
2082 \param menu popup menu
2083 \param context popup menu context
2084 \param context popup menu parent object name
2085 \param context popup menu object name
2087 void SALOME_PYQT_Module::XmlHandler::createPopup( QMenu* menu,
2088 const QString& context,
2089 const QString& parent,
2090 const QString& object )
2092 // get document element
2093 QDomElement aDocElem = myDoc.documentElement();
2095 // get popup menus actions
2096 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
2097 for ( int i = 0; i < aPopupList.count(); i++ ) {
2098 QDomNode n = aPopupList.item( i );
2099 if ( !n.isNull() && n.isElement() ) {
2100 QDomElement e = n.toElement();
2101 // QString lab = attribute( e, "label-id" ); // not used //
2102 QString ctx = attribute( e, "context-id" );
2103 QString prt = attribute( e, "parent-id" );
2104 QString obj = attribute( e, "object-id" );
2105 if ( ctx == context && prt == parent && obj == object ) {
2106 insertPopupItems( n, menu );
2114 \brief Activate menus
2116 \param enable if \c true menus are activated, otherwise menus are deactivated
2118 void SALOME_PYQT_Module::XmlHandler::activateMenus( bool enable )
2123 QtxActionMenuMgr* mgr = myModule->menuMgr();
2125 foreach( id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
2129 \brief Create main menu item and insert actions to it.
2131 \param parentNode XML node with menu description
2132 \param parentMenuId parent menu ID (-1 for top-level menu)
2133 \param parentPopup parent popup menu (0 for top-level menu)
2135 void SALOME_PYQT_Module::XmlHandler::createMenu( QDomNode& parentNode,
2136 const int parentMenuId,
2137 QMenu* parentPopup )
2139 if ( !myModule || parentNode.isNull() )
2142 QDomElement parentElement = parentNode.toElement();
2143 if ( !parentElement.isNull() ) {
2144 QString plabel = attribute( parentElement, "label-id" );
2145 int pid = checkInt( attribute( parentElement, "item-id" ) );
2146 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
2147 int group = checkInt( attribute( parentElement, "group-id" ),
2148 myModule->defaultMenuGroup() );
2149 if ( !plabel.isEmpty() ) {
2153 menuId = myModule->createMenu( plabel, // label
2154 parentMenuId, // parent menu ID, -1 for top-level menu
2158 myMenuItems.append( menuId );
2159 QDomNode node = parentNode.firstChild();
2160 while ( !node.isNull() ) {
2161 if ( node.isElement() ) {
2162 QDomElement elem = node.toElement();
2163 QString aTagName = tagName( elem );
2164 if ( aTagName == "popup-item" ) {
2165 int id = checkInt( attribute( elem, "item-id" ) );
2166 int pos = checkInt( attribute( elem, "pos-id" ) );
2167 int group = checkInt( attribute( elem, "group-id" ),
2168 myModule->defaultMenuGroup() );
2169 QString label = attribute( elem, "label-id" );
2170 QString icon = attribute( elem, "icon-id" );
2171 QString tooltip = attribute( elem, "tooltip-id" );
2172 QString accel = attribute( elem, "accel-id" );
2173 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
2175 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2176 // also check if the action with given ID is already created
2178 // create menu action
2179 QAction* action = myModule->createAction( id, // ID
2183 tooltip, // status-bar text
2184 QKeySequence( accel ), // keyboard accelerator
2185 toggle ); // toogled action
2186 myModule->createMenu( action, // action
2187 menuId, // parent menu ID
2188 id, // ID (same as for createAction())
2193 else if ( aTagName == "submenu" ) {
2195 createMenu( node, menuId, popup );
2197 else if ( aTagName == "separator" ) {
2198 // create menu separator
2199 int id = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
2200 int pos = checkInt( attribute( elem, "pos-id" ) );
2201 int group = checkInt( attribute( elem, "group-id" ),
2202 myModule->defaultMenuGroup() );
2203 QAction* action = myModule->separator();
2204 myModule->createMenu( action, // separator action
2205 menuId, // parent menu ID
2211 node = node.nextSibling();
2218 \brief Create a toolbar and insert actions to it.
2219 \param parentNode XML node with toolbar description
2221 void SALOME_PYQT_Module::XmlHandler::createToolBar( QDomNode& parentNode )
2223 if ( !myModule || parentNode.isNull() )
2226 QDomElement parentElement = parentNode.toElement();
2227 if ( !parentElement.isNull() ) {
2228 QString aLabel = attribute( parentElement, "label-id" );
2229 if ( !aLabel.isEmpty() ) {
2231 int tbId = myModule->createTool( aLabel );
2232 QDomNode node = parentNode.firstChild();
2233 while ( !node.isNull() ) {
2234 if ( node.isElement() ) {
2235 QDomElement elem = node.toElement();
2236 QString aTagName = tagName( elem );
2237 if ( aTagName == "toolbutton-item" ) {
2238 int id = checkInt( attribute( elem, "item-id" ) );
2239 int pos = checkInt( attribute( elem, "pos-id" ) );
2240 QString label = attribute( elem, "label-id" );
2241 QString icon = attribute( elem, "icon-id" );
2242 QString tooltip = attribute( elem, "tooltip-id" );
2243 QString accel = attribute( elem, "accel-id" );
2244 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
2246 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2247 // also check if the action with given ID is already created
2249 // create toolbar action
2250 QAction* action = myModule->createAction( id, // ID
2254 tooltip, // status-bar text
2255 QKeySequence( accel ), // keyboard accelerator
2256 toggle ); // toogled action
2257 myModule->createTool( action, tbId, -1, pos );
2260 else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
2261 // create toolbar separator
2262 int pos = checkInt( attribute( elem, "pos-id" ) );
2263 QAction* action = myModule->separator();
2264 myModule->createTool( action, tbId, -1, pos );
2267 node = node.nextSibling();
2274 \brief Fill popup menu with the items.
2275 \param parentNode XML node with popup menu description
2276 \param menu popup menu
2278 void SALOME_PYQT_Module::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
2280 if ( !myModule && parentNode.isNull() )
2283 // we create popup menus without help of QtxPopupMgr
2284 QDomNode node = parentNode.firstChild();
2285 while ( !node.isNull() ) {
2286 if ( node.isElement() ) {
2287 QDomElement elem = node.toElement();
2288 QString aTagName = tagName( elem );
2289 QList<QAction*> actions = menu->actions();
2290 if ( aTagName == "popup-item" ) {
2291 // insert a command item
2292 int id = checkInt( attribute( elem, "item-id" ) );
2293 int pos = checkInt( attribute( elem, "pos-id" ) );
2294 QString label = attribute( elem, "label-id" );
2295 QString icon = attribute( elem, "icon-id" );
2296 QString tooltip = attribute( elem, "tooltip-id" );
2297 QString accel = attribute( elem, "accel-id" );
2298 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
2300 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2301 // also check if the action with given ID is already created
2303 QAction* action = myModule->createAction( id, // ID
2307 tooltip, // status-bar text
2308 QKeySequence( accel ), // keyboard accelerator
2309 toggle ); // toogled action
2310 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2311 menu->insertAction( before, action );
2314 else if ( aTagName == "submenu" ) {
2316 ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
2317 int pos = checkInt( attribute( elem, "pos-id" ) );
2318 QString label = attribute( elem, "label-id" );
2319 QString icon = attribute( elem, "icon-id" );
2322 if ( !icon.isEmpty() ) {
2323 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(), icon );
2324 if ( !pixmap.isNull() )
2325 anIcon = QIcon( pixmap );
2328 QMenu* newPopup = menu->addMenu( anIcon, label );
2329 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2330 menu->insertMenu( before, newPopup );
2331 insertPopupItems( node, newPopup );
2333 else if ( aTagName == "separator" ) {
2334 // create menu separator
2335 int pos = checkInt( attribute( elem, "pos-id" ) );
2336 QAction* action = myModule->separator();
2337 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2338 menu->insertAction( before, action );
2341 node = node.nextSibling();