summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO26
-rw-r--r--PKGBUILD61
-rw-r--r--salome-gui.sh2
-rw-r--r--sip-4.19.patch2910
4 files changed, 2954 insertions, 45 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 74c3c6f16b6..e2fb9551307 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,9 +1,9 @@
# Generated by mksrcinfo v8
-# Thu Apr 13 20:37:18 UTC 2017
+# Sat Dec 2 14:41:59 UTC 2017
pkgbase = salome-gui
pkgdesc = Generic platform for Pre and Post-Processing for numerical simulation - GUI Module
- pkgver = 7.8.0
- pkgrel = 4
+ pkgver = 8.3.0
+ pkgrel = 1
url = http://www.salome-platform.org
arch = i686
arch = x86_64
@@ -13,22 +13,22 @@ pkgbase = salome-gui
makedepends = boost
makedepends = optipng
makedepends = python2-sphinx
- depends = salome-kernel>=7.8.0
- depends = salome-kernel<7.9.0
- depends = qt4
- depends = python2-pyqt4
- depends = opencascade>=6.9.0
+ depends = salome-kernel>=8.3.0
+ depends = salome-kernel<8.4.0
+ depends = qt5-base
+ depends = python2-pyqt5
+ depends = opencascade7
depends = qwt
- depends = paraview-salome=5.0.1p1
+ depends = paraview-salome=5.1.2plus
depends = sip>=4.19.0
source = salome-gui.sh
source = salome.desktop
source = sip-4.19.patch
- source = http://files.salome-platform.org/Salome/Salome7.8.0/src7.8.0.tar.gz
- md5sums = fd7abc074e23a95ed3dabe841faa7586
+ source = http://files.salome-platform.org/Salome/Salome8.3.0/src8.3.0.tar.gz
+ md5sums = c8d47db1bf99e03fcd48b6aa375de206
md5sums = a102063b779e332914ef0b73843e928a
- md5sums = 9f7b52a2a332681cdd90bf3094dd1ed7
- md5sums = 0f6de10ad9d9c646fce3ca21a7dab46a
+ md5sums = 866b71d9d8efc02245ac4f9c0cff7bbb
+ md5sums = f571984862eb4215dc546edb2464ab4d
pkgname = salome-gui
diff --git a/PKGBUILD b/PKGBUILD
index b814a14bd79..c393db828af 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,11 +1,11 @@
# Maintainer: Michele Mocciola <mickele>
pkgname=salome-gui
-pkgver=7.8.0
-pkgrel=4
+pkgver=8.3.0
+pkgrel=1
pkgdesc="Generic platform for Pre and Post-Processing for numerical simulation - GUI Module"
url="http://www.salome-platform.org"
-depends=("salome-kernel>=${pkgver}" "salome-kernel<${pkgver:0:2}$((${pkgver:2:1}+1)).0" "qt4" "python2-pyqt4" "opencascade>=6.9.0" "qwt" "paraview-salome=5.0.1p1" "sip>=4.19.0")
+depends=("salome-kernel>=${pkgver}" "salome-kernel<${pkgver:0:2}$((${pkgver:2:1}+1)).0" "qt5-base" "python2-pyqt5" "opencascade7" "qwt" "paraview-salome=5.1.2plus" "sip>=4.19.0")
makedepends=('doxygen' 'swig2' 'boost' 'optipng' 'python2-sphinx')
arch=('i686' 'x86_64')
conflicts=()
@@ -13,13 +13,13 @@ provides=()
license=('LGPL')
source=("${pkgname}.sh" "salome.desktop" "sip-4.19.patch" "http://files.salome-platform.org/Salome/Salome${pkgver}/src${pkgver}.tar.gz")
-_source=GUI_SRC
+_source=GUI_SRC_${pkgver}
#_source=gui
_basedir=/opt/salome
_installdir=${_basedir}
_profiledir=${_basedir}/env.d
_paraviewrootdir=/usr
-_paraviewver=5.0
+_paraviewver=5.1
prepare(){
# msg "Connecting to git server..."
@@ -31,20 +31,19 @@ prepare(){
# git checkout V${pkgver:0:1}_${pkgver:2:1}_${pkgver:4:1}
# msg "GIT checkout done or server timeout"
- cd "${srcdir}/${_source}"
- # error "sip: Q_PID is undefined"
- sed -e 's|from PyQt4 import pyqtconfig;|from PyQt4 import QtCore;|' \
- -i adm_local/cmake_files/FindPyQt4.cmake
- sed -e "s|pyqtconfig.Configuration().pyqt_sip_flags|QtCore.PYQT_CONFIGURATION['sip_flags']|" \
- -i adm_local/cmake_files/FindPyQt4.cmake
+ cd "${srcdir}"
+
+ # DESTDIR
+ sed -e 's|\\"\${CMAKE_INSTALL_PREFIX}/\\\${INSTALL_PYIDL_DIR}\\"|\\"\\\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\\\${INSTALL_PYIDL_DIR}\\"|' -i CONFIGURATION_${pkgver}/cmake/UseOmniORB.cmake
- # pyuic4 -> python2-pyuic4
- sed -e 's|pyuic4|python2-pyuic4|' \
- -i adm_local/cmake_files/FindPyQt4.cmake
+ # pyuic5 -> python2-pyuic5
+ sed -e 's|pyuic5|python2-pyuic5|' \
+ -i "${srcdir}/CONFIGURATION_${pkgver}/cmake/FindPyQt5.cmake"
+ sed -e 's|pyrcc5|python2-pyrcc5|' \
+ -i "${srcdir}/CONFIGURATION_${pkgver}/cmake/FindPyQt5.cmake"
- # DESTDIR
- sed -e 's|\\"\${CMAKE_INSTALL_PREFIX}/\\\${INSTALL_TS_DIR}\\"|\\"\\\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\\\${INSTALL_TS_DIR}\\"|' -i adm_local/cmake_files/UseQt4Ext.cmake
+ cd "${srcdir}/${_source}"
# python -> python2
for _FILE in `grep -Rl "/usr/bin/env python" * `
@@ -72,7 +71,6 @@ build() {
# generic options
cmake_options+=" -DCMAKE_BUILD_TYPE=Release"
cmake_options+=" -DCMAKE_INSTALL_PREFIX=${_installdir}"
- cmake_options+=" -DCMAKE_CXX_STANDARD=98"
# debug options
cmake_options+=" -DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF"
@@ -85,14 +83,25 @@ build() {
cmake_options+=" -DSALOME_USE_MPI:BOOL=ON"
# qwt
- cmake_options+=" -DQWT_ROOT_DIR=/usr"
+ cmake_options+=" -DQWT_INCLUDE_DIR=/usr/include/qwt"
+ cmake_options+=" -DQWT_LIBRARY=/usr/lib/libqwt.so"
# opencascade
- cmake_options+=" -DCAS_ROOT_DIR=/opt/opencascade"
+ cmake_options+=" -DCAS_ROOT_DIR=/opt/opencascade7"
# sip
cmake_options+=" -DSIP_ROOT_DIR=/usr"
+ # Qt
+ cmake_options+=" -DQT5_ROOT_DIR=/usr"
+ for _QT_CMP in ui Core Gui Widgets Network Xml OpenGL PrintSupport Help Test Sql Sensors Positioning Quick Qml Multimedia MultimediaWidgets WebChannel UiTools X11Extras
+ do
+ cmake_options+=" -DQt5${_QT_CMP}_DIR=/usr/lib64/cmake/Qt5${_QT_CMP}"
+ done
+
+ # pyqt
+ cmake_options+=" -DPYQT5_ROOT_DIR=/usr"
+
# VTK
cmake_options+=" -DVTK_DIR=${_paraviewrootdir}/lib/cmake/paraview-${_paraviewver}"
@@ -112,8 +121,14 @@ build() {
cmake_options+=" -DHDF5_C_LIBRARY_hdf5:FILEPATH=/usr/lib/hdf5_18/libhdf5.so"
cmake_options+=" -DHDF5_DIFF_EXECUTABLE:FILEPATH=/usr/bin/h5diff_18"
- cmake ${cmake_options} ..
+ # salome configuration root dir
+ cmake_options+=" -DCONFIGURATION_ROOT_DIR=${srcdir}/CONFIGURATION_${pkgver}"
+ cmake_options+=" -DCMAKE_INSTALL_RPATH=/opt/opencascade7/lib"
+
+ cmake ${cmake_options} ..
+
+ # -Wdev --trace
make
}
@@ -147,7 +162,7 @@ package() {
ln -s ${_installdir}/share/salome/resources/gui/SalomeApp.xml ${pkgdir}${_installdir}
ln -s ${_installdir}/share/salome/resources/gui/LightApp.xml ${pkgdir}${_installdir}
}
-md5sums=('fd7abc074e23a95ed3dabe841faa7586'
+md5sums=('c8d47db1bf99e03fcd48b6aa375de206'
'a102063b779e332914ef0b73843e928a'
- '9f7b52a2a332681cdd90bf3094dd1ed7'
- '0f6de10ad9d9c646fce3ca21a7dab46a')
+ '866b71d9d8efc02245ac4f9c0cff7bbb'
+ 'f571984862eb4215dc546edb2464ab4d')
diff --git a/salome-gui.sh b/salome-gui.sh
index b355155fc13..6361c0b1b76 100644
--- a/salome-gui.sh
+++ b/salome-gui.sh
@@ -3,7 +3,7 @@ export GUI_ROOT_DIR=/opt/salome
# vars
_gui_pythonver=2.7
-_gui_paraviewver=5.0
+_gui_paraviewver=5.1
_gui_path=$GUI_ROOT_DIR/bin/salome
_gui_librarypath=$GUI_ROOT_DIR/lib/salome
_gui_pythonpath=$GUI_ROOT_DIR/lib/python${_gui_pythonver}/site-packages/salome
diff --git a/sip-4.19.patch b/sip-4.19.patch
index b48199dd072..29fc1f8ab72 100644
--- a/sip-4.19.patch
+++ b/sip-4.19.patch
@@ -1,7 +1,7 @@
-diff -Naur GUI_SRC.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx GUI_SRC/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx
---- GUI_SRC.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx 2017-03-03 19:31:57.004880382 +0100
-+++ GUI_SRC/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx 2017-03-03 19:35:09.131684278 +0100
-@@ -1813,7 +1813,7 @@
+diff -Naur -i GUI_SRC_8.3.0.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx GUI_SRC_8.3.0/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx
+--- GUI_SRC_8.3.0.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx 2017-11-30 22:50:14.097716795 +0100
++++ GUI_SRC_8.3.0/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx 2017-11-30 22:51:58.686223163 +0100
+@@ -1866,7 +1866,7 @@
#if SIP_VERSION < 0x040800
PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
#else
@@ -10,7 +10,16 @@ diff -Naur GUI_SRC.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModul
#endif
// ... and finally call Python module's setWorkSpace() method (obsolete)
if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
-@@ -2264,7 +2264,7 @@
+@@ -2281,7 +2281,7 @@
+ #if SIP_VERSION < 0x040800
+ PyObjWrapper sipList(sipBuildResult(0, "M", theList, sipClass_QStringList));
+ #else
+- PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
++ PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipFindType("QStringList"), NULL ) );
+ #endif
+ if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
+ {
+@@ -2354,7 +2354,7 @@
#if SIP_VERSION < 0x040800
PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
#else
@@ -19,7 +28,7 @@ diff -Naur GUI_SRC.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModul
#endif
// then call Python module's createPopupMenu() method (for new modules)
-@@ -2475,7 +2475,7 @@
+@@ -2574,7 +2574,7 @@
#if SIP_VERSION < 0x040800
PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
#else
@@ -27,8 +36,8 @@ diff -Naur GUI_SRC.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModul
+ PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipFindType("QStringList"), NULL ) );
#endif
if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
- PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
-@@ -2629,7 +2629,7 @@
+
+@@ -2737,7 +2737,7 @@
#if SIP_VERSION < 0x040800
PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
#else
@@ -37,3 +46,2888 @@ diff -Naur GUI_SRC.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModul
#endif
if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
+diff -Naur -i GUI_SRC_8.3.0.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx.orig GUI_SRC_8.3.0/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx.orig
+--- GUI_SRC_8.3.0.orig/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx.orig 1970-01-01 01:00:00.000000000 +0100
++++ GUI_SRC_8.3.0/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx.orig 2017-11-30 22:50:02.594551445 +0100
+@@ -0,0 +1,2881 @@
++// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
++//
++// This library is free software; you can redistribute it and/or
++// modify it under the terms of the GNU Lesser General Public
++// License as published by the Free Software Foundation; either
++// version 2.1 of the License, or (at your option) any later version.
++//
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++// Lesser General Public License for more details.
++//
++// You should have received a copy of the GNU Lesser General Public
++// License along with this library; if not, write to the Free Software
++// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++//
++// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
++//
++
++// File : SALOME_PYQT_PyModule.cxx
++// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
++//
++
++#include "SALOME_PYQT_PyModule.h"
++#include "SALOME_PYQT_PyInterp.h"
++
++#include "LightApp_Application.h"
++#include "LightApp_DataObject.h"
++#include "LightApp_Module.h"
++#include "LightApp_Study.h"
++#include "PyInterp_Dispatcher.h"
++#include "QtxActionMenuMgr.h"
++#include "QtxWorkspace.h"
++#include "QtxWorkstack.h"
++#include "STD_MDIDesktop.h"
++#include "STD_TabDesktop.h"
++#include "SUITApp_init_python.hxx"
++#include "SUIT_ResourceMgr.h"
++#include "SUIT_ViewManager.h"
++#include "SUIT_ViewModel.h"
++#include "SUIT_ViewWindow.h"
++
++#include <QApplication>
++#include <QDomDocument>
++#include <QDomElement>
++#include <QDomNode>
++#include <QFile>
++#include <QMenu>
++#include <QMutex>
++
++#include <utilities.h>
++
++#include "sipAPISalomePyQtGUILight.h"
++
++/*!
++ \brief Default menu group number.
++ \internal
++*/
++const int DEFAULT_GROUP = 40;
++
++/*!
++ \brief Mutex used to lock access from several threads to the shared
++ (static) data
++ \internal
++*/
++QMutex myInitMutex;
++
++/*! DEBUG mode */
++const bool DEBUG = false;
++
++/*!
++ \var IsCallOldMethods
++ \brief Allow calling obsolete callback methods.
++ \internal
++
++ If the macro CALL_OLD_METHODS is not defined, the invoking
++ of obsolete Python module's methods like setSetting(), definePopup(),
++ etc. is blocked.
++
++ CALL_OLD_METHODS macro can be defined, for example, by adding
++ -DCALL_OLD_METHODS compilation option to the CMakeLists.txt.
++*/
++#ifdef CALL_OLD_METHODS
++const bool IsCallOldMethods = true;
++#else
++const bool IsCallOldMethods = false;
++#endif
++
++/*!
++ \brief Get tag name for the DOM element.
++ \internal
++ \param element DOM element
++ \return tag name or empty string if the element does not have tag name
++*/
++static QString tagName( const QDomElement& element )
++{
++ return element.tagName().trimmed();
++}
++
++/*!
++ \brief Get value of DOM element's attribute.
++ \internal
++ \param element DOM element
++ \param attName attribute name
++ \return attribute value or empty string if the element does not have such attribute
++*/
++static QString attribute( const QDomElement& element, const QString& attName )
++{
++ return element.attribute( attName ).trimmed();
++}
++
++/*!
++ \brief Inspect specified string for the boolean value.
++ \internal
++
++ This function returns \c true if string represents boolean value:
++ - "true", "yes" or "1" for \c true
++ - "false", "no" or "0" for \c false
++ Second parameter allows to specify what boolean value is expected:
++ - 1: \c true
++ - 0: \c false
++ - other value is not taken into account (return represented value)
++
++ \param value inspected string
++ \param check expected boolean value
++ \return boolean value represented by the string (\a check is not 1 or 0)
++ or \c true if value correspond to the specified \a check
++*/
++static bool checkBool( const QString& value, const int check = -1 )
++{
++ QString v = value.toLower();
++ if ( ( v == "true" || v == "yes" || v == "1" ) && ( check != 0 ) )
++ return true;
++ if ( ( v == "false" || v == "no" || v == "0" ) && ( check == 0 ) )
++ return true;
++ return false;
++}
++
++/*!
++ \brief Inspect specified string for the integer value.
++ \internal
++
++ This function returns returns -1 if item is empty or represents
++ an invalid number.
++ \param value inspected string
++ \param def default value
++ \param shift shift value (it is added to the integer value to produce shifted result)
++*/
++static int checkInt( const QString& value, const int def = -1, const int shift = -1 )
++{
++ bool bOk;
++ int val = value.toInt( &bOk );
++ if ( !bOk ) val = def;
++ if ( shift > 0 && bOk && val < 0 )
++ val += shift;
++ return val;
++}
++
++/*!
++ \class FuncMsg
++ \brief Function call in/out tracer.
++ \internal
++*/
++
++class FuncMsg
++{
++public:
++ FuncMsg( const QString& funcName )
++ {
++ myName = funcName;
++ if ( DEBUG )
++ MESSAGE( qPrintable( myName ) << " [ begin ]" );
++ }
++ ~FuncMsg()
++ {
++ if ( DEBUG )
++ MESSAGE( qPrintable( myName ) << " [ end ]" );
++ }
++ void message( const QString& msg )
++ {
++ if ( DEBUG )
++ MESSAGE( qPrintable( myName ) << " : " << qPrintable( msg ) );
++ }
++private:
++ QString myName;
++};
++
++/*!
++ \class PyModuleHelper::InitLocker
++ \brief Initialization locker
++ \internal
++*/
++
++class PyModuleHelper::InitLocker
++{
++public:
++ InitLocker( LightApp_Module* );
++ ~InitLocker();
++};
++
++/*!
++ \brief Constructor
++ \internal
++*/
++PyModuleHelper::InitLocker::InitLocker( LightApp_Module* module )
++{
++ QMutexLocker ml( &myInitMutex );
++ myInitModule = module;
++}
++
++/*!
++ \brief Destructor
++ \internal
++*/
++PyModuleHelper::InitLocker::~InitLocker()
++{
++ QMutexLocker ml( &myInitMutex );
++ myInitModule = 0;
++}
++
++/*!
++ \class PyModuleHelper::XmlHandler
++ \brief XML resource files parser.
++ \internal
++
++ This class is used to provide backward compatibility with
++ existing Python modules in which obsolete menu definition system
++ (via XML files) is used.
++*/
++
++class PyModuleHelper::XmlHandler
++{
++public:
++ XmlHandler( PyModuleHelper* helper, const QString& fileName );
++ void createActions();
++ void createPopup( QMenu* menu,
++ const QString& context,
++ const QString& parent,
++ const QString& object );
++ void activateMenus( bool );
++
++private:
++ LightApp_Module* module() const;
++ QIcon loadIcon( const QString& fileName );
++
++ void createMenu( QDomNode& parentNode,
++ const int parentMenuId = -1,
++ QMenu* parentPopup = 0 );
++ void createToolBar( QDomNode& parentNode );
++ void insertPopupItems( QDomNode& parentNode,
++ QMenu* menu );
++
++private:
++ PyModuleHelper* myHelper;
++ QDomDocument myDoc;
++ QList<int> myMenuItems;
++};
++
++
++/*!
++ \brief Constructor
++ \internal
++ \param module pointer to the GUI module
++ \param fileName path to the XML menu description file
++*/
++PyModuleHelper::XmlHandler::XmlHandler( PyModuleHelper* helper,
++ const QString& fileName )
++: myHelper( helper )
++{
++ if ( !fileName.isEmpty() ) {
++ QFile aFile( fileName );
++ if ( aFile.open( QIODevice::ReadOnly ) ) {
++ myDoc.setContent( &aFile );
++ }
++ }
++}
++
++/*!
++ \brief Parse XML file and create actions.
++ \internal
++
++ Called by PyModuleHelper::initialize() in order to create actions
++ (menus, toolbars).
++*/
++void PyModuleHelper::XmlHandler::createActions()
++{
++ // get document element
++ QDomElement aDocElem = myDoc.documentElement();
++
++ // create main menu actions
++ QDomNodeList aMenuList = aDocElem.elementsByTagName( "menu-item" );
++ for ( int i = 0; i < aMenuList.count(); i++ ) {
++ QDomNode n = aMenuList.item( i );
++ createMenu( n );
++ }
++
++ // create toolbars actions
++ QDomNodeList aToolsList = aDocElem.elementsByTagName( "toolbar" );
++ for ( int i = 0; i < aToolsList.count(); i++ ) {
++ QDomNode n = aToolsList.item( i );
++ createToolBar( n );
++ }
++}
++
++/*!
++ \brief Create popup menu.
++ \internal
++ \param menu popup menu
++ \param context popup menu context
++ \param context popup menu parent object name
++ \param context popup menu object name
++*/
++void PyModuleHelper::XmlHandler::createPopup( QMenu* menu,
++ const QString& context,
++ const QString& parent,
++ const QString& object )
++{
++ // get document element
++ QDomElement aDocElem = myDoc.documentElement();
++
++ // get popup menus actions
++ QDomNodeList aPopupList = aDocElem.elementsByTagName( "popupmenu" );
++ for ( int i = 0; i < aPopupList.count(); i++ ) {
++ QDomNode n = aPopupList.item( i );
++ if ( !n.isNull() && n.isElement() ) {
++ QDomElement e = n.toElement();
++ // QString lab = attribute( e, "label-id" ); // not used //
++ QString ctx = attribute( e, "context-id" );
++ QString prt = attribute( e, "parent-id" );
++ QString obj = attribute( e, "object-id" );
++ if ( ctx == context && prt == parent && obj == object ) {
++ insertPopupItems( n, menu );
++ break;
++ }
++ }
++ }
++}
++
++/*!
++ \brief Activate/deactivate menus
++ \internal
++ \param enable if \c true menus are activated, otherwise menus are deactivated
++*/
++void PyModuleHelper::XmlHandler::activateMenus( bool enable )
++{
++ if ( module() ) {
++ QtxActionMenuMgr* mgr = module()->menuMgr();
++ foreach( int id, myMenuItems ) mgr->setEmptyEnabled( id, enable );
++ }
++}
++
++/*!
++ \brief Get owner module
++*/
++LightApp_Module* PyModuleHelper::XmlHandler::module() const
++{
++ return myHelper->module();
++}
++
++/*!
++ \brief Load an icon from the module resources by the specified file name.
++ \param fileName icon file name
++ \return icon object
++*/
++
++QIcon PyModuleHelper::XmlHandler::loadIcon( const QString& fileName )
++{
++ QIcon icon;
++
++ if ( module() && !fileName.isEmpty() ) {
++ SUIT_ResourceMgr* resMgr = module()->getApp()->resourceMgr();
++ QPixmap pixmap = resMgr->loadPixmap( module()->name(),
++ QApplication::translate( module()->name().toLatin1().data(),
++ fileName.toLatin1().data() ) );
++ if ( !pixmap.isNull() )
++ icon = QIcon( pixmap );
++ }
++
++ return icon;
++}
++
++/*!
++ \brief Create main menu item and insert actions to it.
++ \internal
++ \param parentNode XML node with menu description
++ \param parentMenuId parent menu ID (-1 for top-level menu)
++ \param parentPopup parent popup menu (0 for top-level menu)
++*/
++void PyModuleHelper::XmlHandler::createMenu( QDomNode& parentNode,
++ const int parentMenuId,
++ QMenu* parentPopup )
++{
++ if ( !module() || parentNode.isNull() )
++ return;
++
++ QDomElement parentElement = parentNode.toElement();
++ if ( !parentElement.isNull() ) {
++ QString plabel = attribute( parentElement, "label-id" );
++ int pid = checkInt( attribute( parentElement, "item-id" ) );
++ int ppos = checkInt( attribute( parentElement, "pos-id" ) );
++ int group = checkInt( attribute( parentElement, "group-id" ),
++ PyModuleHelper::defaultMenuGroup() );
++ if ( !plabel.isEmpty() ) {
++ QMenu* popup = 0;
++ int menuId = -1;
++ // create menu
++ menuId = module()->createMenu( plabel, // label
++ parentMenuId, // parent menu ID, -1 for top-level menu
++ pid, // ID
++ group, // group ID
++ ppos ); // position
++ myMenuItems.append( menuId );
++ QDomNode node = parentNode.firstChild();
++ while ( !node.isNull() ) {
++ if ( node.isElement() ) {
++ QDomElement elem = node.toElement();
++ QString aTagName = tagName( elem );
++ if ( aTagName == "popup-item" ) {
++ int id = checkInt( attribute( elem, "item-id" ) );
++ int pos = checkInt( attribute( elem, "pos-id" ) );
++ int group = checkInt( attribute( elem, "group-id" ),
++ PyModuleHelper::defaultMenuGroup() );
++ QString label = attribute( elem, "label-id" );
++ QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
++ QString tooltip = attribute( elem, "tooltip-id" );
++ QString accel = attribute( elem, "accel-id" );
++ bool toggle = checkBool( attribute( elem, "toggle-id" ) );
++
++ // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
++ // also check if the action with given ID is already created
++ if ( id != -1 ) {
++ // create menu action
++ QAction* action = module()->createAction( id, // ID
++ tooltip, // tooltip
++ icon, // icon
++ label, // menu text
++ tooltip, // status-bar text
++ QKeySequence( accel ), // keyboard accelerator
++ module(), // action owner
++ toggle ); // toogled action
++ myHelper->connectAction( action );
++ module()->createMenu( action, // action
++ menuId, // parent menu ID
++ id, // ID (same as for createAction())
++ group, // group ID
++ pos ); // position
++ }
++ }
++ else if ( aTagName == "submenu" ) {
++ // create sub-menu
++ createMenu( node, menuId, popup );
++ }
++ else if ( aTagName == "separator" ) {
++ // create menu separator
++ int id = checkInt( attribute( elem, "item-id" ) ); // separator can have ID
++ int pos = checkInt( attribute( elem, "pos-id" ) );
++ int group = checkInt( attribute( elem, "group-id" ),
++ PyModuleHelper::defaultMenuGroup() );
++ QAction* action = module()->separator();
++ module()->createMenu( action, // separator action
++ menuId, // parent menu ID
++ id, // ID
++ group, // group ID
++ pos ); // position
++ }
++ }
++ node = node.nextSibling();
++ }
++ }
++ }
++}
++
++/*!
++ \brief Create a toolbar and insert actions to it.
++ \param parentNode XML node with toolbar description
++*/
++void PyModuleHelper::XmlHandler::createToolBar( QDomNode& parentNode )
++{
++ if ( !module() || parentNode.isNull() )
++ return;
++
++ QDomElement parentElement = parentNode.toElement();
++ if ( !parentElement.isNull() ) {
++ QString aLabel = attribute( parentElement, "label-id" );
++ QString aName = attribute( parentElement, "name-id" );
++ if ( !aLabel.isEmpty() ) {
++ // create toolbar
++ int tbId = module()->createTool( aLabel, aName );
++ QDomNode node = parentNode.firstChild();
++ while ( !node.isNull() ) {
++ if ( node.isElement() ) {
++ QDomElement elem = node.toElement();
++ QString aTagName = tagName( elem );
++ if ( aTagName == "toolbutton-item" ) {
++ int id = checkInt( attribute( elem, "item-id" ) );
++ int pos = checkInt( attribute( elem, "pos-id" ) );
++ QString label = attribute( elem, "label-id" );
++ QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
++ QString tooltip = attribute( elem, "tooltip-id" );
++ QString accel = attribute( elem, "accel-id" );
++ bool toggle = checkBool( attribute( elem, "toggle-id" ) );
++
++ // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
++ // also check if the action with given ID is already created
++ if ( id != -1 ) {
++ // create toolbar action
++ QAction* action = module()->createAction( id, // ID
++ tooltip, // tooltip
++ icon, // icon
++ label, // menu text
++ tooltip, // status-bar text
++ QKeySequence( accel ), // keyboard accelerator
++ module(), // action owner
++ toggle ); // toogled action
++ myHelper->connectAction( action );
++ module()->createTool( action, tbId, -1, pos );
++ }
++ }
++ else if ( aTagName == "separatorTB" || aTagName == "separator" ) {
++ // create toolbar separator
++ int pos = checkInt( attribute( elem, "pos-id" ) );
++ QAction* action = module()->separator();
++ module()->createTool( action, tbId, -1, pos );
++ }
++ }
++ node = node.nextSibling();
++ }
++ }
++ }
++}
++
++/*!
++ \brief Fill popup menu with the items.
++ \param parentNode XML node with popup menu description
++ \param menu popup menu
++*/
++void PyModuleHelper::XmlHandler::insertPopupItems( QDomNode& parentNode, QMenu* menu )
++{
++ if ( !module() && parentNode.isNull() )
++ return;
++
++ // we create popup menus without help of QtxPopupMgr
++ QDomNode node = parentNode.firstChild();
++ while ( !node.isNull() ) {
++ if ( node.isElement() ) {
++ QDomElement elem = node.toElement();
++ QString aTagName = tagName( elem );
++ QList<QAction*> actions = menu->actions();
++ if ( aTagName == "popup-item" ) {
++ // insert a command item
++ int id = checkInt( attribute( elem, "item-id" ) );
++ int pos = checkInt( attribute( elem, "pos-id" ) );
++ QString label = attribute( elem, "label-id" );
++ QIcon icon = loadIcon( attribute( elem, "icon-id" ) );
++ QString tooltip = attribute( elem, "tooltip-id" );
++ QString accel = attribute( elem, "accel-id" );
++ bool toggle = checkBool( attribute( elem, "toggle-id" ) );
++
++ // -1 action ID is not allowed : it means that <item-id> attribute is missed in the XML file!
++ // also check if the action with given ID is already created
++ if ( id != -1 ) {
++ QAction* action = module()->createAction( id, // ID
++ tooltip, // tooltip
++ icon, // icon
++ label, // menu text
++ tooltip, // status-bar text
++ QKeySequence( accel ), // keyboard accelerator
++ module(), // action owner
++ toggle ); // toogled action
++ myHelper->connectAction( action );
++ QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
++ menu->insertAction( before, action );
++ }
++ }
++ else if ( aTagName == "submenu" ) {
++ // create sub-menu
++ ////int id = checkInt( attribute( elem, "item-id" ) ); // not used //
++ int pos = checkInt( attribute( elem, "pos-id" ) );
++ QString label = attribute( elem, "label-id" );
++ QString icon = attribute( elem, "icon-id" );
++
++ QIcon anIcon;
++ if ( !icon.isEmpty() ) {
++ QPixmap pixmap = module()->getApp()->resourceMgr()->loadPixmap( module()->name(), icon );
++ if ( !pixmap.isNull() )
++ anIcon = QIcon( pixmap );
++ }
++
++ QMenu* newPopup = menu->addMenu( anIcon, label );
++ QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
++ menu->insertMenu( before, newPopup );
++ insertPopupItems( node, newPopup );
++ }
++ else if ( aTagName == "separator" ) {
++ // create menu separator
++ int pos = checkInt( attribute( elem, "pos-id" ) );
++ QAction* action = module()->separator();
++ QAction* before = ( pos >= 0 && pos < actions.count() ) ? actions[ pos ] : 0;
++ menu->insertAction( before, action );
++ }
++ }
++ node = node.nextSibling();
++ }
++}
++
++/*!
++ \class PyModuleHelper
++ \brief This class implements API helper for all the Python-based
++ SALOME GUI modules.
++*/
++
++PyModuleHelper::InterpMap PyModuleHelper::myInterpMap;
++LightApp_Module* PyModuleHelper::myInitModule = 0;
++
++/*!
++ \brief Constructor
++ \param module owner module
++*/
++PyModuleHelper::PyModuleHelper( LightApp_Module* module ) :
++ QObject( module ),
++ myModule( module ),
++ myPyModule( 0 ),
++ myInterp( 0 ),
++ myXmlHandler ( 0 ),
++ myLastActivateStatus( true )
++{
++ setObjectName( "python_module_helper" );
++}
++
++/*!
++ \brief Destructor
++*/
++PyModuleHelper::~PyModuleHelper()
++{
++ delete myXmlHandler;
++ if ( myInterp && myPyModule ) {
++ PyLockWrapper aLock; // Acquire GIL
++ Py_XDECREF( myPyModule );
++ }
++}
++
++/*!
++ \brief Get the module being initialized.
++
++ This is a little trick :) needed to provide an access from Python
++ (SalomePyQt) to the module being currently activated. The problem
++ that during the process of module initialization (initialize()
++ function) it is not yet available via application->activeModule()
++ call.
++
++ This method returns valid pointer only if called in scope of
++ initialize() function or in several other specific cases.
++
++ \return the module being currently initialized
++*/
++LightApp_Module* PyModuleHelper::getInitModule()
++{
++ QMutexLocker ml( &myInitMutex );
++ return myInitModule;
++}
++
++/*!
++ \brief Get default menu group identifier
++ \return menu group ID (40 by default)
++*/
++int PyModuleHelper::defaultMenuGroup()
++{
++ return DEFAULT_GROUP;
++}
++
++/*!
++ \brief Get owner module
++ \return owner module
++*/
++LightApp_Module* PyModuleHelper::module() const
++{
++ return myModule;
++}
++
++/*!
++ \brief Get Python GUI module object
++ \return python module
++*/
++PyObject* PyModuleHelper::pythonModule() const
++{
++ return myPyModule;
++}
++
++/*!
++ \brief Connect action to the internal actionActivated() slot.
++
++ Actions connected to internal actionActivated(), when activated, will
++ be forwarded to the Python GUI module OnGUIEvent() function.
++
++ \param a action being connected
++*/
++void PyModuleHelper::connectAction( QAction* a )
++{
++ if ( myModule && a )
++ QObject::connect( a, SIGNAL( triggered( bool ) ),
++ this, SLOT( actionActivated() ),
++ Qt::UniqueConnection );
++}
++
++/*!
++ \brief Get the dockable windows associated with the module.
++
++ To fill the list of windows the correspondind Python module's windows()
++ method is called during the module initialization.
++
++ By default, ObjectBrowser, PythonConsole and LogWindow windows are
++ associated to the module.
++
++ Allowed dockable windows:
++ - LightApp_Application::WT_ObjectBrowser : object browser
++ - LightApp_Application::WT_PyConsole : python console
++ - LightApp_Application::WT_LogWindow : log messages output window
++
++ Dock area is defined by Qt::DockWidgetArea enumeration:
++ - Qt::TopDockWidgetArea : top dock area
++ - Qt::BottomDockWidgetArea : bottom dock area
++ - Qt::LeftDockWidgetArea : left dock area
++ - Qt::RightDockWidgetArea : right dock area
++
++ \return map of dockable windows in form { <window_type> : <dock_area> }
++*/
++QMap<int, int> PyModuleHelper::windows() const
++{
++ FuncMsg fmsg( "PyModuleHelper::windows()" );
++
++ return myWindowsMap;
++}
++
++/*!
++ \brief Define the compatible view windows associated with the module.
++
++ The associated view windows are opened automatically when the module
++ is activated.
++
++ To fill the list of views the correspondind Python module's views()
++ method is called during the module initialization.
++ By default, the list of view types is empty.
++
++ \return list of view windows types
++*/
++QStringList PyModuleHelper::viewManagers() const
++{
++ FuncMsg fmsg( "PyModuleHelper::viewManagers()" );
++
++ return myViewMgrList;
++}
++
++/*!
++ \brief Initialization of the Python-based SALOME module.
++
++ This method can be used for creation of the menus, toolbars and
++ other such stuff.
++
++ There are two ways to do this:
++ 1) for obsolete modules, the implementation of this method first tries to read
++ the <module>_<language>.xml resource file which contains a menu,
++ toolbars and popup menus description;
++ 2) new modules can create menus by direct calling of the
++ corresponding methods of SalomePyQt Python API in the Python
++ module's initialize() method which is called from here.
++
++ \note SALOME supports two modes of modules loading:
++ - immediate (all the modules are created and initialized
++ immediately when the application object is created);
++ - postponed modules loading (used currently); in this mode
++ the module is loaded only by explicit request.
++ If postponed modules loading is not used, the active
++ study might be not yet defined at this stage, so initialize()
++ method should not perform any study-based initialization.
++ Such actions can be better done in activate() function.
++
++ \param app parent application object
++*/
++void PyModuleHelper::initialize( CAM_Application* app )
++{
++ FuncMsg fmsg( "PyModuleHelper::initialize()" );
++
++ // temporarily store module being currently activated
++ // in the global variable to make it accessible from
++ // Python API
++ InitLocker lock( myModule );
++
++ // try to get XML resource file name
++ SUIT_ResourceMgr* resMgr = myModule->getApp()->resourceMgr();
++ if ( !myXmlHandler && resMgr ) {
++ // get current language
++ QString lang = resMgr->stringValue( "language", "language", "en" );
++ // get menu description file name
++ QString aFileName = QString( "%1_%2.xml" ).arg( myModule->name() ).arg( lang );
++ aFileName = resMgr->path( "resources", myModule->name(), aFileName );
++ if ( !aFileName.isEmpty() && QFile::exists( aFileName ) ) {
++ // create XML handler instance
++ myXmlHandler = new XmlHandler( this, aFileName );
++ // ask XML handler to create actions
++ myXmlHandler->createActions();
++ }
++ }
++
++ class InitializeReq : public PyInterp_Request
++ {
++ public:
++ InitializeReq( PyModuleHelper* _helper,
++ CAM_Application* _app )
++ : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ),
++ myApp( _app )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalInitialize( myApp );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ CAM_Application* myApp;
++ };
++
++ // post request
++ PyInterp_Dispatcher::Get()->Exec( new InitializeReq( this, app ) );
++}
++
++/*!
++ \brief Activation of the module.
++
++ This function is usually used in order to show the module's
++ specific menus and toolbars, update actions state and perform
++ other such actions required when the module is activated.
++
++ \note Returning \c false from this function prevents the
++ module activation.
++
++ \param study parent study
++ \return \c true if activation is successful and \c false otherwise
++*/
++bool PyModuleHelper::activate( SUIT_Study* study )
++{
++ FuncMsg fmsg( "PyModuleHelper::activate()" );
++
++ // reset the activation status to the default value
++ myLastActivateStatus = true;
++
++ class ActivateReq : public PyInterp_Request
++ {
++ public:
++ ActivateReq( PyModuleHelper* _helper,
++ SUIT_Study* _study,
++ bool _customize )
++ : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ),
++ myStudy ( _study ),
++ myCustomize( _customize )
++ {}
++ protected:
++ virtual void execute()
++ {
++ if ( !myCustomize )
++ myHelper->internalActivate( myStudy ); // first activation stage
++ else
++ myHelper->internalCustomize( myStudy ); // second activation stage
++ }
++ private:
++ PyModuleHelper* myHelper;
++ SUIT_Study* myStudy;
++ bool myCustomize;
++ };
++
++ // post request for activation (customize=false)
++ PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, false ) );
++
++ // check activation status (can be set to false by internalActivate())
++ if ( myLastActivateStatus ) {
++ // activate menus, toolbars, etc
++ if ( myXmlHandler ) myXmlHandler->activateMenus( true );
++
++ // show menus / toolbars
++ myModule->setMenuShown( true );
++ myModule->setToolShown( true );
++
++ // post request for customization (customize=true)
++ PyInterp_Dispatcher::Get()->Exec( new ActivateReq( this, study, true ) );
++
++ // check activation status (can be set to false by internalCustomize())
++ if ( myLastActivateStatus ) {
++ // connect preferences changing signal
++ connect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
++ this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
++
++ // connect active view change signal
++ SUIT_Desktop* d = study->application()->desktop();
++ connect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
++ this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
++ // if active window exists, call activeViewChanged() function;
++ // temporary solution: if a getActiveView() in SalomePyQt available
++ // we no longer need this
++ SUIT_ViewWindow* view = d->activeWindow();
++ if ( view ) activeViewChanged( view );
++ // get all view currently opened in the study and connect their signals to
++ // the corresponding slots of the class.
++ foreach ( view, d->windows() ) connectView( view );
++ }
++ else {
++ // hide menus / toolbars in case of error
++ myModule->setMenuShown( false );
++ myModule->setToolShown( false );
++ }
++ }
++
++ return myLastActivateStatus;
++}
++
++/*!
++ \brief Deactivation of the module.
++
++ This function is usually used in order to hide the module's
++ specific menus and toolbars and perform other such actions
++ required when the module is deactivated.
++
++ \param study parent study
++ \return \c true if deactivation is successful and \c false otherwise
++*/
++bool PyModuleHelper::deactivate( SUIT_Study* study )
++{
++ FuncMsg fmsg( "PyModuleHelper::deactivate()" );
++
++ class DeactivateReq : public PyInterp_LockRequest
++ {
++ public:
++ DeactivateReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ SUIT_Study* _study )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ),
++ myStudy ( _study )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalDeactivate( myStudy );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ SUIT_Study* myStudy;
++ };
++
++ // post request
++ PyInterp_Dispatcher::Get()->Exec( new DeactivateReq( myInterp, this, study ) );
++
++ // disconnect preferences changing signal
++ disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
++ this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
++
++ // disconnect the SUIT_Desktop signal windowActivated()
++ SUIT_Desktop* d = study->application()->desktop();
++ disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
++ this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
++
++ // deactivate menus, toolbars, etc
++ if ( myXmlHandler ) myXmlHandler->activateMenus( false );
++
++ // hide menus / toolbars
++ myModule->setMenuShown( false );
++ myModule->setToolShown( false );
++
++ return true;
++}
++
++/*!
++ \brief Close of the module.
++
++ This function is usually used in order to close the module's
++ specific menus and toolbars and perform other such actions
++ required when the module is closed.
++*/
++void PyModuleHelper::modelClosed( SUIT_Study* study )
++{
++ FuncMsg fmsg( "PyModuleHelper::modelClosed()" );
++
++ class StudyClosedReq : public PyInterp_LockRequest
++ {
++ public:
++ StudyClosedReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ SUIT_Study* _study )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ),
++ myStudy ( _study )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalClosedStudy( myStudy );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ SUIT_Study* myStudy;
++ };
++
++ // post request
++ PyInterp_Dispatcher::Get()->Exec( new StudyClosedReq( myInterp, this, study ) );
++
++ // disconnect preferences changing signal
++ disconnect( myModule->getApp(), SIGNAL( preferenceChanged( const QString&, const QString&, const QString& ) ),
++ this, SLOT( preferenceChanged( const QString&, const QString&, const QString& ) ) );
++
++ // disconnect the SUIT_Desktop signal windowActivated()
++ SUIT_Desktop* d = study->application()->desktop();
++ disconnect( d, SIGNAL( windowActivated( SUIT_ViewWindow* ) ),
++ this, SLOT( activeViewChanged( SUIT_ViewWindow* ) ) );
++
++ // deactivate menus, toolbars, etc
++ if ( myXmlHandler ) myXmlHandler->activateMenus( false );
++
++ // hide menus / toolbars
++ myModule->setMenuShown( false );
++ myModule->setToolShown( false );
++}
++
++
++/*!
++ \brief Process module's preferences changing.
++
++ Called when the module's own preferences are changed.
++
++ \param section preference resources section
++ \param parameter preference resources parameter name
++*/
++void PyModuleHelper::preferencesChanged( const QString& section,
++ const QString& parameter )
++{
++ FuncMsg fmsg( "PyModuleHelper::preferencesChanged()" );
++
++ class PrefChangeReq : public PyInterp_LockRequest
++ {
++ public:
++ PrefChangeReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ const QString& _section,
++ const QString& _parameter )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper ( _helper ),
++ mySection( _section ),
++ myParameter( _parameter )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalPreferencesChanged( mySection, myParameter );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ QString mySection, myParameter;
++ };
++
++ // post the request only if dispatcher is not busy!
++ // execute request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new PrefChangeReq( myInterp, this, section, parameter ) );
++}
++
++/*!
++ \brief Process application preferences changing.
++
++ Called when any application setting is changed.
++
++ \param module preference module
++ \param section preference resources section
++ \param parameter preference resources parameter name
++*/
++void PyModuleHelper::preferenceChanged( const QString& module,
++ const QString& section,
++ const QString& parameter )
++{
++ FuncMsg fmsg( "PyModuleHelper::preferenceChanged()" );
++
++ // module's own preferences are processed by other preferencesChanged() method
++ if ( module != myModule->moduleName() ) {
++ // call helper
++ preferencesChanged( section, parameter );
++ }
++}
++
++/*!
++ \brief Process study activation.
++
++ Called when study desktop is activated. Used for notifying the Python
++ module about changing of the active study.
++
++ \param study study being activated
++*/
++void PyModuleHelper::studyActivated( SUIT_Study* study )
++{
++ FuncMsg fmsg( "PyModuleHelper::studyActivated()" );
++
++ // StudyChangedReq: request class for internal studyChanged() operation
++ class StudyChangedReq : public PyInterp_Request
++ {
++ public:
++ StudyChangedReq( PyModuleHelper* _helper,
++ SUIT_Study* _study )
++ : PyInterp_Request(0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ),
++ myStudy ( _study )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalStudyChanged( myStudy );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ SUIT_Study* myStudy;
++ };
++
++ // post request
++ PyInterp_Dispatcher::Get()->Exec( new StudyChangedReq( this, study ) );
++}
++
++/*!
++ \brief Process action activation.
++
++ Called when action is activated. Used for notifying the Python
++ module about any related action activation.
++
++ \sa connectAction()
++*/
++void PyModuleHelper::actionActivated()
++{
++ FuncMsg fmsg( "PyModuleHelper::actionActivated()" );
++
++ // perform synchronous request to Python event dispatcher
++ class ActionReq : public PyInterp_LockRequest
++ {
++ public:
++ ActionReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ int _id )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ),
++ myId ( _id )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalActionActivated( myId );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ int myId;
++ };
++
++ // get sender action
++ QAction* action = qobject_cast<QAction*>( sender() );
++ if ( !action )
++ return;
++
++ // post request
++ PyInterp_Dispatcher::Get()->Exec( new ActionReq( myInterp, this, myModule->actionId( action ) ) );
++}
++
++/*!
++ \brief update selection from other views or modules.
++
++ Called when selection is modified outside.
++*/
++void PyModuleHelper::selectionUpdated(const QStringList& entries)
++{
++ FuncMsg fmsg( "PyModuleHelper::selectionUpdated()" );
++ MESSAGE("selectionUpdated");
++
++ // perform synchronous request to Python event dispatcher
++ class SelectionReq : public PyInterp_LockRequest
++ {
++ public:
++ SelectionReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ const QStringList& _entries )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ),
++ myEntries( _entries )
++ {
++ MESSAGE("SelectionReq");
++ }
++ protected:
++ virtual void execute()
++ {
++ MESSAGE("execute");
++ myHelper->internalSelectionUpdated( myEntries );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ const QStringList& myEntries;
++ };
++
++ // post request
++ PyInterp_Dispatcher::Get()->Exec( new SelectionReq( myInterp, this, entries ) );
++}
++
++/*!
++ \brief Process context popup menu request.
++
++ Called when user activates popup menu in some window
++ (view, object browser, etc).
++
++ \param context popup menu context (e.g. "ObjectBrowser")
++ \param menu popup menu
++*/
++void PyModuleHelper::contextMenu( const QString& context, QMenu* menu )
++{
++ FuncMsg fmsg( "PyModuleHelper::contextMenu()" );
++
++ class ContextMenuReq : public PyInterp_LockRequest
++ {
++ public:
++ ContextMenuReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ const QString& _context,
++ QMenu* _menu )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper ( _helper ),
++ myContext( _context ),
++ myMenu ( _menu )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalContextMenu( myContext, myMenu );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ QString myContext;
++ QMenu* myMenu;
++ };
++
++ // post request only if dispatcher is not busy!
++ // execute request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new ContextMenuReq( myInterp, this, context, menu ) );
++}
++
++/*!
++ \brief Export preferences for the Python module.
++ Called only once when the first instance of the module is created or
++ when common Preferences dialog box is first time invoked.
++*/
++void PyModuleHelper::createPreferences()
++{
++ FuncMsg fmsg( "PyModuleHelper::createPreferences()" );
++
++ // temporary set myInitModule because createPreferences() method
++ // might be called during the module intialization process
++ InitLocker lock( myModule );
++
++ class CreatePrefReq : public PyInterp_LockRequest
++ {
++ public:
++ CreatePrefReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalCreatePreferences();
++ }
++ private:
++ PyModuleHelper* myHelper;
++ };
++
++ // post request only if dispatcher is not busy!
++ // execute request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new CreatePrefReq( myInterp, this ) );
++}
++
++/*!
++ \brief Signal handler windowActivated(SUIT_ViewWindow*) of SUIT_Desktop
++
++ Used to notify Python module that active view has been changed by the user.
++
++ \param view view being activated
++*/
++void PyModuleHelper::activeViewChanged( SUIT_ViewWindow* view )
++{
++ FuncMsg fmsg( "PyModuleHelper::activeViewChanged()" );
++
++ // perform synchronous request to Python event dispatcher
++ class ActiveViewChangeReq : public PyInterp_LockRequest
++ {
++ public:
++ ActiveViewChangeReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ SUIT_ViewWindow* _view )
++ : PyInterp_LockRequest( _py_interp, 0, true ),
++ myHelper( _helper ),
++ myView( _view )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalActiveViewChanged( myView );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ SUIT_ViewWindow* myView;
++ };
++
++ // connect view (if it is not connected yet)
++ connectView( view );
++
++ PyInterp_Dispatcher::Get()->Exec( new ActiveViewChangeReq( myInterp, this, view ) );
++}
++
++/*!
++ \brief Signal handler tryClose(SUIT_ViewWindow*) of a view
++ \param view view being closed
++*/
++void PyModuleHelper::tryCloseView( SUIT_ViewWindow* view )
++{
++ FuncMsg fmsg( "PyModuleHelper::tryCloseView()" );
++
++ class TryCloseViewReq : public PyInterp_LockRequest
++ {
++ public:
++ TryCloseViewReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ SUIT_ViewWindow* _view )
++ : PyInterp_LockRequest( _py_interp, 0, true ),
++ myHelper( _helper ),
++ myView( _view )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalTryCloseView( myView );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ SUIT_ViewWindow* myView;
++ };
++
++ PyInterp_Dispatcher::Get()->Exec( new TryCloseViewReq( myInterp, this, view ) );
++}
++
++/*!
++ \brief Signal handler closing(SUIT_ViewWindow*) of a view
++ \param view view being closed
++*/
++void PyModuleHelper::closeView( SUIT_ViewWindow* view )
++{
++ FuncMsg fmsg( "PyModuleHelper::closeView()" );
++
++ class CloseViewReq : public PyInterp_LockRequest
++ {
++ public:
++ CloseViewReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ SUIT_ViewWindow* _view )
++ : PyInterp_LockRequest( _py_interp, 0, true ),
++ myHelper( _helper ),
++ myView( _view )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalCloseView( myView );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ SUIT_ViewWindow* myView;
++ };
++
++ PyInterp_Dispatcher::Get()->Exec( new CloseViewReq( myInterp, this, view ) );
++}
++
++/*!
++ \brief Signal handler cloneView() of OCCViewer_ViewWindow
++ \param view view being cloned
++*/
++void PyModuleHelper::cloneView( SUIT_ViewWindow* view )
++{
++ FuncMsg fmsg( "PyModuleHelper::cloneView()" );
++
++ class CloneViewReq : public PyInterp_LockRequest
++ {
++ public:
++ CloneViewReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ SUIT_ViewWindow* _view )
++ : PyInterp_LockRequest( _py_interp, 0, true ),
++ myHelper( _helper ),
++ myView( _view )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalCloneView( myView );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ SUIT_ViewWindow* myView;
++ };
++
++ PyInterp_Dispatcher::Get()->Exec( new CloneViewReq( myInterp, this, view ) );
++}
++
++/*!
++ \brief Save module data. Called when user saves study.
++ \param files output list of files where module stores data
++ \param url study URL
++*/
++void PyModuleHelper::save( QStringList& files, const QString& url )
++{
++ FuncMsg fmsg( "PyModuleHelper::save()" );
++
++ // temporary set myInitModule because save() method
++ // might be called by the framework when this module is inactive,
++ // but still it should be possible to access this module's data
++ // from Python
++ InitLocker lock( myModule );
++
++ // perform synchronous request to Python event dispatcher
++ class SaveReq: public PyInterp_LockRequest
++ {
++ public:
++ SaveReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ QStringList& _files,
++ const QString& _url )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ) ,
++ myFiles( _files ),
++ myUrl( _url )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalSave( myFiles, myUrl );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ QStringList& myFiles;
++ QString myUrl;
++ };
++
++ // Posting the request only if dispatcher is not busy!
++ // Executing the request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new SaveReq( myInterp, this, files, url ) );
++}
++
++/*
++ \brief Load module data. Called when user opens study
++ and activates module.
++ \param files list of files where module data is stored
++ \param url study URL
++ \return \c true if loading has been finished successfully or \c false otherwise
++*/
++bool PyModuleHelper::load( const QStringList& files, const QString& url )
++{
++ FuncMsg fmsg( "PyModuleHelper::load()" );
++
++ bool loaded = false;
++
++ class LoadReq: public PyInterp_LockRequest
++ {
++ public:
++ LoadReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ QStringList _files,
++ const QString& _url,
++ bool& _loaded )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ) ,
++ myFiles( _files ),
++ myUrl( _url ),
++ myLoaded( _loaded )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalLoad( myFiles, myUrl, myLoaded );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ QStringList myFiles;
++ QString myUrl;
++ bool& myLoaded;
++ };
++
++ // Posting the request only if dispatcher is not busy!
++ // Executing the request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new LoadReq( myInterp, this, files, url, loaded ) );
++
++ return loaded;
++}
++
++/*!
++ \brief Dump module data to the Python script.
++ Called when user activates dump study operation.
++ \param files output list of files where module stores python script
++*/
++void PyModuleHelper::dumpPython( QStringList& files )
++{
++ FuncMsg fmsg( "PyModuleHelper::dumpPython()" );
++
++ // temporary set myInitModule because dumpPython() method
++ // might be called by the framework when this module is inactive,
++ // but still it should be possible to access this module's data
++ // from Python
++ InitLocker lock( myModule );
++
++ class DumpPythonReq: public PyInterp_LockRequest
++ {
++ public:
++ DumpPythonReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ QStringList& _files )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ) ,
++ myFiles( _files )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalDumpPython( myFiles );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ QStringList& myFiles;
++ };
++
++ // Posting the request only if dispatcher is not busy!
++ // Executing the request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new DumpPythonReq( myInterp, this, files ) );
++}
++
++/*!
++ \brief Test if object \a what can be dragged by the user.
++ \param what data object being tested
++ \return \c true if object can be dragged or \c false otherwise
++*/
++bool PyModuleHelper::isDraggable( const SUIT_DataObject* what ) const
++{
++ FuncMsg fmsg( "PyModuleHelper::isDraggable()" );
++
++ bool draggable = false;
++
++ // perform synchronous request to Python event dispatcher
++ class IsDraggableReq: public PyInterp_LockRequest
++ {
++ public:
++ IsDraggableReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ LightApp_DataObject* _data_object,
++ bool& _is_draggable )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ) ,
++ myDataObject( _data_object ),
++ myIsDraggable( _is_draggable )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myIsDraggable = myHelper->internalIsDraggable( myDataObject );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ LightApp_DataObject* myDataObject;
++ bool& myIsDraggable;
++ };
++
++ const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( what );
++ if ( data_object ) {
++ // Posting the request only if dispatcher is not busy!
++ // Executing the request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new IsDraggableReq( myInterp,
++ const_cast<PyModuleHelper*>( this ),
++ const_cast<LightApp_DataObject*>( data_object ),
++ draggable ) );
++ }
++
++ return draggable;
++}
++
++/*!
++ \brief Test if drop operation can be done on the \a where object.
++ \param where data object being tested
++ \return \c true if if drop operation is supported by object or \c false otherwise
++*/
++bool PyModuleHelper::isDropAccepted( const SUIT_DataObject* where ) const
++{
++ FuncMsg fmsg( "PyModuleHelper::isDropAccepted()" );
++
++ bool dropAccepted = false;
++
++ // perform synchronous request to Python event dispatcher
++ class IsDropAcceptedReq: public PyInterp_LockRequest
++ {
++ public:
++ IsDropAcceptedReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ LightApp_DataObject* _data_object,
++ bool& _is_drop_accepted )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ) ,
++ myDataObject( _data_object ),
++ myIsDropAccepted( _is_drop_accepted )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myIsDropAccepted = myHelper->internalIsDropAccepted( myDataObject );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ LightApp_DataObject* myDataObject;
++ bool& myIsDropAccepted;
++ };
++
++ const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( where );
++ if ( data_object ) {
++ // Posting the request only if dispatcher is not busy!
++ // Executing the request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new IsDropAcceptedReq( myInterp,
++ const_cast<PyModuleHelper*>( this ),
++ const_cast<LightApp_DataObject*>( data_object ),
++ dropAccepted ) );
++ }
++
++ return dropAccepted;
++}
++
++/*!
++ \brief Perform drop operation
++ \param what list of data objects being dropped
++ \param where target data object for drop operation
++ \param row line (child item index) where drop operation is performed to
++ \param action current drop action (copy or move)
++*/
++void PyModuleHelper::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
++ const int row, Qt::DropAction action )
++{
++ FuncMsg fmsg( "PyModuleHelper::dropObjects()" );
++
++ // perform synchronous request to Python event dispatcher
++ class DropObjectsReq: public PyInterp_LockRequest
++ {
++ public:
++ DropObjectsReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ const DataObjectList& _what,
++ SUIT_DataObject* _where,
++ const int _row,
++ Qt::DropAction _action )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ) ,
++ myWhat( _what ),
++ myWhere( _where ),
++ myRow( _row ),
++ myAction ( _action )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalDropObjects( myWhat, myWhere, myRow, myAction );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ DataObjectList myWhat;
++ SUIT_DataObject* myWhere;
++ int myRow;
++ Qt::DropAction myAction;
++ };
++
++ // Posting the request only if dispatcher is not busy!
++ // Executing the request synchronously
++ if ( !PyInterp_Dispatcher::Get()->IsBusy() )
++ PyInterp_Dispatcher::Get()->Exec( new DropObjectsReq( myInterp, this, what, where, row, action ) );
++}
++
++/*!
++ \brief Get module engine IOR
++ \return engine IOR as it is supplied by GUI Python module
++ */
++QString PyModuleHelper::engineIOR() const
++{
++ FuncMsg fmsg( "PyModuleHelper::engineIOR()" );
++
++ class EngineIORReq : public PyInterp_LockRequest
++ {
++ public:
++ EngineIORReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ QString& _ior )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ),
++ myIOR( _ior )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myIOR = myHelper->internalEngineIOR();
++ }
++ private:
++ PyModuleHelper* myHelper;
++ QString& myIOR;
++ };
++
++ static QString anIOR;
++
++ if ( anIOR.isEmpty() ) {
++ // post request
++ PyInterp_Dispatcher::Get()->Exec( new EngineIORReq( myInterp,
++ const_cast<PyModuleHelper*>( this ),
++ anIOR ) );
++ }
++
++ return anIOR;
++}
++
++/*!
++ \brief Initialize python subinterpreter (one per study).
++ \internal
++ \param studyId study ID
++*/
++void PyModuleHelper::initInterp( int studyId )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::initInterp()" );
++
++ // check study Id
++ if ( !studyId ) {
++ // Error! Study Id must not be 0!
++ myInterp = 0;
++ return;
++ }
++
++ QMutexLocker ml( &myInitMutex );
++
++ // try to find the subinterpreter
++ if ( myInterpMap.contains( studyId ) ) {
++ // found!
++ myInterp = myInterpMap[ studyId ];
++ return;
++ }
++
++ myInterp = new SALOME_PYQT_PyInterp();
++ myInterp->initialize();
++ myInterpMap[ studyId ] = myInterp;
++
++#ifndef GUI_DISABLE_CORBA
++ if ( !SUIT_PYTHON::initialized ) {
++ // import 'salome' module and call 'salome_init' method;
++ // do it only once on interpreter creation
++ // ... first get python lock
++ PyLockWrapper aLock; // Acquire GIL
++ // ... then import a module
++ PyObjWrapper aMod = PyImport_ImportModule( "salome" );
++ if ( !aMod ) {
++ // Error!
++ PyErr_Print();
++ return;
++ }
++ // ... then call a method
++ int embedded = 1;
++ PyObjWrapper aRes( PyObject_CallMethod( aMod, (char*)"salome_init", (char*)"ii", studyId, embedded ) );
++ if ( !aRes ) {
++ // Error!
++ PyErr_Print();
++ return;
++ }
++ }
++#endif
++}
++
++/*!
++ \brief Import Python GUI module and store reference to the module.
++ \internal
++
++ Warning! initInterp() should be called first!!!
++*/
++void PyModuleHelper::importModule()
++{
++ FuncMsg fmsg( "--- PyModuleHelper::importModule()" );
++
++ // check if the subinterpreter is initialized
++ if ( !myInterp ) {
++ // Error! Python subinterpreter should be initialized first!
++ myPyModule = 0;
++ return;
++ }
++
++ // import Python GUI module and put it in <myPyModule> attribute
++ // ... first get python lock
++ PyLockWrapper aLock; // Acquire GIL
++ // ... then import a module
++ QString aMod = QString( "%1GUI" ).arg( myModule->name() );
++ try {
++ myPyModule = PyImport_ImportModule( aMod.toLatin1().data() );
++ }
++ catch (...) {
++ }
++
++ if ( !myPyModule ) {
++ // Error!
++ PyErr_Print();
++ return;
++ }
++}
++
++/*!
++ \brief Set study workspace to the Python module.
++ \internal
++
++ Calls setWorkSpace() method of the Python module with
++ PyQt QWidget object to use with interpreter.
++
++ Attention! initInterp() and importModule() should be called first!!!
++*/
++void PyModuleHelper::setWorkSpace()
++{
++ FuncMsg fmsg( "--- PyModuleHelper::setWorkSpace()" );
++
++ if ( !IsCallOldMethods )
++ return;
++
++ // check if the subinterpreter is initialized and Python module is imported
++ if ( !myInterp || !myPyModule ) {
++ // Error! Python subinterpreter should be initialized and module should be imported first!
++ return;
++ }
++
++ // call setWorkSpace() method
++ // ... first get python lock
++ PyLockWrapper aLock; // Acquire GIL
++
++ // ... then try to import SalomePyQt module. If it's not possible don't go on.
++ PyObjWrapper aQtModule( PyImport_ImportModule( "SalomePyQt" ) );
++ if( !aQtModule ) {
++ // Error!
++ PyErr_Print();
++ return;
++ }
++
++ // ... then get workspace object
++ QWidget* aWorkspace = 0;
++ if ( myModule->getApp()->desktop()->inherits( "STD_MDIDesktop" ) ) {
++ STD_MDIDesktop* d = dynamic_cast<STD_MDIDesktop*>( myModule->getApp()->desktop() );
++ if ( d )
++ aWorkspace = d->workspace();
++ }
++ else if ( myModule->getApp()->desktop()->inherits( "STD_TabDesktop" ) ) {
++ STD_TabDesktop* d = dynamic_cast<STD_TabDesktop*>( myModule->getApp()->desktop() );
++ if ( d )
++ aWorkspace = d->workstack();
++ }
++#if SIP_VERSION < 0x040800
++ PyObjWrapper pyws( sipBuildResult( 0, "M", aWorkspace, sipClass_QWidget) );
++#else
++ PyObjWrapper pyws( sipBuildResult( 0, "D", aWorkspace, sipType_QWidget , NULL) );
++#endif
++ // ... and finally call Python module's setWorkSpace() method (obsolete)
++ if ( PyObject_HasAttrString( myPyModule, (char*)"setWorkSpace" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setWorkSpace", (char*)"O", pyws.get() ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief Initialization callback function
++ \internal
++
++ Performs the following actions:
++ - initialize or get the Python interpreter (one per study)
++ - import the Python module
++ - pass the workspace widget to the Python module
++ - call Python module's initialize() method
++ - call Python module's windows() method
++ - call Python module's views() method
++
++ \param app parent application object
++*/
++void PyModuleHelper::internalInitialize( CAM_Application* app )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalInitialize()" );
++
++ // reset interpreter to NULL
++ myInterp = 0;
++
++ // get study Id
++ LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( app );
++ if ( !anApp )
++ return;
++ LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( app->activeStudy() );
++ if ( !aStudy )
++ return;
++ int aStudyId = aStudy ? aStudy->id() : 0;
++
++ // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
++ initInterp( aStudyId );
++ if ( !myInterp )
++ return; // Error
++
++ // import Python GUI module
++ importModule();
++ if ( !myPyModule )
++ return; // Error
++
++ // then call Python module's initialize() method
++ // ... first get python lock
++ PyLockWrapper aLock; // Acquire GIL
++
++ // ... (the Python module is already imported)
++ // ... finally call Python module's initialize() method
++ if ( PyObject_HasAttrString( myPyModule, (char*)"initialize" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"initialize", (char*)"" ) );
++ if ( !res ) {
++ PyErr_Print();
++ }
++ }
++
++ // get required dockable windows list from the Python module
++ // by calling windows() method
++ // ... first put default values
++ myWindowsMap.insert( LightApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea );
++ myWindowsMap.insert( LightApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea );
++ myWindowsMap.insert( LightApp_Application::WT_LogWindow, Qt::BottomDockWidgetArea );
++
++ if ( PyObject_HasAttrString( myPyModule , (char*)"windows" ) ) {
++ PyObjWrapper res1( PyObject_CallMethod( myPyModule, (char*)"windows", (char*)"" ) );
++ if ( !res1 ) {
++ PyErr_Print();
++ }
++ else {
++ myWindowsMap.clear();
++ if ( PyDict_Check( res1 ) ) {
++ PyObject* key;
++ PyObject* value;
++ Py_ssize_t pos = 0;
++ while ( PyDict_Next( res1, &pos, &key, &value ) ) {
++ // parse the return value
++ // it should be a map: {integer:integer}
++ int aKey, aValue;
++ if( key && PyInt_Check( key ) && value && PyInt_Check( value ) ) {
++ aKey = PyInt_AsLong( key );
++ aValue = PyInt_AsLong( value );
++ myWindowsMap[ aKey ] = aValue;
++ }
++ }
++ }
++ }
++ }
++
++ // get compatible view windows types from the Python module
++ // by calling views() method
++ if ( PyObject_HasAttrString( myPyModule , (char*)"views" ) ) {
++ PyObjWrapper res2( PyObject_CallMethod( myPyModule, (char*)"views", (char*)"" ) );
++ if ( !res2 ) {
++ PyErr_Print();
++ }
++ else {
++ // parse the return value
++ // result can be one string...
++ if ( PyString_Check( res2 ) ) {
++ myViewMgrList.append( PyString_AsString( res2 ) );
++ }
++ // ... or list of strings
++ else if ( PyList_Check( res2 ) ) {
++ int size = PyList_Size( res2 );
++ for ( int i = 0; i < size; i++ ) {
++ PyObject* value = PyList_GetItem( res2, i );
++ if( value && PyString_Check( value ) ) {
++ myViewMgrList.append( PyString_AsString( value ) );
++ }
++ }
++ }
++ }
++ }
++}
++
++/*!
++ \brief Activation callback function
++ \internal
++
++ Performs the following actions:
++ - initialize or get the Python interpreter (one per study)
++ - import the Python GUI module
++ - call Python module's activate() method
++
++ \param study parent study
++*/
++void PyModuleHelper::internalActivate( SUIT_Study* study )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalActivate()" );
++
++ // get study Id
++ LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
++ int aStudyId = aStudy ? aStudy->id() : 0;
++
++ // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
++ initInterp( aStudyId );
++ if ( !myInterp ) {
++ myLastActivateStatus = false;
++ return; // Error
++ }
++
++ // import Python GUI module
++ importModule();
++ if ( !myPyModule ) {
++ myLastActivateStatus = false;
++ return; // Error
++ }
++
++ // get python lock
++ PyLockWrapper aLock; // Acquire GIL
++
++ // call Python module's activate() method (for the new modules)
++ if ( PyObject_HasAttrString( myPyModule , (char*)"activate" ) ) {
++ PyObject* res1 = PyObject_CallMethod( myPyModule, (char*)"activate", (char*)"" );
++ if ( !res1 || !PyBool_Check( res1 ) ) {
++ PyErr_Print();
++ // always true for old modules (no return value)
++ myLastActivateStatus = true;
++ }
++ else {
++ // detect return status
++ myLastActivateStatus = PyObject_IsTrue( res1 );
++ }
++ }
++}
++
++/*!
++ \brief Additional menu customization callback function
++ \internal
++
++ Performs the following actions:
++ - get the Python interpreter (one per study)
++ - import the Python GUI module
++ - call Python module's setSettings() method (obsolete function,
++ used for compatibility with old code)
++
++ \param study parent study
++*/
++void PyModuleHelper::internalCustomize( SUIT_Study* study )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalCustomize()" );
++
++ // get study Id
++ LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
++ int aStudyId = aStudy ? aStudy->id() : 0;
++
++ // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
++ initInterp( aStudyId );
++ if ( !myInterp ) {
++ myLastActivateStatus = false;
++ return; // Error
++ }
++
++ // import Python GUI module
++ importModule();
++ if ( !myPyModule ) {
++ myLastActivateStatus = false;
++ return; // Error
++ }
++
++ // call Python module's setWorkSpace() method (obsolete)
++ setWorkSpace();
++
++ // get python lock
++ PyLockWrapper aLock; // Acquire GIL
++
++ if ( IsCallOldMethods ) {
++ // call Python module's setSettings() method (obsolete)
++ if ( PyObject_HasAttrString( myPyModule , (char*)"setSettings" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"setSettings", (char*)"" ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ myLastActivateStatus = myLastActivateStatus && true;
++ }
++ }
++}
++
++/*!
++ \brief Deactivation callback function
++ \internal
++
++ Performs the following actions:
++ - call Python module's deactivate() method
++
++ \param study parent study
++*/
++void PyModuleHelper::internalDeactivate( SUIT_Study* study )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalDeactivate()" );
++
++ // check that Python subinterpreter is initialized and Python module is imported
++ if ( !myInterp || !myPyModule ) {
++ // Error! Python subinterpreter should be initialized and module should be imported first!
++ return;
++ }
++ // then call Python module's deactivate() method
++ if ( PyObject_HasAttrString( myPyModule , (char*)"deactivate" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"deactivate", (char*)"" ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief Internal closure:
++
++ Performs the following actions:
++ - call Python module's closeStudy() method
++
++ \param theStudy parent study object
++*/
++void PyModuleHelper::internalClosedStudy( SUIT_Study* theStudy )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalClosedStudy()" );
++
++ // Get study Id
++ // get study Id
++ LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( theStudy );
++ int aStudyId = aStudy ? aStudy->id() : 0;
++
++ // check that Python subinterpreter is initialized and Python module is imported
++ if ( !myInterp || !myPyModule ) {
++ // Error! Python subinterpreter should be initialized and module should be imported first!
++ return;
++ }
++ // then call Python module's deactivate() method
++ if ( PyObject_HasAttrString( myPyModule , (char*)"closeStudy" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"closeStudy", (char*)"i", aStudyId ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++
++
++/*!
++ \brief Preference changing callback function.
++ \internal
++
++ Performs the following actions:
++ - call Python module's preferenceChanged() method
++
++ \param section resources section name
++ \param setting resources parameter name
++*/
++void PyModuleHelper::internalPreferencesChanged( const QString& section, const QString& setting )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalPreferencesChanged()" );
++
++ // check that Python subinterpreter is initialized and Python module is imported
++ if ( !myInterp || !myPyModule ) {
++ // Error! Python subinterpreter should be initialized and module should be imported first!
++ return;
++ }
++
++ if ( PyObject_HasAttrString( myPyModule, (char*)"preferenceChanged" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule,
++ (char*)"preferenceChanged",
++ (char*)"ss",
++ section.toLatin1().constData(),
++ setting.toLatin1().constData() ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief Active study change callback function.
++ \internal
++
++ Called when active the study is actived (user brings its
++ desktop to top):
++ - initialize or get the Python interpreter (one per study)
++ - import the Python GUI module
++ - call Python module's activeStudyChanged() method
++
++ \param study study being activated
++*/
++void PyModuleHelper::internalStudyChanged( SUIT_Study* study )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalStudyChanged()" );
++
++ // get study Id
++ LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( study );
++ int id = aStudy ? aStudy->id() : 0;
++
++ fmsg.message( QString( "study id = %1" ).arg( id ) );
++
++ // initialize Python subinterpreter (on per study) and put it in <myInterp> variable
++ initInterp( id );
++ if ( !myInterp )
++ return; // Error
++
++ // import Python GUI module
++ importModule();
++ if ( !myPyModule )
++ return; // Error
++
++ // call Python module's setWorkSpace() method
++ setWorkSpace();
++
++ // get python lock
++ PyLockWrapper aLock; // Acquire GIL
++
++ // call Python module's activeStudyChanged() method
++ if ( PyObject_HasAttrString( myPyModule, (char*)"activeStudyChanged" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeStudyChanged", (char*)"i", id ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief GUI event handling callback function
++ \internal
++
++ Performs the following actions:
++ - calls Python module's OnGUIEvent() method
++
++ \param id GUI action ID
++*/
++void PyModuleHelper::internalActionActivated( int id )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalActionActivated()" );
++ fmsg.message( QString( "action id = %1" ).arg( id ) );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( !myInterp || !myPyModule )
++ return; // Error
++
++ if ( PyObject_HasAttrString( myPyModule, (char*)"OnGUIEvent" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"OnGUIEvent", (char*)"i", id ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief update selection from other views or modules
++ \internal
++
++ Performs the following actions:
++ - calls Python module's onSelectionpdated(entries) method
++
++ \param list of entries
++*/
++void PyModuleHelper::internalSelectionUpdated(const QStringList& entries)
++{
++ FuncMsg fmsg("--- PyModuleHelper::internalSelectionUpdated()");
++ MESSAGE("internalSelectionUpdated");
++
++ // Python interpreter should be initialized and Python module should be imported first
++ if (!myInterp || !myPyModule)
++ return; // Error
++
++ QStringList* theList = new QStringList(entries);
++
++#if SIP_VERSION < 0x040800
++ PyObjWrapper sipList(sipBuildResult(0, "M", theList, sipClass_QStringList));
++#else
++ PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
++#endif
++ if (PyObject_HasAttrString(myPyModule, (char*) "onSelectionUpdated"))
++ {
++ MESSAGE("call onSelectionUpdated");
++ PyObjWrapper res(PyObject_CallMethod(myPyModule, (char*) "onSelectionUpdated", (char*) "O", sipList.get()));
++
++ if (!res)
++ {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief Context popup menu handling callback function
++ \internal
++
++ Performs the following actions:
++ - calls Python module's definePopup(...) method (obsolete function,
++ used for compatibility with old code) to define the popup menu context
++ - parses XML resourses file (if exists) and fills the popup menu with the items)
++ - calls Python module's customPopup(...) method (obsolete function,
++ used for compatibility with old code) to allow module to customize the popup menu
++ - for new modules calls createPopupMenu() function to allow the
++ modules to build the popup menu by using insertItem(...) Qt functions.
++
++ \param context popup menu context
++ \param menu popup menu
++*/
++void PyModuleHelper::internalContextMenu( const QString& context, QMenu* menu )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalContextMenu()" );
++ fmsg.message( QString( "context: %1" ).arg( context ) );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( !myInterp || !myPyModule )
++ return; // Error
++
++ QString aContext( "" ), aObject( "" ), aParent( context );
++
++ if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"definePopup" ) ) {
++ // call definePopup() Python module's function
++ // this is obsolete function, used only for compatibility reasons
++ PyObjWrapper res( PyObject_CallMethod( myPyModule,
++ (char*)"definePopup",
++ (char*)"sss",
++ context.toLatin1().constData(),
++ aObject.toLatin1().constData(),
++ aParent.toLatin1().constData() ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ else {
++ // parse return value
++ char *co, *ob, *pa;
++ if( PyArg_ParseTuple( res, "sss", &co, &ob, &pa ) ) {
++ aContext = co;
++ aObject = ob;
++ aParent = pa;
++ }
++ }
++ } // if ( IsCallOldMethods ... )
++
++ // first try to create menu via XML parser:
++ // we create popup menus without help of QtxPopupMgr
++ if ( myXmlHandler )
++ myXmlHandler->createPopup( menu, aContext, aParent, aObject );
++
++#if SIP_VERSION < 0x040800
++ PyObjWrapper sipPopup( sipBuildResult( 0, "M", menu, sipClass_QMenu ) );
++#else
++ PyObjWrapper sipPopup( sipBuildResult( 0, "D", menu, sipType_QMenu, NULL ) );
++#endif
++
++ // then call Python module's createPopupMenu() method (for new modules)
++ if ( PyObject_HasAttrString( myPyModule, (char*)"createPopupMenu" ) ) {
++ PyObjWrapper res1( PyObject_CallMethod( myPyModule,
++ (char*)"createPopupMenu",
++ (char*)"Os",
++ sipPopup.get(),
++ context.toLatin1().constData() ) );
++ if( !res1 ) {
++ PyErr_Print();
++ }
++ }
++
++ if ( IsCallOldMethods && PyObject_HasAttrString( myPyModule, (char*)"customPopup" ) ) {
++ // call customPopup() Python module's function
++ // this is obsolete function, used only for compatibility reasons
++ PyObjWrapper res2( PyObject_CallMethod( myPyModule,
++ (char*)"customPopup",
++ (char*)"Osss",
++ sipPopup.get(),
++ aContext.toLatin1().constData(),
++ aObject.toLatin1().constData(),
++ aParent.toLatin1().constData() ) );
++ if( !res2 ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief Preferences initialization callback function.
++ \internal
++
++ Performs the following actions:
++ - calls Python module's createPreferences() method
++*/
++void PyModuleHelper::internalCreatePreferences()
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalCreatePreferences()" );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( !myInterp || !myPyModule )
++ return; // Error
++
++ if ( PyObject_HasAttrString( myPyModule, (char*)"createPreferences" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"createPreferences", (char*)"" ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief Active view changing callback function
++ \internal
++ \param view view being activated
++*/
++void PyModuleHelper::internalActiveViewChanged( SUIT_ViewWindow* view )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalActiveViewChanged()" );
++
++ if ( !myInterp || !myPyModule || !view )
++ return;
++
++ fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
++
++ if ( PyObject_HasAttrString( myPyModule, (char*)"activeViewChanged" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"activeViewChanged", (char*)"i" , view->getId() ) );
++ if ( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief View closing callback function
++ \internal
++ \param view view user tries to close
++*/
++void PyModuleHelper::internalTryCloseView( SUIT_ViewWindow* view )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalTryCloseView()" );
++
++ if ( !myInterp || !myPyModule || !view )
++ return;
++
++ fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
++
++ if ( PyObject_HasAttrString( myPyModule, (char*)"viewTryClose" ) )
++ {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewTryClose", (char*)"i", view->getId() ) );
++ if ( !res )
++ {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief View closing callback function
++ \internal
++ \param view view being closed
++*/
++void PyModuleHelper::internalCloseView( SUIT_ViewWindow* view )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalCloseView()" );
++
++ if ( !myInterp || !myPyModule || !view )
++ return;
++
++ fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
++
++ if ( PyObject_HasAttrString( myPyModule, (char*)"viewClosed" ) )
++ {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewClosed", (char*)"i", view->getId() ) );
++ if ( !res )
++ {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief View cloning callback function
++ \internal
++ \param view view being cloned
++*/
++void PyModuleHelper::internalCloneView( SUIT_ViewWindow* view )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalCloneView()" );
++
++ if ( !myInterp || !myPyModule || !view )
++ return;
++
++ fmsg.message( QString( "view id: %1" ).arg( view->getId() ) );
++
++ if ( PyObject_HasAttrString( myPyModule, (char*)"viewCloned" ) )
++ {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"viewCloned", (char*)"i", view->getId() ) );
++ if( !res )
++ PyErr_Print();
++ }
++}
++
++/*!
++ \brief Module data saving callback function.
++ \internal
++ \param files output list of files where module stores data
++ \param url study URL
++*/
++void PyModuleHelper::internalSave( QStringList& files, const QString& url )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalSave()" );
++
++ // Python interpreter should be initialized and Python module should be
++ // import firs
++ // files list should contain a path to the temporary directory as a first entry
++ if ( !myInterp || !myPyModule || files.isEmpty() )
++ return;
++
++ if ( PyObject_HasAttrString(myPyModule, (char*)"saveFiles") ) {
++
++ // try with two parameters (new syntax)
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"saveFiles",
++ (char*)"ss",
++ files.first().toLatin1().constData(),
++ url.toLatin1().constData() ) );
++ if ( !res )
++ // try with single parameter (old syntax)
++ res = PyObject_CallMethod( myPyModule, (char*)"saveFiles",
++ (char*)"s", files.first().toLatin1().constData() );
++
++ if ( !res ) {
++ PyErr_Print();
++ }
++ else {
++ // parse the return value
++ // result can be one string...
++ if ( PyString_Check( res ) ) {
++ QString astr = PyString_AsString( res );
++ files.append( astr );
++ }
++ //also result can be a list...
++ else if ( PyList_Check( res ) ) {
++ int size = PyList_Size( res );
++ for ( int i = 0; i < size; i++ ) {
++ PyObject* value = PyList_GetItem( res, i );
++ if ( value && PyString_Check( value ) ) {
++ files.append( PyString_AsString( value ) );
++ }
++ }
++ }
++ }
++ }
++}
++
++/*!
++ \brief Module data loading callback function.
++ \internal
++ \param files list of files where module data is stored
++ \param url study URL
++ \param opened output success flag
++*/
++void PyModuleHelper::internalLoad( const QStringList& files, const QString& url, bool& opened )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalLoad()" );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( !myInterp || !myPyModule || files.isEmpty() )
++ return;
++
++ QStringList* theList = new QStringList( files );
++
++#if SIP_VERSION < 0x040800
++ PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList ) );
++#else
++ PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL ) );
++#endif
++ if ( PyObject_HasAttrString(myPyModule , (char*)"openFiles") ) {
++
++ // try with two parameters (new syntax)
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"openFiles",
++ (char*)"Os", sipList.get(),
++ url.toLatin1().constData() ) );
++
++ if ( !res )
++ // try with single parameter (old syntax)
++ res = PyObject_CallMethod( myPyModule, (char*)"openFiles",
++ (char*)"O", sipList.get() );
++
++ if ( !res || !PyBool_Check( res ) ) {
++ PyErr_Print();
++ opened = false;
++ }
++ else {
++ opened = PyObject_IsTrue( res );
++ }
++ }
++}
++
++/*!
++ \brief Module dump python callback function.
++ \internal
++ \param files output list of files where module stores python script
++*/
++void PyModuleHelper::internalDumpPython( QStringList& files )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalDumpPython()" );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ // files list should contain a path to the temporary directory
++ if ( !myInterp || !myPyModule || files.isEmpty() )
++ return;
++
++ if ( PyObject_HasAttrString(myPyModule, (char*)"dumpStudy") ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dumpStudy",
++ (char*)"s", files.first().toLatin1().constData()));
++
++ if ( !res ) {
++ PyErr_Print();
++ }
++ else {
++ // parse the return value
++ // result can be one string...
++ if ( PyString_Check( res ) ) {
++ QString astr = PyString_AsString( res );
++ //SCRUTE(astr);
++ files.append(astr);
++ }
++ //also result can be a list...
++ else if ( PyList_Check( res ) ) {
++ int size = PyList_Size( res );
++ for ( int i = 0; i < size; i++ ) {
++ PyObject* value = PyList_GetItem( res, i );
++ if( value && PyString_Check( value ) ) {
++ files.append( PyString_AsString( value ) );
++ }
++ }
++ }
++ }
++ }
++}
++
++/*!
++ \brief Check data object's 'draggable' status callback function.
++ \internal
++ \param what data object being tested
++ \return \c true if object can be dragged or \c false otherwise
++*/
++bool PyModuleHelper::internalIsDraggable( LightApp_DataObject* what )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalIsDraggable()" );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( !myInterp || !myPyModule || !what )
++ return false;
++
++ bool draggable = false;
++
++ if ( PyObject_HasAttrString(myPyModule , (char*)"isDraggable") ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDraggable",
++ (char*)"s", what->entry().toLatin1().constData() ) );
++ if( !res || !PyBool_Check( res )) {
++ PyErr_Print();
++ draggable = false;
++ }
++ else{
++ draggable = PyObject_IsTrue( res );
++ }
++ }
++
++ return draggable;
++}
++
++/*!
++ \brief Check data object's 'drop allowed' status callback function.
++ \internal
++ \param where data object being tested
++ \return \c true if if drop operation is supported by object or \c false otherwise
++*/
++bool PyModuleHelper::internalIsDropAccepted( LightApp_DataObject* where )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalIsDropAccepted()" );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( !myInterp || !myPyModule || !where )
++ return false;
++
++ bool dropAccepted = false;
++
++ if ( PyObject_HasAttrString(myPyModule , (char*)"isDropAccepted") ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"isDropAccepted",
++ (char*)"s", where->entry().toLatin1().constData() ) );
++ if( !res || !PyBool_Check( res )) {
++ PyErr_Print();
++ dropAccepted = false;
++ }
++ else{
++ dropAccepted = PyObject_IsTrue( res );
++ }
++ }
++
++ return dropAccepted;
++}
++
++/*!
++ \brief Data dropping callback function.
++ \internal
++ \param what list of data objects being dropped
++ \param where target data object for drop operation
++ \param row line (child item index) where drop operation is performed to
++ \param action current drop action (copy or move)
++*/
++void PyModuleHelper::internalDropObjects( const DataObjectList& what, SUIT_DataObject* where,
++ const int row, Qt::DropAction action )
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalDropObjects()" );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( !myInterp || !myPyModule || what.isEmpty() || !where )
++ return;
++
++ QStringList* theList = new QStringList();
++
++ LightApp_DataObject* whereObject = dynamic_cast<LightApp_DataObject*>( where );
++ if ( !whereObject ) return;
++
++ for ( int i = 0; i < what.count(); i++ ) {
++ LightApp_DataObject* dataObject = dynamic_cast<LightApp_DataObject*>( what[i] );
++ if ( dataObject ) theList->append( dataObject->entry() );
++ }
++
++#if SIP_VERSION < 0x040800
++ PyObjWrapper sipList( sipBuildResult( 0, "M", theList, sipClass_QStringList) );
++#else
++ PyObjWrapper sipList( sipBuildResult( 0, "D", theList, sipType_QStringList, NULL) );
++#endif
++ if ( PyObject_HasAttrString(myPyModule, (char*)"dropObjects") ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"dropObjects", (char*)"Osii",
++ sipList.get(),
++ whereObject->entry().toLatin1().constData(),
++ row, action ) );
++
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++/*!
++ \brief Get engine IOR callback function
++ \internal
++
++ Tries to get engine IOR from the Python module using engineIOR() function.
++ That function can load module engine using appropriate container if required.
++
++ \return engine IOR or empty string if it is not provided by Python module
++*/
++QString PyModuleHelper::internalEngineIOR() const
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalEngineIOR()" );
++
++ QString ior;
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( myInterp && myModule ) {
++ if ( PyObject_HasAttrString( myPyModule , "engineIOR" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"engineIOR", (char*)"" ) );
++ if ( !res ) {
++ PyErr_Print();
++ }
++ else {
++ // parse the return value, result chould be string
++ if ( PyString_Check( res ) ) {
++ ior = PyString_AsString( res );
++ }
++ }
++ }
++ }
++ return ior;
++}
++
++/*!
++ \brief Connects signals about activating and cloning view on internal slots
++ \param view view being connected
++*/
++void PyModuleHelper::connectView( SUIT_ViewWindow* view )
++{
++ SUIT_ViewManager* viewMgr = view->getViewManager();
++ SUIT_ViewModel* viewModel = viewMgr ? viewMgr->getViewModel() : 0;
++
++ // Connect tryCloseView() and deleteView() signals
++ if ( viewMgr ) {
++ connect( viewMgr, SIGNAL( tryCloseView( SUIT_ViewWindow* ) ),
++ this, SLOT( tryCloseView( SUIT_ViewWindow* ) ),
++ Qt::UniqueConnection );
++ connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ),
++ this, SLOT( closeView( SUIT_ViewWindow* ) ),
++ Qt::UniqueConnection );
++ }
++
++ // Connect cloneView() signal of an OCC View
++ if ( view->inherits( "OCCViewer_ViewWindow" ) ) {
++ connect( view, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
++ this, SLOT( cloneView( SUIT_ViewWindow* ) ),
++ Qt::UniqueConnection );
++ }
++ // Connect cloneView() signal of Plot2d View
++ else if ( viewModel && viewModel->inherits( "Plot2d_Viewer" ) ) {
++ connect( viewModel, SIGNAL( viewCloned( SUIT_ViewWindow* ) ),
++ this, SLOT( cloneView( SUIT_ViewWindow* ) ),
++ Qt::UniqueConnection );
++ }
++}
++
++
++
++void PyModuleHelper::internalOBClickedPython( const QString& theObj, int theColumn)
++{
++ FuncMsg fmsg( "--- PyModuleHelper::internalOBClickedPython()" );
++
++ // Python interpreter should be initialized and Python module should be
++ // import first
++ if ( !myInterp || !myPyModule )
++ return; // Error
++
++ if ( PyObject_HasAttrString( myPyModule, (char*)"onObjectBrowserClicked" ) ) {
++ PyObjWrapper res( PyObject_CallMethod( myPyModule, (char*)"onObjectBrowserClicked", (char*)"si", theObj.toLatin1().constData(), theColumn ) );
++ if( !res ) {
++ PyErr_Print();
++ }
++ }
++}
++
++
++
++void PyModuleHelper::onObjectBrowserClicked(SUIT_DataObject* theObj, int theColumn)
++{
++ FuncMsg fmsg( "PyModuleHelper::onObjectBrowserClicked()" );
++
++ // temporary set myInitModule because dumpPython() method
++ // might be called by the framework when this module is inactive,
++ // but still it should be possible to access this module's data
++ // from Python
++ InitLocker lock( myModule );
++
++ class PythonReq: public PyInterp_LockRequest
++ {
++ public:
++ PythonReq( PyInterp_Interp* _py_interp,
++ PyModuleHelper* _helper,
++ const QString& _entry,
++ int _column )
++ : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true)
++ myHelper( _helper ) ,
++ myEntry( _entry ),
++ myColumn( _column )
++ {}
++ protected:
++ virtual void execute()
++ {
++ myHelper->internalOBClickedPython( myEntry, myColumn );
++ }
++ private:
++ PyModuleHelper* myHelper;
++ int myColumn;
++ QString myEntry;
++ };
++
++ // Posting the request only if dispatcher is not busy!
++ // Executing the request synchronously
++ const LightApp_DataObject* data_object = dynamic_cast<const LightApp_DataObject*>( theObj );
++ if ( (!PyInterp_Dispatcher::Get()->IsBusy()) && data_object )
++ PyInterp_Dispatcher::Get()->Exec( new PythonReq( myInterp, this, data_object->entry(), theColumn ) );
++}
++