1 // Copyright (C) 2007-2012 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.
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 Makefile.
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 = myInterp->GetLockWrapper();
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 implementatino this method first tries to read
754 <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 Process module's preferences changing.
968 Called when the module's own preferences are changed.
970 \param section preference resources section
971 \param parameter preference resources parameter name
973 void PyModuleHelper::preferencesChanged( const QString& section,
974 const QString& parameter )
976 FuncMsg fmsg( "PyModuleHelper::preferencesChanged()" );
978 class PrefChangeReq : public PyInterp_LockRequest
981 PrefChangeReq( PyInterp_Interp* _py_interp,
982 PyModuleHelper* _helper,
983 const QString& _section,
984 const QString& _parameter )
985 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
986 myHelper ( _helper ),
987 mySection( _section ),
988 myParameter( _parameter )
991 virtual void execute()
993 myHelper->internalPreferencesChanged( mySection, myParameter );
996 PyModuleHelper* myHelper;
997 QString mySection, myParameter;
1000 // post the request only if dispatcher is not busy!
1001 // execute request synchronously
1002 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1003 PyInterp_Dispatcher::Get()->Exec( new PrefChangeReq( myInterp, this, section, parameter ) );
1007 \brief Process application preferences changing.
1009 Called when any application setting is changed.
1011 \param module preference module
1012 \param section preference resources section
1013 \param parameter preference resources parameter name
1015 void PyModuleHelper::preferenceChanged( const QString& module,
1016 const QString& section,
1017 const QString& parameter )
1019 FuncMsg fmsg( "PyModuleHelper::preferenceChanged()" );
1021 // module's own preferences are processed by other preferencesChanged() method
1022 if ( module != myModule->moduleName() ) {
1024 preferencesChanged( section, parameter );
1029 \brief Process study activation.
1031 Called when study desktop is activated. Used for notifying the Python
1032 module about changing of the active study.
1034 \param study study being activated
1036 void PyModuleHelper::studyActivated( SUIT_Study* study )
1038 FuncMsg fmsg( "PyModuleHelper::studyActivated()" );
1040 // StudyChangedReq: request class for internal studyChanged() operation
1041 class StudyChangedReq : public PyInterp_Request
1044 StudyChangedReq( PyModuleHelper* _helper,
1045 SUIT_Study* _study )
1046 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
1047 myHelper( _helper ),
1051 virtual void execute()
1053 myHelper->internalStudyChanged( myStudy );
1056 PyModuleHelper* myHelper;
1057 SUIT_Study* myStudy;
1061 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( this, study ) );
1065 \brief Process action activation.
1067 Called when action is activated. Used for notifying the Python
1068 module about any related action activation.
1072 void PyModuleHelper::actionActivated()
1074 FuncMsg fmsg( "PyModuleHelper::actionActivated()" );
1076 // perform synchronous request to Python event dispatcher
1077 class ActionReq : public PyInterp_LockRequest
1080 ActionReq( PyInterp_Interp* _py_interp,
1081 PyModuleHelper* _helper,
1083 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1084 myHelper( _helper ),
1088 virtual void execute()
1090 myHelper->internalActionActivated( myId );
1093 PyModuleHelper* myHelper;
1097 // get sender action
1098 QAction* action = qobject_cast<QAction*>( sender() );
1103 PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
1107 \brief Process context popup menu request.
1109 Called when user activates popup menu in some window
1110 (view, object browser, etc).
1112 \param context popup menu context (e.g. "ObjectBrowser")
1113 \param menu popup menu
1115 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1117 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1119 class ContextMenuReq : public PyInterp_LockRequest
1122 ContextMenuReq( PyInterp_Interp* _py_interp,
1123 PyModuleHelper* _helper,
1124 const QString& _context,
1126 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1127 myHelper ( _helper ),
1128 myContext( _context ),
1132 virtual void execute()
1134 myHelper->internalContextMenu( myContext, myMenu );
1137 PyModuleHelper* myHelper;
1142 // post request only if dispatcher is not busy!
1143 // execute request synchronously
1144 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1145 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1149 \brief Export preferences for the Python module.
1150 Called only once when the first instance of the module is created or
1151 when common Preferences dialog box is first time invoked.
1153 void PyModuleHelper::createPreferences()
1155 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1157 // temporary set myInitModule because createPreferences() method
1158 // might be called during the module intialization process
1159 InitLocker lock( myModule );
1161 class CreatePrefReq : public PyInterp_LockRequest
1164 CreatePrefReq( PyInterp_Interp* _py_interp,
1165 PyModuleHelper* _helper )
1166 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1170 virtual void execute()
1172 myHelper->internalCreatePreferences();
1175 PyModuleHelper* myHelper;
1178 // post request only if dispatcher is not busy!
1179 // execute request synchronously
1180 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1181 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1185 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1187 Used to notify Python module that active view has been changed by the user.
1189 \param view view being activated
1191 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1193 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1195 // perform synchronous request to Python event dispatcher
1196 class ActiveViewChangeReq : public PyInterp_LockRequest
1199 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1200 PyModuleHelper* _helper,
1201 SUIT_ViewWindow* _view )
1202 : PyInterp_LockRequest( _py_interp, 0, true ),
1203 myHelper( _helper ),
1207 virtual void execute()
1209 myHelper->internalActiveViewChanged( myView );
1212 PyModuleHelper* myHelper;
1213 SUIT_ViewWindow* myView;
1216 // connect view (if it is not connected yet)
1217 connectView( view );
1219 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1223 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1224 \param view view being closed
1226 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1228 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1230 class TryCloseViewReq : public PyInterp_LockRequest
1233 TryCloseViewReq( PyInterp_Interp* _py_interp,
1234 PyModuleHelper* _helper,
1235 SUIT_ViewWindow* _view )
1236 : PyInterp_LockRequest( _py_interp, 0, true ),
1237 myHelper( _helper ),
1241 virtual void execute()
1243 myHelper->internalTryCloseView( myView );
1246 PyModuleHelper* myHelper;
1247 SUIT_ViewWindow* myView;
1250 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1254 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1255 \param view view being closed
1257 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1259 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1261 class CloseViewReq : public PyInterp_LockRequest
1264 CloseViewReq( PyInterp_Interp* _py_interp,
1265 PyModuleHelper* _helper,
1266 SUIT_ViewWindow* _view )
1267 : PyInterp_LockRequest( _py_interp, 0, true ),
1268 myHelper( _helper ),
1272 virtual void execute()
1274 myHelper->internalCloseView( myView );
1277 PyModuleHelper* myHelper;
1278 SUIT_ViewWindow* myView;
1281 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1285 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1286 \param view view being cloned
1288 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1290 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1292 class CloneViewReq : public PyInterp_LockRequest
1295 CloneViewReq( PyInterp_Interp* _py_interp,
1296 PyModuleHelper* _helper,
1297 SUIT_ViewWindow* _view )
1298 : PyInterp_LockRequest( _py_interp, 0, true ),
1299 myHelper( _helper ),
1303 virtual void execute()
1305 myHelper->internalCloneView( myView );
1308 PyModuleHelper* myHelper;
1309 SUIT_ViewWindow* myView;
1312 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1316 \brief Save module data. Called when user saves study.
1317 \param files output list of files where module stores data
1319 void PyModuleHelper::save( QStringList& files )
1321 FuncMsg fmsg( "PyModuleHelper::save()" );
1323 // temporary set myInitModule because save() method
1324 // might be called by the framework when this module is inactive,
1325 // but still it should be possible to access this module's data
1327 InitLocker lock( myModule );
1329 // perform synchronous request to Python event dispatcher
1330 class SaveReq: public PyInterp_LockRequest
1333 SaveReq( PyInterp_Interp* _py_interp,
1334 PyModuleHelper* _helper,
1335 QStringList& _files )
1336 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1337 myHelper( _helper ) ,
1341 virtual void execute()
1343 myHelper->internalSave( myFiles );
1346 PyModuleHelper* myHelper;
1347 QStringList& myFiles;
1350 // Posting the request only if dispatcher is not busy!
1351 // Executing the request synchronously
1352 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1353 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files ) );
1357 \brief Load module data. Called when user opens study
1358 and activates module.
1359 \param files list of files where module data is stored
1361 bool PyModuleHelper::load( const QStringList& files )
1363 FuncMsg fmsg( "PyModuleHelper::load()" );
1365 bool loaded = false;
1367 class LoadReq: public PyInterp_LockRequest
1370 LoadReq( PyInterp_Interp* _py_interp,
1371 PyModuleHelper* _helper,
1374 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1375 myHelper( _helper ) ,
1380 virtual void execute()
1382 myHelper->internalLoad( myFiles, myLoaded );
1385 PyModuleHelper* myHelper;
1386 QStringList myFiles;
1390 // Posting the request only if dispatcher is not busy!
1391 // Executing the request synchronously
1392 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1393 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, loaded ) );
1399 \brief Dump module data to the Python script.
1400 Called when user activates dump study operation.
1401 \param files output list of files where module stores python script
1403 void PyModuleHelper::dumpPython( QStringList& files )
1405 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1407 // temporary set myInitModule because dumpPython() method
1408 // might be called by the framework when this module is inactive,
1409 // but still it should be possible to access this module's data
1411 InitLocker lock( myModule );
1413 class DumpPythonReq: public PyInterp_LockRequest
1416 DumpPythonReq( PyInterp_Interp* _py_interp,
1417 PyModuleHelper* _helper,
1418 QStringList& _files )
1419 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1420 myHelper( _helper ) ,
1424 virtual void execute()
1426 myHelper->internalDumpPython( myFiles );
1429 PyModuleHelper* myHelper;
1430 QStringList& myFiles;
1433 // Posting the request only if dispatcher is not busy!
1434 // Executing the request synchronously
1435 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1436 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1440 \brief Test if object \a what can be dragged by the user.
1441 \param what data object being tested
1442 \return \c true if object can be dragged or \c false otherwise
1444 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1446 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1448 bool draggable = false;
1450 // perform synchronous request to Python event dispatcher
1451 class IsDraggableReq: public PyInterp_LockRequest
1454 IsDraggableReq( PyInterp_Interp* _py_interp,
1455 PyModuleHelper* _helper,
1456 LightApp_DataObject* _data_object,
1457 bool& _is_draggable )
1458 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1459 myHelper( _helper ) ,
1460 myDataObject( _data_object ),
1461 myIsDraggable( _is_draggable )
1464 virtual void execute()
1466 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1469 PyModuleHelper* myHelper;
1470 LightApp_DataObject* myDataObject;
1471 bool& myIsDraggable;
1474 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1475 if ( data_object ) {
1476 // Posting the request only if dispatcher is not busy!
1477 // Executing the request synchronously
1478 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1479 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1480 const_cast<PyModuleHelper*>( this ),
1481 const_cast<LightApp_DataObject*>( data_object ),
1489 \brief Test if drop operation can be done on the \a where object.
1490 \param where data object being tested
1491 \return \c true if if drop operation is supported by object or \c false otherwise
1493 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1495 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1497 bool dropAccepted = false;
1499 // perform synchronous request to Python event dispatcher
1500 class IsDropAcceptedReq: public PyInterp_LockRequest
1503 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1504 PyModuleHelper* _helper,
1505 LightApp_DataObject* _data_object,
1506 bool& _is_drop_accepted )
1507 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1508 myHelper( _helper ) ,
1509 myDataObject( _data_object ),
1510 myIsDropAccepted( _is_drop_accepted )
1513 virtual void execute()
1515 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1518 PyModuleHelper* myHelper;
1519 LightApp_DataObject* myDataObject;
1520 bool& myIsDropAccepted;
1523 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1524 if ( data_object ) {
1525 // Posting the request only if dispatcher is not busy!
1526 // Executing the request synchronously
1527 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1528 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1529 const_cast<PyModuleHelper*>( this ),
1530 const_cast<LightApp_DataObject*>( data_object ),
1534 return dropAccepted;
1538 \brief Perform drop operation
1539 \param what list of data objects being dropped
1540 \param where target data object for drop operation
1541 \param row line (child item index) where drop operation is performed to
1542 \param action current drop action (copy or move)
1544 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1545 const int row, Qt::DropAction action )
1547 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1549 // perform synchronous request to Python event dispatcher
1550 class DropObjectsReq: public PyInterp_LockRequest
1553 DropObjectsReq( PyInterp_Interp* _py_interp,
1554 PyModuleHelper* _helper,
1555 const DataObjectList& _what,
1556 SUIT_DataObject* _where,
1558 Qt::DropAction _action )
1559 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1560 myHelper( _helper ) ,
1564 myAction ( _action )
1567 virtual void execute()
1569 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1572 PyModuleHelper* myHelper;
1573 DataObjectList myWhat;
1574 SUIT_DataObject* myWhere;
1576 Qt::DropAction myAction;
1579 // Posting the request only if dispatcher is not busy!
1580 // Executing the request synchronously
1581 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1582 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1586 \brief Get module engine IOR
1587 \return engine IOR as it is supplied by GUI Python module
1589 QString PyModuleHelper::engineIOR() const
1591 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1593 class EngineIORReq : public PyInterp_LockRequest
1596 EngineIORReq( PyInterp_Interp* _py_interp,
1597 PyModuleHelper* _helper,
1599 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1600 myHelper( _helper ),
1604 virtual void execute()
1606 myIOR = myHelper->internalEngineIOR();
1609 PyModuleHelper* myHelper;
1613 static QString anIOR;
1615 if ( anIOR.isEmpty() ) {
1617 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1618 const_cast<PyModuleHelper*>( this ),
1626 \brief Initialize python subinterpreter (one per study).
1628 \param studyId study ID
1630 void PyModuleHelper::initInterp( int studyId )
1632 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1636 // Error! Study Id must not be 0!
1641 QMutexLocker ml( &myInitMutex );
1643 // try to find the subinterpreter
1644 if ( myInterpMap.contains( studyId ) ) {
1646 myInterp = myInterpMap[ studyId ];
1650 myInterp = new SALOME_PYQT_PyInterp();
1651 myInterp->initialize();
1652 myInterpMap[ studyId ] = myInterp;
1654 #ifndef GUI_DISABLE_CORBA
1655 if ( !SUIT_PYTHON::initialized ) {
1656 // import 'salome' module and call 'salome_init' method;
1657 // do it only once on interpreter creation
1658 // ... first get python lock
1659 PyLockWrapper aLock = myInterp->GetLockWrapper();
1660 // ... then import a module
1661 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1667 // ... then call a method
1669 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
1680 \brief Import Python GUI module and store reference to the module.
1683 Attention! initInterp() should be called first!!!
1685 void PyModuleHelper::importModule()
1687 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1689 // check if the subinterpreter is initialized
1691 // Error! Python subinterpreter should be initialized first!
1696 // import Python GUI module and put it in <myPyModule> attribute
1697 // ... first get python lock
1698 PyLockWrapper aLock = myInterp->GetLockWrapper();
1699 // ... then import a module
1700 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1702 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1707 if ( !myPyModule ) {
1715 \brief Set study workspace to the Python module.
1718 Calls setWorkSpace() method of the Python module with
1719 PyQt QWidget object to use with interpreter.
1721 Attention! initInterp() and importModule() should be called first!!!
1723 void PyModuleHelper::setWorkSpace()
1725 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1727 if ( !IsCallOldMethods )
1730 // check if the subinterpreter is initialized and Python module is imported
1731 if ( !myInterp || !myPyModule ) {
1732 // Error! Python subinterpreter should be initialized and module should be imported first!
1736 // call setWorkSpace() method
1737 // ... first get python lock
1738 PyLockWrapper aLock = myInterp->GetLockWrapper();
1740 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1741 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1748 // ... then get workspace object
1749 QWidget* aWorkspace = 0;
1750 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1751 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1753 aWorkspace = d->workspace();
1755 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1756 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1758 aWorkspace = d->workstack();
1760 #if SIP_VERSION < 0x040800
1761 PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
1763 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1765 // ... and finally call Python module's setWorkSpace() method (obsolete)
1766 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1767 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1775 \brief Initialization callback function
1778 Performs the following actions:
1779 - initialize or get the Python interpreter (one per study)
1780 - import the Python module
1781 - pass the workspace widget to the Python module
1782 - call Python module's initialize() method
1783 - call Python module's windows() method
1784 - call Python module's views() method
1786 \param app parent application object
1788 void PyModuleHelper::internalInitialize( CAM_Application* app )
1790 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1792 // reset interpreter to NULL
1796 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1799 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1802 int aStudyId = aStudy ? aStudy->id() : 0;
1804 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1805 initInterp( aStudyId );
1809 // import Python GUI module
1814 // then call Python module's initialize() method
1815 // ... first get python lock
1816 PyLockWrapper aLock = myInterp->GetLockWrapper();
1818 // ... (the Python module is already imported)
1819 // ... finally call Python module's initialize() method
1820 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1821 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1827 // get required dockable windows list from the Python module
1828 // by calling windows() method
1829 // ... first put default values
1830 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1831 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1832 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1834 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1835 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1840 myWindowsMap.clear();
1841 if ( PyDict_Check( res1 ) ) {
1845 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1846 // parse the return value
1847 // it should be a map: {integer:integer}
1849 if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
1850 aKey = PyInt_AsLong( key );
1851 aValue = PyInt_AsLong( value );
1852 myWindowsMap[ aKey ] = aValue;
1859 // get compatible view windows types from the Python module
1860 // by calling views() method
1861 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1862 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1867 // parse the return value
1868 // result can be one string...
1869 if ( PyString_Check( res2 ) ) {
1870 myViewMgrList.append( PyString_AsString( res2 ) );
1872 // ... or list of strings
1873 else if ( PyList_Check( res2 ) ) {
1874 int size = PyList_Size( res2 );
1875 for ( int i = 0; i < size; i++ ) {
1876 PyObject* value = PyList_GetItem( res2, i );
1877 if( value && PyString_Check( value ) ) {
1878 myViewMgrList.append( PyString_AsString( value ) );
1887 \brief Activation callback function
1890 Performs the following actions:
1891 - initialize or get the Python interpreter (one per study)
1892 - import the Python GUI module
1893 - call Python module's activate() method
1895 \param study parent study
1897 void PyModuleHelper::internalActivate( SUIT_Study* study )
1899 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1902 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1903 int aStudyId = aStudy ? aStudy->id() : 0;
1905 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1906 initInterp( aStudyId );
1908 myLastActivateStatus = false;
1912 // import Python GUI module
1914 if ( !myPyModule ) {
1915 myLastActivateStatus = false;
1920 PyLockWrapper aLock = myInterp->GetLockWrapper();
1922 // call Python module's activate() method (for the new modules)
1923 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
1924 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
1925 if ( !res1 || !PyBool_Check( res1 ) ) {
1927 // always true for old modules (no return value)
1928 myLastActivateStatus = true;
1931 // detect return status
1932 myLastActivateStatus = PyObject_IsTrue( res1 );
1938 \brief Additional menu customization callback function
1941 Performs the following actions:
1942 - get the Python interpreter (one per study)
1943 - import the Python GUI module
1944 - call Python module's setSettings() method (obsolete function,
1945 used for compatibility with old code)
1947 \param study parent study
1949 void PyModuleHelper::internalCustomize( SUIT_Study* study )
1951 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
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;
1971 // call Python module's setWorkSpace() method (obsolete)
1975 PyLockWrapper aLock = myInterp->GetLockWrapper();
1977 if ( IsCallOldMethods ) {
1978 // call Python module's setSettings() method (obsolete)
1979 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
1980 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
1984 myLastActivateStatus = myLastActivateStatus && true;
1990 \brief Deactivation callback function
1993 Performs the following actions:
1994 - call Python module's deactivate() method
1996 \param study parent study
1998 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2000 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2002 // check that Python subinterpreter is initialized and Python module is imported
2003 if ( !myInterp || !myPyModule ) {
2004 // Error! Python subinterpreter should be initialized and module should be imported first!
2007 // then call Python module's deactivate() method
2008 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2009 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2017 \brief Preference changing callback function.
2020 Performs the following actions:
2021 - call Python module's preferenceChanged() method
2023 \param section resources section name
2024 \param setting resources parameter name
2026 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2028 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2030 // check that Python subinterpreter is initialized and Python module is imported
2031 if ( !myInterp || !myPyModule ) {
2032 // Error! Python subinterpreter should be initialized and module should be imported first!
2036 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2037 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2038 (char*)"preferenceChanged",
2040 section.toLatin1().constData(),
2041 setting.toLatin1().constData() ) );
2049 \brief Active study change callback function.
2052 Called when active the study is actived (user brings its
2054 - initialize or get the Python interpreter (one per study)
2055 - import the Python GUI module
2056 - call Python module's activeStudyChanged() method
2058 \param study study being activated
2060 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2062 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2065 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2066 int id = aStudy ? aStudy->id() : 0;
2068 fmsg.message( QString( "study id = %1" ).arg( id ) );
2070 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2075 // import Python GUI module
2080 // call Python module's setWorkSpace() method
2084 PyLockWrapper aLock = myInterp->GetLockWrapper();
2086 // call Python module's activeStudyChanged() method
2087 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2088 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
2096 \brief GUI event handling callback function
2099 Performs the following actions:
2100 - calls Python module's OnGUIEvent() method
2102 \param id GUI action ID
2104 void PyModuleHelper::internalActionActivated( int id )
2106 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2107 fmsg.message( QString( "action id = %1" ).arg( id ) );
2109 // Python interpreter should be initialized and Python module should be
2111 if ( !myInterp || !myPyModule )
2114 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2115 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2123 \brief Context popup menu handling callback function
2126 Performs the following actions:
2127 - calls Python module's definePopup(...) method (obsolete function,
2128 used for compatibility with old code) to define the popup menu context
2129 - parses XML resourses file (if exists) and fills the popup menu with the items)
2130 - calls Python module's customPopup(...) method (obsolete function,
2131 used for compatibility with old code) to allow module to customize the popup menu
2132 - for new modules calls createPopupMenu() function to allow the
2133 modules to build the popup menu by using insertItem(...) Qt functions.
2135 \param context popup menu context
2136 \param menu popup menu
2138 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2140 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2141 fmsg.message( QString( "context: %1" ).arg( context ) );
2143 // Python interpreter should be initialized and Python module should be
2145 if ( !myInterp || !myPyModule )
2148 QString aContext( "" ), aObject( "" ), aParent( context );
2150 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2151 // call definePopup() Python module's function
2152 // this is obsolete function, used only for compatibility reasons
2153 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2154 (char*)"definePopup",
2156 context.toLatin1().constData(),
2157 aObject.toLatin1().constData(),
2158 aParent.toLatin1().constData() ) );
2163 // parse return value
2165 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2171 } // if ( IsCallOldMethods ... )
2173 // first try to create menu via XML parser:
2174 // we create popup menus without help of QtxPopupMgr
2176 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2178 #if SIP_VERSION < 0x040800
2179 PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
2181 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2184 // then call Python module's createPopupMenu() method (for new modules)
2185 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2186 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2187 (char*)"createPopupMenu",
2190 context.toLatin1().constData() ) );
2196 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2197 // call customPopup() Python module's function
2198 // this is obsolete function, used only for compatibility reasons
2199 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2200 (char*)"customPopup",
2203 aContext.toLatin1().constData(),
2204 aObject.toLatin1().constData(),
2205 aParent.toLatin1().constData() ) );
2213 \brief Preferences initialization callback function.
2216 Performs the following actions:
2217 - calls Python module's createPreferences() method
2219 void PyModuleHelper::internalCreatePreferences()
2221 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2223 // Python interpreter should be initialized and Python module should be
2225 if ( !myInterp || !myPyModule )
2228 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2229 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2237 \brief Active view changing callback function
2239 \param view view being activated
2241 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2243 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2245 if ( !myInterp || !myPyModule || !view )
2248 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2250 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2251 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2259 \brief View closing callback function
2261 \param view view user tries to close
2263 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2265 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2267 if ( !myInterp || !myPyModule || !view )
2270 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2272 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2274 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2283 \brief View closing callback function
2285 \param view view being closed
2287 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2289 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2291 if ( !myInterp || !myPyModule || !view )
2294 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2296 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2298 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2307 \brief View cloning callback function
2309 \param view view being cloned
2311 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2313 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2315 if ( !myInterp || !myPyModule || !view )
2318 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2320 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2322 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2329 \brief Module data saving callback function.
2331 \param files output list of files where module stores data
2333 void PyModuleHelper::internalSave( QStringList& files )
2335 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2337 // Python interpreter should be initialized and Python module should be
2339 // files list should contain a path to the temporary directory as a first entry
2340 if ( !myInterp || !myPyModule || files.isEmpty() )
2343 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2345 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2346 (char*)"s", files.first().toLatin1().constData() ) );
2352 // parse the return value
2353 // result can be one string...
2354 if ( PyString_Check( res ) ) {
2355 QString astr = PyString_AsString( res );
2356 files.append( astr );
2358 //also result can be a list...
2359 else if ( PyList_Check( res ) ) {
2360 int size = PyList_Size( res );
2361 for ( int i = 0; i < size; i++ ) {
2362 PyObject* value = PyList_GetItem( res, i );
2363 if ( value && PyString_Check( value ) ) {
2364 files.append( PyString_AsString( value ) );
2373 \brief Module data loading callback function.
2375 \param files list of files where module data is stored
2376 \param opened output success flag
2378 void PyModuleHelper::internalLoad( const QStringList& files, bool& opened )
2380 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2382 // Python interpreter should be initialized and Python module should be
2384 if ( !myInterp || !myPyModule || files.isEmpty() )
2387 QStringList* theList = new QStringList( files );
2389 #if SIP_VERSION < 0x040800
2390 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
2392 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2394 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2395 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2396 (char*)"O", sipList.get()));
2397 if( !res || !PyBool_Check( res )) {
2402 opened = PyObject_IsTrue( res );
2408 \brief Module dump python callback function.
2410 \param files output list of files where module stores python script
2412 void PyModuleHelper::internalDumpPython( QStringList& files )
2414 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2416 // Python interpreter should be initialized and Python module should be
2418 // files list should contain a path to the temporary directory
2419 if ( !myInterp || !myPyModule || files.isEmpty() )
2422 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2423 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2424 (char*)"s", files.first().toLatin1().constData()));
2430 // parse the return value
2431 // result can be one string...
2432 if ( PyString_Check( res ) ) {
2433 QString astr = PyString_AsString( res );
2437 //also result can be a list...
2438 else if ( PyList_Check( res ) ) {
2439 int size = PyList_Size( res );
2440 for ( int i = 0; i < size; i++ ) {
2441 PyObject* value = PyList_GetItem( res, i );
2442 if( value && PyString_Check( value ) ) {
2443 files.append( PyString_AsString( value ) );
2452 \brief Check data object's 'draggable' status callback function.
2454 \param what data object being tested
2455 \return \c true if object can be dragged or \c false otherwise
2457 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2459 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2461 // Python interpreter should be initialized and Python module should be
2463 if ( !myInterp || !myPyModule || !what )
2466 bool draggable = false;
2468 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2469 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2470 (char*)"s", what->entry().toLatin1().constData() ) );
2471 if( !res || !PyBool_Check( res )) {
2476 draggable = PyObject_IsTrue( res );
2484 \brief Check data object's 'drop allowed' status callback function.
2486 \param where data object being tested
2487 \return \c true if if drop operation is supported by object or \c false otherwise
2489 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2491 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2493 // Python interpreter should be initialized and Python module should be
2495 if ( !myInterp || !myPyModule || !where )
2498 bool dropAccepted = false;
2500 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2501 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2502 (char*)"s", where->entry().toLatin1().constData() ) );
2503 if( !res || !PyBool_Check( res )) {
2505 dropAccepted = false;
2508 dropAccepted = PyObject_IsTrue( res );
2512 return dropAccepted;
2516 \brief Data dropping callback function.
2518 \param what list of data objects being dropped
2519 \param where target data object for drop operation
2520 \param row line (child item index) where drop operation is performed to
2521 \param action current drop action (copy or move)
2523 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2524 const int row, Qt::DropAction action )
2526 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2528 // Python interpreter should be initialized and Python module should be
2530 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2533 QStringList* theList = new QStringList();
2535 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2536 if ( !whereObject ) return;
2538 for ( int i = 0; i < what.count(); i++ ) {
2539 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2540 if ( dataObject ) theList->append( dataObject->entry() );
2543 #if SIP_VERSION < 0x040800
2544 PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
2546 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2548 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2549 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2551 whereObject->entry().toLatin1().constData(),
2561 \brief Get engine IOR callback function
2564 Tries to get engine IOR from the Python module using engineIOR() function.
2565 That function can load module engine using appropriate container if required.
2567 \return engine IOR or empty string if it is not provided by Python module
2569 QString PyModuleHelper::internalEngineIOR() const
2571 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2575 // Python interpreter should be initialized and Python module should be
2577 if ( myInterp && myModule ) {
2578 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2579 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2584 // parse the return value, result chould be string
2585 if ( PyString_Check( res ) ) {
2586 ior = PyString_AsString( res );
2595 \brief Connects signals about activating and cloning view on internal slots
2596 \param view view being connected
2598 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2600 SUIT_ViewManager* viewMgr = view->getViewManager();
2601 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2603 // Connect tryCloseView() and deleteView() signals
2605 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2606 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2607 Qt::UniqueConnection );
2608 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2609 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2610 Qt::UniqueConnection );
2613 // Connect cloneView() signal of an OCC View
2614 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2615 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2616 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2617 Qt::UniqueConnection );
2619 // Connect cloneView() signal of Plot2d View
2620 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2621 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2622 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2623 Qt::UniqueConnection );