1 // Copyright (C) 2007-2016 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, or (at your option) any later version.
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_PyModule.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
24 #include "SALOME_PYQT_PyModule.h"
25 #include "SALOME_PYQT_PyInterp.h"
27 #include "LightApp_Application.h"
28 #include "LightApp_DataObject.h"
29 #include "LightApp_Module.h"
30 #include "LightApp_Study.h"
31 #include "PyInterp_Dispatcher.h"
32 #include "QtxActionMenuMgr.h"
33 #include "QtxWorkspace.h"
34 #include "QtxWorkstack.h"
35 #include "STD_MDIDesktop.h"
36 #include "STD_TabDesktop.h"
37 #include "SUITApp_init_python.hxx"
38 #include "SUIT_ResourceMgr.h"
39 #include "SUIT_ViewManager.h"
40 #include "SUIT_ViewModel.h"
41 #include "SUIT_ViewWindow.h"
43 #include <QApplication>
44 #include <QDomDocument>
45 #include <QDomElement>
51 #include <utilities.h>
53 #include "sipAPISalomePyQtGUILight.h"
56 \brief Default menu group number.
59 const int DEFAULT_GROUP = 40;
62 \brief Mutex used to lock access from several threads to the shared
69 const bool DEBUG = false;
73 \brief Allow calling obsolete callback methods.
76 If the macro CALL_OLD_METHODS is not defined, the invoking
77 of obsolete Python module's methods like setSetting(), definePopup(),
80 CALL_OLD_METHODS macro can be defined, for example, by adding
81 -DCALL_OLD_METHODS compilation option to the CMakeLists.txt.
83 #ifdef CALL_OLD_METHODS
84 const bool IsCallOldMethods = true;
86 const bool IsCallOldMethods = false;
90 \brief Get tag name for the DOM element.
92 \param element DOM element
93 \return tag name or empty string if the element does not have tag name
95 static QString tagName( const QDomElement& element )
97 return element.tagName().trimmed();
101 \brief Get value of DOM element's attribute.
103 \param element DOM element
104 \param attName attribute name
105 \return attribute value or empty string if the element does not have such attribute
107 static QString attribute( const QDomElement& element, const QString& attName )
109 return element.attribute( attName ).trimmed();
113 \brief Inspect specified string for the boolean value.
116 This function returns \c true if string represents boolean value:
117 - "true", "yes" or "1" for \c true
118 - "false", "no" or "0" for \c false
119 Second parameter allows to specify what boolean value is expected:
122 - other value is not taken into account (return represented value)
124 \param value inspected string
125 \param check expected boolean value
126 \return boolean value represented by the string (\a check is not 1 or 0)
127 or \c true if value correspond to the specified \a check
129 static bool checkBool( const QString& value, const int check = -1 )
131 QString v = value.toLower();
132 if ( ( v == "true" || v == "yes" || v == "1" ) && ( check != 0 ) )
134 if ( ( v == "false" || v == "no" || v == "0" ) && ( check == 0 ) )
140 \brief Inspect specified string for the integer value.
143 This function returns returns -1 if item is empty or represents
145 \param value inspected string
146 \param def default value
147 \param shift shift value (it is added to the integer value to produce shifted result)
149 static int checkInt( const QString& value, const int def = -1, const int shift = -1 )
152 int val = value.toInt( &bOk );
153 if ( !bOk ) val = def;
154 if ( shift > 0 && bOk && val < 0 )
161 \brief Function call in/out tracer.
168 FuncMsg( const QString& funcName )
172 MESSAGE( qPrintable( myName ) << " [ begin ]" );
177 MESSAGE( qPrintable( myName ) << " [ end ]" );
179 void message( const QString& msg )
182 MESSAGE( qPrintable( myName ) << " : " << qPrintable( msg ) );
189 \class PyModuleHelper::InitLocker
190 \brief Initialization locker
194 class PyModuleHelper::InitLocker
197 InitLocker( LightApp_Module* );
205 PyModuleHelper::InitLocker::InitLocker( LightApp_Module* module )
207 QMutexLocker ml( &myInitMutex );
208 myInitModule = module;
215 PyModuleHelper::InitLocker::~InitLocker()
217 QMutexLocker ml( &myInitMutex );
222 \class PyModuleHelper::XmlHandler
223 \brief XML resource files parser.
226 This class is used to provide backward compatibility with
227 existing Python modules in which obsolete menu definition system
228 (via XML files) is used.
231 class PyModuleHelper::XmlHandler
234 XmlHandler( PyModuleHelper* helper, const QString& fileName );
235 void createActions();
236 void createPopup( QMenu* menu,
237 const QString& context,
238 const QString& parent,
239 const QString& object );
240 void activateMenus( bool );
243 LightApp_Module* module() const;
244 QIcon loadIcon( const QString& fileName );
246 void createMenu( QDomNode& parentNode,
247 const int parentMenuId = -1,
248 QMenu* parentPopup = 0 );
249 void createToolBar( QDomNode& parentNode );
250 void insertPopupItems( QDomNode& parentNode,
254 PyModuleHelper* myHelper;
256 QList<int> myMenuItems;
263 \param module pointer to the GUI module
264 \param fileName path to the XML menu description file
266 PyModuleHelper::XmlHandler::XmlHandler( PyModuleHelper* helper,
267 const QString& fileName )
270 if ( !fileName.isEmpty() ) {
271 QFile aFile( fileName );
272 if ( aFile.open( QIODevice::ReadOnly ) ) {
273 myDoc.setContent( &aFile );
279 \brief Parse XML file and create actions.
282 Called by PyModuleHelper::initialize() in order to create actions
285 void PyModuleHelper::XmlHandler::createActions()
287 // get document element
288 QDomElement aDocElem = myDoc.documentElement();
290 // create main menu actions
291 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
292 for ( int i = 0; i < aMenuList.count(); i++ ) {
293 QDomNode n = aMenuList.item( i );
297 // create toolbars actions
298 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
299 for ( int i = 0; i < aToolsList.count(); i++ ) {
300 QDomNode n = aToolsList.item( i );
306 \brief Create popup menu.
308 \param menu popup menu
309 \param context popup menu context
310 \param context popup menu parent object name
311 \param context popup menu object name
313 void PyModuleHelper::XmlHandler::createPopup( QMenu* menu,
314 const QString& context,
315 const QString& parent,
316 const QString& object )
318 // get document element
319 QDomElement aDocElem = myDoc.documentElement();
321 // get popup menus actions
322 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
323 for ( int i = 0; i < aPopupList.count(); i++ ) {
324 QDomNode n = aPopupList.item( i );
325 if ( !n.isNull() && n.isElement() ) {
326 QDomElement e = n.toElement();
327 // QString lab = attribute( e, "label-id" ); // not used //
328 QString ctx = attribute( e, "context-id" );
329 QString prt = attribute( e, "parent-id" );
330 QString obj = attribute( e, "object-id" );
331 if ( ctx == context && prt == parent && obj == object ) {
332 insertPopupItems( n, menu );
340 \brief Activate/deactivate menus
342 \param enable if \c true menus are activated, otherwise menus are deactivated
344 void PyModuleHelper::XmlHandler::activateMenus( bool enable )
347 QtxActionMenuMgr* mgr = module()->menuMgr();
348 foreach( int id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
353 \brief Get owner module
355 LightApp_Module* PyModuleHelper::XmlHandler::module() const
357 return myHelper->module();
361 \brief Load an icon from the module resources by the specified file name.
362 \param fileName icon file name
366 QIcon PyModuleHelper::XmlHandler::loadIcon( const QString& fileName )
370 if ( module() && !fileName.isEmpty() ) {
371 SUIT_ResourceMgr* resMgr = module()->getApp()->resourceMgr();
372 QPixmap pixmap = resMgr->loadPixmap( module()->name(),
373 QApplication::translate( module()->name().toLatin1().data(),
374 fileName.toLatin1().data() ) );
375 if ( !pixmap.isNull() )
376 icon = QIcon( pixmap );
383 \brief Create main menu item and insert actions to it.
385 \param parentNode XML node with menu description
386 \param parentMenuId parent menu ID (-1 for top-level menu)
387 \param parentPopup parent popup menu (0 for top-level menu)
389 void PyModuleHelper::XmlHandler::createMenu( QDomNode& parentNode,
390 const int parentMenuId,
393 if ( !module() || parentNode.isNull() )
396 QDomElement parentElement = parentNode.toElement();
397 if ( !parentElement.isNull() ) {
398 QString plabel = attribute( parentElement, "label-id" );
399 int pid = checkInt( attribute( parentElement, "item-id" ) );
400 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
401 int group = checkInt( attribute( parentElement, "group-id" ),
402 PyModuleHelper::defaultMenuGroup() );
403 if ( !plabel.isEmpty() ) {
407 menuId = module()->createMenu( plabel, // label
408 parentMenuId, // parent menu ID, -1 for top-level menu
412 myMenuItems.append( menuId );
413 QDomNode node = parentNode.firstChild();
414 while ( !node.isNull() ) {
415 if ( node.isElement() ) {
416 QDomElement elem = node.toElement();
417 QString aTagName = tagName( elem );
418 if ( aTagName == "popup-item" ) {
419 int id = checkInt( attribute( elem, "item-id" ) );
420 int pos = checkInt( attribute( elem, "pos-id" ) );
421 int group = checkInt( attribute( elem, "group-id" ),
422 PyModuleHelper::defaultMenuGroup() );
423 QString label = attribute( elem, "label-id" );
424 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
425 QString tooltip = attribute( elem, "tooltip-id" );
426 QString accel = attribute( elem, "accel-id" );
427 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
429 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
430 // also check if the action with given ID is already created
432 // create menu action
433 QAction* action = module()->createAction( id, // ID
437 tooltip, // status-bar text
438 QKeySequence( accel ), // keyboard accelerator
439 module(), // action owner
440 toggle ); // toogled action
441 myHelper->connectAction( action );
442 module()->createMenu( action, // action
443 menuId, // parent menu ID
444 id, // ID (same as for createAction())
449 else if ( aTagName == "submenu" ) {
451 createMenu( node, menuId, popup );
453 else if ( aTagName == "separator" ) {
454 // create menu separator
455 int id = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
456 int pos = checkInt( attribute( elem, "pos-id" ) );
457 int group = checkInt( attribute( elem, "group-id" ),
458 PyModuleHelper::defaultMenuGroup() );
459 QAction* action = module()->separator();
460 module()->createMenu( action, // separator action
461 menuId, // parent menu ID
467 node = node.nextSibling();
474 \brief Create a toolbar and insert actions to it.
475 \param parentNode XML node with toolbar description
477 void PyModuleHelper::XmlHandler::createToolBar( QDomNode& parentNode )
479 if ( !module() || parentNode.isNull() )
482 QDomElement parentElement = parentNode.toElement();
483 if ( !parentElement.isNull() ) {
484 QString aLabel = attribute( parentElement, "label-id" );
485 QString aName = attribute( parentElement, "name-id" );
486 if ( !aLabel.isEmpty() ) {
488 int tbId = module()->createTool( aLabel, aName );
489 QDomNode node = parentNode.firstChild();
490 while ( !node.isNull() ) {
491 if ( node.isElement() ) {
492 QDomElement elem = node.toElement();
493 QString aTagName = tagName( elem );
494 if ( aTagName == "toolbutton-item" ) {
495 int id = checkInt( attribute( elem, "item-id" ) );
496 int pos = checkInt( attribute( elem, "pos-id" ) );
497 QString label = attribute( elem, "label-id" );
498 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
499 QString tooltip = attribute( elem, "tooltip-id" );
500 QString accel = attribute( elem, "accel-id" );
501 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
503 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
504 // also check if the action with given ID is already created
506 // create toolbar action
507 QAction* action = module()->createAction( id, // ID
511 tooltip, // status-bar text
512 QKeySequence( accel ), // keyboard accelerator
513 module(), // action owner
514 toggle ); // toogled action
515 myHelper->connectAction( action );
516 module()->createTool( action, tbId, -1, pos );
519 else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
520 // create toolbar separator
521 int pos = checkInt( attribute( elem, "pos-id" ) );
522 QAction* action = module()->separator();
523 module()->createTool( action, tbId, -1, pos );
526 node = node.nextSibling();
533 \brief Fill popup menu with the items.
534 \param parentNode XML node with popup menu description
535 \param menu popup menu
537 void PyModuleHelper::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
539 if ( !module() && parentNode.isNull() )
542 // we create popup menus without help of QtxPopupMgr
543 QDomNode node = parentNode.firstChild();
544 while ( !node.isNull() ) {
545 if ( node.isElement() ) {
546 QDomElement elem = node.toElement();
547 QString aTagName = tagName( elem );
548 QList<QAction*> actions = menu->actions();
549 if ( aTagName == "popup-item" ) {
550 // insert a command item
551 int id = checkInt( attribute( elem, "item-id" ) );
552 int pos = checkInt( attribute( elem, "pos-id" ) );
553 QString label = attribute( elem, "label-id" );
554 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
555 QString tooltip = attribute( elem, "tooltip-id" );
556 QString accel = attribute( elem, "accel-id" );
557 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
559 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
560 // also check if the action with given ID is already created
562 QAction* action = module()->createAction( id, // ID
566 tooltip, // status-bar text
567 QKeySequence( accel ), // keyboard accelerator
568 module(), // action owner
569 toggle ); // toogled action
570 myHelper->connectAction( action );
571 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
572 menu->insertAction( before, action );
575 else if ( aTagName == "submenu" ) {
577 ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
578 int pos = checkInt( attribute( elem, "pos-id" ) );
579 QString label = attribute( elem, "label-id" );
580 QString icon = attribute( elem, "icon-id" );
583 if ( !icon.isEmpty() ) {
584 QPixmap pixmap = module()->getApp()->resourceMgr()->loadPixmap( module()->name(), icon );
585 if ( !pixmap.isNull() )
586 anIcon = QIcon( pixmap );
589 QMenu* newPopup = menu->addMenu( anIcon, label );
590 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
591 menu->insertMenu( before, newPopup );
592 insertPopupItems( node, newPopup );
594 else if ( aTagName == "separator" ) {
595 // create menu separator
596 int pos = checkInt( attribute( elem, "pos-id" ) );
597 QAction* action = module()->separator();
598 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
599 menu->insertAction( before, action );
602 node = node.nextSibling();
607 \class PyModuleHelper
608 \brief This class implements API helper for all the Python-based
612 PyModuleHelper::InterpMap PyModuleHelper::myInterpMap;
613 LightApp_Module* PyModuleHelper::myInitModule = 0;
617 \param module owner module
619 PyModuleHelper::PyModuleHelper( LightApp_Module* module ) :
625 myLastActivateStatus( true )
627 setObjectName( "python_module_helper" );
633 PyModuleHelper::~PyModuleHelper()
636 if ( myInterp && myPyModule ) {
637 PyLockWrapper aLock; // Acquire GIL
638 Py_XDECREF( myPyModule );
643 \brief Get the module being initialized.
645 This is a little trick :) needed to provide an access from Python
646 (SalomePyQt) to the module being currently activated. The problem
647 that during the process of module initialization (initialize()
648 function) it is not yet available via application->activeModule()
651 This method returns valid pointer only if called in scope of
652 initialize() function or in several other specific cases.
654 \return the module being currently initialized
656 LightApp_Module* PyModuleHelper::getInitModule()
658 QMutexLocker ml( &myInitMutex );
663 \brief Get default menu group identifier
664 \return menu group ID (40 by default)
666 int PyModuleHelper::defaultMenuGroup()
668 return DEFAULT_GROUP;
672 \brief Get owner module
675 LightApp_Module* PyModuleHelper::module() const
681 \brief Get Python GUI module object
682 \return python module
684 PyObject* PyModuleHelper::pythonModule() const
690 \brief Connect action to the internal actionActivated() slot.
692 Actions connected to internal actionActivated(), when activated, will
693 be forwarded to the Python GUI module OnGUIEvent() function.
695 \param a action being connected
697 void PyModuleHelper::connectAction( QAction* a )
700 QObject::connect( a, SIGNAL( triggered( bool ) ),
701 this, SLOT( actionActivated() ),
702 Qt::UniqueConnection );
706 \brief Get the dockable windows associated with the module.
708 To fill the list of windows the correspondind Python module's windows()
709 method is called during the module initialization.
711 By default, ObjectBrowser, PythonConsole and LogWindow windows are
712 associated to the module.
714 Allowed dockable windows:
715 - LightApp_Application::WT_ObjectBrowser : object browser
716 - LightApp_Application::WT_PyConsole : python console
717 - LightApp_Application::WT_LogWindow : log messages output window
719 Dock area is defined by Qt::DockWidgetArea enumeration:
720 - Qt::TopDockWidgetArea : top dock area
721 - Qt::BottomDockWidgetArea : bottom dock area
722 - Qt::LeftDockWidgetArea : left dock area
723 - Qt::RightDockWidgetArea : right dock area
725 \return map of dockable windows in form { <window_type> : <dock_area> }
727 QMap<int, int> PyModuleHelper::windows() const
729 FuncMsg fmsg( "PyModuleHelper::windows()" );
735 \brief Define the compatible view windows associated with the module.
737 The associated view windows are opened automatically when the module
740 To fill the list of views the correspondind Python module's views()
741 method is called during the module initialization.
742 By default, the list of view types is empty.
744 \return list of view windows types
746 QStringList PyModuleHelper::viewManagers() const
748 FuncMsg fmsg( "PyModuleHelper::viewManagers()" );
750 return myViewMgrList;
754 \brief Initialization of the Python-based SALOME module.
756 This method can be used for creation of the menus, toolbars and
759 There are two ways to do this:
760 1) for obsolete modules, the implementation of this method first tries to read
761 the <module>_<language>.xml resource file which contains a menu,
762 toolbars and popup menus description;
763 2) new modules can create menus by direct calling of the
764 corresponding methods of SalomePyQt Python API in the Python
765 module's initialize() method which is called from here.
767 \note SALOME supports two modes of modules loading:
768 - immediate (all the modules are created and initialized
769 immediately when the application object is created);
770 - postponed modules loading (used currently); in this mode
771 the module is loaded only by explicit request.
772 If postponed modules loading is not used, the active
773 study might be not yet defined at this stage, so initialize()
774 method should not perform any study-based initialization.
775 Such actions can be better done in activate() function.
777 \param app parent application object
779 void PyModuleHelper::initialize( CAM_Application* app )
781 FuncMsg fmsg( "PyModuleHelper::initialize()" );
783 // temporarily store module being currently activated
784 // in the global variable to make it accessible from
786 InitLocker lock( myModule );
788 // try to get XML resource file name
789 SUIT_ResourceMgr* resMgr = myModule->getApp()->resourceMgr();
790 if ( !myXmlHandler && resMgr ) {
791 // get current language
792 QString lang = resMgr->stringValue( "language", "language", "en" );
793 // get menu description file name
794 QString aFileName = QString( "%1_%2.xml" ).arg( myModule->name() ).arg( lang );
795 aFileName = resMgr->path( "resources", myModule->name(), aFileName );
796 if ( !aFileName.isEmpty() && QFile::exists( aFileName ) ) {
797 // create XML handler instance
798 myXmlHandler = new XmlHandler( this, aFileName );
799 // ask XML handler to create actions
800 myXmlHandler->createActions();
804 class InitializeReq : public PyInterp_Request
807 InitializeReq( PyModuleHelper* _helper,
808 CAM_Application* _app )
809 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
814 virtual void execute()
816 myHelper->internalInitialize( myApp );
819 PyModuleHelper* myHelper;
820 CAM_Application* myApp;
824 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( this, app ) );
828 \brief Activation of the module.
830 This function is usually used in order to show the module's
831 specific menus and toolbars, update actions state and perform
832 other such actions required when the module is activated.
834 \note Returning \c false from this function prevents the
837 \param study parent study
838 \return \c true if activation is successful and \c false otherwise
840 bool PyModuleHelper::activate( SUIT_Study* study )
842 FuncMsg fmsg( "PyModuleHelper::activate()" );
844 // reset the activation status to the default value
845 myLastActivateStatus = true;
847 class ActivateReq : public PyInterp_Request
850 ActivateReq( PyModuleHelper* _helper,
853 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
856 myCustomize( _customize )
859 virtual void execute()
862 myHelper->internalActivate( myStudy ); // first activation stage
864 myHelper->internalCustomize( myStudy ); // second activation stage
867 PyModuleHelper* myHelper;
872 // post request for activation (customize=false)
873 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, false ) );
875 // check activation status (can be set to false by internalActivate())
876 if ( myLastActivateStatus ) {
877 // activate menus, toolbars, etc
878 if ( myXmlHandler ) myXmlHandler->activateMenus( true );
880 // show menus / toolbars
881 myModule->setMenuShown( true );
882 myModule->setToolShown( true );
884 // post request for customization (customize=true)
885 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, true ) );
887 // check activation status (can be set to false by internalCustomize())
888 if ( myLastActivateStatus ) {
889 // connect preferences changing signal
890 connect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
891 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
893 // connect active view change signal
894 SUIT_Desktop* d = study->application()->desktop();
895 connect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
896 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
897 // if active window exists, call activeViewChanged() function;
898 // temporary solution: if a getActiveView() in SalomePyQt available
899 // we no longer need this
900 SUIT_ViewWindow* view = d->activeWindow();
901 if ( view ) activeViewChanged( view );
902 // get all view currently opened in the study and connect their signals to
903 // the corresponding slots of the class.
904 foreach ( view, d->windows() ) connectView( view );
907 // hide menus / toolbars in case of error
908 myModule->setMenuShown( false );
909 myModule->setToolShown( false );
913 return myLastActivateStatus;
917 \brief Deactivation of the module.
919 This function is usually used in order to hide the module's
920 specific menus and toolbars and perform other such actions
921 required when the module is deactivated.
923 \param study parent study
924 \return \c true if deactivation is successful and \c false otherwise
926 bool PyModuleHelper::deactivate( SUIT_Study* study )
928 FuncMsg fmsg( "PyModuleHelper::deactivate()" );
930 class DeactivateReq : public PyInterp_LockRequest
933 DeactivateReq( PyInterp_Interp* _py_interp,
934 PyModuleHelper* _helper,
936 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
941 virtual void execute()
943 myHelper->internalDeactivate( myStudy );
946 PyModuleHelper* myHelper;
951 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, this, study ) );
953 // disconnect preferences changing signal
954 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
955 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
957 // disconnect the SUIT_Desktop signal windowActivated()
958 SUIT_Desktop* d = study->application()->desktop();
959 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
960 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
962 // deactivate menus, toolbars, etc
963 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
965 // hide menus / toolbars
966 myModule->setMenuShown( false );
967 myModule->setToolShown( false );
973 \brief Close of the module.
975 This function is usually used in order to close the module's
976 specific menus and toolbars and perform other such actions
977 required when the module is closed.
979 void PyModuleHelper::modelClosed( SUIT_Study* study )
981 FuncMsg fmsg( "PyModuleHelper::modelClosed()" );
983 class StudyClosedReq : public PyInterp_LockRequest
986 StudyClosedReq( PyInterp_Interp* _py_interp,
987 PyModuleHelper* _helper,
989 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
994 virtual void execute()
996 myHelper->internalClosedStudy( myStudy );
999 PyModuleHelper* myHelper;
1000 SUIT_Study* myStudy;
1004 PyInterp_Dispatcher::Get()->Exec( new StudyClosedReq( myInterp, this, study ) );
1006 // disconnect preferences changing signal
1007 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
1008 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
1010 // disconnect the SUIT_Desktop signal windowActivated()
1011 SUIT_Desktop* d = study->application()->desktop();
1012 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1013 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
1015 // deactivate menus, toolbars, etc
1016 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
1018 // hide menus / toolbars
1019 myModule->setMenuShown( false );
1020 myModule->setToolShown( false );
1025 \brief Process module's preferences changing.
1027 Called when the module's own preferences are changed.
1029 \param section preference resources section
1030 \param parameter preference resources parameter name
1032 void PyModuleHelper::preferencesChanged( const QString& section,
1033 const QString& parameter )
1035 FuncMsg fmsg( "PyModuleHelper::preferencesChanged()" );
1037 class PrefChangeReq : public PyInterp_LockRequest
1040 PrefChangeReq( PyInterp_Interp* _py_interp,
1041 PyModuleHelper* _helper,
1042 const QString& _section,
1043 const QString& _parameter )
1044 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1045 myHelper ( _helper ),
1046 mySection( _section ),
1047 myParameter( _parameter )
1050 virtual void execute()
1052 myHelper->internalPreferencesChanged( mySection, myParameter );
1055 PyModuleHelper* myHelper;
1056 QString mySection, myParameter;
1059 // post the request only if dispatcher is not busy!
1060 // execute request synchronously
1061 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1062 PyInterp_Dispatcher::Get()->Exec( new PrefChangeReq( myInterp, this, section, parameter ) );
1066 \brief Process application preferences changing.
1068 Called when any application setting is changed.
1070 \param module preference module
1071 \param section preference resources section
1072 \param parameter preference resources parameter name
1074 void PyModuleHelper::preferenceChanged( const QString& module,
1075 const QString& section,
1076 const QString& parameter )
1078 FuncMsg fmsg( "PyModuleHelper::preferenceChanged()" );
1080 // module's own preferences are processed by other preferencesChanged() method
1081 if ( module != myModule->moduleName() ) {
1083 preferencesChanged( section, parameter );
1088 \brief Process study activation.
1090 Called when study desktop is activated. Used for notifying the Python
1091 module about changing of the active study.
1093 \param study study being activated
1095 void PyModuleHelper::studyActivated( SUIT_Study* study )
1097 FuncMsg fmsg( "PyModuleHelper::studyActivated()" );
1099 // StudyChangedReq: request class for internal studyChanged() operation
1100 class StudyChangedReq : public PyInterp_Request
1103 StudyChangedReq( PyModuleHelper* _helper,
1104 SUIT_Study* _study )
1105 : PyInterp_Request(0, true ), // this request should be processed synchronously (sync == true)
1106 myHelper( _helper ),
1110 virtual void execute()
1112 myHelper->internalStudyChanged( myStudy );
1115 PyModuleHelper* myHelper;
1116 SUIT_Study* myStudy;
1120 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( this, study ) );
1124 \brief Process action activation.
1126 Called when action is activated. Used for notifying the Python
1127 module about any related action activation.
1131 void PyModuleHelper::actionActivated()
1133 FuncMsg fmsg( "PyModuleHelper::actionActivated()" );
1135 // perform synchronous request to Python event dispatcher
1136 class ActionReq : public PyInterp_LockRequest
1139 ActionReq( PyInterp_Interp* _py_interp,
1140 PyModuleHelper* _helper,
1142 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1143 myHelper( _helper ),
1147 virtual void execute()
1149 myHelper->internalActionActivated( myId );
1152 PyModuleHelper* myHelper;
1156 // get sender action
1157 QAction* action = qobject_cast<QAction*>( sender() );
1162 PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
1166 \brief Process context popup menu request.
1168 Called when user activates popup menu in some window
1169 (view, object browser, etc).
1171 \param context popup menu context (e.g. "ObjectBrowser")
1172 \param menu popup menu
1174 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1176 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1178 class ContextMenuReq : public PyInterp_LockRequest
1181 ContextMenuReq( PyInterp_Interp* _py_interp,
1182 PyModuleHelper* _helper,
1183 const QString& _context,
1185 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1186 myHelper ( _helper ),
1187 myContext( _context ),
1191 virtual void execute()
1193 myHelper->internalContextMenu( myContext, myMenu );
1196 PyModuleHelper* myHelper;
1201 // post request only if dispatcher is not busy!
1202 // execute request synchronously
1203 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1204 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1208 \brief Export preferences for the Python module.
1209 Called only once when the first instance of the module is created or
1210 when common Preferences dialog box is first time invoked.
1212 void PyModuleHelper::createPreferences()
1214 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1216 // temporary set myInitModule because createPreferences() method
1217 // might be called during the module intialization process
1218 InitLocker lock( myModule );
1220 class CreatePrefReq : public PyInterp_LockRequest
1223 CreatePrefReq( PyInterp_Interp* _py_interp,
1224 PyModuleHelper* _helper )
1225 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1229 virtual void execute()
1231 myHelper->internalCreatePreferences();
1234 PyModuleHelper* myHelper;
1237 // post request only if dispatcher is not busy!
1238 // execute request synchronously
1239 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1240 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1244 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1246 Used to notify Python module that active view has been changed by the user.
1248 \param view view being activated
1250 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1252 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1254 // perform synchronous request to Python event dispatcher
1255 class ActiveViewChangeReq : public PyInterp_LockRequest
1258 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1259 PyModuleHelper* _helper,
1260 SUIT_ViewWindow* _view )
1261 : PyInterp_LockRequest( _py_interp, 0, true ),
1262 myHelper( _helper ),
1266 virtual void execute()
1268 myHelper->internalActiveViewChanged( myView );
1271 PyModuleHelper* myHelper;
1272 SUIT_ViewWindow* myView;
1275 // connect view (if it is not connected yet)
1276 connectView( view );
1278 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1282 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1283 \param view view being closed
1285 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1287 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1289 class TryCloseViewReq : public PyInterp_LockRequest
1292 TryCloseViewReq( PyInterp_Interp* _py_interp,
1293 PyModuleHelper* _helper,
1294 SUIT_ViewWindow* _view )
1295 : PyInterp_LockRequest( _py_interp, 0, true ),
1296 myHelper( _helper ),
1300 virtual void execute()
1302 myHelper->internalTryCloseView( myView );
1305 PyModuleHelper* myHelper;
1306 SUIT_ViewWindow* myView;
1309 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1313 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1314 \param view view being closed
1316 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1318 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1320 class CloseViewReq : public PyInterp_LockRequest
1323 CloseViewReq( PyInterp_Interp* _py_interp,
1324 PyModuleHelper* _helper,
1325 SUIT_ViewWindow* _view )
1326 : PyInterp_LockRequest( _py_interp, 0, true ),
1327 myHelper( _helper ),
1331 virtual void execute()
1333 myHelper->internalCloseView( myView );
1336 PyModuleHelper* myHelper;
1337 SUIT_ViewWindow* myView;
1340 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1344 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1345 \param view view being cloned
1347 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1349 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1351 class CloneViewReq : public PyInterp_LockRequest
1354 CloneViewReq( PyInterp_Interp* _py_interp,
1355 PyModuleHelper* _helper,
1356 SUIT_ViewWindow* _view )
1357 : PyInterp_LockRequest( _py_interp, 0, true ),
1358 myHelper( _helper ),
1362 virtual void execute()
1364 myHelper->internalCloneView( myView );
1367 PyModuleHelper* myHelper;
1368 SUIT_ViewWindow* myView;
1371 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1375 \brief Save module data. Called when user saves study.
1376 \param files output list of files where module stores data
1377 \param url study URL
1379 void PyModuleHelper::save( QStringList& files, const QString& url )
1381 FuncMsg fmsg( "PyModuleHelper::save()" );
1383 // temporary set myInitModule because save() method
1384 // might be called by the framework when this module is inactive,
1385 // but still it should be possible to access this module's data
1387 InitLocker lock( myModule );
1389 // perform synchronous request to Python event dispatcher
1390 class SaveReq: public PyInterp_LockRequest
1393 SaveReq( PyInterp_Interp* _py_interp,
1394 PyModuleHelper* _helper,
1395 QStringList& _files,
1396 const QString& _url )
1397 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1398 myHelper( _helper ) ,
1403 virtual void execute()
1405 myHelper->internalSave( myFiles, myUrl );
1408 PyModuleHelper* myHelper;
1409 QStringList& myFiles;
1413 // Posting the request only if dispatcher is not busy!
1414 // Executing the request synchronously
1415 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1416 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files, url ) );
1420 \brief Load module data. Called when user opens study
1421 and activates module.
1422 \param files list of files where module data is stored
1423 \param url study URL
1424 \return \c true if loading has been finished successfully or \c false otherwise
1426 bool PyModuleHelper::load( const QStringList& files, const QString& url )
1428 FuncMsg fmsg( "PyModuleHelper::load()" );
1430 bool loaded = false;
1432 class LoadReq: public PyInterp_LockRequest
1435 LoadReq( PyInterp_Interp* _py_interp,
1436 PyModuleHelper* _helper,
1438 const QString& _url,
1440 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1441 myHelper( _helper ) ,
1447 virtual void execute()
1449 myHelper->internalLoad( myFiles, myUrl, myLoaded );
1452 PyModuleHelper* myHelper;
1453 QStringList myFiles;
1458 // Posting the request only if dispatcher is not busy!
1459 // Executing the request synchronously
1460 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1461 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, url, loaded ) );
1467 \brief Dump module data to the Python script.
1468 Called when user activates dump study operation.
1469 \param files output list of files where module stores python script
1471 void PyModuleHelper::dumpPython( QStringList& files )
1473 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1475 // temporary set myInitModule because dumpPython() method
1476 // might be called by the framework when this module is inactive,
1477 // but still it should be possible to access this module's data
1479 InitLocker lock( myModule );
1481 class DumpPythonReq: public PyInterp_LockRequest
1484 DumpPythonReq( PyInterp_Interp* _py_interp,
1485 PyModuleHelper* _helper,
1486 QStringList& _files )
1487 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1488 myHelper( _helper ) ,
1492 virtual void execute()
1494 myHelper->internalDumpPython( myFiles );
1497 PyModuleHelper* myHelper;
1498 QStringList& myFiles;
1501 // Posting the request only if dispatcher is not busy!
1502 // Executing the request synchronously
1503 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1504 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1508 \brief Test if object \a what can be dragged by the user.
1509 \param what data object being tested
1510 \return \c true if object can be dragged or \c false otherwise
1512 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1514 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1516 bool draggable = false;
1518 // perform synchronous request to Python event dispatcher
1519 class IsDraggableReq: public PyInterp_LockRequest
1522 IsDraggableReq( PyInterp_Interp* _py_interp,
1523 PyModuleHelper* _helper,
1524 LightApp_DataObject* _data_object,
1525 bool& _is_draggable )
1526 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1527 myHelper( _helper ) ,
1528 myDataObject( _data_object ),
1529 myIsDraggable( _is_draggable )
1532 virtual void execute()
1534 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1537 PyModuleHelper* myHelper;
1538 LightApp_DataObject* myDataObject;
1539 bool& myIsDraggable;
1542 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1543 if ( data_object ) {
1544 // Posting the request only if dispatcher is not busy!
1545 // Executing the request synchronously
1546 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1547 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1548 const_cast<PyModuleHelper*>( this ),
1549 const_cast<LightApp_DataObject*>( data_object ),
1557 \brief Test if drop operation can be done on the \a where object.
1558 \param where data object being tested
1559 \return \c true if if drop operation is supported by object or \c false otherwise
1561 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1563 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1565 bool dropAccepted = false;
1567 // perform synchronous request to Python event dispatcher
1568 class IsDropAcceptedReq: public PyInterp_LockRequest
1571 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1572 PyModuleHelper* _helper,
1573 LightApp_DataObject* _data_object,
1574 bool& _is_drop_accepted )
1575 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1576 myHelper( _helper ) ,
1577 myDataObject( _data_object ),
1578 myIsDropAccepted( _is_drop_accepted )
1581 virtual void execute()
1583 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1586 PyModuleHelper* myHelper;
1587 LightApp_DataObject* myDataObject;
1588 bool& myIsDropAccepted;
1591 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1592 if ( data_object ) {
1593 // Posting the request only if dispatcher is not busy!
1594 // Executing the request synchronously
1595 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1596 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1597 const_cast<PyModuleHelper*>( this ),
1598 const_cast<LightApp_DataObject*>( data_object ),
1602 return dropAccepted;
1606 \brief Perform drop operation
1607 \param what list of data objects being dropped
1608 \param where target data object for drop operation
1609 \param row line (child item index) where drop operation is performed to
1610 \param action current drop action (copy or move)
1612 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1613 const int row, Qt::DropAction action )
1615 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1617 // perform synchronous request to Python event dispatcher
1618 class DropObjectsReq: public PyInterp_LockRequest
1621 DropObjectsReq( PyInterp_Interp* _py_interp,
1622 PyModuleHelper* _helper,
1623 const DataObjectList& _what,
1624 SUIT_DataObject* _where,
1626 Qt::DropAction _action )
1627 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1628 myHelper( _helper ) ,
1632 myAction ( _action )
1635 virtual void execute()
1637 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1640 PyModuleHelper* myHelper;
1641 DataObjectList myWhat;
1642 SUIT_DataObject* myWhere;
1644 Qt::DropAction myAction;
1647 // Posting the request only if dispatcher is not busy!
1648 // Executing the request synchronously
1649 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1650 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1654 \brief Get module engine IOR
1655 \return engine IOR as it is supplied by GUI Python module
1657 QString PyModuleHelper::engineIOR() const
1659 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1661 class EngineIORReq : public PyInterp_LockRequest
1664 EngineIORReq( PyInterp_Interp* _py_interp,
1665 PyModuleHelper* _helper,
1667 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1668 myHelper( _helper ),
1672 virtual void execute()
1674 myIOR = myHelper->internalEngineIOR();
1677 PyModuleHelper* myHelper;
1681 static QString anIOR;
1683 if ( anIOR.isEmpty() ) {
1685 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1686 const_cast<PyModuleHelper*>( this ),
1694 \brief Initialize python subinterpreter (one per study).
1696 \param studyId study ID
1698 void PyModuleHelper::initInterp( int studyId )
1700 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1704 // Error! Study Id must not be 0!
1709 QMutexLocker ml( &myInitMutex );
1711 // try to find the subinterpreter
1712 if ( myInterpMap.contains( studyId ) ) {
1714 myInterp = myInterpMap[ studyId ];
1718 myInterp = new SALOME_PYQT_PyInterp();
1719 myInterp->initialize();
1720 myInterpMap[ studyId ] = myInterp;
1722 #ifndef GUI_DISABLE_CORBA
1723 if ( !SUIT_PYTHON::initialized ) {
1724 // import 'salome' module and call 'salome_init' method;
1725 // do it only once on interpreter creation
1726 // ... first get python lock
1727 PyLockWrapper aLock; // Acquire GIL
1728 // ... then import a module
1729 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1735 // ... then call a method
1737 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
1748 \brief Import Python GUI module and store reference to the module.
1751 Warning! initInterp() should be called first!!!
1753 void PyModuleHelper::importModule()
1755 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1757 // check if the subinterpreter is initialized
1759 // Error! Python subinterpreter should be initialized first!
1764 // import Python GUI module and put it in <myPyModule> attribute
1765 // ... first get python lock
1766 PyLockWrapper aLock; // Acquire GIL
1767 // ... then import a module
1768 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1770 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1775 if ( !myPyModule ) {
1783 \brief Set study workspace to the Python module.
1786 Calls setWorkSpace() method of the Python module with
1787 PyQt QWidget object to use with interpreter.
1789 Attention! initInterp() and importModule() should be called first!!!
1791 void PyModuleHelper::setWorkSpace()
1793 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1795 if ( !IsCallOldMethods )
1798 // check if the subinterpreter is initialized and Python module is imported
1799 if ( !myInterp || !myPyModule ) {
1800 // Error! Python subinterpreter should be initialized and module should be imported first!
1804 // call setWorkSpace() method
1805 // ... first get python lock
1806 PyLockWrapper aLock; // Acquire GIL
1808 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1809 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1816 // ... then get workspace object
1817 QWidget* aWorkspace = 0;
1818 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1819 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1821 aWorkspace = d->workspace();
1823 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1824 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1826 aWorkspace = d->workstack();
1828 #if SIP_VERSION < 0x040800
1829 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1831 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1833 // ... and finally call Python module's setWorkSpace() method (obsolete)
1834 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1835 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1843 \brief Initialization callback function
1846 Performs the following actions:
1847 - initialize or get the Python interpreter (one per study)
1848 - import the Python module
1849 - pass the workspace widget to the Python module
1850 - call Python module's initialize() method
1851 - call Python module's windows() method
1852 - call Python module's views() method
1854 \param app parent application object
1856 void PyModuleHelper::internalInitialize( CAM_Application* app )
1858 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1860 // reset interpreter to NULL
1864 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1867 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1870 int aStudyId = aStudy ? aStudy->id() : 0;
1872 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1873 initInterp( aStudyId );
1877 // import Python GUI module
1882 // then call Python module's initialize() method
1883 // ... first get python lock
1884 PyLockWrapper aLock; // Acquire GIL
1886 // ... (the Python module is already imported)
1887 // ... finally call Python module's initialize() method
1888 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1889 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1895 // get required dockable windows list from the Python module
1896 // by calling windows() method
1897 // ... first put default values
1898 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1899 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1900 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1902 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1903 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1908 myWindowsMap.clear();
1909 if ( PyDict_Check( res1 ) ) {
1913 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1914 // parse the return value
1915 // it should be a map: {integer:integer}
1917 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
1918 aKey = PyInt_AsLong( key );
1919 aValue = PyInt_AsLong( value );
1920 myWindowsMap[ aKey ] = aValue;
1927 // get compatible view windows types from the Python module
1928 // by calling views() method
1929 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1930 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1935 // parse the return value
1936 // result can be one string...
1937 if ( PyString_Check( res2 ) ) {
1938 myViewMgrList.append( PyString_AsString( res2 ) );
1940 // ... or list of strings
1941 else if ( PyList_Check( res2 ) ) {
1942 int size = PyList_Size( res2 );
1943 for ( int i = 0; i < size; i++ ) {
1944 PyObject* value = PyList_GetItem( res2, i );
1945 if( value && PyString_Check( value ) ) {
1946 myViewMgrList.append( PyString_AsString( value ) );
1955 \brief Activation callback function
1958 Performs the following actions:
1959 - initialize or get the Python interpreter (one per study)
1960 - import the Python GUI module
1961 - call Python module's activate() method
1963 \param study parent study
1965 void PyModuleHelper::internalActivate( SUIT_Study* study )
1967 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1970 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1971 int aStudyId = aStudy ? aStudy->id() : 0;
1973 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1974 initInterp( aStudyId );
1976 myLastActivateStatus = false;
1980 // import Python GUI module
1982 if ( !myPyModule ) {
1983 myLastActivateStatus = false;
1988 PyLockWrapper aLock; // Acquire GIL
1990 // call Python module's activate() method (for the new modules)
1991 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
1992 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
1993 if ( !res1 || !PyBool_Check( res1 ) ) {
1995 // always true for old modules (no return value)
1996 myLastActivateStatus = true;
1999 // detect return status
2000 myLastActivateStatus = PyObject_IsTrue( res1 );
2006 \brief Additional menu customization callback function
2009 Performs the following actions:
2010 - get the Python interpreter (one per study)
2011 - import the Python GUI module
2012 - call Python module's setSettings() method (obsolete function,
2013 used for compatibility with old code)
2015 \param study parent study
2017 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2019 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2022 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2023 int aStudyId = aStudy ? aStudy->id() : 0;
2025 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2026 initInterp( aStudyId );
2028 myLastActivateStatus = false;
2032 // import Python GUI module
2034 if ( !myPyModule ) {
2035 myLastActivateStatus = false;
2039 // call Python module's setWorkSpace() method (obsolete)
2043 PyLockWrapper aLock; // Acquire GIL
2045 if ( IsCallOldMethods ) {
2046 // call Python module's setSettings() method (obsolete)
2047 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2048 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2052 myLastActivateStatus = myLastActivateStatus && true;
2058 \brief Deactivation callback function
2061 Performs the following actions:
2062 - call Python module's deactivate() method
2064 \param study parent study
2066 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2068 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2070 // check that Python subinterpreter is initialized and Python module is imported
2071 if ( !myInterp || !myPyModule ) {
2072 // Error! Python subinterpreter should be initialized and module should be imported first!
2075 // then call Python module's deactivate() method
2076 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2077 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2085 \brief Internal closure:
2087 Performs the following actions:
2088 - call Python module's closeStudy() method
2090 \param theStudy parent study object
2092 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2094 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2098 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2099 int aStudyId = aStudy ? aStudy->id() : 0;
2101 // check that Python subinterpreter is initialized and Python module is imported
2102 if ( !myInterp || !myPyModule ) {
2103 // Error! Python subinterpreter should be initialized and module should be imported first!
2106 // then call Python module's deactivate() method
2107 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2108 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i", aStudyId ) );
2118 \brief Preference changing callback function.
2121 Performs the following actions:
2122 - call Python module's preferenceChanged() method
2124 \param section resources section name
2125 \param setting resources parameter name
2127 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2129 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2131 // check that Python subinterpreter is initialized and Python module is imported
2132 if ( !myInterp || !myPyModule ) {
2133 // Error! Python subinterpreter should be initialized and module should be imported first!
2137 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2138 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2139 (char*)"preferenceChanged",
2141 section.toLatin1().constData(),
2142 setting.toLatin1().constData() ) );
2150 \brief Active study change callback function.
2153 Called when active the study is actived (user brings its
2155 - initialize or get the Python interpreter (one per study)
2156 - import the Python GUI module
2157 - call Python module's activeStudyChanged() method
2159 \param study study being activated
2161 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2163 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2166 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2167 int id = aStudy ? aStudy->id() : 0;
2169 fmsg.message( QString( "study id = %1" ).arg( id ) );
2171 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2176 // import Python GUI module
2181 // call Python module's setWorkSpace() method
2185 PyLockWrapper aLock; // Acquire GIL
2187 // call Python module's activeStudyChanged() method
2188 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2189 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
2197 \brief GUI event handling callback function
2200 Performs the following actions:
2201 - calls Python module's OnGUIEvent() method
2203 \param id GUI action ID
2205 void PyModuleHelper::internalActionActivated( int id )
2207 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2208 fmsg.message( QString( "action id = %1" ).arg( id ) );
2210 // Python interpreter should be initialized and Python module should be
2212 if ( !myInterp || !myPyModule )
2215 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2216 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2224 \brief Context popup menu handling callback function
2227 Performs the following actions:
2228 - calls Python module's definePopup(...) method (obsolete function,
2229 used for compatibility with old code) to define the popup menu context
2230 - parses XML resourses file (if exists) and fills the popup menu with the items)
2231 - calls Python module's customPopup(...) method (obsolete function,
2232 used for compatibility with old code) to allow module to customize the popup menu
2233 - for new modules calls createPopupMenu() function to allow the
2234 modules to build the popup menu by using insertItem(...) Qt functions.
2236 \param context popup menu context
2237 \param menu popup menu
2239 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2241 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2242 fmsg.message( QString( "context: %1" ).arg( context ) );
2244 // Python interpreter should be initialized and Python module should be
2246 if ( !myInterp || !myPyModule )
2249 QString aContext( "" ), aObject( "" ), aParent( context );
2251 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2252 // call definePopup() Python module's function
2253 // this is obsolete function, used only for compatibility reasons
2254 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2255 (char*)"definePopup",
2257 context.toLatin1().constData(),
2258 aObject.toLatin1().constData(),
2259 aParent.toLatin1().constData() ) );
2264 // parse return value
2266 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2272 } // if ( IsCallOldMethods ... )
2274 // first try to create menu via XML parser:
2275 // we create popup menus without help of QtxPopupMgr
2277 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2279 #if SIP_VERSION < 0x040800
2280 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2282 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2285 // then call Python module's createPopupMenu() method (for new modules)
2286 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2287 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2288 (char*)"createPopupMenu",
2291 context.toLatin1().constData() ) );
2297 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2298 // call customPopup() Python module's function
2299 // this is obsolete function, used only for compatibility reasons
2300 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2301 (char*)"customPopup",
2304 aContext.toLatin1().constData(),
2305 aObject.toLatin1().constData(),
2306 aParent.toLatin1().constData() ) );
2314 \brief Preferences initialization callback function.
2317 Performs the following actions:
2318 - calls Python module's createPreferences() method
2320 void PyModuleHelper::internalCreatePreferences()
2322 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2324 // Python interpreter should be initialized and Python module should be
2326 if ( !myInterp || !myPyModule )
2329 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2330 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2338 \brief Active view changing callback function
2340 \param view view being activated
2342 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2344 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2346 if ( !myInterp || !myPyModule || !view )
2349 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2351 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2352 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2360 \brief View closing callback function
2362 \param view view user tries to close
2364 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2366 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2368 if ( !myInterp || !myPyModule || !view )
2371 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2373 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2375 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2384 \brief View closing callback function
2386 \param view view being closed
2388 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2390 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2392 if ( !myInterp || !myPyModule || !view )
2395 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2397 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2399 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2408 \brief View cloning callback function
2410 \param view view being cloned
2412 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2414 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2416 if ( !myInterp || !myPyModule || !view )
2419 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2421 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2423 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2430 \brief Module data saving callback function.
2432 \param files output list of files where module stores data
2433 \param url study URL
2435 void PyModuleHelper::internalSave( QStringList& files, const QString& url )
2437 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2439 // Python interpreter should be initialized and Python module should be
2441 // files list should contain a path to the temporary directory as a first entry
2442 if ( !myInterp || !myPyModule || files.isEmpty() )
2445 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2447 // try with two parameters (new syntax)
2448 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2450 files.first().toLatin1().constData(),
2451 url.toLatin1().constData() ) );
2453 // try with single parameter (old syntax)
2454 res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2455 (char*)"s", files.first().toLatin1().constData() );
2461 // parse the return value
2462 // result can be one string...
2463 if ( PyString_Check( res ) ) {
2464 QString astr = PyString_AsString( res );
2465 files.append( astr );
2467 //also result can be a list...
2468 else if ( PyList_Check( res ) ) {
2469 int size = PyList_Size( res );
2470 for ( int i = 0; i < size; i++ ) {
2471 PyObject* value = PyList_GetItem( res, i );
2472 if ( value && PyString_Check( value ) ) {
2473 files.append( PyString_AsString( value ) );
2482 \brief Module data loading callback function.
2484 \param files list of files where module data is stored
2485 \param url study URL
2486 \param opened output success flag
2488 void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
2490 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2492 // Python interpreter should be initialized and Python module should be
2494 if ( !myInterp || !myPyModule || files.isEmpty() )
2497 QStringList* theList = new QStringList( files );
2499 #if SIP_VERSION < 0x040800
2500 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2502 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2504 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2506 // try with two parameters (new syntax)
2507 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2508 (char*)"Os", sipList.get(),
2509 url.toLatin1().constData() ) );
2512 // try with single parameter (old syntax)
2513 res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
2514 (char*)"O", sipList.get() );
2516 if ( !res || !PyBool_Check( res ) ) {
2521 opened = PyObject_IsTrue( res );
2527 \brief Module dump python callback function.
2529 \param files output list of files where module stores python script
2531 void PyModuleHelper::internalDumpPython( QStringList& files )
2533 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2535 // Python interpreter should be initialized and Python module should be
2537 // files list should contain a path to the temporary directory
2538 if ( !myInterp || !myPyModule || files.isEmpty() )
2541 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2542 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2543 (char*)"s", files.first().toLatin1().constData()));
2549 // parse the return value
2550 // result can be one string...
2551 if ( PyString_Check( res ) ) {
2552 QString astr = PyString_AsString( res );
2556 //also result can be a list...
2557 else if ( PyList_Check( res ) ) {
2558 int size = PyList_Size( res );
2559 for ( int i = 0; i < size; i++ ) {
2560 PyObject* value = PyList_GetItem( res, i );
2561 if( value && PyString_Check( value ) ) {
2562 files.append( PyString_AsString( value ) );
2571 \brief Check data object's 'draggable' status callback function.
2573 \param what data object being tested
2574 \return \c true if object can be dragged or \c false otherwise
2576 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2578 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2580 // Python interpreter should be initialized and Python module should be
2582 if ( !myInterp || !myPyModule || !what )
2585 bool draggable = false;
2587 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2588 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2589 (char*)"s", what->entry().toLatin1().constData() ) );
2590 if( !res || !PyBool_Check( res )) {
2595 draggable = PyObject_IsTrue( res );
2603 \brief Check data object's 'drop allowed' status callback function.
2605 \param where data object being tested
2606 \return \c true if if drop operation is supported by object or \c false otherwise
2608 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2610 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2612 // Python interpreter should be initialized and Python module should be
2614 if ( !myInterp || !myPyModule || !where )
2617 bool dropAccepted = false;
2619 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2620 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2621 (char*)"s", where->entry().toLatin1().constData() ) );
2622 if( !res || !PyBool_Check( res )) {
2624 dropAccepted = false;
2627 dropAccepted = PyObject_IsTrue( res );
2631 return dropAccepted;
2635 \brief Data dropping callback function.
2637 \param what list of data objects being dropped
2638 \param where target data object for drop operation
2639 \param row line (child item index) where drop operation is performed to
2640 \param action current drop action (copy or move)
2642 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2643 const int row, Qt::DropAction action )
2645 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2647 // Python interpreter should be initialized and Python module should be
2649 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2652 QStringList* theList = new QStringList();
2654 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2655 if ( !whereObject ) return;
2657 for ( int i = 0; i < what.count(); i++ ) {
2658 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2659 if ( dataObject ) theList->append( dataObject->entry() );
2662 #if SIP_VERSION < 0x040800
2663 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2665 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2667 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2668 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2670 whereObject->entry().toLatin1().constData(),
2680 \brief Get engine IOR callback function
2683 Tries to get engine IOR from the Python module using engineIOR() function.
2684 That function can load module engine using appropriate container if required.
2686 \return engine IOR or empty string if it is not provided by Python module
2688 QString PyModuleHelper::internalEngineIOR() const
2690 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2694 // Python interpreter should be initialized and Python module should be
2696 if ( myInterp && myModule ) {
2697 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2698 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2703 // parse the return value, result chould be string
2704 if ( PyString_Check( res ) ) {
2705 ior = PyString_AsString( res );
2714 \brief Connects signals about activating and cloning view on internal slots
2715 \param view view being connected
2717 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2719 SUIT_ViewManager* viewMgr = view->getViewManager();
2720 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2722 // Connect tryCloseView() and deleteView() signals
2724 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2725 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2726 Qt::UniqueConnection );
2727 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2728 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2729 Qt::UniqueConnection );
2732 // Connect cloneView() signal of an OCC View
2733 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2734 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2735 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2736 Qt::UniqueConnection );
2738 // Connect cloneView() signal of Plot2d View
2739 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2740 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2741 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2742 Qt::UniqueConnection );
2748 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2750 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2752 // Python interpreter should be initialized and Python module should be
2754 if ( !myInterp || !myPyModule )
2757 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2758 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2767 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2769 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2771 // temporary set myInitModule because dumpPython() method
2772 // might be called by the framework when this module is inactive,
2773 // but still it should be possible to access this module's data
2775 InitLocker lock( myModule );
2777 class PythonReq: public PyInterp_LockRequest
2780 PythonReq( PyInterp_Interp* _py_interp,
2781 PyModuleHelper* _helper,
2782 const QString& _entry,
2784 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2785 myHelper( _helper ) ,
2790 virtual void execute()
2792 myHelper->internalOBClickedPython( myEntry, myColumn );
2795 PyModuleHelper* myHelper;
2800 // Posting the request only if dispatcher is not busy!
2801 // Executing the request synchronously
2802 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2803 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2804 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );