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()" );
1174 // perform synchronous request to Python event dispatcher
1175 class SelectionReq : public PyInterp_LockRequest
1178 SelectionReq( PyInterp_Interp* _py_interp,
1179 PyModuleHelper* _helper,
1180 const QStringList& _entries )
1181 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1182 myHelper( _helper ),
1183 myEntries( _entries )
1186 virtual void execute()
1188 myHelper->internalSelectionUpdated( myEntries );
1191 PyModuleHelper* myHelper;
1192 const QStringList& myEntries;
1195 // get sender action
1196 QAction* action = qobject_cast<QAction*>( sender() );
1201 PyInterp_Dispatcher::Get()->Exec( new SelectionReq( myInterp, this, entries ) );
1205 \brief Process context popup menu request.
1207 Called when user activates popup menu in some window
1208 (view, object browser, etc).
1210 \param context popup menu context (e.g. "ObjectBrowser")
1211 \param menu popup menu
1213 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1215 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1217 class ContextMenuReq : public PyInterp_LockRequest
1220 ContextMenuReq( PyInterp_Interp* _py_interp,
1221 PyModuleHelper* _helper,
1222 const QString& _context,
1224 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1225 myHelper ( _helper ),
1226 myContext( _context ),
1230 virtual void execute()
1232 myHelper->internalContextMenu( myContext, myMenu );
1235 PyModuleHelper* myHelper;
1240 // post request only if dispatcher is not busy!
1241 // execute request synchronously
1242 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1243 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1247 \brief Export preferences for the Python module.
1248 Called only once when the first instance of the module is created or
1249 when common Preferences dialog box is first time invoked.
1251 void PyModuleHelper::createPreferences()
1253 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1255 // temporary set myInitModule because createPreferences() method
1256 // might be called during the module intialization process
1257 InitLocker lock( myModule );
1259 class CreatePrefReq : public PyInterp_LockRequest
1262 CreatePrefReq( PyInterp_Interp* _py_interp,
1263 PyModuleHelper* _helper )
1264 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1268 virtual void execute()
1270 myHelper->internalCreatePreferences();
1273 PyModuleHelper* myHelper;
1276 // post request only if dispatcher is not busy!
1277 // execute request synchronously
1278 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1279 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1283 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1285 Used to notify Python module that active view has been changed by the user.
1287 \param view view being activated
1289 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1291 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1293 // perform synchronous request to Python event dispatcher
1294 class ActiveViewChangeReq : public PyInterp_LockRequest
1297 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1298 PyModuleHelper* _helper,
1299 SUIT_ViewWindow* _view )
1300 : PyInterp_LockRequest( _py_interp, 0, true ),
1301 myHelper( _helper ),
1305 virtual void execute()
1307 myHelper->internalActiveViewChanged( myView );
1310 PyModuleHelper* myHelper;
1311 SUIT_ViewWindow* myView;
1314 // connect view (if it is not connected yet)
1315 connectView( view );
1317 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1321 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1322 \param view view being closed
1324 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1326 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1328 class TryCloseViewReq : public PyInterp_LockRequest
1331 TryCloseViewReq( PyInterp_Interp* _py_interp,
1332 PyModuleHelper* _helper,
1333 SUIT_ViewWindow* _view )
1334 : PyInterp_LockRequest( _py_interp, 0, true ),
1335 myHelper( _helper ),
1339 virtual void execute()
1341 myHelper->internalTryCloseView( myView );
1344 PyModuleHelper* myHelper;
1345 SUIT_ViewWindow* myView;
1348 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1352 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1353 \param view view being closed
1355 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1357 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1359 class CloseViewReq : public PyInterp_LockRequest
1362 CloseViewReq( PyInterp_Interp* _py_interp,
1363 PyModuleHelper* _helper,
1364 SUIT_ViewWindow* _view )
1365 : PyInterp_LockRequest( _py_interp, 0, true ),
1366 myHelper( _helper ),
1370 virtual void execute()
1372 myHelper->internalCloseView( myView );
1375 PyModuleHelper* myHelper;
1376 SUIT_ViewWindow* myView;
1379 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1383 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1384 \param view view being cloned
1386 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1388 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1390 class CloneViewReq : public PyInterp_LockRequest
1393 CloneViewReq( PyInterp_Interp* _py_interp,
1394 PyModuleHelper* _helper,
1395 SUIT_ViewWindow* _view )
1396 : PyInterp_LockRequest( _py_interp, 0, true ),
1397 myHelper( _helper ),
1401 virtual void execute()
1403 myHelper->internalCloneView( myView );
1406 PyModuleHelper* myHelper;
1407 SUIT_ViewWindow* myView;
1410 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1414 \brief Save module data. Called when user saves study.
1415 \param files output list of files where module stores data
1416 \param url study URL
1418 void PyModuleHelper::save( QStringList& files, const QString& url )
1420 FuncMsg fmsg( "PyModuleHelper::save()" );
1422 // temporary set myInitModule because save() method
1423 // might be called by the framework when this module is inactive,
1424 // but still it should be possible to access this module's data
1426 InitLocker lock( myModule );
1428 // perform synchronous request to Python event dispatcher
1429 class SaveReq: public PyInterp_LockRequest
1432 SaveReq( PyInterp_Interp* _py_interp,
1433 PyModuleHelper* _helper,
1434 QStringList& _files,
1435 const QString& _url )
1436 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1437 myHelper( _helper ) ,
1442 virtual void execute()
1444 myHelper->internalSave( myFiles, myUrl );
1447 PyModuleHelper* myHelper;
1448 QStringList& myFiles;
1452 // Posting the request only if dispatcher is not busy!
1453 // Executing the request synchronously
1454 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1455 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files, url ) );
1459 \brief Load module data. Called when user opens study
1460 and activates module.
1461 \param files list of files where module data is stored
1462 \param url study URL
1463 \return \c true if loading has been finished successfully or \c false otherwise
1465 bool PyModuleHelper::load( const QStringList& files, const QString& url )
1467 FuncMsg fmsg( "PyModuleHelper::load()" );
1469 bool loaded = false;
1471 class LoadReq: public PyInterp_LockRequest
1474 LoadReq( PyInterp_Interp* _py_interp,
1475 PyModuleHelper* _helper,
1477 const QString& _url,
1479 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1480 myHelper( _helper ) ,
1486 virtual void execute()
1488 myHelper->internalLoad( myFiles, myUrl, myLoaded );
1491 PyModuleHelper* myHelper;
1492 QStringList myFiles;
1497 // Posting the request only if dispatcher is not busy!
1498 // Executing the request synchronously
1499 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1500 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, url, loaded ) );
1506 \brief Dump module data to the Python script.
1507 Called when user activates dump study operation.
1508 \param files output list of files where module stores python script
1510 void PyModuleHelper::dumpPython( QStringList& files )
1512 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1514 // temporary set myInitModule because dumpPython() method
1515 // might be called by the framework when this module is inactive,
1516 // but still it should be possible to access this module's data
1518 InitLocker lock( myModule );
1520 class DumpPythonReq: public PyInterp_LockRequest
1523 DumpPythonReq( PyInterp_Interp* _py_interp,
1524 PyModuleHelper* _helper,
1525 QStringList& _files )
1526 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1527 myHelper( _helper ) ,
1531 virtual void execute()
1533 myHelper->internalDumpPython( myFiles );
1536 PyModuleHelper* myHelper;
1537 QStringList& myFiles;
1540 // Posting the request only if dispatcher is not busy!
1541 // Executing the request synchronously
1542 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1543 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1547 \brief Test if object \a what can be dragged by the user.
1548 \param what data object being tested
1549 \return \c true if object can be dragged or \c false otherwise
1551 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1553 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1555 bool draggable = false;
1557 // perform synchronous request to Python event dispatcher
1558 class IsDraggableReq: public PyInterp_LockRequest
1561 IsDraggableReq( PyInterp_Interp* _py_interp,
1562 PyModuleHelper* _helper,
1563 LightApp_DataObject* _data_object,
1564 bool& _is_draggable )
1565 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1566 myHelper( _helper ) ,
1567 myDataObject( _data_object ),
1568 myIsDraggable( _is_draggable )
1571 virtual void execute()
1573 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1576 PyModuleHelper* myHelper;
1577 LightApp_DataObject* myDataObject;
1578 bool& myIsDraggable;
1581 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1582 if ( data_object ) {
1583 // Posting the request only if dispatcher is not busy!
1584 // Executing the request synchronously
1585 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1586 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1587 const_cast<PyModuleHelper*>( this ),
1588 const_cast<LightApp_DataObject*>( data_object ),
1596 \brief Test if drop operation can be done on the \a where object.
1597 \param where data object being tested
1598 \return \c true if if drop operation is supported by object or \c false otherwise
1600 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1602 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1604 bool dropAccepted = false;
1606 // perform synchronous request to Python event dispatcher
1607 class IsDropAcceptedReq: public PyInterp_LockRequest
1610 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1611 PyModuleHelper* _helper,
1612 LightApp_DataObject* _data_object,
1613 bool& _is_drop_accepted )
1614 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1615 myHelper( _helper ) ,
1616 myDataObject( _data_object ),
1617 myIsDropAccepted( _is_drop_accepted )
1620 virtual void execute()
1622 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1625 PyModuleHelper* myHelper;
1626 LightApp_DataObject* myDataObject;
1627 bool& myIsDropAccepted;
1630 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1631 if ( data_object ) {
1632 // Posting the request only if dispatcher is not busy!
1633 // Executing the request synchronously
1634 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1635 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1636 const_cast<PyModuleHelper*>( this ),
1637 const_cast<LightApp_DataObject*>( data_object ),
1641 return dropAccepted;
1645 \brief Perform drop operation
1646 \param what list of data objects being dropped
1647 \param where target data object for drop operation
1648 \param row line (child item index) where drop operation is performed to
1649 \param action current drop action (copy or move)
1651 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1652 const int row, Qt::DropAction action )
1654 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1656 // perform synchronous request to Python event dispatcher
1657 class DropObjectsReq: public PyInterp_LockRequest
1660 DropObjectsReq( PyInterp_Interp* _py_interp,
1661 PyModuleHelper* _helper,
1662 const DataObjectList& _what,
1663 SUIT_DataObject* _where,
1665 Qt::DropAction _action )
1666 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1667 myHelper( _helper ) ,
1671 myAction ( _action )
1674 virtual void execute()
1676 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1679 PyModuleHelper* myHelper;
1680 DataObjectList myWhat;
1681 SUIT_DataObject* myWhere;
1683 Qt::DropAction myAction;
1686 // Posting the request only if dispatcher is not busy!
1687 // Executing the request synchronously
1688 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1689 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1693 \brief Get module engine IOR
1694 \return engine IOR as it is supplied by GUI Python module
1696 QString PyModuleHelper::engineIOR() const
1698 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1700 class EngineIORReq : public PyInterp_LockRequest
1703 EngineIORReq( PyInterp_Interp* _py_interp,
1704 PyModuleHelper* _helper,
1706 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1707 myHelper( _helper ),
1711 virtual void execute()
1713 myIOR = myHelper->internalEngineIOR();
1716 PyModuleHelper* myHelper;
1720 static QString anIOR;
1722 if ( anIOR.isEmpty() ) {
1724 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1725 const_cast<PyModuleHelper*>( this ),
1733 \brief Initialize python subinterpreter (one per study).
1735 \param studyId study ID
1737 void PyModuleHelper::initInterp( int studyId )
1739 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1743 // Error! Study Id must not be 0!
1748 QMutexLocker ml( &myInitMutex );
1750 // try to find the subinterpreter
1751 if ( myInterpMap.contains( studyId ) ) {
1753 myInterp = myInterpMap[ studyId ];
1757 myInterp = new SALOME_PYQT_PyInterp();
1758 myInterp->initialize();
1759 myInterpMap[ studyId ] = myInterp;
1761 #ifndef GUI_DISABLE_CORBA
1762 if ( !SUIT_PYTHON::initialized ) {
1763 // import 'salome' module and call 'salome_init' method;
1764 // do it only once on interpreter creation
1765 // ... first get python lock
1766 PyLockWrapper aLock; // Acquire GIL
1767 // ... then import a module
1768 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1774 // ... then call a method
1776 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
1787 \brief Import Python GUI module and store reference to the module.
1790 Warning! initInterp() should be called first!!!
1792 void PyModuleHelper::importModule()
1794 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1796 // check if the subinterpreter is initialized
1798 // Error! Python subinterpreter should be initialized first!
1803 // import Python GUI module and put it in <myPyModule> attribute
1804 // ... first get python lock
1805 PyLockWrapper aLock; // Acquire GIL
1806 // ... then import a module
1807 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1809 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1814 if ( !myPyModule ) {
1822 \brief Set study workspace to the Python module.
1825 Calls setWorkSpace() method of the Python module with
1826 PyQt QWidget object to use with interpreter.
1828 Attention! initInterp() and importModule() should be called first!!!
1830 void PyModuleHelper::setWorkSpace()
1832 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1834 if ( !IsCallOldMethods )
1837 // check if the subinterpreter is initialized and Python module is imported
1838 if ( !myInterp || !myPyModule ) {
1839 // Error! Python subinterpreter should be initialized and module should be imported first!
1843 // call setWorkSpace() method
1844 // ... first get python lock
1845 PyLockWrapper aLock; // Acquire GIL
1847 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1848 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1855 // ... then get workspace object
1856 QWidget* aWorkspace = 0;
1857 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1858 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1860 aWorkspace = d->workspace();
1862 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1863 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1865 aWorkspace = d->workstack();
1867 #if SIP_VERSION < 0x040800
1868 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1870 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1872 // ... and finally call Python module's setWorkSpace() method (obsolete)
1873 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1874 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1882 \brief Initialization callback function
1885 Performs the following actions:
1886 - initialize or get the Python interpreter (one per study)
1887 - import the Python module
1888 - pass the workspace widget to the Python module
1889 - call Python module's initialize() method
1890 - call Python module's windows() method
1891 - call Python module's views() method
1893 \param app parent application object
1895 void PyModuleHelper::internalInitialize( CAM_Application* app )
1897 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1899 // reset interpreter to NULL
1903 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1906 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1909 int aStudyId = aStudy ? aStudy->id() : 0;
1911 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1912 initInterp( aStudyId );
1916 // import Python GUI module
1921 // then call Python module's initialize() method
1922 // ... first get python lock
1923 PyLockWrapper aLock; // Acquire GIL
1925 // ... (the Python module is already imported)
1926 // ... finally call Python module's initialize() method
1927 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1928 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1934 // get required dockable windows list from the Python module
1935 // by calling windows() method
1936 // ... first put default values
1937 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1938 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1939 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1941 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1942 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1947 myWindowsMap.clear();
1948 if ( PyDict_Check( res1 ) ) {
1952 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1953 // parse the return value
1954 // it should be a map: {integer:integer}
1956 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
1957 aKey = PyInt_AsLong( key );
1958 aValue = PyInt_AsLong( value );
1959 myWindowsMap[ aKey ] = aValue;
1966 // get compatible view windows types from the Python module
1967 // by calling views() method
1968 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1969 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1974 // parse the return value
1975 // result can be one string...
1976 if ( PyString_Check( res2 ) ) {
1977 myViewMgrList.append( PyString_AsString( res2 ) );
1979 // ... or list of strings
1980 else if ( PyList_Check( res2 ) ) {
1981 int size = PyList_Size( res2 );
1982 for ( int i = 0; i < size; i++ ) {
1983 PyObject* value = PyList_GetItem( res2, i );
1984 if( value && PyString_Check( value ) ) {
1985 myViewMgrList.append( PyString_AsString( value ) );
1994 \brief Activation callback function
1997 Performs the following actions:
1998 - initialize or get the Python interpreter (one per study)
1999 - import the Python GUI module
2000 - call Python module's activate() method
2002 \param study parent study
2004 void PyModuleHelper::internalActivate( SUIT_Study* study )
2006 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
2009 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2010 int aStudyId = aStudy ? aStudy->id() : 0;
2012 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2013 initInterp( aStudyId );
2015 myLastActivateStatus = false;
2019 // import Python GUI module
2021 if ( !myPyModule ) {
2022 myLastActivateStatus = false;
2027 PyLockWrapper aLock; // Acquire GIL
2029 // call Python module's activate() method (for the new modules)
2030 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
2031 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
2032 if ( !res1 || !PyBool_Check( res1 ) ) {
2034 // always true for old modules (no return value)
2035 myLastActivateStatus = true;
2038 // detect return status
2039 myLastActivateStatus = PyObject_IsTrue( res1 );
2045 \brief Additional menu customization callback function
2048 Performs the following actions:
2049 - get the Python interpreter (one per study)
2050 - import the Python GUI module
2051 - call Python module's setSettings() method (obsolete function,
2052 used for compatibility with old code)
2054 \param study parent study
2056 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2058 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2061 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2062 int aStudyId = aStudy ? aStudy->id() : 0;
2064 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2065 initInterp( aStudyId );
2067 myLastActivateStatus = false;
2071 // import Python GUI module
2073 if ( !myPyModule ) {
2074 myLastActivateStatus = false;
2078 // call Python module's setWorkSpace() method (obsolete)
2082 PyLockWrapper aLock; // Acquire GIL
2084 if ( IsCallOldMethods ) {
2085 // call Python module's setSettings() method (obsolete)
2086 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2087 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2091 myLastActivateStatus = myLastActivateStatus && true;
2097 \brief Deactivation callback function
2100 Performs the following actions:
2101 - call Python module's deactivate() method
2103 \param study parent study
2105 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2107 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2109 // check that Python subinterpreter is initialized and Python module is imported
2110 if ( !myInterp || !myPyModule ) {
2111 // Error! Python subinterpreter should be initialized and module should be imported first!
2114 // then call Python module's deactivate() method
2115 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2116 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2124 \brief Internal closure:
2126 Performs the following actions:
2127 - call Python module's closeStudy() method
2129 \param theStudy parent study object
2131 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2133 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2137 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2138 int aStudyId = aStudy ? aStudy->id() : 0;
2140 // check that Python subinterpreter is initialized and Python module is imported
2141 if ( !myInterp || !myPyModule ) {
2142 // Error! Python subinterpreter should be initialized and module should be imported first!
2145 // then call Python module's deactivate() method
2146 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2147 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i", aStudyId ) );
2157 \brief Preference changing callback function.
2160 Performs the following actions:
2161 - call Python module's preferenceChanged() method
2163 \param section resources section name
2164 \param setting resources parameter name
2166 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2168 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2170 // check that Python subinterpreter is initialized and Python module is imported
2171 if ( !myInterp || !myPyModule ) {
2172 // Error! Python subinterpreter should be initialized and module should be imported first!
2176 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2177 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2178 (char*)"preferenceChanged",
2180 section.toLatin1().constData(),
2181 setting.toLatin1().constData() ) );
2189 \brief Active study change callback function.
2192 Called when active the study is actived (user brings its
2194 - initialize or get the Python interpreter (one per study)
2195 - import the Python GUI module
2196 - call Python module's activeStudyChanged() method
2198 \param study study being activated
2200 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2202 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2205 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2206 int id = aStudy ? aStudy->id() : 0;
2208 fmsg.message( QString( "study id = %1" ).arg( id ) );
2210 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2215 // import Python GUI module
2220 // call Python module's setWorkSpace() method
2224 PyLockWrapper aLock; // Acquire GIL
2226 // call Python module's activeStudyChanged() method
2227 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2228 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
2236 \brief GUI event handling callback function
2239 Performs the following actions:
2240 - calls Python module's OnGUIEvent() method
2242 \param id GUI action ID
2244 void PyModuleHelper::internalActionActivated( int id )
2246 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2247 fmsg.message( QString( "action id = %1" ).arg( id ) );
2249 // Python interpreter should be initialized and Python module should be
2251 if ( !myInterp || !myPyModule )
2254 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2255 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2263 \brief update selection from other views or modules
2266 Performs the following actions:
2267 - calls Python module's onSelectionpdated(entries) method
2269 \param list of entries
2271 void PyModuleHelper::internalSelectionUpdated(const QStringList& entries)
2273 FuncMsg fmsg("--- PyModuleHelper::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 PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
2298 \brief Context popup menu handling callback function
2301 Performs the following actions:
2302 - calls Python module's definePopup(...) method (obsolete function,
2303 used for compatibility with old code) to define the popup menu context
2304 - parses XML resourses file (if exists) and fills the popup menu with the items)
2305 - calls Python module's customPopup(...) method (obsolete function,
2306 used for compatibility with old code) to allow module to customize the popup menu
2307 - for new modules calls createPopupMenu() function to allow the
2308 modules to build the popup menu by using insertItem(...) Qt functions.
2310 \param context popup menu context
2311 \param menu popup menu
2313 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2315 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2316 fmsg.message( QString( "context: %1" ).arg( context ) );
2318 // Python interpreter should be initialized and Python module should be
2320 if ( !myInterp || !myPyModule )
2323 QString aContext( "" ), aObject( "" ), aParent( context );
2325 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2326 // call definePopup() Python module's function
2327 // this is obsolete function, used only for compatibility reasons
2328 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2329 (char*)"definePopup",
2331 context.toLatin1().constData(),
2332 aObject.toLatin1().constData(),
2333 aParent.toLatin1().constData() ) );
2338 // parse return value
2340 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2346 } // if ( IsCallOldMethods ... )
2348 // first try to create menu via XML parser:
2349 // we create popup menus without help of QtxPopupMgr
2351 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2353 #if SIP_VERSION < 0x040800
2354 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2356 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2359 // then call Python module's createPopupMenu() method (for new modules)
2360 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2361 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2362 (char*)"createPopupMenu",
2365 context.toLatin1().constData() ) );
2371 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2372 // call customPopup() Python module's function
2373 // this is obsolete function, used only for compatibility reasons
2374 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2375 (char*)"customPopup",
2378 aContext.toLatin1().constData(),
2379 aObject.toLatin1().constData(),
2380 aParent.toLatin1().constData() ) );
2388 \brief Preferences initialization callback function.
2391 Performs the following actions:
2392 - calls Python module's createPreferences() method
2394 void PyModuleHelper::internalCreatePreferences()
2396 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2398 // Python interpreter should be initialized and Python module should be
2400 if ( !myInterp || !myPyModule )
2403 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2404 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2412 \brief Active view changing callback function
2414 \param view view being activated
2416 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2418 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2420 if ( !myInterp || !myPyModule || !view )
2423 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2425 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2426 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2434 \brief View closing callback function
2436 \param view view user tries to close
2438 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2440 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2442 if ( !myInterp || !myPyModule || !view )
2445 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2447 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2449 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2458 \brief View closing callback function
2460 \param view view being closed
2462 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2464 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2466 if ( !myInterp || !myPyModule || !view )
2469 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2471 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2473 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2482 \brief View cloning callback function
2484 \param view view being cloned
2486 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2488 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2490 if ( !myInterp || !myPyModule || !view )
2493 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2495 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2497 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2504 \brief Module data saving callback function.
2506 \param files output list of files where module stores data
2507 \param url study URL
2509 void PyModuleHelper::internalSave( QStringList& files, const QString& url )
2511 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2513 // Python interpreter should be initialized and Python module should be
2515 // files list should contain a path to the temporary directory as a first entry
2516 if ( !myInterp || !myPyModule || files.isEmpty() )
2519 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2521 // try with two parameters (new syntax)
2522 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2524 files.first().toLatin1().constData(),
2525 url.toLatin1().constData() ) );
2527 // try with single parameter (old syntax)
2528 res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2529 (char*)"s", files.first().toLatin1().constData() );
2535 // parse the return value
2536 // result can be one string...
2537 if ( PyString_Check( res ) ) {
2538 QString astr = PyString_AsString( res );
2539 files.append( astr );
2541 //also result can be a list...
2542 else if ( PyList_Check( res ) ) {
2543 int size = PyList_Size( res );
2544 for ( int i = 0; i < size; i++ ) {
2545 PyObject* value = PyList_GetItem( res, i );
2546 if ( value && PyString_Check( value ) ) {
2547 files.append( PyString_AsString( value ) );
2556 \brief Module data loading callback function.
2558 \param files list of files where module data is stored
2559 \param url study URL
2560 \param opened output success flag
2562 void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
2564 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2566 // Python interpreter should be initialized and Python module should be
2568 if ( !myInterp || !myPyModule || files.isEmpty() )
2571 QStringList* theList = new QStringList( files );
2573 #if SIP_VERSION < 0x040800
2574 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2576 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2578 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2580 // try with two parameters (new syntax)
2581 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2582 (char*)"Os", sipList.get(),
2583 url.toLatin1().constData() ) );
2586 // try with single parameter (old syntax)
2587 res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
2588 (char*)"O", sipList.get() );
2590 if ( !res || !PyBool_Check( res ) ) {
2595 opened = PyObject_IsTrue( res );
2601 \brief Module dump python callback function.
2603 \param files output list of files where module stores python script
2605 void PyModuleHelper::internalDumpPython( QStringList& files )
2607 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2609 // Python interpreter should be initialized and Python module should be
2611 // files list should contain a path to the temporary directory
2612 if ( !myInterp || !myPyModule || files.isEmpty() )
2615 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2616 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2617 (char*)"s", files.first().toLatin1().constData()));
2623 // parse the return value
2624 // result can be one string...
2625 if ( PyString_Check( res ) ) {
2626 QString astr = PyString_AsString( res );
2630 //also result can be a list...
2631 else if ( PyList_Check( res ) ) {
2632 int size = PyList_Size( res );
2633 for ( int i = 0; i < size; i++ ) {
2634 PyObject* value = PyList_GetItem( res, i );
2635 if( value && PyString_Check( value ) ) {
2636 files.append( PyString_AsString( value ) );
2645 \brief Check data object's 'draggable' status callback function.
2647 \param what data object being tested
2648 \return \c true if object can be dragged or \c false otherwise
2650 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2652 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2654 // Python interpreter should be initialized and Python module should be
2656 if ( !myInterp || !myPyModule || !what )
2659 bool draggable = false;
2661 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2662 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2663 (char*)"s", what->entry().toLatin1().constData() ) );
2664 if( !res || !PyBool_Check( res )) {
2669 draggable = PyObject_IsTrue( res );
2677 \brief Check data object's 'drop allowed' status callback function.
2679 \param where data object being tested
2680 \return \c true if if drop operation is supported by object or \c false otherwise
2682 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2684 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2686 // Python interpreter should be initialized and Python module should be
2688 if ( !myInterp || !myPyModule || !where )
2691 bool dropAccepted = false;
2693 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2694 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2695 (char*)"s", where->entry().toLatin1().constData() ) );
2696 if( !res || !PyBool_Check( res )) {
2698 dropAccepted = false;
2701 dropAccepted = PyObject_IsTrue( res );
2705 return dropAccepted;
2709 \brief Data dropping callback function.
2711 \param what list of data objects being dropped
2712 \param where target data object for drop operation
2713 \param row line (child item index) where drop operation is performed to
2714 \param action current drop action (copy or move)
2716 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2717 const int row, Qt::DropAction action )
2719 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2721 // Python interpreter should be initialized and Python module should be
2723 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2726 QStringList* theList = new QStringList();
2728 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2729 if ( !whereObject ) return;
2731 for ( int i = 0; i < what.count(); i++ ) {
2732 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2733 if ( dataObject ) theList->append( dataObject->entry() );
2736 #if SIP_VERSION < 0x040800
2737 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2739 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2741 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2742 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2744 whereObject->entry().toLatin1().constData(),
2754 \brief Get engine IOR callback function
2757 Tries to get engine IOR from the Python module using engineIOR() function.
2758 That function can load module engine using appropriate container if required.
2760 \return engine IOR or empty string if it is not provided by Python module
2762 QString PyModuleHelper::internalEngineIOR() const
2764 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2768 // Python interpreter should be initialized and Python module should be
2770 if ( myInterp && myModule ) {
2771 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2772 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2777 // parse the return value, result chould be string
2778 if ( PyString_Check( res ) ) {
2779 ior = PyString_AsString( res );
2788 \brief Connects signals about activating and cloning view on internal slots
2789 \param view view being connected
2791 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2793 SUIT_ViewManager* viewMgr = view->getViewManager();
2794 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2796 // Connect tryCloseView() and deleteView() signals
2798 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2799 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2800 Qt::UniqueConnection );
2801 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2802 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2803 Qt::UniqueConnection );
2806 // Connect cloneView() signal of an OCC View
2807 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2808 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2809 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2810 Qt::UniqueConnection );
2812 // Connect cloneView() signal of Plot2d View
2813 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2814 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2815 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2816 Qt::UniqueConnection );
2822 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2824 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2826 // Python interpreter should be initialized and Python module should be
2828 if ( !myInterp || !myPyModule )
2831 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2832 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2841 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2843 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2845 // temporary set myInitModule because dumpPython() method
2846 // might be called by the framework when this module is inactive,
2847 // but still it should be possible to access this module's data
2849 InitLocker lock( myModule );
2851 class PythonReq: public PyInterp_LockRequest
2854 PythonReq( PyInterp_Interp* _py_interp,
2855 PyModuleHelper* _helper,
2856 const QString& _entry,
2858 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2859 myHelper( _helper ) ,
2864 virtual void execute()
2866 myHelper->internalOBClickedPython( myEntry, myColumn );
2869 PyModuleHelper* myHelper;
2874 // Posting the request only if dispatcher is not busy!
2875 // Executing the request synchronously
2876 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2877 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2878 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );