From e3dcb7b17e72479e8409819777bc11f9a6d910c7 Mon Sep 17 00:00:00 2001 From: abn Date: Tue, 15 Jan 2019 14:24:23 +0100 Subject: [PATCH] Porting Python side of the tool. --- tools/CurvePlot/CMakeLists.txt | 38 ++-- .../CurvePlot/SalomeCURVEPLOTConfig.cmake.in | 4 +- tools/CurvePlot/src/cpp/test/CMakeLists.txt | 2 +- .../src/python/controller/CMakeLists.txt | 15 +- .../src/python/controller/PlotController.py | 178 +++++++++--------- .../src/python/controller/__init__.py | 16 +- .../src/python/controller/utils.py.in | 10 +- .../CurvePlot/src/python/model/CMakeLists.txt | 5 +- .../CurvePlot/src/python/model/CurveModel.py | 4 +- .../CurvePlot/src/python/model/PlotManager.py | 6 +- .../CurvePlot/src/python/model/TableModel.py | 4 +- .../src/python/model/XYPlotSetModel.py | 4 +- .../src/python/pyqtside/CMakeLists.txt | 4 + tools/CurvePlot/src/python/pyqtside/QtCore.py | 6 +- tools/CurvePlot/src/python/pyqtside/QtGui.py | 42 ++--- .../src/python/pyqtside/QtWidgets.py | 5 + .../CurvePlot/src/python/pyqtside/__init__.py | 15 +- .../src/python/pyqtside/pyside_dynamic.py | 2 +- tools/CurvePlot/src/python/pyqtside/uic.py | 4 +- .../CurvePlot/src/python/test/CMakeLists.txt | 17 +- .../src/python/test/PlotCurve_Standalone.py | 22 +-- .../CurvePlot/src/python/test/PlotTestBase.py | 91 ++++++--- tools/CurvePlot/src/python/test/README.txt | 10 +- .../src/python/test/SalomePyQt_MockUp.py.in | 15 +- .../CurvePlot/src/python/test/TestDesktop.py | 34 ++-- tools/CurvePlot/src/python/test/plot_test.py | 122 ++++++------ tools/CurvePlot/src/python/ui/CMakeLists.txt | 7 +- .../src/python/ui/CurveTreeDockWidget.py | 8 +- tools/CurvePlot/src/python/ui/PlotSettings.py | 40 ++-- tools/CurvePlot/src/python/ui/PlotWidget.py | 8 +- .../CurvePlot/src/python/views/CMakeLists.txt | 5 +- .../src/python/views/CurveBrowserView.py | 14 +- .../src/python/views/CurveTabsView.py | 6 +- tools/CurvePlot/src/python/views/CurveView.py | 4 +- tools/CurvePlot/src/python/views/XYView.py | 28 +-- 35 files changed, 434 insertions(+), 361 deletions(-) create mode 100644 tools/CurvePlot/src/python/pyqtside/QtWidgets.py diff --git a/tools/CurvePlot/CMakeLists.txt b/tools/CurvePlot/CMakeLists.txt index 41cd1dc67..6794d4563 100755 --- a/tools/CurvePlot/CMakeLists.txt +++ b/tools/CurvePlot/CMakeLists.txt @@ -29,12 +29,12 @@ CMAKE_POLICY(SET CMP0003 NEW) # Project name, upper case STRING(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UC) -SET(${PROJECT_NAME_UC}_MAJOR_VERSION 7) -SET(${PROJECT_NAME_UC}_MINOR_VERSION 6) +SET(${PROJECT_NAME_UC}_MAJOR_VERSION 9) +SET(${PROJECT_NAME_UC}_MINOR_VERSION 2) SET(${PROJECT_NAME_UC}_PATCH_VERSION 0) SET(${PROJECT_NAME_UC}_VERSION ${${PROJECT_NAME_UC}_MAJOR_VERSION}.${${PROJECT_NAME_UC}_MINOR_VERSION}.${${PROJECT_NAME_UC}_PATCH_VERSION}) -SET(${PROJECT_NAME_UC}_VERSION_DEV 1) +SET(${PROJECT_NAME_UC}_VERSION_DEV 0) # User options # ============ @@ -106,20 +106,11 @@ FIND_PACKAGE(SalomePythonInterp REQUIRED) FIND_PACKAGE(SalomePythonLibs REQUIRED) FIND_PACKAGE(SalomeNumPySciPy REQUIRED) -# Qt4 -FIND_PACKAGE(SalomeQt4 REQUIRED COMPONENTS QtCore QtGui) -INCLUDE(${QT_USE_FILE}) +# Qt +FIND_PACKAGE(SalomeQt5 REQUIRED) +FIND_PACKAGE(SalomePyQt5 REQUIRED) # Optional products: - -IF(SALOME_BUILD_DOC) -# FIND_PACKAGE(SalomeDoxygen) -# FIND_PACKAGE(SalomeSphinx) -# SALOME_LOG_OPTIONAL_PACKAGE(Doxygen SALOME_BUILD_DOC) -# SALOME_LOG_OPTIONAL_PACKAGE(Sphinx SALOME_BUILD_DOC) -# ADD_DEFINITIONS(-DDOXYGEN_IS_OK) -ENDIF() - IF(SALOME_BUILD_TESTS) ENABLE_TESTING() ENDIF() @@ -155,12 +146,15 @@ ENDIF() # Specific to CURVEPLOT: SET(SALOME_CURVEPLOT_INSTALL_RES_DATA "${SALOME_INSTALL_RES}/curveplot" CACHE PATH "Install path: SALOME CURVEPLOT specific data") -# Package installation path (lib/python2.7/...) +# Package installation path (lib/python/...) SET(SALOME_CURVEPLOT_INSTALL_PYTHON ${SALOME_INSTALL_PYTHON}/curveplot CACHE INTERNAL "Install path: SALOME CURVEPLOT Python packages" FORCE) SET(SALOME_CURVEPLOT_INSTALL_SCRIPT_PYTHON ${SALOME_INSTALL_SCRIPT_PYTHON} CACHE INTERNAL "Install path: SALOME CURVEPLOT Python main entry points" FORCE) +SET(CRVPLOT_TEST_INSTALL ${CMAKE_BINARY_DIR}/local/curveplot) +SET(PYQTSIDE_TEST_INSTALL ${CMAKE_BINARY_DIR}/local/pyqtside) + # Sources # ======== ADD_SUBDIRECTORY(src) @@ -192,25 +186,25 @@ EXPORT(TARGETS ${_${PROJECT_NAME}_exposed_targets} # Ensure the variables are always defined for the configure: SET(KERNEL_ROOT_DIR "${KERNEL_ROOT_DIR}") SET(GUI_ROOT_DIR "${GUI_ROOT_DIR}") -SET(QT4_ROOT_DIR "${QT4_ROOT_DIR}") -SET(PYQT4_ROOT_DIR "${PYQT4_ROOT_DIR}") +SET(QT5_ROOT_DIR "${QT5_ROOT_DIR}") +SET(PYQT5_ROOT_DIR "${PYQT5_ROOT_DIR}") SET(PYTHON_ROOT_DIR "${PYTHON_ROOT_DIR}") SET(CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/include" "${PROJECT_BINARY_DIR}/include") # Build variables that will be expanded when configuring SalomeConfig.cmake: -SALOME_CONFIGURE_PREPARE(PyQt4 Qt4 Python) +SALOME_CONFIGURE_PREPARE(PyQt5 Qt5 Python) CONFIGURE_PACKAGE_CONFIG_FILE(${PROJECT_NAME}Config.cmake.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake INSTALL_DESTINATION "${SALOME_INSTALL_CMAKE_LOCAL}" PATH_VARS CONF_INCLUDE_DIRS SALOME_INSTALL_CMAKE_LOCAL CMAKE_INSTALL_PREFIX - KERNEL_ROOT_DIR GUI_ROOT_DIR QT4_ROOT_DIR PYQT4_ROOT_DIR PYTHON_ROOT_DIR) + KERNEL_ROOT_DIR GUI_ROOT_DIR QT5_ROOT_DIR PYQT5_ROOT_DIR PYTHON_ROOT_DIR) WRITE_BASIC_PACKAGE_VERSION_FILE(${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${${PROJECT_NAME_UC}_VERSION} COMPATIBILITY AnyNewerVersion) - + # Install the CMake configuration files: INSTALL(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" @@ -222,4 +216,4 @@ IF(NOT SALOME_CURVEPLOT_STANDALONE) INSTALL(EXPORT ${PROJECT_NAME}TargetGroup DESTINATION "${SALOME_INSTALL_CMAKE_LOCAL}" FILE ${PROJECT_NAME}Targets.cmake) ENDIF() - + diff --git a/tools/CurvePlot/SalomeCURVEPLOTConfig.cmake.in b/tools/CurvePlot/SalomeCURVEPLOTConfig.cmake.in index 76b16dda8..cb08b646b 100644 --- a/tools/CurvePlot/SalomeCURVEPLOTConfig.cmake.in +++ b/tools/CurvePlot/SalomeCURVEPLOTConfig.cmake.in @@ -53,8 +53,8 @@ IF(SALOME_CURVEPLOT_STANDALONE) SET_AND_CHECK(GUI_ROOT_DIR_EXP "@PACKAGE_GUI_ROOT_DIR@") ENDIF() -SET_AND_CHECK(QT4_ROOT_DIR_EXP "@PACKAGE_QT4_ROOT_DIR@") -SET_AND_CHECK(PYQT4_ROOT_DIR_EXP "@PACKAGE_PYQT4_ROOT_DIR@") +SET_AND_CHECK(QT5_ROOT_DIR_EXP "@PACKAGE_QT5_ROOT_DIR@") +SET_AND_CHECK(PYQT5_ROOT_DIR_EXP "@PACKAGE_PYQT5_ROOT_DIR@") SET_AND_CHECK(PYTHON_ROOT_DIR_EXP "@PACKAGE_PYTHON_ROOT_DIR@") diff --git a/tools/CurvePlot/src/cpp/test/CMakeLists.txt b/tools/CurvePlot/src/cpp/test/CMakeLists.txt index 17e1c157a..de36b3ec6 100755 --- a/tools/CurvePlot/src/cpp/test/CMakeLists.txt +++ b/tools/CurvePlot/src/cpp/test/CMakeLists.txt @@ -44,7 +44,7 @@ SET(_other_SOURCES # --- rules --- # sources / moc wrappings -QT4_WRAP_CPP(_moc_SOURCES ${_moc_HEADERS}) +QT5_WRAP_CPP(_moc_SOURCES ${_moc_HEADERS}) ADD_EXECUTABLE(test_curveplot ${_other_SOURCES} ${_moc_SOURCES}) TARGET_LINK_LIBRARIES(test_curveplot ${_link_LIBRARIES}) diff --git a/tools/CurvePlot/src/python/controller/CMakeLists.txt b/tools/CurvePlot/src/python/controller/CMakeLists.txt index 1c51e37f5..241c0e76a 100644 --- a/tools/CurvePlot/src/python/controller/CMakeLists.txt +++ b/tools/CurvePlot/src/python/controller/CMakeLists.txt @@ -17,12 +17,25 @@ # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # +SET(SALOME_CURVEPLOT_TEST_MODE "0") SALOME_CONFIGURE_FILE(utils.py.in ${CMAKE_CURRENT_BINARY_DIR}/utils.py) +# For test purposes: +SET(SALOME_CURVEPLOT_TEST_MODE "1") +SALOME_CONFIGURE_FILE(utils.py.in ${CMAKE_CURRENT_BINARY_DIR}/utils_test.py) +SET(SALOME_CURVEPLOT_TEST_MODE "0") + SET(_all_lib_SCRIPTS PlotController.py __init__.py - ${CMAKE_CURRENT_BINARY_DIR}/utils.py ) +SET(_util_SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/utils.py) + SALOME_INSTALL_SCRIPTS("${_all_lib_SCRIPTS}" ${SALOME_CURVEPLOT_INSTALL_PYTHON}) +SALOME_INSTALL_SCRIPTS("${_util_SCRIPT}" ${SALOME_CURVEPLOT_INSTALL_PYTHON}) + +# For test purposes +FILE(COPY ${_all_lib_SCRIPTS} DESTINATION ${CRVPLOT_TEST_INSTALL}) +FILE(COPY ${CMAKE_CURRENT_BINARY_DIR}/utils_test.py DESTINATION ${CRVPLOT_TEST_INSTALL}) +FILE(RENAME ${CRVPLOT_TEST_INSTALL}/utils_test.py ${CRVPLOT_TEST_INSTALL}/utils.py) diff --git a/tools/CurvePlot/src/python/controller/PlotController.py b/tools/CurvePlot/src/python/controller/PlotController.py index f5670e52e..5122b61e2 100644 --- a/tools/CurvePlot/src/python/controller/PlotController.py +++ b/tools/CurvePlot/src/python/controller/PlotController.py @@ -1,20 +1,20 @@ -from CurveBrowserView import CurveBrowserView -from PlotManager import PlotManager -from CurveTabsView import CurveTabsView -from CurveModel import CurveModel -from TableModel import TableModel -from utils import Logger +from .CurveBrowserView import CurveBrowserView +from .PlotManager import PlotManager +from .CurveTabsView import CurveTabsView +from .CurveModel import CurveModel +from .TableModel import TableModel +from .utils import Logger import numpy as np class PlotController(object): """ Controller for 2D curve plotting functionalities. """ __UNIQUE_INSTANCE = None # my poor impl. of a singleton - + ## For testing purposes: WITH_CURVE_BROWSER = True WITH_CURVE_TABS = True - + def __init__(self, sgPyQt=None): if self.__UNIQUE_INSTANCE is None: self.__trueInit(sgPyQt) @@ -31,32 +31,32 @@ class PlotController(object): self._blockNotifications = False self._blockViewClosing = False self._callbacks = [] - + self._plotManager = PlotManager(self) - + if self.WITH_CURVE_BROWSER: self._curveBrowserView = CurveBrowserView(self) self.associate(self._plotManager, self._curveBrowserView) else: - self._curveBrowserView = None + self._curveBrowserView = None if self.WITH_CURVE_TABS: self._curveTabsView = CurveTabsView(self) self.associate(self._plotManager, self._curveTabsView) else: self._curveTabsView = None PlotController.__UNIQUE_INSTANCE = self - + @classmethod def GetInstance(cls, sgPyQt=None): if cls.__UNIQUE_INSTANCE is None: # First instanciation: PlotController(sgPyQt) return cls.__UNIQUE_INSTANCE - + @classmethod def Destroy(cls): cls.__UNIQUE_INSTANCE = None - + def setFixedSizeWidget(self): """ For testing purposes - ensure visible Qt widgets have a fixed size. """ @@ -64,7 +64,7 @@ class PlotController(object): self._curveBrowserView.treeWidget.resize(100,200) if self.WITH_CURVE_TABS: self._sgPyQt._tabWidget.resize(600,600) - + def associate(self, model, view): """ Associates a model to a view, and sets the view to listen to this model @@ -73,28 +73,28 @@ class PlotController(object): :param model: Model -- The model to be associated to the view. :param view: View -- The view. - """ + """ if model is None or view is None: return - + view.setModel(model) self.setModelListener(model, view) - + def setModelListener(self, model, view): """ Sets a view to listen to all changes of the given model """ l = self._modelViews.setdefault(model, []) if not view in l and view is not None: - l.append(view) - + l.append(view) + def removeModelListeners(self, model): """ Removes the given model from the list of listeners. All views previously connected to this model won't receive its update notification anymore. """ self._modelViews.pop(model) - + def notify(self, model, what=""): """ Notifies the view when model changes. @@ -103,10 +103,10 @@ class PlotController(object): """ if model is None or self._blockNotifications: return - + if model not in self._modelViews: return - + for view in self._modelViews[model]: method = "on%s" % what if what != "" and what is not None and hasattr(view, method): @@ -114,27 +114,27 @@ class PlotController(object): elif hasattr(view, "update"): # Generic update: view.update() - + def setBrowserContextualMenu(self, menu): """ Provide a menu to be contextually shown in the curve browser """ self._browserContextualMenu = menu - + def setCurvePlotRequestingClose(self, bool): self._blockViewClosing = bool - + def onCurrentCurveChange(self): ps = self._plotManager.getCurrentPlotSet() if not ps is None: crv = ps.getCurrentCurve() if crv is not None: - crv_id = crv.getID() + crv_id = crv.getID() for c in self._callbacks: c(crv_id) - + ##### ##### Public static API ##### - + @classmethod def AddCurve(cls, x, y, curve_label="", x_label="", y_label="", append=True): """ Add a new curve and make the plot set where it is drawn the active one. @@ -148,7 +148,7 @@ class PlotController(object): @param append whether to add the curve to the active plot set (default) or into a new one. @return the id of the created curve, and the id of the corresponding plot set. """ - from XYView import XYView + from .XYView import XYView control = cls.GetInstance() pm = control._plotManager t = TableModel(control) @@ -158,18 +158,18 @@ class PlotController(object): prevLock = pm.isRepaintLocked() if not prevLock: pm.lockRepaint() - curveID, plotSetID = control.plotCurveFromTable(t, x_col_index=0, y_col_index=1, + curveID, plotSetID = control.plotCurveFromTable(t, x_col_index=0, y_col_index=1, curve_label=curve_label, append=append) ps = pm._plotSets[plotSetID] if x_label != "": ps.setXLabel(x_label) - if y_label != "": + if y_label != "": ps.setYLabel(y_label) if not prevLock: pm.unlockRepaint() return curveID, plotSetID - @classmethod + @classmethod def ExtendCurve(cls, crv_id, x, y): """ Add new points to an already created curve @raise if invalid plot set ID is given @@ -181,7 +181,7 @@ class PlotController(object): crv_mod = ps._curves[crv_id] data = np.transpose(np.vstack([x, y])) crv_mod.extendData(data) - + @classmethod def ResetCurve(cls, crv_id): """ Reset a given curve: all data are cleared, but the curve is still @@ -195,7 +195,7 @@ class PlotController(object): raise ValueError("Curve ID (%d) not found for reset!" % crv_id) crv_mod = ps._curves[crv_id] crv_mod.resetData() - + @classmethod def AddPlotSet(cls, title=""): """ Creates a new plot set (a tab with several curves) and returns its ID. A title can be passed, @@ -210,7 +210,7 @@ class PlotController(object): if title != "": ps.setTitle(title) return ps.getID() - + @classmethod def CopyCurve(cls, curve_id, plot_set_id): """ Copy a given curve to a given plot set ID @@ -229,7 +229,7 @@ class PlotController(object): control.setModelListener(new_crv, control._curveBrowserView) plot_set_tgt.addCurve(new_crv) return new_crv.getID() - + @classmethod def DeleteCurve(cls, curve_id=-1): """ By default, delete the current curve, if any. Otherwise do nothing. @@ -242,8 +242,8 @@ class PlotController(object): curve_id = cls.GetCurrentCurveID() if curve_id == -1: # No current curve, do nothing - return -1 - + return -1 + psID = cls.GetPlotSetID(curve_id) if psID == -1: raise ValueError("Curve ID (%d) not found for deletion!" % curve_id) @@ -251,7 +251,7 @@ class PlotController(object): control._plotManager._plotSets[psID].removeCurve(curve_id) control.removeModelListeners(crv) return curve_id - + @classmethod def DeletePlotSet(cls, plot_set_id=-1): """ By default, delete the current plot set, if any. Otherwise do nothing. @@ -271,11 +271,11 @@ class PlotController(object): for _, crv in list(ps._curves.items()): control.removeModelListeners(crv) control.removeModelListeners(ps) - psets = control._plotManager._plotSets + psets = control._plotManager._plotSets if len(psets): control._plotManager.setCurrentPlotSet(list(psets.keys())[-1]) return plot_set_id - + @classmethod def usedMem(cls): import gc @@ -283,7 +283,7 @@ class PlotController(object): import resource m = resource.getrusage(resource.RUSAGE_SELF)[2]*resource.getpagesize()/1e6 print("** Used memory: %.2f Mb" % m) - + @classmethod def DeleteCurrentItem(cls): """ Delete currently active item, be it a plot set or a curve. @@ -296,7 +296,7 @@ class PlotController(object): if ps_id == -1: Logger.Info("PlotController.DeleteCurrentItem(): nothing selected, nothing to delete!") return True,-1 - # Do we delete a curve or a full plot set + # Do we delete a curve or a full plot set if c_id == -1: cls.DeletePlotSet(ps_id) ret = True, ps_id @@ -304,7 +304,7 @@ class PlotController(object): cls.DeleteCurve(c_id) ret = False, c_id return ret - + @classmethod def ClearPlotSet(cls, ps_id=-1): """ Clear all curves in a given plot set. By default clear the current plot set without deleting it, @@ -322,7 +322,7 @@ class PlotController(object): raise ValueError("Invalid plot set ID (%d)!" % ps_id) ps.eraseAll() return ps_id - + # @classmethod # def ClearAll(cls): # # TODO: optimize @@ -330,7 +330,7 @@ class PlotController(object): # ids = pm._plotSets.keys() # for i in ids: # cls.DeletePlotSet(i) - + @classmethod def SetXLabel(cls, x_label, plot_set_id=-1): """ By default set the X axis label for the current plot set, if any. Otherwise do nothing. @@ -341,14 +341,14 @@ class PlotController(object): plot_set_id = cls.GetCurrentPlotSetID() if plot_set_id == -1: # Do nothing - return False + return False ps = pm._plotSets.get(plot_set_id, None) if ps is None: raise Exception("Invalid plot set ID (%d)!" % plot_set_id) ps.setXLabel(x_label) return True - - @classmethod + + @classmethod def SetYLabel(cls, y_label, plot_set_id=-1): """ By default set the Y axis label for the current plot set, if any. Otherwise do nothing. @return True if the label was set @@ -358,14 +358,14 @@ class PlotController(object): plot_set_id = cls.GetCurrentPlotSetID() if plot_set_id == -1: # Do nothing - return False + return False ps = pm._plotSets.get(plot_set_id, None) if ps is None: raise Exception("Invalid plot set ID (%d)!" % plot_set_id) ps.setYLabel(y_label) return True - - @classmethod + + @classmethod def SetPlotSetTitle(cls, title, plot_set_id=-1): """ By default set the title for the current plot set, if any. Otherwise do nothing. @return True if the title was set @@ -375,13 +375,13 @@ class PlotController(object): plot_set_id = cls.GetCurrentPlotSetID() if plot_set_id == -1: # Do nothing - return False + return False ps = pm._plotSets.get(plot_set_id, None) if ps is None: raise Exception("Invalid plot set ID (%d)!" % plot_set_id) ps.setTitle(title) return True - + @classmethod def GetPlotSetID(cls, curve_id): """ @return plot set id for a given curve or -1 if invalid curve ID @@ -391,17 +391,17 @@ class PlotController(object): if cps is None: return -1 return cps.getID() - + @classmethod def GetPlotSetIDByName(cls, name): """ @return the first plot set whose name matches the provided name. Otherwise returns -1 - """ + """ pm = cls.GetInstance()._plotManager for _, ps in list(pm._plotSets.items()): if ps._title == name: return ps.getID() return -1 - + @classmethod def GetAllPlotSets(cls): """ @return two lists: plot set names, and corresponding plot set IDs @@ -409,12 +409,12 @@ class PlotController(object): pm = cls.GetInstance()._plotManager it = list(pm._plotSets.items()) ids, inst, titles = [], [], [] - if len(it): - ids, inst = list(zip(*it)) + if len(it): + ids, inst = list(zip(*it)) if len(inst): titles = [i.getTitle() for i in inst] return list(ids), titles - + @classmethod def GetCurrentCurveID(cls): """ @return current curve ID or -1 if no curve is currently active @@ -424,8 +424,8 @@ class PlotController(object): if crv is None: return -1 return crv.getID() - - @classmethod + + @classmethod def GetCurrentPlotSetID(cls): """ @return current plot set ID or -1 if no plot set is currently active """ @@ -433,7 +433,7 @@ class PlotController(object): cps = control._plotManager.getCurrentPlotSet() if cps is None: return -1 - return cps.getID() + return cps.getID() @classmethod def SetCurrentPlotSet(cls, ps_id): @@ -470,7 +470,7 @@ class PlotController(object): if cls.__UNIQUE_INSTANCE is not None: raise Exception("ToggleCurveBrowser() must be invoked before doing anything in plot2D!") cls.WITH_CURVE_BROWSER = active - + @classmethod def IsValidPlotSetID(cls, plot_set_id): """ @@ -529,11 +529,11 @@ class PlotController(object): ] @raise if invalid curve ID or marker """ - from XYView import XYView - from CurveView import CurveView + from .XYView import XYView + from .CurveView import CurveView if not marker in XYView.CURVE_MARKERS: raise ValueError("Invalid marker: '%s'" % marker) - + cont = cls.GetInstance() for mod, views in list(cont._modelViews.items()): if isinstance(mod, CurveModel) and mod.getID() == crv_id: @@ -544,7 +544,7 @@ class PlotController(object): v._parentXYView.repaint() v._parentXYView.showHideLegend() found = True - + if not found: raise Exception("Invalid curve ID or curve currently not displayed (curve_id=%d)!" % crv_id) @@ -562,9 +562,9 @@ class PlotController(object): @classmethod def __XYViewOperation(cls, func, ps_id, args, kwargs): """ Private. To factorize methods accessing the XYView to change a display element. """ - from XYPlotSetModel import XYPlotSetModel - from XYView import XYView - + from .XYPlotSetModel import XYPlotSetModel + from .XYView import XYView + cont = cls.GetInstance() for mod, views in list(cont._modelViews.items()): if isinstance(mod, XYPlotSetModel) and mod.getID() == ps_id: @@ -595,7 +595,7 @@ class PlotController(object): """ args, kwargs = [log], {} cls.__XYViewOperation("setYLog", ps_id, args, kwargs) - + @classmethod def SetXSciNotation(cls, ps_id, sciNotation=False): """ Change the format (scientific notation or not) of the X axis. @@ -605,7 +605,7 @@ class PlotController(object): """ args, kwargs = [sciNotation], {} cls.__XYViewOperation("setXSciNotation", ps_id, args, kwargs) - + @classmethod def SetYSciNotation(cls, ps_id, sciNotation=False): """ Change the format (scientific notation or not) of the Y axis. @@ -625,7 +625,7 @@ class PlotController(object): """ args, kwargs = [visible], {} cls.__XYViewOperation("setLegendVisible", ps_id, args, kwargs) - + ### ### More advanced functions @@ -634,28 +634,28 @@ class PlotController(object): def RegisterCallback(cls, callback): cont = cls.GetInstance() cont._callbacks.append(callback) - + @classmethod def ClearCallbacks(cls): cont = cls.GetInstance() cont._callbacks = [] - + @classmethod def LockRepaint(cls): control = cls.GetInstance() control._plotManager.lockRepaint() - + @classmethod def UnlockRepaint(cls): control = cls.GetInstance() - control._plotManager.unlockRepaint() - + control._plotManager.unlockRepaint() + def createTable(self, data, table_name="table"): t = TableModel(self) t.setData(data) t.setTitle(table_name) return t - + def plotCurveFromTable(self, table, x_col_index=0, y_col_index=1, curve_label="", append=True): """ :returns: a tuple containing the unique curve ID and the plot set ID @@ -671,15 +671,15 @@ class PlotController(object): cps_title = None cps = self._plotManager.getCurrentPlotSet() - + cm = CurveModel(self, table, y_col_index) cm.setXAxisIndex(x_col_index) - + # X axis label tix = table.getColumnTitle(x_col_index) if tix != "": cps.setXLabel(tix) - + # Curve label if curve_label != "": cm.setTitle(curve_label) @@ -690,21 +690,21 @@ class PlotController(object): # Plot set title if cps_title != "" and cps_title is not None: - Logger.Debug("about to set title to: " + cps_title) + Logger.Debug("about to set title to: " + cps_title) cps.setTitle(cps_title) - + cps.addCurve(cm) mp = self._curveTabsView.mapModId2ViewId() xyview_id = mp[cps.getID()] xyview = self._curveTabsView._XYViews[xyview_id] - + if cps_title is None: # no plot set was created above self._plotManager.setCurrentPlotSet(cps.getID()) - + # Make CurveBrowser and CurveView depend on changes in the curve itself: self.setModelListener(cm, self._curveBrowserView) self.setModelListener(cm, xyview._curveViews[cm.getID()]) # Upon change on the curve also update the full plot, notably for the auto-fit and the legend: self.setModelListener(cm, xyview) - + return cm.getID(),cps.getID() diff --git a/tools/CurvePlot/src/python/controller/__init__.py b/tools/CurvePlot/src/python/controller/__init__.py index 62d412f2d..a92f4d6ca 100644 --- a/tools/CurvePlot/src/python/controller/__init__.py +++ b/tools/CurvePlot/src/python/controller/__init__.py @@ -6,19 +6,19 @@ try: # and the Bitstream font which is the first one by default. Try to use DejaVu which is more # comprehensive. ## !!Order of the sequence below is highly sensitive!! - import pyqtside # will trigger the PySide/PyQt4 switch + import pyqtside # will trigger the PySide/PyQt switch import matplotlib - matplotlib.use('Qt4Agg') - import matplotlib.pyplot as plt # must come after the PySide/PyQt4 switch! + matplotlib.use('Qt5Agg') + import matplotlib.pyplot as plt # must come after the PySide/PyQt switch! plt.rcParams['font.sans-serif'].insert(0, "DejaVu Sans") except: - print("Warning: could not switch matplotlib to 'Qt4agg' backend. Some characters might be displayed improperly!") + print("Warning: could not switch matplotlib to 'Qt5agg' backend. Some characters might be displayed improperly!") from .PlotController import PlotController -from TableModel import TableModel -from CurveModel import CurveModel -from PlotManager import PlotManager -from XYPlotSetModel import XYPlotSetModel +from .TableModel import TableModel +from .CurveModel import CurveModel +from .PlotManager import PlotManager +from .XYPlotSetModel import XYPlotSetModel ## The static API of PlotController is the main interface of the package and is hence exposed at package level: AddCurve = PlotController.AddCurve diff --git a/tools/CurvePlot/src/python/controller/utils.py.in b/tools/CurvePlot/src/python/controller/utils.py.in index 684858174..f4225711a 100644 --- a/tools/CurvePlot/src/python/controller/utils.py.in +++ b/tools/CurvePlot/src/python/controller/utils.py.in @@ -59,20 +59,20 @@ class Logger(object): def trQ(tag, context="CURVEPLOT"): """ @return a QString read from the translation file """ - from pyqtside.QtGui import QApplication + from pyqtside.QtWidgets import QApplication return QApplication.translate(context, tag) def trU(tag, context="CURVEPLOT"): """ @return same as above, but returns a Python unicode string. """ qs = trQ(tag, context) - return unicode(qs, 'utf-8') + return str(qs, 'utf-8') def toUnicodeWithWarning(s, method_name): try: - s = unicode(s) + s = str(s) except: Logger.Warning("%s - warning, passing non-unicode, non-ASCII string '%s'! Trying to convert myself to UTF-8 ..." % (method_name, s)) - s = unicode(s, 'utf-8') + s = str(s, 'utf-8') return s def completeResPath(fileName): @@ -81,5 +81,7 @@ def completeResPath(fileName): rd = os.environ.get("CURVEPLOT_ROOT_DIR", None) if rd is None: raise Exception("CURVEPLOT_ROOT_DIR is not defined!") + if @SALOME_CURVEPLOT_TEST_MODE@: # do not remove automatically modified in CMake config + subPath = "@CRVPLOT_TEST_INSTALL@" filePath = os.path.join(rd, subPath, fileName) return filePath diff --git a/tools/CurvePlot/src/python/model/CMakeLists.txt b/tools/CurvePlot/src/python/model/CMakeLists.txt index 964d05ebf..64db612c6 100644 --- a/tools/CurvePlot/src/python/model/CMakeLists.txt +++ b/tools/CurvePlot/src/python/model/CMakeLists.txt @@ -24,5 +24,8 @@ SET(_all_lib_SCRIPTS PlotManager.py XYPlotSetModel.py ) - + SALOME_INSTALL_SCRIPTS("${_all_lib_SCRIPTS}" ${SALOME_CURVEPLOT_INSTALL_PYTHON}) + +# For test purposes +FILE(COPY ${_all_lib_SCRIPTS} DESTINATION ${CRVPLOT_TEST_INSTALL}) diff --git a/tools/CurvePlot/src/python/model/CurveModel.py b/tools/CurvePlot/src/python/model/CurveModel.py index c11179679..d426f6906 100644 --- a/tools/CurvePlot/src/python/model/CurveModel.py +++ b/tools/CurvePlot/src/python/model/CurveModel.py @@ -1,5 +1,5 @@ -from Model import Model -from utils import toUnicodeWithWarning +from .Model import Model +from .utils import toUnicodeWithWarning class CurveModel(Model): def __init__(self, controller, table=None, index=-1): diff --git a/tools/CurvePlot/src/python/model/PlotManager.py b/tools/CurvePlot/src/python/model/PlotManager.py index b874737e0..45c665d2d 100644 --- a/tools/CurvePlot/src/python/model/PlotManager.py +++ b/tools/CurvePlot/src/python/model/PlotManager.py @@ -1,6 +1,6 @@ -from Model import Model -from XYPlotSetModel import XYPlotSetModel -from utils import Logger +from .Model import Model +from .XYPlotSetModel import XYPlotSetModel +from .utils import Logger class PlotManager(Model): def __init__(self, controller): diff --git a/tools/CurvePlot/src/python/model/TableModel.py b/tools/CurvePlot/src/python/model/TableModel.py index 2f30aa7db..11600e7b3 100644 --- a/tools/CurvePlot/src/python/model/TableModel.py +++ b/tools/CurvePlot/src/python/model/TableModel.py @@ -1,6 +1,6 @@ import numpy as np -from Model import Model -from utils import toUnicodeWithWarning, Logger +from .Model import Model +from .utils import toUnicodeWithWarning, Logger class TableModel(Model): def __init__(self, controller): diff --git a/tools/CurvePlot/src/python/model/XYPlotSetModel.py b/tools/CurvePlot/src/python/model/XYPlotSetModel.py index 2edacfb9e..9d29b98b3 100644 --- a/tools/CurvePlot/src/python/model/XYPlotSetModel.py +++ b/tools/CurvePlot/src/python/model/XYPlotSetModel.py @@ -1,5 +1,5 @@ -from Model import Model -from utils import toUnicodeWithWarning +from .Model import Model +from .utils import toUnicodeWithWarning class XYPlotSetModel(Model): diff --git a/tools/CurvePlot/src/python/pyqtside/CMakeLists.txt b/tools/CurvePlot/src/python/pyqtside/CMakeLists.txt index 9ec5d8758..2ad9e4faa 100644 --- a/tools/CurvePlot/src/python/pyqtside/CMakeLists.txt +++ b/tools/CurvePlot/src/python/pyqtside/CMakeLists.txt @@ -21,8 +21,12 @@ SET(_pyqtside_SCRIPTS __init__.py QtCore.py QtGui.py + QtWidgets.py pyside_dynamic.py uic.py ) SALOME_INSTALL_SCRIPTS("${_pyqtside_SCRIPTS}" ${SALOME_INSTALL_PYTHON}/pyqtside) + +# For test purposes +FILE(COPY ${_pyqtside_SCRIPTS} DESTINATION ${PYQTSIDE_TEST_INSTALL}) diff --git a/tools/CurvePlot/src/python/pyqtside/QtCore.py b/tools/CurvePlot/src/python/pyqtside/QtCore.py index 6e90c29e8..173cb052c 100644 --- a/tools/CurvePlot/src/python/pyqtside/QtCore.py +++ b/tools/CurvePlot/src/python/pyqtside/QtCore.py @@ -1,7 +1,7 @@ from . import _use_pyqt if _use_pyqt: - from PyQt4.QtCore import * - Slot = pyqtSlot - Signal = pyqtSignal + from PyQt5.QtCore import * + Slot = pyqtSlot + Signal = pyqtSignal else: from PySide.QtCore import * diff --git a/tools/CurvePlot/src/python/pyqtside/QtGui.py b/tools/CurvePlot/src/python/pyqtside/QtGui.py index 15c7ee1f1..64e3d0938 100644 --- a/tools/CurvePlot/src/python/pyqtside/QtGui.py +++ b/tools/CurvePlot/src/python/pyqtside/QtGui.py @@ -1,37 +1,37 @@ from . import _use_pyqt if _use_pyqt: - from PyQt4.QtGui import * - - # Make QVariant invisible in PyQt4 since they don't exist in + from PyQt5.QtGui import * + + # Make QVariant invisible in PyQt5 since they don't exist in # PySide ... - __original_itemData = QComboBox.itemData - def new_itemData(*args, **kargs): - from PyQt4.QtCore import QVariant - variant = __original_itemData(*args, **kargs) - funcS = lambda : (str(variant.toString()), True) - dico = {QVariant.Int: variant.toInt, QVariant.String: funcS, - QVariant.Bool: variant.toBool, QVariant.Double: variant.toDouble} - conv = dico.get(variant.type(), None) - if conv is None: - raise Exception("Unsupported variant type in pyqtside: '%s'!" % variant.typeName()) - return conv()[0] - - QComboBox.itemData = new_itemData +# __original_itemData = QComboBox.itemData +# def new_itemData(*args, **kargs): +# from PyQt5.QtCore import QVariant +# variant = __original_itemData(*args, **kargs) +# funcS = lambda : (str(variant.toString()), True) +# dico = {QVariant.Int: variant.toInt, QVariant.String: funcS, +# QVariant.Bool: variant.toBool, QVariant.Double: variant.toDouble} +# conv = dico.get(variant.type(), None) +# if conv is None: +# raise Exception("Unsupported variant type in pyqtside: '%s'!" % variant.typeName()) +# return conv()[0] +# +# QComboBox.itemData = new_itemData else: from PySide.QtGui import * - + __original_ofn = QFileDialog.getOpenFileName __original_sfn = QFileDialog.getSaveFileName - - # In PySide, getOpenFileName and co returns 2 values, and only one in PyQt4 ... + + # In PySide, getOpenFileName and co returns 2 values, and only one in PyQt ... def newOfn(cls,*args, **kargs): tup = __original_ofn(*args, **kargs) return tup[0] - + def newSfn(cls,*args, **kargs): tup = __original_sfn(*args, **kargs) return tup[0] - + QFileDialog.getOpenFileName = classmethod(newOfn) QFileDialog.getSaveFileName = classmethod(newSfn) diff --git a/tools/CurvePlot/src/python/pyqtside/QtWidgets.py b/tools/CurvePlot/src/python/pyqtside/QtWidgets.py new file mode 100644 index 000000000..dd00a92cc --- /dev/null +++ b/tools/CurvePlot/src/python/pyqtside/QtWidgets.py @@ -0,0 +1,5 @@ +from . import _use_pyqt +if _use_pyqt: + from PyQt5.QtWidgets import * +else: + from PySide.QtWidgets import * diff --git a/tools/CurvePlot/src/python/pyqtside/__init__.py b/tools/CurvePlot/src/python/pyqtside/__init__.py index 1cba014b1..39dc7c587 100644 --- a/tools/CurvePlot/src/python/pyqtside/__init__.py +++ b/tools/CurvePlot/src/python/pyqtside/__init__.py @@ -1,29 +1,32 @@ """ -Group under one hat PySide and PyQt4. PyQt4 is tried first. +Group under one hat PySide and PyQt5. PyQt5 is tried first. """ try: import os if os.getenv("CURVEPLOT_FORCE_PYSIDE") is not None: raise Exception - import PyQt4 + import PyQt5 _use_pyqt = True - print("Using PyQt4 run-time ...") + print("Using PyQt5 run-time ...") except: try: import PySide _use_pyqt = False print("Using PySide run-time ...") except: - raise Exception("Neither PyQt4 nor PySide could be imported!") + raise Exception("Neither PyQt5 nor PySide could be imported!") # Matplotlib has to be handled very early, otherwise it will switch to whatever it # finds first on the machine try: import matplotlib - if _use_pyqt: back = 'PyQt4' + if _use_pyqt: back = 'PyQt5' else: back = 'PySide' - matplotlib.rcParams['backend.qt4'] = back + # As advised by MatPlotlib: + # "The backend.qt5 rcParam was deprecated in version 2.2. In order to force the use of a specific Qt binding, either import that binding first, or set the QT_API environment variable. + # mplDeprecation)" + from matplotlib.backends import backend_qt5agg print("Matplotlib found - Set matplotlib backend to '%s'!" % back) except: # No matplotlib, silently discard err message. diff --git a/tools/CurvePlot/src/python/pyqtside/pyside_dynamic.py b/tools/CurvePlot/src/python/pyqtside/pyside_dynamic.py index a3ab6046d..38a82e3b6 100644 --- a/tools/CurvePlot/src/python/pyqtside/pyside_dynamic.py +++ b/tools/CurvePlot/src/python/pyqtside/pyside_dynamic.py @@ -14,7 +14,7 @@ class UiLoader(QUiLoader): create a new instance of the top-level widget, but creates the user interface in an existing instance of the top-level class. - This mimics the behaviour of :func:`PyQt4.uic.loadUi`. + This mimics the behaviour of :func:`PyQt5.uic.loadUi`. """ def __init__(self, baseinstance, customWidgets=None): """ diff --git a/tools/CurvePlot/src/python/pyqtside/uic.py b/tools/CurvePlot/src/python/pyqtside/uic.py index 6cc146510..278741ff0 100644 --- a/tools/CurvePlot/src/python/pyqtside/uic.py +++ b/tools/CurvePlot/src/python/pyqtside/uic.py @@ -1,7 +1,7 @@ from . import _use_pyqt if _use_pyqt: - from PyQt4.uic import loadUi as loadUiGen -else: + from PyQt5.uic import loadUi as loadUiGen +else: from .pyside_dynamic import loadUi as loadUiGen diff --git a/tools/CurvePlot/src/python/test/CMakeLists.txt b/tools/CurvePlot/src/python/test/CMakeLists.txt index 24f9eca14..17d7c9167 100644 --- a/tools/CurvePlot/src/python/test/CMakeLists.txt +++ b/tools/CurvePlot/src/python/test/CMakeLists.txt @@ -26,20 +26,23 @@ SET(_all_SCRIPTS TestDesktop.py ) +SET(_test_SCRIPTS + ${CMAKE_CURRENT_BINARY_DIR}/mockup/SalomePyQt_MockUp.py + PlotTestBase.py + TestDesktop.py +) + SALOME_INSTALL_SCRIPTS("${_all_SCRIPTS}" ${SALOME_INSTALL_SCRIPT_PYTHON}/tests) SALOME_INSTALL_SCRIPTS(${CMAKE_CURRENT_BINARY_DIR}/mockup/SalomePyQt_MockUp.py ${SALOME_INSTALL_SCRIPT_PYTHON}/tests) INSTALL(DIRECTORY baselines DESTINATION ${SALOME_INSTALL_SCRIPT_PYTHON}/tests) -SALOME_ACCUMULATE_ENVIRONMENT(PYTHONPATH ${CMAKE_CURRENT_SOURCE_DIR}/../model) -SALOME_ACCUMULATE_ENVIRONMENT(PYTHONPATH ${CMAKE_CURRENT_SOURCE_DIR}/../ui) -SALOME_ACCUMULATE_ENVIRONMENT(PYTHONPATH ${CMAKE_CURRENT_SOURCE_DIR}/../views) -SALOME_ACCUMULATE_ENVIRONMENT(PYTHONPATH ${CMAKE_CURRENT_SOURCE_DIR}/../controller) -SALOME_ACCUMULATE_ENVIRONMENT(PYTHONPATH ${CMAKE_CURRENT_SOURCE_DIR}/..) -SALOME_ACCUMULATE_ENVIRONMENT(PYTHONPATH ${PROJECT_BINARY_DIR}/src/python/ui) # for the generated PY files (from UI files) -SALOME_ACCUMULATE_ENVIRONMENT(PYTHONPATH ${PROJECT_BINARY_DIR}/src/python/controller) # for utils.py +SALOME_ACCUMULATE_ENVIRONMENT(PYTHONPATH ${CMAKE_BINARY_DIR}/local) # point to local curveplot package in BUILD dir SALOME_GENERATE_TESTS_ENVIRONMENT(tests_env) ADD_TEST(CurvePlotUnitTests ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/plot_test.py) SET_TESTS_PROPERTIES(CurvePlotUnitTests PROPERTIES ENVIRONMENT "${tests_env}") +# For test purposes +FILE(COPY ${_test_SCRIPTS} DESTINATION ${CRVPLOT_TEST_INSTALL}) +FILE(COPY baselines DESTINATION ${CRVPLOT_TEST_INSTALL}) diff --git a/tools/CurvePlot/src/python/test/PlotCurve_Standalone.py b/tools/CurvePlot/src/python/test/PlotCurve_Standalone.py index 4097dedf0..f43335277 100755 --- a/tools/CurvePlot/src/python/test/PlotCurve_Standalone.py +++ b/tools/CurvePlot/src/python/test/PlotCurve_Standalone.py @@ -22,8 +22,8 @@ # # Author : A. Bruneton # -from pyqtside.QtGui import QApplication -from pyqtside.QtCore import SIGNAL, SLOT, QTimer, QTranslator +from pyqtside.QtWidgets import QApplication +from pyqtside.QtCore import QTimer, QTranslator from TestDesktop import TestDesktop import SalomePyQt_MockUp @@ -43,31 +43,23 @@ def activeViewChanged( viewID ): def main(args) : global desktop - + app = QApplication(args) - ts_files = ["/export/home/adrien/Projets/salome/modules/V7_main/CURVEPLOT_INSTALL/share/salome/resources/curveplot/CURVEPLOT_msg_fr.qm", - "/export/home/adrien/Projets/salome/modules/V7_main/CURVEPLOT_INSTALL/share/salome/resources/curveplot/CURVEPLOT_msg_en.qm" - ] - trans = QTranslator() - for f in ts_files: - if not trans.load(f): - print("could not load translation %s!" % f) - app.installTranslator(trans) dw = app.desktop() x, y = dw.width()*0.25, dw.height()*0.7 - + desktop = TestDesktop(None) sgPyQt = SalomePyQt_MockUp.SalomePyQt(desktop) sgPyQt.currentTabChanged.connect(activeViewChanged) - desktop._sgPyQt = sgPyQt + desktop._sgPyQt = sgPyQt desktop.initialize() desktop.resize(x,y) desktop.show() activate() # - QTimer.singleShot(200, desktop, SLOT("curveSameFig()")) + QTimer.singleShot(200, desktop.curveSameFig) # - app.connect(app,SIGNAL("lastWindowClosed()"),app,SLOT("quit()")) + app.lastWindowClosed.connect(app.quit) app.exec_() if __name__ == "__main__" : diff --git a/tools/CurvePlot/src/python/test/PlotTestBase.py b/tools/CurvePlot/src/python/test/PlotTestBase.py index e9cfc9aaa..5c7e57153 100644 --- a/tools/CurvePlot/src/python/test/PlotTestBase.py +++ b/tools/CurvePlot/src/python/test/PlotTestBase.py @@ -23,9 +23,13 @@ # Author : A. Bruneton # import unittest, sys, os, filecmp, shutil, tempfile -from pyqtside.QtGui import QApplication, QPixmap, QPainter -from PlotController import PlotController -from XYView import XYView +from pyqtside.QtWidgets import QApplication +from pyqtside.QtGui import QPixmap, QPainter +from pyqtside.QtCore import QTimer + +from curveplot.PlotController import PlotController +from curveplot.XYView import XYView +from PyQt5.Qt import QMainWindow def runOnly(func): func.__runOnly__ = True @@ -51,7 +55,7 @@ def processDecorator(mod_name): # Note the "not": if p.startswith("test") and not hasattr(obj.__dict__[p], "__runOnly__"): delattr(obj, p) - + class PlotTestBase(unittest.TestCase): """ Unit test suite for the curve plotter. This class deals with the set up and the screenshot generation/ comparison. The tests themselves are stored in the derived class PlotTest below. @@ -61,31 +65,30 @@ class PlotTestBase(unittest.TestCase): The baselines directory is set relative to the path of this script. """ REBUILD_BASELINES = False - + __BASE_LINE_DIR = "baselines" __FORMAT = "png" - + def __init__(self, methodName): unittest.TestCase.__init__(self, methodName) - + if self.REBUILD_BASELINES: self.tmpBaselineDir = os.path.join(tempfile.gettempdir(), "curveplot_baselines") if not os.path.isdir(self.tmpBaselineDir): os.mkdir(self.tmpBaselineDir) print("### Rebuilding base lines. Reference files will be saved to '%s'" % self.tmpBaselineDir) - + PlotController.WITH_CURVE_BROWSER = True XYView._DEFAULT_LEGEND_STATE = True # always show legend by default self._this_dir = os.path.dirname(os.path.realpath(__file__)) - -# import matplotlib as mpl -# mpl.use('Agg') - + def setUp(self): - from SalomePyQt_MockUp import SalomePyQt - from TableModel import TableModel - from CurveModel import CurveModel - from XYPlotSetModel import XYPlotSetModel + import sys + from curveplot.SalomePyQt_MockUp import SalomePyQt + from curveplot.TableModel import TableModel + from curveplot.CurveModel import CurveModel + from curveplot.XYPlotSetModel import XYPlotSetModel + from curveplot.TestDesktop import TestDesktop self.qpixmap = None self.keepDir = False @@ -93,21 +96,30 @@ class PlotTestBase(unittest.TestCase): self.tmpDir = tempfile.mkdtemp(prefix="curveplot_tests") else: self.tmpDir = None + # Minimal UI setup: - self.sgPyQt = SalomePyQt() - # Reinstanciate from scratch the PlotController: + self.app = QApplication(sys.argv) + desktop = TestDesktop(None) + self.sgPyQt = SalomePyQt(desktop) + desktop._sgPyQt = self.sgPyQt + desktop.initialize() self.plotController = PlotController.GetInstance(self.sgPyQt) - self.plotController.setFixedSizeWidget() + desktop.resize(800, 600) + desktop.show() + self._execQtWasCalled = False # Used to automatically finish Qt execution loop on tests not doing a screenshot + # Reset some class var to make sure IDs appearing in screenshots do not depend on test seq order: CurveModel.START_ID = -1 TableModel.START_ID = -1 XYPlotSetModel.START_ID = -1 - + def tearDown(self): if not self.REBUILD_BASELINES: # Clean up temp dir where the file comparison has been made: if not self.keepDir: shutil.rmtree(self.tmpDir, False) + if not self._execQtWasCalled: + self._execQt(withShot=False) PlotController.Destroy() def getTestName(self): @@ -118,7 +130,29 @@ class PlotTestBase(unittest.TestCase): self.qpixmap.save(fileName, self.__FORMAT) return fileName - def areScreenshotEqual(self, widget, suffix=""): + def _execQt(self, withShot=False): + if withShot: + QTimer.singleShot(50, self._shotEvent) # take picture + QTimer.singleShot(200, self.app.quit) # quit + self.app.exec_() # will hang till quit is fired + + # Important make sure previous app is properly killed before launching next test! + # Qt doesn't like multiple running instance + import gc + gc.collect() + + def areScreenshotEqual(self, widget): + """ Finish the launching of the Qt application so that the widgets and the curve have a chance to display + and trigger snapshot comparison """ + self.saveW = widget + self._execQtWasCalled = True + self._execQt(withShot=True) + return self.retValue + + def _shotEvent(self): + self.retValue = self._snapAndCompare(self.saveW) + + def _snapAndCompare(self, widget, suffix=""): """ Test equality between a reference file saved in the baseline directory, and whose name is built as ".png" and the file generated on the fly by taking a snapshot of the widget provided in argument. @@ -126,13 +160,16 @@ class PlotTestBase(unittest.TestCase): """ import glob # Smiiiile :-) - self.qpixmap = QPixmap.grabWidget(widget) - + self.qpixmap = QPixmap(widget.size()) + widget.repaint() + widget.render(self.qpixmap) + #self.qpixmap = widget.grab() + # Nothing to compare if rebuilding base lines, just saving file: if self.REBUILD_BASELINES: self.saveCurrentPix(self.tmpBaselineDir, suffix) return True - + gen_path = self.saveCurrentPix(self.tmpDir, suffix) base_ref = os.path.join(self._this_dir, self.__BASE_LINE_DIR, self.getTestName() + suffix) ret = False @@ -147,13 +184,13 @@ class PlotTestBase(unittest.TestCase): # Keep file if assert is false self.keepDir = True print("[%s] -- Failed screenshot equality, or unable to open baseline file - directory is kept alive: %s" % (self.getTestName(), self.tmpDir)) - return ret - + return ret + def showTabWidget(self): tabW = self.plotController._sgPyQt._tabWidget # No simpler way found so far: tabW.show() return tabW - + def getBrowserWidget(self): return self.plotController._curveBrowserView._treeWidget diff --git a/tools/CurvePlot/src/python/test/README.txt b/tools/CurvePlot/src/python/test/README.txt index bdb40251f..cbc22eaf7 100644 --- a/tools/CurvePlot/src/python/test/README.txt +++ b/tools/CurvePlot/src/python/test/README.txt @@ -1,4 +1,12 @@ -Unit tests are in plot_test.py +Unit tests are in plot_test.py. + +They should be run: + - without any install of CurvePlot, to be sure there is no conflict + - with the ctest command + +If anything is changed in the code, be sure to explicitely call + cmake . +in the build directory to update the local replica of the 'curveplot' package. PlotCurve_Standalone is a standalone Python executable that shows the various functionalities of the package. diff --git a/tools/CurvePlot/src/python/test/SalomePyQt_MockUp.py.in b/tools/CurvePlot/src/python/test/SalomePyQt_MockUp.py.in index ad5d2d6f8..44814d1d6 100644 --- a/tools/CurvePlot/src/python/test/SalomePyQt_MockUp.py.in +++ b/tools/CurvePlot/src/python/test/SalomePyQt_MockUp.py.in @@ -22,9 +22,10 @@ # Author : A. Bruneton # -from pyqtside.QtGui import QApplication, QTabWidget -from pyqtside.QtGui import QAction, QMenu, QIcon, QPixmap, QDesktopWidget, QFileDialog -from pyqtside.QtCore import SIGNAL, SLOT, QObject, Slot, Signal +from pyqtside.QtWidgets import QApplication, QTabWidget +from pyqtside.QtWidgets import QAction, QMenu, QDesktopWidget, QFileDialog +from pyqtside.QtGui import QIcon, QPixmap +from pyqtside.QtCore import QObject, pyqtSlot, pyqtSignal RESOURCE_DIR = "@SGPYQT_RES_DIR@" @@ -33,7 +34,7 @@ class SalomePyQt(QObject): This class can be used to mimick the true SALOME object without having to launch SALOME """ - currentTabChanged = Signal(int) + currentTabChanged = pyqtSignal(int) START_VIEW_ID = 0 @@ -47,7 +48,7 @@ class SalomePyQt(QObject): if self._mainWindow: self._menuBar = self._mainWindow.menuBar() self._mainWindow.setCentralWidget(self._tabWidget) - self.connect(self._tabWidget, SIGNAL("currentChanged(int)"), self, SLOT("onTabChanged(int)")) + self._tabWidget.currentChanged.connect(self.onTabChanged) self._blockSignal = False def getDesktop(self): @@ -58,12 +59,12 @@ class SalomePyQt(QObject): return QFileDialog.getSaveFileName(parent=parent_widget, caption=caption, directory=initial, filter=fil); - @Slot(int) + @pyqtSlot(int) def onTabChanged(self, index): if self._blockSignal: return invDict = dict([(v, k) for k,v in self._viewIDs.items()]) - if invDict.has_key(index): + if index in invDict: self._blockSignal = True self.currentTabChanged.emit(invDict[index]) self._blockSignal = False diff --git a/tools/CurvePlot/src/python/test/TestDesktop.py b/tools/CurvePlot/src/python/test/TestDesktop.py index 142fcf64f..b09ca0ace 100644 --- a/tools/CurvePlot/src/python/test/TestDesktop.py +++ b/tools/CurvePlot/src/python/test/TestDesktop.py @@ -23,8 +23,8 @@ # Author : A. Bruneton # -from pyqtside.QtCore import SIGNAL, SLOT, Slot, Qt, QTimer -from pyqtside.QtGui import QMainWindow,QMenu +from pyqtside.QtCore import Qt, QTimer, pyqtSlot +from pyqtside.QtWidgets import QMainWindow,QMenu import numpy as np import curveplot @@ -70,15 +70,15 @@ class TestDesktop(QMainWindow): self.createMenus() self.createView() - self.connect(self.curveSameFigAction,SIGNAL("activated()"),self.curveSameFig) - self.connect(self.curveNewFigAction,SIGNAL("activated()"),self.curveNewFig) - self.connect(self.itemDelAction,SIGNAL("activated()"),self.itemDel) - self.connect(self.cpsAction,SIGNAL("activated()"),self.clearPlotSet) - self.connect(self.plotTableAction,SIGNAL("activated()"),self.plotTable) - self.connect(self.addPSAction,SIGNAL("activated()"),self.addPS) - self.connect(self.addTabAction,SIGNAL("activated()"),self.addTab) - self.connect(self.memAction,SIGNAL("activated()"),self.memPrint) - self.connect(self.perfTestAction,SIGNAL("activated()"),self.perfTest) + self.curveSameFigAction.triggered.connect(self.curveSameFig) + self.curveNewFigAction.triggered.connect(self.curveNewFig) + self.itemDelAction.triggered.connect(self.itemDel) + self.cpsAction.triggered.connect(self.clearPlotSet) + self.plotTableAction.triggered.connect(self.plotTable) + self.addPSAction.triggered.connect(self.addPS) + self.addTabAction.triggered.connect(self.addTab) + self.memAction.triggered.connect(self.memPrint) + self.perfTestAction.triggered.connect(self.perfTest) def generateID(self): self._currID += 1 @@ -152,7 +152,7 @@ class TestDesktop(QMainWindow): # y = x return x, y - @Slot() + @pyqtSlot() def curveSameFig(self): x, y = self.__generateRandomData() _, ps_id = curveplot.AddCurve(x, y, x_label="the x axis", y_label="the y axis", append=True) @@ -164,13 +164,13 @@ class TestDesktop(QMainWindow): x, y = self.__generateRandomData() curveplot.AddCurve(x, y, x_label="the x axis", y_label="the y axis", append=False) - @Slot() + @pyqtSlot() def itemDel(self): curveplot.DeleteCurrentItem() if self.cnt >= 0: QTimer.singleShot(self.timeLap, self, SLOT("memPrint()")) - @Slot() + @pyqtSlot() def perfTest(self): lx, ly = [], [] nC = 200 @@ -195,7 +195,7 @@ class TestDesktop(QMainWindow): def addTab(self): pass -# from PyQt4.QtGui import QPushButton +# from PyQt5.QtWidgets import QPushButton # self.qp = QPushButton("Hi!") # self._sgPyQt.createView("Dummy", self.qp) @@ -213,7 +213,7 @@ class TestDesktop(QMainWindow): cont.plotCurveFromTable(t, y_col_index=1, append=False) cont.plotCurveFromTable(t, y_col_index=2, append=True) - @Slot() + @pyqtSlot() def memPrint(self): i, t = curveplot.GetAllPlotSets() print(list(zip(i, t))) @@ -224,4 +224,4 @@ class TestDesktop(QMainWindow): print("** Used memory: %.2f Mb" % m) if self.cnt >= 0 and self.cnt < self.MAX_CNT: self.cnt += 1 - QTimer.singleShot(self.timeLap, self, SLOT("curveSameFig()")) + QTimer.singleShot(self.timeLap, self.curveSameFig) diff --git a/tools/CurvePlot/src/python/test/plot_test.py b/tools/CurvePlot/src/python/test/plot_test.py index 3058925ff..d791e16ca 100644 --- a/tools/CurvePlot/src/python/test/plot_test.py +++ b/tools/CurvePlot/src/python/test/plot_test.py @@ -22,16 +22,14 @@ # # Author : A. Bruneton # +from curveplot import * +from curveplot.PlotTestBase import PlotTestBase, processDecorator +from curveplot.PlotSettings import PlotSettings -from PlotTestBase import PlotTestBase, runOnly, processDecorator - -from PlotController import PlotController -from PlotSettings import PlotSettings - -from pyqtside.QtGui import QApplication +from pyqtside.QtWidgets import QApplication import sys -qapp = QApplication(sys.argv) - +qapp = QApplication(sys.argv) + class PlotTest(PlotTestBase): """ Unit test suite for the curve plotter. The tests themselves are stored in this class, the screenshot comparison logic is in PlotTestBase. @@ -43,10 +41,10 @@ class PlotTest(PlotTestBase): The decorator @runOnly can be used to run/rebuild a single test. """ REBUILD_BASELINES = False - + def __init__(self, methodName): PlotTestBase.__init__(self, methodName) - + ### ### Data generation ### @@ -55,24 +53,24 @@ class PlotTest(PlotTestBase): x = np.arange(100) y = np.sin(x*alpha/np.pi) return x, y - + def generateExp(self, alpha=1.0): import numpy as np x = np.arange(20) + 1.0 y = np.exp(x*alpha) return x, y - + ### ### The tests themselves ### - + # # Non GUI tests (some of them still need to show the widget to work properly but no # screenshot comparison is made). # def testTableModel(self): import numpy as np - from TableModel import TableModel + from curveplot.TableModel import TableModel t = TableModel(None) t.setTitle("coucou") t.addColumn([1.0,2.0,3.0,4.0]) @@ -85,25 +83,25 @@ class PlotTest(PlotTestBase): t.setColumnTitle(1, "a title") self.assertEqual("a title", t.getColumnTitle(1)) self.assertEqual("", t.getColumnTitle(0)) - + def testGetAllPlotSets(self): self.showTabWidget() ids, titles = PlotController.GetAllPlotSets() self.assertEqual([], ids) self.assertEqual([], titles) - + id1 = PlotController.AddPlotSet("toto") id2 = PlotController.AddPlotSet("tutu") id3 = PlotController.AddPlotSet("titi") ids, titles = PlotController.GetAllPlotSets() self.assertEqual([id1,id2,id3], ids) self.assertEqual(["toto","tutu","titi"], titles) - + def testGetCurrentXX(self): self.showTabWidget() self.assertEqual(-1, PlotController.GetCurrentCurveID()) self.assertEqual(-1, PlotController.GetCurrentPlotSetID()) - + x, y = self.generateSine() _, psID1 = PlotController.AddCurve(x, y, append=False) self.assertEqual(psID1, PlotController.GetCurrentPlotSetID()) @@ -116,7 +114,7 @@ class PlotTest(PlotTestBase): PlotController.DeletePlotSet(psID2) self.assertEqual(-1, PlotController.GetCurrentCurveID()) self.assertEqual(-1, PlotController.GetCurrentPlotSetID()) - + def testGetPlotSetID(self): self.showTabWidget() x, y = self.generateSine() @@ -125,7 +123,7 @@ class PlotTest(PlotTestBase): self.assertEqual(-1, PlotController.GetPlotSetID(145)) # invalid ID PlotController.DeletePlotSet(psID) self.assertEqual(-1, PlotController.GetPlotSetID(crvID)) # invalid ID - + def testGetPlotSetIDByName(self): self.showTabWidget() self.assertEqual(-1,PlotController.GetPlotSetIDByName("invalid")) @@ -133,7 +131,7 @@ class PlotTest(PlotTestBase): self.assertEqual(psID,PlotController.GetPlotSetIDByName("ps")) PlotController.DeletePlotSet(psID) self.assertEqual(-1,PlotController.GetPlotSetIDByName("ps")) - + def testIsValidPlotSetID(self): self.showTabWidget() self.assertEqual(False,PlotController.IsValidPlotSetID(0)) @@ -141,16 +139,16 @@ class PlotTest(PlotTestBase): self.assertEqual(True,PlotController.IsValidPlotSetID(psID)) PlotController.DeletePlotSet(psID) self.assertEqual(False,PlotController.IsValidPlotSetID(psID)) - + # # GUI tests - # + # def testAddCurve(self): x, y = self.generateSine() tw = self.showTabWidget() PlotController.AddCurve(x, y, curve_label="My curve", x_label="Lèés X (unicode!)", y_label="Et des ŷ", append=False) self.assertTrue(self.areScreenshotEqual(tw)) - + def testAddCurveAppend(self): x, y = self.generateSine() tw = self.showTabWidget() @@ -162,12 +160,12 @@ class PlotTest(PlotTestBase): tw = self.showTabWidget() PlotController.AddPlotSet("My plotset") self.assertTrue(self.areScreenshotEqual(tw)) - + def testClearPlotSet(self): x, y = self.generateSine() tw = self.showTabWidget() PlotController.AddCurve(x, y, curve_label="My curve", x_label="The X-s", y_label="The Y-s", append=False) - _, psID = PlotController.AddCurve(x, y, curve_label="My curve 2", append=True) + _, psID = PlotController.AddCurve(x, y, curve_label="My curve 2", append=True) clearedID = PlotController.ClearPlotSet() self.assertEqual(clearedID, psID) self.assertTrue(self.areScreenshotEqual(tw)) @@ -179,7 +177,7 @@ class PlotTest(PlotTestBase): clearedID = PlotController.ClearPlotSet(psID) self.assertEqual(psID, clearedID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testCopyCurve(self): x, y = self.generateSine() tw = self.showTabWidget() @@ -189,7 +187,7 @@ class PlotTest(PlotTestBase): PlotController.SetCurrentPlotSet(psID) self.assertNotEqual(crvID, newID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testDeleteCurrentItem_curve(self): x, y = self.generateSine() tw = self.showTabWidget() @@ -200,7 +198,7 @@ class PlotTest(PlotTestBase): self.assertFalse(b) self.assertEqual(crvID, anID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testDeleteCurrentItem_plotSet(self): tw = self.showTabWidget() PlotController.AddPlotSet("tutu") @@ -209,13 +207,13 @@ class PlotTest(PlotTestBase): self.assertTrue(b) self.assertEqual(psID, anID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testDeleteCurrentItem_void(self): self.showTabWidget() - b, anID = PlotController.DeleteCurrentItem() # nothing selected + b, anID = PlotController.DeleteCurrentItem() # nothing selected self.assertTrue(b) self.assertEqual(-1, anID) - + def testDeleteCurve1(self): tw = self.showTabWidget() x, y = self.generateSine() @@ -224,7 +222,7 @@ class PlotTest(PlotTestBase): cID = PlotController.DeleteCurve(crvID) self.assertEqual(crvID, cID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testDeleteCurve2(self): tw = self.showTabWidget() x, y = self.generateSine() @@ -234,16 +232,16 @@ class PlotTest(PlotTestBase): cID = PlotController.DeleteCurve() # current curve self.assertEqual(crvID, cID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testDeleteCurve3(self): """ resulting in an empty plot set, legend should be hidden """ tw = self.showTabWidget() x, y = self.generateSine() crvID, _ = PlotController.AddCurve(x, y, append=False) - cID = PlotController.DeleteCurve(crvID) + cID = PlotController.DeleteCurve(crvID) self.assertEqual(crvID, cID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testDeletePlotSet1(self): tw = self.showTabWidget() psID = PlotController.AddPlotSet("tutu") @@ -251,7 +249,7 @@ class PlotTest(PlotTestBase): psID2 = PlotController.DeletePlotSet(psID) self.assertEqual(psID2, psID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testDeletePlotSet2(self): tw = self.showTabWidget() psID1 = PlotController.DeletePlotSet() @@ -262,7 +260,7 @@ class PlotTest(PlotTestBase): psID3 = PlotController.DeletePlotSet() # current plot set self.assertEqual(psID3, psID2) self.assertTrue(self.areScreenshotEqual(tw)) - + def testSetCurrentCurve(self): tw = self.showTabWidget() self.assertRaises(ValueError, PlotController.SetCurrentCurve, 23) @@ -272,7 +270,7 @@ class PlotTest(PlotTestBase): psID2 = PlotController.SetCurrentCurve(crvID) self.assertEqual(psID, psID2) self.assertTrue(self.areScreenshotEqual(tw)) - + def testSetCurrentCurve2(self): tw = self.showTabWidget() x, y = self.generateSine() @@ -283,7 +281,7 @@ class PlotTest(PlotTestBase): # on first plot set curve should not be selected anymore PlotController.SetCurrentPlotSet(psID) self.assertTrue(self.areScreenshotEqual(tw)) - + def testSetCurrentCurve3(self): tw = self.showTabWidget() x, y = self.generateSine() @@ -292,7 +290,7 @@ class PlotTest(PlotTestBase): PlotController.SetCurrentCurve(crvID) PlotController.SetCurrentCurve(-1) self.assertTrue(self.areScreenshotEqual(tw)) - + def testSetCurrentPlotSet(self): tw = self.showTabWidget() psID = PlotController.AddPlotSet("tutu") @@ -300,7 +298,7 @@ class PlotTest(PlotTestBase): PlotController.SetCurrentPlotSet(psID) self.assertTrue(self.areScreenshotEqual(tw)) self.assertRaises(ValueError, PlotController.SetCurrentPlotSet, 124) # invalid ps_id - + def testSetLabelX(self): tw = self.showTabWidget() ps_id = PlotController.AddPlotSet("My plotset") @@ -321,14 +319,14 @@ class PlotTest(PlotTestBase): PlotController.SetPlotSetTitle("un titre àé", ps_id) PlotController.SetCurrentPlotSet(ps_id) self.assertTrue(self.areScreenshotEqual(tw)) - + # def testToggleCurveBrowser(self): # # hard to test ... # raise NotImplementedError - + def testPlotCurveFromTable(self): tw = self.showTabWidget() - from TableModel import TableModel + from curveplot.TableModel import TableModel t = TableModel(None) t.setTitle("coucou") t.addColumn([1.0,2.0,3.0,4.0]) @@ -352,21 +350,21 @@ class PlotTest(PlotTestBase): def fun(): dlg_test.setRGB(0,0,0) dlg_test.showLegendCheckBox.setChecked(True) - return True + return True dlg_test.exec_ = fun t = list(PlotController.GetInstance()._curveTabsView._XYViews.items()) - t[0][1].onSettings(dlg_test=dlg_test) + t[0][1].onSettings(dlg_test=dlg_test) self.assertTrue(self.areScreenshotEqual(tw)) - + def testExtendCurve(self): tw = self.showTabWidget() x, y = self.generateSine() crvID, _ = PlotController.AddCurve(x, y, append=False) PlotController.SetCurrentCurve(crvID) PlotController.ExtendCurve(crvID, x+100.0, y*2.0) - # Curve must remain blue, bold and with first marker: + # Curve must remain blue, bold and with first marker: self.assertTrue(self.areScreenshotEqual(tw)) - + def testResetCurve(self): tw = self.showTabWidget() x, y = self.generateSine() @@ -374,7 +372,7 @@ class PlotTest(PlotTestBase): PlotController.SetCurrentCurve(crvID) PlotController.ResetCurve(crvID) PlotController.ExtendCurve(crvID, x+100.0, y*x) - # Curve must remain blue, bold and with first marker: + # Curve must remain blue, bold and with first marker: self.assertTrue(self.areScreenshotEqual(tw)) def testSettingsCurveMarker(self): @@ -387,19 +385,19 @@ class PlotTest(PlotTestBase): def fun(): dlg_test.markerCurve.setCurrentIndex(2) dlg_test.showLegendCheckBox.setChecked(True) - return True + return True dlg_test.exec_ = fun t = list(PlotController.GetInstance()._curveTabsView._XYViews.items()) - t[0][1].onSettings(dlg_test=dlg_test) + t[0][1].onSettings(dlg_test=dlg_test) self.assertTrue(self.areScreenshotEqual(tw)) - + def testSetCurveMarker(self): tw = self.showTabWidget() x, y = self.generateSine() crvID, _ = PlotController.AddCurve(x, y, append=False) PlotController.SetCurveMarker(crvID, "v") self.assertTrue(self.areScreenshotEqual(tw)) - + def testSetCurveLabel(self): tw = self.showTabWidget() x, y = self.generateSine() @@ -408,7 +406,7 @@ class PlotTest(PlotTestBase): PlotController.SetCurrentCurve(crvID) PlotController.SetCurveLabel(crvID, "tata") self.assertTrue(self.areScreenshotEqual(tw)) - + def testToggleXLog(self): tw = self.showTabWidget() x, y = self.generateExp() @@ -424,7 +422,7 @@ class PlotTest(PlotTestBase): PlotController.SetYLog(psID, True) PlotController.SetYSciNotation(psID, True) self.assertTrue(self.areScreenshotEqual(tw)) - + def testSetXSciNotation(self): tw = self.showTabWidget() x, y = self.generateSine() @@ -438,7 +436,7 @@ class PlotTest(PlotTestBase): _, psID = PlotController.AddCurve(x*1.0e6, y*1.0e6, curve_label="titi", append=False) PlotController.SetYSciNotation(psID, True) self.assertTrue(self.areScreenshotEqual(tw)) - + def testRegisterCallback(self): global a_callb a_callb = 0 @@ -466,7 +464,7 @@ class PlotTest(PlotTestBase): PlotController.SetCurrentCurve(crvId) _, _ = PlotController.AddCurve(x, y) self.assertEqual(crvId, a_callb) - + def testAddCurveEmptyPs(self): """ Adding a curve when no ps was active was buggy """ self.showTabWidget() @@ -478,7 +476,7 @@ class PlotTest(PlotTestBase): PlotController.AddCurve(x, y, append=True) l, _ = PlotController.GetAllPlotSets() self.assertEqual(2, len(l)) - + def test_onCurrentCurveChange(self): self.showTabWidget() x, y = self.generateSine() @@ -517,10 +515,10 @@ class PlotTest(PlotTestBase): self.assertEqual(psID0, psID2) l, _ = PlotController.GetAllPlotSets() self.assertEqual(1, len(l)) - + # Even if not in main: processDecorator(__name__) if __name__ == "__main__": - import unittest + import unittest unittest.main() diff --git a/tools/CurvePlot/src/python/ui/CMakeLists.txt b/tools/CurvePlot/src/python/ui/CMakeLists.txt index a52511cd2..ccdbc24b4 100644 --- a/tools/CurvePlot/src/python/ui/CMakeLists.txt +++ b/tools/CurvePlot/src/python/ui/CMakeLists.txt @@ -36,5 +36,10 @@ SET(_all_lib_SCRIPTS # --- rules --- SALOME_INSTALL_SCRIPTS("${_pyuic_SCRIPTS}" ${SALOME_CURVEPLOT_INSTALL_PYTHON}) SALOME_INSTALL_SCRIPTS("${_all_lib_SCRIPTS}" ${SALOME_CURVEPLOT_INSTALL_PYTHON}) - INSTALL(FILES ${_pyuic_files} DESTINATION ${SALOME_CURVEPLOT_INSTALL_PYTHON}) + +# For test purposes +FILE(COPY ${_pyuic_SCRIPTS} DESTINATION ${CRVPLOT_TEST_INSTALL}) +FILE(COPY ${_pyuic_files} DESTINATION ${CRVPLOT_TEST_INSTALL}) +FILE(COPY ${_all_lib_SCRIPTS} DESTINATION ${CRVPLOT_TEST_INSTALL}) + diff --git a/tools/CurvePlot/src/python/ui/CurveTreeDockWidget.py b/tools/CurvePlot/src/python/ui/CurveTreeDockWidget.py index bda0f5dec..d6b6d2263 100644 --- a/tools/CurvePlot/src/python/ui/CurveTreeDockWidget.py +++ b/tools/CurvePlot/src/python/ui/CurveTreeDockWidget.py @@ -1,10 +1,10 @@ -from pyqtside import QtGui, QtCore +from pyqtside import QtWidgets, QtCore from pyqtside.uic import loadUiGen -from utils import completeResPath +from .utils import completeResPath -class CurveTreeDockWidget(QtGui.QDockWidget): +class CurveTreeDockWidget(QtWidgets.QDockWidget): def __init__(self): - QtGui.QDockWidget.__init__(self) + QtWidgets.QDockWidget.__init__(self) loadUiGen(completeResPath("CurveTreeDockWidget.ui"), self) self.treeWidget.setHeaderLabel ("Plots") self.treeWidget.sortByColumn(0, QtCore.Qt.AscendingOrder) diff --git a/tools/CurvePlot/src/python/ui/PlotSettings.py b/tools/CurvePlot/src/python/ui/PlotSettings.py index d26461895..201e5074e 100644 --- a/tools/CurvePlot/src/python/ui/PlotSettings.py +++ b/tools/CurvePlot/src/python/ui/PlotSettings.py @@ -1,10 +1,12 @@ -from pyqtside import QtGui, QtCore +from pyqtside.QtWidgets import QDialog, QColorDialog, QMessageBox +from pyqtside.QtGui import QIcon, QPixmap, QColor +from pyqtside.QtCore import pyqtSlot from pyqtside.uic import loadUiGen -from utils import completeResPath +from .utils import completeResPath -class PlotSettings(QtGui.QDialog): +class PlotSettings(QDialog): def __init__(self): - QtGui.QDialog.__init__(self) + QDialog.__init__(self) loadUiGen(completeResPath("PlotSettings.ui"), self) self.initialize() @@ -15,16 +17,16 @@ class PlotSettings(QtGui.QDialog): self._g = 0 self._b = 1 - @QtCore.Slot(int) + @pyqtSlot(int) def onShowLegend(self, index): if index > 0 : self.legendPositionComboBox.setEnabled(True) else : self.legendPositionComboBox.setEnabled(False) - @QtCore.Slot() + @pyqtSlot() def onChangeColor(self): - col = QtGui.QColorDialog.getColor() + col = QColorDialog.getColor() if col.isValid(): r, g, b = [c/255.0 for c in col.getRgb()[:3]] @@ -41,14 +43,14 @@ class PlotSettings(QtGui.QDialog): self._r = r self._g = g self._b = b - self.colorCurve.setIcon(QtGui.QIcon(self.drawColorPixmap(int(r*255), int(g*255), int(b*255)))) + self.colorCurve.setIcon(QIcon(self.drawColorPixmap(int(r*255), int(g*255), int(b*255)))) def getRGB(self): return self._r, self._g, self._b def drawColorPixmap(self, r, g, b): - pix = QtGui.QPixmap( 16, 16 ) - color = QtGui.QColor(r, g, b) + pix = QPixmap( 16, 16 ) + color = QColor(r, g, b) pix.fill(color) return pix @@ -58,32 +60,32 @@ class PlotSettings(QtGui.QDialog): yminText = str(self.axisYMinEdit.text()) ymaxText = str(self.axisYMaxEdit.text()) if (yminText == "" or ymaxText == "") : - QtGui.QMessageBox.critical(self, "Plot settings", "A field \"YMin\" or \"YMax\" is empty") + QMessageBox.critical(self, "Plot settings", "A field \"YMin\" or \"YMax\" is empty") else : try: xmin = float(xminText) except ValueError: - QtGui.QMessageBox.critical(self, "Plot settings", "It is not possible to convert XMin") + QMessageBox.critical(self, "Plot settings", "It is not possible to convert XMin") try: xmax = float(xmaxText) except ValueError: - QtGui.QMessageBox.critical(self, "Plot settings", "It is not possible to convert XMax") + QMessageBox.critical(self, "Plot settings", "It is not possible to convert XMax") try: ymin = float(yminText) except ValueError: - QtGui.QMessageBox.critical(self, "Plot settings", "It is not possible to convert YMin") + QMessageBox.critical(self, "Plot settings", "It is not possible to convert YMin") try: ymax = float(ymaxText) except ValueError: - QtGui.QMessageBox.critical(self, "Plot settings", "It is not possible to convert YMax") + QMessageBox.critical(self, "Plot settings", "It is not possible to convert YMax") if ((xmax-xmin) == 0) : - QtGui.QMessageBox.critical(self, "Plot settings", "XMax is is equal to XMin.") + QMessageBox.critical(self, "Plot settings", "XMax is is equal to XMin.") return if ((ymax-ymin) == 0) : - QtGui.QMessageBox.critical(self, "Plot settings", "YMax is is equal to YMin.") + QMessageBox.critical(self, "Plot settings", "YMax is is equal to YMin.") return if ((xmax-xmin) < 0) : - QtGui.QMessageBox.warning(self, "Plot settings", "XMax is less than XMin.") + QMessageBox.warning(self, "Plot settings", "XMax is less than XMin.") if ((ymax-ymin) < 0) : - QtGui.QMessageBox.warning(self, "Plot settings", "YMax is less than YMin.") + QMessageBox.warning(self, "Plot settings", "YMax is less than YMin.") super(PlotSettings, self).accept() diff --git a/tools/CurvePlot/src/python/ui/PlotWidget.py b/tools/CurvePlot/src/python/ui/PlotWidget.py index 677c5496a..7a8a87fa4 100644 --- a/tools/CurvePlot/src/python/ui/PlotWidget.py +++ b/tools/CurvePlot/src/python/ui/PlotWidget.py @@ -1,10 +1,10 @@ -from pyqtside import QtGui +from pyqtside import QtWidgets from pyqtside.uic import loadUiGen -from utils import completeResPath +from .utils import completeResPath -class PlotWidget(QtGui.QMainWindow): +class PlotWidget(QtWidgets.QMainWindow): def __init__(self): - QtGui.QMainWindow.__init__(self) + QtWidgets.QMainWindow.__init__(self) loadUiGen(completeResPath("PlotWidget.ui"), self) def clearAll(self): diff --git a/tools/CurvePlot/src/python/views/CMakeLists.txt b/tools/CurvePlot/src/python/views/CMakeLists.txt index 7186c1978..2f278d1c0 100644 --- a/tools/CurvePlot/src/python/views/CMakeLists.txt +++ b/tools/CurvePlot/src/python/views/CMakeLists.txt @@ -24,5 +24,8 @@ SET(_all_lib_SCRIPTS CurveView.py XYView.py ) - + SALOME_INSTALL_SCRIPTS("${_all_lib_SCRIPTS}" ${SALOME_CURVEPLOT_INSTALL_PYTHON}) + +# For test purposes +FILE(COPY ${_all_lib_SCRIPTS} DESTINATION ${CRVPLOT_TEST_INSTALL}) diff --git a/tools/CurvePlot/src/python/views/CurveBrowserView.py b/tools/CurvePlot/src/python/views/CurveBrowserView.py index df296b60a..673ef2415 100644 --- a/tools/CurvePlot/src/python/views/CurveBrowserView.py +++ b/tools/CurvePlot/src/python/views/CurveBrowserView.py @@ -1,10 +1,10 @@ -from pyqtside import QtGui -from pyqtside.QtGui import QMenu +from pyqtside import QtWidgets +from pyqtside.QtWidgets import QMenu from pyqtside.QtCore import Qt -from View import View -from CurveTreeDockWidget import CurveTreeDockWidget -from utils import Logger +from .View import View +from .CurveTreeDockWidget import CurveTreeDockWidget +from .utils import Logger class CurveBrowserView( View, CurveTreeDockWidget) : @@ -36,10 +36,10 @@ class CurveBrowserView( View, CurveTreeDockWidget) : # The second (hidden) column in the tree bares the ID of the object and its nature (plotset or curve) for p in list(plotSets.values()): - item = QtGui.QTreeWidgetItem([str(p.getTitle()), str(p.getID()) + '_set']) + item = QtWidgets.QTreeWidgetItem([str(p.getTitle()), str(p.getID()) + '_set']) treeWidget.addTopLevelItem(item) for c in list(p._curves.values()): - chld = QtGui.QTreeWidgetItem([str(c.getTitle()), str(c.getID()) + '_crv']) + chld = QtWidgets.QTreeWidgetItem([str(c.getTitle()), str(c.getID()) + '_crv']) item.addChild(chld) treeWidget.expandAll() diff --git a/tools/CurvePlot/src/python/views/CurveTabsView.py b/tools/CurvePlot/src/python/views/CurveTabsView.py index 9189239f9..46f8224bf 100644 --- a/tools/CurvePlot/src/python/views/CurveTabsView.py +++ b/tools/CurvePlot/src/python/views/CurveTabsView.py @@ -1,6 +1,6 @@ -from View import View -from XYView import XYView -from utils import Logger +from .View import View +from .XYView import XYView +from .utils import Logger class CurveTabsView(View): def __init__(self, controller): diff --git a/tools/CurvePlot/src/python/views/CurveView.py b/tools/CurvePlot/src/python/views/CurveView.py index 39ae897f1..ddf27334f 100644 --- a/tools/CurvePlot/src/python/views/CurveView.py +++ b/tools/CurvePlot/src/python/views/CurveView.py @@ -1,5 +1,5 @@ -from View import View -from utils import Logger +from .View import View +from .utils import Logger class CurveView(View): _PICKER_PRECISION = 20 #pts diff --git a/tools/CurvePlot/src/python/views/XYView.py b/tools/CurvePlot/src/python/views/XYView.py index ba3ca5731..3c64d7d78 100644 --- a/tools/CurvePlot/src/python/views/XYView.py +++ b/tools/CurvePlot/src/python/views/XYView.py @@ -1,15 +1,15 @@ import matplotlib.pyplot as plt import matplotlib.colors as colors -from View import View -from CurveView import CurveView - -from utils import Logger, trQ -from PlotWidget import PlotWidget -from PlotSettings import PlotSettings -from pyqtside import QtGui, QtCore +from pyqtside import QtWidgets, QtCore from pyqtside.QtCore import QObject from matplotlib.figure import Figure -from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg, NavigationToolbar2QT +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT + +from .View import View +from .CurveView import CurveView +from .PlotWidget import PlotWidget +from .PlotSettings import PlotSettings +from .utils import Logger, trQ class EventHandler(QObject): """ Handle the right-click properly so that it only triggers the contextual menu """ @@ -182,7 +182,7 @@ class XYView(View): self._toolbar = self._plotWidget.toolBar self.populateToolbar() - self._popupMenu = QtGui.QMenu() + self._popupMenu = QtWidgets.QMenu() self._popupMenu.addAction(self._actionLegend) # Connect evenement for the graphic scene @@ -208,7 +208,7 @@ class XYView(View): self._panAction.setCheckable(True) self._toolbar.addSeparator() # Actions to change the representation of curves - self._curveActionGroup = QtGui.QActionGroup(self._plotWidget) + self._curveActionGroup = QtWidgets.QActionGroup(self._plotWidget) self._pointsAction = self.createAndAddLocalAction("draw_points.png", trQ("DRAW_POINTS_TXT")) self._pointsAction.setCheckable(True) self._linesAction = self.createAndAddLocalAction("draw_lines.png", trQ("DRAW_LINES_TXT")) @@ -220,7 +220,7 @@ class XYView(View): self._curveActionGroup.setExclusive(True) self._toolbar.addSeparator() # Actions to draw horizontal curves as linear or logarithmic - self._horActionGroup = QtGui.QActionGroup(self._plotWidget) + self._horActionGroup = QtWidgets.QActionGroup(self._plotWidget) self._horLinearAction = self.createAndAddLocalAction("hor_linear.png", trQ("HOR_LINEAR_TXT")) self._horLinearAction.setCheckable(True) self._horLogarithmicAction = self.createAndAddLocalAction("hor_logarithmic.png", trQ("HOR_LOGARITHMIC_TXT")) @@ -231,7 +231,7 @@ class XYView(View): self._horActionGroup.triggered.connect(self.onViewHorizontalMode) self._toolbar.addSeparator() # Actions to draw vertical curves as linear or logarithmic - self._verActionGroup = QtGui.QActionGroup(self._plotWidget) + self._verActionGroup = QtWidgets.QActionGroup(self._plotWidget) self._verLinearAction = self.createAndAddLocalAction("ver_linear.png", trQ("VER_LINEAR_TXT")) self._verLinearAction.setCheckable(True) self._verLogarithmicAction = self.createAndAddLocalAction("ver_logarithmic.png", trQ("VER_LOGARITHMIC_TXT")) @@ -264,7 +264,7 @@ class XYView(View): filters, trQ("DUMP_VIEW_FILE"), False ) - name = str(fileName) + name = str(fileName[0]) if name != "": self._mplAxes.figure.savefig(name) pass @@ -609,7 +609,7 @@ class XYView(View): if not len(self._curveViews): # Reset color cycle - self._mplAxes.set_color_cycle(None) + self._mplAxes.set_prop_cycle(None) for a in added: self.appendCurve(a) -- 2.39.2