1 // Copyright (C) 2007-2014 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
70 \brief Allow calling obsolete callback methods.
73 If the macro CALL_OLD_METHODS is not defined, the invoking
74 of obsolete Python module's methods like setSetting(), definePopup(),
77 CALL_OLD_METHODS macro can be defined, for example, by adding
78 -DCALL_OLD_METHODS compilation option to the CMakeLists.txt.
80 #ifdef CALL_OLD_METHODS
81 const bool IsCallOldMethods = true;
83 const bool IsCallOldMethods = false;
87 \brief Get tag name for the DOM element.
89 \param element DOM element
90 \return tag name or empty string if the element does not have tag name
92 static QString tagName( const QDomElement& element )
94 return element.tagName().trimmed();
98 \brief Get value of DOM element's attribute.
100 \param element DOM element
101 \param attName attribute name
102 \return attribute value or empty string if the element does not have such attribute
104 static QString attribute( const QDomElement& element, const QString& attName )
106 return element.attribute( attName ).trimmed();
110 \brief Inspect specified string for the boolean value.
113 This function returns \c true if string represents boolean value:
114 - "true", "yes" or "1" for \c true
115 - "false", "no" or "0" for \c false
116 Second parameter allows to specify what boolean value is expected:
119 - other value is not taken into account (return represented value)
121 \param value inspected string
122 \param check expected boolean value
123 \return boolean value represented by the string (\a check is not 1 or 0)
124 or \c true if value correspond to the specified \a check
126 static bool checkBool( const QString& value, const int check = -1 )
128 QString v = value.toLower();
129 if ( ( v == "true" || v == "yes" || v == "1" ) && ( check != 0 ) )
131 if ( ( v == "false" || v == "no" || v == "0" ) && ( check == 0 ) )
137 \brief Inspect specified string for the integer value.
140 This function returns returns -1 if item is empty or represents
142 \param value inspected string
143 \param def default value
144 \param shift shift value (it is added to the integer value to produce shifted result)
146 static int checkInt( const QString& value, const int def = -1, const int shift = -1 )
149 int val = value.toInt( &bOk );
150 if ( !bOk ) val = def;
151 if ( shift > 0 && bOk && val < 0 )
158 \brief Function call in/out tracer.
165 FuncMsg( const QString& funcName )
168 MESSAGE( qPrintable( myName ) << " [ begin ]" );
172 MESSAGE( qPrintable( myName ) << " [ end ]" );
174 void message( const QString& msg )
176 MESSAGE( qPrintable( myName ) << " : " << qPrintable( msg ) );
183 \class PyModuleHelper::InitLocker
184 \brief Initialization locker
188 class PyModuleHelper::InitLocker
191 InitLocker( LightApp_Module* );
199 PyModuleHelper::InitLocker::InitLocker( LightApp_Module* module )
201 QMutexLocker ml( &myInitMutex );
202 myInitModule = module;
209 PyModuleHelper::InitLocker::~InitLocker()
211 QMutexLocker ml( &myInitMutex );
216 \class PyModuleHelper::XmlHandler
217 \brief XML resource files parser.
220 This class is used to provide backward compatibility with
221 existing Python modules in which obsolete menu definition system
222 (via XML files) is used.
225 class PyModuleHelper::XmlHandler
228 XmlHandler( PyModuleHelper* helper, const QString& fileName );
229 void createActions();
230 void createPopup( QMenu* menu,
231 const QString& context,
232 const QString& parent,
233 const QString& object );
234 void activateMenus( bool );
237 LightApp_Module* module() const;
238 QIcon loadIcon( const QString& fileName );
240 void createMenu( QDomNode& parentNode,
241 const int parentMenuId = -1,
242 QMenu* parentPopup = 0 );
243 void createToolBar( QDomNode& parentNode );
244 void insertPopupItems( QDomNode& parentNode,
248 PyModuleHelper* myHelper;
250 QList<int> myMenuItems;
257 \param module pointer to the GUI module
258 \param fileName path to the XML menu description file
260 PyModuleHelper::XmlHandler::XmlHandler( PyModuleHelper* helper,
261 const QString& fileName )
264 if ( !fileName.isEmpty() ) {
265 QFile aFile( fileName );
266 if ( aFile.open( QIODevice::ReadOnly ) ) {
267 myDoc.setContent( &aFile );
273 \brief Parse XML file and create actions.
276 Called by PyModuleHelper::initialize() in order to create actions
279 void PyModuleHelper::XmlHandler::createActions()
281 // get document element
282 QDomElement aDocElem = myDoc.documentElement();
284 // create main menu actions
285 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
286 for ( int i = 0; i < aMenuList.count(); i++ ) {
287 QDomNode n = aMenuList.item( i );
291 // create toolbars actions
292 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
293 for ( int i = 0; i < aToolsList.count(); i++ ) {
294 QDomNode n = aToolsList.item( i );
300 \brief Create popup menu.
302 \param menu popup menu
303 \param context popup menu context
304 \param context popup menu parent object name
305 \param context popup menu object name
307 void PyModuleHelper::XmlHandler::createPopup( QMenu* menu,
308 const QString& context,
309 const QString& parent,
310 const QString& object )
312 // get document element
313 QDomElement aDocElem = myDoc.documentElement();
315 // get popup menus actions
316 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
317 for ( int i = 0; i < aPopupList.count(); i++ ) {
318 QDomNode n = aPopupList.item( i );
319 if ( !n.isNull() && n.isElement() ) {
320 QDomElement e = n.toElement();
321 // QString lab = attribute( e, "label-id" ); // not used //
322 QString ctx = attribute( e, "context-id" );
323 QString prt = attribute( e, "parent-id" );
324 QString obj = attribute( e, "object-id" );
325 if ( ctx == context && prt == parent && obj == object ) {
326 insertPopupItems( n, menu );
334 \brief Activate/deactivate menus
336 \param enable if \c true menus are activated, otherwise menus are deactivated
338 void PyModuleHelper::XmlHandler::activateMenus( bool enable )
341 QtxActionMenuMgr* mgr = module()->menuMgr();
342 foreach( int id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
347 \brief Get owner module
349 LightApp_Module* PyModuleHelper::XmlHandler::module() const
351 return myHelper->module();
355 \brief Load an icon from the module resources by the specified file name.
356 \param fileName icon file name
360 QIcon PyModuleHelper::XmlHandler::loadIcon( const QString& fileName )
364 if ( module() && !fileName.isEmpty() ) {
365 SUIT_ResourceMgr* resMgr = module()->getApp()->resourceMgr();
366 QPixmap pixmap = resMgr->loadPixmap( module()->name(),
367 QApplication::translate( module()->name().toLatin1().data(),
368 fileName.toLatin1().data() ) );
369 if ( !pixmap.isNull() )
370 icon = QIcon( pixmap );
377 \brief Create main menu item and insert actions to it.
379 \param parentNode XML node with menu description
380 \param parentMenuId parent menu ID (-1 for top-level menu)
381 \param parentPopup parent popup menu (0 for top-level menu)
383 void PyModuleHelper::XmlHandler::createMenu( QDomNode& parentNode,
384 const int parentMenuId,
387 if ( !module() || parentNode.isNull() )
390 QDomElement parentElement = parentNode.toElement();
391 if ( !parentElement.isNull() ) {
392 QString plabel = attribute( parentElement, "label-id" );
393 int pid = checkInt( attribute( parentElement, "item-id" ) );
394 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
395 int group = checkInt( attribute( parentElement, "group-id" ),
396 PyModuleHelper::defaultMenuGroup() );
397 if ( !plabel.isEmpty() ) {
401 menuId = module()->createMenu( plabel, // label
402 parentMenuId, // parent menu ID, -1 for top-level menu
406 myMenuItems.append( menuId );
407 QDomNode node = parentNode.firstChild();
408 while ( !node.isNull() ) {
409 if ( node.isElement() ) {
410 QDomElement elem = node.toElement();
411 QString aTagName = tagName( elem );
412 if ( aTagName == "popup-item" ) {
413 int id = checkInt( attribute( elem, "item-id" ) );
414 int pos = checkInt( attribute( elem, "pos-id" ) );
415 int group = checkInt( attribute( elem, "group-id" ),
416 PyModuleHelper::defaultMenuGroup() );
417 QString label = attribute( elem, "label-id" );
418 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
419 QString tooltip = attribute( elem, "tooltip-id" );
420 QString accel = attribute( elem, "accel-id" );
421 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
423 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
424 // also check if the action with given ID is already created
426 // create menu action
427 QAction* action = module()->createAction( id, // ID
431 tooltip, // status-bar text
432 QKeySequence( accel ), // keyboard accelerator
433 module(), // action owner
434 toggle ); // toogled action
435 myHelper->connectAction( action );
436 module()->createMenu( action, // action
437 menuId, // parent menu ID
438 id, // ID (same as for createAction())
443 else if ( aTagName == "submenu" ) {
445 createMenu( node, menuId, popup );
447 else if ( aTagName == "separator" ) {
448 // create menu separator
449 int id = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
450 int pos = checkInt( attribute( elem, "pos-id" ) );
451 int group = checkInt( attribute( elem, "group-id" ),
452 PyModuleHelper::defaultMenuGroup() );
453 QAction* action = module()->separator();
454 module()->createMenu( action, // separator action
455 menuId, // parent menu ID
461 node = node.nextSibling();
468 \brief Create a toolbar and insert actions to it.
469 \param parentNode XML node with toolbar description
471 void PyModuleHelper::XmlHandler::createToolBar( QDomNode& parentNode )
473 if ( !module() || parentNode.isNull() )
476 QDomElement parentElement = parentNode.toElement();
477 if ( !parentElement.isNull() ) {
478 QString aLabel = attribute( parentElement, "label-id" );
479 QString aName = attribute( parentElement, "name-id" );
480 if ( !aLabel.isEmpty() ) {
482 int tbId = module()->createTool( aLabel, aName );
483 QDomNode node = parentNode.firstChild();
484 while ( !node.isNull() ) {
485 if ( node.isElement() ) {
486 QDomElement elem = node.toElement();
487 QString aTagName = tagName( elem );
488 if ( aTagName == "toolbutton-item" ) {
489 int id = checkInt( attribute( elem, "item-id" ) );
490 int pos = checkInt( attribute( elem, "pos-id" ) );
491 QString label = attribute( elem, "label-id" );
492 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
493 QString tooltip = attribute( elem, "tooltip-id" );
494 QString accel = attribute( elem, "accel-id" );
495 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
497 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
498 // also check if the action with given ID is already created
500 // create toolbar action
501 QAction* action = module()->createAction( id, // ID
505 tooltip, // status-bar text
506 QKeySequence( accel ), // keyboard accelerator
507 module(), // action owner
508 toggle ); // toogled action
509 myHelper->connectAction( action );
510 module()->createTool( action, tbId, -1, pos );
513 else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
514 // create toolbar separator
515 int pos = checkInt( attribute( elem, "pos-id" ) );
516 QAction* action = module()->separator();
517 module()->createTool( action, tbId, -1, pos );
520 node = node.nextSibling();
527 \brief Fill popup menu with the items.
528 \param parentNode XML node with popup menu description
529 \param menu popup menu
531 void PyModuleHelper::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
533 if ( !module() && parentNode.isNull() )
536 // we create popup menus without help of QtxPopupMgr
537 QDomNode node = parentNode.firstChild();
538 while ( !node.isNull() ) {
539 if ( node.isElement() ) {
540 QDomElement elem = node.toElement();
541 QString aTagName = tagName( elem );
542 QList<QAction*> actions = menu->actions();
543 if ( aTagName == "popup-item" ) {
544 // insert a command item
545 int id = checkInt( attribute( elem, "item-id" ) );
546 int pos = checkInt( attribute( elem, "pos-id" ) );
547 QString label = attribute( elem, "label-id" );
548 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
549 QString tooltip = attribute( elem, "tooltip-id" );
550 QString accel = attribute( elem, "accel-id" );
551 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
553 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
554 // also check if the action with given ID is already created
556 QAction* action = module()->createAction( id, // ID
560 tooltip, // status-bar text
561 QKeySequence( accel ), // keyboard accelerator
562 module(), // action owner
563 toggle ); // toogled action
564 myHelper->connectAction( action );
565 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
566 menu->insertAction( before, action );
569 else if ( aTagName == "submenu" ) {
571 ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
572 int pos = checkInt( attribute( elem, "pos-id" ) );
573 QString label = attribute( elem, "label-id" );
574 QString icon = attribute( elem, "icon-id" );
577 if ( !icon.isEmpty() ) {
578 QPixmap pixmap = module()->getApp()->resourceMgr()->loadPixmap( module()->name(), icon );
579 if ( !pixmap.isNull() )
580 anIcon = QIcon( pixmap );
583 QMenu* newPopup = menu->addMenu( anIcon, label );
584 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
585 menu->insertMenu( before, newPopup );
586 insertPopupItems( node, newPopup );
588 else if ( aTagName == "separator" ) {
589 // create menu separator
590 int pos = checkInt( attribute( elem, "pos-id" ) );
591 QAction* action = module()->separator();
592 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
593 menu->insertAction( before, action );
596 node = node.nextSibling();
601 \class PyModuleHelper
602 \brief This class implements API helper for all the Python-based
606 PyModuleHelper::InterpMap PyModuleHelper::myInterpMap;
607 LightApp_Module* PyModuleHelper::myInitModule = 0;
611 \param module owner module
613 PyModuleHelper::PyModuleHelper( LightApp_Module* module ) :
619 myLastActivateStatus( true )
621 setObjectName( "python_module_helper" );
627 PyModuleHelper::~PyModuleHelper()
630 if ( myInterp && myPyModule ) {
631 PyLockWrapper aLock; // Acquire GIL
632 Py_XDECREF( myPyModule );
637 \brief Get the module being initialized.
639 This is a little trick :) needed to provide an access from Python
640 (SalomePyQt) to the module being currently activated. The problem
641 that during the process of module initialization (initialize()
642 function) it is not yet available via application->activeModule()
645 This method returns valid pointer only if called in scope of
646 initialize() function or in several other specific cases.
648 \return the module being currently initialized
650 LightApp_Module* PyModuleHelper::getInitModule()
652 QMutexLocker ml( &myInitMutex );
657 \brief Get default menu group identifier
658 \return menu group ID (40 by default)
660 int PyModuleHelper::defaultMenuGroup()
662 return DEFAULT_GROUP;
666 \brief Get owner module
669 LightApp_Module* PyModuleHelper::module() const
675 \brief Get Python GUI module object
676 \return python module
678 PyObject* PyModuleHelper::pythonModule() const
684 \brief Connect action to the internal actionActivated() slot.
686 Actions connected to internal actionActivated(), when activated, will
687 be forwarded to the Python GUI module OnGUIEvent() function.
689 \param a action being connected
691 void PyModuleHelper::connectAction( QAction* a )
694 QObject::connect( a, SIGNAL( triggered( bool ) ),
695 this, SLOT( actionActivated() ),
696 Qt::UniqueConnection );
700 \brief Get the dockable windows associated with the module.
702 To fill the list of windows the correspondind Python module's windows()
703 method is called during the module initialization.
705 By default, ObjectBrowser, PythonConsole and LogWindow windows are
706 associated to the module.
708 Allowed dockable windows:
709 - LightApp_Application::WT_ObjectBrowser : object browser
710 - LightApp_Application::WT_PyConsole : python console
711 - LightApp_Application::WT_LogWindow : log messages output window
713 Dock area is defined by Qt::DockWidgetArea enumeration:
714 - Qt::TopDockWidgetArea : top dock area
715 - Qt::BottomDockWidgetArea : bottom dock area
716 - Qt::LeftDockWidgetArea : left dock area
717 - Qt::RightDockWidgetArea : right dock area
719 \return map of dockable windows in form { <window_type> : <dock_area> }
721 QMap<int, int> PyModuleHelper::windows() const
723 FuncMsg fmsg( "PyModuleHelper::windows()" );
729 \brief Define the compatible view windows associated with the module.
731 The associated view windows are opened automatically when the module
734 To fill the list of views the correspondind Python module's views()
735 method is called during the module initialization.
736 By default, the list of view types is empty.
738 \return list of view windows types
740 QStringList PyModuleHelper::viewManagers() const
742 FuncMsg fmsg( "PyModuleHelper::viewManagers()" );
744 return myViewMgrList;
748 \brief Initialization of the Python-based SALOME module.
750 This method can be used for creation of the menus, toolbars and
753 There are two ways to do this:
754 1) for obsolete modules, the implementation of this method first tries to read
755 the <module>_<language>.xml resource file which contains a menu,
756 toolbars and popup menus description;
757 2) new modules can create menus by direct calling of the
758 corresponding methods of SalomePyQt Python API in the Python
759 module's initialize() method which is called from here.
761 \note SALOME supports two modes of modules loading:
762 - immediate (all the modules are created and initialized
763 immediately when the application object is created);
764 - postponed modules loading (used currently); in this mode
765 the module is loaded only by explicit request.
766 If postponed modules loading is not used, the active
767 study might be not yet defined at this stage, so initialize()
768 method should not perform any study-based initialization.
769 Such actions can be better done in activate() function.
771 \param app parent application object
773 void PyModuleHelper::initialize( CAM_Application* app )
775 FuncMsg fmsg( "PyModuleHelper::initialize()" );
777 // temporarily store module being currently activated
778 // in the global variable to make it accessible from
780 InitLocker lock( myModule );
782 // try to get XML resource file name
783 SUIT_ResourceMgr* resMgr = myModule->getApp()->resourceMgr();
784 if ( !myXmlHandler && resMgr ) {
785 // get current language
786 QString lang = resMgr->stringValue( "language", "language", "en" );
787 // get menu description file name
788 QString aFileName = QString( "%1_%2.xml" ).arg( myModule->name() ).arg( lang );
789 aFileName = resMgr->path( "resources", myModule->name(), aFileName );
790 if ( !aFileName.isEmpty() && QFile::exists( aFileName ) ) {
791 // create XML handler instance
792 myXmlHandler = new XmlHandler( this, aFileName );
793 // ask XML handler to create actions
794 myXmlHandler->createActions();
798 class InitializeReq : public PyInterp_Request
801 InitializeReq( PyModuleHelper* _helper,
802 CAM_Application* _app )
803 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
808 virtual void execute()
810 myHelper->internalInitialize( myApp );
813 PyModuleHelper* myHelper;
814 CAM_Application* myApp;
818 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( this, app ) );
822 \brief Activation of the module.
824 This function is usually used in order to show the module's
825 specific menus and toolbars, update actions state and perform
826 other such actions required when the module is activated.
828 \note Returning \c false from this function prevents the
831 \param study parent study
832 \return \c true if activation is successful and \c false otherwise
834 bool PyModuleHelper::activate( SUIT_Study* study )
836 FuncMsg fmsg( "PyModuleHelper::activate()" );
838 // reset the activation status to the default value
839 myLastActivateStatus = true;
841 class ActivateReq : public PyInterp_Request
844 ActivateReq( PyModuleHelper* _helper,
847 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
850 myCustomize( _customize )
853 virtual void execute()
856 myHelper->internalActivate( myStudy ); // first activation stage
858 myHelper->internalCustomize( myStudy ); // second activation stage
861 PyModuleHelper* myHelper;
866 // post request for activation (customize=false)
867 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, false ) );
869 // check activation status (can be set to false by internalActivate())
870 if ( myLastActivateStatus ) {
871 // activate menus, toolbars, etc
872 if ( myXmlHandler ) myXmlHandler->activateMenus( true );
874 // show menus / toolbars
875 myModule->setMenuShown( true );
876 myModule->setToolShown( true );
878 // post request for customization (customize=true)
879 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, true ) );
881 // check activation status (can be set to false by internalCustomize())
882 if ( myLastActivateStatus ) {
883 // connect preferences changing signal
884 connect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
885 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
887 // connect active view change signal
888 SUIT_Desktop* d = study->application()->desktop();
889 connect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
890 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
891 // if active window exists, call activeViewChanged() function;
892 // temporary solution: if a getActiveView() in SalomePyQt available
893 // we no longer need this
894 SUIT_ViewWindow* view = d->activeWindow();
895 if ( view ) activeViewChanged( view );
896 // get all view currently opened in the study and connect their signals to
897 // the corresponding slots of the class.
898 foreach ( view, d->windows() ) connectView( view );
901 // hide menus / toolbars in case of error
902 myModule->setMenuShown( false );
903 myModule->setToolShown( false );
907 return myLastActivateStatus;
911 \brief Deactivation of the module.
913 This function is usually used in order to hide the module's
914 specific menus and toolbars and perform other such actions
915 required when the module is deactivated.
917 \param study parent study
918 \return \c true if deactivation is successful and \c false otherwise
920 bool PyModuleHelper::deactivate( SUIT_Study* study )
922 FuncMsg fmsg( "PyModuleHelper::deactivate()" );
924 class DeactivateReq : public PyInterp_LockRequest
927 DeactivateReq( PyInterp_Interp* _py_interp,
928 PyModuleHelper* _helper,
930 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
935 virtual void execute()
937 myHelper->internalDeactivate( myStudy );
940 PyModuleHelper* myHelper;
945 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, this, study ) );
947 // disconnect preferences changing signal
948 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
949 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
951 // disconnect the SUIT_Desktop signal windowActivated()
952 SUIT_Desktop* d = study->application()->desktop();
953 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
954 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
956 // deactivate menus, toolbars, etc
957 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
959 // hide menus / toolbars
960 myModule->setMenuShown( false );
961 myModule->setToolShown( false );
967 \brief Close of the module.
969 This function is usually used in order to close the module's
970 specific menus and toolbars and perform other such actions
971 required when the module is closed.
973 void PyModuleHelper::modelClosed( SUIT_Study* study )
975 FuncMsg fmsg( "PyModuleHelper::modelClosed()" );
977 class StudyClosedReq : public PyInterp_LockRequest
980 StudyClosedReq( PyInterp_Interp* _py_interp,
981 PyModuleHelper* _helper,
983 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
988 virtual void execute()
990 myHelper->internalClosedStudy( myStudy );
993 PyModuleHelper* myHelper;
998 PyInterp_Dispatcher::Get()->Exec( new StudyClosedReq( myInterp, this, study ) );
1000 // disconnect preferences changing signal
1001 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
1002 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
1004 // disconnect the SUIT_Desktop signal windowActivated()
1005 SUIT_Desktop* d = study->application()->desktop();
1006 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1007 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
1009 // deactivate menus, toolbars, etc
1010 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
1012 // hide menus / toolbars
1013 myModule->setMenuShown( false );
1014 myModule->setToolShown( false );
1019 \brief Process module's preferences changing.
1021 Called when the module's own preferences are changed.
1023 \param section preference resources section
1024 \param parameter preference resources parameter name
1026 void PyModuleHelper::preferencesChanged( const QString& section,
1027 const QString& parameter )
1029 FuncMsg fmsg( "PyModuleHelper::preferencesChanged()" );
1031 class PrefChangeReq : public PyInterp_LockRequest
1034 PrefChangeReq( PyInterp_Interp* _py_interp,
1035 PyModuleHelper* _helper,
1036 const QString& _section,
1037 const QString& _parameter )
1038 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1039 myHelper ( _helper ),
1040 mySection( _section ),
1041 myParameter( _parameter )
1044 virtual void execute()
1046 myHelper->internalPreferencesChanged( mySection, myParameter );
1049 PyModuleHelper* myHelper;
1050 QString mySection, myParameter;
1053 // post the request only if dispatcher is not busy!
1054 // execute request synchronously
1055 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1056 PyInterp_Dispatcher::Get()->Exec( new PrefChangeReq( myInterp, this, section, parameter ) );
1060 \brief Process application preferences changing.
1062 Called when any application setting is changed.
1064 \param module preference module
1065 \param section preference resources section
1066 \param parameter preference resources parameter name
1068 void PyModuleHelper::preferenceChanged( const QString& module,
1069 const QString& section,
1070 const QString& parameter )
1072 FuncMsg fmsg( "PyModuleHelper::preferenceChanged()" );
1074 // module's own preferences are processed by other preferencesChanged() method
1075 if ( module != myModule->moduleName() ) {
1077 preferencesChanged( section, parameter );
1082 \brief Process study activation.
1084 Called when study desktop is activated. Used for notifying the Python
1085 module about changing of the active study.
1087 \param study study being activated
1089 void PyModuleHelper::studyActivated( SUIT_Study* study )
1091 FuncMsg fmsg( "PyModuleHelper::studyActivated()" );
1093 // StudyChangedReq: request class for internal studyChanged() operation
1094 class StudyChangedReq : public PyInterp_Request
1097 StudyChangedReq( PyModuleHelper* _helper,
1098 SUIT_Study* _study )
1099 : PyInterp_Request(0, true ), // this request should be processed synchronously (sync == true)
1100 myHelper( _helper ),
1104 virtual void execute()
1106 myHelper->internalStudyChanged( myStudy );
1109 PyModuleHelper* myHelper;
1110 SUIT_Study* myStudy;
1114 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( this, study ) );
1118 \brief Process action activation.
1120 Called when action is activated. Used for notifying the Python
1121 module about any related action activation.
1125 void PyModuleHelper::actionActivated()
1127 FuncMsg fmsg( "PyModuleHelper::actionActivated()" );
1129 // perform synchronous request to Python event dispatcher
1130 class ActionReq : public PyInterp_LockRequest
1133 ActionReq( PyInterp_Interp* _py_interp,
1134 PyModuleHelper* _helper,
1136 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1137 myHelper( _helper ),
1141 virtual void execute()
1143 myHelper->internalActionActivated( myId );
1146 PyModuleHelper* myHelper;
1150 // get sender action
1151 QAction* action = qobject_cast<QAction*>( sender() );
1156 PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
1160 \brief Process context popup menu request.
1162 Called when user activates popup menu in some window
1163 (view, object browser, etc).
1165 \param context popup menu context (e.g. "ObjectBrowser")
1166 \param menu popup menu
1168 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1170 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1172 class ContextMenuReq : public PyInterp_LockRequest
1175 ContextMenuReq( PyInterp_Interp* _py_interp,
1176 PyModuleHelper* _helper,
1177 const QString& _context,
1179 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1180 myHelper ( _helper ),
1181 myContext( _context ),
1185 virtual void execute()
1187 myHelper->internalContextMenu( myContext, myMenu );
1190 PyModuleHelper* myHelper;
1195 // post request only if dispatcher is not busy!
1196 // execute request synchronously
1197 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1198 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1202 \brief Export preferences for the Python module.
1203 Called only once when the first instance of the module is created or
1204 when common Preferences dialog box is first time invoked.
1206 void PyModuleHelper::createPreferences()
1208 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1210 // temporary set myInitModule because createPreferences() method
1211 // might be called during the module intialization process
1212 InitLocker lock( myModule );
1214 class CreatePrefReq : public PyInterp_LockRequest
1217 CreatePrefReq( PyInterp_Interp* _py_interp,
1218 PyModuleHelper* _helper )
1219 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1223 virtual void execute()
1225 myHelper->internalCreatePreferences();
1228 PyModuleHelper* myHelper;
1231 // post request only if dispatcher is not busy!
1232 // execute request synchronously
1233 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1234 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1238 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1240 Used to notify Python module that active view has been changed by the user.
1242 \param view view being activated
1244 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1246 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1248 // perform synchronous request to Python event dispatcher
1249 class ActiveViewChangeReq : public PyInterp_LockRequest
1252 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1253 PyModuleHelper* _helper,
1254 SUIT_ViewWindow* _view )
1255 : PyInterp_LockRequest( _py_interp, 0, true ),
1256 myHelper( _helper ),
1260 virtual void execute()
1262 myHelper->internalActiveViewChanged( myView );
1265 PyModuleHelper* myHelper;
1266 SUIT_ViewWindow* myView;
1269 // connect view (if it is not connected yet)
1270 connectView( view );
1272 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1276 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1277 \param view view being closed
1279 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1281 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1283 class TryCloseViewReq : public PyInterp_LockRequest
1286 TryCloseViewReq( PyInterp_Interp* _py_interp,
1287 PyModuleHelper* _helper,
1288 SUIT_ViewWindow* _view )
1289 : PyInterp_LockRequest( _py_interp, 0, true ),
1290 myHelper( _helper ),
1294 virtual void execute()
1296 myHelper->internalTryCloseView( myView );
1299 PyModuleHelper* myHelper;
1300 SUIT_ViewWindow* myView;
1303 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1307 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1308 \param view view being closed
1310 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1312 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1314 class CloseViewReq : public PyInterp_LockRequest
1317 CloseViewReq( PyInterp_Interp* _py_interp,
1318 PyModuleHelper* _helper,
1319 SUIT_ViewWindow* _view )
1320 : PyInterp_LockRequest( _py_interp, 0, true ),
1321 myHelper( _helper ),
1325 virtual void execute()
1327 myHelper->internalCloseView( myView );
1330 PyModuleHelper* myHelper;
1331 SUIT_ViewWindow* myView;
1334 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1338 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1339 \param view view being cloned
1341 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1343 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1345 class CloneViewReq : public PyInterp_LockRequest
1348 CloneViewReq( PyInterp_Interp* _py_interp,
1349 PyModuleHelper* _helper,
1350 SUIT_ViewWindow* _view )
1351 : PyInterp_LockRequest( _py_interp, 0, true ),
1352 myHelper( _helper ),
1356 virtual void execute()
1358 myHelper->internalCloneView( myView );
1361 PyModuleHelper* myHelper;
1362 SUIT_ViewWindow* myView;
1365 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1369 \brief Save module data. Called when user saves study.
1370 \param files output list of files where module stores data
1372 void PyModuleHelper::save( QStringList& files )
1374 FuncMsg fmsg( "PyModuleHelper::save()" );
1376 // temporary set myInitModule because save() method
1377 // might be called by the framework when this module is inactive,
1378 // but still it should be possible to access this module's data
1380 InitLocker lock( myModule );
1382 // perform synchronous request to Python event dispatcher
1383 class SaveReq: public PyInterp_LockRequest
1386 SaveReq( PyInterp_Interp* _py_interp,
1387 PyModuleHelper* _helper,
1388 QStringList& _files )
1389 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1390 myHelper( _helper ) ,
1394 virtual void execute()
1396 myHelper->internalSave( myFiles );
1399 PyModuleHelper* myHelper;
1400 QStringList& myFiles;
1403 // Posting the request only if dispatcher is not busy!
1404 // Executing the request synchronously
1405 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1406 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files ) );
1410 \brief Load module data. Called when user opens study
1411 and activates module.
1412 \param files list of files where module data is stored
1414 bool PyModuleHelper::load( const QStringList& files )
1416 FuncMsg fmsg( "PyModuleHelper::load()" );
1418 bool loaded = false;
1420 class LoadReq: public PyInterp_LockRequest
1423 LoadReq( PyInterp_Interp* _py_interp,
1424 PyModuleHelper* _helper,
1427 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1428 myHelper( _helper ) ,
1433 virtual void execute()
1435 myHelper->internalLoad( myFiles, myLoaded );
1438 PyModuleHelper* myHelper;
1439 QStringList myFiles;
1443 // Posting the request only if dispatcher is not busy!
1444 // Executing the request synchronously
1445 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1446 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, loaded ) );
1452 \brief Dump module data to the Python script.
1453 Called when user activates dump study operation.
1454 \param files output list of files where module stores python script
1456 void PyModuleHelper::dumpPython( QStringList& files )
1458 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1460 // temporary set myInitModule because dumpPython() method
1461 // might be called by the framework when this module is inactive,
1462 // but still it should be possible to access this module's data
1464 InitLocker lock( myModule );
1466 class DumpPythonReq: public PyInterp_LockRequest
1469 DumpPythonReq( PyInterp_Interp* _py_interp,
1470 PyModuleHelper* _helper,
1471 QStringList& _files )
1472 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1473 myHelper( _helper ) ,
1477 virtual void execute()
1479 myHelper->internalDumpPython( myFiles );
1482 PyModuleHelper* myHelper;
1483 QStringList& myFiles;
1486 // Posting the request only if dispatcher is not busy!
1487 // Executing the request synchronously
1488 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1489 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1493 \brief Test if object \a what can be dragged by the user.
1494 \param what data object being tested
1495 \return \c true if object can be dragged or \c false otherwise
1497 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1499 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1501 bool draggable = false;
1503 // perform synchronous request to Python event dispatcher
1504 class IsDraggableReq: public PyInterp_LockRequest
1507 IsDraggableReq( PyInterp_Interp* _py_interp,
1508 PyModuleHelper* _helper,
1509 LightApp_DataObject* _data_object,
1510 bool& _is_draggable )
1511 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1512 myHelper( _helper ) ,
1513 myDataObject( _data_object ),
1514 myIsDraggable( _is_draggable )
1517 virtual void execute()
1519 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1522 PyModuleHelper* myHelper;
1523 LightApp_DataObject* myDataObject;
1524 bool& myIsDraggable;
1527 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1528 if ( data_object ) {
1529 // Posting the request only if dispatcher is not busy!
1530 // Executing the request synchronously
1531 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1532 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1533 const_cast<PyModuleHelper*>( this ),
1534 const_cast<LightApp_DataObject*>( data_object ),
1542 \brief Test if drop operation can be done on the \a where object.
1543 \param where data object being tested
1544 \return \c true if if drop operation is supported by object or \c false otherwise
1546 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1548 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1550 bool dropAccepted = false;
1552 // perform synchronous request to Python event dispatcher
1553 class IsDropAcceptedReq: public PyInterp_LockRequest
1556 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1557 PyModuleHelper* _helper,
1558 LightApp_DataObject* _data_object,
1559 bool& _is_drop_accepted )
1560 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1561 myHelper( _helper ) ,
1562 myDataObject( _data_object ),
1563 myIsDropAccepted( _is_drop_accepted )
1566 virtual void execute()
1568 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1571 PyModuleHelper* myHelper;
1572 LightApp_DataObject* myDataObject;
1573 bool& myIsDropAccepted;
1576 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1577 if ( data_object ) {
1578 // Posting the request only if dispatcher is not busy!
1579 // Executing the request synchronously
1580 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1581 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1582 const_cast<PyModuleHelper*>( this ),
1583 const_cast<LightApp_DataObject*>( data_object ),
1587 return dropAccepted;
1591 \brief Perform drop operation
1592 \param what list of data objects being dropped
1593 \param where target data object for drop operation
1594 \param row line (child item index) where drop operation is performed to
1595 \param action current drop action (copy or move)
1597 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1598 const int row, Qt::DropAction action )
1600 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1602 // perform synchronous request to Python event dispatcher
1603 class DropObjectsReq: public PyInterp_LockRequest
1606 DropObjectsReq( PyInterp_Interp* _py_interp,
1607 PyModuleHelper* _helper,
1608 const DataObjectList& _what,
1609 SUIT_DataObject* _where,
1611 Qt::DropAction _action )
1612 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1613 myHelper( _helper ) ,
1617 myAction ( _action )
1620 virtual void execute()
1622 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1625 PyModuleHelper* myHelper;
1626 DataObjectList myWhat;
1627 SUIT_DataObject* myWhere;
1629 Qt::DropAction myAction;
1632 // Posting the request only if dispatcher is not busy!
1633 // Executing the request synchronously
1634 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1635 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1639 \brief Get module engine IOR
1640 \return engine IOR as it is supplied by GUI Python module
1642 QString PyModuleHelper::engineIOR() const
1644 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1646 class EngineIORReq : public PyInterp_LockRequest
1649 EngineIORReq( PyInterp_Interp* _py_interp,
1650 PyModuleHelper* _helper,
1652 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1653 myHelper( _helper ),
1657 virtual void execute()
1659 myIOR = myHelper->internalEngineIOR();
1662 PyModuleHelper* myHelper;
1666 static QString anIOR;
1668 if ( anIOR.isEmpty() ) {
1670 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1671 const_cast<PyModuleHelper*>( this ),
1679 \brief Initialize python subinterpreter (one per study).
1681 \param studyId study ID
1683 void PyModuleHelper::initInterp( int studyId )
1685 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1689 // Error! Study Id must not be 0!
1694 QMutexLocker ml( &myInitMutex );
1696 // try to find the subinterpreter
1697 if ( myInterpMap.contains( studyId ) ) {
1699 myInterp = myInterpMap[ studyId ];
1703 myInterp = new SALOME_PYQT_PyInterp();
1704 myInterp->initialize();
1705 myInterpMap[ studyId ] = myInterp;
1707 #ifndef GUI_DISABLE_CORBA
1708 if ( !SUIT_PYTHON::initialized ) {
1709 // import 'salome' module and call 'salome_init' method;
1710 // do it only once on interpreter creation
1711 // ... first get python lock
1712 PyLockWrapper aLock; // Acquire GIL
1713 // ... then import a module
1714 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1720 // ... then call a method
1722 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
1733 \brief Import Python GUI module and store reference to the module.
1736 Warning! initInterp() should be called first!!!
1738 void PyModuleHelper::importModule()
1740 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1742 // check if the subinterpreter is initialized
1744 // Error! Python subinterpreter should be initialized first!
1749 // import Python GUI module and put it in <myPyModule> attribute
1750 // ... first get python lock
1751 PyLockWrapper aLock; // Acquire GIL
1752 // ... then import a module
1753 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1755 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1760 if ( !myPyModule ) {
1768 \brief Set study workspace to the Python module.
1771 Calls setWorkSpace() method of the Python module with
1772 PyQt QWidget object to use with interpreter.
1774 Attention! initInterp() and importModule() should be called first!!!
1776 void PyModuleHelper::setWorkSpace()
1778 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1780 if ( !IsCallOldMethods )
1783 // check if the subinterpreter is initialized and Python module is imported
1784 if ( !myInterp || !myPyModule ) {
1785 // Error! Python subinterpreter should be initialized and module should be imported first!
1789 // call setWorkSpace() method
1790 // ... first get python lock
1791 PyLockWrapper aLock; // Acquire GIL
1793 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1794 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1801 // ... then get workspace object
1802 QWidget* aWorkspace = 0;
1803 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1804 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1806 aWorkspace = d->workspace();
1808 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1809 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1811 aWorkspace = d->workstack();
1813 #if SIP_VERSION < 0x040800
1814 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1816 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1818 // ... and finally call Python module's setWorkSpace() method (obsolete)
1819 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1820 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1828 \brief Initialization callback function
1831 Performs the following actions:
1832 - initialize or get the Python interpreter (one per study)
1833 - import the Python module
1834 - pass the workspace widget to the Python module
1835 - call Python module's initialize() method
1836 - call Python module's windows() method
1837 - call Python module's views() method
1839 \param app parent application object
1841 void PyModuleHelper::internalInitialize( CAM_Application* app )
1843 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1845 // reset interpreter to NULL
1849 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1852 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1855 int aStudyId = aStudy ? aStudy->id() : 0;
1857 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1858 initInterp( aStudyId );
1862 // import Python GUI module
1867 // then call Python module's initialize() method
1868 // ... first get python lock
1869 PyLockWrapper aLock; // Acquire GIL
1871 // ... (the Python module is already imported)
1872 // ... finally call Python module's initialize() method
1873 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1874 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1880 // get required dockable windows list from the Python module
1881 // by calling windows() method
1882 // ... first put default values
1883 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1884 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1885 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1887 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1888 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1893 myWindowsMap.clear();
1894 if ( PyDict_Check( res1 ) ) {
1898 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1899 // parse the return value
1900 // it should be a map: {integer:integer}
1902 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
1903 aKey = PyInt_AsLong( key );
1904 aValue = PyInt_AsLong( value );
1905 myWindowsMap[ aKey ] = aValue;
1912 // get compatible view windows types from the Python module
1913 // by calling views() method
1914 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1915 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1920 // parse the return value
1921 // result can be one string...
1922 if ( PyString_Check( res2 ) ) {
1923 myViewMgrList.append( PyString_AsString( res2 ) );
1925 // ... or list of strings
1926 else if ( PyList_Check( res2 ) ) {
1927 int size = PyList_Size( res2 );
1928 for ( int i = 0; i < size; i++ ) {
1929 PyObject* value = PyList_GetItem( res2, i );
1930 if( value && PyString_Check( value ) ) {
1931 myViewMgrList.append( PyString_AsString( value ) );
1940 \brief Activation callback function
1943 Performs the following actions:
1944 - initialize or get the Python interpreter (one per study)
1945 - import the Python GUI module
1946 - call Python module's activate() method
1948 \param study parent study
1950 void PyModuleHelper::internalActivate( SUIT_Study* study )
1952 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1955 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1956 int aStudyId = aStudy ? aStudy->id() : 0;
1958 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1959 initInterp( aStudyId );
1961 myLastActivateStatus = false;
1965 // import Python GUI module
1967 if ( !myPyModule ) {
1968 myLastActivateStatus = false;
1973 PyLockWrapper aLock; // Acquire GIL
1975 // call Python module's activate() method (for the new modules)
1976 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
1977 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
1978 if ( !res1 || !PyBool_Check( res1 ) ) {
1980 // always true for old modules (no return value)
1981 myLastActivateStatus = true;
1984 // detect return status
1985 myLastActivateStatus = PyObject_IsTrue( res1 );
1991 \brief Additional menu customization callback function
1994 Performs the following actions:
1995 - get the Python interpreter (one per study)
1996 - import the Python GUI module
1997 - call Python module's setSettings() method (obsolete function,
1998 used for compatibility with old code)
2000 \param study parent study
2002 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2004 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2007 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2008 int aStudyId = aStudy ? aStudy->id() : 0;
2010 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2011 initInterp( aStudyId );
2013 myLastActivateStatus = false;
2017 // import Python GUI module
2019 if ( !myPyModule ) {
2020 myLastActivateStatus = false;
2024 // call Python module's setWorkSpace() method (obsolete)
2028 PyLockWrapper aLock; // Acquire GIL
2030 if ( IsCallOldMethods ) {
2031 // call Python module's setSettings() method (obsolete)
2032 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2033 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2037 myLastActivateStatus = myLastActivateStatus && true;
2043 \brief Deactivation callback function
2046 Performs the following actions:
2047 - call Python module's deactivate() method
2049 \param study parent study
2051 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2053 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2055 // check that Python subinterpreter is initialized and Python module is imported
2056 if ( !myInterp || !myPyModule ) {
2057 // Error! Python subinterpreter should be initialized and module should be imported first!
2060 // then call Python module's deactivate() method
2061 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2062 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2070 \brief Internal closure:
2072 Performs the following actions:
2073 - call Python module's closeStudy() method
2075 \param theStudy parent study object
2077 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2079 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2083 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2084 int aStudyId = aStudy ? aStudy->id() : 0;
2086 // check that Python subinterpreter is initialized and Python module is imported
2087 if ( !myInterp || !myPyModule ) {
2088 // Error! Python subinterpreter should be initialized and module should be imported first!
2091 // then call Python module's deactivate() method
2092 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2093 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i", aStudyId ) );
2103 \brief Preference changing callback function.
2106 Performs the following actions:
2107 - call Python module's preferenceChanged() method
2109 \param section resources section name
2110 \param setting resources parameter name
2112 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2114 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2116 // check that Python subinterpreter is initialized and Python module is imported
2117 if ( !myInterp || !myPyModule ) {
2118 // Error! Python subinterpreter should be initialized and module should be imported first!
2122 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2123 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2124 (char*)"preferenceChanged",
2126 section.toLatin1().constData(),
2127 setting.toLatin1().constData() ) );
2135 \brief Active study change callback function.
2138 Called when active the study is actived (user brings its
2140 - initialize or get the Python interpreter (one per study)
2141 - import the Python GUI module
2142 - call Python module's activeStudyChanged() method
2144 \param study study being activated
2146 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2148 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2151 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2152 int id = aStudy ? aStudy->id() : 0;
2154 fmsg.message( QString( "study id = %1" ).arg( id ) );
2156 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2161 // import Python GUI module
2166 // call Python module's setWorkSpace() method
2170 PyLockWrapper aLock; // Acquire GIL
2172 // call Python module's activeStudyChanged() method
2173 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2174 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
2182 \brief GUI event handling callback function
2185 Performs the following actions:
2186 - calls Python module's OnGUIEvent() method
2188 \param id GUI action ID
2190 void PyModuleHelper::internalActionActivated( int id )
2192 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2193 fmsg.message( QString( "action id = %1" ).arg( id ) );
2195 // Python interpreter should be initialized and Python module should be
2197 if ( !myInterp || !myPyModule )
2200 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2201 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2209 \brief Context popup menu handling callback function
2212 Performs the following actions:
2213 - calls Python module's definePopup(...) method (obsolete function,
2214 used for compatibility with old code) to define the popup menu context
2215 - parses XML resourses file (if exists) and fills the popup menu with the items)
2216 - calls Python module's customPopup(...) method (obsolete function,
2217 used for compatibility with old code) to allow module to customize the popup menu
2218 - for new modules calls createPopupMenu() function to allow the
2219 modules to build the popup menu by using insertItem(...) Qt functions.
2221 \param context popup menu context
2222 \param menu popup menu
2224 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2226 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2227 fmsg.message( QString( "context: %1" ).arg( context ) );
2229 // Python interpreter should be initialized and Python module should be
2231 if ( !myInterp || !myPyModule )
2234 QString aContext( "" ), aObject( "" ), aParent( context );
2236 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2237 // call definePopup() Python module's function
2238 // this is obsolete function, used only for compatibility reasons
2239 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2240 (char*)"definePopup",
2242 context.toLatin1().constData(),
2243 aObject.toLatin1().constData(),
2244 aParent.toLatin1().constData() ) );
2249 // parse return value
2251 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2257 } // if ( IsCallOldMethods ... )
2259 // first try to create menu via XML parser:
2260 // we create popup menus without help of QtxPopupMgr
2262 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2264 #if SIP_VERSION < 0x040800
2265 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2267 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2270 // then call Python module's createPopupMenu() method (for new modules)
2271 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2272 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2273 (char*)"createPopupMenu",
2276 context.toLatin1().constData() ) );
2282 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2283 // call customPopup() Python module's function
2284 // this is obsolete function, used only for compatibility reasons
2285 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2286 (char*)"customPopup",
2289 aContext.toLatin1().constData(),
2290 aObject.toLatin1().constData(),
2291 aParent.toLatin1().constData() ) );
2299 \brief Preferences initialization callback function.
2302 Performs the following actions:
2303 - calls Python module's createPreferences() method
2305 void PyModuleHelper::internalCreatePreferences()
2307 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2309 // Python interpreter should be initialized and Python module should be
2311 if ( !myInterp || !myPyModule )
2314 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2315 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2323 \brief Active view changing callback function
2325 \param view view being activated
2327 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2329 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2331 if ( !myInterp || !myPyModule || !view )
2334 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2336 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2337 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2345 \brief View closing callback function
2347 \param view view user tries to close
2349 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2351 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2353 if ( !myInterp || !myPyModule || !view )
2356 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2358 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2360 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2369 \brief View closing callback function
2371 \param view view being closed
2373 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2375 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2377 if ( !myInterp || !myPyModule || !view )
2380 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2382 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2384 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2393 \brief View cloning callback function
2395 \param view view being cloned
2397 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2399 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2401 if ( !myInterp || !myPyModule || !view )
2404 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2406 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2408 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2415 \brief Module data saving callback function.
2417 \param files output list of files where module stores data
2419 void PyModuleHelper::internalSave( QStringList& files )
2421 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2423 // Python interpreter should be initialized and Python module should be
2425 // files list should contain a path to the temporary directory as a first entry
2426 if ( !myInterp || !myPyModule || files.isEmpty() )
2429 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2431 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2432 (char*)"s", files.first().toLatin1().constData() ) );
2438 // parse the return value
2439 // result can be one string...
2440 if ( PyString_Check( res ) ) {
2441 QString astr = PyString_AsString( res );
2442 files.append( astr );
2444 //also result can be a list...
2445 else if ( PyList_Check( res ) ) {
2446 int size = PyList_Size( res );
2447 for ( int i = 0; i < size; i++ ) {
2448 PyObject* value = PyList_GetItem( res, i );
2449 if ( value && PyString_Check( value ) ) {
2450 files.append( PyString_AsString( value ) );
2459 \brief Module data loading callback function.
2461 \param files list of files where module data is stored
2462 \param opened output success flag
2464 void PyModuleHelper::internalLoad( const QStringList& files, bool& opened )
2466 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2468 // Python interpreter should be initialized and Python module should be
2470 if ( !myInterp || !myPyModule || files.isEmpty() )
2473 QStringList* theList = new QStringList( files );
2475 #if SIP_VERSION < 0x040800
2476 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2478 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2480 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2481 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2482 (char*)"O", sipList.get()));
2483 if( !res || !PyBool_Check( res )) {
2488 opened = PyObject_IsTrue( res );
2494 \brief Module dump python callback function.
2496 \param files output list of files where module stores python script
2498 void PyModuleHelper::internalDumpPython( QStringList& files )
2500 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2502 // Python interpreter should be initialized and Python module should be
2504 // files list should contain a path to the temporary directory
2505 if ( !myInterp || !myPyModule || files.isEmpty() )
2508 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2509 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2510 (char*)"s", files.first().toLatin1().constData()));
2516 // parse the return value
2517 // result can be one string...
2518 if ( PyString_Check( res ) ) {
2519 QString astr = PyString_AsString( res );
2523 //also result can be a list...
2524 else if ( PyList_Check( res ) ) {
2525 int size = PyList_Size( res );
2526 for ( int i = 0; i < size; i++ ) {
2527 PyObject* value = PyList_GetItem( res, i );
2528 if( value && PyString_Check( value ) ) {
2529 files.append( PyString_AsString( value ) );
2538 \brief Check data object's 'draggable' status callback function.
2540 \param what data object being tested
2541 \return \c true if object can be dragged or \c false otherwise
2543 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2545 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2547 // Python interpreter should be initialized and Python module should be
2549 if ( !myInterp || !myPyModule || !what )
2552 bool draggable = false;
2554 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2555 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2556 (char*)"s", what->entry().toLatin1().constData() ) );
2557 if( !res || !PyBool_Check( res )) {
2562 draggable = PyObject_IsTrue( res );
2570 \brief Check data object's 'drop allowed' status callback function.
2572 \param where data object being tested
2573 \return \c true if if drop operation is supported by object or \c false otherwise
2575 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2577 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2579 // Python interpreter should be initialized and Python module should be
2581 if ( !myInterp || !myPyModule || !where )
2584 bool dropAccepted = false;
2586 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2587 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2588 (char*)"s", where->entry().toLatin1().constData() ) );
2589 if( !res || !PyBool_Check( res )) {
2591 dropAccepted = false;
2594 dropAccepted = PyObject_IsTrue( res );
2598 return dropAccepted;
2602 \brief Data dropping callback function.
2604 \param what list of data objects being dropped
2605 \param where target data object for drop operation
2606 \param row line (child item index) where drop operation is performed to
2607 \param action current drop action (copy or move)
2609 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2610 const int row, Qt::DropAction action )
2612 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2614 // Python interpreter should be initialized and Python module should be
2616 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2619 QStringList* theList = new QStringList();
2621 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2622 if ( !whereObject ) return;
2624 for ( int i = 0; i < what.count(); i++ ) {
2625 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2626 if ( dataObject ) theList->append( dataObject->entry() );
2629 #if SIP_VERSION < 0x040800
2630 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2632 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2634 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2635 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2637 whereObject->entry().toLatin1().constData(),
2647 \brief Get engine IOR callback function
2650 Tries to get engine IOR from the Python module using engineIOR() function.
2651 That function can load module engine using appropriate container if required.
2653 \return engine IOR or empty string if it is not provided by Python module
2655 QString PyModuleHelper::internalEngineIOR() const
2657 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2661 // Python interpreter should be initialized and Python module should be
2663 if ( myInterp && myModule ) {
2664 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2665 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2670 // parse the return value, result chould be string
2671 if ( PyString_Check( res ) ) {
2672 ior = PyString_AsString( res );
2681 \brief Connects signals about activating and cloning view on internal slots
2682 \param view view being connected
2684 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2686 SUIT_ViewManager* viewMgr = view->getViewManager();
2687 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2689 // Connect tryCloseView() and deleteView() signals
2691 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2692 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2693 Qt::UniqueConnection );
2694 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2695 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2696 Qt::UniqueConnection );
2699 // Connect cloneView() signal of an OCC View
2700 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2701 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2702 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2703 Qt::UniqueConnection );
2705 // Connect cloneView() signal of Plot2d View
2706 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2707 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2708 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2709 Qt::UniqueConnection );
2715 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2717 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2719 // Python interpreter should be initialized and Python module should be
2721 if ( !myInterp || !myPyModule )
2724 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2725 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2734 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2736 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2738 // temporary set myInitModule because dumpPython() method
2739 // might be called by the framework when this module is inactive,
2740 // but still it should be possible to access this module's data
2742 InitLocker lock( myModule );
2744 class PythonReq: public PyInterp_LockRequest
2747 PythonReq( PyInterp_Interp* _py_interp,
2748 PyModuleHelper* _helper,
2749 const QString& _entry,
2751 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2752 myHelper( _helper ) ,
2757 virtual void execute()
2759 myHelper->internalOBClickedPython( myEntry, myColumn );
2762 PyModuleHelper* myHelper;
2767 // Posting the request only if dispatcher is not busy!
2768 // Executing the request synchronously
2769 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2770 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2771 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );