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 update selection from other views or modules.
1168 Called when selection is modified outside.
1170 void PyModuleHelper::selectionUpdated(const QStringList& entries)
1172 FuncMsg fmsg( "PyModuleHelper::selectionUpdated()" );
1173 MESSAGE("selectionUpdated");
1175 // perform synchronous request to Python event dispatcher
1176 class SelectionReq : public PyInterp_LockRequest
1179 SelectionReq( PyInterp_Interp* _py_interp,
1180 PyModuleHelper* _helper,
1181 const QStringList& _entries )
1182 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1183 myHelper( _helper ),
1184 myEntries( _entries )
1186 MESSAGE("SelectionReq");
1189 virtual void execute()
1192 myHelper->internalSelectionUpdated( myEntries );
1195 PyModuleHelper* myHelper;
1196 const QStringList& myEntries;
1200 PyInterp_Dispatcher::Get()->Exec( new SelectionReq( myInterp, this, entries ) );
1204 \brief Process context popup menu request.
1206 Called when user activates popup menu in some window
1207 (view, object browser, etc).
1209 \param context popup menu context (e.g. "ObjectBrowser")
1210 \param menu popup menu
1212 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1214 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1216 class ContextMenuReq : public PyInterp_LockRequest
1219 ContextMenuReq( PyInterp_Interp* _py_interp,
1220 PyModuleHelper* _helper,
1221 const QString& _context,
1223 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1224 myHelper ( _helper ),
1225 myContext( _context ),
1229 virtual void execute()
1231 myHelper->internalContextMenu( myContext, myMenu );
1234 PyModuleHelper* myHelper;
1239 // post request only if dispatcher is not busy!
1240 // execute request synchronously
1241 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1242 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1246 \brief Export preferences for the Python module.
1247 Called only once when the first instance of the module is created or
1248 when common Preferences dialog box is first time invoked.
1250 void PyModuleHelper::createPreferences()
1252 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1254 // temporary set myInitModule because createPreferences() method
1255 // might be called during the module intialization process
1256 InitLocker lock( myModule );
1258 class CreatePrefReq : public PyInterp_LockRequest
1261 CreatePrefReq( PyInterp_Interp* _py_interp,
1262 PyModuleHelper* _helper )
1263 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1267 virtual void execute()
1269 myHelper->internalCreatePreferences();
1272 PyModuleHelper* myHelper;
1275 // post request only if dispatcher is not busy!
1276 // execute request synchronously
1277 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1278 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1282 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1284 Used to notify Python module that active view has been changed by the user.
1286 \param view view being activated
1288 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1290 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1292 // perform synchronous request to Python event dispatcher
1293 class ActiveViewChangeReq : public PyInterp_LockRequest
1296 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1297 PyModuleHelper* _helper,
1298 SUIT_ViewWindow* _view )
1299 : PyInterp_LockRequest( _py_interp, 0, true ),
1300 myHelper( _helper ),
1304 virtual void execute()
1306 myHelper->internalActiveViewChanged( myView );
1309 PyModuleHelper* myHelper;
1310 SUIT_ViewWindow* myView;
1313 // connect view (if it is not connected yet)
1314 connectView( view );
1316 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1320 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1321 \param view view being closed
1323 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1325 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1327 class TryCloseViewReq : public PyInterp_LockRequest
1330 TryCloseViewReq( PyInterp_Interp* _py_interp,
1331 PyModuleHelper* _helper,
1332 SUIT_ViewWindow* _view )
1333 : PyInterp_LockRequest( _py_interp, 0, true ),
1334 myHelper( _helper ),
1338 virtual void execute()
1340 myHelper->internalTryCloseView( myView );
1343 PyModuleHelper* myHelper;
1344 SUIT_ViewWindow* myView;
1347 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1351 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1352 \param view view being closed
1354 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1356 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1358 class CloseViewReq : public PyInterp_LockRequest
1361 CloseViewReq( PyInterp_Interp* _py_interp,
1362 PyModuleHelper* _helper,
1363 SUIT_ViewWindow* _view )
1364 : PyInterp_LockRequest( _py_interp, 0, true ),
1365 myHelper( _helper ),
1369 virtual void execute()
1371 myHelper->internalCloseView( myView );
1374 PyModuleHelper* myHelper;
1375 SUIT_ViewWindow* myView;
1378 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1382 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1383 \param view view being cloned
1385 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1387 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1389 class CloneViewReq : public PyInterp_LockRequest
1392 CloneViewReq( PyInterp_Interp* _py_interp,
1393 PyModuleHelper* _helper,
1394 SUIT_ViewWindow* _view )
1395 : PyInterp_LockRequest( _py_interp, 0, true ),
1396 myHelper( _helper ),
1400 virtual void execute()
1402 myHelper->internalCloneView( myView );
1405 PyModuleHelper* myHelper;
1406 SUIT_ViewWindow* myView;
1409 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1413 \brief Save module data. Called when user saves study.
1414 \param files output list of files where module stores data
1415 \param url study URL
1417 void PyModuleHelper::save( QStringList& files, const QString& url )
1419 FuncMsg fmsg( "PyModuleHelper::save()" );
1421 // temporary set myInitModule because save() method
1422 // might be called by the framework when this module is inactive,
1423 // but still it should be possible to access this module's data
1425 InitLocker lock( myModule );
1427 // perform synchronous request to Python event dispatcher
1428 class SaveReq: public PyInterp_LockRequest
1431 SaveReq( PyInterp_Interp* _py_interp,
1432 PyModuleHelper* _helper,
1433 QStringList& _files,
1434 const QString& _url )
1435 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1436 myHelper( _helper ) ,
1441 virtual void execute()
1443 myHelper->internalSave( myFiles, myUrl );
1446 PyModuleHelper* myHelper;
1447 QStringList& myFiles;
1451 // Posting the request only if dispatcher is not busy!
1452 // Executing the request synchronously
1453 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1454 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files, url ) );
1458 \brief Load module data. Called when user opens study
1459 and activates module.
1460 \param files list of files where module data is stored
1461 \param url study URL
1462 \return \c true if loading has been finished successfully or \c false otherwise
1464 bool PyModuleHelper::load( const QStringList& files, const QString& url )
1466 FuncMsg fmsg( "PyModuleHelper::load()" );
1468 bool loaded = false;
1470 class LoadReq: public PyInterp_LockRequest
1473 LoadReq( PyInterp_Interp* _py_interp,
1474 PyModuleHelper* _helper,
1476 const QString& _url,
1478 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1479 myHelper( _helper ) ,
1485 virtual void execute()
1487 myHelper->internalLoad( myFiles, myUrl, myLoaded );
1490 PyModuleHelper* myHelper;
1491 QStringList myFiles;
1496 // Posting the request only if dispatcher is not busy!
1497 // Executing the request synchronously
1498 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1499 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, url, loaded ) );
1505 \brief Dump module data to the Python script.
1506 Called when user activates dump study operation.
1507 \param files output list of files where module stores python script
1509 void PyModuleHelper::dumpPython( QStringList& files )
1511 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1513 // temporary set myInitModule because dumpPython() method
1514 // might be called by the framework when this module is inactive,
1515 // but still it should be possible to access this module's data
1517 InitLocker lock( myModule );
1519 class DumpPythonReq: public PyInterp_LockRequest
1522 DumpPythonReq( PyInterp_Interp* _py_interp,
1523 PyModuleHelper* _helper,
1524 QStringList& _files )
1525 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1526 myHelper( _helper ) ,
1530 virtual void execute()
1532 myHelper->internalDumpPython( myFiles );
1535 PyModuleHelper* myHelper;
1536 QStringList& myFiles;
1539 // Posting the request only if dispatcher is not busy!
1540 // Executing the request synchronously
1541 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1542 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1546 \brief Test if object \a what can be dragged by the user.
1547 \param what data object being tested
1548 \return \c true if object can be dragged or \c false otherwise
1550 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1552 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1554 bool draggable = false;
1556 // perform synchronous request to Python event dispatcher
1557 class IsDraggableReq: public PyInterp_LockRequest
1560 IsDraggableReq( PyInterp_Interp* _py_interp,
1561 PyModuleHelper* _helper,
1562 LightApp_DataObject* _data_object,
1563 bool& _is_draggable )
1564 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1565 myHelper( _helper ) ,
1566 myDataObject( _data_object ),
1567 myIsDraggable( _is_draggable )
1570 virtual void execute()
1572 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1575 PyModuleHelper* myHelper;
1576 LightApp_DataObject* myDataObject;
1577 bool& myIsDraggable;
1580 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1581 if ( data_object ) {
1582 // Posting the request only if dispatcher is not busy!
1583 // Executing the request synchronously
1584 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1585 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1586 const_cast<PyModuleHelper*>( this ),
1587 const_cast<LightApp_DataObject*>( data_object ),
1595 \brief Test if drop operation can be done on the \a where object.
1596 \param where data object being tested
1597 \return \c true if if drop operation is supported by object or \c false otherwise
1599 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1601 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1603 bool dropAccepted = false;
1605 // perform synchronous request to Python event dispatcher
1606 class IsDropAcceptedReq: public PyInterp_LockRequest
1609 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1610 PyModuleHelper* _helper,
1611 LightApp_DataObject* _data_object,
1612 bool& _is_drop_accepted )
1613 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1614 myHelper( _helper ) ,
1615 myDataObject( _data_object ),
1616 myIsDropAccepted( _is_drop_accepted )
1619 virtual void execute()
1621 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1624 PyModuleHelper* myHelper;
1625 LightApp_DataObject* myDataObject;
1626 bool& myIsDropAccepted;
1629 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1630 if ( data_object ) {
1631 // Posting the request only if dispatcher is not busy!
1632 // Executing the request synchronously
1633 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1634 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1635 const_cast<PyModuleHelper*>( this ),
1636 const_cast<LightApp_DataObject*>( data_object ),
1640 return dropAccepted;
1644 \brief Perform drop operation
1645 \param what list of data objects being dropped
1646 \param where target data object for drop operation
1647 \param row line (child item index) where drop operation is performed to
1648 \param action current drop action (copy or move)
1650 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1651 const int row, Qt::DropAction action )
1653 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1655 // perform synchronous request to Python event dispatcher
1656 class DropObjectsReq: public PyInterp_LockRequest
1659 DropObjectsReq( PyInterp_Interp* _py_interp,
1660 PyModuleHelper* _helper,
1661 const DataObjectList& _what,
1662 SUIT_DataObject* _where,
1664 Qt::DropAction _action )
1665 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1666 myHelper( _helper ) ,
1670 myAction ( _action )
1673 virtual void execute()
1675 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1678 PyModuleHelper* myHelper;
1679 DataObjectList myWhat;
1680 SUIT_DataObject* myWhere;
1682 Qt::DropAction myAction;
1685 // Posting the request only if dispatcher is not busy!
1686 // Executing the request synchronously
1687 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1688 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1692 \brief Get module engine IOR
1693 \return engine IOR as it is supplied by GUI Python module
1695 QString PyModuleHelper::engineIOR() const
1697 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1699 class EngineIORReq : public PyInterp_LockRequest
1702 EngineIORReq( PyInterp_Interp* _py_interp,
1703 PyModuleHelper* _helper,
1705 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1706 myHelper( _helper ),
1710 virtual void execute()
1712 myIOR = myHelper->internalEngineIOR();
1715 PyModuleHelper* myHelper;
1719 static QString anIOR;
1721 if ( anIOR.isEmpty() ) {
1723 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1724 const_cast<PyModuleHelper*>( this ),
1732 \brief Initialize python subinterpreter (one per study).
1734 \param studyId study ID
1736 void PyModuleHelper::initInterp( int studyId )
1738 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1742 // Error! Study Id must not be 0!
1747 QMutexLocker ml( &myInitMutex );
1749 // try to find the subinterpreter
1750 if ( myInterpMap.contains( studyId ) ) {
1752 myInterp = myInterpMap[ studyId ];
1756 myInterp = new SALOME_PYQT_PyInterp();
1757 myInterp->initialize();
1758 myInterpMap[ studyId ] = myInterp;
1760 #ifndef GUI_DISABLE_CORBA
1761 if ( !SUIT_PYTHON::initialized ) {
1762 // import 'salome' module and call 'salome_init' method;
1763 // do it only once on interpreter creation
1764 // ... first get python lock
1765 PyLockWrapper aLock; // Acquire GIL
1766 // ... then import a module
1767 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1773 // ... then call a method
1775 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
1786 \brief Import Python GUI module and store reference to the module.
1789 Warning! initInterp() should be called first!!!
1791 void PyModuleHelper::importModule()
1793 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1795 // check if the subinterpreter is initialized
1797 // Error! Python subinterpreter should be initialized first!
1802 // import Python GUI module and put it in <myPyModule> attribute
1803 // ... first get python lock
1804 PyLockWrapper aLock; // Acquire GIL
1805 // ... then import a module
1806 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1808 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1813 if ( !myPyModule ) {
1821 \brief Set study workspace to the Python module.
1824 Calls setWorkSpace() method of the Python module with
1825 PyQt QWidget object to use with interpreter.
1827 Attention! initInterp() and importModule() should be called first!!!
1829 void PyModuleHelper::setWorkSpace()
1831 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1833 if ( !IsCallOldMethods )
1836 // check if the subinterpreter is initialized and Python module is imported
1837 if ( !myInterp || !myPyModule ) {
1838 // Error! Python subinterpreter should be initialized and module should be imported first!
1842 // call setWorkSpace() method
1843 // ... first get python lock
1844 PyLockWrapper aLock; // Acquire GIL
1846 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1847 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1854 // ... then get workspace object
1855 QWidget* aWorkspace = 0;
1856 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1857 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1859 aWorkspace = d->workspace();
1861 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1862 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1864 aWorkspace = d->workstack();
1866 #if SIP_VERSION < 0x040800
1867 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1869 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1871 // ... and finally call Python module's setWorkSpace() method (obsolete)
1872 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1873 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1881 \brief Initialization callback function
1884 Performs the following actions:
1885 - initialize or get the Python interpreter (one per study)
1886 - import the Python module
1887 - pass the workspace widget to the Python module
1888 - call Python module's initialize() method
1889 - call Python module's windows() method
1890 - call Python module's views() method
1892 \param app parent application object
1894 void PyModuleHelper::internalInitialize( CAM_Application* app )
1896 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1898 // reset interpreter to NULL
1902 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1905 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1908 int aStudyId = aStudy ? aStudy->id() : 0;
1910 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1911 initInterp( aStudyId );
1915 // import Python GUI module
1920 // then call Python module's initialize() method
1921 // ... first get python lock
1922 PyLockWrapper aLock; // Acquire GIL
1924 // ... (the Python module is already imported)
1925 // ... finally call Python module's initialize() method
1926 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1927 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1933 // get required dockable windows list from the Python module
1934 // by calling windows() method
1935 // ... first put default values
1936 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1937 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1938 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1940 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1941 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1946 myWindowsMap.clear();
1947 if ( PyDict_Check( res1 ) ) {
1951 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1952 // parse the return value
1953 // it should be a map: {integer:integer}
1955 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
1956 aKey = PyInt_AsLong( key );
1957 aValue = PyInt_AsLong( value );
1958 myWindowsMap[ aKey ] = aValue;
1965 // get compatible view windows types from the Python module
1966 // by calling views() method
1967 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1968 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1973 // parse the return value
1974 // result can be one string...
1975 if ( PyString_Check( res2 ) ) {
1976 myViewMgrList.append( PyString_AsString( res2 ) );
1978 // ... or list of strings
1979 else if ( PyList_Check( res2 ) ) {
1980 int size = PyList_Size( res2 );
1981 for ( int i = 0; i < size; i++ ) {
1982 PyObject* value = PyList_GetItem( res2, i );
1983 if( value && PyString_Check( value ) ) {
1984 myViewMgrList.append( PyString_AsString( value ) );
1993 \brief Activation callback function
1996 Performs the following actions:
1997 - initialize or get the Python interpreter (one per study)
1998 - import the Python GUI module
1999 - call Python module's activate() method
2001 \param study parent study
2003 void PyModuleHelper::internalActivate( SUIT_Study* study )
2005 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
2008 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2009 int aStudyId = aStudy ? aStudy->id() : 0;
2011 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2012 initInterp( aStudyId );
2014 myLastActivateStatus = false;
2018 // import Python GUI module
2020 if ( !myPyModule ) {
2021 myLastActivateStatus = false;
2026 PyLockWrapper aLock; // Acquire GIL
2028 // call Python module's activate() method (for the new modules)
2029 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
2030 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
2031 if ( !res1 || !PyBool_Check( res1 ) ) {
2033 // always true for old modules (no return value)
2034 myLastActivateStatus = true;
2037 // detect return status
2038 myLastActivateStatus = PyObject_IsTrue( res1 );
2044 \brief Additional menu customization callback function
2047 Performs the following actions:
2048 - get the Python interpreter (one per study)
2049 - import the Python GUI module
2050 - call Python module's setSettings() method (obsolete function,
2051 used for compatibility with old code)
2053 \param study parent study
2055 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2057 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2060 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2061 int aStudyId = aStudy ? aStudy->id() : 0;
2063 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2064 initInterp( aStudyId );
2066 myLastActivateStatus = false;
2070 // import Python GUI module
2072 if ( !myPyModule ) {
2073 myLastActivateStatus = false;
2077 // call Python module's setWorkSpace() method (obsolete)
2081 PyLockWrapper aLock; // Acquire GIL
2083 if ( IsCallOldMethods ) {
2084 // call Python module's setSettings() method (obsolete)
2085 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2086 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2090 myLastActivateStatus = myLastActivateStatus && true;
2096 \brief Deactivation callback function
2099 Performs the following actions:
2100 - call Python module's deactivate() method
2102 \param study parent study
2104 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2106 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2108 // check that Python subinterpreter is initialized and Python module is imported
2109 if ( !myInterp || !myPyModule ) {
2110 // Error! Python subinterpreter should be initialized and module should be imported first!
2113 // then call Python module's deactivate() method
2114 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2115 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2123 \brief Internal closure:
2125 Performs the following actions:
2126 - call Python module's closeStudy() method
2128 \param theStudy parent study object
2130 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2132 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2136 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2137 int aStudyId = aStudy ? aStudy->id() : 0;
2139 // check that Python subinterpreter is initialized and Python module is imported
2140 if ( !myInterp || !myPyModule ) {
2141 // Error! Python subinterpreter should be initialized and module should be imported first!
2144 // then call Python module's deactivate() method
2145 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2146 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i", aStudyId ) );
2156 \brief Preference changing callback function.
2159 Performs the following actions:
2160 - call Python module's preferenceChanged() method
2162 \param section resources section name
2163 \param setting resources parameter name
2165 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2167 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2169 // check that Python subinterpreter is initialized and Python module is imported
2170 if ( !myInterp || !myPyModule ) {
2171 // Error! Python subinterpreter should be initialized and module should be imported first!
2175 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2176 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2177 (char*)"preferenceChanged",
2179 section.toLatin1().constData(),
2180 setting.toLatin1().constData() ) );
2188 \brief Active study change callback function.
2191 Called when active the study is actived (user brings its
2193 - initialize or get the Python interpreter (one per study)
2194 - import the Python GUI module
2195 - call Python module's activeStudyChanged() method
2197 \param study study being activated
2199 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2201 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2204 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2205 int id = aStudy ? aStudy->id() : 0;
2207 fmsg.message( QString( "study id = %1" ).arg( id ) );
2209 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2214 // import Python GUI module
2219 // call Python module's setWorkSpace() method
2223 PyLockWrapper aLock; // Acquire GIL
2225 // call Python module's activeStudyChanged() method
2226 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2227 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
2235 \brief GUI event handling callback function
2238 Performs the following actions:
2239 - calls Python module's OnGUIEvent() method
2241 \param id GUI action ID
2243 void PyModuleHelper::internalActionActivated( int id )
2245 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2246 fmsg.message( QString( "action id = %1" ).arg( id ) );
2248 // Python interpreter should be initialized and Python module should be
2250 if ( !myInterp || !myPyModule )
2253 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2254 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2262 \brief update selection from other views or modules
2265 Performs the following actions:
2266 - calls Python module's onSelectionpdated(entries) method
2268 \param list of entries
2270 void PyModuleHelper::internalSelectionUpdated(const QStringList& entries)
2272 FuncMsg fmsg("--- PyModuleHelper::internalSelectionUpdated()");
2273 MESSAGE("internalSelectionUpdated");
2275 // Python interpreter should be initialized and Python module should be imported first
2276 if (!myInterp || !myPyModule)
2279 QStringList* theList = new QStringList(entries);
2281 #if SIP_VERSION < 0x040800
2282 PyObjWrapper sipList(sipBuildResult(0, "M", theList, sipClass_QStringList));
2284 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2286 if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
2288 MESSAGE("call onSelectionUpdated");
2289 PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
2299 \brief Context popup menu handling callback function
2302 Performs the following actions:
2303 - calls Python module's definePopup(...) method (obsolete function,
2304 used for compatibility with old code) to define the popup menu context
2305 - parses XML resourses file (if exists) and fills the popup menu with the items)
2306 - calls Python module's customPopup(...) method (obsolete function,
2307 used for compatibility with old code) to allow module to customize the popup menu
2308 - for new modules calls createPopupMenu() function to allow the
2309 modules to build the popup menu by using insertItem(...) Qt functions.
2311 \param context popup menu context
2312 \param menu popup menu
2314 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2316 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2317 fmsg.message( QString( "context: %1" ).arg( context ) );
2319 // Python interpreter should be initialized and Python module should be
2321 if ( !myInterp || !myPyModule )
2324 QString aContext( "" ), aObject( "" ), aParent( context );
2326 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2327 // call definePopup() Python module's function
2328 // this is obsolete function, used only for compatibility reasons
2329 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2330 (char*)"definePopup",
2332 context.toLatin1().constData(),
2333 aObject.toLatin1().constData(),
2334 aParent.toLatin1().constData() ) );
2339 // parse return value
2341 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2347 } // if ( IsCallOldMethods ... )
2349 // first try to create menu via XML parser:
2350 // we create popup menus without help of QtxPopupMgr
2352 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2354 #if SIP_VERSION < 0x040800
2355 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2357 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2360 // then call Python module's createPopupMenu() method (for new modules)
2361 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2362 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2363 (char*)"createPopupMenu",
2366 context.toLatin1().constData() ) );
2372 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2373 // call customPopup() Python module's function
2374 // this is obsolete function, used only for compatibility reasons
2375 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2376 (char*)"customPopup",
2379 aContext.toLatin1().constData(),
2380 aObject.toLatin1().constData(),
2381 aParent.toLatin1().constData() ) );
2389 \brief Preferences initialization callback function.
2392 Performs the following actions:
2393 - calls Python module's createPreferences() method
2395 void PyModuleHelper::internalCreatePreferences()
2397 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2399 // Python interpreter should be initialized and Python module should be
2401 if ( !myInterp || !myPyModule )
2404 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2405 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2413 \brief Active view changing callback function
2415 \param view view being activated
2417 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2419 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2421 if ( !myInterp || !myPyModule || !view )
2424 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2426 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2427 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2435 \brief View closing callback function
2437 \param view view user tries to close
2439 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2441 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2443 if ( !myInterp || !myPyModule || !view )
2446 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2448 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2450 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2459 \brief View closing callback function
2461 \param view view being closed
2463 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2465 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2467 if ( !myInterp || !myPyModule || !view )
2470 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2472 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2474 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2483 \brief View cloning callback function
2485 \param view view being cloned
2487 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2489 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2491 if ( !myInterp || !myPyModule || !view )
2494 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2496 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2498 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2505 \brief Module data saving callback function.
2507 \param files output list of files where module stores data
2508 \param url study URL
2510 void PyModuleHelper::internalSave( QStringList& files, const QString& url )
2512 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2514 // Python interpreter should be initialized and Python module should be
2516 // files list should contain a path to the temporary directory as a first entry
2517 if ( !myInterp || !myPyModule || files.isEmpty() )
2520 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2522 // try with two parameters (new syntax)
2523 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2525 files.first().toLatin1().constData(),
2526 url.toLatin1().constData() ) );
2528 // try with single parameter (old syntax)
2529 res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2530 (char*)"s", files.first().toLatin1().constData() );
2536 // parse the return value
2537 // result can be one string...
2538 if ( PyString_Check( res ) ) {
2539 QString astr = PyString_AsString( res );
2540 files.append( astr );
2542 //also result can be a list...
2543 else if ( PyList_Check( res ) ) {
2544 int size = PyList_Size( res );
2545 for ( int i = 0; i < size; i++ ) {
2546 PyObject* value = PyList_GetItem( res, i );
2547 if ( value && PyString_Check( value ) ) {
2548 files.append( PyString_AsString( value ) );
2557 \brief Module data loading callback function.
2559 \param files list of files where module data is stored
2560 \param url study URL
2561 \param opened output success flag
2563 void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
2565 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2567 // Python interpreter should be initialized and Python module should be
2569 if ( !myInterp || !myPyModule || files.isEmpty() )
2572 QStringList* theList = new QStringList( files );
2574 #if SIP_VERSION < 0x040800
2575 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2577 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2579 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2581 // try with two parameters (new syntax)
2582 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2583 (char*)"Os", sipList.get(),
2584 url.toLatin1().constData() ) );
2587 // try with single parameter (old syntax)
2588 res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
2589 (char*)"O", sipList.get() );
2591 if ( !res || !PyBool_Check( res ) ) {
2596 opened = PyObject_IsTrue( res );
2602 \brief Module dump python callback function.
2604 \param files output list of files where module stores python script
2606 void PyModuleHelper::internalDumpPython( QStringList& files )
2608 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2610 // Python interpreter should be initialized and Python module should be
2612 // files list should contain a path to the temporary directory
2613 if ( !myInterp || !myPyModule || files.isEmpty() )
2616 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2617 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2618 (char*)"s", files.first().toLatin1().constData()));
2624 // parse the return value
2625 // result can be one string...
2626 if ( PyString_Check( res ) ) {
2627 QString astr = PyString_AsString( res );
2631 //also result can be a list...
2632 else if ( PyList_Check( res ) ) {
2633 int size = PyList_Size( res );
2634 for ( int i = 0; i < size; i++ ) {
2635 PyObject* value = PyList_GetItem( res, i );
2636 if( value && PyString_Check( value ) ) {
2637 files.append( PyString_AsString( value ) );
2646 \brief Check data object's 'draggable' status callback function.
2648 \param what data object being tested
2649 \return \c true if object can be dragged or \c false otherwise
2651 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2653 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2655 // Python interpreter should be initialized and Python module should be
2657 if ( !myInterp || !myPyModule || !what )
2660 bool draggable = false;
2662 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2663 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2664 (char*)"s", what->entry().toLatin1().constData() ) );
2665 if( !res || !PyBool_Check( res )) {
2670 draggable = PyObject_IsTrue( res );
2678 \brief Check data object's 'drop allowed' status callback function.
2680 \param where data object being tested
2681 \return \c true if if drop operation is supported by object or \c false otherwise
2683 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2685 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2687 // Python interpreter should be initialized and Python module should be
2689 if ( !myInterp || !myPyModule || !where )
2692 bool dropAccepted = false;
2694 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2695 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2696 (char*)"s", where->entry().toLatin1().constData() ) );
2697 if( !res || !PyBool_Check( res )) {
2699 dropAccepted = false;
2702 dropAccepted = PyObject_IsTrue( res );
2706 return dropAccepted;
2710 \brief Data dropping callback function.
2712 \param what list of data objects being dropped
2713 \param where target data object for drop operation
2714 \param row line (child item index) where drop operation is performed to
2715 \param action current drop action (copy or move)
2717 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2718 const int row, Qt::DropAction action )
2720 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2722 // Python interpreter should be initialized and Python module should be
2724 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2727 QStringList* theList = new QStringList();
2729 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2730 if ( !whereObject ) return;
2732 for ( int i = 0; i < what.count(); i++ ) {
2733 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2734 if ( dataObject ) theList->append( dataObject->entry() );
2737 #if SIP_VERSION < 0x040800
2738 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2740 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2742 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2743 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2745 whereObject->entry().toLatin1().constData(),
2755 \brief Get engine IOR callback function
2758 Tries to get engine IOR from the Python module using engineIOR() function.
2759 That function can load module engine using appropriate container if required.
2761 \return engine IOR or empty string if it is not provided by Python module
2763 QString PyModuleHelper::internalEngineIOR() const
2765 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2769 // Python interpreter should be initialized and Python module should be
2771 if ( myInterp && myModule ) {
2772 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2773 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2778 // parse the return value, result chould be string
2779 if ( PyString_Check( res ) ) {
2780 ior = PyString_AsString( res );
2789 \brief Connects signals about activating and cloning view on internal slots
2790 \param view view being connected
2792 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2794 SUIT_ViewManager* viewMgr = view->getViewManager();
2795 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2797 // Connect tryCloseView() and deleteView() signals
2799 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2800 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2801 Qt::UniqueConnection );
2802 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2803 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2804 Qt::UniqueConnection );
2807 // Connect cloneView() signal of an OCC View
2808 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2809 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2810 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2811 Qt::UniqueConnection );
2813 // Connect cloneView() signal of Plot2d View
2814 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2815 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2816 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2817 Qt::UniqueConnection );
2823 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2825 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2827 // Python interpreter should be initialized and Python module should be
2829 if ( !myInterp || !myPyModule )
2832 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2833 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2842 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2844 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2846 // temporary set myInitModule because dumpPython() method
2847 // might be called by the framework when this module is inactive,
2848 // but still it should be possible to access this module's data
2850 InitLocker lock( myModule );
2852 class PythonReq: public PyInterp_LockRequest
2855 PythonReq( PyInterp_Interp* _py_interp,
2856 PyModuleHelper* _helper,
2857 const QString& _entry,
2859 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2860 myHelper( _helper ) ,
2865 virtual void execute()
2867 myHelper->internalOBClickedPython( myEntry, myColumn );
2870 PyModuleHelper* myHelper;
2875 // Posting the request only if dispatcher is not busy!
2876 // Executing the request synchronously
2877 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2878 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2879 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );