1 // Copyright (C) 2007-2021 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 theDEBUG = 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 )
181 (void)msg; // unused in debug mode
183 MESSAGE( qPrintable( myName ) << " : " << qPrintable( msg ) );
190 \class PyModuleHelper::InitLocker
191 \brief Initialization locker
195 class PyModuleHelper::InitLocker
198 InitLocker( LightApp_Module* );
206 PyModuleHelper::InitLocker::InitLocker( LightApp_Module* module )
208 QMutexLocker ml( &myInitMutex );
209 myInitModule = module;
216 PyModuleHelper::InitLocker::~InitLocker()
218 QMutexLocker ml( &myInitMutex );
223 \class PyModuleHelper::XmlHandler
224 \brief XML resource files parser.
227 This class is used to provide backward compatibility with
228 existing Python modules in which obsolete menu definition system
229 (via XML files) is used.
232 class PyModuleHelper::XmlHandler
235 XmlHandler( PyModuleHelper* helper, const QString& fileName );
236 void createActions();
237 void createPopup( QMenu* menu,
238 const QString& context,
239 const QString& parent,
240 const QString& object );
241 void activateMenus( bool );
244 LightApp_Module* module() const;
245 QIcon loadIcon( const QString& fileName );
247 void createMenu( QDomNode& parentNode,
248 const int parentMenuId = -1,
249 QMenu* parentPopup = 0 );
250 void createToolBar( QDomNode& parentNode );
251 void insertPopupItems( QDomNode& parentNode,
255 PyModuleHelper* myHelper;
257 QList<int> myMenuItems;
264 \param module pointer to the GUI module
265 \param fileName path to the XML menu description file
267 PyModuleHelper::XmlHandler::XmlHandler( PyModuleHelper* helper,
268 const QString& fileName )
271 if ( !fileName.isEmpty() ) {
272 QFile aFile( fileName );
273 if ( aFile.open( QIODevice::ReadOnly ) ) {
274 myDoc.setContent( &aFile );
280 \brief Parse XML file and create actions.
283 Called by PyModuleHelper::initialize() in order to create actions
286 void PyModuleHelper::XmlHandler::createActions()
288 // get document element
289 QDomElement aDocElem = myDoc.documentElement();
291 // create main menu actions
292 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
293 for ( int i = 0; i < aMenuList.count(); i++ ) {
294 QDomNode n = aMenuList.item( i );
298 // create toolbars actions
299 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
300 for ( int i = 0; i < aToolsList.count(); i++ ) {
301 QDomNode n = aToolsList.item( i );
307 \brief Create popup menu.
309 \param menu popup menu
310 \param context popup menu context
311 \param context popup menu parent object name
312 \param context popup menu object name
314 void PyModuleHelper::XmlHandler::createPopup( QMenu* menu,
315 const QString& context,
316 const QString& parent,
317 const QString& object )
319 // get document element
320 QDomElement aDocElem = myDoc.documentElement();
322 // get popup menus actions
323 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
324 for ( int i = 0; i < aPopupList.count(); i++ ) {
325 QDomNode n = aPopupList.item( i );
326 if ( !n.isNull() && n.isElement() ) {
327 QDomElement e = n.toElement();
328 // QString lab = attribute( e, "label-id" ); // not used //
329 QString ctx = attribute( e, "context-id" );
330 QString prt = attribute( e, "parent-id" );
331 QString obj = attribute( e, "object-id" );
332 if ( ctx == context && prt == parent && obj == object ) {
333 insertPopupItems( n, menu );
341 \brief Activate/deactivate menus
343 \param enable if \c true menus are activated, otherwise menus are deactivated
345 void PyModuleHelper::XmlHandler::activateMenus( bool enable )
348 QtxActionMenuMgr* mgr = module()->menuMgr();
349 foreach( int id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
354 \brief Get owner module
356 LightApp_Module* PyModuleHelper::XmlHandler::module() const
358 return myHelper->module();
362 \brief Load an icon from the module resources by the specified file name.
363 \param fileName icon file name
367 QIcon PyModuleHelper::XmlHandler::loadIcon( const QString& fileName )
371 if ( module() && !fileName.isEmpty() ) {
372 SUIT_ResourceMgr* resMgr = module()->getApp()->resourceMgr();
373 QPixmap pixmap = resMgr->loadPixmap( module()->name(),
374 QApplication::translate( module()->name().toLatin1().data(),
375 fileName.toUtf8().data() ) );
376 if ( !pixmap.isNull() )
377 icon = QIcon( pixmap );
384 \brief Create main menu item and insert actions to it.
386 \param parentNode XML node with menu description
387 \param parentMenuId parent menu ID (-1 for top-level menu)
388 \param parentPopup parent popup menu (0 for top-level menu)
390 void PyModuleHelper::XmlHandler::createMenu( QDomNode& parentNode,
391 const int parentMenuId,
392 QMenu* /*parentPopup*/ )
394 if ( !module() || parentNode.isNull() )
397 QDomElement parentElement = parentNode.toElement();
398 if ( !parentElement.isNull() ) {
399 QString plabel = attribute( parentElement, "label-id" );
400 int pid = checkInt( attribute( parentElement, "item-id" ) );
401 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
402 int group = checkInt( attribute( parentElement, "group-id" ),
403 PyModuleHelper::defaultMenuGroup() );
404 if ( !plabel.isEmpty() ) {
408 menuId = module()->createMenu( plabel, // label
409 parentMenuId, // parent menu ID, -1 for top-level menu
413 myMenuItems.append( menuId );
414 QDomNode node = parentNode.firstChild();
415 while ( !node.isNull() ) {
416 if ( node.isElement() ) {
417 QDomElement elem = node.toElement();
418 QString aTagName = tagName( elem );
419 if ( aTagName == "popup-item" ) {
420 int id = checkInt( attribute( elem, "item-id" ) );
421 int pos = checkInt( attribute( elem, "pos-id" ) );
422 int group = checkInt( attribute( elem, "group-id" ),
423 PyModuleHelper::defaultMenuGroup() );
424 QString label = attribute( elem, "label-id" );
425 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
426 QString tooltip = attribute( elem, "tooltip-id" );
427 QString accel = attribute( elem, "accel-id" );
428 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
430 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
431 // also check if the action with given ID is already created
433 // create menu action
434 QAction* action = module()->createAction( id, // ID
438 tooltip, // status-bar text
439 QKeySequence( accel ), // keyboard accelerator
440 module(), // action owner
441 toggle ); // toogled action
442 myHelper->connectAction( action );
443 module()->createMenu( action, // action
444 menuId, // parent menu ID
445 id, // ID (same as for createAction())
450 else if ( aTagName == "submenu" ) {
452 createMenu( node, menuId, popup );
454 else if ( aTagName == "separator" ) {
455 // create menu separator
456 int id = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
457 int pos = checkInt( attribute( elem, "pos-id" ) );
458 int group = checkInt( attribute( elem, "group-id" ),
459 PyModuleHelper::defaultMenuGroup() );
460 QAction* action = module()->separator();
461 module()->createMenu( action, // separator action
462 menuId, // parent menu ID
468 node = node.nextSibling();
475 \brief Create a toolbar and insert actions to it.
476 \param parentNode XML node with toolbar description
478 void PyModuleHelper::XmlHandler::createToolBar( QDomNode& parentNode )
480 if ( !module() || parentNode.isNull() )
483 QDomElement parentElement = parentNode.toElement();
484 if ( !parentElement.isNull() ) {
485 QString aLabel = attribute( parentElement, "label-id" );
486 QString aName = attribute( parentElement, "name-id" );
487 if ( !aLabel.isEmpty() ) {
489 int tbId = module()->createTool( aLabel, aName );
490 QDomNode node = parentNode.firstChild();
491 while ( !node.isNull() ) {
492 if ( node.isElement() ) {
493 QDomElement elem = node.toElement();
494 QString aTagName = tagName( elem );
495 if ( aTagName == "toolbutton-item" ) {
496 int id = checkInt( attribute( elem, "item-id" ) );
497 int pos = checkInt( attribute( elem, "pos-id" ) );
498 QString label = attribute( elem, "label-id" );
499 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
500 QString tooltip = attribute( elem, "tooltip-id" );
501 QString accel = attribute( elem, "accel-id" );
502 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
504 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
505 // also check if the action with given ID is already created
507 // create toolbar action
508 QAction* action = module()->createAction( id, // ID
512 tooltip, // status-bar text
513 QKeySequence( accel ), // keyboard accelerator
514 module(), // action owner
515 toggle ); // toogled action
516 myHelper->connectAction( action );
517 module()->createTool( action, tbId, -1, pos );
520 else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
521 // create toolbar separator
522 int pos = checkInt( attribute( elem, "pos-id" ) );
523 QAction* action = module()->separator();
524 module()->createTool( action, tbId, -1, pos );
527 node = node.nextSibling();
534 \brief Fill popup menu with the items.
535 \param parentNode XML node with popup menu description
536 \param menu popup menu
538 void PyModuleHelper::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
540 if ( !module() && parentNode.isNull() )
543 // we create popup menus without help of QtxPopupMgr
544 QDomNode node = parentNode.firstChild();
545 while ( !node.isNull() ) {
546 if ( node.isElement() ) {
547 QDomElement elem = node.toElement();
548 QString aTagName = tagName( elem );
549 QList<QAction*> actions = menu->actions();
550 if ( aTagName == "popup-item" ) {
551 // insert a command item
552 int id = checkInt( attribute( elem, "item-id" ) );
553 int pos = checkInt( attribute( elem, "pos-id" ) );
554 QString label = attribute( elem, "label-id" );
555 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
556 QString tooltip = attribute( elem, "tooltip-id" );
557 QString accel = attribute( elem, "accel-id" );
558 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
560 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
561 // also check if the action with given ID is already created
563 QAction* action = module()->createAction( id, // ID
567 tooltip, // status-bar text
568 QKeySequence( accel ), // keyboard accelerator
569 module(), // action owner
570 toggle ); // toogled action
571 myHelper->connectAction( action );
572 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
573 menu->insertAction( before, action );
576 else if ( aTagName == "submenu" ) {
578 ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
579 int pos = checkInt( attribute( elem, "pos-id" ) );
580 QString label = attribute( elem, "label-id" );
581 QString icon = attribute( elem, "icon-id" );
584 if ( !icon.isEmpty() ) {
585 QPixmap pixmap = module()->getApp()->resourceMgr()->loadPixmap( module()->name(), icon );
586 if ( !pixmap.isNull() )
587 anIcon = QIcon( pixmap );
590 QMenu* newPopup = menu->addMenu( anIcon, label );
591 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
592 menu->insertMenu( before, newPopup );
593 insertPopupItems( node, newPopup );
595 else if ( aTagName == "separator" ) {
596 // create menu separator
597 int pos = checkInt( attribute( elem, "pos-id" ) );
598 QAction* action = module()->separator();
599 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
600 menu->insertAction( before, action );
603 node = node.nextSibling();
608 \class PyModuleHelper
609 \brief This class implements API helper for all the Python-based
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;
1721 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1722 const_cast<PyModuleHelper*>( this ),
1728 \brief Initialize python subinterpreter (one per study).
1731 void PyModuleHelper::initInterp()
1733 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1735 QMutexLocker ml( &myInitMutex );
1737 myInterp = new SALOME_PYQT_PyInterp();
1738 myInterp->initialize();
1740 #ifndef GUI_DISABLE_CORBA
1741 if ( !SUIT_PYTHON::initialized ) {
1742 // import 'salome' module and call 'salome_init' method;
1743 // do it only once on interpreter creation
1744 // ... first get python lock
1745 PyLockWrapper aLock; // Acquire GIL
1746 // ... then import a module
1747 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1753 // ... then call a method
1755 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"si", NULL, embedded ) );
1766 \brief Import Python GUI module and store reference to the module.
1769 Warning! initInterp() should be called first!!!
1771 void PyModuleHelper::importModule()
1773 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1775 // check if the subinterpreter is initialized
1777 // Error! Python subinterpreter should be initialized first!
1782 // import Python GUI module and put it in <myPyModule> attribute
1783 // ... first get python lock
1784 PyLockWrapper aLock; // Acquire GIL
1785 // ... then import a module
1786 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1788 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1793 if ( !myPyModule ) {
1801 \brief Set study workspace to the Python module.
1804 Calls setWorkSpace() method of the Python module with
1805 PyQt QWidget object to use with interpreter.
1807 Attention! initInterp() and importModule() should be called first!!!
1809 void PyModuleHelper::setWorkSpace()
1811 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1813 if ( !IsCallOldMethods )
1816 // check if the subinterpreter is initialized and Python module is imported
1817 if ( !myInterp || !myPyModule ) {
1818 // Error! Python subinterpreter should be initialized and module should be imported first!
1822 // call setWorkSpace() method
1823 // ... first get python lock
1824 PyLockWrapper aLock; // Acquire GIL
1826 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1827 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1834 // ... then get workspace object
1835 QWidget* aWorkspace = 0;
1836 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1837 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1839 aWorkspace = d->workspace();
1841 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1842 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1844 aWorkspace = d->workstack();
1846 #if SIP_VERSION >= 0x041300
1847 static const sipTypeDef *sipType_QWidget = 0;
1848 if (!sipType_QWidget)
1849 sipType_QWidget = sipFindType("QWidget");
1851 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1852 // ... and finally call Python module's setWorkSpace() method (obsolete)
1853 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1854 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1862 \brief Initialization callback function
1865 Performs the following actions:
1866 - initialize or get the Python interpreter (one per study)
1867 - import the Python module
1868 - pass the workspace widget to the Python module
1869 - call Python module's initialize() method
1870 - call Python module's windows() method
1871 - call Python module's views() method
1873 \param app parent application object
1875 void PyModuleHelper::internalInitialize( CAM_Application* app )
1877 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1879 // reset interpreter to NULL
1883 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1886 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1890 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1895 // import Python GUI module
1900 // then call Python module's initialize() method
1901 // ... first get python lock
1902 PyLockWrapper aLock; // Acquire GIL
1904 // ... (the Python module is already imported)
1905 // ... finally call Python module's initialize() method
1906 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1907 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1913 // get required dockable windows list from the Python module
1914 // by calling windows() method
1915 // ... first put default values
1916 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1917 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1918 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1920 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1921 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1926 myWindowsMap.clear();
1927 if ( PyDict_Check( res1 ) ) {
1931 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1932 // parse the return value
1933 // it should be a map: {integer:integer}
1935 if( key && PyLong_Check( key ) && value && PyLong_Check( value ) ) {
1936 aKey = PyLong_AsLong( key );
1937 aValue = PyLong_AsLong( value );
1938 myWindowsMap[ aKey ] = aValue;
1945 // get compatible view windows types from the Python module
1946 // by calling views() method
1947 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1948 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1953 // parse the return value
1954 // result can be one string...
1955 if ( PyUnicode_Check( res2 ) ) {
1956 myViewMgrList.append( PyUnicode_AsUTF8( res2 ) );
1958 // ... or list of strings
1959 else if ( PyList_Check( res2 ) ) {
1960 int size = PyList_Size( res2 );
1961 for ( int i = 0; i < size; i++ ) {
1962 PyObject* value = PyList_GetItem( res2, i );
1963 if( value && PyUnicode_Check( value ) ) {
1964 myViewMgrList.append( PyUnicode_AsUTF8( value ) );
1973 \brief Activation callback function
1976 Performs the following actions:
1977 - initialize or get the Python interpreter (one per study)
1978 - import the Python GUI module
1979 - call Python module's activate() method
1981 \param study parent study
1983 void PyModuleHelper::internalActivate( SUIT_Study* study )
1985 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1988 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1992 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1995 myLastActivateStatus = false;
1999 // import Python GUI module
2001 if ( !myPyModule ) {
2002 myLastActivateStatus = false;
2007 PyLockWrapper aLock; // Acquire GIL
2009 // call Python module's activate() method (for the new modules)
2010 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
2011 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
2012 if ( !res1 || !PyBool_Check( res1 ) ) {
2014 // always true for old modules (no return value)
2015 myLastActivateStatus = true;
2018 // detect return status
2019 myLastActivateStatus = PyObject_IsTrue( res1 );
2025 \brief Additional menu customization callback function
2028 Performs the following actions:
2029 - get the Python interpreter (one per study)
2030 - import the Python GUI module
2031 - call Python module's setSettings() method (obsolete function,
2032 used for compatibility with old code)
2034 \param study parent study
2036 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2038 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2041 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2045 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2048 myLastActivateStatus = false;
2052 // import Python GUI module
2054 if ( !myPyModule ) {
2055 myLastActivateStatus = false;
2059 // call Python module's setWorkSpace() method (obsolete)
2063 PyLockWrapper aLock; // Acquire GIL
2065 if ( IsCallOldMethods ) {
2066 // call Python module's setSettings() method (obsolete)
2067 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2068 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2072 myLastActivateStatus = myLastActivateStatus && true;
2078 \brief Deactivation callback function
2081 Performs the following actions:
2082 - call Python module's deactivate() method
2084 \param study parent study
2086 void PyModuleHelper::internalDeactivate( SUIT_Study* /*study*/ )
2088 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2090 // check that Python subinterpreter is initialized and Python module is imported
2091 if ( !myInterp || !myPyModule ) {
2092 // Error! Python subinterpreter should be initialized and module should be imported first!
2095 // then call Python module's deactivate() method
2096 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2097 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2105 \brief Internal closure:
2107 Performs the following actions:
2108 - call Python module's closeStudy() method
2110 \param theStudy parent study object
2112 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2114 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2118 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2122 // check that Python subinterpreter is initialized and Python module is imported
2123 if ( !myInterp || !myPyModule ) {
2124 // Error! Python subinterpreter should be initialized and module should be imported first!
2127 // then call Python module's deactivate() method
2128 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2129 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"" ) );
2139 \brief Preference changing callback function.
2142 Performs the following actions:
2143 - call Python module's preferenceChanged() method
2145 \param section resources section name
2146 \param setting resources parameter name
2148 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2150 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2152 // check that Python subinterpreter is initialized and Python module is imported
2153 if ( !myInterp || !myPyModule ) {
2154 // Error! Python subinterpreter should be initialized and module should be imported first!
2158 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2159 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2160 (char*)"preferenceChanged",
2162 section.toUtf8().constData(),
2163 setting.toUtf8().constData() ) );
2171 \brief Active study change callback function.
2174 Called when active the study is actived (user brings its
2176 - initialize or get the Python interpreter (one per study)
2177 - import the Python GUI module
2178 - call Python module's activeStudyChanged() method
2180 \param study study being activated
2182 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2184 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2187 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2191 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2196 // import Python GUI module
2201 // call Python module's setWorkSpace() method
2205 PyLockWrapper aLock; // Acquire GIL
2207 // call Python module's activeStudyChanged() method
2208 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2209 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"" ) );
2217 \brief GUI event handling callback function
2220 Performs the following actions:
2221 - calls Python module's OnGUIEvent() method
2223 \param id GUI action ID
2225 void PyModuleHelper::internalActionActivated( int id )
2227 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2228 fmsg.message( QString( "action id = %1" ).arg( id ) );
2230 // Python interpreter should be initialized and Python module should be
2232 if ( !myInterp || !myPyModule )
2235 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2236 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2244 \brief update selection from other views or modules
2247 Performs the following actions:
2248 - calls Python module's onSelectionpdated(entries) method
2250 \param list of entries
2252 void PyModuleHelper::internalSelectionUpdated(const QStringList& entries)
2254 FuncMsg fmsg("--- PyModuleHelper::internalSelectionUpdated()");
2255 MESSAGE("internalSelectionUpdated");
2257 // Python interpreter should be initialized and Python module should be imported first
2258 if (!myInterp || !myPyModule)
2261 QStringList* theList = new QStringList(entries);
2263 #if SIP_VERSION >= 0x041300
2264 static const sipTypeDef *sipType_QStringList = 0;
2265 if (!sipType_QStringList)
2266 sipType_QStringList = sipFindType("QStringList");
2268 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2269 if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
2271 MESSAGE("call onSelectionUpdated");
2272 PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
2282 \brief Context popup menu handling callback function
2285 Performs the following actions:
2286 - calls Python module's definePopup(...) method (obsolete function,
2287 used for compatibility with old code) to define the popup menu context
2288 - parses XML resourses file (if exists) and fills the popup menu with the items)
2289 - calls Python module's customPopup(...) method (obsolete function,
2290 used for compatibility with old code) to allow module to customize the popup menu
2291 - for new modules calls createPopupMenu() function to allow the
2292 modules to build the popup menu by using insertItem(...) Qt functions.
2294 \param context popup menu context
2295 \param menu popup menu
2297 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2299 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2300 fmsg.message( QString( "context: %1" ).arg( context ) );
2302 // Python interpreter should be initialized and Python module should be
2304 if ( !myInterp || !myPyModule )
2307 QString aContext( "" ), aObject( "" ), aParent( context );
2309 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2310 // call definePopup() Python module's function
2311 // this is obsolete function, used only for compatibility reasons
2312 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2313 (char*)"definePopup",
2315 context.toUtf8().constData(),
2316 aObject.toUtf8().constData(),
2317 aParent.toUtf8().constData() ) );
2322 // parse return value
2324 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2330 } // if ( IsCallOldMethods ... )
2332 // first try to create menu via XML parser:
2333 // we create popup menus without help of QtxPopupMgr
2335 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2337 #if SIP_VERSION >= 0x041300
2338 static const sipTypeDef *sipType_QMenu = 0;
2340 sipType_QMenu = sipFindType("QMenu");
2342 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2344 // then call Python module's createPopupMenu() method (for new modules)
2345 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2346 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2347 (char*)"createPopupMenu",
2350 context.toUtf8().constData() ) );
2356 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2357 // call customPopup() Python module's function
2358 // this is obsolete function, used only for compatibility reasons
2359 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2360 (char*)"customPopup",
2363 aContext.toUtf8().constData(),
2364 aObject.toUtf8().constData(),
2365 aParent.toUtf8().constData() ) );
2373 \brief Preferences initialization callback function.
2376 Performs the following actions:
2377 - calls Python module's createPreferences() method
2379 void PyModuleHelper::internalCreatePreferences()
2381 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2383 // Python interpreter should be initialized and Python module should be
2385 if ( !myInterp || !myPyModule )
2388 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2389 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2397 \brief Active view changing callback function
2399 \param view view being activated
2401 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2403 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2405 if ( !myInterp || !myPyModule || !view )
2408 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2410 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2411 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2419 \brief View closing callback function
2421 \param view view user tries to close
2423 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2425 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2427 if ( !myInterp || !myPyModule || !view )
2430 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2432 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2434 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2443 \brief View closing callback function
2445 \param view view being closed
2447 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2449 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2451 if ( !myInterp || !myPyModule || !view )
2454 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2456 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2458 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2467 \brief View cloning callback function
2469 \param view view being cloned
2471 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2473 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2475 if ( !myInterp || !myPyModule || !view )
2478 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2480 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2482 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2489 \brief Module data saving callback function.
2491 \param files output list of files where module stores data
2492 \param url study URL
2494 void PyModuleHelper::internalSave( QStringList& files, const QString& url )
2496 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2498 // Python interpreter should be initialized and Python module should be
2500 // files list should contain a path to the temporary directory as a first entry
2501 if ( !myInterp || !myPyModule || files.isEmpty() )
2504 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2506 // try with two parameters (new syntax)
2507 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2509 files.first().toUtf8().constData(),
2510 url.toUtf8().constData() ) );
2512 // try with single parameter (old syntax)
2513 res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2514 (char*)"s", files.first().toUtf8().constData() );
2520 // parse the return value
2521 // result can be one string...
2522 if ( PyUnicode_Check( res ) ) {
2523 QString astr = PyUnicode_AsUTF8( res );
2524 files.append( astr );
2526 //also result can be a list...
2527 else if ( PyList_Check( res ) ) {
2528 int size = PyList_Size( res );
2529 for ( int i = 0; i < size; i++ ) {
2530 PyObject* value = PyList_GetItem( res, i );
2531 if ( value && PyUnicode_Check( value ) ) {
2532 files.append( PyUnicode_AsUTF8( value ) );
2541 \brief Module data loading callback function.
2543 \param files list of files where module data is stored
2544 \param url study URL
2545 \param opened output success flag
2547 void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
2549 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2551 // Python interpreter should be initialized and Python module should be
2553 if ( !myInterp || !myPyModule || files.isEmpty() )
2556 QStringList* theList = new QStringList( files );
2558 #if SIP_VERSION >= 0x041300
2559 static const sipTypeDef *sipType_QStringList = 0;
2560 if (!sipType_QStringList)
2561 sipType_QStringList = sipFindType("QStringList");
2563 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2564 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2566 // try with two parameters (new syntax)
2567 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2568 (char*)"Os", sipList.get(),
2569 url.toUtf8().constData() ) );
2572 // try with single parameter (old syntax)
2573 res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
2574 (char*)"O", sipList.get() );
2576 if ( !res || !PyBool_Check( res ) ) {
2581 opened = PyObject_IsTrue( res );
2587 \brief Module dump python callback function.
2589 \param files output list of files where module stores python script
2591 void PyModuleHelper::internalDumpPython( QStringList& files )
2593 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2595 // Python interpreter should be initialized and Python module should be
2597 // files list should contain a path to the temporary directory
2598 if ( !myInterp || !myPyModule || files.isEmpty() )
2601 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2602 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2603 (char*)"s", files.first().toUtf8().constData()));
2609 // parse the return value
2610 // result can be one string...
2611 if ( PyUnicode_Check( res ) ) {
2612 QString astr = PyUnicode_AsUTF8( res );
2616 //also result can be a list...
2617 else if ( PyList_Check( res ) ) {
2618 int size = PyList_Size( res );
2619 for ( int i = 0; i < size; i++ ) {
2620 PyObject* value = PyList_GetItem( res, i );
2621 if( value && PyUnicode_Check( value ) ) {
2622 files.append( PyUnicode_AsUTF8( value ) );
2631 \brief Check data object's 'draggable' status callback function.
2633 \param what data object being tested
2634 \return \c true if object can be dragged or \c false otherwise
2636 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2638 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2640 // Python interpreter should be initialized and Python module should be
2642 if ( !myInterp || !myPyModule || !what )
2645 bool draggable = false;
2647 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2648 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2649 (char*)"s", what->entry().toUtf8().constData() ) );
2650 if( !res || !PyBool_Check( res )) {
2655 draggable = PyObject_IsTrue( res );
2663 \brief Check data object's 'drop allowed' status callback function.
2665 \param where data object being tested
2666 \return \c true if if drop operation is supported by object or \c false otherwise
2668 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2670 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2672 // Python interpreter should be initialized and Python module should be
2674 if ( !myInterp || !myPyModule || !where )
2677 bool dropAccepted = false;
2679 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2680 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2681 (char*)"s", where->entry().toUtf8().constData() ) );
2682 if( !res || !PyBool_Check( res )) {
2684 dropAccepted = false;
2687 dropAccepted = PyObject_IsTrue( res );
2691 return dropAccepted;
2695 \brief Data dropping callback function.
2697 \param what list of data objects being dropped
2698 \param where target data object for drop operation
2699 \param row line (child item index) where drop operation is performed to
2700 \param action current drop action (copy or move)
2702 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2703 const int row, Qt::DropAction action )
2705 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2707 // Python interpreter should be initialized and Python module should be
2709 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2712 QStringList* theList = new QStringList();
2714 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2715 if ( !whereObject ) return;
2717 for ( int i = 0; i < what.count(); i++ ) {
2718 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2719 if ( dataObject ) theList->append( dataObject->entry() );
2722 #if SIP_VERSION >= 0x041300
2723 static const sipTypeDef *sipType_QStringList = 0;
2724 if (!sipType_QStringList)
2725 sipType_QStringList = sipFindType("QStringList");
2727 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2728 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2729 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2731 whereObject->entry().toUtf8().constData(),
2741 \brief Get engine IOR callback function
2744 Tries to get engine IOR from the Python module using engineIOR() function.
2745 That function can load module engine using appropriate container if required.
2747 \return engine IOR or empty string if it is not provided by Python module
2749 QString PyModuleHelper::internalEngineIOR() const
2751 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2755 // Python interpreter should be initialized and Python module should be
2757 if ( myInterp && myModule ) {
2758 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2759 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2764 // parse the return value, result chould be string
2765 if ( PyUnicode_Check( res ) ) {
2766 ior = PyUnicode_AsUTF8( res );
2775 \brief Connects signals about activating and cloning view on internal slots
2776 \param view view being connected
2778 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2780 SUIT_ViewManager* viewMgr = view->getViewManager();
2781 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2783 // Connect tryCloseView() and deleteView() signals
2785 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2786 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2787 Qt::UniqueConnection );
2788 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2789 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2790 Qt::UniqueConnection );
2793 // Connect cloneView() signal of an OCC View
2794 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2795 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2796 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2797 Qt::UniqueConnection );
2799 // Connect cloneView() signal of Plot2d View
2800 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2801 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2802 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2803 Qt::UniqueConnection );
2809 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2811 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2813 // Python interpreter should be initialized and Python module should be
2815 if ( !myInterp || !myPyModule )
2818 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2819 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toUtf8().constData(), theColumn ) );
2828 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2830 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2832 // temporary set myInitModule because dumpPython() method
2833 // might be called by the framework when this module is inactive,
2834 // but still it should be possible to access this module's data
2836 InitLocker lock( myModule );
2838 class PythonReq: public PyInterp_LockRequest
2841 PythonReq( PyInterp_Interp* _py_interp,
2842 PyModuleHelper* _helper,
2843 const QString& _entry,
2845 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2846 myHelper( _helper ) ,
2851 virtual void execute()
2853 myHelper->internalOBClickedPython( myEntry, myColumn );
2856 PyModuleHelper* myHelper;
2861 // Posting the request only if dispatcher is not busy!
2862 // Executing the request synchronously
2863 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2864 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2865 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );