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 LightApp_Module* PyModuleHelper::myInitModule = 0;
616 \param module owner module
618 PyModuleHelper::PyModuleHelper( LightApp_Module* module ) :
624 myLastActivateStatus( true )
626 setObjectName( "python_module_helper" );
632 PyModuleHelper::~PyModuleHelper()
635 if ( myInterp && myPyModule ) {
636 PyLockWrapper aLock; // Acquire GIL
637 Py_XDECREF( myPyModule );
642 \brief Get the module being initialized.
644 This is a little trick :) needed to provide an access from Python
645 (SalomePyQt) to the module being currently activated. The problem
646 that during the process of module initialization (initialize()
647 function) it is not yet available via application->activeModule()
650 This method returns valid pointer only if called in scope of
651 initialize() function or in several other specific cases.
653 \return the module being currently initialized
655 LightApp_Module* PyModuleHelper::getInitModule()
657 QMutexLocker ml( &myInitMutex );
662 \brief Get default menu group identifier
663 \return menu group ID (40 by default)
665 int PyModuleHelper::defaultMenuGroup()
667 return DEFAULT_GROUP;
671 \brief Get owner module
674 LightApp_Module* PyModuleHelper::module() const
680 \brief Get Python GUI module object
681 \return python module
683 PyObject* PyModuleHelper::pythonModule() const
689 \brief Connect action to the internal actionActivated() slot.
691 Actions connected to internal actionActivated(), when activated, will
692 be forwarded to the Python GUI module OnGUIEvent() function.
694 \param a action being connected
696 void PyModuleHelper::connectAction( QAction* a )
699 QObject::connect( a, SIGNAL( triggered( bool ) ),
700 this, SLOT( actionActivated() ),
701 Qt::UniqueConnection );
705 \brief Get the dockable windows associated with the module.
707 To fill the list of windows the correspondind Python module's windows()
708 method is called during the module initialization.
710 By default, ObjectBrowser, PythonConsole and LogWindow windows are
711 associated to the module.
713 Allowed dockable windows:
714 - LightApp_Application::WT_ObjectBrowser : object browser
715 - LightApp_Application::WT_PyConsole : python console
716 - LightApp_Application::WT_LogWindow : log messages output window
718 Dock area is defined by Qt::DockWidgetArea enumeration:
719 - Qt::TopDockWidgetArea : top dock area
720 - Qt::BottomDockWidgetArea : bottom dock area
721 - Qt::LeftDockWidgetArea : left dock area
722 - Qt::RightDockWidgetArea : right dock area
724 \return map of dockable windows in form { <window_type> : <dock_area> }
726 QMap<int, int> PyModuleHelper::windows() const
728 FuncMsg fmsg( "PyModuleHelper::windows()" );
734 \brief Define the compatible view windows associated with the module.
736 The associated view windows are opened automatically when the module
739 To fill the list of views the correspondind Python module's views()
740 method is called during the module initialization.
741 By default, the list of view types is empty.
743 \return list of view windows types
745 QStringList PyModuleHelper::viewManagers() const
747 FuncMsg fmsg( "PyModuleHelper::viewManagers()" );
749 return myViewMgrList;
753 \brief Initialization of the Python-based SALOME module.
755 This method can be used for creation of the menus, toolbars and
758 There are two ways to do this:
759 1) for obsolete modules, the implementation of this method first tries to read
760 the <module>_<language>.xml resource file which contains a menu,
761 toolbars and popup menus description;
762 2) new modules can create menus by direct calling of the
763 corresponding methods of SalomePyQt Python API in the Python
764 module's initialize() method which is called from here.
766 \note SALOME supports two modes of modules loading:
767 - immediate (all the modules are created and initialized
768 immediately when the application object is created);
769 - postponed modules loading (used currently); in this mode
770 the module is loaded only by explicit request.
771 If postponed modules loading is not used, the active
772 study might be not yet defined at this stage, so initialize()
773 method should not perform any study-based initialization.
774 Such actions can be better done in activate() function.
776 \param app parent application object
778 void PyModuleHelper::initialize( CAM_Application* app )
780 FuncMsg fmsg( "PyModuleHelper::initialize()" );
782 // temporarily store module being currently activated
783 // in the global variable to make it accessible from
785 InitLocker lock( myModule );
787 // try to get XML resource file name
788 SUIT_ResourceMgr* resMgr = myModule->getApp()->resourceMgr();
789 if ( !myXmlHandler && resMgr ) {
790 // get current language
791 QString lang = resMgr->stringValue( "language", "language", "en" );
792 // get menu description file name
793 QString aFileName = QString( "%1_%2.xml" ).arg( myModule->name() ).arg( lang );
794 aFileName = resMgr->path( "resources", myModule->name(), aFileName );
795 if ( !aFileName.isEmpty() && QFile::exists( aFileName ) ) {
796 // create XML handler instance
797 myXmlHandler = new XmlHandler( this, aFileName );
798 // ask XML handler to create actions
799 myXmlHandler->createActions();
803 class InitializeReq : public PyInterp_Request
806 InitializeReq( PyModuleHelper* _helper,
807 CAM_Application* _app )
808 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
813 virtual void execute()
815 myHelper->internalInitialize( myApp );
818 PyModuleHelper* myHelper;
819 CAM_Application* myApp;
823 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( this, app ) );
827 \brief Activation of the module.
829 This function is usually used in order to show the module's
830 specific menus and toolbars, update actions state and perform
831 other such actions required when the module is activated.
833 \note Returning \c false from this function prevents the
836 \param study parent study
837 \return \c true if activation is successful and \c false otherwise
839 bool PyModuleHelper::activate( SUIT_Study* study )
841 FuncMsg fmsg( "PyModuleHelper::activate()" );
843 // reset the activation status to the default value
844 myLastActivateStatus = true;
846 class ActivateReq : public PyInterp_Request
849 ActivateReq( PyModuleHelper* _helper,
852 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
855 myCustomize( _customize )
858 virtual void execute()
861 myHelper->internalActivate( myStudy ); // first activation stage
863 myHelper->internalCustomize( myStudy ); // second activation stage
866 PyModuleHelper* myHelper;
871 // post request for activation (customize=false)
872 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, false ) );
874 // check activation status (can be set to false by internalActivate())
875 if ( myLastActivateStatus ) {
876 // activate menus, toolbars, etc
877 if ( myXmlHandler ) myXmlHandler->activateMenus( true );
879 // show menus / toolbars
880 myModule->setMenuShown( true );
881 myModule->setToolShown( true );
883 // post request for customization (customize=true)
884 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, true ) );
886 // check activation status (can be set to false by internalCustomize())
887 if ( myLastActivateStatus ) {
888 // connect preferences changing signal
889 connect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
890 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
892 // connect active view change signal
893 SUIT_Desktop* d = study->application()->desktop();
894 connect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
895 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
896 // if active window exists, call activeViewChanged() function;
897 // temporary solution: if a getActiveView() in SalomePyQt available
898 // we no longer need this
899 SUIT_ViewWindow* view = d->activeWindow();
900 if ( view ) activeViewChanged( view );
901 // get all view currently opened in the study and connect their signals to
902 // the corresponding slots of the class.
903 foreach ( view, d->windows() ) connectView( view );
906 // hide menus / toolbars in case of error
907 myModule->setMenuShown( false );
908 myModule->setToolShown( false );
912 return myLastActivateStatus;
916 \brief Deactivation of the module.
918 This function is usually used in order to hide the module's
919 specific menus and toolbars and perform other such actions
920 required when the module is deactivated.
922 \param study parent study
923 \return \c true if deactivation is successful and \c false otherwise
925 bool PyModuleHelper::deactivate( SUIT_Study* study )
927 FuncMsg fmsg( "PyModuleHelper::deactivate()" );
929 class DeactivateReq : public PyInterp_LockRequest
932 DeactivateReq( PyInterp_Interp* _py_interp,
933 PyModuleHelper* _helper,
935 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
940 virtual void execute()
942 myHelper->internalDeactivate( myStudy );
945 PyModuleHelper* myHelper;
950 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, this, study ) );
952 // disconnect preferences changing signal
953 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
954 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
956 // disconnect the SUIT_Desktop signal windowActivated()
957 SUIT_Desktop* d = study->application()->desktop();
958 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
959 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
961 // deactivate menus, toolbars, etc
962 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
964 // hide menus / toolbars
965 myModule->setMenuShown( false );
966 myModule->setToolShown( false );
972 \brief Close of the module.
974 This function is usually used in order to close the module's
975 specific menus and toolbars and perform other such actions
976 required when the module is closed.
978 void PyModuleHelper::modelClosed( SUIT_Study* study )
980 FuncMsg fmsg( "PyModuleHelper::modelClosed()" );
982 class StudyClosedReq : public PyInterp_LockRequest
985 StudyClosedReq( PyInterp_Interp* _py_interp,
986 PyModuleHelper* _helper,
988 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
993 virtual void execute()
995 myHelper->internalClosedStudy( myStudy );
998 PyModuleHelper* myHelper;
1003 PyInterp_Dispatcher::Get()->Exec( new StudyClosedReq( myInterp, this, study ) );
1005 // disconnect preferences changing signal
1006 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
1007 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
1009 // disconnect the SUIT_Desktop signal windowActivated()
1010 SUIT_Desktop* d = study->application()->desktop();
1011 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1012 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
1014 // deactivate menus, toolbars, etc
1015 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
1017 // hide menus / toolbars
1018 myModule->setMenuShown( false );
1019 myModule->setToolShown( false );
1024 \brief Process module's preferences changing.
1026 Called when the module's own preferences are changed.
1028 \param section preference resources section
1029 \param parameter preference resources parameter name
1031 void PyModuleHelper::preferencesChanged( const QString& section,
1032 const QString& parameter )
1034 FuncMsg fmsg( "PyModuleHelper::preferencesChanged()" );
1036 class PrefChangeReq : public PyInterp_LockRequest
1039 PrefChangeReq( PyInterp_Interp* _py_interp,
1040 PyModuleHelper* _helper,
1041 const QString& _section,
1042 const QString& _parameter )
1043 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1044 myHelper ( _helper ),
1045 mySection( _section ),
1046 myParameter( _parameter )
1049 virtual void execute()
1051 myHelper->internalPreferencesChanged( mySection, myParameter );
1054 PyModuleHelper* myHelper;
1055 QString mySection, myParameter;
1058 // post the request only if dispatcher is not busy!
1059 // execute request synchronously
1060 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1061 PyInterp_Dispatcher::Get()->Exec( new PrefChangeReq( myInterp, this, section, parameter ) );
1065 \brief Process application preferences changing.
1067 Called when any application setting is changed.
1069 \param module preference module
1070 \param section preference resources section
1071 \param parameter preference resources parameter name
1073 void PyModuleHelper::preferenceChanged( const QString& module,
1074 const QString& section,
1075 const QString& parameter )
1077 FuncMsg fmsg( "PyModuleHelper::preferenceChanged()" );
1079 // module's own preferences are processed by other preferencesChanged() method
1080 if ( module != myModule->moduleName() ) {
1082 preferencesChanged( section, parameter );
1087 \brief Process study activation.
1089 Called when study desktop is activated. Used for notifying the Python
1090 module about changing of the active study.
1092 \param study study being activated
1094 void PyModuleHelper::studyActivated( SUIT_Study* study )
1096 FuncMsg fmsg( "PyModuleHelper::studyActivated()" );
1098 // StudyChangedReq: request class for internal studyChanged() operation
1099 class StudyChangedReq : public PyInterp_Request
1102 StudyChangedReq( PyModuleHelper* _helper,
1103 SUIT_Study* _study )
1104 : PyInterp_Request(0, true ), // this request should be processed synchronously (sync == true)
1105 myHelper( _helper ),
1109 virtual void execute()
1111 myHelper->internalStudyChanged( myStudy );
1114 PyModuleHelper* myHelper;
1115 SUIT_Study* myStudy;
1119 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( this, study ) );
1123 \brief Process action activation.
1125 Called when action is activated. Used for notifying the Python
1126 module about any related action activation.
1130 void PyModuleHelper::actionActivated()
1132 FuncMsg fmsg( "PyModuleHelper::actionActivated()" );
1134 // perform synchronous request to Python event dispatcher
1135 class ActionReq : public PyInterp_LockRequest
1138 ActionReq( PyInterp_Interp* _py_interp,
1139 PyModuleHelper* _helper,
1141 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1142 myHelper( _helper ),
1146 virtual void execute()
1148 myHelper->internalActionActivated( myId );
1151 PyModuleHelper* myHelper;
1155 // get sender action
1156 QAction* action = qobject_cast<QAction*>( sender() );
1161 PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
1165 \brief update selection from other views or modules.
1167 Called when selection is modified outside.
1169 void PyModuleHelper::selectionUpdated(const QStringList& entries)
1171 FuncMsg fmsg( "PyModuleHelper::selectionUpdated()" );
1172 MESSAGE("selectionUpdated");
1174 // perform synchronous request to Python event dispatcher
1175 class SelectionReq : public PyInterp_LockRequest
1178 SelectionReq( PyInterp_Interp* _py_interp,
1179 PyModuleHelper* _helper,
1180 const QStringList& _entries )
1181 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1182 myHelper( _helper ),
1183 myEntries( _entries )
1185 MESSAGE("SelectionReq");
1188 virtual void execute()
1191 myHelper->internalSelectionUpdated( myEntries );
1194 PyModuleHelper* myHelper;
1195 const QStringList& myEntries;
1199 PyInterp_Dispatcher::Get()->Exec( new SelectionReq( myInterp, this, entries ) );
1203 \brief Process context popup menu request.
1205 Called when user activates popup menu in some window
1206 (view, object browser, etc).
1208 \param context popup menu context (e.g. "ObjectBrowser")
1209 \param menu popup menu
1211 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1213 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1215 class ContextMenuReq : public PyInterp_LockRequest
1218 ContextMenuReq( PyInterp_Interp* _py_interp,
1219 PyModuleHelper* _helper,
1220 const QString& _context,
1222 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1223 myHelper ( _helper ),
1224 myContext( _context ),
1228 virtual void execute()
1230 myHelper->internalContextMenu( myContext, myMenu );
1233 PyModuleHelper* myHelper;
1238 // post request only if dispatcher is not busy!
1239 // execute request synchronously
1240 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1241 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1245 \brief Export preferences for the Python module.
1246 Called only once when the first instance of the module is created or
1247 when common Preferences dialog box is first time invoked.
1249 void PyModuleHelper::createPreferences()
1251 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1253 // temporary set myInitModule because createPreferences() method
1254 // might be called during the module intialization process
1255 InitLocker lock( myModule );
1257 class CreatePrefReq : public PyInterp_LockRequest
1260 CreatePrefReq( PyInterp_Interp* _py_interp,
1261 PyModuleHelper* _helper )
1262 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1266 virtual void execute()
1268 myHelper->internalCreatePreferences();
1271 PyModuleHelper* myHelper;
1274 // post request only if dispatcher is not busy!
1275 // execute request synchronously
1276 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1277 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1281 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1283 Used to notify Python module that active view has been changed by the user.
1285 \param view view being activated
1287 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1289 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1291 // perform synchronous request to Python event dispatcher
1292 class ActiveViewChangeReq : public PyInterp_LockRequest
1295 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1296 PyModuleHelper* _helper,
1297 SUIT_ViewWindow* _view )
1298 : PyInterp_LockRequest( _py_interp, 0, true ),
1299 myHelper( _helper ),
1303 virtual void execute()
1305 myHelper->internalActiveViewChanged( myView );
1308 PyModuleHelper* myHelper;
1309 SUIT_ViewWindow* myView;
1312 // connect view (if it is not connected yet)
1313 connectView( view );
1315 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1319 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1320 \param view view being closed
1322 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1324 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1326 class TryCloseViewReq : public PyInterp_LockRequest
1329 TryCloseViewReq( PyInterp_Interp* _py_interp,
1330 PyModuleHelper* _helper,
1331 SUIT_ViewWindow* _view )
1332 : PyInterp_LockRequest( _py_interp, 0, true ),
1333 myHelper( _helper ),
1337 virtual void execute()
1339 myHelper->internalTryCloseView( myView );
1342 PyModuleHelper* myHelper;
1343 SUIT_ViewWindow* myView;
1346 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1350 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1351 \param view view being closed
1353 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1355 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1357 class CloseViewReq : public PyInterp_LockRequest
1360 CloseViewReq( PyInterp_Interp* _py_interp,
1361 PyModuleHelper* _helper,
1362 SUIT_ViewWindow* _view )
1363 : PyInterp_LockRequest( _py_interp, 0, true ),
1364 myHelper( _helper ),
1368 virtual void execute()
1370 myHelper->internalCloseView( myView );
1373 PyModuleHelper* myHelper;
1374 SUIT_ViewWindow* myView;
1377 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1381 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1382 \param view view being cloned
1384 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1386 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1388 class CloneViewReq : public PyInterp_LockRequest
1391 CloneViewReq( PyInterp_Interp* _py_interp,
1392 PyModuleHelper* _helper,
1393 SUIT_ViewWindow* _view )
1394 : PyInterp_LockRequest( _py_interp, 0, true ),
1395 myHelper( _helper ),
1399 virtual void execute()
1401 myHelper->internalCloneView( myView );
1404 PyModuleHelper* myHelper;
1405 SUIT_ViewWindow* myView;
1408 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1412 \brief Save module data. Called when user saves study.
1413 \param files output list of files where module stores data
1414 \param url study URL
1416 void PyModuleHelper::save( QStringList& files, const QString& url )
1418 FuncMsg fmsg( "PyModuleHelper::save()" );
1420 // temporary set myInitModule because save() method
1421 // might be called by the framework when this module is inactive,
1422 // but still it should be possible to access this module's data
1424 InitLocker lock( myModule );
1426 // perform synchronous request to Python event dispatcher
1427 class SaveReq: public PyInterp_LockRequest
1430 SaveReq( PyInterp_Interp* _py_interp,
1431 PyModuleHelper* _helper,
1432 QStringList& _files,
1433 const QString& _url )
1434 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1435 myHelper( _helper ) ,
1440 virtual void execute()
1442 myHelper->internalSave( myFiles, myUrl );
1445 PyModuleHelper* myHelper;
1446 QStringList& myFiles;
1450 // Posting the request only if dispatcher is not busy!
1451 // Executing the request synchronously
1452 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1453 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files, url ) );
1457 \brief Load module data. Called when user opens study
1458 and activates module.
1459 \param files list of files where module data is stored
1460 \param url study URL
1461 \return \c true if loading has been finished successfully or \c false otherwise
1463 bool PyModuleHelper::load( const QStringList& files, const QString& url )
1465 FuncMsg fmsg( "PyModuleHelper::load()" );
1467 bool loaded = false;
1469 class LoadReq: public PyInterp_LockRequest
1472 LoadReq( PyInterp_Interp* _py_interp,
1473 PyModuleHelper* _helper,
1475 const QString& _url,
1477 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1478 myHelper( _helper ) ,
1484 virtual void execute()
1486 myHelper->internalLoad( myFiles, myUrl, myLoaded );
1489 PyModuleHelper* myHelper;
1490 QStringList myFiles;
1495 // Posting the request only if dispatcher is not busy!
1496 // Executing the request synchronously
1497 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1498 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, url, loaded ) );
1504 \brief Dump module data to the Python script.
1505 Called when user activates dump study operation.
1506 \param files output list of files where module stores python script
1508 void PyModuleHelper::dumpPython( QStringList& files )
1510 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1512 // temporary set myInitModule because dumpPython() method
1513 // might be called by the framework when this module is inactive,
1514 // but still it should be possible to access this module's data
1516 InitLocker lock( myModule );
1518 class DumpPythonReq: public PyInterp_LockRequest
1521 DumpPythonReq( PyInterp_Interp* _py_interp,
1522 PyModuleHelper* _helper,
1523 QStringList& _files )
1524 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1525 myHelper( _helper ) ,
1529 virtual void execute()
1531 myHelper->internalDumpPython( myFiles );
1534 PyModuleHelper* myHelper;
1535 QStringList& myFiles;
1538 // Posting the request only if dispatcher is not busy!
1539 // Executing the request synchronously
1540 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1541 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1545 \brief Test if object \a what can be dragged by the user.
1546 \param what data object being tested
1547 \return \c true if object can be dragged or \c false otherwise
1549 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1551 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1553 bool draggable = false;
1555 // perform synchronous request to Python event dispatcher
1556 class IsDraggableReq: public PyInterp_LockRequest
1559 IsDraggableReq( PyInterp_Interp* _py_interp,
1560 PyModuleHelper* _helper,
1561 LightApp_DataObject* _data_object,
1562 bool& _is_draggable )
1563 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1564 myHelper( _helper ) ,
1565 myDataObject( _data_object ),
1566 myIsDraggable( _is_draggable )
1569 virtual void execute()
1571 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1574 PyModuleHelper* myHelper;
1575 LightApp_DataObject* myDataObject;
1576 bool& myIsDraggable;
1579 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1580 if ( data_object ) {
1581 // Posting the request only if dispatcher is not busy!
1582 // Executing the request synchronously
1583 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1584 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1585 const_cast<PyModuleHelper*>( this ),
1586 const_cast<LightApp_DataObject*>( data_object ),
1594 \brief Test if drop operation can be done on the \a where object.
1595 \param where data object being tested
1596 \return \c true if if drop operation is supported by object or \c false otherwise
1598 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1600 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1602 bool dropAccepted = false;
1604 // perform synchronous request to Python event dispatcher
1605 class IsDropAcceptedReq: public PyInterp_LockRequest
1608 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1609 PyModuleHelper* _helper,
1610 LightApp_DataObject* _data_object,
1611 bool& _is_drop_accepted )
1612 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1613 myHelper( _helper ) ,
1614 myDataObject( _data_object ),
1615 myIsDropAccepted( _is_drop_accepted )
1618 virtual void execute()
1620 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1623 PyModuleHelper* myHelper;
1624 LightApp_DataObject* myDataObject;
1625 bool& myIsDropAccepted;
1628 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1629 if ( data_object ) {
1630 // Posting the request only if dispatcher is not busy!
1631 // Executing the request synchronously
1632 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1633 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1634 const_cast<PyModuleHelper*>( this ),
1635 const_cast<LightApp_DataObject*>( data_object ),
1639 return dropAccepted;
1643 \brief Perform drop operation
1644 \param what list of data objects being dropped
1645 \param where target data object for drop operation
1646 \param row line (child item index) where drop operation is performed to
1647 \param action current drop action (copy or move)
1649 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1650 const int row, Qt::DropAction action )
1652 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1654 // perform synchronous request to Python event dispatcher
1655 class DropObjectsReq: public PyInterp_LockRequest
1658 DropObjectsReq( PyInterp_Interp* _py_interp,
1659 PyModuleHelper* _helper,
1660 const DataObjectList& _what,
1661 SUIT_DataObject* _where,
1663 Qt::DropAction _action )
1664 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1665 myHelper( _helper ) ,
1669 myAction ( _action )
1672 virtual void execute()
1674 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1677 PyModuleHelper* myHelper;
1678 DataObjectList myWhat;
1679 SUIT_DataObject* myWhere;
1681 Qt::DropAction myAction;
1684 // Posting the request only if dispatcher is not busy!
1685 // Executing the request synchronously
1686 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1687 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1691 \brief Get module engine IOR
1692 \return engine IOR as it is supplied by GUI Python module
1694 QString PyModuleHelper::engineIOR() const
1696 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1698 class EngineIORReq : public PyInterp_LockRequest
1701 EngineIORReq( PyInterp_Interp* _py_interp,
1702 PyModuleHelper* _helper,
1704 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1705 myHelper( _helper ),
1709 virtual void execute()
1711 myIOR = myHelper->internalEngineIOR();
1714 PyModuleHelper* myHelper;
1718 static QString anIOR;
1720 if ( anIOR.isEmpty() ) {
1722 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1723 const_cast<PyModuleHelper*>( this ),
1731 \brief Initialize python subinterpreter (one per study).
1734 void PyModuleHelper::initInterp()
1736 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1738 QMutexLocker ml( &myInitMutex );
1740 myInterp = new SALOME_PYQT_PyInterp();
1741 myInterp->initialize();
1743 #ifndef GUI_DISABLE_CORBA
1744 if ( !SUIT_PYTHON::initialized ) {
1745 // import 'salome' module and call 'salome_init' method;
1746 // do it only once on interpreter creation
1747 // ... first get python lock
1748 PyLockWrapper aLock; // Acquire GIL
1749 // ... then import a module
1750 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1756 // ... then call a method
1758 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"i", embedded ) );
1769 \brief Import Python GUI module and store reference to the module.
1772 Warning! initInterp() should be called first!!!
1774 void PyModuleHelper::importModule()
1776 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1778 // check if the subinterpreter is initialized
1780 // Error! Python subinterpreter should be initialized first!
1785 // import Python GUI module and put it in <myPyModule> attribute
1786 // ... first get python lock
1787 PyLockWrapper aLock; // Acquire GIL
1788 // ... then import a module
1789 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1791 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1796 if ( !myPyModule ) {
1804 \brief Set study workspace to the Python module.
1807 Calls setWorkSpace() method of the Python module with
1808 PyQt QWidget object to use with interpreter.
1810 Attention! initInterp() and importModule() should be called first!!!
1812 void PyModuleHelper::setWorkSpace()
1814 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1816 if ( !IsCallOldMethods )
1819 // check if the subinterpreter is initialized and Python module is imported
1820 if ( !myInterp || !myPyModule ) {
1821 // Error! Python subinterpreter should be initialized and module should be imported first!
1825 // call setWorkSpace() method
1826 // ... first get python lock
1827 PyLockWrapper aLock; // Acquire GIL
1829 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1830 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1837 // ... then get workspace object
1838 QWidget* aWorkspace = 0;
1839 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1840 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1842 aWorkspace = d->workspace();
1844 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1845 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1847 aWorkspace = d->workstack();
1849 #if SIP_VERSION < 0x040800
1850 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1852 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1854 // ... and finally call Python module's setWorkSpace() method (obsolete)
1855 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1856 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1864 \brief Initialization callback function
1867 Performs the following actions:
1868 - initialize or get the Python interpreter (one per study)
1869 - import the Python module
1870 - pass the workspace widget to the Python module
1871 - call Python module's initialize() method
1872 - call Python module's windows() method
1873 - call Python module's views() method
1875 \param app parent application object
1877 void PyModuleHelper::internalInitialize( CAM_Application* app )
1879 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1881 // reset interpreter to NULL
1885 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1888 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1892 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1897 // import Python GUI module
1902 // then call Python module's initialize() method
1903 // ... first get python lock
1904 PyLockWrapper aLock; // Acquire GIL
1906 // ... (the Python module is already imported)
1907 // ... finally call Python module's initialize() method
1908 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1909 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1915 // get required dockable windows list from the Python module
1916 // by calling windows() method
1917 // ... first put default values
1918 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1919 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1920 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1922 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1923 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1928 myWindowsMap.clear();
1929 if ( PyDict_Check( res1 ) ) {
1933 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1934 // parse the return value
1935 // it should be a map: {integer:integer}
1937 if( key && PyLong_Check( key ) && value && PyLong_Check( value ) ) {
1938 aKey = PyLong_AsLong( key );
1939 aValue = PyLong_AsLong( value );
1940 myWindowsMap[ aKey ] = aValue;
1947 // get compatible view windows types from the Python module
1948 // by calling views() method
1949 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1950 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1955 // parse the return value
1956 // result can be one string...
1957 if ( PyUnicode_Check( res2 ) ) {
1958 myViewMgrList.append( PyUnicode_AsUTF8( res2 ) );
1960 // ... or list of strings
1961 else if ( PyList_Check( res2 ) ) {
1962 int size = PyList_Size( res2 );
1963 for ( int i = 0; i < size; i++ ) {
1964 PyObject* value = PyList_GetItem( res2, i );
1965 if( value && PyUnicode_Check( value ) ) {
1966 myViewMgrList.append( PyUnicode_AsUTF8( value ) );
1975 \brief Activation callback function
1978 Performs the following actions:
1979 - initialize or get the Python interpreter (one per study)
1980 - import the Python GUI module
1981 - call Python module's activate() method
1983 \param study parent study
1985 void PyModuleHelper::internalActivate( SUIT_Study* study )
1987 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1990 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1994 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1997 myLastActivateStatus = false;
2001 // import Python GUI module
2003 if ( !myPyModule ) {
2004 myLastActivateStatus = false;
2009 PyLockWrapper aLock; // Acquire GIL
2011 // call Python module's activate() method (for the new modules)
2012 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
2013 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
2014 if ( !res1 || !PyBool_Check( res1 ) ) {
2016 // always true for old modules (no return value)
2017 myLastActivateStatus = true;
2020 // detect return status
2021 myLastActivateStatus = PyObject_IsTrue( res1 );
2027 \brief Additional menu customization callback function
2030 Performs the following actions:
2031 - get the Python interpreter (one per study)
2032 - import the Python GUI module
2033 - call Python module's setSettings() method (obsolete function,
2034 used for compatibility with old code)
2036 \param study parent study
2038 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2040 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2043 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2047 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2050 myLastActivateStatus = false;
2054 // import Python GUI module
2056 if ( !myPyModule ) {
2057 myLastActivateStatus = false;
2061 // call Python module's setWorkSpace() method (obsolete)
2065 PyLockWrapper aLock; // Acquire GIL
2067 if ( IsCallOldMethods ) {
2068 // call Python module's setSettings() method (obsolete)
2069 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2070 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2074 myLastActivateStatus = myLastActivateStatus && true;
2080 \brief Deactivation callback function
2083 Performs the following actions:
2084 - call Python module's deactivate() method
2086 \param study parent study
2088 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2090 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2092 // check that Python subinterpreter is initialized and Python module is imported
2093 if ( !myInterp || !myPyModule ) {
2094 // Error! Python subinterpreter should be initialized and module should be imported first!
2097 // then call Python module's deactivate() method
2098 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2099 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2107 \brief Internal closure:
2109 Performs the following actions:
2110 - call Python module's closeStudy() method
2112 \param theStudy parent study object
2114 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2116 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2120 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2124 // check that Python subinterpreter is initialized and Python module is imported
2125 if ( !myInterp || !myPyModule ) {
2126 // Error! Python subinterpreter should be initialized and module should be imported first!
2129 // then call Python module's deactivate() method
2130 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2131 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i" ) );
2141 \brief Preference changing callback function.
2144 Performs the following actions:
2145 - call Python module's preferenceChanged() method
2147 \param section resources section name
2148 \param setting resources parameter name
2150 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2152 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2154 // check that Python subinterpreter is initialized and Python module is imported
2155 if ( !myInterp || !myPyModule ) {
2156 // Error! Python subinterpreter should be initialized and module should be imported first!
2160 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2161 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2162 (char*)"preferenceChanged",
2164 section.toLatin1().constData(),
2165 setting.toLatin1().constData() ) );
2173 \brief Active study change callback function.
2176 Called when active the study is actived (user brings its
2178 - initialize or get the Python interpreter (one per study)
2179 - import the Python GUI module
2180 - call Python module's activeStudyChanged() method
2182 \param study study being activated
2184 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2186 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2189 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2193 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2198 // import Python GUI module
2203 // call Python module's setWorkSpace() method
2207 PyLockWrapper aLock; // Acquire GIL
2209 // call Python module's activeStudyChanged() method
2210 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2211 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i" ) );
2219 \brief GUI event handling callback function
2222 Performs the following actions:
2223 - calls Python module's OnGUIEvent() method
2225 \param id GUI action ID
2227 void PyModuleHelper::internalActionActivated( int id )
2229 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2230 fmsg.message( QString( "action id = %1" ).arg( id ) );
2232 // Python interpreter should be initialized and Python module should be
2234 if ( !myInterp || !myPyModule )
2237 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2238 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2246 \brief update selection from other views or modules
2249 Performs the following actions:
2250 - calls Python module's onSelectionpdated(entries) method
2252 \param list of entries
2254 void PyModuleHelper::internalSelectionUpdated(const QStringList& entries)
2256 FuncMsg fmsg("--- PyModuleHelper::internalSelectionUpdated()");
2257 MESSAGE("internalSelectionUpdated");
2259 // Python interpreter should be initialized and Python module should be imported first
2260 if (!myInterp || !myPyModule)
2263 QStringList* theList = new QStringList(entries);
2265 #if SIP_VERSION < 0x040800
2266 PyObjWrapper sipList(sipBuildResult(0, "M", theList, sipClass_QStringList));
2268 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2270 if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
2272 MESSAGE("call onSelectionUpdated");
2273 PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
2283 \brief Context popup menu handling callback function
2286 Performs the following actions:
2287 - calls Python module's definePopup(...) method (obsolete function,
2288 used for compatibility with old code) to define the popup menu context
2289 - parses XML resourses file (if exists) and fills the popup menu with the items)
2290 - calls Python module's customPopup(...) method (obsolete function,
2291 used for compatibility with old code) to allow module to customize the popup menu
2292 - for new modules calls createPopupMenu() function to allow the
2293 modules to build the popup menu by using insertItem(...) Qt functions.
2295 \param context popup menu context
2296 \param menu popup menu
2298 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2300 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2301 fmsg.message( QString( "context: %1" ).arg( context ) );
2303 // Python interpreter should be initialized and Python module should be
2305 if ( !myInterp || !myPyModule )
2308 QString aContext( "" ), aObject( "" ), aParent( context );
2310 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2311 // call definePopup() Python module's function
2312 // this is obsolete function, used only for compatibility reasons
2313 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2314 (char*)"definePopup",
2316 context.toLatin1().constData(),
2317 aObject.toLatin1().constData(),
2318 aParent.toLatin1().constData() ) );
2323 // parse return value
2325 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2331 } // if ( IsCallOldMethods ... )
2333 // first try to create menu via XML parser:
2334 // we create popup menus without help of QtxPopupMgr
2336 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2338 #if SIP_VERSION < 0x040800
2339 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2341 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.toLatin1().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.toLatin1().constData(),
2364 aObject.toLatin1().constData(),
2365 aParent.toLatin1().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().toLatin1().constData(),
2510 url.toLatin1().constData() ) );
2512 // try with single parameter (old syntax)
2513 res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2514 (char*)"s", files.first().toLatin1().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 < 0x040800
2559 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2561 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2563 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2565 // try with two parameters (new syntax)
2566 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2567 (char*)"Os", sipList.get(),
2568 url.toLatin1().constData() ) );
2571 // try with single parameter (old syntax)
2572 res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
2573 (char*)"O", sipList.get() );
2575 if ( !res || !PyBool_Check( res ) ) {
2580 opened = PyObject_IsTrue( res );
2586 \brief Module dump python callback function.
2588 \param files output list of files where module stores python script
2590 void PyModuleHelper::internalDumpPython( QStringList& files )
2592 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2594 // Python interpreter should be initialized and Python module should be
2596 // files list should contain a path to the temporary directory
2597 if ( !myInterp || !myPyModule || files.isEmpty() )
2600 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2601 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2602 (char*)"s", files.first().toLatin1().constData()));
2608 // parse the return value
2609 // result can be one string...
2610 if ( PyUnicode_Check( res ) ) {
2611 QString astr = PyUnicode_AsUTF8( res );
2615 //also result can be a list...
2616 else if ( PyList_Check( res ) ) {
2617 int size = PyList_Size( res );
2618 for ( int i = 0; i < size; i++ ) {
2619 PyObject* value = PyList_GetItem( res, i );
2620 if( value && PyUnicode_Check( value ) ) {
2621 files.append( PyUnicode_AsUTF8( value ) );
2630 \brief Check data object's 'draggable' status callback function.
2632 \param what data object being tested
2633 \return \c true if object can be dragged or \c false otherwise
2635 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2637 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2639 // Python interpreter should be initialized and Python module should be
2641 if ( !myInterp || !myPyModule || !what )
2644 bool draggable = false;
2646 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2647 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2648 (char*)"s", what->entry().toLatin1().constData() ) );
2649 if( !res || !PyBool_Check( res )) {
2654 draggable = PyObject_IsTrue( res );
2662 \brief Check data object's 'drop allowed' status callback function.
2664 \param where data object being tested
2665 \return \c true if if drop operation is supported by object or \c false otherwise
2667 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2669 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2671 // Python interpreter should be initialized and Python module should be
2673 if ( !myInterp || !myPyModule || !where )
2676 bool dropAccepted = false;
2678 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2679 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2680 (char*)"s", where->entry().toLatin1().constData() ) );
2681 if( !res || !PyBool_Check( res )) {
2683 dropAccepted = false;
2686 dropAccepted = PyObject_IsTrue( res );
2690 return dropAccepted;
2694 \brief Data dropping callback function.
2696 \param what list of data objects being dropped
2697 \param where target data object for drop operation
2698 \param row line (child item index) where drop operation is performed to
2699 \param action current drop action (copy or move)
2701 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2702 const int row, Qt::DropAction action )
2704 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2706 // Python interpreter should be initialized and Python module should be
2708 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2711 QStringList* theList = new QStringList();
2713 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2714 if ( !whereObject ) return;
2716 for ( int i = 0; i < what.count(); i++ ) {
2717 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2718 if ( dataObject ) theList->append( dataObject->entry() );
2721 #if SIP_VERSION < 0x040800
2722 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2724 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2726 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2727 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2729 whereObject->entry().toLatin1().constData(),
2739 \brief Get engine IOR callback function
2742 Tries to get engine IOR from the Python module using engineIOR() function.
2743 That function can load module engine using appropriate container if required.
2745 \return engine IOR or empty string if it is not provided by Python module
2747 QString PyModuleHelper::internalEngineIOR() const
2749 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2753 // Python interpreter should be initialized and Python module should be
2755 if ( myInterp && myModule ) {
2756 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2757 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2762 // parse the return value, result chould be string
2763 if ( PyUnicode_Check( res ) ) {
2764 ior = PyUnicode_AsUTF8( res );
2773 \brief Connects signals about activating and cloning view on internal slots
2774 \param view view being connected
2776 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2778 SUIT_ViewManager* viewMgr = view->getViewManager();
2779 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2781 // Connect tryCloseView() and deleteView() signals
2783 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2784 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2785 Qt::UniqueConnection );
2786 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2787 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2788 Qt::UniqueConnection );
2791 // Connect cloneView() signal of an OCC View
2792 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2793 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2794 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2795 Qt::UniqueConnection );
2797 // Connect cloneView() signal of Plot2d View
2798 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2799 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2800 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2801 Qt::UniqueConnection );
2807 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2809 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2811 // Python interpreter should be initialized and Python module should be
2813 if ( !myInterp || !myPyModule )
2816 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2817 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2826 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2828 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2830 // temporary set myInitModule because dumpPython() method
2831 // might be called by the framework when this module is inactive,
2832 // but still it should be possible to access this module's data
2834 InitLocker lock( myModule );
2836 class PythonReq: public PyInterp_LockRequest
2839 PythonReq( PyInterp_Interp* _py_interp,
2840 PyModuleHelper* _helper,
2841 const QString& _entry,
2843 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2844 myHelper( _helper ) ,
2849 virtual void execute()
2851 myHelper->internalOBClickedPython( myEntry, myColumn );
2854 PyModuleHelper* myHelper;
2859 // Posting the request only if dispatcher is not busy!
2860 // Executing the request synchronously
2861 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2862 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2863 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );