From 520643871dad6e3213d9f6d148f87665ff9ced20 Mon Sep 17 00:00:00 2001 From: vsr Date: Mon, 5 Dec 2011 10:06:27 +0000 Subject: [PATCH] Merge from V6_4_BR 05/12/2011 --- Makefile.am | 2 + SUIT.pro | 26 - adm_local/cmake_files/FindVTK.cmake | 32 +- adm_local/unix/config_files/Makefile.am | 3 +- adm_local/unix/config_files/check_pyqt.m4 | 64 ++- adm_local/unix/config_files/check_qt.m4 | 2 +- .../unix/config_files/check_salome_version.m4 | 48 ++ adm_local/unix/make_common_starter.am | 3 + adm_local/unix/make_conclude.in | 442 --------------- bin/runLightSalome.sh | 3 +- clean_configure | 11 +- configure.ac | 6 +- .../images/SALOME_pythonplugins_dialog.png | Bin 0 -> 43868 bytes .../gui/images/SALOME_pythonplugins_menu.png | Bin 0 -> 50854 bytes .../images/SALOME_pythonplugins_result.png | Bin 0 -> 150498 bytes doc/salome/gui/input/installing_salome.doc | 10 +- doc/salome/gui/input/mesh_preferences.doc | 20 +- doc/salome/gui/input/occ_3d_viewer.doc | 10 +- doc/salome/gui/input/plot2d_viewer.doc | 5 +- doc/salome/gui/input/postpro_preferences.doc | 48 +- doc/salome/gui/input/salome_desktop.doc | 1 + doc/salome/gui/input/themes.doc | 27 +- doc/salome/gui/input/using_input_widgets.doc | 2 +- doc/salome/gui/input/using_object_browser.doc | 14 +- doc/salome/gui/input/using_pluginsmanager.doc | 144 +++++ doc/salome/gui/input/vtk_3d_viewer.doc | 20 +- src/CAF/CAF.pro | 57 -- src/CAM/CAM.pro | 59 -- src/CAM/CAM_Application.cxx | 11 +- src/DDS/DDS.pro | 56 -- src/Event/Event.pro | 45 -- src/GLViewer/GLViewer.pro | 105 ---- src/GUI_PY/Makefile.am | 3 - src/GuiHelpers/Makefile.am | 84 +++ src/GuiHelpers/QtHelper.hxx | 46 ++ src/GuiHelpers/SALOME_AppStudyEditor.cxx | 106 ++++ src/GuiHelpers/SALOME_AppStudyEditor.hxx | 47 ++ src/GuiHelpers/SALOME_GuiServices.cxx | 134 +++++ src/GuiHelpers/SALOME_GuiServices.hxx | 84 +++ src/GuiHelpers/StandardApp_Module.cxx | 338 ++++++++++++ src/GuiHelpers/StandardApp_Module.hxx | 145 +++++ src/LightApp/LightApp.pro | 252 --------- src/LightApp/LightApp_Application.cxx | 28 +- src/LightApp/LightApp_DataModel.cxx | 40 +- src/LightApp/LightApp_DataModel.h | 6 + src/LightApp/LightApp_DataObject.cxx | 102 +++- src/LightApp/LightApp_DataObject.h | 9 +- src/LightApp/LightApp_ModuleAction.cxx | 34 +- src/LightApp/LightApp_ModuleAction.h | 9 +- src/LightApp/LightApp_Study.cxx | 38 ++ src/LightApp/LightApp_Study.h | 5 + src/LightApp/resources/LightApp.xml | 1 + src/LightApp/resources/LightApp_msg_fr.ts | 40 +- src/LogWindow/LogWindow.pro | 50 -- src/Makefile.am | 7 +- src/OBJECT/OBJECT.pro | 85 --- src/OCCViewer/OCCViewer.pro | 80 --- src/OCCViewer/OCCViewer_ViewModel.h | 10 +- src/OCCViewer/resources/OCCViewer_msg_fr.ts | 18 +- src/Plot2d/Makefile.am | 57 +- src/Plot2d/Plot2d.cxx | 138 +++++ src/Plot2d/Plot2d.h | 31 ++ src/Plot2d/Plot2d.pro | 80 --- src/Plot2d/Plot2d_AnaliticCurve.cxx | 464 ++++++++++++++++ src/Plot2d/Plot2d_AnaliticCurve.h | 140 +++++ src/Plot2d/Plot2d_AnaliticCurveDlg.cxx | 510 ++++++++++++++++++ src/Plot2d/Plot2d_AnaliticCurveDlg.h | 94 ++++ src/Plot2d/Plot2d_AnaliticParcer.cxx | 272 ++++++++++ .../Plot2d_AnaliticParcer.h} | 66 +-- src/Plot2d/Plot2d_Curve.cxx | 57 +- src/Plot2d/Plot2d_Curve.h | 6 - src/Plot2d/Plot2d_Histogram.cxx | 6 +- src/Plot2d/Plot2d_Object.cxx | 38 -- src/Plot2d/Plot2d_Object.h | 12 - src/Plot2d/Plot2d_SetupCurveDlg.cxx | 76 +-- src/Plot2d/Plot2d_SetupCurveDlg.h | 3 - src/Plot2d/Plot2d_SetupCurveScaleDlg.cxx | 2 +- src/Plot2d/Plot2d_ViewFrame.cxx | 216 +++++++- src/Plot2d/Plot2d_ViewFrame.h | 67 ++- src/Plot2d/Plot2d_ViewWindow.cxx | 17 +- src/Plot2d/Plot2d_ViewWindow.h | 3 +- src/Plot2d/resources/Plot2d_images.ts | 4 + src/Plot2d/resources/Plot2d_msg_en.ts | 79 ++- src/Plot2d/resources/Plot2d_msg_fr.ts | 97 +++- .../resources/plot2d_analitic_curve.png | Bin 0 -> 1189 bytes src/Prs/Prs.pro | 39 -- src/PyConsole/Makefile.am | 2 +- src/PyConsole/PyConsole.pro | 63 --- src/PyInterp/PyInterp.pro | 55 -- src/QDS/QDS.h | 15 +- src/QDS/QDS.pro | 74 --- src/QDS/QDS_ComboBox.cxx | 4 +- src/QDS/QDS_Datum.cxx | 8 +- src/QDS/QDS_SpinBoxDbl.cxx | 2 +- src/QDS/resources/QDS_msg_en.ts | 4 +- src/Qtx/Qtx.pro | 118 ---- src/QxGraph/QxGraph.pro | 75 --- src/ResExporter/ResExporter.pro | 36 -- .../SALOME_PYQT_DataModelLight.cxx | 62 ++- .../SALOME_PYQT_DataModelLight.h | 9 +- .../SALOME_PYQT_DataObjectLight.cxx | 49 ++ .../SALOME_PYQT_DataObjectLight.h | 20 +- .../SALOME_PYQT_ModuleLight.cxx | 143 +++++ .../SALOME_PYQT_ModuleLight.h | 30 +- src/SALOME_PYQT/SalomePyQt/SalomePyQt.cxx | 210 ++++++++ src/SALOME_PYQT/SalomePyQt/SalomePyQt.h | 14 + src/SALOME_PYQT/SalomePyQt/SalomePyQt.sip | 19 +- src/SALOME_SWIG/Help.py | 143 ----- src/SALOME_SWIG/PyInterp.py | 104 ---- src/SALOME_SWIG/salome.py | 159 ------ src/SOCC/SOCC.pro | 52 -- src/SPlot2d/SPlot2d.pro | 64 --- src/STD/STD.pro | 60 --- src/STD/STD_Application.cxx | 6 +- src/STD/STD_TabDesktop.cxx | 10 +- src/SUIT/SUIT.pro | 106 ---- src/SUIT/SUIT_Accel.cxx | 13 +- src/SUIT/SUIT_DataBrowser.cxx | 33 ++ src/SUIT/SUIT_DataBrowser.h | 3 + src/SUIT/SUIT_ViewWindow.cxx | 2 +- src/SUITApp/SUITApp.pro | 40 -- src/SUITApp/SUITApp_init_python.hxx | 10 + src/SUPERVGraph/SUPERVGraph.pro | 68 --- src/SVTK/SVTK.pro | 116 ---- src/SVTK/SVTK_DialogBase.cxx | 85 --- src/SalomeApp/Makefile.am | 4 +- src/SalomeApp/SalomeApp.pro | 108 ---- src/SalomeApp/SalomeApp_Application.cxx | 67 +-- src/SalomeApp/SalomeApp_Application.h | 1 - src/SalomeApp/SalomeApp_DataObject.cxx | 49 +- src/SalomeApp/SalomeApp_DataObject.h | 10 +- src/SalomeApp/SalomeApp_Study.cxx | 195 ++++++- src/SalomeApp/SalomeApp_Study.h | 5 +- src/SalomeApp/pluginsdemo/Makefile.am | 38 ++ src/SalomeApp/pluginsdemo/salome_plugins.py | 133 +++++ src/SalomeApp/pluginsdemo/trihedron.py | 22 + src/SalomeApp/pluginsdemo/tube.py | 121 +++++ src/SalomeApp/pluginsdemo/tubedialog.py | 125 +++++ src/SalomeApp/salome_pluginsmanager.py | 93 +++- src/Session/SALOME_Session_Server.cxx | 4 + src/Session/SalomeApp_Engine_i.cxx | 306 +++++++++-- src/Session/SalomeApp_Engine_i.hxx | 41 +- src/Session/Session.pro | 103 ---- src/Session/Session_ServerLauncher.cxx | 11 + src/Session/Session_ServerThread.cxx | 35 +- src/TOOLSGUI/TOOLSGUI.pro | 67 --- src/TreeData/DataModel.cxx | 57 ++ src/TreeData/DataModel.hxx | 64 +++ src/TreeData/DataObject.cxx | 73 +++ src/TreeData/DataObject.hxx | 68 +++ src/TreeData/DataProcessor.cxx | 86 +++ src/TreeData/DataProcessor.hxx | 68 +++ src/TreeData/DockWidgets.cxx | 106 ++++ src/TreeData/DockWidgets.hxx | 51 ++ src/TreeData/Makefile.am | 109 ++++ src/TreeData/Test/Makefile.am | 101 ++++ src/TreeData/Test/MyDataModel.cxx | 48 ++ src/TreeData/Test/MyDataModel.hxx | 35 ++ src/TreeData/Test/data.txt | 15 + src/TreeData/Test/guitester.cxx | 202 +++++++ src/TreeData/Test/mainwindow.cxx | 169 ++++++ src/TreeData/Test/mainwindow.hxx | 35 ++ src/TreeData/Test/mainwindow.ui | 140 +++++ src/TreeData/Test/tester.cxx | 60 +++ src/TreeData/Test/testhelper.cxx | 76 +++ src/TreeData/Test/testhelper.hxx | 15 + .../TreeData.hxx} | 27 +- src/TreeData/TreeGuiManager.cxx | 153 ++++++ src/TreeData/TreeGuiManager.hxx | 64 +++ src/TreeData/TreeItem.cxx | 242 +++++++++ src/TreeData/TreeItem.hxx | 82 +++ src/TreeData/TreeModel.cxx | 210 ++++++++ src/TreeData/TreeModel.hxx | 105 ++++ src/TreeData/TreeObserver.cxx | 54 ++ .../TreeObserver.hxx} | 50 +- src/TreeData/TreeView.cxx | 144 +++++ src/TreeData/TreeView.hxx | 62 +++ src/VTKViewer/VTKViewer.pro | 97 ---- src/VTKViewer/VTKViewer_PassThroughFilter.cxx | 81 --- src/VTKViewer/VTKViewer_PassThroughFilter.h | 51 -- src/VTKViewer/VTKViewer_TransformFilter.cxx | 1 + src/src.pro | 50 -- tools/dlgfactory/Makefile.am | 7 +- 183 files changed, 8246 insertions(+), 4087 deletions(-) delete mode 100644 SUIT.pro create mode 100644 adm_local/unix/config_files/check_salome_version.m4 delete mode 100644 adm_local/unix/make_conclude.in create mode 100644 doc/salome/gui/images/SALOME_pythonplugins_dialog.png create mode 100644 doc/salome/gui/images/SALOME_pythonplugins_menu.png create mode 100644 doc/salome/gui/images/SALOME_pythonplugins_result.png create mode 100644 doc/salome/gui/input/using_pluginsmanager.doc delete mode 100644 src/CAF/CAF.pro delete mode 100644 src/CAM/CAM.pro delete mode 100644 src/DDS/DDS.pro delete mode 100644 src/Event/Event.pro delete mode 100644 src/GLViewer/GLViewer.pro create mode 100644 src/GuiHelpers/Makefile.am create mode 100644 src/GuiHelpers/QtHelper.hxx create mode 100644 src/GuiHelpers/SALOME_AppStudyEditor.cxx create mode 100644 src/GuiHelpers/SALOME_AppStudyEditor.hxx create mode 100644 src/GuiHelpers/SALOME_GuiServices.cxx create mode 100644 src/GuiHelpers/SALOME_GuiServices.hxx create mode 100644 src/GuiHelpers/StandardApp_Module.cxx create mode 100644 src/GuiHelpers/StandardApp_Module.hxx delete mode 100644 src/LightApp/LightApp.pro delete mode 100644 src/LogWindow/LogWindow.pro delete mode 100644 src/OBJECT/OBJECT.pro delete mode 100644 src/OCCViewer/OCCViewer.pro delete mode 100644 src/Plot2d/Plot2d.pro create mode 100644 src/Plot2d/Plot2d_AnaliticCurve.cxx create mode 100644 src/Plot2d/Plot2d_AnaliticCurve.h create mode 100644 src/Plot2d/Plot2d_AnaliticCurveDlg.cxx create mode 100644 src/Plot2d/Plot2d_AnaliticCurveDlg.h create mode 100644 src/Plot2d/Plot2d_AnaliticParcer.cxx rename src/{SVTK/SVTK_DialogBase.h => Plot2d/Plot2d_AnaliticParcer.h} (52%) create mode 100755 src/Plot2d/resources/plot2d_analitic_curve.png delete mode 100644 src/Prs/Prs.pro delete mode 100644 src/PyConsole/PyConsole.pro delete mode 100644 src/PyInterp/PyInterp.pro delete mode 100644 src/QDS/QDS.pro delete mode 100644 src/Qtx/Qtx.pro delete mode 100644 src/QxGraph/QxGraph.pro delete mode 100644 src/ResExporter/ResExporter.pro delete mode 100755 src/SALOME_SWIG/Help.py delete mode 100755 src/SALOME_SWIG/PyInterp.py delete mode 100755 src/SALOME_SWIG/salome.py delete mode 100644 src/SOCC/SOCC.pro delete mode 100644 src/SPlot2d/SPlot2d.pro delete mode 100644 src/STD/STD.pro delete mode 100644 src/SUIT/SUIT.pro delete mode 100644 src/SUITApp/SUITApp.pro delete mode 100644 src/SUPERVGraph/SUPERVGraph.pro delete mode 100644 src/SVTK/SVTK.pro delete mode 100644 src/SVTK/SVTK_DialogBase.cxx delete mode 100644 src/SalomeApp/SalomeApp.pro create mode 100644 src/SalomeApp/pluginsdemo/Makefile.am create mode 100755 src/SalomeApp/pluginsdemo/salome_plugins.py create mode 100644 src/SalomeApp/pluginsdemo/trihedron.py create mode 100644 src/SalomeApp/pluginsdemo/tube.py create mode 100644 src/SalomeApp/pluginsdemo/tubedialog.py delete mode 100644 src/Session/Session.pro delete mode 100644 src/TOOLSGUI/TOOLSGUI.pro create mode 100644 src/TreeData/DataModel.cxx create mode 100644 src/TreeData/DataModel.hxx create mode 100644 src/TreeData/DataObject.cxx create mode 100644 src/TreeData/DataObject.hxx create mode 100644 src/TreeData/DataProcessor.cxx create mode 100644 src/TreeData/DataProcessor.hxx create mode 100644 src/TreeData/DockWidgets.cxx create mode 100644 src/TreeData/DockWidgets.hxx create mode 100644 src/TreeData/Makefile.am create mode 100644 src/TreeData/Test/Makefile.am create mode 100644 src/TreeData/Test/MyDataModel.cxx create mode 100644 src/TreeData/Test/MyDataModel.hxx create mode 100755 src/TreeData/Test/data.txt create mode 100644 src/TreeData/Test/guitester.cxx create mode 100644 src/TreeData/Test/mainwindow.cxx create mode 100644 src/TreeData/Test/mainwindow.hxx create mode 100644 src/TreeData/Test/mainwindow.ui create mode 100644 src/TreeData/Test/tester.cxx create mode 100644 src/TreeData/Test/testhelper.cxx create mode 100644 src/TreeData/Test/testhelper.hxx rename src/{SVTK/SVTK_Extension.h => TreeData/TreeData.hxx} (76%) create mode 100644 src/TreeData/TreeGuiManager.cxx create mode 100644 src/TreeData/TreeGuiManager.hxx create mode 100644 src/TreeData/TreeItem.cxx create mode 100644 src/TreeData/TreeItem.hxx create mode 100644 src/TreeData/TreeModel.cxx create mode 100644 src/TreeData/TreeModel.hxx create mode 100644 src/TreeData/TreeObserver.cxx rename src/{SVTK/SVTK_Extension.cxx => TreeData/TreeObserver.hxx} (58%) mode change 100755 => 100644 create mode 100644 src/TreeData/TreeView.cxx create mode 100644 src/TreeData/TreeView.hxx delete mode 100644 src/VTKViewer/VTKViewer.pro delete mode 100755 src/VTKViewer/VTKViewer_PassThroughFilter.cxx delete mode 100755 src/VTKViewer/VTKViewer_PassThroughFilter.h delete mode 100644 src/src.pro diff --git a/Makefile.am b/Makefile.am index f0ecde575..37609afc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,6 +44,8 @@ DISTCLEANFILES = a.out aclocal.m4 configure local-install.sh hack_libtool adm_lo salomeinclude_DATA = GUI_version.h EXTRA_DIST += \ + build_cmake \ + build_cmake.bat \ build_configure \ clean_configure \ LICENCE diff --git a/SUIT.pro b/SUIT.pro deleted file mode 100644 index 132066039..000000000 --- a/SUIT.pro +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -TEMPLATE = subdirs -CONFIG += ordered - -SUBDIRS = src diff --git a/adm_local/cmake_files/FindVTK.cmake b/adm_local/cmake_files/FindVTK.cmake index 9ab48bdea..718734281 100644 --- a/adm_local/cmake_files/FindVTK.cmake +++ b/adm_local/cmake_files/FindVTK.cmake @@ -25,29 +25,35 @@ FOREACH(dir ${VTK_INCLUDE_DIRS}) ENDFOREACH(dir ${VTK_INCLUDE_DIRS}) SET(VTK_LIBS) -FIND_LIBRARY(VTK_COMMON vtkCommon ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_COMMON vtkCommon PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_COMMON}) -FIND_LIBRARY(VTK_GRAPHICS vtkGraphics ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_GRAPHICS vtkGraphics PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_GRAPHICS}) -FIND_LIBRARY(VTK_IMAGING vtkImaging ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_IMAGING vtkImaging PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_IMAGING}) -FIND_LIBRARY(VTK_FILTERING vtkFiltering ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_FILTERING vtkFiltering PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_FILTERING}) -FIND_LIBRARY(VTK_IO vtkIO ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_IO vtkIO PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_IO}) -FIND_LIBRARY(VTK_RENDERING vtkRendering ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_RENDERING vtkRendering PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_RENDERING}) -FIND_LIBRARY(VTK_HYBRID vtkHybrid ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_HYBRID vtkHybrid PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_HYBRID}) -FIND_LIBRARY(VTK_PARALLEL vtkParallel ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_PARALLEL vtkParallel PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_PARALLEL}) -FIND_LIBRARY(VTK_WIDGETS vtkWidgets ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(VTK_WIDGETS vtkWidgets PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) SET(VTK_LIBS ${VTK_LIBS} ${VTK_WIDGETS}) +FIND_LIBRARY(VTK_GENERIC_FILTERING vtkGenericFiltering PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +SET(VTK_LIBS ${VTK_LIBS} ${VTK_GENERIC_FILTERING}) +FIND_LIBRARY(VTK_INFOVIS vtkInfovis PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +SET(VTK_LIBS ${VTK_LIBS} ${VTK_INFOVIS}) +FIND_LIBRARY(VTK_VOLUME_RENDERING vtkVolumeRendering PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +SET(VTK_LIBS ${VTK_LIBS} ${VTK_VOLUME_RENDERING}) -FIND_LIBRARY(vtkCommonPythonD vtkCommonPythonD ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) -FIND_LIBRARY(vtkGraphicsPythonD vtkGraphicsPythonD ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) -FIND_LIBRARY(vtkImagingPythonD vtkImagingPythonD ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) -FIND_LIBRARY(vtkPythonCore vtkPythonCore ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(vtkCommonPythonD vtkCommonPythonD PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(vtkGraphicsPythonD vtkGraphicsPythonD PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(vtkImagingPythonD vtkImagingPythonD PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) +FIND_LIBRARY(vtkPythonCore vtkPythonCore PATHS ${VTK_LIBRARY_DIRS} NO_DEFAULT_PATH) #IF(NOT WINDOWS) diff --git a/adm_local/unix/config_files/Makefile.am b/adm_local/unix/config_files/Makefile.am index 18a58c12d..137a1002f 100644 --- a/adm_local/unix/config_files/Makefile.am +++ b/adm_local/unix/config_files/Makefile.am @@ -39,4 +39,5 @@ check_sip.m4 \ check_SupervGraphViewer.m4 \ check_VTKViewer.m4 \ check_QxGraphViewer.m4 \ -check_TestRecorder.m4 +check_TestRecorder.m4 \ +check_salome_version.m4 diff --git a/adm_local/unix/config_files/check_pyqt.m4 b/adm_local/unix/config_files/check_pyqt.m4 index da3f70974..d441bf46c 100644 --- a/adm_local/unix/config_files/check_pyqt.m4 +++ b/adm_local/unix/config_files/check_pyqt.m4 @@ -47,28 +47,34 @@ AC_ARG_WITH(pyuic4, AC_MSG_RESULT([Try $withval as pyuic4 executable]) ]) +AC_ARG_WITH(pyrcc4, + [ --with-pyrcc4=EXEC pyrcc4 executable ], + [PYRCC="$withval" + AC_MSG_RESULT([Try $withval as pyrcc4 executable]) + ]) + AC_CHECKING(for pyqt) pyqt_ok=no +TEST_BIN_DIRS="" +if test "x${PYQTDIR}" != "x" ; then + TEST_BIN_DIRS="${TEST_BIN_DIRS} ${PYQTDIR} ${PYQTDIR}/bin" +fi +TEST_BIN_DIRS="${TEST_BIN_DIRS} __CHECK__PATH__" +if test "x${SIPDIR}" != "x" ; then + TEST_BIN_DIRS="${TEST_BIN_DIRS} ${SIPDIR} ${SIPDIR}/bin" +fi +if test "x${PYTHONHOME}" != "x" ; then + TEST_BIN_DIRS="${TEST_BIN_DIRS} ${PYTHONHOME}/bin" +fi +TEST_BIN_DIRS="${TEST_BIN_DIRS} /usr/bin" + dnl check pyuic4 if test "x$PYUIC" != "x" ; then dnl try $withval value AC_CHECK_FILE($PYUIC,pyqt_ok=yes,pyqt_ok=no) else - TEST_BIN_DIRS="" - if test "x${PYQTDIR}" != "x" ; then - TEST_BIN_DIRS="${TEST_BIN_DIRS} ${PYQTDIR} ${PYQTDIR}/bin" - fi - TEST_BIN_DIRS="${TEST_BIN_DIRS} __CHECK__PATH__" - if test "x${SIPDIR}" != "x" ; then - TEST_BIN_DIRS="${TEST_BIN_DIRS} ${SIPDIR} ${SIPDIR}/bin" - fi - if test "x${PYTHONHOME}" != "x" ; then - TEST_BIN_DIRS="${TEST_BIN_DIRS} ${PYTHONHOME}/bin" - fi - TEST_BIN_DIRS="${TEST_BIN_DIRS} /usr/bin" - dnl search pyuic4 pyqt_ok=no for d in ${TEST_BIN_DIRS} ; do @@ -121,6 +127,37 @@ else AC_MSG_RESULT(Warning! pyuic4 is not found!) fi +dnl check pyrcc4 +if test "x$PYRCC" != "x" ; then + dnl try $withval value + AC_CHECK_FILE($PYRCC,pyqt_ok=yes,pyqt_ok=no) +else + dnl search pyrcc4 + pyqt_ok=no + for d in ${TEST_BIN_DIRS} ; do + if test "x${d}" = "x__CHECK__PATH__" ; then + AC_PATH_PROG(TEMP, pyrcc4) + if test "x${TEMP}" != "x" ; then + PYRCC=${TEMP} + if test "x$PYQTDIR" = "x" ; then + PYQTDIR=`dirname ${PYRCC}` + PYQTDIR=`dirname ${PYQTDIR}` + fi + pyqt_ok=yes + break + fi + else + if test -d $d ; then + AC_CHECK_FILE(${d}/pyrcc4,pyqt_ok=yes,pyqt_ok=no) + if test "x$pyqt_ok" == "xyes" ; then + PYRCC=${d}/pyrcc4 + break + fi + fi + fi + done +fi + if test "x$pyqt_ok" == "xyes" ; then TESTLIBFILE=QtCore.so TESTSIPFILE=QtCore/QtCoremod.sip @@ -211,6 +248,7 @@ AC_SUBST(PYQT_INCLUDES) AC_SUBST(PYQT_LIBS) AC_SUBST(PYQT_SIPS) AC_SUBST(PYUIC) +AC_SUBST(PYRCC) AC_SUBST(PYQT_SIPFLAGS) dnl AC_LANG_RESTORE diff --git a/adm_local/unix/config_files/check_qt.m4 b/adm_local/unix/config_files/check_qt.m4 index c7413d700..54636cd49 100644 --- a/adm_local/unix/config_files/check_qt.m4 +++ b/adm_local/unix/config_files/check_qt.m4 @@ -449,7 +449,7 @@ then QT_INCLUDES="$QT_BASE $QTCORE_CPPFLAGS $QTGUI_CPPFLAGS $QTOPENGL_CPPFLAGS $QTXML_CPPFLAGS $QTWEBKIT_CPPFLAGS" - QT_ASSISANT_INCLUDES="$QTASSISTANT_CPPFLAGS $QTNETWORK_CPPFLAGS" + QT_ASSISTANT_INCLUDES="$QTASSISTANT_CPPFLAGS $QTNETWORK_CPPFLAGS" QT_MT_INCLUDES="${QT_INCLUDES}" fi fi diff --git a/adm_local/unix/config_files/check_salome_version.m4 b/adm_local/unix/config_files/check_salome_version.m4 new file mode 100644 index 000000000..03e025af1 --- /dev/null +++ b/adm_local/unix/config_files/check_salome_version.m4 @@ -0,0 +1,48 @@ +dnl Copyright (C) 2007-2010 CEA/DEN, EDF R&D, OPEN CASCADE +dnl +dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl +dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +dnl + +# Check SALOME version +# + +AC_DEFUN([CHECK_SALOME_VERSION],[ +AC_REQUIRE([AC_LINKER_OPTIONS])dnl + +AC_CHECKING(for salome version) + +salome_version_ok=no +SALOME_VERSION="" + +salomeVersionFile=${GUI_ROOT_DIR}/include/salome/GUI_version.h +SALOME_VERSION=$(grep GUI_VERSION_STR ${salomeVersionFile} | cut -d'"' -f2 | cut -d"." -f1) + +if test "x${SALOME_VERSION}" = "x" ; then + AC_MSG_WARN("Cannot determine the SALOME version. GUI module is required. Specify the variable GUI_ROOT_DIR") +else + salome_version_ok=yes + AC_MSG_RESULT(Using salome version ${SALOME_VERSION}) +fi + +AC_SUBST(SALOME_VERSION) +AC_MSG_RESULT(for salome version: $salome_version_ok) + +])dnl + diff --git a/adm_local/unix/make_common_starter.am b/adm_local/unix/make_common_starter.am index 74420fffe..93c75f327 100644 --- a/adm_local/unix/make_common_starter.am +++ b/adm_local/unix/make_common_starter.am @@ -48,6 +48,9 @@ salomeidldir = $(prefix)/idl/salome # Directory for installing resource files salomeresdir = $(prefix)/share/salome/resources/@MODULE_NAME@ +# Directory for installing plugins files +salomepluginsdir = $(prefix)/share/salome/plugins/@MODULE_NAME@ + # Directories for installing admin files admlocaldir = $(prefix)/adm_local admlocalunixdir = $(admlocaldir)/unix diff --git a/adm_local/unix/make_conclude.in b/adm_local/unix/make_conclude.in deleted file mode 100644 index 8e71da4d5..000000000 --- a/adm_local/unix/make_conclude.in +++ /dev/null @@ -1,442 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -#======================================================================= -# This section of this makefile comes from the file -# 'adm/unix/make_conclude' which was generated with config.status -# from file adm/unix/make_conclude.in -#======================================================================= -# -* Makefile *- -# Authors : Patrick GOLDBRONN (CEA) - Marc Tajchman (CEA) -# Date : 6/07/2001 -# $Header$ -# -ifneq ($(GUI_DISABLE_CORBA),yes) -# ORB Specifics rules - -@CORBA@ - -# transform idl reference in appropriate obj file -LIB_CLIENT_SRC = $(LIB_CLIENT_IDL:%.idl=%$(IDL_CLN_CXX)) -LIB_SERVER_SRC = $(LIB_SERVER_IDL:%.idl=%$(IDL_SRV_CXX)) -else -LIB_CLIENT_SRC = -LIB_SERVER_SRC = -LIB_SWIG_SRC = -endif - -LIB_SWIG_SRC = $(SWIG_DEF:%.i=%_wrap.cxx) -LIB_MOC_SRC = $(LIB_MOC:%.h=%_moc.cxx) -LIB_SRC+=$(LIB_MOC_SRC) -LIB_DEP= $(LIB_SRC) $(LIB_CLIENT_SRC) $(LIB_SERVER_SRC) $(LIB_SWIG_SRC) - -LIB_SWIG_OBJ = $(LIB_SWIG_SRC:%.cxx=%.lo) - -ifneq ($(GUI_DISABLE_CORBA),yes) -LIB_CLIENT_OBJ = $(LIB_CLIENT_IDL:%.idl=%$(IDL_CLN_OBJ)) -LIB_SERVER_OBJ = $(LIB_SERVER_IDL:%.idl=%$(IDL_SRV_OBJ)) -else -LIB_CLIENT_OBJ = -LIB_SERVER_OBJ = -endif - -# transform c file in appropriate libtool obj file (.c, .cc and .cxx) -LIB_OBJ_C = $(patsubst %.c, %.lo, $(filter %.c, $(LIB_SRC))) -LIB_OBJ_CC = $(patsubst %.cc, %.lo, $(filter %.cc, $(LIB_SRC))) -LIB_OBJ_CXX = $(patsubst %.cxx, %.lo, $(filter %.cxx, $(LIB_SRC))) -LIB_OBJ_F = $(patsubst %.f, %.lo, $(filter %.f, $(LIB_SRC))) - -# all libtool obj file in library -LIB_OBJ = $(LIB_OBJ_CXX) $(LIB_OBJ_CC) $(LIB_OBJ_C) $(LIB_CLIENT_OBJ:%.o=%.lo) $(LIB_SERVER_OBJ:%.o=%.lo) $(LIB_SWIG_OBJ) $(LIB_OBJ_F) - -# LIB_BUILD = $(LIB:%.la=$(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/%.la) -LIB_BUILD = $(patsubst %.la, $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/%.la, $(filter %.la, $(LIB))) -LIB_BUILD_A = $(patsubst %.a, $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/%.a, $(filter %.a, $(LIB))) -LIB_BUILD_SO = $(patsubst %.so, $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/%.so, $(filter %.so, $(LIB))) - -ifneq ($(findstring cmodule.la,$(filter %.la, $(LIB))),) -LIB_SWIG = $(patsubst %cmodule.la,%.so, $(filter %.la, $(LIB))) -else -LIB_SWIG = -endif - -lib: $(LIB_BUILD) $(LIB_BUILD_SO) $(LIB_CLIENT_PY) -# we don't build static library ! - -$(LIB_BUILD): $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/%.la: %.la - -$(RM) $@ - -$(RM) $(patsubst %.la, %.so, $@) - -$(RM) $(patsubst %.la, %.a, $@) - ln -sf $(CURDIR)/$< $@ || true - ln -sf $(patsubst %.la, %.so, $(CURDIR)/.libs/$<) \ - $(patsubst %.la, %.so, $@) || true - ln -sf $(patsubst %.la, %.so, $(CURDIR)/.libs/$<).0 \ - $(patsubst %.la, %.so, $@).0 || true - - if ! test -z $(LIB_SWIG) ; then \ - ln -sf $(patsubst %.la,%.so, $(CURDIR)/.libs/$<) $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/_$(LIB_SWIG) || true;\ - fi; - -$(LIB_BUILD_A): $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/%.a: %.a - -$(RM) $@ - ln -sf $(CURDIR)/$< $@ || true - -$(LIB_BUILD_SO): $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/%.so: %.so - -$(RM) $@ - ln -sf $(CURDIR)/$< $@ || true - -$(LIB): $(LIB_OBJ) - @$(LT) --mode=link $(CXX) -shared -rpath $(libdir) -o $@ $(CXXFLAGS) $(LIB_OBJ) $(LDFLAGS) $(LIBS) - -# transform idl reference in appropriate obj file -ifneq ($(GUI_DISABLE_CORBA),yes) -BIN_CLIENT_SRC = $(BIN_CLIENT_IDL:%.idl=%$(IDL_CLN_CXX)) -BIN_SERVER_SRC = $(BIN_SERVER_IDL:%.idl=%$(IDL_SRV_CXX)) -else -BIN_CLIENT_SRC = -BIN_SERVER_SRC = -endif -BIN_MOC_SRC = $(BIN_MOC:%.h=%_moc.cxx) -BIN_SRC+=$(BIN_MOC_SRC) -BIN_DEP=$(BIN:%=%.cxx) $(BIN_SRC) $(BIN_CLIENT_SRC) $(BIN_SERVER_SRC) - -ifneq ($(GUI_DISABLE_CORBA),yes) -BIN_CLIENT_OBJ = $(BIN_CLIENT_IDL:%.idl=%$(IDL_CLN_OBJ)) -BIN_SERVER_OBJ = $(BIN_SERVER_IDL:%.idl=%$(IDL_SRV_OBJ)) -else -BIN_CLIENT_OBJ = -BIN_SERVER_OBJ = -endif -# transform c file in appropriate libtool obj file (.c) -BIN_OBJ_C = $(patsubst %.c, %.o, $(filter %.c, $(BIN_SRC))) -# transform c++ file in appropriate libtool obj file (.cc and .cxx) -BIN_OBJ_CC = $(patsubst %.cc, %.o, $(filter %.cc, $(BIN_SRC))) -BIN_OBJ_CXX = $(patsubst %.cxx, %.o, $(filter %.cxx, $(BIN_SRC))) -# all obj file in bin target -BIN_OBJ = $(BIN_OBJ_CC) $(BIN_OBJ_CXX) $(BIN_OBJ_C) $(BIN_CLIENT_OBJ) $(BIN_SERVER_OBJ) - -bin: $(BIN:%=$(top_builddir)/bin/salome/%) $(BIN) $(LIB) pyscripts sharedpyscripts - -BIN_LIB=$(LIB:lib%.la=-l%) - -$(BIN:%=$(top_builddir)/bin/salome/%) $(TEST_PROGS:%=$(top_builddir)/bin/salome/%): $(top_builddir)/bin/salome/%: % - -$(RM) $@ - ln -sf $(CURDIR)/$< $@ - -$(BIN) $(TEST_PROGS): %: %.lo $(BIN_OBJ) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(BIN_LIB) $(LDFLAGSFORBIN) $(LIBSFORBIN) - -# copy python scripts in $(top_builddir)/bin/salome -# - -UI_FILES = $(notdir $(wildcard $(srcdir)/*.ui)) -UI_PY_FILES_PY = $(patsubst %.ui, %.py, $(UI_FILES)) -UI_PY_FILES = $(filter-out $(EXPORT_PYSCRIPTS) ,$(UI_PY_FILES_PY)) - -DEST_PYSCRIPTS = $(EXPORT_PYSCRIPTS:%=$(top_builddir)/bin/salome/%) -DEST_UI_PY_FILES = $(UI_PY_FILES:%=$(top_builddir)/bin/salome/%) -pyscripts: $(DEST_PYSCRIPTS) $(UI_PY_FILES) $(DEST_UI_PY_FILES) -$(DEST_PYSCRIPTS): $(top_builddir)/bin/salome/%: % - cp -f $< $@ - -# generate generic python scripts from *.ui files -# -$(UI_PY_FILES): %.py: %.ui - $(PYUIC) $< -o $@ - -# copy ui-generated python scripts in $(top_builddir)/bin -# -$(DEST_UI_PY_FILES): $(top_builddir)/bin/salome/%: % - cp -f $< $@ - -# copy pyqt files in $(PYTHON_SHARED_SITE) -# -PYTHON_SHARED_SITE=$(top_builddir)/lib@LIB_LOCATION_SUFFIX@/python$(PYTHON_VERSION)/site-packages/salome/shared_modules - -$(PYTHON_SHARED_SITE): - $(INSTALL) -d $@ - -DEST_SHAREDPYSCRIPTS = $(EXPORT_SHAREDPYSCRIPTS:%=$(PYTHON_SHARED_SITE)/%) -sharedpyscripts: $(PYTHON_SHARED_SITE) $(DEST_SHAREDPYSCRIPTS) -$(DEST_SHAREDPYSCRIPTS): $(PYTHON_SHARED_SITE)/%: % - cp -f $< $@ - -check: test - -tests: unittest - -test: $(LIB) $(TEST_PROGS:%=$(top_builddir)/bin/salome/%) - -unittest: - @if test "x$(UNIT_TEST_PROG)" != "x"; then \ - $(top_builddir)/bin/salome/$(UNIT_TEST_PROG); \ - fi; - -# copy header file in $(inc_builddir) -# -DEST_HEADERS = $(EXPORT_HEADERS:%=$(inc_builddir)/%) -inc: $(DEST_HEADERS) -$(DEST_HEADERS): $(inc_builddir)/%: % - cp -f $< $@ - -# build resources file (icons and messages) : .qm file from .po file -RESOURCES_FILES_ALL := $(notdir $(wildcard $(srcdir)/resources/*)) -RESOURCES_FILES_ALL := $(filter-out CVS, $(RESOURCES_FILES_ALL)) -RESOURCES_FILES_ALL := $(filter-out %.po, $(RESOURCES_FILES_ALL)) -RESOURCES_FILES_ALL := $(filter-out %.in, $(RESOURCES_FILES_ALL)) -RESOURCES_FILES ?= $(RESOURCES_FILES_ALL) - -resources: resources-po resources-cp - -resources-po: $(PO_FILES:%.po=$(top_builddir)/share/salome/resources/$(MODULE_NAME)/%.qm) - -resources-cp: $(RESOURCES_FILES:%=$(top_builddir)/share/salome/resources/$(MODULE_NAME)/%) - -$(RESOURCES_FILES:%=$(top_builddir)/share/salome/resources/$(MODULE_NAME)/%): $(top_builddir)/share/salome/resources/$(MODULE_NAME)/% : % - cp -fr $< $@; - -# Make installation directories if they don't exist. -$(libdir) $(includedir) $(bindir) $(datadir) $(idldir) $(sharedpydir): - $(INSTALL) -d $@ && chmod 755 $@ - -# Install the library, the public header files, and programs. -install: $(LIB) $(BIN) $(TEST_PROGS) $(libdir) $(includedir) $(bindir) $(datadir) $(idldir) install-python install-sharedpyqt install-qm install-res - @for f in X $(LIB); do \ - if test $$f != X; then \ - ($(LT_INSTALL_LIB) $$f $(libdir)/. || exit 1); \ - fi; \ - done - @if ! test -z $(LIB_SWIG) ; then \ - (cd $(libdir); ln -sf $(patsubst %.so, %cmodule.so, $(LIB_SWIG)) _$(LIB_SWIG) || true); \ - fi; - @for f in X $(BIN); do \ - if test $$f != X; then \ - ($(LT_INSTALL_PROG) $$f $(bindir)/. || exit 1); \ - fi; \ - done -# Install tests programmes in bindir - @for f in X $(TEST_PROGS); do \ - if test $$f != X; then \ - ($(LT_INSTALL_PROG) $$f $(bindir)/. || exit 1); \ - fi; \ - done -# Install exported includes in includedir - @for f in X $(EXPORT_HEADERS:%=$(srcdir)/%); do \ - if test $$f != X; then \ - (cp -p -f $$f $(includedir) || exit 1); \ - fi; \ - done - -# Install python script in $(bindir) -install-python: $(bindir) $(EXPORT_PYSCRIPTS:%=install-%) $(UI_PY_FILES:%=install-%) - -$(UI_PY_FILES:%=install-%): install-%: % - $(INSTALL_PROGRAM) $< $(bindir)/. - -$(EXPORT_PYSCRIPTS:%=install-%): install-%: % - $(INSTALL_PROGRAM) $< $(bindir)/. - -#install-python: $(bindir) $(EXPORT_PYSCRIPTS) -# @for f in X $(EXPORT_PYSCRIPTS); do \ -# if test $$f != X; then \ -# ($(INSTALL_PROGRAM) $$f $(bindir)/. || exit 1); \ -# fi; \ -# done - -# Install pyqt script in $(install-sharedpyqt) -install-sharedpyqt: $(sharedpydir) $(EXPORT_SHAREDPYSCRIPTS:%=install-%) - -$(EXPORT_SHAREDPYSCRIPTS:%=install-%): install-%: % - $(INSTALL_PROGRAM) $< $(sharedpydir)/. - - -# generic rule to install .qm files : -install-qm: resources - $(INSTALL) -d $(datadir)/resources/$(MODULE_NAME) - @for f in X $(PO_FILES:%.po=$(top_builddir)/share/salome/resources/$(MODULE_NAME)/%.qm); do \ - if test $$f != X; then \ - ($(INSTALL_DATA) $$f $(datadir)/resources/$(MODULE_NAME)/. || exit 1); \ - fi; \ - done - -# generic rule to install resources files (png, ini ...): -install-res: resources - $(INSTALL) -d $(datadir)/resources/$(MODULE_NAME) - @for f in X $(RESOURCES_FILES:%=$(top_builddir)/share/salome/resources/$(MODULE_NAME)/%); do \ - if test $$f != X; then \ - ($(INSTALL_DATA) $$f $(datadir)/resources/$(MODULE_NAME)/. || exit 1); \ - fi; \ - done - -# Removes those things that `make install' (would have) installed. -uninstall: - @if test "X$(LIB)" != X; then \ - for f in $(LIB); do \ - $(LT_UNINSTALL) $(libdir)/$$f; \ - done; \ - fi - @if test "X$(BIN)" != X; then \ - for f in $(BIN); do \ - $(LT_UNINSTALL) $(bindir)/$$f; \ - done; \ - fi - @for f in X $(TEST_PROGS); do \ - if test $$f != X; then \ - $(LT_UNINSTALL) $(bindir)/$$f; \ - fi; \ - done -# Uninstall exported includes in includedir - @for f in X $(EXPORT_HEADERS); do \ - if test $$f != X; then \ - $(LT_UNINSTALL) $(includedir)/$$f; \ - fi; \ - done -# Uninstall python script in $(bindir) - @for f in X $(EXPORT_PYSCRIPTS); do \ - if test $$f != X; then \ - $(LT_UNINSTALL) $(bindir)/$$f ; \ - fi; \ - done - -# Uninstall python script in $(bindir) - @for f in X $(UI_PY_FILES); do \ - if test $$f != X; then \ - $(LT_UNINSTALL) $(bindir)/$$f ; \ - fi; \ - done - -# Uninstall pyqt script in $(sharedpydir) - @for f in X $(EXPORT_SHAREDPYSCRIPTS); do \ - if test $$f != X; then \ - $(LT_UNINSTALL) $(sharedpydir)/$$f ; \ - fi; \ - done - -# Uninstall qm files - @for f in X $(PO_FILES:%.po=%.qm); do \ - if test $$f != X; then \ - $(LT_UNINSTALL) $(datadir)/resources/$(MODULE_NAME)/$$f ; \ - fi; \ - done - -# remove all dependencies files -# -cleandep: - -$(RM) .dep* - -# Removes temporary files without removing the final target files. That is, -# remove things like object files but not libraries or executables. -# -mostlyclean: cleandep - -$(RM) $(LIB_OBJ) $(LIB_OBJ:.lo=.o) - -$(RM) $(BIN_OBJ) $(BIN:%=%.o) - -$(RM) $(TEST_OBJ) $(TEST_OBJ:.lo=.o) - -$(RM) $(PROG_OBJ) $(PROG_OBJ:.lo=.o) $(MOSTLYCLEAN) - -# Like `mostlyclean' except it also removes the final targets: things like -# libraries and executables. This target doesn't remove any file that -# is part of the SALOME distribution. -# -clean: mostlyclean - -$(RM) $(LIB) $(TEST_PROGS) $(BIN) $(CLEAN) - -$(RM) TAGS *~ *# core *.core - -$(RM) -r .libs - -$(RM) $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/$(LIB) - -$(RM) $(patsubst %,$(top_builddir)/bin/salome/%, $(BIN)) - -$(RM) $(patsubst %.la, %.so, $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/$(LIB)) - -$(RM) $(patsubst %.la, %.a, $(top_builddir)/lib@LIB_LOCATION_SUFFIX@/salome/$(LIB)) -# remove idl generated files (sources) - -$(RM) $(LIB_CLIENT_SRC) $(LIB_SERVER_SRC) $(BIN_CLIENT_SRC) $(BIN_SERVER_SRC) -# remove idl generated files (headers) - -$(RM) $(LIB_CLIENT_IDL:%.idl=%$(IDL_CLN_H)) $(LIB_SERVER_IDL:%.idl=%$(IDL_SRV_H)) - -$(RM) $(BIN_CLIENT_IDL:%.idl=%$(IDL_CLN_H)) $(BIN_SERVER_IDL:%.idl=%$(IDL_SRV_H)) - -$(RM) $(LIB_MOC_SRC) $(BIN_MOC_SRC) - -$(RM) $(LIB_SWIG_SRC) - -# Like `clean' except it also removes files that were created by running -# configure. If you've unpacked the source and built without creating -# any other files, then `make distclean' will leave only the files that were -# in the distribution. -# -distclean: clean - #remove qm file ! - -$(RM) $(PO_FILES:%.po=%.qm) $(PO_FILES:%.po=$(top_builddir)/share/salome/resources/$(MODULE_NAME)/%.qm) - #remove include files - -$(RM) $(DEST_HEADERS) - -$(RM) $(DISTCLEAN) *.bak *.old *.new .dep* - @if test -f $(srcdir)/Makefile.in; then \ - (@SETX@; $(RM) Makefile); \ - fi - - -#implicits rules -.cc.o: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< - -.cc.lo: - $(LT) --mode=compile $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< - -.cxx.o: - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< - -.cxx.lo: - $(LT) --mode=compile $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< - -.c.o: - $(CC) $(CFLAGS) $(CPPFLAGS) -c $< - -.c.lo: - $(LT_COMPILE) $(CFLAGS) $(CPPFLAGS) -c $< - -.f.o: - $(FC) $(FFLAGS) -c $< -o $@ - -.f.lo: - $(LT) --mode=compile $(FC) $(FFLAGS) -c $< - -.ui.h: - $(UIC) -o $@ $< - -.ui.cxx: - $(UIC) -o $@ -i $*.h $< - -#pattern rules -%_moc.cxx : %.h - $(MOC) $< -o $@ - -%_wrap.cxx : %.i - $(SWIG) $(SWIG_FLAGS) -o $@ $< - -$(top_builddir)/share/salome/resources/$(MODULE_NAME)/%.qm: %.po - $(MSG2QM) $< $@ ; \ - -#------------------------------------------------------------------------------ -# The following section of this makefile contains dependencies between the -# source files and the header files. If GNU make and GCC are being used then -# the dependencies are in the form of rules that cause the information to -# be kept updated automatically. Otherwise the dependencies are listed -# explicitly and come from the `.distdep' files in the various directories. -# These files are part of the distribution and are generated automatically on -# GNU/GCC systems. -#------------------------------------------------------------------------------ - -@DEPEND@ diff --git a/bin/runLightSalome.sh b/bin/runLightSalome.sh index 6695b8483..8af8227cb 100755 --- a/bin/runLightSalome.sh +++ b/bin/runLightSalome.sh @@ -229,7 +229,8 @@ run_light_salome(){ # start application ### - SUITApp LightApp "$*" & + MODULES=`echo $MODULES | tr " " ","` + SUITApp LightApp --modules=${MODULES} "$*" & } ### diff --git a/clean_configure b/clean_configure index 5aef98f25..550456d90 100755 --- a/clean_configure +++ b/clean_configure @@ -27,12 +27,5 @@ find . -name "*.pyc" -print -exec rm {} \; #exit # ==================== ON SORT AVANT -find bin -name Makefile.in | xargs rm -f -find doc -name Makefile.in | xargs rm -f -find idl -name Makefile.in | xargs rm -f -find resources -name Makefile.in | xargs rm -f -find adm_local -name Makefile.in | xargs rm -f -find src -name Makefile.in | xargs rm -f -rm -f Makefile.in -cd adm_local/unix/config_files -rm -f config.* depcomp install-sh l*.m4 ltmain.sh missing py-compile +find . -name Makefile.in | xargs rm -f +( cd adm_local/unix/config_files && rm -f config.* depcomp install-sh l*.m4 ltmain.sh missing py-compile ) diff --git a/configure.ac b/configure.ac index 203158ba5..25c3f5c92 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ # Modified by : Marc Tajchman (CEA) # Modified by : Mikhail PONIKAROV (OCN) - autotools usage # -AC_INIT([Salome2 Project GUI module], [6.3.1], [webmaster.salome@opencascade.com], [SalomeGUI]) +AC_INIT([Salome2 Project GUI module], [6.4.0], [webmaster.salome@opencascade.com], [SalomeGUI]) AC_CONFIG_AUX_DIR(adm_local/unix/config_files) AC_CANONICAL_HOST AC_CANONICAL_TARGET @@ -660,6 +660,10 @@ AC_OUTPUT([ \ src/Session/Makefile \ src/SalomeApp/Makefile \ src/SalomeApp/Test/Makefile \ + src/SalomeApp/pluginsdemo/Makefile \ + src/GuiHelpers/Makefile \ + src/TreeData/Makefile \ + src/TreeData/Test/Makefile \ src/SALOME_SWIG/Makefile \ src/SALOME_SWIG/supervisionexample.py \ src/SALOME_SWIG/supervisiongeomexample.py \ diff --git a/doc/salome/gui/images/SALOME_pythonplugins_dialog.png b/doc/salome/gui/images/SALOME_pythonplugins_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..9b8c4126527f8edc5319ace185ec83cecce5a107 GIT binary patch literal 43868 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%0Di$BlO_3qKd3j! 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

>l5OBMwOOkO|s zMkNsIMBMzb(aZG6qOub(%w^DBeUbV@(g_lU;d5ShAUzd1%3B&jUtJJKQ$iAPIPuB1 zYhUePorArh$ZVKPJd2BJiq7fv&UO+Dw9cA^3-E_nIuA<`LF-0gYP;k&`jRT~LTobs z*ukILH7~c`&7I~+mR$q1D(dsfc*SuaKf6 zfB^OH)UmG<5Q}o;?<$4gZ2uBGpKM##zJ!pHlkX`z)-U|dlJc|-UL)+fQ+Vrrae{-? zxTVA!i2ZfA`us{2W({@zP78{i^cA$h&F+Zbm!l}5jOe4sFD38`1{2V4Ru=`eX2Xx3 z$fw59Masoz(f{BbQ0>=IbMF7)*3nkeJ0zsmd^z#8aL`=}&6X<@3(*{RhBZooqEJau z7jxo|>1IYT86~w;FNS-r=Oh|49o<);IN<$Yh0sr5eYN7R1jo*_*AA;pOXjTpCqQiQ zEpVfSVQ2gkHMO{xCp-Z6*AbI1qj3Wilw+X;F_bSOMuRHR2^_me&vW0QJ~|Zd7c=a@ z)Y3j^U$uK?#Rwe0q^&s0!+D|iQfDElmRV5JDc3@4JmZQMsHK_AN05z3p{SX}NAvM~ zdC*n9MQ=fc>rECL4mHnAf^g!kVD3*hI~*YJ-#KABkJ)~~zWe+Z3J5;t`LZ1GjcaxX z9cZ-Ob1d>XykofzVQd=8T>bi(gU|CS5v^lAZ`&L9P5U&mlukx7s^GQC+_ps%te~|} z2o8-$EgbwhQENKb^ylMLXn5x;MY_o3A#lyQu}}Z$o5(cGPTJ9xwg69$5*IZZxqv&n z9ViS7m&tOm{Xxg2$X>BInM6`#ErLBr=AC^O>}UZZEC$}%uuzwH!ZYe`^Jm4o%Is#; z_Zue(;847yw|XX##yvJ_VJme2w&JGh244Pt-_&GhMz)7@%0AHH$&TdpG}$9DiI=US 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

{Zt)O4C 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 Otherwise you can experience some troubles with the installation. + Note: you are strongly recommended to avoid using directory names containing spaces. Otherwise you can experience some troubles with the installation. \n \anchor figure_4 \image html directorypage.png @@ -281,7 +281,9 @@ script. The script tries to clear all temporary files. The process of removing temporary files can take some time, so the installation wizard will wait 3 seconds before closing. -At the end of the installation "Installation completed" message will appear in the status bar. You can return to the previous pages to start a new installation or click \em"Next" button to go to the readme page: +At the end of the installation "Installation completed" message +will appear in the status bar. You can return to the previous pages to +start a new installation or click \em "Next" button to go to the readme page: \anchor figure_12 \image html readmepage.png @@ -594,7 +596,7 @@ script.

\n\n This function is called when 'Install binaries' installation mode is selected by the user. The function is responsible for the extracting of the product from the binaries package. It should create environment for the product in the temporary directory (see also the description of \b print_env() function). It is not necessary to implement this function if you do not provide binaries mode installation for the current product.

  • install_source() -This function is called to install SALOME modules sources, if 'Install binaries' installation mode is selected by the user, and to install prerequisite products sources, in 'Install sources' case (\ref Figure_2 "Fig. 2" above). The function is responsible for the building of the product from the sources package. It should create the environment file for the product in the temporary directory (see also the description of print_env() function). It is not necessary to implement this function if you do not provide a sources mode installation for the current product.

    +This function is called to install SALOME modules sources, if 'Install binaries' installation mode is selected by the user, and to install prerequisite products sources, in 'Install sources' case (\ref figure_2 "Fig. 2" above). The function is responsible for the building of the product from the sources package. It should create the environment file for the product in the temporary directory (see also the description of print_env() function). It is not necessary to implement this function if you do not provide a sources mode installation for the current product.

  • install_source_and_build() This function is called when 'Install sources and make a compilation' installation mode is selected. This function should be used to unpack SALOME or prerequisite sources package and then call build/install procedure for it. @@ -705,4 +707,4 @@ The calling signature of the script is the following: Wizard if the attribute \b \ in the XML configuration file is set to the "true" value. -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/input/mesh_preferences.doc b/doc/salome/gui/input/mesh_preferences.doc index c7a8cde85..5939e442d 100644 --- a/doc/salome/gui/input/mesh_preferences.doc +++ b/doc/salome/gui/input/mesh_preferences.doc @@ -65,11 +65,11 @@ information is shown:
  • Simple - as a plain text
  • Tree - in a tree-like form
  • -
  • Automatic nodes compute limit
  • - Size limit for the +
  • Automatic nodes compute limit
  • - allows to define the size limit for the mesh groups for which the number of underlying nodes is calculated -automatically. If the group size exceeds the value set in preferences, +automatically. If the group size exceeds the value set in the preferences, the user will have to press \em Compute button explicitly. Zero value -means "no limit". Default value is set to 100 000 mesh elements. +means "no limit". By default the value is set to 100 000 mesh elements.
  • Automatic Parameters
    • @@ -105,14 +105,14 @@ dialog box.
  • Elements
    • -
    • Surface color - allows to select the color of surface of elements +
    • Surface color - allows to select the surface color of elements (seen in Shading mode). Click on the colored line to access to the \ref select_color_and_font_page "Select Color" dialog box.
    • -
    • Back surface color - allows to select the color of interior surface -of elements. Use slider to select this color. This color generated on base -of the Surface color by changing it's brightness and saturation.
    • -
    • Outline color - allows to select the color of borders of -elements. Click on the colored line to access to the +
    • Back surface color - allows to select the interior surface color +of elements. Use the slider to select the color generated basing on +the Surface color by changing its brightness and saturation.
    • +
    • Outline color - allows to select the color of element +borders. Click on the colored line to access to the \ref select_color_and_font_page "Select Color" dialog box.
    • Wireframe color - allows to select the color of borders of elements in the wireframe mode. Click on the colored line to access to the @@ -184,7 +184,7 @@ side)
      • Multicolor the histogram is colored as Scalar Bar
      • Monocolor the histogram is colored as selected with Distribution color selector
      • -
          +
      */ diff --git a/doc/salome/gui/input/occ_3d_viewer.doc b/doc/salome/gui/input/occ_3d_viewer.doc index cf3b1a288..8eb2b4b15 100644 --- a/doc/salome/gui/input/occ_3d_viewer.doc +++ b/doc/salome/gui/input/occ_3d_viewer.doc @@ -178,17 +178,17 @@ the current view area to the minimized / maximized state.
      \image html occ_view_sync.png -
      Synchronize view - synchronize 3d view parameters.
      +
      Synchronize view - allows to synchronize 3d view parameters.
      This button has two states - checked and unchecked. Clicking on this button opens a drop-down menu listing the compatible 3d views. As soon -as user selects any view from the list, the parameters (view point, -position, zoom coefficient etc) of the current view are synchronized -to the selected view (static synchronization). +as the user selects any view from the list, the parameters (view point, +position, zoom coefficient, etc.) of the current view are synchronized +with the selected view (static synchronization). In addition, when this button is in the "checked" state, the dynamic synchronization of the views is performed, i.e. any zoom, pan, rotate or other view operation done in one view is automatically applied -to the second view. +to the other view.
      \note OCC Viewer features a special Polyline Selection diff --git a/doc/salome/gui/input/plot2d_viewer.doc b/doc/salome/gui/input/plot2d_viewer.doc index ab1a49cd8..c2ff8c43f 100644 --- a/doc/salome/gui/input/plot2d_viewer.doc +++ b/doc/salome/gui/input/plot2d_viewer.doc @@ -97,11 +97,12 @@ The options are as follows: - Main title the title of the XY plot. By default, it will consist of the names of the tables, on the basis of which the curve lines have been constructed. -- Show legend here you can define the position of the - Curve type you can select from \b Points, \b Lines and \b Spline. +- Show legend here you can define the position of the description table on the XY plot (to the \b Left, to the \b Right, on \b Top or on \b Bottom). -- Legend font here you can set type, face and color for the font of Legend item. +- Legend font here you can set type, face and color for the +font of Legend item. - Marker size - size of the points (markers) forming curve lines. - Background color of the XY plot. - Scale mode here you can select the type of scaling (\b diff --git a/doc/salome/gui/input/postpro_preferences.doc b/doc/salome/gui/input/postpro_preferences.doc index f8b0b1fca..f9710bed2 100644 --- a/doc/salome/gui/input/postpro_preferences.doc +++ b/doc/salome/gui/input/postpro_preferences.doc @@ -152,7 +152,6 @@ configuration. Cache
    -
    • Animation preferences
      • Speed - allows to define the speed of the animation.
      • @@ -339,24 +338,24 @@ select_color_and_font_page "Select Color" dialog box.
        • Gauss Points Scalar Bar
          • -
          • Active Bar - this option allows to choose Local or -Global Bar as active.
          • -
          • Display Global Bar - this option allows to visualize or to -hide the Global Bar.
          • -
          • Scalar Bar Mode - this option allows to choose between -Bicolor and Rainbow Scalar Bar Mode.
          • -
          • Spacing - allows to define Spacing from 0.01 to 1.
          • +
          • Active Bar - this option allows to choose Local or + Global Bar as active.
          • +
          • Display Global Bar - this option allows to visualize or to + hide the Global Bar.
          • +
          • Scalar Bar Mode - this option allows to choose between + Bicolor and Rainbow Scalar Bar Mode.
          • +
          • Spacing - allows to define Spacing from 0.01 to 1.
          -Spacemouse +
        • Spacemouse
          • Decrease Gauss Points Magnification - divides the current magnification by magnification ratio.
          • Increase Gauss Points Magnification - multiplies the current magnification by magnification ratio.
          • - +


        Inside and Outside Cursor Preferences allow to set @@ -376,24 +375,23 @@ the point it becomes selected (ranges from 0.001 to 10).
      • Information window - allows to define the Transparency (from 0% = opaque to 100% = transparent) and Position of the window, which can be:
      • -
          -
        • Centred below the point, or
        • -
        • located at Top-left corner of the 3D view
        • -
        +
        • +
        • Centred below the point, or
        • +
        • located at Top-left corner of the 3D view
        • +
      • Movement of the Camera can also be define by the user.
      • -
          -
        • Zoom at first selected point - This value is used to define -the focal distance at the first selected point (at the end of the -movement of the camera). This value is a ratio that will be multiplied -by the current zoom value.
        • -
        • Number of steps between two positions - defines the -smoothness of camera movement at selection by the number of -iterations. If set to 1 the camera is zoomed and centered at the point -momentarily. Greater numbers mean very slow camera movement.
        • -
        +
        • +
        • Zoom at first selected point - This value is used to define + the focal distance at the first selected point (at the end of the + movement of the camera). This value is a ratio that will be multiplied + by the current zoom value.
        • +
        • Number of steps between two positions - defines the + smoothness of camera movement at selection by the number of + iterations. If set to 1 the camera is zoomed and centered at the point + momentarily. Greater numbers mean very slow camera movement.
        • +
      • Display parent mesh element - allows to visualize or hide the patent mesh element of the selected gauss point.
      -
    */ diff --git a/doc/salome/gui/input/salome_desktop.doc b/doc/salome/gui/input/salome_desktop.doc index 5890eae43..f6361c620 100644 --- a/doc/salome/gui/input/salome_desktop.doc +++ b/doc/salome/gui/input/salome_desktop.doc @@ -103,6 +103,7 @@ direction. them.
  • \b Rename - allows to rename the currently selected viewer window.
  • + diff --git a/doc/salome/gui/input/themes.doc b/doc/salome/gui/input/themes.doc index 4204270b3..d5f459f73 100755 --- a/doc/salome/gui/input/themes.doc +++ b/doc/salome/gui/input/themes.doc @@ -2,33 +2,23 @@ \page themes_page Theme -The look-n-feel of the SALOME desktop can be customized by the user -via the SALOME theme preferences. +The look and feel of the SALOME desktop can be customized by the user +via SALOME style preferences. SALOME style prefereces dialog box is used for customization of the look and feel of SALOME style. To open it, select in the Main menu View -> Theme item. -If SALOME theme is not used (Use SALOME Style check bos is -switched off), the global system settings are applied (for -example, those ones specified by the KDE settings). -In such a way, the application's look-n-feel attributes (such as -palette and/or font) can be customized by using of the Qt utility -qtconfig. - -Alternatively look-n-feel of the application desktop can be customized -using SALOME theme. - \image html theme1.png It is possible to choose a predefined scheme in the list to the left -or create custom scheme using the controls to the right. +or to create a custom scheme using the controls to the right. The dialog box contains two pages. -The first page allows specifying of the color palette. +The first page allows specifying the color palette. - \b Quick button allows automatic color palette definition basing on -one main color specified by the user. +the main color specified by the user. \image html theme2.png @@ -42,4 +32,11 @@ boxes and define their parameters. - Widget effect allows to apply special effects to Salome dialog boxes. +If SALOME Theme is not used (Use SALOME Style checkbox is +switched off), the global system settings are applied (for +example, KDE settings). So, the look and feel of the application (for +example, palette and/or font) can be customized by using the Qt utility +qtconfig. + + */ diff --git a/doc/salome/gui/input/using_input_widgets.doc b/doc/salome/gui/input/using_input_widgets.doc index fcb04f032..2b0859c5e 100755 --- a/doc/salome/gui/input/using_input_widgets.doc +++ b/doc/salome/gui/input/using_input_widgets.doc @@ -40,7 +40,7 @@ tooltip near the spin box just as he types in it. The tooltip contains information about the valid data range. For a floating-point input, the tooltip also contains information about the expected precision. -In standard \bSALOME modules the precision value can be adjusted through +In standard \b SALOME modules the precision value can be adjusted through \ref setting_preferences_page "user preferences" of the correspodning modules, and the tooltip contains a reference to the corresponding parameter in the preferences. diff --git a/doc/salome/gui/input/using_object_browser.doc b/doc/salome/gui/input/using_object_browser.doc index e7b7e6b4b..5421cf6b6 100644 --- a/doc/salome/gui/input/using_object_browser.doc +++ b/doc/salome/gui/input/using_object_browser.doc @@ -45,12 +45,14 @@ to the objects

    Enable sorting

    -The objects in the object browser are sorted by creation order by default. +By default the objects in the object browser are sorted in the creation order. \n -It is yet possible to enable sorting -by alphabetical order (or reverse order) by right clicking in the header of the tree (near the "Name" field) and checking "enable sorting" in the popup menu. +However, it is possible to enable sorting +in the alphabetical order (or the reverse alphabetical order) by right +clicking in the header of the tree (near the "Name" field) and +checking "enable sorting" in the popup menu. \n -An arrow appears then near "Name" to reverse the order. -If unchecking "enable sorting" the objects are sorted with default order again. - +It is possible to use the arrow near "Name" to reverse the order. +If "enable sorting" is unchecked, the objects are sorted in the default order again. + */ diff --git a/doc/salome/gui/input/using_pluginsmanager.doc b/doc/salome/gui/input/using_pluginsmanager.doc new file mode 100644 index 000000000..4ac23796c --- /dev/null +++ b/doc/salome/gui/input/using_pluginsmanager.doc @@ -0,0 +1,144 @@ +/*! + +\page using_pluginsmanager Extends SALOME gui functions using python plugins + +-# \ref S1_SALOMEPLUGINS +-# \ref S2_SALOMEPLUGINS +-# \ref S3_SALOMEPLUGINS + +\section S1_SALOMEPLUGINS Objectives + +The SALOME python plugin manager allows the end user to extend the +graphic interface of SALOME with custom functions written as python +modules. The screenshots below show the example of a tool that creates +a mesh from a set of geometrical parameters with the support of +simple graphical interface: + +The default menu for plugins is "Tool->Extensions": +\image html SALOME_pythonplugins_menu.png + +In this example, the plugin provides a small interface to input the +parameters (not provided by the plugin manager): +\image html SALOME_pythonplugins_dialog.png + +Then creates the mesh model: +\image html SALOME_pythonplugins_result.png + +In this example, the end user has to write: + +-# the python script that creates the mesh from the parameters, using + the GEOM and SMESH python interface, as in a clasic use case. +-# the dialog box in PyQt to input the parameters +-# file salome_plugins.py that declares the plugins + +This page explains only the last point. + +\section S2_SALOMEPLUGINS Principles + +The general form of the file salome_plugins.py is: + +\code +import salome_pluginsmanager + +# Creation of the plugin +def myplugin1(context): + ... + # Here is the code of the plugin myplugin1 + ... + +def myplugin2(context): + ... + # Here is the code of the plugin myplugin2 + ... + +# Declaration of the plugins to the pluginsmanager +salome_pluginsmanager.AddFunction('My plugin n°1', 'This action runs the plugin n°1', myplugin1) +salome_pluginsmanager.AddFunction('My plugin n°2', 'This action runs the plugin n°2', myplugin2) +... +\endcode + +The procedure is to define a function that implements the plugin, and +to declare this function to the plugins manager. The implementation +can be very variable. It is advisable to consider this function as a +proxy to your code that you can manage in a python package +installed in the standard SALOME python directories. + +In this code, the variable "context" is automatically transmitted by +the pluginmanager when you request the plugin with at least the following +attributes: + +\code +activeStudy = context.study +salomegui = context.sg +\endcode + +Once terminated, this script salome_plugin.py has to be moved to a +specific place on your filesystem where SALOME is programmed to search +for plugins. The possible directories are: + +-# The directory <*_ROOT_DIR>/share/salome/plugins, when this plugin + is developped in the framework of a SALOME module (<*_ROOT_DIR> + is the root installation directory of the module). +-# The directory ~/.config/salome/Plugins for personnal end user + plugins +-# Any path in the shell variable SALOME_PLUGIN_PATH (each path must be + separated by a comma ":" for unix and ";" for windows). This + variable should be set and exported before running the SALOME application. + +\section S3_SALOMEPLUGINS A complete example + +Suppose that you write a SALOME script that creates a trihedron for each of +your studies (a simple and standard SALOME script, that every end user +is capable to write if he reads the documentation and follows the +training course): + +\code +# Intialize the geompy factory with the active study +import salome +import geompy +geompy.init_geom(salome.myStudy) + +# Create the objects +Vx = geompy.MakeVectorDXDYDZ(10, 0, 0) +Vy = geompy.MakeVectorDXDYDZ(0, 10, 0) +Vz = geompy.MakeVectorDXDYDZ(0, 0, 10) +origin = geompy.MakeVertex(0, 0, 0) + +# Register the objects in the active study +geompy.addToStudy( Vx, "Vx" )) +geompy.addToStudy( Vy, "Vy" )) +geompy.addToStudy( Vz, "Vz" )) +geompy.addToStudy( origin, "origin" )) +\endcode + +The job consists in creating the file salome_plugins.py as follows: + +\code +import salome_pluginsmanager +def trihedron(context): + import geompy + # Intialize the geompy factory with the active study + activeStudy = context.study + geompy.init_geom(activeStudy) + # Create the objects + Vx = geompy.MakeVectorDXDYDZ(10, 0, 0) + Vy = geompy.MakeVectorDXDYDZ(0, 10, 0) + Vz = geompy.MakeVectorDXDYDZ(0, 0, 10) + origin = geompy.MakeVertex(0, 0, 0) + # Register the objects in the active study + geompy.addToStudy( Vx, "Vx" ) + geompy.addToStudy( Vy, "Vy" ) + geompy.addToStudy( Vz, "Vz" ) + geompy.addToStudy( origin, "origin" ) + +# Register the function in the plugin manager +salome_pluginsmanager.AddFunction('O,Vx,Vy,Vz', + 'Creates the trihedron', + trihedron) + +\endcode + +Move this script in the directory +~/.config/salome/Plugins, run SALOME and enjoy your new function. + +*/ diff --git a/doc/salome/gui/input/vtk_3d_viewer.doc b/doc/salome/gui/input/vtk_3d_viewer.doc index 325a32314..eb5ab3d09 100644 --- a/doc/salome/gui/input/vtk_3d_viewer.doc +++ b/doc/salome/gui/input/vtk_3d_viewer.doc @@ -33,11 +33,11 @@ position) zooming styles. The second mode is available only for parallel \image html vtk_view_highlight.png
    Dynamic pre-selection switch - allows to switch -on/off dynamic pre-selection of the objects. When the dynamic -pre-selection is switched on, the objects can be selected by clicking -of the point/cell belonging to this object, with some performance -loss. Switch dynamic pre-selection off to have good performance on the -big objects (like huge meshes).
    +on/off dynamic pre-selection of objects. When the dynamic +pre-selection is switched on, an objects can be selected by clicking +the point/cell belonging to this object, with some performance +loss. Switch dynamic pre-selection off to have a good performance on +big objects (for example, huge meshes).
    \image html vtk_view_triedre.png @@ -196,17 +196,17 @@ of the view:

    \image html vtk_view_sync.png -
    Synchronize view - synchronize 3d view parameters.
    +
    Synchronize view - allows to synchronize 3d view parameters.
    This button has two states - checked and unchecked. Clicking on this button opens a drop-down menu listing the compatible 3d views. As soon -as user selects any view from the list, the parameters (view point, -position, zoom coefficient etc) of the current view are synchronized -to the selected view (static synchronization). +as the user selects any view from the list, the parameters (view point, +position, zoom coefficient, etc.) of the current view are synchronized +with the selected view (static synchronization). In addition, when this button is in the "checked" state, the dynamic synchronization of the views is performed, i.e. any zoom, pan, rotate or other view operation done in one view is automatically applied -to the second view. +to the other view.
    \image html vtk_view_parallel.png diff --git a/src/CAF/CAF.pro b/src/CAF/CAF.pro deleted file mode 100644 index ba145c19a..000000000 --- a/src/CAF/CAF.pro +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = caf -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -INCLUDEPATH += ../../include ../Qtx ../SUIT ../STD -LIBS += -L../../lib -lqtx -lsuit -lstd - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += CAF_EXPORTS - -HEADERS = CAF.h -HEADERS += CAF_Application.h -HEADERS += CAF_Operation.h -HEADERS += CAF_Study.h -HEADERS += CAF_Tools.h - -SOURCES = CAF_Application.cxx -SOURCES += CAF_Operation.cxx -SOURCES += CAF_Study.cxx -SOURCES += CAF_Tools.cxx - -TRANSLATIONS = resources/CAF_images.ts \ - resources/CAF_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm resources/*.xml resources/*.ini -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/CAM/CAM.pro b/src/CAM/CAM.pro deleted file mode 100644 index 76eb52173..000000000 --- a/src/CAM/CAM.pro +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = CAM -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -INCLUDEPATH += ../../include ../Qtx ../SUIT ../STD -LIBS += -L../../lib -lqtx -lsuit -lstd - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += CAM_EXPORTS - -HEADERS = CAM.h -HEADERS += CAM_Application.h -HEADERS += CAM_DataModel.h -HEADERS += CAM_DataObject.h -HEADERS += CAM_Module.h -HEADERS += CAM_Study.h - -SOURCES = CAM_Application.cxx -SOURCES += CAM_DataModel.cxx -SOURCES += CAM_DataObject.cxx -SOURCES += CAM_Module.cxx -SOURCES += CAM_Study.cxx - -TRANSLATIONS = resources/CAM_images.ts \ - resources/CAM_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm resources/*.xml resources/*.ini -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/CAM/CAM_Application.cxx b/src/CAM/CAM_Application.cxx index 7993d9f94..8aa677e3c 100755 --- a/src/CAM/CAM_Application.cxx +++ b/src/CAM/CAM_Application.cxx @@ -45,6 +45,8 @@ #include +namespace +{ class BusyLocker { public: @@ -54,6 +56,7 @@ private: bool myPrev; bool& myBusy; }; +} /*! \brief Create new instance of CAM_Application. @@ -361,10 +364,10 @@ CAM_Module* CAM_Application::loadModule( const QString& modName, const bool show if(version) { for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) { if ( (*it).title == modName ) { - if( (*it).version.isEmpty() ) { - (*it).version = QString(version); - } - break; + if( (*it).version.isEmpty() ) { + (*it).version = QString(version); + } + break; } } } diff --git a/src/DDS/DDS.pro b/src/DDS/DDS.pro deleted file mode 100644 index 28fc670e0..000000000 --- a/src/DDS/DDS.pro +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = DDS -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -CAS_OCAF = -L$${CASROOT}/Linux/lib -lPTKernel -lTKernel -lTKCDF -lTKLCAF -lTKPCAF -lTKStdSchema - -INCLUDEPATH += ../../include $${CAS_CPPFLAGS} -LIBS += $${CAS_KERNEL} $${CAS_OCAF} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=2 OCC_VERSION_MAINTENANCE=0 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = DDS.h -HEADERS += DDS_DicGroup.h -HEADERS += DDS_DicItem.h -HEADERS += DDS_Dictionary.h -HEADERS += DDS_KeyWords.h - -SOURCES = DDS_DicGroup.cxx -SOURCES += DDS_DicItem.cxx -SOURCES += DDS_Dictionary.cxx -SOURCES += DDS_KeyWords.cxx - -includes.files = $$HEADERS -includes.path = ../../include - -INSTALLS += includes diff --git a/src/Event/Event.pro b/src/Event/Event.pro deleted file mode 100644 index 51dfd8442..000000000 --- a/src/Event/Event.pro +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = Event -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -INCLUDEPATH += ../../include -LIBS += - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += EVENT_EXPORTS - -HEADERS = Event.h -HEADERS += SALOME_Event.h -HEADERS += SALOME_EventFilter.h - -SOURCES = SALOME_Event.cxx -SOURCES += SALOME_EventFilter.cxx - -includes.files = $$HEADERS -includes.path = ../../include - -INSTALLS += includes diff --git a/src/GLViewer/GLViewer.pro b/src/GLViewer/GLViewer.pro deleted file mode 100644 index 39e55db47..000000000 --- a/src/GLViewer/GLViewer.pro +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = GLViewer -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -GL_LIBS = -L/usr/X11R6/lib$$(LIB_LOCATION_SUFFIX) -lGLU - -INCLUDEPATH += $$(QTDIR)/include/QtOpenGL ../../include $${CAS_CPPFLAGS} ../Qtx ../SUIT -LIBS += -L../../lib -lqtx -lsuit $${GL_LIBS} $${CAS_KERNEL} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += GLVIEWER_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = GLViewer.h -HEADERS += GLViewer_AspectLine.h -HEADERS += GLViewer_BaseDrawers.h -HEADERS += GLViewer_BaseObjects.h -HEADERS += GLViewer_Compass.h -HEADERS += GLViewer_Context.h -HEADERS += GLViewer_CoordSystem.h -HEADERS += GLViewer_Defs.h -HEADERS += GLViewer_Drawer.h -HEADERS += GLViewer_Geom.h -HEADERS += GLViewer_Grid.h -HEADERS += GLViewer_Group.h -HEADERS += GLViewer_MimeData.h -HEADERS += GLViewer_Object.h -HEADERS += GLViewer_Selector.h -HEADERS += GLViewer_Selector2d.h -HEADERS += GLViewer_Text.h -HEADERS += GLViewer_Tools.h -HEADERS += GLViewer_ToolTip.h -HEADERS += GLViewer_ViewFrame.h -HEADERS += GLViewer_ViewManager.h -HEADERS += GLViewer_ViewPort.h -HEADERS += GLViewer_ViewPort2d.h -HEADERS += GLViewer_Viewer.h -HEADERS += GLViewer_Viewer2d.h -HEADERS += GLViewer_Widget.h - -SOURCES = GLViewer_AspectLine.cxx -SOURCES += GLViewer_BaseDrawers.cxx -SOURCES += GLViewer_BaseObjects.cxx -SOURCES += GLViewer_Compass.cxx -SOURCES += GLViewer_Context.cxx -SOURCES += GLViewer_CoordSystem.cxx -SOURCES += GLViewer_Drawer.cxx -SOURCES += GLViewer_Geom.cxx -SOURCES += GLViewer_Grid.cxx -SOURCES += GLViewer_Group.cxx -SOURCES += GLViewer_MimeData.cxx -SOURCES += GLViewer_Object.cxx -SOURCES += GLViewer_Selector.cxx -SOURCES += GLViewer_Selector2d.cxx -SOURCES += GLViewer_Text.cxx -SOURCES += GLViewer_Tools.cxx -SOURCES += GLViewer_ToolTip.cxx -SOURCES += GLViewer_ViewFrame.cxx -SOURCES += GLViewer_ViewManager.cxx -SOURCES += GLViewer_ViewPort.cxx -SOURCES += GLViewer_ViewPort2d.cxx -SOURCES += GLViewer_Viewer.cxx -SOURCES += GLViewer_Viewer2d.cxx -SOURCES += GLViewer_Widget.cxx - -TRANSLATIONS = resources/GLViewer_images.ts \ - resources/GLViewer_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/GUI_PY/Makefile.am b/src/GUI_PY/Makefile.am index 7cbe52f47..84acb4b2b 100644 --- a/src/GUI_PY/Makefile.am +++ b/src/GUI_PY/Makefile.am @@ -27,11 +27,8 @@ mypkgpythondir = $(salomepythondir)/salome/gui # Python modules to be installed mypkgpython_PYTHON = \ __init__.py \ - SelectVarsDialog_ui.py \ selectvars.py \ - genericdialog_ui.py \ genericdialog.py \ - mytestdialog_ui.py \ mytestdialog.py \ helper.py diff --git a/src/GuiHelpers/Makefile.am b/src/GuiHelpers/Makefile.am new file mode 100644 index 000000000..ea10df9db --- /dev/null +++ b/src/GuiHelpers/Makefile.am @@ -0,0 +1,84 @@ +# Copyright (C) 2011 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. +# +# 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 +# +# -* Makefile *- +# +# Author : Guillaume Boulant (EDF/R&D) +# + +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +# header files +salomeinclude_HEADERS = \ + QtHelper.hxx \ + SALOME_GuiServices.hxx \ + SALOME_AppStudyEditor.hxx \ + StandardApp_Module.hxx + +# Libraries targets +lib_LTLIBRARIES = libSalomeGuiHelpers.la + +dist_libSalomeGuiHelpers_la_SOURCES = \ + SALOME_GuiServices.cxx \ + SALOME_AppStudyEditor.cxx \ + StandardApp_Module.cxx + + +QT_CXXFLAGS=@QT_INCLUDES@ @QT_MT_INCLUDES@ +CAS_CXXFLAGS=@CAS_CPPFLAGS@ @CAS_CXXFLAGS@ +BOOST_CPPFLAGS=@BOOST_CPPFLAGS@ +BOOST_LIBS=@BOOST_LIBS@ +CORBA_CXXFLAGS=@OMNIORB_CXXFLAGS@ @OMNIORB_INCLUDES@ +CORBA_LIBS=@OMNIORB_LIBS@ + +libSalomeGuiHelpers_la_CPPFLAGS = \ + $(QT_CXXFLAGS) \ + $(CAS_CXXFLAGS) \ + $(BOOST_CPPFLAGS) \ + $(CORBA_CXXFLAGS) \ + $(KERNEL_CXXFLAGS) \ + -I$(srcdir)/../SalomeApp \ + -I$(srcdir)/../LightApp \ + -I$(srcdir)/../SUIT \ + -I$(srcdir)/../Qtx \ + -I$(srcdir)/../CAM \ + -I$(srcdir)/../STD \ + -I$(srcdir)/../OBJECT + + +libSalomeGuiHelpers_la_LDFLAGS = \ + ../SalomeApp/libSalomeApp.la \ + $(KERNEL_LDFLAGS) -lSalomeKernelHelpers \ + $(CORBA_LIBS) + +# +# ======================================================= +# Specific definitions for Qt MOC files +# + +# moc-files generation +%_moc.cxx: %.hxx + $(MOC) $< -o $@ + +# MOC pre-processing +MOC_FILES_HXX = \ + StandardApp_Module_moc.cxx + +nodist_libSalomeGuiHelpers_la_SOURCES = $(MOC_FILES_HXX) + +EXTRA_DIST+=$(MOC_FILES_HXX:%_moc.cxx=%.hxx) diff --git a/src/GuiHelpers/QtHelper.hxx b/src/GuiHelpers/QtHelper.hxx new file mode 100644 index 000000000..748c38b18 --- /dev/null +++ b/src/GuiHelpers/QtHelper.hxx @@ -0,0 +1,46 @@ +#ifndef _QT_HELPER_HXX_ +#define _QT_HELPER_HXX_ + +// +// ============================================================= +// Macro and template functions for type conversions. +// ============================================================= +// +#include "Basics_Utils.hxx" // To get ToString macro +#include + +// This can be used to convert QString into char *. +#define QCHARSTAR(qstring) qstring.toLatin1().data() +// This converts a string into a QString +#define S2QS(string) QString(string.c_str()) +// This converts a QString into a string +#define QS2S(qstring) ToString(QCHARSTAR(qstring)) +#define QSTRING(any) S2QS(ToString(any)) + +// +// ============================================================= +// General helper macros for manipulating widgets values +// ============================================================= +// +#define GETTEXT(widget) ToString(QCHARSTAR(widget->text().trimmed())) +#define SETTEXT(widget,str) widget->setText(str.c_str()) + +// +// ============================================================= +// Qt Logger macros +// ============================================================= +// + +// We can use the macros defined by SALOMELocalTrace/utilities.h +// as proposed in the file KERNEL_helper.hxx. But we can use instead +// the Qt debug function: +#include +#define QDEBUG qWarning() +//#define QDEBUG qDebug() +#define QLOG(data) QDEBUG << "[XSALOME] " << data +#ifdef LOG +#undef LOG +#endif +#define LOG QLOG + +#endif // __QT_HELPER_H_ diff --git a/src/GuiHelpers/SALOME_AppStudyEditor.cxx b/src/GuiHelpers/SALOME_AppStudyEditor.cxx new file mode 100644 index 000000000..0dbcc2a07 --- /dev/null +++ b/src/GuiHelpers/SALOME_AppStudyEditor.cxx @@ -0,0 +1,106 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#include "SALOME_AppStudyEditor.hxx" + +#include +#include +#include +#include + +SALOME_AppStudyEditor::SALOME_AppStudyEditor(SalomeApp_Application * salomeApp) + : SALOME_StudyEditor() +{ + _salomeApp = salomeApp; + updateActiveStudy(); +} + +/** + * This updates the editor with the current active study. If the + * active study id is identical to the study id currently associated + * to this object, then no update is performed. + */ +int SALOME_AppStudyEditor::updateActiveStudy() { + int activeStudyId = SALOME_AppStudyEditor::getActiveStudyId(_salomeApp); + if ( activeStudyId != this->getStudyId() ) { + this->setStudyById(activeStudyId); + } + return activeStudyId; +} + +// GUI context only +int SALOME_AppStudyEditor::getActiveStudyId(SalomeApp_Application * salomeApp) { + SalomeApp_Study* appStudy = dynamic_cast (salomeApp->activeStudy()); + _PTR(Study) aCStudy = appStudy->studyDS(); + int studyId = aCStudy->StudyId(); + return studyId; +} + +SALOMEDS::SObject_ptr SALOME_AppStudyEditor::IObjectToSObject(const Handle(SALOME_InteractiveObject)& iobject) { + if (!iobject.IsNull()) { + if (iobject->hasEntry()) { + SALOMEDS::SObject_var sobject = _study->FindObjectID(iobject->getEntry()); + return sobject._retn(); + } + } + return SALOMEDS::SObject::_nil(); +} + +/** + * This function creates a list of all the "study objects" (SObject) + * that map with the selection in the object browser (the "interactive + * objects", IObjects). Please note that the objects belongs to the + * active study. + */ +SALOME_StudyEditor::SObjectList * SALOME_AppStudyEditor::getSelectedObjects() { + + // _GBO_: please note that the use of this function can be + // underoptimal in the case where the number of selected objects is + // high, because we create a vector list of SObjects while the list + // of IObject can be processed directly. In the case where a high + // number of objects is selected (~1000), it's certainly better to + // use directly the SALOME_ListIO and iterators on it. + + LightApp_SelectionMgr* aSelectionMgr = _salomeApp->selectionMgr(); + SALOME_ListIO selectedIObjects; + aSelectionMgr->selectedObjects(selectedIObjects); + /* + * This returns a list containing the selected objects of the objects + * browser. Please note that the objects of the browser ARE NOT the + * objects of the study. What is returned by this function is a list + * of "interactive objects" (IObject) which are objects of the + * graphical context. Each of this IObject is characterized by an + * entry string that corresponds to the entry string of an associated + * "study object" (SObject) in the active study. The mapping can be + * done using the function IObjectToSObject. + */ + + SObjectList * listOfSObject = new SObjectList(); + SALOME_ListIteratorOfListIO It (selectedIObjects); + for (; It.More(); It.Next()) { + SALOMEDS::SObject_ptr sobject = this->IObjectToSObject(It.Value()); + if (!sobject->_is_nil()) { + listOfSObject->push_back(sobject); + } + } + + return listOfSObject; +} diff --git a/src/GuiHelpers/SALOME_AppStudyEditor.hxx b/src/GuiHelpers/SALOME_AppStudyEditor.hxx new file mode 100644 index 000000000..bc1225508 --- /dev/null +++ b/src/GuiHelpers/SALOME_AppStudyEditor.hxx @@ -0,0 +1,47 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#ifndef __APP_STUDY_EDITOR_HXX__ +#define __APP_STUDY_EDITOR_HXX__ + +#include +#include CORBA_CLIENT_HEADER(SALOMEDS) +#include +#include + +#include "SALOME_StudyEditor.hxx" + +class SALOME_AppStudyEditor: public SALOME_StudyEditor { +public: + SALOME_AppStudyEditor(SalomeApp_Application * salomeApp); + int updateActiveStudy(); + + SALOMEDS::SObject_ptr IObjectToSObject(const Handle(SALOME_InteractiveObject)& iobject); + SALOME_StudyEditor::SObjectList * getSelectedObjects(); + + static int getActiveStudyId(SalomeApp_Application * salomeApp); + +private: + SalomeApp_Application * _salomeApp; + +}; + +#endif // __APP_STUDY_EDITOR_HXX__ diff --git a/src/GuiHelpers/SALOME_GuiServices.cxx b/src/GuiHelpers/SALOME_GuiServices.cxx new file mode 100644 index 000000000..0f314631c --- /dev/null +++ b/src/GuiHelpers/SALOME_GuiServices.cxx @@ -0,0 +1,134 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#include "SALOME_GuiServices.hxx" +#include + +namespace GUI { + /*! + * Get the SALOME application, i.e. the main GUI framework of + * SALOME. This function returns a pointer to the singleton instance + * of the SalomeApp_Application. + * + * The SALOME application can be used to get reference to + * the SALOME KERNEL services such that the naming service, the + * lifeCycleCORBA, ... Theses services can be retrieved using static + * functions (not required to get the singleton instance): + * + * SALOME_NamingService *aNamingService = SalomeApp_Application::namingService(); + * + * Please note that this usage can be done only from within the + * SALOME session server process, i.e. the process that embed the + * SALOME application. + */ + SalomeApp_Application * getSalomeApplication() { + static SalomeApp_Application * app; + if ( app == NULL ) { + app = dynamic_cast< SalomeApp_Application* >( SUIT_Session::session()->activeApplication() ); + } + return app; + } + + /*! + * Get the selection manager of the SALOME application. The + * selection manager can be used to get the selected items in the + * objects browser that is a GUI display of the active study. + * The function returns a pointer to the selectionMgr attribute of + * the SalomeApp_Application singleton instance. + */ + LightApp_SelectionMgr * getSelectionManager() { + SalomeApp_Application* anApp = GUI::getSalomeApplication(); + if ( anApp == NULL ) return NULL; + return dynamic_cast( anApp->selectionMgr() ); + } + + + SUIT_ResourceMgr * getResourcesManager() { + return SUIT_Session::session()->resourceMgr(); + } + // Note that the resource manager can be also retrieved from the + // SALOME application using the resourceMgr() method: + // + + /** + * This returns the current active study id if an active study is + * defined in the SALOME session, returns -1 otherwise. Note that + * the active study doesn't make sense outside of the GUI SALOME + * process, i.e. the SALOME_SessionServer embedding the + * SalomeApp_Application. + */ + int getActiveStudyId() { + SALOME::Session_var aSession = KERNEL::getSalomeSession(); + if ( CORBA::is_nil(aSession) ) { + INFOS("ERR: can't request for active study because the session is NULL"); + return -1; + } + return aSession->GetActiveStudyId(); + } + + /** + * This returns the current active study if an active study is + * defined in the SALOME session, returns null otherwise. + */ + SALOMEDS::Study_ptr getActiveStudy() { + return KERNEL::getStudyById(getActiveStudyId()); + } + + + // __GBO__ Question: what is the difference between a + // SALOMEDS::Study and a SalomeApp_Study? + SalomeApp_Study* getSalomeAppActiveStudy() { + SUIT_Application* app = getSalomeApplication(); + if( app ) + return dynamic_cast( app->activeStudy() ); + else + return NULL; + } + + + SALOMEDS::SObject_ptr IObjectToSObject(const Handle(SALOME_InteractiveObject)& iobject) { + if (!iobject.IsNull()) { + if (iobject->hasEntry()) { + SalomeApp_Study* appStudy = getSalomeAppActiveStudy(); + if ( appStudy != NULL ) { + // + // IMPORTANT NOTE: + // + // Note that the SalomeApp_Study give acces to shared + // pointer (i.e. _PTR()) that points to proxy + // objects (i.e. SALOMEDSClien_) that embeds + // the CORBA servants (i.e. SALOMEDS::_ptr). + // Then to retrieve the corba servant, a method is to + // retrieve the SALOMEDS::Study servant first and the to + // request this servant to get the SObject given its entry. + // + _PTR(Study) studyClient = appStudy->studyDS(); + SALOMEDS::Study_var study = KERNEL::getStudyManager()->GetStudyByID(studyClient->StudyId()); + SALOMEDS::SObject_ptr sobject = study->FindObjectID(iobject->getEntry()); + return sobject; + } + } + } + return SALOMEDS::SObject::_nil(); + } + +} + diff --git a/src/GuiHelpers/SALOME_GuiServices.hxx b/src/GuiHelpers/SALOME_GuiServices.hxx new file mode 100644 index 000000000..6188ad0d8 --- /dev/null +++ b/src/GuiHelpers/SALOME_GuiServices.hxx @@ -0,0 +1,84 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#ifndef __GUI_SERVICES_H__ +#define __GUI_SERVICES_H__ + +#include +#include +#include +#include + +#include "SALOMEconfig.h" +#include CORBA_SERVER_HEADER(SALOMEDS) +#include +#include + +namespace GUI { + + // SALOME GUI main services + SalomeApp_Application * getSalomeApplication(); + LightApp_SelectionMgr * getSelectionManager(); + SUIT_ResourceMgr * getResourcesManager(); + + // Helper functions to deal with the study in a GUI context. + // + // First of all, in a GUI context, there is one of the opened + // studies that is considered as active. Please note that the active + // study is a GUI concept and it can be obtained only in the + // SALOME_SessionServer process, i.e. the process that embeds the + // SalomeApp_Application. The concept of active study doesn't make + // sense outside of the GUI context. + // + // The active study is associated with an GUI Objects Browser that + // displays a graphical representation of the study. The items that + // can be selected in the Objects Browser are name Interactive + // Objects (instance of class SALOME_InteractiveObject). To work + // with data, we have to retrieve from this Interactive Object (IO) + // the underlying Study Object (instance of class SALOMEDS::SObject) + // and from this last the data object that can be anything (that + // depends of the SALOME module technical choices). In general, on + // of the attribute of a SObject is a CORBA servant that handles the + // data to work with + SALOMEDS::Study_ptr getActiveStudy(); + int getActiveStudyId(); + + // Another way to get the active study (to be converted in + // SALOMEDS::Study): + SalomeApp_Study * getSalomeAppActiveStudy(); + + SALOMEDS::SObject_ptr IObjectToSObject(const Handle(SALOME_InteractiveObject)& iobject); + + template typename TInterface::_var_type + IObjectToInterface(const Handle(SALOME_InteractiveObject)& iobject) { + SALOMEDS::SObject_ptr sobject = IObjectToSObject(iobject); + return KERNEL::SObjectToInterface(sobject); + } + // _MEM_: note that template functions have to be declared AND + // implemented in the header because they are not compiled in this + // library but in every client library that includes this header + // file. The effective compilation depends on the particular class + // used for TInterface. + + +} + +#endif // GUI_SERVICES diff --git a/src/GuiHelpers/StandardApp_Module.cxx b/src/GuiHelpers/StandardApp_Module.cxx new file mode 100644 index 000000000..1bc997fff --- /dev/null +++ b/src/GuiHelpers/StandardApp_Module.cxx @@ -0,0 +1,338 @@ +// Copyright (C) 2011 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. +// +// 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 +// +// Author : Guillaume Boulant (EDF) + +#include "StandardApp_Module.hxx" + +#include +#include +#include +#include +#include "QtxPopupMgr.h" + +#include CORBA_CLIENT_HEADER(SALOMEDS) +#include CORBA_CLIENT_HEADER(SALOMEDS_Attributes) + +// QT Includes +#include +#include + +#include +#include "QtHelper.hxx" + +// Constructor +StandardApp_Module::StandardApp_Module() : + SalomeApp_Module( "" ), + LightApp_Module( "" ) +{ + // Note that it's no use to specify here the moduleName because it's + // automatically determined by the name of the GUI library + // (lib.so) just before initialize step. If you want + // nevertheless to setup the module name you should specified this + // name in the initialize function below using the CAM_module + // function setModuleName(QString&). You are warn then that the + // loading of resource files may be incorect and should be manually + // managed. In conclusion, if you don't need a special + // configuration, please conform to the standard convention: if + // is the name of the module as declared in the GUI + // configuration file (SalomeApp.xml), then you just have to provide + // a library whose name is lib.so and which contains a + // factory "extern C" function 'CAM_Module* createModule()' +} + +int StandardApp_Module::ACTIONID_DEFAULT_INIT_VALUE = 910; +int StandardApp_Module::ACTIONID_UNDEFINED = -1; + +// Module's initialization +void StandardApp_Module::initialize( CAM_Application* app ) +{ + // + // ---------------------------------------------------------------- + // Standard initialization + // ---------------------------------------------------------------- + // + SalomeApp_Module::initialize( app ); + _resourceMgr = app->resourceMgr(); + // Please note that the moduleName() function is inherited from + // CAM_module classe, and that its value is determined automatically + // from the name specified in the SalomeApp.xml associated to this + // module and which corresponds to the name of the GUI library + // (lib.so). For XSALOME, see + // share/salome/resources/xsalome/SalomeApp.xml in the install + // directory. + _defaultMenuId = this->createMenu( QCHARSTAR(moduleName()), -1, -1, 30 ); + _defaultToolbarId = this->createTool ( QCHARSTAR(moduleName()) ); + _actionId_internalCount = StandardApp_Module::ACTIONID_DEFAULT_INIT_VALUE; + + // + // ---------------------------------------------------------------- + // The following initializes the GUI widget and associated actions + // ---------------------------------------------------------------- + // These functions may be redefined in specialized version of this class + this->createModuleWidgets(); + this->createModuleActions(); +} + +QIcon StandardApp_Module::createIcon(const QString& iconName) { + // The icon file is supposed to be available througth the resource + // manager, i.e. in the folder containing the XSALOME resources (see + // specification in the module file SalomeApp.xml). + QPixmap aPixmap = _resourceMgr->loadPixmap( QCHARSTAR(moduleName()), iconName ); + return QIcon( aPixmap ); +} + +int StandardApp_Module::newActionId() { + _actionId_internalCount++; + return _actionId_internalCount; +} + +/*! + * This function creates an action and connects it to a button in the + * toolbar and to an item in the menu associated to this module. This + * function applies a common and standard procedure with a maximum of + * default values. If no identifier is specified, then a static + * internal counter is used to associate an identifier to the created + * action. In any case, the action identifier is returned. + * + * Note that the action (object of type QAction) can be retrieved + * using the method "QAction * action(int identifier)" of the super + * class. + */ +int StandardApp_Module::createStandardAction(const QString& label, + QObject * slotobject, + const char* slotmember, + const QString& iconName, + const QString& tooltip, + const int identifier) +{ + + // If the tooltip is not defined, we choose instead the label text. + QString effToolTip(tooltip); + if ( effToolTip.isEmpty() ) + effToolTip = label; + + QIcon actionIcon = this->createIcon(iconName); + + // If the identifier is not specified, then we use an internal + // counter. + int effIdentifier = identifier; + if ( effIdentifier == StandardApp_Module::ACTIONID_UNDEFINED ) { + effIdentifier=newActionId(); + } + + // Creating the action + QAction * action= this->createAction( effIdentifier, label, actionIcon, + label, effToolTip, 0, getApp()->desktop(), + false, slotobject, slotmember); + + return effIdentifier; +} + +/** + * Integrate the action in the default toolbar + */ +void StandardApp_Module::addActionInToolbar(int actionId) { + this->createTool( actionId, _defaultToolbarId ); +} + +/** + * Integrate the action in the default menu + */ +void StandardApp_Module::addActionInMenubar(int actionId) { + this->createMenu( actionId, _defaultMenuId, 10 ); +} + +/** + * Add the specified action as an item in the popup menu, with the + * specified visible rule. The default is "visible for object browser". + */ +void StandardApp_Module::addActionInPopupMenu(int actionId,const QString& rule) { + // _GBO_ for a fine customization of the rule (for example with a + // test on the type of the selected object), see the LIGTH module: + // implement "LightApp_Selection* createSelection() const;" + int parentId = -1; + QtxPopupMgr* mgr = this->popupMgr(); + mgr->insert ( this->action( actionId ), parentId, 0 ); + mgr->setRule( this->action( actionId ), rule, QtxPopupMgr::VisibleRule ); +} + +/*! + * This function can be used to create additionnal widget for this + * module GUI (e.g. docked widget). It can be redefined in a + * specialized version of this class. + */ +void StandardApp_Module::createModuleWidgets() { + // Nothing to do in this default gui +} + +/*! + * This function can be used to defined the list of actions for this + * module GUI. It can be redefined in a specialized version of this + * class. + * Depending on wether you use the method "createStandardAction" or + * not, the actions will be automatically plugged in the toolbar and + * the menu associated to the module. + */ +void StandardApp_Module::createModuleActions() { + int actionId = this->createStandardAction("Test", this, SLOT(OnTest()), + "f1.png", "Run the default test function"); + this->addActionInToolbar(actionId); +} + +// Get compatible dockable windows. +void StandardApp_Module::windows( QMap& theMap ) const +{ + theMap.clear(); + theMap.insert( SalomeApp_Application::WT_ObjectBrowser, Qt::LeftDockWidgetArea ); + theMap.insert( SalomeApp_Application::WT_PyConsole, Qt::BottomDockWidgetArea ); +} + +// Module's engine IOR +QString StandardApp_Module::engineIOR() const +{ + CORBA::String_var anIOR = getApp()->orb()->object_to_string(getEngine()); + return QString( anIOR.in() ); +} + +/*! + * This specifies the filename for the icon to be used for the study + * component associated to the module. Note that this icon could be + * different than the module icon which is defined by the + * SalomeApp.xml file dedicated to this module (see the shared + * resources folder dedicated to the module) and which is used for the + * toolbar of the SALOME application. + */ +QString StandardApp_Module::studyIconName() +{ + // By default, we return the module icone name + return iconName(); // inherited from CAM_Module +} + +/*! + * This can be used to switch the layout of main application + * dockwidgets to one of the predefined configuration (see enum + * DockLayoutType). When a layout is set, the previous layout is + * memorized and can be restored using the unsetDockLayout function (1 + * step undo). It is typically to be used in the functions + * activateModule to setup the layout and deactivateModule to unset + * the layout, i.e. restore to the previous state. + */ +void StandardApp_Module::setDockLayout(DockLayoutType layoutType) { + SUIT_Desktop* desk = getApp()->desktop(); + _areaAtBottomLeftCorner = desk->corner(Qt::BottomLeftCorner); + _areaAtBottomRightCorner = desk->corner(Qt::BottomRightCorner); + + if ( layoutType == DOCKLAYOUT_LEFT_VLARGE ) { + desk->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); + desk->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); + } else { + desk->setCorner(Qt::BottomLeftCorner, Qt::BottomDockWidgetArea); + desk->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); + } +} + +/*! + * This function restores the layout state that was previously in + * place before the last setDockLayout call. + */ +void StandardApp_Module::unsetDockLayout() { + SUIT_Desktop* desk = getApp()->desktop(); + desk->setCorner(Qt::BottomLeftCorner, _areaAtBottomLeftCorner); + desk->setCorner(Qt::BottomRightCorner, _areaAtBottomRightCorner); +} + +// Module's activation +bool StandardApp_Module::activateModule( SUIT_Study* theStudy ) +{ + bool bOk = SalomeApp_Module::activateModule( theStudy ); + + setMenuShown( true ); + setToolShown( true ); + + if ( this->createStudyComponentAtActivation() ) { + this->createStudyComponent(theStudy); + } + + return bOk; +} + +/*! + * This function should be implemented in a specialized class and must + * return true if you want to automatically create a study component + * for this module at activation step (when you first load the module + * for a given study). The default function return true. + */ +bool StandardApp_Module::createStudyComponentAtActivation() { + return true; +} + +/*! + * This creates a root entry in the active study for this module, i.e + * a SComponent with the name of the module and the icon specified for + * the module. This component is associated to the engine (return by + * getEngine()) if the engine is a SALOMEDS::Driver. + */ +void StandardApp_Module::createStudyComponent(SUIT_Study* theStudy) { + + SALOME_NamingService *aNamingService = SalomeApp_Application::namingService(); + CORBA::Object_var aSMObject = aNamingService->Resolve("/myStudyManager"); + SALOMEDS::StudyManager_var aStudyManager = SALOMEDS::StudyManager::_narrow(aSMObject); + SALOMEDS::Study_var aDSStudy = aStudyManager->GetStudyByID(theStudy->id()); + + SALOMEDS::SComponent_var aFather = aDSStudy->FindComponent(QCHARSTAR(moduleName())); + if (aFather->_is_nil()) + { + SALOMEDS::StudyBuilder_var aStudyBuilder = aDSStudy->NewBuilder(); + aFather = aStudyBuilder->NewComponent(QCHARSTAR(moduleName())); + SALOMEDS::GenericAttribute_var anAttr = aStudyBuilder->FindOrCreateAttribute(aFather, "AttributeName"); + SALOMEDS::AttributeName_var aName = SALOMEDS::AttributeName::_narrow(anAttr); + aName->SetValue(QCHARSTAR(moduleName())); + aName->Destroy(); + anAttr = aStudyBuilder->FindOrCreateAttribute(aFather, "AttributePixMap"); + SALOMEDS::AttributePixMap_var aPixMap = SALOMEDS::AttributePixMap::_narrow(anAttr); + aPixMap->SetPixMap(QCHARSTAR(studyIconName())); + + // WARN: The engine should be associated to the SComponent IF + // AND ONLY IF it is a SALOMEDS::Driver. Otherwise, there is no + // need to do that, and it could even lead to exception + // raising (eh, you work on SALOME isn't it?) + SALOMEDS::Driver_var driver = SALOMEDS::Driver::_narrow(this->getEngine()); + if ( ! driver->_is_nil() ) { + STDLOG("Associate the SComponent to the engine"); + aStudyBuilder->DefineComponentInstance(aFather, this->getEngine()); + } + } + +} + +// Module's deactivation +bool StandardApp_Module::deactivateModule( SUIT_Study* theStudy ) +{ + setMenuShown( false ); + setToolShown( false ); + + return SalomeApp_Module::deactivateModule( theStudy ); +} + +void StandardApp_Module::OnTest() +{ + // Just for test + STDLOG("OnTest: engine IOR = "< +#include + +#include +#include CORBA_SERVER_HEADER(SALOME_Component) + +#include + +/*! + * This class is provided as the base class for the standard gui part + * of a SALOME module. To define a gui part, you just have to + * implement the virtual functions: + * - getEngine() to specify the engine component associated to this module + * - createModuleActions(...) to specify the action functions + * - createModuleWidgets(...) to specify special additionnal widgets + * + * The gui part of a SALOME module is an extension of the SALOME + * client Application for this module. Technically speaking, it + * consists in a plugin that provides a derived class of CAM_Module + * (provided by the GUI module). + * + * A standard gui is the standard design for most of SALOME module + * (object browser on the left, viewer on the rigth, python console on + * the bottom, and toolbox in the toolbar with standard definition of + * the tool function). This standard design suggests also to specify + * the engine associated to the module by implementing the virtual + * pure function getEngine(). The engine of a module is the SALOME + * component that is associated to the study component for this + * module, and generally which is responsible for the persistency + * functions or data management for the module. + * + * See a use case example in the test module Xsalome provided by the + * test package (tst/module/gui/Xsalome.hxx and + * tst/module/gui/factory.cxx in the XSALOME library). + */ +class StandardApp_Module: public SalomeApp_Module +{ + Q_OBJECT + +public: + + // ================================================================ + // This part defines the standard interface of the SalomeApp_Module + // ================================================================ + + StandardApp_Module(); + void initialize( CAM_Application* ); + QString engineIOR() const; + virtual void windows( QMap& theMap ) const; + +public slots: + bool deactivateModule( SUIT_Study* ); + bool activateModule( SUIT_Study* ); + + // ================================================================ + // This part defines the specific interface of this class + // ================================================================ + +public: + /* Creates an action with standard default values */ + int createStandardAction(const QString& label, + QObject * slotobject, + const char* slotmember, + const QString& iconName=QString(), + const QString& tooltip=QString(), + const int identifier=ACTIONID_UNDEFINED); + + void addActionInToolbar(int actionId); + void addActionInMenubar(int actionId); + void addActionInPopupMenu(int actionId,const QString& rule="client='ObjectBrowser'"); + +protected: + /* Implement this to create additionnal widget (e.g. docked widget) */ + virtual void createModuleWidgets(); + /* Implement this to define the actions for this gui */ + virtual void createModuleActions(); + + /* Use this to create a root entry in the study for this module */ + void createStudyComponent(SUIT_Study*); + /* Implement this to say if study component entry should be created + at activation step */ + virtual bool createStudyComponentAtActivation(); + + /* The engine is the SALOME component associated to the study */ + virtual Engines::EngineComponent_ptr getEngine() const = 0; + // Note that the function getEngine() is virtual pure and must be + // implemented in the specific inherited classes. Note also that the + // const flag is required because getEngine is used by functions + // with const flags (see for ex: engineIOR()). + virtual QString studyIconName(); + + QIcon createIcon(const QString& iconName); + int newActionId(); + + enum DockLayoutType { + DOCKLAYOUT_BOTTOM_HLARGE, // Bottom is Horizontal Large + DOCKLAYOUT_LEFT_VLARGE, // Left is Vertical Large + }; + virtual void setDockLayout(DockLayoutType layoutType); + virtual void unsetDockLayout(); + + SUIT_ResourceMgr* _resourceMgr; + int _defaultMenuId; + int _defaultToolbarId; + + static int ACTIONID_DEFAULT_INIT_VALUE; + static int ACTIONID_UNDEFINED; + +private: + int _actionId_internalCount; + Qt::DockWidgetArea _areaAtBottomLeftCorner; + Qt::DockWidgetArea _areaAtBottomRightCorner; + + // ========================================================= + // This part defines slots for test purposes + // ========================================================= + +protected slots: + void OnTest(); +}; + +#endif diff --git a/src/LightApp/LightApp.pro b/src/LightApp/LightApp.pro deleted file mode 100644 index c1fa87c59..000000000 --- a/src/LightApp/LightApp.pro +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = LightApp -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -PYTHON_INCLUDES = $$(PYTHONHOME)/include/python2.4 - -QT_INCLUDES = $$(QTDIR)/include $$(QTDIR)/include/QtCore $$(QTDIR)/include/QtGui $$(QTDIR)/include/QtOpenGL $$(QTDIR)/include/QtXml - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -HDF5_INCLUDES = $$(HDF5HOME)/include - -KERNEL_CXXFLAGS = $$(KERNEL_ROOT_DIR)/include/salome - -VTK_INCLUDES = $$(VTKHOME)/include/vtk - -QWT_INCLUDES = $$(QWTHOME)/include - -PYTHON_INCLUDES = $$(PYTHONHOME)/include/python2.4 - -INCLUDEPATH += $${PYTHON_INCLUDES} $${QT_INCLUDES} $${CAS_CPPFLAGS} $${HDF5_INCLUDES} $${KERNEL_CXXFLAGS} ../SUIT ../STD ../CAM ../LogWindow ../Prs ../Qtx ../Event #../ObjBrowser - -#if ENABLE_VTKVIEWER - INCLUDEPATH += $${VTK_INCLUDES} ../VTKViewer -#else -# DEFINES += DISABLE_VTKVIEWER -#endif - -#if ENABLE_PLOT2DVIEWER - INCLUDEPATH += $${QWT_INCLUDES} ../Plot2d -#else -# DEFINES += DISABLE_PLOT2DVIEWER -#endif - -#if ENABLE_OCCVIEWER - INCLUDEPATH += ../OCCViewer -#else !ENABLE_OCCVIEWER -# DEFINES += DISABLE_OCCVIEWER -#endif - -#if ENABLE_PYCONSOLE - INCLUDEPATH += $${PYTHON_INCLUDES} ../PyConsole ../PyInterp -#else !ENABLE_PYCONSOLE -# DEFINES += DISABLE_PYCONSOLE -#endif - -#if ENABLE_GLVIEWER - INCLUDEPATH += ../GLViewer -#else !ENABLE_GLVIEWER -# DEFINES += DISABLE_GLVIEWER -#endif - -#if ENABLE_SUPERVGRAPHVIEWER - INCLUDEPATH += ../SUPERVGraph -#else !ENABLE_SUPERVGRAPHVIEWER -# DEFINES += DISABLE_SUPERVGRAPHVIEWER -#endif - -#if ENABLE_SALOMEOBJECT - - INCLUDEPATH += ../OBJECT -#if ENABLE_PLOT2DVIEWER - INCLUDEPATH += ../SPlot2d -#endif - -#if ENABLE_OCCVIEWER - INCLUDEPATH += ../SOCC -#endif - -#if ENABLE_VTKVIEWER - INCLUDEPATH += ../SVTK -#endif - -#else !ENABLE_SALOMEOBJECT -# DEFINES += DISABLE_SALOMEOBJECT - -#endif - -QT_MT_LIBS = -L$$(QTDIR)/lib -lQtCore -lQtXml -lQtGui -lQtOpenGL - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -HDF5_LIBS = -L$$(HDF5HOME)/lib -lhdf5 - -KERNEL_LDFLAGS = -L$$(KERNEL_ROOT_DIR)/lib/salome - -PYTHON_LIBS = -L$$(PYTHONHOME)/lib/python2.4/config -lpython2.4 -ldl -lutil - -LIBS += $${QT_MT_LIBS} -L../../lib -lsuit -lstd -lCAM -lLogWindow $${CAS_KERNEL} -lSalomePrs $${HDF5_LIBS} $${KERNEL_LDFLAGS} -lSalomeHDFPersist #-lObjBrowser - -#if ENABLE_PYCONSOLE - LIBS += $${PYTHON_LIBS} -#endif - -#if ENABLE_SALOMEOBJECT - LIBS += -lSalomeObject -#endif - -#if ENABLE_VTKVIEWER - LIBS += -lVTKViewer -#if ENABLE_SALOMEOBJECT - LIBS += -lSVTK -#endif -#endif - -#if ENABLE_OCCVIEWER - LIBS += -lOCCViewer -#if ENABLE_SALOMEOBJECT - LIBS += -lSOCC -#endif -#endif - -#if ENABLE_GLVIEWER - LIBS += -lGLViewer -#endif - -#if ENABLE_PLOT2DVIEWER - LIBS += -lPlot2d -#if ENABLE_SALOMEOBJECT - LIBS += -lSPlot2d -#endif -#endif - -#if ENABLE_PYCONSOLE - LIBS += -lPyInterp -lPyConsole -#endif - -#if ENABLE_SUPERVGRAPHVIEWER - LIBS += -lSUPERVGraph -#endif - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += LIGHTAPP_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = LightApp.h -HEADERS += LightApp_AboutDlg.h -HEADERS += LightApp_Application.h -HEADERS += LightApp_DataModel.h -HEADERS += LightApp_DataObject.h -HEADERS += LightApp_DataOwner.h -HEADERS += LightApp_DataSubOwner.h -HEADERS += LightApp_Dialog.h -HEADERS += LightApp_Displayer.h -HEADERS += LightApp_Driver.h -HEADERS += LightApp_EventFilter.h -HEADERS += LightApp_HDFDriver.h -HEADERS += LightApp_Module.h -HEADERS += LightApp_ModuleAction.h -HEADERS += LightApp_ModuleDlg.h -HEADERS += LightApp_NameDlg.h -#HEADERS += LightApp_OBFilter.h -HEADERS += LightApp_OBSelector.h -HEADERS += LightApp_Operation.h -HEADERS += LightApp_Selection.h -HEADERS += LightApp_SelectionMgr.h -HEADERS += LightApp_ShowHideOp.h -HEADERS += LightApp_Study.h -HEADERS += LightApp_SwitchOp.h -HEADERS += LightApp_Preferences.h -HEADERS += LightApp_PreferencesDlg.h -HEADERS += LightApp_RootObject.h -HEADERS += LightApp_UpdateFlags.h -HEADERS += LightApp_WidgetContainer.h - -#if ENABLE_VTKVIEWER -#if ENABLE_SALOMEOBJECT - HEADERS += LightApp_VTKSelector.h -#endif -#endif -#if ENABLE_OCCVIEWER - HEADERS += LightApp_OCCSelector.h -#endif -#if ENABLE_GLVIEWER - HEADERS += LightApp_GLSelector.h -#endif - -SOURCES = LightApp_AboutDlg.cxx -SOURCES += LightApp_Application.cxx -SOURCES += LightApp_DataModel.cxx -SOURCES += LightApp_DataObject.cxx -SOURCES += LightApp_DataOwner.cxx -SOURCES += LightApp_DataSubOwner.cxx -SOURCES += LightApp_Dialog.cxx -SOURCES += LightApp_Displayer.cxx -SOURCES += LightApp_Driver.cxx -SOURCES += LightApp_EventFilter.cxx -SOURCES += LightApp_HDFDriver.cxx -SOURCES += LightApp_Module.cxx -SOURCES += LightApp_ModuleAction.cxx -SOURCES += LightApp_ModuleDlg.cxx -SOURCES += LightApp_NameDlg.cxx -#SOURCES += LightApp_OBFilter.cxx -SOURCES += LightApp_OBSelector.cxx -SOURCES += LightApp_Operation.cxx -SOURCES += LightApp_Selection.cxx -SOURCES += LightApp_SelectionMgr.cxx -SOURCES += LightApp_ShowHideOp.cxx -SOURCES += LightApp_Study.cxx -SOURCES += LightApp_SwitchOp.cxx -SOURCES += LightApp_Preferences.cxx -SOURCES += LightApp_PreferencesDlg.cxx -SOURCES += LightApp_WidgetContainer.cxx - -#if ENABLE_VTKVIEWER -#if ENABLE_SALOMEOBJECT - SOURCES += LightApp_VTKSelector.cxx -#endif -#endif -#if ENABLE_OCCVIEWER - SOURCES += LightApp_OCCSelector.cxx -#endif -#if ENABLE_GLVIEWER - SOURCES += LightApp_GLSelector.cxx -#endif - -TRANSLATIONS = resources/LightApp_images.ts \ - resources/LightApp_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/LightApp/LightApp_Application.cxx b/src/LightApp/LightApp_Application.cxx index 828e31f81..bf2a0e81a 100644 --- a/src/LightApp/LightApp_Application.cxx +++ b/src/LightApp/LightApp_Application.cxx @@ -645,7 +645,7 @@ void LightApp_Application::createActions() int newWinMenu = createMenu( tr( "MEN_DESK_NEWWINDOW" ), windowMenu, -1, 0 ); createAction( CloseId, tr( "TOT_CLOSE" ), QIcon(), tr( "MEN_DESK_CLOSE" ), tr( "PRP_CLOSE" ), - Qt::SHIFT+Qt::Key_C, desk, false, this, SLOT( onCloseWindow() ) ); + Qt::CTRL+Qt::Key_F4, desk, false, this, SLOT( onCloseWindow() ) ); createAction( CloseAllId, tr( "TOT_CLOSE_ALL" ), QIcon(), tr( "MEN_DESK_CLOSE_ALL" ), tr( "PRP_CLOSE_ALL" ), 0, desk, false, this, SLOT( onCloseAllWindow() ) ); createAction( GroupAllId, tr( "TOT_GROUP_ALL" ), QIcon(), tr( "MEN_DESK_GROUP_ALL" ), tr( "PRP_GROUP_ALL" ), @@ -675,7 +675,7 @@ void LightApp_Application::createActions() #endif createAction( RenameId, tr( "TOT_RENAME" ), QIcon(), tr( "MEN_DESK_RENAME" ), tr( "PRP_RENAME" ), - Qt::SHIFT+Qt::Key_R, desk, false, this, SLOT( onRenameWindow() ) ); + Qt::ALT+Qt::SHIFT+Qt::Key_R, desk, false, this, SLOT( onRenameWindow() ) ); createMenu( RenameId, windowMenu, -1 ); int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 ); @@ -941,10 +941,8 @@ public: virtual void run() { - if ( !myApp.isEmpty()) { - QString aCommand = QString( "%1 %2 \"%3\"" ).arg( myApp, myParams, myHelpFile ); - if ( !myContext.isEmpty() ) - aCommand += "#" + myContext; + if ( !myApp.isEmpty() && !myHelpFile.isEmpty()) { + QString aCommand = QString( "%1 %2 \"%3%4\"" ).arg( myApp, myParams, myHelpFile, myContext.isEmpty() ? QString("") : QString( "#%1" ).arg( myContext ) ); QProcess* proc = new QProcess(); @@ -1017,6 +1015,18 @@ void LightApp_Application::onHelpContextModule( const QString& theComponentName, const QString& theFileName, const QString& theContext ) { + QString fileName = theFileName; + QString context = theContext; + if ( !QFile::exists( fileName ) && theContext.isEmpty() ) { + // context might be passed within theFileName argument + QStringList comps = fileName.split("#"); + if ( comps.count() > 1 ) { + context = comps.last(); + comps.removeLast(); + fileName = comps.join("#"); + } + } + QString homeDir = ""; if ( !theComponentName.isEmpty() ) { QString dir = getenv( ( theComponentName + "_ROOT_DIR" ).toLatin1().constData() ); @@ -1029,7 +1039,7 @@ void LightApp_Application::onHelpContextModule( const QString& theComponentName, Qtx::addSlash( theComponentName ) ); } - QString helpFile = QFileInfo( homeDir + theFileName ).absoluteFilePath(); + QString helpFile = QFileInfo( homeDir + fileName ).absoluteFilePath(); SUIT_ResourceMgr* resMgr = resourceMgr(); QString platform; #ifdef WIN32 @@ -1050,7 +1060,7 @@ void LightApp_Application::onHelpContextModule( const QString& theComponentName, QString aParams = resMgr->stringValue("ExternalBrowser", "parameters"); if ( !anApp.isEmpty() ) { - RunBrowser* rs = new RunBrowser( this, anApp, aParams, helpFile, theContext ); + RunBrowser* rs = new RunBrowser( this, anApp, aParams, helpFile, context ); rs->start(); } else { @@ -1061,7 +1071,7 @@ void LightApp_Application::onHelpContextModule( const QString& theComponentName, } } else { - QtxWebBrowser::loadUrl(getFile() + helpFile, theContext ); + QtxWebBrowser::loadUrl(getFile() + helpFile, context ); } } diff --git a/src/LightApp/LightApp_DataModel.cxx b/src/LightApp/LightApp_DataModel.cxx index da1544eee..8c9675905 100644 --- a/src/LightApp/LightApp_DataModel.cxx +++ b/src/LightApp/LightApp_DataModel.cxx @@ -79,6 +79,15 @@ bool LightApp_DataModel::saveAs( const QString&, CAM_Study*, QStringList& ) return true; } +/*! + Does nothing by default. Should be redefined in light modules + that want to participate in "Dump study" operation. +*/ +bool LightApp_DataModel::dumpPython( const QString&, CAM_Study*, bool, QStringList& ) +{ + return true; +} + /*! Emit closed() */ @@ -110,7 +119,10 @@ void LightApp_DataModel::updateWidgets() */ void LightApp_DataModel::update( LightApp_DataObject*, LightApp_Study* ) { - LightApp_ModuleObject* modelRoot = dynamic_cast( root() ); + // san: Previously modelRoot was casted to LightApp_ModuleObject*, + // BUT this is incorrect: in full SALOME the model root has different type. + // Hopefully LightApp_DataObject* is sufficient here. + LightApp_DataObject* modelRoot = dynamic_cast( root() ); DataObjectList ch; QMap aMap; if( modelRoot ) @@ -123,7 +135,7 @@ void LightApp_DataModel::update( LightApp_DataObject*, LightApp_Study* ) build(); - modelRoot = dynamic_cast( root() ); + modelRoot = dynamic_cast( root() ); if( modelRoot ) { DataObjectList new_ch = modelRoot->children(); @@ -207,5 +219,27 @@ void LightApp_DataModel::unregisterColumn( SUIT_DataBrowser* browser, const QStr { SUIT_AbstractModel* m = dynamic_cast( browser ? browser->model() : 0 ); if( m ) - m->unregisterColumn( groupId(), name ); + m->unregisterColumn( groupId(), name ); +} + +/*! + Creates the data model's root (module object) using the study services. + This is important because different study classes use different moduel object classes. + Therefore creation of the module object cannot be done at the data model level + where the type of the current study instance should not be known. + The module object returned by this method should be then passed to the model's setRoot(). + \return the module object instance corresponding to the study type + \sa CAM_DataModel class +*/ +CAM_ModuleObject* LightApp_DataModel::createModuleObject( SUIT_DataObject* theRoot ) const +{ + LightApp_RootObject* aStudyRoot = dynamic_cast( theRoot ); + if ( !aStudyRoot ) + return 0; + + LightApp_Study* aStudy = aStudyRoot->study(); + if ( aStudy ) + return aStudy->createModuleObject( const_cast( this ), + theRoot ); + return 0; } diff --git a/src/LightApp/LightApp_DataModel.h b/src/LightApp/LightApp_DataModel.h index 097fe9ab5..4da6d3675 100644 --- a/src/LightApp/LightApp_DataModel.h +++ b/src/LightApp/LightApp_DataModel.h @@ -38,6 +38,7 @@ class LightApp_Module; class LightApp_Study; class LightApp_DataObject; class SUIT_DataBrowser; +class CAM_ModuleObject; /*! Description : Base class of data model @@ -54,6 +55,10 @@ public: virtual bool save( QStringList& ); virtual bool saveAs( const QString&, CAM_Study*, QStringList& ); virtual bool close(); + virtual bool dumpPython( const QString&, + CAM_Study*, + bool, + QStringList& ); virtual void update( LightApp_DataObject* = 0, LightApp_Study* = 0 ); @@ -75,6 +80,7 @@ protected: LightApp_Study* getStudy() const; virtual void build(); virtual void updateWidgets(); + virtual CAM_ModuleObject* createModuleObject( SUIT_DataObject* theRoot ) const; private: int myGroupId; diff --git a/src/LightApp/LightApp_DataObject.cxx b/src/LightApp/LightApp_DataObject.cxx index 6dd9f06dc..90e0a64f1 100644 --- a/src/LightApp/LightApp_DataObject.cxx +++ b/src/LightApp/LightApp_DataObject.cxx @@ -244,6 +244,31 @@ QString LightApp_DataObject::entry() const return QString(); } +/*! + \brief Returns the string identifier of the data objects referenced by this one. + + This method should be reimplemented in the subclasses. + Default implementation returns null string. + + \return ID string of the referenced data object +*/ +QString LightApp_DataObject::refEntry() const +{ + return QString(); +} + +/*! + \brief Tells if this data objects is a reference to some other or not. + + The base implementation retuns true, if refEntry() returns non-empty string. + + \return true if refEntry() is a non-empty string. +*/ +bool LightApp_DataObject::isReference() const +{ + return !refEntry().isEmpty(); +} + /*! \brief Get the data object unique key. \return data object key @@ -257,17 +282,88 @@ SUIT_DataObjectKey* LightApp_DataObject::key() const /*! \brief Get object text data for the specified column. - Column with \a id = 0 (NameId) is supposed to be used + Column with \a id == NameId is supposed to be used to get the object name. - Column with \a id = 1 (EntryId) is supposed to be used + Column with \a id == EntryId is supposed to be used to get the object entry. + Column with \a id == RefEntryId is supposed to be used + to show the entry of the object referenced by this one. \param id column id \return object text data */ QString LightApp_DataObject::text( const int id ) const { - return id == EntryId ? entry() : CAM_DataObject::text( id ); + QString txt; + + switch ( id ) + { + case EntryId: + txt = entry(); + break; + case RefEntryId: + // Issue 21379: reference support at LightApp level + if ( isReference() ) + txt = refEntry(); + break; + default: + // Issue 21379: Note that we cannot return some specially decorated + // name string (like "* ref_obj_name") when isReference() returns true, + // since there is no generic way at LightApp level + // to query the object name using refEntry() up to now. + // TODO: Think how to make reference name generation + // more generic at move it here from SalomeApp level... + txt = CAM_DataObject::text( id ); + break; + } + + return txt; +} + +/*! + \brief Get data object color for the specified column. + \param role color role + \param id column id (not used) + \return object color for the specified column +*/ +QColor LightApp_DataObject::color( const ColorRole role, const int id) const +{ + QColor c; + + // Issue 21379: reference support at LightApp level + // Centralized way for choosing text/background color for references. + // Colors for "normal" objects should be chosen by sub-classes. + switch ( role ) + { + case Text: + case Foreground: + // text color (not selected item) + // TODO: think how to detect invalid references... + if ( isReference() ) + c = QColor( 255, 0, 0 ); // valid reference (red) + break; + + case Highlight: + // background color for the highlighted item + // TODO: think how to detect invalid references... + if ( isReference() ) + c = QColor( 255, 0, 0 ); // valid reference (red) + break; + + case HighlightedText: + // text color for the highlighted item + if ( isReference() ) + c = QColor( 255, 255, 255 ); // white + break; + + default: + break; + } + + if ( !c.isValid() ) + c = CAM_DataObject::color( role, id ); + + return c; } /*! diff --git a/src/LightApp/LightApp_DataObject.h b/src/LightApp/LightApp_DataObject.h index 6af26dcc8..ac7ac8c57 100644 --- a/src/LightApp/LightApp_DataObject.h +++ b/src/LightApp/LightApp_DataObject.h @@ -37,8 +37,9 @@ class LIGHTAPP_EXPORT LightApp_DataObject : public virtual CAM_DataObject public: //! Column id - enum { - EntryId = VisibilityId + 1 //!< entry column + enum { + EntryId = VisibilityId + 1, //!< entry column + RefEntryId //!< reference entry column }; public: @@ -48,7 +49,11 @@ public: virtual SUIT_DataObjectKey* key() const; virtual QString entry() const; + virtual QString refEntry() const; + virtual bool isReference() const; + virtual QString text( const int = NameId ) const; + virtual QColor color( const ColorRole, const int = NameId ) const; virtual SUIT_DataObject* componentObject() const; virtual QString componentDataType() const; diff --git a/src/LightApp/LightApp_ModuleAction.cxx b/src/LightApp/LightApp_ModuleAction.cxx index d178991d3..4f7b136a2 100755 --- a/src/LightApp/LightApp_ModuleAction.cxx +++ b/src/LightApp/LightApp_ModuleAction.cxx @@ -25,6 +25,8 @@ #include #include #include +#include +#include /*! \class LightApp_ModuleAction::ActionSet @@ -181,6 +183,23 @@ QWidget* LightApp_ModuleAction::ComboAction::createWidget( QWidget* parent ) \param item identifier */ +/*! + \class LightApp_ModuleAction::ActivateEvent + \brief Internal class to represent custom event for transfer the activation item id. + \internal +*/ +class LightApp_ModuleAction::ActivateEvent : public QEvent +{ +public: + ActivateEvent( QEvent::Type type, int id ) : QEvent( type ), myId( id ) {}; + ~ActivateEvent() {}; + + int id() const { return myId; } + +private: + int myId; +}; + /*! \class LightApp_ModuleAction \brief An action, representing the list of modules to be inserted to the @@ -413,6 +432,19 @@ void LightApp_ModuleAction::removedFrom( QWidget* w ) w->removeAction( mySet ); } +/*! + \brief Perform delayed activation with specified id. + \param e custom event + \return \c true if the event was processed successfully and \c false otherwise. +*/ +bool LightApp_ModuleAction::event( QEvent* e ) +{ + if ( e->type() == QEvent::MaxUser ) + activate( ((ActivateEvent*)e)->id(), false ); + else + return QtxAction::event( e ); +} + /*! \fn void LightApp_ModuleAction::moduleActivated( const QString& name ); \brief Emitted when the module is activated @@ -568,5 +600,5 @@ void LightApp_ModuleAction::onChanged() */ void LightApp_ModuleAction::onComboActivated( int id ) { - activate( id, false ); + QApplication::postEvent( this, new ActivateEvent( QEvent::MaxUser, id ) ); } diff --git a/src/LightApp/LightApp_ModuleAction.h b/src/LightApp/LightApp_ModuleAction.h index c170e1b38..8c6bdd31c 100755 --- a/src/LightApp/LightApp_ModuleAction.h +++ b/src/LightApp/LightApp_ModuleAction.h @@ -40,6 +40,7 @@ class LIGHTAPP_EXPORT LightApp_ModuleAction : public QtxAction private: class ActionSet; class ComboAction; + class ActivateEvent; public: enum { None = 0x00, Buttons = 0x01, ComboItem = 0x02, All = Buttons | ComboItem }; @@ -67,6 +68,8 @@ protected: virtual void addedTo( QWidget* ); virtual void removedFrom( QWidget* ); + virtual bool event( QEvent* ); + signals: void moduleActivated( const QString& ); @@ -99,11 +102,11 @@ public: QList widgets() const; -protected: - virtual QWidget* createWidget( QWidget* ); - signals: void activatedId( int ); + +protected: + virtual QWidget* createWidget( QWidget* ); }; #endif // LIGHTAPP_MODULEACTION_H diff --git a/src/LightApp/LightApp_Study.cxx b/src/LightApp/LightApp_Study.cxx index 4bbf7a886..69ecbe9cf 100644 --- a/src/LightApp/LightApp_Study.cxx +++ b/src/LightApp/LightApp_Study.cxx @@ -448,6 +448,44 @@ void LightApp_Study::RemoveTemporaryFiles (const char* theModuleName, const bool myDriver->RemoveTemporaryFiles(theModuleName, isDirDeleted); } +/*! + Virtual method that creates the root object (module object) for the given data model. + The type of the created object depends on the study class, therefore data model classes + should not create their module objects directly and should instead use + LightApp_DataModel::createModuleObject() that in its turn relies on this method. + + \param theDataModel - data model instance to create a module object for + \param theParent - the module object's parent (normally it's the study root) + \return the module object instance + \sa LightApp_DataModel class, SalomeApp_Study class, LightApp_ModuleObject class +*/ +CAM_ModuleObject* LightApp_Study::createModuleObject( LightApp_DataModel* theDataModel, + SUIT_DataObject* theParent ) const +{ + // Calling addComponent() for symmetry with SalomeApp_Study + // Currently it has empty implementation, but maybe in the future things will change... + LightApp_Study* that = const_cast( this ); + that->addComponent( theDataModel ); + + // Avoid creating multiple module objects for the same module + CAM_ModuleObject* res = 0; + + DataObjectList children = root()->children(); + DataObjectList::const_iterator anIt = children.begin(), aLast = children.end(); + for( ; !res && anIt!=aLast; anIt++ ) + { + LightApp_ModuleObject* obj = dynamic_cast( *anIt ); + if ( obj && obj->name() == theDataModel->module()->moduleName() ) + res = obj; + } + + if ( !res ){ + res = new LightApp_ModuleObject( theDataModel, theParent ); + } + + return res; +} + /*! Fills list with components names \param comp - list to be filled diff --git a/src/LightApp/LightApp_Study.h b/src/LightApp/LightApp_Study.h index 72dcfe0c5..428900cda 100644 --- a/src/LightApp/LightApp_Study.h +++ b/src/LightApp/LightApp_Study.h @@ -38,7 +38,9 @@ class SUIT_Study; class SUIT_Application; class CAM_DataModel; +class CAM_ModuleObject; class LightApp_DataObject; +class LightApp_DataModel; //Map to store visual property of the object. //Key: Name of the visual property of the object. @@ -123,6 +125,8 @@ protected: protected: virtual bool openDataModel ( const QString&, CAM_DataModel* ); + virtual CAM_ModuleObject* createModuleObject( LightApp_DataModel* theDataModel, + SUIT_DataObject* theParent ) const; signals: void saved ( SUIT_Study* ); @@ -136,6 +140,7 @@ private: ViewMgrMap myViewMgrMap; friend class LightApp_Application; + friend class LightApp_DataModel; }; #endif diff --git a/src/LightApp/resources/LightApp.xml b/src/LightApp/resources/LightApp.xml index fa09126ea..f78854f90 100644 --- a/src/LightApp/resources/LightApp.xml +++ b/src/LightApp/resources/LightApp.xml @@ -76,6 +76,7 @@ +
    diff --git a/src/LightApp/resources/LightApp_msg_fr.ts b/src/LightApp/resources/LightApp_msg_fr.ts index 786cbea35..8478328f7 100755 --- a/src/LightApp/resources/LightApp_msg_fr.ts +++ b/src/LightApp/resources/LightApp_msg_fr.ts @@ -33,7 +33,7 @@ CEA/DEN, CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS ABOUT_CLOSE &Fermer - + APP_NAME SALOME @@ -53,35 +53,35 @@ CEA/DEN, CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS BROWSER_TITLE Aide de SALOME - + BROWSER_TOOLBAR_TITLE Navigation - + BROWSER_FILEMENU &Fichier - + BROWSER_CLOSE &Fermer - + BROWSER_BACK Reculer - + BROWSER_FORWARD Avancer - + BROWSER_FIND &Chercher... - + BROWSER_FINDNEXT Chercher &suivant - + BROWSER_FINDPREV Chercher &précédent @@ -385,7 +385,7 @@ Les modifications seront appliquées à la prochaine session. MEN_RENAME_OBJ Renommer - + LOG_WINDOW Fenêtre de messages @@ -448,7 +448,7 @@ Les modifications seront appliquées à la prochaine session. PREF_AUTO_SIZE_FIRST - Taille automatique pour la colonne "Nom" + Taille automatique pour la colonne "Nom" PREF_RESIZE_ON_EXPAND_ITEM @@ -464,7 +464,7 @@ Les modifications seront appliquées à la prochaine session. PREF_BROWSE_AFTER_APPLY_AND_CLOSE_ONLY - Seulement après "Appliquer et Fermer" + Seulement après "Appliquer et Fermer" PREF_BROWSE_ALWAYS @@ -522,6 +522,10 @@ Les modifications seront appliquées à la prochaine session. PREF_VIEWER_BACKGROUND Couleur de l'arrière-plan + + PREF_VIEWER_SELECTION + Couleur de sélection + PREF_XYVIEWER_BACKGROUND Couleur de l'arrière-plan (vue XY) @@ -542,6 +546,18 @@ Les modifications seront appliquées à la prochaine session. PREF_FONT Police + + PREF_LEGEND_FONT + Police de la légende + + + PREF_FONT_COLOR + Couleur de police de la légende + + + PREF_SELECTED_FONT_COLOR + Couleur de surlignement de la légende + PREF_LEFT Gauche diff --git a/src/LogWindow/LogWindow.pro b/src/LogWindow/LogWindow.pro deleted file mode 100644 index 13c362f9b..000000000 --- a/src/LogWindow/LogWindow.pro +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = LogWindow -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -INCLUDEPATH += ../../include ../Qtx ../SUIT -LIBS += -L../../lib -lqtx -lsuit - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += LOGWINDOW_EXPORTS - -HEADERS = LogWindow.h - -SOURCES = LogWindow.cxx - -TRANSLATIONS = resources/LogWindow_msg_en.ts - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/Makefile.am b/src/Makefile.am index d68377865..68d484e19 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -106,9 +106,10 @@ SUBDIRS_LIGHT = LightApp ResExporter # Full (CORBA) SALOME packages ## if GUI_ENABLE_CORBA - SUBDIRS_CORBA = TOOLSGUI Session SalomeApp + SUBDIRS_CORBA = TOOLSGUI Session SalomeApp GuiHelpers TreeData endif + ## # Extra Python packages ## @@ -122,5 +123,5 @@ SUBDIRS = $(SUBDIRS_COMMON) $(SUBDIRS_OBJECT) $(SUBDIRS_VIEWERTOOLS) $(SUBDIRS_G DIST_SUBDIRS = CASCatch Qtx Style DDS QDS ObjBrowser SUIT SUITApp STD CAF CAM LogWindow Prs Event OpenGLUtils \ OBJECT ViewerTools GLViewer VTKViewer SVTK OCCViewer SOCC Plot2d SPlot2d SUPERVGraph QxGraph QxScene \ - PyInterp PyConsole LightApp ResExporter TOOLSGUI Session SalomeApp SALOME_SWIG SALOME_PY SALOME_PYQT \ - GUI_PY + PyInterp PyConsole LightApp ResExporter TOOLSGUI Session SalomeApp GuiHelpers TreeData \ + SALOME_SWIG SALOME_PY SALOME_PYQT GUI_PY diff --git a/src/OBJECT/OBJECT.pro b/src/OBJECT/OBJECT.pro deleted file mode 100644 index ff7577d4e..000000000 --- a/src/OBJECT/OBJECT.pro +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = SalomeObject -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -CAS_LDPATH = -L$${CASROOT}/Linux/lib -lTKV3d - -INCLUDEPATH += ../../include $${CAS_CPPFLAGS} -LIBS += $${CAS_LDPATH} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = SALOME_InteractiveObject.hxx -HEADERS += Handle_SALOME_InteractiveObject.hxx -HEADERS += SALOME_AISShape.hxx -HEADERS += Handle_SALOME_AISShape.hxx -HEADERS += SALOME_AISObject.hxx -HEADERS += Handle_SALOME_AISObject.hxx -HEADERS += SALOME_ListIO.hxx -HEADERS += SALOME_ListIteratorOfListIO.hxx -HEADERS += Handle_SALOME_ListNodeOfListIO.hxx -HEADERS += SALOME_ListNodeOfListIO.hxx -HEADERS += Handle_SALOME_Filter.hxx -HEADERS += SALOME_Filter.hxx -HEADERS += Handle_SALOME_TypeFilter.hxx -HEADERS += SALOME_TypeFilter.hxx -HEADERS += SALOME_DataMapOfIOMapOfInteger.hxx -HEADERS += SALOME_DataMapIteratorOfDataMapOfIOMapOfInteger.hxx -HEADERS += Handle_SALOME_DataMapNodeOfDataMapOfIOMapOfInteger.hxx -HEADERS += SALOME_Selection.h -HEADERS += SALOME_AISObject.ixx -HEADERS += SALOME_AISObject.jxx -HEADERS += SALOME_AISShape.ixx -HEADERS += SALOME_AISShape.jxx -HEADERS += SALOME_Filter.ixx -HEADERS += SALOME_Filter.jxx -HEADERS += SALOME_InteractiveObject.ixx -HEADERS += SALOME_InteractiveObject.jxx -HEADERS += SALOME_TypeFilter.ixx -HEADERS += SALOME_TypeFilter.jxx -HEADERS += SALOME_DataMapNodeOfDataMapOfIOMapOfInteger.hxx - -SOURCES = SALOME_InteractiveObject.cxx -SOURCES += SALOME_AISShape.cxx -SOURCES += SALOME_AISObject.cxx -SOURCES += SALOME_ListIO_0.cxx -SOURCES += SALOME_ListIteratorOfListIO_0.cxx -SOURCES += SALOME_ListNodeOfListIO_0.cxx -SOURCES += SALOME_Filter.cxx -SOURCES += SALOME_TypeFilter.cxx -SOURCES += SALOME_DataMapOfIOMapOfInteger_0.cxx -SOURCES += SALOME_DataMapNodeOfDataMapOfIOMapOfInteger_0.cxx -SOURCES += SALOME_DataMapIteratorOfDataMapOfIOMapOfInteger_0.cxx - -includes.files = $$HEADERS -includes.path = ../../include - -INSTALLS += includes diff --git a/src/OCCViewer/OCCViewer.pro b/src/OCCViewer/OCCViewer.pro deleted file mode 100644 index 7274dc237..000000000 --- a/src/OCCViewer/OCCViewer.pro +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = OCCViewer -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -OGL_INCLUDES = - -OGL_LIBS = -lGL -L/usr/X11R6/lib -lGLU - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -CAS_VIEWER = -L$${CASROOT}/Linux/lib -lTKV3d -lTKService - -INCLUDEPATH += ../../include $${OGL_INCLUDES} $${CAS_CPPFLAGS} ../Qtx ../SUIT -LIBS += -L../../lib -lqtx -lsuit $${OGL_LIBS} $${CAS_KERNEL} $${CAS_VIEWER} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += OCCVIEWER_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = OCCViewer_AISSelector.h -HEADERS += OCCViewer_ViewManager.h -HEADERS += OCCViewer_ViewModel.h -HEADERS += OCCViewer_ViewPort3d.h -HEADERS += OCCViewer_ViewPort.h -HEADERS += OCCViewer_ViewWindow.h -HEADERS += OCCViewer_VService.h -HEADERS += OCCViewer_CreateRestoreViewDlg.h -HEADERS += OCCViewer.h -HEADERS += OCCViewer_ClippingDlg.h -HEADERS += OCCViewer_SetRotationPointDlg.h - -SOURCES = OCCViewer_AISSelector.cxx -SOURCES += OCCViewer_ViewManager.cxx -SOURCES += OCCViewer_ViewModel.cxx -SOURCES += OCCViewer_ViewPort3d.cxx -SOURCES += OCCViewer_ViewPort.cxx -SOURCES += OCCViewer_ViewWindow.cxx -SOURCES += OCCViewer_VService.cxx -SOURCES += OCCViewer_CreateRestoreViewDlg.cxx -SOURCES += OCCViewer_SetRotationPointDlg.cxx -SOURCES += OCCViewer_ClippingDlg.cxx - -TRANSLATIONS = resources/OCCViewer_images.ts \ - resources/OCCViewer_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/OCCViewer/OCCViewer_ViewModel.h b/src/OCCViewer/OCCViewer_ViewModel.h index 8f08f28c6..5c5c7bfe2 100755 --- a/src/OCCViewer/OCCViewer_ViewModel.h +++ b/src/OCCViewer/OCCViewer_ViewModel.h @@ -144,10 +144,10 @@ signals: protected: protected slots: - void onMousePress(SUIT_ViewWindow*, QMouseEvent*); - void onMouseMove(SUIT_ViewWindow*, QMouseEvent*); - void onMouseRelease(SUIT_ViewWindow*, QMouseEvent*); - void onKeyPress(SUIT_ViewWindow*, QKeyEvent*); + virtual void onMousePress(SUIT_ViewWindow*, QMouseEvent*); + virtual void onMouseMove(SUIT_ViewWindow*, QMouseEvent*); + virtual void onMouseRelease(SUIT_ViewWindow*, QMouseEvent*); + virtual void onKeyPress(SUIT_ViewWindow*, QKeyEvent*); void onDumpView(); void onChangeBgColor(); @@ -155,7 +155,7 @@ protected slots: void onChangeBgImageTiled(); void onChangeBgImageStretched(); -private: +protected: Handle(V3d_Viewer) myV3dViewer; Handle(V3d_Viewer) myV3dCollector; diff --git a/src/OCCViewer/resources/OCCViewer_msg_fr.ts b/src/OCCViewer/resources/OCCViewer_msg_fr.ts index 746f58327..a5091053e 100755 --- a/src/OCCViewer/resources/OCCViewer_msg_fr.ts +++ b/src/OCCViewer/resources/OCCViewer_msg_fr.ts @@ -271,6 +271,10 @@ OCC_IMAGE_FILES Fichiers images (*.bmp *.png *.jpg *.jpeg *.eps *.ps) + + OCC_BG_IMAGE_FILES + Fichiers images (*.bmp *.gif *.pix *.xwd *.rgb *.rs) + DSC_MAXIMIZE_VIEW Maximiser la vue @@ -279,6 +283,10 @@ DSC_MINIMIZE_VIEW Minimiser la vue + + DSC_SYNCHRONIZE_VIEW + Synchroniser la vue + MNU_MAXIMIZE_VIEW Maximiser @@ -287,6 +295,14 @@ MNU_MINIMIZE_VIEW Minimiser + + MNU_SYNCHRONIZE_VIEW + Synchroniser + + + MNU_SYNC_NO_VIEW + [ Pas de vue appropriée ] + OCCViewer_CreateRestoreViewDlg @@ -349,7 +365,7 @@ MEN_CHANGE_IMAGE - Régler/Changer l''image d'arrière-plan... + Régler/Changer l''image d'arrière-plan... SELECT_IMAGE diff --git a/src/Plot2d/Makefile.am b/src/Plot2d/Makefile.am index 7281b669d..02922e499 100755 --- a/src/Plot2d/Makefile.am +++ b/src/Plot2d/Makefile.am @@ -30,22 +30,30 @@ include $(top_srcdir)/adm_local/unix/make_common_starter.am lib_LTLIBRARIES = libPlot2d.la # header files -salomeinclude_HEADERS = \ - Plot2d.h \ - Plot2d_PlotItems.h \ - Plot2d_Object.h \ - Plot2d_Curve.h \ - Plot2d_Histogram.h \ - Plot2d_FitDataDlg.h \ - Plot2d_Prs.h \ - Plot2d_SetupViewDlg.h \ - Plot2d_ViewFrame.h \ - Plot2d_ViewManager.h \ - Plot2d_ViewModel.h \ - Plot2d_ViewWindow.h \ - Plot2d_SetupCurveDlg.h \ - Plot2d_SetupCurveScaleDlg.h \ - Plot2d_ToolTip.h +salomeinclude_HEADERS = \ + Plot2d.h \ + Plot2d_PlotItems.h \ + Plot2d_Object.h \ + Plot2d_Curve.h \ + Plot2d_Histogram.h \ + Plot2d_FitDataDlg.h \ + Plot2d_Prs.h \ + Plot2d_SetupViewDlg.h \ + Plot2d_ViewFrame.h \ + Plot2d_ViewManager.h \ + Plot2d_ViewModel.h \ + Plot2d_ViewWindow.h \ + Plot2d_SetupCurveDlg.h \ + Plot2d_ToolTip.h \ + Plot2d_SetupCurveScaleDlg.h + +if ENABLE_PYCONSOLE +salomeinclude_HEADERS += \ + Plot2d_AnaliticCurve.h \ + Plot2d_AnaliticCurveDlg.h \ + Plot2d_AnaliticParcer.h +endif + dist_libPlot2d_la_SOURCES = \ Plot2d.cxx \ @@ -62,7 +70,14 @@ dist_libPlot2d_la_SOURCES = \ Plot2d_ViewWindow.cxx \ Plot2d_SetupCurveDlg.cxx \ Plot2d_SetupCurveScaleDlg.cxx \ - Plot2d_ToolTip.cxx + Plot2d_ToolTip.cxx + +if ENABLE_PYCONSOLE +dist_libPlot2d_la_SOURCES += \ + Plot2d_AnaliticCurve.cxx \ + Plot2d_AnaliticCurveDlg.cxx \ + Plot2d_AnaliticParcer.cxx +endif MOC_FILES = \ Plot2d_FitDataDlg_moc.cxx \ @@ -74,6 +89,11 @@ MOC_FILES = \ Plot2d_SetupCurveDlg_moc.cxx \ Plot2d_SetupCurveScaleDlg_moc.cxx \ Plot2d_ToolTip_moc.cxx + +if ENABLE_PYCONSOLE +MOC_FILES += Plot2d_AnaliticCurveDlg_moc.cxx +endif + nodist_libPlot2d_la_SOURCES = $(MOC_FILES) dist_salomeres_DATA = \ @@ -93,6 +113,7 @@ dist_salomeres_DATA = \ resources/plot2d_print.png \ resources/plot2d_settings.png \ resources/plot2d_splines.png \ + resources/plot2d_analitic_curve.png \ resources/plot2d_zoom.png nodist_salomeres_DATA = \ @@ -103,6 +124,6 @@ nodist_salomeres_DATA = \ libPlot2d_la_CPPFLAGS = $(QT_INCLUDES) $(PYTHON_INCLUDES) $(QWT_INCLUDES) \ -I$(srcdir)/../Qtx -I$(srcdir)/../SUIT -libPlot2d_la_LDFLAGS = $(QWT_LIBS) $(QT_MT_LIBS) ../SUIT/libsuit.la +libPlot2d_la_LDFLAGS = $(QWT_LIBS) $(QT_MT_LIBS) $(PYTHON_LIBS) ../SUIT/libsuit.la diff --git a/src/Plot2d/Plot2d.cxx b/src/Plot2d/Plot2d.cxx index 4074f0d86..721ab545a 100755 --- a/src/Plot2d/Plot2d.cxx +++ b/src/Plot2d/Plot2d.cxx @@ -24,6 +24,34 @@ #include +#include +#include + +const int MSIZE = 9; +const int MAX_ATTEMPTS = 10; // max attempts + +// color tolerance (used to compare color values) +const long COLOR_DISTANCE = 100; + + + +/*! + Constructor +*/ +Plot2d_Point::Plot2d_Point() + : x( 0. ), y( 0. ) +{ +} + +/*! + Constructor +*/ +Plot2d_Point::Plot2d_Point( double theX, double theY, const QString& theText ) + : x( theX ), y( theY ), text( theText ) +{ +} + + /*! \brief Convert Plot2d marker type to Qwt marker type. \param m Plot2d marker type @@ -348,3 +376,113 @@ void Plot2d::drawMarker( QPainter* painter, int x, int y, int w, int h, { drawMarker( painter, QPoint( x, y ), QRect( 0, 0, w, h ), plot2qwtMarker( type ), color ); } + + +/*! + \brief Create icon pixmap according to the marker type. + \param size icon size + \param type marker type + \param color icon color + \return icon +*/ +QPixmap Plot2d::markerIcon(const QSize &size, const QColor& color, Plot2d::MarkerType type ) +{ + + QPixmap px( size ); + px.fill( QColor( 255, 255, 255, 0 ) ); + QPainter p( &px ); + Plot2d::drawMarker( &p, size.width()/2, size.height()/2, MSIZE, MSIZE, type, color ); + return px; +} + + +/*! + \brief Create icon pixmap according to the line type. + \param size icon size + \param type line type + \param color icon color + \return icon +*/ +QPixmap Plot2d::lineIcon( const QSize& size, const QColor& color, Plot2d::LineType type ) +{ + + QPixmap px( size ); + px.fill( QColor( 255, 255, 255, 0 ) ); + QPainter p( &px ); + drawLine( &p, 5, size.height()/2, size.width()-5, size.height()/2, type, + color, 1 ); + return px; +} + +/*! + Gets new unique marker for item if possible +*/ +void Plot2d::getNextMarker( const int rtti, const QwtPlot* thePlot, QwtSymbol::Style& typeMarker, + QColor& color, Qt::PenStyle& typeLine ) +{ + bool bOk = false; + int cnt = 0; + while ( !bOk ) { + int aRed = (int)( 256.0 * rand() / RAND_MAX ); // generate random color + int aGreen = (int)( 256.0 * rand() / RAND_MAX ); // ... + int aBlue = (int)( 256.0 * rand() / RAND_MAX ); // ... + int aMarker = (int)( 9.0 * rand() / RAND_MAX ) + 1;// 9 markers types( not including empty ) + int aLine = (int)( 5.0 * rand() / RAND_MAX ) + 1;// 5 line types ( not including empty ) + + typeMarker = ( QwtSymbol::Style )aMarker; + color = QColor( aRed, aGreen, aBlue ); + typeLine = ( Qt::PenStyle )aLine; + + bOk = ( ++cnt == MAX_ATTEMPTS ) || !existMarker( rtti, thePlot, typeMarker, color, typeLine ); + } +} + +/*! + Checks if marker belongs to any enitity +*/ +bool Plot2d::existMarker( const int rtti, const QwtPlot* thePlot, const QwtSymbol::Style typeMarker, + const QColor& color, const Qt::PenStyle typeLine ) +{ + bool ok = false; + + QColor bgColor = thePlot->palette().color( QPalette::Background ); + if ( closeColors( color, bgColor ) ) { + ok = true; + } + else { + QwtPlotItemList anItems = thePlot->itemList(); + QwtPlotItemIterator anIt = anItems.begin(), aLast = anItems.end(); + QwtPlotItem* anItem; + for ( ; anIt != aLast && !ok; anIt++ ) { + anItem = *anIt; + if ( anItem && anItem->rtti() == rtti ) { + QwtPlotCurve* crv = dynamic_cast( anItem ); + if ( crv ) { + QwtSymbol::Style aStyle = crv->symbol().style(); + QColor aColor = crv->pen().color(); + Qt::PenStyle aLine = crv->pen().style(); + ok = closeColors( aColor, color ) && aStyle == typeMarker && aLine == typeLine; + } + } + } + } + return ok; +} + +/*! + Checks if two colors are close to each other + uses COLOR_DISTANCE variable as max tolerance for comparing of colors +*/ + +bool Plot2d::closeColors( const QColor& color1, + const QColor& color2, + int distance ) +{ + long tol = + qAbs( color2.red() - color1.red() ) + + qAbs( color2.green() - color1.green() ) + + qAbs( color2.blue() - color1.blue() ) - + ( distance < 0 ? COLOR_DISTANCE : distance ); + + return tol <= 0; +} diff --git a/src/Plot2d/Plot2d.h b/src/Plot2d/Plot2d.h index b6886c1cb..b0e496fb9 100755 --- a/src/Plot2d/Plot2d.h +++ b/src/Plot2d/Plot2d.h @@ -40,6 +40,19 @@ #include class QPainter; +class QwtPlot; + +struct PLOT2D_EXPORT Plot2d_Point +{ + double x; + double y; + QString text; + Plot2d_Point(); + Plot2d_Point( double theX, double theY, const QString& theText = QString() ); +}; + +typedef QList pointList; + namespace Plot2d { @@ -95,6 +108,24 @@ namespace Plot2d void drawMarker( QPainter*, int, int, int, int, MarkerType = Circle, const QColor& = Qt::black ); + + QPixmap markerIcon( const QSize&, const QColor&, + Plot2d::MarkerType ); + + QPixmap lineIcon( const QSize&, const QColor&, + Plot2d::LineType ); + + void getNextMarker( const int rtti, const QwtPlot*, QwtSymbol::Style&, + QColor&, Qt::PenStyle& ); + + bool existMarker( const int rtti , const QwtPlot*, const QwtSymbol::Style, + const QColor&, const Qt::PenStyle ); + + + bool closeColors( const QColor&, + const QColor&, + int distance = -1 ); + } #if defined WIN32 diff --git a/src/Plot2d/Plot2d.pro b/src/Plot2d/Plot2d.pro deleted file mode 100644 index 07b60a0e6..000000000 --- a/src/Plot2d/Plot2d.pro +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = Plot2d -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -PYTHONVER=2.4 -PYTHONHOME=$$(PYTHONHOME) -PYTHONINC=$${PYTHONHOME}/include/python$${PYTHONVER} - -QWTHOME=$$(QWTHOME) -QWTINC=$${QWTHOME}/include -QWTLIB=$${QWTHOME}/lib - -INCLUDEPATH += ../../include $${QWTINC} $${PYTHONINC} -INCLUDEPATH += ../Qtx ../SUIT -unix:LIBS += -L$${QWTLIB} -lqwt -win32:LIBS += /LIBPATH:$$(QWTLIB) -LIBS += -L../../lib -lqtx -lsuit - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += PLOT2D_EXPORTS - -HEADERS = Plot2d.h -HEADERS += Plot2d_Curve.h -HEADERS += Plot2d_FitDataDlg.h -HEADERS += Plot2d_Prs.h -HEADERS += Plot2d_SetupViewDlg.h -HEADERS += Plot2d_ViewFrame.h -HEADERS += Plot2d_ViewManager.h -HEADERS += Plot2d_ViewModel.h -HEADERS += Plot2d_ViewWindow.h -HEADERS += Plot2d_SetupCurveDlg.h -HEADERS += Plot2d_ToolTip.h - -SOURCES = Plot2d_Curve.cxx -SOURCES += Plot2d_FitDataDlg.cxx -SOURCES += Plot2d_Prs.cxx -SOURCES += Plot2d_SetupViewDlg.cxx -SOURCES += Plot2d_ViewFrame.cxx -SOURCES += Plot2d_ViewManager.cxx -SOURCES += Plot2d_ViewModel.cxx -SOURCES += Plot2d_ViewWindow.cxx -SOURCES += Plot2d_SetupCurveDlg.cxx -SOURCES += Plot2d_ToolTip.cxx - -TRANSLATIONS = resources/Plot2d_msg_en.ts \ - resources/Plot2d_images.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/Plot2d/Plot2d_AnaliticCurve.cxx b/src/Plot2d/Plot2d_AnaliticCurve.cxx new file mode 100644 index 000000000..b6052d51e --- /dev/null +++ b/src/Plot2d/Plot2d_AnaliticCurve.cxx @@ -0,0 +1,464 @@ +// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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. +// +// 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 : Plot2d_AnaliticCurve.cxx +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. (roman.nikolaev@opencascade.com) + +#include "Plot2d_AnaliticParcer.h" +#include "Plot2d_AnaliticCurve.h" +#include "Plot2d_PlotItems.h" + + +//Init static data; + +int Plot2d_AnaliticCurve::myNbCurves = 0; + +/*! + Constructor +*/ +Plot2d_AnaliticCurve::Plot2d_AnaliticCurve() : + myAutoAssign(true), + myColor( 0, 0, 0 ), + myMarker( Plot2d::Circle ), + myMarkerSize( 0 ), + myLine( Plot2d::Solid ), + myLineWidth( 0 ), + myRangeBegin(0.0), + myRangeEnd(100.0), + myNbIntervals(100), + myExpression(""), + myAction(Plot2d_AnaliticCurve::ActAddInView), + myState(Plot2d_AnaliticCurve::StateNeedUpdate), + myCurve(0), + myActive(true) +{ + myName = QString("Analitic Curve %1").arg(++myNbCurves); +} + + +/*! + Destructor +*/ +Plot2d_AnaliticCurve::~Plot2d_AnaliticCurve() +{ +} + +/*! + Copy constructor. Makes deep copy of data +*/ +Plot2d_AnaliticCurve::Plot2d_AnaliticCurve( const Plot2d_AnaliticCurve& curve ) +{ + myAutoAssign = curve.isAutoAssign(); + myColor = curve.getColor(); + myMarker = curve.getMarker(); + myMarkerSize = curve.getMarkerSize(); + myLine = curve.getLine(); + myLineWidth = curve.getLineWidth(); + myRangeBegin = curve.getRangeBegin(); + myRangeEnd = curve.getRangeEnd(); + myNbIntervals= curve.getNbIntervals(); + myPoints = curve.myPoints; + myAction = curve.getAction(); + myName = curve.getName(); + myExpression = curve.getExpression(); + myState = curve.state(); + myCurve = curve.myCurve; + myActive = curve.isActive(); +} + +/*! + operator=. Makes deep copy of data +*/ +Plot2d_AnaliticCurve& Plot2d_AnaliticCurve::operator=( const Plot2d_AnaliticCurve& curve ) +{ + myAutoAssign = curve.isAutoAssign(); + myColor = curve.getColor(); + myMarker = curve.getMarker(); + myMarkerSize = curve.getMarkerSize(); + myLine = curve.getLine(); + myLineWidth = curve.getLineWidth(); + myRangeBegin = curve.getRangeBegin(); + myRangeEnd = curve.getRangeEnd(); + myNbIntervals= curve.getNbIntervals(); + myPoints = curve.myPoints; + myAction = curve.getAction(); + myName = curve.getName(); + myExpression = curve.getExpression(); + myState = curve.state(); + myCurve = curve.myCurve; + myActive = curve.isActive(); + return *this; +} + +/*! + Create plot object for the curve +*/ +QwtPlotItem* Plot2d_AnaliticCurve::plotItem() +{ + if(!myCurve) { + myCurve = new Plot2d_QwtPlotCurve(QString("")); + updatePlotItem(); + } + return myCurve; +} + +/*! + Auto fill parameters of object by plot view +*/ +void Plot2d_AnaliticCurve::autoFill( const QwtPlot* thePlot ) +{ + QwtSymbol::Style typeMarker; + QColor color; + Qt::PenStyle typeLine; + Plot2d::getNextMarker( QwtPlotItem::Rtti_PlotCurve, thePlot, typeMarker, color, typeLine ); + + setColor( color ); + setLine( Plot2d::qwt2plotLine( typeLine )); + setLineWidth(1); + setMarker( Plot2d::qwt2plotMarker( typeMarker ) ); +} + +/*! + Updates curve fields +*/ +void Plot2d_AnaliticCurve::updatePlotItem() +{ + if ( !myCurve ) + return; + + Plot2d_QwtPlotCurve* aCurve = dynamic_cast(myCurve); + + if(!aCurve) + return; + + QwtPlot* aPlot = aCurve->plot(); + if ( aPlot ) { + aCurve->detach(); + aCurve->attach( aPlot ); + } + + Qt::PenStyle ps = Plot2d::plot2qwtLine( getLine() ); + QwtSymbol::Style ms = Plot2d::plot2qwtMarker( getMarker() ); + + aCurve->setPen( QPen(getColor() , getLineWidth(), ps ) ); + + aCurve->setSymbol( QwtSymbol( ms, QBrush( getColor() ), + QPen( getColor()), + QSize( getMarkerSize() , getMarkerSize() ) ) ); + double *x, *y; + long nb = getData( &x, &y ); + aCurve->setData( x, y, nb ); + aCurve->setTitle(getName()); +} + + +/*! + Calculate the curve points. +*/ +void Plot2d_AnaliticCurve::calculate() { + if( state() == Plot2d_AnaliticCurve::StateOk ) + return; + + if(myRangeBegin > myRangeEnd) + return; + + Plot2d_AnaliticParcer* parcer = Plot2d_AnaliticParcer::parcer(); + double* x = 0; + double* y = 0; + int nb = parcer->calculate(getExpression(), getRangeBegin(), getRangeEnd(), + getNbIntervals(),&x,&y); + if( nb > 0 ) { + myPoints.clear(); + for( int i = 0; i < nb; i++ ) { + Plot2d_Point pnt(x[i], y[i]); + myPoints.append(pnt); + } + delete x; + delete y; + myState = Plot2d_AnaliticCurve::StateOk; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets object's data +*/ +long Plot2d_AnaliticCurve::getData( double** theX, double** theY ) const +{ + int aNPoints = getNbIntervals() + 1; + *theX = new double[aNPoints]; + *theY = new double[aNPoints]; + for (int i = 0; i < aNPoints; i++) { + (*theX)[i] = myPoints[i].x; + (*theY)[i] = myPoints[i].y; + } + return aNPoints; +} + +/*! + Sets curves's AutoAssign flag - in this case attributes will be set automatically +*/ +void Plot2d_AnaliticCurve::setAutoAssign( bool on ) +{ + if( myAutoAssign != on ) { + myAutoAssign = on; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets curve's AutoAssign flag state +*/ +bool Plot2d_AnaliticCurve::isAutoAssign() const +{ + return myAutoAssign; +} + +/*! + Sets curve's color. +*/ +void Plot2d_AnaliticCurve::setColor( const QColor& color ) +{ + if(myColor != color) { + myColor = color; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets curve's color +*/ +QColor Plot2d_AnaliticCurve::getColor() const +{ + return myColor; +} + + +/*! + Sets marker type ( and resets AutoAssign flag ) +*/ +void Plot2d_AnaliticCurve::setMarker( Plot2d::MarkerType marker ) +{ + if(myMarker != marker) { + myMarker = marker; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets marker type +*/ +Plot2d::MarkerType Plot2d_AnaliticCurve::getMarker() const +{ + return myMarker; +} + +/*! + Sets new marker size +*/ +void Plot2d_AnaliticCurve::setMarkerSize( const int theSize ) +{ + if( myMarkerSize != theSize ) { + myMarkerSize = theSize < 0 ? 0 : theSize; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets marker size +*/ +int Plot2d_AnaliticCurve::getMarkerSize() const +{ + return myMarkerSize; +} + +/*! + Sets line type +*/ +void Plot2d_AnaliticCurve::setLine( Plot2d::LineType line ) +{ + if(myLine != line) { + myLine = line; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets line type +*/ +Plot2d::LineType Plot2d_AnaliticCurve::getLine() const +{ + return myLine; +} + + +/*! + Sets line width +*/ +void Plot2d_AnaliticCurve::setLineWidth( const int lineWidth ) +{ + if( myLineWidth != lineWidth ) { + myLineWidth = lineWidth < 0 ? 0 : lineWidth; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets line width +*/ +int Plot2d_AnaliticCurve::getLineWidth() const +{ + return myLineWidth; +} + +/*! + Sets number of points +*/ +void Plot2d_AnaliticCurve::setNbIntervals( const long nb ) +{ + if( myNbIntervals != nb ) { + myNbIntervals = nb < 1 ? 1 : nb; + myState = Plot2d_AnaliticCurve::StateNeedUpdate; + } +} + +/*! + Gets number of points +*/ +long Plot2d_AnaliticCurve::getNbIntervals() const +{ + return myNbIntervals; +} + +/*! + Sets X coordinate of the first curve points +*/ +void Plot2d_AnaliticCurve::setRangeBegin( const double coord) { + if( myRangeBegin != coord ) { + myRangeBegin = coord; + myState = Plot2d_AnaliticCurve::StateNeedUpdate; + } +} + +/*! + Gets X coordinate of the first curve points +*/ +double Plot2d_AnaliticCurve::getRangeBegin() const { + return myRangeBegin; +} + +/*! + Sets X coordinate of the last curve points +*/ +void Plot2d_AnaliticCurve::setRangeEnd( const double coord) { + if( myRangeEnd != coord ) { + myRangeEnd = coord; + myState = Plot2d_AnaliticCurve::StateNeedUpdate; + } +} + +/*! + Gets X coordinate of the last curve points +*/ +double Plot2d_AnaliticCurve::getRangeEnd() const { + return myRangeEnd; +} + +/*! + Sets the curve expression. +*/ +void Plot2d_AnaliticCurve::setExpression( const QString& expr ) { + if( myExpression != expr ) { + myExpression = expr; + myState = Plot2d_AnaliticCurve::StateNeedUpdate; + } +} + +/*! + Gets the curve expression. +*/ +QString Plot2d_AnaliticCurve::getExpression() const { + return myExpression; +} + +/*! + Sets the curve name. +*/ +void Plot2d_AnaliticCurve::setName( const QString& name ) { + if( myName != name ) { + myName = name; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets the curve name. +*/ +QString Plot2d_AnaliticCurve::getName() const { + return myName; +} + + +/*! + Sets the curve action. +*/ +void Plot2d_AnaliticCurve::setAction(const int act) { + if( act == Plot2d_AnaliticCurve::ActNothing ) { + myAction = act; + return; + } + + if(myAction != Plot2d_AnaliticCurve::ActAddInView && + myAction != Plot2d_AnaliticCurve::ActRemoveFromView) { + myAction = act; + } +} + +/*! + Gets the curve action. +*/ +int Plot2d_AnaliticCurve::getAction() const { + return myAction; +} + +/*! + Gets the curve state. +*/ +int Plot2d_AnaliticCurve::state() const { + return myState; +} + +/*! + Sets the curve active status. +*/ +void Plot2d_AnaliticCurve::setActive(const bool on) { + if( myActive != on ) { + myActive = on; + setAction(Plot2d_AnaliticCurve::ActUpdateInView); + } +} + +/*! + Gets the curve active status. +*/ +bool Plot2d_AnaliticCurve::isActive() const { + return myActive; +} diff --git a/src/Plot2d/Plot2d_AnaliticCurve.h b/src/Plot2d/Plot2d_AnaliticCurve.h new file mode 100644 index 000000000..2cf9914b8 --- /dev/null +++ b/src/Plot2d/Plot2d_AnaliticCurve.h @@ -0,0 +1,140 @@ +// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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. +// +// 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 : Plot2d_AnaliticCurve.h +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. (roman.nikolaev@opencascade.com) + +#ifndef PLOT2D_ANALITIC_CURVE_H +#define PLOT2D_ANALITIC_CURVE_H + +#include "Plot2d.h" + + +class QwtPlot; +class QwtPlotItem; + + +class PLOT2D_EXPORT Plot2d_AnaliticCurve +{ +public: + + /* + Action enumeration. + */ + enum { + ActAddInView = 0, //! Add curve in view + ActRemoveFromView, //! Remove curve from view + ActUpdateInView, //! Update curve in view + ActNothing //! Do nothing + }; + + /* + State enumeration. + */ + enum { + StateOk = 0, + StateNeedUpdate + }; + + Plot2d_AnaliticCurve(); + Plot2d_AnaliticCurve( const Plot2d_AnaliticCurve& ); + Plot2d_AnaliticCurve& operator= ( const Plot2d_AnaliticCurve& ); + + virtual ~Plot2d_AnaliticCurve(); + + virtual QwtPlotItem* plotItem(); + virtual void autoFill( const QwtPlot* ); + virtual void updatePlotItem(); + + virtual void calculate(); + + long getData( double** , double** ) const; + + + void setAutoAssign( bool ); + bool isAutoAssign( ) const; + + void setColor( const QColor& ); + QColor getColor() const; + + void setMarker( Plot2d::MarkerType ); + Plot2d::MarkerType getMarker() const; + + void setMarkerSize( const int ); + int getMarkerSize() const; + + void setLine( Plot2d::LineType ); + Plot2d::LineType getLine() const; + + void setLineWidth( const int ); + int getLineWidth() const; + + void setNbIntervals( const long ); + long getNbIntervals() const; + + void setRangeBegin( const double ); + double getRangeBegin() const; + + void setRangeEnd( const double ); + double getRangeEnd() const; + + void setExpression( const QString& ); + QString getExpression() const; + + void setName( const QString& ); + QString getName() const; + + void setActive(const bool); + bool isActive() const; + + void setAction(const int); + int getAction() const; + int state() const; + + +protected: + + bool myAutoAssign; + QColor myColor; + Plot2d::MarkerType myMarker; + int myMarkerSize; + Plot2d::LineType myLine; + int myLineWidth; + long myNbIntervals; + pointList myPoints; + double myRangeBegin; + double myRangeEnd; + QString myExpression; + QString myName; + int myAction; + int myState; + QwtPlotItem* myCurve; + bool myActive; + +private: + static int myNbCurves; + +}; + +typedef QList AnaliticCurveList; + +#endif //PLOT2D_ANALITIC_CURVE_H + diff --git a/src/Plot2d/Plot2d_AnaliticCurveDlg.cxx b/src/Plot2d/Plot2d_AnaliticCurveDlg.cxx new file mode 100644 index 000000000..62db6b8fa --- /dev/null +++ b/src/Plot2d/Plot2d_AnaliticCurveDlg.cxx @@ -0,0 +1,510 @@ +// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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. +// +// 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 : Plot2d_AnaliticCurveDlg.cxx +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. (roman.nikolaev@opencascade.com) + +//Local includes +#include "Plot2d_AnaliticCurveDlg.h" +#include "Plot2d_AnaliticCurve.h" +#include "Plot2d_ViewFrame.h" + +//Qtx includes +#include +#include + +//SUIT includes +#include + +//Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//qwt includes +#include +#include + + +//debug +#include + +// Controls +#define MIN 0 +#define MAX 10000 +#define STEP 1 +#define MAX_LINE_WIDTH 10 + + +/*! + Constructor +*/ +Plot2d_AnaliticCurveDlg::Plot2d_AnaliticCurveDlg( QWidget* parent, QwtPlot* plot ) + : QDialog( parent , Qt::WindowTitleHint | Qt::WindowSystemMenuHint ), + myPlot(plot) +{ + setWindowTitle( tr( "ANALITIC_CURVE_TLT" ) ); + QGridLayout* mainLayout = new QGridLayout(this); + + //Curves list widget + myCurvesList = new QListWidget( this ); + myCurvesList->setSelectionMode(QAbstractItemView::SingleSelection); + myCurvesList->setMaximumSize(QSize(150, 16777215)); + + //Curve parameters group box + myCurveParams = new QGroupBox( tr( "AC_CURVE_PARAMS" ), this ); + QGridLayout* paramsLayout = new QGridLayout( myCurveParams ); + + QLabel* formulaLabel = new QLabel( tr( "AC_FORMULA" ), myCurveParams ); + myFormula = new QLineEdit( myCurveParams ); + + QLabel* nbIntervalsLabel = new QLabel( tr( "AC_NB_INTERVALS" ), myCurveParams ); + myNbIntervals = new QtxIntSpinBox( 1, MAX, STEP, myCurveParams ); + + paramsLayout->addWidget( formulaLabel, 0, 0, 1, 1 ); + paramsLayout->addWidget( myFormula, 0, 1, 1, 1 ); + paramsLayout->addWidget( nbIntervalsLabel, 1, 0, 1, 1 ); + paramsLayout->addWidget( myNbIntervals, 1, 1, 1, 1 ); + + //Curve properties group box + myCurveProps = new QGroupBox( tr( "AC_CURVE_PROPS" ), this ); + QGridLayout* propsLayout = new QGridLayout( myCurveProps ); + + myAutoAssign = new QCheckBox( tr( "AC_AUTO_ASSIGN" ), myCurveProps ); + + QLabel* markerLabel = new QLabel( tr( "AC_MARKER_TYPE" ), myCurveProps ); + myMarkerType = new QComboBox( myCurveProps ); + + + QLabel* lineTypeLabel = new QLabel( tr( "AC_LINE_TYPE" ), myCurveProps ); + myLineType = new QComboBox( myCurveProps ); + + + QLabel* lineWidthLabel = new QLabel( tr( "AC_LINE_WIDTH" ), myCurveProps ); + myLineWidth = new QtxIntSpinBox( MIN, MAX_LINE_WIDTH, STEP, myCurveProps ); + + + + QLabel* colorLabel = new QLabel( tr("AC_CURVE_COLOR"), myCurveProps ); + myColor = new QtxColorButton(myCurveProps); + + propsLayout->addWidget( myAutoAssign, 0, 0, 1, 1 ); + propsLayout->addWidget( markerLabel, 1, 0, 1, 1 ); + propsLayout->addWidget( myMarkerType, 1, 1, 1, 1 ); + propsLayout->addWidget( lineTypeLabel, 2, 0, 1, 1 ); + propsLayout->addWidget( myLineType, 2, 1, 1, 1 ); + propsLayout->addWidget( lineWidthLabel, 3, 0, 1, 1 ); + propsLayout->addWidget( myLineWidth, 3, 1, 1, 1 ); + propsLayout->addWidget( colorLabel, 4, 0, 1, 1 ); + propsLayout->addWidget( myColor, 4, 1, 1, 1 ); + + propsLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ), 3, 0, 1, 1 ); + propsLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ), 3, 1, 1, 1 ); + + + //Add && Remove buttons + QPushButton* addButton = new QPushButton( tr("AC_ADD_BTN"), this ); + QPushButton* updateButton = new QPushButton( tr("AC_UPD_BTN"), this ); + QPushButton* removeButton = new QPushButton( tr("AC_REM_BTN"), this ); + + //Ok && Close buttons + QFrame* frame = new QFrame( this ); + QHBoxLayout* horizontalLayout = new QHBoxLayout(frame); + + frame->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum ) ); + frame->setFrameShape( QFrame::StyledPanel ); + frame->setFrameShadow( QFrame::Raised ); + + QPushButton* closeButton = new QPushButton( tr( "AC_CLOSE_BTN" ), frame ); + + + horizontalLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ) ); + horizontalLayout->addWidget( closeButton ); + horizontalLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ) ); + + + mainLayout->addWidget( myCurvesList, 0, 0, 3, 1 ); + mainLayout->addWidget( myCurveParams, 0, 1, 1, 5 ); + mainLayout->addWidget( myCurveProps, 1, 1, 1, 5 ); + mainLayout->addWidget( addButton, 2, 1, 1, 1 ); + mainLayout->addItem( new QSpacerItem( 13, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ), 2, 2, 1, 1 ); + mainLayout->addWidget( removeButton, 2, 3, 1, 1 ); + mainLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum), 2, 4, 1, 1); + mainLayout->addWidget( updateButton, 2, 5, 1, 1); + mainLayout->addWidget(frame, 3, 0, 1, 6); + + //Fill combobox + QColor cl = myMarkerType->palette().color( QPalette::Text ); + QSize sz(16, 16); + myMarkerType->setIconSize(sz); + + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::None ), tr( "NONE_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::Circle ), tr( "CIRCLE_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::Rectangle ), tr( "RECTANGLE_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::Diamond ), tr( "DIAMOND_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::DTriangle ), tr( "DTRIANGLE_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::UTriangle ), tr( "UTRIANGLE_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::LTriangle ), tr( "LTRIANGLE_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::RTriangle ), tr( "RTRIANGLE_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::Cross ), tr( "CROSS_MARKER_LBL" ) ); + myMarkerType->addItem( Plot2d::markerIcon( sz, cl, Plot2d::XCross ), tr( "XCROSS_MARKER_LBL" ) ); + + cl = myLineType->palette().color( QPalette::Text ); + QSize lsz( 40, 16 ); + myLineType->setIconSize( lsz ); + + myLineType->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::NoPen ), tr( "NONE_LINE_LBL" ) ); + myLineType->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::Solid ), tr( "SOLID_LINE_LBL" ) ); + myLineType->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::Dash ), tr( "DASH_LINE_LBL" ) ); + myLineType->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::Dot ), tr( "DOT_LINE_LBL" ) ); + myLineType->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::DashDot ), tr( "DASHDOT_LINE_LBL" ) ); + myLineType->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::DashDotDot ), tr( "DAHSDOTDOT_LINE_LBL" ) ); + + //Connections + connect( closeButton, SIGNAL( clicked() ), this, SLOT( reject() ) ); + connect( addButton, SIGNAL( clicked() ), this, SLOT( onAddCurve() ) ); + connect( myAutoAssign, SIGNAL( stateChanged(int) ), this, SLOT( onAutoAssign(int) ) ); + connect( removeButton, SIGNAL( clicked()), this, SLOT(onRemoveCurve())); + connect( updateButton, SIGNAL( clicked()), this, SLOT(onUpdateCurve())); + connectSelectionChanged(); + + //Default values + myNbIntervals->setValue(100); + checkState(); +} + +/*! + Destructor +*/ +Plot2d_AnaliticCurveDlg::~Plot2d_AnaliticCurveDlg() +{ +} + +void Plot2d_AnaliticCurveDlg::setCurveList( AnaliticCurveList& theList ) { + AnaliticCurveList::iterator it = theList.begin(); + QListWidgetItem* newItem = 0; + Plot2d_AnaliticCurve* curve; + for( ;it != theList.end(); it++ ) { + curve = *it; + if(!curve) continue; + newItem = new QListWidgetItem(curve->getName()); + newItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); + newItem->setCheckState(Qt::Checked); + myCurvesList->addItem(newItem); + newItem->setCheckState(curve->isActive() ? Qt::Checked : Qt::Unchecked ); + QVariant var; + var.setValue((void*)curve); + newItem->setData(Qt::UserRole,var); + } + setCurrentCurve(newItem); + checkState(); +} + +/*! + Return list of the curves. +*/ +/*void Plot2d_AnaliticCurveDlg::reject() { + if( myCurvesList->count() == 0 ) + QDialog::reject(); + + QList items = myCurvesList->selectedItems(); + QListWidgetItem* item = items.size() > 0 ? items[0] : 0; + if(item) { + if(!checkItem(item)) { + showErrorMsg(); + return; + } else { + updateInView(myCurrentItem); + QDialog::reject(); + } + } +} +*/ + +/*! + Update curve parameters and curve properties. +*/ +void Plot2d_AnaliticCurveDlg::setCurrentCurve(QListWidgetItem* item, bool select) { + if(item) { + QVariant var = item->data(Qt::UserRole); + Plot2d_AnaliticCurve* curve = (Plot2d_AnaliticCurve*)var.value(); + if(curve) { + myFormula->setText( curve->getExpression() ); + myNbIntervals->setValue( curve->getNbIntervals() ); + myAutoAssign->setChecked( curve->isAutoAssign() ); + if(!curve->isAutoAssign()) { + myMarkerType->setCurrentIndex( curve->getMarker() ); + myLineType->setCurrentIndex( curve->getLine() ); + myLineWidth->setValue( curve->getLineWidth() ); + myColor->setColor( curve->getColor() ); + } else { + myMarkerType->setCurrentIndex( 0 ); + myLineType->setCurrentIndex( 0 ); + myLineWidth->setValue( 1 ); + myColor->setColor( QColor() ); + } + myCurrentItem = item; + if( select ) { + disconnectSelectionChanged(); + item->setSelected(true); + connectSelectionChanged(); + } + } + } +} + +/*! + Enable/Disable "curve parameters" and "curve properties" widgets. +*/ +void Plot2d_AnaliticCurveDlg::checkState() { + bool isEnable = myCurvesList->count() > 0; + myCurveParams->setEnabled(isEnable); + myCurveProps->setEnabled(isEnable); +} +/*! + Show error message + */ +void Plot2d_AnaliticCurveDlg::showErrorMsg() { + SUIT_MessageBox::critical( this, tr( "ERR_ERROR" ), tr( "AC_CANT_CALCULATE" ) ); +} + +/*! + Check the curve attached to the item. +*/ +bool Plot2d_AnaliticCurveDlg::checkItem(const QListWidgetItem* item) { + bool res = false; + if( !myFormula->text().isEmpty() && item ) { + QVariant var = item->data(Qt::UserRole); + Plot2d_AnaliticCurve* curve = (Plot2d_AnaliticCurve*)var.value(); + + if( curve ) { + curve->setExpression(myFormula->text()); + QwtScaleDiv* div = myPlot->axisScaleDiv(QwtPlot::xBottom); + curve->setRangeBegin(div->lowerBound()); + curve->setRangeEnd(div->upperBound()); + curve->setNbIntervals(myNbIntervals->value()); + curve->calculate(); + if( curve->state() == Plot2d_AnaliticCurve::StateOk ) + res = true; + } + } + return res; +} +/*! + Store properties of the curve attached to the item from "Curve properties" widget. +*/ +void Plot2d_AnaliticCurveDlg::storeProps( const QListWidgetItem* item) { + if( item ) { + QVariant var = item->data(Qt::UserRole); + Plot2d_AnaliticCurve* curve = (Plot2d_AnaliticCurve*)var.value(); + if(curve) { + curve->setAutoAssign(myAutoAssign->isChecked()); + curve->setName(item->text()); + curve->setActive(item->checkState() == Qt::Checked); + if(!myAutoAssign->isChecked()) { + curve->setMarker((Plot2d::MarkerType)myMarkerType->currentIndex()); + curve->setLine((Plot2d::LineType)myLineType->currentIndex()); + curve->setLineWidth(myLineWidth->value()); + curve->setColor(myColor->color()); + } + } + } +} + +/*! + Connect currentItemChanged signal. +*/ +void Plot2d_AnaliticCurveDlg::connectSelectionChanged() { + connect( myCurvesList, SIGNAL( itemSelectionChanged ()), + this, SLOT( onCurveSelectionChanged() ) ); +} + +/*! + Disconnect currentItemChanged signal. +*/ +void Plot2d_AnaliticCurveDlg::disconnectSelectionChanged() { + disconnect( myCurvesList, SIGNAL( itemSelectionChanged ()), + this, SLOT( onCurveSelectionChanged() ) ); +} + +/*! + Private slot. Called then "Add curve" button is clicked +*/ +void Plot2d_AnaliticCurveDlg::onAddCurve() { + /* QList items = myCurvesList->selectedItems(); + QListWidgetItem* item = items.size() > 0 ? items[0] : 0; + if(item) { + if(!checkItem(item)) { + showErrorMsg(); + return; + } + else { + updateInView(item); + } + } + */ + + Plot2d_AnaliticCurve* curve = new Plot2d_AnaliticCurve(); + QListWidgetItem* newItem = new QListWidgetItem(curve->getName()); + newItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); + newItem->setCheckState(Qt::Checked); + myCurvesList->addItem(newItem); + QVariant var; + var.setValue((void*)curve); + newItem->setData(Qt::UserRole,var); + + bool autoFlag = curve->isAutoAssign(); + if(autoFlag) { + curve->autoFill(myPlot); + } + + setCurrentCurve(newItem); + checkState(); +} + +/*! + Private slot. Called then "Remove curve" button is clicked +*/ +void Plot2d_AnaliticCurveDlg::onRemoveCurve() { + disconnectSelectionChanged(); + QList items = myCurvesList->selectedItems(); + QListWidgetItem* selected = items.size() > 0 ? items[0] : 0; + int row = myCurvesList->row(selected); + int prevRow = (row == 0) ? 1 : row - 1; + if( selected ) { + QVariant var = selected->data(Qt::UserRole); + Plot2d_AnaliticCurve* curve = (Plot2d_AnaliticCurve*)var.value(); + if( curve ) { + if( curve->getAction() == Plot2d_AnaliticCurve::ActAddInView ) { + delete curve; + } else { + curve->setAction(Plot2d_AnaliticCurve::ActRemoveFromView); + updateInView(selected); + } + } + } + + if( prevRow >= 0 && prevRow < myCurvesList->count() ) { + myCurvesList->item(prevRow)->setSelected(true); + setCurrentCurve(myCurvesList->item(prevRow)); + } + + selected = myCurvesList->takeItem(row); + delete selected; + connectSelectionChanged(); + checkState(); +} + +/*! + Private slot. Called then selection in the curve list is changed. +*/ +void Plot2d_AnaliticCurveDlg::onCurveSelectionChanged() { + + QList items = myCurvesList->selectedItems(); + QListWidgetItem* selected = items.size() > 0 ? items[0] : 0; + + /* bool ok = myCurrentItem ? checkItem(myCurrentItem) : true; + + if(!ok) { + showErrorMsg(); + disconnectSelectionChanged(); + + if( selected ) + selected->setSelected(false); + + if( myCurrentItem ) { + myCurrentItem->setSelected(true); + myCurvesList->setCurrentItem(myCurrentItem); + } + + connectSelectionChanged(); + + } else { + updateInView(myCurrentItem); + */ + + if(selected) { + setCurrentCurve(selected, false); + } + //} +} + +/*! + Private slot. Called then "Auto assign" checkbox is clicked +*/ +void Plot2d_AnaliticCurveDlg::onAutoAssign(int state){ + bool flag = !state; + myMarkerType->setEnabled(flag); + myLineType->setEnabled(flag); + myLineWidth->setEnabled(flag); + myColor->setEnabled(flag); + + QList items = myCurvesList->selectedItems(); + QListWidgetItem* selected = items.size() > 0 ? items[0] : 0; + if(selected) { + QVariant var = selected->data(Qt::UserRole); + Plot2d_AnaliticCurve* curve = (Plot2d_AnaliticCurve*)var.value(); + if(curve) { + curve->setAutoAssign(state); + } + setCurrentCurve(selected); + } +} + +/*! + Update curve in the view +*/ +void Plot2d_AnaliticCurveDlg::updateInView( QListWidgetItem* item ) { + storeProps(item); + Plot2d_ViewFrame* fr = qobject_cast(parent()); + QVariant var = item->data(Qt::UserRole); + + Plot2d_AnaliticCurve* curve = (Plot2d_AnaliticCurve*)var.value(); + if( fr && curve ) { + fr->updateAnaliticCurve(curve, true); + } +} + +/*! + Private slot. Called then "Update Curve" checkbox is clicked +*/ +void Plot2d_AnaliticCurveDlg::onUpdateCurve() { + QList items = myCurvesList->selectedItems(); + int sz = items.size(); + if( sz > 0 ) { + QListWidgetItem* selected = items[0]; + + bool ok = myCurrentItem ? checkItem(myCurrentItem) : false; + if(!ok) { + showErrorMsg(); + return; + } else { + updateInView(selected); + } + } +} diff --git a/src/Plot2d/Plot2d_AnaliticCurveDlg.h b/src/Plot2d/Plot2d_AnaliticCurveDlg.h new file mode 100644 index 000000000..ca26a160d --- /dev/null +++ b/src/Plot2d/Plot2d_AnaliticCurveDlg.h @@ -0,0 +1,94 @@ +// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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. +// +// 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 : Plot2d_AnaliticCurveDlg.h +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. (roman.nikolaev@opencascade.com) + + +#ifndef PLOT2D_ANALITIC_CURVE_DLG_H +#define PLOT2D_ANALITIC_CURVE_DLG_H + +#include "Plot2d.h" +#include "Plot2d_AnaliticCurve.h" + +#include + + +class QListWidget; +class QListWidgetItem; +class QGroupBox; +class QLineEdit; +class QCheckBox; +class QComboBox; + +class QtxIntSpinBox; +class QtxColorButton; +class QwtPlot; + +class Plot2d_AnaliticCurve; +class Plot2d_ViewFrame; + +class PLOT2D_EXPORT Plot2d_AnaliticCurveDlg : public QDialog { + Q_OBJECT +public: + + Plot2d_AnaliticCurveDlg( QWidget* parent, QwtPlot* plot ); + ~Plot2d_AnaliticCurveDlg(); + + AnaliticCurveList getCurveList(); + void setCurveList( AnaliticCurveList& ); + +private: + void setCurrentCurve( QListWidgetItem* , bool = true); + void checkState(); + void showErrorMsg(); + bool checkItem(const QListWidgetItem* ); + void storeProps( const QListWidgetItem* ); + void connectSelectionChanged(); + void disconnectSelectionChanged(); + void updateInView(QListWidgetItem*); + +private slots: + void onAddCurve(); + void onRemoveCurve(); + void onAutoAssign(int); + void onUpdateCurve(); + void onCurveSelectionChanged( ); + +private: + QListWidget* myCurvesList; + + QGroupBox* myCurveParams; + QLineEdit* myFormula; + QtxIntSpinBox* myNbIntervals; + + QGroupBox* myCurveProps; + QCheckBox* myAutoAssign; + QComboBox* myMarkerType; + QComboBox* myLineType; + QtxIntSpinBox* myLineWidth; + QtxColorButton* myColor; + + QwtPlot* myPlot; + QListWidgetItem* myCurrentItem; +}; + +#endif //PLOT2D_ANALITIC_CURVE_DLG_H diff --git a/src/Plot2d/Plot2d_AnaliticParcer.cxx b/src/Plot2d/Plot2d_AnaliticParcer.cxx new file mode 100644 index 000000000..75ebf76f1 --- /dev/null +++ b/src/Plot2d/Plot2d_AnaliticParcer.cxx @@ -0,0 +1,272 @@ +// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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. +// +// 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 : Plot2d_AnaliticParcer.cxx +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. (roman.nikolaev@opencascade.com) +#include "Plot2d_AnaliticParcer.h" +#include + + +/* ================================== + * =========== PYTHON ============== + * ==================================*/ + +typedef struct { + PyObject_HEAD + int softspace; + std::string *out; + } PyStdOut; + +static void +PyStdOut_dealloc(PyStdOut *self) +{ + PyObject_Del(self); +} + +static PyObject * +PyStdOut_write(PyStdOut *self, PyObject *args) +{ + char *c; + int l; + if (!PyArg_ParseTuple(args, "t#:write",&c, &l)) + return NULL; + + //std::cerr << c ; + *(self->out)=*(self->out)+c; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef PyStdOut_methods[] = { + {"write", (PyCFunction)PyStdOut_write, METH_VARARGS, + PyDoc_STR("write(string) -> None")}, + {NULL, NULL} /* sentinel */ +}; + +static PyMemberDef PyStdOut_memberlist[] = { + {(char*)"softspace", T_INT, offsetof(PyStdOut, softspace), 0, + (char*)"flag indicating that a space needs to be printed; used by print"}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject PyStdOut_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "PyOut", /*tp_name*/ + sizeof(PyStdOut), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyStdOut_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr, /*tp_getattro*/ + /* softspace is writable: we must supply tp_setattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + PyStdOut_methods, /*tp_methods*/ + PyStdOut_memberlist, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +PyObject * newPyStdOut( std::string& out ) +{ + PyStdOut *self; + self = PyObject_New(PyStdOut, &PyStdOut_Type); + if (self == NULL) + return NULL; + self->softspace = 0; + self->out=&out; + return (PyObject*)self; +} + + +////////////////////////END PYTHON/////////////////////////// + + +//! The only one instance of parcer +Plot2d_AnaliticParcer* Plot2d_AnaliticParcer::myParcer = 0; + +//Define the script +QString Plot2d_AnaliticParcer::myScript = QString(""); + +/*! + \brief Return the only instance of the Plot2d_AnaliticParcer + \return instance of the Plot2d_AnaliticParcer +*/ +Plot2d_AnaliticParcer* Plot2d_AnaliticParcer::parcer() +{ + if ( !myParcer ) + myParcer = new Plot2d_AnaliticParcer(); + return myParcer; +} + +/*! + \brief Constructor. + + Construct the parcer and initialize python interpritator. +*/ +Plot2d_AnaliticParcer::Plot2d_AnaliticParcer() +{ + /* Initialize the Python interpreter */ + if (Py_IsInitialized()) { + PyGILState_STATE gstate = PyGILState_Ensure(); + myMainMod = PyImport_AddModule("__main__"); + myMainDict = PyModule_GetDict(myMainMod); + PyGILState_Release(gstate); + initScript(); + } +} + +int Plot2d_AnaliticParcer::calculate( const QString& theExpr, + const double theMin, + const double theMax, + const int theNbStep, + double** theX, + double** theY) { + + QString aPyScript = myScript; + aPyScript = aPyScript.arg(theExpr); + int result = -1; + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject* obj = PyRun_String(qPrintable(aPyScript), Py_file_input, myMainDict, NULL); + + if(obj == NULL) { + PyErr_Print(); + PyGILState_Release(gstate); + return result; + + } else { + Py_DECREF(obj); + } + + PyObject* func = NULL; + PyObject* f_y = NULL; + + if(PyObject_HasAttrString(myMainMod, "Y")) { + f_y = PyObject_GetAttrString(myMainMod, "Y"); + } + + if(PyObject_HasAttrString(myMainMod, "coordCalculator")) { + func = PyObject_GetAttrString(myMainMod, "coordCalculator"); + } + + PyObject* new_stderr = NULL; + + if( f_y == NULL || func == NULL ) { + fflush(stderr); + std::string err_description=""; + new_stderr = newPyStdOut(err_description); + PySys_SetObject((char*)"stderr", new_stderr); + PyErr_Print(); + PySys_SetObject((char*)"stderr", PySys_GetObject((char*)"__stderr__")); + Py_DECREF(new_stderr); + PyGILState_Release(gstate); + return result; + } + + PyObject* coords; + coords = PyObject_CallFunction(func,(char*)"(d, d, i)", theMin, theMax, theNbStep ); + + new_stderr = NULL; + + if (coords == NULL){ + fflush(stderr); + std::string err_description=""; + new_stderr = newPyStdOut(err_description); + PySys_SetObject((char*)"stderr", new_stderr); + PyErr_Print(); + PySys_SetObject((char*)"stderr", PySys_GetObject((char*)"__stderr__")); + Py_DECREF(new_stderr); + PyGILState_Release(gstate); + return result; + } + + Py_ssize_t size = PyList_Size( coords ); + if( size <= 0 ) { + Py_DECREF(coords); + return result; + } + + result = size; + + *theX = new double[size]; + *theY = new double[size]; + + for ( Py_ssize_t i = 0; i< size; ++i ) { + PyObject* coord = PyList_GetItem( coords, i ); + (*theX)[i] = PyFloat_AsDouble(PyList_GetItem(coord, 0)); + (*theY)[i] = PyFloat_AsDouble(PyList_GetItem(coord, 1)); + } + + PyGILState_Release(gstate); + return result; +} + +/*! + \brief Initialize python script. +*/ +void Plot2d_AnaliticParcer::initScript() { + myScript.clear(); + myScript += "from math import * \n"; + myScript += "def Y(x): \n"; + myScript += " return "; + myScript += "%1\n"; + + myScript += "def coordCalculator(xmin, xmax, nstep): \n"; + myScript += " coords = [] \n"; + myScript +=" xstep = (xmax - xmin) / nstep \n"; + myScript +=" n = 0 \n"; + myScript +=" while n <= nstep : \n"; + myScript +=" x = xmin + n*xstep \n"; + myScript +=" coords.append([x,Y(x)]) \n"; + myScript +=" n = n+1 \n"; + myScript +=" return coords \n"; +} diff --git a/src/SVTK/SVTK_DialogBase.h b/src/Plot2d/Plot2d_AnaliticParcer.h similarity index 52% rename from src/SVTK/SVTK_DialogBase.h rename to src/Plot2d/Plot2d_AnaliticParcer.h index d5058d65f..3db0cb0bf 100644 --- a/src/SVTK/SVTK_DialogBase.h +++ b/src/Plot2d/Plot2d_AnaliticParcer.h @@ -19,42 +19,34 @@ // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // - -// SALOME VTKViewer : build VTK viewer into Salome desktop -// File : -// Author : -// Module : SALOME -// $Header$ -// -#ifndef SVTK_DIALOGBASE_H -#define SVTK_DIALOGBASE_H - -#include "SVTK.h" - -#include - -class QtxAction; - -class SVTK_EXPORT SVTK_DialogBase : public QDialog -{ - Q_OBJECT; - -public: - SVTK_DialogBase(QtxAction* theAction, - QWidget* theParent, - const char* theName = "", - bool theModal = FALSE, - Qt::WindowFlags theWFalgs = 0); - - ~SVTK_DialogBase(); - -protected slots: - void onParentShow(); - void onParentHide(); - virtual void done( int ); - -protected: - QtxAction* myAction; +// File : Plot2d_AnaliticParcer.h +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. (roman.nikolaev@opencascade.com) + +#ifndef PLOT2D_ANALITIC_PARCER_H +#define PLOT2D_ANALITIC_PARCER_H +#include + +#include "Plot2d.h" + +class PLOT2D_EXPORT Plot2d_AnaliticParcer { +public: + ~Plot2d_AnaliticParcer(); + + + static Plot2d_AnaliticParcer* parcer(); + int calculate( const QString&, const double, + const double, const int, + double**, double**); + +private: + Plot2d_AnaliticParcer(); + void initScript(); + +private: + static Plot2d_AnaliticParcer* myParcer; //!< instance of the parcer + PyObject* myMainMod; //!< main python module + PyObject* myMainDict; //!< main python dictionary + static QString myScript; //!< python script }; -#endif // SVTK_DIALOGBASE_H +#endif //PLOT2D_ANALITIC_PARCER_H diff --git a/src/Plot2d/Plot2d_Curve.cxx b/src/Plot2d/Plot2d_Curve.cxx index c225716a2..ad4b544c9 100755 --- a/src/Plot2d/Plot2d_Curve.cxx +++ b/src/Plot2d/Plot2d_Curve.cxx @@ -28,7 +28,6 @@ const int DEFAULT_LINE_WIDTH = 0; // (default) line width const int DEFAULT_MARKER_SIZE = 9; // default marker size -const int MAX_ATTEMPTS = 10; // max attempts /*! Constructor @@ -103,7 +102,7 @@ void Plot2d_Curve::autoFill( const QwtPlot* thePlot ) QwtSymbol::Style typeMarker; QColor color; Qt::PenStyle typeLine; - getNextMarker( thePlot, typeMarker, color, typeLine ); + Plot2d::getNextMarker( rtti(), thePlot, typeMarker, color, typeLine ); setColor( color ); setLine( Plot2d::qwt2plotLine( typeLine ), DEFAULT_LINE_WIDTH ); @@ -259,57 +258,3 @@ int Plot2d_Curve::getLineWidth() const return myLineWidth; } -/*! - Gets new unique marker for item if possible -*/ -void Plot2d_Curve::getNextMarker( const QwtPlot* thePlot, QwtSymbol::Style& typeMarker, - QColor& color, Qt::PenStyle& typeLine ) -{ - bool bOk = false; - int cnt = 0; - while ( !bOk ) { - int aRed = (int)( 256.0 * rand() / RAND_MAX ); // generate random color - int aGreen = (int)( 256.0 * rand() / RAND_MAX ); // ... - int aBlue = (int)( 256.0 * rand() / RAND_MAX ); // ... - int aMarker = (int)( 9.0 * rand() / RAND_MAX ) + 1;// 9 markers types( not including empty ) - int aLine = (int)( 5.0 * rand() / RAND_MAX ) + 1;// 5 line types ( not including empty ) - - typeMarker = ( QwtSymbol::Style )aMarker; - color = QColor( aRed, aGreen, aBlue ); - typeLine = ( Qt::PenStyle )aLine; - - bOk = ( ++cnt == MAX_ATTEMPTS ) || !existMarker( thePlot, typeMarker, color, typeLine ); - } -} - -/*! - Checks if marker belongs to any enitity -*/ -bool Plot2d_Curve::existMarker( const QwtPlot* thePlot, const QwtSymbol::Style typeMarker, - const QColor& color, const Qt::PenStyle typeLine ) -{ - bool ok = false; - - QColor bgColor = thePlot->palette().color( QPalette::Background ); - if ( closeColors( color, bgColor ) ) { - ok = true; - } - else { - QwtPlotItemList anItems = thePlot->itemList(); - QwtPlotItemIterator anIt = anItems.begin(), aLast = anItems.end(); - QwtPlotItem* anItem; - for ( ; anIt != aLast && !ok; anIt++ ) { - anItem = *anIt; - if ( anItem && anItem->rtti() == rtti() ) { - QwtPlotCurve* crv = dynamic_cast( anItem ); - if ( crv ) { - QwtSymbol::Style aStyle = crv->symbol().style(); - QColor aColor = crv->pen().color(); - Qt::PenStyle aLine = crv->pen().style(); - ok = closeColors( aColor, color ) && aStyle == typeMarker && aLine == typeLine; - } - } - } - } - return ok; -} diff --git a/src/Plot2d/Plot2d_Curve.h b/src/Plot2d/Plot2d_Curve.h index cbb95289a..9517dddc7 100755 --- a/src/Plot2d/Plot2d_Curve.h +++ b/src/Plot2d/Plot2d_Curve.h @@ -59,12 +59,6 @@ public: void setLineWidth( const int ); int getLineWidth() const; -protected: - void getNextMarker( const QwtPlot*, QwtSymbol::Style&, - QColor&, Qt::PenStyle& ); - bool existMarker( const QwtPlot*, const QwtSymbol::Style, - const QColor&, const Qt::PenStyle ); - protected: QColor myColor; Plot2d::MarkerType myMarker; diff --git a/src/Plot2d/Plot2d_Histogram.cxx b/src/Plot2d/Plot2d_Histogram.cxx index f2832fdf1..7bcf09b76 100644 --- a/src/Plot2d/Plot2d_Histogram.cxx +++ b/src/Plot2d/Plot2d_Histogram.cxx @@ -213,7 +213,7 @@ bool Plot2d_Histogram::existColor( const QwtPlot* thePlot, const QColor& theColo bool ok = false; QColor bgColor = thePlot->palette().color( QPalette::Background ); - if ( closeColors( theColor, bgColor ) ) { + if ( Plot2d::closeColors( theColor, bgColor ) ) { ok = true; } else { @@ -226,11 +226,11 @@ bool Plot2d_Histogram::existColor( const QwtPlot* thePlot, const QColor& theColo continue; if ( anItem->rtti() == rtti() ) { Plot2d_HistogramItem* aHItem = dynamic_cast( anItem ); - ok = aHItem && closeColors( theColor, aHItem->color() ); + ok = aHItem && Plot2d::closeColors( theColor, aHItem->color() ); } else if ( anItem->rtti() == QwtPlotItem::Rtti_PlotCurve ) { QwtPlotCurve* aCurve = dynamic_cast( anItem ); - ok = aCurve && closeColors( theColor, aCurve->pen().color() ); + ok = aCurve && Plot2d::closeColors( theColor, aCurve->pen().color() ); } } } diff --git a/src/Plot2d/Plot2d_Object.cxx b/src/Plot2d/Plot2d_Object.cxx index 6f07e7042..3498791bd 100755 --- a/src/Plot2d/Plot2d_Object.cxx +++ b/src/Plot2d/Plot2d_Object.cxx @@ -29,10 +29,6 @@ #include #include -// color tolerance (used to compare color values) -const long COLOR_DISTANCE = 100; - - // Static members QColor Plot2d_Object::mySelectionColor; QColor Plot2d_Object::myHighlightedLegendTextColor; @@ -52,22 +48,6 @@ void Plot2d_Object::initColors() { } } -/*! - Constructor -*/ -Plot2d_Point::Plot2d_Point() - : x( 0. ), y( 0. ) -{ -} - -/*! - Constructor -*/ -Plot2d_Point::Plot2d_Point( double theX, double theY, const QString& theText ) - : x( theX ), y( theY ), text( theText ) -{ -} - /*! Constructor */ @@ -512,24 +492,6 @@ double Plot2d_Object::getMaxY() const return aMaxY; } -/*! - Checks if two colors are close to each other [ static ] - uses COLOR_DISTANCE variable as max tolerance for comparing of colors -*/ - -bool Plot2d_Object::closeColors( const QColor& color1, - const QColor& color2, - int distance ) -{ - long tol = - qAbs( color2.red() - color1.red() ) + - qAbs( color2.green() - color1.green() ) + - qAbs( color2.blue() - color1.blue() ) - - ( distance < 0 ? COLOR_DISTANCE : distance ); - - return tol <= 0; -} - /*! Sets object's selected property */ diff --git a/src/Plot2d/Plot2d_Object.h b/src/Plot2d/Plot2d_Object.h index bc7a5babc..3b7c048f8 100755 --- a/src/Plot2d/Plot2d_Object.h +++ b/src/Plot2d/Plot2d_Object.h @@ -31,16 +31,6 @@ #include #include -struct PLOT2D_EXPORT Plot2d_Point -{ - double x; - double y; - QString text; - Plot2d_Point(); - Plot2d_Point( double theX, double theY, const QString& theText = QString() ); -}; - -typedef QList pointList; class PLOT2D_EXPORT Plot2d_Object { @@ -113,8 +103,6 @@ public: void setSelected(const bool); bool isSelected() const; - - static bool closeColors( const QColor&, const QColor&, int distance = -1 ); static void initColors(); diff --git a/src/Plot2d/Plot2d_SetupCurveDlg.cxx b/src/Plot2d/Plot2d_SetupCurveDlg.cxx index f4b52f65c..d03b57430 100644 --- a/src/Plot2d/Plot2d_SetupCurveDlg.cxx +++ b/src/Plot2d/Plot2d_SetupCurveDlg.cxx @@ -64,7 +64,8 @@ Plot2d_SetupCurveDlg::Plot2d_SetupCurveDlg( QWidget* parent ) myLineCombo = new QComboBox( this ); myLineCombo->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); myLineCombo->setMinimumWidth( MIN_COMBO_WIDTH ); - myLineCombo->setIconSize( QSize( 40, 16 ) ); + QSize lsz( 40, 16 ); + myLineCombo->setIconSize( lsz ); // curve width QLabel* aLineWidthLab = new QLabel( tr( "CURVE_LINE_WIDTH_LAB" ), this ); @@ -80,7 +81,8 @@ Plot2d_SetupCurveDlg::Plot2d_SetupCurveDlg( QWidget* parent ) myMarkerCombo = new QComboBox( this ); myMarkerCombo->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); myMarkerCombo->setMinimumWidth( MIN_COMBO_WIDTH ); - myMarkerCombo->setIconSize( QSize( 16, 16 ) ); + QSize sz(16, 16); + myMarkerCombo->setIconSize(sz); // curve color QLabel* aColorLab = new QLabel( tr( "CURVE_COLOR_LAB" ), this ); @@ -125,23 +127,27 @@ Plot2d_SetupCurveDlg::Plot2d_SetupCurveDlg( QWidget* parent ) topLayout->addLayout( btnLayout, 5, 0, 1, 3 ); // fill then combo boxes - myLineCombo->addItem( lineIcon( Plot2d::NoPen ), tr( "NONE_LINE_LBL" ) ); - myLineCombo->addItem( lineIcon( Plot2d::Solid ), tr( "SOLID_LINE_LBL" ) ); - myLineCombo->addItem( lineIcon( Plot2d::Dash ), tr( "DASH_LINE_LBL" ) ); - myLineCombo->addItem( lineIcon( Plot2d::Dot ), tr( "DOT_LINE_LBL" ) ); - myLineCombo->addItem( lineIcon( Plot2d::DashDot ), tr( "DASHDOT_LINE_LBL" ) ); - myLineCombo->addItem( lineIcon( Plot2d::DashDotDot ), tr( "DAHSDOTDOT_LINE_LBL" ) ); - - myMarkerCombo->addItem( markerIcon( Plot2d::None ), tr( "NONE_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::Circle ), tr( "CIRCLE_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::Rectangle ), tr( "RECTANGLE_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::Diamond ), tr( "DIAMOND_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::DTriangle ), tr( "DTRIANGLE_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::UTriangle ), tr( "UTRIANGLE_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::LTriangle ), tr( "LTRIANGLE_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::RTriangle ), tr( "RTRIANGLE_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::Cross ), tr( "CROSS_MARKER_LBL" ) ); - myMarkerCombo->addItem( markerIcon( Plot2d::XCross ), tr( "XCROSS_MARKER_LBL" ) ); + QColor cl = myLineCombo->palette().color( QPalette::Text ); + + myLineCombo->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::NoPen ), tr( "NONE_LINE_LBL" ) ); + myLineCombo->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::Solid ), tr( "SOLID_LINE_LBL" ) ); + myLineCombo->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::Dash ), tr( "DASH_LINE_LBL" ) ); + myLineCombo->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::Dot ), tr( "DOT_LINE_LBL" ) ); + myLineCombo->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::DashDot ), tr( "DASHDOT_LINE_LBL" ) ); + myLineCombo->addItem( Plot2d::lineIcon( lsz, cl, Plot2d::DashDotDot ), tr( "DAHSDOTDOT_LINE_LBL" ) ); + + cl = myMarkerCombo->palette().color( QPalette::Text ); + + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::None ), tr( "NONE_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::Circle ), tr( "CIRCLE_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::Rectangle ), tr( "RECTANGLE_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::Diamond ), tr( "DIAMOND_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::DTriangle ), tr( "DTRIANGLE_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::UTriangle ), tr( "UTRIANGLE_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::LTriangle ), tr( "LTRIANGLE_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::RTriangle ), tr( "RTRIANGLE_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::Cross ), tr( "CROSS_MARKER_LBL" ) ); + myMarkerCombo->addItem( Plot2d::markerIcon( sz, cl, Plot2d::XCross ), tr( "XCROSS_MARKER_LBL" ) ); // default settings setLine( Plot2d::Solid, 0 ); // solid line, width = 0 @@ -244,38 +250,6 @@ QColor Plot2d_SetupCurveDlg::getColor() const return myColorBtn->color(); } -/*! - \brief Create icon pixmap according to the line type. - \param type line type - \return icon -*/ -QPixmap Plot2d_SetupCurveDlg::lineIcon( Plot2d::LineType type ) const -{ - QSize sz = myLineCombo->iconSize(); - QPixmap px( sz ); - px.fill( QColor( 255, 255, 255, 0 ) ); - QPainter p( &px ); - Plot2d::drawLine( &p, 5, sz.height()/2, sz.width()-5, sz.height()/2, type, - myLineCombo->palette().color( QPalette::Text ), 1 ); - return px; -} - -/*! - \brief Create icon pixmap according to the marker type. - \param type marker type - \return icon -*/ -QPixmap Plot2d_SetupCurveDlg::markerIcon( Plot2d::MarkerType type ) const -{ - QSize sz = myMarkerCombo->iconSize(); - QPixmap px( sz ); - px.fill( QColor( 255, 255, 255, 0 ) ); - QPainter p( &px ); - Plot2d::drawMarker( &p, sz.width()/2, sz.height()/2, MSIZE, MSIZE, type, - myMarkerCombo->palette().color( QPalette::Text ) ); - return px; -} - /* \brief Update preview widget. */ diff --git a/src/Plot2d/Plot2d_SetupCurveDlg.h b/src/Plot2d/Plot2d_SetupCurveDlg.h index 51b7e8ca1..a944da4db 100644 --- a/src/Plot2d/Plot2d_SetupCurveDlg.h +++ b/src/Plot2d/Plot2d_SetupCurveDlg.h @@ -55,9 +55,6 @@ public: void setColor( const QColor& ); QColor getColor() const; -private: - QPixmap lineIcon( Plot2d::LineType ) const; - QPixmap markerIcon( Plot2d::MarkerType ) const; private slots: void updatePreview(); diff --git a/src/Plot2d/Plot2d_SetupCurveScaleDlg.cxx b/src/Plot2d/Plot2d_SetupCurveScaleDlg.cxx index 903af1a38..1fb1403ad 100755 --- a/src/Plot2d/Plot2d_SetupCurveScaleDlg.cxx +++ b/src/Plot2d/Plot2d_SetupCurveScaleDlg.cxx @@ -135,4 +135,4 @@ double Plot2d_SetupCurveScaleDlg::getScale() const void Plot2d_SetupCurveScaleDlg::setUndefinedValue() { myValueSpin->setCleared(true); myValueSpin->setSpecialValueText(""); -} \ No newline at end of file +} diff --git a/src/Plot2d/Plot2d_ViewFrame.cxx b/src/Plot2d/Plot2d_ViewFrame.cxx index 1a231a758..ea4972f95 100755 --- a/src/Plot2d/Plot2d_ViewFrame.cxx +++ b/src/Plot2d/Plot2d_ViewFrame.cxx @@ -28,6 +28,10 @@ #include "Plot2d_FitDataDlg.h" #include "Plot2d_ViewWindow.h" #include "Plot2d_SetupViewDlg.h" +#ifndef DISABLE_PYCONSOLE +#include "Plot2d_AnaliticCurveDlg.h" +#include "Plot2d_AnaliticCurve.h" +#endif #include "Plot2d_ToolTip.h" #include "SUIT_Tools.h" @@ -1123,6 +1127,79 @@ void Plot2d_ViewFrame::onSettings() delete dlg; } +#ifndef DISABLE_PYCONSOLE +/*! + "Analitic Curves" toolbar action slot +*/ +void Plot2d_ViewFrame::onAnaliticCurve() { + Plot2d_AnaliticCurveDlg* dlg = new Plot2d_AnaliticCurveDlg(this, myPlot); + dlg->setCurveList(myAnaliticCurves); + dlg->exec(); + delete dlg; +} +#endif + + +#ifndef DISABLE_PYCONSOLE +/* + Update analitic curve +*/ +void Plot2d_ViewFrame::updateAnaliticCurve(Plot2d_AnaliticCurve* c, bool updateView){ + if(!c) return; + QwtScaleDiv* div = myPlot->axisScaleDiv(QwtPlot::xBottom); + c->setRangeBegin(div->lowerBound()); + c->setRangeEnd(div->upperBound()); + c->calculate(); + c->setMarkerSize(myMarkerSize); + QwtPlotItem* item = c->plotItem(); + + switch( c->getAction() ) { + case Plot2d_AnaliticCurve::ActAddInView: + if( c->isActive() ) { + item->attach( myPlot ); + } + myAnaliticCurves.append(c); + c->setAction(Plot2d_AnaliticCurve::ActNothing); + break; + + case Plot2d_AnaliticCurve::ActUpdateInView: + if(c->isActive()) { + item->attach( myPlot ); + c->updatePlotItem(); + item->show(); + } else { + item->hide(); + item->detach(); + } + + c->setAction(Plot2d_AnaliticCurve::ActNothing); + break; + case Plot2d_AnaliticCurve::ActRemoveFromView: + item->hide(); + item->detach(); + myAnaliticCurves.removeAll(c); + delete c; + break; + } + + if(updateView) + myPlot->replot(); +} +#endif + +#ifndef DISABLE_PYCONSOLE +/* + Update analitic curves +*/ +void Plot2d_ViewFrame::updateAnaliticCurves() { + AnaliticCurveList::iterator it = myAnaliticCurves.begin(); + for( ; it != myAnaliticCurves.end(); it++) { + updateAnaliticCurve(*it); + } + myPlot->replot(); +} +#endif + /*! "Fit Data" command slot */ @@ -1138,6 +1215,9 @@ void Plot2d_ViewFrame::onFitData() fitData(mode,xMin,xMax,yMin,yMax,y2Min,y2Max); } delete dlg; +#ifndef DISABLE_PYCONSOLE + updateAnaliticCurves(); +#endif } /*! @@ -1682,7 +1762,12 @@ void Plot2d_ViewFrame::plotMouseReleased( const QMouseEvent& me ) QContextMenuEvent aEvent( QContextMenuEvent::Mouse, me.pos(), me.globalPos() ); emit contextMenuRequested( &aEvent ); + } +#ifndef DISABLE_PYCONSOLE + else { + updateAnaliticCurves(); } +#endif myPlot->canvas()->setCursor( QCursor( Qt::CrossCursor ) ); myPlot->defaultPicker(); @@ -1715,6 +1800,9 @@ void Plot2d_ViewFrame::wheelEvent(QWheelEvent* event) myPlot->replot(); if ( myPlot->zoomer() ) myPlot->zoomer()->setZoomBase(); myPnt = event->pos(); +#ifndef DISABLE_PYCONSOLE + updateAnaliticCurves(); +#endif } /*! @@ -2293,8 +2381,50 @@ QString Plot2d_ViewFrame::getVisualParameters() double xmin, xmax, ymin, ymax, y2min, y2max; getFitRanges( xmin, xmax, ymin, ymax, y2min, y2max ); QString retStr; - retStr.sprintf( "%d*%d*%d*%.12e*%.12e*%.12e*%.12e*%.12e*%.12e", myXMode, - myYMode, mySecondY, xmin, xmax, ymin, ymax, y2min, y2max ); + //Store font in the visual parameters string as: + // + // ...*FontFamily|FontSize|B|I|U|r:g:b*... + + retStr.sprintf( "%d*%d*%d*%.12e*%.12e*%.12e*%.12e*%.12e*%.12e*%s|%i|%i|%i|%i|%i:%i:%i", + myXMode, myYMode, mySecondY, xmin, xmax, ymin, ymax, y2min, y2max, + qPrintable(myLegendFont.family()), myLegendFont.pointSize(),myLegendFont.bold(), + myLegendFont.italic(), myLegendFont.underline(),myLegendColor.red(), + myLegendColor.green(), myLegendColor.blue()); + +#ifndef DISABLE_PYCONSOLE + //store all analitic curves + //store each curve in the following format + // ...*Name|isActive|Expresion|NbInervals|isAutoAssign[|MarkerType|LineType|LineWidth|r:g:b] + // parameters in the [ ] is optional in case if isAutoAssign == true + AnaliticCurveList::iterator it = myAnaliticCurves.begin(); + Plot2d_AnaliticCurve* c = 0; + bool isAuto; + for( ; it != myAnaliticCurves.end(); it++) { + c = (*it); + if(!c) continue; + QString curveString(""); + isAuto = c->isAutoAssign(); + curveString.sprintf("*%s|%i|%s|%i|%i", + qPrintable(c->getName()), + c->isActive(), + qPrintable(c->getExpression()), + c->getNbIntervals(), + isAuto); + + retStr+=curveString; + if(!isAuto) { + QString optCurveString(""); + optCurveString.sprintf("|%i|%i|%i|%i:%i:%i", + (int)c->getMarker(), + (int)c->getLine(), + c->getLineWidth(), + c->getColor().red(), + c->getColor().green(), + c->getColor().blue()); + retStr+=optCurveString; + } + } +#endif return retStr; } @@ -2303,9 +2433,10 @@ QString Plot2d_ViewFrame::getVisualParameters() */ void Plot2d_ViewFrame::setVisualParameters( const QString& parameters ) { + double xmin, xmax; QStringList paramsLst = parameters.split( '*' ); - if ( paramsLst.size() == 9 ) { - double xmin, xmax, ymin, ymax, y2min, y2max; + if ( paramsLst.size() >= 9 ) { + double ymin, ymax, y2min, y2max; myXMode = paramsLst[0].toInt(); myYMode = paramsLst[1].toInt(); mySecondY = (bool)paramsLst[2].toInt(); @@ -2328,7 +2459,62 @@ void Plot2d_ViewFrame::setVisualParameters( const QString& parameters ) fitData( 0, xmin, xmax, ymin, ymax, y2min, y2max ); fitData( 0, xmin, xmax, ymin, ymax, y2min, y2max ); - } + } + + //Restore legend font + if(paramsLst.size() >= 10) { + QStringList fontList = paramsLst[9].split( '|' ); + if(fontList.size() == 6) { + myLegendFont = QFont(fontList[0]); + myLegendFont.setPointSize(fontList[1].toInt()); + myLegendFont.setBold(fontList[2].toInt()); + myLegendFont.setItalic(fontList[3].toInt()); + myLegendFont.setUnderline(fontList[4].toInt()); + QStringList colorList = fontList[5].split(":"); + setLegendFont( myLegendFont ); + + if(colorList.size() == 3) { + myLegendColor = QColor(colorList[0].toInt(), + colorList[1].toInt(), + colorList[2].toInt()); + setLegendFontColor( myLegendColor ); + } + } + } + +#ifndef DISABLE_PYCONSOLE + //Restore all analitical curves + int startCurveIndex = 10; + if( paramsLst.size() >= startCurveIndex+1 ) { + for( int i=startCurveIndex; isetName(curveLst[0]); + c->setActive(curveLst[1].toInt()); + c->setExpression(curveLst[2]); + c->setNbIntervals(curveLst[3].toLong()); + c->setAutoAssign(curveLst[4].toInt()); + if( !c->isAutoAssign() ) { + c->setMarker((Plot2d::MarkerType)curveLst[5].toInt()); + c->setLine((Plot2d::LineType)curveLst[6].toInt()); + c->setLineWidth(curveLst[7].toInt()); + QStringList colorList = curveLst[8].split(":"); + if( colorList.size() == 3 ) { + c->setColor(QColor(colorList[0].toInt(), + colorList[1].toInt(), + colorList[2].toInt())); + } + } else { + c->autoFill( myPlot ); + } + c->setAction(Plot2d_AnaliticCurve::ActAddInView); + updateAnaliticCurve(c); + } + } + myPlot->replot(); + } +#endif } /*! @@ -2407,6 +2593,9 @@ Plot2d_Curve* Plot2d_ViewFrame::getClosestCurve( QPoint p, double& distance, int void Plot2d_ViewFrame::onPanLeft() { this->incrementalPan( -INCREMENT_FOR_OP, 0 ); +#ifndef DISABLE_PYCONSOLE + updateAnaliticCurves(); +#endif } /*! @@ -2415,6 +2604,9 @@ void Plot2d_ViewFrame::onPanLeft() void Plot2d_ViewFrame::onPanRight() { this->incrementalPan( INCREMENT_FOR_OP, 0 ); +#ifndef DISABLE_PYCONSOLE + updateAnaliticCurves(); +#endif } /*! @@ -2423,6 +2615,9 @@ void Plot2d_ViewFrame::onPanRight() void Plot2d_ViewFrame::onPanUp() { this->incrementalPan( 0, -INCREMENT_FOR_OP ); +#ifndef DISABLE_PYCONSOLE + updateAnaliticCurves(); +#endif } /*! @@ -2431,6 +2626,9 @@ void Plot2d_ViewFrame::onPanUp() void Plot2d_ViewFrame::onPanDown() { this->incrementalPan( 0, INCREMENT_FOR_OP ); +#ifndef DISABLE_PYCONSOLE + updateAnaliticCurves(); +#endif } /*! @@ -2439,6 +2637,9 @@ void Plot2d_ViewFrame::onPanDown() void Plot2d_ViewFrame::onZoomIn() { this->incrementalZoom( INCREMENT_FOR_OP, INCREMENT_FOR_OP ); +#ifndef DISABLE_PYCONSOLE + updateAnaliticCurves(); +#endif } /*! @@ -2447,6 +2648,9 @@ void Plot2d_ViewFrame::onZoomIn() void Plot2d_ViewFrame::onZoomOut() { this->incrementalZoom( -INCREMENT_FOR_OP, -INCREMENT_FOR_OP ); +#ifndef DISABLE_PYCONSOLE + updateAnaliticCurves(); +#endif } /*! @@ -2510,5 +2714,3 @@ QwtText Plot2d_ScaleDraw::label( double value ) const return QwtScaleDraw::label( value ); } - - diff --git a/src/Plot2d/Plot2d_ViewFrame.h b/src/Plot2d/Plot2d_ViewFrame.h index 874188919..71985514a 100755 --- a/src/Plot2d/Plot2d_ViewFrame.h +++ b/src/Plot2d/Plot2d_ViewFrame.h @@ -25,6 +25,9 @@ #include "Plot2d.h" #include "Plot2d_Curve.h" +#ifndef DISABLE_PYCONSOLE +#include "Plot2d_AnaliticCurve.h" +#endif #include #include @@ -108,6 +111,10 @@ public: void getFitRangeByCurves( double&, double&, double&, double&, double&, double& ); +#ifndef DISABLE_PYCONSOLE + void updateAnaliticCurves(); + void updateAnaliticCurve( Plot2d_AnaliticCurve*, bool = false ); +#endif /* view parameters */ void copyPreferences( Plot2d_ViewFrame* ); @@ -163,23 +170,19 @@ public: Plot2d_Curve* getClosestCurve( QPoint, double&, int& ) const; Plot2d_Object* getPlotObject( QwtPlotItem* ) const; - + QwtPlotItem* getPlotObject( Plot2d_Object* ) const; + QwtPlotCurve* getPlotCurve( Plot2d_Curve* ) const; + Plot2d_Plot2d* getPlot() const { return myPlot; } protected: int testOperation( const QMouseEvent& ); void readPreferences(); void writePreferences(); QString getInfo( const QPoint& ); virtual void wheelEvent( QWheelEvent* ); - QwtPlotCurve* getPlotCurve( Plot2d_Curve* ) const; bool hasPlotCurve( Plot2d_Curve* ) const; void setCurveType( QwtPlotCurve*, int ); - QwtPlotItem* getPlotObject( Plot2d_Object* ) const; bool hasPlotObject( Plot2d_Object* ) const; - - - - public slots: void onViewPan(); void onViewZoom(); @@ -187,6 +190,9 @@ public slots: void onViewFitArea(); void onViewGlobalPan(); void onSettings(); +#ifndef DISABLE_PYCONSOLE + void onAnaliticCurve(); +#endif void onFitData(); void onChangeBackground(); void onPanLeft(); @@ -210,28 +216,31 @@ signals: void legendClicked( QwtPlotItem* ); protected: - Plot2d_Plot2d* myPlot; - int myOperation; - QPoint myPnt; - - int myCurveType; - bool myShowLegend; - int myLegendPos; - QFont myLegendFont; - QColor myLegendColor; - int myMarkerSize; - QColor myBackground; - QString myTitle, myXTitle, myYTitle, myY2Title; - bool myTitleEnabled, myXTitleEnabled, myYTitleEnabled, myY2TitleEnabled; - bool myXGridMajorEnabled, myYGridMajorEnabled, myY2GridMajorEnabled; - bool myXGridMinorEnabled, myYGridMinorEnabled, myY2GridMinorEnabled; - int myXGridMaxMajor, myYGridMaxMajor, myY2GridMaxMajor; - int myXGridMaxMinor, myYGridMaxMinor, myY2GridMaxMinor; - int myXMode, myYMode; - double myXDistance, myYDistance, myYDistance2; - bool mySecondY; - ObjectDict myObjects; - bool myIsDefTitle; + Plot2d_Plot2d* myPlot; + int myOperation; + QPoint myPnt; + + int myCurveType; + bool myShowLegend; + int myLegendPos; + QFont myLegendFont; + QColor myLegendColor; + int myMarkerSize; + QColor myBackground; + QString myTitle, myXTitle, myYTitle, myY2Title; + bool myTitleEnabled, myXTitleEnabled, myYTitleEnabled, myY2TitleEnabled; + bool myXGridMajorEnabled, myYGridMajorEnabled, myY2GridMajorEnabled; + bool myXGridMinorEnabled, myYGridMinorEnabled, myY2GridMinorEnabled; + int myXGridMaxMajor, myYGridMaxMajor, myY2GridMaxMajor; + int myXGridMaxMinor, myYGridMaxMinor, myY2GridMaxMinor; + int myXMode, myYMode; + double myXDistance, myYDistance, myYDistance2; + bool mySecondY; + ObjectDict myObjects; +#ifndef DISABLE_PYCONSOLE + AnaliticCurveList myAnaliticCurves; +#endif + bool myIsDefTitle; }; class Plot2d_Plot2d : public QwtPlot diff --git a/src/Plot2d/Plot2d_ViewWindow.cxx b/src/Plot2d/Plot2d_ViewWindow.cxx index eb9fdb2cc..d4bcf6c94 100755 --- a/src/Plot2d/Plot2d_ViewWindow.cxx +++ b/src/Plot2d/Plot2d_ViewWindow.cxx @@ -374,11 +374,23 @@ void Plot2d_ViewWindow::createActions() aResMgr->loadPixmap( "Plot2d", tr( "ICON_PLOT2D_SETTINGS" ) ), tr( "MEN_PLOT2D_SETTINGS" ), 0, this ); + aAction->setStatusTip( tr( "PRP_PLOT2D_SETTINGS" ) ); connect( aAction, SIGNAL( triggered( bool ) ), myViewFrame, SLOT( onSettings() ) ); mgr->registerAction( aAction, CurvSettingsId ); - // 9. Clone + // 9. Analitic curves + aAction = new QtxAction( tr( "TOT_PLOT2D_ANALITIC_CURVES" ), + aResMgr->loadPixmap( "Plot2d", tr( "ICON_PLOT2D_ANALITIC_CURVES" ) ), + tr( "MEN_PLOT2D_ANALITIC_CURVES" ), + 0, this ); + + aAction->setStatusTip( tr( "PRP_PLOT2D_ANALITIC_CURVES" ) ); + connect( aAction, SIGNAL( triggered( bool ) ), myViewFrame, SLOT( onAnaliticCurve() ) ); + mgr->registerAction( aAction, AnaliticCurveId ); + + + // 10. Clone aAction = new QtxAction( tr( "MNU_CLONE_VIEW" ), aResMgr->loadPixmap( "Plot2d", tr( "ICON_PLOT2D_CLONE_VIEW" ) ), tr( "MNU_CLONE_VIEW" ), @@ -387,7 +399,7 @@ void Plot2d_ViewWindow::createActions() connect( aAction, SIGNAL( triggered( bool ) ), this, SIGNAL( cloneView() ) ); mgr->registerAction( aAction, CloneId ); - // 10. Print + // 11. Print aAction = new QtxAction( tr( "MNU_PRINT_VIEW" ), aResMgr->loadPixmap( "STD", tr( "ICON_PLOT2D_PRINT" ) ), tr( "MNU_PRINT_VIEW" ), @@ -426,6 +438,7 @@ void Plot2d_ViewWindow::createToolBar() mgr->append( toolMgr()->separator(), myToolBar ); mgr->append( LegendId, myToolBar ); mgr->append( CurvSettingsId, myToolBar ); + mgr->append( AnaliticCurveId, myToolBar ); mgr->append( CloneId, myToolBar ); mgr->append( PrintId, myToolBar ); } diff --git a/src/Plot2d/Plot2d_ViewWindow.h b/src/Plot2d/Plot2d_ViewWindow.h index fc3bed5b2..86e52053c 100755 --- a/src/Plot2d/Plot2d_ViewWindow.h +++ b/src/Plot2d/Plot2d_ViewWindow.h @@ -56,7 +56,8 @@ public: CurvPointsId, CurvLinesId, CurvSplinesId, LegendId, CurvSettingsId, - CloneId, PrintId }; + CloneId, PrintId, + AnaliticCurveId }; public: Plot2d_ViewWindow( SUIT_Desktop*, Plot2d_Viewer* ); diff --git a/src/Plot2d/resources/Plot2d_images.ts b/src/Plot2d/resources/Plot2d_images.ts index ca85468c5..3c39ca19d 100644 --- a/src/Plot2d/resources/Plot2d_images.ts +++ b/src/Plot2d/resources/Plot2d_images.ts @@ -55,6 +55,10 @@ ICON_PLOT2D_SETTINGS plot2d_settings.png + + ICON_PLOT2D_ANALITIC_CURVES + plot2d_analitic_curve.png + ICON_PLOT2D_CURVES_LINES plot2d_lines.png diff --git a/src/Plot2d/resources/Plot2d_msg_en.ts b/src/Plot2d/resources/Plot2d_msg_en.ts index 81a5ede48..6b16abd6f 100644 --- a/src/Plot2d/resources/Plot2d_msg_en.ts +++ b/src/Plot2d/resources/Plot2d_msg_en.ts @@ -203,6 +203,10 @@ TOT_PLOT2D_SETTINGS Settings + + TOT_PLOT2D_ANALITIC_CURVES + Analytic curves + PLOT2D_CURVE_TYPE_LINES Lines @@ -349,10 +353,14 @@ Logarithmic scale for ordinate axis is not allowed. DSC_ZOOM_VIEW Zoom the view + + PRP_PLOT2D_ANALITIC_CURVES + Setups analytic curves properties + PRP_PLOT2D_SETTINGS Setups view properties - + INF_COORDINATES_SOME_Y Coordinates: X : %1, Y : %2 ( %3 ) @@ -441,6 +449,10 @@ Logarithmic scale for ordinate axis is not allowed. MEN_PLOT2D_SETTINGS &Settings + + MEN_PLOT2D_ANALITIC_CURVES + Analytic curves + CIRCLE_MARKER_LBL Circle @@ -588,6 +600,70 @@ Logarithmic scale for ordinate axis is not allowed. Background color + + Plot2d_AnaliticCurveDlg + + ANALITIC_CURVE_TLT + Analitic curves properties + + + AC_CURVE_PARAMS + Curve parameters + + + AC_CURVE_PROPS + Curve properties + + + AC_FORMULA + y(x) = + + + AC_NB_INTERVALS + Nb. intervals + + + AC_AUTO_ASSIGN + Auto assign + + + AC_MARKER_TYPE + Marker type + + + AC_LINE_TYPE + Line type + + + AC_LINE_WIDTH + Line Width + + + AC_CURVE_COLOR + Curve color + + + AC_ADD_BTN + Add curve + + + AC_REM_BTN + Remove curve + + + AC_UPD_BTN + Update Curve + + + AC_CANT_CALCULATE + Can't calculate curve. +Please, check input parameters!!! + + + AC_CLOSE_BTN + &Close + + Plot2d_SetupCurveScaleDlg @@ -600,3 +676,4 @@ Logarithmic scale for ordinate axis is not allowed. + diff --git a/src/Plot2d/resources/Plot2d_msg_fr.ts b/src/Plot2d/resources/Plot2d_msg_fr.ts index e3a19c6c4..53be6d0ac 100755 --- a/src/Plot2d/resources/Plot2d_msg_fr.ts +++ b/src/Plot2d/resources/Plot2d_msg_fr.ts @@ -203,6 +203,10 @@ TOT_PLOT2D_SETTINGS Paramètres + + TOT_PLOT2D_ANALITIC_CURVES + Courbes analytiques + PLOT2D_CURVE_TYPE_LINES Lignes @@ -341,10 +345,18 @@ L'échelle logarithmique de l'ordonnée n'est pas permise.PLOT2D_ENABLE_LEGEND Afficher la légende + + PLOT2D_LEGEND_FONT + Police de la légende + DSC_ZOOM_VIEW Zoomer la vue + + PRP_PLOT2D_ANALITIC_CURVES + Réglage des propriétés des courbes analytiques + PRP_PLOT2D_SETTINGS Définit les paramètres de visualisation @@ -387,7 +399,7 @@ L'échelle logarithmique de l'ordonnée n'est pas permise. PRP_PLOT2D_FITDATA - Ajuster la vue à la plage de données indiquées + Ajuster la vue à la plage de données indiquée MEN_PLOT2D_MODE_LINEAR_VER @@ -437,6 +449,10 @@ L'échelle logarithmique de l'ordonnée n'est pas permise.MEN_PLOT2D_SETTINGS &Paramètres + + MEN_PLOT2D_ANALITIC_CURVES + Courbes analytiques + CIRCLE_MARKER_LBL Cercle @@ -573,15 +589,90 @@ L'échelle logarithmique de l'ordonnée n'est pas permise. PREF_HOR_AXIS_SCALE - Echelle de l'axe horizontal: + Echelle de l'axe horizontal: PREF_VERT_AXIS_SCALE - Echelle de l'axe vertical: + Echelle de l'axe vertical: PREF_VIEWER_BACKGROUND Couleur du fond + + Plot2d_AnaliticCurveDlg + + ANALITIC_CURVE_TLT + Propriétés des courbes analytiques + + + AC_CURVE_PARAMS + Paramètres de la courbe + + + AC_CURVE_PROPS + Propriétés de la courbe + + + AC_FORMULA + y(x) = + + + AC_NB_INTERVALS + Nb. intervalles + + + AC_AUTO_ASSIGN + Affectation auto. + + + AC_MARKER_TYPE + Type de marqueur + + + AC_LINE_TYPE + Type de ligne + + + AC_LINE_WIDTH + Epaisseur de ligne + + + AC_CURVE_COLOR + Couleur de la courbe + + + AC_ADD_BTN + Ajouter une courbe + + + AC_REM_BTN + Supprimer une courbe + + + AC_UPD_BTN + Mettre à jour la courbe + + + AC_CANT_CALCULATE + La courbe ne peut pas être calculée. +Merci de vérifier les paramètres d'entrée + + + AC_CLOSE_BTN + &Fermer + + + + Plot2d_SetupCurveScaleDlg + + TLT_SETUP_CURVE_SCALE + Mise à l'échelle de courbe(s) + + + CURVE_SCALE_FACTOR + Facteur d'échelle + + diff --git a/src/Plot2d/resources/plot2d_analitic_curve.png b/src/Plot2d/resources/plot2d_analitic_curve.png new file mode 100755 index 0000000000000000000000000000000000000000..7ceb5f2204dd976f140a5203c37cc0f97c9b963c GIT binary patch literal 1189 zcmV;W1X}xvP)R<7oNlO{J!UT`JV6Z z<#{@~ySo`39kn$Ybz6t+;j@O#2GVtOSmVemAM5!3y8Gkz?UpTDY`wj`Hk;jUr%|ty zPbG=O;w(mD#1aX#R01`bB&x*MTjF1EoE6zut<^BOY+cfo%Vl2sZk(3~ue0ym1bart z*(Er<944)-Fg`IsQ1DW4naQbX{EJHjf+6O7K@#C2@mP*hsVsz@*zBF17)F^uRAGMB zLp86WrITE}>*VRD1OzxkOfcm85wc7~~GCk}@Lm&?ntUuEaFk1*w&lDQR& z!*aP(6zXMzOjg4?J4Z5=wwh;V7A>EXxgw9BbWkko6eUZ3HH%S$BV#-G({lqgULc-S z(e*MWmql2q)L4#(@ra6|Rz*!`@cMkD8%;9P6I|>4kmvd?5qN7SuHOAjpFc_O*t4Pv zBJ%TuG&2ttq21mkDs(26#H-{Ia*mswO2l#R+mg;i15G~{o=6i3H)^XvSN_<9bobnYy9)6Gzry14$* ziMx0LDj#2Xo>~*a^M2fYNAaIIPF2=zxNKA_6!j8;rH~B6>tEY&j|@{6tQAYl$;M_? zC#<%3;>&}${3ltJ$(3Y+l!z&{S23#9O);$2h{qH7gFz|iBA0hP#`{-xP|?~_+*#)5 z7sTc;@8BYC(nJTVL_n`ByPY z6)El#wBU=2JGnY>iqqo?FAm<83@cQ%G&NnLR^LQem!sEOgC)uA%>FkKNIqe3 zpYNvc;(iWa*v|0HL!wM3#ork@9otXcVeh#~#->)F-CS3=Z5s_K4?|cyBc}=dFhaR4 z0bh<-anef#{DFBgg(NMBd`vQ$%WE_$I(jZmFp)yd6==6wY!fb<*)c(vU|k&D12*o@6h z>2w+^=7Kd%vrfpGtlLm%@9MHt)dGIYwq(*=ytcMRTZsRO&7V9Ne}K(z^RX_6ty{M; zG&ID(;2`}20}S-{Ga$BKtl9o6HaV=Mtl#1$_+Q%Jr+Ku*5Z^~`00000NkvXXu0mjf DGXFd^ literal 0 HcmV?d00001 diff --git a/src/Prs/Prs.pro b/src/Prs/Prs.pro deleted file mode 100644 index c4afc7f58..000000000 --- a/src/Prs/Prs.pro +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = SalomePrs -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += PRS_EXPORTS - -HEADERS = SALOME_Prs.h - -SOURCES = SALOME_Prs.cxx - -includes.files = $$HEADERS -includes.path = ../../include - -INSTALLS += includes diff --git a/src/PyConsole/Makefile.am b/src/PyConsole/Makefile.am index 430120362..1d71c2e80 100755 --- a/src/PyConsole/Makefile.am +++ b/src/PyConsole/Makefile.am @@ -50,5 +50,5 @@ nodist_salomeres_DATA = \ libPyConsole_la_CPPFLAGS = $(PYTHON_INCLUDES) $(QT_INCLUDES) \ -I$(srcdir)/../PyInterp -I$(srcdir)/../SUIT -I$(srcdir)/../Qtx -libPyConsole_la_LDFLAGS = $(PYTHON_LIBS) $(QT_MT_LIBS) +libPyConsole_la_LDFLAGS = $(PYTHON_LIBS) $(QT_MT_LIBS) $(KERNEL_LDFLAGS) -lSALOMELocalTrace libPyConsole_la_LIBADD = ../Qtx/libqtx.la ../SUIT/libsuit.la ../PyInterp/libPyInterp.la diff --git a/src/PyConsole/PyConsole.pro b/src/PyConsole/PyConsole.pro deleted file mode 100644 index b25ba48ce..000000000 --- a/src/PyConsole/PyConsole.pro +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = PyConsole -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -PYTHONVER=2.4 -PYTHONHOME=$$(PYTHONHOME) -PYTHONINC=$${PYTHONHOME}/include/python$${PYTHONVER} -PYTHONLIB=$${PYTHONHOME}/lib - -INCLUDEPATH += ../../include $${PYTHONINC} -INCLUDEPATH += ../Qtx ../SUIT ../PyInterp -unix:LIBS += -L$${PYTHONLIB} -lpython$${PYTHONVER} -win32:LIBS += /LIBPATH:$$(PYTHONLIB) -LIBS += -L../../lib -lqtx -lsuit -lPyInterp - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += PYCONSOLE_EXPORTS - -HEADERS = PyConsole.h -HEADERS += PyConsole_Console.h -HEADERS += PyConsole_Editor.h -HEADERS += PyConsole_Interp.h - -SOURCES = PyConsole_Console.cxx -SOURCES += PyConsole_Editor.cxx -SOURCES += PyConsole_Interp.cxx - -TRANSLATIONS = resources/PyConsole_msg_en.ts - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/PyInterp/PyInterp.pro b/src/PyInterp/PyInterp.pro deleted file mode 100644 index d0328a4fd..000000000 --- a/src/PyInterp/PyInterp.pro +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = PyInterp -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -PYTHONVER=2.4 -PYTHONHOME=$$(PYTHONHOME) -PYTHONINC=$${PYTHONHOME}/include/python$${PYTHONVER} -PYTHONLIB=$${PYTHONHOME}/lib - -INCLUDEPATH += ../../include $${PYTHONINC} -unix:LIBS += -L$${PYTHONLIB} -lpython$${PYTHONVER} -win32:LIBS += /LIBPATH:$${PYTHONLIB} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += PYINTERP_EXPORTS - -HEADERS = PyInterp.h -HEADERS += PyInterp_Dispatcher.h -HEADERS += PyInterp_Watcher.h -HEADERS += PyInterp_Interp.h - -SOURCES = PyInterp_Dispatcher.cxx -SOURCES += PyInterp_Interp.cxx - -includes.files = $$HEADERS -includes.path = ../../include - -INSTALLS += includes diff --git a/src/QDS/QDS.h b/src/QDS/QDS.h index 54538751b..a24eabbae 100644 --- a/src/QDS/QDS.h +++ b/src/QDS/QDS.h @@ -53,13 +53,14 @@ public: //! Enum describes bit flags of the Qt datum view and behaviour typedef enum { - None = 0x00, //!< Non specified any flags (Default behaviour) - Label = 0x01, //!< Create subwidget for datum label - Control = 0x02, //!< Create subwidget for datum input control - Units = 0x04, //!< Create subwidget for datum units of measure - NotFormat = 0x08, //!< Don't format initial value - NotAccel = 0x10, //!< Not support accelerators in datum label - UnitsWithLabel = 0x20, //!< Display units of measure in label like " () instead separate text" + None = 0x00, //!< Non specified any flags (Default behaviour) + Label = 0x01, //!< Create subwidget for datum label + Control = 0x02, //!< Create subwidget for datum input control + Units = 0x04, //!< Create subwidget for datum units of measure + NotFormat = 0x08, //!< Don't format initial value + NotAccel = 0x10, //!< Not support accelerators in datum label + NotConvert = 0x20, //!< Don't convert numeric value + UnitsWithLabel = 0x40, //!< Display units of measure in label like " () instead separate text" All = Label | Control | Units //!< Create all subwidgets } DatumFlags; diff --git a/src/QDS/QDS.pro b/src/QDS/QDS.pro deleted file mode 100644 index 359e00b57..000000000 --- a/src/QDS/QDS.pro +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = QDS -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -INCLUDEPATH += ../../include $${CAS_CPPFLAGS} ../Qtx ../DDS -LIBS += -L../../lib -lqtx -lDDS $${CAS_KERNEL} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += QDS_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = QDS.h -HEADERS += QDS_CheckBox.h -HEADERS += QDS_ComboBox.h -HEADERS += QDS_Datum.h -HEADERS += QDS_LineEdit.h -HEADERS += QDS_SpinBox.h -HEADERS += QDS_SpinBoxDbl.h -HEADERS += QDS_TextEdit.h -HEADERS += QDS_Validator.h -HEADERS += QDS_RadioBox.h -#HEADERS += QDS_Table.h - -SOURCES = QDS.cxx -SOURCES += QDS_CheckBox.cxx -SOURCES += QDS_ComboBox.cxx -SOURCES += QDS_Datum.cxx -SOURCES += QDS_LineEdit.cxx -SOURCES += QDS_SpinBox.cxx -SOURCES += QDS_SpinBoxDbl.cxx -SOURCES += QDS_TextEdit.cxx -SOURCES += QDS_Validator.cxx -SOURCES += QDS_RadioBox.cxx -#SOURCES += QDS_Table.cxx - -TRANSLATIONS = resources/QDS_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/QDS/QDS_ComboBox.cxx b/src/QDS/QDS_ComboBox.cxx index 64410aea1..608dbe5ee 100644 --- a/src/QDS/QDS_ComboBox.cxx +++ b/src/QDS/QDS_ComboBox.cxx @@ -410,11 +410,11 @@ void QDS_ComboBox::setString( const QString& txt ) else if ( txt.isEmpty() ) { if ( !cb->isEditable() ) - cb->setItemText( cb->currentIndex(), txt ); + cb->setCleared( true ); else cb->lineEdit()->setText( txt ); } - if ( isClear != txt.isEmpty() || ( !isClear && old != cb->currentIndex() ) ) + if ( isClear != txt.isEmpty() || ( !isClear && old != cb->currentIndex() ) || isClear != cb->isCleared() ) { onParamChanged(); QString str = getString(); diff --git a/src/QDS/QDS_Datum.cxx b/src/QDS/QDS_Datum.cxx index 56dd594d6..ec0b53b48 100644 --- a/src/QDS/QDS_Datum.cxx +++ b/src/QDS/QDS_Datum.cxx @@ -588,7 +588,7 @@ double QDS_Datum::doubleValue() const else { res = getString().toDouble(); - if ( !myDicItem.IsNull() ) + if ( !myDicItem.IsNull() && !( flags() & NotConvert ) ) res = myDicItem->ToSI( res ); } @@ -613,7 +613,7 @@ int QDS_Datum::integerValue() const else { double val = getString().toDouble(); - if ( !myDicItem.IsNull() ) + if ( !myDicItem.IsNull() && !( flags() & NotConvert ) ) res = (int)myDicItem->ToSI( val ); } @@ -738,7 +738,7 @@ void QDS_Datum::setDoubleValue( const double num ) mySourceValue = QString().setNum( num, 'g', 16 ); double val = num; - if ( !myDicItem.IsNull() ) + if ( !myDicItem.IsNull() && !( flags() & NotConvert ) ) val = myDicItem->FromSI( val ); QString aStr = format( ( flags() & NotFormat ) ? (QString) "" : format(), type(), val ); @@ -766,7 +766,7 @@ void QDS_Datum::setIntegerValue( const int num ) mySourceValue = QString().setNum( num ); double val = num; - if ( !myDicItem.IsNull() ) + if ( !myDicItem.IsNull() && !( flags() & NotConvert ) ) val = myDicItem->FromSI( val ); QString aStr = format( ( flags() & NotFormat ) ? (QString) "" : format(), type(), val ); diff --git a/src/QDS/QDS_SpinBoxDbl.cxx b/src/QDS/QDS_SpinBoxDbl.cxx index fd135a4f9..84bec04d3 100644 --- a/src/QDS/QDS_SpinBoxDbl.cxx +++ b/src/QDS/QDS_SpinBoxDbl.cxx @@ -195,4 +195,4 @@ void QDS_SpinBoxDbl::unitSystemChanged( const QString& system ) sb->setSingleStep( .1 ); sb->setMinimum( minValue().isEmpty() ? -DBL_MAX : minValue().toDouble() ); sb->setMaximum( maxValue().isEmpty() ? DBL_MAX : maxValue().toDouble() ); -} \ No newline at end of file +} diff --git a/src/QDS/resources/QDS_msg_en.ts b/src/QDS/resources/QDS_msg_en.ts index 2388719f0..dbc68fed2 100644 --- a/src/QDS/resources/QDS_msg_en.ts +++ b/src/QDS/resources/QDS_msg_en.ts @@ -17,7 +17,7 @@ DATA_MIN_LIMIT - is greater than %1 + greater than %1 DATA_FLOAT @@ -41,7 +41,7 @@ DATA_MAX_LIMIT - is less than %1 + less than %1 DATA_INPUT_VALUE diff --git a/src/Qtx/Qtx.pro b/src/Qtx/Qtx.pro deleted file mode 100644 index f5d564325..000000000 --- a/src/Qtx/Qtx.pro +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = qtx -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -QT += xml -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += QTX_EXPORTS - -HEADERS = Qtx.h -HEADERS += QtxAction.h -HEADERS += QtxActionMenuMgr.h -HEADERS += QtxActionMgr.h -HEADERS += QtxActionSet.h -HEADERS += QtxActionToolMgr.h -HEADERS += QtxColorScale.h -HEADERS += QtxComboBox.h -HEADERS += QtxDialog.h -HEADERS += QtxDockAction.h -HEADERS += QtxDockWidget.h -HEADERS += QtxDoubleSpinBox.h -HEADERS += QtxEvalExpr.h -HEADERS += QtxGridBox.h -HEADERS += QtxGroupBox.h -HEADERS += QtxIntSpinBox.h -HEADERS += QtxListAction.h -HEADERS += QtxLogoMgr.h -HEADERS += QtxMainWindow.h -HEADERS += QtxMap.h -HEADERS += QtxMRUAction.h -HEADERS += QtxPathDialog.h -HEADERS += QtxPopupMgr.h -HEADERS += QtxResourceMgr.h -HEADERS += QtxSplash.h -HEADERS += QtxToolBar.h -HEADERS += QtxToolTip.h -HEADERS += QtxValidator.h -HEADERS += QtxWorkspace.h -HEADERS += QtxWorkspaceAction.h -HEADERS += QtxWorkstack.h -HEADERS += QtxWorkstackAction.h -#HEADERS += QtxDirListEditor.h -#HEADERS += QtxListBox.h -#HEADERS += QtxListResourceEdit.h -#HEADERS += QtxListView.h -#HEADERS += QtxPopupMenu.h -#HEADERS += QtxResourceEdit.h -#HEADERS += QtxTable.h - -SOURCES = Qtx.cxx -SOURCES += QtxAction.cxx -SOURCES += QtxActionMenuMgr.cxx -SOURCES += QtxActionMgr.cxx -SOURCES += QtxActionSet.cxx -SOURCES += QtxActionToolMgr.cxx -SOURCES += QtxColorScale.cxx -SOURCES += QtxComboBox.cxx -SOURCES += QtxDialog.cxx -SOURCES += QtxDockAction.cxx -SOURCES += QtxDockWidget.cxx -SOURCES += QtxDoubleSpinBox.cxx -SOURCES += QtxEvalExpr.cxx -SOURCES += QtxGridBox.cxx -SOURCES += QtxGroupBox.cxx -SOURCES += QtxIntSpinBox.cxx -SOURCES += QtxListAction.cxx -SOURCES += QtxLogoMgr.cxx -SOURCES += QtxMainWindow.cxx -SOURCES += QtxMRUAction.cxx -SOURCES += QtxPathDialog.cxx -SOURCES += QtxPopupMgr.cxx -SOURCES += QtxResourceMgr.cxx -SOURCES += QtxSplash.cxx -SOURCES += QtxToolBar.cxx -SOURCES += QtxToolTip.cxx -SOURCES += QtxValidator.cxx -SOURCES += QtxWorkspace.cxx -SOURCES += QtxWorkspaceAction.cxx -SOURCES += QtxWorkstack.cxx -SOURCES += QtxWorkstackAction.cxx -#SOURCES += QtxDirListEditor.cxx -#SOURCES += QtxListBox.cxx -#SOURCES += QtxListResourceEdit.cxx -#SOURCES += QtxListView.cxx -#SOURCES += QtxPopupMenu.cxx -#SOURCES += QtxResourceEdit.cxx -#SOURCES += QtxTable.cxx - -includes.files = $$HEADERS -includes.path = ../../include - -INSTALLS += includes diff --git a/src/QxGraph/QxGraph.pro b/src/QxGraph/QxGraph.pro deleted file mode 100644 index c2f926b99..000000000 --- a/src/QxGraph/QxGraph.pro +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = QxGraph -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -QT_INCLUDES = $$(QTDIR)/include $$(QTDIR)/include/QtCore $$(QTDIR)/include/QtGui $$(QTDIR)/include/QtOpenGL $$(QTDIR)/include/QtXml - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -PYTHON_INCLUDES = $$(PYTHONHOME)/include/python2.4 - -BOOST_CPPFLAGS = $$(BOOSTDIR)/include - -QT_MT_LIBS = -L$$(QTDIR)/lib -lQtCore -lQtXml -lQtGui -lQtOpenGL - -INCLUDEPATH += $${QT_INCLUDES} $${CAS_CPPFLAGS} $${PYTHON_INCLUDES} $${BOOST_CPPFLAGS} ../Qtx ../SUIT - -LIBS += $${QT_MT_LIBS} -L../../lib -lsuit - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += QXGRAPH_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = QxGraph_ActiveItem.h -HEADERS += QxGraph_Prs.h -HEADERS += QxGraph_Canvas.h -HEADERS += QxGraph_CanvasView.h -HEADERS += QxGraph_ViewWindow.h -HEADERS += QxGraph_ViewManager.h -HEADERS += QxGraph_ViewModel.h -HEADERS += QxGraph_Def.h -HEADERS += QxGraph.h - -SOURCES = QxGraph_Prs.cxx -SOURCES += QxGraph_Canvas.cxx -SOURCES += QxGraph_CanvasView.cxx -SOURCES += QxGraph_ViewWindow.cxx -SOURCES += QxGraph_ViewManager.cxx -SOURCES += QxGraph_ViewModel.cxx - -TRANSLATIONS = resources/QxGraph_images.ts \ - resources/QxGraph_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/ResExporter/ResExporter.pro b/src/ResExporter/ResExporter.pro deleted file mode 100644 index acdf7e1f0..000000000 --- a/src/ResExporter/ResExporter.pro +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = -TARGET = ResourceExporter -DESTDIR = ../../bin -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -INCLUDEPATH += ../../include ../Qtx ../SUIT -LIBS += -L../../lib -lqtx -lsuit - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += - -SOURCES = ResourceExporter.cxx - diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataModelLight.cxx b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataModelLight.cxx index 50c68b261..837029613 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataModelLight.cxx +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataModelLight.cxx @@ -40,7 +40,8 @@ SALOME_PYQT_DataModelLight::SALOME_PYQT_DataModelLight(CAM_Module * theModule) : LightApp_DataModel( theModule ), myFileName( "" ), - myStudyURL( "" ) + myStudyURL( "" ), + myModified( false ) { } @@ -66,6 +67,8 @@ bool SALOME_PYQT_DataModelLight::open( const QString& theURL, CAM_Study* study, return false; LightApp_DataModel::open( theURL, aDoc, theListOfFiles ); + + setModified( false ); return aModule->open(theListOfFiles); @@ -93,6 +96,9 @@ bool SALOME_PYQT_DataModelLight::save( QStringList& theListOfFiles) theListOfFiles.append(QString(aTmpDir.c_str())); int listSize = theListOfFiles.size(); aModule->save(theListOfFiles); + + setModified( false ); + //Return true if in the List of files was added item(s) //else return false return theListOfFiles.size() > listSize; @@ -115,25 +121,57 @@ bool SALOME_PYQT_DataModelLight::create( CAM_Study* study ) return true; } +//================================================================================= +// function : dumpPython() +// purpose : Re-defined from LigthApp_DataModel in order to participate +// in dump study process +//================================================================================= +bool SALOME_PYQT_DataModelLight::dumpPython( const QString& theURL, + CAM_Study* theStudy, + bool isMultiFile, + QStringList& theListOfFiles ) +{ + MESSAGE("SALOME_PYQT_DataModelLight::dumpPython()"); + + LightApp_DataModel::dumpPython( theURL, theStudy, isMultiFile, theListOfFiles ); + + LightApp_Study* study = dynamic_cast( theStudy ); + SALOME_PYQT_ModuleLight* aModule = dynamic_cast(module()); + + if(!aModule || !study) + return false; + + std::string aTmpDir = study->GetTmpDir( theURL.toLatin1().constData(), isMultiFile ); + + theListOfFiles.append( QString( aTmpDir.c_str() ) ); + int oldSize = theListOfFiles.size(); + + aModule->dumpPython( theListOfFiles ); + + //Return true if some items have been added, else return false + return theListOfFiles.size() > oldSize; +} + //================================================================================= // function : isModified() -// purpose : default implementation, always returns false so as not to mask study's isModified() +// purpose : returns this model's modification status that can be controlled +// with help of setModified() calls by the underlying Python module //================================================================================= bool SALOME_PYQT_DataModelLight::isModified() const { - return false; + return myModified; } //================================================================================= -// function : isSaved() -// purpose : default implementation, always returns true so as not to mask study's isSaved() +// function : setModified() +// purpose : sets the model's modification status, should be used by +// the underlying Python module when its data changes. //================================================================================= -bool SALOME_PYQT_DataModelLight::isSaved() const +void SALOME_PYQT_DataModelLight::setModified( bool flag ) { - return true; + myModified = flag; } - //================================================================================= // function : close() // purpose : Close data model operation @@ -152,12 +190,12 @@ void SALOME_PYQT_DataModelLight::update ( LightApp_DataObject* theObj, LightApp_ return; } -LightApp_ModuleObject* SALOME_PYQT_DataModelLight::getRoot() +CAM_DataObject* SALOME_PYQT_DataModelLight::getRoot() { LightApp_Study* study = dynamic_cast( module()->application()->activeStudy() ); - LightApp_ModuleObject *aModelRoot = dynamic_cast(root()); - if(aModelRoot == NULL) { - aModelRoot = new LightApp_ModuleObject(this,study->root()); + CAM_ModuleObject *aModelRoot = dynamic_cast(root()); + if(study && aModelRoot == NULL) { + aModelRoot = createModuleObject( study->root() ); aModelRoot->setDataModel( this ); setRoot(aModelRoot); } diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataModelLight.h b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataModelLight.h index b49dc9cc2..6a9e35391 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataModelLight.h +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataModelLight.h @@ -46,17 +46,22 @@ public: virtual bool saveAs ( const QString&, CAM_Study*, QStringList& ); virtual bool close (); virtual bool create ( CAM_Study* ); + virtual bool dumpPython( const QString&, + CAM_Study*, + bool, + QStringList& ); virtual bool isModified () const; - virtual bool isSaved () const; + void setModified( bool ); virtual void update ( LightApp_DataObject* = 0, LightApp_Study* = 0 ); - LightApp_ModuleObject* getRoot(); + CAM_DataObject* getRoot(); private: QString myFileName; QString myStudyURL; + bool myModified; }; #endif // SALOME_PYQT_DATAMODELLIGHT_H diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataObjectLight.cxx b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataObjectLight.cxx index ecdfce0a7..4db8c6fb6 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataObjectLight.cxx +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataObjectLight.cxx @@ -69,6 +69,24 @@ QString SALOME_PYQT_DataObjectLight::entry() const return myEntry; } +//================================================================================= +// function : SALOME_PYQT_DataObjectLight::refEntry() +// purpose : return entry of the data object referenced by this one (if any) +//================================================================================= +QString SALOME_PYQT_DataObjectLight::refEntry() const +{ + return myRefEntry; +} + +//================================================================================= +// function : SALOME_PYQT_DataObjectLight::setRefEntry() +// purpose : sets entry of the data object referenced by this one +//================================================================================= +void SALOME_PYQT_DataObjectLight::setRefEntry( const QString& refEntry ) +{ + myRefEntry = refEntry; +} + //================================================================================= // function : SALOME_PYQT_DataObjectLight::name() // purpose : return name of object @@ -100,6 +118,32 @@ QString SALOME_PYQT_DataObjectLight::toolTip(const int index) const return myToolTip; } +//================================================================================= +// function : SALOME_PYQT_DataObjectLight::toolTip() +// purpose : return toolTip of object +//================================================================================= +QColor SALOME_PYQT_DataObjectLight::color( const ColorRole role, const int id ) const +{ + QColor c; + + switch ( role ) + { + case Text: + case Foreground: + if ( !isReference() ) + c = myColor; + break; + + default: + break; + } + + // Issue 21379: LightApp_DataObject::color() defines colors for valid references + if ( !c.isValid() ) + c = LightApp_DataObject::color( role, id ); + + return c; +} bool SALOME_PYQT_DataObjectLight::setName(const QString& name) { @@ -126,3 +170,8 @@ void SALOME_PYQT_DataObjectLight::setToolTip(const QString& tooltip) { myToolTip = tooltip; } + +void SALOME_PYQT_DataObjectLight::setColor(const QColor& color) +{ + myColor = color; +} diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataObjectLight.h b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataObjectLight.h index 80d5a2ae0..08b59fc97 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataObjectLight.h +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_DataObjectLight.h @@ -42,20 +42,28 @@ class SALOME_PYQT_LIGHT_EXPORT SALOME_PYQT_DataObjectLight : public virtual Ligh virtual ~SALOME_PYQT_DataObjectLight(); virtual QString entry() const; + + virtual QString refEntry() const; + void setRefEntry( const QString& refEntry ); virtual QString name() const; - QPixmap icon(const int = NameId) const; - QString toolTip(const int = NameId) const; - - bool setName(const QString& name); - void setIcon(const QString& icon); - void setToolTip(const QString& tooltip); + virtual QPixmap icon(const int = NameId) const; + virtual QString toolTip(const int = NameId) const; + + bool setName(const QString& name); + void setIcon(const QString& icon); + void setToolTip(const QString& tooltip); + + virtual QColor color( const ColorRole, const int = NameId ) const; + void setColor(const QColor& color); private: QString myEntry; + QString myRefEntry; QString myName; QString myToolTip; QPixmap myIcon; + QColor myColor; }; #endif // SALOME_PYQT_DATAOBJECTLIGHT_H diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_ModuleLight.cxx b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_ModuleLight.cxx index cd02deade..512e2bfec 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_ModuleLight.cxx +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_ModuleLight.cxx @@ -2454,8 +2454,96 @@ void SALOME_PYQT_ModuleLight::saveEvent(QStringList& theListOfFiles) return; if ( PyObject_HasAttrString(myModule, (char*)"saveFiles") ) { + // temporary set myInitModule because saveEvent() 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 + myInitModule = this; + PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"saveFiles", (char*)"s", (*it).toLatin1().constData())); + + myInitModule = 0; + + 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); + theListOfFiles.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 ) ) { + theListOfFiles.append( PyString_AsString( value ) ); + } + } + } + } + } +} + +/* + * Python dump request. + * Called when user activates dump study operation. + */ +void SALOME_PYQT_ModuleLight::dumpPython(QStringList& theListOfFiles) +{ + MESSAGE("SALOME_PYQT_ModuleLight::dumpPython()") + // perform synchronous request to Python event dispatcher + class DumpEvent: public PyInterp_LockRequest + { + public: + DumpEvent(PyInterp_Interp* _py_interp, + SALOME_PYQT_ModuleLight* _obj, + QStringList& _files_list) + : PyInterp_LockRequest( _py_interp, 0, true ), // this request should be processed synchronously (sync == true) + myObj( _obj ) , + myFilesList(_files_list) {} + protected: + virtual void execute() + { + myObj->dumpEvent(myFilesList); + } + private: + SALOME_PYQT_ModuleLight* myObj; + QStringList& myFilesList; + }; + + // Posting the request only if dispatcher is not busy! + // Executing the request synchronously + if ( !PyInterp_Dispatcher::Get()->IsBusy() ) + PyInterp_Dispatcher::Get()->Exec( new DumpEvent( myInterp, this, theListOfFiles ) ); +} + +void SALOME_PYQT_ModuleLight::dumpEvent(QStringList& theListOfFiles) +{ + MESSAGE("SALOME_PYQT_ModuleLight::dumpEvent()"); + QStringList::Iterator it = theListOfFiles.begin(); + // Python interpreter should be initialized and Python module should be + // import first + if ( !myInterp || !myModule || (it == theListOfFiles.end())) + return; + + if ( PyObject_HasAttrString(myModule, (char*)"dumpStudy") ) { + // temporary set myInitModule because dumpEvent() 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 + myInitModule = this; + + PyObjWrapper res( PyObject_CallMethod( myModule, (char*)"dumpStudy", + (char*)"s", (*it).toLatin1().constData())); + + myInitModule = 0; + if( !res ) { PyErr_Print(); } @@ -2681,6 +2769,53 @@ void SALOME_PYQT_ModuleLight::setToolTip(const QString& obj, const QString& tool } } +/* + * Return color of object + */ +QColor SALOME_PYQT_ModuleLight::getColor(const QString& obj) +{ + SALOME_PYQT_DataObjectLight* dataObj = findObject( obj ); + if( dataObj ) { + return dataObj->color( SUIT_DataObject::Foreground ); + } + return QColor(); +} + +/* + * Set color for object + */ +void SALOME_PYQT_ModuleLight::setColor(const QString& obj, const QColor& color) +{ + SALOME_PYQT_DataObjectLight* dataObj = findObject( obj ); + if( dataObj ) { + dataObj->setColor( color ); + } +} + +/* + * Return entry of the referenced object (if any) + */ +QString SALOME_PYQT_ModuleLight::getReference(const QString& obj) +{ + SALOME_PYQT_DataObjectLight* dataObj = findObject(obj); + if(dataObj) { + return dataObj->refEntry(); + } + return QString::null; +} + + +/* + * Set entry of the referenced object + */ +void SALOME_PYQT_ModuleLight::setReference(const QString& obj, const QString& refEntry) +{ + SALOME_PYQT_DataObjectLight* dataObj = findObject(obj); + if(dataObj) { + dataObj->setRefEntry(refEntry); + } +} + /* * Remove object by entry */ @@ -2775,3 +2910,11 @@ CAM_DataModel* SALOME_PYQT_ModuleLight::createDataModel() MESSAGE( "SALOME_PYQT_ModuleLight::createDataModel()" ); return new SALOME_PYQT_DataModelLight(this); } + +/*! + * Returns the Python module object currently loaded. + */ +PyObject* SALOME_PYQT_ModuleLight::getPythonModule() +{ + return myModule; +} diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_ModuleLight.h b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_ModuleLight.h index 0285aef55..cf07211b3 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_ModuleLight.h +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_ModuleLight.h @@ -123,24 +123,32 @@ public: void setPreferenceProperty( const int, const QString&, const QVariant& ); - void save(QStringList& theListOfFiles); - - bool open(QStringList theListOfFiles); + void save(QStringList& theListOfFiles); + bool open(QStringList theListOfFiles); + void dumpPython(QStringList& theListOfFiles); /*create new SALOME_PYQT_DataObjectLight and return its entry*/ - QString createObject(const QString& parent); - QString createObject(const QString& name, - const QString& iconname, - const QString& tooltip, - const QString& parent); + QString createObject(const QString& parent); + QString createObject(const QString& name, + const QString& iconname, + const QString& tooltip, + const QString& parent); /*Sets Name, Icon and Tool Tip for object*/ void setName(const QString& obj,const QString& iconname); void setIcon(const QString& obj,const QString& name); - void setToolTip(const QString& obj, const QString& name); + void setToolTip(const QString& obj, const QString& tooltip); /*Gets Name and Tool Tip for object*/ QString getName(const QString& obj); QString getToolTip(const QString& obj); + + void setColor(const QString& obj, const QColor& color); + QColor getColor(const QString& obj); + + void setReference( const QString& obj, + const QString& refEntry ); + QString getReference( const QString& obj ); + /*remove object*/ void removeObject(const QString& obj); /*remove child*/ @@ -148,6 +156,9 @@ public: /*return list of child objets*/ QStringList getChildren(const QString& obj, const bool rec); + /*Access to the underlying Python module object */ + PyObject* getPythonModule(); + public slots: virtual bool activateModule( SUIT_Study* ); @@ -190,6 +201,7 @@ private: void connectView( const SUIT_ViewWindow* ); void saveEvent(QStringList& theListOfFiles); + void dumpEvent(QStringList& theListOfFiles); void openEvent(QStringList theListOfFiles, bool& opened); SALOME_PYQT_DataObjectLight* findObject(const QString& entry); diff --git a/src/SALOME_PYQT/SalomePyQt/SalomePyQt.cxx b/src/SALOME_PYQT/SalomePyQt/SalomePyQt.cxx index 023840207..632f4713d 100644 --- a/src/SALOME_PYQT/SalomePyQt/SalomePyQt.cxx +++ b/src/SALOME_PYQT/SalomePyQt/SalomePyQt.cxx @@ -32,6 +32,7 @@ #endif #include // this include must be first!!! +#include #include "SalomePyQt.h" #include @@ -519,6 +520,56 @@ const QString SalomePyQt::getActiveComponent() return ProcessEvent( new TGetActiveComponentEvent() ); } +/*! + \fn PyObject* SalomePyQt::getActivePythonModule() + \brief Access to Python module object currently loaded into SALOME_PYQT_ModuleLight container. + \return Python module object currently loaded into SALOME_PYQT_ModuleLight container +*/ + +class TGetActivePyModuleEvent: public SALOME_Event +{ +public: + typedef PyObject* TResult; + TResult myResult; + TGetActivePyModuleEvent() : myResult( 0 ) {} + virtual void Execute() + { + SALOME_PYQT_ModuleLight* module = getActiveModule(); + if ( module ) + myResult = (PyObject*)module->getPythonModule(); + } +}; +PyObject* SalomePyQt::getActivePythonModule() +{ + return ProcessEvent( new TGetActivePyModuleEvent() ); +} + +/*! + \fn bool SalomePyQt::activateModule( const QString& modName ) + \brief Activates SALOME module with the given name + \return True if the module has been activated and False otherwise. +*/ + +class TActivateModuleEvent: public SALOME_Event +{ +public: + typedef bool TResult; + TResult myResult; + QString myModuleName; + TActivateModuleEvent( const QString& modName ) + : myResult( false ), myModuleName( modName ) {} + virtual void Execute() + { + if ( LightApp_Application* anApp = getApplication() ) { + myResult = anApp->activateModule( myModuleName ); + } + } +}; +bool SalomePyQt::activateModule( const QString& modName ) +{ + return ProcessEvent( new TActivateModuleEvent( modName ) ); +} + /*! \brief Update an Object Browser of the specified (by identifier) study. @@ -562,6 +613,76 @@ void SalomePyQt::updateObjBrowser( const int studyId, bool updateSelection ) ProcessVoidEvent( new TEvent( studyId, updateSelection ) ); } + +/*! + SalomePyQt::isModified() + \return The modification status of the data model + for the currently active Python module + \sa setModified() +*/ +class TIsModifiedEvent: public SALOME_Event +{ +public: + typedef bool TResult; + TResult myResult; + TIsModifiedEvent() : myResult( false ) {} + virtual void Execute() + { + SALOME_PYQT_ModuleLight* module = getActiveModule(); + if ( !module ) + return; + + SALOME_PYQT_DataModelLight* aModel = + dynamic_cast( module->dataModel() ); + if ( aModel ) + myResult = aModel->isModified(); + } +}; +bool SalomePyQt::isModified() +{ + return ProcessEvent(new TIsModifiedEvent()); +} + +/*! + SalomePyQt::setModified() + + Sets the modification status of the data model for + the currently active Python module. This method should be used + by the Python code in order to enable/disable "Save" operation + depending on the module's data state. + + \param New modification status of the data model + + \sa isModified() +*/ +void SalomePyQt::setModified( bool flag ) +{ + class TEvent: public SALOME_Event + { + bool myFlag; + public: + TEvent( bool flag ) + : myFlag( flag ) {} + virtual void Execute() + { + SALOME_PYQT_ModuleLight* module = getActiveModule(); + if ( !module ) + return; + + SALOME_PYQT_DataModelLight* aModel = + dynamic_cast( module->dataModel() ); + LightApp_Application* aLApp = + dynamic_cast( module->application() ); + if ( !aModel || !aLApp ) + return; + + aModel->setModified( myFlag ); + aLApp->updateActions(); + } + }; + ProcessVoidEvent( new TEvent( flag ) ); +} + /*! \brief Default resource file section name. \internal @@ -2925,6 +3046,52 @@ void SalomePyQt::setToolTip(const QString& obj,const QString& tooltip) ProcessVoidEvent(new TSetToolTipEvent(obj,tooltip)); } +/*! + SalomePyQt::setReference(obj,refEntry) + Set entry to referenced object +*/ +class TSetRefEvent: public SALOME_Event +{ +public: + QString myObj; + QString myRefEntry; + TSetRefEvent( const QString& obj, + const QString& refEntry) : myObj(obj), + myRefEntry(refEntry) {} + virtual void Execute() { + SALOME_PYQT_ModuleLight* module = getActiveModule(); + if ( module ) + module->setReference(myObj,myRefEntry); + } +}; +void SalomePyQt::setReference(const QString& obj,const QString& refEntry) +{ + ProcessVoidEvent(new TSetRefEvent(obj,refEntry)); +} + +/*! + SalomePyQt::setColor(obj,color) + Set object color +*/ +class TSetColorEvent: public SALOME_Event +{ +public: + QString myObj; + QColor myColor; + TSetColorEvent( const QString& obj, + const QColor& color) : myObj(obj), + myColor(color) {} + virtual void Execute() { + SALOME_PYQT_ModuleLight* module = getActiveModule(); + if ( module ) + module->setColor(myObj,myColor); + } +}; +void SalomePyQt::setColor(const QString& obj,const QColor& color) +{ + ProcessVoidEvent(new TSetColorEvent(obj,color)); +} + /*! SalomePyQt::getName(obj) Return name of object @@ -2970,6 +3137,49 @@ QString SalomePyQt::getToolTip(const QString& obj) return ProcessEvent(new TGetToolTipEvent(obj)); } +/*! + SalomePyQt::getReference(obj) + Return entry of the referenced object (if any) +*/ +class TGetRefEvent: public SALOME_Event +{ +public: + typedef QString TResult; + TResult myResult; + QString myObj; + TGetRefEvent( const QString& obj ) : myObj(obj) {} + virtual void Execute() { + SALOME_PYQT_ModuleLight* module = getActiveModule(); + if ( module ) + myResult = module->getReference(myObj); + } +}; +QString SalomePyQt::getReference(const QString& obj) +{ + return ProcessEvent(new TGetRefEvent(obj)); +} + +/*! + SalomePyQt::getColor(obj) + Return the color of the object +*/ +class TGetColorEvent: public SALOME_Event +{ +public: + typedef QColor TResult; + TResult myResult; + QString myObj; + TGetColorEvent( const QString& obj ) : myObj(obj) {} + virtual void Execute() { + SALOME_PYQT_ModuleLight* module = getActiveModule(); + if ( module ) + myResult = module->getColor(myObj); + } +}; +QColor SalomePyQt::getColor(const QString& obj) +{ + return ProcessEvent(new TGetColorEvent(obj)); +} /*! SalomePyQt::removeChild(obj) diff --git a/src/SALOME_PYQT/SalomePyQt/SalomePyQt.h b/src/SALOME_PYQT/SalomePyQt/SalomePyQt.h index 41ca60456..2fd65b871 100644 --- a/src/SALOME_PYQT/SalomePyQt/SalomePyQt.h +++ b/src/SALOME_PYQT/SalomePyQt/SalomePyQt.h @@ -26,6 +26,8 @@ #ifndef SALOME_PYQT_H #define SALOME_PYQT_H +#include + #include #include #include @@ -127,8 +129,13 @@ public: static int getStudyId(); static void putInfo( const QString&, const int = 0 ); static const QString getActiveComponent(); + static PyObject* getActivePythonModule(); + static bool activateModule( const QString& ); static void updateObjBrowser( const int = 0, bool = true ); + static bool isModified(); + static void setModified( bool ); + static QString getFileName ( QWidget*, const QString&, const QStringList&, const QString&, bool ); static QStringList getOpenFileNames ( QWidget*, const QString&, const QStringList&, const QString& ); static QString getExistingDirectory( QWidget*, const QString&, const QString& ); @@ -148,6 +155,13 @@ public: static QString getName(const QString& obj); static QString getToolTip(const QString& obj); + static void setColor(const QString& obj,const QColor& color); + static QColor getColor(const QString& obj); + + static void setReference( const QString& obj, + const QString& refEntry ); + static QString getReference( const QString& obj ); + static QIcon loadIcon( const QString&, const QString& ); static void helpContext( const QString&, const QString& ); diff --git a/src/SALOME_PYQT/SalomePyQt/SalomePyQt.sip b/src/SALOME_PYQT/SalomePyQt/SalomePyQt.sip index ad092f66a..7c2090b6f 100644 --- a/src/SALOME_PYQT/SalomePyQt/SalomePyQt.sip +++ b/src/SALOME_PYQT/SalomePyQt/SalomePyQt.sip @@ -213,7 +213,12 @@ public: static int getStudyId() /ReleaseGIL/ ; static void putInfo( const QString&, const int = 0 ) /ReleaseGIL/ ; static const QString getActiveComponent() /ReleaseGIL/ ; - static void updateObjBrowser( const int = 0, bool = true ) /ReleaseGIL/ ; + static SIP_PYOBJECT getActivePythonModule() /ReleaseGIL/ ; + static bool activateModule( const QString& ) /ReleaseGIL/ ; + static void updateObjBrowser( const int = 0, bool = true ) /ReleaseGIL/ ; + + static bool isModified() /ReleaseGIL/ ; + static void setModified( bool ) /ReleaseGIL/ ; static QString getFileName ( QWidget*, const QString&, const QStringList&, const QString&, bool ) /ReleaseGIL/ ; static QStringList getOpenFileNames ( QWidget*, const QString&, const QStringList&, const QString& ) /ReleaseGIL/ ; @@ -221,9 +226,9 @@ public: static QString createObject( const QString& = QString("") ) /ReleaseGIL/ ; static QString createObject( const QString&, - const QString&, - const QString&, - const QString& = QString("") ) /ReleaseGIL/ ; + const QString&, + const QString&, + const QString& = QString("") ) /ReleaseGIL/ ; static void setName(const QString& ,const QString& ) /ReleaseGIL/ ; static void setIcon(const QString& ,const QString& ) /ReleaseGIL/ ; @@ -231,6 +236,12 @@ public: static QString getName(const QString& ) /ReleaseGIL/ ; static QString getToolTip(const QString& ) /ReleaseGIL/ ; + static void setColor( const QString&, const QColor& ) /ReleaseGIL/ ; + static QColor getColor( const QString& ) /ReleaseGIL/ ; + + static void setReference( const QString& ,const QString& ) /ReleaseGIL/ ; + static QString getReference( const QString& ) /ReleaseGIL/ ; + static void removeObject(const QString& ) /ReleaseGIL/ ; static void removeChild(const QString& = QString("") ) /ReleaseGIL/ ; static QStringList getChildren(const QString&=QString("") , const bool = false) /ReleaseGIL/ ; diff --git a/src/SALOME_SWIG/Help.py b/src/SALOME_SWIG/Help.py deleted file mode 100755 index 69ef77c2e..000000000 --- a/src/SALOME_SWIG/Help.py +++ /dev/null @@ -1,143 +0,0 @@ -# -*- coding: iso-8859-1 -*- -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 : Help.py -# Author : Paul RASCLE, EDF -# Module : SALOME -# $Header$ -# -class SalomeDoc: - def __init__(self, aDoc): - self.doc = aDoc - def __repr__(self): - print self.doc - return "---" - def salome(self): - doc_salome = ''' -MODULE : salome ---------------- -module salome gives access to Salome ressources: -variables: - - salome:orb : CORBA - salome.naming_service : instance of naming Service class - methods: - Resolve(name) : find a CORBA object (ior) by its pathname - Register(name) : register a CORBA object under a pathname - salome.lcc : instance of lifeCycleCORBA class - methods: - FindOrLoadComponent(server,name) : - obtain an Engine (CORBA object) - or launch the Engine if not found, - with a Server name and an Engine name - salome.sg - methods: - updateObjBrowser(bool): - getActiveStudyId(): - getActiveStudyName(): - - SelectedCount(): returns number of selected objects - getSelected(i): returns entry of selected object number i - getAllSelected(): returns list of entry of selected objects - AddIObject(Entry): select an existing Interactive object - RemoveIObject(Entry): remove object from selection - ClearIObjects(): clear selection - - Display(*Entry): - DisplayOnly(Entry): - Erase(Entry): - DisplayAll(): - EraseAll(): - - IDToObject(Entry): returns CORBA reference from entry - - salome.myStudyName : active Study Name - salome.myStudyId : active Study Id - salome.myStudy : the active Study itself (CORBA ior) - methods : defined in SALOMEDS.idl - -methods: - salome.DumpStudy(study) : Dump a study, given the ior ---- -''' - print doc_salome - - def geompy(self): - doc_geompy = ''' -MODULE : geompy ---------------- -module geompy provides an encapsulation of GEOM Engine methods -variables: - geompy.geom : a Geometry Engine, found or loaded - at first import of module geompy. - methods : defined in GEOM_Gen.idl - geompy.myBuilder : a study builder - geompy.father : GEOM root in current study (salome.myStudy) - -methods: - addToStudy(aShape, aName) : add the shape into the current study - --- all methods of GEOM_Gen.idl that returns a shape are encapsulated, - with the same interface : shapes are named with their ior -''' - print doc_geompy - - def supervision(self): - doc_supervision = ''' -MODULES : SALOME_SuperVisionEditor and SALOME_SuperVisionExecutor ------------------------------------------------------------------ -this modules provide access to Editor and Executor Engine methods - -See SUPERV.idl - -In order to run the example (supervisionexample.py) - - Type : from supervisionexample import * - supervisionexample.py contains comments - -A new python example avoids references to LifeCycleCORBA - avoids references to NamingService - avoids references to ModuleCatalog - avoids SuperVisionComponent creation - allows G.Input(...) instead of AddInput(G,...) - replaces Editor/Executor with Graph - allows Nodes, Ports and Links CORBA objects - shortens methods names - ... - - See /SuperVisionTest/resources/GraphExample.py - and GraphExample.xml ---- -''' - print doc_supervision - - - -help = SalomeDoc(''' -Availables modules: - salome : gives access to Salome ressources - geompy : encapsulation of GEOM Engine methods - supervision : gives access to SuperVision Engine -To obtain specific help on a module "truc", type: help.truc() -To run an example, type: import example3 -''') - diff --git a/src/SALOME_SWIG/PyInterp.py b/src/SALOME_SWIG/PyInterp.py deleted file mode 100755 index ab3a794b8..000000000 --- a/src/SALOME_SWIG/PyInterp.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: iso-8859-1 -*- -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 : PyInterp.py -# Author : Paul RASCLE, EDF -# Module : SALOME -# $Header$ -# -import sys -from omniORB import CORBA -from LifeCycleCORBA import * -from libSALOME_Swig import * -import SALOMEDS -from SALOME_NamingServicePy import * - - #-------------------------------------------------------------------------- - -def DumpComponent(Study, SO, offset): - it = Study.NewChildIterator(SO) - Builder = Study.NewBuilder() - while it.More(): - CSO = it.Value() - it.Next() - anAttr = Builder.FindOrCreateAttribute(CSO, "AttributeName") - AtName = anAttr._narrow(SALOMEDS.AttributeName) - t_name = AtName.Value() - if t_name[0] == 1: - ofs = 1 - a = "" - while ofs <= offset: - a = a + "--" - ofs = ofs +1 - print a + ">" + CSO.GetID() + " " + t_name[1] - t_RefSO = CSO.ReferencedObject() - if t_RefSO[0] == 1: - RefSO = t_RefSO[1] - ofs = 1 - a = "" - while ofs <= offset: - a = a + " " - ofs = ofs +1 - print a + ">" + RefSO.GetID() - DumpComponent(Study, CSO, offset+2) - - #-------------------------------------------------------------------------- - -def DumpStudy(Study): - itcomp = Study.NewComponentIterator() - while itcomp.More(): - SC = itcomp.Value() - itcomp.Next() - name = SC.ComponentDataType() - print "-> ComponentDataType is " + name - DumpComponent(Study, SC, 1) - - - #-------------------------------------------------------------------------- - -# initialise the ORB -orb = CORBA.ORB_init([''], CORBA.ORB_ID) - -# create an LifeCycleCORBA instance -lcc = LifeCycleCORBA(orb) - -# create an SALOMEGUI_Swig instance -sg = SALOMEGUI_Swig() - -#create an naming service instance -naming_service = SALOME_NamingServicePy_i(orb) - -# get active study name and id -myStudyName = sg.getActiveStudyName() -print myStudyName - -myStudyId = sg.getActiveStudyId() -print myStudyId - -# get Study Manager reference -obj = naming_service.Resolve('myStudyManager') -myStudyManager = obj._narrow(SALOMEDS.StudyManager) - -# get active study -myStudy = myStudyManager.GetStudyByName(myStudyName) - diff --git a/src/SALOME_SWIG/salome.py b/src/SALOME_SWIG/salome.py deleted file mode 100755 index 52776da97..000000000 --- a/src/SALOME_SWIG/salome.py +++ /dev/null @@ -1,159 +0,0 @@ -# -*- coding: iso-8859-1 -*- -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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.py -# Author : Paul RASCLE, EDF -# Module : SALOME -# $Header$ -# -from omniORB import CORBA -from LifeCycleCORBA import * -from libSALOME_Swig import * -import SALOMEDS -from SALOME_NamingServicePy import * - -from SALOME_utilities import * - -#-------------------------------------------------------------------------- - -def DumpComponent(Study, SO, offset): - it = Study.NewChildIterator(SO) - Builder = Study.NewBuilder() - while it.More(): - CSO = it.Value() - it.Next() - anAttr = Builder.FindOrCreateAttribute(CSO, "AttributeName") - AtName = anAttr._narrow(SALOMEDS.AttributeName) - t_name = AtName.Value() - if t_name[0] == 1: - ofs = 1 - a = "" - while ofs <= offset: - a = a + "--" - ofs = ofs +1 - MESSAGE( a + ">" + str(CSO.GetID()) + " " + str(t_name[1]) ) - t_RefSO = CSO.ReferencedObject() - if t_RefSO[0] == 1: - RefSO = t_RefSO[1] - ofs = 1 - a = "" - while ofs <= offset: - a = a + " " - ofs = ofs +1 - MESSAGE( a + ">" + str(RefSO.GetID()) ) - DumpComponent(Study, CSO, offset+2) - - #-------------------------------------------------------------------------- - -def DumpStudy(Study): - itcomp = Study.NewComponentIterator() - while itcomp.More(): - SC = itcomp.Value() - itcomp.Next() - name = SC.ComponentDataType() - MESSAGE( "-> ComponentDataType is " + name ) - DumpComponent(Study, SC, 1) - - - #-------------------------------------------------------------------------- - -def ImportComponentGUI(ComponentName): - libName = "lib" + ComponentName + "_Swig" - command = "from " + libName + " import *" - exec ( command ) - constructor = ComponentName + "_Swig()" - command = "gui = " + constructor - exec ( command ) - return gui - - #-------------------------------------------------------------------------- - -def SalomeGUIgetAllSelected(self): - selNumber = self.SelectedCount() - listSelected = [] - for i in range(selNumber): - listSelected.append(self.getSelected(i)) - return listSelected - -class SalomeGUI(SALOMEGUI_Swig): - getAllSelected = SalomeGUIgetAllSelected - - #-------------------------------------------------------------------------- - -def IDToObject(id): - myObj = None - mySO = myStudy.FindObjectID(id); - if mySO is not None: - ok, anAttr = mySO.FindAttribute("AttributeIOR") - if ok: - AtIOR = anAttr._narrow(SALOMEDS.AttributeIOR) - if AtIOR.Value() != "": - myObj = orb.string_to_object(AtIOR.Value()) - return myObj - -def ObjectToSObject(obj): - mySO = None - if obj is not None: - ior = orb.object_to_string(obj) - if ior != "": - mySO = myStudy.FindObjectIOR(ior) - return mySO - -def ObjectToID(obj): - mySO = ObjectToSObject(obj) - if mySO: - return mySO.GetID() - return "" - -def IDToSObject(id): - mySO = myStudy.FindObjectID(id); - return mySO - - #-------------------------------------------------------------------------- - -# initialise the ORB -orb = CORBA.ORB_init([''], CORBA.ORB_ID) - -# create an LifeCycleCORBA instance -lcc = LifeCycleCORBA(orb) - -# create an SALOMEGUI_Swig instance -sg = SalomeGUI() - -#create an naming service instance -naming_service = SALOME_NamingServicePy_i(orb) - -# get active study name and id -myStudyName = sg.getActiveStudyName() -MESSAGE( myStudyName ) - -myStudyId = sg.getActiveStudyId() -MESSAGE( str(myStudyId) ) - -# get Study Manager reference -obj = naming_service.Resolve('myStudyManager') -myStudyManager = obj._narrow(SALOMEDS.StudyManager) - -# get active study -myStudy = myStudyManager.GetStudyByName(myStudyName) - diff --git a/src/SOCC/SOCC.pro b/src/SOCC/SOCC.pro deleted file mode 100644 index c0bd381c1..000000000 --- a/src/SOCC/SOCC.pro +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = SOCC -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -BOOST_CPPFLAGS = $$(BOOSTDIR)/include - -INCLUDEPATH += ../../include $${CAS_CPPFLAGS} $${BOOST_CPPFLAGS} ../Qtx ../SUIT ../OBJECT ../Prs ../OCCViewer -LIBS += -L../../lib -lqtx -lsuit -lSalomeObject -lSalomePrs -lOCCViewer - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += SOCC_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = SOCC.h -HEADERS += SOCC_ViewModel.h -HEADERS += SOCC_Prs.h -HEADERS += SOCC_ViewWindow.h - -SOURCES = SOCC_ViewModel.cxx -SOURCES += SOCC_Prs.cxx -SOURCES += SOCC_ViewWindow.cxx - -includes.files = $$HEADERS -includes.path = ../../include - -INSTALLS += includes diff --git a/src/SPlot2d/SPlot2d.pro b/src/SPlot2d/SPlot2d.pro deleted file mode 100644 index 516fc8b18..000000000 --- a/src/SPlot2d/SPlot2d.pro +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = SPlot2d -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -QWTHOME=$$(QWTHOME) -QWTINC=$${QWTHOME}/include -QWTLIB=$${QWTHOME}/lib - -BOOST_CPPFLAGS = $$(BOOSTDIR)/include - -INCLUDEPATH += ../../include $${CAS_CPPFLAGS} ../../include $${QWTINC} $${BOOST_CPPFLAGS} -INCLUDEPATH += ../Qtx ../SUIT ../Plot2d ../Prs ../OBJECT -unix:LIBS += -L$${QWTLIB} -lqwt -win32:LIBS += /LIBPATH:$$(QWTLIB) -LIBS += -L../../lib -lsuit -lPlot2d -lSalomePrs - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += SPLOT2D_EXPORTS - -HEADERS = SPlot2d.h -HEADERS = SPlot2d_Curve.h -HEADERS = SPlot2d_Prs.h -HEADERS = SPlot2d_ViewModel.h -HEADERS = SPlot2d_ViewWindow.h - -SOURCES = SPlot2d_Curve.cxx -SOURCES += SPlot2d_Curve.cxx -SOURCES += SPlot2d_Prs.cxx -SOURCES += SPlot2d_ViewModel.cxx -SOURCES += SPlot2d_ViewWindow.cxx - -includes.files = $$HEADERS -includes.path = ../../include - -INSTALLS += includes diff --git a/src/STD/STD.pro b/src/STD/STD.pro deleted file mode 100644 index 4809738d1..000000000 --- a/src/STD/STD.pro +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = std -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -INCLUDEPATH += ../../include ../Qtx ../SUIT -LIBS += -L../../lib -lqtx -lsuit - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += STD_EXPORTS - -HEADERS = STD.h -HEADERS += STD_Application.h -HEADERS += STD_MDIDesktop.h -HEADERS += STD_SDIDesktop.h -HEADERS += STD_TabDesktop.h - -SOURCES = STD_Application.cxx -SOURCES += STD_MDIDesktop.cxx -SOURCES += STD_SDIDesktop.cxx -SOURCES += STD_TabDesktop.cxx - -TRANSLATIONS = resources/STD_images.ts \ - resources/STD_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm resources/*.xml resources/*.ini -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/STD/STD_Application.cxx b/src/STD/STD_Application.cxx index 3172bbe01..f3c75a91f 100755 --- a/src/STD/STD_Application.cxx +++ b/src/STD/STD_Application.cxx @@ -182,7 +182,7 @@ void STD_Application::createActions() createAction( FileSaveAsId, tr( "TOT_DESK_FILE_SAVEAS" ), QIcon(), tr( "MEN_DESK_FILE_SAVEAS" ), tr( "PRP_DESK_FILE_SAVEAS" ), - Qt::CTRL+Qt::Key_A, desk, false, this, SLOT( onSaveAsDoc() ) ); + Qt::CTRL+Qt::SHIFT+Qt::Key_S, desk, false, this, SLOT( onSaveAsDoc() ) ); createAction( EditCopyId, tr( "TOT_DESK_EDIT_COPY" ), resMgr->loadPixmap( "STD", tr( "ICON_EDIT_COPY" ) ), @@ -196,7 +196,7 @@ void STD_Application::createActions() QAction* a = createAction( ViewStatusBarId, tr( "TOT_DESK_VIEW_STATUSBAR" ), QIcon(), tr( "MEN_DESK_VIEW_STATUSBAR" ), - tr( "PRP_DESK_VIEW_STATUSBAR" ), Qt::SHIFT+Qt::Key_S, desk, true ); + tr( "PRP_DESK_VIEW_STATUSBAR" ), Qt::ALT+Qt::SHIFT+Qt::Key_S, desk, true ); a->setChecked( desk->statusBar()->isVisibleTo( desk ) ); connect( a, SIGNAL( toggled( bool ) ), this, SLOT( onViewStatusBar( bool ) ) ); @@ -205,7 +205,7 @@ void STD_Application::createActions() createAction( HelpAboutId, tr( "TOT_DESK_HELP_ABOUT" ), QIcon(), tr( "MEN_DESK_HELP_ABOUT" ), tr( "PRP_DESK_HELP_ABOUT" ), - Qt::SHIFT+Qt::Key_A, desk, false, this, SLOT( onHelpAbout() ) ); + Qt::ALT+Qt::SHIFT+Qt::Key_A, desk, false, this, SLOT( onHelpAbout() ) ); QtxDockAction* dwa = new QtxDockAction( tr( "TOT_DOCKWINDOWS" ), tr( "MEN_DESK_VIEW_DOCKWINDOWS" ), desk ); diff --git a/src/STD/STD_TabDesktop.cxx b/src/STD/STD_TabDesktop.cxx index 3c410ebd2..780359e02 100644 --- a/src/STD/STD_TabDesktop.cxx +++ b/src/STD/STD_TabDesktop.cxx @@ -56,9 +56,9 @@ myWorkstackAction( 0 ) // But the workstack must occupy as much space as possible -- set Expanding for it. myWorkstack->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); - myWorkstack->setAccel( QtxWorkstack::SplitVertical, Qt::SHIFT + Qt::Key_V ); - myWorkstack->setAccel( QtxWorkstack::SplitHorizontal, Qt::SHIFT + Qt::Key_H ); - myWorkstack->setAccel( QtxWorkstack::Close, Qt::SHIFT + Qt::Key_C ); + myWorkstack->setAccel( QtxWorkstack::SplitVertical, Qt::ALT + Qt::SHIFT + Qt::Key_V ); + myWorkstack->setAccel( QtxWorkstack::SplitHorizontal, Qt::ALT + Qt::SHIFT + Qt::Key_H ); + myWorkstack->setAccel( QtxWorkstack::Close, Qt::CTRL + Qt::Key_F4 ); SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); if ( resMgr ) { @@ -204,14 +204,14 @@ void STD_TabDesktop::createActions() resMgr->loadPixmap( "STD", tr( "ICON_DESK_WINDOW_HSPLIT" ) ) ); myWorkstackAction->setText( QtxWorkstackAction::SplitHorizontal, tr( "MEN_DESK_WINDOW_HSPLIT" ) ); myWorkstackAction->setStatusTip( QtxWorkstackAction::SplitHorizontal, tr( "PRP_DESK_WINDOW_HSPLIT" ) ); - myWorkstackAction->setAccel( QtxWorkstackAction::SplitHorizontal, Qt::SHIFT + Qt::Key_H ); + myWorkstackAction->setAccel( QtxWorkstackAction::SplitHorizontal, Qt::ALT + Qt::SHIFT + Qt::Key_H ); // Split Vertical myWorkstackAction->setIcon( QtxWorkstackAction::SplitVertical, resMgr->loadPixmap( "STD", tr( "ICON_DESK_WINDOW_VSPLIT" ) ) ); myWorkstackAction->setText( QtxWorkstackAction::SplitVertical, tr( "MEN_DESK_WINDOW_VSPLIT" ) ); myWorkstackAction->setStatusTip( QtxWorkstackAction::SplitVertical, tr( "PRP_DESK_WINDOW_VSPLIT" ) ); - myWorkstackAction->setAccel( QtxWorkstackAction::SplitVertical, Qt::SHIFT + Qt::Key_V ); + myWorkstackAction->setAccel( QtxWorkstackAction::SplitVertical, Qt::ALT + Qt::SHIFT + Qt::Key_V ); QtxActionMenuMgr* mMgr = menuMgr(); if ( !mMgr ) diff --git a/src/SUIT/SUIT.pro b/src/SUIT/SUIT.pro deleted file mode 100644 index b71ef294f..000000000 --- a/src/SUIT/SUIT.pro +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = suit -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -INCLUDEPATH += ../../include ../Qtx -LIBS += -L../../lib -lqtx - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += SUIT_EXPORTS - -HEADERS = SUIT.h -HEADERS += SUIT_Accel.h -HEADERS += SUIT_ActionOperation.h -HEADERS += SUIT_Application.h -HEADERS += SUIT_DataObject.h -HEADERS += SUIT_DataObjectIterator.h -HEADERS += SUIT_DataObjectKey.h -HEADERS += SUIT_DataOwner.h -HEADERS += SUIT_Desktop.h -HEADERS += SUIT_ExceptionHandler.h -HEADERS += SUIT_FileDlg.h -HEADERS += SUIT_FileValidator.h -HEADERS += SUIT_LicenseDlg.h -HEADERS += SUIT_MessageBox.h -HEADERS += SUIT_Operation.h -HEADERS += SUIT_OverrideCursor.h -HEADERS += SUIT_PopupClient.h -HEADERS += SUIT_ResourceMgr.h -HEADERS += SUIT_SelectionFilter.h -HEADERS += SUIT_SelectionMgr.h -HEADERS += SUIT_Selector.h -HEADERS += SUIT_Session.h -HEADERS += SUIT_SmartPtr.h -HEADERS += SUIT_Study.h -HEADERS += SUIT_Tools.h -HEADERS += SUIT_TreeSync.h -HEADERS += SUIT_ViewManager.h -HEADERS += SUIT_ViewModel.h -HEADERS += SUIT_ViewWindow.h - -SOURCES = SUIT_Accel.cxx -SOURCES += SUIT_ActionOperation.cxx -SOURCES += SUIT_Application.cxx -SOURCES += SUIT_DataObject.cxx -SOURCES += SUIT_DataObjectIterator.cxx -SOURCES += SUIT_DataObjectKey.cxx -SOURCES += SUIT_DataOwner.cxx -SOURCES += SUIT_Desktop.cxx -SOURCES += SUIT_ExceptionHandler.cxx -SOURCES += SUIT_FileDlg.cxx -SOURCES += SUIT_FileValidator.cxx -SOURCES += SUIT_LicenseDlg.cxx -SOURCES += SUIT_MessageBox.cxx -SOURCES += SUIT_Operation.cxx -SOURCES += SUIT_OverrideCursor.cxx -SOURCES += SUIT_PopupClient.cxx -SOURCES += SUIT_ResourceMgr.cxx -SOURCES += SUIT_SelectionFilter.cxx -SOURCES += SUIT_SelectionMgr.cxx -SOURCES += SUIT_Selector.cxx -SOURCES += SUIT_Session.cxx -SOURCES += SUIT_Study.cxx -SOURCES += SUIT_Tools.cxx -SOURCES += SUIT_ViewManager.cxx -SOURCES += SUIT_ViewModel.cxx -SOURCES += SUIT_ViewWindow.cxx - -TRANSLATIONS = resources/SUIT_images.ts \ - resources/SUIT_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/SUIT/SUIT_Accel.cxx b/src/SUIT/SUIT_Accel.cxx index fafcaf84b..6f96bd19c 100644 --- a/src/SUIT/SUIT_Accel.cxx +++ b/src/SUIT/SUIT_Accel.cxx @@ -131,11 +131,14 @@ bool SUIT_Accel::eventFilter( QObject *obj, QEvent *event ) if ( key ) { SUIT_ViewWindow* vw = ::getParentViewWindow( obj ); if ( vw ) { - QString type = vw->getViewManager()->getViewModel()->getType(); - if ( myMap.contains( type ) ) { - IdActionMap idActionMap = myMap[type]; - if ( idActionMap.contains( key ) ) { - return vw->onAccelAction( idActionMap[key] ); + if ( vw->getViewManager() && vw->getViewManager()->getViewModel() ) + { + QString type = vw->getViewManager()->getViewModel()->getType(); + if ( myMap.contains( type ) ) { + IdActionMap idActionMap = myMap[type]; + if ( idActionMap.contains( key ) ) { + return vw->onAccelAction( idActionMap[key] ); + } } } } diff --git a/src/SUIT/SUIT_DataBrowser.cxx b/src/SUIT/SUIT_DataBrowser.cxx index be6b8b1d6..f470f6397 100644 --- a/src/SUIT/SUIT_DataBrowser.cxx +++ b/src/SUIT/SUIT_DataBrowser.cxx @@ -269,6 +269,39 @@ void SUIT_DataBrowser::setSelected( const DataObjectList& lst, const bool append } } +/*! + \brief Make the view item for specified data object is visible. + \param obj data object +*/ +void SUIT_DataBrowser::ensureVisible( SUIT_DataObject* obj ) +{ + if ( !obj ) + return; + + DataObjectList lst; + lst.append( obj ); + ensureVisible( lst ); +} + +/*! + \brief Make the view items for specified data objects is visible. + \param lst data object list +*/ +void SUIT_DataBrowser::ensureVisible( const DataObjectList& lst ) +{ + QtxTreeView* tv = treeView(); + SUIT_AbstractModel* treeModel = dynamic_cast( model() ); + if ( !tv || !treeModel ) + return; + + for ( DataObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it ) + { + QModelIndex idx = treeModel->index( *it ); + if ( idx.isValid() ) + tv->scrollTo( idx ); + } +} + /*! \brief Add custom actions to the popup menu. \param menu popup menu diff --git a/src/SUIT/SUIT_DataBrowser.h b/src/SUIT/SUIT_DataBrowser.h index 6807cb953..c7976283e 100644 --- a/src/SUIT/SUIT_DataBrowser.h +++ b/src/SUIT/SUIT_DataBrowser.h @@ -69,6 +69,9 @@ public: void setAutoSizeColumns( const bool on ); void setResizeOnExpandItem( const bool on ); + void ensureVisible( SUIT_DataObject* ); + void ensureVisible( const DataObjectList& ); + protected: virtual void contextMenuEvent( QContextMenuEvent* ); diff --git a/src/SUIT/SUIT_ViewWindow.cxx b/src/SUIT/SUIT_ViewWindow.cxx index 85ceaac33..0786944b4 100755 --- a/src/SUIT/SUIT_ViewWindow.cxx +++ b/src/SUIT/SUIT_ViewWindow.cxx @@ -47,7 +47,7 @@ const int DUMP_EVENT = QEvent::User + 123; /*! Constructor.*/ SUIT_ViewWindow::SUIT_ViewWindow( SUIT_Desktop* theDesktop ) - : QMainWindow( theDesktop ), myIsDropDown( true ) + : QMainWindow( theDesktop ), myManager( 0 ), myIsDropDown( true ) { myDesktop = theDesktop; diff --git a/src/SUITApp/SUITApp.pro b/src/SUITApp/SUITApp.pro deleted file mode 100644 index ebbbb0678..000000000 --- a/src/SUITApp/SUITApp.pro +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -# -# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -# -# 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. -# -# 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 -# - -TEMPLATE = app -TARGET = SUITApp -DESTDIR = ../../bin -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -INCLUDEPATH += ../../include ../Qtx ../SUIT -LIBS += -L../../lib -lqtx -lsuit - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 - -HEADERS = SUITApp_Application.h - -SOURCES = SUITApp.cxx -SOURCES += SUITApp_Application.cxx diff --git a/src/SUITApp/SUITApp_init_python.hxx b/src/SUITApp/SUITApp_init_python.hxx index c086f39ec..a108231e4 100644 --- a/src/SUITApp/SUITApp_init_python.hxx +++ b/src/SUITApp/SUITApp_init_python.hxx @@ -24,6 +24,16 @@ #define _SUITAPP_INIT_PYTHON_ #include + +// rnv: avoid compilation warning on Linux : "_POSIX_C_SOURCE" and "_XOPEN_SOURCE" are redefined +#ifdef _POSIX_C_SOURCE +#undef _POSIX_C_SOURCE +#endif + +#ifdef _XOPEN_SOURCE +#undef _XOPEN_SOURCE +#endif + #include #ifdef WNT diff --git a/src/SUPERVGraph/SUPERVGraph.pro b/src/SUPERVGraph/SUPERVGraph.pro deleted file mode 100644 index b51f8ef94..000000000 --- a/src/SUPERVGraph/SUPERVGraph.pro +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = SUPERVGraph -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -QT_INCLUDES = $$(QTDIR)/include $$(QTDIR)/include/QtCore $$(QTDIR)/include/QtGui $$(QTDIR)/include/QtOpenGL $$(QTDIR)/include/QtXml - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -PYTHON_INCLUDES = $$(PYTHONHOME)/include/python2.4 - -BOOST_CPPFLAGS = $$(BOOSTDIR)/include - -QT_MT_LIBS = -L$$(QTDIR)/lib -lQtCore -lQtXml -lQtGui -lQtOpenGL - -INCLUDEPATH += $${QT_INCLUDES} $${CAS_CPPFLAGS} $${PYTHON_INCLUDES} $${BOOST_CPPFLAGS} ../Qtx ../SUIT ../OBJECT - -LIBS += $${QT_MT_LIBS} -L../../lib -lsuit - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += SUPERVGRAPH_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = SUPERVGraph_ViewFrame.h -HEADERS += SUPERVGraph_ViewManager.h -HEADERS += SUPERVGraph_ViewModel.h -HEADERS += SUPERVGraph.h - -SOURCES = SUPERVGraph.cxx -SOURCES += SUPERVGraph_ViewFrame.cxx -SOURCES += SUPERVGraph_ViewManager.cxx -SOURCES += SUPERVGraph_ViewModel.cxx - -TRANSLATIONS = resources/SUPERVGraph_images.ts \ - resources/SUPERVGraph_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/SVTK/SVTK.pro b/src/SVTK/SVTK.pro deleted file mode 100644 index b30712212..000000000 --- a/src/SVTK/SVTK.pro +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = SVTK -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -VTKHOME = $$(VTKHOME) -VTK_INCLUDES = $${VTKHOME}/include/vtk - -VTK_LIBS = -L$${VTKHOME}/lib/vtk -L$${VTKHOME}/lib/vtk/python -lvtkCommon -lvtkGraphics -lvtkImaging -lvtkFiltering -lvtkIO -lvtkRendering -lvtkHybrid -lvtkParallel -lvtkWidgets -lGL -L/usr/X11R6/lib -lGLU -L/usr/X11R6/lib -lX11 -lXt - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -BOOST_CPPFLAGS = $$(BOOSTDIR)/include - -OCC_LIBS = - -INCLUDEPATH += ../../include $${CAS_CPPFLAGS} $${VTK_INCLUDES} $${BOOST_CPPFLAGS} ../Qtx ../SUIT ../OBJECT ../Prs ../VTKViewer -LIBS += -L../../lib -lqtx -lsuit -lSalomeObject -lSalomePrs -lVTKViewer $${OCC_LIBS} $${VTK_LIBS} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += SVTK_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = SVTK.h -HEADERS += SVTK_Prs.h -HEADERS += SVTK_Actor.h -HEADERS += SALOME_Actor.h -HEADERS += SVTK_RectPicker.h -HEADERS += SVTK_DeviceActor.h -HEADERS += SVTK_DialogBase.h -HEADERS += SVTK_FontWidget.h -HEADERS += SVTK_CubeAxesActor2D.h -HEADERS += SVTK_Functor.h -HEADERS += SVTK_View.h -HEADERS += SVTK_ViewManager.h -HEADERS += SVTK_ViewModel.h -HEADERS += SVTK_ViewWindow.h -HEADERS += SVTK_Renderer.h -HEADERS += SVTK_InteractorStyle.h -HEADERS += SVTK_KeyFreeInteractorStyle.h -HEADERS += SVTK_RenderWindowInteractor.h -HEADERS += SVTK_GenericRenderWindowInteractor.h -HEADERS += SVTK_Selector.h -HEADERS += SVTK_Selection.h -HEADERS += SVTK_SelectionEvent.h -HEADERS += SVTK_SpaceMouse.h -HEADERS += SVTK_Event.h -HEADERS += SVTK_ViewModelBase.h -HEADERS += SVTK_SetRotationPointDlg.h -HEADERS += SVTK_ViewParametersDlg.h -HEADERS += SVTK_ComboAction.h -HEADERS += SVTK_Extension.h - -SOURCES = SVTK_Prs.cxx -SOURCES += SVTK_Actor.cxx -SOURCES += SALOME_Actor.cxx -SOURCES += SVTK_RectPicker.cxx -SOURCES += SVTK_DeviceActor.cxx -SOURCES += SVTK_CubeAxesActor2D.cxx -SOURCES += SVTK_NonIsometricDlg.cxx -SOURCES += SVTK_UpdateRateDlg.cxx -SOURCES += SVTK_CubeAxesDlg.cxx -SOURCES += SVTK_DialogBase.cxx -SOURCES += SVTK_FontWidget.cxx -SOURCES += SVTK_Trihedron.cxx -SOURCES += SVTK_View.cxx -SOURCES += SVTK_ViewManager.cxx -SOURCES += SVTK_ViewModel.cxx -SOURCES += SVTK_Renderer.cxx -SOURCES += SVTK_ViewWindow.cxx -SOURCES += SVTK_InteractorStyle.cxx -SOURCES += SVTK_KeyFreeInteractorStyle.cxx -SOURCES += SVTK_RenderWindowInteractor.cxx -SOURCES += SVTK_GenericRenderWindowInteractor.cxx -SOURCES += SVTK_SpaceMouse.cxx -SOURCES += SVTK_Selector.cxx -SOURCES += SVTK_SetRotationPointDlg.cxx -SOURCES += SVTK_ViewParametersDlg.cxx -SOURCES += SVTK_ComboAction.cxx -SOURCES += SVTK_Extension.cxx - -TRANSLATIONS = resources/SVTK_images.ts \ - resources/SVTK_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/SVTK/SVTK_DialogBase.cxx b/src/SVTK/SVTK_DialogBase.cxx deleted file mode 100644 index 56eb0ef23..000000000 --- a/src/SVTK/SVTK_DialogBase.cxx +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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. -// -// 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 -// - -// SALOME VTKViewer : build VTK viewer into Salome desktop -// File : -// Author : -// Module : SALOME -// $Header$ -// -#include "SVTK_DialogBase.h" - -#include "QtxAction.h" - -/*! - Constructor -*/ -SVTK_DialogBase -::SVTK_DialogBase(QtxAction* theAction, - QWidget* theParent, - const char* theName, - bool theModal, - Qt::WindowFlags theWFalgs): - QDialog(theParent, - theWFalgs | Qt::WindowTitleHint | Qt::WindowSystemMenuHint), - myAction(theAction) -{ - setObjectName(theName); - setModal(theModal); - - connect(theParent, SIGNAL(Show( QShowEvent * )), this, SLOT(onParentShow())); - connect(theParent, SIGNAL(Hide( QHideEvent * )), this, SLOT(onParentHide())); -} - -/* - * Destroys the object and frees any allocated resources - */ -SVTK_DialogBase -::~SVTK_DialogBase() -{ - // no need to delete child widgets, Qt does it all for us -} - -void -SVTK_DialogBase -::onParentShow() -{ - if(myAction->isChecked()) - show(); - else - hide(); -} - -void -SVTK_DialogBase -::onParentHide() -{ - hide(); -} - -void -SVTK_DialogBase -::done( int r ) -{ - myAction->setChecked( false ); - QDialog::done( r ); -} diff --git a/src/SalomeApp/Makefile.am b/src/SalomeApp/Makefile.am index 45060daec..bfa812012 100755 --- a/src/SalomeApp/Makefile.am +++ b/src/SalomeApp/Makefile.am @@ -26,9 +26,11 @@ include $(top_srcdir)/adm_local/unix/make_common_starter.am +SUBDIRS=pluginsdemo + if CPPUNIT_IS_OK if GUI_ENABLE_CORBA - SUBDIRS = Test + SUBDIRS += Test endif endif diff --git a/src/SalomeApp/SalomeApp.pro b/src/SalomeApp/SalomeApp.pro deleted file mode 100644 index 6d2424589..000000000 --- a/src/SalomeApp/SalomeApp.pro +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = SalomeApp -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -PYTHON_INCLUDES = $$(PYTHONHOME)/include/python2.4 - -QT_INCLUDES = $$(QTDIR)/include $$(QTDIR)/include/QtCore $$(QTDIR)/include/QtGui $$(QTDIR)/include/QtOpenGL $$(QTDIR)/include/QtXml - -QWT_INCLUDES = $$(QWTHOME)/include - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -VTK_INCLUDES = $$(VTKHOME)/include/vtk - -BOOST_CPPFLAGS = $$(BOOSTDIR)/include - -KERNEL_CXXFLAGS = $$(KERNEL_ROOT_DIR)/include/salome - -CORBA_INCLUDES = $$(OMNIORBDIR)/include $$(OMNIORBDIR)/include/omniORB4 $$(OMNIORBDIR)/include/COS - -HDF5_INCLUDES = $$(HDF5HOME)/include - -PYTHON_LIBS = -L$$(PYTHONHOME)/lib/python2.4/config -lpython2.4 -ldl -lutil - -QT_MT_LIBS = -L$$(QTDIR)/lib -lQtCore -lQtXml -lQtGui -lQtOpenGL - -KERNEL_LDFLAGS = -L$$(KERNEL_ROOT_DIR)/lib/salome - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - - -INCLUDEPATH += $${PYTHON_INCLUDES} $${QT_INCLUDES} $${QWT_INCLUDES} $${CAS_CPPFLAGS} $${VTK_INCLUDES} $${BOOST_CPPFLAGS} $${KERNEL_CXXFLAGS} ../LightApp ../CAM ../Qtx ../SUIT ../OBJECT ../SVTK ../STD ../VTKViewer ../PyConsole ../TOOLSGUI ../PyInterp ../Session ../../idl ../Event ../../salome_adm/unix $${CORBA_INCLUDES} #../ObjBrowser - -LIBS += $${QT_MT_LIBS} $${PYTHON_LIBS} $${KERNEL_LDFLAGS} -lOpUtil -lSALOMELocalTrace -lSalomeDSClient -L../../lib -lsuit -lstd -lCAM -lSalomePrs -lSPlot2d -lGLViewer -lOCCViewer -lVTKViewer -lSalomeObject -lSVTK -lSOCC -lPyInterp -lPyConsole -lLogWindow -lLightApp -lToolsGUI $${CAS_KERNEL} #-lObjBrowser - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += SALOMEAPP_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS OMNIORB_VERSION=4 __x86__ __linux__ COMP_CORBA_DOUBLE COMP_CORBA_LONG - -HEADERS = SalomeApp.h -HEADERS += SalomeApp_Application.h -HEADERS += SalomeApp_DataModel.h -HEADERS += SalomeApp_DataObject.h -HEADERS += SalomeApp_LoadStudiesDlg.h -HEADERS += SalomeApp_Module.h -HEADERS += SalomeApp_Study.h -HEADERS += SalomeApp_ExceptionHandler.h -HEADERS += SalomeApp_Tools.h -HEADERS += SalomeApp_ImportOperation.h -HEADERS += SalomeApp_Filter.h -HEADERS += SalomeApp_TypeFilter.h -HEADERS += SalomeApp_StudyPropertiesDlg.h -HEADERS += SalomeApp_CheckFileDlg.h -HEADERS += SalomeApp_VisualState.h - -SOURCES = SalomeApp_Module.cxx -SOURCES += SalomeApp_Application.cxx -SOURCES += SalomeApp_DataModel.cxx -SOURCES += SalomeApp_DataObject.cxx -SOURCES += SalomeApp_LoadStudiesDlg.cxx -SOURCES += SalomeApp_Study.cxx -SOURCES += SalomeApp_ExceptionHandler.cxx -SOURCES += SalomeApp_PyInterp.cxx -SOURCES += SalomeApp_Tools.cxx -SOURCES += SalomeApp_ImportOperation.cxx -SOURCES += SalomeApp_Filter.cxx -SOURCES += SalomeApp_TypeFilter.cxx -SOURCES += SalomeApp_StudyPropertiesDlg.cxx -SOURCES += SalomeApp_ListView.cxx -SOURCES += SalomeApp_CheckFileDlg.cxx -SOURCES += SalomeApp_VisualState.cxx - -TRANSLATIONS = resources/SalomeApp_images.ts \ - resources/SalomeApp_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/SalomeApp/SalomeApp_Application.cxx b/src/SalomeApp/SalomeApp_Application.cxx index fcac1c19d..151d84d31 100644 --- a/src/SalomeApp/SalomeApp_Application.cxx +++ b/src/SalomeApp/SalomeApp_Application.cxx @@ -299,7 +299,7 @@ void SalomeApp_Application::createActions() //! Catalog Generator createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(), tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ), - Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) ); + Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) ); //! Registry Display createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ), QIcon(), @@ -812,21 +812,10 @@ void SalomeApp_Application::onDumpStudy( ) QFileInfo aFileInfo(aFileName); if( aFileInfo.isDir() ) // IPAL19257 return; + + // Issue 21377 - dump study implementation moved to SalomeApp_Study class + bool res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI ); - int savePoint; - _PTR(AttributeParameter) ap; - _PTR(IParameters) ip = ClientFactory::getIParameters(ap); - if(ip->isDumpPython(appStudy->studyDS())) ip->setDumpPython(appStudy->studyDS()); //Unset DumpPython flag. - if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method - ip->setDumpPython(appStudy->studyDS()); - savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point - } - bool res = aStudy->DumpStudy( aFileInfo.absolutePath().toStdString(), - aFileInfo.baseName().toStdString(), - toPublish, - isMultiFile); - if ( toSaveGUI ) - appStudy->removeSavePoint(savePoint); //SRN: remove the created temporary save point. if ( !res ) SUIT_MessageBox::warning( desktop(), QObject::tr("WRN_WARNING"), @@ -1001,7 +990,7 @@ void SalomeApp_Application::createPreferences( LightApp_Preferences* pref ) int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) ); int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat ); int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab ); - for ( int i = SalomeApp_DataObject::EntryId; i <= SalomeApp_DataObject::RefEntryId; i++ ) + for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ ) { pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols, LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) ); @@ -1222,20 +1211,6 @@ SALOME_LifeCycleCORBA* SalomeApp_Application::lcc() return &_lcc; } -/*!Return default engine IOR for light modules*/ -QString SalomeApp_Application::defaultEngineIOR() -{ - /// Look for a default module engine (needed for CORBAless modules to use SALOMEDS persistence) - QString anIOR( "" ); - CORBA::Object_ptr anEngine = namingService()->Resolve( "/SalomeAppEngine" ); - if ( !CORBA::is_nil( anEngine ) ) - { - CORBA::String_var objStr = orb()->object_to_string( anEngine ); - anIOR = QString( objStr.in() ); - } - return anIOR; -} - /*!Private SLOT. On preferences.*/ void SalomeApp_Application::onProperties() { @@ -1417,28 +1392,22 @@ void SalomeApp_Application::onRegDisplay() /*!find original object by double click on item */ void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj ) { - SalomeApp_DataObject* obj = dynamic_cast( theObj ); - SalomeApp_Study* study = dynamic_cast( activeStudy() ); + // Issue 21379: References are supported at LightApp_DataObject level + LightApp_DataObject* obj = dynamic_cast( theObj ); - if( study && obj ) + if( obj && obj->isReference() ) { - QString entry = obj->entry(); - _PTR(SObject) sobj = study->studyDS()->FindObjectID( entry.toStdString() ), ref; - - if( sobj && sobj->ReferencedObject( ref ) ) - { - entry = ref->GetID().c_str(); + QString entry = obj->refEntry(); - SUIT_DataOwnerPtrList aList; - aList.append( new LightApp_DataOwner( entry ) ); - selectionMgr()->setSelected( aList, false ); + SUIT_DataOwnerPtrList aList; + aList.append( new LightApp_DataOwner( entry ) ); + selectionMgr()->setSelected( aList, false ); + + SUIT_DataBrowser* ob = objectBrowser(); - SUIT_DataBrowser* ob = objectBrowser(); - - QModelIndexList aSelectedIndexes = ob->selectedIndexes(); - if ( !aSelectedIndexes.isEmpty() ) - ob->treeView()->scrollTo( aSelectedIndexes.first() ); - } + QModelIndexList aSelectedIndexes = ob->selectedIndexes(); + if ( !aSelectedIndexes.isEmpty() ) + ob->treeView()->scrollTo( aSelectedIndexes.first() ); } } @@ -1654,7 +1623,7 @@ bool SalomeApp_Application::useStudy( const QString& theName ) void SalomeApp_Application::objectBrowserColumnsVisibility() { if ( objectBrowser() ) - for ( int i = SalomeApp_DataObject::EntryId; i <= SalomeApp_DataObject::RefEntryId; i++ ) + for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ ) { bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true ); objectBrowser()->treeView()->setColumnHidden( i, !shown ); diff --git a/src/SalomeApp/SalomeApp_Application.h b/src/SalomeApp/SalomeApp_Application.h index aab013d66..711333875 100644 --- a/src/SalomeApp/SalomeApp_Application.h +++ b/src/SalomeApp/SalomeApp_Application.h @@ -96,7 +96,6 @@ public: static SALOMEDSClient_StudyManager* studyMgr(); static SALOME_NamingService* namingService(); static SALOME_LifeCycleCORBA* lcc(); - static QString defaultEngineIOR(); SUIT_ViewManager* newViewManager(const QString&); void updateSavePointDataObjects( SalomeApp_Study* ); diff --git a/src/SalomeApp/SalomeApp_DataObject.cxx b/src/SalomeApp/SalomeApp_DataObject.cxx index fa5cd5a52..481d3fc2b 100644 --- a/src/SalomeApp/SalomeApp_DataObject.cxx +++ b/src/SalomeApp/SalomeApp_DataObject.cxx @@ -125,7 +125,7 @@ QString SalomeApp_DataObject::text( const int id ) const { QString txt; - // add "Value", "IOR", and "Reference Entry" columns + // Text for "Value" and "IOR" columns switch ( id ) { case ValueId: @@ -141,11 +141,9 @@ QString SalomeApp_DataObject::text( const int id ) const case IORId: txt = ior( referencedObject() ); break; - case RefEntryId : - if ( isReference() ) - txt = entry( referencedObject() ); - break; default: + // Issue 21379: LightApp_DataObject::text() treats "Entry" + // and "Reference Entry" columns txt = LightApp_DataObject::text( id ); break; } @@ -202,9 +200,7 @@ QColor SalomeApp_DataObject::color( const ColorRole role, const int id ) const case Foreground: // text color (not selected item) if ( isReference() ) { - if ( !(QString(referencedObject()->GetName().c_str()).isEmpty()) ) - c = QColor( 255, 0, 0 ); // valid reference (red) - else + if ( QString(referencedObject()->GetName().c_str()).isEmpty() ) c = QColor( 200, 200, 200 ); // invalid reference (grayed) } else if ( myObject ) { @@ -216,12 +212,11 @@ QColor SalomeApp_DataObject::color( const ColorRole role, const int id ) const } } break; + case Highlight: // background color for the highlighted item if ( isReference() ) { - if ( !(QString(referencedObject()->GetName().c_str()).isEmpty()) ) - c = QColor( 255, 0, 0 ); // valid reference (red) - else + if ( QString(referencedObject()->GetName().c_str()).isEmpty() ) c = QColor( 200, 200, 200 ); // invalid reference (grayed) } else if ( myObject ) { @@ -235,14 +230,14 @@ QColor SalomeApp_DataObject::color( const ColorRole role, const int id ) const } } break; - case HighlightedText: - // text color for the highlighted item - if ( isReference() ) - c = QColor( 255, 255, 255 ); // white + default: break; } + + // Issue 21379: LightApp_DataObject::color() defines colors for valid references if ( !c.isValid() ) c = LightApp_DataObject::color( role, id ); + return c; } @@ -329,8 +324,23 @@ _PTR(SObject) SalomeApp_DataObject::object() const return myObject; } +/*! + \brief Returns the string identifier of the data objects referenced by this one. + + Re-implemented from LightApp_DataObject using SALOMEDS API. + + \return ID string of the referenced SObject +*/ +QString SalomeApp_DataObject::refEntry() const +{ + return entry( referencedObject() ); +} + /*! \brief Check if the data object is a reference. + + Re-implemented from LightApp_DataObject using SALOMEDS API. + \return \c true if this data object actually refers to another one */ bool SalomeApp_DataObject::isReference() const @@ -671,10 +681,17 @@ QString SalomeApp_ModuleObject::name() const \brief Get data object icon for the specified column. \param id column id \return object icon for the specified column + \sa CAM_ModuleObject class */ QPixmap SalomeApp_ModuleObject::icon( const int id ) const { - return SalomeApp_DataObject::icon( id ); + QPixmap p = SalomeApp_DataObject::icon( id ); + // The module might not provide a separate small icon + // for Obj. Browser representation -> always try to scale it + // See CAM_ModuleObject::icon() + if ( !p.isNull() ) + p = Qtx::scaleIcon( p, 16 ); + return p; } /*! diff --git a/src/SalomeApp/SalomeApp_DataObject.h b/src/SalomeApp/SalomeApp_DataObject.h index dc4f28984..f9b2b7446 100644 --- a/src/SalomeApp/SalomeApp_DataObject.h +++ b/src/SalomeApp/SalomeApp_DataObject.h @@ -37,9 +37,9 @@ class SALOMEAPP_EXPORT SalomeApp_DataObject : public virtual LightApp_DataObject public: //! Column id enum { - ValueId = EntryId + 1, //!< value column - IORId, //!< IOR column - RefEntryId //!< reference entry column + ValueId = RefEntryId + 1, //!< value column + IORId, //!< IOR column + LastId //!< indicates last Id value }; public: @@ -58,8 +58,10 @@ public: virtual _PTR(SObject) object() const; - bool isReference() const; + virtual QString refEntry() const; + virtual bool isReference() const; _PTR(SObject) referencedObject() const; + bool hasChildren() const; bool expandable() const; diff --git a/src/SalomeApp/SalomeApp_Study.cxx b/src/SalomeApp/SalomeApp_Study.cxx index c0bd439b4..f16825432 100644 --- a/src/SalomeApp/SalomeApp_Study.cxx +++ b/src/SalomeApp/SalomeApp_Study.cxx @@ -33,6 +33,7 @@ #include #include +#include #include "SALOME_Event.h" #include "Basics_Utils.hxx" @@ -571,7 +572,10 @@ bool SalomeApp_Study::saveDocumentAs( const QString& theFileName ) QListIterator it( list ); QStringList listOfFiles; while ( it.hasNext() ) { - if ( SalomeApp_DataModel* aModel = (SalomeApp_DataModel*)it.next() ) { + // Cast to LightApp class in order to give a chance + // to light modules to save their data + if ( LightApp_DataModel* aModel = + dynamic_cast( it.next() ) ) { listOfFiles.clear(); aModel->saveAs( theFileName, this, listOfFiles ); if ( !listOfFiles.isEmpty() ) @@ -613,7 +617,10 @@ bool SalomeApp_Study::saveDocument() QListIterator it( list ); QStringList listOfFiles; while ( it.hasNext() ) { - if ( SalomeApp_DataModel* aModel = (SalomeApp_DataModel*)it.next() ) { + // Cast to LightApp class in order to give a chance + // to light modules to save their data + if ( LightApp_DataModel* aModel = + dynamic_cast( it.next() ) ) { listOfFiles.clear(); aModel->save(listOfFiles); if ( !listOfFiles.isEmpty() ) @@ -658,6 +665,80 @@ void SalomeApp_Study::closeDocument(bool permanently) } } +/*! + Dump study operation. Writes a Python dump file using + SALOMEDS services. Additionally, gives a chance to light modules + to participate in dump study operation. + + \param theFileName - full path to the output Python file + \param toPublish - if true, all objects are published in a study + by the output script, including those not orignally present + in the current study. + \param isMultiFile - if true, each module's dump is written into + a separate Python file, otherwise a single output file is written + \param toSaveGUI - if true, the GUI state is written + + \return - true if the operation succeeds, and false otherwise. +*/ +bool SalomeApp_Study::dump( const QString& theFileName, + bool toPublish, + bool isMultiFile, + bool toSaveGUI ) +{ + int savePoint; + _PTR(AttributeParameter) ap; + _PTR(IParameters) ip = ClientFactory::getIParameters(ap); + _PTR(Study) aStudy = studyDS(); + + if( ip->isDumpPython( aStudy ) ) + ip->setDumpPython( aStudy ); //Unset DumpPython flag. + + if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method + ip->setDumpPython( aStudy ); + //SRN: create a temporary save point + savePoint = SalomeApp_VisualState( + dynamic_cast( application() ) ).storeState(); + } + + // Issue 21377 - Each data model is asked to dump its data not present in SALOMEDS study. + // This is an optional but important step, it gives a chance to light modules + // to dump their data as a part of common dump study operation + ModelList list; + dataModels( list ); + + QListIterator it( list ); + QStringList listOfFiles; + while ( it.hasNext() ) { + if ( LightApp_DataModel* aModel = + dynamic_cast( it.next() ) ) { + listOfFiles.clear(); + if ( aModel->dumpPython( theFileName, this, isMultiFile, listOfFiles ) && + !listOfFiles.isEmpty() ) + // This call simply passes the data model's dump output to SalomeApp_Engine servant. + // This code is shared with persistence mechanism. + // NOTE: this should be revised if behavior of saveModuleData() changes! + saveModuleData(aModel->module()->name(), listOfFiles); + } + } + + // Now dump SALOMEDS part that also involves SalomeApp_Engine in case if + // any light module is present in the current configuration + QFileInfo aFileInfo( theFileName ); + bool res = aStudy->DumpStudy( aFileInfo.absolutePath().toStdString(), + aFileInfo.baseName().toStdString(), + toPublish, + isMultiFile); + if ( toSaveGUI ) + removeSavePoint( savePoint ); //SRN: remove the created temporary save point. + + // Issue 21377 - Clean up light module data in SalomeApp_Engine servant + // This code is shared with persistence mechanism. + // NOTE: this should be revised if behavior of saveStudyData() changes! + saveStudyData( theFileName ); + + return res; +} + /*! \return true, if study is modified in comparison with last open/save */ @@ -735,16 +816,22 @@ void SalomeApp_Study::openModuleData( QString theModuleName, QStringList& theLis } /*! - Saves data from study + Re-implemented from LightApp_Study, actually does not save anything but + simply cleans up light modules' data */ bool SalomeApp_Study::saveStudyData( const QString& theFileName ) { ModelList list; dataModels( list ); QListIterator it( list ); std::vector listOfFiles(0); - while ( it.hasNext() ) - if ( SalomeApp_DataModel* aModel = (SalomeApp_DataModel*)it.next() ) - SetListOfFiles(aModel->module()->name().toStdString().c_str(), listOfFiles); + while ( it.hasNext() ){ + LightApp_DataModel* aLModel = + dynamic_cast( it.next() ); + // It is safe to call SetListOfFiles() for any kind of module + // because SetListOfFiles() does nothing for full modules :) + if ( aLModel ) + SetListOfFiles(aLModel->module()->name().toStdString().c_str(), listOfFiles); + } return true; } @@ -764,6 +851,54 @@ void SalomeApp_Study::setStudyDS( const _PTR(Study)& s ) myStudyDS = s; } +/*! + Virtual method re-implemented from LightApp_Study in order to create + the module object connected to SALOMEDS - SalomeApp_ModuleObject. + + \param theDataModel - data model instance to create a module object for + \param theParent - the module object's parent (normally it's the study root) + \return the module object instance + \sa LightApp_Study class, LightApp_DataModel class +*/ +CAM_ModuleObject* SalomeApp_Study::createModuleObject( LightApp_DataModel* theDataModel, + SUIT_DataObject* theParent ) const +{ + SalomeApp_Study* that = const_cast( this ); + + // Ensure that SComponent instance is published in the study for the given module + // This line causes automatic creation of SalomeApp_ModuleObject in case if + // WITH_SALOMEDS_OBSERVER is defined + that->addComponent( theDataModel ); + + // SalomeApp_ModuleObject might have been created by SALOMEDS observer + // or by someone else so check if it exists first of all + CAM_ModuleObject* res = 0; + + DataObjectList children = root()->children(); + DataObjectList::const_iterator anIt = children.begin(), aLast = children.end(); + for( ; !res && anIt!=aLast; anIt++ ) + { + SalomeApp_ModuleObject* obj = dynamic_cast( *anIt ); + if ( obj && obj->componentDataType() == theDataModel->module()->name() ) + res = obj; + } + + if ( !res ){ + _PTR(Study) aStudy = studyDS(); + if ( !aStudy ) + return res; + + _PTR(SComponent) aComp = aStudy->FindComponent( + theDataModel->module()->name().toStdString() ); + if ( !aComp ) + return res; + + res = new SalomeApp_ModuleObject( theDataModel, aComp, theParent ); + } + + return res; +} + /*! Insert data model. */ @@ -788,11 +923,14 @@ void SalomeApp_Study::addComponent(const CAM_DataModel* dm) _PTR(Study) aStudy = studyDS(); if (!aStudy) return; - _PTR(SComponent) aComp = aStudy->FindComponent(dm->module()->name().toStdString()); + + std::string aCompDataType = dm->module()->name().toStdString(); + + _PTR(SComponent) aComp = aStudy->FindComponent(aCompDataType); if (!aComp) { // Create SComponent _PTR(StudyBuilder) aBuilder = aStudy->NewBuilder(); - aComp = aBuilder->NewComponent(dm->module()->name().toStdString()); + aComp = aBuilder->NewComponent(aCompDataType); aBuilder->SetName(aComp, dm->module()->moduleName().toStdString()); QString anIconName = dm->module()->iconName(); if (!anIconName.isEmpty()) { @@ -800,11 +938,26 @@ void SalomeApp_Study::addComponent(const CAM_DataModel* dm) if (anAttr) anAttr->SetPixMap(anIconName.toStdString()); } + // Set default engine IOR - aBuilder->DefineComponentInstance(aComp, SalomeApp_Application::defaultEngineIOR().toStdString()); + // Issue 21377 - using separate engine for each type of light module + std::string anEngineIOR = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(), + true ); + aBuilder->DefineComponentInstance(aComp, anEngineIOR); //SalomeApp_DataModel::BuildTree( aComp, root(), this, /*skipExisitng=*/true ); SalomeApp_DataModel::synchronize( aComp, this ); } + else { + _PTR(StudyBuilder) aBuilder = aStudy->NewBuilder(); + aBuilder->SetName(aComp, dm->module()->moduleName().toStdString()); + QString anIconName = dm->module()->iconName(); + if (!anIconName.isEmpty()) { + _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap"); + if (anAttr) + anAttr->SetPixMap(anIconName.toStdString()); + } + // Set default engine IOR + } } } @@ -823,8 +976,10 @@ bool SalomeApp_Study::openDataModel( const QString& studyName, CAM_DataModel* dm QString anEngine; // 1. aModule == 0 means that this is a light module (no CORBA enigine) if (!aModule) { - anEngine = SalomeApp_Application::defaultEngineIOR(); - aSComp = aStudy->FindComponent(dm->module()->name().toStdString()); + // Issue 21377 - using separate engine for each type of light module + std::string aCompDataType = dm->module()->name().toStdString(); + anEngine = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(), true ).c_str(); + aSComp = aStudy->FindComponent( aCompDataType ); } else { SalomeApp_DataModel* aDM = dynamic_cast( dm ); @@ -896,30 +1051,38 @@ QString SalomeApp_Study::newStudyName() const } /*! + Note that this method does not create or activate SalomeApp_Engine_i instance, + therefore it can be called safely for any kind of module, but for full + modules it returns an empty list. \return list of files used by module: to be used by CORBAless modules \param theModuleName - name of module */ std::vector SalomeApp_Study::GetListOfFiles( const char* theModuleName ) const { - SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance(); + // Issue 21377 - using separate engine for each type of light module + SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false ); if (aDefaultEngine) - return aDefaultEngine->GetListOfFiles(id(), theModuleName); + return aDefaultEngine->GetListOfFiles(id()); std::vector aListOfFiles; return aListOfFiles; } /*! - Sets list of files used by module: to be used by CORBAless modules + Sets list of files used by module: to be used by CORBAless modules. + Note that this method does not create or activate SalomeApp_Engine_i instance, + therefore it can be called safely for any kind of module, but for full + modules it simply does nothing. \param theModuleName - name of module \param theListOfFiles - list of files */ void SalomeApp_Study::SetListOfFiles ( const char* theModuleName, const std::vector theListOfFiles ) { - SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance(); + // Issue 21377 - using separate engine for each type of light module + SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false ); if (aDefaultEngine) - aDefaultEngine->SetListOfFiles(theListOfFiles, id(), theModuleName); + aDefaultEngine->SetListOfFiles(theListOfFiles, id()); } /*! diff --git a/src/SalomeApp/SalomeApp_Study.h b/src/SalomeApp/SalomeApp_Study.h index 6e9cef141..2487cd5de 100644 --- a/src/SalomeApp/SalomeApp_Study.h +++ b/src/SalomeApp/SalomeApp_Study.h @@ -55,6 +55,8 @@ public: virtual void closeDocument(bool permanently = true); + virtual bool dump( const QString&, bool, bool, bool ); + virtual bool isSaved() const; virtual bool isModified() const; virtual void Modified(); @@ -100,7 +102,8 @@ protected: virtual void dataModelInserted( const CAM_DataModel* ); virtual bool openDataModel( const QString&, CAM_DataModel* ); void setStudyDS(const _PTR(Study)& s ); - + virtual CAM_ModuleObject* createModuleObject( LightApp_DataModel* theDataModel, + SUIT_DataObject* theParent ) const; protected slots: virtual void updateModelRoot( const CAM_DataModel* ); diff --git a/src/SalomeApp/pluginsdemo/Makefile.am b/src/SalomeApp/pluginsdemo/Makefile.am new file mode 100644 index 000000000..1776630c0 --- /dev/null +++ b/src/SalomeApp/pluginsdemo/Makefile.am @@ -0,0 +1,38 @@ +# Copyright (C) 2010 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. +# +# 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 +# +# -* Makefile *- +# +# Author : Guillaume Boulant (EDF) +# + +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +# +# Note that the plugins files should be installed in the directory +# (ROOT_DIR/share/salome/plugins) or one of this +# sub-directories (the search of plugins by the plugin manager is +# recurcive from this folder, in each SALOME module, i.e. each +# variable *_ROOT_DIR). +# +pluginsdir =$(salomepluginsdir)/demo +plugins_PYTHON = \ + salome_plugins.py \ + trihedron.py \ + tubedialog.py \ + tube.py diff --git a/src/SalomeApp/pluginsdemo/salome_plugins.py b/src/SalomeApp/pluginsdemo/salome_plugins.py new file mode 100755 index 000000000..2c3ea43a5 --- /dev/null +++ b/src/SalomeApp/pluginsdemo/salome_plugins.py @@ -0,0 +1,133 @@ +# -*- coding: iso-8859-1 -*- +import salome_pluginsmanager + +DEMO_IS_ACTIVATED=False + +# ------------------------------------------------------------------------- +# Example 1: creation of basic objects. +# The plugin function is implemented here and declared in the pluginsmanager. +# +import salome + +def trihedron(context): + import geompy + + # Intialize the geompy factory with the active study + activeStudy = context.study + geompy.init_geom(activeStudy) + + # Create the objects + Vx = geompy.MakeVectorDXDYDZ(10, 0, 0) + Vy = geompy.MakeVectorDXDYDZ(0, 10, 0) + Vz = geompy.MakeVectorDXDYDZ(0, 0, 10) + origin = geompy.MakeVertex(0, 0, 0) + + # Register the objects in the active study + geompy.addToStudy( Vx, "Vx" ) + geompy.addToStudy( Vy, "Vy" ) + geompy.addToStudy( Vz, "Vz" ) + geompy.addToStudy( origin, "origin" ) + +if DEMO_IS_ACTIVATED: + salome_pluginsmanager.AddFunction('O,Vx,Vy,Vz', + 'Creates the trihedron', + trihedron) + + +# ------------------------------------------------------------------------- +# Example 1 bis: creation of basic objects and automatic display +def trihedron_withdisplay(context): + import geompy + + # Intialize the geompy factory with the active study + activeStudy = context.study + geompy.init_geom(activeStudy) + + # Create the objects + Vx = geompy.MakeVectorDXDYDZ(10, 0, 0) + Vy = geompy.MakeVectorDXDYDZ(0, 10, 0) + Vz = geompy.MakeVectorDXDYDZ(0, 0, 10) + origin = geompy.MakeVertex(0, 0, 0) + + # Register the objects in the active study + entries=[] + entries.append(geompy.addToStudy( Vx, "Vx" )) + entries.append(geompy.addToStudy( Vy, "Vy" )) + entries.append(geompy.addToStudy( Vz, "Vz" )) + entries.append(geompy.addToStudy( origin, "origin" )) + + # This part is to automatically display the objects in the active viewer. + gcomp = salome.ImportComponentGUI("GEOM") + for entry in entries: + gcomp.createAndDisplayFitAllGO(entry) + +if DEMO_IS_ACTIVATED: + salome_pluginsmanager.AddFunction('O,Vx,Vy,Vz (2)', + 'Creates Basic GEOM objects', + trihedron_withdisplay) + +# ------------------------------------------------------------------------- +# Example 2: creation of a shape with parameters that can be read from a GUI. +# The plugin function (tube_shapewithgui) delegates some action to +# dedicated imported modules (tube.py and tubedialog.py). +# +import tube + +# A single dialog box is defined and recycled for every call. The +# fields are initialized with default values given by the tube factory +# tube.py. +import tubedialog +dialog = tubedialog.TubeDialog() +dialog.setData(tube.DEFAULT_RADIUS, tube.DEFAULT_LENGTH, tube.DEFAULT_WIDTH) + +def tube_shapewithgui(context): + global tube, dialog + activeStudy = context.study + + # Get the parameter values from a gui dialog box. If the dialog is + # closed using the Ok button, then the data are requested from the + # gui and used to create the shape of the tube. + dialog.exec_() + if dialog.wasOk(): + radius, length, width = dialog.getData() + shape = tube.createGeometry(activeStudy, radius, length, width) + +if DEMO_IS_ACTIVATED: + salome_pluginsmanager.AddFunction('Tube shape from parameters', + 'Creates a tube object from specified parameters', + tube_shapewithgui) + + +# ------------------------------------------------------------------------- +# Example 2 bis: creation of a mesh with parameters that can be read from a GUI. +# The plugin function (tube_meshwithgui) delegates some action to +# dedicated imported modules (tube.py and tubedialog.py). +# +def tube_meshwithgui(context): + global tube, dialog + activeStudy = context.study + + # Get the parameter values from a gui dialog box. If the dialog is + # closed using the Ok button, then the data are requested from the + # gui and used to create the shape of the tube. + dialog.exec_() + if dialog.wasOk(): + radius, length, width = dialog.getData() + mesh = tube.createModel(activeStudy, radius, length, width) + +if DEMO_IS_ACTIVATED: + salome_pluginsmanager.AddFunction('Tube mesh from parameters', + 'Creates a tube object from specified parameters', + tube_meshwithgui) + +# ------------------------------------------------------------------------- +# Example 3: run a shell session in a xterm to get a SALOME python console +def runSalomeShellSession(context): + import os + command="(xterm -e "+os.environ['KERNEL_ROOT_DIR']+"/runSession)&" + os.system(command) + +if DEMO_IS_ACTIVATED: + salome_pluginsmanager.AddFunction('SALOME shell session', + 'Execute a SALOME shell session in an external xterm', + runSalomeShellSession) diff --git a/src/SalomeApp/pluginsdemo/trihedron.py b/src/SalomeApp/pluginsdemo/trihedron.py new file mode 100644 index 000000000..9d0ccbde7 --- /dev/null +++ b/src/SalomeApp/pluginsdemo/trihedron.py @@ -0,0 +1,22 @@ +# -*- coding: iso-8859-1 -*- +# Example 1: creation of basic objects (O, Vx, Vy, Vz) +# + +# Intialize the geompy factory with the active study +import geompy +import salome +geompy.init_geom(salome.myStudy) + +# Create the objects +Vx = geompy.MakeVectorDXDYDZ(10, 0, 0) +Vy = geompy.MakeVectorDXDYDZ(0, 10, 0) +Vz = geompy.MakeVectorDXDYDZ(0, 0, 10) +origin = geompy.MakeVertex(0, 0, 0) + +# Register the objects in the active study +geompy.addToStudy( Vx, "Vx" ) +geompy.addToStudy( Vy, "Vy" ) +geompy.addToStudy( Vz, "Vz" ) +geompy.addToStudy( origin, "origin" ) + + diff --git a/src/SalomeApp/pluginsdemo/tube.py b/src/SalomeApp/pluginsdemo/tube.py new file mode 100644 index 000000000..5245b500a --- /dev/null +++ b/src/SalomeApp/pluginsdemo/tube.py @@ -0,0 +1,121 @@ +# -*- coding: iso-8859-1 -*- + +import salome + +DEFAULT_RADIUS = 100 +DEFAULT_LENGTH = 300 +DEFAULT_WIDTH = 20 + +def createGeometry(study, radius=DEFAULT_RADIUS, length=DEFAULT_LENGTH, width=DEFAULT_WIDTH): + ''' + This function creates the geometry on the specified study and with + given parameters. + ''' + print "TUBE: creating the geometry ..." + import geompy + geompy.init_geom(study) + + radius_ext = radius + radius_int = radius_ext - width + + CylinderExt = geompy.MakeCylinderRH(radius_ext, length) + CylinderInt = geompy.MakeCylinderRH(radius_int, length) + Tube = geompy.MakeCut(CylinderExt, CylinderInt) + entry = geompy.addToStudy( Tube, "Tube" ) + return Tube + +def createGeometryWithPartition(study, radius=DEFAULT_RADIUS, length=DEFAULT_LENGTH, width=DEFAULT_WIDTH): + ''' + This function create the geometrical shape with a partition so + that the hexaedric algorithm could be used for meshing. + ''' + import geompy + shape = createGeometry(study,radius,length,width) + + # We have to create a partition so that we can use an hexaedric + # meshing algorithm. + print "TUBE: creating a partition ..." + toolPlane = geompy.MakeFaceHW(2.1*length,2.1*radius,3) + partition = geompy.MakePartition([shape], [toolPlane], [], [], geompy.ShapeType["SOLID"], 0, [], 0) + entry = geompy.addToStudy( partition, "TubeWithPartition" ) + return partition + +def createMesh(study, shape): + '''This function creates the mesh of the specified shape on the specified study''' + print "TUBE: creating the mesh ..." + import smesh + + smesh.SetCurrentStudy(study) + mesh = smesh.Mesh(shape) + Regular_1D = mesh.Segment() + Nb_Segments = Regular_1D.NumberOfSegments(10) + Nb_Segments.SetDistrType( 0 ) + Quadrangle_2D = mesh.Quadrangle() + Hexa_3D = mesh.Hexahedron() + + isDone = mesh.Compute() + + if salome.sg.hasDesktop(): + smesh.SetName(mesh.GetMesh(), 'TubeMesh') + smesh.SetName(Regular_1D.GetAlgorithm(), 'Regular_1D') + smesh.SetName(Nb_Segments, 'Nb. Segments_1') + smesh.SetName(Quadrangle_2D.GetAlgorithm(), 'Quadrangle_2D') + smesh.SetName(Hexa_3D.GetAlgorithm(), 'Hexa_3D') + salome.sg.updateObjBrowser(0) + + return mesh + + +def createModel(study, radius=DEFAULT_RADIUS, length=DEFAULT_LENGTH,width=DEFAULT_WIDTH): + ''' + This function create the geomtrical shape AND the associated mesh. + ''' + # We first create a shape with a partition so that the hexaedric + # algorithm could be used. + shape = createGeometryWithPartition(study,radius,length,width) + + # Then the mesh can be defined and computed + mesh = createMesh(study,shape) + +def exportModel(mesh, filename): + ''' + This exports the mesh to the specified filename in the med format + ''' + print "TUBE: exporting mesh to file %s ..."%filename + import SMESH + mesh.ExportMED(filename, 0, SMESH.MED_V2_2, 1 ) + + +# +# =================================================================== +# Use cases and test functions +# =================================================================== +# +def TEST_createGeometry(): + salome.salome_init() + theStudy=salome.myStudy + createGeometry(theStudy) + +def TEST_createMesh(): + salome.salome_init() + theStudy=salome.myStudy + shape = createGeometryWithPartition(theStudy) + mesh = createMesh(theStudy, shape) + +def TEST_createModel(): + salome.salome_init() + theStudy=salome.myStudy + createModel(theStudy) + +def TEST_exportModel(): + salome.salome_init() + theStudy=salome.myStudy + shape = createGeometryWithPartition(theStudy) + mesh = createMesh(theStudy, shape) + exportModel(mesh,"tubemesh.med") + +if __name__ == "__main__": + #TEST_createGeometry() + #TEST_createMesh() + TEST_createModel() + #TEST_exportModel() diff --git a/src/SalomeApp/pluginsdemo/tubedialog.py b/src/SalomeApp/pluginsdemo/tubedialog.py new file mode 100644 index 000000000..42b0ee4dd --- /dev/null +++ b/src/SalomeApp/pluginsdemo/tubedialog.py @@ -0,0 +1,125 @@ +# -*- coding: iso-8859-1 -*- +import sys +from PyQt4 import QtGui +from PyQt4 import QtCore + + +class TubeDialog(QtGui.QDialog): + def __init__(self, parent=None): + QtGui.QDialog.__init__(self, parent) + self.setupUi() + + def setupUi(self): + self.setObjectName("Dialog") + self.resize(400, 300) + self.hboxlayout = QtGui.QHBoxLayout(self) + self.hboxlayout.setMargin(9) + self.hboxlayout.setSpacing(6) + self.hboxlayout.setObjectName("hboxlayout") + self.vboxlayout = QtGui.QVBoxLayout() + self.vboxlayout.setMargin(0) + self.vboxlayout.setSpacing(6) + self.vboxlayout.setObjectName("vboxlayout") + self.hboxlayout1 = QtGui.QHBoxLayout() + self.hboxlayout1.setMargin(0) + self.hboxlayout1.setSpacing(6) + self.hboxlayout1.setObjectName("hboxlayout1") + self.vboxlayout1 = QtGui.QVBoxLayout() + self.vboxlayout1.setMargin(0) + self.vboxlayout1.setSpacing(6) + self.vboxlayout1.setObjectName("vboxlayout1") + self.lblRadius = QtGui.QLabel(self) + self.lblRadius.setObjectName("lblRadius") + self.vboxlayout1.addWidget(self.lblRadius) + self.lblLength = QtGui.QLabel(self) + self.lblLength.setObjectName("lblLength") + self.vboxlayout1.addWidget(self.lblLength) + self.lblWidth = QtGui.QLabel(self) + self.lblWidth.setObjectName("lblWidth") + self.vboxlayout1.addWidget(self.lblWidth) + self.hboxlayout1.addLayout(self.vboxlayout1) + self.vboxlayout2 = QtGui.QVBoxLayout() + self.vboxlayout2.setMargin(0) + self.vboxlayout2.setSpacing(6) + self.vboxlayout2.setObjectName("vboxlayout2") + self.txtRadius = QtGui.QLineEdit(self) + self.txtRadius.setObjectName("txtRadius") + self.vboxlayout2.addWidget(self.txtRadius) + self.txtLength = QtGui.QLineEdit(self) + self.txtLength.setObjectName("txtLength") + self.vboxlayout2.addWidget(self.txtLength) + self.txtWidth = QtGui.QLineEdit(self) + self.txtWidth.setObjectName("txtWidth") + self.vboxlayout2.addWidget(self.txtWidth) + self.hboxlayout1.addLayout(self.vboxlayout2) + self.vboxlayout.addLayout(self.hboxlayout1) + spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.vboxlayout.addItem(spacerItem) + self.buttonBox = QtGui.QDialogButtonBox(self) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.NoButton|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.vboxlayout.addWidget(self.buttonBox) + self.hboxlayout.addLayout(self.vboxlayout) + + self.setWindowTitle("Tube construction") + self.lblRadius.setText("Rayon") + self.lblLength.setText("Longueur") + self.lblWidth.setText("Epaisseur") + + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + + def accept(self): + '''Callback function when dialog is accepted (click Ok)''' + self._wasOk = True + QtGui.QDialog.accept(self) + + def reject(self): + '''Callback function when dialog is rejected (click Cancel)''' + self._wasOk = False + QtGui.QDialog.reject(self) + + def wasOk(self): + return self._wasOk + + def setData(self, radius, length, width): + self.txtRadius.setText(str(radius)) + self.txtLength.setText(str(length)) + self.txtWidth.setText(str(width)) + + def getData(self): + try: + radius=eval(str(self.txtRadius.text())) + length=eval(str(self.txtLength.text())) + width=eval(str(self.txtWidth.text())) + except: + print "pb a la saisie" + + return radius, length, width + + +def TEST_getData(): + # This use case illustrates the MVC pattern on this simple dialog example. + a = QtGui.QApplication(sys.argv) + + tubedialog = TubeDialog() + tubedialog.setData(10,50,3) + tubedialog.exec_() + if tubedialog.wasOk(): + radius, length, width = tubedialog.getData() + print radius, length, width + + sys.exit(0) + +def main( args ): + a = QtGui.QApplication(sys.argv) + + tubedialog = TubeDialog() + tubedialog.setData(10,50,3) + sys.exit(tubedialog.exec_()) + +if __name__=="__main__": + #main(sys.argv) + TEST_getData() + diff --git a/src/SalomeApp/salome_pluginsmanager.py b/src/SalomeApp/salome_pluginsmanager.py index 171f5a6fc..8426d4c13 100644 --- a/src/SalomeApp/salome_pluginsmanager.py +++ b/src/SalomeApp/salome_pluginsmanager.py @@ -18,23 +18,28 @@ # """ -This module is imported from C++ SalomeApp_Application -and initialized (call to initialize function with 4 parameters) -module : 0 if it's plugins manager at the application level 1 if it is at the module level -name : the name of the plugins manager. This name is used to build the name of the plugins files -basemenuname : the name of the menu into we want to add the menu of the plugins ("Tools" for example) +This module is imported from C++ SalomeApp_Application and initialized +(call to initialize function with 4 parameters) module : 0 if it's +plugins manager at the application level 1 if it is at the module +level name : the name of the plugins manager. This name is used to +build the name of the plugins files basemenuname : the name of the +menu into we want to add the menu of the plugins ("Tools" for example) menuname : the name of plugins menu A plugins manager is created when calling initialize. -The plugins manager creates a submenu in the menu. +The plugins manager creates a submenu in the +menu. -The plugins manager searches in $HOME/.config/salome/Plugins, $HOME/$APPLI/Plugins, $SALOME_PLUGINS_PATH directories -files named _plugins.py and executes them. +The plugins manager searches in $HOME/.config/salome/Plugins, +$HOME/$APPLI/Plugins, $SALOME_PLUGINS_PATH directories files named +_plugins.py and executes them. -These files should contain python code that register functions into the plugins manager. +These files should contain python code that register functions into +the plugins manager. -Example of a plugins manager with name salome. It searches files with name salome_plugins.py (example follows):: +Example of a plugins manager with name salome. It searches files with +name salome_plugins.py (example follows):: import salome_pluginsmanager @@ -44,12 +49,16 @@ Example of a plugins manager with name salome. It searches files with name salom salome_pluginsmanager.AddFunction('About plugins','About SALOME pluginmanager',about) -All entries in menu are added in the same order as the calls to AddFunction. -It is possible to customize this presentation by getting the entries list (salome_pluginsmanager.entries()) and modifying -it in place. For example, you can do that : salome_pluginsmanager.entries().sort() to order them alphabetically -or salome_pluginsmanager.entries().remove("a") to remove the entry named "a". +All entries in menu are added in the same order as the calls to +AddFunction. It is possible to customize this presentation by getting +the entries list (salome_pluginsmanager.entries()) and modifying it in +place. For example, you can do that : +salome_pluginsmanager.entries().sort() to order them alphabetically or +salome_pluginsmanager.entries().remove("a") to remove the entry named +"a". -It is possible to put entries in submenus. You only need to give a name with / to the entry. for example:: +It is possible to put entries in submenus. You only need to give a +name with / to the entry. for example:: salome_pluginsmanager.AddFunction('a/b/About','About SALOME pluginmanager',about) @@ -57,9 +66,14 @@ will add 2 submenus a and b before creating the entry. In short to add a plugin: - 1. import the python module salome_pluginsmanager (in your salome_plugins.py or _plugins.py) - 2. write a function with one argument context (it's an object with 3 attributes) - 3. register the function with a call to AddFunction (entry in menu plugins, tooltip, function) + 1. import the python module salome_pluginsmanager (in your + salome_plugins.py or _plugins.py) + + 2. write a function with one argument context (it's an object with 3 + attributes) + + 3. register the function with a call to AddFunction (entry in menu plugins, + tooltip, function) context attributes: @@ -123,6 +137,15 @@ def findMenu(lmenu,menu): if a.text() == m: return findMenu(lmenu,a.menu()) +PLUGIN_PATH_PATTERN="share/salome/plugins" +MATCH_ENDING_PATTERN="_plugins.py" +from salome.kernel.syshelper import walktree +from salome.kernel.logger import Logger +#from salome.kernel.termcolor import GREEN +logger=Logger("PluginsManager") #,color=GREEN) +# VSR 21/11/2011 : do not show infos in the debug mode +#logger.showDebug() + class PluginsManager: def __init__(self,module,name,basemenuname,menuname): self.name=name @@ -136,25 +159,42 @@ class PluginsManager: self.plugindirs=[] self.plugins_files=[] + # MODULES plugins directory. + # The SALOME modules may provides natively some plugins. These + # MODULES plugins are supposed to be located in the + # installation folder of the module, in the subdirectory + # "share/salome/plugins". We first look for these directories. + for key in os.environ.keys(): + if key.endswith("_ROOT_DIR"): + rootpath=os.environ[key] + dirpath=os.path.join(rootpath,PLUGIN_PATH_PATTERN) + if os.path.isdir(dirpath) and dirpath not in self.plugindirs: + logger.info("Looking for plugins in the directory %s ..."%dirpath) + walktree(dirpath,self.analyseFile) + # USER plugins directory user_dir = os.path.expanduser("~/.config/salome/Plugins") self.plugindirs.append(user_dir) + logger.info("The user directory %s has been added to plugin paths"%user_dir) # obsolete: USER plugins directory # (for compatibility reasons only; new plugins should be stored in ~/.config/salome/Plugins) user_obsolete_dir = os.path.expanduser("~/.salome/Plugins") self.plugindirs.append(user_obsolete_dir) + logger.info("The user directory %s has been added to plugin paths (deprecated)"%user_obsolete_dir) # APPLI plugins directory appli=os.getenv("APPLI") if appli: appli_dir=os.path.join(os.path.expanduser("~"),appli,"Plugins") self.plugindirs.append(appli_dir) + logger.info("The APPLI directory %s has been added to plugin paths"%appli_dir) #SALOME_PLUGINS_PATH environment variable (list of directories separated by ":") pluginspath=os.getenv("SALOME_PLUGINS_PATH") if pluginspath: for directory in pluginspath.split(SEP): self.plugindirs.append(directory) + logger.info("The directory %s has been added to plugin paths"%directory) self.basemenu = find_menu(self.basemenuname) @@ -169,6 +209,19 @@ class PluginsManager: self.basemenu.connect(self.basemenu, QtCore.SIGNAL("aboutToShow()"), self.importPlugins) + def analyseFile(self,filename): + """ + This function checks if the specified file is a plugins python + module and add the directory name of this file to the list of + plugin paths. This function is aimed to be used as the callback + function of the walktree algorithm. + """ + if str(filename).endswith(MATCH_ENDING_PATTERN): + dirpath=os.path.dirname(filename) + if dirpath not in self.plugindirs: + self.plugindirs.append(dirpath) + logger.info("The directory %s has been added to plugin paths"%dirpath) + def AddFunction(self,name,description,script): """ Add a plugin function """ @@ -197,7 +250,7 @@ class PluginsManager: lasttime=0 plugins_files=[] - plugins_file_name=self.name+"_plugins.py" + plugins_file_name=self.name+MATCH_ENDING_PATTERN for directory in self.plugindirs: plugins_file = os.path.join(directory,plugins_file_name) if os.path.isfile(plugins_file): @@ -228,7 +281,7 @@ class PluginsManager: try: execfile(plugins_file,globals(),{}) except: - print "Error while loading plugins from file:",plugins_file + logger.fatal("Error while loading plugins from file %s"%plugins_file) traceback.print_exc() self.updateMenu() diff --git a/src/Session/SALOME_Session_Server.cxx b/src/Session/SALOME_Session_Server.cxx index 3dd788625..e8c0e7145 100755 --- a/src/Session/SALOME_Session_Server.cxx +++ b/src/Session/SALOME_Session_Server.cxx @@ -178,7 +178,11 @@ protected: { long id = -1; if ( !myExtAppName.isEmpty() ) { +#ifdef WIN32 + QRegExp exp( QString( "%1\\.%2\\.([a-zA-Z0-9.]+)$" ).arg( myExtAppName ).arg( currentFormat() ) ); +#else QRegExp exp( QString( "\\.%1rc\\.([a-zA-Z0-9.]+)$" ).arg( myExtAppName ) ); +#endif QRegExp vers_exp( "^([0-9]+)([A-Za-z]?)([0-9]*)$" ); QString fname = QFileInfo( _fname ).fileName(); diff --git a/src/Session/SalomeApp_Engine_i.cxx b/src/Session/SalomeApp_Engine_i.cxx index 639d3e84f..e43c936e9 100644 --- a/src/Session/SalomeApp_Engine_i.cxx +++ b/src/Session/SalomeApp_Engine_i.cxx @@ -28,20 +28,29 @@ // #include "SalomeApp_Engine_i.hxx" -#include "SALOMEDS_Tool.hxx" +#include +#include +#include +#include +#include +#include + +#include +#include +#include #include using namespace std; -SalomeApp_Engine_i* SalomeApp_Engine_i::myInstance = NULL; - /*! Constructor */ -SalomeApp_Engine_i::SalomeApp_Engine_i() +SalomeApp_Engine_i::SalomeApp_Engine_i( const char* theComponentName ) { - myInstance = this; + myComponentName = theComponentName; + MESSAGE("SalomeApp_Engine_i::SalomeApp_Engine_i(): myComponentName = " << + myComponentName << ", this = " << this); } /*! @@ -49,6 +58,8 @@ SalomeApp_Engine_i::SalomeApp_Engine_i() */ SalomeApp_Engine_i::~SalomeApp_Engine_i() { + MESSAGE("SalomeApp_Engine_i::~SalomeApp_Engine_i(): myComponentName = " << + myComponentName << ", this = " << this); } SALOMEDS::TMPFile* SalomeApp_Engine_i::Save (SALOMEDS::SComponent_ptr theComponent, @@ -57,22 +68,23 @@ SALOMEDS::TMPFile* SalomeApp_Engine_i::Save (SALOMEDS::SComponent_ptr theCompone { SALOMEDS::TMPFile_var aStreamFile = new SALOMEDS::TMPFile; - cout << "SalomeApp_Engine_i::Save() isMultiFile = " << isMultiFile << endl; if (CORBA::is_nil(theComponent) || CORBA::is_nil(theComponent->GetStudy())) return aStreamFile._retn(); const int studyId = theComponent->GetStudy()->StudyId(); - cout << "SalomeApp_Engine_i::Save() - studyId = " << studyId << endl; // Get a temporary directory to store a file //std::string aTmpDir = isMultiFile ? theURL : SALOMEDS_Tool::GetTmpDir(); if (myMap.count(studyId)) { - cout << "SalomeApp_Engine_i::Save() - myMap.count(studyId)" << endl; - MapOfListOfFiles mapOfListOfFiles = myMap[studyId]; std::string componentName (theComponent->ComponentDataType()); - cout << "SalomeApp_Engine_i::Save() - componentName = " << componentName << endl; - ListOfFiles listOfFiles = mapOfListOfFiles[componentName]; + + // Error somewhere outside - Save() called with + // wrong SComponent instance + if ( myComponentName != componentName ) + return aStreamFile._retn(); + + const ListOfFiles& listOfFiles = myMap[studyId]; // listOfFiles must contain temporary directory name in its first item // and names of files (relatively the temporary directory) in the others @@ -80,7 +92,6 @@ SALOMEDS::TMPFile* SalomeApp_Engine_i::Save (SALOMEDS::SComponent_ptr theCompone if (n > 0) { // there are some files, containing persistent data of the component std::string aTmpDir = listOfFiles[0]; - cout << "SalomeApp_Engine_i::Save() - aTmpDir = " << aTmpDir << endl; // Create a list to store names of created files SALOMEDS::ListOfFileNames_var aSeq = new SALOMEDS::ListOfFileNames; @@ -108,6 +119,12 @@ CORBA::Boolean SalomeApp_Engine_i::Load (SALOMEDS::SComponent_ptr theComponent, if (CORBA::is_nil(theComponent) || CORBA::is_nil(theComponent->GetStudy())) return false; + // Error somewhere outside - Load() called with + // wrong SComponent instance + std::string componentName (theComponent->ComponentDataType()); + if ( myComponentName != componentName ) + return false; + const int studyId = theComponent->GetStudy()->StudyId(); // Create a temporary directory for the component's data files @@ -125,52 +142,259 @@ CORBA::Boolean SalomeApp_Engine_i::Load (SALOMEDS::SComponent_ptr theComponent, for (int i = 1; i < n; i++) listOfFiles[i] = std::string(aSeq[i - 1]); - //MapOfListOfFiles mapOfListOfFiles; - //if (myMap.count(studyId)) - // mapOfListOfFiles = myMap[studyId]; - //std::string componentName (theComponent->ComponentDataType()); - //mapOfListOfFiles[componentName] = listOfFiles; - //myMap[studyId] = mapOfListOfFiles; - - SetListOfFiles(listOfFiles, studyId, theComponent->ComponentDataType()); + SetListOfFiles(listOfFiles, studyId); return true; } -SalomeApp_Engine_i::ListOfFiles SalomeApp_Engine_i::GetListOfFiles (const int theStudyId, - const char* theComponentName) +SalomeApp_Engine_i::ListOfFiles SalomeApp_Engine_i::GetListOfFiles (const int theStudyId) { ListOfFiles aListOfFiles; - if (myMap.count(theStudyId)) + if (myMap.find(theStudyId) != myMap.end()) { - MapOfListOfFiles mapOfListOfFiles = myMap[theStudyId]; - std::string componentName (theComponentName); - if (mapOfListOfFiles.count(componentName)) - aListOfFiles = mapOfListOfFiles[componentName]; + aListOfFiles = myMap[theStudyId]; } return aListOfFiles; } -void SalomeApp_Engine_i::SetListOfFiles (const ListOfFiles theListOfFiles, - const int theStudyId, - const char* theComponentName) +void SalomeApp_Engine_i::SetListOfFiles (const ListOfFiles& theListOfFiles, + const int theStudyId) { - //if (!myMap.count(theStudyId)) { - // MapOfListOfFiles mapOfListOfFiles; - // myMap[theStudyId] = mapOfListOfFiles; - //} - - MapOfListOfFiles& mapOfListOfFiles = myMap[theStudyId]; - std::string componentName (theComponentName); - mapOfListOfFiles[componentName] = theListOfFiles; + myMap[theStudyId] = theListOfFiles; +} + +/*! + * DumpPython implementation for light modules + */ +Engines::TMPFile* SalomeApp_Engine_i::DumpPython(CORBA::Object_ptr theStudy, + CORBA::Boolean isPublished, + CORBA::Boolean isMultiFile, + CORBA::Boolean& isValidScript) +{ + MESSAGE("SalomeApp_Engine_i::DumpPython(): myComponentName = "<< + myComponentName << ", this = " << this); + + // Temporary solution: returning a non-empty sequence + // even if there's nothing to dump, to avoid crashes in SALOMEDS + // TODO: Improve SALOMEDSImpl_Study::DumpStudy() by skipping the components + // with isValidScript == false, and initialize isValidScript by false below. + Engines::TMPFile_var aStreamFile = new Engines::TMPFile(1); + aStreamFile->length( 1 ); + aStreamFile[0] = '\0'; + isValidScript = true; + + if (CORBA::is_nil(theStudy)) + return aStreamFile._retn(); + + SALOMEDS::Study_var studyDS = SALOMEDS::Study::_narrow( theStudy ); + const int studyId = studyDS->StudyId(); + + if (!myMap.count(studyId)) + return aStreamFile._retn(); + + ListOfFiles listOfFiles = myMap[studyId]; + + // listOfFiles must contain temporary directory name in its first item + // and names of files (relatively the temporary directory) in the others + if ( listOfFiles.size() < 2 ) + return aStreamFile._retn(); + + // there are some files, containing persistent data of the component + QString aTmpPath( listOfFiles.front().c_str() ); + QDir aTmpDir( aTmpPath ); + if ( !aTmpDir.exists() ) + return aStreamFile._retn(); + + // Calculate file sizes + QStringList aFilePaths; + QList aFileSizes; + qint64 aBuffSize = 0; + ListOfFiles::const_iterator aFIt = listOfFiles.begin(); + ListOfFiles::const_iterator aFEnd = listOfFiles.end(); + aFIt++; + for (; aFIt != aFEnd; aFIt++){ + QString aFileName( (*aFIt).c_str() ); + if ( !aTmpDir.exists( aFileName ) ){ + continue; + } + + QFile aFile( aTmpDir.filePath( aFileName ) ); + if ( !aFile.open( QIODevice::ReadOnly ) ){ + continue; + } + + aFilePaths.push_back( aTmpDir.filePath( aFileName ) ); + aFileSizes.push_back( aFile.size() ); + aBuffSize += aFileSizes.back(); + + aFile.close(); + } + + if ( !aFilePaths.size() || !aBuffSize ) + return aStreamFile._retn(); + + char* aBuffer = new char[aBuffSize + 1]; + if ( !aBuffer ) + return aStreamFile._retn(); + + // Convert the file(s) to the byte stream, multiple files are simply + // concatenated + // TODO: imporve multi-script support if necessary... + qint64 aCurrPos = 0; + QStringList::const_iterator aFileIt = aFilePaths.begin(); + QStringList::const_iterator aFileEnd = aFilePaths.end(); + QList::const_iterator aSIt = aFileSizes.begin(); + for ( ; aFileIt != aFileEnd; aFileIt++, aSIt++ ){ + QFile aFile( aTmpDir.filePath( *aFileIt ) ); + if ( !aFile.open( QIODevice::ReadOnly ) ){ + continue; + } + + // Incorrect size of file + // Do not remove the bad file to have some diagnostic means + if ( aFile.read( aBuffer + aCurrPos, *aSIt ) != *aSIt ){ + aFile.close(); + return aStreamFile._retn(); + } + + aCurrPos += (*aSIt); + aFile.remove(); + } + + // Here we should end up with empty aTmpDir + // TODO: Handle QDir::rmdir() error status somehow... + aTmpDir.rmdir( aTmpPath ); + + aBuffer[aBuffSize] = '\0'; + CORBA::Octet* anOctetBuf = (CORBA::Octet*)aBuffer; + aStreamFile = new Engines::TMPFile(aBuffSize + 1, aBuffSize + 1, anOctetBuf, 1); + + return aStreamFile._retn(); } /*! - \return shared instance of engine + \return Component data type string for this instance of the engine */ -SalomeApp_Engine_i* SalomeApp_Engine_i::GetInstance() +char* SalomeApp_Engine_i::ComponentDataType() { - return myInstance; + return const_cast( myComponentName.c_str() ); } + +/*! + \return +*/ +CORBA::ORB_var SalomeApp_Engine_i::orb() +{ + ORB_INIT& init = *SINGLETON_::Instance(); + // TODO: using QApplication here looks ugly, think how to + // obtain the ORB reference in a nicer way... + static CORBA::ORB_var _orb = init( qApp->argc(), qApp->argv() ); + return _orb; +} + +/*! + \return +*/ +PortableServer::POA_var SalomeApp_Engine_i::poa() +{ + static PortableServer::POA_var _poa; + if ( CORBA::is_nil( _poa ) ){ + CORBA::Object_var obj = orb()->resolve_initial_references( "RootPOA" ); + _poa = PortableServer::POA::_narrow( obj ); + } + return _poa; +} + +/*! + \return +*/ +SALOME_NamingService* SalomeApp_Engine_i::namingService() +{ + static SALOME_NamingService _ns(orb()); + return &_ns; +} + +/*! + Internal method, creates a CORBA engine for a light SALOME module + with the given "component data type" string, + activates it and registers in SALOME naming service with + /SalomeAppEngine/comp_data_type path. If the engine is already in the + naming service, simply returns and object reference to it. + \param theComponentName - synthetic "component data type" used to identify a given light module + \return Object reference to the CORBA engine +*/ +CORBA::Object_ptr SalomeApp_Engine_i::engineForComponent( const char* theComponentName, + bool toCreate ) +{ + CORBA::Object_var anEngine; + if ( !theComponentName || !strlen( theComponentName ) ) + return anEngine._retn(); + + std::string aPath( "/SalomeAppEngine/" ); + aPath += theComponentName; + anEngine = namingService()->Resolve( aPath.c_str() ); + + // Activating a new instance of the servant + if ( toCreate && CORBA::is_nil( anEngine ) ){ + try { + SalomeApp_Engine_i* aServant = new SalomeApp_Engine_i( theComponentName ); + PortableServer::ObjectId_var id = poa()->activate_object( aServant ); + anEngine = aServant->_this(); + aServant->_remove_ref(); + namingService()->Register( anEngine.in(), aPath.c_str() ); + } + catch (CORBA::SystemException&) { + INFOS("Caught CORBA::SystemException."); + } + catch (CORBA::Exception&) { + INFOS("Caught CORBA::Exception."); + } + catch (...) { + INFOS("Caught unknown exception."); + } + } + + return anEngine._retn(); +} + +/*! + \param theComponentName - synthetic "component data type" used to identify a given light module + \return IOR string for the CORBA engine for a light SALOME module + with the given "component data type" string + \sa GetInstance( const char* theComponentName ) +*/ +std::string SalomeApp_Engine_i::EngineIORForComponent( const char* theComponentName, + bool toCreate ) +{ + std::string anIOR( "" ); + CORBA::Object_var anEngine = engineForComponent( theComponentName, toCreate ); + if ( !CORBA::is_nil( anEngine ) ) + { + CORBA::String_var objStr = orb()->object_to_string( anEngine.in() ); + anIOR = std::string( objStr.in() ); + } + return anIOR; +} + +/*! + \param theComponentName - synthetic "component data type" used to identify a given light module + \return A pointer to corresponding C++ engine instance, null means some internal problems. + \sa EngineIORForComponent( const char* theComponentName ) +*/ +SalomeApp_Engine_i* SalomeApp_Engine_i::GetInstance( const char* theComponentName, + bool toCreate ) +{ + SalomeApp_Engine_i* aServant = 0; + CORBA::Object_var anEngine = engineForComponent( theComponentName, toCreate ); + if ( !CORBA::is_nil( anEngine ) ) + { + PortableServer::Servant aServantBase = poa()->reference_to_servant( anEngine.in() ); + aServant = dynamic_cast( aServantBase ); + } + MESSAGE("SalomeApp_Engine_i::GetInstance(): theComponentName = " << + theComponentName << ", aServant = " << aServant); + return aServant; +} + diff --git a/src/Session/SalomeApp_Engine_i.hxx b/src/Session/SalomeApp_Engine_i.hxx index 965f46b9c..e9030cbb7 100755 --- a/src/Session/SalomeApp_Engine_i.hxx +++ b/src/Session/SalomeApp_Engine_i.hxx @@ -39,11 +39,13 @@ #include #include CORBA_SERVER_HEADER(SalomeApp_Engine) +class SALOME_NamingService; + class SESSION_EXPORT SalomeApp_Engine_i: public POA_SalomeApp::Engine, - public Engines_Component_i + public Engines_Component_i { public: - SalomeApp_Engine_i(); + SalomeApp_Engine_i( const char* theComponentName ); ~SalomeApp_Engine_i(); SALOMEDS::TMPFile* Save( SALOMEDS::SComponent_ptr theComponent, @@ -55,17 +57,22 @@ public: const char* theURL, bool isMultiFile ); + virtual Engines::TMPFile* DumpPython(CORBA::Object_ptr theStudy, + CORBA::Boolean isPublished, + CORBA::Boolean isMultiFile, + CORBA::Boolean& isValidScript); + public: typedef std::vector ListOfFiles; - ListOfFiles GetListOfFiles (const int theStudyId, - const char* theComponentName); - - void SetListOfFiles (const ListOfFiles theListOfFiles, - const int theStudyId, - const char* theComponentName); + ListOfFiles GetListOfFiles (const int theStudyId); + void SetListOfFiles (const ListOfFiles& theListOfFiles, + const int theStudyId); - static SalomeApp_Engine_i* GetInstance(); + static std::string EngineIORForComponent( const char* theComponentName, + bool toCreate ); + static SalomeApp_Engine_i* GetInstance ( const char* theComponentName, + bool toCreate ); public: // methods from SALOMEDS::Driver without implementation. Must be redefined because @@ -73,7 +80,7 @@ public: SALOMEDS::TMPFile* SaveASCII( SALOMEDS::SComponent_ptr, const char*, bool ) {return 0;} CORBA::Boolean LoadASCII( SALOMEDS::SComponent_ptr, const SALOMEDS::TMPFile&, const char*, bool ) {return 0;} void Close( SALOMEDS::SComponent_ptr ) {} - char* ComponentDataType() {return 0;} + char* ComponentDataType(); char* IORToLocalPersistentID( SALOMEDS::SObject_ptr, const char*, CORBA::Boolean, CORBA::Boolean ) {return 0;} char* LocalPersistentIDToIOR( SALOMEDS::SObject_ptr, const char*, CORBA::Boolean, CORBA::Boolean ) {return 0;} bool CanPublishInStudy( CORBA::Object_ptr ) {return 0;} @@ -84,11 +91,17 @@ public: SALOMEDS::SObject_ptr PasteInto( const SALOMEDS::TMPFile&, CORBA::Long, SALOMEDS::SObject_ptr ) {return 0;} private: - typedef std::map MapOfListOfFiles; - typedef std::map MapOfMapOfListOfFiles; - MapOfMapOfListOfFiles myMap; + static CORBA::ORB_var orb(); + static PortableServer::POA_var poa(); + static SALOME_NamingService* namingService(); + static CORBA::Object_ptr engineForComponent( const char* theComponentName, + bool toCreate ); + +private: + typedef std::map MapOfListOfFiles; + MapOfListOfFiles myMap; - static SalomeApp_Engine_i* myInstance; + std::string myComponentName; }; #endif diff --git a/src/Session/Session.pro b/src/Session/Session.pro deleted file mode 100644 index 02a936cc7..000000000 --- a/src/Session/Session.pro +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -#TEMPLATE = lib -#TARGET = SalomeSession -#DESTDIR = ../../lib -#MOC_DIR = ../../moc -#OBJECTS_DIR = ../../obj/$$TARGET -# ================> -# -TEMPLATE = -TARGET = SALOME_Session_Server -DESTDIR = ../../bin -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -# <================ - - -QT_MT_INCLUDES = $$(QTDIR)/include $$(QTDIR)/include/QtCore $$(QTDIR)/include/QtGui $$(QTDIR)/include/QtOpenGL $$(QTDIR)/include/QtXml - -PYTHON_INCLUDES = $$(PYTHONHOME)/include/python2.4 - -HDF5_INCLUDES = $$(HDF5HOME)/include - -BOOST_CPPFLAGS = $$(BOOSTDIR)/include - -KERNEL_CXXFLAGS = $$(KERNEL_ROOT_DIR)/include/salome - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -OMNIORBDIR = $$(OMNIORBDIR) -CORBA_INCLUDES = $${OMNIORBDIR}/include $${OMNIORBDIR}/include/omniORB4 $${OMNIORBDIR}/include/COS - -INCLUDEPATH += $${QT_MT_INCLUDES} $${PYTHON_INCLUDES} $${HDF5_INCLUDES} $${BOOST_CPPFLAGS} $${KERNEL_CXXFLAGS} $${CAS_CPPFLAGS} $${CORBA_INCLUDES} ../../salome_adm/unix $$(GUI_ROOT_DIR)/idl ../Qtx ../SUIT ../Event - -QT_MT_LIBS = -L$$(QTDIR)/lib -lQtCore -lQtXml -lQtGui -lQtOpenGL - -KERNEL_LDFLAGS = -L$$(KERNEL_ROOT_DIR)/lib/salome - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -LIBS += $${QT_MT_LIBS} $${KERNEL_LDFLAGS} -lSalomeNS -lSalomeLifeCycleCORBA -lOpUtil -lSALOMELocalTrace -lSalomeCatalog -lSalomeDSClient $${CAS_KERNEL} -L../../lib -lwith_loggerTraceCollector -lsuit -lEvent -L$$(GUI_ROOT_DIR)/idl -lSalomeIDLGUI - -# ================> - -OMNIORB_LIBS = -L$${OMNIORBDIR}/lib -lomniORB4 -lomniDynamic4 -lCOS4 -lCOSDynamic4 -lomnithread - -HDF5_LIBS = -L$$(HDF5HOME)/lib -lhdf5 - -INCLUDEPATH = $${INCLUDEPATH} -LIBS = $${LIBS} $${OMNIORB_LIBS} $${HDF5_LIBS} -lSalomeContainer -lSalomeResourcesManager -lTOOLSDS -lSalomeHDFPersist -lSalomeDSImpl -lSalomeGenericObj -lRegistry -lSalomeNotification -lSALOMEBasics -L$$(GUI_ROOT_DIR)/lib -lqtx -lSalomeSession - -# <================ - - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += SESSION_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS OMNIORB_VERSION=4 __x86__ __linux__ COMP_CORBA_DOUBLE COMP_CORBA_LONG - -HEADERS = Session_Session_i.hxx -HEADERS += Session_ServerLauncher.hxx -HEADERS += Session_ServerThread.hxx -HEADERS += Session_ServerCheck.hxx -HEADERS += SalomeApp_Engine_i.hxx - -SOURCES = Session_Session_i.cxx -SOURCES += Session_ServerThread.cxx -SOURCES += Session_ServerLauncher.cxx -SOURCES += Session_ServerCheck.cxx -SOURCES += SalomeApp_Engine_i.cxx - -# ================> - -SOURCES += SALOME_Session_Server.cxx - -# <================ - -#includes.files = $$HEADERS -#includes.path = ../../include - -#INSTALLS += includes - - diff --git a/src/Session/Session_ServerLauncher.cxx b/src/Session/Session_ServerLauncher.cxx index df8422f8f..71a8503aa 100755 --- a/src/Session/Session_ServerLauncher.cxx +++ b/src/Session/Session_ServerLauncher.cxx @@ -127,6 +127,17 @@ void Session_ServerLauncher::CheckArgs() } case 1: // looking for server type { + // Temporary solution + // Issue 21337 - no more SalomeApp_Engine_i activation here + // TODO: To be removed as soon as any trace of SalomeAppEngine + // has been eliminated from KERNEL scripts + if (strcmp(_argv[iarg], "SalomeAppEngine")==0){ + argState = 0; + iarg += 2; // skipping "()" + break; + } + // Temporary solution + for (int i=0; i #include "Session_Session_i.hxx" -#include "SalomeApp_Engine_i.hxx" #include #include @@ -56,13 +55,12 @@ using namespace std; -const int Session_ServerThread::NB_SRV_TYP = 7; +const int Session_ServerThread::NB_SRV_TYP = 6; const char* Session_ServerThread::_serverTypes[NB_SRV_TYP] = {"Container", "ModuleCatalog", "Registry", "SALOMEDS", "Session", - "SalomeAppEngine", "ContainerManager"}; /*! @@ -158,13 +156,7 @@ void Session_ServerThread::Init() ActivateSession(_argc, _argv); break; } - case 5: // SalomeApp_Engine - { - NamingService_WaitForServerReadiness(_NS,"/myStudyManager"); - ActivateEngine(_argc, _argv); - break; - } - case 6: // Container Manager + case 5: // Container Manager { NamingService_WaitForServerReadiness(_NS,""); ActivateContainerManager(_argc, _argv); @@ -390,29 +382,6 @@ void Session_ServerThread::ActivateContainer(int argc, } } -void Session_ServerThread::ActivateEngine(int /*argc*/, char ** /*argv*/) -{ - try { - MESSAGE("SalomeApp_Engine thread started"); - SalomeApp_Engine_i* anEngine = new SalomeApp_Engine_i(); - PortableServer::ObjectId_var id =_root_poa->activate_object( anEngine ); - MESSAGE("poa->activate_object( SalomeApp_Engine )"); - - CORBA::Object_var obj = anEngine->_this(); - anEngine->_remove_ref(); - _NS->Register( obj ,"/SalomeAppEngine"); - } - catch (CORBA::SystemException&) { - INFOS("Caught CORBA::SystemException."); - } - catch (CORBA::Exception&) { - INFOS("Caught CORBA::Exception."); - } - catch (...) { - INFOS("Caught unknown exception."); - } -} - void Session_ServerThread::ActivateSession(int argc, char ** argv) { diff --git a/src/TOOLSGUI/TOOLSGUI.pro b/src/TOOLSGUI/TOOLSGUI.pro deleted file mode 100644 index 9348c64d1..000000000 --- a/src/TOOLSGUI/TOOLSGUI.pro +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = ToolsGUI -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -BOOST_CPPFLAGS = $$(BOOSTDIR)/include - -KERNEL_CXXFLAGS = $$(KERNEL_ROOT_DIR)/include/salome - -CORBA_INCLUDES = $$(OMNIORBDIR)/include $$(OMNIORBDIR)/include/omniORB4 $$(OMNIORBDIR)/include/COS - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -KERNEL_LDFLAGS = -L$$(KERNEL_ROOT_DIR)/lib/salome - -INCLUDEPATH += $${CAS_CPPFLAGS} $${BOOST_CPPFLAGS} $${KERNEL_CXXFLAGS} $${CORBA_INCLUDES} ../../salome_adm/unix ../../idl ../Qtx ../SUIT -LIBS += -L../../lib -lsuit -lSalomeNS -lOpUtil $${CAS_KERNEL} $${KERNEL_LDFLAGS} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = ToolsGUI.h -HEADERS += ToolsGUI_RegWidget.h -HEADERS += ToolsGUI_CatalogGeneratorDlg.h - -SOURCES = ToolsGUI_CatalogGeneratorDlg.cxx -SOURCES += ToolsGUI_RegWidget.cxx -SOURCES += ToolsGUI.cxx - -TRANSLATIONS = resources/ToolsGUI_icons.ts \ - resources/ToolsGUI_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/TreeData/DataModel.cxx b/src/TreeData/DataModel.cxx new file mode 100644 index 000000000..615cd865d --- /dev/null +++ b/src/TreeData/DataModel.cxx @@ -0,0 +1,57 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#include "DataModel.hxx" + +DataModel::DataModel() { + +} + +DataModel::~DataModel() { + _mapDataObjects.clear(); +} + +bool DataModel::addDataObject(DataObject * dataObject) { + _mapDataObjects[dataObject->getNameId()] = dataObject; + return true; +} +bool DataModel::removeDataObject(string nameId) { + _mapDataObjects.erase(nameId); + return true; +} +bool DataModel::removeDataObject(DataObject * dataObject) { + if ( dataObject == NULL ) return false; + return removeDataObject(dataObject->getNameId()); +} + +DataObject * DataModel::getDataObject(string id) { + return _mapDataObjects[id]; +} + + + +map::iterator DataModel::begin() { + return _mapDataObjects.begin(); +} + +map::iterator DataModel::end() { + return _mapDataObjects.end(); +} diff --git a/src/TreeData/DataModel.hxx b/src/TreeData/DataModel.hxx new file mode 100644 index 000000000..7dd6d3b76 --- /dev/null +++ b/src/TreeData/DataModel.hxx @@ -0,0 +1,64 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + + +#ifndef DATAMODEL_H +#define DATAMODEL_H + +#include "TreeData.hxx" + +#include +#include "DataObject.hxx" + +class TREEDATA_EXPORT DataModel { + +public: + DataModel(); + ~DataModel(); + + /*! + * This function can be used to create a specific instance of + * DataObject. Note that this function is a pure virtual method and + * then no default behavior is done. In particular, the newly + * created object is not automatically added to the data model. This + * behavior should be implemented in a dedicated version of this + * class. + */ + virtual DataObject * newDataObject() = 0; + + /*! Function to add data object to the model */ + bool addDataObject(DataObject * dataObject); + /*! Functions to remove data object from the model */ + bool removeDataObject(string nameId); + bool removeDataObject(DataObject * dataObject); + /*! Function to retrieve a data object in the model */ + DataObject * getDataObject(string nameId); + + map::iterator begin(); + map::iterator end(); + +private: + map _mapDataObjects; + + +}; + +#endif // DATAMODEL_H diff --git a/src/TreeData/DataObject.cxx b/src/TreeData/DataObject.cxx new file mode 100644 index 000000000..ac651472e --- /dev/null +++ b/src/TreeData/DataObject.cxx @@ -0,0 +1,73 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#include "DataObject.hxx" +#include + +// Static assignement +int DataObject::_lastId=0; +const string DataObject::_BASENAME = string("object_"); +const string DataObject::pathsep = string("/"); + +DataObject::DataObject() { + _nameId = _BASENAME+ToString(_lastId); + _lastId++; + // The default label is set to the nameId, but it can be modified + // using setLabel whereas the nameId can't be modified. + _label = _nameId; +} + +DataObject::~DataObject() { + _properties.clear(); +} + +void DataObject::setLabel(string label) { + _label = label; +} +string DataObject::getLabel() { + return _label; +} + +string DataObject::getPathName() { + string pathName; + pathName = this->getPath() + pathsep + this->getLabel(); + return pathName; +} + + +string DataObject::getNameId() { + return _nameId; +} + +void DataObject::setProperty(string key, string value) { + _properties[key] = value; +} +string DataObject::getProperty(string key) { + return _properties[key]; +} + +string DataObject::toString() { + string serialize="\n"; + serialize+="nameId = "+getNameId()+"\n"; + serialize+="label = "+getLabel()+"\n"; + serialize+="path = "+getPath(); + return serialize; +} diff --git a/src/TreeData/DataObject.hxx b/src/TreeData/DataObject.hxx new file mode 100644 index 000000000..ffee2939e --- /dev/null +++ b/src/TreeData/DataObject.hxx @@ -0,0 +1,68 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#ifndef DATAOBJECT_H +#define DATAOBJECT_H + +#include "TreeData.hxx" + +#include +#include +using namespace std; + +class TREEDATA_EXPORT DataObject { + +public: + DataObject(); + ~DataObject(); + + void setLabel(string label); + string getLabel(); + void setProperty(string key, string value); + string getProperty(string key); + + /*! + * This function specifies the localization of the object in the + * hierarchical organization that can be defined by the DataModel it + * belongs to. + */ + virtual string getPath() = 0; + string getPathName(); + string getNameId(); + + static const string pathsep; + + /*! This function can be used for debug */ + string toString(); + +private: + /*! The name this object can be displayed with */ + string _label; + /*! The identifier of this object. An identifier is invariant all the session long */ + string _nameId; + /*! The dictionnary of properties that characterize this object */ + map _properties; + + static int _lastId; + static const string _BASENAME; +}; + +#endif // DATAOBJECT_H diff --git a/src/TreeData/DataProcessor.cxx b/src/TreeData/DataProcessor.cxx new file mode 100644 index 000000000..f35676527 --- /dev/null +++ b/src/TreeData/DataProcessor.cxx @@ -0,0 +1,86 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + + +#include "DataProcessor.hxx" +#include +#include "QtHelper.hxx" + +DataProcessor::DataProcessor(DataModel * dataModel) { + _dataModel = dataModel; +} + +/*! + * This function retrieves in the data model all the DataObject + * associated to the item nameIds contained in the specified list. The + * input list is what the TreeView sends via the notification signal. + */ +DataObjectVector * DataProcessor::extract(QStringList itemNameIdList) { + if ( _dataModel == NULL ) { + STDLOG("No data model associated to this processor"); + return NULL; + } + + DataObjectVector * dataObjectList = new DataObjectVector(); + + // We can request the dataModel to obtain the dataObject associated + // to each of the item (iteNameId is a TreeView id, Qt stuff only). + QStringList::const_iterator it; + for (it = itemNameIdList.constBegin(); it != itemNameIdList.constEnd(); ++it) { + QString itemNameId = *it; + DataObject * dataObject = _dataModel->getDataObject(QS2S(itemNameId)); + + if ( dataObject != NULL ) { + dataObjectList->push_back(dataObject); + } else { + LOG("No data object associated to the item "<preprocess(itemNameIdList); + + // We can request the dataModel to obtain the dataObject associated + // to each of the item (iteNameId is a TreeView id, Qt stuff only). + QStringList::const_iterator it; + for (it = itemNameIdList.constBegin(); it != itemNameIdList.constEnd(); ++it) { + QString itemNameId = *it; + DataObject * dataObject = _dataModel->getDataObject(QS2S(itemNameId)); + + if ( dataObject != NULL ) { + this->processDataObject(dataObject); + } else { + STDLOG("No data object associated to the item "<postprocess(itemNameIdList); +} + + diff --git a/src/TreeData/DataProcessor.hxx b/src/TreeData/DataProcessor.hxx new file mode 100644 index 000000000..1e914db28 --- /dev/null +++ b/src/TreeData/DataProcessor.hxx @@ -0,0 +1,68 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#ifndef _DATAPROCESSOR_H_ +#define _DATAPROCESSOR_H_ + +#include "TreeData.hxx" + +#include "DataModel.hxx" +#include "DataObject.hxx" +#include +#include + +#include +typedef std::vector DataObjectVector; + +// +// ================================================================= +// This class can be used to automize processing on data objects. +// The inputs of public functions are list of selected items in the +// tree view. The function automize the extraction of associated +// DataObject and can automize the processing of these object if the +// virtual function processDataObject is implemented. +// ================================================================= +// + +class TREEDATA_EXPORT DataProcessor { + +public: + DataProcessor(DataModel * dataModel); + + DataObjectVector * extract(QStringList itemNameIdList); + void process(QStringList itemNameIdList); + + +protected: + virtual void preprocess(QStringList itemNameIdList) { + // Implement something to be executed at the begining of the process function + }; + virtual void postprocess(QStringList itemNameIdList) { + // Implement something to be executed at the end of the process function + }; + // Implement what must be done with each DataObject during the process function + virtual void processDataObject(DataObject * dataObject) = 0; + +private: + DataModel * _dataModel; +}; + +#endif diff --git a/src/TreeData/DockWidgets.cxx b/src/TreeData/DockWidgets.cxx new file mode 100644 index 000000000..ead3b5ce7 --- /dev/null +++ b/src/TreeData/DockWidgets.cxx @@ -0,0 +1,106 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#include "DockWidgets.hxx" + +// Qt includes +#include + +// SALOME includes +#include +#include +#include + +/*! + * This create a gui container to hold widgets dedicated to the XCAD + * data model. By default, the dock widgets are not visible. Use the + * show() method to control the visibility (usefull when activating + * and desactivating the module to show/hide the dock widgets). + * + * This class does not make any hypothesis on what will be embedded in + * the dock widgets (only that it is QTreeView). The QTreeView is + * defined elsewhere and is generaly rendering a tree model containing + * tree items. + */ +DockWidgets::DockWidgets(SalomeApp_Application* salomeApp, + bool tabify, + const char * title) { + _salomeApp = salomeApp; + + QMainWindow *parent = _salomeApp->desktop(); + _dwDataPanel = new QDockWidget(parent); + _dwDataPanel->setVisible(false); + _dwDataPanel->setWindowTitle(title); + parent->addDockWidget(Qt::LeftDockWidgetArea, _dwDataPanel); + // + // At this step, the _dwDataPanel is located side by side with the object + // browser (or one over the other). + // + // It is possible to tabify the different dock widgets in one single + // tabbed widget. See the above example: + this->tabify(tabify); +} + +void DockWidgets::tabify(bool tabify) { + if ( tabify ) { + // We first get the object browser tree view, and then the + // associated DockWidget. Note that the tree view is a SALOME + // specific extention of the originate QTreeView and called + // QtxTreeView. + SUIT_DataBrowser* objectBrowser = _salomeApp->objectBrowser(); + QtxTreeView* treeView = objectBrowser->treeView(); + QWidget* pw = treeView->parentWidget(); + QDockWidget* dwObjectBrowser = 0; + while ( pw && !dwObjectBrowser ) { + dwObjectBrowser = ::qobject_cast( pw ); + pw = pw->parentWidget(); + }; + QMainWindow *parent = _salomeApp->desktop(); + parent->tabifyDockWidget(_dwDataPanel, dwObjectBrowser); + parent->setTabOrder(_dwDataPanel, dwObjectBrowser); + parent->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); + } + else { + // TO BE IMPLEMENTED + } +} + +/*! + * This function controls the visibility of the dock widgets. + */ +void DockWidgets::show(bool isVisible) { + _dwDataPanel->setVisible(isVisible); +} + +/*! + * This function initializes the central part of the dock widget with + * a tree view that can hold a hierarchical data model. + */ +void DockWidgets::setDataView(QTreeView * dataView) { + _tvDataView = dataView; + _tvDataView->setParent(_dwDataPanel); + _tvDataView->setMinimumHeight(200); + _dwDataPanel->setWidget(_tvDataView); +} + +void DockWidgets::setPropertiesView(QTreeView * propertiesView) { + // Not implemented yet +} diff --git a/src/TreeData/DockWidgets.hxx b/src/TreeData/DockWidgets.hxx new file mode 100644 index 000000000..c374c6722 --- /dev/null +++ b/src/TreeData/DockWidgets.hxx @@ -0,0 +1,51 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#ifndef _DOCKWIDGETS_H_ +#define _DOCKWIDGETS_H_ + +#include "TreeData.hxx" + +// Qt includes +#include +#include + +// SALOME includes +#include + +class TREEDATA_EXPORT DockWidgets { + public: + DockWidgets(SalomeApp_Application* salomeApp, + bool tabify=false, + const char * title="Data Model"); + + void tabify(bool tabify); + void show(bool isVisible); + void setDataView(QTreeView * dataView); + void setPropertiesView(QTreeView * propertyView); + + private: + SalomeApp_Application* _salomeApp; + QDockWidget * _dwDataPanel; + QTreeView * _tvDataView; +}; + +#endif // _DOCKWIDGETS_H_ diff --git a/src/TreeData/Makefile.am b/src/TreeData/Makefile.am new file mode 100644 index 000000000..3ec85867d --- /dev/null +++ b/src/TreeData/Makefile.am @@ -0,0 +1,109 @@ +# Copyright (C) 2010 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. +# +# 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 +# +# -* Makefile *- +# +# Author : Guillaume Boulant (EDF) +# + +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +SUBDIRS = . Test + +# moc-files generation +%_moc.cxx: %.hxx + $(MOC) $< -o $@ + +mostlyclean-local: + rm -f @builddir@/*_moc.cxx + rm -f @builddir@/*.qm + +# Libraries targets +lib_LTLIBRARIES = libSalomeTreeData.la + +# +# Generic source files +# +salomeinclude_HEADERS = \ + TreeData.hxx \ + DockWidgets.hxx \ + TreeModel.hxx \ + TreeItem.hxx \ + TreeView.hxx \ + TreeObserver.hxx \ + DataModel.hxx \ + DataObject.hxx \ + DataProcessor.hxx \ + TreeGuiManager.hxx + +dist_libSalomeTreeData_la_SOURCES = \ + DockWidgets.cxx \ + TreeModel.cxx \ + TreeItem.cxx \ + TreeView.cxx \ + TreeObserver.cxx \ + DataModel.cxx \ + DataObject.cxx \ + DataProcessor.cxx \ + TreeGuiManager.cxx + +# MOC pre-processing +MOC_FILES_HXX = \ + TreeModel_moc.cxx \ + TreeView_moc.cxx \ + TreeObserver_moc.cxx + +nodist_libSalomeTreeData_la_SOURCES = $(MOC_FILES_HXX) + +CORBA_CXXFLAGS=@OMNIORB_CXXFLAGS@ @OMNIORB_INCLUDES@ +CORBA_LIBS=@OMNIORB_LIBS@ + +QT_CXXFLAGS=@QT_INCLUDES@ @QT_MT_INCLUDES@ +CAS_CXXFLAGS=@CAS_CPPFLAGS@ @CAS_CXXFLAGS@ + +BOOST_CPPFLAGS=@BOOST_CPPFLAGS@ +BOOST_LIBS=@BOOST_LIBS@ + +libSalomeTreeData_la_CPPFLAGS = \ + $(QT_CXXFLAGS) \ + $(CAS_CXXFLAGS) \ + $(BOOST_CPPFLAGS) \ + $(CORBA_CXXFLAGS) \ + $(KERNEL_CXXFLAGS) \ + -I$(srcdir)/../SalomeApp \ + -I$(srcdir)/../LightApp \ + -I$(srcdir)/../CAM \ + -I$(srcdir)/../STD \ + -I$(srcdir)/../ObjBrowser \ + -I$(srcdir)/../SUIT \ + -I$(srcdir)/../Qtx \ + -I$(srcdir)/../GuiHelpers + + + + +libSalomeTreeData_la_LDFLAGS = \ + $(CORBA_LIBS) $(QT_LIBS)\ + $(KERNEL_LDFLAGS) -lSalomeLifeCycleCORBA -lSalomeKernelHelpers \ + $(top_builddir)/src/SalomeApp/libSalomeApp.la \ + $(top_builddir)/src/LightApp/libLightApp.la \ + $(top_builddir)/src/SUIT/libsuit.la \ + $(top_builddir)/src/Qtx/libqtx.la \ + $(top_builddir)/src/CAM/libCAM.la \ + $(top_builddir)/src/STD/libstd.la \ + $(top_builddir)/src/ObjBrowser/libObjBrowser.la diff --git a/src/TreeData/Test/Makefile.am b/src/TreeData/Test/Makefile.am new file mode 100644 index 000000000..a1a9bd4bc --- /dev/null +++ b/src/TreeData/Test/Makefile.am @@ -0,0 +1,101 @@ +# Copyright (C) 2010 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. +# +# 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 +# +# -* Makefile *- +# +# Author : Guillaume Boulant (EDF) +# + +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +# moc-files generation +%_moc.cxx: %.hxx + $(MOC) $< -o $@ + +mostlyclean-local: + rm -f @builddir@/*_moc.cxx + rm -f @builddir@/*.qm + rm -f @builddir@/*_moc.cxx @builddir@/ui_*.h + +# qt forms files generation (uic) +ui_%.h: %.ui + $(UIC) -o $@ $< + + +CORBA_CXXFLAGS=@OMNIORB_CXXFLAGS@ @OMNIORB_INCLUDES@ +CORBA_LIBS=@OMNIORB_LIBS@ +QT_CXXFLAGS=@QT_INCLUDES@ @QT_MT_INCLUDES@ + +TEST_CPPFLAGS = \ + $(QT_CXXFLAGS) \ + $(CORBA_CXXFLAGS) \ + $(KERNEL_CXXFLAGS) \ + -I$(top_srcdir)/src/GuiHelpers \ + -I$(top_srcdir)/src/TreeData \ + -I. + +TEST_LDFLAGS = \ + $(CORBA_LIBS) $(QT_LIBS) \ + $(top_builddir)/src/TreeData/libSalomeTreeData.la \ + $(top_builddir)/src/GuiHelpers/libSalomeGuiHelpers.la \ + $(KERNEL_LDFLAGS) -lSalomeLifeCycleCORBA -lSalomeKernelHelpers + +# Program targets +bin_PROGRAMS = TreeData_guitester TreeData_tester + +MOC_FILES_HXX = \ + mainwindow_moc.cxx + +UIC_FILES = \ + ui_mainwindow.h + +BUILT_SOURCES = $(UIC_FILES) + +nodist_TreeData_guitester_SOURCES = $(MOC_FILES_HXX) $(UIC_FILES) + +TreeData_guitester_SOURCES = \ + testhelper.hxx \ + testhelper.cxx \ + guitester.cxx \ + mainwindow.hxx \ + mainwindow.cxx \ + MyDataModel.hxx \ + MyDataModel.cxx + +TreeData_guitester_CPPFLAGS = \ + $(TEST_CPPFLAGS) + +TreeData_guitester_LDFLAGS = \ + $(TEST_LDFLAGS) + +TreeData_tester_SOURCES = \ + tester.cxx \ + MyDataModel.cxx + +TreeData_tester_CPPFLAGS = \ + $(TEST_CPPFLAGS) + +TreeData_tester_LDFLAGS = \ + $(TEST_LDFLAGS) + +# test data files +testdir = $(salomeresdir)/testdata +test_DATA = \ + data.txt + +EXTRA_DIST+=$(test_DATA) diff --git a/src/TreeData/Test/MyDataModel.cxx b/src/TreeData/Test/MyDataModel.cxx new file mode 100644 index 000000000..0517147de --- /dev/null +++ b/src/TreeData/Test/MyDataModel.cxx @@ -0,0 +1,48 @@ +#include "MyDataModel.hxx" + +// +// ================================================================= +// MyDataObject implementation +// ================================================================= +// + +const string MyDataObject::PROPERTY_KEY_TYPE = "type"; +const string MyDataObject::PROPERTY_KEY_CIRCUIT ="circuit"; +const string MyDataObject::PROPERTY_KEY_REPFONC ="rf"; + +MyDataObject::MyDataObject() : DataObject() { + this->setProperty(PROPERTY_KEY_TYPE, "Tuyauterie"); + this->setProperty(PROPERTY_KEY_CIRCUIT,"RRA"); + this->setProperty(PROPERTY_KEY_REPFONC,"RF_01"); +} + +/*! This function specified the localization of the object in the + * hierarchical organization + */ +string MyDataObject::getPath() { + // We choose here a convention for organizing the path for this + // class of object. + /* + string path = getProperty(PROPERTY_KEY_CIRCUIT) + pathsep + + getProperty(PROPERTY_KEY_REPFONC) + pathsep + + getProperty(PROPERTY_KEY_TYPE); + */ + string path = getProperty(PROPERTY_KEY_TYPE) + pathsep + + getProperty(PROPERTY_KEY_CIRCUIT) + pathsep + + getProperty(PROPERTY_KEY_REPFONC); + + return path; +} + +// +// ================================================================= +// MyDataModel implementation +// ================================================================= +// +MyDataModel::MyDataModel() : DataModel() { +} + +DataObject * MyDataModel::newDataObject() { + MyDataObject * dataObject = new MyDataObject(); + return dataObject; +} diff --git a/src/TreeData/Test/MyDataModel.hxx b/src/TreeData/Test/MyDataModel.hxx new file mode 100644 index 000000000..4354ba5f7 --- /dev/null +++ b/src/TreeData/Test/MyDataModel.hxx @@ -0,0 +1,35 @@ +#ifndef _MYDATAMODEL_H_ +#define _MYDATAMODEL_H_ + +// +// ================================================================= +// Definition of an atom in the data model as an implementation of +// the virtual class DataObject +// ================================================================= +// + +#include "DataObject.hxx" +class MyDataObject: public DataObject { +public: + MyDataObject(); + virtual string getPath(); + static const string PROPERTY_KEY_TYPE; + static const string PROPERTY_KEY_CIRCUIT; + static const string PROPERTY_KEY_REPFONC; +}; + + +// +// ================================================================= +// Definition of the data model as an implementation of the virtual +// class DataModel. It implements the DataObject factory. +// ================================================================= +// +#include "DataModel.hxx" +class MyDataModel: public DataModel { +public: + MyDataModel(); + virtual DataObject * newDataObject(); +}; + +#endif // _MYDATAMODEL_H_ diff --git a/src/TreeData/Test/data.txt b/src/TreeData/Test/data.txt new file mode 100755 index 000000000..d28285b3b --- /dev/null +++ b/src/TreeData/Test/data.txt @@ -0,0 +1,15 @@ +Tuyauterie;RF1;T1 +Tuyauterie;RF1;T2 +Tuyauterie;RF1;T3 +Tuyauterie;RF2;T1 +Tuyauterie;RF3;T1 +Tuyauterie;RF3;T2 +Composansts;RF1;T1 +Composansts;RF1;T2 +Composansts;RF3;T2 +Genie Civil;RF1;T1 +Genie Civil;RF1;T2 +Genie Civil;RF1;T3 +Genie Civil;RF2;T1 +Genie Civil;RF3;T1 +Genie Civil;RF3;T2 diff --git a/src/TreeData/Test/guitester.cxx b/src/TreeData/Test/guitester.cxx new file mode 100644 index 000000000..5b36e7b93 --- /dev/null +++ b/src/TreeData/Test/guitester.cxx @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +#include + +// +// ================================================================= +// Generic helper functions +// ================================================================= +// +/*! + * This functions displays a main window that embeds the specified + * widget. A dockwidget is used to create a context similar to as the + * SALOME target context. + */ +void showWidget(QWidget * widget) { + + QMainWindow * window = new QMainWindow(); + + // Prepare a gui framework for testing the widget. We use a + // dockwidget, just to be in a context similar to as the SALOME + // target context. + QDockWidget * dwDataPanel = new QDockWidget(window); + dwDataPanel->setVisible(true); + dwDataPanel->setWindowTitle("XCAD data model"); + window->addDockWidget(Qt::LeftDockWidgetArea, dwDataPanel); + + // Then plug the widget in the dock widget framework: + widget->setParent(dwDataPanel); + widget->setMinimumHeight(300); + dwDataPanel->setWidget(widget); + + window->show(); +} + +// +// ================================================================= +// Tests functions for TreeModel +// ================================================================= +// +#include "TreeModel.hxx" +#include "MyDataModel.hxx" +#include "testhelper.hxx" + + +/*! + * This function fills the specified tree with data that show + * different levels of path in the tree. + */ +void _TEST_treemodel_addData_01(TreeModel * dataTreeModel) { + // We can first add categories (for example to set categories + // properties) + QStringList path; + DataObject * folder; + + path << "folder_1"; + folder = TESTHELPER_dummyObject("folder_1.1"); + dataTreeModel->addData(folder, path); + folder = TESTHELPER_dummyObject("folder_1.2"); + dataTreeModel->addData(folder, path); + + path.clear(); + path << "folder_2"; + folder = TESTHELPER_dummyObject("folder_2.1"); + dataTreeModel->addData(folder, path); + + // Then we can add data + DataObject * data; + path.clear(); + path << "folder_1" << "folder_1.1"; + data = TESTHELPER_dummyObject("data_1.1.1"); + dataTreeModel->addData(data, path); + data = TESTHELPER_dummyObject("data_1.1.2"); + dataTreeModel->addData(data, path); + // You can notice that there is no conceptual difference between a + // folder and an item, as in the QTreeModel. + + // No limit to the depth + path.clear(); + path << "xcad" << "data1" << "x" << "y"; + data = TESTHELPER_dummyObject("z"); + dataTreeModel->addData(data,path); +} + +#define LOOPSIZE 15 +/*! + * This function fills the specified tree with a huge amount of data + */ +void _TEST_treemodel_addData_02(TreeModel * dataTreeModel) { + QStringList path; + DataObject * data; + + START_TIMING(treemodel); + for (int i=0; iaddData(data,path); + path.clear(); + } + } + } + END_TIMING(treemodel,1); +} + +void _TEST_treemodel_addData_03(TreeModel * dataTreeModel) { + MyDataObject * dataObject = new MyDataObject(); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_TYPE, "Tuyauterie"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_CIRCUIT, "RCP"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_REPFONC, "RF1"); + dataTreeModel->addData(dataObject); + + dataObject = new MyDataObject(); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_TYPE, "Tuyauterie"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_CIRCUIT, "RCP"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_REPFONC, "RF1"); + dataTreeModel->addData(dataObject); + + dataObject = new MyDataObject(); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_TYPE, "Tuyauterie"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_CIRCUIT, "RCP"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_REPFONC, "RF2"); + dataTreeModel->addData(dataObject); + + dataObject = new MyDataObject(); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_TYPE, "Tuyauterie"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_CIRCUIT, "RRA"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_REPFONC, "RF1"); + dataTreeModel->addData(dataObject); + + dataObject = new MyDataObject(); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_TYPE, "Génie civil"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_CIRCUIT, "RRA"); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_REPFONC, "RF1"); + dataTreeModel->addData(dataObject); +} + +/*! + * This test function shows how it's possible to load data from a file + * to populate the tree model. + */ +void _TEST_treemodel_loadDataFromFile(TreeModel * dataTreeModel, const QString &filename) { + TESTHELPER_loadDataFromFile(dataTreeModel, filename); +} + +/*! + * Main test function for the tree model demo. + */ +#include "TreeModel.hxx" +#include "TreeView.hxx" +void TEST_treemodel() { + + START_TIMING(treemodel); + + // We first prepare a data view embedding a tree model + TreeView * dataView = new TreeView(); + QStringList headers; + headers << QObject::tr("Name") << QObject::tr("Value"); + TreeModel * dataTreeModel = new TreeModel(headers); + dataView->setModel(dataTreeModel); + END_TIMING(treemodel,1); + + // Then we can fill the tree model with data. Can proceed with + // different ways (comment/uncomment which you want to test): + _TEST_treemodel_loadDataFromFile(dataTreeModel, TESTHELPER_testfilename(DATAFILENAME)); + //_TEST_treemodel_addData_01(dataTreeModel); + //_TEST_treemodel_addData_02(dataTreeModel); + //_TEST_treemodel_addData_03(dataTreeModel); + // Finally, show the widget in a main window + END_TIMING(treemodel,1); + + showWidget(dataView); + END_TIMING(treemodel,1); +} + +// +// ================================================================= +// Tests functions for TreeModel with interactive changes +// ================================================================= +// +#include "mainwindow.hxx" +void TEST_treemodel_interactif() { + MainWindow * window = new MainWindow(); + window->show(); +} + +// +// ================================================================= +// +int main(int argc, char * argv[ ]) +{ + QApplication app(argc, argv); + TEST_treemodel(); + //TST_treemodel_interactif(); + return app.exec(); +} diff --git a/src/TreeData/Test/mainwindow.cxx b/src/TreeData/Test/mainwindow.cxx new file mode 100644 index 000000000..3dcf75853 --- /dev/null +++ b/src/TreeData/Test/mainwindow.cxx @@ -0,0 +1,169 @@ +#include + +#include "mainwindow.hxx" +#include "TreeModel.hxx" + +#include +#include "testhelper.hxx" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) +{ + setupUi(this); + + QStringList headers; + headers << tr("Title") << tr("Description"); + + TreeModel *model = new TreeModel(headers); + TESTHELPER_loadDataFromFile(model, TESTHELPER_testfilename(DATAFILENAME)); + + view->setModel(model); + for (int column = 0; column < model->columnCount(); ++column) + view->resizeColumnToContents(column); + + connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + + connect(view->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection &, + const QItemSelection &)), + this, SLOT(updateActions())); + + connect(actionsMenu, SIGNAL(aboutToShow()), this, SLOT(updateActions())); + connect(insertRowAction, SIGNAL(triggered()), this, SLOT(insertRow())); + connect(insertColumnAction, SIGNAL(triggered()), this, SLOT(insertColumn())); + connect(removeRowAction, SIGNAL(triggered()), this, SLOT(removeRow())); + connect(removeColumnAction, SIGNAL(triggered()), this, SLOT(removeColumn())); + connect(insertChildAction, SIGNAL(triggered()), this, SLOT(insertChild())); + connect(newDataAction, SIGNAL(triggered()), this, SLOT(newData())); + + updateActions(); +} + +void MainWindow::newData() { + LOG("MainWindow::newData(): START"); + + bool ok; + QString text = QInputDialog::getText(this, tr("QInputDialog::getText()"), + tr("Data path:"), QLineEdit::Normal, + "folder/subfolder/item", &ok); + if (!ok || text.trimmed().isEmpty()) + return; + + QStringList path = text.trimmed().split("/"); + TreeModel *model = (TreeModel *)view->model(); + + QString label = path.last(); + path.removeLast(); + DataObject * data = TESTHELPER_dummyObject(label); + model->addData(data,path); + + LOG("MainWindow::newData(): END"); +} + +void MainWindow::insertChild() +{ + QModelIndex index = view->selectionModel()->currentIndex(); + QAbstractItemModel *model = view->model(); + + if (model->columnCount(index) == 0) { + if (!model->insertColumn(0, index)) + return; + } + + if (!model->insertRow(0, index)) + return; + + for (int column = 0; column < model->columnCount(index); ++column) { + QModelIndex child = model->index(0, column, index); + model->setData(child, QVariant("[No data]"), Qt::EditRole); + if (!model->headerData(column, Qt::Horizontal).isValid()) + model->setHeaderData(column, Qt::Horizontal, QVariant("[No header]"), + Qt::EditRole); + } + + view->selectionModel()->setCurrentIndex(model->index(0, 0, index), + QItemSelectionModel::ClearAndSelect); + updateActions(); +} + +bool MainWindow::insertColumn(const QModelIndex &parent) +{ + QAbstractItemModel *model = view->model(); + int column = view->selectionModel()->currentIndex().column(); + + // Insert a column in the parent item. + bool changed = model->insertColumn(column + 1, parent); + if (changed) + model->setHeaderData(column + 1, Qt::Horizontal, QVariant("[No header]"), + Qt::EditRole); + + updateActions(); + + return changed; +} + +void MainWindow::insertRow() +{ + QModelIndex index = view->selectionModel()->currentIndex(); + QAbstractItemModel *model = view->model(); + + if (!model->insertRow(index.row()+1, index.parent())) + return; + + updateActions(); + + for (int column = 0; column < model->columnCount(index.parent()); ++column) { + QModelIndex child = model->index(index.row()+1, column, index.parent()); + model->setData(child, QVariant("[No data]"), Qt::EditRole); + } +} + +bool MainWindow::removeColumn(const QModelIndex &parent) +{ + QAbstractItemModel *model = view->model(); + int column = view->selectionModel()->currentIndex().column(); + + // Insert columns in each child of the parent item. + bool changed = model->removeColumn(column, parent); + + if (!parent.isValid() && changed) + updateActions(); + + return changed; +} + +void MainWindow::removeRow() +{ + QModelIndex index = view->selectionModel()->currentIndex(); + QAbstractItemModel *model = view->model(); + if (model->removeRow(index.row(), index.parent())) + updateActions(); +} + +void MainWindow::updateActions() +{ + bool hasSelection = !view->selectionModel()->selection().isEmpty(); + removeRowAction->setEnabled(hasSelection); + removeColumnAction->setEnabled(hasSelection); + + bool hasCurrent = view->selectionModel()->currentIndex().isValid(); + insertRowAction->setEnabled(hasCurrent); + insertColumnAction->setEnabled(hasCurrent); + + if (hasCurrent) { + view->closePersistentEditor(view->selectionModel()->currentIndex()); + + int row = view->selectionModel()->currentIndex().row(); + int column = view->selectionModel()->currentIndex().column(); + if (view->selectionModel()->currentIndex().parent().isValid()) + statusBar()->showMessage(tr("Position: (%1,%2)").arg(row).arg(column)); + else + statusBar()->showMessage(tr("Position: (%1,%2) in top level").arg(row).arg(column)); + } +} + +void MainWindow::contextMenuEvent(QContextMenuEvent *event) { + QMenu menu(this); + menu.addAction(newDataAction); + menu.exec(event->globalPos()); +} diff --git a/src/TreeData/Test/mainwindow.hxx b/src/TreeData/Test/mainwindow.hxx new file mode 100644 index 000000000..323121a80 --- /dev/null +++ b/src/TreeData/Test/mainwindow.hxx @@ -0,0 +1,35 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +#include "ui_mainwindow.h" + +class QAction; +class QTreeView; +class QWidget; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = 0); + +protected: + void contextMenuEvent(QContextMenuEvent *event); + +public slots: + void updateActions(); + +private slots: + void insertChild(); + bool insertColumn(const QModelIndex &parent = QModelIndex()); + void insertRow(); + bool removeColumn(const QModelIndex &parent = QModelIndex()); + void removeRow(); + void newData(); +}; + +#endif diff --git a/src/TreeData/Test/mainwindow.ui b/src/TreeData/Test/mainwindow.ui new file mode 100644 index 000000000..a7cfe6bdf --- /dev/null +++ b/src/TreeData/Test/mainwindow.ui @@ -0,0 +1,140 @@ + + MainWindow + + + + 0 + 0 + 573 + 468 + + + + Editable Tree Model + + + + + 0 + + + 0 + + + + + true + + + QAbstractItemView::SelectItems + + + QAbstractItemView::ScrollPerPixel + + + false + + + true + + + + + + + + + 0 + 0 + 573 + 29 + + + + + &Actions + + + + + + + + + + + + &File + + + + + + Data + + + + + + + + + + + E&xit + + + Ctrl+Q + + + + + Insert Row + + + Ctrl+I, R + + + + + Remove Row + + + Ctrl+R, R + + + + + Insert Column + + + Ctrl+I, C + + + + + Remove Column + + + Ctrl+R, C + + + + + Insert Child + + + Ctrl+N + + + + + new + + + + + + + + diff --git a/src/TreeData/Test/tester.cxx b/src/TreeData/Test/tester.cxx new file mode 100644 index 000000000..413c6727d --- /dev/null +++ b/src/TreeData/Test/tester.cxx @@ -0,0 +1,60 @@ +#include "QtHelper.hxx" + +// +// ================================================================= +// Helper functions for DataObject and DataModel classes +// ================================================================= +// + +// ---- +// A DataObject can't be used as is but must be specialized to +// specify the behavior in the hierarchic model. +#include "MyDataModel.hxx" + +void TEST_DataObject() { + // In this test, the object id should increase at each instance + DataObject * dataObject; + for (int i=0; i<100; i++) { + dataObject = new MyDataObject(); + QLOG("object nameId = " << dataObject->getNameId().c_str()); + } + QLOG("path = " << dataObject->getPath().c_str()); + QLOG("pathname = " << dataObject->getPathName().c_str()); + + QLOG("serialize= " << dataObject->toString().c_str()); + +} + +void TEST_DataModel() { + MyDataModel * dataModel = new MyDataModel(); + + int refIter = 53; + string refNameId; + + DataObject * dataObject; + for (int i=0; i<100; i++) { + // We can either create the data object using its constructor or + // using the factory of the model (the prefered way): + // dataObject = new MyDataObject(); + dataObject = dataModel->newDataObject(); + dataObject->setLabel("myobject"+ToString(i)); + if ( i == refIter ) { + refNameId = dataObject->getNameId(); + } + dataModel->addDataObject(dataObject); + } + + dataObject = dataModel->getDataObject(refNameId); + QLOG("object nameId = " << dataObject->getNameId().c_str()); + QLOG("path = " << dataObject->getPath().c_str()); + QLOG("pathname = " << dataObject->getPathName().c_str()); +} + +// +// ================================================================= +// +int main(int argc, char * argv[ ]) +{ + TEST_DataObject(); + //TEST_DataModel(); +} diff --git a/src/TreeData/Test/testhelper.cxx b/src/TreeData/Test/testhelper.cxx new file mode 100644 index 000000000..1b37c0276 --- /dev/null +++ b/src/TreeData/Test/testhelper.cxx @@ -0,0 +1,76 @@ + + +#include "testhelper.hxx" + +#include +#include + +#include "QtHelper.hxx" +#include "MyDataModel.hxx" + +// Standard C include (for getenv) +#include + +QString TESTHELPER_testfilename(const char * basefilename) { + QString aFile; + char * GUI_ROOT_DIR = getenv("GUI_ROOT_DIR"); + QString * root; + if ( GUI_ROOT_DIR != NULL ) { + root = new QString(GUI_ROOT_DIR); + } + else { + root = new QString("/home/gboulant/development/projets/salome/devel/XSALOME/install"); + } + QString relativePathName = "/share/salome/resources/gui/testdata/"; + aFile.append(*root + relativePathName + basefilename); + + QLOG("The test file is : "<setLabel(QCHARSTAR(label)); + return dataObject; +} + + +#define SEP ";" +/*! + * This test function shows how it's possible to load data from a file + * to populate the tree model. + */ +void TESTHELPER_loadDataFromFile(TreeModel * dataTreeModel, const QString &filename) { + QFile file ( filename ); + file.open ( QIODevice::ReadOnly ); + + MyDataObject * dataObject; + while ( 1 ) { + QByteArray byteArray = file.readLine(); + + if ( byteArray.isEmpty() ) + break; + + QString data = (QString ( byteArray.mid(0, byteArray.size()-1))).trimmed(); + QStringList dataList = data.split ( SEP ); + // The data list is used here to set properties (and then the path + // of location in the tree model). + + dataObject = new MyDataObject(); + // The label is autogenerated, but we may specify here a custom + // one. We just fill the properties with data values read in the + // file. + dataObject->setProperty(MyDataObject::PROPERTY_KEY_TYPE, QCHARSTAR(dataList[0])); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_REPFONC, QCHARSTAR(dataList[1])); + dataObject->setProperty(MyDataObject::PROPERTY_KEY_CIRCUIT, QCHARSTAR(dataList[2])); + if ( ! dataTreeModel->addData(dataObject) ) { + QLOG("ERR: data not added"); + } + } + + file.close(); +} diff --git a/src/TreeData/Test/testhelper.hxx b/src/TreeData/Test/testhelper.hxx new file mode 100644 index 000000000..53faa4679 --- /dev/null +++ b/src/TreeData/Test/testhelper.hxx @@ -0,0 +1,15 @@ + +#ifndef _TESTHELPER_HXX_ +#define _TESTHELPER_HXX_ + +#include +#include "TreeModel.hxx" +#include "DataObject.hxx" + +#define DATAFILENAME "data.txt" + +QString TESTHELPER_testfilename(const char * basefilename); +DataObject * TESTHELPER_dummyObject(QString label); +void TESTHELPER_loadDataFromFile(TreeModel * dataTreeModel, const QString &filename); + +#endif // _TESTHELPER_HXX_ diff --git a/src/SVTK/SVTK_Extension.h b/src/TreeData/TreeData.hxx similarity index 76% rename from src/SVTK/SVTK_Extension.h rename to src/TreeData/TreeData.hxx index ebbedc3a9..0934b72c9 100755 --- a/src/SVTK/SVTK_Extension.h +++ b/src/TreeData/TreeData.hxx @@ -20,20 +20,17 @@ // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -#ifndef SVTK_Extension_H -#define SVTK_Extension_H - -/*! - \file SVTK_Extension.h - Functions of loading OpenGL extensions. -*/ - -#include "SVTK.h" - -namespace SVTK -{ - SVTK_EXPORT void* getOpenGLExtension( const char* theExtension ); -} - +#ifndef TREEDATA_HXX +#define TREEDATA_HXX +#ifdef WIN32 +# if defined SALOMETREEDATA_EXPORTS || defined SalomeTreeData_EXPORTS +# define TREEDATA_EXPORT __declspec( dllexport ) +# else +# define TREEDATA_EXPORT __declspec( dllimport ) +# endif +#else +# define TREEDATA_EXPORT #endif + +#endif //TREEDATA_HXX \ No newline at end of file diff --git a/src/TreeData/TreeGuiManager.cxx b/src/TreeData/TreeGuiManager.cxx new file mode 100644 index 000000000..297692b3b --- /dev/null +++ b/src/TreeData/TreeGuiManager.cxx @@ -0,0 +1,153 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#include "TreeGuiManager.hxx" + +// SALOME includes +#include + +// XCAD includes +#include "TreeView.hxx" +#include "QtHelper.hxx" + +// TODO: +// IMP: The constructor should have a dockwidget as argument. +// The creation of the void dockwidget (without the data tree +// embedded) should be done previously by the StandardApp_Module. +// The constructor would fill the dockwidget with a data tree view + +/*! + * The construction of the gui manager setups a graphic framework that + * consists in a set of dock widgets implanted in the SALOME GUI and + * embedding a tree view rendering a data model (collection of data + * objects) in a hierarchic graphical organisation. + * + * The data model is a straith list of data objects while the view is + * a tree representation of this collection where folders corresponds + * to specific properties of the objects. + * + * This represention is for the needs of navigation in a huge amount + * of data and to ease the selection and processing of items. + */ +TreeGuiManager::TreeGuiManager(SalomeApp_Application * salomeApp, const char * title) + : TreeObserver() +{ + _salomeApp = salomeApp; + + bool tabify = false; + _dockWidgets = new DockWidgets(_salomeApp, tabify, title); + + // Create a TreeView to be associated to a TreeModel dedicated to + // the Xcad data: + _dataTreeView = new TreeView(); + QStringList headers; + headers << tr("Name") << tr("Value"); + _dataTreeModel = new TreeModel(headers); + _dataTreeView->setModel(_dataTreeModel); + + // Then plug the TreeView in the dock widget: + _dockWidgets->setDataView(_dataTreeView); + + // We specify here the dataview to be observed + this->observe(_dataTreeView); +} + +/*! + * This returns the SALOME application (SalomeApp_Application + * instance) associated to this TreeGuiManager. + */ +SalomeApp_Application * TreeGuiManager::getSalomeApplication() { + return _salomeApp; +} + + +/*! + * This function set a layout of the different dock widgets in one + * single tabbed widget. + */ +void TreeGuiManager::tabifyDockWidgets(bool tabify) { + _dockWidgets->tabify(tabify); +} + +/*! + * This function switch on/off the dock widgets managed by this + * gui manager. + */ +void TreeGuiManager::showDockWidgets(bool isVisible) { + _dockWidgets->show(isVisible); +} + +/*! + * This returns the data tree model defined in this + * TreeGuiManager. The data tree model is a tree representation of the + * data model associated to this TreeGuiManager. + */ +TreeModel * TreeGuiManager::getDataTreeModel() { + return _dataTreeModel; +} + +/*! + * This returns the data tree view defined in this + * TreeGuiManager. The data tree view can be request to customize the + * popup menu associated to the tree representation. + */ +TreeView * TreeGuiManager::getDataTreeView() { + return _dataTreeView; +} + + +/*! + * This function specifies the data model to be used by the + * TreeGuiManager. + */ +void TreeGuiManager::setDataModel(DataModel * dataModel) { + _dataModel = dataModel; +} + +DataModel * TreeGuiManager::getDataModel() { + return _dataModel; +} + +/*! + * This function processes the edit signals received from the + * TreeView. This is a default implementation that only prints the + * reception of the signal and some information about the dataObject + * associated to the item whose id is specified. In practice, the data + * model could be requested here to retrieve the data object to be + * edited from the nameId. + * TO BE IMPLEMENTED IN A DOMAIN SPECIFIC VERSION OF THIS CLASS + */ + +void TreeGuiManager::processItemList(QStringList itemNameIdList, + int actionId) +{ + // WARN: THIS IS A DEFAULT IMPLEMENTATION GIVEN FOR DEMONSTRATION + // OF WHAT TO DO WITH THE PARAMETERS + + QString itemNameId = itemNameIdList[0]; + LOG("TreeGuiManager: signal received : process item "<getDataObject(QS2S(itemNameId)); + if ( dataObject != NULL ) { + LOG("TreeGuiManager: dataObject = "<toString().c_str()); + } else { + LOG("TreeGuiManager: no data object associated to this item"); + } +} diff --git a/src/TreeData/TreeGuiManager.hxx b/src/TreeData/TreeGuiManager.hxx new file mode 100644 index 000000000..c705680c6 --- /dev/null +++ b/src/TreeData/TreeGuiManager.hxx @@ -0,0 +1,64 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#ifndef _TREEGUIMANAGER_H_ +#define _TREEGUIMANAGER_H_ + +#include "TreeData.hxx" + +// SALOME includes +#include + +// TREEDATA includes +#include "DockWidgets.hxx" +#include "TreeModel.hxx" +#include "DataModel.hxx" +#include "TreeView.hxx" +#include "TreeObserver.hxx" + +class TREEDATA_EXPORT TreeGuiManager : public TreeObserver { + +public: + TreeGuiManager(SalomeApp_Application * salomeApp, const char * title="Data Model"); + void tabifyDockWidgets(bool tabify); + void showDockWidgets(bool isVisible); + SalomeApp_Application * getSalomeApplication(); + + TreeModel * getDataTreeModel(); + TreeView * getDataTreeView(); + + void setDataModel(DataModel * dataModel); + DataModel * getDataModel(); + +private: + SalomeApp_Application * _salomeApp; + + DockWidgets * _dockWidgets; + TreeView * _dataTreeView; + + TreeModel * _dataTreeModel; + DataModel * _dataModel; + +public slots: + virtual void processItemList(QStringList itemNameIdList, int actionId); +}; + +#endif /* _TREEGUIMANAGER_H_ */ diff --git a/src/TreeData/TreeItem.cxx b/src/TreeData/TreeItem.cxx new file mode 100644 index 000000000..9af3dca18 --- /dev/null +++ b/src/TreeData/TreeItem.cxx @@ -0,0 +1,242 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + + +#include +#include "TreeItem.hxx" + +TreeItem::TreeItem(const QString &nameId, + const QVector &columnValues, + TreeItem *parent) +{ + this->initialize(nameId, columnValues, parent); +} + +/*! + * This initializes the tree item. Called by the constructors. + */ +void TreeItem::initialize(const QString &nameId, + const QVector &columnValues, + TreeItem *parent) { + _itemNameId = nameId; + _itemData = columnValues; + _parentItem = parent; + + // An item is associated to the model of its parent. It can't be + //done automatically in the case where the item has no parent. Then + //the function associatedToModel has to be called explicitely from + //within the user code (for exemple at construction of the model, + //when creating the root item). + if ( parent != NULL ) { + this->associateToModel(parent->associatedModel()); + } +} + +TreeItem::~TreeItem() +{ + qDeleteAll(_childItems); + qDeleteAll(_childItemsMapById); + qDeleteAll(_childItemsMapByLabel); +} + +/*! + * This must be used to specified which model this item belongs to. It + * is required because the item sometimes requests its model for + * global informations about the data tree. In standard usage, this + * function is automatically set when the item is instantiated by + * requested the parent item. Then only the root item needs a manual + * setting. + */ +void TreeItem::associateToModel(TreeModel * model) { + _associatedModel = model; +} + +TreeModel * TreeItem::associatedModel() { + return _associatedModel; +} + +/*! + * This provide an identifier for this item + */ +QString TreeItem::nameId() { + return _itemNameId; +} + +/*! + * This creates an item from the specified dataObject and put it in + * the decendency of this item at the specified relativePath. The + * created item could not be a direct child of this item in the case + * where the relative path is not null. Note that no reference on the + * dataObject is kept in this instance. Only the identifier is used to + * set the item identifier, and the properties may be used to set the + * values of the item (stored in columns). + */ +void TreeItem::appendChild(DataObject * dataObject, + const QStringList &relativePath) { + // Definition of the nameId + QString nameId = QString(dataObject->getNameId().c_str()); + + // Definition of columns data values + QVector columnValues; + columnValues << QString(dataObject->getLabel().c_str()); + columnValues << "No value"; // We could use the dataObject properties + + // Append the item at the specified location with the specified values: + this->appendChild(nameId, columnValues, relativePath); +} + +void TreeItem::appendChild(const QString &nameId, + const QVector &columnValues, + const QStringList &relativePath) { + + if ( relativePath.isEmpty() ) { + // It is a direct child => just create and append to this. + TreeItem * child = new TreeItem(nameId, columnValues, this); + this->appendChild(child); + return; + } + + // The child is embedded in a sub-folder (to be created if doesn't + // exist). + // We first check if the sub-folder already exist: + TreeItem * folder = this->childByLabel(relativePath[0]); + if ( folder == NULL ) { + // The folder does not exist. It must be created before going any + // further. By convention we choose the folder name as + // identifier. + QString folderNameId = relativePath[0]; + QVector folderColumnValues; + folderColumnValues << relativePath[0] << "No value"; + folder = new TreeItem(folderNameId, folderColumnValues, this); + this->appendChild(folder); + } + + // We create the relative path of the next iteration (delete the + // first folder path). + QStringList nextRelativePath; + for (int i = 1; i < relativePath.size(); ++i) + nextRelativePath << relativePath[i]; + + folder->appendChild(nameId, columnValues, nextRelativePath); +} + +/*! + * This appends the specified child to this item. This item is the + * direct parent of the specified child. + */ +void TreeItem::appendChild(TreeItem *item) +{ + TreeModel * model = this->associatedModel(); + + int position = this->childCount(); + model->beginInsertRows(this->modelIndex(), position, position); + _childItems.append(item); + _childItemsMapById[item->nameId()] = item; + _childItemsMapByLabel[item->data(0).toString()] = item; + model->endInsertRows(); +} + +/*! + * The child() function returns the child that corresponds to the + * specified row number in the item's list of child items. + */ +TreeItem *TreeItem::child(int row) +{ + return _childItems.value(row); +} + +TreeItem *TreeItem::childById(const QString &nameId) +{ + QMap ::iterator it; + it = _childItemsMapById.find ( nameId ); + + if ( it != _childItemsMapById.end() ) { + return it.value(); + } + return NULL; +} + +TreeItem *TreeItem::childByLabel(const QString &label) +{ + QMap ::iterator it; + it = _childItemsMapByLabel.find ( label ); + + if ( it != _childItemsMapByLabel.end() ) { + return it.value(); + } + return NULL; +} + + + +int TreeItem::childCount() const +{ + return _childItems.count(); +} + +/*! + * The rowIndex() function reports the item's location within its + * parent's list of items. + */ +int TreeItem::rowIndex() const +{ + if (_parentItem) + return _parentItem->_childItems.indexOf(const_cast(this)); + + return 0; +} + +int TreeItem::columnCount() const +{ + return _itemData.count(); +} + +QVariant TreeItem::data(int column) const +{ + return _itemData.value(column); +} + +TreeItem *TreeItem::parent() +{ + return _parentItem; +} + +bool TreeItem::setData(int column, const QVariant &value) +{ + if (column < 0 || column >= _itemData.size()) + return false; + + _itemData[column] = value; + return true; +} + +QModelIndex TreeItem::modelIndex(int column) +{ + TreeModel * model = this->associatedModel(); + if (_parentItem && (_parentItem != model->getRootItem())) + return model->index(rowIndex(), + column, + _parentItem->modelIndex()); + else + return model->index(rowIndex(), + column, + QModelIndex()); +} diff --git a/src/TreeData/TreeItem.hxx b/src/TreeData/TreeItem.hxx new file mode 100644 index 000000000..2e813d202 --- /dev/null +++ b/src/TreeData/TreeItem.hxx @@ -0,0 +1,82 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#ifndef TREEITEM_H +#define TREEITEM_H + +#include "TreeData.hxx" + +#include +#include +#include +#include + +#include "DataObject.hxx" +#include "TreeModel.hxx" + +class TREEDATA_EXPORT TreeItem +{ + public: + TreeItem(const QString &nameId, const QVector &columnValues, TreeItem *parent = 0); + ~TreeItem(); + + QString nameId(); + void associateToModel(TreeModel * model); + TreeModel * associatedModel(); + QModelIndex modelIndex(int column=0); + TreeItem *parent(); + + void appendChild(TreeItem * child); + void appendChild(DataObject * dataObject, + const QStringList &relativePath=QStringList()); + void appendChild(const QString &nameId, + const QVector &columnValues, + const QStringList &relativePath=QStringList()); + + + + TreeItem *child(int row); + TreeItem *childById(const QString &nameId); + TreeItem *childByLabel(const QString &label); + int childCount() const; + int columnCount() const; + int rowIndex() const; + QVariant data(int column) const; + bool setData(int column, const QVariant &value); + + + private: + void initialize(const QString &nameId, + const QVector &columnValues, + TreeItem *parent); + + QList _childItems; + QMap _childItemsMapById; + QMap _childItemsMapByLabel; + + QString _itemNameId; + QVector _itemData; + TreeItem * _parentItem; + TreeModel * _associatedModel; + +}; + +#endif diff --git a/src/TreeData/TreeModel.cxx b/src/TreeData/TreeModel.cxx new file mode 100644 index 000000000..3e8f451f9 --- /dev/null +++ b/src/TreeData/TreeModel.cxx @@ -0,0 +1,210 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + + +#include + +#include "TreeItem.hxx" +#include "TreeModel.hxx" + +TreeModel::TreeModel(const QStringList &headers, QObject *parent) + : QAbstractItemModel(parent) +{ + QVector rootData; + foreach (QString header, headers) + rootData << header; + + // _MEM_ We have to specify a string identifier for each item so + // that it could be easily retrieved by its parent. In the case of + // the root item, the value of the identifier doesn't matter => we + // choose an arbitrary value + //QString rootNameId = "rootItem"; + _rootItem = new TreeItem("rootItem", rootData); + _rootItem->associateToModel(this); +} + +TreeModel::~TreeModel() +{ + delete _rootItem; +} + +TreeItem * TreeModel::getRootItem() { + return _rootItem; +} + +// +// ================================================================= +// This part of the implementation is the standard interface required +// for providing an operational TreeModel. These methods are used by +// the TreeView for display purpose. We just have to implement how we +// want the items to be displayed in the view. +// ================================================================= +// +int TreeModel::columnCount(const QModelIndex & /* parent */) const +{ + return _rootItem->columnCount(); +} + +/*! + * This function is used by the tree model to inform the tree view of + * what data used for rendering of the item in the different possible + * role (edition, ...). + */ +QVariant TreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role != Qt::DisplayRole && role != Qt::EditRole) + return QVariant(); + + TreeItem *item = getItem(index); + + return item->data(index.column()); +} + +Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +/*! + * This retrieves the item asociated to the specified index. + */ +TreeItem *TreeModel::getItem(const QModelIndex &index) const +{ + // The item associated to an index can be retrieved using the + // internalPointer() function on the index object. + if (index.isValid()) { + TreeItem *item = static_cast(index.internalPointer()); + if (item) return item; + } + return _rootItem; +} + +QVariant TreeModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + return _rootItem->data(section); + + return QVariant(); +} + +/*! + * This retrieves the index of the item located at (row,column) place + * relative to the parent specified by its index. + */ +QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid() && parent.column() != 0) + return QModelIndex(); + + TreeItem *parentItem = getItem(parent); + + TreeItem *childItem = parentItem->child(row); + if (childItem) + return createIndex(row, column, childItem); + else + return QModelIndex(); +} + +QModelIndex TreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + TreeItem *childItem = getItem(index); + TreeItem *parentItem = childItem->parent(); + + if (parentItem == _rootItem) + return QModelIndex(); + + return createIndex(parentItem->rowIndex(), 0, parentItem); +} + +int TreeModel::rowCount(const QModelIndex &parent) const +{ + TreeItem *parentItem = getItem(parent); + + return parentItem->childCount(); +} + +bool TreeModel::setData(const QModelIndex &index, const QVariant &value, + int role) +{ + if (role != Qt::EditRole) + return false; + + TreeItem *item = getItem(index); + bool result = item->setData(index.column(), value); + + if (result) + emit dataChanged(index, index); + + return result; +} + +bool TreeModel::setHeaderData(int section, Qt::Orientation orientation, + const QVariant &value, int role) +{ + if (role != Qt::EditRole || orientation != Qt::Horizontal) + return false; + + bool result = _rootItem->setData(section, value); + + if (result) + emit headerDataChanged(orientation, section, section); + + return result; +} + + +// +// ================================================================= +// This part is a specific behavior to get a TreeModel that can +// organize itself the tree hierarchy using data provided in a +// filesystem-like format: +// +// data="a/b/c" ==> creation/filling of the hierarchy a->b->c +// The "folder" categories are unique whereas the leaves may exists +// in multiple instances. +// ================================================================= +// + +/* + * The addData functions run a recurcive filling of the tree model, starting + * form the rootItem and descending in the tree using the recurcive + * function TreeItem::addData. + */ + +bool TreeModel::addData(DataObject * dataObject) { + QStringList path = QString(dataObject->getPath().c_str()).split(DataObject::pathsep.c_str()); + return addData(dataObject, path); +} +bool TreeModel::addData(DataObject * dataObject, const QStringList &path) { + TreeItem * rootItem = this->getItem(); + rootItem->appendChild(dataObject, path); + return true; +} diff --git a/src/TreeData/TreeModel.hxx b/src/TreeData/TreeModel.hxx new file mode 100644 index 000000000..3cc4fe41a --- /dev/null +++ b/src/TreeData/TreeModel.hxx @@ -0,0 +1,105 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + + +#ifndef TREEMODEL_H +#define TREEMODEL_H + +#include "TreeData.hxx" + +#include +#include +#include +#include + +#include "DataObject.hxx" + +class TreeItem; +class TreeView; + +class TREEDATA_EXPORT TreeModel : public QAbstractItemModel +{ + Q_OBJECT + + // IMPORTANT NOTE: + // In this implementation of QAbstractItemModel, a tree item is + // associated to the tree model it belongs to (it can request its + // model throw a pointer to this model). Then we declare the + // TreeItem as a friend class so that it can request the protected + // methods (for example beginInsertRows and endInsertRows, required + // to manage correctly the addition of an item in the model. An + // item can append a child to itself, so it needs to inform the + // model when it begins and when it ends). + friend class TreeItem; + friend class TreeView; + +public: + TreeModel(const QStringList &headers, QObject *parent = 0); + ~TreeModel(); + + // + // ================================================================= + // This part of the specification is the standard interface required + // for providing an operational TreeModel. These methods are used by + // the TreeView for display purpose. We just have to implement how + // we want the items to be displayed in the view. + // ================================================================= + // + // MEM: note that these methods are not intended to be used + // directly. That's the job of the viewer to know how to use + // them. We just have to give the implementation for customizing the + // appearance of the tree. The implementation generally requests the + // items'data to set the appearance features. + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) const; + + // + // ================================================================= + // This part is a specific behavior to get a TreeModel that can + // organize itself the tree hierarchy using data provided in a + // filesystem-like format: + // + // data="a/b/c" ==> creation/filling of the hierarchy a->b->c + // The "folder" categories are unique whereas the leaves may exists + // in multiple instances. + // ================================================================= + // + bool addData(DataObject * dataObject); + bool addData(DataObject * dataObject, const QStringList &path); + + // TODO: We should implement the delete and the update fucntions + + // This part contains helper functions for general purposes + TreeItem * getRootItem(); + +private: + TreeItem *getItem(const QModelIndex &index = QModelIndex()) const; + TreeItem * _rootItem; +}; + +#endif // TREEMODEL_H diff --git a/src/TreeData/TreeObserver.cxx b/src/TreeData/TreeObserver.cxx new file mode 100644 index 000000000..310ea13ed --- /dev/null +++ b/src/TreeData/TreeObserver.cxx @@ -0,0 +1,54 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + +#include "TreeObserver.hxx" +#include "QtHelper.hxx" + +TreeObserver::TreeObserver() : QObject() { +} + +/*! + * This declares the TreeView to be observed by this tree events + * listener. + */ +void TreeObserver::observe(TreeView * treeView) { + // We just connect the signals emitted from the treeview to + // corresponding slots of this observer. + connect(treeView, SIGNAL(itemListToProcess(QStringList,int)), + this, SLOT(processItemList(QStringList,int))); +} + +/*! + * This slot should be implemented in a specialized version of + * TreeObserver to process the signal emitted from the TreeView. The + * parameters are: + * - itemNameIdList: the list of name identifiers of model objects + * associated to the selected qt items in the TreeView (ids for + * requesting directly the data model). + * - actionId: the identifier of the action selected in the popup menu + * that triggered this signal. + */ +void TreeObserver::processItemList(QStringList itemNameIdList, int actionId) { + LOG("TreeObserver::processItemList: signal received:\n"<< + "item list: "< -#else -#include -#endif - -void* SVTK::getOpenGLExtension( const char* theExtension ) -{ -#ifdef WIN32 - return wglGetProcAddress( theExtension ); -#else - void* OpenGLLibrary = dlopen( "libGL.so", RTLD_LAZY ); - return dlsym( OpenGLLibrary, theExtension ); -#endif -} +// Author: Guillaume Boulant (EDF/R&D) + +#ifndef _TREEOBSERVER_ +#define _TREEOBSERVER_ + +#include "TreeData.hxx" + +#include +#include "TreeView.hxx" + +class TREEDATA_EXPORT TreeObserver : public QObject { + + Q_OBJECT + +public: + TreeObserver(); + void observe(TreeView * treeView); + +public slots: + /* These slots should be implemented in a specialized version of + the TreeObserver to process signals emitted from the TreeView */ + virtual void processItemList(QStringList itemNameIdList, int actionId); +}; + +#endif // _TREEOBSERVER_ diff --git a/src/TreeData/TreeView.cxx b/src/TreeData/TreeView.cxx new file mode 100644 index 000000000..462fad8c8 --- /dev/null +++ b/src/TreeData/TreeView.cxx @@ -0,0 +1,144 @@ +// Copyright (C) 2007-2011 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. +// +// 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 +// + +// Author: Guillaume Boulant (EDF/R&D) + + +// include Qt +#include +#include +#include +#include + +// include Xcad +#include "TreeView.hxx" +#include "TreeModel.hxx" +#include "TreeItem.hxx" +#include "QtHelper.hxx" + +TreeView::TreeView(QWidget * parent) + : QTreeView(parent) +{ + // We authorize the multiple selection of items + this->setSelectionMode(QAbstractItemView::ExtendedSelection); + + _lastActionId = 0; + + // Default actions for tests + int displayActionId = addAction(QObject::tr("Afficher")); + int editActionId = addAction(QObject::tr("Editer")); +} + +TreeView::~TreeView() { +} + +/*! + * This function defines a menu item to add in the popup menu + * associated to this TreeView, and return an integer that corresponds + * to the unique identifier of this action (identifier used in the + * signal emitted to notify observers that an action has been + * selected). Then the caller of this function has to take care of + * this return id (i.e. has to store it in its internal tables) to be + * able to process the notifications from this TreeView. + */ +int TreeView::addAction(QString label) { + QAction * action = new QAction(this); + int actionId = _lastActionId; + action->setObjectName(_idToName(actionId)); + action->setText(label); + _listActions << action; + _lastActionId++; + + return actionId; +} + +/*! + * This function removes all actions previously defined for the popup + * menu of this TreeView. + */ +void TreeView::clearActions() { + _listActions.clear(); +} + +/*! + * You must use this function to create the name of an action object + * from its id. + */ +QString TreeView::_idToName(int actionId) { + return QString::number(actionId); +} +/*! + * You must use this function to create the id of an action object + * from its name (stored in objectName() attribute of the QAction). + */ +int TreeView::_nameToId(QString actionName) { + return actionName.toInt(); +} + +void TreeView::contextMenuEvent(QContextMenuEvent *event) { + if ( _listActions.size() == 0 ) { + // Just return there is no actions defined for this popup menu + return; + } + + // _TODO_ display the QMenu only if the selected item is acceptable + QMenu menu(this); + for (int i = 0; i < _listActions.size(); ++i) { + menu.addAction(_listActions.at(i)); + } + connect(&menu, SIGNAL(triggered(QAction*)), + this, SLOT(processMenuAction(QAction*))); + + menu.exec(event->globalPos()); +} + +/*! + * This SLOT is connected on the signal emited by the menu when an + * action is selected. + */ +void TreeView::processMenuAction(QAction * actionSelected) { + LOG("processMenuAction: START"); + + // We first check than at least on item is selected + QModelIndexList indexList = this->selectionModel()->selectedRows(0); + if ( indexList.isEmpty() ) { + LOG("No item selected"); + return; + } + + // Then we can gather the list of model item ids associated the + // selection. + TreeModel *model = (TreeModel *)this->model(); + QListIterator it(indexList); + QStringList nameIdList; + while (it.hasNext()) { + TreeItem * item = model->getItem(it.next()); + nameIdList << item->nameId(); + } + + // Finally, one can emit a signal to observers specifying the list of + // id and the type of action (i.e. the action identifier) + int actionId = _nameToId(actionSelected->objectName()); + LOG("TreeView::processMenuAction: signal emitted:\n"<< + "item list: "< +#include +#include +#include + +class TREEDATA_EXPORT TreeView : public QTreeView +{ + Q_OBJECT + +public: + TreeView(QWidget * parent = 0 ); + ~TreeView(); + int addAction(QString label); + void clearActions(); + +protected: + void contextMenuEvent(QContextMenuEvent *event); + +private: + int _lastActionId; + QList _listActions; + QString _idToName(int actionId); + int _nameToId(QString actionName); + + // --- + +private slots: + void processMenuAction(QAction * actionSelected); + +signals: + void itemListToProcess(QStringList itemNameIdList, int actionId); +}; + +#endif // TREEVIEW_H + diff --git a/src/VTKViewer/VTKViewer.pro b/src/VTKViewer/VTKViewer.pro deleted file mode 100644 index c0632ee2b..000000000 --- a/src/VTKViewer/VTKViewer.pro +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = lib -TARGET = VTKViewer -DESTDIR = ../../lib -MOC_DIR = ../../moc -OBJECTS_DIR = ../../obj/$$TARGET - -VTKHOME = $$(VTKHOME) -VTK_INCLUDES = $${VTKHOME}/include/vtk - -VTK_LIBS = -L$${VTKHOME}/lib/vtk -L$${VTKHOME}/lib/vtk/python -lvtkCommon -lvtkGraphics -lvtkImaging -lvtkFiltering -lvtkIO -lvtkRendering -lvtkHybrid -lvtkParallel -lvtkWidgets -lGL -L/usr/X11R6/lib -lGLU -L/usr/X11R6/lib -lX11 -lXt - -CASROOT = $$(CASROOT) -CAS_CPPFLAGS = $${CASROOT}/inc - -CAS_KERNEL = -L$${CASROOT}/Linux/lib -lTKernel - -INCLUDEPATH += ../../include $${VTK_INCLUDES} $${CAS_CPPFLAGS} ../Qtx ../SUIT -LIBS += -L../../lib -lqtx -lsuit $${VTK_LIBS} $${CAS_KERNEL} - -CONFIG -= debug release debug_and_release -CONFIG += qt thread debug dll shared - -win32:DEFINES += WIN32 -DEFINES += VTKVIEWER_EXPORTS OCC_VERSION_MAJOR=6 OCC_VERSION_MINOR=1 OCC_VERSION_MAINTENANCE=1 LIN LINTEL CSFDB No_exception HAVE_CONFIG_H HAVE_LIMITS_H HAVE_WOK_CONFIG_H OCC_CONVERT_SIGNALS - -HEADERS = VTKViewer.h -HEADERS += VTKViewer_CellLocationsArray.h -HEADERS += VTKViewer_Actor.h -HEADERS += VTKViewer_ExtractUnstructuredGrid.h -HEADERS += VTKViewer_ConvexTool.h -HEADERS += VTKViewer_Filter.h -HEADERS += VTKViewer_GeometryFilter.h -HEADERS += VTKViewer_AppendFilter.h -HEADERS += VTKViewer_Algorithm.h -HEADERS += VTKViewer_InteractorStyle.h -HEADERS += VTKViewer_RenderWindow.h -HEADERS += VTKViewer_RenderWindowInteractor.h -HEADERS += VTKViewer_ShrinkFilter.h -HEADERS += VTKViewer_TransformFilter.h -HEADERS += VTKViewer_Transform.h -HEADERS += VTKViewer_Trihedron.h -HEADERS += VTKViewer_Utilities.h -HEADERS += VTKViewer_ViewManager.h -HEADERS += VTKViewer_ViewModel.h -HEADERS += VTKViewer_ViewWindow.h -HEADERS += VTKViewer_Functor.h - -SOURCES = VTKViewer_CellLocationsArray.cxx -SOURCES += VTKViewer_Actor.cxx -SOURCES += VTKViewer_ExtractUnstructuredGrid.cxx -SOURCES += VTKViewer_Filter.cxx -SOURCES += VTKViewer_GeometryFilter.cxx -SOURCES += VTKViewer_AppendFilter.cxx -SOURCES += VTKViewer_InteractorStyle.cxx -SOURCES += VTKViewer_RenderWindow.cxx -SOURCES += VTKViewer_RenderWindowInteractor.cxx -SOURCES += VTKViewer_ShrinkFilter.cxx -SOURCES += VTKViewer_Transform.cxx -SOURCES += VTKViewer_TransformFilter.cxx -SOURCES += VTKViewer_Trihedron.cxx -SOURCES += VTKViewer_Utilities.cxx -SOURCES += VTKViewer_ViewManager.cxx -SOURCES += VTKViewer_ViewModel.cxx -SOURCES += VTKViewer_ConvexTool.cxx -SOURCES += VTKViewer_ViewWindow.cxx - -TRANSLATIONS = resources/VTKViewer_images.ts \ - resources/VTKViewer_msg_en.ts - -ICONS = resources/*.png - -includes.files = $$HEADERS -includes.path = ../../include - -resources.files = $$ICONS resources/*.qm -resources.path = ../../resources - -INSTALLS += includes resources diff --git a/src/VTKViewer/VTKViewer_PassThroughFilter.cxx b/src/VTKViewer/VTKViewer_PassThroughFilter.cxx deleted file mode 100755 index 78e89d5f8..000000000 --- a/src/VTKViewer/VTKViewer_PassThroughFilter.cxx +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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. -// -// 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 -// - -// SALOME FILTER : interactive object for VISU entities implementation -// File : SALOME_PassThroughFilter.cxx -// Author : Laurent CORNABE with help of Nicolas REJNERI -// Module : SALOME -// -#include "VTKViewer_PassThroughFilter.h" - -#include -#include -#include -#include -#include -#include - -vtkCxxRevisionMacro(VTKViewer_PassThroughFilter, "$Revision$"); -vtkStandardNewMacro(VTKViewer_PassThroughFilter); - -/*! \class VTKViewer_PassThroughFilter - * Passive filter take a dataset as input and create a dataset as output.\n - * The form of the input geometry is not changed in these filters, \n - * only the point attributes (e.g. scalars, vectors, etc.). - */ - -/*!Execute method.Output calculation.*/ -int VTKViewer_PassThroughFilter::RequestData( - vtkInformation *, - vtkInformationVector **inputVector, - vtkInformationVector *outputVector) -{ - // get the info objects - vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); - vtkInformation *outInfo = outputVector->GetInformationObject(0); - - // get the input and ouptut - vtkDataSet *input = vtkDataSet::SafeDownCast( - inInfo->Get(vtkDataObject::DATA_OBJECT())); - vtkDataSet *output = vtkDataSet::SafeDownCast( - outInfo->Get(vtkDataObject::DATA_OBJECT())); - - // This has to be here because it initialized all field datas. - output->CopyStructure( input ); - - //! Pass all. (data object's field data is passed by the - //! superclass after this method) - output->GetPointData()->PassData( input->GetPointData() ); - output->GetCellData()->PassData( input->GetCellData() ); - - return 1; -} - -/*!Methods invoked by print to print information about the object including superclasses.\n - * Typically not called by the user (use Print() instead) but used in the hierarchical \n - * print process to combine the output of several classes. - *\param os - output stream. - */ -void VTKViewer_PassThroughFilter::PrintSelf(ostream& os, vtkIndent indent) -{ - this->Superclass::PrintSelf(os,indent); -} diff --git a/src/VTKViewer/VTKViewer_PassThroughFilter.h b/src/VTKViewer/VTKViewer_PassThroughFilter.h deleted file mode 100755 index 2c7a90e44..000000000 --- a/src/VTKViewer/VTKViewer_PassThroughFilter.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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. -// -// 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 -// - -#ifndef VTKVIEWER_PASSTHROUGHFILTER_H -#define VTKVIEWER_PASSTHROUGHFILTER_H - -#include "VTKViewer.h" - -#include - -class VTKVIEWER_EXPORT VTKViewer_PassThroughFilter : public vtkDataSetToDataSetFilter -{ -public: - vtkTypeRevisionMacro( VTKViewer_PassThroughFilter, vtkDataSetToDataSetFilter ); - void PrintSelf( ostream& os, vtkIndent indent ); - - /*!Create a new VTKViewer_PassThroughFilter.*/ - static VTKViewer_PassThroughFilter *New(); - -protected: - VTKViewer_PassThroughFilter() {};//!< Null body. - virtual ~VTKViewer_PassThroughFilter() {};//!< Null body. - - virtual int RequestData(vtkInformation *, vtkInformationVector **, - vtkInformationVector *); //generate output data - -private: - VTKViewer_PassThroughFilter( const VTKViewer_PassThroughFilter& ); //!< Not implemented. - void operator=( const VTKViewer_PassThroughFilter& ); //!< Not implemented. -}; - -#endif diff --git a/src/VTKViewer/VTKViewer_TransformFilter.cxx b/src/VTKViewer/VTKViewer_TransformFilter.cxx index dc2bc83cc..eec36008a 100755 --- a/src/VTKViewer/VTKViewer_TransformFilter.cxx +++ b/src/VTKViewer/VTKViewer_TransformFilter.cxx @@ -79,6 +79,7 @@ int VTKViewer_TransformFilter::RequestData( } outPD->PassData(pd); outCD->PassData(cd); + InvokeEvent("VTKViewer_TransformFilter::TransformationFinished"); return 1; } diff --git a/src/src.pro b/src/src.pro deleted file mode 100644 index e3f0a9757..000000000 --- a/src/src.pro +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2007-2011 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. -# -# 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 -# - -TEMPLATE = subdirs -CONFIG += ordered - -SUBDIRS = Qtx -SUBDIRS += DDS -SUBDIRS += QDS -SUBDIRS += SUIT -SUBDIRS += SUITApp -SUBDIRS += STD -SUBDIRS += CAF -SUBDIRS += CAM -SUBDIRS += LogWindow -SUBDIRS += PyInterp -SUBDIRS += PyConsole -SUBDIRS += Prs -SUBDIRS += OBJECT -SUBDIRS += GLViewer -SUBDIRS += VTKViewer -SUBDIRS += SVTK -SUBDIRS += OCCViewer -SUBDIRS += SOCC -SUBDIRS += Plot2d -SUBDIRS += SPlot2d -SUBDIRS += SUPERVGraph -SUBDIRS += QxGraph -SUBDIRS += Event -SUBDIRS += LightApp -SUBDIRS += ResExporter -SUBDIRS += TOOLSGUI -SUBDIRS += SalomeApp -SUBDIRS += Session diff --git a/tools/dlgfactory/Makefile.am b/tools/dlgfactory/Makefile.am index 6945a9b9b..0bbee5a1e 100644 --- a/tools/dlgfactory/Makefile.am +++ b/tools/dlgfactory/Makefile.am @@ -83,7 +83,8 @@ nodist_qtester_SOURCES = \ $(UIC_FILES_QDIALOG) qtester_CPPFLAGS = \ - $(QT_CXXFLAGS) + $(QT_CXXFLAGS) \ + -I. qtester_LDFLAGS = \ $(QT_LIBS) @@ -98,7 +99,9 @@ nodist_gtester_SOURCES = \ $(UIC_FILES_GDIALOG) gtester_CPPFLAGS = \ - $(QT_CXXFLAGS) + $(QT_CXXFLAGS) \ + -I@srcdir@ \ + -I. gtester_LDFLAGS = \ $(QT_LIBS) -- 2.39.2