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 if ( !aLabel.isEmpty() ) {
481 int tbId = module()->createTool( aLabel );
482 QDomNode node = parentNode.firstChild();
483 while ( !node.isNull() ) {
484 if ( node.isElement() ) {
485 QDomElement elem = node.toElement();
486 QString aTagName = tagName( elem );
487 if ( aTagName == "toolbutton-item" ) {
488 int id = checkInt( attribute( elem, "item-id" ) );
489 int pos = checkInt( attribute( elem, "pos-id" ) );
490 QString label = attribute( elem, "label-id" );
491 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
492 QString tooltip = attribute( elem, "tooltip-id" );
493 QString accel = attribute( elem, "accel-id" );
494 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
496 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
497 // also check if the action with given ID is already created
499 // create toolbar action
500 QAction* action = module()->createAction( id, // ID
504 tooltip, // status-bar text
505 QKeySequence( accel ), // keyboard accelerator
506 module(), // action owner
507 toggle ); // toogled action
508 myHelper->connectAction( action );
509 module()->createTool( action, tbId, -1, pos );
512 else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
513 // create toolbar separator
514 int pos = checkInt( attribute( elem, "pos-id" ) );
515 QAction* action = module()->separator();
516 module()->createTool( action, tbId, -1, pos );
519 node = node.nextSibling();
526 \brief Fill popup menu with the items.
527 \param parentNode XML node with popup menu description
528 \param menu popup menu
530 void PyModuleHelper::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
532 if ( !module() && parentNode.isNull() )
535 // we create popup menus without help of QtxPopupMgr
536 QDomNode node = parentNode.firstChild();
537 while ( !node.isNull() ) {
538 if ( node.isElement() ) {
539 QDomElement elem = node.toElement();
540 QString aTagName = tagName( elem );
541 QList<QAction*> actions = menu->actions();
542 if ( aTagName == "popup-item" ) {
543 // insert a command item
544 int id = checkInt( attribute( elem, "item-id" ) );
545 int pos = checkInt( attribute( elem, "pos-id" ) );
546 QString label = attribute( elem, "label-id" );
547 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
548 QString tooltip = attribute( elem, "tooltip-id" );
549 QString accel = attribute( elem, "accel-id" );
550 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
552 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
553 // also check if the action with given ID is already created
555 QAction* action = module()->createAction( id, // ID
559 tooltip, // status-bar text
560 QKeySequence( accel ), // keyboard accelerator
561 module(), // action owner
562 toggle ); // toogled action
563 myHelper->connectAction( action );
564 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
565 menu->insertAction( before, action );
568 else if ( aTagName == "submenu" ) {
570 ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
571 int pos = checkInt( attribute( elem, "pos-id" ) );
572 QString label = attribute( elem, "label-id" );
573 QString icon = attribute( elem, "icon-id" );
576 if ( !icon.isEmpty() ) {
577 QPixmap pixmap = module()->getApp()->resourceMgr()->loadPixmap( module()->name(), icon );
578 if ( !pixmap.isNull() )
579 anIcon = QIcon( pixmap );
582 QMenu* newPopup = menu->addMenu( anIcon, label );
583 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
584 menu->insertMenu( before, newPopup );
585 insertPopupItems( node, newPopup );
587 else if ( aTagName == "separator" ) {
588 // create menu separator
589 int pos = checkInt( attribute( elem, "pos-id" ) );
590 QAction* action = module()->separator();
591 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
592 menu->insertAction( before, action );
595 node = node.nextSibling();
600 \class PyModuleHelper
601 \brief This class implements API helper for all the Python-based
605 PyModuleHelper::InterpMap PyModuleHelper::myInterpMap;
606 LightApp_Module* PyModuleHelper::myInitModule = 0;
610 \param module owner module
612 PyModuleHelper::PyModuleHelper( LightApp_Module* module ) :
618 myLastActivateStatus( true )
620 setObjectName( "python_module_helper" );
626 PyModuleHelper::~PyModuleHelper()
629 if ( myInterp && myPyModule ) {
630 PyLockWrapper aLock; // Acquire GIL
631 Py_XDECREF( myPyModule );
636 \brief Get the module being initialized.
638 This is a little trick :) needed to provide an access from Python
639 (SalomePyQt) to the module being currently activated. The problem
640 that during the process of module initialization (initialize()
641 function) it is not yet available via application->activeModule()
644 This method returns valid pointer only if called in scope of
645 initialize() function or in several other specific cases.
647 \return the module being currently initialized
649 LightApp_Module* PyModuleHelper::getInitModule()
651 QMutexLocker ml( &myInitMutex );
656 \brief Get default menu group identifier
657 \return menu group ID (40 by default)
659 int PyModuleHelper::defaultMenuGroup()
661 return DEFAULT_GROUP;
665 \brief Get owner module
668 LightApp_Module* PyModuleHelper::module() const
674 \brief Get Python GUI module object
675 \return python module
677 PyObject* PyModuleHelper::pythonModule() const
683 \brief Connect action to the internal actionActivated() slot.
685 Actions connected to internal actionActivated(), when activated, will
686 be forwarded to the Python GUI module OnGUIEvent() function.
688 \param a action being connected
690 void PyModuleHelper::connectAction( QAction* a )
693 QObject::connect( a, SIGNAL( triggered( bool ) ),
694 this, SLOT( actionActivated() ),
695 Qt::UniqueConnection );
699 \brief Get the dockable windows associated with the module.
701 To fill the list of windows the correspondind Python module's windows()
702 method is called during the module initialization.
704 By default, ObjectBrowser, PythonConsole and LogWindow windows are
705 associated to the module.
707 Allowed dockable windows:
708 - LightApp_Application::WT_ObjectBrowser : object browser
709 - LightApp_Application::WT_PyConsole : python console
710 - LightApp_Application::WT_LogWindow : log messages output window
712 Dock area is defined by Qt::DockWidgetArea enumeration:
713 - Qt::TopDockWidgetArea : top dock area
714 - Qt::BottomDockWidgetArea : bottom dock area
715 - Qt::LeftDockWidgetArea : left dock area
716 - Qt::RightDockWidgetArea : right dock area
718 \return map of dockable windows in form { <window_type> : <dock_area> }
720 QMap<int, int> PyModuleHelper::windows() const
722 FuncMsg fmsg( "PyModuleHelper::windows()" );
728 \brief Define the compatible view windows associated with the module.
730 The associated view windows are opened automatically when the module
733 To fill the list of views the correspondind Python module's views()
734 method is called during the module initialization.
735 By default, the list of view types is empty.
737 \return list of view windows types
739 QStringList PyModuleHelper::viewManagers() const
741 FuncMsg fmsg( "PyModuleHelper::viewManagers()" );
743 return myViewMgrList;
747 \brief Initialization of the Python-based SALOME module.
749 This method can be used for creation of the menus, toolbars and
752 There are two ways to do this:
753 1) for obsolete modules, the implementation of this method first tries to read
754 the <module>_<language>.xml resource file which contains a menu,
755 toolbars and popup menus description;
756 2) new modules can create menus by direct calling of the
757 corresponding methods of SalomePyQt Python API in the Python
758 module's initialize() method which is called from here.
760 \note SALOME supports two modes of modules loading:
761 - immediate (all the modules are created and initialized
762 immediately when the application object is created);
763 - postponed modules loading (used currently); in this mode
764 the module is loaded only by explicit request.
765 If postponed modules loading is not used, the active
766 study might be not yet defined at this stage, so initialize()
767 method should not perform any study-based initialization.
768 Such actions can be better done in activate() function.
770 \param app parent application object
772 void PyModuleHelper::initialize( CAM_Application* app )
774 FuncMsg fmsg( "PyModuleHelper::initialize()" );
776 // temporarily store module being currently activated
777 // in the global variable to make it accessible from
779 InitLocker lock( myModule );
781 // try to get XML resource file name
782 SUIT_ResourceMgr* resMgr = myModule->getApp()->resourceMgr();
783 if ( !myXmlHandler && resMgr ) {
784 // get current language
785 QString lang = resMgr->stringValue( "language", "language", "en" );
786 // get menu description file name
787 QString aFileName = QString( "%1_%2.xml" ).arg( myModule->name() ).arg( lang );
788 aFileName = resMgr->path( "resources", myModule->name(), aFileName );
789 if ( !aFileName.isEmpty() && QFile::exists( aFileName ) ) {
790 // create XML handler instance
791 myXmlHandler = new XmlHandler( this, aFileName );
792 // ask XML handler to create actions
793 myXmlHandler->createActions();
797 class InitializeReq : public PyInterp_Request
800 InitializeReq( PyModuleHelper* _helper,
801 CAM_Application* _app )
802 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
807 virtual void execute()
809 myHelper->internalInitialize( myApp );
812 PyModuleHelper* myHelper;
813 CAM_Application* myApp;
817 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( this, app ) );
821 \brief Activation of the module.
823 This function is usually used in order to show the module's
824 specific menus and toolbars, update actions state and perform
825 other such actions required when the module is activated.
827 \note Returning \c false from this function prevents the
830 \param study parent study
831 \return \c true if activation is successful and \c false otherwise
833 bool PyModuleHelper::activate( SUIT_Study* study )
835 FuncMsg fmsg( "PyModuleHelper::activate()" );
837 // reset the activation status to the default value
838 myLastActivateStatus = true;
840 class ActivateReq : public PyInterp_Request
843 ActivateReq( PyModuleHelper* _helper,
846 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
849 myCustomize( _customize )
852 virtual void execute()
855 myHelper->internalActivate( myStudy ); // first activation stage
857 myHelper->internalCustomize( myStudy ); // second activation stage
860 PyModuleHelper* myHelper;
865 // post request for activation (customize=false)
866 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, false ) );
868 // check activation status (can be set to false by internalActivate())
869 if ( myLastActivateStatus ) {
870 // activate menus, toolbars, etc
871 if ( myXmlHandler ) myXmlHandler->activateMenus( true );
873 // show menus / toolbars
874 myModule->setMenuShown( true );
875 myModule->setToolShown( true );
877 // post request for customization (customize=true)
878 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, true ) );
880 // check activation status (can be set to false by internalCustomize())
881 if ( myLastActivateStatus ) {
882 // connect preferences changing signal
883 connect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
884 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
886 // connect active view change signal
887 SUIT_Desktop* d = study->application()->desktop();
888 connect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
889 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
890 // if active window exists, call activeViewChanged() function;
891 // temporary solution: if a getActiveView() in SalomePyQt available
892 // we no longer need this
893 SUIT_ViewWindow* view = d->activeWindow();
894 if ( view ) activeViewChanged( view );
895 // get all view currently opened in the study and connect their signals to
896 // the corresponding slots of the class.
897 foreach ( view, d->windows() ) connectView( view );
900 // hide menus / toolbars in case of error
901 myModule->setMenuShown( false );
902 myModule->setToolShown( false );
906 return myLastActivateStatus;
910 \brief Deactivation of the module.
912 This function is usually used in order to hide the module's
913 specific menus and toolbars and perform other such actions
914 required when the module is deactivated.
916 \param study parent study
917 \return \c true if deactivation is successful and \c false otherwise
919 bool PyModuleHelper::deactivate( SUIT_Study* study )
921 FuncMsg fmsg( "PyModuleHelper::deactivate()" );
923 class DeactivateReq : public PyInterp_LockRequest
926 DeactivateReq( PyInterp_Interp* _py_interp,
927 PyModuleHelper* _helper,
929 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
934 virtual void execute()
936 myHelper->internalDeactivate( myStudy );
939 PyModuleHelper* myHelper;
944 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, this, study ) );
946 // disconnect preferences changing signal
947 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
948 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
950 // disconnect the SUIT_Desktop signal windowActivated()
951 SUIT_Desktop* d = study->application()->desktop();
952 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
953 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
955 // deactivate menus, toolbars, etc
956 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
958 // hide menus / toolbars
959 myModule->setMenuShown( false );
960 myModule->setToolShown( false );
966 \brief Close of the module.
968 This function is usually used in order to close the module's
969 specific menus and toolbars and perform other such actions
970 required when the module is closed.
972 void PyModuleHelper::modelClosed( SUIT_Study* study )
974 FuncMsg fmsg( "PyModuleHelper::modelClosed()" );
976 class StudyClosedReq : public PyInterp_LockRequest
979 StudyClosedReq( PyInterp_Interp* _py_interp,
980 PyModuleHelper* _helper,
982 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
987 virtual void execute()
989 myHelper->internalClosedStudy( myStudy );
992 PyModuleHelper* myHelper;
997 PyInterp_Dispatcher::Get()->Exec( new StudyClosedReq( myInterp, this, study ) );
999 // disconnect preferences changing signal
1000 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
1001 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
1003 // disconnect the SUIT_Desktop signal windowActivated()
1004 SUIT_Desktop* d = study->application()->desktop();
1005 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1006 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
1008 // deactivate menus, toolbars, etc
1009 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
1011 // hide menus / toolbars
1012 myModule->setMenuShown( false );
1013 myModule->setToolShown( false );
1018 \brief Process module's preferences changing.
1020 Called when the module's own preferences are changed.
1022 \param section preference resources section
1023 \param parameter preference resources parameter name
1025 void PyModuleHelper::preferencesChanged( const QString& section,
1026 const QString& parameter )
1028 FuncMsg fmsg( "PyModuleHelper::preferencesChanged()" );
1030 class PrefChangeReq : public PyInterp_LockRequest
1033 PrefChangeReq( PyInterp_Interp* _py_interp,
1034 PyModuleHelper* _helper,
1035 const QString& _section,
1036 const QString& _parameter )
1037 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1038 myHelper ( _helper ),
1039 mySection( _section ),
1040 myParameter( _parameter )
1043 virtual void execute()
1045 myHelper->internalPreferencesChanged( mySection, myParameter );
1048 PyModuleHelper* myHelper;
1049 QString mySection, myParameter;
1052 // post the request only if dispatcher is not busy!
1053 // execute request synchronously
1054 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1055 PyInterp_Dispatcher::Get()->Exec( new PrefChangeReq( myInterp, this, section, parameter ) );
1059 \brief Process application preferences changing.
1061 Called when any application setting is changed.
1063 \param module preference module
1064 \param section preference resources section
1065 \param parameter preference resources parameter name
1067 void PyModuleHelper::preferenceChanged( const QString& module,
1068 const QString& section,
1069 const QString& parameter )
1071 FuncMsg fmsg( "PyModuleHelper::preferenceChanged()" );
1073 // module's own preferences are processed by other preferencesChanged() method
1074 if ( module != myModule->moduleName() ) {
1076 preferencesChanged( section, parameter );
1081 \brief Process study activation.
1083 Called when study desktop is activated. Used for notifying the Python
1084 module about changing of the active study.
1086 \param study study being activated
1088 void PyModuleHelper::studyActivated( SUIT_Study* study )
1090 FuncMsg fmsg( "PyModuleHelper::studyActivated()" );
1092 // StudyChangedReq: request class for internal studyChanged() operation
1093 class StudyChangedReq : public PyInterp_Request
1096 StudyChangedReq( PyModuleHelper* _helper,
1097 SUIT_Study* _study )
1098 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
1099 myHelper( _helper ),
1103 virtual void execute()
1105 myHelper->internalStudyChanged( myStudy );
1108 PyModuleHelper* myHelper;
1109 SUIT_Study* myStudy;
1113 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( this, study ) );
1117 \brief Process action activation.
1119 Called when action is activated. Used for notifying the Python
1120 module about any related action activation.
1124 void PyModuleHelper::actionActivated()
1126 FuncMsg fmsg( "PyModuleHelper::actionActivated()" );
1128 // perform synchronous request to Python event dispatcher
1129 class ActionReq : public PyInterp_LockRequest
1132 ActionReq( PyInterp_Interp* _py_interp,
1133 PyModuleHelper* _helper,
1135 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1136 myHelper( _helper ),
1140 virtual void execute()
1142 myHelper->internalActionActivated( myId );
1145 PyModuleHelper* myHelper;
1149 // get sender action
1150 QAction* action = qobject_cast<QAction*>( sender() );
1155 PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
1159 \brief Process context popup menu request.
1161 Called when user activates popup menu in some window
1162 (view, object browser, etc).
1164 \param context popup menu context (e.g. "ObjectBrowser")
1165 \param menu popup menu
1167 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1169 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1171 class ContextMenuReq : public PyInterp_LockRequest
1174 ContextMenuReq( PyInterp_Interp* _py_interp,
1175 PyModuleHelper* _helper,
1176 const QString& _context,
1178 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1179 myHelper ( _helper ),
1180 myContext( _context ),
1184 virtual void execute()
1186 myHelper->internalContextMenu( myContext, myMenu );
1189 PyModuleHelper* myHelper;
1194 // post request only if dispatcher is not busy!
1195 // execute request synchronously
1196 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1197 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1201 \brief Export preferences for the Python module.
1202 Called only once when the first instance of the module is created or
1203 when common Preferences dialog box is first time invoked.
1205 void PyModuleHelper::createPreferences()
1207 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1209 // temporary set myInitModule because createPreferences() method
1210 // might be called during the module intialization process
1211 InitLocker lock( myModule );
1213 class CreatePrefReq : public PyInterp_LockRequest
1216 CreatePrefReq( PyInterp_Interp* _py_interp,
1217 PyModuleHelper* _helper )
1218 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1222 virtual void execute()
1224 myHelper->internalCreatePreferences();
1227 PyModuleHelper* myHelper;
1230 // post request only if dispatcher is not busy!
1231 // execute request synchronously
1232 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1233 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1237 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1239 Used to notify Python module that active view has been changed by the user.
1241 \param view view being activated
1243 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1245 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1247 // perform synchronous request to Python event dispatcher
1248 class ActiveViewChangeReq : public PyInterp_LockRequest
1251 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1252 PyModuleHelper* _helper,
1253 SUIT_ViewWindow* _view )
1254 : PyInterp_LockRequest( _py_interp, 0, true ),
1255 myHelper( _helper ),
1259 virtual void execute()
1261 myHelper->internalActiveViewChanged( myView );
1264 PyModuleHelper* myHelper;
1265 SUIT_ViewWindow* myView;
1268 // connect view (if it is not connected yet)
1269 connectView( view );
1271 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1275 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1276 \param view view being closed
1278 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1280 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1282 class TryCloseViewReq : public PyInterp_LockRequest
1285 TryCloseViewReq( PyInterp_Interp* _py_interp,
1286 PyModuleHelper* _helper,
1287 SUIT_ViewWindow* _view )
1288 : PyInterp_LockRequest( _py_interp, 0, true ),
1289 myHelper( _helper ),
1293 virtual void execute()
1295 myHelper->internalTryCloseView( myView );
1298 PyModuleHelper* myHelper;
1299 SUIT_ViewWindow* myView;
1302 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1306 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1307 \param view view being closed
1309 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1311 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1313 class CloseViewReq : public PyInterp_LockRequest
1316 CloseViewReq( PyInterp_Interp* _py_interp,
1317 PyModuleHelper* _helper,
1318 SUIT_ViewWindow* _view )
1319 : PyInterp_LockRequest( _py_interp, 0, true ),
1320 myHelper( _helper ),
1324 virtual void execute()
1326 myHelper->internalCloseView( myView );
1329 PyModuleHelper* myHelper;
1330 SUIT_ViewWindow* myView;
1333 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1337 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1338 \param view view being cloned
1340 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1342 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1344 class CloneViewReq : public PyInterp_LockRequest
1347 CloneViewReq( PyInterp_Interp* _py_interp,
1348 PyModuleHelper* _helper,
1349 SUIT_ViewWindow* _view )
1350 : PyInterp_LockRequest( _py_interp, 0, true ),
1351 myHelper( _helper ),
1355 virtual void execute()
1357 myHelper->internalCloneView( myView );
1360 PyModuleHelper* myHelper;
1361 SUIT_ViewWindow* myView;
1364 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1368 \brief Save module data. Called when user saves study.
1369 \param files output list of files where module stores data
1371 void PyModuleHelper::save( QStringList& files )
1373 FuncMsg fmsg( "PyModuleHelper::save()" );
1375 // temporary set myInitModule because save() method
1376 // might be called by the framework when this module is inactive,
1377 // but still it should be possible to access this module's data
1379 InitLocker lock( myModule );
1381 // perform synchronous request to Python event dispatcher
1382 class SaveReq: public PyInterp_LockRequest
1385 SaveReq( PyInterp_Interp* _py_interp,
1386 PyModuleHelper* _helper,
1387 QStringList& _files )
1388 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1389 myHelper( _helper ) ,
1393 virtual void execute()
1395 myHelper->internalSave( myFiles );
1398 PyModuleHelper* myHelper;
1399 QStringList& myFiles;
1402 // Posting the request only if dispatcher is not busy!
1403 // Executing the request synchronously
1404 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1405 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files ) );
1409 \brief Load module data. Called when user opens study
1410 and activates module.
1411 \param files list of files where module data is stored
1413 bool PyModuleHelper::load( const QStringList& files )
1415 FuncMsg fmsg( "PyModuleHelper::load()" );
1417 bool loaded = false;
1419 class LoadReq: public PyInterp_LockRequest
1422 LoadReq( PyInterp_Interp* _py_interp,
1423 PyModuleHelper* _helper,
1426 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1427 myHelper( _helper ) ,
1432 virtual void execute()
1434 myHelper->internalLoad( myFiles, myLoaded );
1437 PyModuleHelper* myHelper;
1438 QStringList myFiles;
1442 // Posting the request only if dispatcher is not busy!
1443 // Executing the request synchronously
1444 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1445 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, loaded ) );
1451 \brief Dump module data to the Python script.
1452 Called when user activates dump study operation.
1453 \param files output list of files where module stores python script
1455 void PyModuleHelper::dumpPython( QStringList& files )
1457 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1459 // temporary set myInitModule because dumpPython() method
1460 // might be called by the framework when this module is inactive,
1461 // but still it should be possible to access this module's data
1463 InitLocker lock( myModule );
1465 class DumpPythonReq: public PyInterp_LockRequest
1468 DumpPythonReq( PyInterp_Interp* _py_interp,
1469 PyModuleHelper* _helper,
1470 QStringList& _files )
1471 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1472 myHelper( _helper ) ,
1476 virtual void execute()
1478 myHelper->internalDumpPython( myFiles );
1481 PyModuleHelper* myHelper;
1482 QStringList& myFiles;
1485 // Posting the request only if dispatcher is not busy!
1486 // Executing the request synchronously
1487 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1488 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1492 \brief Test if object \a what can be dragged by the user.
1493 \param what data object being tested
1494 \return \c true if object can be dragged or \c false otherwise
1496 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1498 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1500 bool draggable = false;
1502 // perform synchronous request to Python event dispatcher
1503 class IsDraggableReq: public PyInterp_LockRequest
1506 IsDraggableReq( PyInterp_Interp* _py_interp,
1507 PyModuleHelper* _helper,
1508 LightApp_DataObject* _data_object,
1509 bool& _is_draggable )
1510 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1511 myHelper( _helper ) ,
1512 myDataObject( _data_object ),
1513 myIsDraggable( _is_draggable )
1516 virtual void execute()
1518 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1521 PyModuleHelper* myHelper;
1522 LightApp_DataObject* myDataObject;
1523 bool& myIsDraggable;
1526 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1527 if ( data_object ) {
1528 // Posting the request only if dispatcher is not busy!
1529 // Executing the request synchronously
1530 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1531 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1532 const_cast<PyModuleHelper*>( this ),
1533 const_cast<LightApp_DataObject*>( data_object ),
1541 \brief Test if drop operation can be done on the \a where object.
1542 \param where data object being tested
1543 \return \c true if if drop operation is supported by object or \c false otherwise
1545 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1547 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1549 bool dropAccepted = false;
1551 // perform synchronous request to Python event dispatcher
1552 class IsDropAcceptedReq: public PyInterp_LockRequest
1555 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1556 PyModuleHelper* _helper,
1557 LightApp_DataObject* _data_object,
1558 bool& _is_drop_accepted )
1559 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1560 myHelper( _helper ) ,
1561 myDataObject( _data_object ),
1562 myIsDropAccepted( _is_drop_accepted )
1565 virtual void execute()
1567 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1570 PyModuleHelper* myHelper;
1571 LightApp_DataObject* myDataObject;
1572 bool& myIsDropAccepted;
1575 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1576 if ( data_object ) {
1577 // Posting the request only if dispatcher is not busy!
1578 // Executing the request synchronously
1579 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1580 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1581 const_cast<PyModuleHelper*>( this ),
1582 const_cast<LightApp_DataObject*>( data_object ),
1586 return dropAccepted;
1590 \brief Perform drop operation
1591 \param what list of data objects being dropped
1592 \param where target data object for drop operation
1593 \param row line (child item index) where drop operation is performed to
1594 \param action current drop action (copy or move)
1596 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1597 const int row, Qt::DropAction action )
1599 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1601 // perform synchronous request to Python event dispatcher
1602 class DropObjectsReq: public PyInterp_LockRequest
1605 DropObjectsReq( PyInterp_Interp* _py_interp,
1606 PyModuleHelper* _helper,
1607 const DataObjectList& _what,
1608 SUIT_DataObject* _where,
1610 Qt::DropAction _action )
1611 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1612 myHelper( _helper ) ,
1616 myAction ( _action )
1619 virtual void execute()
1621 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1624 PyModuleHelper* myHelper;
1625 DataObjectList myWhat;
1626 SUIT_DataObject* myWhere;
1628 Qt::DropAction myAction;
1631 // Posting the request only if dispatcher is not busy!
1632 // Executing the request synchronously
1633 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1634 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1638 \brief Get module engine IOR
1639 \return engine IOR as it is supplied by GUI Python module
1641 QString PyModuleHelper::engineIOR() const
1643 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1645 class EngineIORReq : public PyInterp_LockRequest
1648 EngineIORReq( PyInterp_Interp* _py_interp,
1649 PyModuleHelper* _helper,
1651 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1652 myHelper( _helper ),
1656 virtual void execute()
1658 myIOR = myHelper->internalEngineIOR();
1661 PyModuleHelper* myHelper;
1665 static QString anIOR;
1667 if ( anIOR.isEmpty() ) {
1669 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1670 const_cast<PyModuleHelper*>( this ),
1678 \brief Initialize python subinterpreter (one per study).
1680 \param studyId study ID
1682 void PyModuleHelper::initInterp( int studyId )
1684 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1688 // Error! Study Id must not be 0!
1693 QMutexLocker ml( &myInitMutex );
1695 // try to find the subinterpreter
1696 if ( myInterpMap.contains( studyId ) ) {
1698 myInterp = myInterpMap[ studyId ];
1702 myInterp = new SALOME_PYQT_PyInterp();
1703 myInterp->initialize();
1704 myInterpMap[ studyId ] = myInterp;
1706 #ifndef GUI_DISABLE_CORBA
1707 if ( !SUIT_PYTHON::initialized ) {
1708 // import 'salome' module and call 'salome_init' method;
1709 // do it only once on interpreter creation
1710 // ... first get python lock
1711 PyLockWrapper aLock; // Acquire GIL
1712 // ... then import a module
1713 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1719 // ... then call a method
1721 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
1732 \brief Import Python GUI module and store reference to the module.
1735 Warning! initInterp() should be called first!!!
1737 void PyModuleHelper::importModule()
1739 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1741 // check if the subinterpreter is initialized
1743 // Error! Python subinterpreter should be initialized first!
1748 // import Python GUI module and put it in <myPyModule> attribute
1749 // ... first get python lock
1750 PyLockWrapper aLock; // Acquire GIL
1751 // ... then import a module
1752 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1754 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1759 if ( !myPyModule ) {
1767 \brief Set study workspace to the Python module.
1770 Calls setWorkSpace() method of the Python module with
1771 PyQt QWidget object to use with interpreter.
1773 Attention! initInterp() and importModule() should be called first!!!
1775 void PyModuleHelper::setWorkSpace()
1777 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1779 if ( !IsCallOldMethods )
1782 // check if the subinterpreter is initialized and Python module is imported
1783 if ( !myInterp || !myPyModule ) {
1784 // Error! Python subinterpreter should be initialized and module should be imported first!
1788 // call setWorkSpace() method
1789 // ... first get python lock
1790 PyLockWrapper aLock; // Acquire GIL
1792 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1793 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1800 // ... then get workspace object
1801 QWidget* aWorkspace = 0;
1802 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1803 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1805 aWorkspace = d->workspace();
1807 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1808 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1810 aWorkspace = d->workstack();
1812 #if SIP_VERSION < 0x040800
1813 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1815 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1817 // ... and finally call Python module's setWorkSpace() method (obsolete)
1818 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1819 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1827 \brief Initialization callback function
1830 Performs the following actions:
1831 - initialize or get the Python interpreter (one per study)
1832 - import the Python module
1833 - pass the workspace widget to the Python module
1834 - call Python module's initialize() method
1835 - call Python module's windows() method
1836 - call Python module's views() method
1838 \param app parent application object
1840 void PyModuleHelper::internalInitialize( CAM_Application* app )
1842 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1844 // reset interpreter to NULL
1848 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1851 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1854 int aStudyId = aStudy ? aStudy->id() : 0;
1856 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1857 initInterp( aStudyId );
1861 // import Python GUI module
1866 // then call Python module's initialize() method
1867 // ... first get python lock
1868 PyLockWrapper aLock; // Acquire GIL
1870 // ... (the Python module is already imported)
1871 // ... finally call Python module's initialize() method
1872 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1873 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1879 // get required dockable windows list from the Python module
1880 // by calling windows() method
1881 // ... first put default values
1882 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1883 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1884 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1886 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1887 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1892 myWindowsMap.clear();
1893 if ( PyDict_Check( res1 ) ) {
1897 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1898 // parse the return value
1899 // it should be a map: {integer:integer}
1901 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
1902 aKey = PyInt_AsLong( key );
1903 aValue = PyInt_AsLong( value );
1904 myWindowsMap[ aKey ] = aValue;
1911 // get compatible view windows types from the Python module
1912 // by calling views() method
1913 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1914 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1919 // parse the return value
1920 // result can be one string...
1921 if ( PyString_Check( res2 ) ) {
1922 myViewMgrList.append( PyString_AsString( res2 ) );
1924 // ... or list of strings
1925 else if ( PyList_Check( res2 ) ) {
1926 int size = PyList_Size( res2 );
1927 for ( int i = 0; i < size; i++ ) {
1928 PyObject* value = PyList_GetItem( res2, i );
1929 if( value && PyString_Check( value ) ) {
1930 myViewMgrList.append( PyString_AsString( value ) );
1939 \brief Activation callback function
1942 Performs the following actions:
1943 - initialize or get the Python interpreter (one per study)
1944 - import the Python GUI module
1945 - call Python module's activate() method
1947 \param study parent study
1949 void PyModuleHelper::internalActivate( SUIT_Study* study )
1951 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1954 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1955 int aStudyId = aStudy ? aStudy->id() : 0;
1957 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1958 initInterp( aStudyId );
1960 myLastActivateStatus = false;
1964 // import Python GUI module
1966 if ( !myPyModule ) {
1967 myLastActivateStatus = false;
1972 PyLockWrapper aLock; // Acquire GIL
1974 // call Python module's activate() method (for the new modules)
1975 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
1976 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
1977 if ( !res1 || !PyBool_Check( res1 ) ) {
1979 // always true for old modules (no return value)
1980 myLastActivateStatus = true;
1983 // detect return status
1984 myLastActivateStatus = PyObject_IsTrue( res1 );
1990 \brief Additional menu customization callback function
1993 Performs the following actions:
1994 - get the Python interpreter (one per study)
1995 - import the Python GUI module
1996 - call Python module's setSettings() method (obsolete function,
1997 used for compatibility with old code)
1999 \param study parent study
2001 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2003 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2006 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2007 int aStudyId = aStudy ? aStudy->id() : 0;
2009 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2010 initInterp( aStudyId );
2012 myLastActivateStatus = false;
2016 // import Python GUI module
2018 if ( !myPyModule ) {
2019 myLastActivateStatus = false;
2023 // call Python module's setWorkSpace() method (obsolete)
2027 PyLockWrapper aLock; // Acquire GIL
2029 if ( IsCallOldMethods ) {
2030 // call Python module's setSettings() method (obsolete)
2031 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2032 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2036 myLastActivateStatus = myLastActivateStatus && true;
2042 \brief Deactivation callback function
2045 Performs the following actions:
2046 - call Python module's deactivate() method
2048 \param study parent study
2050 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2052 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2054 // check that Python subinterpreter is initialized and Python module is imported
2055 if ( !myInterp || !myPyModule ) {
2056 // Error! Python subinterpreter should be initialized and module should be imported first!
2059 // then call Python module's deactivate() method
2060 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2061 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2069 \brief Internal closure:
2071 Performs the following actions:
2072 - call Python module's closeStudy() method
2074 \param theStudy parent study object
2076 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2078 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2082 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2083 int aStudyId = aStudy ? aStudy->id() : 0;
2085 // check that Python subinterpreter is initialized and Python module is imported
2086 if ( !myInterp || !myPyModule ) {
2087 // Error! Python subinterpreter should be initialized and module should be imported first!
2090 // then call Python module's deactivate() method
2091 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2092 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i", aStudyId ) );
2102 \brief Preference changing callback function.
2105 Performs the following actions:
2106 - call Python module's preferenceChanged() method
2108 \param section resources section name
2109 \param setting resources parameter name
2111 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2113 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2115 // check that Python subinterpreter is initialized and Python module is imported
2116 if ( !myInterp || !myPyModule ) {
2117 // Error! Python subinterpreter should be initialized and module should be imported first!
2121 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2122 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2123 (char*)"preferenceChanged",
2125 section.toLatin1().constData(),
2126 setting.toLatin1().constData() ) );
2134 \brief Active study change callback function.
2137 Called when active the study is actived (user brings its
2139 - initialize or get the Python interpreter (one per study)
2140 - import the Python GUI module
2141 - call Python module's activeStudyChanged() method
2143 \param study study being activated
2145 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2147 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2150 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2151 int id = aStudy ? aStudy->id() : 0;
2153 fmsg.message( QString( "study id = %1" ).arg( id ) );
2155 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2160 // import Python GUI module
2165 // call Python module's setWorkSpace() method
2169 PyLockWrapper aLock; // Acquire GIL
2171 // call Python module's activeStudyChanged() method
2172 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2173 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
2181 \brief GUI event handling callback function
2184 Performs the following actions:
2185 - calls Python module's OnGUIEvent() method
2187 \param id GUI action ID
2189 void PyModuleHelper::internalActionActivated( int id )
2191 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2192 fmsg.message( QString( "action id = %1" ).arg( id ) );
2194 // Python interpreter should be initialized and Python module should be
2196 if ( !myInterp || !myPyModule )
2199 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2200 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2208 \brief Context popup menu handling callback function
2211 Performs the following actions:
2212 - calls Python module's definePopup(...) method (obsolete function,
2213 used for compatibility with old code) to define the popup menu context
2214 - parses XML resourses file (if exists) and fills the popup menu with the items)
2215 - calls Python module's customPopup(...) method (obsolete function,
2216 used for compatibility with old code) to allow module to customize the popup menu
2217 - for new modules calls createPopupMenu() function to allow the
2218 modules to build the popup menu by using insertItem(...) Qt functions.
2220 \param context popup menu context
2221 \param menu popup menu
2223 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2225 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2226 fmsg.message( QString( "context: %1" ).arg( context ) );
2228 // Python interpreter should be initialized and Python module should be
2230 if ( !myInterp || !myPyModule )
2233 QString aContext( "" ), aObject( "" ), aParent( context );
2235 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2236 // call definePopup() Python module's function
2237 // this is obsolete function, used only for compatibility reasons
2238 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2239 (char*)"definePopup",
2241 context.toLatin1().constData(),
2242 aObject.toLatin1().constData(),
2243 aParent.toLatin1().constData() ) );
2248 // parse return value
2250 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2256 } // if ( IsCallOldMethods ... )
2258 // first try to create menu via XML parser:
2259 // we create popup menus without help of QtxPopupMgr
2261 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2263 #if SIP_VERSION < 0x040800
2264 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2266 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2269 // then call Python module's createPopupMenu() method (for new modules)
2270 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2271 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2272 (char*)"createPopupMenu",
2275 context.toLatin1().constData() ) );
2281 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2282 // call customPopup() Python module's function
2283 // this is obsolete function, used only for compatibility reasons
2284 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2285 (char*)"customPopup",
2288 aContext.toLatin1().constData(),
2289 aObject.toLatin1().constData(),
2290 aParent.toLatin1().constData() ) );
2298 \brief Preferences initialization callback function.
2301 Performs the following actions:
2302 - calls Python module's createPreferences() method
2304 void PyModuleHelper::internalCreatePreferences()
2306 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2308 // Python interpreter should be initialized and Python module should be
2310 if ( !myInterp || !myPyModule )
2313 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2314 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2322 \brief Active view changing callback function
2324 \param view view being activated
2326 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2328 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2330 if ( !myInterp || !myPyModule || !view )
2333 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2335 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2336 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2344 \brief View closing callback function
2346 \param view view user tries to close
2348 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2350 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2352 if ( !myInterp || !myPyModule || !view )
2355 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2357 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2359 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2368 \brief View closing callback function
2370 \param view view being closed
2372 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2374 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2376 if ( !myInterp || !myPyModule || !view )
2379 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2381 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2383 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2392 \brief View cloning callback function
2394 \param view view being cloned
2396 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2398 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2400 if ( !myInterp || !myPyModule || !view )
2403 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2405 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2407 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2414 \brief Module data saving callback function.
2416 \param files output list of files where module stores data
2418 void PyModuleHelper::internalSave( QStringList& files )
2420 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2422 // Python interpreter should be initialized and Python module should be
2424 // files list should contain a path to the temporary directory as a first entry
2425 if ( !myInterp || !myPyModule || files.isEmpty() )
2428 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2430 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2431 (char*)"s", files.first().toLatin1().constData() ) );
2437 // parse the return value
2438 // result can be one string...
2439 if ( PyString_Check( res ) ) {
2440 QString astr = PyString_AsString( res );
2441 files.append( astr );
2443 //also result can be a list...
2444 else if ( PyList_Check( res ) ) {
2445 int size = PyList_Size( res );
2446 for ( int i = 0; i < size; i++ ) {
2447 PyObject* value = PyList_GetItem( res, i );
2448 if ( value && PyString_Check( value ) ) {
2449 files.append( PyString_AsString( value ) );
2458 \brief Module data loading callback function.
2460 \param files list of files where module data is stored
2461 \param opened output success flag
2463 void PyModuleHelper::internalLoad( const QStringList& files, bool& opened )
2465 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2467 // Python interpreter should be initialized and Python module should be
2469 if ( !myInterp || !myPyModule || files.isEmpty() )
2472 QStringList* theList = new QStringList( files );
2474 #if SIP_VERSION < 0x040800
2475 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2477 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2479 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2480 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2481 (char*)"O", sipList.get()));
2482 if( !res || !PyBool_Check( res )) {
2487 opened = PyObject_IsTrue( res );
2493 \brief Module dump python callback function.
2495 \param files output list of files where module stores python script
2497 void PyModuleHelper::internalDumpPython( QStringList& files )
2499 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2501 // Python interpreter should be initialized and Python module should be
2503 // files list should contain a path to the temporary directory
2504 if ( !myInterp || !myPyModule || files.isEmpty() )
2507 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2508 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2509 (char*)"s", files.first().toLatin1().constData()));
2515 // parse the return value
2516 // result can be one string...
2517 if ( PyString_Check( res ) ) {
2518 QString astr = PyString_AsString( res );
2522 //also result can be a list...
2523 else if ( PyList_Check( res ) ) {
2524 int size = PyList_Size( res );
2525 for ( int i = 0; i < size; i++ ) {
2526 PyObject* value = PyList_GetItem( res, i );
2527 if( value && PyString_Check( value ) ) {
2528 files.append( PyString_AsString( value ) );
2537 \brief Check data object's 'draggable' status callback function.
2539 \param what data object being tested
2540 \return \c true if object can be dragged or \c false otherwise
2542 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2544 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2546 // Python interpreter should be initialized and Python module should be
2548 if ( !myInterp || !myPyModule || !what )
2551 bool draggable = false;
2553 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2554 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2555 (char*)"s", what->entry().toLatin1().constData() ) );
2556 if( !res || !PyBool_Check( res )) {
2561 draggable = PyObject_IsTrue( res );
2569 \brief Check data object's 'drop allowed' status callback function.
2571 \param where data object being tested
2572 \return \c true if if drop operation is supported by object or \c false otherwise
2574 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2576 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2578 // Python interpreter should be initialized and Python module should be
2580 if ( !myInterp || !myPyModule || !where )
2583 bool dropAccepted = false;
2585 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2586 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2587 (char*)"s", where->entry().toLatin1().constData() ) );
2588 if( !res || !PyBool_Check( res )) {
2590 dropAccepted = false;
2593 dropAccepted = PyObject_IsTrue( res );
2597 return dropAccepted;
2601 \brief Data dropping callback function.
2603 \param what list of data objects being dropped
2604 \param where target data object for drop operation
2605 \param row line (child item index) where drop operation is performed to
2606 \param action current drop action (copy or move)
2608 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2609 const int row, Qt::DropAction action )
2611 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2613 // Python interpreter should be initialized and Python module should be
2615 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2618 QStringList* theList = new QStringList();
2620 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2621 if ( !whereObject ) return;
2623 for ( int i = 0; i < what.count(); i++ ) {
2624 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2625 if ( dataObject ) theList->append( dataObject->entry() );
2628 #if SIP_VERSION < 0x040800
2629 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2631 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2633 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2634 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2636 whereObject->entry().toLatin1().constData(),
2646 \brief Get engine IOR callback function
2649 Tries to get engine IOR from the Python module using engineIOR() function.
2650 That function can load module engine using appropriate container if required.
2652 \return engine IOR or empty string if it is not provided by Python module
2654 QString PyModuleHelper::internalEngineIOR() const
2656 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2660 // Python interpreter should be initialized and Python module should be
2662 if ( myInterp && myModule ) {
2663 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2664 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2669 // parse the return value, result chould be string
2670 if ( PyString_Check( res ) ) {
2671 ior = PyString_AsString( res );
2680 \brief Connects signals about activating and cloning view on internal slots
2681 \param view view being connected
2683 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2685 SUIT_ViewManager* viewMgr = view->getViewManager();
2686 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2688 // Connect tryCloseView() and deleteView() signals
2690 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2691 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2692 Qt::UniqueConnection );
2693 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2694 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2695 Qt::UniqueConnection );
2698 // Connect cloneView() signal of an OCC View
2699 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2700 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2701 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2702 Qt::UniqueConnection );
2704 // Connect cloneView() signal of Plot2d View
2705 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2706 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2707 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2708 Qt::UniqueConnection );
2714 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2716 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2718 // Python interpreter should be initialized and Python module should be
2720 if ( !myInterp || !myPyModule )
2723 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2724 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2733 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2735 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2737 // temporary set myInitModule because dumpPython() method
2738 // might be called by the framework when this module is inactive,
2739 // but still it should be possible to access this module's data
2741 InitLocker lock( myModule );
2743 class PythonReq: public PyInterp_LockRequest
2746 PythonReq( PyInterp_Interp* _py_interp,
2747 PyModuleHelper* _helper,
2748 const QString& _entry,
2750 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2751 myHelper( _helper ) ,
2756 virtual void execute()
2758 myHelper->internalOBClickedPython( myEntry, myColumn );
2761 PyModuleHelper* myHelper;
2766 // Posting the request only if dispatcher is not busy!
2767 // Executing the request synchronously
2768 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2769 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2770 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );