1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // File : SALOME_PYQT_PyModule.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
24 #include "SALOME_PYQT_PyModule.h"
25 #include "SALOME_PYQT_PyInterp.h"
27 #include "LightApp_Application.h"
28 #include "LightApp_DataObject.h"
29 #include "LightApp_Module.h"
30 #include "LightApp_Study.h"
31 #include "PyInterp_Dispatcher.h"
32 #include "QtxActionMenuMgr.h"
33 #include "QtxWorkspace.h"
34 #include "QtxWorkstack.h"
35 #include "STD_MDIDesktop.h"
36 #include "STD_TabDesktop.h"
37 #include "SUITApp_init_python.hxx"
38 #include "SUIT_ResourceMgr.h"
39 #include "SUIT_ViewManager.h"
40 #include "SUIT_ViewModel.h"
41 #include "SUIT_ViewWindow.h"
43 #include <QApplication>
44 #include <QDomDocument>
45 #include <QDomElement>
51 #include <utilities.h>
53 #include "sipAPISalomePyQtGUILight.h"
56 \brief Default menu group number.
59 const int DEFAULT_GROUP = 40;
62 \brief Mutex used to lock access from several threads to the shared
69 const bool DEBUG = false;
73 \brief Allow calling obsolete callback methods.
76 If the macro CALL_OLD_METHODS is not defined, the invoking
77 of obsolete Python module's methods like setSetting(), definePopup(),
80 CALL_OLD_METHODS macro can be defined, for example, by adding
81 -DCALL_OLD_METHODS compilation option to the CMakeLists.txt.
83 #ifdef CALL_OLD_METHODS
84 const bool IsCallOldMethods = true;
86 const bool IsCallOldMethods = false;
90 \brief Get tag name for the DOM element.
92 \param element DOM element
93 \return tag name or empty string if the element does not have tag name
95 static QString tagName( const QDomElement& element )
97 return element.tagName().trimmed();
101 \brief Get value of DOM element's attribute.
103 \param element DOM element
104 \param attName attribute name
105 \return attribute value or empty string if the element does not have such attribute
107 static QString attribute( const QDomElement& element, const QString& attName )
109 return element.attribute( attName ).trimmed();
113 \brief Inspect specified string for the boolean value.
116 This function returns \c true if string represents boolean value:
117 - "true", "yes" or "1" for \c true
118 - "false", "no" or "0" for \c false
119 Second parameter allows to specify what boolean value is expected:
122 - other value is not taken into account (return represented value)
124 \param value inspected string
125 \param check expected boolean value
126 \return boolean value represented by the string (\a check is not 1 or 0)
127 or \c true if value correspond to the specified \a check
129 static bool checkBool( const QString& value, const int check = -1 )
131 QString v = value.toLower();
132 if ( ( v == "true" || v == "yes" || v == "1" ) && ( check != 0 ) )
134 if ( ( v == "false" || v == "no" || v == "0" ) && ( check == 0 ) )
140 \brief Inspect specified string for the integer value.
143 This function returns returns -1 if item is empty or represents
145 \param value inspected string
146 \param def default value
147 \param shift shift value (it is added to the integer value to produce shifted result)
149 static int checkInt( const QString& value, const int def = -1, const int shift = -1 )
152 int val = value.toInt( &bOk );
153 if ( !bOk ) val = def;
154 if ( shift > 0 && bOk && val < 0 )
161 \brief Function call in/out tracer.
168 FuncMsg( const QString& funcName )
172 MESSAGE( qPrintable( myName ) << " [ begin ]" );
177 MESSAGE( qPrintable( myName ) << " [ end ]" );
179 void message( const QString& msg )
182 MESSAGE( qPrintable( myName ) << " : " << qPrintable( msg ) );
189 \class PyModuleHelper::InitLocker
190 \brief Initialization locker
194 class PyModuleHelper::InitLocker
197 InitLocker( LightApp_Module* );
205 PyModuleHelper::InitLocker::InitLocker( LightApp_Module* module )
207 QMutexLocker ml( &myInitMutex );
208 myInitModule = module;
215 PyModuleHelper::InitLocker::~InitLocker()
217 QMutexLocker ml( &myInitMutex );
222 \class PyModuleHelper::XmlHandler
223 \brief XML resource files parser.
226 This class is used to provide backward compatibility with
227 existing Python modules in which obsolete menu definition system
228 (via XML files) is used.
231 class PyModuleHelper::XmlHandler
234 XmlHandler( PyModuleHelper* helper, const QString& fileName );
235 void createActions();
236 void createPopup( QMenu* menu,
237 const QString& context,
238 const QString& parent,
239 const QString& object );
240 void activateMenus( bool );
243 LightApp_Module* module() const;
244 QIcon loadIcon( const QString& fileName );
246 void createMenu( QDomNode& parentNode,
247 const int parentMenuId = -1,
248 QMenu* parentPopup = 0 );
249 void createToolBar( QDomNode& parentNode );
250 void insertPopupItems( QDomNode& parentNode,
254 PyModuleHelper* myHelper;
256 QList<int> myMenuItems;
263 \param module pointer to the GUI module
264 \param fileName path to the XML menu description file
266 PyModuleHelper::XmlHandler::XmlHandler( PyModuleHelper* helper,
267 const QString& fileName )
270 if ( !fileName.isEmpty() ) {
271 QFile aFile( fileName );
272 if ( aFile.open( QIODevice::ReadOnly ) ) {
273 myDoc.setContent( &aFile );
279 \brief Parse XML file and create actions.
282 Called by PyModuleHelper::initialize() in order to create actions
285 void PyModuleHelper::XmlHandler::createActions()
287 // get document element
288 QDomElement aDocElem = myDoc.documentElement();
290 // create main menu actions
291 QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
292 for ( int i = 0; i < aMenuList.count(); i++ ) {
293 QDomNode n = aMenuList.item( i );
297 // create toolbars actions
298 QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
299 for ( int i = 0; i < aToolsList.count(); i++ ) {
300 QDomNode n = aToolsList.item( i );
306 \brief Create popup menu.
308 \param menu popup menu
309 \param context popup menu context
310 \param context popup menu parent object name
311 \param context popup menu object name
313 void PyModuleHelper::XmlHandler::createPopup( QMenu* menu,
314 const QString& context,
315 const QString& parent,
316 const QString& object )
318 // get document element
319 QDomElement aDocElem = myDoc.documentElement();
321 // get popup menus actions
322 QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
323 for ( int i = 0; i < aPopupList.count(); i++ ) {
324 QDomNode n = aPopupList.item( i );
325 if ( !n.isNull() && n.isElement() ) {
326 QDomElement e = n.toElement();
327 // QString lab = attribute( e, "label-id" ); // not used //
328 QString ctx = attribute( e, "context-id" );
329 QString prt = attribute( e, "parent-id" );
330 QString obj = attribute( e, "object-id" );
331 if ( ctx == context && prt == parent && obj == object ) {
332 insertPopupItems( n, menu );
340 \brief Activate/deactivate menus
342 \param enable if \c true menus are activated, otherwise menus are deactivated
344 void PyModuleHelper::XmlHandler::activateMenus( bool enable )
347 QtxActionMenuMgr* mgr = module()->menuMgr();
348 foreach( int id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
353 \brief Get owner module
355 LightApp_Module* PyModuleHelper::XmlHandler::module() const
357 return myHelper->module();
361 \brief Load an icon from the module resources by the specified file name.
362 \param fileName icon file name
366 QIcon PyModuleHelper::XmlHandler::loadIcon( const QString& fileName )
370 if ( module() && !fileName.isEmpty() ) {
371 SUIT_ResourceMgr* resMgr = module()->getApp()->resourceMgr();
372 QPixmap pixmap = resMgr->loadPixmap( module()->name(),
373 QApplication::translate( module()->name().toLatin1().data(),
374 fileName.toLatin1().data() ) );
375 if ( !pixmap.isNull() )
376 icon = QIcon( pixmap );
383 \brief Create main menu item and insert actions to it.
385 \param parentNode XML node with menu description
386 \param parentMenuId parent menu ID (-1 for top-level menu)
387 \param parentPopup parent popup menu (0 for top-level menu)
389 void PyModuleHelper::XmlHandler::createMenu( QDomNode& parentNode,
390 const int parentMenuId,
393 if ( !module() || parentNode.isNull() )
396 QDomElement parentElement = parentNode.toElement();
397 if ( !parentElement.isNull() ) {
398 QString plabel = attribute( parentElement, "label-id" );
399 int pid = checkInt( attribute( parentElement, "item-id" ) );
400 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
401 int group = checkInt( attribute( parentElement, "group-id" ),
402 PyModuleHelper::defaultMenuGroup() );
403 if ( !plabel.isEmpty() ) {
407 menuId = module()->createMenu( plabel, // label
408 parentMenuId, // parent menu ID, -1 for top-level menu
412 myMenuItems.append( menuId );
413 QDomNode node = parentNode.firstChild();
414 while ( !node.isNull() ) {
415 if ( node.isElement() ) {
416 QDomElement elem = node.toElement();
417 QString aTagName = tagName( elem );
418 if ( aTagName == "popup-item" ) {
419 int id = checkInt( attribute( elem, "item-id" ) );
420 int pos = checkInt( attribute( elem, "pos-id" ) );
421 int group = checkInt( attribute( elem, "group-id" ),
422 PyModuleHelper::defaultMenuGroup() );
423 QString label = attribute( elem, "label-id" );
424 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
425 QString tooltip = attribute( elem, "tooltip-id" );
426 QString accel = attribute( elem, "accel-id" );
427 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
429 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
430 // also check if the action with given ID is already created
432 // create menu action
433 QAction* action = module()->createAction( id, // ID
437 tooltip, // status-bar text
438 QKeySequence( accel ), // keyboard accelerator
439 module(), // action owner
440 toggle ); // toogled action
441 myHelper->connectAction( action );
442 module()->createMenu( action, // action
443 menuId, // parent menu ID
444 id, // ID (same as for createAction())
449 else if ( aTagName == "submenu" ) {
451 createMenu( node, menuId, popup );
453 else if ( aTagName == "separator" ) {
454 // create menu separator
455 int id = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
456 int pos = checkInt( attribute( elem, "pos-id" ) );
457 int group = checkInt( attribute( elem, "group-id" ),
458 PyModuleHelper::defaultMenuGroup() );
459 QAction* action = module()->separator();
460 module()->createMenu( action, // separator action
461 menuId, // parent menu ID
467 node = node.nextSibling();
474 \brief Create a toolbar and insert actions to it.
475 \param parentNode XML node with toolbar description
477 void PyModuleHelper::XmlHandler::createToolBar( QDomNode& parentNode )
479 if ( !module() || parentNode.isNull() )
482 QDomElement parentElement = parentNode.toElement();
483 if ( !parentElement.isNull() ) {
484 QString aLabel = attribute( parentElement, "label-id" );
485 QString aName = attribute( parentElement, "name-id" );
486 if ( !aLabel.isEmpty() ) {
488 int tbId = module()->createTool( aLabel, aName );
489 QDomNode node = parentNode.firstChild();
490 while ( !node.isNull() ) {
491 if ( node.isElement() ) {
492 QDomElement elem = node.toElement();
493 QString aTagName = tagName( elem );
494 if ( aTagName == "toolbutton-item" ) {
495 int id = checkInt( attribute( elem, "item-id" ) );
496 int pos = checkInt( attribute( elem, "pos-id" ) );
497 QString label = attribute( elem, "label-id" );
498 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
499 QString tooltip = attribute( elem, "tooltip-id" );
500 QString accel = attribute( elem, "accel-id" );
501 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
503 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
504 // also check if the action with given ID is already created
506 // create toolbar action
507 QAction* action = module()->createAction( id, // ID
511 tooltip, // status-bar text
512 QKeySequence( accel ), // keyboard accelerator
513 module(), // action owner
514 toggle ); // toogled action
515 myHelper->connectAction( action );
516 module()->createTool( action, tbId, -1, pos );
519 else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
520 // create toolbar separator
521 int pos = checkInt( attribute( elem, "pos-id" ) );
522 QAction* action = module()->separator();
523 module()->createTool( action, tbId, -1, pos );
526 node = node.nextSibling();
533 \brief Fill popup menu with the items.
534 \param parentNode XML node with popup menu description
535 \param menu popup menu
537 void PyModuleHelper::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
539 if ( !module() && parentNode.isNull() )
542 // we create popup menus without help of QtxPopupMgr
543 QDomNode node = parentNode.firstChild();
544 while ( !node.isNull() ) {
545 if ( node.isElement() ) {
546 QDomElement elem = node.toElement();
547 QString aTagName = tagName( elem );
548 QList<QAction*> actions = menu->actions();
549 if ( aTagName == "popup-item" ) {
550 // insert a command item
551 int id = checkInt( attribute( elem, "item-id" ) );
552 int pos = checkInt( attribute( elem, "pos-id" ) );
553 QString label = attribute( elem, "label-id" );
554 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
555 QString tooltip = attribute( elem, "tooltip-id" );
556 QString accel = attribute( elem, "accel-id" );
557 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
559 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
560 // also check if the action with given ID is already created
562 QAction* action = module()->createAction( id, // ID
566 tooltip, // status-bar text
567 QKeySequence( accel ), // keyboard accelerator
568 module(), // action owner
569 toggle ); // toogled action
570 myHelper->connectAction( action );
571 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
572 menu->insertAction( before, action );
575 else if ( aTagName == "submenu" ) {
577 ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
578 int pos = checkInt( attribute( elem, "pos-id" ) );
579 QString label = attribute( elem, "label-id" );
580 QString icon = attribute( elem, "icon-id" );
583 if ( !icon.isEmpty() ) {
584 QPixmap pixmap = module()->getApp()->resourceMgr()->loadPixmap( module()->name(), icon );
585 if ( !pixmap.isNull() )
586 anIcon = QIcon( pixmap );
589 QMenu* newPopup = menu->addMenu( anIcon, label );
590 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
591 menu->insertMenu( before, newPopup );
592 insertPopupItems( node, newPopup );
594 else if ( aTagName == "separator" ) {
595 // create menu separator
596 int pos = checkInt( attribute( elem, "pos-id" ) );
597 QAction* action = module()->separator();
598 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
599 menu->insertAction( before, action );
602 node = node.nextSibling();
607 \class PyModuleHelper
608 \brief This class implements API helper for all the Python-based
612 PyModuleHelper::InterpMap PyModuleHelper::myInterpMap;
613 LightApp_Module* PyModuleHelper::myInitModule = 0;
617 \param module owner module
619 PyModuleHelper::PyModuleHelper( LightApp_Module* module ) :
625 myLastActivateStatus( true )
627 setObjectName( "python_module_helper" );
633 PyModuleHelper::~PyModuleHelper()
636 if ( myInterp && myPyModule ) {
637 PyLockWrapper aLock; // Acquire GIL
638 Py_XDECREF( myPyModule );
643 \brief Get the module being initialized.
645 This is a little trick :) needed to provide an access from Python
646 (SalomePyQt) to the module being currently activated. The problem
647 that during the process of module initialization (initialize()
648 function) it is not yet available via application->activeModule()
651 This method returns valid pointer only if called in scope of
652 initialize() function or in several other specific cases.
654 \return the module being currently initialized
656 LightApp_Module* PyModuleHelper::getInitModule()
658 QMutexLocker ml( &myInitMutex );
663 \brief Get default menu group identifier
664 \return menu group ID (40 by default)
666 int PyModuleHelper::defaultMenuGroup()
668 return DEFAULT_GROUP;
672 \brief Get owner module
675 LightApp_Module* PyModuleHelper::module() const
681 \brief Get Python GUI module object
682 \return python module
684 PyObject* PyModuleHelper::pythonModule() const
690 \brief Connect action to the internal actionActivated() slot.
692 Actions connected to internal actionActivated(), when activated, will
693 be forwarded to the Python GUI module OnGUIEvent() function.
695 \param a action being connected
697 void PyModuleHelper::connectAction( QAction* a )
700 QObject::connect( a, SIGNAL( triggered( bool ) ),
701 this, SLOT( actionActivated() ),
702 Qt::UniqueConnection );
706 \brief Get the dockable windows associated with the module.
708 To fill the list of windows the correspondind Python module's windows()
709 method is called during the module initialization.
711 By default, ObjectBrowser, PythonConsole and LogWindow windows are
712 associated to the module.
714 Allowed dockable windows:
715 - LightApp_Application::WT_ObjectBrowser : object browser
716 - LightApp_Application::WT_PyConsole : python console
717 - LightApp_Application::WT_LogWindow : log messages output window
719 Dock area is defined by Qt::DockWidgetArea enumeration:
720 - Qt::TopDockWidgetArea : top dock area
721 - Qt::BottomDockWidgetArea : bottom dock area
722 - Qt::LeftDockWidgetArea : left dock area
723 - Qt::RightDockWidgetArea : right dock area
725 \return map of dockable windows in form { <window_type> : <dock_area> }
727 QMap<int, int> PyModuleHelper::windows() const
729 FuncMsg fmsg( "PyModuleHelper::windows()" );
735 \brief Define the compatible view windows associated with the module.
737 The associated view windows are opened automatically when the module
740 To fill the list of views the correspondind Python module's views()
741 method is called during the module initialization.
742 By default, the list of view types is empty.
744 \return list of view windows types
746 QStringList PyModuleHelper::viewManagers() const
748 FuncMsg fmsg( "PyModuleHelper::viewManagers()" );
750 return myViewMgrList;
754 \brief Initialization of the Python-based SALOME module.
756 This method can be used for creation of the menus, toolbars and
759 There are two ways to do this:
760 1) for obsolete modules, the implementation of this method first tries to read
761 the <module>_<language>.xml resource file which contains a menu,
762 toolbars and popup menus description;
763 2) new modules can create menus by direct calling of the
764 corresponding methods of SalomePyQt Python API in the Python
765 module's initialize() method which is called from here.
767 \note SALOME supports two modes of modules loading:
768 - immediate (all the modules are created and initialized
769 immediately when the application object is created);
770 - postponed modules loading (used currently); in this mode
771 the module is loaded only by explicit request.
772 If postponed modules loading is not used, the active
773 study might be not yet defined at this stage, so initialize()
774 method should not perform any study-based initialization.
775 Such actions can be better done in activate() function.
777 \param app parent application object
779 void PyModuleHelper::initialize( CAM_Application* app )
781 FuncMsg fmsg( "PyModuleHelper::initialize()" );
783 // temporarily store module being currently activated
784 // in the global variable to make it accessible from
786 InitLocker lock( myModule );
788 // try to get XML resource file name
789 SUIT_ResourceMgr* resMgr = myModule->getApp()->resourceMgr();
790 if ( !myXmlHandler && resMgr ) {
791 // get current language
792 QString lang = resMgr->stringValue( "language", "language", "en" );
793 // get menu description file name
794 QString aFileName = QString( "%1_%2.xml" ).arg( myModule->name() ).arg( lang );
795 aFileName = resMgr->path( "resources", myModule->name(), aFileName );
796 if ( !aFileName.isEmpty() && QFile::exists( aFileName ) ) {
797 // create XML handler instance
798 myXmlHandler = new XmlHandler( this, aFileName );
799 // ask XML handler to create actions
800 myXmlHandler->createActions();
804 class InitializeReq : public PyInterp_Request
807 InitializeReq( PyModuleHelper* _helper,
808 CAM_Application* _app )
809 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
814 virtual void execute()
816 myHelper->internalInitialize( myApp );
819 PyModuleHelper* myHelper;
820 CAM_Application* myApp;
824 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( this, app ) );
828 \brief Activation of the module.
830 This function is usually used in order to show the module's
831 specific menus and toolbars, update actions state and perform
832 other such actions required when the module is activated.
834 \note Returning \c false from this function prevents the
837 \param study parent study
838 \return \c true if activation is successful and \c false otherwise
840 bool PyModuleHelper::activate( SUIT_Study* study )
842 FuncMsg fmsg( "PyModuleHelper::activate()" );
844 // reset the activation status to the default value
845 myLastActivateStatus = true;
847 class ActivateReq : public PyInterp_Request
850 ActivateReq( PyModuleHelper* _helper,
853 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
856 myCustomize( _customize )
859 virtual void execute()
862 myHelper->internalActivate( myStudy ); // first activation stage
864 myHelper->internalCustomize( myStudy ); // second activation stage
867 PyModuleHelper* myHelper;
872 // post request for activation (customize=false)
873 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, false ) );
875 // check activation status (can be set to false by internalActivate())
876 if ( myLastActivateStatus ) {
877 // activate menus, toolbars, etc
878 if ( myXmlHandler ) myXmlHandler->activateMenus( true );
880 // show menus / toolbars
881 myModule->setMenuShown( true );
882 myModule->setToolShown( true );
884 // post request for customization (customize=true)
885 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, true ) );
887 // check activation status (can be set to false by internalCustomize())
888 if ( myLastActivateStatus ) {
889 // connect preferences changing signal
890 connect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
891 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
893 // connect active view change signal
894 SUIT_Desktop* d = study->application()->desktop();
895 connect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
896 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
897 // if active window exists, call activeViewChanged() function;
898 // temporary solution: if a getActiveView() in SalomePyQt available
899 // we no longer need this
900 SUIT_ViewWindow* view = d->activeWindow();
901 if ( view ) activeViewChanged( view );
902 // get all view currently opened in the study and connect their signals to
903 // the corresponding slots of the class.
904 foreach ( view, d->windows() ) connectView( view );
907 // hide menus / toolbars in case of error
908 myModule->setMenuShown( false );
909 myModule->setToolShown( false );
913 return myLastActivateStatus;
917 \brief Deactivation of the module.
919 This function is usually used in order to hide the module's
920 specific menus and toolbars and perform other such actions
921 required when the module is deactivated.
923 \param study parent study
924 \return \c true if deactivation is successful and \c false otherwise
926 bool PyModuleHelper::deactivate( SUIT_Study* study )
928 FuncMsg fmsg( "PyModuleHelper::deactivate()" );
930 class DeactivateReq : public PyInterp_LockRequest
933 DeactivateReq( PyInterp_Interp* _py_interp,
934 PyModuleHelper* _helper,
936 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
941 virtual void execute()
943 myHelper->internalDeactivate( myStudy );
946 PyModuleHelper* myHelper;
951 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, this, study ) );
953 // disconnect preferences changing signal
954 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
955 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
957 // disconnect the SUIT_Desktop signal windowActivated()
958 SUIT_Desktop* d = study->application()->desktop();
959 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
960 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
962 // deactivate menus, toolbars, etc
963 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
965 // hide menus / toolbars
966 myModule->setMenuShown( false );
967 myModule->setToolShown( false );
973 \brief Close of the module.
975 This function is usually used in order to close the module's
976 specific menus and toolbars and perform other such actions
977 required when the module is closed.
979 void PyModuleHelper::modelClosed( SUIT_Study* study )
981 FuncMsg fmsg( "PyModuleHelper::modelClosed()" );
983 class StudyClosedReq : public PyInterp_LockRequest
986 StudyClosedReq( PyInterp_Interp* _py_interp,
987 PyModuleHelper* _helper,
989 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
994 virtual void execute()
996 myHelper->internalClosedStudy( myStudy );
999 PyModuleHelper* myHelper;
1000 SUIT_Study* myStudy;
1004 PyInterp_Dispatcher::Get()->Exec( new StudyClosedReq( myInterp, this, study ) );
1006 // disconnect preferences changing signal
1007 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
1008 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
1010 // disconnect the SUIT_Desktop signal windowActivated()
1011 SUIT_Desktop* d = study->application()->desktop();
1012 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1013 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
1015 // deactivate menus, toolbars, etc
1016 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
1018 // hide menus / toolbars
1019 myModule->setMenuShown( false );
1020 myModule->setToolShown( false );
1025 \brief Process module's preferences changing.
1027 Called when the module's own preferences are changed.
1029 \param section preference resources section
1030 \param parameter preference resources parameter name
1032 void PyModuleHelper::preferencesChanged( const QString& section,
1033 const QString& parameter )
1035 FuncMsg fmsg( "PyModuleHelper::preferencesChanged()" );
1037 class PrefChangeReq : public PyInterp_LockRequest
1040 PrefChangeReq( PyInterp_Interp* _py_interp,
1041 PyModuleHelper* _helper,
1042 const QString& _section,
1043 const QString& _parameter )
1044 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1045 myHelper ( _helper ),
1046 mySection( _section ),
1047 myParameter( _parameter )
1050 virtual void execute()
1052 myHelper->internalPreferencesChanged( mySection, myParameter );
1055 PyModuleHelper* myHelper;
1056 QString mySection, myParameter;
1059 // post the request only if dispatcher is not busy!
1060 // execute request synchronously
1061 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1062 PyInterp_Dispatcher::Get()->Exec( new PrefChangeReq( myInterp, this, section, parameter ) );
1066 \brief Process application preferences changing.
1068 Called when any application setting is changed.
1070 \param module preference module
1071 \param section preference resources section
1072 \param parameter preference resources parameter name
1074 void PyModuleHelper::preferenceChanged( const QString& module,
1075 const QString& section,
1076 const QString& parameter )
1078 FuncMsg fmsg( "PyModuleHelper::preferenceChanged()" );
1080 // module's own preferences are processed by other preferencesChanged() method
1081 if ( module != myModule->moduleName() ) {
1083 preferencesChanged( section, parameter );
1088 \brief Process study activation.
1090 Called when study desktop is activated. Used for notifying the Python
1091 module about changing of the active study.
1093 \param study study being activated
1095 void PyModuleHelper::studyActivated( SUIT_Study* study )
1097 FuncMsg fmsg( "PyModuleHelper::studyActivated()" );
1099 // StudyChangedReq: request class for internal studyChanged() operation
1100 class StudyChangedReq : public PyInterp_Request
1103 StudyChangedReq( PyModuleHelper* _helper,
1104 SUIT_Study* _study )
1105 : PyInterp_Request(0, true ), // this request should be processed synchronously (sync == true)
1106 myHelper( _helper ),
1110 virtual void execute()
1112 myHelper->internalStudyChanged( myStudy );
1115 PyModuleHelper* myHelper;
1116 SUIT_Study* myStudy;
1120 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( this, study ) );
1124 \brief Process action activation.
1126 Called when action is activated. Used for notifying the Python
1127 module about any related action activation.
1131 void PyModuleHelper::actionActivated()
1133 FuncMsg fmsg( "PyModuleHelper::actionActivated()" );
1135 // perform synchronous request to Python event dispatcher
1136 class ActionReq : public PyInterp_LockRequest
1139 ActionReq( PyInterp_Interp* _py_interp,
1140 PyModuleHelper* _helper,
1142 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1143 myHelper( _helper ),
1147 virtual void execute()
1149 myHelper->internalActionActivated( myId );
1152 PyModuleHelper* myHelper;
1156 // get sender action
1157 QAction* action = qobject_cast<QAction*>( sender() );
1162 PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
1166 \brief Process context popup menu request.
1168 Called when user activates popup menu in some window
1169 (view, object browser, etc).
1171 \param context popup menu context (e.g. "ObjectBrowser")
1172 \param menu popup menu
1174 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1176 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1178 class ContextMenuReq : public PyInterp_LockRequest
1181 ContextMenuReq( PyInterp_Interp* _py_interp,
1182 PyModuleHelper* _helper,
1183 const QString& _context,
1185 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1186 myHelper ( _helper ),
1187 myContext( _context ),
1191 virtual void execute()
1193 myHelper->internalContextMenu( myContext, myMenu );
1196 PyModuleHelper* myHelper;
1201 // post request only if dispatcher is not busy!
1202 // execute request synchronously
1203 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1204 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1208 \brief Export preferences for the Python module.
1209 Called only once when the first instance of the module is created or
1210 when common Preferences dialog box is first time invoked.
1212 void PyModuleHelper::createPreferences()
1214 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1216 // temporary set myInitModule because createPreferences() method
1217 // might be called during the module intialization process
1218 InitLocker lock( myModule );
1220 class CreatePrefReq : public PyInterp_LockRequest
1223 CreatePrefReq( PyInterp_Interp* _py_interp,
1224 PyModuleHelper* _helper )
1225 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1229 virtual void execute()
1231 myHelper->internalCreatePreferences();
1234 PyModuleHelper* myHelper;
1237 // post request only if dispatcher is not busy!
1238 // execute request synchronously
1239 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1240 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1244 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1246 Used to notify Python module that active view has been changed by the user.
1248 \param view view being activated
1250 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1252 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1254 // perform synchronous request to Python event dispatcher
1255 class ActiveViewChangeReq : public PyInterp_LockRequest
1258 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1259 PyModuleHelper* _helper,
1260 SUIT_ViewWindow* _view )
1261 : PyInterp_LockRequest( _py_interp, 0, true ),
1262 myHelper( _helper ),
1266 virtual void execute()
1268 myHelper->internalActiveViewChanged( myView );
1271 PyModuleHelper* myHelper;
1272 SUIT_ViewWindow* myView;
1275 // connect view (if it is not connected yet)
1276 connectView( view );
1278 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1282 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1283 \param view view being closed
1285 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1287 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1289 class TryCloseViewReq : public PyInterp_LockRequest
1292 TryCloseViewReq( PyInterp_Interp* _py_interp,
1293 PyModuleHelper* _helper,
1294 SUIT_ViewWindow* _view )
1295 : PyInterp_LockRequest( _py_interp, 0, true ),
1296 myHelper( _helper ),
1300 virtual void execute()
1302 myHelper->internalTryCloseView( myView );
1305 PyModuleHelper* myHelper;
1306 SUIT_ViewWindow* myView;
1309 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1313 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1314 \param view view being closed
1316 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1318 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1320 class CloseViewReq : public PyInterp_LockRequest
1323 CloseViewReq( PyInterp_Interp* _py_interp,
1324 PyModuleHelper* _helper,
1325 SUIT_ViewWindow* _view )
1326 : PyInterp_LockRequest( _py_interp, 0, true ),
1327 myHelper( _helper ),
1331 virtual void execute()
1333 myHelper->internalCloseView( myView );
1336 PyModuleHelper* myHelper;
1337 SUIT_ViewWindow* myView;
1340 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1344 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1345 \param view view being cloned
1347 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1349 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1351 class CloneViewReq : public PyInterp_LockRequest
1354 CloneViewReq( PyInterp_Interp* _py_interp,
1355 PyModuleHelper* _helper,
1356 SUIT_ViewWindow* _view )
1357 : PyInterp_LockRequest( _py_interp, 0, true ),
1358 myHelper( _helper ),
1362 virtual void execute()
1364 myHelper->internalCloneView( myView );
1367 PyModuleHelper* myHelper;
1368 SUIT_ViewWindow* myView;
1371 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1375 \brief Save module data. Called when user saves study.
1376 \param files output list of files where module stores data
1378 void PyModuleHelper::save( QStringList& files )
1380 FuncMsg fmsg( "PyModuleHelper::save()" );
1382 // temporary set myInitModule because save() method
1383 // might be called by the framework when this module is inactive,
1384 // but still it should be possible to access this module's data
1386 InitLocker lock( myModule );
1388 // perform synchronous request to Python event dispatcher
1389 class SaveReq: public PyInterp_LockRequest
1392 SaveReq( PyInterp_Interp* _py_interp,
1393 PyModuleHelper* _helper,
1394 QStringList& _files )
1395 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1396 myHelper( _helper ) ,
1400 virtual void execute()
1402 myHelper->internalSave( myFiles );
1405 PyModuleHelper* myHelper;
1406 QStringList& myFiles;
1409 // Posting the request only if dispatcher is not busy!
1410 // Executing the request synchronously
1411 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1412 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files ) );
1416 \brief Load module data. Called when user opens study
1417 and activates module.
1418 \param files list of files where module data is stored
1420 bool PyModuleHelper::load( const QStringList& files )
1422 FuncMsg fmsg( "PyModuleHelper::load()" );
1424 bool loaded = false;
1426 class LoadReq: public PyInterp_LockRequest
1429 LoadReq( PyInterp_Interp* _py_interp,
1430 PyModuleHelper* _helper,
1433 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1434 myHelper( _helper ) ,
1439 virtual void execute()
1441 myHelper->internalLoad( myFiles, myLoaded );
1444 PyModuleHelper* myHelper;
1445 QStringList myFiles;
1449 // Posting the request only if dispatcher is not busy!
1450 // Executing the request synchronously
1451 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1452 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, loaded ) );
1458 \brief Dump module data to the Python script.
1459 Called when user activates dump study operation.
1460 \param files output list of files where module stores python script
1462 void PyModuleHelper::dumpPython( QStringList& files )
1464 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1466 // temporary set myInitModule because dumpPython() method
1467 // might be called by the framework when this module is inactive,
1468 // but still it should be possible to access this module's data
1470 InitLocker lock( myModule );
1472 class DumpPythonReq: public PyInterp_LockRequest
1475 DumpPythonReq( PyInterp_Interp* _py_interp,
1476 PyModuleHelper* _helper,
1477 QStringList& _files )
1478 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1479 myHelper( _helper ) ,
1483 virtual void execute()
1485 myHelper->internalDumpPython( myFiles );
1488 PyModuleHelper* myHelper;
1489 QStringList& myFiles;
1492 // Posting the request only if dispatcher is not busy!
1493 // Executing the request synchronously
1494 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1495 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1499 \brief Test if object \a what can be dragged by the user.
1500 \param what data object being tested
1501 \return \c true if object can be dragged or \c false otherwise
1503 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1505 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1507 bool draggable = false;
1509 // perform synchronous request to Python event dispatcher
1510 class IsDraggableReq: public PyInterp_LockRequest
1513 IsDraggableReq( PyInterp_Interp* _py_interp,
1514 PyModuleHelper* _helper,
1515 LightApp_DataObject* _data_object,
1516 bool& _is_draggable )
1517 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1518 myHelper( _helper ) ,
1519 myDataObject( _data_object ),
1520 myIsDraggable( _is_draggable )
1523 virtual void execute()
1525 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1528 PyModuleHelper* myHelper;
1529 LightApp_DataObject* myDataObject;
1530 bool& myIsDraggable;
1533 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1534 if ( data_object ) {
1535 // Posting the request only if dispatcher is not busy!
1536 // Executing the request synchronously
1537 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1538 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1539 const_cast<PyModuleHelper*>( this ),
1540 const_cast<LightApp_DataObject*>( data_object ),
1548 \brief Test if drop operation can be done on the \a where object.
1549 \param where data object being tested
1550 \return \c true if if drop operation is supported by object or \c false otherwise
1552 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1554 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1556 bool dropAccepted = false;
1558 // perform synchronous request to Python event dispatcher
1559 class IsDropAcceptedReq: public PyInterp_LockRequest
1562 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1563 PyModuleHelper* _helper,
1564 LightApp_DataObject* _data_object,
1565 bool& _is_drop_accepted )
1566 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1567 myHelper( _helper ) ,
1568 myDataObject( _data_object ),
1569 myIsDropAccepted( _is_drop_accepted )
1572 virtual void execute()
1574 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1577 PyModuleHelper* myHelper;
1578 LightApp_DataObject* myDataObject;
1579 bool& myIsDropAccepted;
1582 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1583 if ( data_object ) {
1584 // Posting the request only if dispatcher is not busy!
1585 // Executing the request synchronously
1586 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1587 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1588 const_cast<PyModuleHelper*>( this ),
1589 const_cast<LightApp_DataObject*>( data_object ),
1593 return dropAccepted;
1597 \brief Perform drop operation
1598 \param what list of data objects being dropped
1599 \param where target data object for drop operation
1600 \param row line (child item index) where drop operation is performed to
1601 \param action current drop action (copy or move)
1603 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1604 const int row, Qt::DropAction action )
1606 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1608 // perform synchronous request to Python event dispatcher
1609 class DropObjectsReq: public PyInterp_LockRequest
1612 DropObjectsReq( PyInterp_Interp* _py_interp,
1613 PyModuleHelper* _helper,
1614 const DataObjectList& _what,
1615 SUIT_DataObject* _where,
1617 Qt::DropAction _action )
1618 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1619 myHelper( _helper ) ,
1623 myAction ( _action )
1626 virtual void execute()
1628 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1631 PyModuleHelper* myHelper;
1632 DataObjectList myWhat;
1633 SUIT_DataObject* myWhere;
1635 Qt::DropAction myAction;
1638 // Posting the request only if dispatcher is not busy!
1639 // Executing the request synchronously
1640 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1641 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1645 \brief Get module engine IOR
1646 \return engine IOR as it is supplied by GUI Python module
1648 QString PyModuleHelper::engineIOR() const
1650 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1652 class EngineIORReq : public PyInterp_LockRequest
1655 EngineIORReq( PyInterp_Interp* _py_interp,
1656 PyModuleHelper* _helper,
1658 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1659 myHelper( _helper ),
1663 virtual void execute()
1665 myIOR = myHelper->internalEngineIOR();
1668 PyModuleHelper* myHelper;
1672 static QString anIOR;
1674 if ( anIOR.isEmpty() ) {
1676 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1677 const_cast<PyModuleHelper*>( this ),
1685 \brief Initialize python subinterpreter (one per study).
1687 \param studyId study ID
1689 void PyModuleHelper::initInterp( int studyId )
1691 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1695 // Error! Study Id must not be 0!
1700 QMutexLocker ml( &myInitMutex );
1702 // try to find the subinterpreter
1703 if ( myInterpMap.contains( studyId ) ) {
1705 myInterp = myInterpMap[ studyId ];
1709 myInterp = new SALOME_PYQT_PyInterp();
1710 myInterp->initialize();
1711 myInterpMap[ studyId ] = myInterp;
1713 #ifndef GUI_DISABLE_CORBA
1714 if ( !SUIT_PYTHON::initialized ) {
1715 // import 'salome' module and call 'salome_init' method;
1716 // do it only once on interpreter creation
1717 // ... first get python lock
1718 PyLockWrapper aLock; // Acquire GIL
1719 // ... then import a module
1720 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1726 // ... then call a method
1728 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
1739 \brief Import Python GUI module and store reference to the module.
1742 Warning! initInterp() should be called first!!!
1744 void PyModuleHelper::importModule()
1746 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1748 // check if the subinterpreter is initialized
1750 // Error! Python subinterpreter should be initialized first!
1755 // import Python GUI module and put it in <myPyModule> attribute
1756 // ... first get python lock
1757 PyLockWrapper aLock; // Acquire GIL
1758 // ... then import a module
1759 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1761 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1766 if ( !myPyModule ) {
1774 \brief Set study workspace to the Python module.
1777 Calls setWorkSpace() method of the Python module with
1778 PyQt QWidget object to use with interpreter.
1780 Attention! initInterp() and importModule() should be called first!!!
1782 void PyModuleHelper::setWorkSpace()
1784 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1786 if ( !IsCallOldMethods )
1789 // check if the subinterpreter is initialized and Python module is imported
1790 if ( !myInterp || !myPyModule ) {
1791 // Error! Python subinterpreter should be initialized and module should be imported first!
1795 // call setWorkSpace() method
1796 // ... first get python lock
1797 PyLockWrapper aLock; // Acquire GIL
1799 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1800 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1807 // ... then get workspace object
1808 QWidget* aWorkspace = 0;
1809 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1810 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1812 aWorkspace = d->workspace();
1814 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1815 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1817 aWorkspace = d->workstack();
1819 #if SIP_VERSION < 0x040800
1820 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1822 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1824 // ... and finally call Python module's setWorkSpace() method (obsolete)
1825 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1826 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1834 \brief Initialization callback function
1837 Performs the following actions:
1838 - initialize or get the Python interpreter (one per study)
1839 - import the Python module
1840 - pass the workspace widget to the Python module
1841 - call Python module's initialize() method
1842 - call Python module's windows() method
1843 - call Python module's views() method
1845 \param app parent application object
1847 void PyModuleHelper::internalInitialize( CAM_Application* app )
1849 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1851 // reset interpreter to NULL
1855 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1858 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1861 int aStudyId = aStudy ? aStudy->id() : 0;
1863 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1864 initInterp( aStudyId );
1868 // import Python GUI module
1873 // then call Python module's initialize() method
1874 // ... first get python lock
1875 PyLockWrapper aLock; // Acquire GIL
1877 // ... (the Python module is already imported)
1878 // ... finally call Python module's initialize() method
1879 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1880 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1886 // get required dockable windows list from the Python module
1887 // by calling windows() method
1888 // ... first put default values
1889 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1890 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1891 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1893 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1894 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1899 myWindowsMap.clear();
1900 if ( PyDict_Check( res1 ) ) {
1904 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1905 // parse the return value
1906 // it should be a map: {integer:integer}
1908 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
1909 aKey = PyInt_AsLong( key );
1910 aValue = PyInt_AsLong( value );
1911 myWindowsMap[ aKey ] = aValue;
1918 // get compatible view windows types from the Python module
1919 // by calling views() method
1920 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1921 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1926 // parse the return value
1927 // result can be one string...
1928 if ( PyString_Check( res2 ) ) {
1929 myViewMgrList.append( PyString_AsString( res2 ) );
1931 // ... or list of strings
1932 else if ( PyList_Check( res2 ) ) {
1933 int size = PyList_Size( res2 );
1934 for ( int i = 0; i < size; i++ ) {
1935 PyObject* value = PyList_GetItem( res2, i );
1936 if( value && PyString_Check( value ) ) {
1937 myViewMgrList.append( PyString_AsString( value ) );
1946 \brief Activation callback function
1949 Performs the following actions:
1950 - initialize or get the Python interpreter (one per study)
1951 - import the Python GUI module
1952 - call Python module's activate() method
1954 \param study parent study
1956 void PyModuleHelper::internalActivate( SUIT_Study* study )
1958 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1961 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1962 int aStudyId = aStudy ? aStudy->id() : 0;
1964 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1965 initInterp( aStudyId );
1967 myLastActivateStatus = false;
1971 // import Python GUI module
1973 if ( !myPyModule ) {
1974 myLastActivateStatus = false;
1979 PyLockWrapper aLock; // Acquire GIL
1981 // call Python module's activate() method (for the new modules)
1982 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
1983 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
1984 if ( !res1 || !PyBool_Check( res1 ) ) {
1986 // always true for old modules (no return value)
1987 myLastActivateStatus = true;
1990 // detect return status
1991 myLastActivateStatus = PyObject_IsTrue( res1 );
1997 \brief Additional menu customization callback function
2000 Performs the following actions:
2001 - get the Python interpreter (one per study)
2002 - import the Python GUI module
2003 - call Python module's setSettings() method (obsolete function,
2004 used for compatibility with old code)
2006 \param study parent study
2008 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2010 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2013 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2014 int aStudyId = aStudy ? aStudy->id() : 0;
2016 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2017 initInterp( aStudyId );
2019 myLastActivateStatus = false;
2023 // import Python GUI module
2025 if ( !myPyModule ) {
2026 myLastActivateStatus = false;
2030 // call Python module's setWorkSpace() method (obsolete)
2034 PyLockWrapper aLock; // Acquire GIL
2036 if ( IsCallOldMethods ) {
2037 // call Python module's setSettings() method (obsolete)
2038 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2039 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2043 myLastActivateStatus = myLastActivateStatus && true;
2049 \brief Deactivation callback function
2052 Performs the following actions:
2053 - call Python module's deactivate() method
2055 \param study parent study
2057 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2059 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2061 // check that Python subinterpreter is initialized and Python module is imported
2062 if ( !myInterp || !myPyModule ) {
2063 // Error! Python subinterpreter should be initialized and module should be imported first!
2066 // then call Python module's deactivate() method
2067 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2068 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2076 \brief Internal closure:
2078 Performs the following actions:
2079 - call Python module's closeStudy() method
2081 \param theStudy parent study object
2083 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2085 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2089 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2090 int aStudyId = aStudy ? aStudy->id() : 0;
2092 // check that Python subinterpreter is initialized and Python module is imported
2093 if ( !myInterp || !myPyModule ) {
2094 // Error! Python subinterpreter should be initialized and module should be imported first!
2097 // then call Python module's deactivate() method
2098 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2099 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i", aStudyId ) );
2109 \brief Preference changing callback function.
2112 Performs the following actions:
2113 - call Python module's preferenceChanged() method
2115 \param section resources section name
2116 \param setting resources parameter name
2118 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2120 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2122 // check that Python subinterpreter is initialized and Python module is imported
2123 if ( !myInterp || !myPyModule ) {
2124 // Error! Python subinterpreter should be initialized and module should be imported first!
2128 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2129 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2130 (char*)"preferenceChanged",
2132 section.toLatin1().constData(),
2133 setting.toLatin1().constData() ) );
2141 \brief Active study change callback function.
2144 Called when active the study is actived (user brings its
2146 - initialize or get the Python interpreter (one per study)
2147 - import the Python GUI module
2148 - call Python module's activeStudyChanged() method
2150 \param study study being activated
2152 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2154 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2157 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2158 int id = aStudy ? aStudy->id() : 0;
2160 fmsg.message( QString( "study id = %1" ).arg( id ) );
2162 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2167 // import Python GUI module
2172 // call Python module's setWorkSpace() method
2176 PyLockWrapper aLock; // Acquire GIL
2178 // call Python module's activeStudyChanged() method
2179 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2180 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
2188 \brief GUI event handling callback function
2191 Performs the following actions:
2192 - calls Python module's OnGUIEvent() method
2194 \param id GUI action ID
2196 void PyModuleHelper::internalActionActivated( int id )
2198 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2199 fmsg.message( QString( "action id = %1" ).arg( id ) );
2201 // Python interpreter should be initialized and Python module should be
2203 if ( !myInterp || !myPyModule )
2206 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2207 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2215 \brief Context popup menu handling callback function
2218 Performs the following actions:
2219 - calls Python module's definePopup(...) method (obsolete function,
2220 used for compatibility with old code) to define the popup menu context
2221 - parses XML resourses file (if exists) and fills the popup menu with the items)
2222 - calls Python module's customPopup(...) method (obsolete function,
2223 used for compatibility with old code) to allow module to customize the popup menu
2224 - for new modules calls createPopupMenu() function to allow the
2225 modules to build the popup menu by using insertItem(...) Qt functions.
2227 \param context popup menu context
2228 \param menu popup menu
2230 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2232 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2233 fmsg.message( QString( "context: %1" ).arg( context ) );
2235 // Python interpreter should be initialized and Python module should be
2237 if ( !myInterp || !myPyModule )
2240 QString aContext( "" ), aObject( "" ), aParent( context );
2242 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2243 // call definePopup() Python module's function
2244 // this is obsolete function, used only for compatibility reasons
2245 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2246 (char*)"definePopup",
2248 context.toLatin1().constData(),
2249 aObject.toLatin1().constData(),
2250 aParent.toLatin1().constData() ) );
2255 // parse return value
2257 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2263 } // if ( IsCallOldMethods ... )
2265 // first try to create menu via XML parser:
2266 // we create popup menus without help of QtxPopupMgr
2268 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2270 #if SIP_VERSION < 0x040800
2271 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2273 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2276 // then call Python module's createPopupMenu() method (for new modules)
2277 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2278 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2279 (char*)"createPopupMenu",
2282 context.toLatin1().constData() ) );
2288 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2289 // call customPopup() Python module's function
2290 // this is obsolete function, used only for compatibility reasons
2291 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2292 (char*)"customPopup",
2295 aContext.toLatin1().constData(),
2296 aObject.toLatin1().constData(),
2297 aParent.toLatin1().constData() ) );
2305 \brief Preferences initialization callback function.
2308 Performs the following actions:
2309 - calls Python module's createPreferences() method
2311 void PyModuleHelper::internalCreatePreferences()
2313 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2315 // Python interpreter should be initialized and Python module should be
2317 if ( !myInterp || !myPyModule )
2320 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2321 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2329 \brief Active view changing callback function
2331 \param view view being activated
2333 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2335 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2337 if ( !myInterp || !myPyModule || !view )
2340 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2342 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2343 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2351 \brief View closing callback function
2353 \param view view user tries to close
2355 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2357 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2359 if ( !myInterp || !myPyModule || !view )
2362 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2364 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2366 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2375 \brief View closing callback function
2377 \param view view being closed
2379 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2381 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2383 if ( !myInterp || !myPyModule || !view )
2386 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2388 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2390 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2399 \brief View cloning callback function
2401 \param view view being cloned
2403 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2405 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2407 if ( !myInterp || !myPyModule || !view )
2410 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2412 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2414 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2421 \brief Module data saving callback function.
2423 \param files output list of files where module stores data
2425 void PyModuleHelper::internalSave( QStringList& files )
2427 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2429 // Python interpreter should be initialized and Python module should be
2431 // files list should contain a path to the temporary directory as a first entry
2432 if ( !myInterp || !myPyModule || files.isEmpty() )
2435 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2437 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2438 (char*)"s", files.first().toLatin1().constData() ) );
2444 // parse the return value
2445 // result can be one string...
2446 if ( PyString_Check( res ) ) {
2447 QString astr = PyString_AsString( res );
2448 files.append( astr );
2450 //also result can be a list...
2451 else if ( PyList_Check( res ) ) {
2452 int size = PyList_Size( res );
2453 for ( int i = 0; i < size; i++ ) {
2454 PyObject* value = PyList_GetItem( res, i );
2455 if ( value && PyString_Check( value ) ) {
2456 files.append( PyString_AsString( value ) );
2465 \brief Module data loading callback function.
2467 \param files list of files where module data is stored
2468 \param opened output success flag
2470 void PyModuleHelper::internalLoad( const QStringList& files, bool& opened )
2472 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2474 // Python interpreter should be initialized and Python module should be
2476 if ( !myInterp || !myPyModule || files.isEmpty() )
2479 QStringList* theList = new QStringList( files );
2481 #if SIP_VERSION < 0x040800
2482 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2484 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2486 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2487 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2488 (char*)"O", sipList.get()));
2489 if( !res || !PyBool_Check( res )) {
2494 opened = PyObject_IsTrue( res );
2500 \brief Module dump python callback function.
2502 \param files output list of files where module stores python script
2504 void PyModuleHelper::internalDumpPython( QStringList& files )
2506 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2508 // Python interpreter should be initialized and Python module should be
2510 // files list should contain a path to the temporary directory
2511 if ( !myInterp || !myPyModule || files.isEmpty() )
2514 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2515 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2516 (char*)"s", files.first().toLatin1().constData()));
2522 // parse the return value
2523 // result can be one string...
2524 if ( PyString_Check( res ) ) {
2525 QString astr = PyString_AsString( res );
2529 //also result can be a list...
2530 else if ( PyList_Check( res ) ) {
2531 int size = PyList_Size( res );
2532 for ( int i = 0; i < size; i++ ) {
2533 PyObject* value = PyList_GetItem( res, i );
2534 if( value && PyString_Check( value ) ) {
2535 files.append( PyString_AsString( value ) );
2544 \brief Check data object's 'draggable' status callback function.
2546 \param what data object being tested
2547 \return \c true if object can be dragged or \c false otherwise
2549 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2551 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2553 // Python interpreter should be initialized and Python module should be
2555 if ( !myInterp || !myPyModule || !what )
2558 bool draggable = false;
2560 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2561 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2562 (char*)"s", what->entry().toLatin1().constData() ) );
2563 if( !res || !PyBool_Check( res )) {
2568 draggable = PyObject_IsTrue( res );
2576 \brief Check data object's 'drop allowed' status callback function.
2578 \param where data object being tested
2579 \return \c true if if drop operation is supported by object or \c false otherwise
2581 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2583 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2585 // Python interpreter should be initialized and Python module should be
2587 if ( !myInterp || !myPyModule || !where )
2590 bool dropAccepted = false;
2592 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2593 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2594 (char*)"s", where->entry().toLatin1().constData() ) );
2595 if( !res || !PyBool_Check( res )) {
2597 dropAccepted = false;
2600 dropAccepted = PyObject_IsTrue( res );
2604 return dropAccepted;
2608 \brief Data dropping callback function.
2610 \param what list of data objects being dropped
2611 \param where target data object for drop operation
2612 \param row line (child item index) where drop operation is performed to
2613 \param action current drop action (copy or move)
2615 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2616 const int row, Qt::DropAction action )
2618 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2620 // Python interpreter should be initialized and Python module should be
2622 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2625 QStringList* theList = new QStringList();
2627 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2628 if ( !whereObject ) return;
2630 for ( int i = 0; i < what.count(); i++ ) {
2631 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2632 if ( dataObject ) theList->append( dataObject->entry() );
2635 #if SIP_VERSION < 0x040800
2636 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2638 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2640 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2641 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2643 whereObject->entry().toLatin1().constData(),
2653 \brief Get engine IOR callback function
2656 Tries to get engine IOR from the Python module using engineIOR() function.
2657 That function can load module engine using appropriate container if required.
2659 \return engine IOR or empty string if it is not provided by Python module
2661 QString PyModuleHelper::internalEngineIOR() const
2663 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2667 // Python interpreter should be initialized and Python module should be
2669 if ( myInterp && myModule ) {
2670 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2671 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2676 // parse the return value, result chould be string
2677 if ( PyString_Check( res ) ) {
2678 ior = PyString_AsString( res );
2687 \brief Connects signals about activating and cloning view on internal slots
2688 \param view view being connected
2690 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2692 SUIT_ViewManager* viewMgr = view->getViewManager();
2693 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2695 // Connect tryCloseView() and deleteView() signals
2697 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2698 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2699 Qt::UniqueConnection );
2700 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2701 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2702 Qt::UniqueConnection );
2705 // Connect cloneView() signal of an OCC View
2706 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2707 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2708 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2709 Qt::UniqueConnection );
2711 // Connect cloneView() signal of Plot2d View
2712 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2713 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2714 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2715 Qt::UniqueConnection );
2721 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2723 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2725 // Python interpreter should be initialized and Python module should be
2727 if ( !myInterp || !myPyModule )
2730 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2731 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
2740 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2742 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2744 // temporary set myInitModule because dumpPython() method
2745 // might be called by the framework when this module is inactive,
2746 // but still it should be possible to access this module's data
2748 InitLocker lock( myModule );
2750 class PythonReq: public PyInterp_LockRequest
2753 PythonReq( PyInterp_Interp* _py_interp,
2754 PyModuleHelper* _helper,
2755 const QString& _entry,
2757 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2758 myHelper( _helper ) ,
2763 virtual void execute()
2765 myHelper->internalOBClickedPython( myEntry, myColumn );
2768 PyModuleHelper* myHelper;
2773 // Posting the request only if dispatcher is not busy!
2774 // Executing the request synchronously
2775 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2776 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2777 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );