Source code for PyMca5.PyMcaGui.plotting.PlotWidget
#/*##########################################################################
# Copyright (C) 2004-2015 V.A. Sole, European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
# the ESRF by the Software group.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#############################################################################*/
__author__ = "V.A. Sole - ESRF Data Analysis"
__contact__ = "sole@esrf.fr"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
import sys
import os
from PyMca5.PyMcaGraph import Plot
SVG = True
if "PySide" in sys.modules:
from PySide import QtCore, QtGui
try:
from PySide import QtSvg
except ImportError:
SVG = False
elif ("PyQt5" in sys.modules) or ("PyQt5" in sys.argv):
from PyQt5 import QtCore, QtGui, QtWidgets
QtGui.QApplication = QtWidgets.QApplication
QtGui.QMainWindow = QtWidgets.QMainWindow
QtGui.QWidget = QtWidgets.QWidget
QtGui.QVBoxLayout = QtWidgets.QVBoxLayout
QtGui.qApp = QtWidgets.qApp
try:
from PyQt5 import QtSvg
except ImportError:
SVG = False
else:
from PyQt4 import QtCore, QtGui
try:
from PyQt4 import QtSvg
except ImportError:
SVG = False
if not hasattr(QtCore, "Signal"):
QtCore.Signal = QtCore.pyqtSignal
DEBUG = 0
if DEBUG:
Plot.DEBUG = DEBUG
[docs]class PlotWidget(QtGui.QMainWindow, Plot.Plot):
sigPlotSignal = QtCore.Signal(object)
def __init__(self, parent=None, backend=None,
legends=False, callback=None, **kw):
QtGui.QMainWindow.__init__(self, parent)
Plot.Plot.__init__(self, parent, backend=backend)
if parent is not None:
# behave as a widget
self.setWindowFlags(QtCore.Qt.Widget)
self.containerWidget = QtGui.QWidget()
self.containerWidget.mainLayout = QtGui.QVBoxLayout(self.containerWidget)
self.containerWidget.mainLayout.setContentsMargins(0, 0, 0, 0)
self.containerWidget.mainLayout.setSpacing(0)
widget = self.getWidgetHandle()
if widget is not None:
self.containerWidget.mainLayout.addWidget(widget)
self.setCentralWidget(self.containerWidget)
else:
print("WARNING: No backend. Using default.")
# defaultPrinter
self._printer = None
if legends:
print("Legends widget to be implemented")
self.setGraphTitle(" ")
self.setGraphXLabel("X")
self.setGraphYLabel("Y")
self.setCallback(callback)
[docs] def graphCallback(self, ddict=None):
if ddict is not None:
Plot.Plot.graphCallback(self, ddict)
self.sigPlotSignal.emit(ddict)
#Should I reset the zoom or replot?
#self.resetZoom()
[docs] def replot(self):
Plot.Plot.replot(self)
# force update of the widget!!!
# should this be made at the backend level?
w = self.centralWidget()
QtGui.qApp.postEvent(w, QtGui.QResizeEvent(w.size(),
w.size()))
[docs] def saveGraph(self, fileName, fileFormat=None, dpi=None, **kw):
supportedFormats = ["png", "svg", "pdf", "ps", "eps",
"tif", "tiff","jpeg", "jpg"]
if fileFormat is None:
fileFormat = (fileName.split(".")[-1]).lower()
if fileFormat not in supportedFormats:
print("Probably unsupported format %s" % fileFormat)
fileFormat = "svg"
return super(PlotWidget, self).saveGraph(fileName, fileFormat, dpi=dpi, **kw)
[docs] def getSvgRenderer(self, printer=None):
if not SVG:
raise RuntimeError("QtSvg module missing. Please compile Qt with SVG support")
return
if sys.version < '3.0':
import cStringIO as StringIO
imgData = StringIO.StringIO()
else:
from io import BytesIO
imgData = BytesIO()
self.saveGraph(imgData, fileFormat='svg')
imgData.flush()
imgData.seek(0)
svgRawData = imgData.read()
svgRendererData = QtCore.QXmlStreamReader(svgRawData)
svgRenderer = QtSvg.QSvgRenderer(svgRendererData)
svgRenderer._svgRawData = svgRawData
svgRenderer._svgRendererData = svgRendererData
return svgRenderer
[docs] def printGraph(self, width=None, height=None, xOffset=0.0, yOffset=0.0,
units="inches", dpi=None, printer=None,
dialog=True, keepAspectRatio=True, **kw):
if printer is None:
if self._printer is None:
printer = QtGui.QPrinter()
else:
printer = self._printer
if (printer is None) or dialog:
# allow printer selection/configuration
printDialog = QtGui.QPrintDialog(printer, self)
actualPrint = printDialog.exec_()
else:
actualPrint = True
if actualPrint:
self._printer = printer
try:
painter = QtGui.QPainter()
if not(painter.begin(printer)):
return 0
dpix = printer.logicalDpiX()
dpiy = printer.logicalDpiY()
#margin = int((2/2.54) * dpiy) #2cm margin
availableWidth = printer.width() #- 1 * margin
availableHeight = printer.height() #- 2 * margin
# get the available space
# convert the offsets to dpi
if units.lower() in ['inch', 'inches']:
xOffset = xOffset * dpix
yOffset = yOffset * dpiy
if width is not None:
width = width * dpix
if height is not None:
height = height * dpiy
elif units.lower() in ['cm', 'centimeters']:
xOffset = (xOffset/2.54) * dpix
yOffset = (yOffset/2.54) * dpiy
if width is not None:
width = (width/2.54) * dpix
if height is not None:
height = (height/2.54) * dpiy
else:
# page units
xOffset = availableWidth * xOffset
yOffset = availableHeight * yOffset
if width is not None:
width = availableWidth * width
if height is not None:
height = availableHeight * height
availableWidth -= xOffset
availableHeight -= yOffset
if width is not None:
if (availableWidth + 0.1) < width:
txt = "Available width %f is less than requested width %f" % \
(availableWidth, width)
raise ValueError(txt)
availableWidth = width
if height is not None:
if (availableHeight + 0.1) < height:
txt = "Available height %f is less than requested height %f" % \
(availableHeight, height)
raise ValueError(txt)
availableHeight = height
if keepAspectRatio:
#get the aspect ratio
widget = self.getWidgetHandle()
if widget is None:
# does this make sense?
graphWidth = availableWidth
graphHeight = availableHeight
else:
graphWidth = float(widget.width())
graphHeight = float(widget.height())
graphRatio = graphHeight / graphWidth
# that ratio has to be respected
bodyWidth = availableWidth
bodyHeight = availableWidth * graphRatio
if bodyHeight > availableHeight:
bodyHeight = availableHeight
bodyWidth = bodyHeight / graphRatio
else:
bodyWidth = availableWidth
bodyHeight = availableHeight
body = QtCore.QRectF(xOffset,
yOffset,
bodyWidth,
bodyHeight)
svgRenderer = self.getSvgRenderer()
svgRenderer.render(painter, body)
finally:
painter.end()
if __name__ == "__main__":
import time
backend = None
if ("matplotlib" in sys.argv) or ("mpl" in sys.argv):
backend = "matplotlib"
print("USING matplotlib")
time.sleep(1)
elif ("pyqtgraph" in sys.argv):
backend = "pyqtgraph"
print("USING PyQtGraph")
time.sleep(1)
elif ("OpenGL" in sys.argv) or ("opengl" in sys.argv) or ("gl" in sys.argv):
backend = "opengl"
print("USING OpenGL")
time.sleep(1)
elif ("GLUT" in sys.argv) or ("glut" in sys.argv):
backend = "glut"
print("USING GLUT")
time.sleep(1)
else:
print ("USING default backend")
time.sleep(1)
import numpy
x = numpy.arange(100.)
y = x * x
app = QtGui.QApplication([])
plot = PlotWidget(None, backend=backend, legends=True)
plot.show()
if 1:
plot.addCurve(x, y, "dummy")
plot.addCurve(x+100, x*x)
plot.addCurve(x, -y, "dummy 2")
print("Active curve = ", plot.getActiveCurve())
print("X Limits = ", plot.getGraphXLimits())
print("Y Limits = ", plot.getGraphYLimits())
print("All curves = ", plot.getAllCurves())
#print("REMOVING dummy")
#plot.removeCurve("dummy")
plot.insertXMarker(50., legend="X", text="X", draggable=True)
#plot.insertYMarker(50., draggable=True)
plot.setYAxisLogarithmic(True)
else:
# insert a few curves
cSin={}
cCos={}
nplots=50
for i in range(nplots):
# calculate 3 NumPy arrays
x = numpy.arange(0.0, 10.0, 0.1)
y = 10*numpy.sin(x+(i/10.0) * 3.14)
z = numpy.cos(x+(i/10.0) * 3.14)
#build a key
a="%d" % i
#plot the data
cSin[a] = plot.addCurve(x, y, 'y = sin(x)' + a, replot=False)
cCos[a] = plot.addCurve(x, z, 'y = cos(x)' + a, replot=False)
cCos[a] = plot.addCurve(x, z, 'y = cos(x)' + a, replot=True)
plot.insertXMarker(5., legend="X", text="X", draggable=True)
plot.insertYMarker(5., legend="Y", text="Y", draggable=True)
print("All curves = ", plot.getAllCurves(just_legend=True))
app.exec_()