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 >= 0x041300
1867 static const sipTypeDef *sipType_QWidget = 0;
1868 if (!sipType_QWidget)
1869 sipType_QWidget = sipFindType("QWidget");
1871 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()");
2274 MESSAGE("internalSelectionUpdated");
2276 // Python interpreter should be initialized and Python module should be imported first
2277 if (!myInterp || !myPyModule)
2280 QStringList* theList = new QStringList(entries);
2282 #if SIP_VERSION >= 0x041300
2283 static const sipTypeDef *sipType_QStringList = 0;
2284 if (!sipType_QStringList)
2285 sipType_QStringList = sipFindType("QStringList");
2287 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2288 if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
2290 MESSAGE("call onSelectionUpdated");
2291 PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
2301 \brief Context popup menu handling callback function
2304 Performs the following actions:
2305 - calls Python module's definePopup(...) method (obsolete function,
2306 used for compatibility with old code) to define the popup menu context
2307 - parses XML resourses file (if exists) and fills the popup menu with the items)
2308 - calls Python module's customPopup(...) method (obsolete function,
2309 used for compatibility with old code) to allow module to customize the popup menu
2310 - for new modules calls createPopupMenu() function to allow the
2311 modules to build the popup menu by using insertItem(...) Qt functions.
2313 \param context popup menu context
2314 \param menu popup menu
2316 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2318 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2319 fmsg.message( QString( "context: %1" ).arg( context ) );
2321 // Python interpreter should be initialized and Python module should be
2323 if ( !myInterp || !myPyModule )
2326 QString aContext( "" ), aObject( "" ), aParent( context );
2328 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2329 // call definePopup() Python module's function
2330 // this is obsolete function, used only for compatibility reasons
2331 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2332 (char*)"definePopup",
2334 context.toLatin1().constData(),
2335 aObject.toLatin1().constData(),
2336 aParent.toLatin1().constData() ) );
2341 // parse return value
2343 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2349 } // if ( IsCallOldMethods ... )
2351 // first try to create menu via XML parser:
2352 // we create popup menus without help of QtxPopupMgr
2354 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2356 #if SIP_VERSION >= 0x041300
2357 static const sipTypeDef *sipType_QMenu = 0;
2359 sipType_QMenu = sipFindType("QMenu");
2361 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2363 // then call Python module's createPopupMenu() method (for new modules)
2364 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2365 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2366 (char*)"createPopupMenu",
2369 context.toLatin1().constData() ) );
2375 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2376 // call customPopup() Python module's function
2377 // this is obsolete function, used only for compatibility reasons
2378 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2379 (char*)"customPopup",
2382 aContext.toLatin1().constData(),
2383 aObject.toLatin1().constData(),
2384 aParent.toLatin1().constData() ) );
2392 \brief Preferences initialization callback function.
2395 Performs the following actions:
2396 - calls Python module's createPreferences() method
2398 void PyModuleHelper::internalCreatePreferences()
2400 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2402 // Python interpreter should be initialized and Python module should be
2404 if ( !myInterp || !myPyModule )
2407 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2408 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2416 \brief Active view changing callback function
2418 \param view view being activated
2420 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2422 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2424 if ( !myInterp || !myPyModule || !view )
2427 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2429 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2430 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2438 \brief View closing callback function
2440 \param view view user tries to close
2442 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2444 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2446 if ( !myInterp || !myPyModule || !view )
2449 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2451 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2453 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2462 \brief View closing callback function
2464 \param view view being closed
2466 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2468 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2470 if ( !myInterp || !myPyModule || !view )
2473 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2475 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2477 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2486 \brief View cloning callback function
2488 \param view view being cloned
2490 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2492 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2494 if ( !myInterp || !myPyModule || !view )
2497 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2499 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2501 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2508 \brief Module data saving callback function.
2510 \param files output list of files where module stores data
2511 \param url study URL
2513 void PyModuleHelper::internalSave( QStringList& files, const QString& url )
2515 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2517 // Python interpreter should be initialized and Python module should be
2519 // files list should contain a path to the temporary directory as a first entry
2520 if ( !myInterp || !myPyModule || files.isEmpty() )
2523 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2525 // try with two parameters (new syntax)
2526 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2528 files.first().toLatin1().constData(),
2529 url.toLatin1().constData() ) );
2531 // try with single parameter (old syntax)
2532 res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2533 (char*)"s", files.first().toLatin1().constData() );
2539 // parse the return value
2540 // result can be one string...
2541 if ( PyString_Check( res ) ) {
2542 QString astr = PyString_AsString( res );
2543 files.append( astr );
2545 //also result can be a list...
2546 else if ( PyList_Check( res ) ) {
2547 int size = PyList_Size( res );
2548 for ( int i = 0; i < size; i++ ) {
2549 PyObject* value = PyList_GetItem( res, i );
2550 if ( value && PyString_Check( value ) ) {
2551 files.append( PyString_AsString( value ) );
2560 \brief Module data loading callback function.
2562 \param files list of files where module data is stored
2563 \param url study URL
2564 \param opened output success flag
2566 void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
2568 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2570 // Python interpreter should be initialized and Python module should be
2572 if ( !myInterp || !myPyModule || files.isEmpty() )
2575 QStringList* theList = new QStringList( files );
2577 #if SIP_VERSION >= 0x041300
2578 static const sipTypeDef *sipType_QStringList = 0;
2579 if (!sipType_QStringList)
2580 sipType_QStringList = sipFindType("QStringList");
2582 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2583 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2585 // try with two parameters (new syntax)
2586 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2587 (char*)"Os", sipList.get(),
2588 url.toLatin1().constData() ) );
2591 // try with single parameter (old syntax)
2592 res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
2593 (char*)"O", sipList.get() );
2595 if ( !res || !PyBool_Check( res ) ) {
2600 opened = PyObject_IsTrue( res );
2606 \brief Module dump python callback function.
2608 \param files output list of files where module stores python script
2610 void PyModuleHelper::internalDumpPython( QStringList& files )
2612 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2614 // Python interpreter should be initialized and Python module should be
2616 // files list should contain a path to the temporary directory
2617 if ( !myInterp || !myPyModule || files.isEmpty() )
2620 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2621 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2622 (char*)"s", files.first().toLatin1().constData()));
2628 // parse the return value
2629 // result can be one string...
2630 if ( PyString_Check( res ) ) {
2631 QString astr = PyString_AsString( res );
2635 //also result can be a list...
2636 else if ( PyList_Check( res ) ) {
2637 int size = PyList_Size( res );
2638 for ( int i = 0; i < size; i++ ) {
2639 PyObject* value = PyList_GetItem( res, i );
2640 if( value && PyString_Check( value ) ) {
2641 files.append( PyString_AsString( value ) );
2650 \brief Check data object's 'draggable' status callback function.
2652 \param what data object being tested
2653 \return \c true if object can be dragged or \c false otherwise
2655 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2657 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2659 // Python interpreter should be initialized and Python module should be
2661 if ( !myInterp || !myPyModule || !what )
2664 bool draggable = false;
2666 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2667 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2668 (char*)"s", what->entry().toLatin1().constData() ) );
2669 if( !res || !PyBool_Check( res )) {
2674 draggable = PyObject_IsTrue( res );
2682 \brief Check data object's 'drop allowed' status callback function.
2684 \param where data object being tested
2685 \return \c true if if drop operation is supported by object or \c false otherwise
2687 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2689 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2691 // Python interpreter should be initialized and Python module should be
2693 if ( !myInterp || !myPyModule || !where )
2696 bool dropAccepted = false;
2698 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2699 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2700 (char*)"s", where->entry().toLatin1().constData() ) );
2701 if( !res || !PyBool_Check( res )) {
2703 dropAccepted = false;
2706 dropAccepted = PyObject_IsTrue( res );
2710 return dropAccepted;
2714 \brief Data dropping callback function.
2716 \param what list of data objects being dropped
2717 \param where target data object for drop operation
2718 \param row line (child item index) where drop operation is performed to
2719 \param action current drop action (copy or move)
2721 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2722 const int row, Qt::DropAction action )
2724 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2726 // Python interpreter should be initialized and Python module should be
2728 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2731 QStringList* theList = new QStringList();
2733 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2734 if ( !whereObject ) return;
2736 for ( int i = 0; i < what.count(); i++ ) {
2737 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2738 if ( dataObject ) theList->append( dataObject->entry() );
2741 #if SIP_VERSION >= 0x041300
2742 static const sipTypeDef *sipType_QStringList = 0;
2743 if (!sipType_QStringList)
2744 sipType_QStringList = sipFindType("QStringList");
2746 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2747 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2748 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2750 whereObject->entry().toLatin1().constData(),
2760 \brief Get engine IOR callback function
2763 Tries to get engine IOR from the Python module using engineIOR() function.
2764 That function can load module engine using appropriate container if required.
2766 \return engine IOR or empty string if it is not provided by Python module
2768 QString PyModuleHelper::internalEngineIOR() const
2770 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2774 // Python interpreter should be initialized and Python module should be
2776 if ( myInterp && myModule ) {
2777 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2778 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2783 // parse the return value, result chould be string
2784 if ( PyString_Check( res ) ) {
2785 ior = PyString_AsString( res );
2794 \brief Connects signals about activating and cloning view on internal slots
2795 \param view view being connected
2797 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2799 SUIT_ViewManager* viewMgr = view->getViewManager();
2800 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2802 // Connect tryCloseView() and deleteView() signals
2804 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2805 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2806 Qt::UniqueConnection );
2807 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2808 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2809 Qt::UniqueConnection );
2812 // Connect cloneView() signal of an OCC View
2813 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2814 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2815 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2816 Qt::UniqueConnection );
2818 // Connect cloneView() signal of Plot2d View
2819 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2820 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2821 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2822 Qt::UniqueConnection );
2828 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2830 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2832 // Python interpreter should be initialized and Python module should be
2834 if ( !myInterp || !myPyModule )
2837 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2838 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2847 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2849 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2851 // temporary set myInitModule because dumpPython() method
2852 // might be called by the framework when this module is inactive,
2853 // but still it should be possible to access this module's data
2855 InitLocker lock( myModule );
2857 class PythonReq: public PyInterp_LockRequest
2860 PythonReq( PyInterp_Interp* _py_interp,
2861 PyModuleHelper* _helper,
2862 const QString& _entry,
2864 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2865 myHelper( _helper ) ,
2870 virtual void execute()
2872 myHelper->internalOBClickedPython( myEntry, myColumn );
2875 PyModuleHelper* myHelper;
2880 // Posting the request only if dispatcher is not busy!
2881 // Executing the request synchronously
2882 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2883 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2884 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );