Handle axis SI prefix scaling in MatplotlibExporter (#1282)
* Handle axis SI prefix scaling in MatplotlibExporter * Added some MatplotlibExporter tests and added matplotlib to CI deps * Install mpl with pip instead of conda * Cleanup
This commit is contained in:
parent
96d1ef986f
commit
4110b3e539
@ -106,8 +106,9 @@ jobs:
|
||||
fi
|
||||
conda info
|
||||
conda install $(qt.bindings) numpy scipy pyopengl h5py six --yes --quiet
|
||||
pip install matplotlib
|
||||
else
|
||||
pip install $(qt.bindings) numpy scipy pyopengl h5py six
|
||||
pip install $(qt.bindings) numpy scipy pyopengl h5py six matplotlib
|
||||
fi
|
||||
pip install pytest pytest-cov coverage pytest-xdist
|
||||
if [ $(python.version) == "2.7" ]
|
||||
|
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..Qt import QtGui, QtCore
|
||||
from .Exporter import Exporter
|
||||
from .. import PlotItem
|
||||
@ -31,6 +32,7 @@ publication. Fonts are not vectorized (outlined), and window colors are white.
|
||||
class MatplotlibExporter(Exporter):
|
||||
Name = "Matplotlib Window"
|
||||
windows = []
|
||||
|
||||
def __init__(self, item):
|
||||
Exporter.__init__(self, item)
|
||||
|
||||
@ -55,58 +57,70 @@ class MatplotlibExporter(Exporter):
|
||||
ax.xaxis.set_ticks_position('bottom')
|
||||
|
||||
def export(self, fileName=None):
|
||||
if not isinstance(self.item, PlotItem):
|
||||
raise Exception("MatplotlibExporter currently only works with PlotItem")
|
||||
|
||||
mpw = MatplotlibWindow()
|
||||
MatplotlibExporter.windows.append(mpw)
|
||||
|
||||
fig = mpw.getFigure()
|
||||
|
||||
xax = self.item.getAxis('bottom')
|
||||
yax = self.item.getAxis('left')
|
||||
|
||||
if isinstance(self.item, PlotItem):
|
||||
mpw = MatplotlibWindow()
|
||||
MatplotlibExporter.windows.append(mpw)
|
||||
# get labels from the graphic item
|
||||
xlabel = xax.label.toPlainText()
|
||||
ylabel = yax.label.toPlainText()
|
||||
title = self.item.titleLabel.text
|
||||
|
||||
stdFont = 'Arial'
|
||||
|
||||
fig = mpw.getFigure()
|
||||
|
||||
# get labels from the graphic item
|
||||
xlabel = self.item.axes['bottom']['item'].label.toPlainText()
|
||||
ylabel = self.item.axes['left']['item'].label.toPlainText()
|
||||
title = self.item.titleLabel.text
|
||||
# if axes use autoSIPrefix, scale the data so mpl doesn't add its own
|
||||
# scale factor label
|
||||
xscale = yscale = 1.0
|
||||
if xax.autoSIPrefix:
|
||||
xscale = xax.autoSIPrefixScale
|
||||
if yax.autoSIPrefix:
|
||||
yscale = yax.autoSIPrefixScale
|
||||
|
||||
ax = fig.add_subplot(111, title=title)
|
||||
ax.clear()
|
||||
self.cleanAxes(ax)
|
||||
#ax.grid(True)
|
||||
for item in self.item.curves:
|
||||
x, y = item.getData()
|
||||
opts = item.opts
|
||||
pen = fn.mkPen(opts['pen'])
|
||||
if pen.style() == QtCore.Qt.NoPen:
|
||||
linestyle = ''
|
||||
else:
|
||||
linestyle = '-'
|
||||
color = tuple([c/255. for c in fn.colorTuple(pen.color())])
|
||||
symbol = opts['symbol']
|
||||
if symbol == 't':
|
||||
symbol = '^'
|
||||
symbolPen = fn.mkPen(opts['symbolPen'])
|
||||
symbolBrush = fn.mkBrush(opts['symbolBrush'])
|
||||
markeredgecolor = tuple([c/255. for c in fn.colorTuple(symbolPen.color())])
|
||||
markerfacecolor = tuple([c/255. for c in fn.colorTuple(symbolBrush.color())])
|
||||
markersize = opts['symbolSize']
|
||||
|
||||
if opts['fillLevel'] is not None and opts['fillBrush'] is not None:
|
||||
fillBrush = fn.mkBrush(opts['fillBrush'])
|
||||
fillcolor = tuple([c/255. for c in fn.colorTuple(fillBrush.color())])
|
||||
ax.fill_between(x=x, y1=y, y2=opts['fillLevel'], facecolor=fillcolor)
|
||||
|
||||
pl = ax.plot(x, y, marker=symbol, color=color, linewidth=pen.width(),
|
||||
linestyle=linestyle, markeredgecolor=markeredgecolor, markerfacecolor=markerfacecolor,
|
||||
markersize=markersize)
|
||||
xr, yr = self.item.viewRange()
|
||||
ax.set_xbound(*xr)
|
||||
ax.set_ybound(*yr)
|
||||
ax.set_xlabel(xlabel) # place the labels.
|
||||
ax.set_ylabel(ylabel)
|
||||
mpw.draw()
|
||||
else:
|
||||
raise Exception("Matplotlib export currently only works with plot items")
|
||||
ax = fig.add_subplot(111, title=title)
|
||||
ax.clear()
|
||||
self.cleanAxes(ax)
|
||||
for item in self.item.curves:
|
||||
x, y = item.getData()
|
||||
x = x * xscale
|
||||
y = y * yscale
|
||||
|
||||
opts = item.opts
|
||||
pen = fn.mkPen(opts['pen'])
|
||||
if pen.style() == QtCore.Qt.NoPen:
|
||||
linestyle = ''
|
||||
else:
|
||||
linestyle = '-'
|
||||
color = tuple([c/255. for c in fn.colorTuple(pen.color())])
|
||||
symbol = opts['symbol']
|
||||
if symbol == 't':
|
||||
symbol = '^'
|
||||
symbolPen = fn.mkPen(opts['symbolPen'])
|
||||
symbolBrush = fn.mkBrush(opts['symbolBrush'])
|
||||
markeredgecolor = tuple([c/255. for c in fn.colorTuple(symbolPen.color())])
|
||||
markerfacecolor = tuple([c/255. for c in fn.colorTuple(symbolBrush.color())])
|
||||
markersize = opts['symbolSize']
|
||||
|
||||
if opts['fillLevel'] is not None and opts['fillBrush'] is not None:
|
||||
fillBrush = fn.mkBrush(opts['fillBrush'])
|
||||
fillcolor = tuple([c/255. for c in fn.colorTuple(fillBrush.color())])
|
||||
ax.fill_between(x=x, y1=y, y2=opts['fillLevel'], facecolor=fillcolor)
|
||||
|
||||
pl = ax.plot(x, y, marker=symbol, color=color, linewidth=pen.width(),
|
||||
linestyle=linestyle, markeredgecolor=markeredgecolor, markerfacecolor=markerfacecolor,
|
||||
markersize=markersize)
|
||||
|
||||
xr, yr = self.item.viewRange()
|
||||
ax.set_xbound(xr[0]*xscale, xr[1]*xscale)
|
||||
ax.set_ybound(yr[0]*yscale, yr[1]*yscale)
|
||||
|
||||
ax.set_xlabel(xlabel) # place the labels.
|
||||
ax.set_ylabel(ylabel)
|
||||
mpw.draw()
|
||||
|
||||
MatplotlibExporter.register()
|
||||
|
||||
|
50
pyqtgraph/exporters/tests/test_matplotlib.py
Normal file
50
pyqtgraph/exporters/tests/test_matplotlib.py
Normal file
@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.exporters import MatplotlibExporter
|
||||
pytest.importorskip("matplotlib")
|
||||
|
||||
app = pg.mkQApp()
|
||||
|
||||
|
||||
def test_MatplotlibExporter():
|
||||
plt = pg.plot()
|
||||
|
||||
# curve item
|
||||
plt.plot([0, 1, 2], [0, 1, 2])
|
||||
# scatter item
|
||||
plt.plot([0, 1, 2], [1, 2, 3], pen=None, symbolBrush='r')
|
||||
# curve + scatter
|
||||
plt.plot([0, 1, 2], [2, 3, 4], pen='k', symbolBrush='r')
|
||||
|
||||
exp = MatplotlibExporter(plt.getPlotItem())
|
||||
exp.export()
|
||||
|
||||
|
||||
def test_MatplotlibExporter_nonplotitem():
|
||||
# attempting to export something other than a PlotItem raises an exception
|
||||
plt = pg.plot()
|
||||
plt.plot([0, 1, 2], [2, 3, 4])
|
||||
exp = MatplotlibExporter(plt.getPlotItem().getViewBox())
|
||||
with pytest.raises(Exception):
|
||||
exp.export()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('scale', [1e10, 1e-9])
|
||||
def test_MatplotlibExporter_siscale(scale):
|
||||
# coarse test to verify that plot data is scaled before export when
|
||||
# autoSIPrefix is in effect (so mpl doesn't add its own multiplier label)
|
||||
plt = pg.plot([0, 1, 2], [(i+1)*scale for i in range(3)])
|
||||
# set the label so autoSIPrefix works
|
||||
plt.setLabel('left', 'magnitude')
|
||||
exp = MatplotlibExporter(plt.getPlotItem())
|
||||
exp.export()
|
||||
|
||||
mpw = MatplotlibExporter.windows[-1]
|
||||
fig = mpw.getFigure()
|
||||
ymin, ymax = fig.axes[0].get_ylim()
|
||||
|
||||
if scale < 1:
|
||||
assert ymax > scale
|
||||
else:
|
||||
assert ymax < scale
|
Loading…
Reference in New Issue
Block a user