1 // Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // File : SALOME_PYQT_Module.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
23 #include "SALOME_PYQT_PyInterp.h"
24 #include <SUITApp_init_python.hxx>
25 #include <PyInterp_Interp.h>
26 #include <PyConsole_Interp.h>
27 #include <PyConsole_Console.h>
28 #include <PyInterp_Dispatcher.h>
30 #include "SALOME_PYQT_ModuleLight.h"
31 #include "SALOME_PYQT_DataModelLight.h"
33 #ifndef GUI_DISABLE_CORBA
34 #include <Container_init_python.hxx>
37 #include <SUIT_ResourceMgr.h>
38 #include <SUIT_DataObjectIterator.h>
39 #include <SUIT_Desktop.h>
40 #include <SUIT_ViewModel.h>
41 #include <SUIT_ViewWindow.h>
42 #include <SUIT_ViewManager.h>
43 #include <STD_MDIDesktop.h>
44 #include <STD_TabDesktop.h>
45 #include <LightApp_Preferences.h>
46 #include <LightApp_Application.h>
47 #include <LightApp_Study.h>
49 #include <QtxWorkstack.h>
50 #include <QtxWorkspace.h>
51 #include <QtxActionGroup.h>
52 #include <QtxActionMenuMgr.h>
53 #include <QtxActionToolMgr.h>
56 #include <QDomDocument>
58 #include <QDomElement>
63 #include "sipAPISalomePyQtGUILight.h"
66 #if SIP_VERSION < 0x040700
67 #include "sipQtGuiQWidget.h"
68 #include "sipQtGuiQMenu.h"
71 #include <utilities.h>
74 \brief Default name of the module, replaced at the moment
78 const char* DEFAULT_NAME = "SALOME_PYQT_ModuleLight";
81 \brief Default menu group number.
84 const int DEFAULT_GROUP = 40;
88 \brief Allow calling obsolete callback methods.
91 If the macro CALL_OLD_METHODS is not defined, the invoking
92 of obsolete Python module's methods like setSetting(), definePopup(),
95 CALL_OLD_METHODS macro can be defined for example by adding
96 -DCALL_OLD_METHODS compilation option to the Makefile.
98 #ifdef CALL_OLD_METHODS
99 const bool IsCallOldMethods = true;
101 const bool IsCallOldMethods = false;
104 /* Py_ssize_t for old Pythons */
105 /* This code is as recommended by: */
106 /* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */
107 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
108 typedef int Py_ssize_t;
109 # define PY_SSIZE_T_MAX INT_MAX
110 # define PY_SSIZE_T_MIN INT_MIN
114 // NB: Python requests.
115 // General rule for Python requests created by SALOME_PYQT_Module:
116 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
117 // However, it is obligatory that ANY Python call is wrapped with a request object,
118 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
122 \class SALOME_PYQT_Module::XmlHandler
123 \brief XML resource files parser.
126 This class is used to provide backward compatibility with
127 existing Python modules in which obsolete menu definition system
128 (via XML files) is used.
131 class SALOME_PYQT_ModuleLight::XmlHandler
134 XmlHandler( SALOME_PYQT_ModuleLight* module, const QString& fileName );
135 void createActions();
136 void createPopup ( QMenu* menu,
137 const QString& context,
138 const QString& parent,
139 const QString& object );
140 void activateMenus( bool );
143 void createToolBar ( QDomNode& parentNode );
144 void createMenu ( QDomNode& parentNode,
145 const int parentMenuId = -1,
146 QMenu* parentPopup = 0 );
148 void insertPopupItems( QDomNode& parentNode,
152 SALOME_PYQT_ModuleLight* myModule;
154 QList<int> myMenuItems;
158 // NB: Library initialization
159 // Since the SalomePyQtGUILight library is not imported in Python it's initialization function
160 // should be called manually (and only once) in order to initialize global sip data
161 // and to get C API from sip : sipBuildResult for example
164 #define INIT_FUNCTION initSalomePyQtGUILight
165 #if defined(SIP_STATIC_MODULE)
166 extern "C" void INIT_FUNCTION();
168 PyMODINIT_FUNC INIT_FUNCTION();
172 \fn CAM_Module* createModule()
173 \brief Module factory function.
176 Creates an instance of SALOME_PYQT_Module object by request
177 of an application when the module is loaded and initialized.
179 \return new module object
183 SALOME_PYQT_LIGHT_EXPORT CAM_Module* createModule() {
185 static bool alreadyInitialized = false;
186 if ( !alreadyInitialized ) {
187 // call only once (see comment above) !
188 static PyThreadState *gtstate = 0;
189 #ifndef GUI_DISABLE_CORBA
190 if(SUIT_PYTHON::initialized)
191 gtstate = SUIT_PYTHON::_gtstate;
193 gtstate = KERNEL_PYTHON::_gtstate;
195 gtstate = SUIT_PYTHON::_gtstate;
197 PyEval_RestoreThread( gtstate );
199 PyEval_ReleaseThread( gtstate );
200 alreadyInitialized = !alreadyInitialized;
202 return new SALOME_PYQT_ModuleLight();
208 \brief Function call in/out tracer.
215 FuncMsg( const QString& funcName )
218 MESSAGE( myName.toLatin1().constData() << " [ begin ]" );
222 MESSAGE( myName.toLatin1().constData() << " [ end ]" );
224 void message( const QString& msg )
226 MESSAGE( myName.toLatin1().constData() << " : " << msg.toLatin1().constData() );
233 \class SALOME_PYQT_ModuleLight
234 \brief This class implements module API for all the Python-based
239 // Static variables definition
241 SALOME_PYQT_ModuleLight::InterpMap SALOME_PYQT_ModuleLight::myInterpMap;
242 SALOME_PYQT_ModuleLight* SALOME_PYQT_ModuleLight::myInitModule = 0;
245 \brief Get the module being initialized.
247 This is a little trick :) needed to provide an access from Python
248 (SalomePyQt) to the module being currently activated. The problem
249 that during the process of module initialization (initialize()
250 function) it is not yet available via application->activeModule()
253 This method returns valid pointer only if called in scope of
254 initialize() function.
256 \return the module being currently initialized
258 SALOME_PYQT_ModuleLight* SALOME_PYQT_ModuleLight::getInitModule()
266 SALOME_PYQT_ModuleLight::SALOME_PYQT_ModuleLight()
267 : LightApp_Module( DEFAULT_NAME ),
271 myLastActivateStatus( true )
278 SALOME_PYQT_ModuleLight::~SALOME_PYQT_ModuleLight()
282 if ( myInterp && myModule ) {
283 PyLockWrapper aLock = myInterp->GetLockWrapper();
284 Py_XDECREF(myModule);
289 \brief Initialization of the module.
291 This method can be used for creation of the menus, toolbars and
294 There are two ways to do this:
295 - for obsolete modules this method first tries to read
296 <module>_<language>.xml resource file which contains a menu,
297 toolbars and popup menus description;
298 - new modules can create menus by direct calling of the
299 corresponding methods of SalomePyQt Python API in the Python
300 module's initialize() method which is called from here.
302 NOTE: SALOME supports two modes of modules loading:
303 - immediate (all the modules are created and initialized
304 immediately when the application object is created;
305 - postponed modules loading (used currently); in this mode
306 the module is loaded only be request.
307 If postponed modules loading is not used, the active
308 study might be not yet defined at this stage, so initialize()
309 method should not perform any study-based initialization.
311 \param app parent application object
313 void SALOME_PYQT_ModuleLight::initialize( CAM_Application* app )
315 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initialize()" );
317 // call base implementation
318 LightApp_Module::initialize( app );
320 // try to get XML resource file name
321 SUIT_ResourceMgr* aResMgr = getApp()->resourceMgr();
322 if ( !myXmlHandler && aResMgr ) {
323 // get current language
324 QString aLang = aResMgr->stringValue( "language", "language", QString() );
325 if ( aLang.isEmpty() )
327 // define resource file name
328 QString aFileName = name() + "_" + aLang + ".xml";
329 aFileName = aResMgr->path( "resources", name(), aFileName );
330 // create XML handler instance
331 if ( !aFileName.isEmpty() && QFile::exists( aFileName ) )
332 myXmlHandler = new SALOME_PYQT_ModuleLight::XmlHandler( this, aFileName );
333 // create menus & toolbars from XML file if required
335 myXmlHandler->createActions();
338 // perform internal initialization and call module's initialize() funtion
339 // InitializeReq: request class for internal init() operation
340 class InitializeReq : public PyInterp_Request
343 InitializeReq( CAM_Application* _app,
344 SALOME_PYQT_ModuleLight* _obj )
345 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
350 virtual void execute()
352 myObj->init( myApp );
356 CAM_Application* myApp;
357 SALOME_PYQT_ModuleLight* myObj;
361 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( app, this ) );
365 \brief Activation of the module.
367 This function is usually used in order to show the module's
368 specific menus and toolbars, update actions state and perform
369 other such actions required when the module is activated.
371 Note, that returning \c false in this function prevents the
374 \param theStudy parent study
375 \return \c true if activation is successful and \c false otherwise
377 bool SALOME_PYQT_ModuleLight::activateModule( SUIT_Study* theStudy )
379 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::activateModule()" );
381 // call base implementation
382 bool res = LightApp_Module::activateModule( theStudy );
387 // internal activation
388 return activateModuleInternal( theStudy );
392 \brief Perform internal activation of the module.
394 The only goal of this function is to extract common functionality
395 for LightApp_Module and SalomeApp_module classes requried by the
396 specific architecture aspects of "light" / "full" SALOME modes.
400 bool SALOME_PYQT_ModuleLight::activateModuleInternal( SUIT_Study* theStudy )
402 // reset the activation status to the default value
403 myLastActivateStatus = true;
405 // perform internal activation
406 // ActivateReq: request class for internal activate() operation
407 class ActivateReq : public PyInterp_Request
410 ActivateReq( SUIT_Study* _study,
411 SALOME_PYQT_ModuleLight* _obj )
412 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
417 virtual void execute()
419 myObj->activate( myStudy );
424 SALOME_PYQT_ModuleLight* myObj;
428 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( theStudy, this ) );
430 // check activation status (set by activate())
431 if ( !lastActivationStatus() )
434 // activate menus, toolbars, etc
435 if ( myXmlHandler ) myXmlHandler->activateMenus( true );
436 setMenuShown( true );
437 setToolShown( true );
439 // connect preferences changing signal
440 connect( getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
441 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
443 // perform custom activation actions
444 // CustomizeReq: request class for internal customize() operation
445 class CustomizeReq : public PyInterp_Request
448 CustomizeReq( SUIT_Study* _study,
449 SALOME_PYQT_ModuleLight* _obj )
450 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
455 virtual void execute()
457 myObj->customize( myStudy );
462 SALOME_PYQT_ModuleLight* myObj;
466 PyInterp_Dispatcher::Get()->Exec( new CustomizeReq( theStudy, this ) );
472 \brief Deactivation of the module.
474 This function is usually used in order to hide the module's
475 specific menus and toolbars and perform other such actions
476 required when the module is deactivated.
478 \param theStudy parent study
479 \return \c true if deactivation is successful and \c false otherwise
481 bool SALOME_PYQT_ModuleLight::deactivateModule( SUIT_Study* theStudy )
483 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::deactivateModule()" );
485 // disconnect preferences changing signal
486 disconnect( getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
487 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
489 // perform internal deactivation
490 // DeactivateReq: request class for internal deactivate() operation
491 class DeactivateReq : public PyInterp_LockRequest
494 DeactivateReq( PyInterp_Interp* _py_interp,
496 SALOME_PYQT_ModuleLight* _obj )
497 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
502 virtual void execute()
504 myObj->deactivate( myStudy );
509 SALOME_PYQT_ModuleLight* myObj;
513 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, theStudy, this ) );
515 // deactivate menus, toolbars, etc
516 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
517 setMenuShown( false );
518 setToolShown( false );
520 // call base implementation
521 return LightApp_Module::deactivateModule( theStudy );
525 \brief Get last activation status.
526 \return status of last module activation operation
529 bool SALOME_PYQT_ModuleLight::lastActivationStatus() const
531 return myLastActivateStatus;
535 \breif Process application preferences changing.
537 Called when any application setting is changed.
539 \param module preference module
540 \param section preference resource file section
541 \param setting preference resource name
543 void SALOME_PYQT_ModuleLight::preferenceChanged( const QString& module,
544 const QString& section,
545 const QString& setting )
547 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::preferenceChanged()" );
549 // perform synchronous request to Python event dispatcher
550 class Event : public PyInterp_LockRequest
553 Event( PyInterp_Interp* _py_interp,
554 SALOME_PYQT_ModuleLight* _obj,
555 const QString& _section,
556 const QString& _setting )
557 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
559 mySection( _section ),
560 mySetting( _setting ) {}
563 virtual void execute()
565 myObj->prefChanged( mySection, mySetting );
569 SALOME_PYQT_ModuleLight* myObj;
570 QString mySection, mySetting;
573 if ( module != moduleName() ) {
574 // module's own preferences are processed by preferencesChanged() method
576 // post the request only if dispatcher is not busy!
577 // execute request synchronously
578 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
579 PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this, section, setting ) );
584 \brief Process study activation.
586 Called when study desktop is activated. Used for notifying the Python
587 module about changing of the active study.
589 void SALOME_PYQT_ModuleLight::studyActivated()
591 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::studyActivated()" );
593 // StudyChangedReq: request class for internal studyChanged() operation
594 class StudyChangedReq : public PyInterp_Request
597 StudyChangedReq( SUIT_Study* _study,
598 SALOME_PYQT_ModuleLight* _obj )
599 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
604 virtual void execute()
606 myObj->studyChanged( myStudy );
611 SALOME_PYQT_ModuleLight* myObj;
615 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( application()->activeStudy(), this ) );
619 \brief Process GUI action (from main menu, toolbar or
620 context popup menu action).
622 void SALOME_PYQT_ModuleLight::onGUIEvent()
624 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::onGUIEvent()" );
627 QAction* action = qobject_cast<QAction*>( sender() );
632 int id = actionId( action );
633 fmsg.message( QString( "action id = %1" ).arg( id ) );
635 // perform synchronous request to Python event dispatcher
636 class GUIEvent : public PyInterp_LockRequest
639 GUIEvent( PyInterp_Interp* _py_interp,
640 SALOME_PYQT_ModuleLight* _obj,
642 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
647 virtual void execute()
649 myObj->guiEvent( myId );
654 SALOME_PYQT_ModuleLight* myObj;
658 PyInterp_Dispatcher::Get()->Exec( new GUIEvent( myInterp, this, id ) );
662 \brief Process context popup menu request.
664 Called when user activates popup menu in some window
665 (view, object browser, etc).
667 \param theContext popup menu context (e.g. "ObjectBrowser")
668 \param thePopupMenu popup menu
669 \param title popup menu title (not used)
671 void SALOME_PYQT_ModuleLight::contextMenuPopup( const QString& theContext,
675 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::contextMenuPopup()" );
676 fmsg.message( QString( "context: %1" ).arg( theContext ) );
678 // perform synchronous request to Python event dispatcher
679 class PopupMenuEvent : public PyInterp_LockRequest
682 PopupMenuEvent( PyInterp_Interp* _py_interp,
683 SALOME_PYQT_ModuleLight* _obj,
684 const QString& _context,
686 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
687 myContext( _context ),
692 virtual void execute()
694 myObj->contextMenu( myContext, myPopup );
698 SALOME_PYQT_ModuleLight* myObj;
703 // post request only if dispatcher is not busy!
704 // execute request synchronously
705 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
706 PyInterp_Dispatcher::Get()->Exec( new PopupMenuEvent( myInterp, this, theContext, thePopupMenu ) );
710 \brief Export preferences for the Python module.
712 Called only once when the first instance of the module is created.
714 void SALOME_PYQT_ModuleLight::createPreferences()
716 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::createPreferences()" );
718 // perform synchronous request to Python event dispatcher
719 class Event : public PyInterp_LockRequest
722 Event( PyInterp_Interp* _py_interp,
723 SALOME_PYQT_ModuleLight* _obj )
724 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
728 virtual void execute()
730 myObj->initPreferences();
734 SALOME_PYQT_ModuleLight* myObj;
737 // post request only if dispatcher is not busy!
738 // execute request synchronously
739 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
740 PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this ) );
744 \brief Define the dockable windows associated with the module.
746 To fill the list of windows the correspondind Python module's windows()
747 method is called from SALOME_PYQT_ModuleLight::init() method.
749 By default, ObjectBrowser, PythonConsole and LogWindow windows are
750 associated to the module.
752 Allowed dockable windows:
753 - LightApp_Application::WT_ObjectBrowser : object browser
754 - LightApp_Application::WT_PyConsole : python console
755 - LightApp_Application::WT_LogWindow : log messages output window
757 Dock area is defined by Qt::DockWidgetArea enumeration:
758 - Qt::TopDockWidgetArea : top dock area
759 - Qt::BottomDockWidgetArea : bottom dock area
760 - Qt::LeftDockWidgetArea : left dock area
761 - Qt::RightDockWidgetArea : right dock area
763 \param mappa map of dockable windows: { <window_type> : <dock_area> }
765 void SALOME_PYQT_ModuleLight::windows( QMap<int, int>& mappa ) const
767 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::windows()" );
769 mappa = myWindowsMap;
773 \brief Define the compatible view windows associated with the module.
775 The associated view windows are opened automatically when the module
778 To fill the list of views the correspondind Python module's views()
779 method is called from SALOME_PYQT_ModuleLight::init() method.
780 By default, the list is empty.
782 \param listik list of view windows types
784 void SALOME_PYQT_ModuleLight::viewManagers( QStringList& lst ) const
786 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::viewManagers()" );
792 \brief Process module's preferences changing.
794 Called when the module's preferences are changed.
796 \param section setting section
797 \param setting setting name
799 void SALOME_PYQT_ModuleLight::preferencesChanged( const QString& section, const QString& setting )
801 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::preferencesChanged()" );
803 // perform synchronous request to Python event dispatcher
804 class Event : public PyInterp_LockRequest
807 Event( PyInterp_Interp* _py_interp,
808 SALOME_PYQT_ModuleLight* _obj,
809 const QString& _section,
810 const QString& _setting )
811 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
813 mySection( _section ),
814 mySetting( _setting ) {}
817 virtual void execute()
819 myObj->prefChanged( mySection, mySetting );
823 SALOME_PYQT_ModuleLight* myObj;
824 QString mySection, mySetting;
827 // post request only if dispatcher is not busy!
828 // execut request synchronously
829 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
830 PyInterp_Dispatcher::Get()->Exec( new Event( myInterp, this, section, setting ) );
834 \brief Internal module initialization:
836 Performs the following actions:
837 - initialize or get the Python interpreter (one per study)
838 - import the Python module
839 - pass the workspace widget to the Python module
840 - call Python module's initialize() method
841 - call Python module's windows() method
842 - call Python module's views() method
844 \param app parent application object
846 void SALOME_PYQT_ModuleLight::init( CAM_Application* app )
848 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::init()" );
850 // reset interpreter to NULL
854 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
857 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
860 int aStudyId = aStudy ? aStudy->id() : 0;
862 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
863 initInterp( aStudyId );
867 // import Python GUI module
872 // this module is being activated now!
875 // then call Python module's initialize() method
876 // ... first get python lock
877 PyLockWrapper aLock = myInterp->GetLockWrapper();
878 // ... (the Python module is already imported)
879 // ... finally call Python module's initialize() method
880 if ( PyObject_HasAttrString( myModule, (char*)"initialize" ) ) {
881 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"initialize", (char*)"" ) );
887 // get required dockable windows list from the Python module
888 // by calling windows() method
889 // ... first put default values
890 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
891 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
892 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
894 if ( PyObject_HasAttrString( myModule , (char*)"windows" ) ) {
895 PyObjWrapper res1( PyObject_CallMethod( myModule, (char*)"windows", (char*)"" ) );
900 myWindowsMap.clear();
901 if ( PyDict_Check( res1 ) ) {
905 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
906 // parse the return value
907 // it should be a map: {integer:integer}
909 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
910 aKey = PyInt_AsLong( key );
911 aValue = PyInt_AsLong( value );
912 myWindowsMap[ aKey ] = aValue;
919 // get compatible view windows types from the Python module
920 // by calling views() method
921 if ( PyObject_HasAttrString( myModule , (char*)"views" ) ) {
922 PyObjWrapper res2( PyObject_CallMethod( myModule, (char*)"views", (char*)"" ) );
927 // parse the return value
928 // result can be one string...
929 if ( PyString_Check( res2 ) ) {
930 myViewMgrList.append( PyString_AsString( res2 ) );
932 // ... or list of strings
933 else if ( PyList_Check( res2 ) ) {
934 int size = PyList_Size( res2 );
935 for ( int i = 0; i < size; i++ ) {
936 PyObject* value = PyList_GetItem( res2, i );
937 if( value && PyString_Check( value ) ) {
938 myViewMgrList.append( PyString_AsString( value ) );
944 // module is already activated!
949 \brief Internal activation:
951 Performs the following actions:
952 - initialize or get the Python interpreter (one per study)
953 - import the Python GUI module
954 - call Python module's activate() method
956 \param theStudy parent study object
958 void SALOME_PYQT_ModuleLight::activate( SUIT_Study* theStudy )
960 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::activate()" );
963 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
964 int aStudyId = aStudy ? aStudy->id() : 0;
966 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
967 initInterp( aStudyId );
969 myLastActivateStatus = false;
973 // import Python GUI module
976 myLastActivateStatus = false;
981 PyLockWrapper aLock = myInterp->GetLockWrapper();
983 // call Python module's activate() method (for the new modules)
984 if ( PyObject_HasAttrString( myModule , (char*)"activate" ) ) {
985 PyObject* res1 = PyObject_CallMethod( myModule, (char*)"activate", (char*)"" );
986 if ( !res1 || !PyBool_Check( res1 ) ) {
988 // always true for old modules (no return value)
989 myLastActivateStatus = true;
992 // detect return status
993 myLastActivateStatus = PyObject_IsTrue( res1 );
997 // Connect the SUIT_Desktop signal windowActivated() to this->onActiveViewChanged()
998 SUIT_Desktop* aDesk = theStudy->application()->desktop();
1001 connect( aDesk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1002 this, SLOT( onActiveViewChanged( SUIT_ViewWindow* ) ) );
1003 // If a active window exists send activeViewChanged
1004 // If a getActiveView() in SalomePyQt available we no longer need this
1005 SUIT_ViewWindow* aView = aDesk->activeWindow();
1007 activeViewChanged( aView );
1009 // get all view currently opened in the study and connect their signals to
1010 // the corresponding slots of the class.
1011 QList<SUIT_ViewWindow*> wndList = aDesk->windows();
1012 SUIT_ViewWindow* wnd;
1013 foreach ( wnd, wndList )
1019 \brief Additional customization after module is activated:
1021 Performs the following actions:
1022 - get the Python interpreter (one per study)
1023 - import the Python GUI module
1024 - call Python module's setSettings() method (obsolete function,
1025 used for compatibility with old code)
1027 \param theStudy parent study object
1029 void SALOME_PYQT_ModuleLight::customize( SUIT_Study* theStudy )
1031 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::customize()" );
1034 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
1035 int aStudyId = aStudy ? aStudy->id() : 0;
1037 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1038 initInterp( aStudyId );
1042 // import Python GUI module
1047 if ( IsCallOldMethods ) {
1048 // call Python module's setWorkspace() method
1053 PyLockWrapper aLock = myInterp->GetLockWrapper();
1055 if ( IsCallOldMethods ) {
1056 // call Python module's setSettings() method (obsolete)
1057 if ( PyObject_HasAttrString( myModule , (char*)"setSettings" ) ) {
1058 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"setSettings", (char*)"" ) );
1067 \brief Internal deactivation:
1069 Performs the following actions:
1070 - call Python module's deactivate() method
1072 \param theStudy parent study object
1074 void SALOME_PYQT_ModuleLight::deactivate( SUIT_Study* theStudy )
1076 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::deactivate()" );
1078 // check if the subinterpreter is initialized and Python module is imported
1079 if ( !myInterp || !myModule ) {
1080 // Error! Python subinterpreter should be initialized and module should be imported first!
1083 // then call Python module's deactivate() method
1084 if ( PyObject_HasAttrString( myModule , (char*)"deactivate" ) ) {
1085 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"deactivate", (char*)"" ) );
1091 // Disconnect the SUIT_Desktop signal windowActivated()
1092 SUIT_Desktop* aDesk = theStudy->application()->desktop();
1095 disconnect( aDesk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1096 this, SLOT( onActiveViewChanged( SUIT_ViewWindow* ) ) );
1101 \brief Perform internal actions when active study is changed.
1103 Called when active the study is actived (user brings its
1105 - initialize or get the Python interpreter (one per study)
1106 - import the Python GUI module
1107 - call Python module's activeStudyChanged() method
1109 \param theStudy study being activated
1111 void SALOME_PYQT_ModuleLight::studyChanged( SUIT_Study* theStudy )
1113 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::studyChanged()" );
1116 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
1117 int aStudyId = aStudy ? aStudy->id() : 0;
1119 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1120 initInterp( aStudyId );
1124 // import Python GUI module
1129 if ( IsCallOldMethods ) {
1130 // call Python module's setWorkspace() method
1135 PyLockWrapper aLock = myInterp->GetLockWrapper();
1137 // call Python module's activeStudyChanged() method
1138 if ( PyObject_HasAttrString( myModule, (char*)"activeStudyChanged" ) ) {
1139 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"activeStudyChanged", (char*)"i", aStudyId ) );
1147 \brief Process (internally) context popup menu request.
1149 Performs the following actions:
1150 - calls Python module's definePopup(...) method (obsolete function,
1151 used for compatibility with old code) to define the popup menu context
1152 - parses XML resourses file (if exists) and fills the popup menu with the items)
1153 - calls Python module's customPopup(...) method (obsolete function,
1154 used for compatibility with old code) to allow module to customize the popup menu
1155 - for new modules calls createPopupMenu() function to allow the
1156 modules to build the popup menu by using insertItem(...) Qt functions.
1158 \param theContext popup menu context
1159 \param thePopupMenu popup menu
1161 void SALOME_PYQT_ModuleLight::contextMenu( const QString& theContext, QMenu* thePopupMenu )
1163 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::contextMenu()" );
1165 // Python interpreter should be initialized and Python module should be
1167 if ( !myInterp || !myModule )
1170 QString aContext( "" ), aObject( "" ), aParent( theContext );
1172 if ( IsCallOldMethods && PyObject_HasAttrString( myModule, (char*)"definePopup" ) ) {
1173 // call definePopup() Python module's function
1174 // this is obsolete function, used only for compatibility reasons
1175 PyObjWrapper res( PyObject_CallMethod( myModule,
1176 (char*)"definePopup",
1178 theContext.toLatin1().constData(),
1179 aObject.toLatin1().constData(),
1180 aParent.toLatin1().constData() ) );
1185 // parse return value
1187 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
1193 } // if ( IsCallOldMethods ... )
1195 // first try to create menu via XML parser:
1196 // we create popup menus without help of QtxPopupMgr
1198 myXmlHandler->createPopup( thePopupMenu, aContext, aParent, aObject );
1200 #if SIP_VERSION < 0x040800
1201 PyObjWrapper sipPopup( sipBuildResult( 0, "M", thePopupMenu, sipClass_QMenu) );
1203 PyObjWrapper sipPopup( sipBuildResult( 0, "D", thePopupMenu, sipType_QMenu, NULL) );
1206 // then call Python module's createPopupMenu() method (for new modules)
1207 if ( PyObject_HasAttrString( myModule, (char*)"createPopupMenu" ) ) {
1208 PyObjWrapper res1( PyObject_CallMethod( myModule,
1209 (char*)"createPopupMenu",
1212 theContext.toLatin1().constData() ) );
1218 if ( IsCallOldMethods && PyObject_HasAttrString( myModule, (char*)"customPopup" ) ) {
1219 // call customPopup() Python module's function
1220 // this is obsolete function, used only for compatibility reasons
1221 PyObjWrapper res2( PyObject_CallMethod( myModule,
1222 (char*)"customPopup",
1225 aContext.toLatin1().constData(),
1226 aObject.toLatin1().constData(),
1227 aParent.toLatin1().constData() ) );
1235 \brief Internal GUI event handling.
1237 Performs the following actions:
1238 - calls Python module's OnGUIEvent() method
1240 \param theId GUI action ID
1242 void SALOME_PYQT_ModuleLight::guiEvent( const int theId )
1244 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::guiEvent()" );
1246 // Python interpreter should be initialized and Python module should be
1248 if ( !myInterp || !myModule )
1251 if ( PyObject_HasAttrString( myModule, (char*)"OnGUIEvent" ) ) {
1252 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"OnGUIEvent", (char*)"i", theId ) );
1260 \brief Initialize (internally) preferences for the module.
1262 Performs the following actions:
1263 - calls Python module's createPreferences() method
1265 void SALOME_PYQT_ModuleLight::initPreferences()
1267 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initPreferences()" );
1269 // Python interpreter should be initialized and Python module should be
1271 if ( !myInterp || !myModule )
1274 // temporary set myInitModule because createPreferences() method
1275 // might be called during the module intialization process
1276 myInitModule = this;
1278 if ( PyObject_HasAttrString( myModule, (char*)"createPreferences" ) ) {
1279 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"createPreferences", (char*)"" ) );
1289 \brief Initialize python subinterpreter (one per study).
1290 \param theStudyId study ID
1292 void SALOME_PYQT_ModuleLight::initInterp( int theStudyId )
1294 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::initInterp()" );
1297 if ( !theStudyId ) {
1298 // Error! Study Id must not be 0!
1302 // try to find the subinterpreter
1303 if( myInterpMap.contains( theStudyId ) ) {
1305 myInterp = myInterpMap[ theStudyId ];
1309 myInterp = new SALOME_PYQT_PyInterp();
1313 myInterp->initialize();
1314 myInterpMap[ theStudyId ] = myInterp;
1316 #ifndef GUI_DISABLE_CORBA
1317 if(!SUIT_PYTHON::initialized) {
1318 // import 'salome' module and call 'salome_init' method;
1319 // do it only once on interpreter creation
1320 // ... first get python lock
1321 PyLockWrapper aLock = myInterp->GetLockWrapper();
1322 // ... then import a module
1323 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1329 // ... then call a method
1331 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", theStudyId, embedded ) );
1342 \brief Import Python GUI module and remember the reference to the module.
1344 Attention! initInterp() should be called first!!!
1346 void SALOME_PYQT_ModuleLight::importModule()
1348 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::importModule()" );
1350 // check if the subinterpreter is initialized
1352 // Error! Python subinterpreter should be initialized first!
1356 // import Python GUI module and puts it in <myModule> attribute
1357 // ... first get python lock
1358 PyLockWrapper aLock = myInterp->GetLockWrapper();
1359 // ... then import a module
1360 QString aMod = name() + "GUI";
1362 myModule = PyImport_ImportModule( aMod.toLatin1().data() );
1374 \brief Set study workspace to the Python module.
1376 Calls setWorkSpace() method of the Pythohn module with
1377 PyQt QWidget object to use with interpreter.
1379 Attention! initInterp() and importModule() should be called first!!!
1381 void SALOME_PYQT_ModuleLight::setWorkSpace()
1383 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::setWorkSpace()" );
1385 // check if the subinterpreter is initialized and Python module is imported
1386 if ( !myInterp || !myModule ) {
1387 // Error! Python subinterpreter should be initialized and module should be imported first!
1391 // call setWorkspace() method
1392 // ... first get python lock
1393 PyLockWrapper aLock = myInterp->GetLockWrapper();
1395 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1396 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1403 if ( IsCallOldMethods ) {
1404 // ... then get workspace object
1405 QWidget* aWorkspace = 0;
1406 if ( getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1407 STD_MDIDesktop* aDesktop = dynamic_cast<STD_MDIDesktop*>( getApp()->desktop() );
1409 aWorkspace = aDesktop->workspace();
1411 else if ( getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1412 STD_TabDesktop* aDesktop = dynamic_cast<STD_TabDesktop*>( getApp()->desktop() );
1414 aWorkspace = aDesktop->workstack();
1416 #if SIP_VERSION < 0x040800
1417 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1419 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1421 // ... and finally call Python module's setWorkspace() method (obsolete)
1422 if ( PyObject_HasAttrString( myModule, (char*)"setWorkSpace" ) ) {
1423 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1432 \brief Preference changing callback function (internal).
1434 Performs the following actions:
1435 - call Python module's preferenceChanged() method
1437 \param section setting section name
1438 \param setting setting name
1440 void SALOME_PYQT_ModuleLight::prefChanged( const QString& section, const QString& setting )
1442 FuncMsg fmsg( "SALOME_PYQT_ModuleLight::prefChanged()" );
1444 // Python interpreter should be initialized and Python module should be
1446 if ( !myInterp || !myModule )
1449 if ( PyObject_HasAttrString( myModule, (char*)"preferenceChanged" ) ) {
1450 PyObjWrapper res( PyObject_CallMethod( myModule,
1451 (char*)"preferenceChanged",
1453 section.toLatin1().constData(),
1454 setting.toLatin1().constData() ) );
1462 \brief Get default menu group identifier
1463 \return menu group ID (40 by default)
1465 int SALOME_PYQT_ModuleLight::defaultMenuGroup()
1467 return DEFAULT_GROUP;
1471 // The next methods call the parent implementation.
1472 // This is done to open protected methods from CAM_Module class.
1476 \brief Create toolbar with specified \a name.
1477 \param name toolbar name
1478 \return toolbar ID or -1 if toolbar creation is failed
1480 int SALOME_PYQT_ModuleLight::createTool( const QString& name )
1482 return LightApp_Module::createTool( name );
1486 \brief Insert action with specified \a id to the toolbar.
1488 \param tBar toolbar ID
1489 \param idx required index in the toolbar
1490 \return action ID or -1 if action could not be added
1492 int SALOME_PYQT_ModuleLight::createTool( const int id, const int tBar, const int idx )
1494 return LightApp_Module::createTool( id, tBar, idx );
1498 \brief Insert action with specified \a id to the toolbar.
1500 \param tBar toolbar name
1501 \param idx required index in the toolbar
1502 \return action ID or -1 if action could not be added
1504 int SALOME_PYQT_ModuleLight::createTool( const int id, const QString& tBar, const int idx )
1506 return LightApp_Module::createTool( id, tBar, idx );
1510 \brief Insert action to the toolbar.
1512 \param tBar toolbar ID
1513 \param id required action ID
1514 \param idx required index in the toolbar
1515 \return action ID or -1 if action could not be added
1517 int SALOME_PYQT_ModuleLight::createTool( QAction* a, const int tBar, const int id, const int idx )
1519 return LightApp_Module::createTool( a, tBar, id, idx );
1523 \brief Insert action to the toolbar.
1525 \param tBar toolbar name
1526 \param id required action ID
1527 \param idx required index in the toolbar
1528 \return action ID or -1 if action could not be added
1530 int SALOME_PYQT_ModuleLight::createTool( QAction* a, const QString& tBar, const int id, const int idx )
1532 return LightApp_Module::createTool( a, tBar, id, idx );
1536 \brief Create main menu.
1537 \param subMenu menu name
1538 \param menu parent menu ID
1539 \param id required menu ID
1540 \param group menu group ID
1541 \param idx required index in the menu
1542 \return menu ID or -1 if menu could not be added
1544 int SALOME_PYQT_ModuleLight::createMenu( const QString& subMenu, const int menu, const int id, const int group, const int idx )
1546 return LightApp_Module::createMenu( subMenu, menu, id, group, idx );
1550 \brief Create main menu.
1551 \param subMenu menu name
1552 \param menu parent menu name (list of menu names separated by "|")
1553 \param id required menu ID
1554 \param group menu group ID
1555 \param idx required index in the menu
1556 \return menu ID or -1 if menu could not be added
1558 int SALOME_PYQT_ModuleLight::createMenu( const QString& subMenu, const QString& menu, const int id, const int group, const int idx )
1560 return LightApp_Module::createMenu( subMenu, menu, id, group, idx );
1564 \brief Insert action to the main menu.
1566 \param menu parent menu ID
1567 \param group menu group ID
1568 \param idx required index in the menu
1569 \return action ID or -1 if action could not be added
1571 int SALOME_PYQT_ModuleLight::createMenu( const int id, const int menu, const int group, const int idx )
1573 return LightApp_Module::createMenu( id, menu, group, idx );
1577 \brief Insert action to the main menu.
1579 \param menu parent menu name (list of menu names separated by "|")
1580 \param group menu group ID
1581 \param idx required index in the menu
1582 \return action ID or -1 if action could not be added
1584 int SALOME_PYQT_ModuleLight::createMenu( const int id, const QString& menu, const int group, const int idx )
1586 return LightApp_Module::createMenu( id, menu, group, idx );
1590 \brief Insert action to the main menu.
1592 \param menu parent menu ID
1593 \param group menu group ID
1594 \param idx required index in the menu
1595 \return action ID or -1 if action could not be added
1597 int SALOME_PYQT_ModuleLight::createMenu( QAction* a, const int menu, const int id, const int group, const int idx )
1599 return LightApp_Module::createMenu( a, menu, id, group, idx );
1603 \brief Insert action to the main menu.
1605 \param menu parent menu name (list of menu names separated by "|")
1606 \param group menu group ID
1607 \param idx required index in the menu
1608 \return action ID or -1 if action could not be added
1610 int SALOME_PYQT_ModuleLight::createMenu( QAction* a, const QString& menu, const int id, const int group, const int idx )
1612 return LightApp_Module::createMenu( a, menu, id, group, idx );
1616 \brief Create separator action which can be used in the menu or toolbar.
1617 \return new separator action
1619 QAction* SALOME_PYQT_ModuleLight::separator()
1621 return LightApp_Module::separator();
1625 \brief Get action by specified \a id.
1626 \return action or 0 if it is not found
1628 QAction* SALOME_PYQT_ModuleLight::action( const int id ) const
1630 QAction* a = LightApp_Module::action( id );
1633 QMenu* m = menuMgr()->findMenu( id );
1634 if ( m ) a = m->menuAction();
1640 \brief Get action identifier.
1641 \return action ID or -1 if action is not registered
1643 int SALOME_PYQT_ModuleLight::actionId( const QAction* a ) const
1645 return LightApp_Module::actionId( a );
1649 \brief Create new action.
1651 If the action with specified identifier already registered
1652 it is not created, but its attributes are only modified.
1655 \param text tooltip text
1657 \param menu menu text
1658 \param tip status tip
1659 \param key keyboard shortcut
1660 \param toggle \c true for checkable action
1661 \return created action
1663 QAction* SALOME_PYQT_ModuleLight::createAction( const int id, const QString& text, const QString& icon,
1664 const QString& menu, const QString& tip, const int key,
1665 const bool toggle, QObject* parent )
1667 QIcon anIcon = loadIcon( icon );
1668 QAction* a = action( id );
1670 if ( a->toolTip().isEmpty() && !text.isEmpty() ) a->setToolTip( text );
1671 if ( a->text().isEmpty() && !menu.isEmpty() ) a->setText( menu );
1672 if ( a->icon().isNull() && !anIcon.isNull() ) a->setIcon( anIcon );
1673 if ( a->statusTip().isEmpty() && !tip.isEmpty() ) a->setStatusTip( tip );
1674 if ( a->shortcut().isEmpty() && key ) a->setShortcut( key );
1675 if ( a->isCheckable() != toggle ) a->setCheckable( toggle );
1676 disconnect( a, SIGNAL( triggered( bool ) ), this, SLOT( onGUIEvent() ) );
1677 connect( a, SIGNAL( triggered( bool ) ), this, SLOT( onGUIEvent() ) );
1680 a = LightApp_Module::createAction( id,
1686 parent ? parent : this,
1689 SLOT( onGUIEvent() ) );
1695 \brief Create new action group.
1697 If the action with specified identifier already registered
1698 it is not created, but its attributes are only modified.
1701 \param text tooltip text
1703 \param menu menu text
1704 \param tip status tip
1705 \param key keyboard shortcut
1706 \param toggle \c true for checkable action
1707 \return created action
1709 QtxActionGroup* SALOME_PYQT_ModuleLight::createActionGroup(const int id, const bool exclusive)
1711 QtxActionGroup* a = qobject_cast<QtxActionGroup*>( action( id ) );
1713 a = new QtxActionGroup( this );
1714 LightApp_Module::registerAction( id, a );
1716 a->setExclusive( exclusive );
1721 \brief Load icon from resource file.
1722 \param fileName icon file name
1723 \return icon (null icon if loading failed)
1725 QIcon SALOME_PYQT_ModuleLight::loadIcon( const QString& fileName )
1728 if ( !fileName.isEmpty() ) {
1729 QPixmap pixmap = getApp()->resourceMgr()->loadPixmap( name(), tr( fileName.toLatin1() ) );
1730 if ( !pixmap.isNull() )
1731 anIcon = QIcon( pixmap );
1737 \brief Add global application preference (for example,
1738 application specific section).
1739 \param label preference name
1740 \return preference ID
1742 int SALOME_PYQT_ModuleLight::addGlobalPreference( const QString& label )
1744 LightApp_Preferences* pref = preferences();
1748 return pref->addPreference( label, -1 );
1752 \brief Add preference.
1753 \param label preference name
1754 \return preference ID
1756 int SALOME_PYQT_ModuleLight::addPreference( const QString& label )
1758 return LightApp_Module::addPreference( label );
1762 \brief Add preference.
1763 \param label preference name
1764 \param pId parent preference ID
1765 \param type preference type
1766 \param section resource file section name
1767 \param param resource file setting name
1768 \return preference ID
1770 int SALOME_PYQT_ModuleLight::addPreference( const QString& label,
1771 const int pId, const int type,
1772 const QString& section,
1773 const QString& param )
1775 return LightApp_Module::addPreference( label, pId, type, section, param );
1779 \brief Get the preference property.
1780 \param id preference ID
1781 \param prop property name
1782 \return property value (invalid QVariant() if property is not found)
1784 QVariant SALOME_PYQT_ModuleLight::preferenceProperty( const int id,
1785 const QString& prop ) const
1787 QVariant v = LightApp_Module::preferenceProperty( id, prop );
1792 \brief Set the preference property.
1793 \param id preference ID
1794 \param prop property name
1795 \param var property value
1797 void SALOME_PYQT_ModuleLight::setPreferenceProperty( const int id,
1798 const QString& prop,
1799 const QVariant& var )
1801 LightApp_Module::setPreferenceProperty( id, prop, var );
1806 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1807 \param pview view being activated
1809 void SALOME_PYQT_ModuleLight::onActiveViewChanged( SUIT_ViewWindow* pview )
1811 class ActiveViewChange : public PyInterp_LockRequest
1814 ActiveViewChange( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
1815 : PyInterp_LockRequest( _py_interp, 0, true ),
1816 myObj(_obj),myView(_pview) {}
1819 virtual void execute()
1821 myObj->activeViewChanged( myView );
1825 SALOME_PYQT_ModuleLight* myObj;
1826 const SUIT_ViewWindow * myView;
1829 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChange( myInterp, this, pview ) );
1833 \brief Processes the view changing, calls Python module's activeViewChanged() method
1834 \param pview view being activated
1836 void SALOME_PYQT_ModuleLight::activeViewChanged( const SUIT_ViewWindow* pview )
1838 if ( !myInterp || !myModule )
1841 // Do not use SUIT_ViewWindow::closing() signal here. View manager reacts on
1842 // this signal and deletes view. So our slot does not works if it is connected
1843 // on this signal. SUIT_ViewManager::deleteView(SUIT_ViewWindow*) is used here
1845 connectView( pview );
1847 if ( PyObject_HasAttrString( myModule, (char*)"activeViewChanged" ) )
1852 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"activeViewChanged", (char*)"i" , pview->getId() ) );
1859 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1860 \param pview view being cloned
1862 void SALOME_PYQT_ModuleLight::onViewCloned( SUIT_ViewWindow* pview )
1864 class ViewClone : public PyInterp_LockRequest
1867 ViewClone( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
1868 : PyInterp_LockRequest( _py_interp, 0, true ),
1869 myObj(_obj), myView(_pview) {}
1872 virtual void execute()
1874 myObj->viewCloned( myView );
1878 SALOME_PYQT_ModuleLight* myObj;
1879 const SUIT_ViewWindow* myView;
1882 PyInterp_Dispatcher::Get()->Exec( new ViewClone( myInterp, this, pview ) );
1886 \brief Processes the view cloning, calls Python module's activeViewCloned() method
1887 \param pview view being cloned
1889 void SALOME_PYQT_ModuleLight::viewCloned( const SUIT_ViewWindow* pview )
1891 if ( !myInterp || !myModule || !pview )
1894 if ( PyObject_HasAttrString( myModule, (char*)"viewCloned" ) )
1896 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewCloned", (char*)"i", pview->getId() ) );
1903 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1904 \param pview view being closed
1906 void SALOME_PYQT_ModuleLight::onViewTryClose( SUIT_ViewWindow* pview )
1908 class ViewTryClose : public PyInterp_LockRequest
1911 ViewTryClose( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
1912 : PyInterp_LockRequest( _py_interp, 0, true ),
1913 myObj(_obj),myView(_pview) {}
1916 virtual void execute()
1918 myObj->viewTryClose( myView );
1922 SALOME_PYQT_ModuleLight* myObj;
1923 const SUIT_ViewWindow * myView;
1926 PyInterp_Dispatcher::Get()->Exec( new ViewTryClose( myInterp, this, pview ) );
1930 \brief Processes the view closing attempt, calls Python module's viewTryClose() method
1931 \param pview view user tries to close
1933 void SALOME_PYQT_ModuleLight::viewTryClose( const SUIT_ViewWindow* pview )
1935 if ( !myInterp || !myModule )
1938 if ( PyObject_HasAttrString( myModule, (char*)"viewTryClose" ) )
1940 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewTryClose", (char*)"i", pview->getId() ) );
1949 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1950 \param pview view being closed
1952 void SALOME_PYQT_ModuleLight::onViewClosed( SUIT_ViewWindow* pview )
1954 class ViewClose : public PyInterp_LockRequest
1957 ViewClose( PyInterp_Interp* _py_interp, SALOME_PYQT_ModuleLight* _obj, const SUIT_ViewWindow* _pview )
1958 : PyInterp_LockRequest( _py_interp, 0, true ),
1959 myObj(_obj),myView(_pview) {}
1962 virtual void execute()
1964 myObj->viewClosed( myView );
1968 SALOME_PYQT_ModuleLight* myObj;
1969 const SUIT_ViewWindow * myView;
1972 PyInterp_Dispatcher::Get()->Exec( new ViewClose( myInterp, this, pview ) );
1976 \brief Processes the view closing, calls Python module's viewClosed() method
1977 \param pview view being closed
1979 void SALOME_PYQT_ModuleLight::viewClosed( const SUIT_ViewWindow* pview )
1981 if ( !myInterp || !myModule )
1984 if ( PyObject_HasAttrString( myModule, (char*)"viewClosed" ) )
1986 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"viewClosed", (char*)"i", pview->getId() ) );
1995 \brief Connects or disconnects signals about activating and cloning view on the module slots
1996 \param pview view which is connected/disconnected
1998 void SALOME_PYQT_ModuleLight::connectView( const SUIT_ViewWindow* pview )
2000 SUIT_ViewManager* viewMgr = pview->getViewManager();
2001 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2005 disconnect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2006 this, SLOT( onViewTryClose( SUIT_ViewWindow* ) ) );
2007 disconnect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2008 this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) );
2010 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2011 this, SLOT( onViewTryClose( SUIT_ViewWindow* ) ) );
2012 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2013 this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) );
2016 // Connect cloneView() signal of an OCC View
2017 if ( pview->inherits( "OCCViewer_ViewWindow" ) )
2019 disconnect( pview, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2020 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
2021 connect( pview, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2022 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
2024 // Connect cloneView() signal of Plot2d View manager
2025 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) )
2027 disconnect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2028 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
2029 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2030 this, SLOT( onViewCloned( SUIT_ViewWindow* ) ) );
2035 \brief Get tag name for the DOM element.
2036 \param element DOM element
2037 \return empty string if the element does not have tag name
2040 static QString tagName( const QDomElement& element )
2042 return element.tagName().trimmed();
2046 \brief Get DOM element's attribute by its name.
2047 \param element DOM element
2048 \param attName attribute name
2049 \return empty string if the element does not have such attribute
2052 static QString attribute( const QDomElement& element, const QString& attName )
2054 return element.attribute( attName ).trimmed();
2058 \brief Inspect specified string for the boolean value.
2060 This function returns \c true if string represents boolean value:
2061 - "true", "yes" or "1" for \c true
2062 - "false", "no" or "0" for \c false
2063 Second parameter allows to specify what boolean value is expected:
2066 - other value is not taken into account (return represented value)
2068 \param value inspected string
2069 \param check expected boolean value
2070 \return boolean value represented by the string (\a check is not 1 or 0)
2071 or \c true if value correspond to the specified \a check
2073 static bool checkBool( const QString& value, const int check = -1 )
2075 QString v = value.toLower();
2076 if ( ( v == "true" || v == "yes" || v == "1" ) && ( check != 0 ) )
2078 if ( ( v == "false" || v == "no" || v == "0" ) && ( check == 0 ) )
2084 \brief Inspect specified string for the integer value.
2086 This function returns returns -1 if item is empty or represents
2088 \param value inspected string
2089 \param def default value
2090 \param shift shift value (it is added to the integer value to produce shifted result)
2092 static int checkInt( const QString& value, const int def = -1, const int shift = -1 )
2095 int val = value.toInt( &bOk );
2096 if ( !bOk ) val = def;
2097 if ( shift > 0 && bOk && val < 0 )
2105 \param module parent module pointer
2106 \param fileName XML file path
2108 SALOME_PYQT_ModuleLight::XmlHandler::XmlHandler( SALOME_PYQT_ModuleLight* module,
2109 const QString& fileName )
2110 : myModule( module )
2112 if ( fileName.isEmpty() )
2114 QFile aFile( fileName );
2115 if ( !aFile.open( QIODevice::ReadOnly ) )
2117 myDoc.setContent( &aFile );
2122 \brief Parse XML file and create actions.
2125 Called by SALOME_PYQT_ModuleLight::activate() in order to create actions
2128 void SALOME_PYQT_ModuleLight::XmlHandler::createActions()
2130 // get document element
2131 QDomElement aDocElem = myDoc.documentElement();
2133 // create main menu actions
2134 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
2135 for ( int i = 0; i < aMenuList.count(); i++ ) {
2136 QDomNode n = aMenuList.item( i );
2140 // create toolbars actions
2141 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
2142 for ( int i = 0; i < aToolsList.count(); i++ ) {
2143 QDomNode n = aToolsList.item( i );
2149 \brief Create popup menu.
2151 \param menu popup menu
2152 \param context popup menu context
2153 \param context popup menu parent object name
2154 \param context popup menu object name
2156 void SALOME_PYQT_ModuleLight::XmlHandler::createPopup( QMenu* menu,
2157 const QString& context,
2158 const QString& parent,
2159 const QString& object )
2161 // get document element
2162 QDomElement aDocElem = myDoc.documentElement();
2164 // get popup menus actions
2165 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
2166 for ( int i = 0; i < aPopupList.count(); i++ ) {
2167 QDomNode n = aPopupList.item( i );
2168 if ( !n.isNull() && n.isElement() ) {
2169 QDomElement e = n.toElement();
2170 // QString lab = attribute( e, "label-id" ); // not used //
2171 QString ctx = attribute( e, "context-id" );
2172 QString prt = attribute( e, "parent-id" );
2173 QString obj = attribute( e, "object-id" );
2174 if ( ctx == context && prt == parent && obj == object ) {
2175 insertPopupItems( n, menu );
2183 \brief Activate menus
2185 \param enable if \c true menus are activated, otherwise menus are deactivated
2187 void SALOME_PYQT_ModuleLight::XmlHandler::activateMenus( bool enable )
2192 QtxActionMenuMgr* mgr = myModule->menuMgr();
2194 foreach( id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
2198 \brief Create main menu item and insert actions to it.
2200 \param parentNode XML node with menu description
2201 \param parentMenuId parent menu ID (-1 for top-level menu)
2202 \param parentPopup parent popup menu (0 for top-level menu)
2204 void SALOME_PYQT_ModuleLight::XmlHandler::createMenu( QDomNode& parentNode,
2205 const int parentMenuId,
2206 QMenu* parentPopup )
2208 if ( !myModule || parentNode.isNull() )
2211 QDomElement parentElement = parentNode.toElement();
2212 if ( !parentElement.isNull() ) {
2213 QString plabel = attribute( parentElement, "label-id" );
2214 int pid = checkInt( attribute( parentElement, "item-id" ) );
2215 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
2216 int group = checkInt( attribute( parentElement, "group-id" ),
2217 myModule->defaultMenuGroup() );
2218 if ( !plabel.isEmpty() ) {
2222 menuId = myModule->createMenu( plabel, // label
2223 parentMenuId, // parent menu ID, -1 for top-level menu
2227 myMenuItems.append( menuId );
2228 QDomNode node = parentNode.firstChild();
2229 while ( !node.isNull() ) {
2230 if ( node.isElement() ) {
2231 QDomElement elem = node.toElement();
2232 QString aTagName = tagName( elem );
2233 if ( aTagName == "popup-item" ) {
2234 int id = checkInt( attribute( elem, "item-id" ) );
2235 int pos = checkInt( attribute( elem, "pos-id" ) );
2236 int group = checkInt( attribute( elem, "group-id" ),
2237 myModule->defaultMenuGroup() );
2238 QString label = attribute( elem, "label-id" );
2239 QString icon = attribute( elem, "icon-id" );
2240 QString tooltip = attribute( elem, "tooltip-id" );
2241 QString accel = attribute( elem, "accel-id" );
2242 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
2244 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2245 // also check if the action with given ID is already created
2247 // create menu action
2248 QAction* action = myModule->createAction( id, // ID
2252 tooltip, // status-bar text
2253 QKeySequence( accel ), // keyboard accelerator
2254 toggle ); // toogled action
2255 myModule->createMenu( action, // action
2256 menuId, // parent menu ID
2257 id, // ID (same as for createAction())
2262 else if ( aTagName == "submenu" ) {
2264 createMenu( node, menuId, popup );
2266 else if ( aTagName == "separator" ) {
2267 // create menu separator
2268 int id = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
2269 int pos = checkInt( attribute( elem, "pos-id" ) );
2270 int group = checkInt( attribute( elem, "group-id" ),
2271 myModule->defaultMenuGroup() );
2272 QAction* action = myModule->separator();
2273 myModule->createMenu( action, // separator action
2274 menuId, // parent menu ID
2280 node = node.nextSibling();
2287 \brief Create a toolbar and insert actions to it.
2288 \param parentNode XML node with toolbar description
2290 void SALOME_PYQT_ModuleLight::XmlHandler::createToolBar( QDomNode& parentNode )
2292 if ( !myModule || parentNode.isNull() )
2295 QDomElement parentElement = parentNode.toElement();
2296 if ( !parentElement.isNull() ) {
2297 QString aLabel = attribute( parentElement, "label-id" );
2298 if ( !aLabel.isEmpty() ) {
2300 int tbId = myModule->createTool( aLabel );
2301 QDomNode node = parentNode.firstChild();
2302 while ( !node.isNull() ) {
2303 if ( node.isElement() ) {
2304 QDomElement elem = node.toElement();
2305 QString aTagName = tagName( elem );
2306 if ( aTagName == "toolbutton-item" ) {
2307 int id = checkInt( attribute( elem, "item-id" ) );
2308 int pos = checkInt( attribute( elem, "pos-id" ) );
2309 QString label = attribute( elem, "label-id" );
2310 QString icon = attribute( elem, "icon-id" );
2311 QString tooltip = attribute( elem, "tooltip-id" );
2312 QString accel = attribute( elem, "accel-id" );
2313 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
2315 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2316 // also check if the action with given ID is already created
2318 // create toolbar action
2319 QAction* action = myModule->createAction( id, // ID
2323 tooltip, // status-bar text
2324 QKeySequence( accel ), // keyboard accelerator
2325 toggle ); // toogled action
2326 myModule->createTool( action, tbId, -1, pos );
2329 else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
2330 // create toolbar separator
2331 int pos = checkInt( attribute( elem, "pos-id" ) );
2332 QAction* action = myModule->separator();
2333 myModule->createTool( action, tbId, -1, pos );
2336 node = node.nextSibling();
2343 \brief Fill popup menu with the items.
2344 \param parentNode XML node with popup menu description
2345 \param menu popup menu
2347 void SALOME_PYQT_ModuleLight::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
2349 if ( !myModule && parentNode.isNull() )
2352 // we create popup menus without help of QtxPopupMgr
2353 QDomNode node = parentNode.firstChild();
2354 while ( !node.isNull() ) {
2355 if ( node.isElement() ) {
2356 QDomElement elem = node.toElement();
2357 QString aTagName = tagName( elem );
2358 QList<QAction*> actions = menu->actions();
2359 if ( aTagName == "popup-item" ) {
2360 // insert a command item
2361 int id = checkInt( attribute( elem, "item-id" ) );
2362 int pos = checkInt( attribute( elem, "pos-id" ) );
2363 QString label = attribute( elem, "label-id" );
2364 QString icon = attribute( elem, "icon-id" );
2365 QString tooltip = attribute( elem, "tooltip-id" );
2366 QString accel = attribute( elem, "accel-id" );
2367 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
2369 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
2370 // also check if the action with given ID is already created
2372 QAction* action = myModule->createAction( id, // ID
2376 tooltip, // status-bar text
2377 QKeySequence( accel ), // keyboard accelerator
2378 toggle ); // toogled action
2379 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2380 menu->insertAction( before, action );
2383 else if ( aTagName == "submenu" ) {
2385 ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
2386 int pos = checkInt( attribute( elem, "pos-id" ) );
2387 QString label = attribute( elem, "label-id" );
2388 QString icon = attribute( elem, "icon-id" );
2391 if ( !icon.isEmpty() ) {
2392 QPixmap pixmap = myModule->getApp()->resourceMgr()->loadPixmap( myModule->name(), icon );
2393 if ( !pixmap.isNull() )
2394 anIcon = QIcon( pixmap );
2397 QMenu* newPopup = menu->addMenu( anIcon, label );
2398 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2399 menu->insertMenu( before, newPopup );
2400 insertPopupItems( node, newPopup );
2402 else if ( aTagName == "separator" ) {
2403 // create menu separator
2404 int pos = checkInt( attribute( elem, "pos-id" ) );
2405 QAction* action = myModule->separator();
2406 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
2407 menu->insertAction( before, action );
2410 node = node.nextSibling();
2415 * Save study request.
2416 * Called when user save study.
2418 void SALOME_PYQT_ModuleLight::save(QStringList& theListOfFiles)
2420 MESSAGE("SALOME_PYQT_ModuleLight::save()")
2421 // perform synchronous request to Python event dispatcher
2422 class SaveEvent: public PyInterp_LockRequest
2425 SaveEvent(PyInterp_Interp* _py_interp,
2426 SALOME_PYQT_ModuleLight* _obj,
2427 QStringList& _files_list)
2428 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2430 myFilesList(_files_list) {}
2432 virtual void execute()
2434 myObj->saveEvent(myFilesList);
2437 SALOME_PYQT_ModuleLight* myObj;
2438 QStringList& myFilesList;
2441 // Posting the request only if dispatcher is not busy!
2442 // Executing the request synchronously
2443 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
2444 PyInterp_Dispatcher::Get()->Exec( new SaveEvent( myInterp, this, theListOfFiles ) );
2447 void SALOME_PYQT_ModuleLight::saveEvent(QStringList& theListOfFiles)
2449 MESSAGE("SALOME_PYQT_ModuleLight::saveEvent()");
2450 QStringList::Iterator it = theListOfFiles.begin();
2451 // Python interpreter should be initialized and Python module should be
2453 if ( !myInterp || !myModule || (it == theListOfFiles.end()))
2456 if ( PyObject_HasAttrString(myModule, (char*)"saveFiles") ) {
2457 // temporary set myInitModule because saveEvent() method
2458 // might be called by the framework when this module is inactive,
2459 // but still it should be possible to access this module's data
2461 myInitModule = this;
2463 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"saveFiles",
2464 (char*)"s", (*it).toLatin1().constData()));
2472 // parse the return value
2473 // result can be one string...
2474 if ( PyString_Check( res ) ) {
2475 QString astr = PyString_AsString( res );
2477 theListOfFiles.append(astr);
2479 //also result can be a list...
2480 else if ( PyList_Check( res ) ) {
2481 int size = PyList_Size( res );
2482 for ( int i = 0; i < size; i++ ) {
2483 PyObject* value = PyList_GetItem( res, i );
2484 if( value && PyString_Check( value ) ) {
2485 theListOfFiles.append( PyString_AsString( value ) );
2494 * Python dump request.
2495 * Called when user activates dump study operation.
2497 void SALOME_PYQT_ModuleLight::dumpPython(QStringList& theListOfFiles)
2499 MESSAGE("SALOME_PYQT_ModuleLight::dumpPython()")
2500 // perform synchronous request to Python event dispatcher
2501 class DumpEvent: public PyInterp_LockRequest
2504 DumpEvent(PyInterp_Interp* _py_interp,
2505 SALOME_PYQT_ModuleLight* _obj,
2506 QStringList& _files_list)
2507 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2509 myFilesList(_files_list) {}
2511 virtual void execute()
2513 myObj->dumpEvent(myFilesList);
2516 SALOME_PYQT_ModuleLight* myObj;
2517 QStringList& myFilesList;
2520 // Posting the request only if dispatcher is not busy!
2521 // Executing the request synchronously
2522 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
2523 PyInterp_Dispatcher::Get()->Exec( new DumpEvent( myInterp, this, theListOfFiles ) );
2526 void SALOME_PYQT_ModuleLight::dumpEvent(QStringList& theListOfFiles)
2528 MESSAGE("SALOME_PYQT_ModuleLight::dumpEvent()");
2529 QStringList::Iterator it = theListOfFiles.begin();
2530 // Python interpreter should be initialized and Python module should be
2532 if ( !myInterp || !myModule || (it == theListOfFiles.end()))
2535 if ( PyObject_HasAttrString(myModule, (char*)"dumpStudy") ) {
2536 // temporary set myInitModule because dumpEvent() method
2537 // might be called by the framework when this module is inactive,
2538 // but still it should be possible to access this module's data
2540 myInitModule = this;
2542 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"dumpStudy",
2543 (char*)"s", (*it).toLatin1().constData()));
2551 // parse the return value
2552 // result can be one string...
2553 if ( PyString_Check( res ) ) {
2554 QString astr = PyString_AsString( res );
2556 theListOfFiles.append(astr);
2558 //also result can be a list...
2559 else if ( PyList_Check( res ) ) {
2560 int size = PyList_Size( res );
2561 for ( int i = 0; i < size; i++ ) {
2562 PyObject* value = PyList_GetItem( res, i );
2563 if( value && PyString_Check( value ) ) {
2564 theListOfFiles.append( PyString_AsString( value ) );
2573 * Open study request.
2574 * Called when user open study.
2576 bool SALOME_PYQT_ModuleLight::open(QStringList theListOfFiles)
2578 MESSAGE("SALOME_PYQT_ModuleLight::open()");
2579 // perform synchronous request to Python event dispatcher
2580 bool opened = false;
2581 class OpenEvent: public PyInterp_LockRequest
2584 OpenEvent(PyInterp_Interp* _py_interp,
2585 SALOME_PYQT_ModuleLight* _obj,
2586 QStringList _files_list,
2588 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2590 myFilesList(_files_list),
2591 myOpened(_opened) {}
2593 virtual void execute()
2595 myObj->openEvent(myFilesList,myOpened);
2599 SALOME_PYQT_ModuleLight* myObj;
2600 QStringList myFilesList;
2604 // Posting the request only if dispatcher is not busy!
2605 // Executing the request synchronously
2606 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
2607 PyInterp_Dispatcher::Get()->Exec( new OpenEvent( myInterp, this, theListOfFiles, opened) );
2612 void SALOME_PYQT_ModuleLight::openEvent(QStringList theListOfFiles, bool &opened)
2614 MESSAGE("SALOME_PYQT_ModuleLight::openEvent()");
2615 // Python interpreter should be initialized and Python module should be
2617 if ( !myInterp || !myModule || theListOfFiles.isEmpty())
2619 QStringList* theList = new QStringList(theListOfFiles);
2621 #if SIP_VERSION < 0x040800
2622 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2624 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2626 if ( PyObject_HasAttrString(myModule , (char*)"openFiles") ) {
2627 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"openFiles",
2628 (char*)"O", sipList.get()));
2629 if( !res || !PyBool_Check( res )) {
2634 opened = PyObject_IsTrue( res );
2641 * Create new empty Data Object and return its entry
2643 QString SALOME_PYQT_ModuleLight::createObject(const QString& parent)
2645 SALOME_PYQT_DataObjectLight* obj=0;
2646 if(!parent.isEmpty())
2648 SALOME_PYQT_DataObjectLight* parentObj = findObject(parent);
2651 obj = new SALOME_PYQT_DataObjectLight(parentObj);
2656 SALOME_PYQT_DataModelLight* dm =
2657 dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
2660 obj = new SALOME_PYQT_DataObjectLight(dm->getRoot());
2664 return obj->entry();
2666 return QString::null;
2670 * Create new Data Object with name, icon and tooltip
2671 * and return its entry
2673 QString SALOME_PYQT_ModuleLight::createObject(const QString& aname,
2674 const QString& iconname,
2675 const QString& tooltip,
2676 const QString& parent)
2678 QString entry = createObject(parent);
2679 SALOME_PYQT_DataObjectLight* obj = findObject(entry);
2683 obj->setName(aname);
2684 obj->setToolTip(tooltip);
2685 obj->setIcon(iconname);
2686 return obj->entry();
2689 return QString::null;
2693 * Find object by entry
2695 SALOME_PYQT_DataObjectLight* SALOME_PYQT_ModuleLight::findObject(const QString& entry)
2697 SALOME_PYQT_DataObjectLight* obj = 0;
2698 SALOME_PYQT_DataModelLight* dm =
2699 dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
2700 if(!entry.isEmpty() && dm ){
2701 for ( SUIT_DataObjectIterator it( dm->getRoot(), SUIT_DataObjectIterator::DepthLeft ); it.current(); ++it ) {
2702 SALOME_PYQT_DataObjectLight* curentobj =
2703 dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.current() );
2705 if(curentobj && curentobj->entry() == entry) {
2715 * Set Name for object
2717 void SALOME_PYQT_ModuleLight::setName(const QString& obj, const QString& name)
2719 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2721 dataObj->setName(name);
2726 * Set Icon for object
2728 void SALOME_PYQT_ModuleLight::setIcon(const QString& obj, const QString& iconname)
2730 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2732 dataObj->setIcon(iconname);
2737 * Return Name of object
2739 QString SALOME_PYQT_ModuleLight::getName(const QString& obj)
2741 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2743 return dataObj->name();
2745 return QString::null;
2749 * Return Tool Tip of object
2751 QString SALOME_PYQT_ModuleLight::getToolTip(const QString& obj)
2753 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2755 return dataObj->toolTip();
2757 return QString::null;
2762 * Set Tool Tip for object
2764 void SALOME_PYQT_ModuleLight::setToolTip(const QString& obj, const QString& tooltip)
2766 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2768 dataObj->setToolTip(tooltip);
2773 * Return color of object
2775 QColor SALOME_PYQT_ModuleLight::getColor(const QString& obj)
2777 SALOME_PYQT_DataObjectLight* dataObj = findObject( obj );
2779 return dataObj->color( SUIT_DataObject::Foreground );
2785 * Set color for object
2787 void SALOME_PYQT_ModuleLight::setColor(const QString& obj, const QColor& color)
2789 SALOME_PYQT_DataObjectLight* dataObj = findObject( obj );
2791 dataObj->setColor( color );
2796 * Return entry of the referenced object (if any)
2798 QString SALOME_PYQT_ModuleLight::getReference(const QString& obj)
2800 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2802 return dataObj->refEntry();
2804 return QString::null;
2809 * Set entry of the referenced object
2811 void SALOME_PYQT_ModuleLight::setReference(const QString& obj, const QString& refEntry)
2813 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2815 dataObj->setRefEntry(refEntry);
2820 * Remove object by entry
2822 void SALOME_PYQT_ModuleLight::removeObject(const QString& obj)
2824 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2826 dataObj->parent()->removeChild(dataObj);
2832 * Remove chields from object
2834 void SALOME_PYQT_ModuleLight::removeChild(const QString& obj)
2836 MESSAGE("SALOME_PYQT_ModuleLight::removeChild()");
2840 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2843 dataObj->children(lst);
2844 QListIterator<SUIT_DataObject*> it( lst );
2845 while( it.hasNext() )
2847 SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
2848 dataObj->removeChild(sobj);
2854 SALOME_PYQT_DataModelLight* dm =
2855 dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
2858 dm->getRoot()->children(lst);
2859 QListIterator<SUIT_DataObject*> it( lst );
2860 while(it.hasNext() )
2862 SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
2863 dm->getRoot()->removeChild(sobj);
2869 QStringList SALOME_PYQT_ModuleLight::getChildren(const QString& obj, const bool rec)
2872 QStringList entryList;
2875 SALOME_PYQT_DataObjectLight* dataObj = findObject(obj);
2878 dataObj->children(lst,rec);
2879 QListIterator<SUIT_DataObject*> it( lst );
2882 SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
2883 entryList.append(sobj->entry());
2889 SALOME_PYQT_DataModelLight* dm =
2890 dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel());
2893 dm->getRoot()->children(lst);
2894 QListIterator<SUIT_DataObject*> it( lst );
2895 while( it.hasNext() )
2897 SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
2898 entryList.append(sobj->entry());
2906 * Create new instance of data model and return it.
2908 CAM_DataModel* SALOME_PYQT_ModuleLight::createDataModel()
2910 MESSAGE( "SALOME_PYQT_ModuleLight::createDataModel()" );
2911 return new SALOME_PYQT_DataModelLight(this);
2915 * Returns the Python module object currently loaded.
2917 PyObject* SALOME_PYQT_ModuleLight::getPythonModule()
2922 bool SALOME_PYQT_ModuleLight::isDraggable( const SUIT_DataObject* what ) const
2924 MESSAGE("SALOME_PYQT_ModuleLight::isDraggable()");
2925 // perform synchronous request to Python event dispatcher
2926 bool draggable = false;
2927 class IsDraggableEvent: public PyInterp_LockRequest
2930 IsDraggableEvent(PyInterp_Interp* _py_interp,
2931 SALOME_PYQT_ModuleLight* _obj,
2932 LightApp_DataObject* _data_object,
2933 bool& _is_draggable )
2934 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2936 myDataObject( _data_object ),
2937 myIsDraggable( _is_draggable ) {}
2939 virtual void execute()
2941 myIsDraggable = myObj->isDraggableEvent( myDataObject );
2945 SALOME_PYQT_ModuleLight* myObj;
2946 LightApp_DataObject* myDataObject;
2947 bool& myIsDraggable;
2950 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
2952 // Posting the request only if dispatcher is not busy!
2953 // Executing the request synchronously
2954 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
2955 PyInterp_Dispatcher::Get()->Exec( new IsDraggableEvent( myInterp,
2956 const_cast<SALOME_PYQT_ModuleLight*>( this ),
2957 const_cast<LightApp_DataObject*>( data_object ),
2962 bool SALOME_PYQT_ModuleLight::isDraggableEvent( LightApp_DataObject* what )
2964 MESSAGE("SALOME_PYQT_ModuleLight::isDraggableEvent()");
2966 bool draggable = false;
2968 // Python interpreter should be initialized and Python module should be
2970 if ( !myInterp || !myModule || !what )
2973 if ( PyObject_HasAttrString(myModule , (char*)"isDraggable") ) {
2974 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"isDraggable",
2975 (char*)"s", what->entry().toLatin1().constData() ) );
2976 if( !res || !PyBool_Check( res )) {
2981 draggable = PyObject_IsTrue( res );
2988 bool SALOME_PYQT_ModuleLight::isDropAccepted( const SUIT_DataObject* where ) const
2990 MESSAGE("SALOME_PYQT_ModuleLight::isDropAccepted()");
2991 // perform synchronous request to Python event dispatcher
2992 bool dropAccepted = false;
2993 class IsDropAcceptedEvent: public PyInterp_LockRequest
2996 IsDropAcceptedEvent(PyInterp_Interp* _py_interp,
2997 SALOME_PYQT_ModuleLight* _obj,
2998 LightApp_DataObject* _data_object,
2999 bool& _is_drop_accepted )
3000 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
3002 myDataObject( _data_object ),
3003 myIsDropAccepted( _is_drop_accepted ) {}
3005 virtual void execute()
3007 myIsDropAccepted = myObj->isDropAcceptedEvent( myDataObject );
3011 SALOME_PYQT_ModuleLight* myObj;
3012 LightApp_DataObject* myDataObject;
3013 bool& myIsDropAccepted;
3016 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
3018 // Posting the request only if dispatcher is not busy!
3019 // Executing the request synchronously
3020 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
3021 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedEvent( myInterp,
3022 const_cast<SALOME_PYQT_ModuleLight*>( this ),
3023 const_cast<LightApp_DataObject*>( data_object ),
3025 return dropAccepted;
3028 bool SALOME_PYQT_ModuleLight::isDropAcceptedEvent( LightApp_DataObject* where )
3030 MESSAGE("SALOME_PYQT_ModuleLight::isDropAcceptedEvent()");
3032 bool dropAccepted = false;
3034 // Python interpreter should be initialized and Python module should be
3036 if ( !myInterp || !myModule || !where )
3037 return dropAccepted;
3039 if ( PyObject_HasAttrString(myModule , (char*)"isDropAccepted") ) {
3040 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"isDropAccepted",
3041 (char*)"s", where->entry().toLatin1().constData() ) );
3042 if( !res || !PyBool_Check( res )) {
3044 dropAccepted = false;
3047 dropAccepted = PyObject_IsTrue( res );
3051 return dropAccepted;
3054 void SALOME_PYQT_ModuleLight::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
3055 const int row, Qt::DropAction action )
3057 MESSAGE("SALOME_PYQT_ModuleLight::dropObjects()");
3058 // perform synchronous request to Python event dispatcher
3059 class DropObjectsEvent: public PyInterp_LockRequest
3062 DropObjectsEvent(PyInterp_Interp* _py_interp,
3063 SALOME_PYQT_ModuleLight* _obj,
3064 const DataObjectList& _what,
3065 SUIT_DataObject* _where,
3067 Qt::DropAction _action )
3068 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
3073 myAction ( _action ){}
3075 virtual void execute()
3077 myObj->dropObjectsEvent( myWhat, myWhere, myRow, myAction );
3081 SALOME_PYQT_ModuleLight* myObj;
3082 DataObjectList myWhat;
3083 SUIT_DataObject* myWhere;
3085 Qt::DropAction myAction;
3088 // Posting the request only if dispatcher is not busy!
3089 // Executing the request synchronously
3090 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
3091 PyInterp_Dispatcher::Get()->Exec( new DropObjectsEvent( myInterp, this, what, where, row, action ) );
3094 void SALOME_PYQT_ModuleLight::dropObjectsEvent( const DataObjectList& what, SUIT_DataObject* where,
3095 const int row, Qt::DropAction action )
3097 MESSAGE("SALOME_PYQT_ModuleLight::dropObjectsEvent()");
3098 // Python interpreter should be initialized and Python module should be
3100 if ( !myInterp || !myModule || what.isEmpty() || !where )
3103 QStringList* theList = new QStringList();
3105 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
3106 if ( !whereObject ) return;
3108 for ( int i = 0; i < what.count(); i++ ) {
3109 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
3110 if ( dataObject ) theList->append( dataObject->entry() );
3113 #if SIP_VERSION < 0x040800
3114 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
3116 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
3118 if ( PyObject_HasAttrString(myModule, (char*)"dropObjects") ) {
3119 PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"dropObjects", (char*)"Osii",
3121 whereObject->entry().toLatin1().constData(),