1 // Copyright (C) 2007-2019 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 theDEBUG = 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.toUtf8().data() ) );
375 if ( !pixmap.isNull() )
376 icon = QIcon( pixmap );
383 \brief Create main menu item and insert actions to it.
385 \param parentNode XML node with menu description
386 \param parentMenuId parent menu ID (-1 for top-level menu)
387 \param parentPopup parent popup menu (0 for top-level menu)
389 void PyModuleHelper::XmlHandler::createMenu( QDomNode& parentNode,
390 const int parentMenuId,
393 if ( !module() || parentNode.isNull() )
396 QDomElement parentElement = parentNode.toElement();
397 if ( !parentElement.isNull() ) {
398 QString plabel = attribute( parentElement, "label-id" );
399 int pid = checkInt( attribute( parentElement, "item-id" ) );
400 int ppos = checkInt( attribute( parentElement, "pos-id" ) );
401 int group = checkInt( attribute( parentElement, "group-id" ),
402 PyModuleHelper::defaultMenuGroup() );
403 if ( !plabel.isEmpty() ) {
407 menuId = module()->createMenu( plabel, // label
408 parentMenuId, // parent menu ID, -1 for top-level menu
412 myMenuItems.append( menuId );
413 QDomNode node = parentNode.firstChild();
414 while ( !node.isNull() ) {
415 if ( node.isElement() ) {
416 QDomElement elem = node.toElement();
417 QString aTagName = tagName( elem );
418 if ( aTagName == "popup-item" ) {
419 int id = checkInt( attribute( elem, "item-id" ) );
420 int pos = checkInt( attribute( elem, "pos-id" ) );
421 int group = checkInt( attribute( elem, "group-id" ),
422 PyModuleHelper::defaultMenuGroup() );
423 QString label = attribute( elem, "label-id" );
424 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
425 QString tooltip = attribute( elem, "tooltip-id" );
426 QString accel = attribute( elem, "accel-id" );
427 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
429 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
430 // also check if the action with given ID is already created
432 // create menu action
433 QAction* action = module()->createAction( id, // ID
437 tooltip, // status-bar text
438 QKeySequence( accel ), // keyboard accelerator
439 module(), // action owner
440 toggle ); // toogled action
441 myHelper->connectAction( action );
442 module()->createMenu( action, // action
443 menuId, // parent menu ID
444 id, // ID (same as for createAction())
449 else if ( aTagName == "submenu" ) {
451 createMenu( node, menuId, popup );
453 else if ( aTagName == "separator" ) {
454 // create menu separator
455 int id = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
456 int pos = checkInt( attribute( elem, "pos-id" ) );
457 int group = checkInt( attribute( elem, "group-id" ),
458 PyModuleHelper::defaultMenuGroup() );
459 QAction* action = module()->separator();
460 module()->createMenu( action, // separator action
461 menuId, // parent menu ID
467 node = node.nextSibling();
474 \brief Create a toolbar and insert actions to it.
475 \param parentNode XML node with toolbar description
477 void PyModuleHelper::XmlHandler::createToolBar( QDomNode& parentNode )
479 if ( !module() || parentNode.isNull() )
482 QDomElement parentElement = parentNode.toElement();
483 if ( !parentElement.isNull() ) {
484 QString aLabel = attribute( parentElement, "label-id" );
485 QString aName = attribute( parentElement, "name-id" );
486 if ( !aLabel.isEmpty() ) {
488 int tbId = module()->createTool( aLabel, aName );
489 QDomNode node = parentNode.firstChild();
490 while ( !node.isNull() ) {
491 if ( node.isElement() ) {
492 QDomElement elem = node.toElement();
493 QString aTagName = tagName( elem );
494 if ( aTagName == "toolbutton-item" ) {
495 int id = checkInt( attribute( elem, "item-id" ) );
496 int pos = checkInt( attribute( elem, "pos-id" ) );
497 QString label = attribute( elem, "label-id" );
498 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
499 QString tooltip = attribute( elem, "tooltip-id" );
500 QString accel = attribute( elem, "accel-id" );
501 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
503 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
504 // also check if the action with given ID is already created
506 // create toolbar action
507 QAction* action = module()->createAction( id, // ID
511 tooltip, // status-bar text
512 QKeySequence( accel ), // keyboard accelerator
513 module(), // action owner
514 toggle ); // toogled action
515 myHelper->connectAction( action );
516 module()->createTool( action, tbId, -1, pos );
519 else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
520 // create toolbar separator
521 int pos = checkInt( attribute( elem, "pos-id" ) );
522 QAction* action = module()->separator();
523 module()->createTool( action, tbId, -1, pos );
526 node = node.nextSibling();
533 \brief Fill popup menu with the items.
534 \param parentNode XML node with popup menu description
535 \param menu popup menu
537 void PyModuleHelper::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
539 if ( !module() && parentNode.isNull() )
542 // we create popup menus without help of QtxPopupMgr
543 QDomNode node = parentNode.firstChild();
544 while ( !node.isNull() ) {
545 if ( node.isElement() ) {
546 QDomElement elem = node.toElement();
547 QString aTagName = tagName( elem );
548 QList<QAction*> actions = menu->actions();
549 if ( aTagName == "popup-item" ) {
550 // insert a command item
551 int id = checkInt( attribute( elem, "item-id" ) );
552 int pos = checkInt( attribute( elem, "pos-id" ) );
553 QString label = attribute( elem, "label-id" );
554 QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
555 QString tooltip = attribute( elem, "tooltip-id" );
556 QString accel = attribute( elem, "accel-id" );
557 bool toggle = checkBool( attribute( elem, "toggle-id" ) );
559 // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
560 // also check if the action with given ID is already created
562 QAction* action = module()->createAction( id, // ID
566 tooltip, // status-bar text
567 QKeySequence( accel ), // keyboard accelerator
568 module(), // action owner
569 toggle ); // toogled action
570 myHelper->connectAction( action );
571 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
572 menu->insertAction( before, action );
575 else if ( aTagName == "submenu" ) {
577 ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
578 int pos = checkInt( attribute( elem, "pos-id" ) );
579 QString label = attribute( elem, "label-id" );
580 QString icon = attribute( elem, "icon-id" );
583 if ( !icon.isEmpty() ) {
584 QPixmap pixmap = module()->getApp()->resourceMgr()->loadPixmap( module()->name(), icon );
585 if ( !pixmap.isNull() )
586 anIcon = QIcon( pixmap );
589 QMenu* newPopup = menu->addMenu( anIcon, label );
590 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
591 menu->insertMenu( before, newPopup );
592 insertPopupItems( node, newPopup );
594 else if ( aTagName == "separator" ) {
595 // create menu separator
596 int pos = checkInt( attribute( elem, "pos-id" ) );
597 QAction* action = module()->separator();
598 QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
599 menu->insertAction( before, action );
602 node = node.nextSibling();
607 \class PyModuleHelper
608 \brief This class implements API helper for all the Python-based
612 LightApp_Module* PyModuleHelper::myInitModule = 0;
616 \param module owner module
618 PyModuleHelper::PyModuleHelper( LightApp_Module* module ) :
624 myLastActivateStatus( true )
626 setObjectName( "python_module_helper" );
632 PyModuleHelper::~PyModuleHelper()
635 if ( myInterp && myPyModule ) {
636 PyLockWrapper aLock; // Acquire GIL
637 Py_XDECREF( myPyModule );
642 \brief Get the module being initialized.
644 This is a little trick :) needed to provide an access from Python
645 (SalomePyQt) to the module being currently activated. The problem
646 that during the process of module initialization (initialize()
647 function) it is not yet available via application->activeModule()
650 This method returns valid pointer only if called in scope of
651 initialize() function or in several other specific cases.
653 \return the module being currently initialized
655 LightApp_Module* PyModuleHelper::getInitModule()
657 QMutexLocker ml( &myInitMutex );
662 \brief Get default menu group identifier
663 \return menu group ID (40 by default)
665 int PyModuleHelper::defaultMenuGroup()
667 return DEFAULT_GROUP;
671 \brief Get owner module
674 LightApp_Module* PyModuleHelper::module() const
680 \brief Get Python GUI module object
681 \return python module
683 PyObject* PyModuleHelper::pythonModule() const
689 \brief Connect action to the internal actionActivated() slot.
691 Actions connected to internal actionActivated(), when activated, will
692 be forwarded to the Python GUI module OnGUIEvent() function.
694 \param a action being connected
696 void PyModuleHelper::connectAction( QAction* a )
699 QObject::connect( a, SIGNAL( triggered( bool ) ),
700 this, SLOT( actionActivated() ),
701 Qt::UniqueConnection );
705 \brief Get the dockable windows associated with the module.
707 To fill the list of windows the correspondind Python module's windows()
708 method is called during the module initialization.
710 By default, ObjectBrowser, PythonConsole and LogWindow windows are
711 associated to the module.
713 Allowed dockable windows:
714 - LightApp_Application::WT_ObjectBrowser : object browser
715 - LightApp_Application::WT_PyConsole : python console
716 - LightApp_Application::WT_LogWindow : log messages output window
718 Dock area is defined by Qt::DockWidgetArea enumeration:
719 - Qt::TopDockWidgetArea : top dock area
720 - Qt::BottomDockWidgetArea : bottom dock area
721 - Qt::LeftDockWidgetArea : left dock area
722 - Qt::RightDockWidgetArea : right dock area
724 \return map of dockable windows in form { <window_type> : <dock_area> }
726 QMap<int, int> PyModuleHelper::windows() const
728 FuncMsg fmsg( "PyModuleHelper::windows()" );
734 \brief Define the compatible view windows associated with the module.
736 The associated view windows are opened automatically when the module
739 To fill the list of views the correspondind Python module's views()
740 method is called during the module initialization.
741 By default, the list of view types is empty.
743 \return list of view windows types
745 QStringList PyModuleHelper::viewManagers() const
747 FuncMsg fmsg( "PyModuleHelper::viewManagers()" );
749 return myViewMgrList;
753 \brief Initialization of the Python-based SALOME module.
755 This method can be used for creation of the menus, toolbars and
758 There are two ways to do this:
759 1) for obsolete modules, the implementation of this method first tries to read
760 the <module>_<language>.xml resource file which contains a menu,
761 toolbars and popup menus description;
762 2) new modules can create menus by direct calling of the
763 corresponding methods of SalomePyQt Python API in the Python
764 module's initialize() method which is called from here.
766 \note SALOME supports two modes of modules loading:
767 - immediate (all the modules are created and initialized
768 immediately when the application object is created);
769 - postponed modules loading (used currently); in this mode
770 the module is loaded only by explicit request.
771 If postponed modules loading is not used, the active
772 study might be not yet defined at this stage, so initialize()
773 method should not perform any study-based initialization.
774 Such actions can be better done in activate() function.
776 \param app parent application object
778 void PyModuleHelper::initialize( CAM_Application* app )
780 FuncMsg fmsg( "PyModuleHelper::initialize()" );
782 // temporarily store module being currently activated
783 // in the global variable to make it accessible from
785 InitLocker lock( myModule );
787 // try to get XML resource file name
788 SUIT_ResourceMgr* resMgr = myModule->getApp()->resourceMgr();
789 if ( !myXmlHandler && resMgr ) {
790 // get current language
791 QString lang = resMgr->stringValue( "language", "language", "en" );
792 // get menu description file name
793 QString aFileName = QString( "%1_%2.xml" ).arg( myModule->name() ).arg( lang );
794 aFileName = resMgr->path( "resources", myModule->name(), aFileName );
795 if ( !aFileName.isEmpty() && QFile::exists( aFileName ) ) {
796 // create XML handler instance
797 myXmlHandler = new XmlHandler( this, aFileName );
798 // ask XML handler to create actions
799 myXmlHandler->createActions();
803 class InitializeReq : public PyInterp_Request
806 InitializeReq( PyModuleHelper* _helper,
807 CAM_Application* _app )
808 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
813 virtual void execute()
815 myHelper->internalInitialize( myApp );
818 PyModuleHelper* myHelper;
819 CAM_Application* myApp;
823 PyInterp_Dispatcher::Get()->Exec( new InitializeReq( this, app ) );
827 \brief Activation of the module.
829 This function is usually used in order to show the module's
830 specific menus and toolbars, update actions state and perform
831 other such actions required when the module is activated.
833 \note Returning \c false from this function prevents the
836 \param study parent study
837 \return \c true if activation is successful and \c false otherwise
839 bool PyModuleHelper::activate( SUIT_Study* study )
841 FuncMsg fmsg( "PyModuleHelper::activate()" );
843 // reset the activation status to the default value
844 myLastActivateStatus = true;
846 class ActivateReq : public PyInterp_Request
849 ActivateReq( PyModuleHelper* _helper,
852 : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
855 myCustomize( _customize )
858 virtual void execute()
861 myHelper->internalActivate( myStudy ); // first activation stage
863 myHelper->internalCustomize( myStudy ); // second activation stage
866 PyModuleHelper* myHelper;
871 // post request for activation (customize=false)
872 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, false ) );
874 // check activation status (can be set to false by internalActivate())
875 if ( myLastActivateStatus ) {
876 // activate menus, toolbars, etc
877 if ( myXmlHandler ) myXmlHandler->activateMenus( true );
879 // show menus / toolbars
880 myModule->setMenuShown( true );
881 myModule->setToolShown( true );
883 // post request for customization (customize=true)
884 PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, true ) );
886 // check activation status (can be set to false by internalCustomize())
887 if ( myLastActivateStatus ) {
888 // connect preferences changing signal
889 connect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
890 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
892 // connect active view change signal
893 SUIT_Desktop* d = study->application()->desktop();
894 connect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
895 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
896 // if active window exists, call activeViewChanged() function;
897 // temporary solution: if a getActiveView() in SalomePyQt available
898 // we no longer need this
899 SUIT_ViewWindow* view = d->activeWindow();
900 if ( view ) activeViewChanged( view );
901 // get all view currently opened in the study and connect their signals to
902 // the corresponding slots of the class.
903 foreach ( view, d->windows() ) connectView( view );
906 // hide menus / toolbars in case of error
907 myModule->setMenuShown( false );
908 myModule->setToolShown( false );
912 return myLastActivateStatus;
916 \brief Deactivation of the module.
918 This function is usually used in order to hide the module's
919 specific menus and toolbars and perform other such actions
920 required when the module is deactivated.
922 \param study parent study
923 \return \c true if deactivation is successful and \c false otherwise
925 bool PyModuleHelper::deactivate( SUIT_Study* study )
927 FuncMsg fmsg( "PyModuleHelper::deactivate()" );
929 class DeactivateReq : public PyInterp_LockRequest
932 DeactivateReq( PyInterp_Interp* _py_interp,
933 PyModuleHelper* _helper,
935 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
940 virtual void execute()
942 myHelper->internalDeactivate( myStudy );
945 PyModuleHelper* myHelper;
950 PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, this, study ) );
952 // disconnect preferences changing signal
953 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
954 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
956 // disconnect the SUIT_Desktop signal windowActivated()
957 SUIT_Desktop* d = study->application()->desktop();
958 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
959 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
961 // deactivate menus, toolbars, etc
962 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
964 // hide menus / toolbars
965 myModule->setMenuShown( false );
966 myModule->setToolShown( false );
972 \brief Close of the module.
974 This function is usually used in order to close the module's
975 specific menus and toolbars and perform other such actions
976 required when the module is closed.
978 void PyModuleHelper::modelClosed( SUIT_Study* study )
980 FuncMsg fmsg( "PyModuleHelper::modelClosed()" );
982 class StudyClosedReq : public PyInterp_LockRequest
985 StudyClosedReq( PyInterp_Interp* _py_interp,
986 PyModuleHelper* _helper,
988 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
993 virtual void execute()
995 myHelper->internalClosedStudy( myStudy );
998 PyModuleHelper* myHelper;
1003 PyInterp_Dispatcher::Get()->Exec( new StudyClosedReq( myInterp, this, study ) );
1005 // disconnect preferences changing signal
1006 disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
1007 this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
1009 // disconnect the SUIT_Desktop signal windowActivated()
1010 SUIT_Desktop* d = study->application()->desktop();
1011 disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
1012 this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
1014 // deactivate menus, toolbars, etc
1015 if ( myXmlHandler ) myXmlHandler->activateMenus( false );
1017 // hide menus / toolbars
1018 myModule->setMenuShown( false );
1019 myModule->setToolShown( false );
1024 \brief Process module's preferences changing.
1026 Called when the module's own preferences are changed.
1028 \param section preference resources section
1029 \param parameter preference resources parameter name
1031 void PyModuleHelper::preferencesChanged( const QString& section,
1032 const QString& parameter )
1034 FuncMsg fmsg( "PyModuleHelper::preferencesChanged()" );
1036 class PrefChangeReq : public PyInterp_LockRequest
1039 PrefChangeReq( PyInterp_Interp* _py_interp,
1040 PyModuleHelper* _helper,
1041 const QString& _section,
1042 const QString& _parameter )
1043 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1044 myHelper ( _helper ),
1045 mySection( _section ),
1046 myParameter( _parameter )
1049 virtual void execute()
1051 myHelper->internalPreferencesChanged( mySection, myParameter );
1054 PyModuleHelper* myHelper;
1055 QString mySection, myParameter;
1058 // post the request only if dispatcher is not busy!
1059 // execute request synchronously
1060 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1061 PyInterp_Dispatcher::Get()->Exec( new PrefChangeReq( myInterp, this, section, parameter ) );
1065 \brief Process application preferences changing.
1067 Called when any application setting is changed.
1069 \param module preference module
1070 \param section preference resources section
1071 \param parameter preference resources parameter name
1073 void PyModuleHelper::preferenceChanged( const QString& module,
1074 const QString& section,
1075 const QString& parameter )
1077 FuncMsg fmsg( "PyModuleHelper::preferenceChanged()" );
1079 // module's own preferences are processed by other preferencesChanged() method
1080 if ( module != myModule->moduleName() ) {
1082 preferencesChanged( section, parameter );
1087 \brief Process study activation.
1089 Called when study desktop is activated. Used for notifying the Python
1090 module about changing of the active study.
1092 \param study study being activated
1094 void PyModuleHelper::studyActivated( SUIT_Study* study )
1096 FuncMsg fmsg( "PyModuleHelper::studyActivated()" );
1098 // StudyChangedReq: request class for internal studyChanged() operation
1099 class StudyChangedReq : public PyInterp_Request
1102 StudyChangedReq( PyModuleHelper* _helper,
1103 SUIT_Study* _study )
1104 : PyInterp_Request(0, true ), // this request should be processed synchronously (sync == true)
1105 myHelper( _helper ),
1109 virtual void execute()
1111 myHelper->internalStudyChanged( myStudy );
1114 PyModuleHelper* myHelper;
1115 SUIT_Study* myStudy;
1119 PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( this, study ) );
1123 \brief Process action activation.
1125 Called when action is activated. Used for notifying the Python
1126 module about any related action activation.
1130 void PyModuleHelper::actionActivated()
1132 FuncMsg fmsg( "PyModuleHelper::actionActivated()" );
1134 // perform synchronous request to Python event dispatcher
1135 class ActionReq : public PyInterp_LockRequest
1138 ActionReq( PyInterp_Interp* _py_interp,
1139 PyModuleHelper* _helper,
1141 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1142 myHelper( _helper ),
1146 virtual void execute()
1148 myHelper->internalActionActivated( myId );
1151 PyModuleHelper* myHelper;
1155 // get sender action
1156 QAction* action = qobject_cast<QAction*>( sender() );
1161 PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
1165 \brief update selection from other views or modules.
1167 Called when selection is modified outside.
1169 void PyModuleHelper::selectionUpdated(const QStringList& entries)
1171 FuncMsg fmsg( "PyModuleHelper::selectionUpdated()" );
1172 MESSAGE("selectionUpdated");
1174 // perform synchronous request to Python event dispatcher
1175 class SelectionReq : public PyInterp_LockRequest
1178 SelectionReq( PyInterp_Interp* _py_interp,
1179 PyModuleHelper* _helper,
1180 const QStringList& _entries )
1181 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1182 myHelper( _helper ),
1183 myEntries( _entries )
1185 MESSAGE("SelectionReq");
1188 virtual void execute()
1191 myHelper->internalSelectionUpdated( myEntries );
1194 PyModuleHelper* myHelper;
1195 const QStringList& myEntries;
1199 PyInterp_Dispatcher::Get()->Exec( new SelectionReq( myInterp, this, entries ) );
1203 \brief Process context popup menu request.
1205 Called when user activates popup menu in some window
1206 (view, object browser, etc).
1208 \param context popup menu context (e.g. "ObjectBrowser")
1209 \param menu popup menu
1211 void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
1213 FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
1215 class ContextMenuReq : public PyInterp_LockRequest
1218 ContextMenuReq( PyInterp_Interp* _py_interp,
1219 PyModuleHelper* _helper,
1220 const QString& _context,
1222 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1223 myHelper ( _helper ),
1224 myContext( _context ),
1228 virtual void execute()
1230 myHelper->internalContextMenu( myContext, myMenu );
1233 PyModuleHelper* myHelper;
1238 // post request only if dispatcher is not busy!
1239 // execute request synchronously
1240 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1241 PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
1245 \brief Export preferences for the Python module.
1246 Called only once when the first instance of the module is created or
1247 when common Preferences dialog box is first time invoked.
1249 void PyModuleHelper::createPreferences()
1251 FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
1253 // temporary set myInitModule because createPreferences() method
1254 // might be called during the module intialization process
1255 InitLocker lock( myModule );
1257 class CreatePrefReq : public PyInterp_LockRequest
1260 CreatePrefReq( PyInterp_Interp* _py_interp,
1261 PyModuleHelper* _helper )
1262 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1266 virtual void execute()
1268 myHelper->internalCreatePreferences();
1271 PyModuleHelper* myHelper;
1274 // post request only if dispatcher is not busy!
1275 // execute request synchronously
1276 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1277 PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
1281 \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
1283 Used to notify Python module that active view has been changed by the user.
1285 \param view view being activated
1287 void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
1289 FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
1291 // perform synchronous request to Python event dispatcher
1292 class ActiveViewChangeReq : public PyInterp_LockRequest
1295 ActiveViewChangeReq( PyInterp_Interp* _py_interp,
1296 PyModuleHelper* _helper,
1297 SUIT_ViewWindow* _view )
1298 : PyInterp_LockRequest( _py_interp, 0, true ),
1299 myHelper( _helper ),
1303 virtual void execute()
1305 myHelper->internalActiveViewChanged( myView );
1308 PyModuleHelper* myHelper;
1309 SUIT_ViewWindow* myView;
1312 // connect view (if it is not connected yet)
1313 connectView( view );
1315 PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
1319 \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
1320 \param view view being closed
1322 void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
1324 FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
1326 class TryCloseViewReq : public PyInterp_LockRequest
1329 TryCloseViewReq( PyInterp_Interp* _py_interp,
1330 PyModuleHelper* _helper,
1331 SUIT_ViewWindow* _view )
1332 : PyInterp_LockRequest( _py_interp, 0, true ),
1333 myHelper( _helper ),
1337 virtual void execute()
1339 myHelper->internalTryCloseView( myView );
1342 PyModuleHelper* myHelper;
1343 SUIT_ViewWindow* myView;
1346 PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
1350 \brief Signal handler closing(SUIT_ViewWindow*) of a view
1351 \param view view being closed
1353 void PyModuleHelper::closeView( SUIT_ViewWindow* view )
1355 FuncMsg fmsg( "PyModuleHelper::closeView()" );
1357 class CloseViewReq : public PyInterp_LockRequest
1360 CloseViewReq( PyInterp_Interp* _py_interp,
1361 PyModuleHelper* _helper,
1362 SUIT_ViewWindow* _view )
1363 : PyInterp_LockRequest( _py_interp, 0, true ),
1364 myHelper( _helper ),
1368 virtual void execute()
1370 myHelper->internalCloseView( myView );
1373 PyModuleHelper* myHelper;
1374 SUIT_ViewWindow* myView;
1377 PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
1381 \brief Signal handler cloneView() of OCCViewer_ViewWindow
1382 \param view view being cloned
1384 void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
1386 FuncMsg fmsg( "PyModuleHelper::cloneView()" );
1388 class CloneViewReq : public PyInterp_LockRequest
1391 CloneViewReq( PyInterp_Interp* _py_interp,
1392 PyModuleHelper* _helper,
1393 SUIT_ViewWindow* _view )
1394 : PyInterp_LockRequest( _py_interp, 0, true ),
1395 myHelper( _helper ),
1399 virtual void execute()
1401 myHelper->internalCloneView( myView );
1404 PyModuleHelper* myHelper;
1405 SUIT_ViewWindow* myView;
1408 PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
1412 \brief Save module data. Called when user saves study.
1413 \param files output list of files where module stores data
1414 \param url study URL
1416 void PyModuleHelper::save( QStringList& files, const QString& url )
1418 FuncMsg fmsg( "PyModuleHelper::save()" );
1420 // temporary set myInitModule because save() method
1421 // might be called by the framework when this module is inactive,
1422 // but still it should be possible to access this module's data
1424 InitLocker lock( myModule );
1426 // perform synchronous request to Python event dispatcher
1427 class SaveReq: public PyInterp_LockRequest
1430 SaveReq( PyInterp_Interp* _py_interp,
1431 PyModuleHelper* _helper,
1432 QStringList& _files,
1433 const QString& _url )
1434 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1435 myHelper( _helper ) ,
1440 virtual void execute()
1442 myHelper->internalSave( myFiles, myUrl );
1445 PyModuleHelper* myHelper;
1446 QStringList& myFiles;
1450 // Posting the request only if dispatcher is not busy!
1451 // Executing the request synchronously
1452 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1453 PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files, url ) );
1457 \brief Load module data. Called when user opens study
1458 and activates module.
1459 \param files list of files where module data is stored
1460 \param url study URL
1461 \return \c true if loading has been finished successfully or \c false otherwise
1463 bool PyModuleHelper::load( const QStringList& files, const QString& url )
1465 FuncMsg fmsg( "PyModuleHelper::load()" );
1467 bool loaded = false;
1469 class LoadReq: public PyInterp_LockRequest
1472 LoadReq( PyInterp_Interp* _py_interp,
1473 PyModuleHelper* _helper,
1475 const QString& _url,
1477 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1478 myHelper( _helper ) ,
1484 virtual void execute()
1486 myHelper->internalLoad( myFiles, myUrl, myLoaded );
1489 PyModuleHelper* myHelper;
1490 QStringList myFiles;
1495 // Posting the request only if dispatcher is not busy!
1496 // Executing the request synchronously
1497 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1498 PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, url, loaded ) );
1504 \brief Dump module data to the Python script.
1505 Called when user activates dump study operation.
1506 \param files output list of files where module stores python script
1508 void PyModuleHelper::dumpPython( QStringList& files )
1510 FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
1512 // temporary set myInitModule because dumpPython() method
1513 // might be called by the framework when this module is inactive,
1514 // but still it should be possible to access this module's data
1516 InitLocker lock( myModule );
1518 class DumpPythonReq: public PyInterp_LockRequest
1521 DumpPythonReq( PyInterp_Interp* _py_interp,
1522 PyModuleHelper* _helper,
1523 QStringList& _files )
1524 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1525 myHelper( _helper ) ,
1529 virtual void execute()
1531 myHelper->internalDumpPython( myFiles );
1534 PyModuleHelper* myHelper;
1535 QStringList& myFiles;
1538 // Posting the request only if dispatcher is not busy!
1539 // Executing the request synchronously
1540 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1541 PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
1545 \brief Test if object \a what can be dragged by the user.
1546 \param what data object being tested
1547 \return \c true if object can be dragged or \c false otherwise
1549 bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
1551 FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
1553 bool draggable = false;
1555 // perform synchronous request to Python event dispatcher
1556 class IsDraggableReq: public PyInterp_LockRequest
1559 IsDraggableReq( PyInterp_Interp* _py_interp,
1560 PyModuleHelper* _helper,
1561 LightApp_DataObject* _data_object,
1562 bool& _is_draggable )
1563 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1564 myHelper( _helper ) ,
1565 myDataObject( _data_object ),
1566 myIsDraggable( _is_draggable )
1569 virtual void execute()
1571 myIsDraggable = myHelper->internalIsDraggable( myDataObject );
1574 PyModuleHelper* myHelper;
1575 LightApp_DataObject* myDataObject;
1576 bool& myIsDraggable;
1579 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
1580 if ( data_object ) {
1581 // Posting the request only if dispatcher is not busy!
1582 // Executing the request synchronously
1583 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1584 PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
1585 const_cast<PyModuleHelper*>( this ),
1586 const_cast<LightApp_DataObject*>( data_object ),
1594 \brief Test if drop operation can be done on the \a where object.
1595 \param where data object being tested
1596 \return \c true if if drop operation is supported by object or \c false otherwise
1598 bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
1600 FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
1602 bool dropAccepted = false;
1604 // perform synchronous request to Python event dispatcher
1605 class IsDropAcceptedReq: public PyInterp_LockRequest
1608 IsDropAcceptedReq( PyInterp_Interp* _py_interp,
1609 PyModuleHelper* _helper,
1610 LightApp_DataObject* _data_object,
1611 bool& _is_drop_accepted )
1612 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1613 myHelper( _helper ) ,
1614 myDataObject( _data_object ),
1615 myIsDropAccepted( _is_drop_accepted )
1618 virtual void execute()
1620 myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
1623 PyModuleHelper* myHelper;
1624 LightApp_DataObject* myDataObject;
1625 bool& myIsDropAccepted;
1628 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
1629 if ( data_object ) {
1630 // Posting the request only if dispatcher is not busy!
1631 // Executing the request synchronously
1632 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1633 PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
1634 const_cast<PyModuleHelper*>( this ),
1635 const_cast<LightApp_DataObject*>( data_object ),
1639 return dropAccepted;
1643 \brief Perform drop operation
1644 \param what list of data objects being dropped
1645 \param where target data object for drop operation
1646 \param row line (child item index) where drop operation is performed to
1647 \param action current drop action (copy or move)
1649 void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
1650 const int row, Qt::DropAction action )
1652 FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
1654 // perform synchronous request to Python event dispatcher
1655 class DropObjectsReq: public PyInterp_LockRequest
1658 DropObjectsReq( PyInterp_Interp* _py_interp,
1659 PyModuleHelper* _helper,
1660 const DataObjectList& _what,
1661 SUIT_DataObject* _where,
1663 Qt::DropAction _action )
1664 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1665 myHelper( _helper ) ,
1669 myAction ( _action )
1672 virtual void execute()
1674 myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
1677 PyModuleHelper* myHelper;
1678 DataObjectList myWhat;
1679 SUIT_DataObject* myWhere;
1681 Qt::DropAction myAction;
1684 // Posting the request only if dispatcher is not busy!
1685 // Executing the request synchronously
1686 if ( !PyInterp_Dispatcher::Get()->IsBusy() )
1687 PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
1691 \brief Get module engine IOR
1692 \return engine IOR as it is supplied by GUI Python module
1694 QString PyModuleHelper::engineIOR() const
1696 FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
1698 class EngineIORReq : public PyInterp_LockRequest
1701 EngineIORReq( PyInterp_Interp* _py_interp,
1702 PyModuleHelper* _helper,
1704 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
1705 myHelper( _helper ),
1709 virtual void execute()
1711 myIOR = myHelper->internalEngineIOR();
1714 PyModuleHelper* myHelper;
1718 static QString anIOR;
1720 if ( anIOR.isEmpty() ) {
1722 PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
1723 const_cast<PyModuleHelper*>( this ),
1731 \brief Initialize python subinterpreter (one per study).
1734 void PyModuleHelper::initInterp()
1736 FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
1738 QMutexLocker ml( &myInitMutex );
1740 myInterp = new SALOME_PYQT_PyInterp();
1741 myInterp->initialize();
1743 #ifndef GUI_DISABLE_CORBA
1744 if ( !SUIT_PYTHON::initialized ) {
1745 // import 'salome' module and call 'salome_init' method;
1746 // do it only once on interpreter creation
1747 // ... first get python lock
1748 PyLockWrapper aLock; // Acquire GIL
1749 // ... then import a module
1750 PyObjWrapper aMod = PyImport_ImportModule( "salome" );
1756 // ... then call a method
1758 PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"si", NULL, embedded ) );
1769 \brief Import Python GUI module and store reference to the module.
1772 Warning! initInterp() should be called first!!!
1774 void PyModuleHelper::importModule()
1776 FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
1778 // check if the subinterpreter is initialized
1780 // Error! Python subinterpreter should be initialized first!
1785 // import Python GUI module and put it in <myPyModule> attribute
1786 // ... first get python lock
1787 PyLockWrapper aLock; // Acquire GIL
1788 // ... then import a module
1789 QString aMod = QString( "%1GUI" ).arg( myModule->name() );
1791 myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
1796 if ( !myPyModule ) {
1804 \brief Set study workspace to the Python module.
1807 Calls setWorkSpace() method of the Python module with
1808 PyQt QWidget object to use with interpreter.
1810 Attention! initInterp() and importModule() should be called first!!!
1812 void PyModuleHelper::setWorkSpace()
1814 FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
1816 if ( !IsCallOldMethods )
1819 // check if the subinterpreter is initialized and Python module is imported
1820 if ( !myInterp || !myPyModule ) {
1821 // Error! Python subinterpreter should be initialized and module should be imported first!
1825 // call setWorkSpace() method
1826 // ... first get python lock
1827 PyLockWrapper aLock; // Acquire GIL
1829 // ... then try to import SalomePyQt module. If it's not possible don't go on.
1830 PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
1837 // ... then get workspace object
1838 QWidget* aWorkspace = 0;
1839 if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
1840 STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
1842 aWorkspace = d->workspace();
1844 else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
1845 STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
1847 aWorkspace = d->workstack();
1849 #if SIP_VERSION >= 0x041300
1850 static const sipTypeDef *sipType_QWidget = 0;
1851 if (!sipType_QWidget)
1852 sipType_QWidget = sipFindType("QWidget");
1854 PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
1855 // ... and finally call Python module's setWorkSpace() method (obsolete)
1856 if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
1857 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
1865 \brief Initialization callback function
1868 Performs the following actions:
1869 - initialize or get the Python interpreter (one per study)
1870 - import the Python module
1871 - pass the workspace widget to the Python module
1872 - call Python module's initialize() method
1873 - call Python module's windows() method
1874 - call Python module's views() method
1876 \param app parent application object
1878 void PyModuleHelper::internalInitialize( CAM_Application* app )
1880 FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
1882 // reset interpreter to NULL
1886 LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
1889 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
1893 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1898 // import Python GUI module
1903 // then call Python module's initialize() method
1904 // ... first get python lock
1905 PyLockWrapper aLock; // Acquire GIL
1907 // ... (the Python module is already imported)
1908 // ... finally call Python module's initialize() method
1909 if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
1910 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
1916 // get required dockable windows list from the Python module
1917 // by calling windows() method
1918 // ... first put default values
1919 myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
1920 myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
1921 myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
1923 if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
1924 PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
1929 myWindowsMap.clear();
1930 if ( PyDict_Check( res1 ) ) {
1934 while ( PyDict_Next( res1, &pos, &key, &value ) ) {
1935 // parse the return value
1936 // it should be a map: {integer:integer}
1938 if( key && PyLong_Check( key ) && value && PyLong_Check( value ) ) {
1939 aKey = PyLong_AsLong( key );
1940 aValue = PyLong_AsLong( value );
1941 myWindowsMap[ aKey ] = aValue;
1948 // get compatible view windows types from the Python module
1949 // by calling views() method
1950 if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
1951 PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
1956 // parse the return value
1957 // result can be one string...
1958 if ( PyUnicode_Check( res2 ) ) {
1959 myViewMgrList.append( PyUnicode_AsUTF8( res2 ) );
1961 // ... or list of strings
1962 else if ( PyList_Check( res2 ) ) {
1963 int size = PyList_Size( res2 );
1964 for ( int i = 0; i < size; i++ ) {
1965 PyObject* value = PyList_GetItem( res2, i );
1966 if( value && PyUnicode_Check( value ) ) {
1967 myViewMgrList.append( PyUnicode_AsUTF8( value ) );
1976 \brief Activation callback function
1979 Performs the following actions:
1980 - initialize or get the Python interpreter (one per study)
1981 - import the Python GUI module
1982 - call Python module's activate() method
1984 \param study parent study
1986 void PyModuleHelper::internalActivate( SUIT_Study* study )
1988 FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
1991 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
1995 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
1998 myLastActivateStatus = false;
2002 // import Python GUI module
2004 if ( !myPyModule ) {
2005 myLastActivateStatus = false;
2010 PyLockWrapper aLock; // Acquire GIL
2012 // call Python module's activate() method (for the new modules)
2013 if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
2014 PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
2015 if ( !res1 || !PyBool_Check( res1 ) ) {
2017 // always true for old modules (no return value)
2018 myLastActivateStatus = true;
2021 // detect return status
2022 myLastActivateStatus = PyObject_IsTrue( res1 );
2028 \brief Additional menu customization callback function
2031 Performs the following actions:
2032 - get the Python interpreter (one per study)
2033 - import the Python GUI module
2034 - call Python module's setSettings() method (obsolete function,
2035 used for compatibility with old code)
2037 \param study parent study
2039 void PyModuleHelper::internalCustomize( SUIT_Study* study )
2041 FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
2044 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2048 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2051 myLastActivateStatus = false;
2055 // import Python GUI module
2057 if ( !myPyModule ) {
2058 myLastActivateStatus = false;
2062 // call Python module's setWorkSpace() method (obsolete)
2066 PyLockWrapper aLock; // Acquire GIL
2068 if ( IsCallOldMethods ) {
2069 // call Python module's setSettings() method (obsolete)
2070 if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
2071 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
2075 myLastActivateStatus = myLastActivateStatus && true;
2081 \brief Deactivation callback function
2084 Performs the following actions:
2085 - call Python module's deactivate() method
2087 \param study parent study
2089 void PyModuleHelper::internalDeactivate( SUIT_Study* study )
2091 FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
2093 // check that Python subinterpreter is initialized and Python module is imported
2094 if ( !myInterp || !myPyModule ) {
2095 // Error! Python subinterpreter should be initialized and module should be imported first!
2098 // then call Python module's deactivate() method
2099 if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
2100 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
2108 \brief Internal closure:
2110 Performs the following actions:
2111 - call Python module's closeStudy() method
2113 \param theStudy parent study object
2115 void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
2117 FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
2121 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
2125 // check that Python subinterpreter is initialized and Python module is imported
2126 if ( !myInterp || !myPyModule ) {
2127 // Error! Python subinterpreter should be initialized and module should be imported first!
2130 // then call Python module's deactivate() method
2131 if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
2132 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"" ) );
2142 \brief Preference changing callback function.
2145 Performs the following actions:
2146 - call Python module's preferenceChanged() method
2148 \param section resources section name
2149 \param setting resources parameter name
2151 void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
2153 FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
2155 // check that Python subinterpreter is initialized and Python module is imported
2156 if ( !myInterp || !myPyModule ) {
2157 // Error! Python subinterpreter should be initialized and module should be imported first!
2161 if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
2162 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2163 (char*)"preferenceChanged",
2165 section.toUtf8().constData(),
2166 setting.toUtf8().constData() ) );
2174 \brief Active study change callback function.
2177 Called when active the study is actived (user brings its
2179 - initialize or get the Python interpreter (one per study)
2180 - import the Python GUI module
2181 - call Python module's activeStudyChanged() method
2183 \param study study being activated
2185 void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
2187 FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
2190 LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
2194 // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
2199 // import Python GUI module
2204 // call Python module's setWorkSpace() method
2208 PyLockWrapper aLock; // Acquire GIL
2210 // call Python module's activeStudyChanged() method
2211 if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
2212 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"" ) );
2220 \brief GUI event handling callback function
2223 Performs the following actions:
2224 - calls Python module's OnGUIEvent() method
2226 \param id GUI action ID
2228 void PyModuleHelper::internalActionActivated( int id )
2230 FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
2231 fmsg.message( QString( "action id = %1" ).arg( id ) );
2233 // Python interpreter should be initialized and Python module should be
2235 if ( !myInterp || !myPyModule )
2238 if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
2239 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
2247 \brief update selection from other views or modules
2250 Performs the following actions:
2251 - calls Python module's onSelectionpdated(entries) method
2253 \param list of entries
2255 void PyModuleHelper::internalSelectionUpdated(const QStringList& entries)
2257 FuncMsg fmsg("--- PyModuleHelper::internalSelectionUpdated()");
2258 MESSAGE("internalSelectionUpdated");
2260 // Python interpreter should be initialized and Python module should be imported first
2261 if (!myInterp || !myPyModule)
2264 QStringList* theList = new QStringList(entries);
2266 #if SIP_VERSION >= 0x041300
2267 static const sipTypeDef *sipType_QStringList = 0;
2268 if (!sipType_QStringList)
2269 sipType_QStringList = sipFindType("QStringList");
2271 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2272 if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
2274 MESSAGE("call onSelectionUpdated");
2275 PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
2285 \brief Context popup menu handling callback function
2288 Performs the following actions:
2289 - calls Python module's definePopup(...) method (obsolete function,
2290 used for compatibility with old code) to define the popup menu context
2291 - parses XML resourses file (if exists) and fills the popup menu with the items)
2292 - calls Python module's customPopup(...) method (obsolete function,
2293 used for compatibility with old code) to allow module to customize the popup menu
2294 - for new modules calls createPopupMenu() function to allow the
2295 modules to build the popup menu by using insertItem(...) Qt functions.
2297 \param context popup menu context
2298 \param menu popup menu
2300 void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
2302 FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
2303 fmsg.message( QString( "context: %1" ).arg( context ) );
2305 // Python interpreter should be initialized and Python module should be
2307 if ( !myInterp || !myPyModule )
2310 QString aContext( "" ), aObject( "" ), aParent( context );
2312 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
2313 // call definePopup() Python module's function
2314 // this is obsolete function, used only for compatibility reasons
2315 PyObjWrapper res( PyObject_CallMethod( myPyModule,
2316 (char*)"definePopup",
2318 context.toUtf8().constData(),
2319 aObject.toUtf8().constData(),
2320 aParent.toUtf8().constData() ) );
2325 // parse return value
2327 if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
2333 } // if ( IsCallOldMethods ... )
2335 // first try to create menu via XML parser:
2336 // we create popup menus without help of QtxPopupMgr
2338 myXmlHandler->createPopup( menu, aContext, aParent, aObject );
2340 #if SIP_VERSION >= 0x041300
2341 static const sipTypeDef *sipType_QMenu = 0;
2343 sipType_QMenu = sipFindType("QMenu");
2345 PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
2347 // then call Python module's createPopupMenu() method (for new modules)
2348 if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
2349 PyObjWrapper res1( PyObject_CallMethod( myPyModule,
2350 (char*)"createPopupMenu",
2353 context.toUtf8().constData() ) );
2359 if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
2360 // call customPopup() Python module's function
2361 // this is obsolete function, used only for compatibility reasons
2362 PyObjWrapper res2( PyObject_CallMethod( myPyModule,
2363 (char*)"customPopup",
2366 aContext.toUtf8().constData(),
2367 aObject.toUtf8().constData(),
2368 aParent.toUtf8().constData() ) );
2376 \brief Preferences initialization callback function.
2379 Performs the following actions:
2380 - calls Python module's createPreferences() method
2382 void PyModuleHelper::internalCreatePreferences()
2384 FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
2386 // Python interpreter should be initialized and Python module should be
2388 if ( !myInterp || !myPyModule )
2391 if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
2392 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
2400 \brief Active view changing callback function
2402 \param view view being activated
2404 void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
2406 FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
2408 if ( !myInterp || !myPyModule || !view )
2411 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2413 if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
2414 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
2422 \brief View closing callback function
2424 \param view view user tries to close
2426 void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
2428 FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
2430 if ( !myInterp || !myPyModule || !view )
2433 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2435 if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
2437 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
2446 \brief View closing callback function
2448 \param view view being closed
2450 void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
2452 FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
2454 if ( !myInterp || !myPyModule || !view )
2457 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2459 if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
2461 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
2470 \brief View cloning callback function
2472 \param view view being cloned
2474 void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
2476 FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
2478 if ( !myInterp || !myPyModule || !view )
2481 fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
2483 if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
2485 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
2492 \brief Module data saving callback function.
2494 \param files output list of files where module stores data
2495 \param url study URL
2497 void PyModuleHelper::internalSave( QStringList& files, const QString& url )
2499 FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
2501 // Python interpreter should be initialized and Python module should be
2503 // files list should contain a path to the temporary directory as a first entry
2504 if ( !myInterp || !myPyModule || files.isEmpty() )
2507 if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
2509 // try with two parameters (new syntax)
2510 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2512 files.first().toUtf8().constData(),
2513 url.toUtf8().constData() ) );
2515 // try with single parameter (old syntax)
2516 res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
2517 (char*)"s", files.first().toUtf8().constData() );
2523 // parse the return value
2524 // result can be one string...
2525 if ( PyUnicode_Check( res ) ) {
2526 QString astr = PyUnicode_AsUTF8( res );
2527 files.append( astr );
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 && PyUnicode_Check( value ) ) {
2535 files.append( PyUnicode_AsUTF8( value ) );
2544 \brief Module data loading callback function.
2546 \param files list of files where module data is stored
2547 \param url study URL
2548 \param opened output success flag
2550 void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
2552 FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
2554 // Python interpreter should be initialized and Python module should be
2556 if ( !myInterp || !myPyModule || files.isEmpty() )
2559 QStringList* theList = new QStringList( files );
2561 #if SIP_VERSION >= 0x041300
2562 static const sipTypeDef *sipType_QStringList = 0;
2563 if (!sipType_QStringList)
2564 sipType_QStringList = sipFindType("QStringList");
2566 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
2567 if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
2569 // try with two parameters (new syntax)
2570 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
2571 (char*)"Os", sipList.get(),
2572 url.toUtf8().constData() ) );
2575 // try with single parameter (old syntax)
2576 res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
2577 (char*)"O", sipList.get() );
2579 if ( !res || !PyBool_Check( res ) ) {
2584 opened = PyObject_IsTrue( res );
2590 \brief Module dump python callback function.
2592 \param files output list of files where module stores python script
2594 void PyModuleHelper::internalDumpPython( QStringList& files )
2596 FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
2598 // Python interpreter should be initialized and Python module should be
2600 // files list should contain a path to the temporary directory
2601 if ( !myInterp || !myPyModule || files.isEmpty() )
2604 if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
2605 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
2606 (char*)"s", files.first().toUtf8().constData()));
2612 // parse the return value
2613 // result can be one string...
2614 if ( PyUnicode_Check( res ) ) {
2615 QString astr = PyUnicode_AsUTF8( res );
2619 //also result can be a list...
2620 else if ( PyList_Check( res ) ) {
2621 int size = PyList_Size( res );
2622 for ( int i = 0; i < size; i++ ) {
2623 PyObject* value = PyList_GetItem( res, i );
2624 if( value && PyUnicode_Check( value ) ) {
2625 files.append( PyUnicode_AsUTF8( value ) );
2634 \brief Check data object's 'draggable' status callback function.
2636 \param what data object being tested
2637 \return \c true if object can be dragged or \c false otherwise
2639 bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
2641 FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
2643 // Python interpreter should be initialized and Python module should be
2645 if ( !myInterp || !myPyModule || !what )
2648 bool draggable = false;
2650 if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
2651 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
2652 (char*)"s", what->entry().toUtf8().constData() ) );
2653 if( !res || !PyBool_Check( res )) {
2658 draggable = PyObject_IsTrue( res );
2666 \brief Check data object's 'drop allowed' status callback function.
2668 \param where data object being tested
2669 \return \c true if if drop operation is supported by object or \c false otherwise
2671 bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
2673 FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
2675 // Python interpreter should be initialized and Python module should be
2677 if ( !myInterp || !myPyModule || !where )
2680 bool dropAccepted = false;
2682 if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
2683 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
2684 (char*)"s", where->entry().toUtf8().constData() ) );
2685 if( !res || !PyBool_Check( res )) {
2687 dropAccepted = false;
2690 dropAccepted = PyObject_IsTrue( res );
2694 return dropAccepted;
2698 \brief Data dropping callback function.
2700 \param what list of data objects being dropped
2701 \param where target data object for drop operation
2702 \param row line (child item index) where drop operation is performed to
2703 \param action current drop action (copy or move)
2705 void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
2706 const int row, Qt::DropAction action )
2708 FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
2710 // Python interpreter should be initialized and Python module should be
2712 if ( !myInterp || !myPyModule || what.isEmpty() || !where )
2715 QStringList* theList = new QStringList();
2717 LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
2718 if ( !whereObject ) return;
2720 for ( int i = 0; i < what.count(); i++ ) {
2721 LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
2722 if ( dataObject ) theList->append( dataObject->entry() );
2725 #if SIP_VERSION >= 0x041300
2726 static const sipTypeDef *sipType_QStringList = 0;
2727 if (!sipType_QStringList)
2728 sipType_QStringList = sipFindType("QStringList");
2730 PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
2731 if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
2732 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
2734 whereObject->entry().toUtf8().constData(),
2744 \brief Get engine IOR callback function
2747 Tries to get engine IOR from the Python module using engineIOR() function.
2748 That function can load module engine using appropriate container if required.
2750 \return engine IOR or empty string if it is not provided by Python module
2752 QString PyModuleHelper::internalEngineIOR() const
2754 FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
2758 // Python interpreter should be initialized and Python module should be
2760 if ( myInterp && myModule ) {
2761 if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
2762 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
2767 // parse the return value, result chould be string
2768 if ( PyUnicode_Check( res ) ) {
2769 ior = PyUnicode_AsUTF8( res );
2778 \brief Connects signals about activating and cloning view on internal slots
2779 \param view view being connected
2781 void PyModuleHelper::connectView( SUIT_ViewWindow* view )
2783 SUIT_ViewManager* viewMgr = view->getViewManager();
2784 SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
2786 // Connect tryCloseView() and deleteView() signals
2788 connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
2789 this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
2790 Qt::UniqueConnection );
2791 connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
2792 this, SLOT( closeView( SUIT_ViewWindow* ) ),
2793 Qt::UniqueConnection );
2796 // Connect cloneView() signal of an OCC View
2797 if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
2798 connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2799 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2800 Qt::UniqueConnection );
2802 // Connect cloneView() signal of Plot2d View
2803 else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
2804 connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
2805 this, SLOT( cloneView( SUIT_ViewWindow* ) ),
2806 Qt::UniqueConnection );
2812 void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
2814 FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
2816 // Python interpreter should be initialized and Python module should be
2818 if ( !myInterp || !myPyModule )
2821 if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
2822 PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toUtf8().constData(), theColumn ) );
2831 void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
2833 FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
2835 // temporary set myInitModule because dumpPython() method
2836 // might be called by the framework when this module is inactive,
2837 // but still it should be possible to access this module's data
2839 InitLocker lock( myModule );
2841 class PythonReq: public PyInterp_LockRequest
2844 PythonReq( PyInterp_Interp* _py_interp,
2845 PyModuleHelper* _helper,
2846 const QString& _entry,
2848 : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
2849 myHelper( _helper ) ,
2854 virtual void execute()
2856 myHelper->internalOBClickedPython( myEntry, myColumn );
2859 PyModuleHelper* myHelper;
2864 // Posting the request only if dispatcher is not busy!
2865 // Executing the request synchronously
2866 const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
2867 if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
2868 PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );