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 #if SIP_VERSION >= 0x041303
1853 static const sipTypeDef *sipType_QWidget = 0;
1854 if (!sipType_QWidget)
1855 sipType_QWidget = sipFindType("QWidget");
1857 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1859 // ... and finally call Python module's setWorkSpace() method (obsolete)
1860 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1861 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1869 \brief Initialization callback function
1872 Performs the following actions:
1873 - initialize or get the Python interpreter (one per study)
1874 - import the Python module
1875 - pass the workspace widget to the Python module
1876 - call Python module's initialize() method
1877 - call Python module's windows() method
1878 - call Python module's views() method
1880 \param app parent application object
1882 void PyModuleHelper::internalInitialize( CAM_Application* app )
1884 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1886 // reset interpreter to NULL
1890 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1893 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1897 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1902 // import Python GUI module
1907 // then call Python module's initialize() method
1908 // ... first get python lock
1909 PyLockWrapper aLock; // Acquire GIL
1911 // ... (the Python module is already imported)
1912 // ... finally call Python module's initialize() method
1913 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1914 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1920 // get required dockable windows list from the Python module
1921 // by calling windows() method
1922 // ... first put default values
1923 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1924 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1925 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1927 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1928 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1933 myWindowsMap.clear();
1934 if ( PyDict_Check( res1 ) ) {
1938 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1939 // parse the return value
1940 // it should be a map: {integer:integer}
1942 if( key && PyLong_Check( key ) && value && PyLong_Check( value ) ) {
1943 aKey = PyLong_AsLong( key );
1944 aValue = PyLong_AsLong( value );
1945 myWindowsMap[ aKey ] = aValue;
1952 // get compatible view windows types from the Python module
1953 // by calling views() method
1954 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1955 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1960 // parse the return value
1961 // result can be one string...
1962 if ( PyUnicode_Check( res2 ) ) {
1963 myViewMgrList.append( PyUnicode_AsUTF8( res2 ) );
1965 // ... or list of strings
1966 else if ( PyList_Check( res2 ) ) {
1967 int size = PyList_Size( res2 );
1968 for ( int i = 0; i < size; i++ ) {
1969 PyObject* value = PyList_GetItem( res2, i );
1970 if( value && PyUnicode_Check( value ) ) {
1971 myViewMgrList.append( PyUnicode_AsUTF8( value ) );
1980 \brief Activation callback function
1983 Performs the following actions:
1984 - initialize or get the Python interpreter (one per study)
1985 - import the Python GUI module
1986 - call Python module's activate() method
1988 \param study parent study
1990 void PyModuleHelper::internalActivate( SUIT_Study* study )
1992 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1995 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1999 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2002 myLastActivateStatus = false;
2006 // import Python GUI module
2008 if ( !myPyModule ) {
2009 myLastActivateStatus = false;
2014 PyLockWrapper aLock; // Acquire GIL
2016 // call Python module's activate() method (for the new modules)
2017 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
2018 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
2019 if ( !res1 || !PyBool_Check( res1 ) ) {
2021 // always true for old modules (no return value)
2022 myLastActivateStatus = true;
2025 // detect return status
2026 myLastActivateStatus = PyObject_IsTrue( res1 );
2032 \brief Additional menu customization callback function
2035 Performs the following actions:
2036 - get the Python interpreter (one per study)
2037 - import the Python GUI module
2038 - call Python module's setSettings() method (obsolete function,
2039 used for compatibility with old code)
2041 \param study parent study
2043 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2045 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2048 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2052 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2055 myLastActivateStatus = false;
2059 // import Python GUI module
2061 if ( !myPyModule ) {
2062 myLastActivateStatus = false;
2066 // call Python module's setWorkSpace() method (obsolete)
2070 PyLockWrapper aLock; // Acquire GIL
2072 if ( IsCallOldMethods ) {
2073 // call Python module's setSettings() method (obsolete)
2074 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2075 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2079 myLastActivateStatus = myLastActivateStatus && true;
2085 \brief Deactivation callback function
2088 Performs the following actions:
2089 - call Python module's deactivate() method
2091 \param study parent study
2093 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2095 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2097 // check that Python subinterpreter is initialized and Python module is imported
2098 if ( !myInterp || !myPyModule ) {
2099 // Error! Python subinterpreter should be initialized and module should be imported first!
2102 // then call Python module's deactivate() method
2103 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2104 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2112 \brief Internal closure:
2114 Performs the following actions:
2115 - call Python module's closeStudy() method
2117 \param theStudy parent study object
2119 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2121 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2125 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2129 // check that Python subinterpreter is initialized and Python module is imported
2130 if ( !myInterp || !myPyModule ) {
2131 // Error! Python subinterpreter should be initialized and module should be imported first!
2134 // then call Python module's deactivate() method
2135 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2136 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i" ) );
2146 \brief Preference changing callback function.
2149 Performs the following actions:
2150 - call Python module's preferenceChanged() method
2152 \param section resources section name
2153 \param setting resources parameter name
2155 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2157 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2159 // check that Python subinterpreter is initialized and Python module is imported
2160 if ( !myInterp || !myPyModule ) {
2161 // Error! Python subinterpreter should be initialized and module should be imported first!
2165 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2166 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2167 (char*)"preferenceChanged",
2169 section.toLatin1().constData(),
2170 setting.toLatin1().constData() ) );
2178 \brief Active study change callback function.
2181 Called when active the study is actived (user brings its
2183 - initialize or get the Python interpreter (one per study)
2184 - import the Python GUI module
2185 - call Python module's activeStudyChanged() method
2187 \param study study being activated
2189 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2191 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2194 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2198 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2203 // import Python GUI module
2208 // call Python module's setWorkSpace() method
2212 PyLockWrapper aLock; // Acquire GIL
2214 // call Python module's activeStudyChanged() method
2215 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2216 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i" ) );
2224 \brief GUI event handling callback function
2227 Performs the following actions:
2228 - calls Python module's OnGUIEvent() method
2230 \param id GUI action ID
2232 void PyModuleHelper::internalActionActivated( int id )
2234 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2235 fmsg.message( QString( "action id = %1" ).arg( id ) );
2237 // Python interpreter should be initialized and Python module should be
2239 if ( !myInterp || !myPyModule )
2242 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2243 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2251 \brief update selection from other views or modules
2254 Performs the following actions:
2255 - calls Python module's onSelectionpdated(entries) method
2257 \param list of entries
2259 void PyModuleHelper::internalSelectionUpdated(const QStringList& entries)
2261 FuncMsg fmsg("--- PyModuleHelper::internalSelectionUpdated()");
2262 MESSAGE("internalSelectionUpdated");
2264 // Python interpreter should be initialized and Python module should be imported first
2265 if (!myInterp || !myPyModule)
2268 QStringList* theList = new QStringList(entries);
2270 #if SIP_VERSION < 0x040800
2271 PyObjWrapper sipList(sipBuildResult(0, "M", theList, sipClass_QStringList));
2273 #if SIP_VERSION >= 0x041303
2274 static const sipTypeDef *sipType_QStringList = 0;
2275 if (!sipType_QStringList)
2276 sipType_QStringList = sipFindType("QStringList");
2278 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2280 if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
2282 MESSAGE("call onSelectionUpdated");
2283 PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
2293 \brief Context popup menu handling callback function
2296 Performs the following actions:
2297 - calls Python module's definePopup(...) method (obsolete function,
2298 used for compatibility with old code) to define the popup menu context
2299 - parses XML resourses file (if exists) and fills the popup menu with the items)
2300 - calls Python module's customPopup(...) method (obsolete function,
2301 used for compatibility with old code) to allow module to customize the popup menu
2302 - for new modules calls createPopupMenu() function to allow the
2303 modules to build the popup menu by using insertItem(...) Qt functions.
2305 \param context popup menu context
2306 \param menu popup menu
2308 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2310 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2311 fmsg.message( QString( "context: %1" ).arg( context ) );
2313 // Python interpreter should be initialized and Python module should be
2315 if ( !myInterp || !myPyModule )
2318 QString aContext( "" ), aObject( "" ), aParent( context );
2320 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2321 // call definePopup() Python module's function
2322 // this is obsolete function, used only for compatibility reasons
2323 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2324 (char*)"definePopup",
2326 context.toLatin1().constData(),
2327 aObject.toLatin1().constData(),
2328 aParent.toLatin1().constData() ) );
2333 // parse return value
2335 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2341 } // if ( IsCallOldMethods ... )
2343 // first try to create menu via XML parser:
2344 // we create popup menus without help of QtxPopupMgr
2346 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2348 #if SIP_VERSION < 0x040800
2349 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2351 #if SIP_VERSION >= 0x041303
2352 static const sipTypeDef *sipType_QMenu = 0;
2354 sipType_QMenu = sipFindType("QMenu");
2356 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2359 // then call Python module's createPopupMenu() method (for new modules)
2360 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2361 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2362 (char*)"createPopupMenu",
2365 context.toLatin1().constData() ) );
2371 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2372 // call customPopup() Python module's function
2373 // this is obsolete function, used only for compatibility reasons
2374 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2375 (char*)"customPopup",
2378 aContext.toLatin1().constData(),
2379 aObject.toLatin1().constData(),
2380 aParent.toLatin1().constData() ) );
2388 \brief Preferences initialization callback function.
2391 Performs the following actions:
2392 - calls Python module's createPreferences() method
2394 void PyModuleHelper::internalCreatePreferences()
2396 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2398 // Python interpreter should be initialized and Python module should be
2400 if ( !myInterp || !myPyModule )
2403 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2404 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2412 \brief Active view changing callback function
2414 \param view view being activated
2416 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2418 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2420 if ( !myInterp || !myPyModule || !view )
2423 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2425 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2426 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2434 \brief View closing callback function
2436 \param view view user tries to close
2438 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2440 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2442 if ( !myInterp || !myPyModule || !view )
2445 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2447 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2449 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2458 \brief View closing callback function
2460 \param view view being closed
2462 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2464 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2466 if ( !myInterp || !myPyModule || !view )
2469 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2471 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2473 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2482 \brief View cloning callback function
2484 \param view view being cloned
2486 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2488 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2490 if ( !myInterp || !myPyModule || !view )
2493 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2495 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2497 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2504 \brief Module data saving callback function.
2506 \param files output list of files where module stores data
2507 \param url study URL
2509 void PyModuleHelper::internalSave( QStringList& files, const QString& url )
2511 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2513 // Python interpreter should be initialized and Python module should be
2515 // files list should contain a path to the temporary directory as a first entry
2516 if ( !myInterp || !myPyModule || files.isEmpty() )
2519 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2521 // try with two parameters (new syntax)
2522 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2524 files.first().toLatin1().constData(),
2525 url.toLatin1().constData() ) );
2527 // try with single parameter (old syntax)
2528 res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2529 (char*)"s", files.first().toLatin1().constData() );
2535 // parse the return value
2536 // result can be one string...
2537 if ( PyUnicode_Check( res ) ) {
2538 QString astr = PyUnicode_AsUTF8( res );
2539 files.append( astr );
2541 //also result can be a list...
2542 else if ( PyList_Check( res ) ) {
2543 int size = PyList_Size( res );
2544 for ( int i = 0; i < size; i++ ) {
2545 PyObject* value = PyList_GetItem( res, i );
2546 if ( value && PyUnicode_Check( value ) ) {
2547 files.append( PyUnicode_AsUTF8( value ) );
2556 \brief Module data loading callback function.
2558 \param files list of files where module data is stored
2559 \param url study URL
2560 \param opened output success flag
2562 void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
2564 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2566 // Python interpreter should be initialized and Python module should be
2568 if ( !myInterp || !myPyModule || files.isEmpty() )
2571 QStringList* theList = new QStringList( files );
2573 #if SIP_VERSION < 0x040800
2574 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2576 #if SIP_VERSION >= 0x041303
2577 static const sipTypeDef *sipType_QStringList = 0;
2578 if (!sipType_QStringList)
2579 sipType_QStringList = sipFindType("QStringList");
2581 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2583 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2585 // try with two parameters (new syntax)
2586 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2587 (char*)"Os", sipList.get(),
2588 url.toLatin1().constData() ) );
2591 // try with single parameter (old syntax)
2592 res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
2593 (char*)"O", sipList.get() );
2595 if ( !res || !PyBool_Check( res ) ) {
2600 opened = PyObject_IsTrue( res );
2606 \brief Module dump python callback function.
2608 \param files output list of files where module stores python script
2610 void PyModuleHelper::internalDumpPython( QStringList& files )
2612 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2614 // Python interpreter should be initialized and Python module should be
2616 // files list should contain a path to the temporary directory
2617 if ( !myInterp || !myPyModule || files.isEmpty() )
2620 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2621 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2622 (char*)"s", files.first().toLatin1().constData()));
2628 // parse the return value
2629 // result can be one string...
2630 if ( PyUnicode_Check( res ) ) {
2631 QString astr = PyUnicode_AsUTF8( res );
2635 //also result can be a list...
2636 else if ( PyList_Check( res ) ) {
2637 int size = PyList_Size( res );
2638 for ( int i = 0; i < size; i++ ) {
2639 PyObject* value = PyList_GetItem( res, i );
2640 if( value && PyUnicode_Check( value ) ) {
2641 files.append( PyUnicode_AsUTF8( value ) );
2650 \brief Check data object's 'draggable' status callback function.
2652 \param what data object being tested
2653 \return \c true if object can be dragged or \c false otherwise
2655 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2657 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2659 // Python interpreter should be initialized and Python module should be
2661 if ( !myInterp || !myPyModule || !what )
2664 bool draggable = false;
2666 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2667 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2668 (char*)"s", what->entry().toLatin1().constData() ) );
2669 if( !res || !PyBool_Check( res )) {
2674 draggable = PyObject_IsTrue( res );
2682 \brief Check data object's 'drop allowed' status callback function.
2684 \param where data object being tested
2685 \return \c true if if drop operation is supported by object or \c false otherwise
2687 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2689 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2691 // Python interpreter should be initialized and Python module should be
2693 if ( !myInterp || !myPyModule || !where )
2696 bool dropAccepted = false;
2698 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2699 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2700 (char*)"s", where->entry().toLatin1().constData() ) );
2701 if( !res || !PyBool_Check( res )) {
2703 dropAccepted = false;
2706 dropAccepted = PyObject_IsTrue( res );
2710 return dropAccepted;
2714 \brief Data dropping callback function.
2716 \param what list of data objects being dropped
2717 \param where target data object for drop operation
2718 \param row line (child item index) where drop operation is performed to
2719 \param action current drop action (copy or move)
2721 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2722 const int row, Qt::DropAction action )
2724 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2726 // Python interpreter should be initialized and Python module should be
2728 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2731 QStringList* theList = new QStringList();
2733 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2734 if ( !whereObject ) return;
2736 for ( int i = 0; i < what.count(); i++ ) {
2737 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2738 if ( dataObject ) theList->append( dataObject->entry() );
2741 #if SIP_VERSION < 0x040800
2742 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2744 #if SIP_VERSION >= 0x041303
2745 static const sipTypeDef *sipType_QStringList = 0;
2746 if (!sipType_QStringList)
2747 sipType_QStringList = sipFindType("QStringList");
2749 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2751 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2752 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2754 whereObject->entry().toLatin1().constData(),
2764 \brief Get engine IOR callback function
2767 Tries to get engine IOR from the Python module using engineIOR() function.
2768 That function can load module engine using appropriate container if required.
2770 \return engine IOR or empty string if it is not provided by Python module
2772 QString PyModuleHelper::internalEngineIOR() const
2774 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2778 // Python interpreter should be initialized and Python module should be
2780 if ( myInterp && myModule ) {
2781 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2782 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2787 // parse the return value, result chould be string
2788 if ( PyUnicode_Check( res ) ) {
2789 ior = PyUnicode_AsUTF8( res );
2798 \brief Connects signals about activating and cloning view on internal slots
2799 \param view view being connected
2801 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2803 SUIT_ViewManager* viewMgr = view->getViewManager();
2804 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2806 // Connect tryCloseView() and deleteView() signals
2808 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2809 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2810 Qt::UniqueConnection );
2811 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2812 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2813 Qt::UniqueConnection );
2816 // Connect cloneView() signal of an OCC View
2817 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2818 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2819 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2820 Qt::UniqueConnection );
2822 // Connect cloneView() signal of Plot2d View
2823 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2824 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2825 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2826 Qt::UniqueConnection );
2832 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2834 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2836 // Python interpreter should be initialized and Python module should be
2838 if ( !myInterp || !myPyModule )
2841 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2842 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2851 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2853 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2855 // temporary set myInitModule because dumpPython() method
2856 // might be called by the framework when this module is inactive,
2857 // but still it should be possible to access this module's data
2859 InitLocker lock( myModule );
2861 class PythonReq: public PyInterp_LockRequest
2864 PythonReq( PyInterp_Interp* _py_interp,
2865 PyModuleHelper* _helper,
2866 const QString& _entry,
2868 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2869 myHelper( _helper ) ,
2874 virtual void execute()
2876 myHelper->internalOBClickedPython( myEntry, myColumn );
2879 PyModuleHelper* myHelper;
2884 // Posting the request only if dispatcher is not busy!
2885 // Executing the request synchronously
2886 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2887 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2888 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );