# 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
# ============
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()
# 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<x.y>/...)
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)
# 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 Salome<MODULE>Config.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"
INSTALL(EXPORT ${PROJECT_NAME}TargetGroup DESTINATION "${SALOME_INSTALL_CMAKE_LOCAL}"
FILE ${PROJECT_NAME}Targets.cmake)
ENDIF()
-
+
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@")
# --- 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})
# 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)
-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)
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.
"""
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
: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.
"""
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):
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.
@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)
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
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
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,
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
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.
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)
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.
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
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.
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
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,
raise ValueError("Invalid plot set ID (%d)!" % ps_id)
ps.eraseAll()
return ps_id
-
+
# @classmethod
# def ClearAll(cls):
# # TODO: optimize
# 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.
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
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
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
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
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
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
"""
cps = control._plotManager.getCurrentPlotSet()
if cps is None:
return -1
- return cps.getID()
+ return cps.getID()
@classmethod
def SetCurrentPlotSet(cls, ps_id):
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):
"""
]
@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:
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)
@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:
"""
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.
"""
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.
"""
args, kwargs = [visible], {}
cls.__XYViewOperation("setLegendVisible", ps_id, args, kwargs)
-
+
###
### More advanced functions
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
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)
# 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()
# 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
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):
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
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})
-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):
-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):
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):
-from Model import Model
-from utils import toUnicodeWithWarning
+from .Model import Model
+from .utils import toUnicodeWithWarning
class XYPlotSetModel(Model):
__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})
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 *
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)
--- /dev/null
+from . import _use_pyqt
+if _use_pyqt:
+ from PyQt5.QtWidgets import *
+else:
+ from PySide.QtWidgets import *
"""
-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.
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):
"""
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
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})
#
# 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
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__" :
# 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
# 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.
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
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):
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
"<test_name><suffix>.png"
and the file generated on the fly by taking a snapshot of the widget provided in argument.
"""
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
# 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
-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.
# 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@"
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
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):
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
# 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
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
# 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)
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
def addTab(self):
pass
-# from PyQt4.QtGui import QPushButton
+# from PyQt5.QtWidgets import QPushButton
# self.qp = QPushButton("Hi!")
# self._sgPyQt.createView("Dummy", self.qp)
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)))
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)
#
# 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.
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
###
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])
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())
PlotController.DeletePlotSet(psID2)
self.assertEqual(-1, PlotController.GetCurrentCurveID())
self.assertEqual(-1, PlotController.GetCurrentPlotSetID())
-
+
def testGetPlotSetID(self):
self.showTabWidget()
x, y = self.generateSine()
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"))
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))
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()
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))
clearedID = PlotController.ClearPlotSet(psID)
self.assertEqual(psID, clearedID)
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testCopyCurve(self):
x, y = self.generateSine()
tw = self.showTabWidget()
PlotController.SetCurrentPlotSet(psID)
self.assertNotEqual(crvID, newID)
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testDeleteCurrentItem_curve(self):
x, y = self.generateSine()
tw = self.showTabWidget()
self.assertFalse(b)
self.assertEqual(crvID, anID)
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testDeleteCurrentItem_plotSet(self):
tw = self.showTabWidget()
PlotController.AddPlotSet("tutu")
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()
cID = PlotController.DeleteCurve(crvID)
self.assertEqual(crvID, cID)
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testDeleteCurve2(self):
tw = self.showTabWidget()
x, y = self.generateSine()
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")
psID2 = PlotController.DeletePlotSet(psID)
self.assertEqual(psID2, psID)
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testDeletePlotSet2(self):
tw = self.showTabWidget()
psID1 = PlotController.DeletePlotSet()
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)
psID2 = PlotController.SetCurrentCurve(crvID)
self.assertEqual(psID, psID2)
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testSetCurrentCurve2(self):
tw = self.showTabWidget()
x, y = self.generateSine()
# 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()
PlotController.SetCurrentCurve(crvID)
PlotController.SetCurrentCurve(-1)
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testSetCurrentPlotSet(self):
tw = self.showTabWidget()
psID = PlotController.AddPlotSet("tutu")
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")
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])
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()
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):
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()
PlotController.SetCurrentCurve(crvID)
PlotController.SetCurveLabel(crvID, "tata")
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testToggleXLog(self):
tw = self.showTabWidget()
x, y = self.generateExp()
PlotController.SetYLog(psID, True)
PlotController.SetYSciNotation(psID, True)
self.assertTrue(self.areScreenshotEqual(tw))
-
+
def testSetXSciNotation(self):
tw = self.showTabWidget()
x, y = self.generateSine()
_, 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
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()
PlotController.AddCurve(x, y, append=True)
l, _ = PlotController.GetAllPlotSets()
self.assertEqual(2, len(l))
-
+
def test_onCurrentCurveChange(self):
self.showTabWidget()
x, y = self.generateSine()
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()
# --- 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})
+
-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)
-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()
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]]
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
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()
-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):
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})
-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) :
# 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()
-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):
-from View import View
-from utils import Logger
+from .View import View
+from .utils import Logger
class CurveView(View):
_PICKER_PRECISION = 20 #pts
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 """
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
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"))
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"))
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"))
filters,
trQ("DUMP_VIEW_FILE"),
False )
- name = str(fileName)
+ name = str(fileName[0])
if name != "":
self._mplAxes.figure.savefig(name)
pass
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)