Static code analysis and corrections
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
import difflib
|
||||
import os
|
||||
|
||||
from matplotlib import cbook
|
||||
from matplotlib.testing import setup
|
||||
|
||||
|
||||
# Check that the test directories exist
|
||||
if not os.path.exists(os.path.join(
|
||||
os.path.dirname(__file__), 'baseline_images')):
|
||||
raise IOError(
|
||||
'The baseline image directory does not exist. '
|
||||
'This is most likely because the test data is not installed. '
|
||||
'You may need to install matplotlib from source to get the '
|
||||
'test data.')
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
from matplotlib.testing.conftest import (mpl_test_settings,
|
||||
mpl_image_comparison_parameters,
|
||||
pytest_configure, pytest_unconfigure,
|
||||
pd)
|
||||
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
from io import BytesIO
|
||||
|
||||
import matplotlib.afm as afm
|
||||
|
||||
|
||||
AFM_TEST_DATA = b"""StartFontMetrics 2.0
|
||||
Comment Comments are ignored.
|
||||
Comment Creation Date:Mon Nov 13 12:34:11 GMT 2017
|
||||
FontName MyFont-Bold
|
||||
EncodingScheme FontSpecific
|
||||
FullName My Font Bold
|
||||
FamilyName Test Fonts
|
||||
Weight Bold
|
||||
ItalicAngle 0.0
|
||||
IsFixedPitch false
|
||||
UnderlinePosition -100
|
||||
UnderlineThickness 50
|
||||
Version 001.000
|
||||
Notice Copyright (c) 2017 No one.
|
||||
FontBBox 0 -321 1234 369
|
||||
StartCharMetrics 3
|
||||
C 0 ; WX 250 ; N space ; B 0 0 0 0 ;
|
||||
C 42 ; WX 1141 ; N foo ; B 40 60 800 360 ;
|
||||
C 99 ; WX 583 ; N bar ; B 40 -10 543 210 ;
|
||||
EndCharMetrics
|
||||
EndFontMetrics
|
||||
"""
|
||||
|
||||
|
||||
def test_nonascii_str():
|
||||
# This tests that we also decode bytes as utf-8 properly.
|
||||
# Else, font files with non ascii characters fail to load.
|
||||
inp_str = "привет"
|
||||
byte_str = inp_str.encode("utf8")
|
||||
|
||||
ret = afm._to_str(byte_str)
|
||||
assert ret == inp_str
|
||||
|
||||
|
||||
def test_parse_header():
|
||||
fh = BytesIO(AFM_TEST_DATA)
|
||||
header = afm._parse_header(fh)
|
||||
assert header == {
|
||||
b'StartFontMetrics': 2.0,
|
||||
b'FontName': 'MyFont-Bold',
|
||||
b'EncodingScheme': 'FontSpecific',
|
||||
b'FullName': 'My Font Bold',
|
||||
b'FamilyName': 'Test Fonts',
|
||||
b'Weight': 'Bold',
|
||||
b'ItalicAngle': 0.0,
|
||||
b'IsFixedPitch': False,
|
||||
b'UnderlinePosition': -100,
|
||||
b'UnderlineThickness': 50,
|
||||
b'Version': '001.000',
|
||||
b'Notice': 'Copyright (c) 2017 No one.',
|
||||
b'FontBBox': [0, -321, 1234, 369],
|
||||
b'StartCharMetrics': 3,
|
||||
}
|
||||
|
||||
|
||||
def test_parse_char_metrics():
|
||||
fh = BytesIO(AFM_TEST_DATA)
|
||||
afm._parse_header(fh) # position
|
||||
metrics = afm._parse_char_metrics(fh)
|
||||
assert metrics == (
|
||||
{0: (250.0, 'space', [0, 0, 0, 0]),
|
||||
42: (1141.0, 'foo', [40, 60, 800, 360]),
|
||||
99: (583.0, 'bar', [40, -10, 543, 210]),
|
||||
},
|
||||
{'space': (250.0, 'space', [0, 0, 0, 0]),
|
||||
'foo': (1141.0, 'foo', [40, 60, 800, 360]),
|
||||
'bar': (583.0, 'bar', [40, -10, 543, 210]),
|
||||
})
|
||||
|
||||
|
||||
def test_get_familyname_guessed():
|
||||
fh = BytesIO(AFM_TEST_DATA)
|
||||
fm = afm.AFM(fh)
|
||||
del fm._header[b'FamilyName'] # remove FamilyName, so we have to guess
|
||||
assert fm.get_familyname() == 'My Font'
|
||||
@@ -0,0 +1,241 @@
|
||||
import io
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_almost_equal
|
||||
import pytest
|
||||
|
||||
from matplotlib import (
|
||||
collections, path, pyplot as plt, transforms as mtransforms, rcParams)
|
||||
from matplotlib.image import imread
|
||||
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
|
||||
def test_repeated_save_with_alpha():
|
||||
# We want an image which has a background color of bluish green, with an
|
||||
# alpha of 0.25.
|
||||
|
||||
fig = Figure([1, 0.4])
|
||||
canvas = FigureCanvas(fig)
|
||||
fig.set_facecolor((0, 1, 0.4))
|
||||
fig.patch.set_alpha(0.25)
|
||||
|
||||
# The target color is fig.patch.get_facecolor()
|
||||
|
||||
buf = io.BytesIO()
|
||||
|
||||
fig.savefig(buf,
|
||||
facecolor=fig.get_facecolor(),
|
||||
edgecolor='none')
|
||||
|
||||
# Save the figure again to check that the
|
||||
# colors don't bleed from the previous renderer.
|
||||
buf.seek(0)
|
||||
fig.savefig(buf,
|
||||
facecolor=fig.get_facecolor(),
|
||||
edgecolor='none')
|
||||
|
||||
# Check the first pixel has the desired color & alpha
|
||||
# (approx: 0, 1.0, 0.4, 0.25)
|
||||
buf.seek(0)
|
||||
assert_array_almost_equal(tuple(imread(buf)[0, 0]),
|
||||
(0.0, 1.0, 0.4, 0.250),
|
||||
decimal=3)
|
||||
|
||||
|
||||
def test_large_single_path_collection():
|
||||
buff = io.BytesIO()
|
||||
|
||||
# Generates a too-large single path in a path collection that
|
||||
# would cause a segfault if the draw_markers optimization is
|
||||
# applied.
|
||||
f, ax = plt.subplots()
|
||||
collection = collections.PathCollection(
|
||||
[path.Path([[-10, 5], [10, 5], [10, -5], [-10, -5], [-10, 5]])])
|
||||
ax.add_artist(collection)
|
||||
ax.set_xlim(10**-3, 1)
|
||||
plt.savefig(buff)
|
||||
|
||||
|
||||
def test_marker_with_nan():
|
||||
# This creates a marker with nans in it, which was segfaulting the
|
||||
# Agg backend (see #3722)
|
||||
fig, ax = plt.subplots(1)
|
||||
steps = 1000
|
||||
data = np.arange(steps)
|
||||
ax.semilogx(data)
|
||||
ax.fill_between(data, data*0.8, data*1.2)
|
||||
buf = io.BytesIO()
|
||||
fig.savefig(buf, format='png')
|
||||
|
||||
|
||||
def test_long_path():
|
||||
buff = io.BytesIO()
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
np.random.seed(0)
|
||||
points = np.random.rand(70000)
|
||||
ax.plot(points)
|
||||
fig.savefig(buff, format='png')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['agg_filter'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_agg_filter():
|
||||
def smooth1d(x, window_len):
|
||||
s = np.r_[2*x[0] - x[window_len:1:-1],
|
||||
x,
|
||||
2*x[-1] - x[-1:-window_len:-1]]
|
||||
w = np.hanning(window_len)
|
||||
y = np.convolve(w/w.sum(), s, mode='same')
|
||||
return y[window_len-1:-window_len+1]
|
||||
|
||||
def smooth2d(A, sigma=3):
|
||||
window_len = max(int(sigma), 3)*2 + 1
|
||||
A1 = np.array([smooth1d(x, window_len) for x in np.asarray(A)])
|
||||
A2 = np.transpose(A1)
|
||||
A3 = np.array([smooth1d(x, window_len) for x in A2])
|
||||
A4 = np.transpose(A3)
|
||||
|
||||
return A4
|
||||
|
||||
class BaseFilter(object):
|
||||
def prepare_image(self, src_image, dpi, pad):
|
||||
ny, nx, depth = src_image.shape
|
||||
padded_src = np.zeros([pad*2 + ny, pad*2 + nx, depth], dtype="d")
|
||||
padded_src[pad:-pad, pad:-pad, :] = src_image[:, :, :]
|
||||
|
||||
return padded_src # , tgt_image
|
||||
|
||||
def get_pad(self, dpi):
|
||||
return 0
|
||||
|
||||
def __call__(self, im, dpi):
|
||||
pad = self.get_pad(dpi)
|
||||
padded_src = self.prepare_image(im, dpi, pad)
|
||||
tgt_image = self.process_image(padded_src, dpi)
|
||||
return tgt_image, -pad, -pad
|
||||
|
||||
class OffsetFilter(BaseFilter):
|
||||
def __init__(self, offsets=None):
|
||||
if offsets is None:
|
||||
self.offsets = (0, 0)
|
||||
else:
|
||||
self.offsets = offsets
|
||||
|
||||
def get_pad(self, dpi):
|
||||
return int(max(*self.offsets)/72.*dpi)
|
||||
|
||||
def process_image(self, padded_src, dpi):
|
||||
ox, oy = self.offsets
|
||||
a1 = np.roll(padded_src, int(ox/72.*dpi), axis=1)
|
||||
a2 = np.roll(a1, -int(oy/72.*dpi), axis=0)
|
||||
return a2
|
||||
|
||||
class GaussianFilter(BaseFilter):
|
||||
"simple gauss filter"
|
||||
|
||||
def __init__(self, sigma, alpha=0.5, color=None):
|
||||
self.sigma = sigma
|
||||
self.alpha = alpha
|
||||
if color is None:
|
||||
self.color = (0, 0, 0)
|
||||
else:
|
||||
self.color = color
|
||||
|
||||
def get_pad(self, dpi):
|
||||
return int(self.sigma*3/72.*dpi)
|
||||
|
||||
def process_image(self, padded_src, dpi):
|
||||
tgt_image = np.zeros_like(padded_src)
|
||||
aa = smooth2d(padded_src[:, :, -1]*self.alpha,
|
||||
self.sigma/72.*dpi)
|
||||
tgt_image[:, :, -1] = aa
|
||||
tgt_image[:, :, :-1] = self.color
|
||||
return tgt_image
|
||||
|
||||
class DropShadowFilter(BaseFilter):
|
||||
def __init__(self, sigma, alpha=0.3, color=None, offsets=None):
|
||||
self.gauss_filter = GaussianFilter(sigma, alpha, color)
|
||||
self.offset_filter = OffsetFilter(offsets)
|
||||
|
||||
def get_pad(self, dpi):
|
||||
return max(self.gauss_filter.get_pad(dpi),
|
||||
self.offset_filter.get_pad(dpi))
|
||||
|
||||
def process_image(self, padded_src, dpi):
|
||||
t1 = self.gauss_filter.process_image(padded_src, dpi)
|
||||
t2 = self.offset_filter.process_image(t1, dpi)
|
||||
return t2
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
|
||||
# draw lines
|
||||
l1, = ax.plot([0.1, 0.5, 0.9], [0.1, 0.9, 0.5], "bo-",
|
||||
mec="b", mfc="w", lw=5, mew=3, ms=10, label="Line 1")
|
||||
l2, = ax.plot([0.1, 0.5, 0.9], [0.5, 0.2, 0.7], "ro-",
|
||||
mec="r", mfc="w", lw=5, mew=3, ms=10, label="Line 1")
|
||||
|
||||
gauss = DropShadowFilter(4)
|
||||
|
||||
for l in [l1, l2]:
|
||||
|
||||
# draw shadows with same lines with slight offset.
|
||||
|
||||
xx = l.get_xdata()
|
||||
yy = l.get_ydata()
|
||||
shadow, = ax.plot(xx, yy)
|
||||
shadow.update_from(l)
|
||||
|
||||
# offset transform
|
||||
ot = mtransforms.offset_copy(l.get_transform(), ax.figure,
|
||||
x=4.0, y=-6.0, units='points')
|
||||
|
||||
shadow.set_transform(ot)
|
||||
|
||||
# adjust zorder of the shadow lines so that it is drawn below the
|
||||
# original lines
|
||||
shadow.set_zorder(l.get_zorder() - 0.5)
|
||||
shadow.set_agg_filter(gauss)
|
||||
shadow.set_rasterized(True) # to support mixed-mode renderers
|
||||
|
||||
ax.set_xlim(0., 1.)
|
||||
ax.set_ylim(0., 1.)
|
||||
|
||||
ax.xaxis.set_visible(False)
|
||||
ax.yaxis.set_visible(False)
|
||||
|
||||
|
||||
def test_too_large_image():
|
||||
fig = plt.figure(figsize=(300, 1000))
|
||||
buff = io.BytesIO()
|
||||
with pytest.raises(ValueError):
|
||||
fig.savefig(buff)
|
||||
|
||||
|
||||
def test_chunksize():
|
||||
x = range(200)
|
||||
|
||||
# Test without chunksize
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, np.sin(x))
|
||||
fig.canvas.draw()
|
||||
|
||||
# Test with chunksize
|
||||
fig, ax = plt.subplots()
|
||||
rcParams['agg.path.chunksize'] = 105
|
||||
ax.plot(x, np.sin(x))
|
||||
fig.canvas.draw()
|
||||
|
||||
|
||||
@pytest.mark.backend('Agg')
|
||||
def test_jpeg_dpi():
|
||||
Image = pytest.importorskip("PIL.Image")
|
||||
# Check that dpi is set correctly in jpg files.
|
||||
plt.plot([0, 1, 2], [0, 1, 0])
|
||||
buf = io.BytesIO()
|
||||
plt.savefig(buf, format="jpg", dpi=200)
|
||||
im = Image.open(buf)
|
||||
assert im.info['dpi'] == (200, 200)
|
||||
@@ -0,0 +1,263 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import matplotlib as mpl
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib import animation
|
||||
|
||||
|
||||
class NullMovieWriter(animation.AbstractMovieWriter):
|
||||
"""
|
||||
A minimal MovieWriter. It doesn't actually write anything.
|
||||
It just saves the arguments that were given to the setup() and
|
||||
grab_frame() methods as attributes, and counts how many times
|
||||
grab_frame() is called.
|
||||
|
||||
This class doesn't have an __init__ method with the appropriate
|
||||
signature, and it doesn't define an isAvailable() method, so
|
||||
it cannot be added to the 'writers' registry.
|
||||
"""
|
||||
|
||||
def setup(self, fig, outfile, dpi, *args):
|
||||
self.fig = fig
|
||||
self.outfile = outfile
|
||||
self.dpi = dpi
|
||||
self.args = args
|
||||
self._count = 0
|
||||
|
||||
def grab_frame(self, **savefig_kwargs):
|
||||
self.savefig_kwargs = savefig_kwargs
|
||||
self._count += 1
|
||||
|
||||
def finish(self):
|
||||
pass
|
||||
|
||||
|
||||
def make_animation(**kwargs):
|
||||
fig, ax = plt.subplots()
|
||||
line, = ax.plot([])
|
||||
|
||||
def init():
|
||||
pass
|
||||
|
||||
def animate(i):
|
||||
line.set_data([0, 1], [0, i])
|
||||
return line,
|
||||
|
||||
return animation.FuncAnimation(fig, animate, **kwargs)
|
||||
|
||||
|
||||
def test_null_movie_writer():
|
||||
# Test running an animation with NullMovieWriter.
|
||||
|
||||
num_frames = 5
|
||||
anim = make_animation(frames=num_frames)
|
||||
|
||||
filename = "unused.null"
|
||||
dpi = 50
|
||||
savefig_kwargs = dict(foo=0)
|
||||
writer = NullMovieWriter()
|
||||
|
||||
anim.save(filename, dpi=dpi, writer=writer,
|
||||
savefig_kwargs=savefig_kwargs)
|
||||
|
||||
assert writer.fig == plt.figure(1) # The figure used by make_animation.
|
||||
assert writer.outfile == filename
|
||||
assert writer.dpi == dpi
|
||||
assert writer.args == ()
|
||||
assert writer.savefig_kwargs == savefig_kwargs
|
||||
assert writer._count == num_frames
|
||||
|
||||
|
||||
def test_movie_writer_dpi_default():
|
||||
# Test setting up movie writer with figure.dpi default.
|
||||
|
||||
fig = plt.figure()
|
||||
|
||||
filename = "unused.null"
|
||||
fps = 5
|
||||
codec = "unused"
|
||||
bitrate = 1
|
||||
extra_args = ["unused"]
|
||||
|
||||
def run():
|
||||
pass
|
||||
|
||||
writer = animation.MovieWriter(fps, codec, bitrate, extra_args)
|
||||
writer._run = run
|
||||
writer.setup(fig, filename)
|
||||
assert writer.dpi == fig.dpi
|
||||
|
||||
|
||||
@animation.writers.register('null')
|
||||
class RegisteredNullMovieWriter(NullMovieWriter):
|
||||
|
||||
# To be able to add NullMovieWriter to the 'writers' registry,
|
||||
# we must define an __init__ method with a specific signature,
|
||||
# and we must define the class method isAvailable().
|
||||
# (These methods are not actually required to use an instance
|
||||
# of this class as the 'writer' argument of Animation.save().)
|
||||
|
||||
def __init__(self, fps=None, codec=None, bitrate=None,
|
||||
extra_args=None, metadata=None):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def isAvailable(cls):
|
||||
return True
|
||||
|
||||
|
||||
WRITER_OUTPUT = [
|
||||
('ffmpeg', 'movie.mp4'),
|
||||
('ffmpeg_file', 'movie.mp4'),
|
||||
('avconv', 'movie.mp4'),
|
||||
('avconv_file', 'movie.mp4'),
|
||||
('imagemagick', 'movie.gif'),
|
||||
('imagemagick_file', 'movie.gif'),
|
||||
('pillow', 'movie.gif'),
|
||||
('html', 'movie.html'),
|
||||
('null', 'movie.null')
|
||||
]
|
||||
if sys.version_info >= (3, 6):
|
||||
from pathlib import Path
|
||||
WRITER_OUTPUT += [
|
||||
(writer, Path(output)) for writer, output in WRITER_OUTPUT]
|
||||
|
||||
|
||||
# Smoke test for saving animations. In the future, we should probably
|
||||
# design more sophisticated tests which compare resulting frames a-la
|
||||
# matplotlib.testing.image_comparison
|
||||
@pytest.mark.parametrize('writer, output', WRITER_OUTPUT)
|
||||
def test_save_animation_smoketest(tmpdir, writer, output):
|
||||
if writer == 'pillow':
|
||||
pytest.importorskip("PIL")
|
||||
try:
|
||||
# for ImageMagick the rcparams must be patched to account for
|
||||
# 'convert' being a built in MS tool, not the imagemagick
|
||||
# tool.
|
||||
writer._init_from_registry()
|
||||
except AttributeError:
|
||||
pass
|
||||
if not animation.writers.is_available(writer):
|
||||
pytest.skip("writer '%s' not available on this system" % writer)
|
||||
fig, ax = plt.subplots()
|
||||
line, = ax.plot([], [])
|
||||
|
||||
ax.set_xlim(0, 10)
|
||||
ax.set_ylim(-1, 1)
|
||||
|
||||
dpi = None
|
||||
codec = None
|
||||
if writer == 'ffmpeg':
|
||||
# Issue #8253
|
||||
fig.set_size_inches((10.85, 9.21))
|
||||
dpi = 100.
|
||||
codec = 'h264'
|
||||
|
||||
def init():
|
||||
line.set_data([], [])
|
||||
return line,
|
||||
|
||||
def animate(i):
|
||||
x = np.linspace(0, 10, 100)
|
||||
y = np.sin(x + i)
|
||||
line.set_data(x, y)
|
||||
return line,
|
||||
|
||||
# Use temporary directory for the file-based writers, which produce a file
|
||||
# per frame with known names.
|
||||
with tmpdir.as_cwd():
|
||||
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=5)
|
||||
try:
|
||||
anim.save(output, fps=30, writer=writer, bitrate=500, dpi=dpi,
|
||||
codec=codec)
|
||||
except UnicodeDecodeError:
|
||||
pytest.xfail("There can be errors in the numpy import stack, "
|
||||
"see issues #1891 and #2679")
|
||||
|
||||
|
||||
def test_no_length_frames():
|
||||
(make_animation(frames=iter(range(5)))
|
||||
.save('unused.null', writer=NullMovieWriter()))
|
||||
|
||||
|
||||
def test_movie_writer_registry():
|
||||
ffmpeg_path = mpl.rcParams['animation.ffmpeg_path']
|
||||
# Not sure about the first state as there could be some writer
|
||||
# which set rcparams
|
||||
# assert not animation.writers._dirty
|
||||
assert len(animation.writers._registered) > 0
|
||||
animation.writers.list() # resets dirty state
|
||||
assert not animation.writers._dirty
|
||||
mpl.rcParams['animation.ffmpeg_path'] = "not_available_ever_xxxx"
|
||||
assert animation.writers._dirty
|
||||
animation.writers.list() # resets
|
||||
assert not animation.writers._dirty
|
||||
assert not animation.writers.is_available("ffmpeg")
|
||||
# something which is guaranteed to be available in path
|
||||
# and exits immediately
|
||||
bin = "true" if sys.platform != 'win32' else "where"
|
||||
mpl.rcParams['animation.ffmpeg_path'] = bin
|
||||
assert animation.writers._dirty
|
||||
animation.writers.list() # resets
|
||||
assert not animation.writers._dirty
|
||||
assert animation.writers.is_available("ffmpeg")
|
||||
mpl.rcParams['animation.ffmpeg_path'] = ffmpeg_path
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not animation.writers.is_available(mpl.rcParams["animation.writer"]),
|
||||
reason="animation writer not installed")
|
||||
@pytest.mark.parametrize("method_name", ["to_html5_video", "to_jshtml"])
|
||||
def test_embed_limit(method_name, caplog, tmpdir):
|
||||
with tmpdir.as_cwd():
|
||||
with mpl.rc_context({"animation.embed_limit": 1e-6}): # ~1 byte.
|
||||
getattr(make_animation(frames=1), method_name)()
|
||||
assert len(caplog.records) == 1
|
||||
record, = caplog.records
|
||||
assert (record.name == "matplotlib.animation"
|
||||
and record.levelname == "WARNING")
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not animation.writers.is_available(mpl.rcParams["animation.writer"]),
|
||||
reason="animation writer not installed")
|
||||
@pytest.mark.parametrize(
|
||||
"method_name",
|
||||
["to_html5_video",
|
||||
pytest.param("to_jshtml",
|
||||
marks=pytest.mark.xfail)])
|
||||
def test_cleanup_temporaries(method_name, tmpdir):
|
||||
with tmpdir.as_cwd():
|
||||
getattr(make_animation(frames=1), method_name)()
|
||||
assert list(Path(str(tmpdir)).iterdir()) == []
|
||||
|
||||
|
||||
# Currently, this fails with a ValueError after we try to communicate() twice
|
||||
# with the Popen.
|
||||
@pytest.mark.xfail
|
||||
@pytest.mark.skipif(os.name != "posix", reason="requires a POSIX OS")
|
||||
def test_failing_ffmpeg(tmpdir, monkeypatch):
|
||||
"""
|
||||
Test that we correctly raise an OSError when ffmpeg fails.
|
||||
|
||||
To do so, mock ffmpeg using a simple executable shell script that
|
||||
succeeds when called with no arguments (so that it gets registered by
|
||||
`isAvailable`), but fails otherwise, and add it to the $PATH.
|
||||
"""
|
||||
try:
|
||||
with tmpdir.as_cwd():
|
||||
monkeypatch.setenv("PATH", ".:" + os.environ["PATH"])
|
||||
exe_path = Path(tmpdir, "ffmpeg")
|
||||
exe_path.write_text("#!/bin/sh\n"
|
||||
"[[ $@ -eq 0 ]]\n")
|
||||
os.chmod(str(exe_path), 0o755)
|
||||
animation.writers.reset_available_writers()
|
||||
with pytest.raises(OSError):
|
||||
make_animation().save("test.mpeg")
|
||||
finally:
|
||||
animation.writers.reset_available_writers()
|
||||
@@ -0,0 +1,170 @@
|
||||
import pytest
|
||||
import platform
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.patches as mpatches
|
||||
|
||||
|
||||
def draw_arrow(ax, t, r):
|
||||
ax.annotate('', xy=(0.5, 0.5 + r), xytext=(0.5, 0.5), size=30,
|
||||
arrowprops=dict(arrowstyle=t,
|
||||
fc="b", ec='k'))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fancyarrow_test_image'])
|
||||
def test_fancyarrow():
|
||||
# Added 0 to test division by zero error described in issue 3930
|
||||
r = [0.4, 0.3, 0.2, 0.1, 0]
|
||||
t = ["fancy", "simple", mpatches.ArrowStyle.Fancy()]
|
||||
|
||||
fig, axes = plt.subplots(len(t), len(r), squeeze=False,
|
||||
subplot_kw=dict(aspect=True),
|
||||
figsize=(8, 4.5))
|
||||
|
||||
for i_r, r1 in enumerate(r):
|
||||
for i_t, t1 in enumerate(t):
|
||||
ax = axes[i_t, i_r]
|
||||
draw_arrow(ax, t1, r1)
|
||||
ax.tick_params(labelleft=False, labelbottom=False)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['boxarrow_test_image'], extensions=['png'])
|
||||
def test_boxarrow():
|
||||
|
||||
styles = mpatches.BoxStyle.get_styles()
|
||||
|
||||
n = len(styles)
|
||||
spacing = 1.2
|
||||
|
||||
figheight = (n * spacing + .5)
|
||||
fig1 = plt.figure(1, figsize=(4 / 1.5, figheight / 1.5))
|
||||
|
||||
fontsize = 0.3 * 72
|
||||
|
||||
for i, stylename in enumerate(sorted(styles)):
|
||||
fig1.text(0.5, ((n - i) * spacing - 0.5)/figheight, stylename,
|
||||
ha="center",
|
||||
size=fontsize,
|
||||
transform=fig1.transFigure,
|
||||
bbox=dict(boxstyle=stylename, fc="w", ec="k"))
|
||||
|
||||
|
||||
def __prepare_fancyarrow_dpi_cor_test():
|
||||
"""
|
||||
Convenience function that prepares and returns a FancyArrowPatch. It aims
|
||||
at being used to test that the size of the arrow head does not depend on
|
||||
the DPI value of the exported picture.
|
||||
|
||||
NB: this function *is not* a test in itself!
|
||||
"""
|
||||
fig2 = plt.figure("fancyarrow_dpi_cor_test", figsize=(4, 3), dpi=50)
|
||||
ax = fig2.add_subplot(111)
|
||||
ax.set_xlim([0, 1])
|
||||
ax.set_ylim([0, 1])
|
||||
ax.add_patch(mpatches.FancyArrowPatch(posA=(0.3, 0.4), posB=(0.8, 0.6),
|
||||
lw=3, arrowstyle='->',
|
||||
mutation_scale=100))
|
||||
return fig2
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fancyarrow_dpi_cor_100dpi'],
|
||||
remove_text=True, extensions=['png'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
savefig_kwarg=dict(dpi=100))
|
||||
def test_fancyarrow_dpi_cor_100dpi():
|
||||
"""
|
||||
Check the export of a FancyArrowPatch @ 100 DPI. FancyArrowPatch is
|
||||
instantiated through a dedicated function because another similar test
|
||||
checks a similar export but with a different DPI value.
|
||||
|
||||
Remark: test only a rasterized format.
|
||||
"""
|
||||
|
||||
__prepare_fancyarrow_dpi_cor_test()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fancyarrow_dpi_cor_200dpi'],
|
||||
remove_text=True, extensions=['png'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
savefig_kwarg=dict(dpi=200))
|
||||
def test_fancyarrow_dpi_cor_200dpi():
|
||||
"""
|
||||
As test_fancyarrow_dpi_cor_100dpi, but exports @ 200 DPI. The relative size
|
||||
of the arrow head should be the same.
|
||||
"""
|
||||
|
||||
__prepare_fancyarrow_dpi_cor_test()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fancyarrow_dash'],
|
||||
remove_text=True, extensions=['png'],
|
||||
style='default')
|
||||
def test_fancyarrow_dash():
|
||||
from matplotlib.patches import FancyArrowPatch
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
e = FancyArrowPatch((0, 0), (0.5, 0.5),
|
||||
arrowstyle='-|>',
|
||||
connectionstyle='angle3,angleA=0,angleB=90',
|
||||
mutation_scale=10.0,
|
||||
linewidth=2,
|
||||
linestyle='dashed',
|
||||
color='k')
|
||||
|
||||
e2 = FancyArrowPatch((0, 0), (0.5, 0.5),
|
||||
arrowstyle='-|>',
|
||||
connectionstyle='angle3',
|
||||
mutation_scale=10.0,
|
||||
linewidth=2,
|
||||
linestyle='dotted',
|
||||
color='k')
|
||||
ax.add_patch(e)
|
||||
ax.add_patch(e2)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['arrow_styles'], extensions=['png'],
|
||||
style='mpl20', remove_text=True)
|
||||
def test_arrow_styles():
|
||||
styles = mpatches.ArrowStyle.get_styles()
|
||||
|
||||
n = len(styles)
|
||||
fig, ax = plt.subplots(figsize=(6, 10))
|
||||
ax.set_xlim(0, 1)
|
||||
ax.set_ylim(-1, n)
|
||||
|
||||
for i, stylename in enumerate(sorted(styles)):
|
||||
patch = mpatches.FancyArrowPatch((0.1, i), (0.8, i),
|
||||
arrowstyle=stylename,
|
||||
mutation_scale=25)
|
||||
ax.add_patch(patch)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['connection_styles'], extensions=['png'],
|
||||
style='mpl20', remove_text=True)
|
||||
def test_connection_styles():
|
||||
styles = mpatches.ConnectionStyle.get_styles()
|
||||
|
||||
n = len(styles)
|
||||
fig, ax = plt.subplots(figsize=(6, 10))
|
||||
ax.set_xlim(0, 1)
|
||||
ax.set_ylim(-1, n)
|
||||
|
||||
for i, stylename in enumerate(sorted(styles)):
|
||||
patch = mpatches.FancyArrowPatch((0.1, i), (0.8, i + 0.5),
|
||||
arrowstyle="->",
|
||||
connectionstyle=stylename,
|
||||
mutation_scale=25)
|
||||
ax.add_patch(patch)
|
||||
|
||||
|
||||
def test_invalid_intersection():
|
||||
conn_style_1 = mpatches.ConnectionStyle.Angle3(angleA=20, angleB=200)
|
||||
p1 = mpatches.FancyArrowPatch((.2, .2), (.5, .5),
|
||||
connectionstyle=conn_style_1)
|
||||
with pytest.raises(ValueError):
|
||||
plt.gca().add_patch(p1)
|
||||
|
||||
conn_style_2 = mpatches.ConnectionStyle.Angle3(angleA=20, angleB=199.9)
|
||||
p2 = mpatches.FancyArrowPatch((.2, .2), (.5, .5),
|
||||
connectionstyle=conn_style_2)
|
||||
plt.gca().add_patch(p2)
|
||||
@@ -0,0 +1,278 @@
|
||||
import io
|
||||
from itertools import chain
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
import pytest
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as mpatches
|
||||
import matplotlib.lines as mlines
|
||||
import matplotlib.path as mpath
|
||||
import matplotlib.transforms as mtransforms
|
||||
import matplotlib.collections as mcollections
|
||||
import matplotlib.artist as martist
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
|
||||
def test_patch_transform_of_none():
|
||||
# tests the behaviour of patches added to an Axes with various transform
|
||||
# specifications
|
||||
|
||||
ax = plt.axes()
|
||||
ax.set_xlim([1, 3])
|
||||
ax.set_ylim([1, 3])
|
||||
|
||||
# Draw an ellipse over data coord (2,2) by specifying device coords.
|
||||
xy_data = (2, 2)
|
||||
xy_pix = ax.transData.transform_point(xy_data)
|
||||
|
||||
# Not providing a transform of None puts the ellipse in data coordinates .
|
||||
e = mpatches.Ellipse(xy_data, width=1, height=1, fc='yellow', alpha=0.5)
|
||||
ax.add_patch(e)
|
||||
assert e._transform == ax.transData
|
||||
|
||||
# Providing a transform of None puts the ellipse in device coordinates.
|
||||
e = mpatches.Ellipse(xy_pix, width=120, height=120, fc='coral',
|
||||
transform=None, alpha=0.5)
|
||||
assert e.is_transform_set() is True
|
||||
ax.add_patch(e)
|
||||
assert isinstance(e._transform, mtransforms.IdentityTransform)
|
||||
|
||||
# Providing an IdentityTransform puts the ellipse in device coordinates.
|
||||
e = mpatches.Ellipse(xy_pix, width=100, height=100,
|
||||
transform=mtransforms.IdentityTransform(), alpha=0.5)
|
||||
ax.add_patch(e)
|
||||
assert isinstance(e._transform, mtransforms.IdentityTransform)
|
||||
|
||||
# Not providing a transform, and then subsequently "get_transform" should
|
||||
# not mean that "is_transform_set".
|
||||
e = mpatches.Ellipse(xy_pix, width=120, height=120, fc='coral',
|
||||
alpha=0.5)
|
||||
intermediate_transform = e.get_transform()
|
||||
assert e.is_transform_set() is False
|
||||
ax.add_patch(e)
|
||||
assert e.get_transform() != intermediate_transform
|
||||
assert e.is_transform_set() is True
|
||||
assert e._transform == ax.transData
|
||||
|
||||
|
||||
def test_collection_transform_of_none():
|
||||
# tests the behaviour of collections added to an Axes with various
|
||||
# transform specifications
|
||||
|
||||
ax = plt.axes()
|
||||
ax.set_xlim([1, 3])
|
||||
ax.set_ylim([1, 3])
|
||||
|
||||
# draw an ellipse over data coord (2,2) by specifying device coords
|
||||
xy_data = (2, 2)
|
||||
xy_pix = ax.transData.transform_point(xy_data)
|
||||
|
||||
# not providing a transform of None puts the ellipse in data coordinates
|
||||
e = mpatches.Ellipse(xy_data, width=1, height=1)
|
||||
c = mcollections.PatchCollection([e], facecolor='yellow', alpha=0.5)
|
||||
ax.add_collection(c)
|
||||
# the collection should be in data coordinates
|
||||
assert c.get_offset_transform() + c.get_transform() == ax.transData
|
||||
|
||||
# providing a transform of None puts the ellipse in device coordinates
|
||||
e = mpatches.Ellipse(xy_pix, width=120, height=120)
|
||||
c = mcollections.PatchCollection([e], facecolor='coral',
|
||||
alpha=0.5)
|
||||
c.set_transform(None)
|
||||
ax.add_collection(c)
|
||||
assert isinstance(c.get_transform(), mtransforms.IdentityTransform)
|
||||
|
||||
# providing an IdentityTransform puts the ellipse in device coordinates
|
||||
e = mpatches.Ellipse(xy_pix, width=100, height=100)
|
||||
c = mcollections.PatchCollection([e],
|
||||
transform=mtransforms.IdentityTransform(),
|
||||
alpha=0.5)
|
||||
ax.add_collection(c)
|
||||
assert isinstance(c._transOffset, mtransforms.IdentityTransform)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=["clip_path_clipping"], remove_text=True)
|
||||
def test_clipping():
|
||||
exterior = mpath.Path.unit_rectangle().deepcopy()
|
||||
exterior.vertices *= 4
|
||||
exterior.vertices -= 2
|
||||
interior = mpath.Path.unit_circle().deepcopy()
|
||||
interior.vertices = interior.vertices[::-1]
|
||||
clip_path = mpath.Path(vertices=np.concatenate([exterior.vertices,
|
||||
interior.vertices]),
|
||||
codes=np.concatenate([exterior.codes,
|
||||
interior.codes]))
|
||||
|
||||
star = mpath.Path.unit_regular_star(6).deepcopy()
|
||||
star.vertices *= 2.6
|
||||
|
||||
ax1 = plt.subplot(121)
|
||||
col = mcollections.PathCollection([star], lw=5, edgecolor='blue',
|
||||
facecolor='red', alpha=0.7, hatch='*')
|
||||
col.set_clip_path(clip_path, ax1.transData)
|
||||
ax1.add_collection(col)
|
||||
|
||||
ax2 = plt.subplot(122, sharex=ax1, sharey=ax1)
|
||||
patch = mpatches.PathPatch(star, lw=5, edgecolor='blue', facecolor='red',
|
||||
alpha=0.7, hatch='*')
|
||||
patch.set_clip_path(clip_path, ax2.transData)
|
||||
ax2.add_patch(patch)
|
||||
|
||||
ax1.set_xlim([-3, 3])
|
||||
ax1.set_ylim([-3, 3])
|
||||
|
||||
|
||||
def test_cull_markers():
|
||||
x = np.random.random(20000)
|
||||
y = np.random.random(20000)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y, 'k.')
|
||||
ax.set_xlim(2, 3)
|
||||
|
||||
pdf = io.BytesIO()
|
||||
fig.savefig(pdf, format="pdf")
|
||||
assert len(pdf.getvalue()) < 8000
|
||||
|
||||
svg = io.BytesIO()
|
||||
fig.savefig(svg, format="svg")
|
||||
assert len(svg.getvalue()) < 20000
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['hatching'], remove_text=True,
|
||||
style='default')
|
||||
def test_hatching():
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
|
||||
# Default hatch color.
|
||||
rect1 = mpatches.Rectangle((0, 0), 3, 4, hatch='/')
|
||||
ax.add_patch(rect1)
|
||||
|
||||
rect2 = mcollections.RegularPolyCollection(4, sizes=[16000],
|
||||
offsets=[(1.5, 6.5)],
|
||||
transOffset=ax.transData,
|
||||
hatch='/')
|
||||
ax.add_collection(rect2)
|
||||
|
||||
# Ensure edge color is not applied to hatching.
|
||||
rect3 = mpatches.Rectangle((4, 0), 3, 4, hatch='/', edgecolor='C1')
|
||||
ax.add_patch(rect3)
|
||||
|
||||
rect4 = mcollections.RegularPolyCollection(4, sizes=[16000],
|
||||
offsets=[(5.5, 6.5)],
|
||||
transOffset=ax.transData,
|
||||
hatch='/', edgecolor='C1')
|
||||
ax.add_collection(rect4)
|
||||
|
||||
ax.set_xlim(0, 7)
|
||||
ax.set_ylim(0, 9)
|
||||
|
||||
|
||||
def test_remove():
|
||||
fig, ax = plt.subplots()
|
||||
im = ax.imshow(np.arange(36).reshape(6, 6))
|
||||
ln, = ax.plot(range(5))
|
||||
|
||||
assert fig.stale
|
||||
assert ax.stale
|
||||
|
||||
fig.canvas.draw()
|
||||
assert not fig.stale
|
||||
assert not ax.stale
|
||||
assert not ln.stale
|
||||
|
||||
assert im in ax._mouseover_set
|
||||
assert ln not in ax._mouseover_set
|
||||
assert im.axes is ax
|
||||
|
||||
im.remove()
|
||||
ln.remove()
|
||||
|
||||
for art in [im, ln]:
|
||||
assert art.axes is None
|
||||
assert art.figure is None
|
||||
|
||||
assert im not in ax._mouseover_set
|
||||
assert fig.stale
|
||||
assert ax.stale
|
||||
|
||||
|
||||
@image_comparison(baseline_images=["default_edges"], remove_text=True,
|
||||
extensions=['png'], style='default')
|
||||
def test_default_edges():
|
||||
fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2)
|
||||
|
||||
ax1.plot(np.arange(10), np.arange(10), 'x',
|
||||
np.arange(10) + 1, np.arange(10), 'o')
|
||||
ax2.bar(np.arange(10), np.arange(10), align='edge')
|
||||
ax3.text(0, 0, "BOX", size=24, bbox=dict(boxstyle='sawtooth'))
|
||||
ax3.set_xlim((-1, 1))
|
||||
ax3.set_ylim((-1, 1))
|
||||
pp1 = mpatches.PathPatch(
|
||||
mpath.Path([(0, 0), (1, 0), (1, 1), (0, 0)],
|
||||
[mpath.Path.MOVETO, mpath.Path.CURVE3,
|
||||
mpath.Path.CURVE3, mpath.Path.CLOSEPOLY]),
|
||||
fc="none", transform=ax4.transData)
|
||||
ax4.add_patch(pp1)
|
||||
|
||||
|
||||
def test_properties():
|
||||
ln = mlines.Line2D([], [])
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
# Cause all warnings to always be triggered.
|
||||
warnings.simplefilter("always")
|
||||
ln.properties()
|
||||
assert len(w) == 0
|
||||
|
||||
|
||||
def test_setp():
|
||||
# Check empty list
|
||||
plt.setp([])
|
||||
plt.setp([[]])
|
||||
|
||||
# Check arbitrary iterables
|
||||
fig, axes = plt.subplots()
|
||||
lines1 = axes.plot(range(3))
|
||||
lines2 = axes.plot(range(3))
|
||||
martist.setp(chain(lines1, lines2), 'lw', 5)
|
||||
plt.setp(axes.spines.values(), color='green')
|
||||
|
||||
# Check `file` argument
|
||||
sio = io.StringIO()
|
||||
plt.setp(lines1, 'zorder', file=sio)
|
||||
assert sio.getvalue() == ' zorder: float\n'
|
||||
|
||||
|
||||
def test_None_zorder():
|
||||
fig, ax = plt.subplots()
|
||||
ln, = ax.plot(range(5), zorder=None)
|
||||
assert ln.get_zorder() == mlines.Line2D.zorder
|
||||
ln.set_zorder(123456)
|
||||
assert ln.get_zorder() == 123456
|
||||
ln.set_zorder(None)
|
||||
assert ln.get_zorder() == mlines.Line2D.zorder
|
||||
|
||||
|
||||
@pytest.mark.parametrize('accept_clause, expected', [
|
||||
('', 'unknown'),
|
||||
("ACCEPTS: [ '-' | '--' | '-.' ]", "[ '-' | '--' | '-.' ] "),
|
||||
('ACCEPTS: Some description.', 'Some description. '),
|
||||
('.. ACCEPTS: Some description.', 'Some description. '),
|
||||
('arg : int', 'int'),
|
||||
('arg : int\nACCEPTS: Something else.', 'Something else. '),
|
||||
])
|
||||
def test_artist_inspector_get_valid_values(accept_clause, expected):
|
||||
class TestArtist(martist.Artist):
|
||||
def set_f(self, arg):
|
||||
pass
|
||||
|
||||
TestArtist.set_f.__doc__ = """
|
||||
Some text.
|
||||
|
||||
%s
|
||||
""" % accept_clause
|
||||
valid_values = martist.ArtistInspector(TestArtist).get_valid_values('f')
|
||||
assert valid_values == expected
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,95 @@
|
||||
from matplotlib.backend_bases import (
|
||||
FigureCanvasBase, LocationEvent, RendererBase)
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.transforms as transforms
|
||||
import matplotlib.path as path
|
||||
import os
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
|
||||
def test_uses_per_path():
|
||||
id = transforms.Affine2D()
|
||||
paths = [path.Path.unit_regular_polygon(i) for i in range(3, 7)]
|
||||
tforms = [id.rotate(i) for i in range(1, 5)]
|
||||
offsets = np.arange(20).reshape((10, 2))
|
||||
facecolors = ['red', 'green']
|
||||
edgecolors = ['red', 'green']
|
||||
|
||||
def check(master_transform, paths, all_transforms,
|
||||
offsets, facecolors, edgecolors):
|
||||
rb = RendererBase()
|
||||
raw_paths = list(rb._iter_collection_raw_paths(
|
||||
master_transform, paths, all_transforms))
|
||||
gc = rb.new_gc()
|
||||
ids = [path_id for xo, yo, path_id, gc0, rgbFace in
|
||||
rb._iter_collection(gc, master_transform, all_transforms,
|
||||
range(len(raw_paths)), offsets,
|
||||
transforms.IdentityTransform(),
|
||||
facecolors, edgecolors, [], [], [False],
|
||||
[], 'data')]
|
||||
uses = rb._iter_collection_uses_per_path(
|
||||
paths, all_transforms, offsets, facecolors, edgecolors)
|
||||
if raw_paths:
|
||||
seen = np.bincount(ids, minlength=len(raw_paths))
|
||||
assert set(seen).issubset([uses - 1, uses])
|
||||
|
||||
check(id, paths, tforms, offsets, facecolors, edgecolors)
|
||||
check(id, paths[0:1], tforms, offsets, facecolors, edgecolors)
|
||||
check(id, [], tforms, offsets, facecolors, edgecolors)
|
||||
check(id, paths, tforms[0:1], offsets, facecolors, edgecolors)
|
||||
check(id, paths, [], offsets, facecolors, edgecolors)
|
||||
for n in range(0, offsets.shape[0]):
|
||||
check(id, paths, tforms, offsets[0:n, :], facecolors, edgecolors)
|
||||
check(id, paths, tforms, offsets, [], edgecolors)
|
||||
check(id, paths, tforms, offsets, facecolors, [])
|
||||
check(id, paths, tforms, offsets, [], [])
|
||||
check(id, paths, tforms, offsets, facecolors[0:1], edgecolors)
|
||||
|
||||
|
||||
def test_get_default_filename(tmpdir):
|
||||
plt.rcParams['savefig.directory'] = str(tmpdir)
|
||||
fig = plt.figure()
|
||||
canvas = FigureCanvasBase(fig)
|
||||
filename = canvas.get_default_filename()
|
||||
assert filename == 'image.png'
|
||||
|
||||
|
||||
@pytest.mark.backend('pdf')
|
||||
def test_non_gui_warning(monkeypatch):
|
||||
plt.subplots()
|
||||
|
||||
monkeypatch.setitem(os.environ, "DISPLAY", ":999")
|
||||
|
||||
with pytest.warns(UserWarning) as rec:
|
||||
plt.show()
|
||||
assert len(rec) == 1
|
||||
assert ('Matplotlib is currently using pdf, which is a non-GUI backend'
|
||||
in str(rec[0].message))
|
||||
|
||||
with pytest.warns(UserWarning) as rec:
|
||||
plt.gcf().show()
|
||||
assert len(rec) == 1
|
||||
assert ('Matplotlib is currently using pdf, which is a non-GUI backend'
|
||||
in str(rec[0].message))
|
||||
|
||||
|
||||
def test_location_event_position():
|
||||
# LocationEvent should cast its x and y arguments
|
||||
# to int unless it is None
|
||||
fig = plt.figure()
|
||||
canvas = FigureCanvasBase(fig)
|
||||
test_positions = [(42, 24), (None, 42), (None, None),
|
||||
(200, 100.01), (205.75, 2.0)]
|
||||
for x, y in test_positions:
|
||||
event = LocationEvent("test_event", canvas, x, y)
|
||||
if x is None:
|
||||
assert event.x is None
|
||||
else:
|
||||
assert event.x == int(x)
|
||||
assert isinstance(event.x, int)
|
||||
if y is None:
|
||||
assert event.y is None
|
||||
else:
|
||||
assert event.y == int(y)
|
||||
assert isinstance(event.y, int)
|
||||
@@ -0,0 +1,35 @@
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
nbformat = pytest.importorskip('nbformat')
|
||||
|
||||
# From https://blog.thedataincubator.com/2016/06/testing-jupyter-notebooks/
|
||||
|
||||
|
||||
def _notebook_run(nb_file):
|
||||
"""Execute a notebook via nbconvert and collect output.
|
||||
:returns (parsed nb object, execution errors)
|
||||
"""
|
||||
with tempfile.NamedTemporaryFile(suffix=".ipynb",
|
||||
mode='w+t') as fout:
|
||||
args = ["jupyter", "nbconvert", "--to", "notebook", "--execute",
|
||||
"--ExecutePreprocessor.timeout=500",
|
||||
"--output", fout.name, nb_file]
|
||||
subprocess.check_call(args)
|
||||
|
||||
fout.seek(0)
|
||||
nb = nbformat.read(fout, nbformat.current_nbformat)
|
||||
|
||||
errors = [output for cell in nb.cells if "outputs" in cell
|
||||
for output in cell["outputs"]
|
||||
if output.output_type == "error"]
|
||||
return nb, errors
|
||||
|
||||
|
||||
def test_ipynb():
|
||||
nb, errors = _notebook_run(
|
||||
str(Path(__file__).parent / 'test_nbagg_01.ipynb'))
|
||||
assert errors == []
|
||||
@@ -0,0 +1,241 @@
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib import dviread, pyplot as plt, checkdep_usetex, rcParams
|
||||
from matplotlib.backends.backend_pdf import PdfPages
|
||||
from matplotlib.testing.compare import compare_images
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
from matplotlib.testing.determinism import (_determinism_source_date_epoch,
|
||||
_determinism_check)
|
||||
|
||||
|
||||
needs_usetex = pytest.mark.skipif(
|
||||
not checkdep_usetex(True),
|
||||
reason="This test needs a TeX installation")
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['pdf_use14corefonts'],
|
||||
extensions=['pdf'])
|
||||
def test_use14corefonts():
|
||||
rcParams['pdf.use14corefonts'] = True
|
||||
rcParams['font.family'] = 'sans-serif'
|
||||
rcParams['font.size'] = 8
|
||||
rcParams['font.sans-serif'] = ['Helvetica']
|
||||
rcParams['pdf.compression'] = 0
|
||||
|
||||
text = '''A three-line text positioned just above a blue line
|
||||
and containing some French characters and the euro symbol:
|
||||
"Merci pépé pour les 10 €"'''
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.set_title('Test PDF backend with option use14corefonts=True')
|
||||
ax.text(0.5, 0.5, text, horizontalalignment='center',
|
||||
verticalalignment='bottom',
|
||||
fontsize=14)
|
||||
ax.axhline(0.5, linewidth=0.5)
|
||||
|
||||
|
||||
def test_type42():
|
||||
rcParams['pdf.fonttype'] = 42
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.plot([1, 2, 3])
|
||||
fig.savefig(io.BytesIO())
|
||||
|
||||
|
||||
def test_multipage_pagecount():
|
||||
with PdfPages(io.BytesIO()) as pdf:
|
||||
assert pdf.get_pagecount() == 0
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.plot([1, 2, 3])
|
||||
fig.savefig(pdf, format="pdf")
|
||||
assert pdf.get_pagecount() == 1
|
||||
pdf.savefig()
|
||||
assert pdf.get_pagecount() == 2
|
||||
|
||||
|
||||
def test_multipage_properfinalize():
|
||||
pdfio = io.BytesIO()
|
||||
with PdfPages(pdfio) as pdf:
|
||||
for i in range(10):
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.set_title('This is a long title')
|
||||
fig.savefig(pdf, format="pdf")
|
||||
pdfio.seek(0)
|
||||
assert sum(b'startxref' in line for line in pdfio) == 1
|
||||
assert sys.getsizeof(pdfio) < 40000
|
||||
|
||||
|
||||
def test_multipage_keep_empty():
|
||||
from matplotlib.backends.backend_pdf import PdfPages
|
||||
from tempfile import NamedTemporaryFile
|
||||
# test empty pdf files
|
||||
# test that an empty pdf is left behind with keep_empty=True (default)
|
||||
with NamedTemporaryFile(delete=False) as tmp:
|
||||
with PdfPages(tmp) as pdf:
|
||||
filename = pdf._file.fh.name
|
||||
assert os.path.exists(filename)
|
||||
os.remove(filename)
|
||||
# test if an empty pdf is deleting itself afterwards with keep_empty=False
|
||||
with PdfPages(filename, keep_empty=False) as pdf:
|
||||
pass
|
||||
assert not os.path.exists(filename)
|
||||
# test pdf files with content, they should never be deleted
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.plot([1, 2, 3])
|
||||
# test that a non-empty pdf is left behind with keep_empty=True (default)
|
||||
with NamedTemporaryFile(delete=False) as tmp:
|
||||
with PdfPages(tmp) as pdf:
|
||||
filename = pdf._file.fh.name
|
||||
pdf.savefig()
|
||||
assert os.path.exists(filename)
|
||||
os.remove(filename)
|
||||
# test that a non-empty pdf is left behind with keep_empty=False
|
||||
with NamedTemporaryFile(delete=False) as tmp:
|
||||
with PdfPages(tmp, keep_empty=False) as pdf:
|
||||
filename = pdf._file.fh.name
|
||||
pdf.savefig()
|
||||
assert os.path.exists(filename)
|
||||
os.remove(filename)
|
||||
|
||||
|
||||
def test_composite_image():
|
||||
# Test that figures can be saved with and without combining multiple images
|
||||
# (on a single set of axes) into a single composite image.
|
||||
X, Y = np.meshgrid(np.arange(-5, 5, 1), np.arange(-5, 5, 1))
|
||||
Z = np.sin(Y ** 2)
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.set_xlim(0, 3)
|
||||
ax.imshow(Z, extent=[0, 1, 0, 1])
|
||||
ax.imshow(Z[::-1], extent=[2, 3, 0, 1])
|
||||
plt.rcParams['image.composite_image'] = True
|
||||
with PdfPages(io.BytesIO()) as pdf:
|
||||
fig.savefig(pdf, format="pdf")
|
||||
assert len(pdf._file._images) == 1
|
||||
plt.rcParams['image.composite_image'] = False
|
||||
with PdfPages(io.BytesIO()) as pdf:
|
||||
fig.savefig(pdf, format="pdf")
|
||||
assert len(pdf._file._images) == 2
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires Python 3.6+")
|
||||
def test_pdfpages_fspath():
|
||||
from pathlib import Path
|
||||
with PdfPages(Path(os.devnull)) as pdf:
|
||||
pdf.savefig(plt.figure())
|
||||
|
||||
|
||||
def test_source_date_epoch():
|
||||
"""Test SOURCE_DATE_EPOCH support for PDF output"""
|
||||
_determinism_source_date_epoch("pdf", b"/CreationDate (D:20000101000000Z)")
|
||||
|
||||
|
||||
def test_determinism_plain():
|
||||
"""Test for reproducible PDF output: simple figure"""
|
||||
_determinism_check('', format="pdf")
|
||||
|
||||
|
||||
def test_determinism_images():
|
||||
"""Test for reproducible PDF output: figure with different images"""
|
||||
_determinism_check('i', format="pdf")
|
||||
|
||||
|
||||
def test_determinism_hatches():
|
||||
"""Test for reproducible PDF output: figure with different hatches"""
|
||||
_determinism_check('h', format="pdf")
|
||||
|
||||
|
||||
def test_determinism_markers():
|
||||
"""Test for reproducible PDF output: figure with different markers"""
|
||||
_determinism_check('m', format="pdf")
|
||||
|
||||
|
||||
def test_determinism_all():
|
||||
"""Test for reproducible PDF output"""
|
||||
_determinism_check(format="pdf")
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['hatching_legend'],
|
||||
extensions=['pdf'])
|
||||
def test_hatching_legend():
|
||||
"""Test for correct hatching on patches in legend"""
|
||||
fig = plt.figure(figsize=(1, 2))
|
||||
|
||||
a = plt.Rectangle([0, 0], 0, 0, facecolor="green", hatch="XXXX")
|
||||
b = plt.Rectangle([0, 0], 0, 0, facecolor="blue", hatch="XXXX")
|
||||
|
||||
fig.legend([a, b, a, b], ["", "", "", ""])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['grayscale_alpha'],
|
||||
extensions=['pdf'])
|
||||
def test_grayscale_alpha():
|
||||
"""Masking images with NaN did not work for grayscale images"""
|
||||
x, y = np.ogrid[-2:2:.1, -2:2:.1]
|
||||
dd = np.exp(-(x**2 + y**2))
|
||||
dd[dd < .1] = np.nan
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(dd, interpolation='none', cmap='gray_r')
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
|
||||
|
||||
# This tests tends to hit a TeX cache lock on AppVeyor.
|
||||
@pytest.mark.flaky(reruns=3)
|
||||
@needs_usetex
|
||||
def test_missing_psfont(monkeypatch):
|
||||
"""An error is raised if a TeX font lacks a Type-1 equivalent"""
|
||||
def psfont(*args, **kwargs):
|
||||
return dviread.PsFont(texname='texfont', psname='Some Font',
|
||||
effects=None, encoding=None, filename=None)
|
||||
|
||||
monkeypatch.setattr(dviread.PsfontsMap, '__getitem__', psfont)
|
||||
rcParams['text.usetex'] = True
|
||||
fig, ax = plt.subplots()
|
||||
ax.text(0.5, 0.5, 'hello')
|
||||
with tempfile.TemporaryFile() as tmpfile, pytest.raises(ValueError):
|
||||
fig.savefig(tmpfile, format='pdf')
|
||||
|
||||
|
||||
@pytest.mark.style('default')
|
||||
def test_pdf_savefig_when_color_is_none(tmpdir):
|
||||
fig, ax = plt.subplots()
|
||||
plt.axis('off')
|
||||
ax.plot(np.sin(np.linspace(-5, 5, 100)), 'v', c='none')
|
||||
actual_image = tmpdir.join('figure.pdf')
|
||||
expected_image = tmpdir.join('figure.eps')
|
||||
fig.savefig(str(actual_image), format='pdf')
|
||||
fig.savefig(str(expected_image), format='eps')
|
||||
result = compare_images(str(actual_image), str(expected_image), 0)
|
||||
assert result is None
|
||||
|
||||
|
||||
@needs_usetex
|
||||
def test_failing_latex(tmpdir):
|
||||
"""Test failing latex subprocess call"""
|
||||
path = str(tmpdir.join("tmpoutput.pdf"))
|
||||
|
||||
rcParams['text.usetex'] = True
|
||||
|
||||
# This fails with "Double subscript"
|
||||
plt.xlabel("$22_2_2$")
|
||||
with pytest.raises(RuntimeError):
|
||||
plt.savefig(path)
|
||||
|
||||
|
||||
def test_empty_rasterized():
|
||||
# Check that emtpy figures that are rasterised save to pdf files fine
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot([], [], rasterized=True)
|
||||
fig.savefig(io.BytesIO(), format="pdf")
|
||||
@@ -0,0 +1,272 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import matplotlib as mpl
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.compare import compare_images, ImageComparisonFailure
|
||||
from matplotlib.testing.decorators import image_comparison, _image_directories
|
||||
from matplotlib.backends.backend_pgf import PdfPages
|
||||
|
||||
baseline_dir, result_dir = _image_directories(lambda: 'dummy func')
|
||||
|
||||
|
||||
def check_for(texsystem):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
tex_path = Path(tmpdir, "test.tex")
|
||||
tex_path.write_text(r"""
|
||||
\documentclass{minimal}
|
||||
\usepackage{pgf}
|
||||
\begin{document}
|
||||
\typeout{pgfversion=\pgfversion}
|
||||
\makeatletter
|
||||
\@@end
|
||||
""")
|
||||
try:
|
||||
subprocess.check_call(
|
||||
[texsystem, "-halt-on-error", str(tex_path)], cwd=tmpdir,
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
needs_xelatex = pytest.mark.skipif(not check_for('xelatex'),
|
||||
reason='xelatex + pgf is required')
|
||||
needs_pdflatex = pytest.mark.skipif(not check_for('pdflatex'),
|
||||
reason='pdflatex + pgf is required')
|
||||
needs_lualatex = pytest.mark.skipif(not check_for('lualatex'),
|
||||
reason='lualatex + pgf is required')
|
||||
|
||||
|
||||
def compare_figure(fname, savefig_kwargs={}, tol=0):
|
||||
actual = os.path.join(result_dir, fname)
|
||||
plt.savefig(actual, **savefig_kwargs)
|
||||
|
||||
expected = os.path.join(result_dir, "expected_%s" % fname)
|
||||
shutil.copyfile(os.path.join(baseline_dir, fname), expected)
|
||||
err = compare_images(expected, actual, tol=tol)
|
||||
if err:
|
||||
raise ImageComparisonFailure(err)
|
||||
|
||||
|
||||
def create_figure():
|
||||
plt.figure()
|
||||
x = np.linspace(0, 1, 15)
|
||||
|
||||
# line plot
|
||||
plt.plot(x, x ** 2, "b-")
|
||||
|
||||
# marker
|
||||
plt.plot(x, 1 - x**2, "g>")
|
||||
|
||||
# filled paths and patterns
|
||||
plt.fill_between([0., .4], [.4, 0.], hatch='//', facecolor="lightgray",
|
||||
edgecolor="red")
|
||||
plt.fill([3, 3, .8, .8, 3], [2, -2, -2, 0, 2], "b")
|
||||
|
||||
# text and typesetting
|
||||
plt.plot([0.9], [0.5], "ro", markersize=3)
|
||||
plt.text(0.9, 0.5, 'unicode (ü, °, µ) and math ($\\mu_i = x_i^2$)',
|
||||
ha='right', fontsize=20)
|
||||
plt.ylabel('sans-serif, blue, $\\frac{\\sqrt{x}}{y^2}$..',
|
||||
family='sans-serif', color='blue')
|
||||
|
||||
plt.xlim(0, 1)
|
||||
plt.ylim(0, 1)
|
||||
|
||||
|
||||
# test compiling a figure to pdf with xelatex
|
||||
@needs_xelatex
|
||||
@pytest.mark.backend('pgf')
|
||||
@image_comparison(baseline_images=['pgf_xelatex'], extensions=['pdf'],
|
||||
style='default')
|
||||
def test_xelatex():
|
||||
rc_xelatex = {'font.family': 'serif',
|
||||
'pgf.rcfonts': False}
|
||||
mpl.rcParams.update(rc_xelatex)
|
||||
create_figure()
|
||||
|
||||
|
||||
# test compiling a figure to pdf with pdflatex
|
||||
@needs_pdflatex
|
||||
@pytest.mark.backend('pgf')
|
||||
@image_comparison(baseline_images=['pgf_pdflatex'], extensions=['pdf'],
|
||||
style='default')
|
||||
def test_pdflatex():
|
||||
if os.environ.get('APPVEYOR', False):
|
||||
pytest.xfail("pdflatex test does not work on appveyor due to missing "
|
||||
"LaTeX fonts")
|
||||
|
||||
rc_pdflatex = {'font.family': 'serif',
|
||||
'pgf.rcfonts': False,
|
||||
'pgf.texsystem': 'pdflatex',
|
||||
'pgf.preamble': ['\\usepackage[utf8x]{inputenc}',
|
||||
'\\usepackage[T1]{fontenc}']}
|
||||
mpl.rcParams.update(rc_pdflatex)
|
||||
create_figure()
|
||||
|
||||
|
||||
# test updating the rc parameters for each figure
|
||||
@needs_xelatex
|
||||
@needs_pdflatex
|
||||
@pytest.mark.style('default')
|
||||
@pytest.mark.backend('pgf')
|
||||
def test_rcupdate():
|
||||
rc_sets = [{'font.family': 'sans-serif',
|
||||
'font.size': 30,
|
||||
'figure.subplot.left': .2,
|
||||
'lines.markersize': 10,
|
||||
'pgf.rcfonts': False,
|
||||
'pgf.texsystem': 'xelatex'},
|
||||
{'font.family': 'monospace',
|
||||
'font.size': 10,
|
||||
'figure.subplot.left': .1,
|
||||
'lines.markersize': 20,
|
||||
'pgf.rcfonts': False,
|
||||
'pgf.texsystem': 'pdflatex',
|
||||
'pgf.preamble': ['\\usepackage[utf8x]{inputenc}',
|
||||
'\\usepackage[T1]{fontenc}',
|
||||
'\\usepackage{sfmath}']}]
|
||||
tol = [6, 0]
|
||||
for i, rc_set in enumerate(rc_sets):
|
||||
with mpl.rc_context(rc_set):
|
||||
create_figure()
|
||||
compare_figure('pgf_rcupdate%d.pdf' % (i + 1), tol=tol[i])
|
||||
|
||||
|
||||
# test backend-side clipping, since large numbers are not supported by TeX
|
||||
@needs_xelatex
|
||||
@pytest.mark.style('default')
|
||||
@pytest.mark.backend('pgf')
|
||||
def test_pathclip():
|
||||
rc_xelatex = {'font.family': 'serif',
|
||||
'pgf.rcfonts': False}
|
||||
mpl.rcParams.update(rc_xelatex)
|
||||
|
||||
plt.figure()
|
||||
plt.plot([0., 1e100], [0., 1e100])
|
||||
plt.xlim(0, 1)
|
||||
plt.ylim(0, 1)
|
||||
# this test passes if compiling/saving to pdf works (no image comparison)
|
||||
plt.savefig(os.path.join(result_dir, "pgf_pathclip.pdf"))
|
||||
|
||||
|
||||
# test mixed mode rendering
|
||||
@needs_xelatex
|
||||
@pytest.mark.backend('pgf')
|
||||
@image_comparison(baseline_images=['pgf_mixedmode'], extensions=['pdf'],
|
||||
style='default')
|
||||
def test_mixedmode():
|
||||
rc_xelatex = {'font.family': 'serif',
|
||||
'pgf.rcfonts': False}
|
||||
mpl.rcParams.update(rc_xelatex)
|
||||
|
||||
Y, X = np.ogrid[-1:1:40j, -1:1:40j]
|
||||
plt.figure()
|
||||
plt.pcolor(X**2 + Y**2).set_rasterized(True)
|
||||
|
||||
|
||||
# test bbox_inches clipping
|
||||
@needs_xelatex
|
||||
@pytest.mark.style('default')
|
||||
@pytest.mark.backend('pgf')
|
||||
def test_bbox_inches():
|
||||
rc_xelatex = {'font.family': 'serif',
|
||||
'pgf.rcfonts': False}
|
||||
mpl.rcParams.update(rc_xelatex)
|
||||
|
||||
Y, X = np.ogrid[-1:1:40j, -1:1:40j]
|
||||
fig = plt.figure()
|
||||
ax1 = fig.add_subplot(121)
|
||||
ax1.plot(range(5))
|
||||
ax2 = fig.add_subplot(122)
|
||||
ax2.plot(range(5))
|
||||
plt.tight_layout()
|
||||
|
||||
bbox = ax1.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
|
||||
compare_figure('pgf_bbox_inches.pdf', savefig_kwargs={'bbox_inches': bbox},
|
||||
tol=0)
|
||||
|
||||
|
||||
@needs_pdflatex
|
||||
@pytest.mark.style('default')
|
||||
@pytest.mark.backend('pgf')
|
||||
def test_pdf_pages():
|
||||
rc_pdflatex = {
|
||||
'font.family': 'serif',
|
||||
'pgf.rcfonts': False,
|
||||
'pgf.texsystem': 'pdflatex',
|
||||
}
|
||||
mpl.rcParams.update(rc_pdflatex)
|
||||
|
||||
fig1 = plt.figure()
|
||||
ax1 = fig1.add_subplot(1, 1, 1)
|
||||
ax1.plot(range(5))
|
||||
fig1.tight_layout()
|
||||
|
||||
fig2 = plt.figure(figsize=(3, 2))
|
||||
ax2 = fig2.add_subplot(1, 1, 1)
|
||||
ax2.plot(range(5))
|
||||
fig2.tight_layout()
|
||||
|
||||
with PdfPages(os.path.join(result_dir, 'pdfpages.pdf')) as pdf:
|
||||
pdf.savefig(fig1)
|
||||
pdf.savefig(fig2)
|
||||
|
||||
|
||||
@needs_xelatex
|
||||
@pytest.mark.style('default')
|
||||
@pytest.mark.backend('pgf')
|
||||
def test_pdf_pages_metadata():
|
||||
rc_pdflatex = {
|
||||
'font.family': 'serif',
|
||||
'pgf.rcfonts': False,
|
||||
'pgf.texsystem': 'xelatex',
|
||||
}
|
||||
mpl.rcParams.update(rc_pdflatex)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.plot(range(5))
|
||||
fig.tight_layout()
|
||||
|
||||
md = {'author': 'me', 'title': 'Multipage PDF with pgf'}
|
||||
path = os.path.join(result_dir, 'pdfpages_meta.pdf')
|
||||
|
||||
with PdfPages(path, metadata=md) as pdf:
|
||||
pdf.savefig(fig)
|
||||
pdf.savefig(fig)
|
||||
pdf.savefig(fig)
|
||||
|
||||
assert pdf.get_pagecount() == 3
|
||||
|
||||
|
||||
@needs_lualatex
|
||||
@pytest.mark.style('default')
|
||||
@pytest.mark.backend('pgf')
|
||||
def test_pdf_pages_lualatex():
|
||||
rc_pdflatex = {
|
||||
'font.family': 'serif',
|
||||
'pgf.rcfonts': False,
|
||||
'pgf.texsystem': 'lualatex'
|
||||
}
|
||||
mpl.rcParams.update(rc_pdflatex)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.plot(range(5))
|
||||
fig.tight_layout()
|
||||
|
||||
md = {'author': 'me', 'title': 'Multipage PDF with pgf'}
|
||||
path = os.path.join(result_dir, 'pdfpages_lua.pdf')
|
||||
with PdfPages(path, metadata=md) as pdf:
|
||||
pdf.savefig(fig)
|
||||
pdf.savefig(fig)
|
||||
|
||||
assert pdf.get_pagecount() == 2
|
||||
@@ -0,0 +1,141 @@
|
||||
import io
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import cbook, patheffects
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
from matplotlib.testing.determinism import (_determinism_source_date_epoch,
|
||||
_determinism_check)
|
||||
|
||||
|
||||
needs_ghostscript = pytest.mark.skipif(
|
||||
matplotlib.checkdep_ghostscript()[0] is None,
|
||||
reason="This test needs a ghostscript installation")
|
||||
needs_usetex = pytest.mark.skipif(
|
||||
not matplotlib.checkdep_usetex(True),
|
||||
reason="This test needs a TeX installation")
|
||||
|
||||
|
||||
# This tests tends to hit a TeX cache lock on AppVeyor.
|
||||
@pytest.mark.flaky(reruns=3)
|
||||
@pytest.mark.parametrize('format, use_log, rcParams', [
|
||||
('ps', False, {}),
|
||||
pytest.param('ps', False, {'ps.usedistiller': 'ghostscript'},
|
||||
marks=needs_ghostscript),
|
||||
pytest.param('ps', False, {'text.usetex': True},
|
||||
marks=[needs_ghostscript, needs_usetex]),
|
||||
('eps', False, {}),
|
||||
('eps', True, {'ps.useafm': True}),
|
||||
pytest.param('eps', False, {'text.usetex': True},
|
||||
marks=[needs_ghostscript, needs_usetex]),
|
||||
], ids=[
|
||||
'ps',
|
||||
'ps with distiller',
|
||||
'ps with usetex',
|
||||
'eps',
|
||||
'eps afm',
|
||||
'eps with usetex'
|
||||
])
|
||||
def test_savefig_to_stringio(format, use_log, rcParams):
|
||||
matplotlib.rcParams.update(rcParams)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
with io.StringIO() as s_buf, io.BytesIO() as b_buf:
|
||||
|
||||
if use_log:
|
||||
ax.set_yscale('log')
|
||||
|
||||
ax.plot([1, 2], [1, 2])
|
||||
ax.set_title("Déjà vu")
|
||||
fig.savefig(s_buf, format=format)
|
||||
fig.savefig(b_buf, format=format)
|
||||
|
||||
s_val = s_buf.getvalue().encode('ascii')
|
||||
b_val = b_buf.getvalue()
|
||||
|
||||
# Remove comments from the output. This includes things that could
|
||||
# change from run to run, such as the time.
|
||||
s_val, b_val = [re.sub(b'%%.*?\n', b'', x) for x in [s_val, b_val]]
|
||||
|
||||
assert s_val == b_val.replace(b'\r\n', b'\n')
|
||||
|
||||
|
||||
def test_patheffects():
|
||||
with matplotlib.rc_context():
|
||||
matplotlib.rcParams['path.effects'] = [
|
||||
patheffects.withStroke(linewidth=4, foreground='w')]
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot([1, 2, 3])
|
||||
with io.BytesIO() as ps:
|
||||
fig.savefig(ps, format='ps')
|
||||
|
||||
|
||||
@needs_usetex
|
||||
@needs_ghostscript
|
||||
def test_tilde_in_tempfilename(tmpdir):
|
||||
# Tilde ~ in the tempdir path (e.g. TMPDIR, TMP or TEMP on windows
|
||||
# when the username is very long and windows uses a short name) breaks
|
||||
# latex before https://github.com/matplotlib/matplotlib/pull/5928
|
||||
base_tempdir = Path(str(tmpdir), "short-1")
|
||||
base_tempdir.mkdir()
|
||||
# Change the path for new tempdirs, which is used internally by the ps
|
||||
# backend to write a file.
|
||||
with cbook._setattr_cm(tempfile, tempdir=str(base_tempdir)):
|
||||
# usetex results in the latex call, which does not like the ~
|
||||
plt.rc('text', usetex=True)
|
||||
plt.plot([1, 2, 3, 4])
|
||||
plt.xlabel(r'\textbf{time} (s)')
|
||||
output_eps = os.path.join(str(base_tempdir), 'tex_demo.eps')
|
||||
# use the PS backend to write the file...
|
||||
plt.savefig(output_eps, format="ps")
|
||||
|
||||
|
||||
def test_source_date_epoch():
|
||||
"""Test SOURCE_DATE_EPOCH support for PS output"""
|
||||
# SOURCE_DATE_EPOCH support is not tested with text.usetex,
|
||||
# because the produced timestamp comes from ghostscript:
|
||||
# %%CreationDate: D:20000101000000Z00\'00\', and this could change
|
||||
# with another ghostscript version.
|
||||
_determinism_source_date_epoch(
|
||||
"ps", b"%%CreationDate: Sat Jan 01 00:00:00 2000")
|
||||
|
||||
|
||||
def test_determinism_all():
|
||||
"""Test for reproducible PS output"""
|
||||
_determinism_check(format="ps")
|
||||
|
||||
|
||||
@needs_usetex
|
||||
@needs_ghostscript
|
||||
def test_determinism_all_tex():
|
||||
"""Test for reproducible PS/tex output"""
|
||||
_determinism_check(format="ps", usetex=True)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=["empty"], extensions=["eps"])
|
||||
def test_transparency():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_axis_off()
|
||||
ax.plot([0, 1], color="r", alpha=0)
|
||||
ax.text(.5, .5, "foo", color="r", alpha=0)
|
||||
|
||||
|
||||
@needs_usetex
|
||||
def test_failing_latex(tmpdir):
|
||||
"""Test failing latex subprocess call"""
|
||||
path = str(tmpdir.join("tmpoutput.ps"))
|
||||
|
||||
matplotlib.rcParams['text.usetex'] = True
|
||||
|
||||
# This fails with "Double subscript"
|
||||
plt.xlabel("$22_2_2$")
|
||||
with pytest.raises(RuntimeError):
|
||||
plt.savefig(path)
|
||||
@@ -0,0 +1,148 @@
|
||||
import copy
|
||||
from unittest import mock
|
||||
|
||||
import matplotlib
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib._pylab_helpers import Gcf
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mpl_test_settings(qt4_module, mpl_test_settings):
|
||||
"""
|
||||
Ensure qt4_module fixture is *first* fixture.
|
||||
|
||||
We override the `mpl_test_settings` fixture and depend on the `qt4_module`
|
||||
fixture first. It is very important that it is first, because it skips
|
||||
tests when Qt4 is not available, and if not, then the main
|
||||
`mpl_test_settings` fixture will try to switch backends before the skip can
|
||||
be triggered.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def qt4_module():
|
||||
try:
|
||||
import PyQt4
|
||||
# RuntimeError if PyQt5 already imported.
|
||||
except (ImportError, RuntimeError):
|
||||
try:
|
||||
import PySide
|
||||
except ImportError:
|
||||
pytest.skip("Failed to import a Qt4 binding.")
|
||||
|
||||
qt_compat = pytest.importorskip('matplotlib.backends.qt_compat')
|
||||
QtCore = qt_compat.QtCore
|
||||
|
||||
try:
|
||||
py_qt_ver = int(QtCore.PYQT_VERSION_STR.split('.')[0])
|
||||
except AttributeError:
|
||||
py_qt_ver = QtCore.__version_info__[0]
|
||||
|
||||
if py_qt_ver != 4:
|
||||
pytest.skip(reason='Qt4 is not available')
|
||||
|
||||
from matplotlib.backends.backend_qt4 import (
|
||||
MODIFIER_KEYS, SUPER, ALT, CTRL, SHIFT) # noqa
|
||||
|
||||
mods = {}
|
||||
keys = {}
|
||||
for name, index in zip(['Alt', 'Control', 'Shift', 'Super'],
|
||||
[ALT, CTRL, SHIFT, SUPER]):
|
||||
_, mod, key = MODIFIER_KEYS[index]
|
||||
mods[name + 'Modifier'] = mod
|
||||
keys[name + 'Key'] = key
|
||||
|
||||
return QtCore, mods, keys
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def qt_key(request):
|
||||
QtCore, _, keys = request.getfixturevalue('qt4_module')
|
||||
if request.param.startswith('Key'):
|
||||
return getattr(QtCore.Qt, request.param)
|
||||
else:
|
||||
return keys[request.param]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def qt_mods(request):
|
||||
QtCore, mods, _ = request.getfixturevalue('qt4_module')
|
||||
result = QtCore.Qt.NoModifier
|
||||
for mod in request.param:
|
||||
result |= mods[mod]
|
||||
return result
|
||||
|
||||
|
||||
@pytest.mark.backend('Qt4Agg')
|
||||
def test_fig_close():
|
||||
# save the state of Gcf.figs
|
||||
init_figs = copy.copy(Gcf.figs)
|
||||
|
||||
# make a figure using pyplot interface
|
||||
fig = plt.figure()
|
||||
|
||||
# simulate user clicking the close button by reaching in
|
||||
# and calling close on the underlying Qt object
|
||||
fig.canvas.manager.window.close()
|
||||
|
||||
# assert that we have removed the reference to the FigureManager
|
||||
# that got added by plt.figure()
|
||||
assert init_figs == Gcf.figs
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'qt_key, qt_mods, answer',
|
||||
[
|
||||
('Key_A', ['ShiftModifier'], 'A'),
|
||||
('Key_A', [], 'a'),
|
||||
('Key_A', ['ControlModifier'], 'ctrl+a'),
|
||||
('Key_Aacute', ['ShiftModifier'],
|
||||
'\N{LATIN CAPITAL LETTER A WITH ACUTE}'),
|
||||
('Key_Aacute', [],
|
||||
'\N{LATIN SMALL LETTER A WITH ACUTE}'),
|
||||
('ControlKey', ['AltModifier'], 'alt+control'),
|
||||
('AltKey', ['ControlModifier'], 'ctrl+alt'),
|
||||
('Key_Aacute', ['ControlModifier', 'AltModifier', 'SuperModifier'],
|
||||
'ctrl+alt+super+\N{LATIN SMALL LETTER A WITH ACUTE}'),
|
||||
('Key_Backspace', [], 'backspace'),
|
||||
('Key_Backspace', ['ControlModifier'], 'ctrl+backspace'),
|
||||
('Key_Play', [], None),
|
||||
],
|
||||
indirect=['qt_key', 'qt_mods'],
|
||||
ids=[
|
||||
'shift',
|
||||
'lower',
|
||||
'control',
|
||||
'unicode_upper',
|
||||
'unicode_lower',
|
||||
'alt_control',
|
||||
'control_alt',
|
||||
'modifier_order',
|
||||
'backspace',
|
||||
'backspace_mod',
|
||||
'non_unicode_key',
|
||||
]
|
||||
)
|
||||
@pytest.mark.backend('Qt4Agg')
|
||||
def test_correct_key(qt_key, qt_mods, answer):
|
||||
"""
|
||||
Make a figure
|
||||
Send a key_press_event event (using non-public, qt4 backend specific api)
|
||||
Catch the event
|
||||
Assert sent and caught keys are the same
|
||||
"""
|
||||
qt_canvas = plt.figure().canvas
|
||||
|
||||
event = mock.Mock()
|
||||
event.isAutoRepeat.return_value = False
|
||||
event.key.return_value = qt_key
|
||||
event.modifiers.return_value = qt_mods
|
||||
|
||||
def receive(event):
|
||||
assert event.key == answer
|
||||
|
||||
qt_canvas.mpl_connect('key_press_event', receive)
|
||||
qt_canvas.keyPressEvent(event)
|
||||
@@ -0,0 +1,223 @@
|
||||
import copy
|
||||
from unittest import mock
|
||||
|
||||
import matplotlib
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib._pylab_helpers import Gcf
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mpl_test_settings(qt5_module, mpl_test_settings):
|
||||
"""
|
||||
Ensure qt5_module fixture is *first* fixture.
|
||||
|
||||
We override the `mpl_test_settings` fixture and depend on the `qt5_module`
|
||||
fixture first. It is very important that it is first, because it skips
|
||||
tests when Qt5 is not available, and if not, then the main
|
||||
`mpl_test_settings` fixture will try to switch backends before the skip can
|
||||
be triggered.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def qt5_module():
|
||||
try:
|
||||
import PyQt5
|
||||
# RuntimeError if PyQt4 already imported.
|
||||
except (ImportError, RuntimeError):
|
||||
try:
|
||||
import PySide2
|
||||
except ImportError:
|
||||
pytest.skip("Failed to import a Qt5 binding.")
|
||||
|
||||
qt_compat = pytest.importorskip('matplotlib.backends.qt_compat')
|
||||
QtCore = qt_compat.QtCore
|
||||
|
||||
from matplotlib.backends.backend_qt5 import (
|
||||
MODIFIER_KEYS, SUPER, ALT, CTRL, SHIFT) # noqa
|
||||
|
||||
mods = {}
|
||||
keys = {}
|
||||
for name, index in zip(['Alt', 'Control', 'Shift', 'Super'],
|
||||
[ALT, CTRL, SHIFT, SUPER]):
|
||||
_, mod, key = MODIFIER_KEYS[index]
|
||||
mods[name + 'Modifier'] = mod
|
||||
keys[name + 'Key'] = key
|
||||
|
||||
return QtCore, mods, keys
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def qt_key(request):
|
||||
QtCore, _, keys = request.getfixturevalue('qt5_module')
|
||||
if request.param.startswith('Key'):
|
||||
return getattr(QtCore.Qt, request.param)
|
||||
else:
|
||||
return keys[request.param]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def qt_mods(request):
|
||||
QtCore, mods, _ = request.getfixturevalue('qt5_module')
|
||||
result = QtCore.Qt.NoModifier
|
||||
for mod in request.param:
|
||||
result |= mods[mod]
|
||||
return result
|
||||
|
||||
|
||||
@pytest.mark.backend('Qt5Agg')
|
||||
def test_fig_close():
|
||||
# save the state of Gcf.figs
|
||||
init_figs = copy.copy(Gcf.figs)
|
||||
|
||||
# make a figure using pyplot interface
|
||||
fig = plt.figure()
|
||||
|
||||
# simulate user clicking the close button by reaching in
|
||||
# and calling close on the underlying Qt object
|
||||
fig.canvas.manager.window.close()
|
||||
|
||||
# assert that we have removed the reference to the FigureManager
|
||||
# that got added by plt.figure()
|
||||
assert init_figs == Gcf.figs
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'qt_key, qt_mods, answer',
|
||||
[
|
||||
('Key_A', ['ShiftModifier'], 'A'),
|
||||
('Key_A', [], 'a'),
|
||||
('Key_A', ['ControlModifier'], 'ctrl+a'),
|
||||
('Key_Aacute', ['ShiftModifier'],
|
||||
'\N{LATIN CAPITAL LETTER A WITH ACUTE}'),
|
||||
('Key_Aacute', [],
|
||||
'\N{LATIN SMALL LETTER A WITH ACUTE}'),
|
||||
('ControlKey', ['AltModifier'], 'alt+control'),
|
||||
('AltKey', ['ControlModifier'], 'ctrl+alt'),
|
||||
('Key_Aacute', ['ControlModifier', 'AltModifier', 'SuperModifier'],
|
||||
'ctrl+alt+super+\N{LATIN SMALL LETTER A WITH ACUTE}'),
|
||||
('Key_Backspace', [], 'backspace'),
|
||||
('Key_Backspace', ['ControlModifier'], 'ctrl+backspace'),
|
||||
('Key_Play', [], None),
|
||||
],
|
||||
indirect=['qt_key', 'qt_mods'],
|
||||
ids=[
|
||||
'shift',
|
||||
'lower',
|
||||
'control',
|
||||
'unicode_upper',
|
||||
'unicode_lower',
|
||||
'alt_control',
|
||||
'control_alt',
|
||||
'modifier_order',
|
||||
'backspace',
|
||||
'backspace_mod',
|
||||
'non_unicode_key',
|
||||
]
|
||||
)
|
||||
@pytest.mark.backend('Qt5Agg')
|
||||
def test_correct_key(qt_key, qt_mods, answer):
|
||||
"""
|
||||
Make a figure
|
||||
Send a key_press_event event (using non-public, qt5 backend specific api)
|
||||
Catch the event
|
||||
Assert sent and caught keys are the same
|
||||
"""
|
||||
qt_canvas = plt.figure().canvas
|
||||
|
||||
event = mock.Mock()
|
||||
event.isAutoRepeat.return_value = False
|
||||
event.key.return_value = qt_key
|
||||
event.modifiers.return_value = qt_mods
|
||||
|
||||
def receive(event):
|
||||
assert event.key == answer
|
||||
|
||||
qt_canvas.mpl_connect('key_press_event', receive)
|
||||
qt_canvas.keyPressEvent(event)
|
||||
|
||||
|
||||
@pytest.mark.backend('Qt5Agg')
|
||||
def test_dpi_ratio_change():
|
||||
"""
|
||||
Make sure that if _dpi_ratio changes, the figure dpi changes but the
|
||||
widget remains the same physical size.
|
||||
"""
|
||||
|
||||
prop = 'matplotlib.backends.backend_qt5.FigureCanvasQT._dpi_ratio'
|
||||
|
||||
with mock.patch(prop, new_callable=mock.PropertyMock) as p:
|
||||
|
||||
p.return_value = 3
|
||||
|
||||
fig = plt.figure(figsize=(5, 2), dpi=120)
|
||||
qt_canvas = fig.canvas
|
||||
qt_canvas.show()
|
||||
|
||||
from matplotlib.backends.backend_qt5 import qApp
|
||||
|
||||
# Make sure the mocking worked
|
||||
assert qt_canvas._dpi_ratio == 3
|
||||
|
||||
size = qt_canvas.size()
|
||||
|
||||
qt_canvas.manager.show()
|
||||
qt_canvas.draw()
|
||||
qApp.processEvents()
|
||||
|
||||
# The DPI and the renderer width/height change
|
||||
assert fig.dpi == 360
|
||||
assert qt_canvas.renderer.width == 1800
|
||||
assert qt_canvas.renderer.height == 720
|
||||
|
||||
# The actual widget size and figure physical size don't change
|
||||
assert size.width() == 600
|
||||
assert size.height() == 240
|
||||
assert qt_canvas.get_width_height() == (600, 240)
|
||||
assert (fig.get_size_inches() == (5, 2)).all()
|
||||
|
||||
p.return_value = 2
|
||||
|
||||
assert qt_canvas._dpi_ratio == 2
|
||||
|
||||
qt_canvas.draw()
|
||||
qApp.processEvents()
|
||||
# this second processEvents is required to fully run the draw.
|
||||
# On `update` we notice the DPI has changed and trigger a
|
||||
# resize event to refresh, the second processEvents is
|
||||
# required to process that and fully update the window sizes.
|
||||
qApp.processEvents()
|
||||
|
||||
# The DPI and the renderer width/height change
|
||||
assert fig.dpi == 240
|
||||
assert qt_canvas.renderer.width == 1200
|
||||
assert qt_canvas.renderer.height == 480
|
||||
|
||||
# The actual widget size and figure physical size don't change
|
||||
assert size.width() == 600
|
||||
assert size.height() == 240
|
||||
assert qt_canvas.get_width_height() == (600, 240)
|
||||
assert (fig.get_size_inches() == (5, 2)).all()
|
||||
|
||||
|
||||
@pytest.mark.backend('Qt5Agg')
|
||||
def test_subplottool():
|
||||
fig, ax = plt.subplots()
|
||||
with mock.patch(
|
||||
"matplotlib.backends.backend_qt5.SubplotToolQt.exec_",
|
||||
lambda self: None):
|
||||
fig.canvas.manager.toolbar.configure_subplots()
|
||||
|
||||
|
||||
@pytest.mark.backend('Qt5Agg')
|
||||
def test_figureoptions():
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot([1, 2])
|
||||
ax.imshow([[1]])
|
||||
with mock.patch(
|
||||
"matplotlib.backends.qt_editor.formlayout.FormDialog.exec_",
|
||||
lambda self: None):
|
||||
fig.canvas.manager.toolbar.edit_parameters()
|
||||
@@ -0,0 +1,176 @@
|
||||
import numpy as np
|
||||
from io import BytesIO
|
||||
import os
|
||||
import tempfile
|
||||
import xml.parsers.expat
|
||||
|
||||
import pytest
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib
|
||||
from matplotlib import dviread
|
||||
|
||||
|
||||
needs_usetex = pytest.mark.skipif(
|
||||
not matplotlib.checkdep_usetex(True),
|
||||
reason="This test needs a TeX installation")
|
||||
|
||||
|
||||
def test_visibility():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
x = np.linspace(0, 4 * np.pi, 50)
|
||||
y = np.sin(x)
|
||||
yerr = np.ones_like(y)
|
||||
|
||||
a, b, c = ax.errorbar(x, y, yerr=yerr, fmt='ko')
|
||||
for artist in b:
|
||||
artist.set_visible(False)
|
||||
|
||||
fd = BytesIO()
|
||||
fig.savefig(fd, format='svg')
|
||||
|
||||
fd.seek(0)
|
||||
buf = fd.read()
|
||||
fd.close()
|
||||
|
||||
parser = xml.parsers.expat.ParserCreate()
|
||||
parser.Parse(buf) # this will raise ExpatError if the svg is invalid
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fill_black_with_alpha'], remove_text=True,
|
||||
extensions=['svg'])
|
||||
def test_fill_black_with_alpha():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.scatter(x=[0, 0.1, 1], y=[0, 0, 0], c='k', alpha=0.1, s=10000)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['noscale'], remove_text=True)
|
||||
def test_noscale():
|
||||
X, Y = np.meshgrid(np.arange(-5, 5, 1), np.arange(-5, 5, 1))
|
||||
Z = np.sin(Y ** 2)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.imshow(Z, cmap='gray', interpolation='none')
|
||||
|
||||
|
||||
def test_text_urls():
|
||||
fig = plt.figure()
|
||||
|
||||
test_url = "http://test_text_urls.matplotlib.org"
|
||||
fig.suptitle("test_text_urls", url=test_url)
|
||||
|
||||
fd = BytesIO()
|
||||
fig.savefig(fd, format='svg')
|
||||
fd.seek(0)
|
||||
buf = fd.read().decode()
|
||||
fd.close()
|
||||
|
||||
expected = '<a xlink:href="{0}">'.format(test_url)
|
||||
assert expected in buf
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['bold_font_output'], extensions=['svg'])
|
||||
def test_bold_font_output():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.plot(np.arange(10), np.arange(10))
|
||||
ax.set_xlabel('nonbold-xlabel')
|
||||
ax.set_ylabel('bold-ylabel', fontweight='bold')
|
||||
ax.set_title('bold-title', fontweight='bold')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['bold_font_output_with_none_fonttype'],
|
||||
extensions=['svg'])
|
||||
def test_bold_font_output_with_none_fonttype():
|
||||
plt.rcParams['svg.fonttype'] = 'none'
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.plot(np.arange(10), np.arange(10))
|
||||
ax.set_xlabel('nonbold-xlabel')
|
||||
ax.set_ylabel('bold-ylabel', fontweight='bold')
|
||||
ax.set_title('bold-title', fontweight='bold')
|
||||
|
||||
|
||||
def _test_determinism_save(filename, usetex):
|
||||
# This function is mostly copy&paste from "def test_visibility"
|
||||
# To require no GUI, we use Figure and FigureCanvasSVG
|
||||
# instead of plt.figure and fig.savefig
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_svg import FigureCanvasSVG
|
||||
from matplotlib import rc
|
||||
rc('svg', hashsalt='asdf')
|
||||
rc('text', usetex=usetex)
|
||||
|
||||
fig = Figure()
|
||||
ax = fig.add_subplot(111)
|
||||
|
||||
x = np.linspace(0, 4 * np.pi, 50)
|
||||
y = np.sin(x)
|
||||
yerr = np.ones_like(y)
|
||||
|
||||
a, b, c = ax.errorbar(x, y, yerr=yerr, fmt='ko')
|
||||
for artist in b:
|
||||
artist.set_visible(False)
|
||||
ax.set_title('A string $1+2+\\sigma$')
|
||||
ax.set_xlabel('A string $1+2+\\sigma$')
|
||||
ax.set_ylabel('A string $1+2+\\sigma$')
|
||||
|
||||
FigureCanvasSVG(fig).print_svg(filename)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"filename, usetex",
|
||||
# unique filenames to allow for parallel testing
|
||||
[("determinism_notex.svg", False),
|
||||
pytest.param("determinism_tex.svg", True, marks=needs_usetex)])
|
||||
def test_determinism(filename, usetex):
|
||||
import sys
|
||||
from subprocess import check_output, STDOUT, CalledProcessError
|
||||
plots = []
|
||||
for i in range(3):
|
||||
# Using check_output and setting stderr to STDOUT will capture the real
|
||||
# problem in the output property of the exception
|
||||
try:
|
||||
check_output(
|
||||
[sys.executable, '-R', '-c',
|
||||
'import matplotlib; '
|
||||
'matplotlib._called_from_pytest = True; '
|
||||
'matplotlib.use("svg", force=True); '
|
||||
'from matplotlib.tests.test_backend_svg '
|
||||
'import _test_determinism_save;'
|
||||
'_test_determinism_save(%r, %r)' % (filename, usetex)],
|
||||
stderr=STDOUT)
|
||||
except CalledProcessError as e:
|
||||
# it's easier to use utf8 and ask for forgiveness than try
|
||||
# to figure out what the current console has as an
|
||||
# encoding :-/
|
||||
print(e.output.decode(encoding="utf-8", errors="ignore"))
|
||||
raise e
|
||||
else:
|
||||
with open(filename, 'rb') as fd:
|
||||
plots.append(fd.read())
|
||||
finally:
|
||||
os.unlink(filename)
|
||||
for p in plots[1:]:
|
||||
assert p == plots[0]
|
||||
|
||||
|
||||
@needs_usetex
|
||||
def test_missing_psfont(monkeypatch):
|
||||
"""An error is raised if a TeX font lacks a Type-1 equivalent"""
|
||||
from matplotlib import rc
|
||||
|
||||
def psfont(*args, **kwargs):
|
||||
return dviread.PsFont(texname='texfont', psname='Some Font',
|
||||
effects=None, encoding=None, filename=None)
|
||||
|
||||
monkeypatch.setattr(dviread.PsfontsMap, '__getitem__', psfont)
|
||||
rc('text', usetex=True)
|
||||
fig, ax = plt.subplots()
|
||||
ax.text(0.5, 0.5, 'hello')
|
||||
with tempfile.TemporaryFile() as tmpfile, pytest.raises(ValueError):
|
||||
fig.savefig(tmpfile, format='svg')
|
||||
@@ -0,0 +1,20 @@
|
||||
import pytest
|
||||
|
||||
from matplotlib.backend_tools import ToolHelpBase
|
||||
|
||||
|
||||
@pytest.mark.parametrize('rc_shortcut,expected', [
|
||||
('home', 'Home'),
|
||||
('backspace', 'Backspace'),
|
||||
('f1', 'F1'),
|
||||
('ctrl+a', 'Ctrl+A'),
|
||||
('ctrl+A', 'Ctrl+Shift+A'),
|
||||
('a', 'a'),
|
||||
('A', 'A'),
|
||||
('ctrl+shift+f1', 'Ctrl+Shift+F1'),
|
||||
('1', '1'),
|
||||
('cmd+p', 'Cmd+P'),
|
||||
('cmd+1', 'Cmd+1'),
|
||||
])
|
||||
def test_format_shortcut(rc_shortcut, expected):
|
||||
assert ToolHelpBase.format_shortcut(rc_shortcut) == expected
|
||||
@@ -0,0 +1,144 @@
|
||||
import importlib
|
||||
import importlib.util
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
import pytest
|
||||
|
||||
import matplotlib as mpl
|
||||
|
||||
|
||||
# Minimal smoke-testing of the backends for which the dependencies are
|
||||
# PyPI-installable on Travis. They are not available for all tested Python
|
||||
# versions so we don't fail on missing backends.
|
||||
|
||||
def _get_testable_interactive_backends():
|
||||
backends = []
|
||||
for deps, backend in [
|
||||
# gtk3agg fails on Travis, needs to be investigated.
|
||||
# (["cairocffi", "pgi"], "gtk3agg"),
|
||||
(["cairocffi", "pgi"], "gtk3cairo"),
|
||||
(["PyQt5"], "qt5agg"),
|
||||
(["PyQt5", "cairocffi"], "qt5cairo"),
|
||||
(["tkinter"], "tkagg"),
|
||||
(["wx"], "wx"),
|
||||
(["wx"], "wxagg"),
|
||||
]:
|
||||
reason = None
|
||||
if not os.environ.get("DISPLAY"):
|
||||
reason = "No $DISPLAY"
|
||||
elif any(importlib.util.find_spec(dep) is None for dep in deps):
|
||||
reason = "Missing dependency"
|
||||
if reason:
|
||||
backend = pytest.param(
|
||||
backend, marks=pytest.mark.skip(reason=reason))
|
||||
backends.append(backend)
|
||||
return backends
|
||||
|
||||
|
||||
# Using a timer not only allows testing of timers (on other backends), but is
|
||||
# also necessary on gtk3 and wx, where a direct call to key_press_event("q")
|
||||
# from draw_event causes breakage due to the canvas widget being deleted too
|
||||
# early. Also, gtk3 redefines key_press_event with a different signature, so
|
||||
# we directly invoke it from the superclass instead.
|
||||
_test_script = """\
|
||||
import importlib
|
||||
import importlib.util
|
||||
import sys
|
||||
from unittest import TestCase
|
||||
|
||||
import matplotlib as mpl
|
||||
from matplotlib import pyplot as plt, rcParams
|
||||
from matplotlib.backend_bases import FigureCanvasBase
|
||||
rcParams.update({
|
||||
"webagg.open_in_browser": False,
|
||||
"webagg.port_retries": 1,
|
||||
})
|
||||
backend = plt.rcParams["backend"].lower()
|
||||
assert_equal = TestCase().assertEqual
|
||||
assert_raises = TestCase().assertRaises
|
||||
|
||||
if backend.endswith("agg") and not backend.startswith(("gtk3", "web")):
|
||||
# Force interactive framework setup.
|
||||
plt.figure()
|
||||
|
||||
# Check that we cannot switch to a backend using another interactive
|
||||
# framework, but can switch to a backend using cairo instead of agg, or a
|
||||
# non-interactive backend. In the first case, we use tkagg as the "other"
|
||||
# interactive backend as it is (essentially) guaranteed to be present.
|
||||
# Moreover, don't test switching away from gtk3 (as Gtk.main_level() is
|
||||
# not set up at this point yet) and webagg (which uses no interactive
|
||||
# framework).
|
||||
|
||||
if backend != "tkagg":
|
||||
with assert_raises(ImportError):
|
||||
mpl.use("tkagg", force=True)
|
||||
|
||||
def check_alt_backend(alt_backend):
|
||||
mpl.use(alt_backend, force=True)
|
||||
fig = plt.figure()
|
||||
assert_equal(
|
||||
type(fig.canvas).__module__,
|
||||
"matplotlib.backends.backend_{}".format(alt_backend))
|
||||
|
||||
if importlib.util.find_spec("cairocffi"):
|
||||
check_alt_backend(backend[:-3] + "cairo")
|
||||
check_alt_backend("svg")
|
||||
|
||||
mpl.use(backend, force=True)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
assert_equal(
|
||||
type(fig.canvas).__module__,
|
||||
"matplotlib.backends.backend_{}".format(backend))
|
||||
|
||||
ax.plot([0, 1], [2, 3])
|
||||
|
||||
timer = fig.canvas.new_timer(1)
|
||||
timer.add_callback(FigureCanvasBase.key_press_event, fig.canvas, "q")
|
||||
# Trigger quitting upon draw.
|
||||
fig.canvas.mpl_connect("draw_event", lambda event: timer.start())
|
||||
|
||||
plt.show()
|
||||
"""
|
||||
_test_timeout = 10 # Empirically, 1s is not enough on Travis.
|
||||
|
||||
|
||||
@pytest.mark.parametrize("backend", _get_testable_interactive_backends())
|
||||
@pytest.mark.flaky(reruns=3)
|
||||
def test_interactive_backend(backend):
|
||||
if subprocess.run([sys.executable, "-c", _test_script],
|
||||
env={**os.environ, "MPLBACKEND": backend},
|
||||
timeout=_test_timeout).returncode:
|
||||
pytest.fail("The subprocess returned an error.")
|
||||
|
||||
|
||||
@pytest.mark.skipif('SYSTEM_TEAMFOUNDATIONCOLLECTIONURI' in os.environ,
|
||||
reason="this test fails an azure for unknown reasons")
|
||||
@pytest.mark.skipif(os.name == "nt", reason="Cannot send SIGINT on Windows.")
|
||||
def test_webagg():
|
||||
pytest.importorskip("tornado")
|
||||
proc = subprocess.Popen([sys.executable, "-c", _test_script],
|
||||
env={**os.environ, "MPLBACKEND": "webagg"})
|
||||
url = "http://{}:{}".format(
|
||||
mpl.rcParams["webagg.address"], mpl.rcParams["webagg.port"])
|
||||
timeout = time.perf_counter() + _test_timeout
|
||||
while True:
|
||||
try:
|
||||
retcode = proc.poll()
|
||||
# check that the subprocess for the server is not dead
|
||||
assert retcode is None
|
||||
conn = urllib.request.urlopen(url)
|
||||
break
|
||||
except urllib.error.URLError:
|
||||
if time.perf_counter() > timeout:
|
||||
pytest.fail("Failed to connect to the webagg server.")
|
||||
else:
|
||||
continue
|
||||
conn.close()
|
||||
proc.send_signal(signal.SIGINT)
|
||||
assert proc.wait(timeout=_test_timeout) == 0
|
||||
@@ -0,0 +1,36 @@
|
||||
import builtins
|
||||
|
||||
import matplotlib
|
||||
|
||||
|
||||
def test_simple():
|
||||
assert 1 + 1 == 2
|
||||
|
||||
|
||||
def test_override_builtins():
|
||||
import pylab
|
||||
|
||||
ok_to_override = {
|
||||
'__name__',
|
||||
'__doc__',
|
||||
'__package__',
|
||||
'__loader__',
|
||||
'__spec__',
|
||||
'any',
|
||||
'all',
|
||||
'sum',
|
||||
'divmod'
|
||||
}
|
||||
overridden = False
|
||||
for key in dir(pylab):
|
||||
if key in dir(builtins):
|
||||
if (getattr(pylab, key) != getattr(builtins, key) and
|
||||
key not in ok_to_override):
|
||||
print("'%s' was overridden in globals()." % key)
|
||||
overridden = True
|
||||
|
||||
assert not overridden
|
||||
|
||||
|
||||
def test_verbose():
|
||||
assert isinstance(matplotlib.verbose, matplotlib.Verbose)
|
||||
@@ -0,0 +1,88 @@
|
||||
import numpy as np
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.path as mpath
|
||||
import matplotlib.patches as mpatches
|
||||
from matplotlib.ticker import FuncFormatter
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['bbox_inches_tight'], remove_text=True,
|
||||
savefig_kwarg=dict(bbox_inches='tight'))
|
||||
def test_bbox_inches_tight():
|
||||
#: Test that a figure saved using bbox_inches='tight' is clipped correctly
|
||||
data = [[66386, 174296, 75131, 577908, 32015],
|
||||
[58230, 381139, 78045, 99308, 160454],
|
||||
[89135, 80552, 152558, 497981, 603535],
|
||||
[78415, 81858, 150656, 193263, 69638],
|
||||
[139361, 331509, 343164, 781380, 52269]]
|
||||
|
||||
colLabels = rowLabels = [''] * 5
|
||||
|
||||
rows = len(data)
|
||||
ind = np.arange(len(colLabels)) + 0.3 # the x locations for the groups
|
||||
cellText = []
|
||||
width = 0.4 # the width of the bars
|
||||
yoff = np.zeros(len(colLabels))
|
||||
# the bottom values for stacked bar chart
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
for row in range(rows):
|
||||
ax.bar(ind, data[row], width, bottom=yoff, align='edge', color='b')
|
||||
yoff = yoff + data[row]
|
||||
cellText.append([''])
|
||||
plt.xticks([])
|
||||
plt.legend([''] * 5, loc=(1.2, 0.2))
|
||||
# Add a table at the bottom of the axes
|
||||
cellText.reverse()
|
||||
the_table = plt.table(cellText=cellText,
|
||||
rowLabels=rowLabels,
|
||||
colLabels=colLabels, loc='bottom')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['bbox_inches_tight_suptile_legend'],
|
||||
remove_text=False, savefig_kwarg={'bbox_inches': 'tight'})
|
||||
def test_bbox_inches_tight_suptile_legend():
|
||||
plt.plot(np.arange(10), label='a straight line')
|
||||
plt.legend(bbox_to_anchor=(0.9, 1), loc='upper left')
|
||||
plt.title('Axis title')
|
||||
plt.suptitle('Figure title')
|
||||
|
||||
# put an extra long y tick on to see that the bbox is accounted for
|
||||
def y_formatter(y, pos):
|
||||
if int(y) == 4:
|
||||
return 'The number 4'
|
||||
else:
|
||||
return str(y)
|
||||
plt.gca().yaxis.set_major_formatter(FuncFormatter(y_formatter))
|
||||
|
||||
plt.xlabel('X axis')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['bbox_inches_tight_clipping'],
|
||||
remove_text=True, savefig_kwarg={'bbox_inches': 'tight'})
|
||||
def test_bbox_inches_tight_clipping():
|
||||
# tests bbox clipping on scatter points, and path clipping on a patch
|
||||
# to generate an appropriately tight bbox
|
||||
plt.scatter(np.arange(10), np.arange(10))
|
||||
ax = plt.gca()
|
||||
ax.set_xlim([0, 5])
|
||||
ax.set_ylim([0, 5])
|
||||
|
||||
# make a massive rectangle and clip it with a path
|
||||
patch = mpatches.Rectangle([-50, -50], 100, 100,
|
||||
transform=ax.transData,
|
||||
facecolor='blue', alpha=0.5)
|
||||
|
||||
path = mpath.Path.unit_regular_star(5).deepcopy()
|
||||
path.vertices *= 0.25
|
||||
patch.set_clip_path(path, transform=ax.transAxes)
|
||||
plt.gcf().artists.append(patch)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['bbox_inches_tight_raster'],
|
||||
remove_text=True, savefig_kwarg={'bbox_inches': 'tight'})
|
||||
def test_bbox_inches_tight_raster():
|
||||
"""Test rasterization with tight_layout"""
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.plot([1.0, 2.0], rasterized=True)
|
||||
@@ -0,0 +1,275 @@
|
||||
"""Catch all for categorical functions"""
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
from matplotlib.axes import Axes
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.category as cat
|
||||
|
||||
# Python2/3 text handling
|
||||
_to_str = cat.StrCategoryFormatter._text
|
||||
|
||||
|
||||
class TestUnitData(object):
|
||||
test_cases = [('single', (["hello world"], [0])),
|
||||
('unicode', (["Здравствуйте мир"], [0])),
|
||||
('mixed', (['A', "np.nan", 'B', "3.14", "мир"],
|
||||
[0, 1, 2, 3, 4]))]
|
||||
ids, data = zip(*test_cases)
|
||||
|
||||
@pytest.mark.parametrize("data, locs", data, ids=ids)
|
||||
def test_unit(self, data, locs):
|
||||
unit = cat.UnitData(data)
|
||||
assert list(unit._mapping.keys()) == data
|
||||
assert list(unit._mapping.values()) == locs
|
||||
|
||||
def test_update(self):
|
||||
data = ['a', 'd']
|
||||
locs = [0, 1]
|
||||
|
||||
data_update = ['b', 'd', 'e']
|
||||
unique_data = ['a', 'd', 'b', 'e']
|
||||
updated_locs = [0, 1, 2, 3]
|
||||
|
||||
unit = cat.UnitData(data)
|
||||
assert list(unit._mapping.keys()) == data
|
||||
assert list(unit._mapping.values()) == locs
|
||||
|
||||
unit.update(data_update)
|
||||
assert list(unit._mapping.keys()) == unique_data
|
||||
assert list(unit._mapping.values()) == updated_locs
|
||||
|
||||
failing_test_cases = [("number", 3.14), ("nan", np.nan),
|
||||
("list", [3.14, 12]), ("mixed type", ["A", 2])]
|
||||
|
||||
fids, fdata = zip(*test_cases)
|
||||
|
||||
@pytest.mark.parametrize("fdata", fdata, ids=fids)
|
||||
def test_non_string_fails(self, fdata):
|
||||
with pytest.raises(TypeError):
|
||||
cat.UnitData(fdata)
|
||||
|
||||
@pytest.mark.parametrize("fdata", fdata, ids=fids)
|
||||
def test_non_string_update_fails(self, fdata):
|
||||
unitdata = cat.UnitData()
|
||||
with pytest.raises(TypeError):
|
||||
unitdata.update(fdata)
|
||||
|
||||
|
||||
class FakeAxis(object):
|
||||
def __init__(self, units):
|
||||
self.units = units
|
||||
|
||||
|
||||
class TestStrCategoryConverter(object):
|
||||
"""Based on the pandas conversion and factorization tests:
|
||||
|
||||
ref: /pandas/tseries/tests/test_converter.py
|
||||
/pandas/tests/test_algos.py:TestFactorize
|
||||
"""
|
||||
test_cases = [("unicode", ["Здравствуйте мир"]),
|
||||
("ascii", ["hello world"]),
|
||||
("single", ['a', 'b', 'c']),
|
||||
("integer string", ["1", "2"]),
|
||||
("single + values>10", ["A", "B", "C", "D", "E", "F", "G",
|
||||
"H", "I", "J", "K", "L", "M", "N",
|
||||
"O", "P", "Q", "R", "S", "T", "U",
|
||||
"V", "W", "X", "Y", "Z"])]
|
||||
|
||||
ids, values = zip(*test_cases)
|
||||
|
||||
failing_test_cases = [("mixed", [3.14, 'A', np.inf]),
|
||||
("string integer", ['42', 42])]
|
||||
|
||||
fids, fvalues = zip(*failing_test_cases)
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_axis(self, request):
|
||||
self.cc = cat.StrCategoryConverter()
|
||||
# self.unit should be probably be replaced with real mock unit
|
||||
self.unit = cat.UnitData()
|
||||
self.ax = FakeAxis(self.unit)
|
||||
|
||||
@pytest.mark.parametrize("vals", values, ids=ids)
|
||||
def test_convert(self, vals):
|
||||
np.testing.assert_allclose(self.cc.convert(vals, self.ax.units,
|
||||
self.ax),
|
||||
range(len(vals)))
|
||||
|
||||
@pytest.mark.parametrize("value", ["hi", "мир"], ids=["ascii", "unicode"])
|
||||
def test_convert_one_string(self, value):
|
||||
assert self.cc.convert(value, self.unit, self.ax) == 0
|
||||
|
||||
def test_convert_one_number(self):
|
||||
actual = self.cc.convert(0.0, self.unit, self.ax)
|
||||
np.testing.assert_allclose(actual, np.array([0.]))
|
||||
|
||||
def test_convert_float_array(self):
|
||||
data = np.array([1, 2, 3], dtype=float)
|
||||
actual = self.cc.convert(data, self.unit, self.ax)
|
||||
np.testing.assert_allclose(actual, np.array([1., 2., 3.]))
|
||||
|
||||
@pytest.mark.parametrize("fvals", fvalues, ids=fids)
|
||||
def test_convert_fail(self, fvals):
|
||||
with pytest.raises(TypeError):
|
||||
self.cc.convert(fvals, self.unit, self.ax)
|
||||
|
||||
def test_axisinfo(self):
|
||||
axis = self.cc.axisinfo(self.unit, self.ax)
|
||||
assert isinstance(axis.majloc, cat.StrCategoryLocator)
|
||||
assert isinstance(axis.majfmt, cat.StrCategoryFormatter)
|
||||
|
||||
def test_default_units(self):
|
||||
assert isinstance(self.cc.default_units(["a"], self.ax), cat.UnitData)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ax():
|
||||
return plt.figure().subplots()
|
||||
|
||||
|
||||
PLOT_LIST = [Axes.scatter, Axes.plot, Axes.bar]
|
||||
PLOT_IDS = ["scatter", "plot", "bar"]
|
||||
|
||||
|
||||
class TestStrCategoryLocator(object):
|
||||
def test_StrCategoryLocator(self):
|
||||
locs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
unit = cat.UnitData([str(j) for j in locs])
|
||||
ticks = cat.StrCategoryLocator(unit._mapping)
|
||||
np.testing.assert_array_equal(ticks.tick_values(None, None), locs)
|
||||
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
def test_StrCategoryLocatorPlot(self, ax, plotter):
|
||||
ax.plot(["a", "b", "c"])
|
||||
np.testing.assert_array_equal(ax.yaxis.major.locator(), range(3))
|
||||
|
||||
|
||||
class TestStrCategoryFormatter(object):
|
||||
test_cases = [("ascii", ["hello", "world", "hi"]),
|
||||
("unicode", ["Здравствуйте", "привет"])]
|
||||
|
||||
ids, cases = zip(*test_cases)
|
||||
|
||||
@pytest.mark.parametrize("ydata", cases, ids=ids)
|
||||
def test_StrCategoryFormatter(self, ax, ydata):
|
||||
unit = cat.UnitData(ydata)
|
||||
labels = cat.StrCategoryFormatter(unit._mapping)
|
||||
for i, d in enumerate(ydata):
|
||||
assert labels(i, i) == _to_str(d)
|
||||
|
||||
@pytest.mark.parametrize("ydata", cases, ids=ids)
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
def test_StrCategoryFormatterPlot(self, ax, ydata, plotter):
|
||||
plotter(ax, range(len(ydata)), ydata)
|
||||
for i, d in enumerate(ydata):
|
||||
assert ax.yaxis.major.formatter(i, i) == _to_str(d)
|
||||
assert ax.yaxis.major.formatter(i+1, i+1) == ""
|
||||
assert ax.yaxis.major.formatter(0, None) == ""
|
||||
|
||||
|
||||
def axis_test(axis, labels):
|
||||
ticks = list(range(len(labels)))
|
||||
np.testing.assert_array_equal(axis.get_majorticklocs(), ticks)
|
||||
graph_labels = [axis.major.formatter(i, i) for i in ticks]
|
||||
assert graph_labels == [_to_str(l) for l in labels]
|
||||
assert list(axis.units._mapping.keys()) == [l for l in labels]
|
||||
assert list(axis.units._mapping.values()) == ticks
|
||||
|
||||
|
||||
class TestPlotBytes(object):
|
||||
bytes_cases = [('string list', ['a', 'b', 'c']),
|
||||
('bytes list', [b'a', b'b', b'c']),
|
||||
('bytes ndarray', np.array([b'a', b'b', b'c']))]
|
||||
|
||||
bytes_ids, bytes_data = zip(*bytes_cases)
|
||||
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
@pytest.mark.parametrize("bdata", bytes_data, ids=bytes_ids)
|
||||
def test_plot_bytes(self, ax, plotter, bdata):
|
||||
counts = np.array([4, 6, 5])
|
||||
plotter(ax, bdata, counts)
|
||||
axis_test(ax.xaxis, bdata)
|
||||
|
||||
|
||||
class TestPlotNumlike(object):
|
||||
numlike_cases = [('string list', ['1', '11', '3']),
|
||||
('string ndarray', np.array(['1', '11', '3'])),
|
||||
('bytes list', [b'1', b'11', b'3']),
|
||||
('bytes ndarray', np.array([b'1', b'11', b'3']))]
|
||||
numlike_ids, numlike_data = zip(*numlike_cases)
|
||||
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
@pytest.mark.parametrize("ndata", numlike_data, ids=numlike_ids)
|
||||
def test_plot_numlike(self, ax, plotter, ndata):
|
||||
counts = np.array([4, 6, 5])
|
||||
plotter(ax, ndata, counts)
|
||||
axis_test(ax.xaxis, ndata)
|
||||
|
||||
|
||||
class TestPlotTypes(object):
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
def test_plot_unicode(self, ax, plotter):
|
||||
words = ['Здравствуйте', 'привет']
|
||||
plotter(ax, words, [0, 1])
|
||||
axis_test(ax.xaxis, words)
|
||||
|
||||
@pytest.fixture
|
||||
def test_data(self):
|
||||
self.x = ["hello", "happy", "world"]
|
||||
self.xy = [2, 6, 3]
|
||||
self.y = ["Python", "is", "fun"]
|
||||
self.yx = [3, 4, 5]
|
||||
|
||||
@pytest.mark.usefixtures("test_data")
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
def test_plot_xaxis(self, ax, test_data, plotter):
|
||||
plotter(ax, self.x, self.xy)
|
||||
axis_test(ax.xaxis, self.x)
|
||||
|
||||
@pytest.mark.usefixtures("test_data")
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
def test_plot_yaxis(self, ax, test_data, plotter):
|
||||
plotter(ax, self.yx, self.y)
|
||||
axis_test(ax.yaxis, self.y)
|
||||
|
||||
@pytest.mark.usefixtures("test_data")
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
def test_plot_xyaxis(self, ax, test_data, plotter):
|
||||
plotter(ax, self.x, self.y)
|
||||
axis_test(ax.xaxis, self.x)
|
||||
axis_test(ax.yaxis, self.y)
|
||||
|
||||
@pytest.mark.parametrize("plotter", PLOT_LIST, ids=PLOT_IDS)
|
||||
def test_update_plot(self, ax, plotter):
|
||||
plotter(ax, ['a', 'b'], ['e', 'g'])
|
||||
plotter(ax, ['a', 'b', 'd'], ['f', 'a', 'b'])
|
||||
plotter(ax, ['b', 'c', 'd'], ['g', 'e', 'd'])
|
||||
axis_test(ax.xaxis, ['a', 'b', 'd', 'c'])
|
||||
axis_test(ax.yaxis, ['e', 'g', 'f', 'a', 'b', 'd'])
|
||||
|
||||
failing_test_cases = [("mixed", ['A', 3.14]),
|
||||
("number integer", ['1', 1]),
|
||||
("string integer", ['42', 42]),
|
||||
("missing", ['12', np.nan])]
|
||||
|
||||
fids, fvalues = zip(*failing_test_cases)
|
||||
|
||||
PLOT_BROKEN_LIST = [Axes.scatter,
|
||||
pytest.param(Axes.plot, marks=pytest.mark.xfail),
|
||||
pytest.param(Axes.bar, marks=pytest.mark.xfail)]
|
||||
|
||||
PLOT_BROKEN_IDS = ["scatter", "plot", "bar"]
|
||||
|
||||
@pytest.mark.parametrize("plotter", PLOT_BROKEN_LIST, ids=PLOT_BROKEN_IDS)
|
||||
@pytest.mark.parametrize("xdata", fvalues, ids=fids)
|
||||
def test_mixed_type_exception(self, ax, plotter, xdata):
|
||||
with pytest.raises(TypeError):
|
||||
plotter(ax, xdata, [1, 2])
|
||||
|
||||
@pytest.mark.parametrize("plotter", PLOT_BROKEN_LIST, ids=PLOT_BROKEN_IDS)
|
||||
@pytest.mark.parametrize("xdata", fvalues, ids=fids)
|
||||
def test_mixed_type_update_exception(self, ax, plotter, xdata):
|
||||
with pytest.raises(TypeError):
|
||||
plotter(ax, [0, 3], [1, 3])
|
||||
plotter(ax, xdata, [1, 2])
|
||||
@@ -0,0 +1,501 @@
|
||||
import itertools
|
||||
import pickle
|
||||
from weakref import ref
|
||||
import warnings
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import (assert_array_equal, assert_approx_equal,
|
||||
assert_array_almost_equal)
|
||||
import pytest
|
||||
|
||||
import matplotlib.cbook as cbook
|
||||
import matplotlib.colors as mcolors
|
||||
from matplotlib.cbook import delete_masked_points as dmp
|
||||
|
||||
|
||||
def test_is_hashable():
|
||||
s = 'string'
|
||||
assert cbook.is_hashable(s)
|
||||
|
||||
lst = ['list', 'of', 'stings']
|
||||
assert not cbook.is_hashable(lst)
|
||||
|
||||
|
||||
class Test_delete_masked_points(object):
|
||||
def setup_method(self):
|
||||
self.mask1 = [False, False, True, True, False, False]
|
||||
self.arr0 = np.arange(1.0, 7.0)
|
||||
self.arr1 = [1, 2, 3, np.nan, np.nan, 6]
|
||||
self.arr2 = np.array(self.arr1)
|
||||
self.arr3 = np.ma.array(self.arr2, mask=self.mask1)
|
||||
self.arr_s = ['a', 'b', 'c', 'd', 'e', 'f']
|
||||
self.arr_s2 = np.array(self.arr_s)
|
||||
self.arr_dt = [datetime(2008, 1, 1), datetime(2008, 1, 2),
|
||||
datetime(2008, 1, 3), datetime(2008, 1, 4),
|
||||
datetime(2008, 1, 5), datetime(2008, 1, 6)]
|
||||
self.arr_dt2 = np.array(self.arr_dt)
|
||||
self.arr_colors = ['r', 'g', 'b', 'c', 'm', 'y']
|
||||
self.arr_rgba = mcolors.to_rgba_array(self.arr_colors)
|
||||
|
||||
def test_bad_first_arg(self):
|
||||
with pytest.raises(ValueError):
|
||||
dmp('a string', self.arr0)
|
||||
|
||||
def test_string_seq(self):
|
||||
actual = dmp(self.arr_s, self.arr1)
|
||||
ind = [0, 1, 2, 5]
|
||||
expected = (self.arr_s2.take(ind), self.arr2.take(ind))
|
||||
assert_array_equal(actual[0], expected[0])
|
||||
assert_array_equal(actual[1], expected[1])
|
||||
|
||||
def test_datetime(self):
|
||||
actual = dmp(self.arr_dt, self.arr3)
|
||||
ind = [0, 1, 5]
|
||||
expected = (self.arr_dt2.take(ind),
|
||||
self.arr3.take(ind).compressed())
|
||||
assert_array_equal(actual[0], expected[0])
|
||||
assert_array_equal(actual[1], expected[1])
|
||||
|
||||
def test_rgba(self):
|
||||
actual = dmp(self.arr3, self.arr_rgba)
|
||||
ind = [0, 1, 5]
|
||||
expected = (self.arr3.take(ind).compressed(),
|
||||
self.arr_rgba.take(ind, axis=0))
|
||||
assert_array_equal(actual[0], expected[0])
|
||||
assert_array_equal(actual[1], expected[1])
|
||||
|
||||
|
||||
class Test_boxplot_stats(object):
|
||||
def setup(self):
|
||||
np.random.seed(937)
|
||||
self.nrows = 37
|
||||
self.ncols = 4
|
||||
self.data = np.random.lognormal(size=(self.nrows, self.ncols),
|
||||
mean=1.5, sigma=1.75)
|
||||
self.known_keys = sorted([
|
||||
'mean', 'med', 'q1', 'q3', 'iqr',
|
||||
'cilo', 'cihi', 'whislo', 'whishi',
|
||||
'fliers', 'label'
|
||||
])
|
||||
self.std_results = cbook.boxplot_stats(self.data)
|
||||
|
||||
self.known_nonbootstrapped_res = {
|
||||
'cihi': 6.8161283264444847,
|
||||
'cilo': -0.1489815330368689,
|
||||
'iqr': 13.492709959447094,
|
||||
'mean': 13.00447442387868,
|
||||
'med': 3.3335733967038079,
|
||||
'fliers': np.array([
|
||||
92.55467075, 87.03819018, 42.23204914, 39.29390996
|
||||
]),
|
||||
'q1': 1.3597529879465153,
|
||||
'q3': 14.85246294739361,
|
||||
'whishi': 27.899688243699629,
|
||||
'whislo': 0.042143774965502923
|
||||
}
|
||||
|
||||
self.known_bootstrapped_ci = {
|
||||
'cihi': 8.939577523357828,
|
||||
'cilo': 1.8692703958676578,
|
||||
}
|
||||
|
||||
self.known_whis3_res = {
|
||||
'whishi': 42.232049135969874,
|
||||
'whislo': 0.042143774965502923,
|
||||
'fliers': np.array([92.55467075, 87.03819018]),
|
||||
}
|
||||
|
||||
self.known_res_percentiles = {
|
||||
'whislo': 0.1933685896907924,
|
||||
'whishi': 42.232049135969874
|
||||
}
|
||||
|
||||
self.known_res_range = {
|
||||
'whislo': 0.042143774965502923,
|
||||
'whishi': 92.554670752188699
|
||||
|
||||
}
|
||||
|
||||
def test_form_main_list(self):
|
||||
assert isinstance(self.std_results, list)
|
||||
|
||||
def test_form_each_dict(self):
|
||||
for res in self.std_results:
|
||||
assert isinstance(res, dict)
|
||||
|
||||
def test_form_dict_keys(self):
|
||||
for res in self.std_results:
|
||||
assert set(res) <= set(self.known_keys)
|
||||
|
||||
def test_results_baseline(self):
|
||||
res = self.std_results[0]
|
||||
for key, value in self.known_nonbootstrapped_res.items():
|
||||
assert_array_almost_equal(res[key], value)
|
||||
|
||||
def test_results_bootstrapped(self):
|
||||
results = cbook.boxplot_stats(self.data, bootstrap=10000)
|
||||
res = results[0]
|
||||
for key, value in self.known_bootstrapped_ci.items():
|
||||
assert_approx_equal(res[key], value)
|
||||
|
||||
def test_results_whiskers_float(self):
|
||||
results = cbook.boxplot_stats(self.data, whis=3)
|
||||
res = results[0]
|
||||
for key, value in self.known_whis3_res.items():
|
||||
assert_array_almost_equal(res[key], value)
|
||||
|
||||
def test_results_whiskers_range(self):
|
||||
results = cbook.boxplot_stats(self.data, whis='range')
|
||||
res = results[0]
|
||||
for key, value in self.known_res_range.items():
|
||||
assert_array_almost_equal(res[key], value)
|
||||
|
||||
def test_results_whiskers_percentiles(self):
|
||||
results = cbook.boxplot_stats(self.data, whis=[5, 95])
|
||||
res = results[0]
|
||||
for key, value in self.known_res_percentiles.items():
|
||||
assert_array_almost_equal(res[key], value)
|
||||
|
||||
def test_results_withlabels(self):
|
||||
labels = ['Test1', 2, 'ardvark', 4]
|
||||
results = cbook.boxplot_stats(self.data, labels=labels)
|
||||
res = results[0]
|
||||
for lab, res in zip(labels, results):
|
||||
assert res['label'] == lab
|
||||
|
||||
results = cbook.boxplot_stats(self.data)
|
||||
for res in results:
|
||||
assert 'label' not in res
|
||||
|
||||
def test_label_error(self):
|
||||
labels = [1, 2]
|
||||
with pytest.raises(ValueError):
|
||||
results = cbook.boxplot_stats(self.data, labels=labels)
|
||||
|
||||
def test_bad_dims(self):
|
||||
data = np.random.normal(size=(34, 34, 34))
|
||||
with pytest.raises(ValueError):
|
||||
results = cbook.boxplot_stats(data)
|
||||
|
||||
def test_boxplot_stats_autorange_false(self):
|
||||
x = np.zeros(shape=140)
|
||||
x = np.hstack([-25, x, 25])
|
||||
bstats_false = cbook.boxplot_stats(x, autorange=False)
|
||||
bstats_true = cbook.boxplot_stats(x, autorange=True)
|
||||
|
||||
assert bstats_false[0]['whislo'] == 0
|
||||
assert bstats_false[0]['whishi'] == 0
|
||||
assert_array_almost_equal(bstats_false[0]['fliers'], [-25, 25])
|
||||
|
||||
assert bstats_true[0]['whislo'] == -25
|
||||
assert bstats_true[0]['whishi'] == 25
|
||||
assert_array_almost_equal(bstats_true[0]['fliers'], [])
|
||||
|
||||
|
||||
class Test_callback_registry(object):
|
||||
def setup(self):
|
||||
self.signal = 'test'
|
||||
self.callbacks = cbook.CallbackRegistry()
|
||||
|
||||
def connect(self, s, func):
|
||||
return self.callbacks.connect(s, func)
|
||||
|
||||
def is_empty(self):
|
||||
assert self.callbacks._func_cid_map == {}
|
||||
assert self.callbacks.callbacks == {}
|
||||
|
||||
def is_not_empty(self):
|
||||
assert self.callbacks._func_cid_map != {}
|
||||
assert self.callbacks.callbacks != {}
|
||||
|
||||
def test_callback_complete(self):
|
||||
# ensure we start with an empty registry
|
||||
self.is_empty()
|
||||
|
||||
# create a class for testing
|
||||
mini_me = Test_callback_registry()
|
||||
|
||||
# test that we can add a callback
|
||||
cid1 = self.connect(self.signal, mini_me.dummy)
|
||||
assert type(cid1) == int
|
||||
self.is_not_empty()
|
||||
|
||||
# test that we don't add a second callback
|
||||
cid2 = self.connect(self.signal, mini_me.dummy)
|
||||
assert cid1 == cid2
|
||||
self.is_not_empty()
|
||||
assert len(self.callbacks._func_cid_map) == 1
|
||||
assert len(self.callbacks.callbacks) == 1
|
||||
|
||||
del mini_me
|
||||
|
||||
# check we now have no callbacks registered
|
||||
self.is_empty()
|
||||
|
||||
def dummy(self):
|
||||
pass
|
||||
|
||||
def test_pickling(self):
|
||||
assert hasattr(pickle.loads(pickle.dumps(cbook.CallbackRegistry())),
|
||||
"callbacks")
|
||||
|
||||
|
||||
def raising_cb_reg(func):
|
||||
class TestException(Exception):
|
||||
pass
|
||||
|
||||
def raising_function():
|
||||
raise RuntimeError
|
||||
|
||||
def transformer(excp):
|
||||
if isinstance(excp, RuntimeError):
|
||||
raise TestException
|
||||
raise excp
|
||||
|
||||
# default behavior
|
||||
cb = cbook.CallbackRegistry()
|
||||
cb.connect('foo', raising_function)
|
||||
|
||||
# old default
|
||||
cb_old = cbook.CallbackRegistry(exception_handler=None)
|
||||
cb_old.connect('foo', raising_function)
|
||||
|
||||
# filter
|
||||
cb_filt = cbook.CallbackRegistry(exception_handler=transformer)
|
||||
cb_filt.connect('foo', raising_function)
|
||||
|
||||
return pytest.mark.parametrize('cb, excp',
|
||||
[[cb, None],
|
||||
[cb_old, RuntimeError],
|
||||
[cb_filt, TestException]])(func)
|
||||
|
||||
|
||||
@raising_cb_reg
|
||||
def test_callbackregistry_process_exception(cb, excp):
|
||||
if excp is not None:
|
||||
with pytest.raises(excp):
|
||||
cb.process('foo')
|
||||
else:
|
||||
cb.process('foo')
|
||||
|
||||
|
||||
def test_sanitize_sequence():
|
||||
d = {'a': 1, 'b': 2, 'c': 3}
|
||||
k = ['a', 'b', 'c']
|
||||
v = [1, 2, 3]
|
||||
i = [('a', 1), ('b', 2), ('c', 3)]
|
||||
assert k == sorted(cbook.sanitize_sequence(d.keys()))
|
||||
assert v == sorted(cbook.sanitize_sequence(d.values()))
|
||||
assert i == sorted(cbook.sanitize_sequence(d.items()))
|
||||
assert i == cbook.sanitize_sequence(i)
|
||||
assert k == cbook.sanitize_sequence(k)
|
||||
|
||||
|
||||
fail_mapping = (
|
||||
({'a': 1}, {'forbidden': ('a')}),
|
||||
({'a': 1}, {'required': ('b')}),
|
||||
({'a': 1, 'b': 2}, {'required': ('a'), 'allowed': ()})
|
||||
)
|
||||
|
||||
warn_passing_mapping = (
|
||||
({'a': 1, 'b': 2}, {'a': 1}, {'alias_mapping': {'a': ['b']}}, 1),
|
||||
({'a': 1, 'b': 2}, {'a': 1},
|
||||
{'alias_mapping': {'a': ['b']}, 'allowed': ('a',)}, 1),
|
||||
({'a': 1, 'b': 2}, {'a': 2}, {'alias_mapping': {'a': ['a', 'b']}}, 1),
|
||||
({'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'c': 3},
|
||||
{'alias_mapping': {'a': ['b']}, 'required': ('a', )}, 1),
|
||||
)
|
||||
|
||||
pass_mapping = (
|
||||
({'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {}),
|
||||
({'b': 2}, {'a': 2}, {'alias_mapping': {'a': ['a', 'b']}}),
|
||||
({'b': 2}, {'a': 2},
|
||||
{'alias_mapping': {'a': ['b']}, 'forbidden': ('b', )}),
|
||||
({'a': 1, 'c': 3}, {'a': 1, 'c': 3},
|
||||
{'required': ('a', ), 'allowed': ('c', )}),
|
||||
({'a': 1, 'c': 3}, {'a': 1, 'c': 3},
|
||||
{'required': ('a', 'c'), 'allowed': ('c', )}),
|
||||
({'a': 1, 'c': 3}, {'a': 1, 'c': 3},
|
||||
{'required': ('a', 'c'), 'allowed': ('a', 'c')}),
|
||||
({'a': 1, 'c': 3}, {'a': 1, 'c': 3},
|
||||
{'required': ('a', 'c'), 'allowed': ()}),
|
||||
({'a': 1, 'c': 3}, {'a': 1, 'c': 3}, {'required': ('a', 'c')}),
|
||||
({'a': 1, 'c': 3}, {'a': 1, 'c': 3}, {'allowed': ('a', 'c')}),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('inp, kwargs_to_norm', fail_mapping)
|
||||
def test_normalize_kwargs_fail(inp, kwargs_to_norm):
|
||||
with pytest.raises(TypeError):
|
||||
cbook.normalize_kwargs(inp, **kwargs_to_norm)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('inp, expected, kwargs_to_norm, warn_count',
|
||||
warn_passing_mapping)
|
||||
def test_normalize_kwargs_warn(inp, expected, kwargs_to_norm, warn_count):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
assert expected == cbook.normalize_kwargs(inp, **kwargs_to_norm)
|
||||
assert len(w) == warn_count
|
||||
|
||||
|
||||
@pytest.mark.parametrize('inp, expected, kwargs_to_norm',
|
||||
pass_mapping)
|
||||
def test_normalize_kwargs_pass(inp, expected, kwargs_to_norm):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
assert expected == cbook.normalize_kwargs(inp, **kwargs_to_norm)
|
||||
assert len(w) == 0
|
||||
|
||||
|
||||
def test_warn_external_frame_embedded_python():
|
||||
with patch.object(cbook, "sys") as mock_sys:
|
||||
mock_sys._getframe = Mock(return_value=None)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cbook._warn_external("dummy")
|
||||
assert len(w) == 1
|
||||
assert str(w[0].message) == "dummy"
|
||||
|
||||
|
||||
def test_to_prestep():
|
||||
x = np.arange(4)
|
||||
y1 = np.arange(4)
|
||||
y2 = np.arange(4)[::-1]
|
||||
|
||||
xs, y1s, y2s = cbook.pts_to_prestep(x, y1, y2)
|
||||
|
||||
x_target = np.asarray([0, 0, 1, 1, 2, 2, 3], dtype='float')
|
||||
y1_target = np.asarray([0, 1, 1, 2, 2, 3, 3], dtype='float')
|
||||
y2_target = np.asarray([3, 2, 2, 1, 1, 0, 0], dtype='float')
|
||||
|
||||
assert_array_equal(x_target, xs)
|
||||
assert_array_equal(y1_target, y1s)
|
||||
assert_array_equal(y2_target, y2s)
|
||||
|
||||
xs, y1s = cbook.pts_to_prestep(x, y1)
|
||||
assert_array_equal(x_target, xs)
|
||||
assert_array_equal(y1_target, y1s)
|
||||
|
||||
|
||||
def test_to_prestep_empty():
|
||||
steps = cbook.pts_to_prestep([], [])
|
||||
assert steps.shape == (2, 0)
|
||||
|
||||
|
||||
def test_to_poststep():
|
||||
x = np.arange(4)
|
||||
y1 = np.arange(4)
|
||||
y2 = np.arange(4)[::-1]
|
||||
|
||||
xs, y1s, y2s = cbook.pts_to_poststep(x, y1, y2)
|
||||
|
||||
x_target = np.asarray([0, 1, 1, 2, 2, 3, 3], dtype='float')
|
||||
y1_target = np.asarray([0, 0, 1, 1, 2, 2, 3], dtype='float')
|
||||
y2_target = np.asarray([3, 3, 2, 2, 1, 1, 0], dtype='float')
|
||||
|
||||
assert_array_equal(x_target, xs)
|
||||
assert_array_equal(y1_target, y1s)
|
||||
assert_array_equal(y2_target, y2s)
|
||||
|
||||
xs, y1s = cbook.pts_to_poststep(x, y1)
|
||||
assert_array_equal(x_target, xs)
|
||||
assert_array_equal(y1_target, y1s)
|
||||
|
||||
|
||||
def test_to_poststep_empty():
|
||||
steps = cbook.pts_to_poststep([], [])
|
||||
assert steps.shape == (2, 0)
|
||||
|
||||
|
||||
def test_to_midstep():
|
||||
x = np.arange(4)
|
||||
y1 = np.arange(4)
|
||||
y2 = np.arange(4)[::-1]
|
||||
|
||||
xs, y1s, y2s = cbook.pts_to_midstep(x, y1, y2)
|
||||
|
||||
x_target = np.asarray([0, .5, .5, 1.5, 1.5, 2.5, 2.5, 3], dtype='float')
|
||||
y1_target = np.asarray([0, 0, 1, 1, 2, 2, 3, 3], dtype='float')
|
||||
y2_target = np.asarray([3, 3, 2, 2, 1, 1, 0, 0], dtype='float')
|
||||
|
||||
assert_array_equal(x_target, xs)
|
||||
assert_array_equal(y1_target, y1s)
|
||||
assert_array_equal(y2_target, y2s)
|
||||
|
||||
xs, y1s = cbook.pts_to_midstep(x, y1)
|
||||
assert_array_equal(x_target, xs)
|
||||
assert_array_equal(y1_target, y1s)
|
||||
|
||||
|
||||
def test_to_midstep_empty():
|
||||
steps = cbook.pts_to_midstep([], [])
|
||||
assert steps.shape == (2, 0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"args",
|
||||
[(np.arange(12).reshape(3, 4), 'a'),
|
||||
(np.arange(12), 'a'),
|
||||
(np.arange(12), np.arange(3))])
|
||||
def test_step_fails(args):
|
||||
with pytest.raises(ValueError):
|
||||
cbook.pts_to_prestep(*args)
|
||||
|
||||
|
||||
def test_grouper():
|
||||
class dummy():
|
||||
pass
|
||||
a, b, c, d, e = objs = [dummy() for j in range(5)]
|
||||
g = cbook.Grouper()
|
||||
g.join(*objs)
|
||||
assert set(list(g)[0]) == set(objs)
|
||||
assert set(g.get_siblings(a)) == set(objs)
|
||||
|
||||
for other in objs[1:]:
|
||||
assert g.joined(a, other)
|
||||
|
||||
g.remove(a)
|
||||
for other in objs[1:]:
|
||||
assert not g.joined(a, other)
|
||||
|
||||
for A, B in itertools.product(objs[1:], objs[1:]):
|
||||
assert g.joined(A, B)
|
||||
|
||||
|
||||
def test_grouper_private():
|
||||
class dummy():
|
||||
pass
|
||||
objs = [dummy() for j in range(5)]
|
||||
g = cbook.Grouper()
|
||||
g.join(*objs)
|
||||
# reach in and touch the internals !
|
||||
mapping = g._mapping
|
||||
|
||||
for o in objs:
|
||||
assert ref(o) in mapping
|
||||
|
||||
base_set = mapping[ref(objs[0])]
|
||||
for o in objs[1:]:
|
||||
assert mapping[ref(o)] is base_set
|
||||
|
||||
|
||||
def test_flatiter():
|
||||
x = np.arange(5)
|
||||
it = x.flat
|
||||
assert 0 == next(it)
|
||||
assert 1 == next(it)
|
||||
ret = cbook.safe_first_element(it)
|
||||
assert ret == 0
|
||||
|
||||
assert 0 == next(it)
|
||||
assert 1 == next(it)
|
||||
|
||||
|
||||
def test_safe_first_element_pandas_series(pd):
|
||||
# delibrately create a pandas series with index not starting from 0
|
||||
s = pd.Series(range(5), index=range(10, 15))
|
||||
actual = cbook.safe_first_element(s)
|
||||
assert actual == 0
|
||||
@@ -0,0 +1,671 @@
|
||||
"""
|
||||
Tests specific to the collections module.
|
||||
"""
|
||||
import io
|
||||
import platform
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal, assert_array_almost_equal
|
||||
import pytest
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.collections as mcollections
|
||||
import matplotlib.transforms as mtransforms
|
||||
from matplotlib.collections import Collection, LineCollection, EventCollection
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
|
||||
def generate_EventCollection_plot():
|
||||
'''
|
||||
generate the initial collection and plot it
|
||||
'''
|
||||
positions = np.array([0., 1., 2., 3., 5., 8., 13., 21.])
|
||||
extra_positions = np.array([34., 55., 89.])
|
||||
orientation = 'horizontal'
|
||||
lineoffset = 1
|
||||
linelength = .5
|
||||
linewidth = 2
|
||||
color = [1, 0, 0, 1]
|
||||
linestyle = 'solid'
|
||||
antialiased = True
|
||||
|
||||
coll = EventCollection(positions,
|
||||
orientation=orientation,
|
||||
lineoffset=lineoffset,
|
||||
linelength=linelength,
|
||||
linewidth=linewidth,
|
||||
color=color,
|
||||
linestyle=linestyle,
|
||||
antialiased=antialiased
|
||||
)
|
||||
|
||||
fig = plt.figure()
|
||||
splt = fig.add_subplot(1, 1, 1)
|
||||
splt.add_collection(coll)
|
||||
splt.set_title('EventCollection: default')
|
||||
props = {'positions': positions,
|
||||
'extra_positions': extra_positions,
|
||||
'orientation': orientation,
|
||||
'lineoffset': lineoffset,
|
||||
'linelength': linelength,
|
||||
'linewidth': linewidth,
|
||||
'color': color,
|
||||
'linestyle': linestyle,
|
||||
'antialiased': antialiased
|
||||
}
|
||||
splt.set_xlim(-1, 22)
|
||||
splt.set_ylim(0, 2)
|
||||
return splt, coll, props
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__default'])
|
||||
def test__EventCollection__get_segments():
|
||||
'''
|
||||
check to make sure the default segments have the correct coordinates
|
||||
'''
|
||||
_, coll, props = generate_EventCollection_plot()
|
||||
check_segments(coll,
|
||||
props['positions'],
|
||||
props['linelength'],
|
||||
props['lineoffset'],
|
||||
props['orientation'])
|
||||
|
||||
|
||||
def test__EventCollection__get_positions():
|
||||
'''
|
||||
check to make sure the default positions match the input positions
|
||||
'''
|
||||
_, coll, props = generate_EventCollection_plot()
|
||||
np.testing.assert_array_equal(props['positions'], coll.get_positions())
|
||||
|
||||
|
||||
def test__EventCollection__get_orientation():
|
||||
'''
|
||||
check to make sure the default orientation matches the input
|
||||
orientation
|
||||
'''
|
||||
_, coll, props = generate_EventCollection_plot()
|
||||
assert props['orientation'] == coll.get_orientation()
|
||||
|
||||
|
||||
def test__EventCollection__is_horizontal():
|
||||
'''
|
||||
check to make sure the default orientation matches the input
|
||||
orientation
|
||||
'''
|
||||
_, coll, _ = generate_EventCollection_plot()
|
||||
assert coll.is_horizontal()
|
||||
|
||||
|
||||
def test__EventCollection__get_linelength():
|
||||
'''
|
||||
check to make sure the default linelength matches the input linelength
|
||||
'''
|
||||
_, coll, props = generate_EventCollection_plot()
|
||||
assert props['linelength'] == coll.get_linelength()
|
||||
|
||||
|
||||
def test__EventCollection__get_lineoffset():
|
||||
'''
|
||||
check to make sure the default lineoffset matches the input lineoffset
|
||||
'''
|
||||
_, coll, props = generate_EventCollection_plot()
|
||||
assert props['lineoffset'] == coll.get_lineoffset()
|
||||
|
||||
|
||||
def test__EventCollection__get_linestyle():
|
||||
'''
|
||||
check to make sure the default linestyle matches the input linestyle
|
||||
'''
|
||||
_, coll, _ = generate_EventCollection_plot()
|
||||
assert coll.get_linestyle() == [(None, None)]
|
||||
|
||||
|
||||
def test__EventCollection__get_color():
|
||||
'''
|
||||
check to make sure the default color matches the input color
|
||||
'''
|
||||
_, coll, props = generate_EventCollection_plot()
|
||||
np.testing.assert_array_equal(props['color'], coll.get_color())
|
||||
check_allprop_array(coll.get_colors(), props['color'])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__set_positions'])
|
||||
def test__EventCollection__set_positions():
|
||||
'''
|
||||
check to make sure set_positions works properly
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
new_positions = np.hstack([props['positions'], props['extra_positions']])
|
||||
coll.set_positions(new_positions)
|
||||
np.testing.assert_array_equal(new_positions, coll.get_positions())
|
||||
check_segments(coll, new_positions,
|
||||
props['linelength'],
|
||||
props['lineoffset'],
|
||||
props['orientation'])
|
||||
splt.set_title('EventCollection: set_positions')
|
||||
splt.set_xlim(-1, 90)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__add_positions'])
|
||||
def test__EventCollection__add_positions():
|
||||
'''
|
||||
check to make sure add_positions works properly
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
new_positions = np.hstack([props['positions'],
|
||||
props['extra_positions'][0]])
|
||||
coll.add_positions(props['extra_positions'][0])
|
||||
np.testing.assert_array_equal(new_positions, coll.get_positions())
|
||||
check_segments(coll,
|
||||
new_positions,
|
||||
props['linelength'],
|
||||
props['lineoffset'],
|
||||
props['orientation'])
|
||||
splt.set_title('EventCollection: add_positions')
|
||||
splt.set_xlim(-1, 35)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__append_positions'])
|
||||
def test__EventCollection__append_positions():
|
||||
'''
|
||||
check to make sure append_positions works properly
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
new_positions = np.hstack([props['positions'],
|
||||
props['extra_positions'][2]])
|
||||
coll.append_positions(props['extra_positions'][2])
|
||||
np.testing.assert_array_equal(new_positions, coll.get_positions())
|
||||
check_segments(coll,
|
||||
new_positions,
|
||||
props['linelength'],
|
||||
props['lineoffset'],
|
||||
props['orientation'])
|
||||
splt.set_title('EventCollection: append_positions')
|
||||
splt.set_xlim(-1, 90)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__extend_positions'])
|
||||
def test__EventCollection__extend_positions():
|
||||
'''
|
||||
check to make sure extend_positions works properly
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
new_positions = np.hstack([props['positions'],
|
||||
props['extra_positions'][1:]])
|
||||
coll.extend_positions(props['extra_positions'][1:])
|
||||
np.testing.assert_array_equal(new_positions, coll.get_positions())
|
||||
check_segments(coll,
|
||||
new_positions,
|
||||
props['linelength'],
|
||||
props['lineoffset'],
|
||||
props['orientation'])
|
||||
splt.set_title('EventCollection: extend_positions')
|
||||
splt.set_xlim(-1, 90)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__switch_orientation'])
|
||||
def test__EventCollection__switch_orientation():
|
||||
'''
|
||||
check to make sure switch_orientation works properly
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
new_orientation = 'vertical'
|
||||
coll.switch_orientation()
|
||||
assert new_orientation == coll.get_orientation()
|
||||
assert not coll.is_horizontal()
|
||||
new_positions = coll.get_positions()
|
||||
check_segments(coll,
|
||||
new_positions,
|
||||
props['linelength'],
|
||||
props['lineoffset'], new_orientation)
|
||||
splt.set_title('EventCollection: switch_orientation')
|
||||
splt.set_ylim(-1, 22)
|
||||
splt.set_xlim(0, 2)
|
||||
|
||||
|
||||
@image_comparison(
|
||||
baseline_images=['EventCollection_plot__switch_orientation__2x'])
|
||||
def test__EventCollection__switch_orientation_2x():
|
||||
'''
|
||||
check to make sure calling switch_orientation twice sets the
|
||||
orientation back to the default
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
coll.switch_orientation()
|
||||
coll.switch_orientation()
|
||||
new_positions = coll.get_positions()
|
||||
assert props['orientation'] == coll.get_orientation()
|
||||
assert coll.is_horizontal()
|
||||
np.testing.assert_array_equal(props['positions'], new_positions)
|
||||
check_segments(coll,
|
||||
new_positions,
|
||||
props['linelength'],
|
||||
props['lineoffset'],
|
||||
props['orientation'])
|
||||
splt.set_title('EventCollection: switch_orientation 2x')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__set_orientation'])
|
||||
def test__EventCollection__set_orientation():
|
||||
'''
|
||||
check to make sure set_orientation works properly
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
new_orientation = 'vertical'
|
||||
coll.set_orientation(new_orientation)
|
||||
assert new_orientation == coll.get_orientation()
|
||||
assert not coll.is_horizontal()
|
||||
check_segments(coll,
|
||||
props['positions'],
|
||||
props['linelength'],
|
||||
props['lineoffset'],
|
||||
new_orientation)
|
||||
splt.set_title('EventCollection: set_orientation')
|
||||
splt.set_ylim(-1, 22)
|
||||
splt.set_xlim(0, 2)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__set_linelength'])
|
||||
def test__EventCollection__set_linelength():
|
||||
'''
|
||||
check to make sure set_linelength works properly
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
new_linelength = 15
|
||||
coll.set_linelength(new_linelength)
|
||||
assert new_linelength == coll.get_linelength()
|
||||
check_segments(coll,
|
||||
props['positions'],
|
||||
new_linelength,
|
||||
props['lineoffset'],
|
||||
props['orientation'])
|
||||
splt.set_title('EventCollection: set_linelength')
|
||||
splt.set_ylim(-20, 20)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__set_lineoffset'])
|
||||
def test__EventCollection__set_lineoffset():
|
||||
'''
|
||||
check to make sure set_lineoffset works properly
|
||||
'''
|
||||
splt, coll, props = generate_EventCollection_plot()
|
||||
new_lineoffset = -5.
|
||||
coll.set_lineoffset(new_lineoffset)
|
||||
assert new_lineoffset == coll.get_lineoffset()
|
||||
check_segments(coll,
|
||||
props['positions'],
|
||||
props['linelength'],
|
||||
new_lineoffset,
|
||||
props['orientation'])
|
||||
splt.set_title('EventCollection: set_lineoffset')
|
||||
splt.set_ylim(-6, -4)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__set_linestyle'])
|
||||
def test__EventCollection__set_linestyle():
|
||||
'''
|
||||
check to make sure set_linestyle works properly
|
||||
'''
|
||||
splt, coll, _ = generate_EventCollection_plot()
|
||||
new_linestyle = 'dashed'
|
||||
coll.set_linestyle(new_linestyle)
|
||||
assert coll.get_linestyle() == [(0, (6.0, 6.0))]
|
||||
splt.set_title('EventCollection: set_linestyle')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__set_ls_dash'],
|
||||
remove_text=True)
|
||||
def test__EventCollection__set_linestyle_single_dash():
|
||||
'''
|
||||
check to make sure set_linestyle accepts a single dash pattern
|
||||
'''
|
||||
splt, coll, _ = generate_EventCollection_plot()
|
||||
new_linestyle = (0, (6., 6.))
|
||||
coll.set_linestyle(new_linestyle)
|
||||
assert coll.get_linestyle() == [(0, (6.0, 6.0))]
|
||||
splt.set_title('EventCollection: set_linestyle')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__set_linewidth'])
|
||||
def test__EventCollection__set_linewidth():
|
||||
'''
|
||||
check to make sure set_linestyle works properly
|
||||
'''
|
||||
splt, coll, _ = generate_EventCollection_plot()
|
||||
new_linewidth = 5
|
||||
coll.set_linewidth(new_linewidth)
|
||||
assert coll.get_linewidth() == new_linewidth
|
||||
splt.set_title('EventCollection: set_linewidth')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EventCollection_plot__set_color'])
|
||||
def test__EventCollection__set_color():
|
||||
'''
|
||||
check to make sure set_color works properly
|
||||
'''
|
||||
splt, coll, _ = generate_EventCollection_plot()
|
||||
new_color = np.array([0, 1, 1, 1])
|
||||
coll.set_color(new_color)
|
||||
np.testing.assert_array_equal(new_color, coll.get_color())
|
||||
check_allprop_array(coll.get_colors(), new_color)
|
||||
splt.set_title('EventCollection: set_color')
|
||||
|
||||
|
||||
def check_segments(coll, positions, linelength, lineoffset, orientation):
|
||||
'''
|
||||
check to make sure all values in the segment are correct, given a
|
||||
particular set of inputs
|
||||
|
||||
note: this is not a test, it is used by tests
|
||||
'''
|
||||
segments = coll.get_segments()
|
||||
if (orientation.lower() == 'horizontal'
|
||||
or orientation.lower() == 'none' or orientation is None):
|
||||
# if horizontal, the position in is in the y-axis
|
||||
pos1 = 1
|
||||
pos2 = 0
|
||||
elif orientation.lower() == 'vertical':
|
||||
# if vertical, the position in is in the x-axis
|
||||
pos1 = 0
|
||||
pos2 = 1
|
||||
else:
|
||||
raise ValueError("orientation must be 'horizontal' or 'vertical'")
|
||||
|
||||
# test to make sure each segment is correct
|
||||
for i, segment in enumerate(segments):
|
||||
assert segment[0, pos1] == lineoffset + linelength / 2
|
||||
assert segment[1, pos1] == lineoffset - linelength / 2
|
||||
assert segment[0, pos2] == positions[i]
|
||||
assert segment[1, pos2] == positions[i]
|
||||
|
||||
|
||||
def check_allprop_array(values, target):
|
||||
'''
|
||||
check to make sure all values match the given target if arrays
|
||||
|
||||
note: this is not a test, it is used by tests
|
||||
'''
|
||||
for value in values:
|
||||
np.testing.assert_array_equal(value, target)
|
||||
|
||||
|
||||
def test_null_collection_datalim():
|
||||
col = mcollections.PathCollection([])
|
||||
col_data_lim = col.get_datalim(mtransforms.IdentityTransform())
|
||||
assert_array_equal(col_data_lim.get_points(),
|
||||
mtransforms.Bbox.null().get_points())
|
||||
|
||||
|
||||
def test_add_collection():
|
||||
# Test if data limits are unchanged by adding an empty collection.
|
||||
# Github issue #1490, pull #1497.
|
||||
plt.figure()
|
||||
ax = plt.axes()
|
||||
coll = ax.scatter([0, 1], [0, 1])
|
||||
ax.add_collection(coll)
|
||||
bounds = ax.dataLim.bounds
|
||||
coll = ax.scatter([], [])
|
||||
assert ax.dataLim.bounds == bounds
|
||||
|
||||
|
||||
def test_quiver_limits():
|
||||
ax = plt.axes()
|
||||
x, y = np.arange(8), np.arange(10)
|
||||
u = v = np.linspace(0, 10, 80).reshape(10, 8)
|
||||
q = plt.quiver(x, y, u, v)
|
||||
assert q.get_datalim(ax.transData).bounds == (0., 0., 7., 9.)
|
||||
|
||||
plt.figure()
|
||||
ax = plt.axes()
|
||||
x = np.linspace(-5, 10, 20)
|
||||
y = np.linspace(-2, 4, 10)
|
||||
y, x = np.meshgrid(y, x)
|
||||
trans = mtransforms.Affine2D().translate(25, 32) + ax.transData
|
||||
plt.quiver(x, y, np.sin(x), np.cos(y), transform=trans)
|
||||
assert ax.dataLim.bounds == (20.0, 30.0, 15.0, 6.0)
|
||||
|
||||
|
||||
def test_barb_limits():
|
||||
ax = plt.axes()
|
||||
x = np.linspace(-5, 10, 20)
|
||||
y = np.linspace(-2, 4, 10)
|
||||
y, x = np.meshgrid(y, x)
|
||||
trans = mtransforms.Affine2D().translate(25, 32) + ax.transData
|
||||
plt.barbs(x, y, np.sin(x), np.cos(y), transform=trans)
|
||||
# The calculated bounds are approximately the bounds of the original data,
|
||||
# this is because the entire path is taken into account when updating the
|
||||
# datalim.
|
||||
assert_array_almost_equal(ax.dataLim.bounds, (20, 30, 15, 6),
|
||||
decimal=1)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['EllipseCollection_test_image'],
|
||||
extensions=['png'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
remove_text=True)
|
||||
def test_EllipseCollection():
|
||||
# Test basic functionality
|
||||
fig, ax = plt.subplots()
|
||||
x = np.arange(4)
|
||||
y = np.arange(3)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
XY = np.vstack((X.ravel(), Y.ravel())).T
|
||||
|
||||
ww = X / x[-1]
|
||||
hh = Y / y[-1]
|
||||
aa = np.ones_like(ww) * 20 # first axis is 20 degrees CCW from x axis
|
||||
|
||||
ec = mcollections.EllipseCollection(ww, hh, aa,
|
||||
units='x',
|
||||
offsets=XY,
|
||||
transOffset=ax.transData,
|
||||
facecolors='none')
|
||||
ax.add_collection(ec)
|
||||
ax.autoscale_view()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['polycollection_close'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_polycollection_close():
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
vertsQuad = [
|
||||
[[0., 0.], [0., 1.], [1., 1.], [1., 0.]],
|
||||
[[0., 1.], [2., 3.], [2., 2.], [1., 1.]],
|
||||
[[2., 2.], [2., 3.], [4., 1.], [3., 1.]],
|
||||
[[3., 0.], [3., 1.], [4., 1.], [4., 0.]]]
|
||||
|
||||
fig = plt.figure()
|
||||
ax = Axes3D(fig)
|
||||
|
||||
colors = ['r', 'g', 'b', 'y', 'k']
|
||||
zpos = list(range(5))
|
||||
|
||||
poly = mcollections.PolyCollection(
|
||||
vertsQuad * len(zpos), linewidth=0.25)
|
||||
poly.set_alpha(0.7)
|
||||
|
||||
# need to have a z-value for *each* polygon = element!
|
||||
zs = []
|
||||
cs = []
|
||||
for z, c in zip(zpos, colors):
|
||||
zs.extend([z] * len(vertsQuad))
|
||||
cs.extend([c] * len(vertsQuad))
|
||||
|
||||
poly.set_color(cs)
|
||||
|
||||
ax.add_collection3d(poly, zs=zs, zdir='y')
|
||||
|
||||
# axis limit settings:
|
||||
ax.set_xlim3d(0, 4)
|
||||
ax.set_zlim3d(0, 3)
|
||||
ax.set_ylim3d(0, 4)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['regularpolycollection_rotate'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_regularpolycollection_rotate():
|
||||
xx, yy = np.mgrid[:10, :10]
|
||||
xy_points = np.transpose([xx.flatten(), yy.flatten()])
|
||||
rotations = np.linspace(0, 2*np.pi, len(xy_points))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
for xy, alpha in zip(xy_points, rotations):
|
||||
col = mcollections.RegularPolyCollection(
|
||||
4, sizes=(100,), rotation=alpha,
|
||||
offsets=[xy], transOffset=ax.transData)
|
||||
ax.add_collection(col, autolim=True)
|
||||
ax.autoscale_view()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['regularpolycollection_scale'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_regularpolycollection_scale():
|
||||
# See issue #3860
|
||||
|
||||
class SquareCollection(mcollections.RegularPolyCollection):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(4, rotation=np.pi/4., **kwargs)
|
||||
|
||||
def get_transform(self):
|
||||
"""Return transform scaling circle areas to data space."""
|
||||
ax = self.axes
|
||||
|
||||
pts2pixels = 72.0 / ax.figure.dpi
|
||||
|
||||
scale_x = pts2pixels * ax.bbox.width / ax.viewLim.width
|
||||
scale_y = pts2pixels * ax.bbox.height / ax.viewLim.height
|
||||
return mtransforms.Affine2D().scale(scale_x, scale_y)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
xy = [(0, 0)]
|
||||
# Unit square has a half-diagonal of `1 / sqrt(2)`, so `pi * r**2`
|
||||
# equals...
|
||||
circle_areas = [np.pi / 2]
|
||||
squares = SquareCollection(sizes=circle_areas, offsets=xy,
|
||||
transOffset=ax.transData)
|
||||
ax.add_collection(squares, autolim=True)
|
||||
ax.axis([-1, 1, -1, 1])
|
||||
|
||||
|
||||
def test_picking():
|
||||
fig, ax = plt.subplots()
|
||||
col = ax.scatter([0], [0], [1000], picker=True)
|
||||
fig.savefig(io.BytesIO(), dpi=fig.dpi)
|
||||
|
||||
class MouseEvent(object):
|
||||
pass
|
||||
event = MouseEvent()
|
||||
event.x = 325
|
||||
event.y = 240
|
||||
|
||||
found, indices = col.contains(event)
|
||||
assert found
|
||||
assert_array_equal(indices['ind'], [0])
|
||||
|
||||
|
||||
def test_linestyle_single_dashes():
|
||||
plt.scatter([0, 1, 2], [0, 1, 2], linestyle=(0., [2., 2.]))
|
||||
plt.draw()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['size_in_xy'], remove_text=True,
|
||||
extensions=['png'])
|
||||
def test_size_in_xy():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
widths, heights, angles = (10, 10), 10, 0
|
||||
widths = 10, 10
|
||||
coords = [(10, 10), (15, 15)]
|
||||
e = mcollections.EllipseCollection(
|
||||
widths, heights, angles,
|
||||
units='xy',
|
||||
offsets=coords,
|
||||
transOffset=ax.transData)
|
||||
|
||||
ax.add_collection(e)
|
||||
|
||||
ax.set_xlim(0, 30)
|
||||
ax.set_ylim(0, 30)
|
||||
|
||||
|
||||
def test_pandas_indexing(pd):
|
||||
|
||||
# Should not fail break when faced with a
|
||||
# non-zero indexed series
|
||||
index = [11, 12, 13]
|
||||
ec = fc = pd.Series(['red', 'blue', 'green'], index=index)
|
||||
lw = pd.Series([1, 2, 3], index=index)
|
||||
ls = pd.Series(['solid', 'dashed', 'dashdot'], index=index)
|
||||
aa = pd.Series([True, False, True], index=index)
|
||||
|
||||
Collection(edgecolors=ec)
|
||||
Collection(facecolors=fc)
|
||||
Collection(linewidths=lw)
|
||||
Collection(linestyles=ls)
|
||||
Collection(antialiaseds=aa)
|
||||
|
||||
|
||||
@pytest.mark.style('default')
|
||||
def test_lslw_bcast():
|
||||
col = mcollections.PathCollection([])
|
||||
col.set_linestyles(['-', '-'])
|
||||
col.set_linewidths([1, 2, 3])
|
||||
|
||||
assert col.get_linestyles() == [(None, None)] * 6
|
||||
assert col.get_linewidths() == [1, 2, 3] * 2
|
||||
|
||||
col.set_linestyles(['-', '-', '-'])
|
||||
assert col.get_linestyles() == [(None, None)] * 3
|
||||
assert (col.get_linewidths() == [1, 2, 3]).all()
|
||||
|
||||
|
||||
@pytest.mark.style('default')
|
||||
def test_capstyle():
|
||||
col = mcollections.PathCollection([], capstyle='round')
|
||||
assert col.get_capstyle() == 'round'
|
||||
col.set_capstyle('butt')
|
||||
assert col.get_capstyle() == 'butt'
|
||||
|
||||
|
||||
@pytest.mark.style('default')
|
||||
def test_joinstyle():
|
||||
col = mcollections.PathCollection([], joinstyle='round')
|
||||
assert col.get_joinstyle() == 'round'
|
||||
col.set_joinstyle('miter')
|
||||
assert col.get_joinstyle() == 'miter'
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['cap_and_joinstyle'],
|
||||
extensions=['png'])
|
||||
def test_cap_and_joinstyle_image():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.set_xlim([-0.5, 1.5])
|
||||
ax.set_ylim([-0.5, 2.5])
|
||||
|
||||
x = np.array([0.0, 1.0, 0.5])
|
||||
ys = np.array([[0.0], [0.5], [1.0]]) + np.array([[0.0, 0.0, 1.0]])
|
||||
|
||||
segs = np.zeros((3, 3, 2))
|
||||
segs[:, :, 0] = x
|
||||
segs[:, :, 1] = ys
|
||||
line_segments = LineCollection(segs, linewidth=[10, 15, 20])
|
||||
line_segments.set_capstyle("round")
|
||||
line_segments.set_joinstyle("miter")
|
||||
|
||||
ax.add_collection(line_segments)
|
||||
ax.set_title('Line collection with customized caps and joinstyle')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['scatter_post_alpha'],
|
||||
extensions=['png'], remove_text=True,
|
||||
style='default')
|
||||
def test_scatter_post_alpha():
|
||||
fig, ax = plt.subplots()
|
||||
sc = ax.scatter(range(5), range(5), c=range(5))
|
||||
# this needs to be here to update internal state
|
||||
fig.canvas.draw()
|
||||
sc.set_alpha(.1)
|
||||
@@ -0,0 +1,448 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib import rc_context
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.colors import BoundaryNorm, LogNorm, PowerNorm
|
||||
from matplotlib.cm import get_cmap
|
||||
from matplotlib.colorbar import ColorbarBase
|
||||
from matplotlib.ticker import LogLocator, LogFormatter
|
||||
|
||||
|
||||
def _get_cmap_norms():
|
||||
"""
|
||||
Define a colormap and appropriate norms for each of the four
|
||||
possible settings of the extend keyword.
|
||||
|
||||
Helper function for _colorbar_extension_shape and
|
||||
colorbar_extension_length.
|
||||
"""
|
||||
# Create a color map and specify the levels it represents.
|
||||
cmap = get_cmap("RdBu", lut=5)
|
||||
clevs = [-5., -2.5, -.5, .5, 1.5, 3.5]
|
||||
# Define norms for the color maps.
|
||||
norms = dict()
|
||||
norms['neither'] = BoundaryNorm(clevs, len(clevs) - 1)
|
||||
norms['min'] = BoundaryNorm([-10] + clevs[1:], len(clevs) - 1)
|
||||
norms['max'] = BoundaryNorm(clevs[:-1] + [10], len(clevs) - 1)
|
||||
norms['both'] = BoundaryNorm([-10] + clevs[1:-1] + [10], len(clevs) - 1)
|
||||
return cmap, norms
|
||||
|
||||
|
||||
def _colorbar_extension_shape(spacing):
|
||||
'''
|
||||
Produce 4 colorbars with rectangular extensions for either uniform
|
||||
or proportional spacing.
|
||||
|
||||
Helper function for test_colorbar_extension_shape.
|
||||
'''
|
||||
# Get a colormap and appropriate norms for each extension type.
|
||||
cmap, norms = _get_cmap_norms()
|
||||
# Create a figure and adjust whitespace for subplots.
|
||||
fig = plt.figure()
|
||||
fig.subplots_adjust(hspace=4)
|
||||
for i, extension_type in enumerate(('neither', 'min', 'max', 'both')):
|
||||
# Get the appropriate norm and use it to get colorbar boundaries.
|
||||
norm = norms[extension_type]
|
||||
boundaries = values = norm.boundaries
|
||||
# Create a subplot.
|
||||
cax = fig.add_subplot(4, 1, i + 1)
|
||||
# Generate the colorbar.
|
||||
cb = ColorbarBase(cax, cmap=cmap, norm=norm,
|
||||
boundaries=boundaries, values=values,
|
||||
extend=extension_type, extendrect=True,
|
||||
orientation='horizontal', spacing=spacing)
|
||||
# Turn off text and ticks.
|
||||
cax.tick_params(left=False, labelleft=False,
|
||||
bottom=False, labelbottom=False)
|
||||
# Return the figure to the caller.
|
||||
return fig
|
||||
|
||||
|
||||
def _colorbar_extension_length(spacing):
|
||||
'''
|
||||
Produce 12 colorbars with variable length extensions for either
|
||||
uniform or proportional spacing.
|
||||
|
||||
Helper function for test_colorbar_extension_length.
|
||||
'''
|
||||
# Get a colormap and appropriate norms for each extension type.
|
||||
cmap, norms = _get_cmap_norms()
|
||||
# Create a figure and adjust whitespace for subplots.
|
||||
fig = plt.figure()
|
||||
fig.subplots_adjust(hspace=.6)
|
||||
for i, extension_type in enumerate(('neither', 'min', 'max', 'both')):
|
||||
# Get the appropriate norm and use it to get colorbar boundaries.
|
||||
norm = norms[extension_type]
|
||||
boundaries = values = norm.boundaries
|
||||
for j, extendfrac in enumerate((None, 'auto', 0.1)):
|
||||
# Create a subplot.
|
||||
cax = fig.add_subplot(12, 1, i*3 + j + 1)
|
||||
# Generate the colorbar.
|
||||
ColorbarBase(cax, cmap=cmap, norm=norm,
|
||||
boundaries=boundaries, values=values,
|
||||
extend=extension_type, extendfrac=extendfrac,
|
||||
orientation='horizontal', spacing=spacing)
|
||||
# Turn off text and ticks.
|
||||
cax.tick_params(left=False, labelleft=False,
|
||||
bottom=False, labelbottom=False)
|
||||
# Return the figure to the caller.
|
||||
return fig
|
||||
|
||||
|
||||
@image_comparison(
|
||||
baseline_images=['colorbar_extensions_shape_uniform',
|
||||
'colorbar_extensions_shape_proportional'],
|
||||
extensions=['png'])
|
||||
def test_colorbar_extension_shape():
|
||||
'''Test rectangular colorbar extensions.'''
|
||||
# Create figures for uniform and proportionally spaced colorbars.
|
||||
_colorbar_extension_shape('uniform')
|
||||
_colorbar_extension_shape('proportional')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['colorbar_extensions_uniform',
|
||||
'colorbar_extensions_proportional'],
|
||||
extensions=['png'])
|
||||
def test_colorbar_extension_length():
|
||||
'''Test variable length colorbar extensions.'''
|
||||
# Create figures for uniform and proportionally spaced colorbars.
|
||||
_colorbar_extension_length('uniform')
|
||||
_colorbar_extension_length('proportional')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['cbar_with_orientation',
|
||||
'cbar_locationing',
|
||||
'double_cbar',
|
||||
'cbar_sharing',
|
||||
],
|
||||
extensions=['png'], remove_text=True,
|
||||
savefig_kwarg={'dpi': 40})
|
||||
def test_colorbar_positioning():
|
||||
data = np.arange(1200).reshape(30, 40)
|
||||
levels = [0, 200, 400, 600, 800, 1000, 1200]
|
||||
|
||||
# -------------------
|
||||
plt.figure()
|
||||
plt.contourf(data, levels=levels)
|
||||
plt.colorbar(orientation='horizontal', use_gridspec=False)
|
||||
|
||||
locations = ['left', 'right', 'top', 'bottom']
|
||||
plt.figure()
|
||||
for i, location in enumerate(locations):
|
||||
plt.subplot(2, 2, i + 1)
|
||||
plt.contourf(data, levels=levels)
|
||||
plt.colorbar(location=location, use_gridspec=False)
|
||||
|
||||
# -------------------
|
||||
plt.figure()
|
||||
# make some other data (random integers)
|
||||
data_2nd = np.array([[2, 3, 2, 3], [1.5, 2, 2, 3], [2, 3, 3, 4]])
|
||||
# make the random data expand to the shape of the main data
|
||||
data_2nd = np.repeat(np.repeat(data_2nd, 10, axis=1), 10, axis=0)
|
||||
|
||||
color_mappable = plt.contourf(data, levels=levels, extend='both')
|
||||
# test extend frac here
|
||||
hatch_mappable = plt.contourf(data_2nd, levels=[1, 2, 3], colors='none',
|
||||
hatches=['/', 'o', '+'], extend='max')
|
||||
plt.contour(hatch_mappable, colors='black')
|
||||
|
||||
plt.colorbar(color_mappable, location='left', label='variable 1',
|
||||
use_gridspec=False)
|
||||
plt.colorbar(hatch_mappable, location='right', label='variable 2',
|
||||
use_gridspec=False)
|
||||
|
||||
# -------------------
|
||||
plt.figure()
|
||||
ax1 = plt.subplot(211, anchor='NE', aspect='equal')
|
||||
plt.contourf(data, levels=levels)
|
||||
ax2 = plt.subplot(223)
|
||||
plt.contourf(data, levels=levels)
|
||||
ax3 = plt.subplot(224)
|
||||
plt.contourf(data, levels=levels)
|
||||
|
||||
plt.colorbar(ax=[ax2, ax3, ax1], location='right', pad=0.0, shrink=0.5,
|
||||
panchor=False, use_gridspec=False)
|
||||
plt.colorbar(ax=[ax2, ax3, ax1], location='left', shrink=0.5,
|
||||
panchor=False, use_gridspec=False)
|
||||
plt.colorbar(ax=[ax1], location='bottom', panchor=False,
|
||||
anchor=(0.8, 0.5), shrink=0.6, use_gridspec=False)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['cbar_with_subplots_adjust'],
|
||||
extensions=['png'], remove_text=True,
|
||||
savefig_kwarg={'dpi': 40})
|
||||
def test_gridspec_make_colorbar():
|
||||
plt.figure()
|
||||
data = np.arange(1200).reshape(30, 40)
|
||||
levels = [0, 200, 400, 600, 800, 1000, 1200]
|
||||
|
||||
plt.subplot(121)
|
||||
plt.contourf(data, levels=levels)
|
||||
plt.colorbar(use_gridspec=True, orientation='vertical')
|
||||
|
||||
plt.subplot(122)
|
||||
plt.contourf(data, levels=levels)
|
||||
plt.colorbar(use_gridspec=True, orientation='horizontal')
|
||||
|
||||
plt.subplots_adjust(top=0.95, right=0.95, bottom=0.2, hspace=0.25)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['colorbar_single_scatter'],
|
||||
extensions=['png'], remove_text=True,
|
||||
savefig_kwarg={'dpi': 40})
|
||||
def test_colorbar_single_scatter():
|
||||
# Issue #2642: if a path collection has only one entry,
|
||||
# the norm scaling within the colorbar must ensure a
|
||||
# finite range, otherwise a zero denominator will occur in _locate.
|
||||
plt.figure()
|
||||
x = np.arange(4)
|
||||
y = x.copy()
|
||||
z = np.ma.masked_greater(np.arange(50, 54), 50)
|
||||
cmap = plt.get_cmap('jet', 16)
|
||||
cs = plt.scatter(x, y, z, c=z, cmap=cmap)
|
||||
plt.colorbar(cs)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('use_gridspec', [False, True],
|
||||
ids=['no gridspec', 'with gridspec'])
|
||||
def test_remove_from_figure(use_gridspec):
|
||||
"""
|
||||
Test `remove_from_figure` with the specified ``use_gridspec`` setting
|
||||
"""
|
||||
fig, ax = plt.subplots()
|
||||
sc = ax.scatter([1, 2], [3, 4], cmap="spring")
|
||||
sc.set_array(np.array([5, 6]))
|
||||
pre_figbox = np.array(ax.figbox)
|
||||
cb = fig.colorbar(sc, use_gridspec=use_gridspec)
|
||||
fig.subplots_adjust()
|
||||
cb.remove()
|
||||
fig.subplots_adjust()
|
||||
post_figbox = np.array(ax.figbox)
|
||||
assert (pre_figbox == post_figbox).all()
|
||||
|
||||
|
||||
def test_colorbarbase():
|
||||
# smoke test from #3805
|
||||
ax = plt.gca()
|
||||
ColorbarBase(ax, plt.cm.bone)
|
||||
|
||||
|
||||
@image_comparison(
|
||||
baseline_images=['colorbar_closed_patch'],
|
||||
remove_text=True)
|
||||
def test_colorbar_closed_patch():
|
||||
fig = plt.figure(figsize=(8, 6))
|
||||
ax1 = fig.add_axes([0.05, 0.85, 0.9, 0.1])
|
||||
ax2 = fig.add_axes([0.1, 0.65, 0.75, 0.1])
|
||||
ax3 = fig.add_axes([0.05, 0.45, 0.9, 0.1])
|
||||
ax4 = fig.add_axes([0.05, 0.25, 0.9, 0.1])
|
||||
ax5 = fig.add_axes([0.05, 0.05, 0.9, 0.1])
|
||||
|
||||
cmap = get_cmap("RdBu", lut=5)
|
||||
|
||||
im = ax1.pcolormesh(np.linspace(0, 10, 16).reshape((4, 4)), cmap=cmap)
|
||||
values = np.linspace(0, 10, 5)
|
||||
|
||||
with rc_context({'axes.linewidth': 16}):
|
||||
plt.colorbar(im, cax=ax2, cmap=cmap, orientation='horizontal',
|
||||
extend='both', extendfrac=0.5, values=values)
|
||||
plt.colorbar(im, cax=ax3, cmap=cmap, orientation='horizontal',
|
||||
extend='both', values=values)
|
||||
plt.colorbar(im, cax=ax4, cmap=cmap, orientation='horizontal',
|
||||
extend='both', extendrect=True, values=values)
|
||||
plt.colorbar(im, cax=ax5, cmap=cmap, orientation='horizontal',
|
||||
extend='neither', values=values)
|
||||
|
||||
|
||||
def test_colorbar_ticks():
|
||||
# test fix for #5673
|
||||
fig, ax = plt.subplots()
|
||||
x = np.arange(-3.0, 4.001)
|
||||
y = np.arange(-4.0, 3.001)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
Z = X * Y
|
||||
clevs = np.array([-12, -5, 0, 5, 12], dtype=float)
|
||||
colors = ['r', 'g', 'b', 'c']
|
||||
cs = ax.contourf(X, Y, Z, clevs, colors=colors)
|
||||
cbar = fig.colorbar(cs, ax=ax, extend='neither',
|
||||
orientation='horizontal', ticks=clevs)
|
||||
assert len(cbar.ax.xaxis.get_ticklocs()) == len(clevs)
|
||||
|
||||
|
||||
def test_colorbar_minorticks_on_off():
|
||||
# test for github issue #11510 and PR #11584
|
||||
np.random.seed(seed=12345)
|
||||
data = np.random.randn(20, 20)
|
||||
with rc_context({'_internal.classic_mode': False}):
|
||||
fig, ax = plt.subplots()
|
||||
# purposefully setting vmin and vmax to odd fractions
|
||||
# so as to check for the correct locations of the minor ticks
|
||||
im = ax.pcolormesh(data, vmin=-2.3, vmax=3.3)
|
||||
|
||||
cbar = fig.colorbar(im, extend='both')
|
||||
cbar.minorticks_on()
|
||||
correct_minorticklocs = np.array([-2.2, -1.8, -1.6, -1.4, -1.2, -0.8,
|
||||
-0.6, -0.4, -0.2, 0.2, 0.4, 0.6,
|
||||
0.8, 1.2, 1.4, 1.6, 1.8, 2.2, 2.4,
|
||||
2.6, 2.8, 3.2])
|
||||
# testing after minorticks_on()
|
||||
np.testing.assert_almost_equal(cbar.ax.yaxis.get_minorticklocs(),
|
||||
correct_minorticklocs)
|
||||
cbar.minorticks_off()
|
||||
# testing after minorticks_off()
|
||||
np.testing.assert_almost_equal(cbar.ax.yaxis.get_minorticklocs(),
|
||||
np.array([]))
|
||||
|
||||
im.set_clim(vmin=-1.2, vmax=1.2)
|
||||
cbar.minorticks_on()
|
||||
correct_minorticklocs = np.array([-1.2, -1.1, -0.9, -0.8, -0.7, -0.6,
|
||||
-0.4, -0.3, -0.2, -0.1, 0.1, 0.2,
|
||||
0.3, 0.4, 0.6, 0.7, 0.8, 0.9,
|
||||
1.1, 1.2])
|
||||
np.testing.assert_almost_equal(cbar.ax.yaxis.get_minorticklocs(),
|
||||
correct_minorticklocs)
|
||||
|
||||
|
||||
def test_colorbar_autoticks():
|
||||
# Test new autotick modes. Needs to be classic because
|
||||
# non-classic doesn't go this route.
|
||||
with rc_context({'_internal.classic_mode': False}):
|
||||
fig, ax = plt.subplots(2, 1)
|
||||
x = np.arange(-3.0, 4.001)
|
||||
y = np.arange(-4.0, 3.001)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
Z = X * Y
|
||||
pcm = ax[0].pcolormesh(X, Y, Z)
|
||||
cbar = fig.colorbar(pcm, ax=ax[0], extend='both',
|
||||
orientation='vertical')
|
||||
|
||||
pcm = ax[1].pcolormesh(X, Y, Z)
|
||||
cbar2 = fig.colorbar(pcm, ax=ax[1], extend='both',
|
||||
orientation='vertical', shrink=0.4)
|
||||
np.testing.assert_almost_equal(cbar.ax.yaxis.get_ticklocs(),
|
||||
np.arange(-10, 11., 5.))
|
||||
np.testing.assert_almost_equal(cbar2.ax.yaxis.get_ticklocs(),
|
||||
np.arange(-10, 11., 10.))
|
||||
|
||||
|
||||
def test_colorbar_autotickslog():
|
||||
# Test new autotick modes...
|
||||
with rc_context({'_internal.classic_mode': False}):
|
||||
fig, ax = plt.subplots(2, 1)
|
||||
x = np.arange(-3.0, 4.001)
|
||||
y = np.arange(-4.0, 3.001)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
Z = X * Y
|
||||
pcm = ax[0].pcolormesh(X, Y, 10**Z, norm=LogNorm())
|
||||
cbar = fig.colorbar(pcm, ax=ax[0], extend='both',
|
||||
orientation='vertical')
|
||||
|
||||
pcm = ax[1].pcolormesh(X, Y, 10**Z, norm=LogNorm())
|
||||
cbar2 = fig.colorbar(pcm, ax=ax[1], extend='both',
|
||||
orientation='vertical', shrink=0.4)
|
||||
np.testing.assert_almost_equal(cbar.ax.yaxis.get_ticklocs(),
|
||||
10**np.arange(-12, 12.2, 4.))
|
||||
np.testing.assert_almost_equal(cbar2.ax.yaxis.get_ticklocs(),
|
||||
10**np.arange(-12, 13., 12.))
|
||||
|
||||
|
||||
def test_colorbar_get_ticks():
|
||||
# test feature for #5792
|
||||
plt.figure()
|
||||
data = np.arange(1200).reshape(30, 40)
|
||||
levels = [0, 200, 400, 600, 800, 1000, 1200]
|
||||
|
||||
plt.subplot()
|
||||
plt.contourf(data, levels=levels)
|
||||
|
||||
# testing getter for user set ticks
|
||||
userTicks = plt.colorbar(ticks=[0, 600, 1200])
|
||||
assert userTicks.get_ticks().tolist() == [0, 600, 1200]
|
||||
|
||||
# testing for getter after calling set_ticks
|
||||
userTicks.set_ticks([600, 700, 800])
|
||||
assert userTicks.get_ticks().tolist() == [600, 700, 800]
|
||||
|
||||
# testing for getter after calling set_ticks with some ticks out of bounds
|
||||
userTicks.set_ticks([600, 1300, 1400, 1500])
|
||||
assert userTicks.get_ticks().tolist() == [600]
|
||||
|
||||
# testing getter when no ticks are assigned
|
||||
defTicks = plt.colorbar(orientation='horizontal')
|
||||
assert defTicks.get_ticks().tolist() == levels
|
||||
|
||||
|
||||
def test_colorbar_lognorm_extension():
|
||||
# Test that colorbar with lognorm is extended correctly
|
||||
f, ax = plt.subplots()
|
||||
cb = ColorbarBase(ax, norm=LogNorm(vmin=0.1, vmax=1000.0),
|
||||
orientation='vertical', extend='both')
|
||||
assert cb._values[0] >= 0.0
|
||||
|
||||
|
||||
def test_colorbar_powernorm_extension():
|
||||
# Test that colorbar with powernorm is extended correctly
|
||||
f, ax = plt.subplots()
|
||||
cb = ColorbarBase(ax, norm=PowerNorm(gamma=0.5, vmin=0.0, vmax=1.0),
|
||||
orientation='vertical', extend='both')
|
||||
assert cb._values[0] >= 0.0
|
||||
|
||||
|
||||
def test_colorbar_axes_kw():
|
||||
# test fix for #8493: This does only test, that axes-related keywords pass
|
||||
# and do not raise an exception.
|
||||
plt.figure()
|
||||
plt.imshow(([[1, 2], [3, 4]]))
|
||||
plt.colorbar(orientation='horizontal', fraction=0.2, pad=0.2, shrink=0.5,
|
||||
aspect=10, anchor=(0., 0.), panchor=(0., 1.))
|
||||
|
||||
|
||||
def test_colorbar_log_minortick_labels():
|
||||
with rc_context({'_internal.classic_mode': False}):
|
||||
fig, ax = plt.subplots()
|
||||
pcm = ax.imshow([[10000, 50000]], norm=LogNorm())
|
||||
cb = fig.colorbar(pcm)
|
||||
fig.canvas.draw()
|
||||
lb = cb.ax.yaxis.get_ticklabels(which='both')
|
||||
expected = [r'$\mathdefault{10^{4}}$',
|
||||
r'$\mathdefault{2\times10^{4}}$',
|
||||
r'$\mathdefault{3\times10^{4}}$',
|
||||
r'$\mathdefault{4\times10^{4}}$']
|
||||
for l, exp in zip(lb, expected):
|
||||
assert l.get_text() == exp
|
||||
|
||||
|
||||
def test_colorbar_renorm():
|
||||
x, y = np.ogrid[-4:4:31j, -4:4:31j]
|
||||
z = 120000*np.exp(-x**2 - y**2)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
im = ax.imshow(z)
|
||||
cbar = fig.colorbar(im)
|
||||
|
||||
norm = LogNorm(z.min(), z.max())
|
||||
im.set_norm(norm)
|
||||
cbar.set_norm(norm)
|
||||
cbar.locator = LogLocator()
|
||||
cbar.formatter = LogFormatter()
|
||||
cbar.update_normal(im)
|
||||
assert np.isclose(cbar.vmin, z.min())
|
||||
|
||||
norm = LogNorm(z.min() * 1000, z.max() * 1000)
|
||||
im.set_norm(norm)
|
||||
cbar.set_norm(norm)
|
||||
cbar.update_normal(im)
|
||||
assert np.isclose(cbar.vmin, z.min() * 1000)
|
||||
assert np.isclose(cbar.vmax, z.max() * 1000)
|
||||
|
||||
|
||||
def test_colorbar_get_ticks():
|
||||
with rc_context({'_internal.classic_mode': False}):
|
||||
|
||||
fig, ax = plt. subplots()
|
||||
np.random.seed(19680801)
|
||||
pc = ax.pcolormesh(np.random.rand(30, 30))
|
||||
cb = fig.colorbar(pc)
|
||||
np.testing.assert_allclose(cb.get_ticks(), [0.2, 0.4, 0.6, 0.8])
|
||||
@@ -0,0 +1,715 @@
|
||||
import copy
|
||||
import itertools
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from numpy.testing import assert_array_equal, assert_array_almost_equal
|
||||
|
||||
from matplotlib import cycler
|
||||
import matplotlib
|
||||
import matplotlib.colors as mcolors
|
||||
import matplotlib.cm as cm
|
||||
import matplotlib.colorbar as mcolorbar
|
||||
import matplotlib.cbook as cbook
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
|
||||
def test_resample():
|
||||
"""
|
||||
Github issue #6025 pointed to incorrect ListedColormap._resample;
|
||||
here we test the method for LinearSegmentedColormap as well.
|
||||
"""
|
||||
n = 101
|
||||
colorlist = np.empty((n, 4), float)
|
||||
colorlist[:, 0] = np.linspace(0, 1, n)
|
||||
colorlist[:, 1] = 0.2
|
||||
colorlist[:, 2] = np.linspace(1, 0, n)
|
||||
colorlist[:, 3] = 0.7
|
||||
lsc = mcolors.LinearSegmentedColormap.from_list('lsc', colorlist)
|
||||
lc = mcolors.ListedColormap(colorlist)
|
||||
lsc3 = lsc._resample(3)
|
||||
lc3 = lc._resample(3)
|
||||
expected = np.array([[0.0, 0.2, 1.0, 0.7],
|
||||
[0.5, 0.2, 0.5, 0.7],
|
||||
[1.0, 0.2, 0.0, 0.7]], float)
|
||||
assert_array_almost_equal(lsc3([0, 0.5, 1]), expected)
|
||||
assert_array_almost_equal(lc3([0, 0.5, 1]), expected)
|
||||
|
||||
|
||||
def test_colormap_copy():
|
||||
cm = plt.cm.Reds
|
||||
cm_copy = copy.copy(cm)
|
||||
with np.errstate(invalid='ignore'):
|
||||
ret1 = cm_copy([-1, 0, .5, 1, np.nan, np.inf])
|
||||
cm2 = copy.copy(cm_copy)
|
||||
cm2.set_bad('g')
|
||||
with np.errstate(invalid='ignore'):
|
||||
ret2 = cm_copy([-1, 0, .5, 1, np.nan, np.inf])
|
||||
assert_array_equal(ret1, ret2)
|
||||
|
||||
|
||||
def test_colormap_endian():
|
||||
"""
|
||||
Github issue #1005: a bug in putmask caused erroneous
|
||||
mapping of 1.0 when input from a non-native-byteorder
|
||||
array.
|
||||
"""
|
||||
cmap = cm.get_cmap("jet")
|
||||
# Test under, over, and invalid along with values 0 and 1.
|
||||
a = [-0.5, 0, 0.5, 1, 1.5, np.nan]
|
||||
for dt in ["f2", "f4", "f8"]:
|
||||
anative = np.ma.masked_invalid(np.array(a, dtype=dt))
|
||||
aforeign = anative.byteswap().newbyteorder()
|
||||
assert_array_equal(cmap(anative), cmap(aforeign))
|
||||
|
||||
|
||||
def test_BoundaryNorm():
|
||||
"""
|
||||
Github issue #1258: interpolation was failing with numpy
|
||||
1.7 pre-release.
|
||||
"""
|
||||
|
||||
boundaries = [0, 1.1, 2.2]
|
||||
vals = [-1, 0, 1, 2, 2.2, 4]
|
||||
|
||||
# Without interpolation
|
||||
expected = [-1, 0, 0, 1, 2, 2]
|
||||
ncolors = len(boundaries) - 1
|
||||
bn = mcolors.BoundaryNorm(boundaries, ncolors)
|
||||
assert_array_equal(bn(vals), expected)
|
||||
|
||||
# ncolors != len(boundaries) - 1 triggers interpolation
|
||||
expected = [-1, 0, 0, 2, 3, 3]
|
||||
ncolors = len(boundaries)
|
||||
bn = mcolors.BoundaryNorm(boundaries, ncolors)
|
||||
assert_array_equal(bn(vals), expected)
|
||||
|
||||
# more boundaries for a third color
|
||||
boundaries = [0, 1, 2, 3]
|
||||
vals = [-1, 0.1, 1.1, 2.2, 4]
|
||||
ncolors = 5
|
||||
expected = [-1, 0, 2, 4, 5]
|
||||
bn = mcolors.BoundaryNorm(boundaries, ncolors)
|
||||
assert_array_equal(bn(vals), expected)
|
||||
|
||||
# a scalar as input should not trigger an error and should return a scalar
|
||||
boundaries = [0, 1, 2]
|
||||
vals = [-1, 0.1, 1.1, 2.2]
|
||||
bn = mcolors.BoundaryNorm(boundaries, 2)
|
||||
expected = [-1, 0, 1, 2]
|
||||
for v, ex in zip(vals, expected):
|
||||
ret = bn(v)
|
||||
assert isinstance(ret, int)
|
||||
assert_array_equal(ret, ex)
|
||||
assert_array_equal(bn([v]), ex)
|
||||
|
||||
# same with interp
|
||||
bn = mcolors.BoundaryNorm(boundaries, 3)
|
||||
expected = [-1, 0, 2, 3]
|
||||
for v, ex in zip(vals, expected):
|
||||
ret = bn(v)
|
||||
assert isinstance(ret, int)
|
||||
assert_array_equal(ret, ex)
|
||||
assert_array_equal(bn([v]), ex)
|
||||
|
||||
# Clipping
|
||||
bn = mcolors.BoundaryNorm(boundaries, 3, clip=True)
|
||||
expected = [0, 0, 2, 2]
|
||||
for v, ex in zip(vals, expected):
|
||||
ret = bn(v)
|
||||
assert isinstance(ret, int)
|
||||
assert_array_equal(ret, ex)
|
||||
assert_array_equal(bn([v]), ex)
|
||||
|
||||
# Masked arrays
|
||||
boundaries = [0, 1.1, 2.2]
|
||||
vals = np.ma.masked_invalid([-1., np.NaN, 0, 1.4, 9])
|
||||
|
||||
# Without interpolation
|
||||
ncolors = len(boundaries) - 1
|
||||
bn = mcolors.BoundaryNorm(boundaries, ncolors)
|
||||
expected = np.ma.masked_array([-1, -99, 0, 1, 2], mask=[0, 1, 0, 0, 0])
|
||||
assert_array_equal(bn(vals), expected)
|
||||
|
||||
# With interpolation
|
||||
bn = mcolors.BoundaryNorm(boundaries, len(boundaries))
|
||||
expected = np.ma.masked_array([-1, -99, 0, 2, 3], mask=[0, 1, 0, 0, 0])
|
||||
assert_array_equal(bn(vals), expected)
|
||||
|
||||
# Non-trivial masked arrays
|
||||
vals = np.ma.masked_invalid([np.Inf, np.NaN])
|
||||
assert np.all(bn(vals).mask)
|
||||
vals = np.ma.masked_invalid([np.Inf])
|
||||
assert np.all(bn(vals).mask)
|
||||
|
||||
|
||||
def test_LogNorm():
|
||||
"""
|
||||
LogNorm ignored clip, now it has the same
|
||||
behavior as Normalize, e.g., values > vmax are bigger than 1
|
||||
without clip, with clip they are 1.
|
||||
"""
|
||||
ln = mcolors.LogNorm(clip=True, vmax=5)
|
||||
assert_array_equal(ln([1, 6]), [0, 1.0])
|
||||
|
||||
|
||||
def test_PowerNorm():
|
||||
a = np.array([0, 0.5, 1, 1.5], dtype=float)
|
||||
pnorm = mcolors.PowerNorm(1)
|
||||
norm = mcolors.Normalize()
|
||||
assert_array_almost_equal(norm(a), pnorm(a))
|
||||
|
||||
a = np.array([-0.5, 0, 2, 4, 8], dtype=float)
|
||||
expected = [0, 0, 1/16, 1/4, 1]
|
||||
pnorm = mcolors.PowerNorm(2, vmin=0, vmax=8)
|
||||
assert_array_almost_equal(pnorm(a), expected)
|
||||
assert pnorm(a[0]) == expected[0]
|
||||
assert pnorm(a[2]) == expected[2]
|
||||
assert_array_almost_equal(a[1:], pnorm.inverse(pnorm(a))[1:])
|
||||
|
||||
# Clip = True
|
||||
a = np.array([-0.5, 0, 1, 8, 16], dtype=float)
|
||||
expected = [0, 0, 0, 1, 1]
|
||||
pnorm = mcolors.PowerNorm(2, vmin=2, vmax=8, clip=True)
|
||||
assert_array_almost_equal(pnorm(a), expected)
|
||||
assert pnorm(a[0]) == expected[0]
|
||||
assert pnorm(a[-1]) == expected[-1]
|
||||
|
||||
# Clip = True at call time
|
||||
a = np.array([-0.5, 0, 1, 8, 16], dtype=float)
|
||||
expected = [0, 0, 0, 1, 1]
|
||||
pnorm = mcolors.PowerNorm(2, vmin=2, vmax=8, clip=False)
|
||||
assert_array_almost_equal(pnorm(a, clip=True), expected)
|
||||
assert pnorm(a[0], clip=True) == expected[0]
|
||||
assert pnorm(a[-1], clip=True) == expected[-1]
|
||||
|
||||
|
||||
def test_PowerNorm_translation_invariance():
|
||||
a = np.array([0, 1/2, 1], dtype=float)
|
||||
expected = [0, 1/8, 1]
|
||||
pnorm = mcolors.PowerNorm(vmin=0, vmax=1, gamma=3)
|
||||
assert_array_almost_equal(pnorm(a), expected)
|
||||
pnorm = mcolors.PowerNorm(vmin=-2, vmax=-1, gamma=3)
|
||||
assert_array_almost_equal(pnorm(a - 2), expected)
|
||||
|
||||
|
||||
def test_Normalize():
|
||||
norm = mcolors.Normalize()
|
||||
vals = np.arange(-10, 10, 1, dtype=float)
|
||||
_inverse_tester(norm, vals)
|
||||
_scalar_tester(norm, vals)
|
||||
_mask_tester(norm, vals)
|
||||
|
||||
# Handle integer input correctly (don't overflow when computing max-min,
|
||||
# i.e. 127-(-128) here).
|
||||
vals = np.array([-128, 127], dtype=np.int8)
|
||||
norm = mcolors.Normalize(vals.min(), vals.max())
|
||||
assert_array_equal(np.asarray(norm(vals)), [0, 1])
|
||||
|
||||
# Don't lose precision on longdoubles (float128 on Linux):
|
||||
# for array inputs...
|
||||
vals = np.array([1.2345678901, 9.8765432109], dtype=np.longdouble)
|
||||
norm = mcolors.Normalize(vals.min(), vals.max())
|
||||
assert_array_equal(np.asarray(norm(vals)), [0, 1])
|
||||
# and for scalar ones.
|
||||
eps = np.finfo(np.longdouble).resolution
|
||||
norm = plt.Normalize(1, 1 + 100 * eps)
|
||||
# This returns exactly 0.5 when longdouble is extended precision (80-bit),
|
||||
# but only a value close to it when it is quadruple precision (128-bit).
|
||||
assert 0 < norm(1 + 50 * eps) < 1
|
||||
|
||||
|
||||
def test_SymLogNorm():
|
||||
"""
|
||||
Test SymLogNorm behavior
|
||||
"""
|
||||
norm = mcolors.SymLogNorm(3, vmax=5, linscale=1.2)
|
||||
vals = np.array([-30, -1, 2, 6], dtype=float)
|
||||
normed_vals = norm(vals)
|
||||
expected = [0., 0.53980074, 0.826991, 1.02758204]
|
||||
assert_array_almost_equal(normed_vals, expected)
|
||||
_inverse_tester(norm, vals)
|
||||
_scalar_tester(norm, vals)
|
||||
_mask_tester(norm, vals)
|
||||
|
||||
# Ensure that specifying vmin returns the same result as above
|
||||
norm = mcolors.SymLogNorm(3, vmin=-30, vmax=5, linscale=1.2)
|
||||
normed_vals = norm(vals)
|
||||
assert_array_almost_equal(normed_vals, expected)
|
||||
|
||||
|
||||
def test_SymLogNorm_colorbar():
|
||||
"""
|
||||
Test un-called SymLogNorm in a colorbar.
|
||||
"""
|
||||
norm = mcolors.SymLogNorm(0.1, vmin=-1, vmax=1, linscale=1)
|
||||
fig = plt.figure()
|
||||
cbar = mcolorbar.ColorbarBase(fig.add_subplot(111), norm=norm)
|
||||
plt.close(fig)
|
||||
|
||||
|
||||
def test_SymLogNorm_single_zero():
|
||||
"""
|
||||
Test SymLogNorm to ensure it is not adding sub-ticks to zero label
|
||||
"""
|
||||
fig = plt.figure()
|
||||
norm = mcolors.SymLogNorm(1e-5, vmin=-1, vmax=1)
|
||||
cbar = mcolorbar.ColorbarBase(fig.add_subplot(111), norm=norm)
|
||||
ticks = cbar.get_ticks()
|
||||
assert sum(ticks == 0) == 1
|
||||
plt.close(fig)
|
||||
|
||||
|
||||
def _inverse_tester(norm_instance, vals):
|
||||
"""
|
||||
Checks if the inverse of the given normalization is working.
|
||||
"""
|
||||
assert_array_almost_equal(norm_instance.inverse(norm_instance(vals)), vals)
|
||||
|
||||
|
||||
def _scalar_tester(norm_instance, vals):
|
||||
"""
|
||||
Checks if scalars and arrays are handled the same way.
|
||||
Tests only for float.
|
||||
"""
|
||||
scalar_result = [norm_instance(float(v)) for v in vals]
|
||||
assert_array_almost_equal(scalar_result, norm_instance(vals))
|
||||
|
||||
|
||||
def _mask_tester(norm_instance, vals):
|
||||
"""
|
||||
Checks mask handling
|
||||
"""
|
||||
masked_array = np.ma.array(vals)
|
||||
masked_array[0] = np.ma.masked
|
||||
assert_array_equal(masked_array.mask, norm_instance(masked_array).mask)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['levels_and_colors'],
|
||||
extensions=['png'])
|
||||
def test_cmap_and_norm_from_levels_and_colors():
|
||||
data = np.linspace(-2, 4, 49).reshape(7, 7)
|
||||
levels = [-1, 2, 2.5, 3]
|
||||
colors = ['red', 'green', 'blue', 'yellow', 'black']
|
||||
extend = 'both'
|
||||
cmap, norm = mcolors.from_levels_and_colors(levels, colors, extend=extend)
|
||||
|
||||
ax = plt.axes()
|
||||
m = plt.pcolormesh(data, cmap=cmap, norm=norm)
|
||||
plt.colorbar(m)
|
||||
|
||||
# Hide the axes labels (but not the colorbar ones, as they are useful)
|
||||
ax.tick_params(labelleft=False, labelbottom=False)
|
||||
|
||||
|
||||
def test_cmap_and_norm_from_levels_and_colors2():
|
||||
levels = [-1, 2, 2.5, 3]
|
||||
colors = ['red', (0, 1, 0), 'blue', (0.5, 0.5, 0.5), (0.0, 0.0, 0.0, 1.0)]
|
||||
clr = mcolors.to_rgba_array(colors)
|
||||
bad = (0.1, 0.1, 0.1, 0.1)
|
||||
no_color = (0.0, 0.0, 0.0, 0.0)
|
||||
masked_value = 'masked_value'
|
||||
|
||||
# Define the test values which are of interest.
|
||||
# Note: levels are lev[i] <= v < lev[i+1]
|
||||
tests = [('both', None, {-2: clr[0],
|
||||
-1: clr[1],
|
||||
2: clr[2],
|
||||
2.25: clr[2],
|
||||
3: clr[4],
|
||||
3.5: clr[4],
|
||||
masked_value: bad}),
|
||||
|
||||
('min', -1, {-2: clr[0],
|
||||
-1: clr[1],
|
||||
2: clr[2],
|
||||
2.25: clr[2],
|
||||
3: no_color,
|
||||
3.5: no_color,
|
||||
masked_value: bad}),
|
||||
|
||||
('max', -1, {-2: no_color,
|
||||
-1: clr[0],
|
||||
2: clr[1],
|
||||
2.25: clr[1],
|
||||
3: clr[3],
|
||||
3.5: clr[3],
|
||||
masked_value: bad}),
|
||||
|
||||
('neither', -2, {-2: no_color,
|
||||
-1: clr[0],
|
||||
2: clr[1],
|
||||
2.25: clr[1],
|
||||
3: no_color,
|
||||
3.5: no_color,
|
||||
masked_value: bad}),
|
||||
]
|
||||
|
||||
for extend, i1, cases in tests:
|
||||
cmap, norm = mcolors.from_levels_and_colors(levels, colors[0:i1],
|
||||
extend=extend)
|
||||
cmap.set_bad(bad)
|
||||
for d_val, expected_color in cases.items():
|
||||
if d_val == masked_value:
|
||||
d_val = np.ma.array([1], mask=True)
|
||||
else:
|
||||
d_val = [d_val]
|
||||
assert_array_equal(expected_color, cmap(norm(d_val))[0],
|
||||
'Wih extend={0!r} and data '
|
||||
'value={1!r}'.format(extend, d_val))
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
mcolors.from_levels_and_colors(levels, colors)
|
||||
|
||||
|
||||
def test_rgb_hsv_round_trip():
|
||||
for a_shape in [(500, 500, 3), (500, 3), (1, 3), (3,)]:
|
||||
np.random.seed(0)
|
||||
tt = np.random.random(a_shape)
|
||||
assert_array_almost_equal(tt,
|
||||
mcolors.hsv_to_rgb(mcolors.rgb_to_hsv(tt)))
|
||||
assert_array_almost_equal(tt,
|
||||
mcolors.rgb_to_hsv(mcolors.hsv_to_rgb(tt)))
|
||||
|
||||
|
||||
def test_autoscale_masked():
|
||||
# Test for #2336. Previously fully masked data would trigger a ValueError.
|
||||
data = np.ma.masked_all((12, 20))
|
||||
plt.pcolor(data)
|
||||
plt.draw()
|
||||
|
||||
|
||||
def test_colors_no_float():
|
||||
# Gray must be a string to distinguish 3-4 grays from RGB or RGBA.
|
||||
with pytest.raises(ValueError):
|
||||
mcolors.to_rgba(0.4)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['light_source_shading_topo'],
|
||||
extensions=['png'])
|
||||
def test_light_source_topo_surface():
|
||||
"""Shades a DEM using different v.e.'s and blend modes."""
|
||||
fname = cbook.get_sample_data('jacksboro_fault_dem.npz', asfileobj=False)
|
||||
dem = np.load(fname)
|
||||
elev = dem['elevation']
|
||||
# Get the true cellsize in meters for accurate vertical exaggeration
|
||||
# Convert from decimal degrees to meters
|
||||
dx, dy = dem['dx'], dem['dy']
|
||||
dx = 111320.0 * dx * np.cos(dem['ymin'])
|
||||
dy = 111320.0 * dy
|
||||
dem.close()
|
||||
|
||||
ls = mcolors.LightSource(315, 45)
|
||||
cmap = cm.gist_earth
|
||||
|
||||
fig, axes = plt.subplots(nrows=3, ncols=3)
|
||||
for row, mode in zip(axes, ['hsv', 'overlay', 'soft']):
|
||||
for ax, ve in zip(row, [0.1, 1, 10]):
|
||||
rgb = ls.shade(elev, cmap, vert_exag=ve, dx=dx, dy=dy,
|
||||
blend_mode=mode)
|
||||
ax.imshow(rgb)
|
||||
ax.set(xticks=[], yticks=[])
|
||||
|
||||
|
||||
def test_light_source_shading_default():
|
||||
"""Array comparison test for the default "hsv" blend mode. Ensure the
|
||||
default result doesn't change without warning."""
|
||||
y, x = np.mgrid[-1.2:1.2:8j, -1.2:1.2:8j]
|
||||
z = 10 * np.cos(x**2 + y**2)
|
||||
|
||||
cmap = plt.cm.copper
|
||||
ls = mcolors.LightSource(315, 45)
|
||||
rgb = ls.shade(z, cmap)
|
||||
|
||||
# Result stored transposed and rounded for more compact display...
|
||||
expect = np.array(
|
||||
[[[0.00, 0.45, 0.90, 0.90, 0.82, 0.62, 0.28, 0.00],
|
||||
[0.45, 0.94, 0.99, 1.00, 1.00, 0.96, 0.65, 0.17],
|
||||
[0.90, 0.99, 1.00, 1.00, 1.00, 1.00, 0.94, 0.35],
|
||||
[0.90, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.49],
|
||||
[0.82, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 0.41],
|
||||
[0.62, 0.96, 1.00, 1.00, 1.00, 1.00, 0.90, 0.07],
|
||||
[0.28, 0.65, 0.94, 1.00, 1.00, 0.90, 0.35, 0.01],
|
||||
[0.00, 0.17, 0.35, 0.49, 0.41, 0.07, 0.01, 0.00]],
|
||||
|
||||
[[0.00, 0.28, 0.59, 0.72, 0.62, 0.40, 0.18, 0.00],
|
||||
[0.28, 0.78, 0.93, 0.92, 0.83, 0.66, 0.39, 0.11],
|
||||
[0.59, 0.93, 0.99, 1.00, 0.92, 0.75, 0.50, 0.21],
|
||||
[0.72, 0.92, 1.00, 0.99, 0.93, 0.76, 0.51, 0.18],
|
||||
[0.62, 0.83, 0.92, 0.93, 0.87, 0.68, 0.42, 0.08],
|
||||
[0.40, 0.66, 0.75, 0.76, 0.68, 0.52, 0.23, 0.02],
|
||||
[0.18, 0.39, 0.50, 0.51, 0.42, 0.23, 0.00, 0.00],
|
||||
[0.00, 0.11, 0.21, 0.18, 0.08, 0.02, 0.00, 0.00]],
|
||||
|
||||
[[0.00, 0.18, 0.38, 0.46, 0.39, 0.26, 0.11, 0.00],
|
||||
[0.18, 0.50, 0.70, 0.75, 0.64, 0.44, 0.25, 0.07],
|
||||
[0.38, 0.70, 0.91, 0.98, 0.81, 0.51, 0.29, 0.13],
|
||||
[0.46, 0.75, 0.98, 0.96, 0.84, 0.48, 0.22, 0.12],
|
||||
[0.39, 0.64, 0.81, 0.84, 0.71, 0.31, 0.11, 0.05],
|
||||
[0.26, 0.44, 0.51, 0.48, 0.31, 0.10, 0.03, 0.01],
|
||||
[0.11, 0.25, 0.29, 0.22, 0.11, 0.03, 0.00, 0.00],
|
||||
[0.00, 0.07, 0.13, 0.12, 0.05, 0.01, 0.00, 0.00]],
|
||||
|
||||
[[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00]]
|
||||
]).T
|
||||
|
||||
assert_array_almost_equal(rgb, expect, decimal=2)
|
||||
|
||||
|
||||
# Numpy 1.9.1 fixed a bug in masked arrays which resulted in
|
||||
# additional elements being masked when calculating the gradient thus
|
||||
# the output is different with earlier numpy versions.
|
||||
def test_light_source_masked_shading():
|
||||
"""Array comparison test for a surface with a masked portion. Ensures that
|
||||
we don't wind up with "fringes" of odd colors around masked regions."""
|
||||
y, x = np.mgrid[-1.2:1.2:8j, -1.2:1.2:8j]
|
||||
z = 10 * np.cos(x**2 + y**2)
|
||||
|
||||
z = np.ma.masked_greater(z, 9.9)
|
||||
|
||||
cmap = plt.cm.copper
|
||||
ls = mcolors.LightSource(315, 45)
|
||||
rgb = ls.shade(z, cmap)
|
||||
|
||||
# Result stored transposed and rounded for more compact display...
|
||||
expect = np.array(
|
||||
[[[0.00, 0.46, 0.91, 0.91, 0.84, 0.64, 0.29, 0.00],
|
||||
[0.46, 0.96, 1.00, 1.00, 1.00, 0.97, 0.67, 0.18],
|
||||
[0.91, 1.00, 1.00, 1.00, 1.00, 1.00, 0.96, 0.36],
|
||||
[0.91, 1.00, 1.00, 0.00, 0.00, 1.00, 1.00, 0.51],
|
||||
[0.84, 1.00, 1.00, 0.00, 0.00, 1.00, 1.00, 0.44],
|
||||
[0.64, 0.97, 1.00, 1.00, 1.00, 1.00, 0.94, 0.09],
|
||||
[0.29, 0.67, 0.96, 1.00, 1.00, 0.94, 0.38, 0.01],
|
||||
[0.00, 0.18, 0.36, 0.51, 0.44, 0.09, 0.01, 0.00]],
|
||||
|
||||
[[0.00, 0.29, 0.61, 0.75, 0.64, 0.41, 0.18, 0.00],
|
||||
[0.29, 0.81, 0.95, 0.93, 0.85, 0.68, 0.40, 0.11],
|
||||
[0.61, 0.95, 1.00, 0.78, 0.78, 0.77, 0.52, 0.22],
|
||||
[0.75, 0.93, 0.78, 0.00, 0.00, 0.78, 0.54, 0.19],
|
||||
[0.64, 0.85, 0.78, 0.00, 0.00, 0.78, 0.45, 0.08],
|
||||
[0.41, 0.68, 0.77, 0.78, 0.78, 0.55, 0.25, 0.02],
|
||||
[0.18, 0.40, 0.52, 0.54, 0.45, 0.25, 0.00, 0.00],
|
||||
[0.00, 0.11, 0.22, 0.19, 0.08, 0.02, 0.00, 0.00]],
|
||||
|
||||
[[0.00, 0.19, 0.39, 0.48, 0.41, 0.26, 0.12, 0.00],
|
||||
[0.19, 0.52, 0.73, 0.78, 0.66, 0.46, 0.26, 0.07],
|
||||
[0.39, 0.73, 0.95, 0.50, 0.50, 0.53, 0.30, 0.14],
|
||||
[0.48, 0.78, 0.50, 0.00, 0.00, 0.50, 0.23, 0.12],
|
||||
[0.41, 0.66, 0.50, 0.00, 0.00, 0.50, 0.11, 0.05],
|
||||
[0.26, 0.46, 0.53, 0.50, 0.50, 0.11, 0.03, 0.01],
|
||||
[0.12, 0.26, 0.30, 0.23, 0.11, 0.03, 0.00, 0.00],
|
||||
[0.00, 0.07, 0.14, 0.12, 0.05, 0.01, 0.00, 0.00]],
|
||||
|
||||
[[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 0.00, 0.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 0.00, 0.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00],
|
||||
[1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00]],
|
||||
]).T
|
||||
|
||||
assert_array_almost_equal(rgb, expect, decimal=2)
|
||||
|
||||
|
||||
def test_light_source_hillshading():
|
||||
"""Compare the current hillshading method against one that should be
|
||||
mathematically equivalent. Illuminates a cone from a range of angles."""
|
||||
|
||||
def alternative_hillshade(azimuth, elev, z):
|
||||
illum = _sph2cart(*_azimuth2math(azimuth, elev))
|
||||
illum = np.array(illum)
|
||||
|
||||
dy, dx = np.gradient(-z)
|
||||
dy = -dy
|
||||
dz = np.ones_like(dy)
|
||||
normals = np.dstack([dx, dy, dz])
|
||||
normals /= np.linalg.norm(normals, axis=2)[..., None]
|
||||
|
||||
intensity = np.tensordot(normals, illum, axes=(2, 0))
|
||||
intensity -= intensity.min()
|
||||
intensity /= intensity.ptp()
|
||||
return intensity
|
||||
|
||||
y, x = np.mgrid[5:0:-1, :5]
|
||||
z = -np.hypot(x - x.mean(), y - y.mean())
|
||||
|
||||
for az, elev in itertools.product(range(0, 390, 30), range(0, 105, 15)):
|
||||
ls = mcolors.LightSource(az, elev)
|
||||
h1 = ls.hillshade(z)
|
||||
h2 = alternative_hillshade(az, elev, z)
|
||||
assert_array_almost_equal(h1, h2)
|
||||
|
||||
|
||||
def test_light_source_planar_hillshading():
|
||||
"""Ensure that the illumination intensity is correct for planar
|
||||
surfaces."""
|
||||
|
||||
def plane(azimuth, elevation, x, y):
|
||||
"""Create a plane whose normal vector is at the given azimuth and
|
||||
elevation."""
|
||||
theta, phi = _azimuth2math(azimuth, elevation)
|
||||
a, b, c = _sph2cart(theta, phi)
|
||||
z = -(a*x + b*y) / c
|
||||
return z
|
||||
|
||||
def angled_plane(azimuth, elevation, angle, x, y):
|
||||
"""Create a plane whose normal vector is at an angle from the given
|
||||
azimuth and elevation."""
|
||||
elevation = elevation + angle
|
||||
if elevation > 90:
|
||||
azimuth = (azimuth + 180) % 360
|
||||
elevation = (90 - elevation) % 90
|
||||
return plane(azimuth, elevation, x, y)
|
||||
|
||||
y, x = np.mgrid[5:0:-1, :5]
|
||||
for az, elev in itertools.product(range(0, 390, 30), range(0, 105, 15)):
|
||||
ls = mcolors.LightSource(az, elev)
|
||||
|
||||
# Make a plane at a range of angles to the illumination
|
||||
for angle in range(0, 105, 15):
|
||||
z = angled_plane(az, elev, angle, x, y)
|
||||
h = ls.hillshade(z)
|
||||
assert_array_almost_equal(h, np.cos(np.radians(angle)))
|
||||
|
||||
|
||||
def test_color_names():
|
||||
assert mcolors.to_hex("blue") == "#0000ff"
|
||||
assert mcolors.to_hex("xkcd:blue") == "#0343df"
|
||||
assert mcolors.to_hex("tab:blue") == "#1f77b4"
|
||||
|
||||
|
||||
def _sph2cart(theta, phi):
|
||||
x = np.cos(theta) * np.sin(phi)
|
||||
y = np.sin(theta) * np.sin(phi)
|
||||
z = np.cos(phi)
|
||||
return x, y, z
|
||||
|
||||
|
||||
def _azimuth2math(azimuth, elevation):
|
||||
"""Converts from clockwise-from-north and up-from-horizontal to
|
||||
mathematical conventions."""
|
||||
theta = np.radians((90 - azimuth) % 360)
|
||||
phi = np.radians(90 - elevation)
|
||||
return theta, phi
|
||||
|
||||
|
||||
def test_pandas_iterable(pd):
|
||||
# Using a list or series yields equivalent
|
||||
# color maps, i.e the series isn't seen as
|
||||
# a single color
|
||||
lst = ['red', 'blue', 'green']
|
||||
s = pd.Series(lst)
|
||||
cm1 = mcolors.ListedColormap(lst, N=5)
|
||||
cm2 = mcolors.ListedColormap(s, N=5)
|
||||
assert_array_equal(cm1.colors, cm2.colors)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', sorted(cm.cmap_d))
|
||||
def test_colormap_reversing(name):
|
||||
"""Check the generated _lut data of a colormap and corresponding
|
||||
reversed colormap if they are almost the same."""
|
||||
cmap = plt.get_cmap(name)
|
||||
cmap_r = cmap.reversed()
|
||||
if not cmap_r._isinit:
|
||||
cmap._init()
|
||||
cmap_r._init()
|
||||
assert_array_almost_equal(cmap._lut[:-3], cmap_r._lut[-4::-1])
|
||||
|
||||
|
||||
def test_cn():
|
||||
matplotlib.rcParams['axes.prop_cycle'] = cycler('color',
|
||||
['blue', 'r'])
|
||||
assert mcolors.to_hex("C0") == '#0000ff'
|
||||
assert mcolors.to_hex("C1") == '#ff0000'
|
||||
|
||||
matplotlib.rcParams['axes.prop_cycle'] = cycler('color',
|
||||
['xkcd:blue', 'r'])
|
||||
assert mcolors.to_hex("C0") == '#0343df'
|
||||
assert mcolors.to_hex("C1") == '#ff0000'
|
||||
|
||||
matplotlib.rcParams['axes.prop_cycle'] = cycler('color', ['8e4585', 'r'])
|
||||
|
||||
assert mcolors.to_hex("C0") == '#8e4585'
|
||||
# if '8e4585' gets parsed as a float before it gets detected as a hex
|
||||
# colour it will be interpreted as a very large number.
|
||||
# this mustn't happen.
|
||||
assert mcolors.to_rgb("C0")[0] != np.inf
|
||||
|
||||
|
||||
def test_conversions():
|
||||
# to_rgba_array("none") returns a (0, 4) array.
|
||||
assert_array_equal(mcolors.to_rgba_array("none"), np.zeros((0, 4)))
|
||||
# a list of grayscale levels, not a single color.
|
||||
assert_array_equal(
|
||||
mcolors.to_rgba_array([".2", ".5", ".8"]),
|
||||
np.vstack([mcolors.to_rgba(c) for c in [".2", ".5", ".8"]]))
|
||||
# alpha is properly set.
|
||||
assert mcolors.to_rgba((1, 1, 1), .5) == (1, 1, 1, .5)
|
||||
assert mcolors.to_rgba(".1", .5) == (.1, .1, .1, .5)
|
||||
# builtin round differs between py2 and py3.
|
||||
assert mcolors.to_hex((.7, .7, .7)) == "#b2b2b2"
|
||||
# hex roundtrip.
|
||||
hex_color = "#1234abcd"
|
||||
assert mcolors.to_hex(mcolors.to_rgba(hex_color), keep_alpha=True) == \
|
||||
hex_color
|
||||
|
||||
|
||||
def test_grey_gray():
|
||||
color_mapping = mcolors._colors_full_map
|
||||
for k in color_mapping.keys():
|
||||
if 'grey' in k:
|
||||
assert color_mapping[k] == color_mapping[k.replace('grey', 'gray')]
|
||||
if 'gray' in k:
|
||||
assert color_mapping[k] == color_mapping[k.replace('gray', 'grey')]
|
||||
|
||||
|
||||
def test_tableau_order():
|
||||
dflt_cycle = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728',
|
||||
'#9467bd', '#8c564b', '#e377c2', '#7f7f7f',
|
||||
'#bcbd22', '#17becf']
|
||||
|
||||
assert list(mcolors.TABLEAU_COLORS.values()) == dflt_cycle
|
||||
|
||||
|
||||
def test_ndarray_subclass_norm(recwarn):
|
||||
# Emulate an ndarray subclass that handles units
|
||||
# which objects when adding or subtracting with other
|
||||
# arrays. See #6622 and #8696
|
||||
class MyArray(np.ndarray):
|
||||
def __isub__(self, other):
|
||||
raise RuntimeError
|
||||
|
||||
def __add__(self, other):
|
||||
raise RuntimeError
|
||||
|
||||
data = np.arange(-10, 10, 1, dtype=float)
|
||||
data.shape = (10, 2)
|
||||
mydata = data.view(MyArray)
|
||||
|
||||
for norm in [mcolors.Normalize(), mcolors.LogNorm(),
|
||||
mcolors.SymLogNorm(3, vmax=5, linscale=1),
|
||||
mcolors.Normalize(vmin=mydata.min(), vmax=mydata.max()),
|
||||
mcolors.SymLogNorm(3, vmin=mydata.min(), vmax=mydata.max()),
|
||||
mcolors.PowerNorm(1)]:
|
||||
assert_array_equal(norm(mydata), norm(data))
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(mydata, norm=norm)
|
||||
fig.canvas.draw()
|
||||
assert len(recwarn) == 0
|
||||
recwarn.clear()
|
||||
|
||||
|
||||
def test_same_color():
|
||||
assert mcolors.same_color('k', (0, 0, 0))
|
||||
assert not mcolors.same_color('w', (1, 1, 0))
|
||||
@@ -0,0 +1,72 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
from pytest import approx
|
||||
|
||||
from matplotlib.testing.compare import compare_images
|
||||
from matplotlib.testing.decorators import _image_directories
|
||||
|
||||
|
||||
baseline_dir, result_dir = _image_directories(lambda: 'dummy func')
|
||||
|
||||
|
||||
# Tests of the image comparison algorithm.
|
||||
@pytest.mark.parametrize(
|
||||
'im1, im2, tol, expect_rms',
|
||||
[
|
||||
# Comparison of an image and the same image with minor differences.
|
||||
# This expects the images to compare equal under normal tolerance, and
|
||||
# have a small RMS.
|
||||
('basn3p02.png', 'basn3p02-minorchange.png', 10, None),
|
||||
# Now test with no tolerance.
|
||||
('basn3p02.png', 'basn3p02-minorchange.png', 0, 6.50646),
|
||||
# Comparison with an image that is shifted by 1px in the X axis.
|
||||
('basn3p02.png', 'basn3p02-1px-offset.png', 0, 90.15611),
|
||||
# Comparison with an image with half the pixels shifted by 1px in the X
|
||||
# axis.
|
||||
('basn3p02.png', 'basn3p02-half-1px-offset.png', 0, 63.75),
|
||||
# Comparison of an image and the same image scrambled.
|
||||
# This expects the images to compare completely different, with a very
|
||||
# large RMS.
|
||||
# Note: The image has been scrambled in a specific way, by having
|
||||
# each color component of each pixel randomly placed somewhere in the
|
||||
# image. It contains exactly the same number of pixels of each color
|
||||
# value of R, G and B, but in a totally different position.
|
||||
# Test with no tolerance to make sure that we pick up even a very small
|
||||
# RMS error.
|
||||
('basn3p02.png', 'basn3p02-scrambled.png', 0, 172.63582),
|
||||
# Comparison of an image and a slightly brighter image.
|
||||
# The two images are solid color, with the second image being exactly 1
|
||||
# color value brighter.
|
||||
# This expects the images to compare equal under normal tolerance, and
|
||||
# have an RMS of exactly 1.
|
||||
('all127.png', 'all128.png', 0, 1),
|
||||
# Now test the reverse comparison.
|
||||
('all128.png', 'all127.png', 0, 1),
|
||||
])
|
||||
def test_image_comparison_expect_rms(im1, im2, tol, expect_rms):
|
||||
"""Compare two images, expecting a particular RMS error.
|
||||
|
||||
im1 and im2 are filenames relative to the baseline_dir directory.
|
||||
|
||||
tol is the tolerance to pass to compare_images.
|
||||
|
||||
expect_rms is the expected RMS value, or None. If None, the test will
|
||||
succeed if compare_images succeeds. Otherwise, the test will succeed if
|
||||
compare_images fails and returns an RMS error almost equal to this value.
|
||||
"""
|
||||
im1 = os.path.join(baseline_dir, im1)
|
||||
im2_src = os.path.join(baseline_dir, im2)
|
||||
im2 = os.path.join(result_dir, im2)
|
||||
# Move im2 from baseline_dir to result_dir. This will ensure that
|
||||
# compare_images writes the diff file to result_dir, instead of trying to
|
||||
# write to the (possibly read-only) baseline_dir.
|
||||
shutil.copyfile(im2_src, im2)
|
||||
results = compare_images(im1, im2, tol=tol, in_decorator=True)
|
||||
|
||||
if expect_rms is None:
|
||||
assert results is None
|
||||
else:
|
||||
assert results is not None
|
||||
assert results['rms'] == approx(expect_rms, abs=1e-4)
|
||||
@@ -0,0 +1,406 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
from matplotlib import ticker, rcParams
|
||||
|
||||
|
||||
def example_plot(ax, fontsize=12, nodec=False):
|
||||
ax.plot([1, 2])
|
||||
ax.locator_params(nbins=3)
|
||||
if not nodec:
|
||||
ax.set_xlabel('x-label', fontsize=fontsize)
|
||||
ax.set_ylabel('y-label', fontsize=fontsize)
|
||||
ax.set_title('Title', fontsize=fontsize)
|
||||
else:
|
||||
ax.set_xticklabels('')
|
||||
ax.set_yticklabels('')
|
||||
|
||||
|
||||
def example_pcolor(ax, fontsize=12):
|
||||
dx, dy = 0.6, 0.6
|
||||
y, x = np.mgrid[slice(-3, 3 + dy, dy),
|
||||
slice(-3, 3 + dx, dx)]
|
||||
z = (1 - x / 2. + x ** 5 + y ** 3) * np.exp(-x ** 2 - y ** 2)
|
||||
pcm = ax.pcolormesh(x, y, z, cmap='RdBu_r', vmin=-1., vmax=1.,
|
||||
rasterized=True)
|
||||
# ax.locator_params(nbins=3)
|
||||
ax.set_xlabel('x-label', fontsize=fontsize)
|
||||
ax.set_ylabel('y-label', fontsize=fontsize)
|
||||
ax.set_title('Title', fontsize=fontsize)
|
||||
return pcm
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout1'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout1():
|
||||
'Test constrained_layout for a single subplot'
|
||||
fig = plt.figure(constrained_layout=True)
|
||||
ax = fig.add_subplot(111)
|
||||
example_plot(ax, fontsize=24)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout2'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout2():
|
||||
'Test constrained_layout for 2x2 subplots'
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flatten():
|
||||
example_plot(ax, fontsize=24)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout3'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout3():
|
||||
'Test constrained_layout for colorbars with subplots'
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for nn, ax in enumerate(axs.flatten()):
|
||||
pcm = example_pcolor(ax, fontsize=24)
|
||||
if nn == 3:
|
||||
pad = 0.08
|
||||
else:
|
||||
pad = 0.02 # default
|
||||
fig.colorbar(pcm, ax=ax, pad=pad)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout4'])
|
||||
def test_constrained_layout4():
|
||||
'Test constrained_layout for a single colorbar with subplots'
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flatten():
|
||||
pcm = example_pcolor(ax, fontsize=24)
|
||||
fig.colorbar(pcm, ax=axs, pad=0.01, shrink=0.6)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout5'],
|
||||
tol=5.e-2, extensions=['png'])
|
||||
def test_constrained_layout5():
|
||||
'''
|
||||
Test constrained_layout for a single colorbar with subplots,
|
||||
colorbar bottom
|
||||
'''
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flatten():
|
||||
pcm = example_pcolor(ax, fontsize=24)
|
||||
fig.colorbar(pcm, ax=axs,
|
||||
use_gridspec=False, pad=0.01, shrink=0.6,
|
||||
location='bottom')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout6'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout6():
|
||||
'Test constrained_layout for nested gridspecs'
|
||||
fig = plt.figure(constrained_layout=True)
|
||||
gs = fig.add_gridspec(1, 2, figure=fig)
|
||||
gsl = gs[0].subgridspec(2, 2)
|
||||
gsr = gs[1].subgridspec(1, 2)
|
||||
axsl = []
|
||||
for gs in gsl:
|
||||
ax = fig.add_subplot(gs)
|
||||
axsl += [ax]
|
||||
example_plot(ax, fontsize=12)
|
||||
ax.set_xlabel('x-label\nMultiLine')
|
||||
axsr = []
|
||||
for gs in gsr:
|
||||
ax = fig.add_subplot(gs)
|
||||
axsr += [ax]
|
||||
pcm = example_pcolor(ax, fontsize=12)
|
||||
|
||||
fig.colorbar(pcm, ax=axsr,
|
||||
pad=0.01, shrink=0.99, location='bottom',
|
||||
ticks=ticker.MaxNLocator(nbins=5))
|
||||
|
||||
|
||||
def test_constrained_layout7():
|
||||
'Test for proper warning if fig not set in GridSpec'
|
||||
with pytest.warns(UserWarning, match='Calling figure.constrained_layout, '
|
||||
'but figure not setup to do constrained layout'):
|
||||
fig = plt.figure(constrained_layout=True)
|
||||
gs = gridspec.GridSpec(1, 2)
|
||||
gsl = gridspec.GridSpecFromSubplotSpec(2, 2, gs[0])
|
||||
gsr = gridspec.GridSpecFromSubplotSpec(1, 2, gs[1])
|
||||
axsl = []
|
||||
for gs in gsl:
|
||||
ax = fig.add_subplot(gs)
|
||||
# need to trigger a draw to get warning
|
||||
fig.draw(fig.canvas.get_renderer())
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout8'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout8():
|
||||
'Test for gridspecs that are not completely full'
|
||||
fig = plt.figure(figsize=(10, 5), constrained_layout=True)
|
||||
gs = gridspec.GridSpec(3, 5, figure=fig)
|
||||
axs = []
|
||||
for j in [0, 1]:
|
||||
if j == 0:
|
||||
ilist = [1]
|
||||
else:
|
||||
ilist = [0, 4]
|
||||
for i in ilist:
|
||||
ax = fig.add_subplot(gs[j, i])
|
||||
axs += [ax]
|
||||
pcm = example_pcolor(ax, fontsize=9)
|
||||
if i > 0:
|
||||
ax.set_ylabel('')
|
||||
if j < 1:
|
||||
ax.set_xlabel('')
|
||||
ax.set_title('')
|
||||
ax = fig.add_subplot(gs[2, :])
|
||||
axs += [ax]
|
||||
pcm = example_pcolor(ax, fontsize=9)
|
||||
|
||||
fig.colorbar(pcm, ax=axs, pad=0.01, shrink=0.6)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout9'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout9():
|
||||
'Test for handling suptitle and for sharex and sharey'
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True,
|
||||
sharex=False, sharey=False)
|
||||
for ax in axs.flatten():
|
||||
pcm = example_pcolor(ax, fontsize=24)
|
||||
ax.set_xlabel('')
|
||||
ax.set_ylabel('')
|
||||
ax.set_aspect(2.)
|
||||
fig.colorbar(pcm, ax=axs, pad=0.01, shrink=0.6)
|
||||
fig.suptitle('Test Suptitle', fontsize=28)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout10'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout10():
|
||||
'Test for handling legend outside axis'
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flatten():
|
||||
ax.plot(np.arange(12), label='This is a label')
|
||||
ax.legend(loc='center left', bbox_to_anchor=(0.8, 0.5))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout11'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout11():
|
||||
'Test for multiple nested gridspecs '
|
||||
fig = plt.figure(constrained_layout=True, figsize=(10, 3))
|
||||
gs0 = gridspec.GridSpec(1, 2, figure=fig)
|
||||
gsl = gridspec.GridSpecFromSubplotSpec(1, 2, gs0[0])
|
||||
gsl0 = gridspec.GridSpecFromSubplotSpec(2, 2, gsl[1])
|
||||
ax = fig.add_subplot(gs0[1])
|
||||
example_plot(ax, fontsize=9)
|
||||
axs = []
|
||||
for gs in gsl0:
|
||||
ax = fig.add_subplot(gs)
|
||||
axs += [ax]
|
||||
pcm = example_pcolor(ax, fontsize=9)
|
||||
fig.colorbar(pcm, ax=axs, shrink=0.6, aspect=70.)
|
||||
ax = fig.add_subplot(gsl[0])
|
||||
example_plot(ax, fontsize=9)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout11rat'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout11rat():
|
||||
'Test for multiple nested gridspecs with width_ratios'
|
||||
fig = plt.figure(constrained_layout=True, figsize=(10, 3))
|
||||
gs0 = gridspec.GridSpec(1, 2, figure=fig, width_ratios=[6., 1.])
|
||||
gsl = gridspec.GridSpecFromSubplotSpec(1, 2, gs0[0])
|
||||
gsl0 = gridspec.GridSpecFromSubplotSpec(2, 2, gsl[1],
|
||||
height_ratios=[2., 1.])
|
||||
ax = fig.add_subplot(gs0[1])
|
||||
example_plot(ax, fontsize=9)
|
||||
axs = []
|
||||
for gs in gsl0:
|
||||
ax = fig.add_subplot(gs)
|
||||
axs += [ax]
|
||||
pcm = example_pcolor(ax, fontsize=9)
|
||||
fig.colorbar(pcm, ax=axs, shrink=0.6, aspect=70.)
|
||||
ax = fig.add_subplot(gsl[0])
|
||||
example_plot(ax, fontsize=9)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout12'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout12():
|
||||
'Test that very unbalanced labeling still works.'
|
||||
fig = plt.figure(constrained_layout=True)
|
||||
|
||||
gs0 = gridspec.GridSpec(6, 2, figure=fig)
|
||||
|
||||
ax1 = fig.add_subplot(gs0[:3, 1])
|
||||
ax2 = fig.add_subplot(gs0[3:, 1])
|
||||
|
||||
example_plot(ax1, fontsize=24)
|
||||
example_plot(ax2, fontsize=24)
|
||||
|
||||
ax = fig.add_subplot(gs0[0:2, 0])
|
||||
example_plot(ax, nodec=True)
|
||||
ax = fig.add_subplot(gs0[2:4, 0])
|
||||
example_plot(ax, nodec=True)
|
||||
ax = fig.add_subplot(gs0[4:, 0])
|
||||
example_plot(ax, nodec=True)
|
||||
ax.set_xlabel('x-label')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout13'], tol=2.e-2,
|
||||
extensions=['png'])
|
||||
def test_constrained_layout13():
|
||||
'Test that padding works.'
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flatten():
|
||||
pcm = example_pcolor(ax, fontsize=12)
|
||||
fig.colorbar(pcm, ax=ax, shrink=0.6, aspect=20., pad=0.02)
|
||||
fig.set_constrained_layout_pads(w_pad=24./72., h_pad=24./72.)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout14'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout14():
|
||||
'Test that padding works.'
|
||||
fig, axs = plt.subplots(2, 2, constrained_layout=True)
|
||||
for ax in axs.flatten():
|
||||
pcm = example_pcolor(ax, fontsize=12)
|
||||
fig.colorbar(pcm, ax=ax, shrink=0.6, aspect=20., pad=0.02)
|
||||
fig.set_constrained_layout_pads(
|
||||
w_pad=3./72., h_pad=3./72.,
|
||||
hspace=0.2, wspace=0.2)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout15'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout15():
|
||||
'Test that rcparams work.'
|
||||
rcParams['figure.constrained_layout.use'] = True
|
||||
fig, axs = plt.subplots(2, 2)
|
||||
for ax in axs.flatten():
|
||||
example_plot(ax, fontsize=12)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout16'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout16():
|
||||
'Test ax.set_position.'
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
example_plot(ax, fontsize=12)
|
||||
ax2 = fig.add_axes([0.2, 0.2, 0.4, 0.4])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['constrained_layout17'],
|
||||
extensions=['png'])
|
||||
def test_constrained_layout17():
|
||||
'Test uneven gridspecs'
|
||||
fig = plt.figure(constrained_layout=True)
|
||||
gs = gridspec.GridSpec(3, 3, figure=fig)
|
||||
|
||||
ax1 = fig.add_subplot(gs[0, 0])
|
||||
ax2 = fig.add_subplot(gs[0, 1:])
|
||||
ax3 = fig.add_subplot(gs[1:, 0:2])
|
||||
ax4 = fig.add_subplot(gs[1:, -1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
|
||||
|
||||
def test_constrained_layout18():
|
||||
'Test twinx'
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
ax2 = ax.twinx()
|
||||
example_plot(ax)
|
||||
example_plot(ax2, fontsize=24)
|
||||
fig.canvas.draw()
|
||||
assert all(ax.get_position().extents == ax2.get_position().extents)
|
||||
|
||||
|
||||
def test_constrained_layout19():
|
||||
'Test twiny'
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
ax2 = ax.twiny()
|
||||
example_plot(ax)
|
||||
example_plot(ax2, fontsize=24)
|
||||
ax2.set_title('')
|
||||
ax.set_title('')
|
||||
fig.canvas.draw()
|
||||
assert all(ax.get_position().extents == ax2.get_position().extents)
|
||||
|
||||
|
||||
def test_constrained_layout20():
|
||||
'Smoke test cl does not mess up added axes'
|
||||
gx = np.linspace(-5, 5, 4)
|
||||
img = np.hypot(gx, gx[:, None])
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_axes([0, 0, 1, 1])
|
||||
mesh = ax.pcolormesh(gx, gx, img)
|
||||
fig.colorbar(mesh)
|
||||
|
||||
|
||||
def test_constrained_layout21():
|
||||
'#11035: repeated calls to suptitle should not alter the layout'
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
|
||||
fig.suptitle("Suptitle0")
|
||||
fig.canvas.draw()
|
||||
extents0 = np.copy(ax.get_position().extents)
|
||||
|
||||
fig.suptitle("Suptitle1")
|
||||
fig.canvas.draw()
|
||||
extents1 = np.copy(ax.get_position().extents)
|
||||
|
||||
np.testing.assert_allclose(extents0, extents1)
|
||||
|
||||
|
||||
def test_constrained_layout22():
|
||||
'#11035: suptitle should not be include in CL if manually positioned'
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
|
||||
fig.canvas.draw()
|
||||
extents0 = np.copy(ax.get_position().extents)
|
||||
|
||||
fig.suptitle("Suptitle", y=0.5)
|
||||
fig.canvas.draw()
|
||||
extents1 = np.copy(ax.get_position().extents)
|
||||
|
||||
np.testing.assert_allclose(extents0, extents1)
|
||||
|
||||
|
||||
def test_constrained_layout23():
|
||||
'''
|
||||
Comment in #11035: suptitle used to cause an exception when
|
||||
reusing a figure w/ CL with ``clear=True``.
|
||||
'''
|
||||
|
||||
for i in range(2):
|
||||
fig, ax = plt.subplots(num="123", constrained_layout=True, clear=True)
|
||||
fig.suptitle("Suptitle{}".format(i))
|
||||
|
||||
|
||||
# This test occasionally fails the image comparison tests, so we mark as
|
||||
# flaky. Apparently the constraint solver occasionally doesn't fully
|
||||
# optimize. Would be nice if this were more deterministic...
|
||||
@pytest.mark.timeout(30)
|
||||
@pytest.mark.flaky(reruns=3)
|
||||
@image_comparison(baseline_images=['test_colorbar_location'],
|
||||
extensions=['png'], remove_text=True, style='mpl20')
|
||||
def test_colorbar_location():
|
||||
"""
|
||||
Test that colorbar handling is as expected for various complicated
|
||||
cases...
|
||||
"""
|
||||
|
||||
fig, axs = plt.subplots(4, 5, constrained_layout=True)
|
||||
for ax in axs.flatten():
|
||||
pcm = example_pcolor(ax)
|
||||
ax.set_xlabel('')
|
||||
ax.set_ylabel('')
|
||||
fig.colorbar(pcm, ax=axs[:, 1], shrink=0.4)
|
||||
fig.colorbar(pcm, ax=axs[-1, :2], shrink=0.5, location='bottom')
|
||||
fig.colorbar(pcm, ax=axs[0, 2:], shrink=0.5, location='bottom')
|
||||
fig.colorbar(pcm, ax=axs[-2, 3:], shrink=0.5, location='top')
|
||||
fig.colorbar(pcm, ax=axs[0, 0], shrink=0.5, location='left')
|
||||
fig.colorbar(pcm, ax=axs[1:3, 2], shrink=0.5, location='right')
|
||||
@@ -0,0 +1,30 @@
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def test_stem_remove():
|
||||
ax = plt.gca()
|
||||
st = ax.stem([1, 2], [1, 2])
|
||||
st.remove()
|
||||
|
||||
|
||||
def test_errorbar_remove():
|
||||
|
||||
# Regression test for a bug that caused remove to fail when using
|
||||
# fmt='none'
|
||||
|
||||
ax = plt.gca()
|
||||
|
||||
eb = ax.errorbar([1], [1])
|
||||
eb.remove()
|
||||
|
||||
eb = ax.errorbar([1], [1], xerr=1)
|
||||
eb.remove()
|
||||
|
||||
eb = ax.errorbar([1], [1], yerr=2)
|
||||
eb.remove()
|
||||
|
||||
eb = ax.errorbar([1], [1], xerr=[2], yerr=2)
|
||||
eb.remove()
|
||||
|
||||
eb = ax.errorbar([1], [1], fmt='none')
|
||||
eb.remove()
|
||||
@@ -0,0 +1,417 @@
|
||||
import datetime
|
||||
|
||||
import numpy as np
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
from matplotlib import pyplot as plt
|
||||
from numpy.testing import assert_array_almost_equal
|
||||
from matplotlib.colors import LogNorm
|
||||
import pytest
|
||||
import warnings
|
||||
|
||||
|
||||
def test_contour_shape_1d_valid():
|
||||
|
||||
x = np.arange(10)
|
||||
y = np.arange(9)
|
||||
z = np.random.random((9, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.contour(x, y, z)
|
||||
|
||||
|
||||
def test_contour_shape_2d_valid():
|
||||
|
||||
x = np.arange(10)
|
||||
y = np.arange(9)
|
||||
xg, yg = np.meshgrid(x, y)
|
||||
z = np.random.random((9, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.contour(xg, yg, z)
|
||||
|
||||
|
||||
def test_contour_shape_mismatch_1():
|
||||
|
||||
x = np.arange(9)
|
||||
y = np.arange(9)
|
||||
z = np.random.random((9, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
ax.contour(x, y, z)
|
||||
excinfo.match(r'Length of x must be number of columns in z.')
|
||||
|
||||
|
||||
def test_contour_shape_mismatch_2():
|
||||
|
||||
x = np.arange(10)
|
||||
y = np.arange(10)
|
||||
z = np.random.random((9, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
ax.contour(x, y, z)
|
||||
excinfo.match(r'Length of y must be number of rows in z.')
|
||||
|
||||
|
||||
def test_contour_shape_mismatch_3():
|
||||
|
||||
x = np.arange(10)
|
||||
y = np.arange(10)
|
||||
xg, yg = np.meshgrid(x, y)
|
||||
z = np.random.random((9, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
ax.contour(xg, y, z)
|
||||
excinfo.match(r'Number of dimensions of x and y should match.')
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
ax.contour(x, yg, z)
|
||||
excinfo.match(r'Number of dimensions of x and y should match.')
|
||||
|
||||
|
||||
def test_contour_shape_mismatch_4():
|
||||
|
||||
g = np.random.random((9, 10))
|
||||
b = np.random.random((9, 9))
|
||||
z = np.random.random((9, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
ax.contour(b, g, z)
|
||||
excinfo.match(r'Shape of x does not match that of z: found \(9L?, 9L?\) ' +
|
||||
r'instead of \(9L?, 10L?\)')
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
ax.contour(g, b, z)
|
||||
excinfo.match(r'Shape of y does not match that of z: found \(9L?, 9L?\) ' +
|
||||
r'instead of \(9L?, 10L?\)')
|
||||
|
||||
|
||||
def test_contour_shape_invalid_1():
|
||||
|
||||
x = np.random.random((3, 3, 3))
|
||||
y = np.random.random((3, 3, 3))
|
||||
z = np.random.random((9, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
ax.contour(x, y, z)
|
||||
excinfo.match(r'Inputs x and y must be 1D or 2D.')
|
||||
|
||||
|
||||
def test_contour_shape_invalid_2():
|
||||
|
||||
x = np.random.random((3, 3, 3))
|
||||
y = np.random.random((3, 3, 3))
|
||||
z = np.random.random((3, 3, 3))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
ax.contour(x, y, z)
|
||||
excinfo.match(r'Input z must be a 2D array.')
|
||||
|
||||
|
||||
def test_contour_empty_levels():
|
||||
|
||||
x = np.arange(9)
|
||||
z = np.random.random((9, 9))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
with pytest.warns(UserWarning) as record:
|
||||
ax.contour(x, x, z, levels=[])
|
||||
assert len(record) == 1
|
||||
|
||||
|
||||
def test_contour_Nlevels():
|
||||
# A scalar levels arg or kwarg should trigger auto level generation.
|
||||
# https://github.com/matplotlib/matplotlib/issues/11913
|
||||
z = np.arange(12).reshape((3, 4))
|
||||
fig, ax = plt.subplots()
|
||||
cs1 = ax.contour(z, 5)
|
||||
assert len(cs1.levels) > 1
|
||||
cs2 = ax.contour(z, levels=5)
|
||||
assert (cs1.levels == cs2.levels).all()
|
||||
|
||||
|
||||
def test_contour_badlevel_fmt():
|
||||
# test funny edge case from
|
||||
# https://github.com/matplotlib/matplotlib/issues/9742
|
||||
# User supplied fmt for each level as a dictionary, but
|
||||
# MPL changed the level to the minimum data value because
|
||||
# no contours possible.
|
||||
# This would error out pre
|
||||
# https://github.com/matplotlib/matplotlib/pull/9743
|
||||
x = np.arange(9)
|
||||
z = np.zeros((9, 9))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
fmt = {1.: '%1.2f'}
|
||||
with pytest.warns(UserWarning) as record:
|
||||
cs = ax.contour(x, x, z, levels=[1.])
|
||||
ax.clabel(cs, fmt=fmt)
|
||||
assert len(record) == 1
|
||||
|
||||
|
||||
def test_contour_uniform_z():
|
||||
|
||||
x = np.arange(9)
|
||||
z = np.ones((9, 9))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
with pytest.warns(UserWarning) as record:
|
||||
ax.contour(x, x, z)
|
||||
assert len(record) == 1
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['contour_manual_labels'],
|
||||
savefig_kwarg={'dpi': 200}, remove_text=True, style='mpl20')
|
||||
def test_contour_manual_labels():
|
||||
|
||||
x, y = np.meshgrid(np.arange(0, 10), np.arange(0, 10))
|
||||
z = np.max(np.dstack([abs(x), abs(y)]), 2)
|
||||
|
||||
plt.figure(figsize=(6, 2), dpi=200)
|
||||
cs = plt.contour(x, y, z)
|
||||
pts = np.array([(1.5, 3.0), (1.5, 4.4), (1.5, 6.0)])
|
||||
plt.clabel(cs, manual=pts)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['contour_labels_size_color'],
|
||||
extensions=['png'], remove_text=True, style='mpl20')
|
||||
def test_contour_labels_size_color():
|
||||
|
||||
x, y = np.meshgrid(np.arange(0, 10), np.arange(0, 10))
|
||||
z = np.max(np.dstack([abs(x), abs(y)]), 2)
|
||||
|
||||
plt.figure(figsize=(6, 2))
|
||||
cs = plt.contour(x, y, z)
|
||||
pts = np.array([(1.5, 3.0), (1.5, 4.4), (1.5, 6.0)])
|
||||
plt.clabel(cs, manual=pts, fontsize='small', colors=('r', 'g'))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['contour_manual_colors_and_levels'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_given_colors_levels_and_extends():
|
||||
_, axes = plt.subplots(2, 4)
|
||||
|
||||
data = np.arange(12).reshape(3, 4)
|
||||
|
||||
colors = ['red', 'yellow', 'pink', 'blue', 'black']
|
||||
levels = [2, 4, 8, 10]
|
||||
|
||||
for i, ax in enumerate(axes.flatten()):
|
||||
filled = i % 2 == 0.
|
||||
extend = ['neither', 'min', 'max', 'both'][i // 2]
|
||||
|
||||
if filled:
|
||||
# If filled, we have 3 colors with no extension,
|
||||
# 4 colors with one extension, and 5 colors with both extensions
|
||||
first_color = 1 if extend in ['max', 'neither'] else None
|
||||
last_color = -1 if extend in ['min', 'neither'] else None
|
||||
c = ax.contourf(data, colors=colors[first_color:last_color],
|
||||
levels=levels, extend=extend)
|
||||
else:
|
||||
# If not filled, we have 4 levels and 4 colors
|
||||
c = ax.contour(data, colors=colors[:-1],
|
||||
levels=levels, extend=extend)
|
||||
|
||||
plt.colorbar(c, ax=ax)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['contour_datetime_axis'],
|
||||
extensions=['png'], remove_text=False, style='mpl20')
|
||||
def test_contour_datetime_axis():
|
||||
fig = plt.figure()
|
||||
fig.subplots_adjust(hspace=0.4, top=0.98, bottom=.15)
|
||||
base = datetime.datetime(2013, 1, 1)
|
||||
x = np.array([base + datetime.timedelta(days=d) for d in range(20)])
|
||||
y = np.arange(20)
|
||||
z1, z2 = np.meshgrid(np.arange(20), np.arange(20))
|
||||
z = z1 * z2
|
||||
plt.subplot(221)
|
||||
plt.contour(x, y, z)
|
||||
plt.subplot(222)
|
||||
plt.contourf(x, y, z)
|
||||
x = np.repeat(x[np.newaxis], 20, axis=0)
|
||||
y = np.repeat(y[:, np.newaxis], 20, axis=1)
|
||||
plt.subplot(223)
|
||||
plt.contour(x, y, z)
|
||||
plt.subplot(224)
|
||||
plt.contourf(x, y, z)
|
||||
for ax in fig.get_axes():
|
||||
for label in ax.get_xticklabels():
|
||||
label.set_ha('right')
|
||||
label.set_rotation(30)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['contour_test_label_transforms'],
|
||||
extensions=['png'], remove_text=True, style='mpl20')
|
||||
def test_labels():
|
||||
# Adapted from pylab_examples example code: contour_demo.py
|
||||
# see issues #2475, #2843, and #2818 for explanation
|
||||
delta = 0.025
|
||||
x = np.arange(-3.0, 3.0, delta)
|
||||
y = np.arange(-2.0, 2.0, delta)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
Z1 = np.exp(-(X**2 + Y**2) / 2) / (2 * np.pi)
|
||||
Z2 = (np.exp(-(((X - 1) / 1.5)**2 + ((Y - 1) / 0.5)**2) / 2) /
|
||||
(2 * np.pi * 0.5 * 1.5))
|
||||
|
||||
# difference of Gaussians
|
||||
Z = 10.0 * (Z2 - Z1)
|
||||
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
CS = ax.contour(X, Y, Z)
|
||||
disp_units = [(216, 177), (359, 290), (521, 406)]
|
||||
data_units = [(-2, .5), (0, -1.5), (2.8, 1)]
|
||||
|
||||
CS.clabel()
|
||||
|
||||
for x, y in data_units:
|
||||
CS.add_label_near(x, y, inline=True, transform=None)
|
||||
|
||||
for x, y in disp_units:
|
||||
CS.add_label_near(x, y, inline=True, transform=False)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['contour_corner_mask_False',
|
||||
'contour_corner_mask_True'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_corner_mask():
|
||||
n = 60
|
||||
mask_level = 0.95
|
||||
noise_amp = 1.0
|
||||
np.random.seed([1])
|
||||
x, y = np.meshgrid(np.linspace(0, 2.0, n), np.linspace(0, 2.0, n))
|
||||
z = np.cos(7*x)*np.sin(8*y) + noise_amp*np.random.rand(n, n)
|
||||
mask = np.where(np.random.rand(n, n) >= mask_level, True, False)
|
||||
z = np.ma.array(z, mask=mask)
|
||||
|
||||
for corner_mask in [False, True]:
|
||||
fig = plt.figure()
|
||||
plt.contourf(z, corner_mask=corner_mask)
|
||||
|
||||
|
||||
def test_contourf_decreasing_levels():
|
||||
# github issue 5477.
|
||||
z = [[0.1, 0.3], [0.5, 0.7]]
|
||||
plt.figure()
|
||||
with pytest.raises(ValueError):
|
||||
plt.contourf(z, [1.0, 0.0])
|
||||
|
||||
|
||||
def test_contourf_symmetric_locator():
|
||||
# github issue 7271
|
||||
z = np.arange(12).reshape((3, 4))
|
||||
locator = plt.MaxNLocator(nbins=4, symmetric=True)
|
||||
cs = plt.contourf(z, locator=locator)
|
||||
assert_array_almost_equal(cs.levels, np.linspace(-12, 12, 5))
|
||||
|
||||
|
||||
def test_contour_1x1_array():
|
||||
# github issue 8197
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
plt.contour([[0]])
|
||||
excinfo.match(r'Input z must be at least a 2x2 array.')
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
plt.contour([0], [0], [[0]])
|
||||
excinfo.match(r'Input z must be at least a 2x2 array.')
|
||||
|
||||
|
||||
def test_internal_cpp_api():
|
||||
# Following github issue 8197.
|
||||
import matplotlib._contour as _contour
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
qcg = _contour.QuadContourGenerator()
|
||||
excinfo.match(r'function takes exactly 6 arguments \(0 given\)')
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
qcg = _contour.QuadContourGenerator(1, 2, 3, 4, 5, 6)
|
||||
excinfo.match(r'Expected 2-dimensional array, got 0')
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
qcg = _contour.QuadContourGenerator([[0]], [[0]], [[]], None, True, 0)
|
||||
excinfo.match(r'x, y and z must all be 2D arrays with the same dimensions')
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
qcg = _contour.QuadContourGenerator([[0]], [[0]], [[0]], None, True, 0)
|
||||
excinfo.match(r'x, y and z must all be at least 2x2 arrays')
|
||||
|
||||
arr = [[0, 1], [2, 3]]
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
qcg = _contour.QuadContourGenerator(arr, arr, arr, [[0]], True, 0)
|
||||
excinfo.match(r'If mask is set it must be a 2D array with the same ' +
|
||||
r'dimensions as x.')
|
||||
|
||||
qcg = _contour.QuadContourGenerator(arr, arr, arr, None, True, 0)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
qcg.create_filled_contour(1, 0)
|
||||
excinfo.match(r'filled contour levels must be increasing')
|
||||
|
||||
|
||||
def test_circular_contour_warning():
|
||||
# Check that almost circular contours don't throw a warning
|
||||
with pytest.warns(None) as record:
|
||||
x, y = np.meshgrid(np.linspace(-2, 2, 4), np.linspace(-2, 2, 4))
|
||||
r = np.sqrt(x ** 2 + y ** 2)
|
||||
|
||||
plt.figure()
|
||||
cs = plt.contour(x, y, r)
|
||||
plt.clabel(cs)
|
||||
assert len(record) == 0
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['contour_log_extension'],
|
||||
extensions=['png'], remove_text=True, style='mpl20')
|
||||
def test_contourf_log_extension():
|
||||
# Test that contourf with lognorm is extended correctly
|
||||
fig = plt.figure(figsize=(10, 5))
|
||||
fig.subplots_adjust(left=0.05, right=0.95)
|
||||
ax1 = fig.add_subplot(131)
|
||||
ax2 = fig.add_subplot(132)
|
||||
ax3 = fig.add_subplot(133)
|
||||
|
||||
# make data set with large range e.g. between 1e-8 and 1e10
|
||||
data_exp = np.linspace(-7.5, 9.5, 1200)
|
||||
data = np.power(10, data_exp).reshape(30, 40)
|
||||
# make manual levels e.g. between 1e-4 and 1e-6
|
||||
levels_exp = np.arange(-4., 7.)
|
||||
levels = np.power(10., levels_exp)
|
||||
|
||||
# original data
|
||||
c1 = ax1.contourf(data,
|
||||
norm=LogNorm(vmin=data.min(), vmax=data.max()))
|
||||
# just show data in levels
|
||||
c2 = ax2.contourf(data, levels=levels,
|
||||
norm=LogNorm(vmin=levels.min(), vmax=levels.max()),
|
||||
extend='neither')
|
||||
# extend data from levels
|
||||
c3 = ax3.contourf(data, levels=levels,
|
||||
norm=LogNorm(vmin=levels.min(), vmax=levels.max()),
|
||||
extend='both')
|
||||
plt.colorbar(c1, ax=ax1)
|
||||
plt.colorbar(c2, ax=ax2)
|
||||
plt.colorbar(c3, ax=ax3)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['contour_addlines'],
|
||||
extensions=['png'], remove_text=True, style='mpl20')
|
||||
def test_contour_addlines():
|
||||
fig, ax = plt.subplots()
|
||||
np.random.seed(19680812)
|
||||
X = np.random.rand(10, 10)*10000
|
||||
pcm = ax.pcolormesh(X)
|
||||
# add 1000 to make colors visible...
|
||||
cont = ax.contour(X+1000)
|
||||
cb = fig.colorbar(pcm)
|
||||
cb.add_lines(cont)
|
||||
@@ -0,0 +1,208 @@
|
||||
import warnings
|
||||
import platform
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from cycler import cycler
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['color_cycle_basic'], remove_text=True,
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
extensions=['png'])
|
||||
def test_colorcycle_basic():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_prop_cycle(cycler('color', ['r', 'g', 'y']))
|
||||
xs = np.arange(10)
|
||||
ys = 0.25 * xs + 2
|
||||
ax.plot(xs, ys, label='red', lw=4)
|
||||
ys = 0.45 * xs + 3
|
||||
ax.plot(xs, ys, label='green', lw=4)
|
||||
ys = 0.65 * xs + 4
|
||||
ax.plot(xs, ys, label='yellow', lw=4)
|
||||
ys = 0.85 * xs + 5
|
||||
ax.plot(xs, ys, label='red2', lw=4)
|
||||
ax.legend(loc='upper left')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['marker_cycle', 'marker_cycle'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
remove_text=True, extensions=['png'])
|
||||
def test_marker_cycle():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_prop_cycle(cycler('c', ['r', 'g', 'y']) +
|
||||
cycler('marker', ['.', '*', 'x']))
|
||||
xs = np.arange(10)
|
||||
ys = 0.25 * xs + 2
|
||||
ax.plot(xs, ys, label='red dot', lw=4, ms=16)
|
||||
ys = 0.45 * xs + 3
|
||||
ax.plot(xs, ys, label='green star', lw=4, ms=16)
|
||||
ys = 0.65 * xs + 4
|
||||
ax.plot(xs, ys, label='yellow x', lw=4, ms=16)
|
||||
ys = 0.85 * xs + 5
|
||||
ax.plot(xs, ys, label='red2 dot', lw=4, ms=16)
|
||||
ax.legend(loc='upper left')
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
# Test keyword arguments, numpy arrays, and generic iterators
|
||||
ax.set_prop_cycle(c=np.array(['r', 'g', 'y']),
|
||||
marker=iter(['.', '*', 'x']))
|
||||
xs = np.arange(10)
|
||||
ys = 0.25 * xs + 2
|
||||
ax.plot(xs, ys, label='red dot', lw=4, ms=16)
|
||||
ys = 0.45 * xs + 3
|
||||
ax.plot(xs, ys, label='green star', lw=4, ms=16)
|
||||
ys = 0.65 * xs + 4
|
||||
ax.plot(xs, ys, label='yellow x', lw=4, ms=16)
|
||||
ys = 0.85 * xs + 5
|
||||
ax.plot(xs, ys, label='red2 dot', lw=4, ms=16)
|
||||
ax.legend(loc='upper left')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['lineprop_cycle_basic'], remove_text=True,
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
extensions=['png'])
|
||||
def test_linestylecycle_basic():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_prop_cycle(cycler('ls', ['-', '--', ':']))
|
||||
xs = np.arange(10)
|
||||
ys = 0.25 * xs + 2
|
||||
ax.plot(xs, ys, label='solid', lw=4, color='k')
|
||||
ys = 0.45 * xs + 3
|
||||
ax.plot(xs, ys, label='dashed', lw=4, color='k')
|
||||
ys = 0.65 * xs + 4
|
||||
ax.plot(xs, ys, label='dotted', lw=4, color='k')
|
||||
ys = 0.85 * xs + 5
|
||||
ax.plot(xs, ys, label='solid2', lw=4, color='k')
|
||||
ax.legend(loc='upper left')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fill_cycle_basic'], remove_text=True,
|
||||
extensions=['png'])
|
||||
def test_fillcycle_basic():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_prop_cycle(cycler('c', ['r', 'g', 'y']) +
|
||||
cycler('hatch', ['xx', 'O', '|-']) +
|
||||
cycler('linestyle', ['-', '--', ':']))
|
||||
xs = np.arange(10)
|
||||
ys = 0.25 * xs**.5 + 2
|
||||
ax.fill(xs, ys, label='red, xx', linewidth=3)
|
||||
ys = 0.45 * xs**.5 + 3
|
||||
ax.fill(xs, ys, label='green, circle', linewidth=3)
|
||||
ys = 0.65 * xs**.5 + 4
|
||||
ax.fill(xs, ys, label='yellow, cross', linewidth=3)
|
||||
ys = 0.85 * xs**.5 + 5
|
||||
ax.fill(xs, ys, label='red2, xx', linewidth=3)
|
||||
ax.legend(loc='upper left')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fill_cycle_ignore'], remove_text=True,
|
||||
extensions=['png'])
|
||||
def test_fillcycle_ignore():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_prop_cycle(cycler('color', ['r', 'g', 'y']) +
|
||||
cycler('hatch', ['xx', 'O', '|-']) +
|
||||
cycler('marker', ['.', '*', 'D']))
|
||||
xs = np.arange(10)
|
||||
ys = 0.25 * xs**.5 + 2
|
||||
# Should not advance the cycler, even though there is an
|
||||
# unspecified property in the cycler "marker".
|
||||
# "marker" is not a Polygon property, and should be ignored.
|
||||
ax.fill(xs, ys, 'r', hatch='xx', label='red, xx')
|
||||
ys = 0.45 * xs**.5 + 3
|
||||
# Allow the cycler to advance, but specify some properties
|
||||
ax.fill(xs, ys, hatch='O', label='red, circle')
|
||||
ys = 0.65 * xs**.5 + 4
|
||||
ax.fill(xs, ys, label='green, circle')
|
||||
ys = 0.85 * xs**.5 + 5
|
||||
ax.fill(xs, ys, label='yellow, cross')
|
||||
ax.legend(loc='upper left')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['property_collision_plot'],
|
||||
remove_text=True, extensions=['png'])
|
||||
def test_property_collision_plot():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_prop_cycle('linewidth', [2, 4])
|
||||
for c in range(1, 4):
|
||||
ax.plot(np.arange(10), c * np.arange(10), lw=0.1, color='k')
|
||||
ax.plot(np.arange(10), 4 * np.arange(10), color='k')
|
||||
ax.plot(np.arange(10), 5 * np.arange(10), color='k')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['property_collision_fill'],
|
||||
remove_text=True, extensions=['png'])
|
||||
def test_property_collision_fill():
|
||||
fig, ax = plt.subplots()
|
||||
xs = np.arange(10)
|
||||
ys = 0.25 * xs**.5 + 2
|
||||
ax.set_prop_cycle(linewidth=[2, 3, 4, 5, 6], facecolor='bgcmy')
|
||||
for c in range(1, 4):
|
||||
ax.fill(xs, c * ys, lw=0.1)
|
||||
ax.fill(xs, 4 * ys)
|
||||
ax.fill(xs, 5 * ys)
|
||||
|
||||
|
||||
def test_valid_input_forms():
|
||||
fig, ax = plt.subplots()
|
||||
# These should not raise an error.
|
||||
ax.set_prop_cycle(None)
|
||||
ax.set_prop_cycle(cycler('linewidth', [1, 2]))
|
||||
ax.set_prop_cycle('color', 'rgywkbcm')
|
||||
ax.set_prop_cycle('lw', (1, 2))
|
||||
ax.set_prop_cycle('linewidth', [1, 2])
|
||||
ax.set_prop_cycle('linewidth', iter([1, 2]))
|
||||
ax.set_prop_cycle('linewidth', np.array([1, 2]))
|
||||
ax.set_prop_cycle('color', np.array([[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1]]))
|
||||
ax.set_prop_cycle('dashes', [[], [13, 2], [8, 3, 1, 3], [None, None]])
|
||||
ax.set_prop_cycle(lw=[1, 2], color=['k', 'w'], ls=['-', '--'])
|
||||
ax.set_prop_cycle(lw=np.array([1, 2]),
|
||||
color=np.array(['k', 'w']),
|
||||
ls=np.array(['-', '--']))
|
||||
assert True
|
||||
|
||||
|
||||
def test_cycle_reset():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
# Can't really test a reset because only a cycle object is stored
|
||||
# but we can test the first item of the cycle.
|
||||
prop = next(ax._get_lines.prop_cycler)
|
||||
ax.set_prop_cycle(linewidth=[10, 9, 4])
|
||||
assert prop != next(ax._get_lines.prop_cycler)
|
||||
ax.set_prop_cycle(None)
|
||||
got = next(ax._get_lines.prop_cycler)
|
||||
assert prop == got
|
||||
|
||||
|
||||
def test_invalid_input_forms():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle(1)
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle([1, 2])
|
||||
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle('color', 'fish')
|
||||
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle('linewidth', 1)
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle('linewidth', {'1': 1, '2': 2})
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle(linewidth=1, color='r')
|
||||
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle('foobar', [1, 2])
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle(foobar=[1, 2])
|
||||
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
ax.set_prop_cycle(cycler(foobar=[1, 2]))
|
||||
with pytest.raises(ValueError):
|
||||
ax.set_prop_cycle(cycler(color='rgb', c='cmy'))
|
||||
@@ -0,0 +1,649 @@
|
||||
import datetime
|
||||
import tempfile
|
||||
from unittest.mock import Mock
|
||||
|
||||
import dateutil.tz
|
||||
import dateutil.rrule
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.cbook import MatplotlibDeprecationWarning
|
||||
import matplotlib.dates as mdates
|
||||
|
||||
|
||||
def __has_pytz():
|
||||
try:
|
||||
import pytz
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
|
||||
def test_date_numpyx():
|
||||
# test that numpy dates work properly...
|
||||
base = datetime.datetime(2017, 1, 1)
|
||||
time = [base + datetime.timedelta(days=x) for x in range(0, 3)]
|
||||
timenp = np.array(time, dtype='datetime64[ns]')
|
||||
data = np.array([0., 2., 1.])
|
||||
fig = plt.figure(figsize=(10, 2))
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
h, = ax.plot(time, data)
|
||||
hnp, = ax.plot(timenp, data)
|
||||
assert np.array_equal(h.get_xdata(orig=False), hnp.get_xdata(orig=False))
|
||||
fig = plt.figure(figsize=(10, 2))
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
h, = ax.plot(data, time)
|
||||
hnp, = ax.plot(data, timenp)
|
||||
assert np.array_equal(h.get_ydata(orig=False), hnp.get_ydata(orig=False))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('t0', [datetime.datetime(2017, 1, 1, 0, 1, 1),
|
||||
|
||||
[datetime.datetime(2017, 1, 1, 0, 1, 1),
|
||||
datetime.datetime(2017, 1, 1, 1, 1, 1)],
|
||||
|
||||
[[datetime.datetime(2017, 1, 1, 0, 1, 1),
|
||||
datetime.datetime(2017, 1, 1, 1, 1, 1)],
|
||||
[datetime.datetime(2017, 1, 1, 2, 1, 1),
|
||||
datetime.datetime(2017, 1, 1, 3, 1, 1)]]])
|
||||
@pytest.mark.parametrize('dtype', ['datetime64[s]',
|
||||
'datetime64[us]',
|
||||
'datetime64[ms]',
|
||||
'datetime64[ns]'])
|
||||
def test_date_date2num_numpy(t0, dtype):
|
||||
time = mdates.date2num(t0)
|
||||
tnp = np.array(t0, dtype=dtype)
|
||||
nptime = mdates.date2num(tnp)
|
||||
assert np.array_equal(time, nptime)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dtype', ['datetime64[s]',
|
||||
'datetime64[us]',
|
||||
'datetime64[ms]',
|
||||
'datetime64[ns]'])
|
||||
def test_date2num_NaT(dtype):
|
||||
t0 = datetime.datetime(2017, 1, 1, 0, 1, 1)
|
||||
tmpl = [mdates.date2num(t0), np.nan]
|
||||
tnp = np.array([t0, 'NaT'], dtype=dtype)
|
||||
nptime = mdates.date2num(tnp)
|
||||
np.testing.assert_array_equal(tmpl, nptime)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('units', ['s', 'ms', 'us', 'ns'])
|
||||
def test_date2num_NaT_scalar(units):
|
||||
tmpl = mdates.date2num(np.datetime64('NaT', units))
|
||||
assert np.isnan(tmpl)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['date_empty'], extensions=['png'])
|
||||
def test_date_empty():
|
||||
# make sure mpl does the right thing when told to plot dates even
|
||||
# if no date data has been presented, cf
|
||||
# http://sourceforge.net/tracker/?func=detail&aid=2850075&group_id=80706&atid=560720
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.xaxis_date()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['date_axhspan'], extensions=['png'])
|
||||
def test_date_axhspan():
|
||||
# test ax hspan with date inputs
|
||||
t0 = datetime.datetime(2009, 1, 20)
|
||||
tf = datetime.datetime(2009, 1, 21)
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.axhspan(t0, tf, facecolor="blue", alpha=0.25)
|
||||
ax.set_ylim(t0 - datetime.timedelta(days=5),
|
||||
tf + datetime.timedelta(days=5))
|
||||
fig.subplots_adjust(left=0.25)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['date_axvspan'], extensions=['png'])
|
||||
def test_date_axvspan():
|
||||
# test ax hspan with date inputs
|
||||
t0 = datetime.datetime(2000, 1, 20)
|
||||
tf = datetime.datetime(2010, 1, 21)
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.axvspan(t0, tf, facecolor="blue", alpha=0.25)
|
||||
ax.set_xlim(t0 - datetime.timedelta(days=720),
|
||||
tf + datetime.timedelta(days=720))
|
||||
fig.autofmt_xdate()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['date_axhline'],
|
||||
extensions=['png'])
|
||||
def test_date_axhline():
|
||||
# test ax hline with date inputs
|
||||
t0 = datetime.datetime(2009, 1, 20)
|
||||
tf = datetime.datetime(2009, 1, 31)
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.axhline(t0, color="blue", lw=3)
|
||||
ax.set_ylim(t0 - datetime.timedelta(days=5),
|
||||
tf + datetime.timedelta(days=5))
|
||||
fig.subplots_adjust(left=0.25)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['date_axvline'],
|
||||
extensions=['png'])
|
||||
def test_date_axvline():
|
||||
# test ax hline with date inputs
|
||||
t0 = datetime.datetime(2000, 1, 20)
|
||||
tf = datetime.datetime(2000, 1, 21)
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.axvline(t0, color="red", lw=3)
|
||||
ax.set_xlim(t0 - datetime.timedelta(days=5),
|
||||
tf + datetime.timedelta(days=5))
|
||||
fig.autofmt_xdate()
|
||||
|
||||
|
||||
def test_too_many_date_ticks():
|
||||
# Attempt to test SF 2715172, see
|
||||
# https://sourceforge.net/tracker/?func=detail&aid=2715172&group_id=80706&atid=560720
|
||||
# setting equal datetimes triggers and expander call in
|
||||
# transforms.nonsingular which results in too many ticks in the
|
||||
# DayLocator. This should trigger a Locator.MAXTICKS RuntimeError
|
||||
t0 = datetime.datetime(2000, 1, 20)
|
||||
tf = datetime.datetime(2000, 1, 20)
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
with pytest.warns(UserWarning) as rec:
|
||||
ax.set_xlim((t0, tf), auto=True)
|
||||
assert len(rec) == 1
|
||||
assert 'Attempting to set identical left==right' in str(rec[0].message)
|
||||
ax.plot([], [])
|
||||
ax.xaxis.set_major_locator(mdates.DayLocator())
|
||||
with pytest.raises(RuntimeError):
|
||||
fig.savefig('junk.png')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['RRuleLocator_bounds'], extensions=['png'])
|
||||
def test_RRuleLocator():
|
||||
import matplotlib.testing.jpl_units as units
|
||||
units.register()
|
||||
|
||||
# This will cause the RRuleLocator to go out of bounds when it tries
|
||||
# to add padding to the limits, so we make sure it caps at the correct
|
||||
# boundary values.
|
||||
t0 = datetime.datetime(1000, 1, 1)
|
||||
tf = datetime.datetime(6000, 1, 1)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = plt.subplot(111)
|
||||
ax.set_autoscale_on(True)
|
||||
ax.plot([t0, tf], [0.0, 1.0], marker='o')
|
||||
|
||||
rrule = mdates.rrulewrapper(dateutil.rrule.YEARLY, interval=500)
|
||||
locator = mdates.RRuleLocator(rrule)
|
||||
ax.xaxis.set_major_locator(locator)
|
||||
ax.xaxis.set_major_formatter(mdates.AutoDateFormatter(locator))
|
||||
|
||||
ax.autoscale_view()
|
||||
fig.autofmt_xdate()
|
||||
|
||||
|
||||
def test_RRuleLocator_dayrange():
|
||||
loc = mdates.DayLocator()
|
||||
x1 = datetime.datetime(year=1, month=1, day=1, tzinfo=mdates.UTC)
|
||||
y1 = datetime.datetime(year=1, month=1, day=16, tzinfo=mdates.UTC)
|
||||
loc.tick_values(x1, y1)
|
||||
# On success, no overflow error shall be thrown
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['DateFormatter_fractionalSeconds'],
|
||||
extensions=['png'])
|
||||
def test_DateFormatter():
|
||||
import matplotlib.testing.jpl_units as units
|
||||
units.register()
|
||||
|
||||
# Lets make sure that DateFormatter will allow us to have tick marks
|
||||
# at intervals of fractional seconds.
|
||||
|
||||
t0 = datetime.datetime(2001, 1, 1, 0, 0, 0)
|
||||
tf = datetime.datetime(2001, 1, 1, 0, 0, 1)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = plt.subplot(111)
|
||||
ax.set_autoscale_on(True)
|
||||
ax.plot([t0, tf], [0.0, 1.0], marker='o')
|
||||
|
||||
# rrule = mpldates.rrulewrapper( dateutil.rrule.YEARLY, interval=500 )
|
||||
# locator = mpldates.RRuleLocator( rrule )
|
||||
# ax.xaxis.set_major_locator( locator )
|
||||
# ax.xaxis.set_major_formatter( mpldates.AutoDateFormatter(locator) )
|
||||
|
||||
ax.autoscale_view()
|
||||
fig.autofmt_xdate()
|
||||
|
||||
|
||||
def test_date_formatter_strftime():
|
||||
"""
|
||||
Tests that DateFormatter matches datetime.strftime,
|
||||
check microseconds for years before 1900 for bug #3179
|
||||
as well as a few related issues for years before 1900.
|
||||
"""
|
||||
def test_strftime_fields(dt):
|
||||
"""For datetime object dt, check DateFormatter fields"""
|
||||
# Note: the last couple of %%s are to check multiple %s are handled
|
||||
# properly; %% should get replaced by %.
|
||||
formatter = mdates.DateFormatter("%w %d %m %y %Y %H %I %M %S %%%f %%x")
|
||||
# Compute date fields without using datetime.strftime,
|
||||
# since datetime.strftime does not work before year 1900
|
||||
formatted_date_str = (
|
||||
"{weekday} {day:02d} {month:02d} {year:02d} {full_year:04d} "
|
||||
"{hour24:02d} {hour12:02d} {minute:02d} {second:02d} "
|
||||
"%{microsecond:06d} %x"
|
||||
.format(
|
||||
weekday=str((dt.weekday() + 1) % 7),
|
||||
day=dt.day,
|
||||
month=dt.month,
|
||||
year=dt.year % 100,
|
||||
full_year=dt.year,
|
||||
hour24=dt.hour,
|
||||
hour12=((dt.hour-1) % 12) + 1,
|
||||
minute=dt.minute,
|
||||
second=dt.second,
|
||||
microsecond=dt.microsecond))
|
||||
with pytest.warns(MatplotlibDeprecationWarning):
|
||||
assert formatter.strftime(dt) == formatted_date_str
|
||||
|
||||
try:
|
||||
# Test strftime("%x") with the current locale.
|
||||
import locale # Might not exist on some platforms, such as Windows
|
||||
locale_formatter = mdates.DateFormatter("%x")
|
||||
locale_d_fmt = locale.nl_langinfo(locale.D_FMT)
|
||||
expanded_formatter = mdates.DateFormatter(locale_d_fmt)
|
||||
with pytest.warns(MatplotlibDeprecationWarning):
|
||||
assert locale_formatter.strftime(dt) == \
|
||||
expanded_formatter.strftime(dt)
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
for year in range(1, 3000, 71):
|
||||
# Iterate through random set of years
|
||||
test_strftime_fields(datetime.datetime(year, 1, 1))
|
||||
test_strftime_fields(datetime.datetime(year, 2, 3, 4, 5, 6, 12345))
|
||||
|
||||
|
||||
def test_date_formatter_callable():
|
||||
scale = -11
|
||||
locator = Mock(_get_unit=Mock(return_value=scale))
|
||||
callable_formatting_function = (lambda dates, _:
|
||||
[dt.strftime('%d-%m//%Y') for dt in dates])
|
||||
|
||||
formatter = mdates.AutoDateFormatter(locator)
|
||||
formatter.scaled[-10] = callable_formatting_function
|
||||
assert formatter([datetime.datetime(2014, 12, 25)]) == ['25-12//2014']
|
||||
|
||||
|
||||
def test_drange():
|
||||
"""
|
||||
This test should check if drange works as expected, and if all the
|
||||
rounding errors are fixed
|
||||
"""
|
||||
start = datetime.datetime(2011, 1, 1, tzinfo=mdates.UTC)
|
||||
end = datetime.datetime(2011, 1, 2, tzinfo=mdates.UTC)
|
||||
delta = datetime.timedelta(hours=1)
|
||||
# We expect 24 values in drange(start, end, delta), because drange returns
|
||||
# dates from an half open interval [start, end)
|
||||
assert len(mdates.drange(start, end, delta)) == 24
|
||||
|
||||
# if end is a little bit later, we expect the range to contain one element
|
||||
# more
|
||||
end = end + datetime.timedelta(microseconds=1)
|
||||
assert len(mdates.drange(start, end, delta)) == 25
|
||||
|
||||
# reset end
|
||||
end = datetime.datetime(2011, 1, 2, tzinfo=mdates.UTC)
|
||||
|
||||
# and tst drange with "complicated" floats:
|
||||
# 4 hours = 1/6 day, this is an "dangerous" float
|
||||
delta = datetime.timedelta(hours=4)
|
||||
daterange = mdates.drange(start, end, delta)
|
||||
assert len(daterange) == 6
|
||||
assert mdates.num2date(daterange[-1]) == (end - delta)
|
||||
|
||||
|
||||
def test_empty_date_with_year_formatter():
|
||||
# exposes sf bug 2861426:
|
||||
# https://sourceforge.net/tracker/?func=detail&aid=2861426&group_id=80706&atid=560720
|
||||
|
||||
# update: I am no longer believe this is a bug, as I commented on
|
||||
# the tracker. The question is now: what to do with this test
|
||||
|
||||
import matplotlib.dates as dates
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
|
||||
yearFmt = dates.DateFormatter('%Y')
|
||||
ax.xaxis.set_major_formatter(yearFmt)
|
||||
|
||||
with tempfile.TemporaryFile() as fh:
|
||||
with pytest.raises(ValueError):
|
||||
fig.savefig(fh)
|
||||
|
||||
|
||||
def test_auto_date_locator():
|
||||
def _create_auto_date_locator(date1, date2):
|
||||
locator = mdates.AutoDateLocator(interval_multiples=False)
|
||||
locator.create_dummy_axis()
|
||||
locator.set_view_interval(mdates.date2num(date1),
|
||||
mdates.date2num(date2))
|
||||
return locator
|
||||
|
||||
d1 = datetime.datetime(1990, 1, 1)
|
||||
results = ([datetime.timedelta(weeks=52 * 200),
|
||||
['1990-01-01 00:00:00+00:00', '2010-01-01 00:00:00+00:00',
|
||||
'2030-01-01 00:00:00+00:00', '2050-01-01 00:00:00+00:00',
|
||||
'2070-01-01 00:00:00+00:00', '2090-01-01 00:00:00+00:00',
|
||||
'2110-01-01 00:00:00+00:00', '2130-01-01 00:00:00+00:00',
|
||||
'2150-01-01 00:00:00+00:00', '2170-01-01 00:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(weeks=52),
|
||||
['1990-01-01 00:00:00+00:00', '1990-02-01 00:00:00+00:00',
|
||||
'1990-03-01 00:00:00+00:00', '1990-04-01 00:00:00+00:00',
|
||||
'1990-05-01 00:00:00+00:00', '1990-06-01 00:00:00+00:00',
|
||||
'1990-07-01 00:00:00+00:00', '1990-08-01 00:00:00+00:00',
|
||||
'1990-09-01 00:00:00+00:00', '1990-10-01 00:00:00+00:00',
|
||||
'1990-11-01 00:00:00+00:00', '1990-12-01 00:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(days=141),
|
||||
['1990-01-05 00:00:00+00:00', '1990-01-26 00:00:00+00:00',
|
||||
'1990-02-16 00:00:00+00:00', '1990-03-09 00:00:00+00:00',
|
||||
'1990-03-30 00:00:00+00:00', '1990-04-20 00:00:00+00:00',
|
||||
'1990-05-11 00:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(days=40),
|
||||
['1990-01-03 00:00:00+00:00', '1990-01-10 00:00:00+00:00',
|
||||
'1990-01-17 00:00:00+00:00', '1990-01-24 00:00:00+00:00',
|
||||
'1990-01-31 00:00:00+00:00', '1990-02-07 00:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(hours=40),
|
||||
['1990-01-01 00:00:00+00:00', '1990-01-01 04:00:00+00:00',
|
||||
'1990-01-01 08:00:00+00:00', '1990-01-01 12:00:00+00:00',
|
||||
'1990-01-01 16:00:00+00:00', '1990-01-01 20:00:00+00:00',
|
||||
'1990-01-02 00:00:00+00:00', '1990-01-02 04:00:00+00:00',
|
||||
'1990-01-02 08:00:00+00:00', '1990-01-02 12:00:00+00:00',
|
||||
'1990-01-02 16:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(minutes=20),
|
||||
['1990-01-01 00:00:00+00:00', '1990-01-01 00:05:00+00:00',
|
||||
'1990-01-01 00:10:00+00:00', '1990-01-01 00:15:00+00:00',
|
||||
'1990-01-01 00:20:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(seconds=40),
|
||||
['1990-01-01 00:00:00+00:00', '1990-01-01 00:00:05+00:00',
|
||||
'1990-01-01 00:00:10+00:00', '1990-01-01 00:00:15+00:00',
|
||||
'1990-01-01 00:00:20+00:00', '1990-01-01 00:00:25+00:00',
|
||||
'1990-01-01 00:00:30+00:00', '1990-01-01 00:00:35+00:00',
|
||||
'1990-01-01 00:00:40+00:00']
|
||||
],
|
||||
[datetime.timedelta(microseconds=1500),
|
||||
['1989-12-31 23:59:59.999500+00:00',
|
||||
'1990-01-01 00:00:00+00:00',
|
||||
'1990-01-01 00:00:00.000500+00:00',
|
||||
'1990-01-01 00:00:00.001000+00:00',
|
||||
'1990-01-01 00:00:00.001500+00:00']
|
||||
],
|
||||
)
|
||||
|
||||
for t_delta, expected in results:
|
||||
d2 = d1 + t_delta
|
||||
locator = _create_auto_date_locator(d1, d2)
|
||||
assert list(map(str, mdates.num2date(locator()))) == expected
|
||||
|
||||
|
||||
def test_auto_date_locator_intmult():
|
||||
def _create_auto_date_locator(date1, date2):
|
||||
locator = mdates.AutoDateLocator(interval_multiples=True)
|
||||
locator.create_dummy_axis()
|
||||
locator.set_view_interval(mdates.date2num(date1),
|
||||
mdates.date2num(date2))
|
||||
return locator
|
||||
|
||||
d1 = datetime.datetime(1997, 1, 1)
|
||||
results = ([datetime.timedelta(weeks=52 * 200),
|
||||
['1980-01-01 00:00:00+00:00', '2000-01-01 00:00:00+00:00',
|
||||
'2020-01-01 00:00:00+00:00', '2040-01-01 00:00:00+00:00',
|
||||
'2060-01-01 00:00:00+00:00', '2080-01-01 00:00:00+00:00',
|
||||
'2100-01-01 00:00:00+00:00', '2120-01-01 00:00:00+00:00',
|
||||
'2140-01-01 00:00:00+00:00', '2160-01-01 00:00:00+00:00',
|
||||
'2180-01-01 00:00:00+00:00', '2200-01-01 00:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(weeks=52),
|
||||
['1997-01-01 00:00:00+00:00', '1997-02-01 00:00:00+00:00',
|
||||
'1997-03-01 00:00:00+00:00', '1997-04-01 00:00:00+00:00',
|
||||
'1997-05-01 00:00:00+00:00', '1997-06-01 00:00:00+00:00',
|
||||
'1997-07-01 00:00:00+00:00', '1997-08-01 00:00:00+00:00',
|
||||
'1997-09-01 00:00:00+00:00', '1997-10-01 00:00:00+00:00',
|
||||
'1997-11-01 00:00:00+00:00', '1997-12-01 00:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(days=141),
|
||||
['1997-01-01 00:00:00+00:00', '1997-01-22 00:00:00+00:00',
|
||||
'1997-02-01 00:00:00+00:00', '1997-02-22 00:00:00+00:00',
|
||||
'1997-03-01 00:00:00+00:00', '1997-03-22 00:00:00+00:00',
|
||||
'1997-04-01 00:00:00+00:00', '1997-04-22 00:00:00+00:00',
|
||||
'1997-05-01 00:00:00+00:00', '1997-05-22 00:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(days=40),
|
||||
['1997-01-01 00:00:00+00:00', '1997-01-05 00:00:00+00:00',
|
||||
'1997-01-09 00:00:00+00:00', '1997-01-13 00:00:00+00:00',
|
||||
'1997-01-17 00:00:00+00:00', '1997-01-21 00:00:00+00:00',
|
||||
'1997-01-25 00:00:00+00:00', '1997-01-29 00:00:00+00:00',
|
||||
'1997-02-01 00:00:00+00:00', '1997-02-05 00:00:00+00:00',
|
||||
'1997-02-09 00:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(hours=40),
|
||||
['1997-01-01 00:00:00+00:00', '1997-01-01 04:00:00+00:00',
|
||||
'1997-01-01 08:00:00+00:00', '1997-01-01 12:00:00+00:00',
|
||||
'1997-01-01 16:00:00+00:00', '1997-01-01 20:00:00+00:00',
|
||||
'1997-01-02 00:00:00+00:00', '1997-01-02 04:00:00+00:00',
|
||||
'1997-01-02 08:00:00+00:00', '1997-01-02 12:00:00+00:00',
|
||||
'1997-01-02 16:00:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(minutes=20),
|
||||
['1997-01-01 00:00:00+00:00', '1997-01-01 00:05:00+00:00',
|
||||
'1997-01-01 00:10:00+00:00', '1997-01-01 00:15:00+00:00',
|
||||
'1997-01-01 00:20:00+00:00']
|
||||
],
|
||||
[datetime.timedelta(seconds=40),
|
||||
['1997-01-01 00:00:00+00:00', '1997-01-01 00:00:05+00:00',
|
||||
'1997-01-01 00:00:10+00:00', '1997-01-01 00:00:15+00:00',
|
||||
'1997-01-01 00:00:20+00:00', '1997-01-01 00:00:25+00:00',
|
||||
'1997-01-01 00:00:30+00:00', '1997-01-01 00:00:35+00:00',
|
||||
'1997-01-01 00:00:40+00:00']
|
||||
],
|
||||
[datetime.timedelta(microseconds=1500),
|
||||
['1996-12-31 23:59:59.999500+00:00',
|
||||
'1997-01-01 00:00:00+00:00',
|
||||
'1997-01-01 00:00:00.000500+00:00',
|
||||
'1997-01-01 00:00:00.001000+00:00',
|
||||
'1997-01-01 00:00:00.001500+00:00']
|
||||
],
|
||||
)
|
||||
|
||||
for t_delta, expected in results:
|
||||
d2 = d1 + t_delta
|
||||
locator = _create_auto_date_locator(d1, d2)
|
||||
assert list(map(str, mdates.num2date(locator()))) == expected
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['date_inverted_limit'],
|
||||
extensions=['png'])
|
||||
def test_date_inverted_limit():
|
||||
# test ax hline with date inputs
|
||||
t0 = datetime.datetime(2009, 1, 20)
|
||||
tf = datetime.datetime(2009, 1, 31)
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.axhline(t0, color="blue", lw=3)
|
||||
ax.set_ylim(t0 - datetime.timedelta(days=5),
|
||||
tf + datetime.timedelta(days=5))
|
||||
ax.invert_yaxis()
|
||||
fig.subplots_adjust(left=0.25)
|
||||
|
||||
|
||||
def _test_date2num_dst(date_range, tz_convert):
|
||||
# Timezones
|
||||
BRUSSELS = dateutil.tz.gettz('Europe/Brussels')
|
||||
UTC = mdates.UTC
|
||||
|
||||
# Create a list of timezone-aware datetime objects in UTC
|
||||
# Interval is 0b0.0000011 days, to prevent float rounding issues
|
||||
dtstart = datetime.datetime(2014, 3, 30, 0, 0, tzinfo=UTC)
|
||||
interval = datetime.timedelta(minutes=33, seconds=45)
|
||||
interval_days = 0.0234375 # 2025 / 86400 seconds
|
||||
N = 8
|
||||
|
||||
dt_utc = date_range(start=dtstart, freq=interval, periods=N)
|
||||
dt_bxl = tz_convert(dt_utc, BRUSSELS)
|
||||
|
||||
expected_ordinalf = [735322.0 + (i * interval_days) for i in range(N)]
|
||||
actual_ordinalf = list(mdates.date2num(dt_bxl))
|
||||
|
||||
assert actual_ordinalf == expected_ordinalf
|
||||
|
||||
|
||||
def test_date2num_dst():
|
||||
# Test for github issue #3896, but in date2num around DST transitions
|
||||
# with a timezone-aware pandas date_range object.
|
||||
|
||||
class dt_tzaware(datetime.datetime):
|
||||
"""
|
||||
This bug specifically occurs because of the normalization behavior of
|
||||
pandas Timestamp objects, so in order to replicate it, we need a
|
||||
datetime-like object that applies timezone normalization after
|
||||
subtraction.
|
||||
"""
|
||||
def __sub__(self, other):
|
||||
r = super().__sub__(other)
|
||||
tzinfo = getattr(r, 'tzinfo', None)
|
||||
|
||||
if tzinfo is not None:
|
||||
localizer = getattr(tzinfo, 'normalize', None)
|
||||
if localizer is not None:
|
||||
r = tzinfo.normalize(r)
|
||||
|
||||
if isinstance(r, datetime.datetime):
|
||||
r = self.mk_tzaware(r)
|
||||
|
||||
return r
|
||||
|
||||
def __add__(self, other):
|
||||
return self.mk_tzaware(super().__add__(other))
|
||||
|
||||
def astimezone(self, tzinfo):
|
||||
dt = super().astimezone(tzinfo)
|
||||
return self.mk_tzaware(dt)
|
||||
|
||||
@classmethod
|
||||
def mk_tzaware(cls, datetime_obj):
|
||||
kwargs = {}
|
||||
attrs = ('year',
|
||||
'month',
|
||||
'day',
|
||||
'hour',
|
||||
'minute',
|
||||
'second',
|
||||
'microsecond',
|
||||
'tzinfo')
|
||||
|
||||
for attr in attrs:
|
||||
val = getattr(datetime_obj, attr, None)
|
||||
if val is not None:
|
||||
kwargs[attr] = val
|
||||
|
||||
return cls(**kwargs)
|
||||
|
||||
# Define a date_range function similar to pandas.date_range
|
||||
def date_range(start, freq, periods):
|
||||
dtstart = dt_tzaware.mk_tzaware(start)
|
||||
|
||||
return [dtstart + (i * freq) for i in range(periods)]
|
||||
|
||||
# Define a tz_convert function that converts a list to a new time zone.
|
||||
def tz_convert(dt_list, tzinfo):
|
||||
return [d.astimezone(tzinfo) for d in dt_list]
|
||||
|
||||
_test_date2num_dst(date_range, tz_convert)
|
||||
|
||||
|
||||
def test_date2num_dst_pandas(pd):
|
||||
# Test for github issue #3896, but in date2num around DST transitions
|
||||
# with a timezone-aware pandas date_range object.
|
||||
|
||||
def tz_convert(*args):
|
||||
return pd.DatetimeIndex.tz_convert(*args).astype(object)
|
||||
|
||||
_test_date2num_dst(pd.date_range, tz_convert)
|
||||
|
||||
|
||||
def _test_rrulewrapper(attach_tz, get_tz):
|
||||
SYD = get_tz('Australia/Sydney')
|
||||
|
||||
dtstart = attach_tz(datetime.datetime(2017, 4, 1, 0), SYD)
|
||||
dtend = attach_tz(datetime.datetime(2017, 4, 4, 0), SYD)
|
||||
|
||||
rule = mdates.rrulewrapper(freq=dateutil.rrule.DAILY, dtstart=dtstart)
|
||||
|
||||
act = rule.between(dtstart, dtend)
|
||||
exp = [datetime.datetime(2017, 4, 1, 13, tzinfo=dateutil.tz.tzutc()),
|
||||
datetime.datetime(2017, 4, 2, 14, tzinfo=dateutil.tz.tzutc())]
|
||||
|
||||
assert act == exp
|
||||
|
||||
|
||||
def test_rrulewrapper():
|
||||
def attach_tz(dt, zi):
|
||||
return dt.replace(tzinfo=zi)
|
||||
|
||||
_test_rrulewrapper(attach_tz, dateutil.tz.gettz)
|
||||
|
||||
|
||||
@pytest.mark.pytz
|
||||
@pytest.mark.skipif(not __has_pytz(), reason="Requires pytz")
|
||||
def test_rrulewrapper_pytz():
|
||||
# Test to make sure pytz zones are supported in rrules
|
||||
import pytz
|
||||
|
||||
def attach_tz(dt, zi):
|
||||
return zi.localize(dt)
|
||||
|
||||
_test_rrulewrapper(attach_tz, pytz.timezone)
|
||||
|
||||
|
||||
def test_DayLocator():
|
||||
with pytest.raises(ValueError):
|
||||
mdates.DayLocator(interval=-1)
|
||||
with pytest.raises(ValueError):
|
||||
mdates.DayLocator(interval=-1.5)
|
||||
with pytest.raises(ValueError):
|
||||
mdates.DayLocator(interval=0)
|
||||
with pytest.raises(ValueError):
|
||||
mdates.DayLocator(interval=1.3)
|
||||
mdates.DayLocator(interval=1.0)
|
||||
|
||||
|
||||
def test_tz_utc():
|
||||
dt = datetime.datetime(1970, 1, 1, tzinfo=mdates.UTC)
|
||||
dt.tzname()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("x, tdelta",
|
||||
[(1, datetime.timedelta(days=1)),
|
||||
([1, 1.5], [datetime.timedelta(days=1),
|
||||
datetime.timedelta(days=1.5)])])
|
||||
def test_num2timedelta(x, tdelta):
|
||||
dt = mdates.num2timedelta(x)
|
||||
assert dt == tdelta
|
||||
|
||||
|
||||
def test_datetime64_in_list():
|
||||
dt = [np.datetime64('2000-01-01'), np.datetime64('2001-01-01')]
|
||||
dn = mdates.date2num(dt)
|
||||
assert np.array_equal(dn, [730120., 730486.])
|
||||
@@ -0,0 +1,66 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
import matplotlib.dviread as dr
|
||||
import pytest
|
||||
|
||||
|
||||
def test_PsfontsMap(monkeypatch):
|
||||
monkeypatch.setattr(dr, 'find_tex_file', lambda x: x)
|
||||
|
||||
filename = str(Path(__file__).parent / 'baseline_images/dviread/test.map')
|
||||
fontmap = dr.PsfontsMap(filename)
|
||||
# Check all properties of a few fonts
|
||||
for n in [1, 2, 3, 4, 5]:
|
||||
key = b'TeXfont%d' % n
|
||||
entry = fontmap[key]
|
||||
assert entry.texname == key
|
||||
assert entry.psname == b'PSfont%d' % n
|
||||
if n not in [3, 5]:
|
||||
assert entry.encoding == b'font%d.enc' % n
|
||||
elif n == 3:
|
||||
assert entry.encoding == b'enc3.foo'
|
||||
# We don't care about the encoding of TeXfont5, which specifies
|
||||
# multiple encodings.
|
||||
if n not in [1, 5]:
|
||||
assert entry.filename == b'font%d.pfa' % n
|
||||
else:
|
||||
assert entry.filename == b'font%d.pfb' % n
|
||||
if n == 4:
|
||||
assert entry.effects == {'slant': -0.1, 'extend': 2.2}
|
||||
else:
|
||||
assert entry.effects == {}
|
||||
# Some special cases
|
||||
entry = fontmap[b'TeXfont6']
|
||||
assert entry.filename is None
|
||||
assert entry.encoding is None
|
||||
entry = fontmap[b'TeXfont7']
|
||||
assert entry.filename is None
|
||||
assert entry.encoding == b'font7.enc'
|
||||
entry = fontmap[b'TeXfont8']
|
||||
assert entry.filename == b'font8.pfb'
|
||||
assert entry.encoding is None
|
||||
entry = fontmap[b'TeXfont9']
|
||||
assert entry.filename == b'/absolute/font9.pfb'
|
||||
# Missing font
|
||||
with pytest.raises(KeyError) as exc:
|
||||
fontmap[b'no-such-font']
|
||||
assert 'no-such-font' in str(exc.value)
|
||||
|
||||
|
||||
@pytest.mark.skipif(shutil.which("kpsewhich") is None,
|
||||
reason="kpsewhich is not available")
|
||||
def test_dviread():
|
||||
dirpath = Path(__file__).parent / 'baseline_images/dviread'
|
||||
with (dirpath / 'test.json').open() as f:
|
||||
correct = json.load(f)
|
||||
with dr.Dvi(str(dirpath / 'test.dvi'), None) as dvi:
|
||||
data = [{'text': [[t.x, t.y,
|
||||
chr(t.glyph),
|
||||
t.font.texname.decode('ascii'),
|
||||
round(t.font.size, 2)]
|
||||
for t in page.text],
|
||||
'boxes': [[b.x, b.y, b.height, b.width] for b in page.boxes]}
|
||||
for page in dvi]
|
||||
assert data == correct
|
||||
@@ -0,0 +1,449 @@
|
||||
import sys
|
||||
import warnings
|
||||
import platform
|
||||
|
||||
from matplotlib import rcParams
|
||||
from matplotlib.testing.decorators import image_comparison, check_figures_equal
|
||||
from matplotlib.axes import Axes
|
||||
from matplotlib.ticker import AutoMinorLocator, FixedFormatter
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.dates as mdates
|
||||
import matplotlib.gridspec as gridspec
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['figure_align_labels'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0))
|
||||
def test_align_labels():
|
||||
# Check the figure.align_labels() command
|
||||
fig = plt.figure(tight_layout=True)
|
||||
gs = gridspec.GridSpec(3, 3)
|
||||
|
||||
ax = fig.add_subplot(gs[0, :2])
|
||||
ax.plot(np.arange(0, 1e6, 1000))
|
||||
ax.set_ylabel('Ylabel0 0')
|
||||
ax = fig.add_subplot(gs[0, -1])
|
||||
ax.plot(np.arange(0, 1e4, 100))
|
||||
|
||||
for i in range(3):
|
||||
ax = fig.add_subplot(gs[1, i])
|
||||
ax.set_ylabel('YLabel1 %d' % i)
|
||||
ax.set_xlabel('XLabel1 %d' % i)
|
||||
if i in [0, 2]:
|
||||
ax.xaxis.set_label_position("top")
|
||||
ax.xaxis.tick_top()
|
||||
if i == 0:
|
||||
for tick in ax.get_xticklabels():
|
||||
tick.set_rotation(90)
|
||||
if i == 2:
|
||||
ax.yaxis.set_label_position("right")
|
||||
ax.yaxis.tick_right()
|
||||
|
||||
for i in range(3):
|
||||
ax = fig.add_subplot(gs[2, i])
|
||||
ax.set_xlabel('XLabel2 %d' % (i))
|
||||
ax.set_ylabel('YLabel2 %d' % (i))
|
||||
|
||||
if i == 2:
|
||||
ax.plot(np.arange(0, 1e4, 10))
|
||||
ax.yaxis.set_label_position("right")
|
||||
ax.yaxis.tick_right()
|
||||
for tick in ax.get_xticklabels():
|
||||
tick.set_rotation(90)
|
||||
|
||||
fig.align_labels()
|
||||
|
||||
|
||||
def test_figure_label():
|
||||
# pyplot figure creation, selection and closing with figure label and
|
||||
# number
|
||||
plt.close('all')
|
||||
plt.figure('today')
|
||||
plt.figure(3)
|
||||
plt.figure('tomorrow')
|
||||
plt.figure()
|
||||
plt.figure(0)
|
||||
plt.figure(1)
|
||||
plt.figure(3)
|
||||
assert plt.get_fignums() == [0, 1, 3, 4, 5]
|
||||
assert plt.get_figlabels() == ['', 'today', '', 'tomorrow', '']
|
||||
plt.close(10)
|
||||
plt.close()
|
||||
plt.close(5)
|
||||
plt.close('tomorrow')
|
||||
assert plt.get_fignums() == [0, 1]
|
||||
assert plt.get_figlabels() == ['', 'today']
|
||||
|
||||
|
||||
def test_fignum_exists():
|
||||
# pyplot figure creation, selection and closing with fignum_exists
|
||||
plt.figure('one')
|
||||
plt.figure(2)
|
||||
plt.figure('three')
|
||||
plt.figure()
|
||||
assert plt.fignum_exists('one')
|
||||
assert plt.fignum_exists(2)
|
||||
assert plt.fignum_exists('three')
|
||||
assert plt.fignum_exists(4)
|
||||
plt.close('one')
|
||||
plt.close(4)
|
||||
assert not plt.fignum_exists('one')
|
||||
assert not plt.fignum_exists(4)
|
||||
|
||||
|
||||
def test_clf_keyword():
|
||||
# test if existing figure is cleared with figure() and subplots()
|
||||
text1 = 'A fancy plot'
|
||||
text2 = 'Really fancy!'
|
||||
|
||||
fig0 = plt.figure(num=1)
|
||||
fig0.suptitle(text1)
|
||||
assert [t.get_text() for t in fig0.texts] == [text1]
|
||||
|
||||
fig1 = plt.figure(num=1, clear=False)
|
||||
fig1.text(0.5, 0.5, text2)
|
||||
assert fig0 is fig1
|
||||
assert [t.get_text() for t in fig1.texts] == [text1, text2]
|
||||
|
||||
fig2, ax2 = plt.subplots(2, 1, num=1, clear=True)
|
||||
assert fig0 is fig2
|
||||
assert [t.get_text() for t in fig2.texts] == []
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['figure_today'])
|
||||
def test_figure():
|
||||
# named figure support
|
||||
fig = plt.figure('today')
|
||||
ax = fig.add_subplot(111)
|
||||
ax.set_title(fig.get_label())
|
||||
ax.plot(np.arange(5))
|
||||
# plot red line in a different figure.
|
||||
plt.figure('tomorrow')
|
||||
plt.plot([0, 1], [1, 0], 'r')
|
||||
# Return to the original; make sure the red line is not there.
|
||||
plt.figure('today')
|
||||
plt.close('tomorrow')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['figure_legend'])
|
||||
def test_figure_legend():
|
||||
fig, axes = plt.subplots(2)
|
||||
axes[0].plot([0, 1], [1, 0], label='x', color='g')
|
||||
axes[0].plot([0, 1], [0, 1], label='y', color='r')
|
||||
axes[0].plot([0, 1], [0.5, 0.5], label='y', color='k')
|
||||
|
||||
axes[1].plot([0, 1], [1, 0], label='_y', color='r')
|
||||
axes[1].plot([0, 1], [0, 1], label='z', color='b')
|
||||
fig.legend()
|
||||
|
||||
|
||||
def test_gca():
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = fig.add_axes([0, 0, 1, 1])
|
||||
assert fig.gca(projection='rectilinear') is ax1
|
||||
assert fig.gca() is ax1
|
||||
|
||||
ax2 = fig.add_subplot(121, projection='polar')
|
||||
assert fig.gca() is ax2
|
||||
assert fig.gca(polar=True)is ax2
|
||||
|
||||
ax3 = fig.add_subplot(122)
|
||||
assert fig.gca() is ax3
|
||||
|
||||
# the final request for a polar axes will end up creating one
|
||||
# with a spec of 111.
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter('always')
|
||||
# Changing the projection will throw a warning
|
||||
assert fig.gca(polar=True) is not ax3
|
||||
assert len(w) == 1
|
||||
assert fig.gca(polar=True) is not ax2
|
||||
assert fig.gca().get_geometry() == (1, 1, 1)
|
||||
|
||||
fig.sca(ax1)
|
||||
assert fig.gca(projection='rectilinear') is ax1
|
||||
assert fig.gca() is ax1
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['figure_suptitle'])
|
||||
def test_suptitle():
|
||||
fig, _ = plt.subplots()
|
||||
fig.suptitle('hello', color='r')
|
||||
fig.suptitle('title', color='g', rotation='30')
|
||||
|
||||
|
||||
def test_suptitle_fontproperties():
|
||||
from matplotlib.font_manager import FontProperties
|
||||
fig, ax = plt.subplots()
|
||||
fps = FontProperties(size='large', weight='bold')
|
||||
txt = fig.suptitle('fontprops title', fontproperties=fps)
|
||||
assert txt.get_fontsize() == fps.get_size_in_points()
|
||||
assert txt.get_weight() == fps.get_weight()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['alpha_background'],
|
||||
# only test png and svg. The PDF output appears correct,
|
||||
# but Ghostscript does not preserve the background color.
|
||||
extensions=['png', 'svg'],
|
||||
savefig_kwarg={'facecolor': (0, 1, 0.4),
|
||||
'edgecolor': 'none'})
|
||||
def test_alpha():
|
||||
# We want an image which has a background color and an
|
||||
# alpha of 0.4.
|
||||
fig = plt.figure(figsize=[2, 1])
|
||||
fig.set_facecolor((0, 1, 0.4))
|
||||
fig.patch.set_alpha(0.4)
|
||||
|
||||
import matplotlib.patches as mpatches
|
||||
fig.patches.append(mpatches.CirclePolygon([20, 20],
|
||||
radius=15,
|
||||
alpha=0.6,
|
||||
facecolor='red'))
|
||||
|
||||
|
||||
def test_too_many_figures():
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
for i in range(rcParams['figure.max_open_warning'] + 1):
|
||||
plt.figure()
|
||||
assert len(w) == 1
|
||||
|
||||
|
||||
def test_iterability_axes_argument():
|
||||
|
||||
# This is a regression test for matplotlib/matplotlib#3196. If one of the
|
||||
# arguments returned by _as_mpl_axes defines __getitem__ but is not
|
||||
# iterable, this would raise an exception. This is because we check
|
||||
# whether the arguments are iterable, and if so we try and convert them
|
||||
# to a tuple. However, the ``iterable`` function returns True if
|
||||
# __getitem__ is present, but some classes can define __getitem__ without
|
||||
# being iterable. The tuple conversion is now done in a try...except in
|
||||
# case it fails.
|
||||
|
||||
class MyAxes(Axes):
|
||||
def __init__(self, *args, myclass=None, **kwargs):
|
||||
return Axes.__init__(self, *args, **kwargs)
|
||||
|
||||
class MyClass(object):
|
||||
|
||||
def __getitem__(self, item):
|
||||
if item != 'a':
|
||||
raise ValueError("item should be a")
|
||||
|
||||
def _as_mpl_axes(self):
|
||||
return MyAxes, {'myclass': self}
|
||||
|
||||
fig = plt.figure()
|
||||
fig.add_subplot(1, 1, 1, projection=MyClass())
|
||||
plt.close(fig)
|
||||
|
||||
|
||||
def test_set_fig_size():
|
||||
fig = plt.figure()
|
||||
|
||||
# check figwidth
|
||||
fig.set_figwidth(5)
|
||||
assert fig.get_figwidth() == 5
|
||||
|
||||
# check figheight
|
||||
fig.set_figheight(1)
|
||||
assert fig.get_figheight() == 1
|
||||
|
||||
# check using set_size_inches
|
||||
fig.set_size_inches(2, 4)
|
||||
assert fig.get_figwidth() == 2
|
||||
assert fig.get_figheight() == 4
|
||||
|
||||
# check using tuple to first argument
|
||||
fig.set_size_inches((1, 3))
|
||||
assert fig.get_figwidth() == 1
|
||||
assert fig.get_figheight() == 3
|
||||
|
||||
|
||||
def test_axes_remove():
|
||||
fig, axes = plt.subplots(2, 2)
|
||||
axes[-1, -1].remove()
|
||||
for ax in axes.ravel()[:-1]:
|
||||
assert ax in fig.axes
|
||||
assert axes[-1, -1] not in fig.axes
|
||||
assert len(fig.axes) == 3
|
||||
|
||||
|
||||
def test_figaspect():
|
||||
w, h = plt.figaspect(np.float64(2) / np.float64(1))
|
||||
assert h / w == 2
|
||||
w, h = plt.figaspect(2)
|
||||
assert h / w == 2
|
||||
w, h = plt.figaspect(np.zeros((1, 2)))
|
||||
assert h / w == 0.5
|
||||
w, h = plt.figaspect(np.zeros((2, 2)))
|
||||
assert h / w == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize('which', [None, 'both', 'major', 'minor'])
|
||||
def test_autofmt_xdate(which):
|
||||
date = ['3 Jan 2013', '4 Jan 2013', '5 Jan 2013', '6 Jan 2013',
|
||||
'7 Jan 2013', '8 Jan 2013', '9 Jan 2013', '10 Jan 2013',
|
||||
'11 Jan 2013', '12 Jan 2013', '13 Jan 2013', '14 Jan 2013']
|
||||
|
||||
time = ['16:44:00', '16:45:00', '16:46:00', '16:47:00', '16:48:00',
|
||||
'16:49:00', '16:51:00', '16:52:00', '16:53:00', '16:55:00',
|
||||
'16:56:00', '16:57:00']
|
||||
|
||||
angle = 60
|
||||
minors = [1, 2, 3, 4, 5, 6, 7]
|
||||
|
||||
x = mdates.datestr2num(date)
|
||||
y = mdates.datestr2num(time)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
ax.plot(x, y)
|
||||
ax.yaxis_date()
|
||||
ax.xaxis_date()
|
||||
|
||||
ax.xaxis.set_minor_locator(AutoMinorLocator(2))
|
||||
ax.xaxis.set_minor_formatter(FixedFormatter(minors))
|
||||
|
||||
fig.autofmt_xdate(0.2, angle, 'right', which)
|
||||
|
||||
if which in ('both', 'major', None):
|
||||
for label in fig.axes[0].get_xticklabels(False, 'major'):
|
||||
assert int(label.get_rotation()) == angle
|
||||
|
||||
if which in ('both', 'minor'):
|
||||
for label in fig.axes[0].get_xticklabels(True, 'minor'):
|
||||
assert int(label.get_rotation()) == angle
|
||||
|
||||
|
||||
@pytest.mark.style('default')
|
||||
def test_change_dpi():
|
||||
fig = plt.figure(figsize=(4, 4))
|
||||
fig.canvas.draw()
|
||||
assert fig.canvas.renderer.height == 400
|
||||
assert fig.canvas.renderer.width == 400
|
||||
fig.dpi = 50
|
||||
fig.canvas.draw()
|
||||
assert fig.canvas.renderer.height == 200
|
||||
assert fig.canvas.renderer.width == 200
|
||||
|
||||
|
||||
def test_invalid_figure_size():
|
||||
with pytest.raises(ValueError):
|
||||
plt.figure(figsize=(1, np.nan))
|
||||
|
||||
fig = plt.figure()
|
||||
with pytest.raises(ValueError):
|
||||
fig.set_size_inches(1, np.nan)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
fig.add_axes((.1, .1, .5, np.nan))
|
||||
|
||||
|
||||
def test_subplots_shareax_loglabels():
|
||||
fig, ax_arr = plt.subplots(2, 2, sharex=True, sharey=True, squeeze=False)
|
||||
for ax in ax_arr.flatten():
|
||||
ax.plot([10, 20, 30], [10, 20, 30])
|
||||
|
||||
ax.set_yscale("log")
|
||||
ax.set_xscale("log")
|
||||
|
||||
for ax in ax_arr[0, :]:
|
||||
assert 0 == len(ax.xaxis.get_ticklabels(which='both'))
|
||||
|
||||
for ax in ax_arr[1, :]:
|
||||
assert 0 < len(ax.xaxis.get_ticklabels(which='both'))
|
||||
|
||||
for ax in ax_arr[:, 1]:
|
||||
assert 0 == len(ax.yaxis.get_ticklabels(which='both'))
|
||||
|
||||
for ax in ax_arr[:, 0]:
|
||||
assert 0 < len(ax.yaxis.get_ticklabels(which='both'))
|
||||
|
||||
|
||||
def test_savefig():
|
||||
fig = plt.figure()
|
||||
msg = "savefig() takes 2 positional arguments but 3 were given"
|
||||
with pytest.raises(TypeError, message=msg):
|
||||
fig.savefig("fname1.png", "fname2.png")
|
||||
|
||||
|
||||
def test_figure_repr():
|
||||
fig = plt.figure(figsize=(10, 20), dpi=10)
|
||||
assert repr(fig) == "<Figure size 100x200 with 0 Axes>"
|
||||
|
||||
|
||||
def test_warn_cl_plus_tl():
|
||||
fig, ax = plt.subplots(constrained_layout=True)
|
||||
with pytest.warns(UserWarning):
|
||||
# this should warn,
|
||||
fig.subplots_adjust(top=0.8)
|
||||
assert not(fig.get_constrained_layout())
|
||||
|
||||
|
||||
@check_figures_equal(extensions=["png", "pdf"])
|
||||
def test_add_artist(fig_test, fig_ref):
|
||||
fig_test.set_dpi(100)
|
||||
fig_ref.set_dpi(100)
|
||||
|
||||
ax = fig_test.subplots()
|
||||
l1 = plt.Line2D([.2, .7], [.7, .7], gid='l1')
|
||||
l2 = plt.Line2D([.2, .7], [.8, .8], gid='l2')
|
||||
r1 = plt.Circle((20, 20), 100, transform=None, gid='C1')
|
||||
r2 = plt.Circle((.7, .5), .05, gid='C2')
|
||||
r3 = plt.Circle((4.5, .8), .55, transform=fig_test.dpi_scale_trans,
|
||||
facecolor='crimson', gid='C3')
|
||||
for a in [l1, l2, r1, r2, r3]:
|
||||
fig_test.add_artist(a)
|
||||
l2.remove()
|
||||
|
||||
ax2 = fig_ref.subplots()
|
||||
l1 = plt.Line2D([.2, .7], [.7, .7], transform=fig_ref.transFigure,
|
||||
gid='l1', zorder=21)
|
||||
r1 = plt.Circle((20, 20), 100, transform=None, clip_on=False, zorder=20,
|
||||
gid='C1')
|
||||
r2 = plt.Circle((.7, .5), .05, transform=fig_ref.transFigure, gid='C2',
|
||||
zorder=20)
|
||||
r3 = plt.Circle((4.5, .8), .55, transform=fig_ref.dpi_scale_trans,
|
||||
facecolor='crimson', clip_on=False, zorder=20, gid='C3')
|
||||
for a in [l1, r1, r2, r3]:
|
||||
ax2.add_artist(a)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires Python 3.6+")
|
||||
@pytest.mark.parametrize("fmt", ["png", "pdf", "ps", "eps", "svg"])
|
||||
def test_fspath(fmt, tmpdir):
|
||||
from pathlib import Path
|
||||
out = Path(tmpdir, "test.{}".format(fmt))
|
||||
plt.savefig(out)
|
||||
with out.open("rb") as file:
|
||||
# All the supported formats include the format name (case-insensitive)
|
||||
# in the first 100 bytes.
|
||||
assert fmt.encode("ascii") in file.read(100).lower()
|
||||
|
||||
|
||||
def test_tightbbox():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_xlim(0, 1)
|
||||
t = ax.text(1., 0.5, 'This dangles over end')
|
||||
renderer = fig.canvas.get_renderer()
|
||||
x1Nom0 = 9.035 # inches
|
||||
assert np.abs(t.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2
|
||||
assert np.abs(ax.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2
|
||||
assert np.abs(fig.get_tightbbox(renderer).x1 - x1Nom0) < 0.05
|
||||
assert np.abs(fig.get_tightbbox(renderer).x0 - 0.679) < 0.05
|
||||
# now exclude t from the tight bbox so now the bbox is quite a bit
|
||||
# smaller
|
||||
t.set_in_layout(False)
|
||||
x1Nom = 7.333
|
||||
assert np.abs(ax.get_tightbbox(renderer).x1 - x1Nom * fig.dpi) < 2
|
||||
assert np.abs(fig.get_tightbbox(renderer).x1 - x1Nom) < 0.05
|
||||
|
||||
t.set_in_layout(True)
|
||||
x1Nom = 7.333
|
||||
assert np.abs(ax.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2
|
||||
# test bbox_extra_artists method...
|
||||
assert np.abs(ax.get_tightbbox(renderer,
|
||||
bbox_extra_artists=[]).x1 - x1Nom * fig.dpi) < 2
|
||||
@@ -0,0 +1,98 @@
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib.font_manager import (
|
||||
findfont, FontProperties, fontManager, json_dump, json_load, get_font,
|
||||
get_fontconfig_fonts, is_opentype_cff_font)
|
||||
from matplotlib import rc_context
|
||||
|
||||
has_fclist = shutil.which('fc-list') is not None
|
||||
|
||||
|
||||
def test_font_priority():
|
||||
with rc_context(rc={
|
||||
'font.sans-serif':
|
||||
['cmmi10', 'Bitstream Vera Sans']}):
|
||||
font = findfont(
|
||||
FontProperties(family=["sans-serif"]))
|
||||
assert os.path.basename(font) == 'cmmi10.ttf'
|
||||
|
||||
# Smoketest get_charmap, which isn't used internally anymore
|
||||
font = get_font(font)
|
||||
cmap = font.get_charmap()
|
||||
assert len(cmap) == 131
|
||||
assert cmap[8729] == 30
|
||||
|
||||
|
||||
def test_score_weight():
|
||||
assert 0 == fontManager.score_weight("regular", "regular")
|
||||
assert 0 == fontManager.score_weight("bold", "bold")
|
||||
assert (0 < fontManager.score_weight(400, 400) <
|
||||
fontManager.score_weight("normal", "bold"))
|
||||
assert (0 < fontManager.score_weight("normal", "regular") <
|
||||
fontManager.score_weight("normal", "bold"))
|
||||
assert (fontManager.score_weight("normal", "regular") ==
|
||||
fontManager.score_weight(400, 400))
|
||||
|
||||
|
||||
def test_json_serialization():
|
||||
# on windows, we can't open a file twice, so save the name and unlink
|
||||
# manually...
|
||||
try:
|
||||
name = None
|
||||
with tempfile.NamedTemporaryFile(delete=False) as temp:
|
||||
name = temp.name
|
||||
json_dump(fontManager, name)
|
||||
copy = json_load(name)
|
||||
finally:
|
||||
if name and os.path.exists(name):
|
||||
os.remove(name)
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', 'findfont: Font family.*not found')
|
||||
for prop in ({'family': 'STIXGeneral'},
|
||||
{'family': 'Bitstream Vera Sans', 'weight': 700},
|
||||
{'family': 'no such font family'}):
|
||||
fp = FontProperties(**prop)
|
||||
assert (fontManager.findfont(fp, rebuild_if_missing=False) ==
|
||||
copy.findfont(fp, rebuild_if_missing=False))
|
||||
|
||||
|
||||
def test_otf():
|
||||
fname = '/usr/share/fonts/opentype/freefont/FreeMono.otf'
|
||||
if os.path.exists(fname):
|
||||
assert is_opentype_cff_font(fname)
|
||||
|
||||
for f in fontManager.ttflist:
|
||||
if 'otf' in f.fname:
|
||||
with open(f.fname, 'rb') as fd:
|
||||
res = fd.read(4) == b'OTTO'
|
||||
assert res == is_opentype_cff_font(f.fname)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not has_fclist, reason='no fontconfig installed')
|
||||
def test_get_fontconfig_fonts():
|
||||
assert len(get_fontconfig_fonts()) > 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize('factor', [2, 4, 6, 8])
|
||||
def test_hinting_factor(factor):
|
||||
font = findfont(FontProperties(family=["sans-serif"]))
|
||||
|
||||
font1 = get_font(font, hinting_factor=1)
|
||||
font1.clear()
|
||||
font1.set_size(12, 100)
|
||||
font1.set_text('abc')
|
||||
expected = font1.get_width_height()
|
||||
|
||||
hinted_font = get_font(font, hinting_factor=factor)
|
||||
hinted_font.clear()
|
||||
hinted_font.set_size(12, 100)
|
||||
hinted_font.set_text('abc')
|
||||
# Check that hinting only changes text layout by a small (10%) amount.
|
||||
np.testing.assert_allclose(hinted_font.get_width_height(), expected,
|
||||
rtol=0.1)
|
||||
@@ -0,0 +1,26 @@
|
||||
import matplotlib.gridspec as gridspec
|
||||
import pytest
|
||||
|
||||
|
||||
def test_equal():
|
||||
gs = gridspec.GridSpec(2, 1)
|
||||
assert gs[0, 0] == gs[0, 0]
|
||||
assert gs[:, 0] == gs[:, 0]
|
||||
|
||||
|
||||
def test_width_ratios():
|
||||
"""
|
||||
Addresses issue #5835.
|
||||
See at https://github.com/matplotlib/matplotlib/issues/5835.
|
||||
"""
|
||||
with pytest.raises(ValueError):
|
||||
gridspec.GridSpec(1, 1, width_ratios=[2, 1, 3])
|
||||
|
||||
|
||||
def test_height_ratios():
|
||||
"""
|
||||
Addresses issue #5835.
|
||||
See at https://github.com/matplotlib/matplotlib/issues/5835.
|
||||
"""
|
||||
with pytest.raises(ValueError):
|
||||
gridspec.GridSpec(1, 1, height_ratios=[2, 1, 3])
|
||||
@@ -0,0 +1,924 @@
|
||||
from contextlib import ExitStack
|
||||
from copy import copy
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import urllib.request
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
from numpy import ma
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
from matplotlib import (
|
||||
colors, image as mimage, patches, pyplot as plt,
|
||||
rc_context, rcParams)
|
||||
from matplotlib.image import (AxesImage, BboxImage, FigureImage,
|
||||
NonUniformImage, PcolorImage)
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
from matplotlib.transforms import Bbox, Affine2D, TransformedBbox
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['image_interps'], style='mpl20')
|
||||
def test_image_interps():
|
||||
'make the basic nearest, bilinear and bicubic interps'
|
||||
X = np.arange(100)
|
||||
X = X.reshape(5, 20)
|
||||
|
||||
fig = plt.figure()
|
||||
ax1 = fig.add_subplot(311)
|
||||
ax1.imshow(X, interpolation='nearest')
|
||||
ax1.set_title('three interpolations')
|
||||
ax1.set_ylabel('nearest')
|
||||
|
||||
ax2 = fig.add_subplot(312)
|
||||
ax2.imshow(X, interpolation='bilinear')
|
||||
ax2.set_ylabel('bilinear')
|
||||
|
||||
ax3 = fig.add_subplot(313)
|
||||
ax3.imshow(X, interpolation='bicubic')
|
||||
ax3.set_ylabel('bicubic')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['interp_nearest_vs_none'],
|
||||
extensions=['pdf', 'svg'], remove_text=True)
|
||||
def test_interp_nearest_vs_none():
|
||||
'Test the effect of "nearest" and "none" interpolation'
|
||||
# Setting dpi to something really small makes the difference very
|
||||
# visible. This works fine with pdf, since the dpi setting doesn't
|
||||
# affect anything but images, but the agg output becomes unusably
|
||||
# small.
|
||||
rcParams['savefig.dpi'] = 3
|
||||
X = np.array([[[218, 165, 32], [122, 103, 238]],
|
||||
[[127, 255, 0], [255, 99, 71]]], dtype=np.uint8)
|
||||
fig = plt.figure()
|
||||
ax1 = fig.add_subplot(121)
|
||||
ax1.imshow(X, interpolation='none')
|
||||
ax1.set_title('interpolation none')
|
||||
ax2 = fig.add_subplot(122)
|
||||
ax2.imshow(X, interpolation='nearest')
|
||||
ax2.set_title('interpolation nearest')
|
||||
|
||||
|
||||
def do_figimage(suppressComposite):
|
||||
""" Helper for the next two tests """
|
||||
fig = plt.figure(figsize=(2,2), dpi=100)
|
||||
fig.suppressComposite = suppressComposite
|
||||
x,y = np.ix_(np.arange(100.0)/100.0, np.arange(100.0)/100.0)
|
||||
z = np.sin(x**2 + y**2 - x*y)
|
||||
c = np.sin(20*x**2 + 50*y**2)
|
||||
img = z + c/5
|
||||
|
||||
fig.figimage(img, xo=0, yo=0, origin='lower')
|
||||
fig.figimage(img[::-1,:], xo=0, yo=100, origin='lower')
|
||||
fig.figimage(img[:,::-1], xo=100, yo=0, origin='lower')
|
||||
fig.figimage(img[::-1,::-1], xo=100, yo=100, origin='lower')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['figimage-0'],
|
||||
extensions=['png','pdf'])
|
||||
def test_figimage0():
|
||||
'test the figimage method'
|
||||
|
||||
suppressComposite = False
|
||||
do_figimage(suppressComposite)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['figimage-1'],
|
||||
extensions=['png','pdf'])
|
||||
def test_figimage1():
|
||||
'test the figimage method'
|
||||
suppressComposite = True
|
||||
do_figimage(suppressComposite)
|
||||
|
||||
|
||||
def test_image_python_io():
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot([1,2,3])
|
||||
buffer = io.BytesIO()
|
||||
fig.savefig(buffer)
|
||||
buffer.seek(0)
|
||||
plt.imread(buffer)
|
||||
|
||||
|
||||
def test_imread_pil_uint16():
|
||||
pytest.importorskip("PIL")
|
||||
img = plt.imread(os.path.join(os.path.dirname(__file__),
|
||||
'baseline_images', 'test_image', 'uint16.tif'))
|
||||
assert img.dtype == np.uint16
|
||||
assert np.sum(img) == 134184960
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires Python 3.6+")
|
||||
def test_imread_fspath():
|
||||
pytest.importorskip("PIL")
|
||||
from pathlib import Path
|
||||
img = plt.imread(
|
||||
Path(__file__).parent / 'baseline_images/test_image/uint16.tif')
|
||||
assert img.dtype == np.uint16
|
||||
assert np.sum(img) == 134184960
|
||||
|
||||
|
||||
def test_imsave():
|
||||
# The goal here is that the user can specify an output logical DPI
|
||||
# for the image, but this will not actually add any extra pixels
|
||||
# to the image, it will merely be used for metadata purposes.
|
||||
|
||||
# So we do the traditional case (dpi == 1), and the new case (dpi
|
||||
# == 100) and read the resulting PNG files back in and make sure
|
||||
# the data is 100% identical.
|
||||
np.random.seed(1)
|
||||
data = np.random.rand(256, 128)
|
||||
|
||||
buff_dpi1 = io.BytesIO()
|
||||
plt.imsave(buff_dpi1, data, dpi=1)
|
||||
|
||||
buff_dpi100 = io.BytesIO()
|
||||
plt.imsave(buff_dpi100, data, dpi=100)
|
||||
|
||||
buff_dpi1.seek(0)
|
||||
arr_dpi1 = plt.imread(buff_dpi1)
|
||||
|
||||
buff_dpi100.seek(0)
|
||||
arr_dpi100 = plt.imread(buff_dpi100)
|
||||
|
||||
assert arr_dpi1.shape == (256, 128, 4)
|
||||
assert arr_dpi100.shape == (256, 128, 4)
|
||||
|
||||
assert_array_equal(arr_dpi1, arr_dpi100)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires Python 3.6+")
|
||||
@pytest.mark.parametrize("fmt", ["png", "pdf", "ps", "eps", "svg"])
|
||||
def test_imsave_fspath(fmt):
|
||||
Path = pytest.importorskip("pathlib").Path
|
||||
plt.imsave(Path(os.devnull), np.array([[0, 1]]), format=fmt)
|
||||
|
||||
|
||||
def test_imsave_color_alpha():
|
||||
# Test that imsave accept arrays with ndim=3 where the third dimension is
|
||||
# color and alpha without raising any exceptions, and that the data is
|
||||
# acceptably preserved through a save/read roundtrip.
|
||||
np.random.seed(1)
|
||||
|
||||
for origin in ['lower', 'upper']:
|
||||
data = np.random.rand(16, 16, 4)
|
||||
buff = io.BytesIO()
|
||||
plt.imsave(buff, data, origin=origin, format="png")
|
||||
|
||||
buff.seek(0)
|
||||
arr_buf = plt.imread(buff)
|
||||
|
||||
# Recreate the float -> uint8 conversion of the data
|
||||
# We can only expect to be the same with 8 bits of precision,
|
||||
# since that's what the PNG file used.
|
||||
data = (255*data).astype('uint8')
|
||||
if origin == 'lower':
|
||||
data = data[::-1]
|
||||
arr_buf = (255*arr_buf).astype('uint8')
|
||||
|
||||
assert_array_equal(data, arr_buf)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['image_alpha'], remove_text=True)
|
||||
def test_image_alpha():
|
||||
plt.figure()
|
||||
|
||||
np.random.seed(0)
|
||||
Z = np.random.rand(6, 6)
|
||||
|
||||
plt.subplot(131)
|
||||
plt.imshow(Z, alpha=1.0, interpolation='none')
|
||||
|
||||
plt.subplot(132)
|
||||
plt.imshow(Z, alpha=0.5, interpolation='none')
|
||||
|
||||
plt.subplot(133)
|
||||
plt.imshow(Z, alpha=0.5, interpolation='nearest')
|
||||
|
||||
|
||||
def test_cursor_data():
|
||||
from matplotlib.backend_bases import MouseEvent
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
im = ax.imshow(np.arange(100).reshape(10, 10), origin='upper')
|
||||
|
||||
x, y = 4, 4
|
||||
xdisp, ydisp = ax.transData.transform_point([x, y])
|
||||
|
||||
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
|
||||
assert im.get_cursor_data(event) == 44
|
||||
|
||||
# Now try for a point outside the image
|
||||
# Tests issue #4957
|
||||
x, y = 10.1, 4
|
||||
xdisp, ydisp = ax.transData.transform_point([x, y])
|
||||
|
||||
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
|
||||
assert im.get_cursor_data(event) is None
|
||||
|
||||
# Hmm, something is wrong here... I get 0, not None...
|
||||
# But, this works further down in the tests with extents flipped
|
||||
#x, y = 0.1, -0.1
|
||||
#xdisp, ydisp = ax.transData.transform_point([x, y])
|
||||
#event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
|
||||
#z = im.get_cursor_data(event)
|
||||
#assert z is None, "Did not get None, got %d" % z
|
||||
|
||||
ax.clear()
|
||||
# Now try with the extents flipped.
|
||||
im = ax.imshow(np.arange(100).reshape(10, 10), origin='lower')
|
||||
|
||||
x, y = 4, 4
|
||||
xdisp, ydisp = ax.transData.transform_point([x, y])
|
||||
|
||||
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
|
||||
assert im.get_cursor_data(event) == 44
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
im = ax.imshow(np.arange(100).reshape(10, 10), extent=[0, 0.5, 0, 0.5])
|
||||
|
||||
x, y = 0.25, 0.25
|
||||
xdisp, ydisp = ax.transData.transform_point([x, y])
|
||||
|
||||
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
|
||||
assert im.get_cursor_data(event) == 55
|
||||
|
||||
# Now try for a point outside the image
|
||||
# Tests issue #4957
|
||||
x, y = 0.75, 0.25
|
||||
xdisp, ydisp = ax.transData.transform_point([x, y])
|
||||
|
||||
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
|
||||
assert im.get_cursor_data(event) is None
|
||||
|
||||
x, y = 0.01, -0.01
|
||||
xdisp, ydisp = ax.transData.transform_point([x, y])
|
||||
|
||||
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
|
||||
assert im.get_cursor_data(event) is None
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['image_clip'], style='mpl20')
|
||||
def test_image_clip():
|
||||
d = [[1, 2], [3, 4]]
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
im = ax.imshow(d)
|
||||
patch = patches.Circle((0, 0), radius=1, transform=ax.transData)
|
||||
im.set_clip_path(patch)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['image_cliprect'], style='mpl20')
|
||||
def test_image_cliprect():
|
||||
import matplotlib.patches as patches
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
d = [[1,2],[3,4]]
|
||||
|
||||
im = ax.imshow(d, extent=(0,5,0,5))
|
||||
|
||||
rect = patches.Rectangle(
|
||||
xy=(1,1), width=2, height=2, transform=im.axes.transData)
|
||||
im.set_clip_path(rect)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['imshow'], remove_text=True, style='mpl20')
|
||||
def test_imshow():
|
||||
fig, ax = plt.subplots()
|
||||
arr = np.arange(100).reshape((10, 10))
|
||||
ax.imshow(arr, interpolation="bilinear", extent=(1,2,1,2))
|
||||
ax.set_xlim(0,3)
|
||||
ax.set_ylim(0,3)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['no_interpolation_origin'],
|
||||
remove_text=True)
|
||||
def test_no_interpolation_origin():
|
||||
fig, axs = plt.subplots(2)
|
||||
axs[0].imshow(np.arange(100).reshape((2, 50)), origin="lower",
|
||||
interpolation='none')
|
||||
axs[1].imshow(np.arange(100).reshape((2, 50)), interpolation='none')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['image_shift'], remove_text=True,
|
||||
extensions=['pdf', 'svg'])
|
||||
def test_image_shift():
|
||||
from matplotlib.colors import LogNorm
|
||||
|
||||
imgData = [[1 / x + 1 / y for x in range(1, 100)] for y in range(1, 100)]
|
||||
tMin = 734717.945208
|
||||
tMax = 734717.946366
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(imgData, norm=LogNorm(), interpolation='none',
|
||||
extent=(tMin, tMax, 1, 100))
|
||||
ax.set_aspect('auto')
|
||||
|
||||
|
||||
def test_image_edges():
|
||||
f = plt.figure(figsize=[1, 1])
|
||||
ax = f.add_axes([0, 0, 1, 1], frameon=False)
|
||||
|
||||
data = np.tile(np.arange(12), 15).reshape(20, 9)
|
||||
|
||||
im = ax.imshow(data, origin='upper', extent=[-10, 10, -10, 10],
|
||||
interpolation='none', cmap='gray')
|
||||
|
||||
x = y = 2
|
||||
ax.set_xlim([-x, x])
|
||||
ax.set_ylim([-y, y])
|
||||
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
|
||||
buf = io.BytesIO()
|
||||
f.savefig(buf, facecolor=(0, 1, 0))
|
||||
|
||||
buf.seek(0)
|
||||
|
||||
im = plt.imread(buf)
|
||||
r, g, b, a = sum(im[:, 0])
|
||||
r, g, b, a = sum(im[:, -1])
|
||||
|
||||
assert g != 100, 'Expected a non-green edge - but sadly, it was.'
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['image_composite_background'],
|
||||
remove_text=True,
|
||||
style='mpl20')
|
||||
def test_image_composite_background():
|
||||
fig, ax = plt.subplots()
|
||||
arr = np.arange(12).reshape(4, 3)
|
||||
ax.imshow(arr, extent=[0, 2, 15, 0])
|
||||
ax.imshow(arr, extent=[4, 6, 15, 0])
|
||||
ax.set_facecolor((1, 0, 0, 0.5))
|
||||
ax.set_xlim([0, 12])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['image_composite_alpha'],
|
||||
remove_text=True)
|
||||
def test_image_composite_alpha():
|
||||
"""
|
||||
Tests that the alpha value is recognized and correctly applied in the
|
||||
process of compositing images together.
|
||||
"""
|
||||
fig, ax = plt.subplots()
|
||||
arr = np.zeros((11, 21, 4))
|
||||
arr[:, :, 0] = 1
|
||||
arr[:, :, 3] = np.concatenate(
|
||||
(np.arange(0, 1.1, 0.1), np.arange(0, 1, 0.1)[::-1]))
|
||||
arr2 = np.zeros((21, 11, 4))
|
||||
arr2[:, :, 0] = 1
|
||||
arr2[:, :, 1] = 1
|
||||
arr2[:, :, 3] = np.concatenate(
|
||||
(np.arange(0, 1.1, 0.1), np.arange(0, 1, 0.1)[::-1]))[:, np.newaxis]
|
||||
ax.imshow(arr, extent=[1, 2, 5, 0], alpha=0.3)
|
||||
ax.imshow(arr, extent=[2, 3, 5, 0], alpha=0.6)
|
||||
ax.imshow(arr, extent=[3, 4, 5, 0])
|
||||
ax.imshow(arr2, extent=[0, 5, 1, 2])
|
||||
ax.imshow(arr2, extent=[0, 5, 2, 3], alpha=0.6)
|
||||
ax.imshow(arr2, extent=[0, 5, 3, 4], alpha=0.3)
|
||||
ax.set_facecolor((0, 0.5, 0, 1))
|
||||
ax.set_xlim([0, 5])
|
||||
ax.set_ylim([5, 0])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['rasterize_10dpi'],
|
||||
extensions=['pdf', 'svg'],
|
||||
remove_text=True, style='mpl20')
|
||||
def test_rasterize_dpi():
|
||||
# This test should check rasterized rendering with high output resolution.
|
||||
# It plots a rasterized line and a normal image with implot. So it will
|
||||
# catch when images end up in the wrong place in case of non-standard dpi
|
||||
# setting. Instead of high-res rasterization I use low-res. Therefore
|
||||
# the fact that the resolution is non-standard is easily checked by
|
||||
# image_comparison.
|
||||
img = np.asarray([[1, 2], [3, 4]])
|
||||
|
||||
fig, axes = plt.subplots(1, 3, figsize=(3, 1))
|
||||
|
||||
axes[0].imshow(img)
|
||||
|
||||
axes[1].plot([0,1], [0,1], linewidth=20., rasterized=True)
|
||||
axes[1].set(xlim=(0, 1), ylim=(-1, 2))
|
||||
|
||||
axes[2].plot([0,1], [0,1], linewidth=20.)
|
||||
axes[2].set(xlim=(0, 1), ylim=(-1, 2))
|
||||
|
||||
# Low-dpi PDF rasterization errors prevent proper image comparison tests.
|
||||
# Hide detailed structures like the axes spines.
|
||||
for ax in axes:
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
for spine in ax.spines.values():
|
||||
spine.set_visible(False)
|
||||
|
||||
rcParams['savefig.dpi'] = 10
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['bbox_image_inverted'], remove_text=True,
|
||||
style='mpl20')
|
||||
def test_bbox_image_inverted():
|
||||
# This is just used to produce an image to feed to BboxImage
|
||||
image = np.arange(100).reshape((10, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
bbox_im = BboxImage(
|
||||
TransformedBbox(Bbox([[100, 100], [0, 0]]), ax.transData))
|
||||
bbox_im.set_data(image)
|
||||
bbox_im.set_clip_on(False)
|
||||
ax.set_xlim(0, 100)
|
||||
ax.set_ylim(0, 100)
|
||||
ax.add_artist(bbox_im)
|
||||
|
||||
image = np.identity(10)
|
||||
|
||||
bbox_im = BboxImage(TransformedBbox(Bbox([[0.1, 0.2], [0.3, 0.25]]),
|
||||
ax.figure.transFigure))
|
||||
bbox_im.set_data(image)
|
||||
bbox_im.set_clip_on(False)
|
||||
ax.add_artist(bbox_im)
|
||||
|
||||
|
||||
def test_get_window_extent_for_AxisImage():
|
||||
# Create a figure of known size (1000x1000 pixels), place an image
|
||||
# object at a given location and check that get_window_extent()
|
||||
# returns the correct bounding box values (in pixels).
|
||||
|
||||
im = np.array([[0.25, 0.75, 1.0, 0.75], [0.1, 0.65, 0.5, 0.4],
|
||||
[0.6, 0.3, 0.0, 0.2], [0.7, 0.9, 0.4, 0.6]])
|
||||
fig, ax = plt.subplots(figsize=(10, 10), dpi=100)
|
||||
ax.set_position([0, 0, 1, 1])
|
||||
ax.set_xlim(0, 1)
|
||||
ax.set_ylim(0, 1)
|
||||
im_obj = ax.imshow(
|
||||
im, extent=[0.4, 0.7, 0.2, 0.9], interpolation='nearest')
|
||||
|
||||
fig.canvas.draw()
|
||||
renderer = fig.canvas.renderer
|
||||
im_bbox = im_obj.get_window_extent(renderer)
|
||||
|
||||
assert_array_equal(im_bbox.get_points(), [[400, 200], [700, 900]])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['zoom_and_clip_upper_origin'],
|
||||
remove_text=True,
|
||||
extensions=['png'],
|
||||
style='mpl20')
|
||||
def test_zoom_and_clip_upper_origin():
|
||||
image = np.arange(100)
|
||||
image = image.reshape((10, 10))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(image)
|
||||
ax.set_ylim(2.0, -0.5)
|
||||
ax.set_xlim(-0.5, 2.0)
|
||||
|
||||
|
||||
def test_nonuniformimage_setcmap():
|
||||
ax = plt.gca()
|
||||
im = NonUniformImage(ax)
|
||||
im.set_cmap('Blues')
|
||||
|
||||
|
||||
def test_nonuniformimage_setnorm():
|
||||
ax = plt.gca()
|
||||
im = NonUniformImage(ax)
|
||||
im.set_norm(plt.Normalize())
|
||||
|
||||
|
||||
def test_jpeg_2d():
|
||||
Image = pytest.importorskip('PIL.Image')
|
||||
# smoke test that mode-L pillow images work.
|
||||
imd = np.ones((10, 10), dtype='uint8')
|
||||
for i in range(10):
|
||||
imd[i, :] = np.linspace(0.0, 1.0, 10) * 255
|
||||
im = Image.new('L', (10, 10))
|
||||
im.putdata(imd.flatten())
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(im)
|
||||
|
||||
|
||||
def test_jpeg_alpha():
|
||||
Image = pytest.importorskip('PIL.Image')
|
||||
|
||||
plt.figure(figsize=(1, 1), dpi=300)
|
||||
# Create an image that is all black, with a gradient from 0-1 in
|
||||
# the alpha channel from left to right.
|
||||
im = np.zeros((300, 300, 4), dtype=float)
|
||||
im[..., 3] = np.linspace(0.0, 1.0, 300)
|
||||
|
||||
plt.figimage(im)
|
||||
|
||||
buff = io.BytesIO()
|
||||
with rc_context({'savefig.facecolor': 'red'}):
|
||||
plt.savefig(buff, transparent=True, format='jpg', dpi=300)
|
||||
|
||||
buff.seek(0)
|
||||
image = Image.open(buff)
|
||||
|
||||
# If this fails, there will be only one color (all black). If this
|
||||
# is working, we should have all 256 shades of grey represented.
|
||||
num_colors = len(image.getcolors(256))
|
||||
assert 175 <= num_colors <= 185
|
||||
# The fully transparent part should be red.
|
||||
corner_pixel = image.getpixel((0, 0))
|
||||
assert corner_pixel == (254, 0, 0)
|
||||
|
||||
|
||||
def test_nonuniformimage_setdata():
|
||||
ax = plt.gca()
|
||||
im = NonUniformImage(ax)
|
||||
x = np.arange(3, dtype=float)
|
||||
y = np.arange(4, dtype=float)
|
||||
z = np.arange(12, dtype=float).reshape((4, 3))
|
||||
im.set_data(x, y, z)
|
||||
x[0] = y[0] = z[0, 0] = 9.9
|
||||
assert im._A[0, 0] == im._Ax[0] == im._Ay[0] == 0, 'value changed'
|
||||
|
||||
|
||||
def test_axesimage_setdata():
|
||||
ax = plt.gca()
|
||||
im = AxesImage(ax)
|
||||
z = np.arange(12, dtype=float).reshape((4, 3))
|
||||
im.set_data(z)
|
||||
z[0, 0] = 9.9
|
||||
assert im._A[0, 0] == 0, 'value changed'
|
||||
|
||||
|
||||
def test_figureimage_setdata():
|
||||
fig = plt.gcf()
|
||||
im = FigureImage(fig)
|
||||
z = np.arange(12, dtype=float).reshape((4, 3))
|
||||
im.set_data(z)
|
||||
z[0, 0] = 9.9
|
||||
assert im._A[0, 0] == 0, 'value changed'
|
||||
|
||||
|
||||
def test_pcolorimage_setdata():
|
||||
ax = plt.gca()
|
||||
im = PcolorImage(ax)
|
||||
x = np.arange(3, dtype=float)
|
||||
y = np.arange(4, dtype=float)
|
||||
z = np.arange(6, dtype=float).reshape((3, 2))
|
||||
im.set_data(x, y, z)
|
||||
x[0] = y[0] = z[0, 0] = 9.9
|
||||
assert im._A[0, 0] == im._Ax[0] == im._Ay[0] == 0, 'value changed'
|
||||
|
||||
|
||||
def test_minimized_rasterized():
|
||||
# This ensures that the rasterized content in the colorbars is
|
||||
# only as thick as the colorbar, and doesn't extend to other parts
|
||||
# of the image. See #5814. While the original bug exists only
|
||||
# in Postscript, the best way to detect it is to generate SVG
|
||||
# and then parse the output to make sure the two colorbar images
|
||||
# are the same size.
|
||||
from xml.etree import ElementTree
|
||||
|
||||
np.random.seed(0)
|
||||
data = np.random.rand(10, 10)
|
||||
|
||||
fig, ax = plt.subplots(1, 2)
|
||||
p1 = ax[0].pcolormesh(data)
|
||||
p2 = ax[1].pcolormesh(data)
|
||||
|
||||
plt.colorbar(p1, ax=ax[0])
|
||||
plt.colorbar(p2, ax=ax[1])
|
||||
|
||||
buff = io.BytesIO()
|
||||
plt.savefig(buff, format='svg')
|
||||
|
||||
buff = io.BytesIO(buff.getvalue())
|
||||
tree = ElementTree.parse(buff)
|
||||
width = None
|
||||
for image in tree.iter('image'):
|
||||
if width is None:
|
||||
width = image['width']
|
||||
else:
|
||||
if image['width'] != width:
|
||||
assert False
|
||||
|
||||
|
||||
@pytest.mark.network
|
||||
def test_load_from_url():
|
||||
url = "http://matplotlib.org/_static/logo_sidebar_horiz.png"
|
||||
plt.imread(url)
|
||||
plt.imread(urllib.request.urlopen(url))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['log_scale_image'],
|
||||
remove_text=True)
|
||||
# The recwarn fixture captures a warning in image_comparison.
|
||||
def test_log_scale_image(recwarn):
|
||||
Z = np.zeros((10, 10))
|
||||
Z[::2] = 1
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(Z, extent=[1, 100, 1, 100], cmap='viridis',
|
||||
vmax=1, vmin=-1)
|
||||
ax.set_yscale('log')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['rotate_image'],
|
||||
remove_text=True)
|
||||
def test_rotate_image():
|
||||
delta = 0.25
|
||||
x = y = np.arange(-3.0, 3.0, delta)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
Z1 = np.exp(-(X**2 + Y**2) / 2) / (2 * np.pi)
|
||||
Z2 = (np.exp(-(((X - 1) / 1.5)**2 + ((Y - 1) / 0.5)**2) / 2) /
|
||||
(2 * np.pi * 0.5 * 1.5))
|
||||
Z = Z2 - Z1 # difference of Gaussians
|
||||
|
||||
fig, ax1 = plt.subplots(1, 1)
|
||||
im1 = ax1.imshow(Z, interpolation='none', cmap='viridis',
|
||||
origin='lower',
|
||||
extent=[-2, 4, -3, 2], clip_on=True)
|
||||
|
||||
trans_data2 = Affine2D().rotate_deg(30) + ax1.transData
|
||||
im1.set_transform(trans_data2)
|
||||
|
||||
# display intended extent of the image
|
||||
x1, x2, y1, y2 = im1.get_extent()
|
||||
|
||||
ax1.plot([x1, x2, x2, x1, x1], [y1, y1, y2, y2, y1], "r--", lw=3,
|
||||
transform=trans_data2)
|
||||
|
||||
ax1.set_xlim(2, 5)
|
||||
ax1.set_ylim(0, 4)
|
||||
|
||||
|
||||
def test_image_preserve_size():
|
||||
buff = io.BytesIO()
|
||||
|
||||
im = np.zeros((481, 321))
|
||||
plt.imsave(buff, im, format="png")
|
||||
|
||||
buff.seek(0)
|
||||
img = plt.imread(buff)
|
||||
|
||||
assert img.shape[:2] == im.shape
|
||||
|
||||
|
||||
def test_image_preserve_size2():
|
||||
n = 7
|
||||
data = np.identity(n, float)
|
||||
|
||||
fig = plt.figure(figsize=(n, n), frameon=False)
|
||||
|
||||
ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0])
|
||||
ax.set_axis_off()
|
||||
fig.add_axes(ax)
|
||||
ax.imshow(data, interpolation='nearest', origin='lower',aspect='auto')
|
||||
buff = io.BytesIO()
|
||||
fig.savefig(buff, dpi=1)
|
||||
|
||||
buff.seek(0)
|
||||
img = plt.imread(buff)
|
||||
|
||||
assert img.shape == (7, 7, 4)
|
||||
|
||||
assert_array_equal(np.asarray(img[:, :, 0], bool),
|
||||
np.identity(n, bool)[::-1])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['mask_image_over_under'],
|
||||
remove_text=True, extensions=['png'])
|
||||
def test_mask_image_over_under():
|
||||
delta = 0.025
|
||||
x = y = np.arange(-3.0, 3.0, delta)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
Z1 = np.exp(-(X**2 + Y**2) / 2) / (2 * np.pi)
|
||||
Z2 = (np.exp(-(((X - 1) / 1.5)**2 + ((Y - 1) / 0.5)**2) / 2) /
|
||||
(2 * np.pi * 0.5 * 1.5))
|
||||
Z = 10*(Z2 - Z1) # difference of Gaussians
|
||||
|
||||
palette = copy(plt.cm.gray)
|
||||
palette.set_over('r', 1.0)
|
||||
palette.set_under('g', 1.0)
|
||||
palette.set_bad('b', 1.0)
|
||||
Zm = ma.masked_where(Z > 1.2, Z)
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2)
|
||||
im = ax1.imshow(Zm, interpolation='bilinear',
|
||||
cmap=palette,
|
||||
norm=colors.Normalize(vmin=-1.0, vmax=1.0, clip=False),
|
||||
origin='lower', extent=[-3, 3, -3, 3])
|
||||
ax1.set_title('Green=low, Red=high, Blue=bad')
|
||||
fig.colorbar(im, extend='both', orientation='horizontal',
|
||||
ax=ax1, aspect=10)
|
||||
|
||||
im = ax2.imshow(Zm, interpolation='nearest',
|
||||
cmap=palette,
|
||||
norm=colors.BoundaryNorm([-1, -0.5, -0.2, 0, 0.2, 0.5, 1],
|
||||
ncolors=256, clip=False),
|
||||
origin='lower', extent=[-3, 3, -3, 3])
|
||||
ax2.set_title('With BoundaryNorm')
|
||||
fig.colorbar(im, extend='both', spacing='proportional',
|
||||
orientation='horizontal', ax=ax2, aspect=10)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['mask_image'],
|
||||
remove_text=True)
|
||||
def test_mask_image():
|
||||
# Test mask image two ways: Using nans and using a masked array.
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2)
|
||||
|
||||
A = np.ones((5, 5))
|
||||
A[1:2, 1:2] = np.nan
|
||||
|
||||
ax1.imshow(A, interpolation='nearest')
|
||||
|
||||
A = np.zeros((5, 5), dtype=bool)
|
||||
A[1:2, 1:2] = True
|
||||
A = np.ma.masked_array(np.ones((5, 5), dtype=np.uint16), A)
|
||||
|
||||
ax2.imshow(A, interpolation='nearest')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['imshow_endianess'],
|
||||
remove_text=True, extensions=['png'])
|
||||
def test_imshow_endianess():
|
||||
x = np.arange(10)
|
||||
X, Y = np.meshgrid(x, x)
|
||||
Z = ((X-5)**2 + (Y-5)**2)**0.5
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2)
|
||||
|
||||
kwargs = dict(origin="lower", interpolation='nearest',
|
||||
cmap='viridis')
|
||||
|
||||
ax1.imshow(Z.astype('<f8'), **kwargs)
|
||||
ax2.imshow(Z.astype('>f8'), **kwargs)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['imshow_masked_interpolation'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
remove_text=True, style='mpl20')
|
||||
def test_imshow_masked_interpolation():
|
||||
|
||||
cm = copy(plt.get_cmap('viridis'))
|
||||
cm.set_over('r')
|
||||
cm.set_under('b')
|
||||
cm.set_bad('k')
|
||||
|
||||
N = 20
|
||||
n = colors.Normalize(vmin=0, vmax=N*N-1)
|
||||
|
||||
data = np.arange(N*N, dtype='float').reshape(N, N)
|
||||
|
||||
data[5, 5] = -1
|
||||
# This will cause crazy ringing for the higher-order
|
||||
# interpolations
|
||||
data[15, 5] = 1e5
|
||||
|
||||
# data[3, 3] = np.nan
|
||||
|
||||
data[15, 15] = np.inf
|
||||
|
||||
mask = np.zeros_like(data).astype('bool')
|
||||
mask[5, 15] = True
|
||||
|
||||
data = np.ma.masked_array(data, mask)
|
||||
|
||||
fig, ax_grid = plt.subplots(3, 6)
|
||||
|
||||
for interp, ax in zip(sorted(mimage._interpd_), ax_grid.ravel()):
|
||||
ax.set_title(interp)
|
||||
ax.imshow(data, norm=n, cmap=cm, interpolation=interp)
|
||||
ax.axis('off')
|
||||
|
||||
|
||||
def test_imshow_no_warn_invalid():
|
||||
with warnings.catch_warnings(record=True) as warns:
|
||||
warnings.simplefilter("always")
|
||||
plt.imshow([[1, 2], [3, np.nan]])
|
||||
assert len(warns) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'dtype', [np.dtype(s) for s in 'u2 u4 i2 i4 i8 f4 f8'.split()])
|
||||
def test_imshow_clips_rgb_to_valid_range(dtype):
|
||||
arr = np.arange(300, dtype=dtype).reshape((10, 10, 3))
|
||||
if dtype.kind != 'u':
|
||||
arr -= 10
|
||||
too_low = arr < 0
|
||||
too_high = arr > 255
|
||||
if dtype.kind == 'f':
|
||||
arr = arr / 255
|
||||
_, ax = plt.subplots()
|
||||
out = ax.imshow(arr).get_array()
|
||||
assert (out[too_low] == 0).all()
|
||||
if dtype.kind == 'f':
|
||||
assert (out[too_high] == 1).all()
|
||||
assert out.dtype.kind == 'f'
|
||||
else:
|
||||
assert (out[too_high] == 255).all()
|
||||
assert out.dtype == np.uint8
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['imshow_flatfield'],
|
||||
remove_text=True, style='mpl20',
|
||||
extensions=['png'])
|
||||
def test_imshow_flatfield():
|
||||
fig, ax = plt.subplots()
|
||||
im = ax.imshow(np.ones((5, 5)))
|
||||
im.set_clim(.5, 1.5)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['imshow_bignumbers'],
|
||||
remove_text=True, style='mpl20',
|
||||
extensions=['png'])
|
||||
def test_imshow_bignumbers():
|
||||
# putting a big number in an array of integers shouldn't
|
||||
# ruin the dynamic range of the resolved bits.
|
||||
fig, ax = plt.subplots()
|
||||
img = np.array([[1, 2, 1e12],[3, 1, 4]], dtype=np.uint64)
|
||||
pc = ax.imshow(img)
|
||||
pc.set_clim(0, 5)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['imshow_bignumbers_real'],
|
||||
remove_text=True, style='mpl20',
|
||||
extensions=['png'])
|
||||
def test_imshow_bignumbers_real():
|
||||
# putting a big number in an array of integers shouldn't
|
||||
# ruin the dynamic range of the resolved bits.
|
||||
fig, ax = plt.subplots()
|
||||
img = np.array([[2., 1., 1.e22],[4., 1., 3.]])
|
||||
pc = ax.imshow(img)
|
||||
pc.set_clim(0, 5)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"make_norm",
|
||||
[colors.Normalize,
|
||||
colors.LogNorm,
|
||||
lambda: colors.SymLogNorm(1),
|
||||
lambda: colors.PowerNorm(1)])
|
||||
def test_empty_imshow(make_norm):
|
||||
fig, ax = plt.subplots()
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings(
|
||||
"ignore", "Attempting to set identical left==right")
|
||||
im = ax.imshow([[]], norm=make_norm())
|
||||
im.set_extent([-5, 5, -5, 5])
|
||||
fig.canvas.draw()
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
im.make_image(fig._cachedRenderer)
|
||||
|
||||
|
||||
def test_imshow_float128():
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(np.zeros((3, 3), dtype=np.longdouble))
|
||||
with (ExitStack() if np.can_cast(np.longdouble, np.float64, "equiv")
|
||||
else pytest.warns(UserWarning)):
|
||||
# Ensure that drawing doesn't cause crash.
|
||||
fig.canvas.draw()
|
||||
|
||||
|
||||
def test_imshow_bool():
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(np.array([[True, False], [False, True]], dtype=bool))
|
||||
|
||||
|
||||
def test_full_invalid():
|
||||
x = np.ones((10, 10))
|
||||
x[:] = np.nan
|
||||
|
||||
f, ax = plt.subplots()
|
||||
ax.imshow(x)
|
||||
|
||||
f.canvas.draw()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fmt,counted",
|
||||
[("ps", b" colorimage"), ("svg", b"<image")])
|
||||
@pytest.mark.parametrize("composite_image,count", [(True, 1), (False, 2)])
|
||||
def test_composite(fmt, counted, composite_image, count):
|
||||
# Test that figures can be saved with and without combining multiple images
|
||||
# (on a single set of axes) into a single composite image.
|
||||
X, Y = np.meshgrid(np.arange(-5, 5, 1), np.arange(-5, 5, 1))
|
||||
Z = np.sin(Y ** 2)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_xlim(0, 3)
|
||||
ax.imshow(Z, extent=[0, 1, 0, 1])
|
||||
ax.imshow(Z[::-1], extent=[2, 3, 0, 1])
|
||||
plt.rcParams['image.composite_image'] = composite_image
|
||||
buf = io.BytesIO()
|
||||
fig.savefig(buf, format=fmt)
|
||||
assert buf.getvalue().count(counted) == count
|
||||
|
||||
|
||||
def test_relim():
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow([[0]], extent=(0, 1, 0, 1))
|
||||
ax.relim()
|
||||
ax.autoscale()
|
||||
assert ax.get_xlim() == ax.get_ylim() == (0, 1)
|
||||
@@ -0,0 +1,565 @@
|
||||
import collections
|
||||
import inspect
|
||||
import platform
|
||||
from unittest import mock
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
import matplotlib.transforms as mtransforms
|
||||
import matplotlib.collections as mcollections
|
||||
from matplotlib.legend_handler import HandlerTuple
|
||||
import matplotlib.legend as mlegend
|
||||
from matplotlib.cbook.deprecation import MatplotlibDeprecationWarning
|
||||
|
||||
|
||||
def test_legend_ordereddict():
|
||||
# smoketest that ordereddict inputs work...
|
||||
|
||||
X = np.random.randn(10)
|
||||
Y = np.random.randn(10)
|
||||
labels = ['a'] * 5 + ['b'] * 5
|
||||
colors = ['r'] * 5 + ['g'] * 5
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
for x, y, label, color in zip(X, Y, labels, colors):
|
||||
ax.scatter(x, y, label=label, c=color)
|
||||
|
||||
handles, labels = ax.get_legend_handles_labels()
|
||||
legend = collections.OrderedDict(zip(labels, handles))
|
||||
ax.legend(legend.values(), legend.keys(),
|
||||
loc='center left', bbox_to_anchor=(1, .5))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['legend_auto1'], remove_text=True)
|
||||
def test_legend_auto1():
|
||||
'Test automatic legend placement'
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
x = np.arange(100)
|
||||
ax.plot(x, 50 - x, 'o', label='y=1')
|
||||
ax.plot(x, x - 50, 'o', label='y=-1')
|
||||
ax.legend(loc='best')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['legend_auto2'], remove_text=True)
|
||||
def test_legend_auto2():
|
||||
'Test automatic legend placement'
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
x = np.arange(100)
|
||||
b1 = ax.bar(x, x, align='edge', color='m')
|
||||
b2 = ax.bar(x, x[::-1], align='edge', color='g')
|
||||
ax.legend([b1[0], b2[0]], ['up', 'down'], loc='best')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['legend_auto3'])
|
||||
def test_legend_auto3():
|
||||
'Test automatic legend placement'
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
x = [0.9, 0.1, 0.1, 0.9, 0.9, 0.5]
|
||||
y = [0.95, 0.95, 0.05, 0.05, 0.5, 0.5]
|
||||
ax.plot(x, y, 'o-', label='line')
|
||||
ax.set_xlim(0.0, 1.0)
|
||||
ax.set_ylim(0.0, 1.0)
|
||||
ax.legend(loc='best')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['legend_various_labels'], remove_text=True)
|
||||
def test_various_labels():
|
||||
# tests all sorts of label types
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(121)
|
||||
ax.plot(np.arange(4), 'o', label=1)
|
||||
ax.plot(np.linspace(4, 4.1), 'o', label='Développés')
|
||||
ax.plot(np.arange(4, 1, -1), 'o', label='__nolegend__')
|
||||
ax.legend(numpoints=1, loc='best')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['legend_labels_first'], extensions=['png'],
|
||||
remove_text=True)
|
||||
def test_labels_first():
|
||||
# test labels to left of markers
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.plot(np.arange(10), '-o', label=1)
|
||||
ax.plot(np.ones(10)*5, ':x', label="x")
|
||||
ax.plot(np.arange(20, 10, -1), 'd', label="diamond")
|
||||
ax.legend(loc='best', markerfirst=False)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['legend_multiple_keys'], extensions=['png'],
|
||||
remove_text=True)
|
||||
def test_multiple_keys():
|
||||
# test legend entries with multiple keys
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
p1, = ax.plot([1, 2, 3], '-o')
|
||||
p2, = ax.plot([2, 3, 4], '-x')
|
||||
p3, = ax.plot([3, 4, 5], '-d')
|
||||
ax.legend([(p1, p2), (p2, p1), p3], ['two keys', 'pad=0', 'one key'],
|
||||
numpoints=1,
|
||||
handler_map={(p1, p2): HandlerTuple(ndivide=None),
|
||||
(p2, p1): HandlerTuple(ndivide=None, pad=0)})
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['rgba_alpha'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_alpha_rgba():
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
ax.plot(range(10), lw=5)
|
||||
leg = plt.legend(['Longlabel that will go away'], loc='center')
|
||||
leg.legendPatch.set_facecolor([1, 0, 0, 0.5])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['rcparam_alpha'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_alpha_rcparam():
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
ax.plot(range(10), lw=5)
|
||||
with mpl.rc_context(rc={'legend.framealpha': .75}):
|
||||
leg = plt.legend(['Longlabel that will go away'], loc='center')
|
||||
# this alpha is going to be over-ridden by the rcparam with
|
||||
# sets the alpha of the patch to be non-None which causes the alpha
|
||||
# value of the face color to be discarded. This behavior may not be
|
||||
# ideal, but it is what it is and we should keep track of it changing
|
||||
leg.legendPatch.set_facecolor([1, 0, 0, 0.5])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fancy'], remove_text=True)
|
||||
def test_fancy():
|
||||
# using subplot triggers some offsetbox functionality untested elsewhere
|
||||
plt.subplot(121)
|
||||
plt.scatter(np.arange(10), np.arange(10, 0, -1), label='XX\nXX')
|
||||
plt.plot([5] * 10, 'o--', label='XX')
|
||||
plt.errorbar(np.arange(10), np.arange(10), xerr=0.5,
|
||||
yerr=0.5, label='XX')
|
||||
plt.legend(loc="center left", bbox_to_anchor=[1.0, 0.5],
|
||||
ncol=2, shadow=True, title="My legend", numpoints=1)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['framealpha'], remove_text=True,
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0))
|
||||
def test_framealpha():
|
||||
x = np.linspace(1, 100, 100)
|
||||
y = x
|
||||
plt.plot(x, y, label='mylabel', lw=10)
|
||||
plt.legend(framealpha=0.5)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['scatter_rc3', 'scatter_rc1'],
|
||||
remove_text=True)
|
||||
def test_rc():
|
||||
# using subplot triggers some offsetbox functionality untested elsewhere
|
||||
plt.figure()
|
||||
ax = plt.subplot(121)
|
||||
ax.scatter(np.arange(10), np.arange(10, 0, -1), label='three')
|
||||
ax.legend(loc="center left", bbox_to_anchor=[1.0, 0.5],
|
||||
title="My legend")
|
||||
|
||||
mpl.rcParams['legend.scatterpoints'] = 1
|
||||
plt.figure()
|
||||
ax = plt.subplot(121)
|
||||
ax.scatter(np.arange(10), np.arange(10, 0, -1), label='one')
|
||||
ax.legend(loc="center left", bbox_to_anchor=[1.0, 0.5],
|
||||
title="My legend")
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['legend_expand'], remove_text=True)
|
||||
def test_legend_expand():
|
||||
'Test expand mode'
|
||||
legend_modes = [None, "expand"]
|
||||
fig, axes_list = plt.subplots(len(legend_modes), 1)
|
||||
x = np.arange(100)
|
||||
for ax, mode in zip(axes_list, legend_modes):
|
||||
ax.plot(x, 50 - x, 'o', label='y=1')
|
||||
l1 = ax.legend(loc='upper left', mode=mode)
|
||||
ax.add_artist(l1)
|
||||
ax.plot(x, x - 50, 'o', label='y=-1')
|
||||
l2 = ax.legend(loc='right', mode=mode)
|
||||
ax.add_artist(l2)
|
||||
ax.legend(loc='lower left', mode=mode, ncol=2)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['hatching'], remove_text=True,
|
||||
style='default')
|
||||
def test_hatching():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
# Patches
|
||||
patch = plt.Rectangle((0, 0), 0.3, 0.3, hatch='xx',
|
||||
label='Patch\ndefault color\nfilled')
|
||||
ax.add_patch(patch)
|
||||
patch = plt.Rectangle((0.33, 0), 0.3, 0.3, hatch='||', edgecolor='C1',
|
||||
label='Patch\nexplicit color\nfilled')
|
||||
ax.add_patch(patch)
|
||||
patch = plt.Rectangle((0, 0.4), 0.3, 0.3, hatch='xx', fill=False,
|
||||
label='Patch\ndefault color\nunfilled')
|
||||
ax.add_patch(patch)
|
||||
patch = plt.Rectangle((0.33, 0.4), 0.3, 0.3, hatch='||', fill=False,
|
||||
edgecolor='C1',
|
||||
label='Patch\nexplicit color\nunfilled')
|
||||
ax.add_patch(patch)
|
||||
|
||||
# Paths
|
||||
ax.fill_between([0, .15, .3], [.8, .8, .8], [.9, 1.0, .9],
|
||||
hatch='+', label='Path\ndefault color')
|
||||
ax.fill_between([.33, .48, .63], [.8, .8, .8], [.9, 1.0, .9],
|
||||
hatch='+', edgecolor='C2', label='Path\nexplicit color')
|
||||
|
||||
ax.set_xlim(-0.01, 1.1)
|
||||
ax.set_ylim(-0.01, 1.1)
|
||||
ax.legend(handlelength=4, handleheight=4)
|
||||
|
||||
|
||||
def test_legend_remove():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
lines = ax.plot(range(10))
|
||||
leg = fig.legend(lines, "test")
|
||||
leg.remove()
|
||||
assert fig.legends == []
|
||||
leg = ax.legend("test")
|
||||
leg.remove()
|
||||
assert ax.get_legend() is None
|
||||
|
||||
|
||||
class TestLegendFunction(object):
|
||||
# Tests the legend function on the Axes and pyplot.
|
||||
def test_legend_handle_label(self):
|
||||
lines = plt.plot(range(10))
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
plt.legend(lines, ['hello world'])
|
||||
Legend.assert_called_with(plt.gca(), lines, ['hello world'])
|
||||
|
||||
def test_legend_no_args(self):
|
||||
lines = plt.plot(range(10), label='hello world')
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
plt.legend()
|
||||
Legend.assert_called_with(plt.gca(), lines, ['hello world'])
|
||||
|
||||
def test_legend_label_args(self):
|
||||
lines = plt.plot(range(10), label='hello world')
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
plt.legend(['foobar'])
|
||||
Legend.assert_called_with(plt.gca(), lines, ['foobar'])
|
||||
|
||||
def test_legend_three_args(self):
|
||||
lines = plt.plot(range(10), label='hello world')
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
plt.legend(lines, ['foobar'], loc='right')
|
||||
Legend.assert_called_with(plt.gca(), lines, ['foobar'], loc='right')
|
||||
|
||||
def test_legend_handler_map(self):
|
||||
lines = plt.plot(range(10), label='hello world')
|
||||
with mock.patch('matplotlib.legend.'
|
||||
'_get_legend_handles_labels') as handles_labels:
|
||||
handles_labels.return_value = lines, ['hello world']
|
||||
plt.legend(handler_map={'1': 2})
|
||||
handles_labels.assert_called_with([plt.gca()], {'1': 2})
|
||||
|
||||
def test_kwargs(self):
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
th = np.linspace(0, 2*np.pi, 1024)
|
||||
lns, = ax.plot(th, np.sin(th), label='sin', lw=5)
|
||||
lnc, = ax.plot(th, np.cos(th), label='cos', lw=5)
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
ax.legend(labels=('a', 'b'), handles=(lnc, lns))
|
||||
Legend.assert_called_with(ax, (lnc, lns), ('a', 'b'))
|
||||
|
||||
def test_warn_args_kwargs(self):
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
th = np.linspace(0, 2*np.pi, 1024)
|
||||
lns, = ax.plot(th, np.sin(th), label='sin', lw=5)
|
||||
lnc, = ax.plot(th, np.cos(th), label='cos', lw=5)
|
||||
with mock.patch('warnings.warn') as warn:
|
||||
ax.legend((lnc, lns), labels=('a', 'b'))
|
||||
|
||||
warn.assert_called_with("You have mixed positional and keyword "
|
||||
"arguments, some input may be "
|
||||
"discarded.")
|
||||
|
||||
def test_parasite(self):
|
||||
from mpl_toolkits.axes_grid1 import host_subplot
|
||||
|
||||
host = host_subplot(111)
|
||||
par = host.twinx()
|
||||
|
||||
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
|
||||
p2, = par.plot([0, 1, 2], [0, 3, 2], label="Temperature")
|
||||
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
leg = plt.legend()
|
||||
Legend.assert_called_with(host, [p1, p2],
|
||||
['Density', 'Temperature'])
|
||||
|
||||
|
||||
class TestLegendFigureFunction(object):
|
||||
# Tests the legend function for figure
|
||||
def test_legend_handle_label(self):
|
||||
fig, ax = plt.subplots()
|
||||
lines = ax.plot(range(10))
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
fig.legend(lines, ['hello world'])
|
||||
Legend.assert_called_with(fig, lines, ['hello world'])
|
||||
|
||||
def test_legend_no_args(self):
|
||||
fig, ax = plt.subplots()
|
||||
lines = ax.plot(range(10), label='hello world')
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
fig.legend()
|
||||
Legend.assert_called_with(fig, lines, ['hello world'])
|
||||
|
||||
def test_legend_label_arg(self):
|
||||
fig, ax = plt.subplots()
|
||||
lines = ax.plot(range(10))
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
fig.legend(['foobar'])
|
||||
Legend.assert_called_with(fig, lines, ['foobar'])
|
||||
|
||||
def test_legend_label_three_args(self):
|
||||
fig, ax = plt.subplots()
|
||||
lines = ax.plot(range(10))
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
fig.legend(lines, ['foobar'], 'right')
|
||||
Legend.assert_called_with(fig, lines, ['foobar'], 'right')
|
||||
|
||||
def test_legend_label_three_args_pluskw(self):
|
||||
# test that third argument and loc= called together give
|
||||
# Exception
|
||||
fig, ax = plt.subplots()
|
||||
lines = ax.plot(range(10))
|
||||
with pytest.raises(Exception):
|
||||
fig.legend(lines, ['foobar'], 'right', loc='left')
|
||||
|
||||
def test_legend_kw_args(self):
|
||||
fig, axs = plt.subplots(1, 2)
|
||||
lines = axs[0].plot(range(10))
|
||||
lines2 = axs[1].plot(np.arange(10) * 2.)
|
||||
with mock.patch('matplotlib.legend.Legend') as Legend:
|
||||
fig.legend(loc='right', labels=('a', 'b'),
|
||||
handles=(lines, lines2))
|
||||
Legend.assert_called_with(fig, (lines, lines2), ('a', 'b'),
|
||||
loc='right')
|
||||
|
||||
def test_warn_args_kwargs(self):
|
||||
fig, axs = plt.subplots(1, 2)
|
||||
lines = axs[0].plot(range(10))
|
||||
lines2 = axs[1].plot(np.arange(10) * 2.)
|
||||
with mock.patch('warnings.warn') as warn:
|
||||
fig.legend((lines, lines2), labels=('a', 'b'))
|
||||
warn.assert_called_with("You have mixed positional and keyword "
|
||||
"arguments, some input may be "
|
||||
"discarded.")
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['legend_stackplot'], extensions=['png'])
|
||||
def test_legend_stackplot():
|
||||
'''test legend for PolyCollection using stackplot'''
|
||||
# related to #1341, #1943, and PR #3303
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
x = np.linspace(0, 10, 10)
|
||||
y1 = 1.0 * x
|
||||
y2 = 2.0 * x + 1
|
||||
y3 = 3.0 * x + 2
|
||||
ax.stackplot(x, y1, y2, y3, labels=['y1', 'y2', 'y3'])
|
||||
ax.set_xlim((0, 10))
|
||||
ax.set_ylim((0, 70))
|
||||
ax.legend(loc='best')
|
||||
|
||||
|
||||
def test_cross_figure_patch_legend():
|
||||
fig, ax = plt.subplots()
|
||||
fig2, ax2 = plt.subplots()
|
||||
|
||||
brs = ax.bar(range(3), range(3))
|
||||
fig2.legend(brs, 'foo')
|
||||
|
||||
|
||||
def test_nanscatter():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
h = ax.scatter([np.nan], [np.nan], marker="o",
|
||||
facecolor="r", edgecolor="r", s=3)
|
||||
|
||||
ax.legend([h], ["scatter"])
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
for color in ['red', 'green', 'blue']:
|
||||
n = 750
|
||||
x, y = np.random.rand(2, n)
|
||||
scale = 200.0 * np.random.rand(n)
|
||||
ax.scatter(x, y, c=color, s=scale, label=color,
|
||||
alpha=0.3, edgecolors='none')
|
||||
|
||||
ax.legend()
|
||||
ax.grid(True)
|
||||
|
||||
|
||||
def test_legend_repeatcheckok():
|
||||
fig, ax = plt.subplots()
|
||||
ax.scatter(0.0, 1.0, color='k', marker='o', label='test')
|
||||
ax.scatter(0.5, 0.0, color='r', marker='v', label='test')
|
||||
hl = ax.legend()
|
||||
hand, lab = mlegend._get_legend_handles_labels([ax])
|
||||
assert len(lab) == 2
|
||||
fig, ax = plt.subplots()
|
||||
ax.scatter(0.0, 1.0, color='k', marker='o', label='test')
|
||||
ax.scatter(0.5, 0.0, color='k', marker='v', label='test')
|
||||
hl = ax.legend()
|
||||
hand, lab = mlegend._get_legend_handles_labels([ax])
|
||||
assert len(lab) == 2
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['not_covering_scatter'], extensions=['png'])
|
||||
def test_not_covering_scatter():
|
||||
colors = ['b', 'g', 'r']
|
||||
|
||||
for n in range(3):
|
||||
plt.scatter([n], [n], color=colors[n])
|
||||
|
||||
plt.legend(['foo', 'foo', 'foo'], loc='best')
|
||||
plt.gca().set_xlim(-0.5, 2.2)
|
||||
plt.gca().set_ylim(-0.5, 2.2)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['not_covering_scatter_transform'],
|
||||
extensions=['png'])
|
||||
def test_not_covering_scatter_transform():
|
||||
# Offsets point to top left, the default auto position
|
||||
offset = mtransforms.Affine2D().translate(-20, 20)
|
||||
x = np.linspace(0, 30, 1000)
|
||||
plt.plot(x, x)
|
||||
|
||||
plt.scatter([20], [10], transform=offset + plt.gca().transData)
|
||||
|
||||
plt.legend(['foo', 'bar'], loc='best')
|
||||
|
||||
|
||||
def test_linecollection_scaled_dashes():
|
||||
lines1 = [[(0, .5), (.5, 1)], [(.3, .6), (.2, .2)]]
|
||||
lines2 = [[[0.7, .2], [.8, .4]], [[.5, .7], [.6, .1]]]
|
||||
lines3 = [[[0.6, .2], [.8, .4]], [[.5, .7], [.1, .1]]]
|
||||
lc1 = mcollections.LineCollection(lines1, linestyles="--", lw=3)
|
||||
lc2 = mcollections.LineCollection(lines2, linestyles="-.")
|
||||
lc3 = mcollections.LineCollection(lines3, linestyles=":", lw=.5)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.add_collection(lc1)
|
||||
ax.add_collection(lc2)
|
||||
ax.add_collection(lc3)
|
||||
|
||||
leg = ax.legend([lc1, lc2, lc3], ["line1", "line2", 'line 3'])
|
||||
h1, h2, h3 = leg.legendHandles
|
||||
|
||||
for oh, lh in zip((lc1, lc2, lc3), (h1, h2, h3)):
|
||||
assert oh.get_linestyles()[0][1] == lh._dashSeq
|
||||
assert oh.get_linestyles()[0][0] == lh._dashOffset
|
||||
|
||||
|
||||
def test_handler_numpoints():
|
||||
'''test legend handler with numponts less than or equal to 1'''
|
||||
# related to #6921 and PR #8478
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(range(5), label='test')
|
||||
ax.legend(numpoints=0.5)
|
||||
|
||||
|
||||
def test_shadow_framealpha():
|
||||
# Test if framealpha is activated when shadow is True
|
||||
# and framealpha is not explicitly passed'''
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(range(100), label="test")
|
||||
leg = ax.legend(shadow=True, facecolor='w')
|
||||
assert leg.get_frame().get_alpha() == 1
|
||||
|
||||
|
||||
def test_legend_title_empty():
|
||||
# test that if we don't set the legend title, that
|
||||
# it comes back as an empty string, and that it is not
|
||||
# visible:
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(range(10))
|
||||
leg = ax.legend()
|
||||
assert leg.get_title().get_text() == ""
|
||||
assert leg.get_title().get_visible() is False
|
||||
|
||||
|
||||
def test_legend_proper_window_extent():
|
||||
# test that legend returns the expected extent under various dpi...
|
||||
fig, ax = plt.subplots(dpi=100)
|
||||
ax.plot(range(10), label='Aardvark')
|
||||
leg = ax.legend()
|
||||
x01 = leg.get_window_extent(fig.canvas.get_renderer()).x0
|
||||
|
||||
fig, ax = plt.subplots(dpi=200)
|
||||
ax.plot(range(10), label='Aardvark')
|
||||
leg = ax.legend()
|
||||
x02 = leg.get_window_extent(fig.canvas.get_renderer()).x0
|
||||
assert pytest.approx(x01*2, 0.1) == x02
|
||||
|
||||
|
||||
def test_window_extent_cached_renderer():
|
||||
fig, ax = plt.subplots(dpi=100)
|
||||
ax.plot(range(10), label='Aardvark')
|
||||
leg = ax.legend()
|
||||
leg2 = fig.legend()
|
||||
fig.canvas.draw()
|
||||
# check that get_window_extent will use the cached renderer
|
||||
leg.get_window_extent()
|
||||
leg2.get_window_extent()
|
||||
|
||||
|
||||
def test_legend_title_fontsize():
|
||||
# test the title_fontsize kwarg
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(range(10))
|
||||
leg = ax.legend(title='Aardvark', title_fontsize=22)
|
||||
assert leg.get_title().get_fontsize() == 22
|
||||
|
||||
|
||||
def test_get_set_draggable():
|
||||
legend = plt.legend()
|
||||
assert not legend.get_draggable()
|
||||
legend.set_draggable(True)
|
||||
assert legend.get_draggable()
|
||||
legend.set_draggable(False)
|
||||
assert not legend.get_draggable()
|
||||
|
||||
|
||||
def test_draggable():
|
||||
legend = plt.legend()
|
||||
with pytest.warns(MatplotlibDeprecationWarning):
|
||||
legend.draggable(True)
|
||||
assert legend.get_draggable()
|
||||
with pytest.warns(MatplotlibDeprecationWarning):
|
||||
legend.draggable(False)
|
||||
assert not legend.get_draggable()
|
||||
|
||||
# test toggle
|
||||
with pytest.warns(MatplotlibDeprecationWarning):
|
||||
legend.draggable()
|
||||
assert legend.get_draggable()
|
||||
with pytest.warns(MatplotlibDeprecationWarning):
|
||||
legend.draggable()
|
||||
assert not legend.get_draggable()
|
||||
|
||||
|
||||
def test_alpha_handles():
|
||||
x, n, hh = plt.hist([1, 2, 3], alpha=0.25, label='data', color='red')
|
||||
legend = plt.legend()
|
||||
for lh in legend.legendHandles:
|
||||
lh.set_alpha(1.0)
|
||||
assert lh.get_facecolor()[:-1] == hh[1].get_facecolor()[:-1]
|
||||
assert lh.get_edgecolor()[:-1] == hh[1].get_edgecolor()[:-1]
|
||||
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
Tests specific to the lines module.
|
||||
"""
|
||||
|
||||
from io import BytesIO
|
||||
import itertools
|
||||
import timeit
|
||||
|
||||
from cycler import cycler
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.lines as mlines
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison, check_figures_equal
|
||||
|
||||
|
||||
# Runtimes on a loaded system are inherently flaky. Not so much that a rerun
|
||||
# won't help, hopefully.
|
||||
@pytest.mark.flaky(reruns=3)
|
||||
def test_invisible_Line_rendering():
|
||||
"""
|
||||
Github issue #1256 identified a bug in Line.draw method
|
||||
|
||||
Despite visibility attribute set to False, the draw method was not
|
||||
returning early enough and some pre-rendering code was executed
|
||||
though not necessary.
|
||||
|
||||
Consequence was an excessive draw time for invisible Line instances
|
||||
holding a large number of points (Npts> 10**6)
|
||||
"""
|
||||
# Creates big x and y data:
|
||||
N = 10**7
|
||||
x = np.linspace(0, 1, N)
|
||||
y = np.random.normal(size=N)
|
||||
|
||||
# Create a plot figure:
|
||||
fig = plt.figure()
|
||||
ax = plt.subplot(111)
|
||||
|
||||
# Create a "big" Line instance:
|
||||
l = mlines.Line2D(x, y)
|
||||
l.set_visible(False)
|
||||
# but don't add it to the Axis instance `ax`
|
||||
|
||||
# [here Interactive panning and zooming is pretty responsive]
|
||||
# Time the canvas drawing:
|
||||
t_no_line = min(timeit.repeat(fig.canvas.draw, number=1, repeat=3))
|
||||
# (gives about 25 ms)
|
||||
|
||||
# Add the big invisible Line:
|
||||
ax.add_line(l)
|
||||
|
||||
# [Now interactive panning and zooming is very slow]
|
||||
# Time the canvas drawing:
|
||||
t_unvisible_line = min(timeit.repeat(fig.canvas.draw, number=1, repeat=3))
|
||||
# gives about 290 ms for N = 10**7 pts
|
||||
|
||||
slowdown_factor = (t_unvisible_line/t_no_line)
|
||||
slowdown_threshold = 2 # trying to avoid false positive failures
|
||||
assert slowdown_factor < slowdown_threshold
|
||||
|
||||
|
||||
def test_set_line_coll_dash():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
|
||||
np.random.seed(0)
|
||||
# Testing setting linestyles for line collections.
|
||||
# This should not produce an error.
|
||||
cs = ax.contour(np.random.randn(20, 30), linestyles=[(0, (3, 3))])
|
||||
|
||||
assert True
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['line_dashes'], remove_text=True)
|
||||
def test_line_dashes():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
|
||||
ax.plot(range(10), linestyle=(0, (3, 3)), lw=5)
|
||||
|
||||
|
||||
def test_line_colors():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.plot(range(10), color='none')
|
||||
ax.plot(range(10), color='r')
|
||||
ax.plot(range(10), color='.3')
|
||||
ax.plot(range(10), color=(1, 0, 0, 1))
|
||||
ax.plot(range(10), color=(1, 0, 0))
|
||||
fig.canvas.draw()
|
||||
assert True
|
||||
|
||||
|
||||
def test_linestyle_variants():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
for ls in ["-", "solid", "--", "dashed",
|
||||
"-.", "dashdot", ":", "dotted"]:
|
||||
ax.plot(range(10), linestyle=ls)
|
||||
|
||||
fig.canvas.draw()
|
||||
assert True
|
||||
|
||||
|
||||
def test_valid_linestyles():
|
||||
line = mlines.Line2D([], [])
|
||||
with pytest.raises(ValueError):
|
||||
line.set_linestyle('aardvark')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['drawstyle_variants'], remove_text=True,
|
||||
extensions=["png"])
|
||||
def test_drawstyle_variants():
|
||||
fig, axs = plt.subplots(6)
|
||||
dss = ["default", "steps-mid", "steps-pre", "steps-post", "steps", None]
|
||||
# We want to check that drawstyles are properly handled even for very long
|
||||
# lines (for which the subslice optimization is on); however, we need
|
||||
# to zoom in so that the difference between the drawstyles is actually
|
||||
# visible.
|
||||
for ax, ds in zip(axs.flat, dss):
|
||||
ax.plot(range(2000), drawstyle=ds)
|
||||
ax.set(xlim=(0, 2), ylim=(0, 2))
|
||||
|
||||
|
||||
def test_valid_drawstyles():
|
||||
line = mlines.Line2D([], [])
|
||||
with pytest.raises(ValueError):
|
||||
line.set_drawstyle('foobar')
|
||||
|
||||
|
||||
def test_set_drawstyle():
|
||||
x = np.linspace(0, 2*np.pi, 10)
|
||||
y = np.sin(x)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
line, = ax.plot(x, y)
|
||||
line.set_drawstyle("steps-pre")
|
||||
assert len(line.get_path().vertices) == 2*len(x)-1
|
||||
|
||||
line.set_drawstyle("default")
|
||||
assert len(line.get_path().vertices) == len(x)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['line_collection_dashes'],
|
||||
remove_text=True, style='mpl20')
|
||||
def test_set_line_coll_dash_image():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
|
||||
np.random.seed(0)
|
||||
cs = ax.contour(np.random.randn(20, 30), linestyles=[(0, (3, 3))])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['marker_fill_styles'], remove_text=True,
|
||||
extensions=['png'])
|
||||
def test_marker_fill_styles():
|
||||
colors = itertools.cycle([[0, 0, 1], 'g', '#ff0000', 'c', 'm', 'y',
|
||||
np.array([0, 0, 0])])
|
||||
altcolor = 'lightgreen'
|
||||
|
||||
y = np.array([1, 1])
|
||||
x = np.array([0, 9])
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
for j, marker in enumerate(mlines.Line2D.filled_markers):
|
||||
for i, fs in enumerate(mlines.Line2D.fillStyles):
|
||||
color = next(colors)
|
||||
ax.plot(j * 10 + x, y + i + .5 * (j % 2),
|
||||
marker=marker,
|
||||
markersize=20,
|
||||
markerfacecoloralt=altcolor,
|
||||
fillstyle=fs,
|
||||
label=fs,
|
||||
linewidth=5,
|
||||
color=color,
|
||||
markeredgecolor=color,
|
||||
markeredgewidth=2)
|
||||
|
||||
ax.set_ylim([0, 7.5])
|
||||
ax.set_xlim([-5, 155])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['scaled_lines'], style='default')
|
||||
def test_lw_scaling():
|
||||
th = np.linspace(0, 32)
|
||||
fig, ax = plt.subplots()
|
||||
lins_styles = ['dashed', 'dotted', 'dashdot']
|
||||
cy = cycler(matplotlib.rcParams['axes.prop_cycle'])
|
||||
for j, (ls, sty) in enumerate(zip(lins_styles, cy)):
|
||||
for lw in np.linspace(.5, 10, 10):
|
||||
ax.plot(th, j*np.ones(50) + .1 * lw, linestyle=ls, lw=lw, **sty)
|
||||
|
||||
|
||||
def test_nan_is_sorted():
|
||||
line = mlines.Line2D([], [])
|
||||
assert line._is_sorted(np.array([1, 2, 3]))
|
||||
assert line._is_sorted(np.array([1, np.nan, 3]))
|
||||
assert not line._is_sorted([3, 5] + [np.nan] * 100 + [0, 2])
|
||||
|
||||
|
||||
@check_figures_equal()
|
||||
def test_step_markers(fig_test, fig_ref):
|
||||
fig_test.subplots().step([0, 1], "-o")
|
||||
fig_ref.subplots().plot([0, 0, 1], [0, 1, 1], "-o", markevery=[0, 2])
|
||||
@@ -0,0 +1,28 @@
|
||||
import numpy as np
|
||||
from matplotlib import markers
|
||||
from matplotlib.path import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_markers_valid():
|
||||
marker_style = markers.MarkerStyle()
|
||||
mrk_array = np.array([[-0.5, 0],
|
||||
[0.5, 0]])
|
||||
# Checking this doesn't fail.
|
||||
marker_style.set_marker(mrk_array)
|
||||
|
||||
|
||||
def test_markers_invalid():
|
||||
marker_style = markers.MarkerStyle()
|
||||
mrk_array = np.array([[-0.5, 0, 1, 2, 3]])
|
||||
# Checking this does fail.
|
||||
with pytest.raises(ValueError):
|
||||
marker_style.set_marker(mrk_array)
|
||||
|
||||
|
||||
def test_marker_path():
|
||||
marker_style = markers.MarkerStyle()
|
||||
path = Path([[0, 0], [1, 0]], [Path.MOVETO, Path.LINETO])
|
||||
# Checking this doesn't fail.
|
||||
marker_style.set_marker(path)
|
||||
@@ -0,0 +1,273 @@
|
||||
import io
|
||||
import re
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import matplotlib
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import mathtext
|
||||
|
||||
|
||||
math_tests = [
|
||||
r'$a+b+\dot s+\dot{s}+\ldots$',
|
||||
r'$x \doteq y$',
|
||||
r'\$100.00 $\alpha \_$',
|
||||
r'$\frac{\$100.00}{y}$',
|
||||
r'$x y$',
|
||||
r'$x+y\ x=y\ x<y\ x:y\ x,y\ x@y$',
|
||||
r'$100\%y\ x*y\ x/y x\$y$',
|
||||
r'$x\leftarrow y\ x\forall y\ x-y$',
|
||||
r'$x \sf x \bf x {\cal X} \rm x$',
|
||||
r'$x\ x\,x\;x\quad x\qquad x\!x\hspace{ 0.5 }y$',
|
||||
r'$\{ \rm braces \}$',
|
||||
r'$\left[\left\lfloor\frac{5}{\frac{\left(3\right)}{4}} y\right)\right]$',
|
||||
r'$\left(x\right)$',
|
||||
r'$\sin(x)$',
|
||||
r'$x_2$',
|
||||
r'$x^2$',
|
||||
r'$x^2_y$',
|
||||
r'$x_y^2$',
|
||||
r'$\prod_{i=\alpha_{i+1}}^\infty$',
|
||||
r'$x = \frac{x+\frac{5}{2}}{\frac{y+3}{8}}$',
|
||||
r'$dz/dt = \gamma x^2 + {\rm sin}(2\pi y+\phi)$',
|
||||
r'Foo: $\alpha_{i+1}^j = {\rm sin}(2\pi f_j t_i) e^{-5 t_i/\tau}$',
|
||||
r'$\mathcal{R}\prod_{i=\alpha_{i+1}}^\infty a_i \sin(2 \pi f x_i)$',
|
||||
r'Variable $i$ is good',
|
||||
r'$\Delta_i^j$',
|
||||
r'$\Delta^j_{i+1}$',
|
||||
r'$\ddot{o}\acute{e}\grave{e}\hat{O}\breve{\imath}\tilde{n}\vec{q}$',
|
||||
r"$\arccos((x^i))$",
|
||||
r"$\gamma = \frac{x=\frac{6}{8}}{y} \delta$",
|
||||
r'$\limsup_{x\to\infty}$',
|
||||
r'$\oint^\infty_0$',
|
||||
r"$f'\quad f'''(x)\quad ''/\mathrm{yr}$",
|
||||
r'$\frac{x_2888}{y}$',
|
||||
r"$\sqrt[3]{\frac{X_2}{Y}}=5$",
|
||||
r"$\sqrt[5]{\prod^\frac{x}{2\pi^2}_\infty}$",
|
||||
r"$\sqrt[3]{x}=5$",
|
||||
r'$\frac{X}{\frac{X}{Y}}$',
|
||||
r"$W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = U^{3\beta}_{\delta_1 \rho_1} + \frac{1}{8 \pi 2} \int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 \left[\frac{ U^{2\beta}_{\delta_1 \rho_1} - \alpha^\prime_2U^{1\beta}_{\rho_1 \sigma_2} }{U^{0\beta}_{\rho_1 \sigma_2}}\right]$",
|
||||
r'$\mathcal{H} = \int d \tau \left(\epsilon E^2 + \mu H^2\right)$',
|
||||
r'$\widehat{abc}\widetilde{def}$',
|
||||
'$\\Gamma \\Delta \\Theta \\Lambda \\Xi \\Pi \\Sigma \\Upsilon \\Phi \\Psi \\Omega$',
|
||||
'$\\alpha \\beta \\gamma \\delta \\epsilon \\zeta \\eta \\theta \\iota \\lambda \\mu \\nu \\xi \\pi \\kappa \\rho \\sigma \\tau \\upsilon \\phi \\chi \\psi$',
|
||||
|
||||
# The examples prefixed by 'mmltt' are from the MathML torture test here:
|
||||
# http://www.mozilla.org/projects/mathml/demo/texvsmml.xhtml
|
||||
r'${x}^{2}{y}^{2}$',
|
||||
r'${}_{2}F_{3}$',
|
||||
r'$\frac{x+{y}^{2}}{k+1}$',
|
||||
r'$x+{y}^{\frac{2}{k+1}}$',
|
||||
r'$\frac{a}{b/2}$',
|
||||
r'${a}_{0}+\frac{1}{{a}_{1}+\frac{1}{{a}_{2}+\frac{1}{{a}_{3}+\frac{1}{{a}_{4}}}}}$',
|
||||
r'${a}_{0}+\frac{1}{{a}_{1}+\frac{1}{{a}_{2}+\frac{1}{{a}_{3}+\frac{1}{{a}_{4}}}}}$',
|
||||
r'$\binom{n}{k/2}$',
|
||||
r'$\binom{p}{2}{x}^{2}{y}^{p-2}-\frac{1}{1-x}\frac{1}{1-{x}^{2}}$',
|
||||
r'${x}^{2y}$',
|
||||
r'$\sum _{i=1}^{p}\sum _{j=1}^{q}\sum _{k=1}^{r}{a}_{ij}{b}_{jk}{c}_{ki}$',
|
||||
r'$\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+x}}}}}}}$',
|
||||
r'$\left(\frac{{\partial }^{2}}{\partial {x}^{2}}+\frac{{\partial }^{2}}{\partial {y}^{2}}\right){|\varphi \left(x+iy\right)|}^{2}=0$',
|
||||
r'${2}^{{2}^{{2}^{x}}}$',
|
||||
r'${\int }_{1}^{x}\frac{\mathrm{dt}}{t}$',
|
||||
r'$\int {\int }_{D}\mathrm{dx} \mathrm{dy}$',
|
||||
# mathtex doesn't support array
|
||||
# 'mmltt18' : r'$f\left(x\right)=\left\{\begin{array}{cc}\hfill 1/3\hfill & \text{if_}0\le x\le 1;\hfill \\ \hfill 2/3\hfill & \hfill \text{if_}3\le x\le 4;\hfill \\ \hfill 0\hfill & \text{elsewhere.}\hfill \end{array}$',
|
||||
# mathtex doesn't support stackrel
|
||||
# 'mmltt19' : ur'$\stackrel{\stackrel{k\text{times}}{\ufe37}}{x+...+x}$',
|
||||
r'${y}_{{x}^{2}}$',
|
||||
# mathtex doesn't support the "\text" command
|
||||
# 'mmltt21' : r'$\sum _{p\text{\prime}}f\left(p\right)={\int }_{t>1}f\left(t\right) d\pi \left(t\right)$',
|
||||
# mathtex doesn't support array
|
||||
# 'mmltt23' : r'$\left(\begin{array}{cc}\hfill \left(\begin{array}{cc}\hfill a\hfill & \hfill b\hfill \\ \hfill c\hfill & \hfill d\hfill \end{array}\right)\hfill & \hfill \left(\begin{array}{cc}\hfill e\hfill & \hfill f\hfill \\ \hfill g\hfill & \hfill h\hfill \end{array}\right)\hfill \\ \hfill 0\hfill & \hfill \left(\begin{array}{cc}\hfill i\hfill & \hfill j\hfill \\ \hfill k\hfill & \hfill l\hfill \end{array}\right)\hfill \end{array}\right)$',
|
||||
# mathtex doesn't support array
|
||||
# 'mmltt24' : r'$det|\begin{array}{ccccc}\hfill {c}_{0}\hfill & \hfill {c}_{1}\hfill & \hfill {c}_{2}\hfill & \hfill \dots \hfill & \hfill {c}_{n}\hfill \\ \hfill {c}_{1}\hfill & \hfill {c}_{2}\hfill & \hfill {c}_{3}\hfill & \hfill \dots \hfill & \hfill {c}_{n+1}\hfill \\ \hfill {c}_{2}\hfill & \hfill {c}_{3}\hfill & \hfill {c}_{4}\hfill & \hfill \dots \hfill & \hfill {c}_{n+2}\hfill \\ \hfill \u22ee\hfill & \hfill \u22ee\hfill & \hfill \u22ee\hfill & \hfill \hfill & \hfill \u22ee\hfill \\ \hfill {c}_{n}\hfill & \hfill {c}_{n+1}\hfill & \hfill {c}_{n+2}\hfill & \hfill \dots \hfill & \hfill {c}_{2n}\hfill \end{array}|>0$',
|
||||
r'${y}_{{x}_{2}}$',
|
||||
r'${x}_{92}^{31415}+\pi $',
|
||||
r'${x}_{{y}_{b}^{a}}^{{z}_{c}^{d}}$',
|
||||
r'${y}_{3}^{\prime \prime \prime }$',
|
||||
r"$\left( \xi \left( 1 - \xi \right) \right)$", # Bug 2969451
|
||||
r"$\left(2 \, a=b\right)$", # Sage bug #8125
|
||||
r"$? ! &$", # github issue #466
|
||||
r'$\operatorname{cos} x$', # github issue #553
|
||||
r'$\sum _{\genfrac{}{}{0}{}{0\leq i\leq m}{0<j<n}}P\left(i,j\right)$',
|
||||
r"$\left\Vert a \right\Vert \left\vert b \right\vert \left| a \right| \left\| b\right\| \Vert a \Vert \vert b \vert$",
|
||||
r'$\mathring{A} \stackrel{\circ}{A} \AA$',
|
||||
r'$M \, M \thinspace M \/ M \> M \: M \; M \ M \enspace M \quad M \qquad M \! M$',
|
||||
r'$\Cup$ $\Cap$ $\leftharpoonup$ $\barwedge$ $\rightharpoonup$',
|
||||
r'$\dotplus$ $\doteq$ $\doteqdot$ $\ddots$',
|
||||
r'$xyz^kx_kx^py^{p-2} d_i^jb_jc_kd x^j_i E^0 E^0_u$', # github issue #4873
|
||||
r'${xyz}^k{x}_{k}{x}^{p}{y}^{p-2} {d}_{i}^{j}{b}_{j}{c}_{k}{d} {x}^{j}_{i}{E}^{0}{E}^0_u$',
|
||||
r'${\int}_x^x x\oint_x^x x\int_{X}^{X}x\int_x x \int^x x \int_{x} x\int^{x}{\int}_{x} x{\int}^{x}_{x}x$',
|
||||
r'testing$^{123}$',
|
||||
' '.join('$\\' + p + '$' for p in sorted(mathtext.Parser._snowflake)),
|
||||
r'$6-2$; $-2$; $ -2$; ${-2}$; ${ -2}$; $20^{+3}_{-2}$',
|
||||
r'$\overline{\omega}^x \frac{1}{2}_0^x$', # github issue #5444
|
||||
r'$,$ $.$ $1{,}234{, }567{ , }890$ and $1,234,567,890$', # github issue 5799
|
||||
r'$\left(X\right)_{a}^{b}$', # github issue 7615
|
||||
r'$\dfrac{\$100.00}{y}$', # github issue #1888
|
||||
]
|
||||
|
||||
digits = "0123456789"
|
||||
uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
lowercase = "abcdefghijklmnopqrstuvwxyz"
|
||||
uppergreek = ("\\Gamma \\Delta \\Theta \\Lambda \\Xi \\Pi \\Sigma \\Upsilon \\Phi \\Psi "
|
||||
"\\Omega")
|
||||
lowergreek = ("\\alpha \\beta \\gamma \\delta \\epsilon \\zeta \\eta \\theta \\iota "
|
||||
"\\lambda \\mu \\nu \\xi \\pi \\kappa \\rho \\sigma \\tau \\upsilon "
|
||||
"\\phi \\chi \\psi")
|
||||
all = [digits, uppercase, lowercase, uppergreek, lowergreek]
|
||||
|
||||
font_test_specs = [
|
||||
([], all),
|
||||
(['mathrm'], all),
|
||||
(['mathbf'], all),
|
||||
(['mathit'], all),
|
||||
(['mathtt'], [digits, uppercase, lowercase]),
|
||||
(['mathcircled'], [digits, uppercase, lowercase]),
|
||||
(['mathrm', 'mathcircled'], [digits, uppercase, lowercase]),
|
||||
(['mathbf', 'mathcircled'], [digits, uppercase, lowercase]),
|
||||
(['mathbb'], [digits, uppercase, lowercase,
|
||||
r'\Gamma \Pi \Sigma \gamma \pi']),
|
||||
(['mathrm', 'mathbb'], [digits, uppercase, lowercase,
|
||||
r'\Gamma \Pi \Sigma \gamma \pi']),
|
||||
(['mathbf', 'mathbb'], [digits, uppercase, lowercase,
|
||||
r'\Gamma \Pi \Sigma \gamma \pi']),
|
||||
(['mathcal'], [uppercase]),
|
||||
(['mathfrak'], [uppercase, lowercase]),
|
||||
(['mathbf', 'mathfrak'], [uppercase, lowercase]),
|
||||
(['mathscr'], [uppercase, lowercase]),
|
||||
(['mathsf'], [digits, uppercase, lowercase]),
|
||||
(['mathrm', 'mathsf'], [digits, uppercase, lowercase]),
|
||||
(['mathbf', 'mathsf'], [digits, uppercase, lowercase])
|
||||
]
|
||||
|
||||
font_tests = []
|
||||
for fonts, chars in font_test_specs:
|
||||
wrapper = [' '.join(fonts), ' $']
|
||||
for font in fonts:
|
||||
wrapper.append(r'\%s{' % font)
|
||||
wrapper.append('%s')
|
||||
for font in fonts:
|
||||
wrapper.append('}')
|
||||
wrapper.append('$')
|
||||
wrapper = ''.join(wrapper)
|
||||
|
||||
for set in chars:
|
||||
font_tests.append(wrapper % set)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def baseline_images(request, fontset, index):
|
||||
return ['%s_%s_%02d' % (request.param, fontset, index)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('index, test', enumerate(math_tests),
|
||||
ids=[str(index) for index in range(len(math_tests))])
|
||||
@pytest.mark.parametrize('fontset',
|
||||
['cm', 'stix', 'stixsans', 'dejavusans',
|
||||
'dejavuserif'])
|
||||
@pytest.mark.parametrize('baseline_images', ['mathtext'], indirect=True)
|
||||
@image_comparison(baseline_images=None)
|
||||
def test_mathtext_rendering(baseline_images, fontset, index, test):
|
||||
matplotlib.rcParams['mathtext.fontset'] = fontset
|
||||
fig = plt.figure(figsize=(5.25, 0.75))
|
||||
fig.text(0.5, 0.5, test,
|
||||
horizontalalignment='center', verticalalignment='center')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('index, test', enumerate(font_tests),
|
||||
ids=[str(index) for index in range(len(font_tests))])
|
||||
@pytest.mark.parametrize('fontset',
|
||||
['cm', 'stix', 'stixsans', 'dejavusans',
|
||||
'dejavuserif'])
|
||||
@pytest.mark.parametrize('baseline_images', ['mathfont'], indirect=True)
|
||||
@image_comparison(baseline_images=None, extensions=['png'])
|
||||
def test_mathfont_rendering(baseline_images, fontset, index, test):
|
||||
matplotlib.rcParams['mathtext.fontset'] = fontset
|
||||
fig = plt.figure(figsize=(5.25, 0.75))
|
||||
fig.text(0.5, 0.5, test,
|
||||
horizontalalignment='center', verticalalignment='center')
|
||||
|
||||
|
||||
def test_fontinfo():
|
||||
import matplotlib.font_manager as font_manager
|
||||
import matplotlib.ft2font as ft2font
|
||||
fontpath = font_manager.findfont("DejaVu Sans")
|
||||
font = ft2font.FT2Font(fontpath)
|
||||
table = font.get_sfnt_table("head")
|
||||
assert table['version'] == (1, 0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'math, msg',
|
||||
[
|
||||
(r'$\hspace{}$', r'Expected \hspace{n}'),
|
||||
(r'$\hspace{foo}$', r'Expected \hspace{n}'),
|
||||
(r'$\frac$', r'Expected \frac{num}{den}'),
|
||||
(r'$\frac{}{}$', r'Expected \frac{num}{den}'),
|
||||
(r'$\stackrel$', r'Expected \stackrel{num}{den}'),
|
||||
(r'$\stackrel{}{}$', r'Expected \stackrel{num}{den}'),
|
||||
(r'$\binom$', r'Expected \binom{num}{den}'),
|
||||
(r'$\binom{}{}$', r'Expected \binom{num}{den}'),
|
||||
(r'$\genfrac$',
|
||||
r'Expected \genfrac{ldelim}{rdelim}{rulesize}{style}{num}{den}'),
|
||||
(r'$\genfrac{}{}{}{}{}{}$',
|
||||
r'Expected \genfrac{ldelim}{rdelim}{rulesize}{style}{num}{den}'),
|
||||
(r'$\sqrt$', r'Expected \sqrt{value}'),
|
||||
(r'$\sqrt f$', r'Expected \sqrt{value}'),
|
||||
(r'$\overline$', r'Expected \overline{value}'),
|
||||
(r'$\overline{}$', r'Expected \overline{value}'),
|
||||
(r'$\leftF$', r'Expected a delimiter'),
|
||||
(r'$\rightF$', r'Unknown symbol: \rightF'),
|
||||
(r'$\left(\right$', r'Expected a delimiter'),
|
||||
(r'$\left($', r'Expected "\right"'),
|
||||
(r'$\dfrac$', r'Expected \dfrac{num}{den}'),
|
||||
(r'$\dfrac{}{}$', r'Expected \dfrac{num}{den}'),
|
||||
],
|
||||
ids=[
|
||||
'hspace without value',
|
||||
'hspace with invalid value',
|
||||
'frac without parameters',
|
||||
'frac with empty parameters',
|
||||
'stackrel without parameters',
|
||||
'stackrel with empty parameters',
|
||||
'binom without parameters',
|
||||
'binom with empty parameters',
|
||||
'genfrac without parameters',
|
||||
'genfrac with empty parameters',
|
||||
'sqrt without parameters',
|
||||
'sqrt with invalid value',
|
||||
'overline without parameters',
|
||||
'overline with empty parameter',
|
||||
'left with invalid delimiter',
|
||||
'right with invalid delimiter',
|
||||
'unclosed parentheses with sizing',
|
||||
'unclosed parentheses without sizing',
|
||||
'dfrac without parameters',
|
||||
'dfrac with empty parameters',
|
||||
]
|
||||
)
|
||||
def test_mathtext_exceptions(math, msg):
|
||||
parser = mathtext.MathTextParser('agg')
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
parser.parse(math)
|
||||
excinfo.match(re.escape(msg))
|
||||
|
||||
|
||||
def test_single_minus_sign():
|
||||
plt.figure(figsize=(0.3, 0.3))
|
||||
plt.text(0.5, 0.5, '$-$')
|
||||
for spine in plt.gca().spines.values():
|
||||
spine.set_visible(False)
|
||||
plt.gca().set_xticks([])
|
||||
plt.gca().set_yticks([])
|
||||
|
||||
buff = io.BytesIO()
|
||||
plt.savefig(buff, format="rgba", dpi=1000)
|
||||
array = np.fromstring(buff.getvalue(), dtype=np.uint8)
|
||||
|
||||
# If this fails, it would be all white
|
||||
assert not np.all(array == 0xff)
|
||||
@@ -0,0 +1,21 @@
|
||||
import matplotlib
|
||||
import matplotlib.rcsetup
|
||||
|
||||
|
||||
def test_use_doc_standard_backends():
|
||||
"""
|
||||
Test that the standard backends mentioned in the docstring of
|
||||
matplotlib.use() are the same as in matplotlib.rcsetup.
|
||||
"""
|
||||
def parse(key):
|
||||
backends = []
|
||||
for line in matplotlib.use.__doc__.split(key)[1].split('\n'):
|
||||
if not line.strip():
|
||||
break
|
||||
backends += [e.strip() for e in line.split(',') if e]
|
||||
return backends
|
||||
|
||||
assert (set(parse('- interactive backends:\n')) ==
|
||||
set(matplotlib.rcsetup.interactive_bk))
|
||||
assert (set(parse('- non-interactive backends:\n')) ==
|
||||
set(matplotlib.rcsetup.non_interactive_bk))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,126 @@
|
||||
import pytest
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as mpatches
|
||||
import matplotlib.lines as mlines
|
||||
from matplotlib.offsetbox import (
|
||||
AnchoredOffsetbox, DrawingArea, _get_packed_offsets)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['offsetbox_clipping'], remove_text=True)
|
||||
def test_offsetbox_clipping():
|
||||
# - create a plot
|
||||
# - put an AnchoredOffsetbox with a child DrawingArea
|
||||
# at the center of the axes
|
||||
# - give the DrawingArea a gray background
|
||||
# - put a black line across the bounds of the DrawingArea
|
||||
# - see that the black line is clipped to the edges of
|
||||
# the DrawingArea.
|
||||
fig, ax = plt.subplots()
|
||||
size = 100
|
||||
da = DrawingArea(size, size, clip=True)
|
||||
bg = mpatches.Rectangle((0, 0), size, size,
|
||||
facecolor='#CCCCCC',
|
||||
edgecolor='None',
|
||||
linewidth=0)
|
||||
line = mlines.Line2D([-size*.5, size*1.5], [size/2, size/2],
|
||||
color='black',
|
||||
linewidth=10)
|
||||
anchored_box = AnchoredOffsetbox(
|
||||
loc='center',
|
||||
child=da,
|
||||
pad=0.,
|
||||
frameon=False,
|
||||
bbox_to_anchor=(.5, .5),
|
||||
bbox_transform=ax.transAxes,
|
||||
borderpad=0.)
|
||||
|
||||
da.add_artist(bg)
|
||||
da.add_artist(line)
|
||||
ax.add_artist(anchored_box)
|
||||
ax.set_xlim((0, 1))
|
||||
ax.set_ylim((0, 1))
|
||||
|
||||
|
||||
def test_offsetbox_clip_children():
|
||||
# - create a plot
|
||||
# - put an AnchoredOffsetbox with a child DrawingArea
|
||||
# at the center of the axes
|
||||
# - give the DrawingArea a gray background
|
||||
# - put a black line across the bounds of the DrawingArea
|
||||
# - see that the black line is clipped to the edges of
|
||||
# the DrawingArea.
|
||||
fig, ax = plt.subplots()
|
||||
size = 100
|
||||
da = DrawingArea(size, size, clip=True)
|
||||
bg = mpatches.Rectangle((0, 0), size, size,
|
||||
facecolor='#CCCCCC',
|
||||
edgecolor='None',
|
||||
linewidth=0)
|
||||
line = mlines.Line2D([-size*.5, size*1.5], [size/2, size/2],
|
||||
color='black',
|
||||
linewidth=10)
|
||||
anchored_box = AnchoredOffsetbox(
|
||||
loc='center',
|
||||
child=da,
|
||||
pad=0.,
|
||||
frameon=False,
|
||||
bbox_to_anchor=(.5, .5),
|
||||
bbox_transform=ax.transAxes,
|
||||
borderpad=0.)
|
||||
|
||||
da.add_artist(bg)
|
||||
da.add_artist(line)
|
||||
ax.add_artist(anchored_box)
|
||||
|
||||
fig.canvas.draw()
|
||||
assert not fig.stale
|
||||
da.clip_children = True
|
||||
assert fig.stale
|
||||
|
||||
|
||||
def test_offsetbox_loc_codes():
|
||||
# Check that valid string location codes all work with an AnchoredOffsetbox
|
||||
codes = {'upper right': 1,
|
||||
'upper left': 2,
|
||||
'lower left': 3,
|
||||
'lower right': 4,
|
||||
'right': 5,
|
||||
'center left': 6,
|
||||
'center right': 7,
|
||||
'lower center': 8,
|
||||
'upper center': 9,
|
||||
'center': 10,
|
||||
}
|
||||
fig, ax = plt.subplots()
|
||||
da = DrawingArea(100, 100)
|
||||
for code in codes:
|
||||
anchored_box = AnchoredOffsetbox(loc=code, child=da)
|
||||
ax.add_artist(anchored_box)
|
||||
fig.canvas.draw()
|
||||
|
||||
|
||||
def test_expand_with_tight_layout():
|
||||
# Check issue reported in #10476, and updated due to #10784
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
d1 = [1, 2]
|
||||
d2 = [2, 1]
|
||||
ax.plot(d1, label='series 1')
|
||||
ax.plot(d2, label='series 2')
|
||||
ax.legend(ncol=2, mode='expand')
|
||||
|
||||
fig.tight_layout() # where the crash used to happen
|
||||
|
||||
|
||||
@pytest.mark.parametrize('wd_list',
|
||||
([(150, 1)], [(150, 1)]*3, [(0.1, 1)], [(0.1, 1)]*2))
|
||||
@pytest.mark.parametrize('total', (250, 100, 0, -1, None))
|
||||
@pytest.mark.parametrize('sep', (250, 1, 0, -1))
|
||||
@pytest.mark.parametrize('mode', ("expand", "fixed", "equal"))
|
||||
def test_get_packed_offsets(wd_list, total, sep, mode):
|
||||
# Check a (rather arbitrary) set of parameters due to successive similar
|
||||
# issue tickets (at least #10476 and #10784) related to corner cases
|
||||
# triggered inside this function when calling higher-level functions
|
||||
# (e.g. `Axes.legend`).
|
||||
_get_packed_offsets(wd_list, total, sep, mode=mode)
|
||||
@@ -0,0 +1,470 @@
|
||||
"""
|
||||
Tests specific to the patches module.
|
||||
"""
|
||||
import numpy as np
|
||||
from numpy.testing import assert_almost_equal, assert_array_equal
|
||||
import pytest
|
||||
|
||||
from matplotlib.cbook import MatplotlibDeprecationWarning
|
||||
from matplotlib.patches import Polygon, Rectangle
|
||||
from matplotlib.testing.decorators import image_comparison, check_figures_equal
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import (
|
||||
collections as mcollections, colors as mcolors, patches as mpatches,
|
||||
path as mpath, style as mstyle, transforms as mtransforms)
|
||||
|
||||
import sys
|
||||
on_win = (sys.platform == 'win32')
|
||||
|
||||
|
||||
def test_Polygon_close():
|
||||
#: Github issue #1018 identified a bug in the Polygon handling
|
||||
#: of the closed attribute; the path was not getting closed
|
||||
#: when set_xy was used to set the vertices.
|
||||
|
||||
# open set of vertices:
|
||||
xy = [[0, 0], [0, 1], [1, 1]]
|
||||
# closed set:
|
||||
xyclosed = xy + [[0, 0]]
|
||||
|
||||
# start with open path and close it:
|
||||
p = Polygon(xy, closed=True)
|
||||
assert_array_equal(p.get_xy(), xyclosed)
|
||||
p.set_xy(xy)
|
||||
assert_array_equal(p.get_xy(), xyclosed)
|
||||
|
||||
# start with closed path and open it:
|
||||
p = Polygon(xyclosed, closed=False)
|
||||
assert_array_equal(p.get_xy(), xy)
|
||||
p.set_xy(xyclosed)
|
||||
assert_array_equal(p.get_xy(), xy)
|
||||
|
||||
# start with open path and leave it open:
|
||||
p = Polygon(xy, closed=False)
|
||||
assert_array_equal(p.get_xy(), xy)
|
||||
p.set_xy(xy)
|
||||
assert_array_equal(p.get_xy(), xy)
|
||||
|
||||
# start with closed path and leave it closed:
|
||||
p = Polygon(xyclosed, closed=True)
|
||||
assert_array_equal(p.get_xy(), xyclosed)
|
||||
p.set_xy(xyclosed)
|
||||
assert_array_equal(p.get_xy(), xyclosed)
|
||||
|
||||
|
||||
def test_rotate_rect():
|
||||
loc = np.asarray([1.0, 2.0])
|
||||
width = 2
|
||||
height = 3
|
||||
angle = 30.0
|
||||
|
||||
# A rotated rectangle
|
||||
rect1 = Rectangle(loc, width, height, angle=angle)
|
||||
|
||||
# A non-rotated rectangle
|
||||
rect2 = Rectangle(loc, width, height)
|
||||
|
||||
# Set up an explicit rotation matrix (in radians)
|
||||
angle_rad = np.pi * angle / 180.0
|
||||
rotation_matrix = np.array([[np.cos(angle_rad), -np.sin(angle_rad)],
|
||||
[np.sin(angle_rad), np.cos(angle_rad)]])
|
||||
|
||||
# Translate to origin, rotate each vertex, and then translate back
|
||||
new_verts = np.inner(rotation_matrix, rect2.get_verts() - loc).T + loc
|
||||
|
||||
# They should be the same
|
||||
assert_almost_equal(rect1.get_verts(), new_verts)
|
||||
|
||||
|
||||
def test_negative_rect():
|
||||
# These two rectangles have the same vertices, but starting from a
|
||||
# different point. (We also drop the last vertex, which is a duplicate.)
|
||||
pos_vertices = Rectangle((-3, -2), 3, 2).get_verts()[:-1]
|
||||
neg_vertices = Rectangle((0, 0), -3, -2).get_verts()[:-1]
|
||||
assert_array_equal(np.roll(neg_vertices, 2, 0), pos_vertices)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['clip_to_bbox'])
|
||||
def test_clip_to_bbox():
|
||||
fig = plt.figure()
|
||||
|
||||
ax = fig.add_subplot(111)
|
||||
ax.set_xlim([-18, 20])
|
||||
ax.set_ylim([-150, 100])
|
||||
|
||||
path = mpath.Path.unit_regular_star(8).deepcopy()
|
||||
path.vertices *= [10, 100]
|
||||
path.vertices -= [5, 25]
|
||||
|
||||
path2 = mpath.Path.unit_circle().deepcopy()
|
||||
path2.vertices *= [10, 100]
|
||||
path2.vertices += [10, -25]
|
||||
|
||||
combined = mpath.Path.make_compound_path(path, path2)
|
||||
|
||||
patch = mpatches.PathPatch(
|
||||
combined, alpha=0.5, facecolor='coral', edgecolor='none')
|
||||
ax.add_patch(patch)
|
||||
|
||||
bbox = mtransforms.Bbox([[-12, -77.5], [50, -110]])
|
||||
result_path = combined.clip_to_bbox(bbox)
|
||||
result_patch = mpatches.PathPatch(
|
||||
result_path, alpha=0.5, facecolor='green', lw=4, edgecolor='black')
|
||||
|
||||
ax.add_patch(result_patch)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['patch_alpha_coloring'], remove_text=True)
|
||||
def test_patch_alpha_coloring():
|
||||
"""
|
||||
Test checks that the patch and collection are rendered with the specified
|
||||
alpha values in their facecolor and edgecolor.
|
||||
"""
|
||||
star = mpath.Path.unit_regular_star(6)
|
||||
circle = mpath.Path.unit_circle()
|
||||
# concatenate the star with an internal cutout of the circle
|
||||
verts = np.concatenate([circle.vertices, star.vertices[::-1]])
|
||||
codes = np.concatenate([circle.codes, star.codes])
|
||||
cut_star1 = mpath.Path(verts, codes)
|
||||
cut_star2 = mpath.Path(verts + 1, codes)
|
||||
|
||||
ax = plt.axes()
|
||||
patch = mpatches.PathPatch(cut_star1,
|
||||
linewidth=5, linestyle='dashdot',
|
||||
facecolor=(1, 0, 0, 0.5),
|
||||
edgecolor=(0, 0, 1, 0.75))
|
||||
ax.add_patch(patch)
|
||||
|
||||
col = mcollections.PathCollection([cut_star2],
|
||||
linewidth=5, linestyles='dashdot',
|
||||
facecolor=(1, 0, 0, 0.5),
|
||||
edgecolor=(0, 0, 1, 0.75))
|
||||
ax.add_collection(col)
|
||||
|
||||
ax.set_xlim([-1, 2])
|
||||
ax.set_ylim([-1, 2])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['patch_alpha_override'], remove_text=True)
|
||||
def test_patch_alpha_override():
|
||||
#: Test checks that specifying an alpha attribute for a patch or
|
||||
#: collection will override any alpha component of the facecolor
|
||||
#: or edgecolor.
|
||||
star = mpath.Path.unit_regular_star(6)
|
||||
circle = mpath.Path.unit_circle()
|
||||
# concatenate the star with an internal cutout of the circle
|
||||
verts = np.concatenate([circle.vertices, star.vertices[::-1]])
|
||||
codes = np.concatenate([circle.codes, star.codes])
|
||||
cut_star1 = mpath.Path(verts, codes)
|
||||
cut_star2 = mpath.Path(verts + 1, codes)
|
||||
|
||||
ax = plt.axes()
|
||||
patch = mpatches.PathPatch(cut_star1,
|
||||
linewidth=5, linestyle='dashdot',
|
||||
alpha=0.25,
|
||||
facecolor=(1, 0, 0, 0.5),
|
||||
edgecolor=(0, 0, 1, 0.75))
|
||||
ax.add_patch(patch)
|
||||
|
||||
col = mcollections.PathCollection([cut_star2],
|
||||
linewidth=5, linestyles='dashdot',
|
||||
alpha=0.25,
|
||||
facecolor=(1, 0, 0, 0.5),
|
||||
edgecolor=(0, 0, 1, 0.75))
|
||||
ax.add_collection(col)
|
||||
|
||||
ax.set_xlim([-1, 2])
|
||||
ax.set_ylim([-1, 2])
|
||||
|
||||
|
||||
@pytest.mark.style('default')
|
||||
def test_patch_color_none():
|
||||
# Make sure the alpha kwarg does not override 'none' facecolor.
|
||||
# Addresses issue #7478.
|
||||
c = plt.Circle((0, 0), 1, facecolor='none', alpha=1)
|
||||
assert c.get_facecolor()[0] == 0
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['patch_custom_linestyle'],
|
||||
remove_text=True)
|
||||
def test_patch_custom_linestyle():
|
||||
#: A test to check that patches and collections accept custom dash
|
||||
#: patterns as linestyle and that they display correctly.
|
||||
star = mpath.Path.unit_regular_star(6)
|
||||
circle = mpath.Path.unit_circle()
|
||||
# concatenate the star with an internal cutout of the circle
|
||||
verts = np.concatenate([circle.vertices, star.vertices[::-1]])
|
||||
codes = np.concatenate([circle.codes, star.codes])
|
||||
cut_star1 = mpath.Path(verts, codes)
|
||||
cut_star2 = mpath.Path(verts + 1, codes)
|
||||
|
||||
ax = plt.axes()
|
||||
patch = mpatches.PathPatch(cut_star1,
|
||||
linewidth=5, linestyle=(0.0, (5.0, 7.0, 10.0, 7.0)),
|
||||
facecolor=(1, 0, 0),
|
||||
edgecolor=(0, 0, 1))
|
||||
ax.add_patch(patch)
|
||||
|
||||
col = mcollections.PathCollection([cut_star2],
|
||||
linewidth=5, linestyles=[(0.0, (5.0, 7.0, 10.0, 7.0))],
|
||||
facecolor=(1, 0, 0),
|
||||
edgecolor=(0, 0, 1))
|
||||
ax.add_collection(col)
|
||||
|
||||
ax.set_xlim([-1, 2])
|
||||
ax.set_ylim([-1, 2])
|
||||
|
||||
|
||||
def test_patch_linestyle_accents():
|
||||
#: Test if linestyle can also be specified with short menoics
|
||||
#: like "--"
|
||||
#: c.f. Gihub issue #2136
|
||||
star = mpath.Path.unit_regular_star(6)
|
||||
circle = mpath.Path.unit_circle()
|
||||
# concatenate the star with an internal cutout of the circle
|
||||
verts = np.concatenate([circle.vertices, star.vertices[::-1]])
|
||||
codes = np.concatenate([circle.codes, star.codes])
|
||||
|
||||
linestyles = ["-", "--", "-.", ":",
|
||||
"solid", "dashed", "dashdot", "dotted"]
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
for i, ls in enumerate(linestyles):
|
||||
star = mpath.Path(verts + i, codes)
|
||||
patch = mpatches.PathPatch(star,
|
||||
linewidth=3, linestyle=ls,
|
||||
facecolor=(1, 0, 0),
|
||||
edgecolor=(0, 0, 1))
|
||||
ax.add_patch(patch)
|
||||
|
||||
ax.set_xlim([-1, i + 1])
|
||||
ax.set_ylim([-1, i + 1])
|
||||
fig.canvas.draw()
|
||||
assert True
|
||||
|
||||
|
||||
def test_wedge_movement():
|
||||
param_dict = {'center': ((0, 0), (1, 1), 'set_center'),
|
||||
'r': (5, 8, 'set_radius'),
|
||||
'width': (2, 3, 'set_width'),
|
||||
'theta1': (0, 30, 'set_theta1'),
|
||||
'theta2': (45, 50, 'set_theta2')}
|
||||
|
||||
init_args = {k: v[0] for k, v in param_dict.items()}
|
||||
|
||||
w = mpatches.Wedge(**init_args)
|
||||
for attr, (old_v, new_v, func) in param_dict.items():
|
||||
assert getattr(w, attr) == old_v
|
||||
getattr(w, func)(new_v)
|
||||
assert getattr(w, attr) == new_v
|
||||
|
||||
|
||||
# png needs tol>=0.06, pdf tol>=1.617
|
||||
@image_comparison(baseline_images=['wedge_range'],
|
||||
remove_text=True, tol=1.65 if on_win else 0)
|
||||
def test_wedge_range():
|
||||
ax = plt.axes()
|
||||
|
||||
t1 = 2.313869244286224
|
||||
|
||||
args = [[52.31386924, 232.31386924],
|
||||
[52.313869244286224, 232.31386924428622],
|
||||
[t1, t1 + 180.0],
|
||||
[0, 360],
|
||||
[90, 90 + 360],
|
||||
[-180, 180],
|
||||
[0, 380],
|
||||
[45, 46],
|
||||
[46, 45]]
|
||||
|
||||
for i, (theta1, theta2) in enumerate(args):
|
||||
x = i % 3
|
||||
y = i // 3
|
||||
|
||||
wedge = mpatches.Wedge((x * 3, y * 3), 1, theta1, theta2,
|
||||
facecolor='none', edgecolor='k', lw=3)
|
||||
|
||||
ax.add_artist(wedge)
|
||||
|
||||
ax.set_xlim([-2, 8])
|
||||
ax.set_ylim([-2, 9])
|
||||
|
||||
|
||||
def test_patch_str():
|
||||
"""
|
||||
Check that patches have nice and working `str` representation.
|
||||
|
||||
Note that the logic is that `__str__` is defined such that:
|
||||
str(eval(str(p))) == str(p)
|
||||
"""
|
||||
p = mpatches.Circle(xy=(1, 2), radius=3)
|
||||
assert str(p) == 'Circle(xy=(1, 2), radius=3)'
|
||||
|
||||
p = mpatches.Ellipse(xy=(1, 2), width=3, height=4, angle=5)
|
||||
assert str(p) == 'Ellipse(xy=(1, 2), width=3, height=4, angle=5)'
|
||||
|
||||
p = mpatches.Rectangle(xy=(1, 2), width=3, height=4, angle=5)
|
||||
assert str(p) == 'Rectangle(xy=(1, 2), width=3, height=4, angle=5)'
|
||||
|
||||
p = mpatches.Wedge(center=(1, 2), r=3, theta1=4, theta2=5, width=6)
|
||||
assert str(p) == 'Wedge(center=(1, 2), r=3, theta1=4, theta2=5, width=6)'
|
||||
|
||||
p = mpatches.Arc(xy=(1, 2), width=3, height=4, angle=5, theta1=6, theta2=7)
|
||||
expected = 'Arc(xy=(1, 2), width=3, height=4, angle=5, theta1=6, theta2=7)'
|
||||
assert str(p) == expected
|
||||
|
||||
p = mpatches.RegularPolygon((1, 2), 20, radius=5)
|
||||
assert str(p) == "RegularPolygon((1, 2), 20, radius=5, orientation=0)"
|
||||
|
||||
p = mpatches.CirclePolygon(xy=(1, 2), radius=5, resolution=20)
|
||||
assert str(p) == "CirclePolygon((1, 2), radius=5, resolution=20)"
|
||||
|
||||
p = mpatches.FancyBboxPatch((1, 2), width=3, height=4)
|
||||
assert str(p) == "FancyBboxPatch((1, 2), width=3, height=4)"
|
||||
|
||||
# Further nice __str__ which cannot be `eval`uated:
|
||||
path_data = [([1, 2], mpath.Path.MOVETO), ([2, 2], mpath.Path.LINETO),
|
||||
([1, 2], mpath.Path.CLOSEPOLY)]
|
||||
p = mpatches.PathPatch(mpath.Path(*zip(*path_data)))
|
||||
assert str(p) == "PathPatch3((1, 2) ...)"
|
||||
|
||||
data = [[1, 2], [2, 2], [1, 2]]
|
||||
p = mpatches.Polygon(data)
|
||||
assert str(p) == "Polygon3((1, 2) ...)"
|
||||
|
||||
p = mpatches.FancyArrowPatch(path=mpath.Path(*zip(*path_data)))
|
||||
assert str(p)[:27] == "FancyArrowPatch(Path(array("
|
||||
|
||||
p = mpatches.FancyArrowPatch((1, 2), (3, 4))
|
||||
assert str(p) == "FancyArrowPatch((1, 2)->(3, 4))"
|
||||
|
||||
p = mpatches.ConnectionPatch((1, 2), (3, 4), 'data')
|
||||
assert str(p) == "ConnectionPatch((1, 2), (3, 4))"
|
||||
|
||||
s = mpatches.Shadow(p, 1, 1)
|
||||
assert str(s) == "Shadow(ConnectionPatch((1, 2), (3, 4)))"
|
||||
|
||||
with pytest.warns(MatplotlibDeprecationWarning):
|
||||
p = mpatches.YAArrow(plt.gcf(), (1, 0), (2, 1), width=0.1)
|
||||
assert str(p) == "YAArrow()"
|
||||
|
||||
# Not testing Arrow, FancyArrow here
|
||||
# because they seem to exist only for historical reasons.
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['multi_color_hatch'],
|
||||
remove_text=True, style='default')
|
||||
def test_multi_color_hatch():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
rects = ax.bar(range(5), range(1, 6))
|
||||
for i, rect in enumerate(rects):
|
||||
rect.set_facecolor('none')
|
||||
rect.set_edgecolor('C{}'.format(i))
|
||||
rect.set_hatch('/')
|
||||
|
||||
for i in range(5):
|
||||
with mstyle.context({'hatch.color': 'C{}'.format(i)}):
|
||||
r = Rectangle((i - .8 / 2, 5), .8, 1, hatch='//', fc='none')
|
||||
ax.add_patch(r)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['units_rectangle'], extensions=['png'])
|
||||
def test_units_rectangle():
|
||||
import matplotlib.testing.jpl_units as U
|
||||
U.register()
|
||||
|
||||
p = mpatches.Rectangle((5*U.km, 6*U.km), 1*U.km, 2*U.km)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.add_patch(p)
|
||||
ax.set_xlim([4*U.km, 7*U.km])
|
||||
ax.set_ylim([5*U.km, 9*U.km])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['connection_patch'], extensions=['png'],
|
||||
style='mpl20', remove_text=True)
|
||||
def test_connection_patch():
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2)
|
||||
|
||||
con = mpatches.ConnectionPatch(xyA=(0.1, 0.1), xyB=(0.9, 0.9),
|
||||
coordsA='data', coordsB='data',
|
||||
axesA=ax2, axesB=ax1,
|
||||
arrowstyle="->")
|
||||
ax2.add_artist(con)
|
||||
|
||||
|
||||
def test_datetime_rectangle():
|
||||
# Check that creating a rectangle with timedeltas doesn't fail
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
start = datetime(2017, 1, 1, 0, 0, 0)
|
||||
delta = timedelta(seconds=16)
|
||||
patch = mpatches.Rectangle((start, 0), delta, 1)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.add_patch(patch)
|
||||
|
||||
|
||||
def test_datetime_datetime_fails():
|
||||
from datetime import datetime
|
||||
|
||||
start = datetime(2017, 1, 1, 0, 0, 0)
|
||||
dt_delta = datetime(1970, 1, 5) # Will be 5 days if units are done wrong
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
mpatches.Rectangle((start, 0), dt_delta, 1)
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
mpatches.Rectangle((0, start), 1, dt_delta)
|
||||
|
||||
|
||||
def test_contains_point():
|
||||
ell = mpatches.Ellipse((0.5, 0.5), 0.5, 1.0, 0)
|
||||
points = [(0.0, 0.5), (0.2, 0.5), (0.25, 0.5), (0.5, 0.5)]
|
||||
path = ell.get_path()
|
||||
transform = ell.get_transform()
|
||||
radius = ell._process_radius(None)
|
||||
expected = np.array([path.contains_point(point,
|
||||
transform,
|
||||
radius) for point in points])
|
||||
result = np.array([ell.contains_point(point) for point in points])
|
||||
assert np.all(result == expected)
|
||||
|
||||
|
||||
def test_contains_points():
|
||||
ell = mpatches.Ellipse((0.5, 0.5), 0.5, 1.0, 0)
|
||||
points = [(0.0, 0.5), (0.2, 0.5), (0.25, 0.5), (0.5, 0.5)]
|
||||
path = ell.get_path()
|
||||
transform = ell.get_transform()
|
||||
radius = ell._process_radius(None)
|
||||
expected = path.contains_points(points, transform, radius)
|
||||
result = ell.contains_points(points)
|
||||
assert np.all(result == expected)
|
||||
|
||||
|
||||
# Currently fails with pdf/svg, probably because some parts assume a dpi of 72.
|
||||
@check_figures_equal(extensions=["png"])
|
||||
def test_shadow(fig_test, fig_ref):
|
||||
xy = np.array([.2, .3])
|
||||
dxy = np.array([.1, .2])
|
||||
# We need to work around the nonsensical (dpi-dependent) interpretation of
|
||||
# offsets by the Shadow class...
|
||||
plt.rcParams["savefig.dpi"] = "figure"
|
||||
# Test image.
|
||||
a1 = fig_test.subplots()
|
||||
rect = mpatches.Rectangle(xy=xy, width=.5, height=.5)
|
||||
shadow = mpatches.Shadow(rect, ox=dxy[0], oy=dxy[1])
|
||||
a1.add_patch(rect)
|
||||
a1.add_patch(shadow)
|
||||
# Reference image.
|
||||
a2 = fig_ref.subplots()
|
||||
rect = mpatches.Rectangle(xy=xy, width=.5, height=.5)
|
||||
shadow = mpatches.Rectangle(
|
||||
xy=xy + fig_ref.dpi / 72 * dxy, width=.5, height=.5,
|
||||
fc=np.asarray(mcolors.to_rgb(rect.get_fc())) * .3,
|
||||
ec=np.asarray(mcolors.to_rgb(rect.get_fc())) * .3,
|
||||
alpha=.5)
|
||||
a2.add_patch(shadow)
|
||||
a2.add_patch(rect)
|
||||
@@ -0,0 +1,243 @@
|
||||
import copy
|
||||
|
||||
import numpy as np
|
||||
|
||||
from numpy.testing import assert_array_equal
|
||||
import pytest
|
||||
|
||||
from matplotlib.path import Path
|
||||
from matplotlib.patches import Polygon
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import transforms
|
||||
|
||||
|
||||
def test_empty_closed_path():
|
||||
path = Path(np.zeros((0, 2)), closed=True)
|
||||
assert path.vertices.shape == (0, 2)
|
||||
assert path.codes is None
|
||||
|
||||
|
||||
def test_readonly_path():
|
||||
path = Path.unit_circle()
|
||||
|
||||
def modify_vertices():
|
||||
path.vertices = path.vertices * 2.0
|
||||
|
||||
with pytest.raises(AttributeError):
|
||||
modify_vertices()
|
||||
|
||||
|
||||
def test_point_in_path():
|
||||
# Test #1787
|
||||
verts2 = [(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]
|
||||
|
||||
path = Path(verts2, closed=True)
|
||||
points = [(0.5, 0.5), (1.5, 0.5)]
|
||||
ret = path.contains_points(points)
|
||||
assert ret.dtype == 'bool'
|
||||
assert np.all(ret == [True, False])
|
||||
|
||||
|
||||
def test_contains_points_negative_radius():
|
||||
path = Path.unit_circle()
|
||||
|
||||
points = [(0.0, 0.0), (1.25, 0.0), (0.9, 0.9)]
|
||||
expected = [True, False, False]
|
||||
result = path.contains_points(points, radius=-0.5)
|
||||
|
||||
assert np.all(result == expected)
|
||||
|
||||
|
||||
def test_point_in_path_nan():
|
||||
box = np.array([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])
|
||||
p = Path(box)
|
||||
test = np.array([[np.nan, 0.5]])
|
||||
contains = p.contains_points(test)
|
||||
assert len(contains) == 1
|
||||
assert not contains[0]
|
||||
|
||||
|
||||
def test_nonlinear_containment():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set(xscale="log", ylim=(0, 1))
|
||||
polygon = ax.axvspan(1, 10)
|
||||
assert polygon.get_path().contains_point(
|
||||
ax.transData.transform_point((5, .5)), ax.transData)
|
||||
assert not polygon.get_path().contains_point(
|
||||
ax.transData.transform_point((.5, .5)), ax.transData)
|
||||
assert not polygon.get_path().contains_point(
|
||||
ax.transData.transform_point((50, .5)), ax.transData)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['path_clipping'],
|
||||
extensions=['svg'], remove_text=True)
|
||||
def test_path_clipping():
|
||||
fig = plt.figure(figsize=(6.0, 6.2))
|
||||
|
||||
for i, xy in enumerate([
|
||||
[(200, 200), (200, 350), (400, 350), (400, 200)],
|
||||
[(200, 200), (200, 350), (400, 350), (400, 100)],
|
||||
[(200, 100), (200, 350), (400, 350), (400, 100)],
|
||||
[(200, 100), (200, 415), (400, 350), (400, 100)],
|
||||
[(200, 100), (200, 415), (400, 415), (400, 100)],
|
||||
[(200, 415), (400, 415), (400, 100), (200, 100)],
|
||||
[(400, 415), (400, 100), (200, 100), (200, 415)]]):
|
||||
ax = fig.add_subplot(4, 2, i+1)
|
||||
bbox = [0, 140, 640, 260]
|
||||
ax.set_xlim(bbox[0], bbox[0] + bbox[2])
|
||||
ax.set_ylim(bbox[1], bbox[1] + bbox[3])
|
||||
ax.add_patch(Polygon(
|
||||
xy, facecolor='none', edgecolor='red', closed=True))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['semi_log_with_zero'], extensions=['png'],
|
||||
style='mpl20')
|
||||
def test_log_transform_with_zero():
|
||||
x = np.arange(-10, 10)
|
||||
y = (1.0 - 1.0/(x**2+1))**20
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.semilogy(x, y, "-o", lw=15, markeredgecolor='k')
|
||||
ax.set_ylim(1e-7, 1)
|
||||
ax.grid(True)
|
||||
|
||||
|
||||
def test_make_compound_path_empty():
|
||||
# We should be able to make a compound path with no arguments.
|
||||
# This makes it easier to write generic path based code.
|
||||
r = Path.make_compound_path()
|
||||
assert r.vertices.shape == (0, 2)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['xkcd'], extensions=['png'],
|
||||
remove_text=True)
|
||||
def test_xkcd():
|
||||
np.random.seed(0)
|
||||
|
||||
x = np.linspace(0, 2 * np.pi, 100)
|
||||
y = np.sin(x)
|
||||
|
||||
with plt.xkcd():
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['xkcd_marker'], extensions=['png'],
|
||||
remove_text=True)
|
||||
def test_xkcd_marker():
|
||||
np.random.seed(0)
|
||||
|
||||
x = np.linspace(0, 5, 8)
|
||||
y1 = x
|
||||
y2 = 5 - x
|
||||
y3 = 2.5 * np.ones(8)
|
||||
|
||||
with plt.xkcd():
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y1, '+', ms=10)
|
||||
ax.plot(x, y2, 'o', ms=10)
|
||||
ax.plot(x, y3, '^', ms=10)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['marker_paths'], extensions=['pdf'],
|
||||
remove_text=True)
|
||||
def test_marker_paths_pdf():
|
||||
N = 7
|
||||
|
||||
plt.errorbar(np.arange(N),
|
||||
np.ones(N) + 4,
|
||||
np.ones(N))
|
||||
plt.xlim(-1, N)
|
||||
plt.ylim(-1, 7)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['nan_path'], style='default',
|
||||
remove_text=True, extensions=['pdf', 'svg', 'eps', 'png'])
|
||||
def test_nan_isolated_points():
|
||||
|
||||
y0 = [0, np.nan, 2, np.nan, 4, 5, 6]
|
||||
y1 = [np.nan, 7, np.nan, 9, 10, np.nan, 12]
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
ax.plot(y0, '-o')
|
||||
ax.plot(y1, '-o')
|
||||
|
||||
|
||||
def test_path_no_doubled_point_in_to_polygon():
|
||||
hand = np.array(
|
||||
[[1.64516129, 1.16145833],
|
||||
[1.64516129, 1.59375],
|
||||
[1.35080645, 1.921875],
|
||||
[1.375, 2.18229167],
|
||||
[1.68548387, 1.9375],
|
||||
[1.60887097, 2.55208333],
|
||||
[1.68548387, 2.69791667],
|
||||
[1.76209677, 2.56770833],
|
||||
[1.83064516, 1.97395833],
|
||||
[1.89516129, 2.75],
|
||||
[1.9516129, 2.84895833],
|
||||
[2.01209677, 2.76041667],
|
||||
[1.99193548, 1.99479167],
|
||||
[2.11290323, 2.63020833],
|
||||
[2.2016129, 2.734375],
|
||||
[2.25403226, 2.60416667],
|
||||
[2.14919355, 1.953125],
|
||||
[2.30645161, 2.36979167],
|
||||
[2.39112903, 2.36979167],
|
||||
[2.41532258, 2.1875],
|
||||
[2.1733871, 1.703125],
|
||||
[2.07782258, 1.16666667]])
|
||||
|
||||
(r0, c0, r1, c1) = (1.0, 1.5, 2.1, 2.5)
|
||||
|
||||
poly = Path(np.vstack((hand[:, 1], hand[:, 0])).T, closed=True)
|
||||
clip_rect = transforms.Bbox([[r0, c0], [r1, c1]])
|
||||
poly_clipped = poly.clip_to_bbox(clip_rect).to_polygons()[0]
|
||||
|
||||
assert np.all(poly_clipped[-2] != poly_clipped[-1])
|
||||
assert np.all(poly_clipped[-1] == poly_clipped[0])
|
||||
|
||||
|
||||
def test_path_to_polygons():
|
||||
data = [[10, 10], [20, 20]]
|
||||
p = Path(data)
|
||||
|
||||
assert_array_equal(p.to_polygons(width=40, height=40), [])
|
||||
assert_array_equal(p.to_polygons(width=40, height=40, closed_only=False),
|
||||
[data])
|
||||
assert_array_equal(p.to_polygons(), [])
|
||||
assert_array_equal(p.to_polygons(closed_only=False), [data])
|
||||
|
||||
data = [[10, 10], [20, 20], [30, 30]]
|
||||
closed_data = [[10, 10], [20, 20], [30, 30], [10, 10]]
|
||||
p = Path(data)
|
||||
|
||||
assert_array_equal(p.to_polygons(width=40, height=40), [closed_data])
|
||||
assert_array_equal(p.to_polygons(width=40, height=40, closed_only=False),
|
||||
[data])
|
||||
assert_array_equal(p.to_polygons(), [closed_data])
|
||||
assert_array_equal(p.to_polygons(closed_only=False), [data])
|
||||
|
||||
|
||||
def test_path_deepcopy():
|
||||
# Should not raise any error
|
||||
verts = [[0, 0], [1, 1]]
|
||||
codes = [Path.MOVETO, Path.LINETO]
|
||||
path1 = Path(verts)
|
||||
path2 = Path(verts, codes)
|
||||
copy.deepcopy(path1)
|
||||
copy.deepcopy(path2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('offset', range(-720, 361, 45))
|
||||
def test_full_arc(offset):
|
||||
low = offset
|
||||
high = 360 + offset
|
||||
|
||||
path = Path.arc(low, high)
|
||||
mins = np.min(path.vertices, axis=0)
|
||||
maxs = np.max(path.vertices, axis=0)
|
||||
np.testing.assert_allclose(mins, -1)
|
||||
assert np.allclose(maxs, 1)
|
||||
@@ -0,0 +1,138 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patheffects as path_effects
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['patheffect1'], remove_text=True)
|
||||
def test_patheffect1():
|
||||
ax1 = plt.subplot(111)
|
||||
ax1.imshow([[1, 2], [2, 3]])
|
||||
txt = ax1.annotate("test", (1., 1.), (0., 0),
|
||||
arrowprops=dict(arrowstyle="->",
|
||||
connectionstyle="angle3", lw=2),
|
||||
size=20, ha="center",
|
||||
path_effects=[path_effects.withStroke(linewidth=3,
|
||||
foreground="w")])
|
||||
txt.arrow_patch.set_path_effects([path_effects.Stroke(linewidth=5,
|
||||
foreground="w"),
|
||||
path_effects.Normal()])
|
||||
|
||||
pe = [path_effects.withStroke(linewidth=3, foreground="w")]
|
||||
ax1.grid(True, linestyle="-", path_effects=pe)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['patheffect2'], remove_text=True,
|
||||
style='mpl20')
|
||||
def test_patheffect2():
|
||||
|
||||
ax2 = plt.subplot(111)
|
||||
arr = np.arange(25).reshape((5, 5))
|
||||
ax2.imshow(arr)
|
||||
cntr = ax2.contour(arr, colors="k")
|
||||
|
||||
plt.setp(cntr.collections,
|
||||
path_effects=[path_effects.withStroke(linewidth=3,
|
||||
foreground="w")])
|
||||
|
||||
clbls = ax2.clabel(cntr, fmt="%2.0f", use_clabeltext=True)
|
||||
plt.setp(clbls,
|
||||
path_effects=[path_effects.withStroke(linewidth=3,
|
||||
foreground="w")])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['patheffect3'])
|
||||
def test_patheffect3():
|
||||
p1, = plt.plot([1, 3, 5, 4, 3], 'o-b', lw=4)
|
||||
p1.set_path_effects([path_effects.SimpleLineShadow(),
|
||||
path_effects.Normal()])
|
||||
plt.title(r'testing$^{123}$',
|
||||
path_effects=[path_effects.withStroke(linewidth=1, foreground="r")])
|
||||
leg = plt.legend([p1], [r'Line 1$^2$'], fancybox=True, loc='upper left')
|
||||
leg.legendPatch.set_path_effects([path_effects.withSimplePatchShadow()])
|
||||
|
||||
text = plt.text(2, 3, 'Drop test', color='white',
|
||||
bbox={'boxstyle': 'circle,pad=0.1', 'color': 'red'})
|
||||
pe = [path_effects.Stroke(linewidth=3.75, foreground='k'),
|
||||
path_effects.withSimplePatchShadow((6, -3), shadow_rgbFace='blue')]
|
||||
text.set_path_effects(pe)
|
||||
text.get_bbox_patch().set_path_effects(pe)
|
||||
|
||||
pe = [path_effects.PathPatchEffect(offset=(4, -4), hatch='xxxx',
|
||||
facecolor='gray'),
|
||||
path_effects.PathPatchEffect(edgecolor='white', facecolor='black',
|
||||
lw=1.1)]
|
||||
|
||||
t = plt.gcf().text(0.02, 0.1, 'Hatch shadow', fontsize=75, weight=1000,
|
||||
va='center')
|
||||
t.set_path_effects(pe)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['stroked_text'], extensions=['png'])
|
||||
def test_patheffects_stroked_text():
|
||||
text_chunks = [
|
||||
'A B C D E F G H I J K L',
|
||||
'M N O P Q R S T U V W',
|
||||
'X Y Z a b c d e f g h i j',
|
||||
'k l m n o p q r s t u v',
|
||||
'w x y z 0123456789',
|
||||
r"!@#$%^&*()-=_+[]\;'",
|
||||
',./{}|:"<>?'
|
||||
]
|
||||
font_size = 50
|
||||
|
||||
ax = plt.axes([0, 0, 1, 1])
|
||||
for i, chunk in enumerate(text_chunks):
|
||||
text = ax.text(x=0.01, y=(0.9 - i * 0.13), s=chunk,
|
||||
fontdict={'ha': 'left', 'va': 'center',
|
||||
'size': font_size, 'color': 'white'})
|
||||
|
||||
text.set_path_effects([path_effects.Stroke(linewidth=font_size / 10,
|
||||
foreground='black'),
|
||||
path_effects.Normal()])
|
||||
|
||||
ax.set_xlim(0, 1)
|
||||
ax.set_ylim(0, 1)
|
||||
ax.axis('off')
|
||||
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_PathEffect_points_to_pixels():
|
||||
fig = plt.figure(dpi=150)
|
||||
p1, = plt.plot(range(10))
|
||||
p1.set_path_effects([path_effects.SimpleLineShadow(),
|
||||
path_effects.Normal()])
|
||||
|
||||
renderer = fig.canvas.get_renderer()
|
||||
pe_renderer = path_effects.SimpleLineShadow().get_proxy_renderer(renderer)
|
||||
|
||||
assert isinstance(pe_renderer, path_effects.PathEffectRenderer)
|
||||
# Confirm that using a path effects renderer maintains point sizes
|
||||
# appropriately. Otherwise rendered font would be the wrong size.
|
||||
assert renderer.points_to_pixels(15) == pe_renderer.points_to_pixels(15)
|
||||
|
||||
|
||||
def test_SimplePatchShadow_offset():
|
||||
pe = path_effects.SimplePatchShadow(offset=(4, 5))
|
||||
assert pe._offset == (4, 5)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['collection'], tol=0.02, style='mpl20')
|
||||
def test_collection():
|
||||
x, y = np.meshgrid(np.linspace(0, 10, 150), np.linspace(-5, 5, 100))
|
||||
data = np.sin(x) + np.cos(y)
|
||||
cs = plt.contour(data)
|
||||
pe = [path_effects.PathPatchEffect(edgecolor='black', facecolor='none',
|
||||
linewidth=12),
|
||||
path_effects.Stroke(linewidth=5)]
|
||||
|
||||
for collection in cs.collections:
|
||||
collection.set_path_effects(pe)
|
||||
|
||||
for text in plt.clabel(cs, colors='white'):
|
||||
text.set_path_effects([path_effects.withStroke(foreground='k',
|
||||
linewidth=3)])
|
||||
text.set_bbox({'boxstyle': 'sawtooth', 'facecolor': 'none',
|
||||
'edgecolor': 'blue'})
|
||||
@@ -0,0 +1,196 @@
|
||||
import pickle
|
||||
import platform
|
||||
from io import BytesIO
|
||||
|
||||
import numpy as np
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
from matplotlib.dates import rrulewrapper
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.transforms as mtransforms
|
||||
|
||||
try: # https://docs.python.org/3/library/exceptions.html#RecursionError
|
||||
RecursionError # Python 3.5+
|
||||
except NameError:
|
||||
RecursionError = RuntimeError # Python < 3.5
|
||||
|
||||
|
||||
def test_simple():
|
||||
fig = plt.figure()
|
||||
pickle.dump(fig, BytesIO(), pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
ax = plt.subplot(121)
|
||||
pickle.dump(ax, BytesIO(), pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
ax = plt.axes(projection='polar')
|
||||
plt.plot(np.arange(10), label='foobar')
|
||||
plt.legend()
|
||||
|
||||
pickle.dump(ax, BytesIO(), pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
# ax = plt.subplot(121, projection='hammer')
|
||||
# pickle.dump(ax, BytesIO(), pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
plt.figure()
|
||||
plt.bar(x=np.arange(10), height=np.arange(10))
|
||||
pickle.dump(plt.gca(), BytesIO(), pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = plt.axes()
|
||||
plt.plot(np.arange(10))
|
||||
ax.set_yscale('log')
|
||||
pickle.dump(fig, BytesIO(), pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['multi_pickle'],
|
||||
extensions=['png'], remove_text=True,
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
style='mpl20')
|
||||
def test_complete():
|
||||
fig = plt.figure('Figure with a label?', figsize=(10, 6))
|
||||
|
||||
plt.suptitle('Can you fit any more in a figure?')
|
||||
|
||||
# make some arbitrary data
|
||||
x, y = np.arange(8), np.arange(10)
|
||||
data = u = v = np.linspace(0, 10, 80).reshape(10, 8)
|
||||
v = np.sin(v * -0.6)
|
||||
|
||||
# Ensure lists also pickle correctly.
|
||||
plt.subplot(3, 3, 1)
|
||||
plt.plot(list(range(10)))
|
||||
|
||||
plt.subplot(3, 3, 2)
|
||||
plt.contourf(data, hatches=['//', 'ooo'])
|
||||
plt.colorbar()
|
||||
|
||||
plt.subplot(3, 3, 3)
|
||||
plt.pcolormesh(data)
|
||||
|
||||
plt.subplot(3, 3, 4)
|
||||
plt.imshow(data)
|
||||
|
||||
plt.subplot(3, 3, 5)
|
||||
plt.pcolor(data)
|
||||
|
||||
ax = plt.subplot(3, 3, 6)
|
||||
ax.set_xlim(0, 7)
|
||||
ax.set_ylim(0, 9)
|
||||
plt.streamplot(x, y, u, v)
|
||||
|
||||
ax = plt.subplot(3, 3, 7)
|
||||
ax.set_xlim(0, 7)
|
||||
ax.set_ylim(0, 9)
|
||||
plt.quiver(x, y, u, v)
|
||||
|
||||
plt.subplot(3, 3, 8)
|
||||
plt.scatter(x, x**2, label='$x^2$')
|
||||
plt.legend(loc='upper left')
|
||||
|
||||
plt.subplot(3, 3, 9)
|
||||
plt.errorbar(x, x * -0.5, xerr=0.2, yerr=0.4)
|
||||
|
||||
#
|
||||
# plotting is done, now test its pickle-ability
|
||||
#
|
||||
result_fh = BytesIO()
|
||||
pickle.dump(fig, result_fh, pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
plt.close('all')
|
||||
|
||||
# make doubly sure that there are no figures left
|
||||
assert plt._pylab_helpers.Gcf.figs == {}
|
||||
|
||||
# wind back the fh and load in the figure
|
||||
result_fh.seek(0)
|
||||
fig = pickle.load(result_fh)
|
||||
|
||||
# make sure there is now a figure manager
|
||||
assert plt._pylab_helpers.Gcf.figs != {}
|
||||
|
||||
assert fig.get_label() == 'Figure with a label?'
|
||||
|
||||
|
||||
def test_no_pyplot():
|
||||
# tests pickle-ability of a figure not created with pyplot
|
||||
from matplotlib.backends.backend_pdf import FigureCanvasPdf
|
||||
from matplotlib.figure import Figure
|
||||
|
||||
fig = Figure()
|
||||
_ = FigureCanvasPdf(fig)
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.plot([1, 2, 3], [1, 2, 3])
|
||||
pickle.dump(fig, BytesIO(), pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
|
||||
def test_renderer():
|
||||
from matplotlib.backends.backend_agg import RendererAgg
|
||||
renderer = RendererAgg(10, 20, 30)
|
||||
pickle.dump(renderer, BytesIO())
|
||||
|
||||
|
||||
def test_image():
|
||||
# Prior to v1.4.0 the Image would cache data which was not picklable
|
||||
# once it had been drawn.
|
||||
from matplotlib.backends.backend_agg import new_figure_manager
|
||||
manager = new_figure_manager(1000)
|
||||
fig = manager.canvas.figure
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.imshow(np.arange(12).reshape(3, 4))
|
||||
manager.canvas.draw()
|
||||
pickle.dump(fig, BytesIO())
|
||||
|
||||
|
||||
def test_polar():
|
||||
ax = plt.subplot(111, polar=True)
|
||||
fig = plt.gcf()
|
||||
pf = pickle.dumps(fig)
|
||||
pickle.loads(pf)
|
||||
plt.draw()
|
||||
|
||||
|
||||
class TransformBlob(object):
|
||||
def __init__(self):
|
||||
self.identity = mtransforms.IdentityTransform()
|
||||
self.identity2 = mtransforms.IdentityTransform()
|
||||
# Force use of the more complex composition.
|
||||
self.composite = mtransforms.CompositeGenericTransform(
|
||||
self.identity,
|
||||
self.identity2)
|
||||
# Check parent -> child links of TransformWrapper.
|
||||
self.wrapper = mtransforms.TransformWrapper(self.composite)
|
||||
# Check child -> parent links of TransformWrapper.
|
||||
self.composite2 = mtransforms.CompositeGenericTransform(
|
||||
self.wrapper,
|
||||
self.identity)
|
||||
|
||||
|
||||
def test_transform():
|
||||
obj = TransformBlob()
|
||||
pf = pickle.dumps(obj)
|
||||
del obj
|
||||
|
||||
obj = pickle.loads(pf)
|
||||
# Check parent -> child links of TransformWrapper.
|
||||
assert obj.wrapper._child == obj.composite
|
||||
# Check child -> parent links of TransformWrapper.
|
||||
assert [v() for v in obj.wrapper._parents.values()] == [obj.composite2]
|
||||
# Check input and output dimensions are set as expected.
|
||||
assert obj.wrapper.input_dims == obj.composite.input_dims
|
||||
assert obj.wrapper.output_dims == obj.composite.output_dims
|
||||
|
||||
|
||||
def test_rrulewrapper():
|
||||
r = rrulewrapper(2)
|
||||
try:
|
||||
pickle.loads(pickle.dumps(r))
|
||||
except RecursionError:
|
||||
print('rrulewrapper pickling test failed')
|
||||
raise
|
||||
|
||||
|
||||
def test_shared():
|
||||
fig, axs = plt.subplots(2, sharex=True)
|
||||
fig = pickle.loads(pickle.dumps(fig))
|
||||
fig.axes[0].set_xlim(10, 20)
|
||||
assert fig.axes[1].get_xlim() == (10, 20)
|
||||
@@ -0,0 +1,68 @@
|
||||
from io import BytesIO
|
||||
import glob
|
||||
import os
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
from matplotlib import pyplot as plt
|
||||
import matplotlib.cm as cm
|
||||
import sys
|
||||
on_win = (sys.platform == 'win32')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['pngsuite'], extensions=['png'],
|
||||
tol=0.03)
|
||||
def test_pngsuite():
|
||||
dirname = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'baseline_images',
|
||||
'pngsuite')
|
||||
files = sorted(glob.iglob(os.path.join(dirname, 'basn*.png')))
|
||||
|
||||
fig = plt.figure(figsize=(len(files), 2))
|
||||
|
||||
for i, fname in enumerate(files):
|
||||
data = plt.imread(fname)
|
||||
cmap = None # use default colormap
|
||||
if data.ndim == 2:
|
||||
# keep grayscale images gray
|
||||
cmap = cm.gray
|
||||
plt.imshow(data, extent=[i, i + 1, 0, 1], cmap=cmap)
|
||||
|
||||
plt.gca().patch.set_facecolor("#ddffff")
|
||||
plt.gca().set_xlim(0, len(files))
|
||||
|
||||
|
||||
def test_imread_png_uint16():
|
||||
from matplotlib import _png
|
||||
img = _png.read_png_int(os.path.join(os.path.dirname(__file__),
|
||||
'baseline_images/test_png/uint16.png'))
|
||||
|
||||
assert (img.dtype == np.uint16)
|
||||
assert np.sum(img.flatten()) == 134184960
|
||||
|
||||
|
||||
def test_truncated_file(tmpdir):
|
||||
d = tmpdir.mkdir('test')
|
||||
fname = str(d.join('test.png'))
|
||||
fname_t = str(d.join('test_truncated.png'))
|
||||
plt.savefig(fname)
|
||||
with open(fname, 'rb') as fin:
|
||||
buf = fin.read()
|
||||
with open(fname_t, 'wb') as fout:
|
||||
fout.write(buf[:20])
|
||||
|
||||
with pytest.raises(Exception):
|
||||
plt.imread(fname_t)
|
||||
|
||||
|
||||
def test_truncated_buffer():
|
||||
b = BytesIO()
|
||||
plt.savefig(b)
|
||||
b.seek(0)
|
||||
b2 = BytesIO(b.read(20))
|
||||
b2.seek(0)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
plt.imread(b2)
|
||||
@@ -0,0 +1,384 @@
|
||||
import re
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from matplotlib import _preprocess_data
|
||||
|
||||
|
||||
# Notes on testing the plotting functions itself
|
||||
# * the individual decorated plotting functions are tested in 'test_axes.py'
|
||||
# * that pyplot functions accept a data kwarg is only tested in
|
||||
# test_axes.test_pie_linewidth_0
|
||||
|
||||
|
||||
# these two get used in multiple tests, so define them here
|
||||
@_preprocess_data(replace_names=["x", "y"], label_namer="y")
|
||||
def plot_func(ax, x, y, ls="x", label=None, w="xyz"):
|
||||
return ("x: %s, y: %s, ls: %s, w: %s, label: %s" % (
|
||||
list(x), list(y), ls, w, label))
|
||||
|
||||
|
||||
@_preprocess_data(replace_names=["x", "y"], label_namer="y",
|
||||
positional_parameter_names=["x", "y", "ls", "label", "w"])
|
||||
def plot_func_varargs(ax, *args, **kwargs):
|
||||
all_args = [None, None, "x", None, "xyz"]
|
||||
for i, v in enumerate(args):
|
||||
all_args[i] = v
|
||||
for i, k in enumerate(["x", "y", "ls", "label", "w"]):
|
||||
if k in kwargs:
|
||||
all_args[i] = kwargs[k]
|
||||
x, y, ls, label, w = all_args
|
||||
return ("x: %s, y: %s, ls: %s, w: %s, label: %s" % (
|
||||
list(x), list(y), ls, w, label))
|
||||
|
||||
|
||||
all_funcs = [plot_func, plot_func_varargs]
|
||||
all_func_ids = ['plot_func', 'plot_func_varargs']
|
||||
|
||||
|
||||
def test_compiletime_checks():
|
||||
"""test decorator invocations -> no replacements"""
|
||||
|
||||
def func(ax, x, y): pass
|
||||
|
||||
def func_args(ax, x, y, *args): pass
|
||||
|
||||
def func_kwargs(ax, x, y, **kwargs): pass
|
||||
|
||||
def func_no_ax_args(*args, **kwargs): pass
|
||||
|
||||
# this is ok
|
||||
_preprocess_data(replace_names=["x", "y"])(func)
|
||||
_preprocess_data(replace_names=["x", "y"])(func_kwargs)
|
||||
# this has "enough" information to do all the replaces
|
||||
_preprocess_data(replace_names=["x", "y"])(func_args)
|
||||
|
||||
# no positional_parameter_names but needed due to replaces
|
||||
with pytest.raises(AssertionError):
|
||||
# z is unknown
|
||||
_preprocess_data(replace_names=["x", "y", "z"])(func_args)
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
_preprocess_data(replace_names=["x", "y"])(func_no_ax_args)
|
||||
|
||||
# no replacements at all -> all ok...
|
||||
_preprocess_data(replace_names=[], label_namer=None)(func)
|
||||
_preprocess_data(replace_names=[], label_namer=None)(func_args)
|
||||
_preprocess_data(replace_names=[], label_namer=None)(func_kwargs)
|
||||
_preprocess_data(replace_names=[], label_namer=None)(func_no_ax_args)
|
||||
|
||||
# label namer is unknown
|
||||
with pytest.raises(AssertionError):
|
||||
_preprocess_data(label_namer="z")(func)
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
_preprocess_data(label_namer="z")(func_args)
|
||||
|
||||
# but "ok-ish", if func has kwargs -> will show up at runtime :-(
|
||||
_preprocess_data(label_namer="z")(func_kwargs)
|
||||
_preprocess_data(label_namer="z")(func_no_ax_args)
|
||||
|
||||
|
||||
def test_label_problems_at_runtime():
|
||||
"""Tests for behaviour which would actually be nice to get rid of."""
|
||||
|
||||
@_preprocess_data(label_namer="z")
|
||||
def func(*args, **kwargs):
|
||||
pass
|
||||
|
||||
# This is a programming mistake: the parameter which should add the
|
||||
# label is not present in the function call. Unfortunately this was masked
|
||||
# due to the **kwargs usage
|
||||
# This would be nice to handle as a compiletime check (see above...)
|
||||
with pytest.warns(RuntimeWarning):
|
||||
func(None, x="a", y="b")
|
||||
|
||||
def real_func(x, y):
|
||||
pass
|
||||
|
||||
@_preprocess_data(label_namer="x")
|
||||
def func(*args, **kwargs):
|
||||
real_func(**kwargs)
|
||||
|
||||
# This sets a label although the function can't handle it.
|
||||
with pytest.raises(TypeError):
|
||||
func(None, x="a", y="b")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
|
||||
def test_function_call_without_data(func):
|
||||
"""test without data -> no replacements"""
|
||||
assert (func(None, "x", "y") ==
|
||||
"x: ['x'], y: ['y'], ls: x, w: xyz, label: None")
|
||||
assert (func(None, x="x", y="y") ==
|
||||
"x: ['x'], y: ['y'], ls: x, w: xyz, label: None")
|
||||
assert (func(None, "x", "y", label="") ==
|
||||
"x: ['x'], y: ['y'], ls: x, w: xyz, label: ")
|
||||
assert (func(None, "x", "y", label="text") ==
|
||||
"x: ['x'], y: ['y'], ls: x, w: xyz, label: text")
|
||||
assert (func(None, x="x", y="y", label="") ==
|
||||
"x: ['x'], y: ['y'], ls: x, w: xyz, label: ")
|
||||
assert (func(None, x="x", y="y", label="text") ==
|
||||
"x: ['x'], y: ['y'], ls: x, w: xyz, label: text")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
|
||||
def test_function_call_with_dict_data(func):
|
||||
"""Test with dict data -> label comes from the value of 'x' parameter """
|
||||
data = {"a": [1, 2], "b": [8, 9], "w": "NOT"}
|
||||
assert (func(None, "a", "b", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
|
||||
assert (func(None, x="a", y="b", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
|
||||
assert (func(None, "a", "b", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (func(None, "a", "b", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
assert (func(None, x="a", y="b", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (func(None, x="a", y="b", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
|
||||
def test_function_call_with_dict_data_not_in_data(func):
|
||||
"test for the case that one var is not in data -> half replaces, half kept"
|
||||
data = {"a": [1, 2], "w": "NOT"}
|
||||
assert (func(None, "a", "b", data=data) ==
|
||||
"x: [1, 2], y: ['b'], ls: x, w: xyz, label: b")
|
||||
assert (func(None, x="a", y="b", data=data) ==
|
||||
"x: [1, 2], y: ['b'], ls: x, w: xyz, label: b")
|
||||
assert (func(None, "a", "b", label="", data=data) ==
|
||||
"x: [1, 2], y: ['b'], ls: x, w: xyz, label: ")
|
||||
assert (func(None, "a", "b", label="text", data=data) ==
|
||||
"x: [1, 2], y: ['b'], ls: x, w: xyz, label: text")
|
||||
assert (func(None, x="a", y="b", label="", data=data) ==
|
||||
"x: [1, 2], y: ['b'], ls: x, w: xyz, label: ")
|
||||
assert (func(None, x="a", y="b", label="text", data=data) ==
|
||||
"x: [1, 2], y: ['b'], ls: x, w: xyz, label: text")
|
||||
|
||||
|
||||
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
|
||||
def test_function_call_with_pandas_data(func, pd):
|
||||
"""test with pandas dataframe -> label comes from data["col"].name """
|
||||
data = pd.DataFrame({"a": np.array([1, 2], dtype=np.int32),
|
||||
"b": np.array([8, 9], dtype=np.int32),
|
||||
"w": ["NOT", "NOT"]})
|
||||
|
||||
assert (func(None, "a", "b", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
|
||||
assert (func(None, x="a", y="b", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
|
||||
assert (func(None, "a", "b", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (func(None, "a", "b", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
assert (func(None, x="a", y="b", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (func(None, x="a", y="b", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
|
||||
|
||||
def test_function_call_replace_all():
|
||||
"""Test without a "replace_names" argument, all vars should be replaced"""
|
||||
data = {"a": [1, 2], "b": [8, 9], "x": "xyz"}
|
||||
|
||||
@_preprocess_data(label_namer="y")
|
||||
def func_replace_all(ax, x, y, ls="x", label=None, w="NOT"):
|
||||
return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (
|
||||
list(x), list(y), ls, w, label)
|
||||
|
||||
assert (func_replace_all(None, "a", "b", w="x", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
|
||||
assert (func_replace_all(None, x="a", y="b", w="x", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
|
||||
assert (func_replace_all(None, "a", "b", w="x", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (
|
||||
func_replace_all(None, "a", "b", w="x", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
assert (
|
||||
func_replace_all(None, x="a", y="b", w="x", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (
|
||||
func_replace_all(None, x="a", y="b", w="x", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
|
||||
@_preprocess_data(label_namer="y")
|
||||
def func_varags_replace_all(ax, *args, **kwargs):
|
||||
all_args = [None, None, "x", None, "xyz"]
|
||||
for i, v in enumerate(args):
|
||||
all_args[i] = v
|
||||
for i, k in enumerate(["x", "y", "ls", "label", "w"]):
|
||||
if k in kwargs:
|
||||
all_args[i] = kwargs[k]
|
||||
x, y, ls, label, w = all_args
|
||||
return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (
|
||||
list(x), list(y), ls, w, label)
|
||||
|
||||
# in the first case, we can't get a "y" argument,
|
||||
# as we don't know the names of the *args
|
||||
assert (func_varags_replace_all(None, x="a", y="b", w="x", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
|
||||
assert (
|
||||
func_varags_replace_all(None, "a", "b", w="x", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (
|
||||
func_varags_replace_all(None, "a", "b", w="x", label="text",
|
||||
data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
assert (
|
||||
func_varags_replace_all(None, x="a", y="b", w="x", label="",
|
||||
data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (
|
||||
func_varags_replace_all(None, x="a", y="b", w="x", label="text",
|
||||
data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
|
||||
with pytest.warns(RuntimeWarning):
|
||||
assert (func_varags_replace_all(None, "a", "b", w="x", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None")
|
||||
|
||||
|
||||
def test_no_label_replacements():
|
||||
"""Test with "label_namer=None" -> no label replacement at all"""
|
||||
|
||||
@_preprocess_data(replace_names=["x", "y"], label_namer=None)
|
||||
def func_no_label(ax, x, y, ls="x", label=None, w="xyz"):
|
||||
return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (
|
||||
list(x), list(y), ls, w, label)
|
||||
|
||||
data = {"a": [1, 2], "b": [8, 9], "w": "NOT"}
|
||||
assert (func_no_label(None, "a", "b", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None")
|
||||
assert (func_no_label(None, x="a", y="b", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None")
|
||||
assert (func_no_label(None, "a", "b", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (func_no_label(None, "a", "b", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
|
||||
|
||||
def test_more_args_than_pos_parameter():
|
||||
@_preprocess_data(replace_names=["x", "y"], label_namer="y")
|
||||
def func(ax, x, y, z=1):
|
||||
pass
|
||||
|
||||
data = {"a": [1, 2], "b": [8, 9], "w": "NOT"}
|
||||
with pytest.raises(RuntimeError):
|
||||
func(None, "a", "b", "z", "z", data=data)
|
||||
|
||||
|
||||
def test_function_call_with_replace_all_args():
|
||||
"""Test with a "replace_all_args" argument, all *args should be replaced"""
|
||||
data = {"a": [1, 2], "b": [8, 9], "x": "xyz"}
|
||||
|
||||
def funcy(ax, *args, **kwargs):
|
||||
all_args = [None, None, "x", None, "NOT"]
|
||||
for i, v in enumerate(args):
|
||||
all_args[i] = v
|
||||
for i, k in enumerate(["x", "y", "ls", "label", "w"]):
|
||||
if k in kwargs:
|
||||
all_args[i] = kwargs[k]
|
||||
x, y, ls, label, w = all_args
|
||||
return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (
|
||||
list(x), list(y), ls, w, label)
|
||||
|
||||
func = _preprocess_data(replace_all_args=True, replace_names=["w"],
|
||||
label_namer="y")(funcy)
|
||||
|
||||
assert (func(None, "a", "b", w="x", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (func(None, "a", "b", w="x", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
|
||||
func2 = _preprocess_data(replace_all_args=True, replace_names=["w"],
|
||||
label_namer="y",
|
||||
positional_parameter_names=["x", "y", "ls",
|
||||
"label", "w"])(funcy)
|
||||
|
||||
assert (func2(None, "a", "b", w="x", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
|
||||
assert (func2(None, "a", "b", w="x", label="", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
|
||||
assert (func2(None, "a", "b", w="x", label="text", data=data) ==
|
||||
"x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
|
||||
|
||||
|
||||
def test_docstring_addition():
|
||||
@_preprocess_data()
|
||||
def funcy(ax, *args, **kwargs):
|
||||
"""Funcy does nothing"""
|
||||
pass
|
||||
|
||||
assert re.search(r".*All positional and all keyword arguments\.",
|
||||
funcy.__doc__)
|
||||
assert not re.search(r".*All positional arguments\.", funcy.__doc__)
|
||||
assert not re.search(r".*All arguments with the following names: .*",
|
||||
funcy.__doc__)
|
||||
|
||||
@_preprocess_data(replace_all_args=True, replace_names=[])
|
||||
def funcy(ax, x, y, z, bar=None):
|
||||
"""Funcy does nothing"""
|
||||
pass
|
||||
|
||||
assert re.search(r".*All positional arguments\.",
|
||||
funcy.__doc__)
|
||||
assert not re.search(r".*All positional and all keyword arguments\.",
|
||||
funcy.__doc__)
|
||||
assert not re.search(r".*All arguments with the following names: .*",
|
||||
funcy.__doc__)
|
||||
|
||||
@_preprocess_data(replace_all_args=True, replace_names=["bar"])
|
||||
def funcy(ax, x, y, z, bar=None):
|
||||
"""Funcy does nothing"""
|
||||
pass
|
||||
|
||||
assert re.search(r".*All positional arguments\.", funcy.__doc__)
|
||||
assert re.search(r".*All arguments with the following names: 'bar'\.",
|
||||
funcy.__doc__)
|
||||
assert not re.search(r".*All positional and all keyword arguments\.",
|
||||
funcy.__doc__)
|
||||
|
||||
@_preprocess_data(replace_names=["x", "bar"])
|
||||
def funcy(ax, x, y, z, bar=None):
|
||||
"""Funcy does nothing"""
|
||||
pass
|
||||
|
||||
# lists can print in any order, so test for both x,bar and bar,x
|
||||
assert re.search(r".*All arguments with the following names: '.*', '.*'\.",
|
||||
funcy.__doc__)
|
||||
assert re.search(r".*'x'.*", funcy.__doc__)
|
||||
assert re.search(r".*'bar'.*", funcy.__doc__)
|
||||
assert not re.search(r".*All positional and all keyword arguments\.",
|
||||
funcy.__doc__)
|
||||
assert not re.search(r".*All positional arguments\.",
|
||||
funcy.__doc__)
|
||||
|
||||
|
||||
def test_positional_parameter_names_as_function():
|
||||
# Also test the _plot_arg_replacer for plot...
|
||||
from matplotlib.axes._axes import _plot_args_replacer
|
||||
|
||||
@_preprocess_data(replace_names=["x", "y"],
|
||||
positional_parameter_names=_plot_args_replacer)
|
||||
def funcy(ax, *args, **kwargs):
|
||||
return "{args} | {kwargs}".format(args=args, kwargs=kwargs)
|
||||
|
||||
# the normal case...
|
||||
data = {"x": "X", "hy1": "Y"}
|
||||
assert funcy(None, "x", "hy1", data=data) == "('X', 'Y') | {}"
|
||||
assert funcy(None, "x", "hy1", "c", data=data) == "('X', 'Y', 'c') | {}"
|
||||
|
||||
# no arbitrary long args with data
|
||||
with pytest.raises(ValueError):
|
||||
assert (funcy(None, "x", "y", "c", "x", "y", "x", "y", data=data) ==
|
||||
"('X', 'Y', 'c', 'X', 'Y', 'X', 'Y') | {}")
|
||||
|
||||
# In the two arg case, if a valid color spec is in data, we warn but use
|
||||
# it as data...
|
||||
data = {"x": "X", "y": "Y", "ro": "!!"}
|
||||
with pytest.warns(RuntimeWarning):
|
||||
assert funcy(None, "y", "ro", data=data) == "('Y', '!!') | {}"
|
||||
@@ -0,0 +1,38 @@
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import matplotlib as mpl
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
|
||||
def test_pyplot_up_to_date():
|
||||
gen_script = Path(mpl.__file__).parents[2] / "tools/boilerplate.py"
|
||||
if not gen_script.exists():
|
||||
pytest.skip("boilerplate.py not found")
|
||||
orig_contents = Path(plt.__file__).read_text()
|
||||
try:
|
||||
subprocess.run([sys.executable, str(gen_script)], check=True)
|
||||
new_contents = Path(plt.__file__).read_text()
|
||||
assert orig_contents == new_contents
|
||||
finally:
|
||||
Path(plt.__file__).write_text(orig_contents)
|
||||
|
||||
|
||||
def test_pyplot_box():
|
||||
fig, ax = plt.subplots()
|
||||
plt.box(False)
|
||||
assert not ax.get_frame_on()
|
||||
plt.box(True)
|
||||
assert ax.get_frame_on()
|
||||
plt.box()
|
||||
assert not ax.get_frame_on()
|
||||
plt.box()
|
||||
assert ax.get_frame_on()
|
||||
|
||||
|
||||
def test_stackplot_smoke():
|
||||
# Small smoke test for stackplot (see #12405)
|
||||
plt.stackplot([1, 2, 3], [1, 2, 3])
|
||||
@@ -0,0 +1,201 @@
|
||||
import warnings
|
||||
import numpy as np
|
||||
import pytest
|
||||
import sys
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
|
||||
def draw_quiver(ax, **kw):
|
||||
X, Y = np.meshgrid(np.arange(0, 2 * np.pi, 1),
|
||||
np.arange(0, 2 * np.pi, 1))
|
||||
U = np.cos(X)
|
||||
V = np.sin(Y)
|
||||
|
||||
Q = ax.quiver(U, V, **kw)
|
||||
return Q
|
||||
|
||||
|
||||
def test_quiver_memory_leak():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
Q = draw_quiver(ax)
|
||||
ttX = Q.X
|
||||
Q.remove()
|
||||
|
||||
del Q
|
||||
|
||||
assert sys.getrefcount(ttX) == 2
|
||||
|
||||
|
||||
def test_quiver_key_memory_leak():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
Q = draw_quiver(ax)
|
||||
|
||||
qk = ax.quiverkey(Q, 0.5, 0.92, 2, r'$2 \frac{m}{s}$',
|
||||
labelpos='W',
|
||||
fontproperties={'weight': 'bold'})
|
||||
assert sys.getrefcount(qk) == 3
|
||||
qk.remove()
|
||||
assert sys.getrefcount(qk) == 2
|
||||
|
||||
|
||||
def test_no_warnings():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
X, Y = np.meshgrid(np.arange(15), np.arange(10))
|
||||
U = V = np.ones_like(X)
|
||||
|
||||
phi = (np.random.rand(15, 10) - .5) * 150
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
ax.quiver(X, Y, U, V, angles=phi)
|
||||
fig.canvas.draw()
|
||||
assert len(w) == 0
|
||||
|
||||
|
||||
def test_zero_headlength():
|
||||
# Based on report by Doug McNeil:
|
||||
# http://matplotlib.1069221.n5.nabble.com/quiver-warnings-td28107.html
|
||||
fig, ax = plt.subplots()
|
||||
X, Y = np.meshgrid(np.arange(10), np.arange(10))
|
||||
U, V = np.cos(X), np.sin(Y)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
ax.quiver(U, V, headlength=0, headaxislength=0)
|
||||
fig.canvas.draw()
|
||||
assert len(w) == 0
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['quiver_animated_test_image'],
|
||||
extensions=['png'])
|
||||
def test_quiver_animate():
|
||||
# Tests fix for #2616
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
Q = draw_quiver(ax, animated=True)
|
||||
|
||||
qk = ax.quiverkey(Q, 0.5, 0.92, 2, r'$2 \frac{m}{s}$',
|
||||
labelpos='W',
|
||||
fontproperties={'weight': 'bold'})
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['quiver_with_key_test_image'],
|
||||
extensions=['png'])
|
||||
def test_quiver_with_key():
|
||||
fig, ax = plt.subplots()
|
||||
ax.margins(0.1)
|
||||
|
||||
Q = draw_quiver(ax)
|
||||
|
||||
qk = ax.quiverkey(Q, 0.5, 0.95, 2,
|
||||
r'$2\, \mathrm{m}\, \mathrm{s}^{-1}$',
|
||||
angle=-10,
|
||||
coordinates='figure',
|
||||
labelpos='W',
|
||||
fontproperties={'weight': 'bold',
|
||||
'size': 'large'})
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['quiver_single_test_image'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_quiver_single():
|
||||
fig, ax = plt.subplots()
|
||||
ax.margins(0.1)
|
||||
|
||||
ax.quiver([1], [1], [2], [2])
|
||||
|
||||
|
||||
def test_quiver_copy():
|
||||
fig, ax = plt.subplots()
|
||||
uv = dict(u=np.array([1.1]), v=np.array([2.0]))
|
||||
q0 = ax.quiver([1], [1], uv['u'], uv['v'])
|
||||
uv['v'][0] = 0
|
||||
assert q0.V[0] == 2.0
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['quiver_key_pivot'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_quiver_key_pivot():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
u, v = np.mgrid[0:2*np.pi:10j, 0:2*np.pi:10j]
|
||||
|
||||
q = ax.quiver(np.sin(u), np.cos(v))
|
||||
ax.set_xlim(-2, 11)
|
||||
ax.set_ylim(-2, 11)
|
||||
ax.quiverkey(q, 0.5, 1, 1, 'N', labelpos='N')
|
||||
ax.quiverkey(q, 1, 0.5, 1, 'E', labelpos='E')
|
||||
ax.quiverkey(q, 0.5, 0, 1, 'S', labelpos='S')
|
||||
ax.quiverkey(q, 0, 0.5, 1, 'W', labelpos='W')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['barbs_test_image'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_barbs():
|
||||
x = np.linspace(-5, 5, 5)
|
||||
X, Y = np.meshgrid(x, x)
|
||||
U, V = 12*X, 12*Y
|
||||
fig, ax = plt.subplots()
|
||||
ax.barbs(X, Y, U, V, np.sqrt(U*U + V*V), fill_empty=True, rounding=False,
|
||||
sizes=dict(emptybarb=0.25, spacing=0.2, height=0.3),
|
||||
cmap='viridis')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['barbs_pivot_test_image'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_barbs_pivot():
|
||||
x = np.linspace(-5, 5, 5)
|
||||
X, Y = np.meshgrid(x, x)
|
||||
U, V = 12*X, 12*Y
|
||||
fig, ax = plt.subplots()
|
||||
ax.barbs(X, Y, U, V, fill_empty=True, rounding=False, pivot=1.7,
|
||||
sizes=dict(emptybarb=0.25, spacing=0.2, height=0.3))
|
||||
ax.scatter(X, Y, s=49, c='black')
|
||||
|
||||
|
||||
def test_bad_masked_sizes():
|
||||
'Test error handling when given differing sized masked arrays'
|
||||
x = np.arange(3)
|
||||
y = np.arange(3)
|
||||
u = np.ma.array(15. * np.ones((4,)))
|
||||
v = np.ma.array(15. * np.ones_like(u))
|
||||
u[1] = np.ma.masked
|
||||
v[1] = np.ma.masked
|
||||
fig, ax = plt.subplots()
|
||||
with pytest.raises(ValueError):
|
||||
ax.barbs(x, y, u, v)
|
||||
|
||||
|
||||
def test_angles_and_scale():
|
||||
# angles array + scale_units kwarg
|
||||
fig, ax = plt.subplots()
|
||||
X, Y = np.meshgrid(np.arange(15), np.arange(10))
|
||||
U = V = np.ones_like(X)
|
||||
phi = (np.random.rand(15, 10) - .5) * 150
|
||||
ax.quiver(X, Y, U, V, angles=phi, scale_units='xy')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['quiver_xy'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_quiver_xy():
|
||||
# simple arrow pointing from SW to NE
|
||||
fig, ax = plt.subplots(subplot_kw=dict(aspect='equal'))
|
||||
ax.quiver(0, 0, 1, 1, angles='xy', scale_units='xy', scale=1)
|
||||
ax.set_xlim(0, 1.1)
|
||||
ax.set_ylim(0, 1.1)
|
||||
ax.grid()
|
||||
|
||||
|
||||
def test_quiverkey_angles():
|
||||
# Check that only a single arrow is plotted for a quiverkey when an array
|
||||
# of angles is given to the original quiver plot
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
X, Y = np.meshgrid(np.arange(2), np.arange(2))
|
||||
U = V = angles = np.ones_like(X)
|
||||
|
||||
q = ax.quiver(X, Y, U, V, angles=angles)
|
||||
qk = ax.quiverkey(q, 1, 1, 2, 'Label')
|
||||
# The arrows are only created when the key is drawn
|
||||
fig.canvas.draw()
|
||||
assert len(qk.verts) == 1
|
||||
@@ -0,0 +1,506 @@
|
||||
from collections import OrderedDict
|
||||
import copy
|
||||
from itertools import chain
|
||||
import locale
|
||||
import os
|
||||
from unittest import mock
|
||||
import warnings
|
||||
|
||||
from cycler import cycler, Cycler
|
||||
import pytest
|
||||
|
||||
import matplotlib as mpl
|
||||
from matplotlib.cbook import MatplotlibDeprecationWarning
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.colors as mcolors
|
||||
import numpy as np
|
||||
from matplotlib.rcsetup import (validate_bool_maybe_none,
|
||||
validate_stringlist,
|
||||
validate_colorlist,
|
||||
validate_color,
|
||||
validate_bool,
|
||||
validate_nseq_int,
|
||||
validate_nseq_float,
|
||||
validate_cycler,
|
||||
validate_hatch,
|
||||
validate_hist_bins,
|
||||
validate_markevery,
|
||||
_validate_linestyle)
|
||||
|
||||
|
||||
def test_rcparams():
|
||||
mpl.rc('text', usetex=False)
|
||||
mpl.rc('lines', linewidth=22)
|
||||
|
||||
usetex = mpl.rcParams['text.usetex']
|
||||
linewidth = mpl.rcParams['lines.linewidth']
|
||||
fname = os.path.join(os.path.dirname(__file__), 'test_rcparams.rc')
|
||||
|
||||
# test context given dictionary
|
||||
with mpl.rc_context(rc={'text.usetex': not usetex}):
|
||||
assert mpl.rcParams['text.usetex'] == (not usetex)
|
||||
assert mpl.rcParams['text.usetex'] == usetex
|
||||
|
||||
# test context given filename (mpl.rc sets linewdith to 33)
|
||||
with mpl.rc_context(fname=fname):
|
||||
assert mpl.rcParams['lines.linewidth'] == 33
|
||||
assert mpl.rcParams['lines.linewidth'] == linewidth
|
||||
|
||||
# test context given filename and dictionary
|
||||
with mpl.rc_context(fname=fname, rc={'lines.linewidth': 44}):
|
||||
assert mpl.rcParams['lines.linewidth'] == 44
|
||||
assert mpl.rcParams['lines.linewidth'] == linewidth
|
||||
|
||||
# test rc_file
|
||||
mpl.rc_file(fname)
|
||||
assert mpl.rcParams['lines.linewidth'] == 33
|
||||
|
||||
|
||||
def test_RcParams_class():
|
||||
rc = mpl.RcParams({'font.cursive': ['Apple Chancery',
|
||||
'Textile',
|
||||
'Zapf Chancery',
|
||||
'cursive'],
|
||||
'font.family': 'sans-serif',
|
||||
'font.weight': 'normal',
|
||||
'font.size': 12})
|
||||
|
||||
expected_repr = """
|
||||
RcParams({'font.cursive': ['Apple Chancery',
|
||||
'Textile',
|
||||
'Zapf Chancery',
|
||||
'cursive'],
|
||||
'font.family': ['sans-serif'],
|
||||
'font.size': 12.0,
|
||||
'font.weight': 'normal'})""".lstrip()
|
||||
|
||||
assert expected_repr == repr(rc)
|
||||
|
||||
expected_str = """
|
||||
font.cursive: ['Apple Chancery', 'Textile', 'Zapf Chancery', 'cursive']
|
||||
font.family: ['sans-serif']
|
||||
font.size: 12.0
|
||||
font.weight: normal""".lstrip()
|
||||
|
||||
assert expected_str == str(rc)
|
||||
|
||||
# test the find_all functionality
|
||||
assert ['font.cursive', 'font.size'] == sorted(rc.find_all('i[vz]'))
|
||||
assert ['font.family'] == list(rc.find_all('family'))
|
||||
|
||||
|
||||
def test_rcparams_update():
|
||||
rc = mpl.RcParams({'figure.figsize': (3.5, 42)})
|
||||
bad_dict = {'figure.figsize': (3.5, 42, 1)}
|
||||
# make sure validation happens on input
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore',
|
||||
message='.*(validate)',
|
||||
category=UserWarning)
|
||||
rc.update(bad_dict)
|
||||
|
||||
|
||||
def test_rcparams_init():
|
||||
with pytest.raises(ValueError):
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore',
|
||||
message='.*(validate)',
|
||||
category=UserWarning)
|
||||
mpl.RcParams({'figure.figsize': (3.5, 42, 1)})
|
||||
|
||||
|
||||
def test_Bug_2543():
|
||||
# Test that it possible to add all values to itself / deepcopy
|
||||
# This was not possible because validate_bool_maybe_none did not
|
||||
# accept None as an argument.
|
||||
# https://github.com/matplotlib/matplotlib/issues/2543
|
||||
# We filter warnings at this stage since a number of them are raised
|
||||
# for deprecated rcparams as they should. We don't want these in the
|
||||
# printed in the test suite.
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore',
|
||||
category=MatplotlibDeprecationWarning)
|
||||
with mpl.rc_context():
|
||||
_copy = mpl.rcParams.copy()
|
||||
for key in _copy:
|
||||
mpl.rcParams[key] = _copy[key]
|
||||
mpl.rcParams['text.dvipnghack'] = None
|
||||
with mpl.rc_context():
|
||||
_deep_copy = copy.deepcopy(mpl.rcParams)
|
||||
# real test is that this does not raise
|
||||
assert validate_bool_maybe_none(None) is None
|
||||
assert validate_bool_maybe_none("none") is None
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
validate_bool_maybe_none("blah")
|
||||
with pytest.raises(ValueError):
|
||||
validate_bool(None)
|
||||
with pytest.raises(ValueError):
|
||||
with mpl.rc_context():
|
||||
mpl.rcParams['svg.fonttype'] = True
|
||||
|
||||
|
||||
legend_color_tests = [
|
||||
('face', {'color': 'r'}, mcolors.to_rgba('r')),
|
||||
('face', {'color': 'inherit', 'axes.facecolor': 'r'},
|
||||
mcolors.to_rgba('r')),
|
||||
('face', {'color': 'g', 'axes.facecolor': 'r'}, mcolors.to_rgba('g')),
|
||||
('edge', {'color': 'r'}, mcolors.to_rgba('r')),
|
||||
('edge', {'color': 'inherit', 'axes.edgecolor': 'r'},
|
||||
mcolors.to_rgba('r')),
|
||||
('edge', {'color': 'g', 'axes.facecolor': 'r'}, mcolors.to_rgba('g'))
|
||||
]
|
||||
legend_color_test_ids = [
|
||||
'same facecolor',
|
||||
'inherited facecolor',
|
||||
'different facecolor',
|
||||
'same edgecolor',
|
||||
'inherited edgecolor',
|
||||
'different facecolor',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('color_type, param_dict, target', legend_color_tests,
|
||||
ids=legend_color_test_ids)
|
||||
def test_legend_colors(color_type, param_dict, target):
|
||||
param_dict['legend.%scolor' % (color_type, )] = param_dict.pop('color')
|
||||
get_func = 'get_%scolor' % (color_type, )
|
||||
|
||||
with mpl.rc_context(param_dict):
|
||||
_, ax = plt.subplots()
|
||||
ax.plot(range(3), label='test')
|
||||
leg = ax.legend()
|
||||
assert getattr(leg.legendPatch, get_func)() == target
|
||||
|
||||
|
||||
def test_mfc_rcparams():
|
||||
mpl.rcParams['lines.markerfacecolor'] = 'r'
|
||||
ln = mpl.lines.Line2D([1, 2], [1, 2])
|
||||
assert ln.get_markerfacecolor() == 'r'
|
||||
|
||||
|
||||
def test_mec_rcparams():
|
||||
mpl.rcParams['lines.markeredgecolor'] = 'r'
|
||||
ln = mpl.lines.Line2D([1, 2], [1, 2])
|
||||
assert ln.get_markeredgecolor() == 'r'
|
||||
|
||||
|
||||
def test_Issue_1713():
|
||||
utf32_be = os.path.join(os.path.dirname(__file__),
|
||||
'test_utf32_be_rcparams.rc')
|
||||
with mock.patch('locale.getpreferredencoding', return_value='UTF-32-BE'):
|
||||
rc = mpl.rc_params_from_file(utf32_be, True, False)
|
||||
assert rc.get('timezone') == 'UTC'
|
||||
|
||||
|
||||
def generate_validator_testcases(valid):
|
||||
validation_tests = (
|
||||
{'validator': validate_bool,
|
||||
'success': chain(((_, True) for _ in
|
||||
('t', 'y', 'yes', 'on', 'true', '1', 1, True)),
|
||||
((_, False) for _ in
|
||||
('f', 'n', 'no', 'off', 'false', '0', 0, False))),
|
||||
'fail': ((_, ValueError)
|
||||
for _ in ('aardvark', 2, -1, [], ))},
|
||||
{'validator': validate_stringlist,
|
||||
'success': (('', []),
|
||||
('a,b', ['a', 'b']),
|
||||
('aardvark', ['aardvark']),
|
||||
('aardvark, ', ['aardvark']),
|
||||
('aardvark, ,', ['aardvark']),
|
||||
(['a', 'b'], ['a', 'b']),
|
||||
(('a', 'b'), ['a', 'b']),
|
||||
(iter(['a', 'b']), ['a', 'b']),
|
||||
(np.array(['a', 'b']), ['a', 'b']),
|
||||
((1, 2), ['1', '2']),
|
||||
(np.array([1, 2]), ['1', '2']),
|
||||
),
|
||||
'fail': ((dict(), ValueError),
|
||||
(1, ValueError),
|
||||
)
|
||||
},
|
||||
{'validator': validate_nseq_int(2),
|
||||
'success': ((_, [1, 2])
|
||||
for _ in ('1, 2', [1.5, 2.5], [1, 2],
|
||||
(1, 2), np.array((1, 2)))),
|
||||
'fail': ((_, ValueError)
|
||||
for _ in ('aardvark', ('a', 1),
|
||||
(1, 2, 3)
|
||||
))
|
||||
},
|
||||
{'validator': validate_nseq_float(2),
|
||||
'success': ((_, [1.5, 2.5])
|
||||
for _ in ('1.5, 2.5', [1.5, 2.5], [1.5, 2.5],
|
||||
(1.5, 2.5), np.array((1.5, 2.5)))),
|
||||
'fail': ((_, ValueError)
|
||||
for _ in ('aardvark', ('a', 1),
|
||||
(1, 2, 3)
|
||||
))
|
||||
},
|
||||
{'validator': validate_cycler,
|
||||
'success': (('cycler("color", "rgb")',
|
||||
cycler("color", 'rgb')),
|
||||
(cycler('linestyle', ['-', '--']),
|
||||
cycler('linestyle', ['-', '--'])),
|
||||
("""(cycler("color", ["r", "g", "b"]) +
|
||||
cycler("mew", [2, 3, 5]))""",
|
||||
(cycler("color", 'rgb') +
|
||||
cycler("markeredgewidth", [2, 3, 5]))),
|
||||
("cycler(c='rgb', lw=[1, 2, 3])",
|
||||
cycler('color', 'rgb') + cycler('linewidth', [1, 2, 3])),
|
||||
("cycler('c', 'rgb') * cycler('linestyle', ['-', '--'])",
|
||||
(cycler('color', 'rgb') *
|
||||
cycler('linestyle', ['-', '--']))),
|
||||
(cycler('ls', ['-', '--']),
|
||||
cycler('linestyle', ['-', '--'])),
|
||||
(cycler(mew=[2, 5]),
|
||||
cycler('markeredgewidth', [2, 5])),
|
||||
),
|
||||
# This is *so* incredibly important: validate_cycler() eval's
|
||||
# an arbitrary string! I think I have it locked down enough,
|
||||
# and that is what this is testing.
|
||||
# TODO: Note that these tests are actually insufficient, as it may
|
||||
# be that they raised errors, but still did an action prior to
|
||||
# raising the exception. We should devise some additional tests
|
||||
# for that...
|
||||
'fail': ((4, ValueError), # Gotta be a string or Cycler object
|
||||
('cycler("bleh, [])', ValueError), # syntax error
|
||||
('Cycler("linewidth", [1, 2, 3])',
|
||||
ValueError), # only 'cycler()' function is allowed
|
||||
('1 + 2', ValueError), # doesn't produce a Cycler object
|
||||
('os.system("echo Gotcha")', ValueError), # os not available
|
||||
('import os', ValueError), # should not be able to import
|
||||
('def badjuju(a): return a; badjuju(cycler("color", "rgb"))',
|
||||
ValueError), # Should not be able to define anything
|
||||
# even if it does return a cycler
|
||||
('cycler("waka", [1, 2, 3])', ValueError), # not a property
|
||||
('cycler(c=[1, 2, 3])', ValueError), # invalid values
|
||||
("cycler(lw=['a', 'b', 'c'])", ValueError), # invalid values
|
||||
(cycler('waka', [1, 3, 5]), ValueError), # not a property
|
||||
(cycler('color', ['C1', 'r', 'g']), ValueError) # no CN
|
||||
)
|
||||
},
|
||||
{'validator': validate_hatch,
|
||||
'success': (('--|', '--|'), ('\\oO', '\\oO'),
|
||||
('/+*/.x', '/+*/.x'), ('', '')),
|
||||
'fail': (('--_', ValueError),
|
||||
(8, ValueError),
|
||||
('X', ValueError)),
|
||||
},
|
||||
{'validator': validate_colorlist,
|
||||
'success': (('r,g,b', ['r', 'g', 'b']),
|
||||
(['r', 'g', 'b'], ['r', 'g', 'b']),
|
||||
('r, ,', ['r']),
|
||||
(['', 'g', 'blue'], ['g', 'blue']),
|
||||
([np.array([1, 0, 0]), np.array([0, 1, 0])],
|
||||
np.array([[1, 0, 0], [0, 1, 0]])),
|
||||
(np.array([[1, 0, 0], [0, 1, 0]]),
|
||||
np.array([[1, 0, 0], [0, 1, 0]])),
|
||||
),
|
||||
'fail': (('fish', ValueError),
|
||||
),
|
||||
},
|
||||
{'validator': validate_color,
|
||||
'success': (('None', 'none'),
|
||||
('none', 'none'),
|
||||
('AABBCC', '#AABBCC'), # RGB hex code
|
||||
('AABBCC00', '#AABBCC00'), # RGBA hex code
|
||||
('tab:blue', 'tab:blue'), # named color
|
||||
('C0', 'C0'), # color from cycle
|
||||
('(0, 1, 0)', [0.0, 1.0, 0.0]), # RGB tuple
|
||||
((0, 1, 0), (0, 1, 0)), # non-string version
|
||||
('(0, 1, 0, 1)', [0.0, 1.0, 0.0, 1.0]), # RGBA tuple
|
||||
((0, 1, 0, 1), (0, 1, 0, 1)), # non-string version
|
||||
('(0, 1, "0.5")', [0.0, 1.0, 0.5]), # unusual but valid
|
||||
|
||||
),
|
||||
'fail': (('tab:veryblue', ValueError), # invalid name
|
||||
('C123', ValueError), # invalid RGB(A) code and cycle index
|
||||
('(0, 1)', ValueError), # tuple with length < 3
|
||||
('(0, 1, 0, 1, 0)', ValueError), # tuple with length > 4
|
||||
('(0, 1, none)', ValueError), # cannot cast none to float
|
||||
),
|
||||
},
|
||||
{'validator': validate_hist_bins,
|
||||
'success': (('auto', 'auto'),
|
||||
('10', 10),
|
||||
('1, 2, 3', [1, 2, 3]),
|
||||
([1, 2, 3], [1, 2, 3]),
|
||||
(np.arange(15), np.arange(15))
|
||||
),
|
||||
'fail': (('aardvark', ValueError),
|
||||
)
|
||||
},
|
||||
{'validator': validate_markevery,
|
||||
'success': ((None, None),
|
||||
(1, 1),
|
||||
(0.1, 0.1),
|
||||
((1, 1), (1, 1)),
|
||||
((0.1, 0.1), (0.1, 0.1)),
|
||||
([1, 2, 3], [1, 2, 3]),
|
||||
(slice(2), slice(None, 2, None)),
|
||||
(slice(1, 2, 3), slice(1, 2, 3))
|
||||
),
|
||||
'fail': (((1, 2, 3), TypeError),
|
||||
([1, 2, 0.3], TypeError),
|
||||
(['a', 2, 3], TypeError),
|
||||
([1, 2, 'a'], TypeError),
|
||||
((0.1, 0.2, 0.3), TypeError),
|
||||
((0.1, 2, 3), TypeError),
|
||||
((1, 0.2, 0.3), TypeError),
|
||||
((1, 0.1), TypeError),
|
||||
((0.1, 1), TypeError),
|
||||
(('abc'), TypeError),
|
||||
((1, 'a'), TypeError),
|
||||
((0.1, 'b'), TypeError),
|
||||
(('a', 1), TypeError),
|
||||
(('a', 0.1), TypeError),
|
||||
('abc', TypeError),
|
||||
('a', TypeError),
|
||||
(object(), TypeError)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
# The behavior of _validate_linestyle depends on the version of Python.
|
||||
# ASCII-compliant bytes arguments should pass on Python 2 because of the
|
||||
# automatic conversion between bytes and strings. Python 3 does not
|
||||
# perform such a conversion, so the same cases should raise an exception.
|
||||
#
|
||||
# Common cases:
|
||||
ls_test = {'validator': _validate_linestyle,
|
||||
'success': (('-', '-'), ('solid', 'solid'),
|
||||
('--', '--'), ('dashed', 'dashed'),
|
||||
('-.', '-.'), ('dashdot', 'dashdot'),
|
||||
(':', ':'), ('dotted', 'dotted'),
|
||||
('', ''), (' ', ' '),
|
||||
('None', 'none'), ('none', 'none'),
|
||||
('DoTtEd', 'dotted'), # case-insensitive
|
||||
(['1.23', '4.56'], (None, [1.23, 4.56])),
|
||||
([1.23, 456], (None, [1.23, 456.0])),
|
||||
([1, 2, 3, 4], (None, [1.0, 2.0, 3.0, 4.0])),
|
||||
),
|
||||
'fail': (('aardvark', ValueError), # not a valid string
|
||||
('dotted'.encode('utf-16'), ValueError), # even on PY2
|
||||
((None, [1, 2]), ValueError), # (offset, dashes) != OK
|
||||
((0, [1, 2]), ValueError), # idem
|
||||
((-1, [1, 2]), ValueError), # idem
|
||||
([1, 2, 3], ValueError), # sequence with odd length
|
||||
(1.23, ValueError), # not a sequence
|
||||
)
|
||||
}
|
||||
# Add some cases of bytes arguments that Python 2 can convert silently:
|
||||
ls_bytes_args = (b'dotted', 'dotted'.encode('ascii'))
|
||||
ls_test['fail'] += tuple((arg, ValueError) for arg in ls_bytes_args)
|
||||
# Update the validation test sequence.
|
||||
validation_tests += (ls_test,)
|
||||
|
||||
for validator_dict in validation_tests:
|
||||
validator = validator_dict['validator']
|
||||
if valid:
|
||||
for arg, target in validator_dict['success']:
|
||||
yield validator, arg, target
|
||||
else:
|
||||
for arg, error_type in validator_dict['fail']:
|
||||
yield validator, arg, error_type
|
||||
|
||||
|
||||
@pytest.mark.parametrize('validator, arg, target',
|
||||
generate_validator_testcases(True))
|
||||
def test_validator_valid(validator, arg, target):
|
||||
res = validator(arg)
|
||||
if isinstance(target, np.ndarray):
|
||||
assert np.all(res == target)
|
||||
elif not isinstance(target, Cycler):
|
||||
assert res == target
|
||||
else:
|
||||
# Cyclers can't simply be asserted equal. They don't implement __eq__
|
||||
assert list(res) == list(target)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('validator, arg, exception_type',
|
||||
generate_validator_testcases(False))
|
||||
def test_validator_invalid(validator, arg, exception_type):
|
||||
with pytest.raises(exception_type):
|
||||
validator(arg)
|
||||
|
||||
|
||||
def test_keymaps():
|
||||
key_list = [k for k in mpl.rcParams if 'keymap' in k]
|
||||
for k in key_list:
|
||||
assert isinstance(mpl.rcParams[k], list)
|
||||
|
||||
|
||||
def test_rcparams_reset_after_fail():
|
||||
|
||||
# There was previously a bug that meant that if rc_context failed and
|
||||
# raised an exception due to issues in the supplied rc parameters, the
|
||||
# global rc parameters were left in a modified state.
|
||||
|
||||
with mpl.rc_context(rc={'text.usetex': False}):
|
||||
|
||||
assert mpl.rcParams['text.usetex'] is False
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
with mpl.rc_context(rc=OrderedDict([('text.usetex', True),
|
||||
('test.blah', True)])):
|
||||
pass
|
||||
|
||||
assert mpl.rcParams['text.usetex'] is False
|
||||
|
||||
|
||||
def test_if_rctemplate_is_up_to_date():
|
||||
# This tests if the matplotlibrc.template file contains all valid rcParams.
|
||||
deprecated = {*mpl._all_deprecated, *mpl._deprecated_remain_as_none}
|
||||
path_to_rc = os.path.join(mpl.get_data_path(), 'matplotlibrc')
|
||||
with open(path_to_rc, "r") as f:
|
||||
rclines = f.readlines()
|
||||
missing = {}
|
||||
for k, v in mpl.defaultParams.items():
|
||||
if k[0] == "_":
|
||||
continue
|
||||
if k in deprecated:
|
||||
continue
|
||||
if k.startswith(
|
||||
("verbose.", "examples.directory", "text.latex.unicode")):
|
||||
continue
|
||||
found = False
|
||||
for line in rclines:
|
||||
if k in line:
|
||||
found = True
|
||||
if not found:
|
||||
missing.update({k: v})
|
||||
if missing:
|
||||
raise ValueError("The following params are missing in the "
|
||||
"matplotlibrc.template file: {}"
|
||||
.format(missing.items()))
|
||||
|
||||
|
||||
def test_if_rctemplate_would_be_valid(tmpdir):
|
||||
# This tests if the matplotlibrc.template file would result in a valid
|
||||
# rc file if all lines are uncommented.
|
||||
path_to_rc = os.path.join(mpl.get_data_path(), 'matplotlibrc')
|
||||
with open(path_to_rc, "r") as f:
|
||||
rclines = f.readlines()
|
||||
newlines = []
|
||||
for line in rclines:
|
||||
if line[0] == "#":
|
||||
newline = line[1:]
|
||||
else:
|
||||
newline = line
|
||||
if "$TEMPLATE_BACKEND" in newline:
|
||||
newline = "backend : Agg"
|
||||
if "datapath" in newline:
|
||||
newline = ""
|
||||
newlines.append(newline)
|
||||
d = tmpdir.mkdir('test1')
|
||||
fname = str(d.join('testrcvalid.temp'))
|
||||
with open(fname, "w") as f:
|
||||
f.writelines(newlines)
|
||||
with pytest.warns(None) as record:
|
||||
mpl.rc_params_from_file(fname,
|
||||
fail_on_error=True,
|
||||
use_default_template=False)
|
||||
assert len(record) == 0
|
||||
@@ -0,0 +1,3 @@
|
||||
# this file is used by the tests in test_rcparams.py
|
||||
|
||||
lines.linewidth: 33
|
||||
@@ -0,0 +1,7 @@
|
||||
from matplotlib.sankey import Sankey
|
||||
|
||||
|
||||
def test_sankey():
|
||||
# lets just create a sankey instance and check the code runs
|
||||
sankey = Sankey()
|
||||
sankey.add()
|
||||
@@ -0,0 +1,150 @@
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.scale import Log10Transform, InvertedLog10Transform
|
||||
import numpy as np
|
||||
import io
|
||||
import platform
|
||||
import pytest
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['log_scales'], remove_text=True)
|
||||
def test_log_scales():
|
||||
ax = plt.figure().add_subplot(122, yscale='log', xscale='symlog')
|
||||
|
||||
ax.axvline(24.1)
|
||||
ax.axhline(24.1)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['logit_scales'], remove_text=True,
|
||||
extensions=['png'])
|
||||
def test_logit_scales():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
# Typical extinction curve for logit
|
||||
x = np.array([0.001, 0.003, 0.01, 0.03, 0.1, 0.2, 0.3, 0.4, 0.5,
|
||||
0.6, 0.7, 0.8, 0.9, 0.97, 0.99, 0.997, 0.999])
|
||||
y = 1.0 / x
|
||||
|
||||
ax.plot(x, y)
|
||||
ax.set_xscale('logit')
|
||||
ax.grid(True)
|
||||
bbox = ax.get_tightbbox(fig.canvas.get_renderer())
|
||||
assert np.isfinite(bbox.x0)
|
||||
assert np.isfinite(bbox.y0)
|
||||
|
||||
|
||||
def test_log_scatter():
|
||||
"""Issue #1799"""
|
||||
fig, ax = plt.subplots(1)
|
||||
|
||||
x = np.arange(10)
|
||||
y = np.arange(10) - 1
|
||||
|
||||
ax.scatter(x, y)
|
||||
|
||||
buf = io.BytesIO()
|
||||
fig.savefig(buf, format='pdf')
|
||||
|
||||
buf = io.BytesIO()
|
||||
fig.savefig(buf, format='eps')
|
||||
|
||||
buf = io.BytesIO()
|
||||
fig.savefig(buf, format='svg')
|
||||
|
||||
|
||||
def test_logscale_subs():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_yscale('log', subsy=np.array([2, 3, 4]))
|
||||
# force draw
|
||||
fig.canvas.draw()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['logscale_mask'], remove_text=True,
|
||||
extensions=['png'])
|
||||
def test_logscale_mask():
|
||||
# Check that zero values are masked correctly on log scales.
|
||||
# See github issue 8045
|
||||
xs = np.linspace(0, 50, 1001)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(np.exp(-xs**2))
|
||||
fig.canvas.draw()
|
||||
ax.set(yscale="log")
|
||||
|
||||
|
||||
def test_extra_kwargs_raise():
|
||||
fig, ax = plt.subplots()
|
||||
with pytest.raises(ValueError):
|
||||
ax.set_yscale('log', nonpos='mask')
|
||||
|
||||
|
||||
def test_logscale_invert_transform():
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_yscale('log')
|
||||
# get transformation from data to axes
|
||||
tform = (ax.transAxes + ax.transData.inverted()).inverted()
|
||||
|
||||
# direct test of log transform inversion
|
||||
assert isinstance(Log10Transform().inverted(), InvertedLog10Transform)
|
||||
|
||||
|
||||
def test_logscale_transform_repr():
|
||||
# check that repr of log transform succeeds
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_yscale('log')
|
||||
s = repr(ax.transData)
|
||||
|
||||
# check that repr of log transform succeeds
|
||||
s = repr(Log10Transform(nonpos='clip'))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['logscale_nonpos_values'], remove_text=True,
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
extensions=['png'], style='mpl20')
|
||||
def test_logscale_nonpos_values():
|
||||
np.random.seed(19680801)
|
||||
xs = np.random.normal(size=int(1e3))
|
||||
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
|
||||
ax1.hist(xs, range=(-5, 5), bins=10)
|
||||
ax1.set_yscale('log')
|
||||
ax2.hist(xs, range=(-5, 5), bins=10)
|
||||
ax2.set_yscale('log', nonposy='mask')
|
||||
|
||||
xdata = np.arange(0, 10, 0.01)
|
||||
ydata = np.exp(-xdata)
|
||||
edata = 0.2*(10-xdata)*np.cos(5*xdata)*np.exp(-xdata)
|
||||
|
||||
ax3.fill_between(xdata, ydata - edata, ydata + edata)
|
||||
ax3.set_yscale('log')
|
||||
|
||||
x = np.logspace(-1, 1)
|
||||
y = x ** 3
|
||||
yerr = x**2
|
||||
ax4.errorbar(x, y, yerr=yerr)
|
||||
|
||||
ax4.set_yscale('log')
|
||||
ax4.set_xscale('log')
|
||||
|
||||
|
||||
def test_invalid_log_lims():
|
||||
# Check that invalid log scale limits are ignored
|
||||
fig, ax = plt.subplots()
|
||||
ax.scatter(range(0, 4), range(0, 4))
|
||||
|
||||
ax.set_xscale('log')
|
||||
original_xlim = ax.get_xlim()
|
||||
with pytest.warns(UserWarning):
|
||||
ax.set_xlim(left=0)
|
||||
assert ax.get_xlim() == original_xlim
|
||||
with pytest.warns(UserWarning):
|
||||
ax.set_xlim(right=-1)
|
||||
assert ax.get_xlim() == original_xlim
|
||||
|
||||
ax.set_yscale('log')
|
||||
original_ylim = ax.get_ylim()
|
||||
with pytest.warns(UserWarning):
|
||||
ax.set_ylim(bottom=0)
|
||||
assert ax.get_ylim() == original_ylim
|
||||
with pytest.warns(UserWarning):
|
||||
ax.set_ylim(top=-1)
|
||||
assert ax.get_ylim() == original_ylim
|
||||
@@ -0,0 +1,339 @@
|
||||
import base64
|
||||
import io
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_almost_equal, assert_array_equal
|
||||
|
||||
import pytest
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from matplotlib import patches, transforms
|
||||
from matplotlib.path import Path
|
||||
|
||||
|
||||
# NOTE: All of these tests assume that path.simplify is set to True
|
||||
# (the default)
|
||||
|
||||
@image_comparison(baseline_images=['clipping'], remove_text=True)
|
||||
def test_clipping():
|
||||
t = np.arange(0.0, 2.0, 0.01)
|
||||
s = np.sin(2*np.pi*t)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(t, s, linewidth=1.0)
|
||||
ax.set_ylim((-0.20, -0.28))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['overflow'], remove_text=True)
|
||||
def test_overflow():
|
||||
x = np.array([1.0, 2.0, 3.0, 2.0e5])
|
||||
y = np.arange(len(x))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y)
|
||||
ax.set_xlim(2, 6)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['clipping_diamond'], remove_text=True)
|
||||
def test_diamond():
|
||||
x = np.array([0.0, 1.0, 0.0, -1.0, 0.0])
|
||||
y = np.array([1.0, 0.0, -1.0, 0.0, 1.0])
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y)
|
||||
ax.set_xlim(-0.6, 0.6)
|
||||
ax.set_ylim(-0.6, 0.6)
|
||||
|
||||
|
||||
def test_noise():
|
||||
np.random.seed(0)
|
||||
x = np.random.uniform(size=(50000,)) * 50
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
p1 = ax.plot(x, solid_joinstyle='round', linewidth=2.0)
|
||||
|
||||
path = p1[0].get_path()
|
||||
transform = p1[0].get_transform()
|
||||
path = transform.transform_path(path)
|
||||
simplified = path.cleaned(simplify=True)
|
||||
|
||||
assert simplified.vertices.size == 25512
|
||||
|
||||
|
||||
def test_antiparallel_simplification():
|
||||
def _get_simplified(x, y):
|
||||
fig, ax = plt.subplots()
|
||||
p1 = ax.plot(x, y)
|
||||
|
||||
path = p1[0].get_path()
|
||||
transform = p1[0].get_transform()
|
||||
path = transform.transform_path(path)
|
||||
simplified = path.cleaned(simplify=True)
|
||||
simplified = transform.inverted().transform_path(simplified)
|
||||
|
||||
return simplified
|
||||
|
||||
# test ending on a maximum
|
||||
x = [0, 0, 0, 0, 0, 1]
|
||||
y = [.5, 1, -1, 1, 2, .5]
|
||||
|
||||
simplified = _get_simplified(x, y)
|
||||
|
||||
assert_array_almost_equal([[0., 0.5],
|
||||
[0., -1.],
|
||||
[0., 2.],
|
||||
[1., 0.5]],
|
||||
simplified.vertices[:-2, :])
|
||||
|
||||
# test ending on a minimum
|
||||
x = [0, 0, 0, 0, 0, 1]
|
||||
y = [.5, 1, -1, 1, -2, .5]
|
||||
|
||||
simplified = _get_simplified(x, y)
|
||||
|
||||
assert_array_almost_equal([[0., 0.5],
|
||||
[0., 1.],
|
||||
[0., -2.],
|
||||
[1., 0.5]],
|
||||
simplified.vertices[:-2, :])
|
||||
|
||||
# test ending in between
|
||||
x = [0, 0, 0, 0, 0, 1]
|
||||
y = [.5, 1, -1, 1, 0, .5]
|
||||
|
||||
simplified = _get_simplified(x, y)
|
||||
|
||||
assert_array_almost_equal([[0., 0.5],
|
||||
[0., 1.],
|
||||
[0., -1.],
|
||||
[0., 0.],
|
||||
[1., 0.5]],
|
||||
simplified.vertices[:-2, :])
|
||||
|
||||
# test no anti-parallel ending at max
|
||||
x = [0, 0, 0, 0, 0, 1]
|
||||
y = [.5, 1, 2, 1, 3, .5]
|
||||
|
||||
simplified = _get_simplified(x, y)
|
||||
|
||||
assert_array_almost_equal([[0., 0.5],
|
||||
[0., 3.],
|
||||
[1., 0.5]],
|
||||
simplified.vertices[:-2, :])
|
||||
|
||||
# test no anti-parallel ending in middle
|
||||
x = [0, 0, 0, 0, 0, 1]
|
||||
y = [.5, 1, 2, 1, 1, .5]
|
||||
|
||||
simplified = _get_simplified(x, y)
|
||||
|
||||
assert_array_almost_equal([[0., 0.5],
|
||||
[0., 2.],
|
||||
[0., 1.],
|
||||
[1., 0.5]],
|
||||
simplified.vertices[:-2, :])
|
||||
|
||||
|
||||
# Only consider angles in 0 <= angle <= pi/2, otherwise
|
||||
# using min/max will get the expected results out of order:
|
||||
# min/max for simplification code depends on original vector,
|
||||
# and if angle is outside above range then simplification
|
||||
# min/max will be opposite from actual min/max.
|
||||
@pytest.mark.parametrize('angle', [0, np.pi/4, np.pi/3, np.pi/2])
|
||||
@pytest.mark.parametrize('offset', [0, .5])
|
||||
def test_angled_antiparallel(angle, offset):
|
||||
scale = 5
|
||||
np.random.seed(19680801)
|
||||
# get 15 random offsets
|
||||
# TODO: guarantee offset > 0 results in some offsets < 0
|
||||
vert_offsets = (np.random.rand(15) - offset) * scale
|
||||
# always start at 0 so rotation makes sense
|
||||
vert_offsets[0] = 0
|
||||
# always take the first step the same direction
|
||||
vert_offsets[1] = 1
|
||||
# compute points along a diagonal line
|
||||
x = np.sin(angle) * vert_offsets
|
||||
y = np.cos(angle) * vert_offsets
|
||||
|
||||
# will check these later
|
||||
x_max = x[1:].max()
|
||||
x_min = x[1:].min()
|
||||
|
||||
y_max = y[1:].max()
|
||||
y_min = y[1:].min()
|
||||
|
||||
if offset > 0:
|
||||
p_expected = Path([[0, 0],
|
||||
[x_max, y_max],
|
||||
[x_min, y_min],
|
||||
[x[-1], y[-1]],
|
||||
[0, 0]],
|
||||
codes=[1, 2, 2, 2, 0])
|
||||
|
||||
else:
|
||||
p_expected = Path([[0, 0],
|
||||
[x_max, y_max],
|
||||
[x[-1], y[-1]],
|
||||
[0, 0]],
|
||||
codes=[1, 2, 2, 0])
|
||||
|
||||
p = Path(np.vstack([x, y]).T)
|
||||
p2 = p.cleaned(simplify=True)
|
||||
|
||||
assert_array_almost_equal(p_expected.vertices,
|
||||
p2.vertices)
|
||||
assert_array_equal(p_expected.codes, p2.codes)
|
||||
|
||||
|
||||
def test_sine_plus_noise():
|
||||
np.random.seed(0)
|
||||
x = (np.sin(np.linspace(0, np.pi * 2.0, 50000)) +
|
||||
np.random.uniform(size=(50000,)) * 0.01)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
p1 = ax.plot(x, solid_joinstyle='round', linewidth=2.0)
|
||||
|
||||
path = p1[0].get_path()
|
||||
transform = p1[0].get_transform()
|
||||
path = transform.transform_path(path)
|
||||
simplified = path.cleaned(simplify=True)
|
||||
|
||||
assert simplified.vertices.size == 25240
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['simplify_curve'], remove_text=True)
|
||||
def test_simplify_curve():
|
||||
pp1 = patches.PathPatch(
|
||||
Path([(0, 0), (1, 0), (1, 1), (np.nan, 1), (0, 0), (2, 0), (2, 2),
|
||||
(0, 0)],
|
||||
[Path.MOVETO, Path.CURVE3, Path.CURVE3, Path.CURVE3, Path.CURVE3,
|
||||
Path.CURVE3, Path.CURVE3, Path.CLOSEPOLY]),
|
||||
fc="none")
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.add_patch(pp1)
|
||||
ax.set_xlim((0, 2))
|
||||
ax.set_ylim((0, 2))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['hatch_simplify'], remove_text=True)
|
||||
def test_hatch():
|
||||
fig, ax = plt.subplots()
|
||||
ax.add_patch(plt.Rectangle((0, 0), 1, 1, fill=False, hatch="/"))
|
||||
ax.set_xlim((0.45, 0.55))
|
||||
ax.set_ylim((0.45, 0.55))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['fft_peaks'], remove_text=True)
|
||||
def test_fft_peaks():
|
||||
fig, ax = plt.subplots()
|
||||
t = np.arange(65536)
|
||||
p1 = ax.plot(abs(np.fft.fft(np.sin(2*np.pi*.01*t)*np.blackman(len(t)))))
|
||||
|
||||
path = p1[0].get_path()
|
||||
transform = p1[0].get_transform()
|
||||
path = transform.transform_path(path)
|
||||
simplified = path.cleaned(simplify=True)
|
||||
|
||||
assert simplified.vertices.size == 36
|
||||
|
||||
|
||||
def test_start_with_moveto():
|
||||
# Should be entirely clipped away to a single MOVETO
|
||||
data = b"""
|
||||
ZwAAAAku+v9UAQAA+Tj6/z8CAADpQ/r/KAMAANlO+v8QBAAAyVn6//UEAAC6ZPr/2gUAAKpv+v+8
|
||||
BgAAm3r6/50HAACLhfr/ewgAAHyQ+v9ZCQAAbZv6/zQKAABepvr/DgsAAE+x+v/lCwAAQLz6/7wM
|
||||
AAAxx/r/kA0AACPS+v9jDgAAFN36/zQPAAAF6Pr/AxAAAPfy+v/QEAAA6f36/5wRAADbCPv/ZhIA
|
||||
AMwT+/8uEwAAvh77//UTAACwKfv/uRQAAKM0+/98FQAAlT/7/z0WAACHSvv//RYAAHlV+/+7FwAA
|
||||
bGD7/3cYAABea/v/MRkAAFF2+//pGQAARIH7/6AaAAA3jPv/VRsAACmX+/8JHAAAHKL7/7ocAAAP
|
||||
rfv/ah0AAAO4+/8YHgAA9sL7/8QeAADpzfv/bx8AANzY+/8YIAAA0OP7/78gAADD7vv/ZCEAALf5
|
||||
+/8IIgAAqwT8/6kiAACeD/z/SiMAAJIa/P/oIwAAhiX8/4QkAAB6MPz/HyUAAG47/P+4JQAAYkb8
|
||||
/1AmAABWUfz/5SYAAEpc/P95JwAAPmf8/wsoAAAzcvz/nCgAACd9/P8qKQAAHIj8/7cpAAAQk/z/
|
||||
QyoAAAWe/P/MKgAA+aj8/1QrAADus/z/2isAAOO+/P9eLAAA2Mn8/+AsAADM1Pz/YS0AAMHf/P/g
|
||||
LQAAtur8/10uAACr9fz/2C4AAKEA/f9SLwAAlgv9/8ovAACLFv3/QDAAAIAh/f+1MAAAdSz9/ycx
|
||||
AABrN/3/mDEAAGBC/f8IMgAAVk39/3UyAABLWP3/4TIAAEFj/f9LMwAANm79/7MzAAAsef3/GjQA
|
||||
ACKE/f9+NAAAF4/9/+E0AAANmv3/QzUAAAOl/f+iNQAA+a/9/wA2AADvuv3/XDYAAOXF/f+2NgAA
|
||||
29D9/w83AADR2/3/ZjcAAMfm/f+7NwAAvfH9/w44AACz/P3/XzgAAKkH/v+vOAAAnxL+//04AACW
|
||||
Hf7/SjkAAIwo/v+UOQAAgjP+/905AAB5Pv7/JDoAAG9J/v9pOgAAZVT+/606AABcX/7/7zoAAFJq
|
||||
/v8vOwAASXX+/207AAA/gP7/qjsAADaL/v/lOwAALZb+/x48AAAjof7/VTwAABqs/v+LPAAAELf+
|
||||
/788AAAHwv7/8TwAAP7M/v8hPQAA9df+/1A9AADr4v7/fT0AAOLt/v+oPQAA2fj+/9E9AADQA///
|
||||
+T0AAMYO//8fPgAAvRn//0M+AAC0JP//ZT4AAKsv//+GPgAAojr//6U+AACZRf//wj4AAJBQ///d
|
||||
PgAAh1v///c+AAB+Zv//Dz8AAHRx//8lPwAAa3z//zk/AABih///TD8AAFmS//9dPwAAUJ3//2w/
|
||||
AABHqP//ej8AAD6z//+FPwAANb7//48/AAAsyf//lz8AACPU//+ePwAAGt///6M/AAAR6v//pj8A
|
||||
AAj1//+nPwAA/////w=="""
|
||||
|
||||
verts = np.fromstring(base64.decodebytes(data), dtype='<i4')
|
||||
verts = verts.reshape((len(verts) // 2, 2))
|
||||
path = Path(verts)
|
||||
segs = path.iter_segments(transforms.IdentityTransform(),
|
||||
clip=(0.0, 0.0, 100.0, 100.0))
|
||||
segs = list(segs)
|
||||
assert len(segs) == 1
|
||||
assert segs[0][1] == Path.MOVETO
|
||||
|
||||
|
||||
def test_throw_rendering_complexity_exceeded():
|
||||
plt.rcParams['path.simplify'] = False
|
||||
xx = np.arange(200000)
|
||||
yy = np.random.rand(200000)
|
||||
yy[1000] = np.nan
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(xx, yy)
|
||||
with pytest.raises(OverflowError):
|
||||
fig.savefig(io.BytesIO())
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['clipper_edge'], remove_text=True)
|
||||
def test_clipper():
|
||||
dat = (0, 1, 0, 2, 0, 3, 0, 4, 0, 5)
|
||||
fig = plt.figure(figsize=(2, 1))
|
||||
fig.subplots_adjust(left=0, bottom=0, wspace=0, hspace=0)
|
||||
|
||||
ax = fig.add_axes((0, 0, 1.0, 1.0), ylim=(0, 5), autoscale_on=False)
|
||||
ax.plot(dat)
|
||||
ax.xaxis.set_major_locator(plt.MultipleLocator(1))
|
||||
ax.yaxis.set_major_locator(plt.MultipleLocator(1))
|
||||
ax.xaxis.set_ticks_position('bottom')
|
||||
ax.yaxis.set_ticks_position('left')
|
||||
|
||||
ax.set_xlim(5, 9)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['para_equal_perp'], remove_text=True)
|
||||
def test_para_equal_perp():
|
||||
x = np.array([0, 1, 2, 1, 0, -1, 0, 1] + [1] * 128)
|
||||
y = np.array([1, 1, 2, 1, 0, -1, 0, 0] + [0] * 128)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x + 1, y + 1)
|
||||
ax.plot(x + 1, y + 1, 'ro')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['clipping_with_nans'])
|
||||
def test_clipping_with_nans():
|
||||
x = np.linspace(0, 3.14 * 2, 3000)
|
||||
y = np.sin(x)
|
||||
x[::100] = np.nan
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(x, y)
|
||||
ax.set_ylim(-0.25, 0.25)
|
||||
|
||||
|
||||
def test_clipping_full():
|
||||
p = Path([[1e30, 1e30]] * 5)
|
||||
simplified = list(p.iter_segments(clip=[0, 0, 100, 100]))
|
||||
assert simplified == []
|
||||
|
||||
p = Path([[50, 40], [75, 65]], [1, 2])
|
||||
simplified = list(p.iter_segments(clip=[0, 0, 100, 100]))
|
||||
assert ([(list(x), y) for x, y in simplified] ==
|
||||
[([50, 40], 1), ([75, 65], 2)])
|
||||
|
||||
p = Path([[50, 40]], [1])
|
||||
simplified = list(p.iter_segments(clip=[0, 0, 100, 100]))
|
||||
assert ([(list(x), y) for x, y in simplified] ==
|
||||
[([50, 40], 1)])
|
||||
@@ -0,0 +1,209 @@
|
||||
"""
|
||||
Testing that skewed axes properly work
|
||||
"""
|
||||
import itertools
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
from matplotlib.axes import Axes
|
||||
import matplotlib.transforms as transforms
|
||||
import matplotlib.axis as maxis
|
||||
import matplotlib.spines as mspines
|
||||
import matplotlib.patches as mpatch
|
||||
from matplotlib.projections import register_projection
|
||||
|
||||
|
||||
# The sole purpose of this class is to look at the upper, lower, or total
|
||||
# interval as appropriate and see what parts of the tick to draw, if any.
|
||||
class SkewXTick(maxis.XTick):
|
||||
def update_position(self, loc):
|
||||
# This ensures that the new value of the location is set before
|
||||
# any other updates take place
|
||||
self._loc = loc
|
||||
super().update_position(loc)
|
||||
|
||||
def _has_default_loc(self):
|
||||
return self.get_loc() is None
|
||||
|
||||
def _need_lower(self):
|
||||
return (self._has_default_loc() or
|
||||
transforms.interval_contains(self.axes.lower_xlim,
|
||||
self.get_loc()))
|
||||
|
||||
def _need_upper(self):
|
||||
return (self._has_default_loc() or
|
||||
transforms.interval_contains(self.axes.upper_xlim,
|
||||
self.get_loc()))
|
||||
|
||||
@property
|
||||
def gridOn(self):
|
||||
return (self._gridOn and (self._has_default_loc() or
|
||||
transforms.interval_contains(self.get_view_interval(),
|
||||
self.get_loc())))
|
||||
|
||||
@gridOn.setter
|
||||
def gridOn(self, value):
|
||||
self._gridOn = value
|
||||
|
||||
@property
|
||||
def tick1On(self):
|
||||
return self._tick1On and self._need_lower()
|
||||
|
||||
@tick1On.setter
|
||||
def tick1On(self, value):
|
||||
self._tick1On = value
|
||||
|
||||
@property
|
||||
def label1On(self):
|
||||
return self._label1On and self._need_lower()
|
||||
|
||||
@label1On.setter
|
||||
def label1On(self, value):
|
||||
self._label1On = value
|
||||
|
||||
@property
|
||||
def tick2On(self):
|
||||
return self._tick2On and self._need_upper()
|
||||
|
||||
@tick2On.setter
|
||||
def tick2On(self, value):
|
||||
self._tick2On = value
|
||||
|
||||
@property
|
||||
def label2On(self):
|
||||
return self._label2On and self._need_upper()
|
||||
|
||||
@label2On.setter
|
||||
def label2On(self, value):
|
||||
self._label2On = value
|
||||
|
||||
def get_view_interval(self):
|
||||
return self.axes.xaxis.get_view_interval()
|
||||
|
||||
|
||||
# This class exists to provide two separate sets of intervals to the tick,
|
||||
# as well as create instances of the custom tick
|
||||
class SkewXAxis(maxis.XAxis):
|
||||
def _get_tick(self, major):
|
||||
return SkewXTick(self.axes, None, '', major=major)
|
||||
|
||||
def get_view_interval(self):
|
||||
return self.axes.upper_xlim[0], self.axes.lower_xlim[1]
|
||||
|
||||
|
||||
# This class exists to calculate the separate data range of the
|
||||
# upper X-axis and draw the spine there. It also provides this range
|
||||
# to the X-axis artist for ticking and gridlines
|
||||
class SkewSpine(mspines.Spine):
|
||||
def _adjust_location(self):
|
||||
pts = self._path.vertices
|
||||
if self.spine_type == 'top':
|
||||
pts[:, 0] = self.axes.upper_xlim
|
||||
else:
|
||||
pts[:, 0] = self.axes.lower_xlim
|
||||
|
||||
|
||||
# This class handles registration of the skew-xaxes as a projection as well
|
||||
# as setting up the appropriate transformations. It also overrides standard
|
||||
# spines and axes instances as appropriate.
|
||||
class SkewXAxes(Axes):
|
||||
# The projection must specify a name. This will be used be the
|
||||
# user to select the projection, i.e. ``subplot(111,
|
||||
# projection='skewx')``.
|
||||
name = 'skewx'
|
||||
|
||||
def _init_axis(self):
|
||||
# Taken from Axes and modified to use our modified X-axis
|
||||
self.xaxis = SkewXAxis(self)
|
||||
self.spines['top'].register_axis(self.xaxis)
|
||||
self.spines['bottom'].register_axis(self.xaxis)
|
||||
self.yaxis = maxis.YAxis(self)
|
||||
self.spines['left'].register_axis(self.yaxis)
|
||||
self.spines['right'].register_axis(self.yaxis)
|
||||
|
||||
def _gen_axes_spines(self):
|
||||
spines = {'top': SkewSpine.linear_spine(self, 'top'),
|
||||
'bottom': mspines.Spine.linear_spine(self, 'bottom'),
|
||||
'left': mspines.Spine.linear_spine(self, 'left'),
|
||||
'right': mspines.Spine.linear_spine(self, 'right')}
|
||||
return spines
|
||||
|
||||
def _set_lim_and_transforms(self):
|
||||
"""
|
||||
This is called once when the plot is created to set up all the
|
||||
transforms for the data, text and grids.
|
||||
"""
|
||||
rot = 30
|
||||
|
||||
# Get the standard transform setup from the Axes base class
|
||||
Axes._set_lim_and_transforms(self)
|
||||
|
||||
# Need to put the skew in the middle, after the scale and limits,
|
||||
# but before the transAxes. This way, the skew is done in Axes
|
||||
# coordinates thus performing the transform around the proper origin
|
||||
# We keep the pre-transAxes transform around for other users, like the
|
||||
# spines for finding bounds
|
||||
self.transDataToAxes = (self.transScale +
|
||||
(self.transLimits +
|
||||
transforms.Affine2D().skew_deg(rot, 0)))
|
||||
|
||||
# Create the full transform from Data to Pixels
|
||||
self.transData = self.transDataToAxes + self.transAxes
|
||||
|
||||
# Blended transforms like this need to have the skewing applied using
|
||||
# both axes, in axes coords like before.
|
||||
self._xaxis_transform = (transforms.blended_transform_factory(
|
||||
self.transScale + self.transLimits,
|
||||
transforms.IdentityTransform()) +
|
||||
transforms.Affine2D().skew_deg(rot, 0)) + self.transAxes
|
||||
|
||||
@property
|
||||
def lower_xlim(self):
|
||||
return self.axes.viewLim.intervalx
|
||||
|
||||
@property
|
||||
def upper_xlim(self):
|
||||
pts = [[0., 1.], [1., 1.]]
|
||||
return self.transDataToAxes.inverted().transform(pts)[:, 0]
|
||||
|
||||
|
||||
# Now register the projection with matplotlib so the user can select
|
||||
# it.
|
||||
register_projection(SkewXAxes)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['skew_axes'], remove_text=True)
|
||||
def test_set_line_coll_dash_image():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1, projection='skewx')
|
||||
ax.set_xlim(-50, 50)
|
||||
ax.set_ylim(50, -50)
|
||||
ax.grid(True)
|
||||
|
||||
# An example of a slanted line at constant X
|
||||
ax.axvline(0, color='b')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['skew_rects'], remove_text=True)
|
||||
def test_skew_rectangle():
|
||||
|
||||
fix, axes = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(8, 8))
|
||||
axes = axes.flat
|
||||
|
||||
rotations = list(itertools.product([-3, -1, 0, 1, 3], repeat=2))
|
||||
|
||||
axes[0].set_xlim([-3, 3])
|
||||
axes[0].set_ylim([-3, 3])
|
||||
axes[0].set_aspect('equal', share=True)
|
||||
|
||||
for ax, (xrots, yrots) in zip(axes, rotations):
|
||||
xdeg, ydeg = 45 * xrots, 45 * yrots
|
||||
t = transforms.Affine2D().skew_deg(xdeg, ydeg)
|
||||
|
||||
ax.set_title('Skew of {0} in X and {1} in Y'.format(xdeg, ydeg))
|
||||
ax.add_patch(mpatch.Rectangle([-1, -1], 2, 2,
|
||||
transform=t + ax.transData,
|
||||
alpha=0.5, facecolor='coral'))
|
||||
|
||||
plt.subplots_adjust(wspace=0, left=0.01, right=0.99, bottom=0.01, top=0.99)
|
||||
@@ -0,0 +1,73 @@
|
||||
import numpy as np
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['spines_axes_positions'])
|
||||
def test_spines_axes_positions():
|
||||
# SF bug 2852168
|
||||
fig = plt.figure()
|
||||
x = np.linspace(0, 2*np.pi, 100)
|
||||
y = 2*np.sin(x)
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.set_title('centered spines')
|
||||
ax.plot(x, y)
|
||||
ax.spines['right'].set_position(('axes', 0.1))
|
||||
ax.yaxis.set_ticks_position('right')
|
||||
ax.spines['top'].set_position(('axes', 0.25))
|
||||
ax.xaxis.set_ticks_position('top')
|
||||
ax.spines['left'].set_color('none')
|
||||
ax.spines['bottom'].set_color('none')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['spines_data_positions'])
|
||||
def test_spines_data_positions():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.spines['left'].set_position(('data', -1.5))
|
||||
ax.spines['top'].set_position(('data', 0.5))
|
||||
ax.spines['right'].set_position(('data', -0.5))
|
||||
ax.spines['bottom'].set_position('zero')
|
||||
ax.set_xlim([-2, 2])
|
||||
ax.set_ylim([-2, 2])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['spines_capstyle'])
|
||||
def test_spines_capstyle():
|
||||
# issue 2542
|
||||
plt.rc('axes', linewidth=20)
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
|
||||
|
||||
def test_label_without_ticks():
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(1, 1, 1)
|
||||
plt.subplots_adjust(left=0.3, bottom=0.3)
|
||||
ax.plot(np.arange(10))
|
||||
ax.yaxis.set_ticks_position('left')
|
||||
ax.spines['left'].set_position(('outward', 30))
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.set_ylabel('y label')
|
||||
ax.xaxis.set_ticks_position('bottom')
|
||||
ax.spines['bottom'].set_position(('outward', 30))
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.set_xlabel('x label')
|
||||
ax.xaxis.set_ticks([])
|
||||
ax.yaxis.set_ticks([])
|
||||
plt.draw()
|
||||
|
||||
spine = ax.spines['left']
|
||||
spinebbox = spine.get_transform().transform_path(
|
||||
spine.get_path()).get_extents()
|
||||
assert ax.yaxis.label.get_position()[0] < spinebbox.xmin, \
|
||||
"Y-Axis label not left of the spine"
|
||||
|
||||
spine = ax.spines['bottom']
|
||||
spinebbox = spine.get_transform().transform_path(
|
||||
spine.get_path()).get_extents()
|
||||
assert ax.xaxis.label.get_position()[1] < spinebbox.ymin, \
|
||||
"X-Axis label not below the spine"
|
||||
@@ -0,0 +1,104 @@
|
||||
import sys
|
||||
import platform
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_almost_equal
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.transforms as mtransforms
|
||||
|
||||
|
||||
on_win = (sys.platform == 'win32')
|
||||
|
||||
|
||||
def velocity_field():
|
||||
Y, X = np.mgrid[-3:3:100j, -3:3:100j]
|
||||
U = -1 - X**2 + Y
|
||||
V = 1 + X - Y**2
|
||||
return X, Y, U, V
|
||||
|
||||
|
||||
def swirl_velocity_field():
|
||||
x = np.linspace(-3., 3., 100)
|
||||
y = np.linspace(-3., 3., 100)
|
||||
X, Y = np.meshgrid(x, y)
|
||||
a = 0.1
|
||||
U = np.cos(a) * (-Y) - np.sin(a) * X
|
||||
V = np.sin(a) * (-Y) + np.cos(a) * X
|
||||
return x, y, U, V
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['streamplot_startpoints'],
|
||||
remove_text=True, style='mpl20')
|
||||
def test_startpoints():
|
||||
X, Y, U, V = velocity_field()
|
||||
start_x = np.linspace(X.min(), X.max(), 10)
|
||||
start_y = np.linspace(Y.min(), Y.max(), 10)
|
||||
start_points = np.column_stack([start_x, start_y])
|
||||
plt.streamplot(X, Y, U, V, start_points=start_points)
|
||||
plt.plot(start_x, start_y, 'ok')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['streamplot_colormap'],
|
||||
tol=.04, remove_text=True, style='mpl20')
|
||||
def test_colormap():
|
||||
X, Y, U, V = velocity_field()
|
||||
plt.streamplot(X, Y, U, V, color=U, density=0.6, linewidth=2,
|
||||
cmap=plt.cm.autumn)
|
||||
plt.colorbar()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['streamplot_linewidth'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
remove_text=True, style='mpl20')
|
||||
def test_linewidth():
|
||||
X, Y, U, V = velocity_field()
|
||||
speed = np.sqrt(U*U + V*V)
|
||||
lw = 5*speed/speed.max()
|
||||
df = 25. / 30. # Compatibility factor for old test image
|
||||
plt.streamplot(X, Y, U, V, density=[0.5 * df, 1. * df], color='k',
|
||||
linewidth=lw)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['streamplot_masks_and_nans'],
|
||||
tol=0.04 if on_win else 0,
|
||||
remove_text=True, style='mpl20')
|
||||
def test_masks_and_nans():
|
||||
X, Y, U, V = velocity_field()
|
||||
mask = np.zeros(U.shape, dtype=bool)
|
||||
mask[40:60, 40:60] = 1
|
||||
U[:20, :20] = np.nan
|
||||
U = np.ma.array(U, mask=mask)
|
||||
with np.errstate(invalid='ignore'):
|
||||
plt.streamplot(X, Y, U, V, color=U, cmap=plt.cm.Blues)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['streamplot_maxlength'],
|
||||
extensions=['png'], remove_text=True, style='mpl20')
|
||||
def test_maxlength():
|
||||
x, y, U, V = swirl_velocity_field()
|
||||
plt.streamplot(x, y, U, V, maxlength=10., start_points=[[0., 1.5]],
|
||||
linewidth=2, density=2)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['streamplot_direction'],
|
||||
extensions=['png'], remove_text=True, style='mpl20')
|
||||
def test_direction():
|
||||
x, y, U, V = swirl_velocity_field()
|
||||
plt.streamplot(x, y, U, V, integration_direction='backward',
|
||||
maxlength=1.5, start_points=[[1.5, 0.]],
|
||||
linewidth=2, density=2)
|
||||
|
||||
|
||||
def test_streamplot_limits():
|
||||
ax = plt.axes()
|
||||
x = np.linspace(-5, 10, 20)
|
||||
y = np.linspace(-2, 4, 10)
|
||||
y, x = np.meshgrid(y, x)
|
||||
trans = mtransforms.Affine2D().translate(25, 32) + ax.transData
|
||||
plt.barbs(x, y, np.sin(x), np.cos(y), transform=trans)
|
||||
# The calculated bounds are approximately the bounds of the original data,
|
||||
# this is because the entire path is taken into account when updating the
|
||||
# datalim.
|
||||
assert_array_almost_equal(ax.dataLim.bounds, (20, 30, 15, 6),
|
||||
decimal=1)
|
||||
@@ -0,0 +1,168 @@
|
||||
from collections import OrderedDict
|
||||
from contextlib import contextmanager
|
||||
import gc
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from tempfile import TemporaryDirectory
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
|
||||
import matplotlib as mpl
|
||||
from matplotlib import pyplot as plt, style
|
||||
from matplotlib.style.core import USER_LIBRARY_PATHS, STYLE_EXTENSION
|
||||
|
||||
|
||||
PARAM = 'image.cmap'
|
||||
VALUE = 'pink'
|
||||
DUMMY_SETTINGS = {PARAM: VALUE}
|
||||
|
||||
|
||||
@contextmanager
|
||||
def temp_style(style_name, settings=None):
|
||||
"""Context manager to create a style sheet in a temporary directory."""
|
||||
if not settings:
|
||||
settings = DUMMY_SETTINGS
|
||||
temp_file = '%s.%s' % (style_name, STYLE_EXTENSION)
|
||||
try:
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
# Write style settings to file in the tmpdir.
|
||||
Path(tmpdir, temp_file).write_text(
|
||||
"\n".join("{}: {}".format(k, v) for k, v in settings.items()))
|
||||
# Add tmpdir to style path and reload so we can access this style.
|
||||
USER_LIBRARY_PATHS.append(tmpdir)
|
||||
style.reload_library()
|
||||
yield
|
||||
finally:
|
||||
style.reload_library()
|
||||
|
||||
|
||||
def test_invalid_rc_warning_includes_filename():
|
||||
SETTINGS = {'foo': 'bar'}
|
||||
basename = 'basename'
|
||||
with warnings.catch_warnings(record=True) as warns:
|
||||
with temp_style(basename, SETTINGS):
|
||||
# style.reload_library() in temp_style() triggers the warning
|
||||
pass
|
||||
|
||||
for w in warns:
|
||||
assert basename in str(w.message)
|
||||
|
||||
|
||||
def test_available():
|
||||
with temp_style('_test_', DUMMY_SETTINGS):
|
||||
assert '_test_' in style.available
|
||||
|
||||
|
||||
def test_use():
|
||||
mpl.rcParams[PARAM] = 'gray'
|
||||
with temp_style('test', DUMMY_SETTINGS):
|
||||
with style.context('test'):
|
||||
assert mpl.rcParams[PARAM] == VALUE
|
||||
|
||||
|
||||
@pytest.mark.network
|
||||
def test_use_url():
|
||||
with temp_style('test', DUMMY_SETTINGS):
|
||||
with style.context('https://gist.github.com/adrn/6590261/raw'):
|
||||
assert mpl.rcParams['axes.facecolor'] == "#adeade"
|
||||
|
||||
|
||||
def test_context():
|
||||
mpl.rcParams[PARAM] = 'gray'
|
||||
with temp_style('test', DUMMY_SETTINGS):
|
||||
with style.context('test'):
|
||||
assert mpl.rcParams[PARAM] == VALUE
|
||||
# Check that this value is reset after the exiting the context.
|
||||
assert mpl.rcParams[PARAM] == 'gray'
|
||||
|
||||
|
||||
def test_context_with_dict():
|
||||
original_value = 'gray'
|
||||
other_value = 'blue'
|
||||
mpl.rcParams[PARAM] = original_value
|
||||
with style.context({PARAM: other_value}):
|
||||
assert mpl.rcParams[PARAM] == other_value
|
||||
assert mpl.rcParams[PARAM] == original_value
|
||||
|
||||
|
||||
def test_context_with_dict_after_namedstyle():
|
||||
# Test dict after style name where dict modifies the same parameter.
|
||||
original_value = 'gray'
|
||||
other_value = 'blue'
|
||||
mpl.rcParams[PARAM] = original_value
|
||||
with temp_style('test', DUMMY_SETTINGS):
|
||||
with style.context(['test', {PARAM: other_value}]):
|
||||
assert mpl.rcParams[PARAM] == other_value
|
||||
assert mpl.rcParams[PARAM] == original_value
|
||||
|
||||
|
||||
def test_context_with_dict_before_namedstyle():
|
||||
# Test dict before style name where dict modifies the same parameter.
|
||||
original_value = 'gray'
|
||||
other_value = 'blue'
|
||||
mpl.rcParams[PARAM] = original_value
|
||||
with temp_style('test', DUMMY_SETTINGS):
|
||||
with style.context([{PARAM: other_value}, 'test']):
|
||||
assert mpl.rcParams[PARAM] == VALUE
|
||||
assert mpl.rcParams[PARAM] == original_value
|
||||
|
||||
|
||||
def test_context_with_union_of_dict_and_namedstyle():
|
||||
# Test dict after style name where dict modifies the a different parameter.
|
||||
original_value = 'gray'
|
||||
other_param = 'text.usetex'
|
||||
other_value = True
|
||||
d = {other_param: other_value}
|
||||
mpl.rcParams[PARAM] = original_value
|
||||
mpl.rcParams[other_param] = (not other_value)
|
||||
with temp_style('test', DUMMY_SETTINGS):
|
||||
with style.context(['test', d]):
|
||||
assert mpl.rcParams[PARAM] == VALUE
|
||||
assert mpl.rcParams[other_param] == other_value
|
||||
assert mpl.rcParams[PARAM] == original_value
|
||||
assert mpl.rcParams[other_param] == (not other_value)
|
||||
|
||||
|
||||
def test_context_with_badparam():
|
||||
original_value = 'gray'
|
||||
other_value = 'blue'
|
||||
d = OrderedDict([(PARAM, original_value), ('badparam', None)])
|
||||
with style.context({PARAM: other_value}):
|
||||
assert mpl.rcParams[PARAM] == other_value
|
||||
x = style.context([d])
|
||||
with pytest.raises(KeyError):
|
||||
with x:
|
||||
pass
|
||||
assert mpl.rcParams[PARAM] == other_value
|
||||
|
||||
|
||||
@pytest.mark.parametrize('equiv_styles',
|
||||
[('mpl20', 'default'),
|
||||
('mpl15', 'classic')],
|
||||
ids=['mpl20', 'mpl15'])
|
||||
def test_alias(equiv_styles):
|
||||
rc_dicts = []
|
||||
for sty in equiv_styles:
|
||||
with style.context(sty):
|
||||
rc_dicts.append(dict(mpl.rcParams))
|
||||
|
||||
rc_base = rc_dicts[0]
|
||||
for nm, rc in zip(equiv_styles[1:], rc_dicts[1:]):
|
||||
assert rc_base == rc
|
||||
|
||||
|
||||
def test_xkcd_no_cm():
|
||||
assert mpl.rcParams["path.sketch"] is None
|
||||
plt.xkcd()
|
||||
assert mpl.rcParams["path.sketch"] == (1, 100, 2)
|
||||
gc.collect()
|
||||
assert mpl.rcParams["path.sketch"] == (1, 100, 2)
|
||||
|
||||
|
||||
def test_xkcd_cm():
|
||||
assert mpl.rcParams["path.sketch"] is None
|
||||
with plt.xkcd():
|
||||
assert mpl.rcParams["path.sketch"] == (1, 100, 2)
|
||||
assert mpl.rcParams["path.sketch"] is None
|
||||
@@ -0,0 +1,160 @@
|
||||
import itertools
|
||||
import warnings
|
||||
|
||||
import numpy
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def check_shared(axs, x_shared, y_shared):
|
||||
"""
|
||||
x_shared and y_shared are n x n boolean matrices; entry (i, j) indicates
|
||||
whether the x (or y) axes of subplots i and j should be shared.
|
||||
"""
|
||||
for (i1, ax1), (i2, ax2), (i3, (name, shared)) in itertools.product(
|
||||
enumerate(axs),
|
||||
enumerate(axs),
|
||||
enumerate(zip("xy", [x_shared, y_shared]))):
|
||||
if i2 <= i1:
|
||||
continue
|
||||
assert \
|
||||
(getattr(axs[0], "_shared_{}_axes".format(name)).joined(ax1, ax2)
|
||||
== shared[i1, i2]), \
|
||||
"axes %i and %i incorrectly %ssharing %s axis" % (
|
||||
i1, i2, "not " if shared[i1, i2] else "", name)
|
||||
|
||||
|
||||
def check_visible(axs, x_visible, y_visible):
|
||||
def tostr(v):
|
||||
return "invisible" if v else "visible"
|
||||
|
||||
for ax, vx, vy in zip(axs, x_visible, y_visible):
|
||||
for l in ax.get_xticklabels() + [ax.get_xaxis().offsetText]:
|
||||
assert l.get_visible() == vx, \
|
||||
"X axis was incorrectly %s" % (tostr(vx))
|
||||
for l in ax.get_yticklabels() + [ax.get_yaxis().offsetText]:
|
||||
assert l.get_visible() == vy, \
|
||||
"Y axis was incorrectly %s" % (tostr(vy))
|
||||
|
||||
|
||||
def test_shared():
|
||||
rdim = (4, 4, 2)
|
||||
share = {
|
||||
'all': numpy.ones(rdim[:2], dtype=bool),
|
||||
'none': numpy.zeros(rdim[:2], dtype=bool),
|
||||
'row': numpy.array([
|
||||
[False, True, False, False],
|
||||
[True, False, False, False],
|
||||
[False, False, False, True],
|
||||
[False, False, True, False]]),
|
||||
'col': numpy.array([
|
||||
[False, False, True, False],
|
||||
[False, False, False, True],
|
||||
[True, False, False, False],
|
||||
[False, True, False, False]]),
|
||||
}
|
||||
visible = {
|
||||
'x': {
|
||||
'all': [False, False, True, True],
|
||||
'col': [False, False, True, True],
|
||||
'row': [True] * 4,
|
||||
'none': [True] * 4,
|
||||
False: [True] * 4,
|
||||
True: [False, False, True, True],
|
||||
},
|
||||
'y': {
|
||||
'all': [True, False, True, False],
|
||||
'col': [True] * 4,
|
||||
'row': [True, False, True, False],
|
||||
'none': [True] * 4,
|
||||
False: [True] * 4,
|
||||
True: [True, False, True, False],
|
||||
},
|
||||
}
|
||||
share[False] = share['none']
|
||||
share[True] = share['all']
|
||||
|
||||
# test default
|
||||
f, ((a1, a2), (a3, a4)) = plt.subplots(2, 2)
|
||||
axs = [a1, a2, a3, a4]
|
||||
check_shared(axs, share['none'], share['none'])
|
||||
plt.close(f)
|
||||
|
||||
# test all option combinations
|
||||
ops = [False, True, 'all', 'none', 'row', 'col']
|
||||
for xo in ops:
|
||||
for yo in ops:
|
||||
f, ((a1, a2), (a3, a4)) = plt.subplots(2, 2, sharex=xo, sharey=yo)
|
||||
axs = [a1, a2, a3, a4]
|
||||
check_shared(axs, share[xo], share[yo])
|
||||
check_visible(axs, visible['x'][xo], visible['y'][yo])
|
||||
plt.close(f)
|
||||
|
||||
# test label_outer
|
||||
f, ((a1, a2), (a3, a4)) = plt.subplots(2, 2, sharex=True, sharey=True)
|
||||
axs = [a1, a2, a3, a4]
|
||||
for ax in axs:
|
||||
ax.label_outer()
|
||||
check_visible(axs, [False, False, True, True], [True, False, True, False])
|
||||
|
||||
|
||||
def test_shared_and_moved():
|
||||
# test if sharey is on, but then tick_left is called that labels don't
|
||||
# re-appear. Seaborn does this just to be sure yaxis is on left...
|
||||
f, (a1, a2) = plt.subplots(1, 2, sharey=True)
|
||||
check_visible([a2], [True], [False])
|
||||
a2.yaxis.tick_left()
|
||||
check_visible([a2], [True], [False])
|
||||
|
||||
f, (a1, a2) = plt.subplots(2, 1, sharex=True)
|
||||
check_visible([a1], [False], [True])
|
||||
a2.xaxis.tick_bottom()
|
||||
check_visible([a1], [False], [True])
|
||||
|
||||
|
||||
def test_exceptions():
|
||||
# TODO should this test more options?
|
||||
with pytest.raises(ValueError):
|
||||
plt.subplots(2, 2, sharex='blah')
|
||||
with pytest.raises(ValueError):
|
||||
plt.subplots(2, 2, sharey='blah')
|
||||
# We filter warnings in this test which are genuine since
|
||||
# the point of this test is to ensure that this raises.
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore',
|
||||
message='.*sharex argument to subplots',
|
||||
category=UserWarning)
|
||||
with pytest.raises(ValueError):
|
||||
plt.subplots(2, 2, -1)
|
||||
with pytest.raises(ValueError):
|
||||
plt.subplots(2, 2, 0)
|
||||
with pytest.raises(ValueError):
|
||||
plt.subplots(2, 2, 5)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['subplots_offset_text'], remove_text=False)
|
||||
def test_subplots_offsettext():
|
||||
x = numpy.arange(0, 1e10, 1e9)
|
||||
y = numpy.arange(0, 100, 10)+1e4
|
||||
fig, axes = plt.subplots(2, 2, sharex='col', sharey='all')
|
||||
axes[0, 0].plot(x, x)
|
||||
axes[1, 0].plot(x, x)
|
||||
axes[0, 1].plot(y, x)
|
||||
axes[1, 1].plot(y, x)
|
||||
|
||||
|
||||
def test_get_gridspec():
|
||||
# ahem, pretty trivial, but...
|
||||
fig, ax = plt.subplots()
|
||||
assert ax.get_subplotspec().get_gridspec() == ax.get_gridspec()
|
||||
|
||||
|
||||
def test_dont_mutate_kwargs():
|
||||
subplot_kw = {'sharex': 'all'}
|
||||
gridspec_kw = {'width_ratios': [1, 2]}
|
||||
fig, ax = plt.subplots(1, 2, subplot_kw=subplot_kw,
|
||||
gridspec_kw=gridspec_kw)
|
||||
assert subplot_kw == {'sharex': 'all'}
|
||||
assert gridspec_kw == {'width_ratios': [1, 2]}
|
||||
@@ -0,0 +1,197 @@
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
from matplotlib.table import CustomCell, Table
|
||||
from matplotlib.path import Path
|
||||
|
||||
|
||||
def test_non_square():
|
||||
# Check that creating a non-square table works
|
||||
cellcolors = ['b', 'r']
|
||||
plt.table(cellColours=cellcolors)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['table_zorder'],
|
||||
extensions=['png'],
|
||||
remove_text=True)
|
||||
def test_zorder():
|
||||
data = [[66386, 174296],
|
||||
[58230, 381139]]
|
||||
|
||||
colLabels = ('Freeze', 'Wind')
|
||||
rowLabels = ['%d year' % x for x in (100, 50)]
|
||||
|
||||
cellText = []
|
||||
yoff = np.zeros(len(colLabels))
|
||||
for row in reversed(data):
|
||||
yoff += row
|
||||
cellText.append(['%1.1f' % (x/1000.0) for x in yoff])
|
||||
|
||||
t = np.linspace(0, 2*np.pi, 100)
|
||||
plt.plot(t, np.cos(t), lw=4, zorder=2)
|
||||
|
||||
plt.table(cellText=cellText,
|
||||
rowLabels=rowLabels,
|
||||
colLabels=colLabels,
|
||||
loc='center',
|
||||
zorder=-2,
|
||||
)
|
||||
|
||||
plt.table(cellText=cellText,
|
||||
rowLabels=rowLabels,
|
||||
colLabels=colLabels,
|
||||
loc='upper center',
|
||||
zorder=4,
|
||||
)
|
||||
plt.yticks([])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['table_labels'],
|
||||
extensions=['png'])
|
||||
def test_label_colours():
|
||||
dim = 3
|
||||
|
||||
c = np.linspace(0, 1, dim)
|
||||
colours = plt.cm.RdYlGn(c)
|
||||
cellText = [['1'] * dim] * dim
|
||||
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = fig.add_subplot(4, 1, 1)
|
||||
ax1.axis('off')
|
||||
ax1.table(cellText=cellText,
|
||||
rowColours=colours,
|
||||
loc='best')
|
||||
|
||||
ax2 = fig.add_subplot(4, 1, 2)
|
||||
ax2.axis('off')
|
||||
ax2.table(cellText=cellText,
|
||||
rowColours=colours,
|
||||
rowLabels=['Header'] * dim,
|
||||
loc='best')
|
||||
|
||||
ax3 = fig.add_subplot(4, 1, 3)
|
||||
ax3.axis('off')
|
||||
ax3.table(cellText=cellText,
|
||||
colColours=colours,
|
||||
loc='best')
|
||||
|
||||
ax4 = fig.add_subplot(4, 1, 4)
|
||||
ax4.axis('off')
|
||||
ax4.table(cellText=cellText,
|
||||
colColours=colours,
|
||||
colLabels=['Header'] * dim,
|
||||
loc='best')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['table_cell_manipulation'],
|
||||
extensions=['png'], remove_text=True)
|
||||
def test_diff_cell_table():
|
||||
cells = ('horizontal', 'vertical', 'open', 'closed', 'T', 'R', 'B', 'L')
|
||||
cellText = [['1'] * len(cells)] * 2
|
||||
colWidths = [0.1] * len(cells)
|
||||
|
||||
_, axes = plt.subplots(nrows=len(cells), figsize=(4, len(cells)+1))
|
||||
for ax, cell in zip(axes, cells):
|
||||
ax.table(
|
||||
colWidths=colWidths,
|
||||
cellText=cellText,
|
||||
loc='center',
|
||||
edges=cell,
|
||||
)
|
||||
ax.axis('off')
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
def test_customcell():
|
||||
types = ('horizontal', 'vertical', 'open', 'closed', 'T', 'R', 'B', 'L')
|
||||
codes = (
|
||||
(Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO, Path.MOVETO),
|
||||
(Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO, Path.LINETO),
|
||||
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO),
|
||||
(Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY),
|
||||
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO),
|
||||
(Path.MOVETO, Path.MOVETO, Path.LINETO, Path.MOVETO, Path.MOVETO),
|
||||
(Path.MOVETO, Path.LINETO, Path.MOVETO, Path.MOVETO, Path.MOVETO),
|
||||
(Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.MOVETO, Path.LINETO),
|
||||
)
|
||||
|
||||
for t, c in zip(types, codes):
|
||||
cell = CustomCell((0, 0), visible_edges=t, width=1, height=1)
|
||||
code = tuple(s for _, s in cell.get_path().iter_segments())
|
||||
assert c == code
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['table_auto_column'],
|
||||
extensions=['png'])
|
||||
def test_auto_column():
|
||||
fig = plt.figure()
|
||||
|
||||
# iterable list input
|
||||
ax1 = fig.add_subplot(4, 1, 1)
|
||||
ax1.axis('off')
|
||||
tb1 = ax1.table(cellText=[['Fit Text', 2],
|
||||
['very long long text, Longer text than default', 1]],
|
||||
rowLabels=["A", "B"],
|
||||
colLabels=["Col1", "Col2"],
|
||||
loc="center")
|
||||
tb1.auto_set_font_size(False)
|
||||
tb1.set_fontsize(12)
|
||||
tb1.auto_set_column_width([-1, 0, 1])
|
||||
|
||||
# iterable tuple input
|
||||
ax2 = fig.add_subplot(4, 1, 2)
|
||||
ax2.axis('off')
|
||||
tb2 = ax2.table(cellText=[['Fit Text', 2],
|
||||
['very long long text, Longer text than default', 1]],
|
||||
rowLabels=["A", "B"],
|
||||
colLabels=["Col1", "Col2"],
|
||||
loc="center")
|
||||
tb2.auto_set_font_size(False)
|
||||
tb2.set_fontsize(12)
|
||||
tb2.auto_set_column_width((-1, 0, 1))
|
||||
|
||||
#3 single inputs
|
||||
ax3 = fig.add_subplot(4, 1, 3)
|
||||
ax3.axis('off')
|
||||
tb3 = ax3.table(cellText=[['Fit Text', 2],
|
||||
['very long long text, Longer text than default', 1]],
|
||||
rowLabels=["A", "B"],
|
||||
colLabels=["Col1", "Col2"],
|
||||
loc="center")
|
||||
tb3.auto_set_font_size(False)
|
||||
tb3.set_fontsize(12)
|
||||
tb3.auto_set_column_width(-1)
|
||||
tb3.auto_set_column_width(0)
|
||||
tb3.auto_set_column_width(1)
|
||||
|
||||
#4 non integer interable input
|
||||
ax4 = fig.add_subplot(4, 1, 4)
|
||||
ax4.axis('off')
|
||||
tb4 = ax4.table(cellText=[['Fit Text', 2],
|
||||
['very long long text, Longer text than default', 1]],
|
||||
rowLabels=["A", "B"],
|
||||
colLabels=["Col1", "Col2"],
|
||||
loc="center")
|
||||
tb4.auto_set_font_size(False)
|
||||
tb4.set_fontsize(12)
|
||||
tb4.auto_set_column_width("-101")
|
||||
|
||||
|
||||
def test_table_cells():
|
||||
fig, ax = plt.subplots()
|
||||
table = Table(ax)
|
||||
|
||||
cell = table.add_cell(1, 2, 1, 1)
|
||||
assert isinstance(cell, CustomCell)
|
||||
assert cell is table[1, 2]
|
||||
|
||||
cell2 = CustomCell((0, 0), 1, 2, visible_edges=None)
|
||||
table[2, 1] = cell2
|
||||
assert table[2, 1] is cell2
|
||||
|
||||
# make sure gettitem support has not broken
|
||||
# properties and setp
|
||||
table.properties()
|
||||
plt.setp(table)
|
||||
@@ -0,0 +1,18 @@
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.texmanager import TexManager
|
||||
|
||||
|
||||
def test_fontconfig_preamble():
|
||||
"""
|
||||
Test that the preamble is included in _fontconfig
|
||||
"""
|
||||
plt.rcParams['text.usetex'] = True
|
||||
|
||||
tm1 = TexManager()
|
||||
font_config1 = tm1.get_font_config()
|
||||
|
||||
plt.rcParams['text.latex.preamble'] = ['\\usepackage{txfonts}']
|
||||
tm2 = TexManager()
|
||||
font_config2 = tm2.get_font_config()
|
||||
|
||||
assert font_config1 != font_config2
|
||||
@@ -0,0 +1,512 @@
|
||||
import io
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_almost_equal
|
||||
import pytest
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
|
||||
needs_usetex = pytest.mark.skipif(
|
||||
not matplotlib.checkdep_usetex(True),
|
||||
reason="This test needs a TeX installation")
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['font_styles'])
|
||||
def test_font_styles():
|
||||
from matplotlib import _get_data_path
|
||||
data_path = _get_data_path()
|
||||
|
||||
def find_matplotlib_font(**kw):
|
||||
prop = FontProperties(**kw)
|
||||
path = findfont(prop, directory=data_path)
|
||||
return FontProperties(fname=path)
|
||||
|
||||
from matplotlib.font_manager import FontProperties, findfont
|
||||
warnings.filterwarnings(
|
||||
'ignore',
|
||||
r"findfont: Font family \[u?'Foo'\] not found. Falling back to .",
|
||||
UserWarning,
|
||||
module='matplotlib.font_manager')
|
||||
|
||||
plt.figure()
|
||||
ax = plt.subplot(1, 1, 1)
|
||||
|
||||
normalFont = find_matplotlib_font(
|
||||
family="sans-serif",
|
||||
style="normal",
|
||||
variant="normal",
|
||||
size=14)
|
||||
ax.annotate(
|
||||
"Normal Font",
|
||||
(0.1, 0.1),
|
||||
xycoords='axes fraction',
|
||||
fontproperties=normalFont)
|
||||
|
||||
boldFont = find_matplotlib_font(
|
||||
family="Foo",
|
||||
style="normal",
|
||||
variant="normal",
|
||||
weight="bold",
|
||||
stretch=500,
|
||||
size=14)
|
||||
ax.annotate(
|
||||
"Bold Font",
|
||||
(0.1, 0.2),
|
||||
xycoords='axes fraction',
|
||||
fontproperties=boldFont)
|
||||
|
||||
boldItemFont = find_matplotlib_font(
|
||||
family="sans serif",
|
||||
style="italic",
|
||||
variant="normal",
|
||||
weight=750,
|
||||
stretch=500,
|
||||
size=14)
|
||||
ax.annotate(
|
||||
"Bold Italic Font",
|
||||
(0.1, 0.3),
|
||||
xycoords='axes fraction',
|
||||
fontproperties=boldItemFont)
|
||||
|
||||
lightFont = find_matplotlib_font(
|
||||
family="sans-serif",
|
||||
style="normal",
|
||||
variant="normal",
|
||||
weight=200,
|
||||
stretch=500,
|
||||
size=14)
|
||||
ax.annotate(
|
||||
"Light Font",
|
||||
(0.1, 0.4),
|
||||
xycoords='axes fraction',
|
||||
fontproperties=lightFont)
|
||||
|
||||
condensedFont = find_matplotlib_font(
|
||||
family="sans-serif",
|
||||
style="normal",
|
||||
variant="normal",
|
||||
weight=500,
|
||||
stretch=100,
|
||||
size=14)
|
||||
ax.annotate(
|
||||
"Condensed Font",
|
||||
(0.1, 0.5),
|
||||
xycoords='axes fraction',
|
||||
fontproperties=condensedFont)
|
||||
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['multiline'])
|
||||
def test_multiline():
|
||||
plt.figure()
|
||||
ax = plt.subplot(1, 1, 1)
|
||||
ax.set_title("multiline\ntext alignment")
|
||||
|
||||
plt.text(
|
||||
0.2, 0.5, "TpTpTp\n$M$\nTpTpTp", size=20, ha="center", va="top")
|
||||
|
||||
plt.text(
|
||||
0.5, 0.5, "TpTpTp\n$M^{M^{M^{M}}}$\nTpTpTp", size=20,
|
||||
ha="center", va="top")
|
||||
|
||||
plt.text(
|
||||
0.8, 0.5, "TpTpTp\n$M_{q_{q_{q}}}$\nTpTpTp", size=20,
|
||||
ha="center", va="top")
|
||||
|
||||
plt.xlim(0, 1)
|
||||
plt.ylim(0, 0.8)
|
||||
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['antialiased'], extensions=['png'])
|
||||
def test_antialiasing():
|
||||
matplotlib.rcParams['text.antialiased'] = True
|
||||
|
||||
fig = plt.figure(figsize=(5.25, 0.75))
|
||||
fig.text(0.5, 0.75, "antialiased", horizontalalignment='center',
|
||||
verticalalignment='center')
|
||||
fig.text(0.5, 0.25, r"$\sqrt{x}$", horizontalalignment='center',
|
||||
verticalalignment='center')
|
||||
# NOTE: We don't need to restore the rcParams here, because the
|
||||
# test cleanup will do it for us. In fact, if we do it here, it
|
||||
# will turn antialiasing back off before the images are actually
|
||||
# rendered.
|
||||
|
||||
|
||||
def test_afm_kerning():
|
||||
from matplotlib.afm import AFM
|
||||
from matplotlib.font_manager import findfont
|
||||
|
||||
fn = findfont("Helvetica", fontext="afm")
|
||||
with open(fn, 'rb') as fh:
|
||||
afm = AFM(fh)
|
||||
assert afm.string_width_height('VAVAVAVAVAVA') == (7174.0, 718)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['text_contains'], extensions=['png'])
|
||||
def test_contains():
|
||||
import matplotlib.backend_bases as mbackend
|
||||
|
||||
fig = plt.figure()
|
||||
ax = plt.axes()
|
||||
|
||||
mevent = mbackend.MouseEvent(
|
||||
'button_press_event', fig.canvas, 0.5, 0.5, 1, None)
|
||||
|
||||
xs = np.linspace(0.25, 0.75, 30)
|
||||
ys = np.linspace(0.25, 0.75, 30)
|
||||
xs, ys = np.meshgrid(xs, ys)
|
||||
|
||||
txt = plt.text(
|
||||
0.48, 0.52, 'hello world', ha='center', fontsize=30, rotation=30)
|
||||
# uncomment to draw the text's bounding box
|
||||
# txt.set_bbox(dict(edgecolor='black', facecolor='none'))
|
||||
|
||||
# draw the text. This is important, as the contains method can only work
|
||||
# when a renderer exists.
|
||||
fig.canvas.draw()
|
||||
|
||||
for x, y in zip(xs.flat, ys.flat):
|
||||
mevent.x, mevent.y = plt.gca().transAxes.transform_point([x, y])
|
||||
contains, _ = txt.contains(mevent)
|
||||
color = 'yellow' if contains else 'red'
|
||||
|
||||
# capture the viewLim, plot a point, and reset the viewLim
|
||||
vl = ax.viewLim.frozen()
|
||||
ax.plot(x, y, 'o', color=color)
|
||||
ax.viewLim.set(vl)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['titles'])
|
||||
def test_titles():
|
||||
# left and right side titles
|
||||
plt.figure()
|
||||
ax = plt.subplot(1, 1, 1)
|
||||
ax.set_title("left title", loc="left")
|
||||
ax.set_title("right title", loc="right")
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['text_alignment'])
|
||||
def test_alignment():
|
||||
plt.figure()
|
||||
ax = plt.subplot(1, 1, 1)
|
||||
|
||||
x = 0.1
|
||||
for rotation in (0, 30):
|
||||
for alignment in ('top', 'bottom', 'baseline', 'center'):
|
||||
ax.text(
|
||||
x, 0.5, alignment + " Tj", va=alignment, rotation=rotation,
|
||||
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
|
||||
ax.text(
|
||||
x, 1.0, r'$\sum_{i=0}^{j}$', va=alignment, rotation=rotation)
|
||||
x += 0.1
|
||||
|
||||
ax.plot([0, 1], [0.5, 0.5])
|
||||
ax.plot([0, 1], [1.0, 1.0])
|
||||
|
||||
ax.set_xlim([0, 1])
|
||||
ax.set_ylim([0, 1.5])
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['axes_titles'], extensions=['png'])
|
||||
def test_axes_titles():
|
||||
# Related to issue #3327
|
||||
plt.figure()
|
||||
ax = plt.subplot(1, 1, 1)
|
||||
ax.set_title('center', loc='center', fontsize=20, fontweight=700)
|
||||
ax.set_title('left', loc='left', fontsize=12, fontweight=400)
|
||||
ax.set_title('right', loc='right', fontsize=12, fontweight=400)
|
||||
|
||||
|
||||
def test_set_position():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
# test set_position
|
||||
ann = ax.annotate(
|
||||
'test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
|
||||
fig.canvas.draw()
|
||||
|
||||
init_pos = ann.get_window_extent(fig.canvas.renderer)
|
||||
shift_val = 15
|
||||
ann.set_position((shift_val, shift_val))
|
||||
fig.canvas.draw()
|
||||
post_pos = ann.get_window_extent(fig.canvas.renderer)
|
||||
|
||||
for a, b in zip(init_pos.min, post_pos.min):
|
||||
assert a + shift_val == b
|
||||
|
||||
# test xyann
|
||||
ann = ax.annotate(
|
||||
'test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
|
||||
fig.canvas.draw()
|
||||
|
||||
init_pos = ann.get_window_extent(fig.canvas.renderer)
|
||||
shift_val = 15
|
||||
ann.xyann = (shift_val, shift_val)
|
||||
fig.canvas.draw()
|
||||
post_pos = ann.get_window_extent(fig.canvas.renderer)
|
||||
|
||||
for a, b in zip(init_pos.min, post_pos.min):
|
||||
assert a + shift_val == b
|
||||
|
||||
|
||||
def test_get_rotation_string():
|
||||
from matplotlib import text
|
||||
assert text.get_rotation('horizontal') == 0.
|
||||
assert text.get_rotation('vertical') == 90.
|
||||
assert text.get_rotation('15.') == 15.
|
||||
|
||||
|
||||
def test_get_rotation_float():
|
||||
from matplotlib import text
|
||||
for i in [15., 16.70, 77.4]:
|
||||
assert text.get_rotation(i) == i
|
||||
|
||||
|
||||
def test_get_rotation_int():
|
||||
from matplotlib import text
|
||||
for i in [67, 16, 41]:
|
||||
assert text.get_rotation(i) == float(i)
|
||||
|
||||
|
||||
def test_get_rotation_raises():
|
||||
from matplotlib import text
|
||||
with pytest.raises(ValueError):
|
||||
text.get_rotation('hozirontal')
|
||||
|
||||
|
||||
def test_get_rotation_none():
|
||||
from matplotlib import text
|
||||
assert text.get_rotation(None) == 0.0
|
||||
|
||||
|
||||
def test_get_rotation_mod360():
|
||||
from matplotlib import text
|
||||
for i, j in zip([360., 377., 720+177.2], [0., 17., 177.2]):
|
||||
assert_almost_equal(text.get_rotation(i), j)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['text_bboxclip'])
|
||||
def test_bbox_clipping():
|
||||
plt.text(0.9, 0.2, 'Is bbox clipped?', backgroundcolor='r', clip_on=True)
|
||||
t = plt.text(0.9, 0.5, 'Is fancy bbox clipped?', clip_on=True)
|
||||
t.set_bbox({"boxstyle": "round, pad=0.1"})
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['annotation_negative_ax_coords'],
|
||||
extensions=['png'])
|
||||
def test_annotation_negative_ax_coords():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
ax.annotate('+ pts',
|
||||
xytext=[30, 20], textcoords='axes points',
|
||||
xy=[30, 20], xycoords='axes points', fontsize=32)
|
||||
ax.annotate('- pts',
|
||||
xytext=[30, -20], textcoords='axes points',
|
||||
xy=[30, -20], xycoords='axes points', fontsize=32,
|
||||
va='top')
|
||||
ax.annotate('+ frac',
|
||||
xytext=[0.75, 0.05], textcoords='axes fraction',
|
||||
xy=[0.75, 0.05], xycoords='axes fraction', fontsize=32)
|
||||
ax.annotate('- frac',
|
||||
xytext=[0.75, -0.05], textcoords='axes fraction',
|
||||
xy=[0.75, -0.05], xycoords='axes fraction', fontsize=32,
|
||||
va='top')
|
||||
|
||||
ax.annotate('+ pixels',
|
||||
xytext=[160, 25], textcoords='axes pixels',
|
||||
xy=[160, 25], xycoords='axes pixels', fontsize=32)
|
||||
ax.annotate('- pixels',
|
||||
xytext=[160, -25], textcoords='axes pixels',
|
||||
xy=[160, -25], xycoords='axes pixels', fontsize=32,
|
||||
va='top')
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['annotation_negative_fig_coords'],
|
||||
extensions=['png'])
|
||||
def test_annotation_negative_fig_coords():
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
ax.annotate('+ pts',
|
||||
xytext=[10, 120], textcoords='figure points',
|
||||
xy=[10, 120], xycoords='figure points', fontsize=32)
|
||||
ax.annotate('- pts',
|
||||
xytext=[-10, 180], textcoords='figure points',
|
||||
xy=[-10, 180], xycoords='figure points', fontsize=32,
|
||||
va='top')
|
||||
ax.annotate('+ frac',
|
||||
xytext=[0.05, 0.55], textcoords='figure fraction',
|
||||
xy=[0.05, 0.55], xycoords='figure fraction', fontsize=32)
|
||||
ax.annotate('- frac',
|
||||
xytext=[-0.05, 0.5], textcoords='figure fraction',
|
||||
xy=[-0.05, 0.5], xycoords='figure fraction', fontsize=32,
|
||||
va='top')
|
||||
|
||||
ax.annotate('+ pixels',
|
||||
xytext=[50, 50], textcoords='figure pixels',
|
||||
xy=[50, 50], xycoords='figure pixels', fontsize=32)
|
||||
ax.annotate('- pixels',
|
||||
xytext=[-50, 100], textcoords='figure pixels',
|
||||
xy=[-50, 100], xycoords='figure pixels', fontsize=32,
|
||||
va='top')
|
||||
|
||||
|
||||
def test_text_stale():
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2)
|
||||
plt.draw_all()
|
||||
assert not ax1.stale
|
||||
assert not ax2.stale
|
||||
assert not fig.stale
|
||||
|
||||
txt1 = ax1.text(.5, .5, 'aardvark')
|
||||
assert ax1.stale
|
||||
assert txt1.stale
|
||||
assert fig.stale
|
||||
|
||||
ann1 = ax2.annotate('aardvark', xy=[.5, .5])
|
||||
assert ax2.stale
|
||||
assert ann1.stale
|
||||
assert fig.stale
|
||||
|
||||
plt.draw_all()
|
||||
assert not ax1.stale
|
||||
assert not ax2.stale
|
||||
assert not fig.stale
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['agg_text_clip'],
|
||||
extensions=['png'])
|
||||
def test_agg_text_clip():
|
||||
np.random.seed(1)
|
||||
fig, (ax1, ax2) = plt.subplots(2)
|
||||
for x, y in np.random.rand(10, 2):
|
||||
ax1.text(x, y, "foo", clip_on=True)
|
||||
ax2.text(x, y, "foo")
|
||||
|
||||
|
||||
def test_text_size_binding():
|
||||
from matplotlib.font_manager import FontProperties
|
||||
|
||||
matplotlib.rcParams['font.size'] = 10
|
||||
fp = FontProperties(size='large')
|
||||
sz1 = fp.get_size_in_points()
|
||||
matplotlib.rcParams['font.size'] = 100
|
||||
|
||||
assert sz1 == fp.get_size_in_points()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['font_scaling'],
|
||||
extensions=['pdf'])
|
||||
def test_font_scaling():
|
||||
matplotlib.rcParams['pdf.fonttype'] = 42
|
||||
fig, ax = plt.subplots(figsize=(6.4, 12.4))
|
||||
ax.xaxis.set_major_locator(plt.NullLocator())
|
||||
ax.yaxis.set_major_locator(plt.NullLocator())
|
||||
ax.set_ylim(-10, 600)
|
||||
|
||||
for i, fs in enumerate(range(4, 43, 2)):
|
||||
ax.text(0.1, i*30, "{fs} pt font size".format(fs=fs), fontsize=fs)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('spacing1, spacing2', [(0.4, 2), (2, 0.4), (2, 2)])
|
||||
def test_two_2line_texts(spacing1, spacing2):
|
||||
text_string = 'line1\nline2'
|
||||
fig = plt.figure()
|
||||
renderer = fig.canvas.get_renderer()
|
||||
|
||||
text1 = plt.text(0.25, 0.5, text_string, linespacing=spacing1)
|
||||
text2 = plt.text(0.25, 0.5, text_string, linespacing=spacing2)
|
||||
fig.canvas.draw()
|
||||
|
||||
box1 = text1.get_window_extent(renderer=renderer)
|
||||
box2 = text2.get_window_extent(renderer=renderer)
|
||||
|
||||
# line spacing only affects height
|
||||
assert box1.width == box2.width
|
||||
if spacing1 == spacing2:
|
||||
assert box1.height == box2.height
|
||||
else:
|
||||
assert box1.height != box2.height
|
||||
|
||||
|
||||
def test_nonfinite_pos():
|
||||
fig, ax = plt.subplots()
|
||||
ax.text(0, np.nan, 'nan')
|
||||
ax.text(np.inf, 0, 'inf')
|
||||
fig.canvas.draw()
|
||||
|
||||
|
||||
def test_hinting_factor_backends():
|
||||
plt.rcParams['text.hinting_factor'] = 1
|
||||
fig = plt.figure()
|
||||
t = fig.text(0.5, 0.5, 'some text')
|
||||
|
||||
fig.savefig(io.BytesIO(), format='svg')
|
||||
expected = t.get_window_extent().intervalx
|
||||
|
||||
fig.savefig(io.BytesIO(), format='png')
|
||||
# Backends should apply hinting_factor consistently (within 10%).
|
||||
np.testing.assert_allclose(t.get_window_extent().intervalx, expected,
|
||||
rtol=0.1)
|
||||
|
||||
|
||||
@needs_usetex
|
||||
def test_single_artist_usetex():
|
||||
# Check that a single artist marked with usetex does not get passed through
|
||||
# the mathtext parser at all (for the Agg backend) (the mathtext parser
|
||||
# currently fails to parse \frac12, requiring \frac{1}{2} instead).
|
||||
fig, ax = plt.subplots()
|
||||
ax.text(.5, .5, r"$\frac12$", usetex=True)
|
||||
fig.canvas.draw()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['text_as_path_opacity'],
|
||||
extensions=['svg'])
|
||||
def test_text_as_path_opacity():
|
||||
plt.figure()
|
||||
plt.gca().set_axis_off()
|
||||
plt.text(0.25, 0.25, 'c', color=(0, 0, 0, 0.5))
|
||||
plt.text(0.25, 0.5, 'a', alpha=0.5)
|
||||
plt.text(0.25, 0.75, 'x', alpha=0.5, color=(0, 0, 0, 1))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['text_as_text_opacity'],
|
||||
extensions=['svg'])
|
||||
def test_text_as_text_opacity():
|
||||
matplotlib.rcParams['svg.fonttype'] = 'none'
|
||||
plt.figure()
|
||||
plt.gca().set_axis_off()
|
||||
plt.text(0.25, 0.25, '50% using `color`', color=(0, 0, 0, 0.5))
|
||||
plt.text(0.25, 0.5, '50% using `alpha`', alpha=0.5)
|
||||
plt.text(0.25, 0.75, '50% using `alpha` and 100% `color`', alpha=0.5,
|
||||
color=(0, 0, 0, 1))
|
||||
|
||||
|
||||
def test_text_repr():
|
||||
# smoketest to make sure text repr doesn't error for category
|
||||
plt.plot(['A', 'B'], [1, 2])
|
||||
txt = plt.text(['A'], 0.5, 'Boo')
|
||||
print(txt)
|
||||
|
||||
|
||||
def test_annotation_update():
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
an = ax.annotate('annotation', xy=(0.5, 0.5))
|
||||
extent1 = an.get_window_extent(fig.canvas.get_renderer())
|
||||
fig.tight_layout()
|
||||
extent2 = an.get_window_extent(fig.canvas.get_renderer())
|
||||
|
||||
assert not np.allclose(extent1.get_points(), extent2.get_points(),
|
||||
rtol=1e-6)
|
||||
@@ -0,0 +1,841 @@
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_almost_equal
|
||||
import pytest
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.ticker as mticker
|
||||
|
||||
|
||||
class TestMaxNLocator(object):
|
||||
basic_data = [
|
||||
(20, 100, np.array([20., 40., 60., 80., 100.])),
|
||||
(0.001, 0.0001, np.array([0., 0.0002, 0.0004, 0.0006, 0.0008, 0.001])),
|
||||
(-1e15, 1e15, np.array([-1.0e+15, -5.0e+14, 0e+00, 5e+14, 1.0e+15])),
|
||||
(0, 0.85e-50, np.arange(6) * 2e-51),
|
||||
(-0.85e-50, 0, np.arange(-5, 1) * 2e-51),
|
||||
]
|
||||
|
||||
integer_data = [
|
||||
(-0.1, 1.1, None, np.array([-1, 0, 1, 2])),
|
||||
(-0.1, 0.95, None, np.array([-0.25, 0, 0.25, 0.5, 0.75, 1.0])),
|
||||
(1, 55, [1, 1.5, 5, 6, 10], np.array([0, 15, 30, 45, 60])),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('vmin, vmax, expected', basic_data)
|
||||
def test_basic(self, vmin, vmax, expected):
|
||||
loc = mticker.MaxNLocator(nbins=5)
|
||||
assert_almost_equal(loc.tick_values(vmin, vmax), expected)
|
||||
|
||||
@pytest.mark.parametrize('vmin, vmax, steps, expected', integer_data)
|
||||
def test_integer(self, vmin, vmax, steps, expected):
|
||||
loc = mticker.MaxNLocator(nbins=5, integer=True, steps=steps)
|
||||
assert_almost_equal(loc.tick_values(vmin, vmax), expected)
|
||||
|
||||
|
||||
class TestLinearLocator(object):
|
||||
def test_basic(self):
|
||||
loc = mticker.LinearLocator(numticks=3)
|
||||
test_value = np.array([-0.8, -0.3, 0.2])
|
||||
assert_almost_equal(loc.tick_values(-0.8, 0.2), test_value)
|
||||
|
||||
def test_set_params(self):
|
||||
"""
|
||||
Create linear locator with presets={}, numticks=2 and change it to
|
||||
something else. See if change was successful. Should not exception.
|
||||
"""
|
||||
loc = mticker.LinearLocator(numticks=2)
|
||||
loc.set_params(numticks=8, presets={(0, 1): []})
|
||||
assert loc.numticks == 8
|
||||
assert loc.presets == {(0, 1): []}
|
||||
|
||||
|
||||
class TestMultipleLocator(object):
|
||||
def test_basic(self):
|
||||
loc = mticker.MultipleLocator(base=3.147)
|
||||
test_value = np.array([-9.441, -6.294, -3.147, 0., 3.147, 6.294,
|
||||
9.441, 12.588])
|
||||
assert_almost_equal(loc.tick_values(-7, 10), test_value)
|
||||
|
||||
def test_view_limits(self):
|
||||
"""
|
||||
Test basic behavior of view limits.
|
||||
"""
|
||||
with matplotlib.rc_context({'axes.autolimit_mode': 'data'}):
|
||||
loc = mticker.MultipleLocator(base=3.147)
|
||||
assert_almost_equal(loc.view_limits(-5, 5), (-5, 5))
|
||||
|
||||
def test_view_limits_round_numbers(self):
|
||||
"""
|
||||
Test that everything works properly with 'round_numbers' for auto
|
||||
limit.
|
||||
"""
|
||||
with matplotlib.rc_context({'axes.autolimit_mode': 'round_numbers'}):
|
||||
loc = mticker.MultipleLocator(base=3.147)
|
||||
assert_almost_equal(loc.view_limits(-4, 4), (-6.294, 6.294))
|
||||
|
||||
def test_set_params(self):
|
||||
"""
|
||||
Create multiple locator with 0.7 base, and change it to something else.
|
||||
See if change was successful.
|
||||
"""
|
||||
mult = mticker.MultipleLocator(base=0.7)
|
||||
mult.set_params(base=1.7)
|
||||
assert mult._edge.step == 1.7
|
||||
|
||||
|
||||
class TestAutoMinorLocator(object):
|
||||
def test_basic(self):
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_xlim(0, 1.39)
|
||||
ax.minorticks_on()
|
||||
test_value = np.array([0.05, 0.1, 0.15, 0.25, 0.3, 0.35, 0.45,
|
||||
0.5, 0.55, 0.65, 0.7, 0.75, 0.85, 0.9,
|
||||
0.95, 1.05, 1.1, 1.15, 1.25, 1.3, 1.35])
|
||||
assert_almost_equal(ax.xaxis.get_ticklocs(minor=True), test_value)
|
||||
|
||||
# NB: the following values are assuming that *xlim* is [0, 5]
|
||||
params = [
|
||||
(0, 0), # no major tick => no minor tick either
|
||||
(1, 0), # a single major tick => no minor tick
|
||||
(2, 4), # 1 "nice" major step => 1*5 minor **divisions**
|
||||
(3, 6) # 2 "not nice" major steps => 2*4 minor **divisions**
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('nb_majorticks, expected_nb_minorticks', params)
|
||||
def test_low_number_of_majorticks(
|
||||
self, nb_majorticks, expected_nb_minorticks):
|
||||
# This test is related to issue #8804
|
||||
fig, ax = plt.subplots()
|
||||
xlims = (0, 5) # easier to test the different code paths
|
||||
ax.set_xlim(*xlims)
|
||||
ax.set_xticks(np.linspace(xlims[0], xlims[1], nb_majorticks))
|
||||
ax.minorticks_on()
|
||||
ax.xaxis.set_minor_locator(mticker.AutoMinorLocator())
|
||||
assert len(ax.xaxis.get_minorticklocs()) == expected_nb_minorticks
|
||||
|
||||
limits = [(0, 1.39), (0, 0.139),
|
||||
(0, 0.11e-19), (0, 0.112e-12),
|
||||
(-2.0e-07, -3.3e-08), (1.20e-06, 1.42e-06),
|
||||
(-1.34e-06, -1.44e-06), (-8.76e-07, -1.51e-06)]
|
||||
|
||||
reference = [
|
||||
[0.05, 0.1, 0.15, 0.25, 0.3, 0.35, 0.45, 0.5, 0.55, 0.65, 0.7,
|
||||
0.75, 0.85, 0.9, 0.95, 1.05, 1.1, 1.15, 1.25, 1.3, 1.35],
|
||||
[0.005, 0.01, 0.015, 0.025, 0.03, 0.035, 0.045, 0.05, 0.055, 0.065,
|
||||
0.07, 0.075, 0.085, 0.09, 0.095, 0.105, 0.11, 0.115, 0.125, 0.13,
|
||||
0.135],
|
||||
[5.00e-22, 1.00e-21, 1.50e-21, 2.50e-21, 3.00e-21, 3.50e-21, 4.50e-21,
|
||||
5.00e-21, 5.50e-21, 6.50e-21, 7.00e-21, 7.50e-21, 8.50e-21, 9.00e-21,
|
||||
9.50e-21, 1.05e-20, 1.10e-20],
|
||||
[5.00e-15, 1.00e-14, 1.50e-14, 2.50e-14, 3.00e-14, 3.50e-14, 4.50e-14,
|
||||
5.00e-14, 5.50e-14, 6.50e-14, 7.00e-14, 7.50e-14, 8.50e-14, 9.00e-14,
|
||||
9.50e-14, 1.05e-13, 1.10e-13],
|
||||
[-1.95e-07, -1.90e-07, -1.85e-07, -1.75e-07, -1.70e-07, -1.65e-07,
|
||||
-1.55e-07, -1.50e-07, -1.45e-07, -1.35e-07, -1.30e-07, -1.25e-07,
|
||||
-1.15e-07, -1.10e-07, -1.05e-07, -9.50e-08, -9.00e-08, -8.50e-08,
|
||||
-7.50e-08, -7.00e-08, -6.50e-08, -5.50e-08, -5.00e-08, -4.50e-08,
|
||||
-3.50e-08],
|
||||
[1.21e-06, 1.22e-06, 1.23e-06, 1.24e-06, 1.26e-06, 1.27e-06, 1.28e-06,
|
||||
1.29e-06, 1.31e-06, 1.32e-06, 1.33e-06, 1.34e-06, 1.36e-06, 1.37e-06,
|
||||
1.38e-06, 1.39e-06, 1.41e-06, 1.42e-06],
|
||||
[-1.435e-06, -1.430e-06, -1.425e-06, -1.415e-06, -1.410e-06,
|
||||
-1.405e-06, -1.395e-06, -1.390e-06, -1.385e-06, -1.375e-06,
|
||||
-1.370e-06, -1.365e-06, -1.355e-06, -1.350e-06, -1.345e-06],
|
||||
[-1.48e-06, -1.46e-06, -1.44e-06, -1.42e-06, -1.38e-06, -1.36e-06,
|
||||
-1.34e-06, -1.32e-06, -1.28e-06, -1.26e-06, -1.24e-06, -1.22e-06,
|
||||
-1.18e-06, -1.16e-06, -1.14e-06, -1.12e-06, -1.08e-06, -1.06e-06,
|
||||
-1.04e-06, -1.02e-06, -9.80e-07, -9.60e-07, -9.40e-07, -9.20e-07,
|
||||
-8.80e-07]]
|
||||
|
||||
additional_data = list(zip(limits, reference))
|
||||
|
||||
@pytest.mark.parametrize('lim, ref', additional_data)
|
||||
def test_additional(self, lim, ref):
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
ax.minorticks_on()
|
||||
ax.grid(True, 'minor', 'y', linewidth=1)
|
||||
ax.grid(True, 'major', color='k', linewidth=1)
|
||||
ax.set_ylim(lim)
|
||||
|
||||
assert_almost_equal(ax.yaxis.get_ticklocs(minor=True), ref)
|
||||
|
||||
|
||||
class TestLogLocator(object):
|
||||
def test_basic(self):
|
||||
loc = mticker.LogLocator(numticks=5)
|
||||
with pytest.raises(ValueError):
|
||||
loc.tick_values(0, 1000)
|
||||
|
||||
test_value = np.array([1.00000000e-05, 1.00000000e-03, 1.00000000e-01,
|
||||
1.00000000e+01, 1.00000000e+03, 1.00000000e+05,
|
||||
1.00000000e+07, 1.000000000e+09])
|
||||
assert_almost_equal(loc.tick_values(0.001, 1.1e5), test_value)
|
||||
|
||||
loc = mticker.LogLocator(base=2)
|
||||
test_value = np.array([0.5, 1., 2., 4., 8., 16., 32., 64., 128., 256.])
|
||||
assert_almost_equal(loc.tick_values(1, 100), test_value)
|
||||
|
||||
def test_set_params(self):
|
||||
"""
|
||||
Create log locator with default value, base=10.0, subs=[1.0],
|
||||
numdecs=4, numticks=15 and change it to something else.
|
||||
See if change was successful. Should not raise exception.
|
||||
"""
|
||||
loc = mticker.LogLocator()
|
||||
loc.set_params(numticks=7, numdecs=8, subs=[2.0], base=4)
|
||||
assert loc.numticks == 7
|
||||
assert loc.numdecs == 8
|
||||
assert loc._base == 4
|
||||
assert list(loc._subs) == [2.0]
|
||||
|
||||
|
||||
class TestNullLocator(object):
|
||||
def test_set_params(self):
|
||||
"""
|
||||
Create null locator, and attempt to call set_params() on it.
|
||||
Should not exception, and should raise a warning.
|
||||
"""
|
||||
loc = mticker.NullLocator()
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
loc.set_params()
|
||||
assert len(w) == 1
|
||||
|
||||
|
||||
class TestLogitLocator(object):
|
||||
def test_set_params(self):
|
||||
"""
|
||||
Create logit locator with default minor=False, and change it to
|
||||
something else. See if change was successful. Should not exception.
|
||||
"""
|
||||
loc = mticker.LogitLocator() # Defaults to false.
|
||||
loc.set_params(minor=True)
|
||||
assert loc.minor
|
||||
|
||||
|
||||
class TestFixedLocator(object):
|
||||
def test_set_params(self):
|
||||
"""
|
||||
Create fixed locator with 5 nbins, and change it to something else.
|
||||
See if change was successful.
|
||||
Should not exception.
|
||||
"""
|
||||
fixed = mticker.FixedLocator(range(0, 24), nbins=5)
|
||||
fixed.set_params(nbins=7)
|
||||
assert fixed.nbins == 7
|
||||
|
||||
|
||||
class TestIndexLocator(object):
|
||||
def test_set_params(self):
|
||||
"""
|
||||
Create index locator with 3 base, 4 offset. and change it to something
|
||||
else. See if change was successful.
|
||||
Should not exception.
|
||||
"""
|
||||
index = mticker.IndexLocator(base=3, offset=4)
|
||||
index.set_params(base=7, offset=7)
|
||||
assert index._base == 7
|
||||
assert index.offset == 7
|
||||
|
||||
|
||||
class TestSymmetricalLogLocator(object):
|
||||
def test_set_params(self):
|
||||
"""
|
||||
Create symmetrical log locator with default subs =[1.0] numticks = 15,
|
||||
and change it to something else.
|
||||
See if change was successful.
|
||||
Should not exception.
|
||||
"""
|
||||
sym = mticker.SymmetricalLogLocator(base=10, linthresh=1)
|
||||
sym.set_params(subs=[2.0], numticks=8)
|
||||
assert sym._subs == [2.0]
|
||||
assert sym.numticks == 8
|
||||
|
||||
|
||||
class TestScalarFormatter(object):
|
||||
offset_data = [
|
||||
(123, 189, 0),
|
||||
(-189, -123, 0),
|
||||
(12341, 12349, 12340),
|
||||
(-12349, -12341, -12340),
|
||||
(99999.5, 100010.5, 100000),
|
||||
(-100010.5, -99999.5, -100000),
|
||||
(99990.5, 100000.5, 100000),
|
||||
(-100000.5, -99990.5, -100000),
|
||||
(1233999, 1234001, 1234000),
|
||||
(-1234001, -1233999, -1234000),
|
||||
(1, 1, 1),
|
||||
(123, 123, 120),
|
||||
# Test cases courtesy of @WeatherGod
|
||||
(.4538, .4578, .45),
|
||||
(3789.12, 3783.1, 3780),
|
||||
(45124.3, 45831.75, 45000),
|
||||
(0.000721, 0.0007243, 0.00072),
|
||||
(12592.82, 12591.43, 12590),
|
||||
(9., 12., 0),
|
||||
(900., 1200., 0),
|
||||
(1900., 1200., 0),
|
||||
(0.99, 1.01, 1),
|
||||
(9.99, 10.01, 10),
|
||||
(99.99, 100.01, 100),
|
||||
(5.99, 6.01, 6),
|
||||
(15.99, 16.01, 16),
|
||||
(-0.452, 0.492, 0),
|
||||
(-0.492, 0.492, 0),
|
||||
(12331.4, 12350.5, 12300),
|
||||
(-12335.3, 12335.3, 0),
|
||||
]
|
||||
|
||||
use_offset_data = [True, False]
|
||||
|
||||
scilimits_data = [
|
||||
(False, (0, 0), (10.0, 20.0), 0),
|
||||
(True, (-2, 2), (-10, 20), 0),
|
||||
(True, (-2, 2), (-20, 10), 0),
|
||||
(True, (-2, 2), (-110, 120), 2),
|
||||
(True, (-2, 2), (-120, 110), 2),
|
||||
(True, (-2, 2), (-.001, 0.002), -3),
|
||||
(True, (0, 0), (-1e5, 1e5), 5),
|
||||
(True, (6, 6), (-1e5, 1e5), 6),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('left, right, offset', offset_data)
|
||||
def test_offset_value(self, left, right, offset):
|
||||
fig, ax = plt.subplots()
|
||||
formatter = ax.get_xaxis().get_major_formatter()
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.filterwarnings('always', 'Attempting to set identical',
|
||||
UserWarning)
|
||||
ax.set_xlim(left, right)
|
||||
assert len(w) == (1 if left == right else 0)
|
||||
# Update ticks.
|
||||
next(ax.get_xaxis().iter_ticks())
|
||||
assert formatter.offset == offset
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.filterwarnings('always', 'Attempting to set identical',
|
||||
UserWarning)
|
||||
ax.set_xlim(right, left)
|
||||
assert len(w) == (1 if left == right else 0)
|
||||
# Update ticks.
|
||||
next(ax.get_xaxis().iter_ticks())
|
||||
assert formatter.offset == offset
|
||||
|
||||
@pytest.mark.parametrize('use_offset', use_offset_data)
|
||||
def test_use_offset(self, use_offset):
|
||||
with matplotlib.rc_context({'axes.formatter.useoffset': use_offset}):
|
||||
tmp_form = mticker.ScalarFormatter()
|
||||
assert use_offset == tmp_form.get_useOffset()
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'sci_type, scilimits, lim, orderOfMag', scilimits_data)
|
||||
def test_scilimits(self, sci_type, scilimits, lim, orderOfMag):
|
||||
tmp_form = mticker.ScalarFormatter()
|
||||
tmp_form.set_scientific(sci_type)
|
||||
tmp_form.set_powerlimits(scilimits)
|
||||
fig, ax = plt.subplots()
|
||||
ax.yaxis.set_major_formatter(tmp_form)
|
||||
ax.set_ylim(*lim)
|
||||
tmp_form.set_locs(ax.yaxis.get_majorticklocs())
|
||||
assert orderOfMag == tmp_form.orderOfMagnitude
|
||||
|
||||
|
||||
class FakeAxis(object):
|
||||
"""Allow Formatter to be called without having a "full" plot set up."""
|
||||
def __init__(self, vmin=1, vmax=10):
|
||||
self.vmin = vmin
|
||||
self.vmax = vmax
|
||||
|
||||
def get_view_interval(self):
|
||||
return self.vmin, self.vmax
|
||||
|
||||
|
||||
class TestLogFormatterExponent(object):
|
||||
param_data = [
|
||||
(True, 4, np.arange(-3, 4.0), np.arange(-3, 4.0),
|
||||
['-3', '-2', '-1', '0', '1', '2', '3']),
|
||||
# With labelOnlyBase=False, non-integer powers should be nicely
|
||||
# formatted.
|
||||
(False, 10, np.array([0.1, 0.00001, np.pi, 0.2, -0.2, -0.00001]),
|
||||
range(6), ['0.1', '1e-05', '3.14', '0.2', '-0.2', '-1e-05']),
|
||||
(False, 50, np.array([3, 5, 12, 42], dtype='float'), range(6),
|
||||
['3', '5', '12', '42']),
|
||||
]
|
||||
|
||||
base_data = [2.0, 5.0, 10.0, np.pi, np.e]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'labelOnlyBase, exponent, locs, positions, expected', param_data)
|
||||
@pytest.mark.parametrize('base', base_data)
|
||||
def test_basic(self, labelOnlyBase, base, exponent, locs, positions,
|
||||
expected):
|
||||
formatter = mticker.LogFormatterExponent(base=base,
|
||||
labelOnlyBase=labelOnlyBase)
|
||||
formatter.axis = FakeAxis(1, base**exponent)
|
||||
vals = base**locs
|
||||
labels = [formatter(x, pos) for (x, pos) in zip(vals, positions)]
|
||||
assert labels == expected
|
||||
|
||||
def test_blank(self):
|
||||
# Should be a blank string for non-integer powers if labelOnlyBase=True
|
||||
formatter = mticker.LogFormatterExponent(base=10, labelOnlyBase=True)
|
||||
formatter.axis = FakeAxis()
|
||||
assert formatter(10**0.1) == ''
|
||||
|
||||
|
||||
class TestLogFormatterMathtext():
|
||||
fmt = mticker.LogFormatterMathtext()
|
||||
test_data = [
|
||||
(0, 1, '$\\mathdefault{10^{0}}$'),
|
||||
(0, 1e-2, '$\\mathdefault{10^{-2}}$'),
|
||||
(0, 1e2, '$\\mathdefault{10^{2}}$'),
|
||||
(3, 1, '$\\mathdefault{1}$'),
|
||||
(3, 1e-2, '$\\mathdefault{0.01}$'),
|
||||
(3, 1e2, '$\\mathdefault{100}$'),
|
||||
(3, 1e-3, '$\\mathdefault{10^{-3}}$'),
|
||||
(3, 1e3, '$\\mathdefault{10^{3}}$'),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('min_exponent, value, expected', test_data)
|
||||
def test_min_exponent(self, min_exponent, value, expected):
|
||||
with matplotlib.rc_context({'axes.formatter.min_exponent':
|
||||
min_exponent}):
|
||||
assert self.fmt(value) == expected
|
||||
|
||||
|
||||
class TestLogFormatterSciNotation(object):
|
||||
test_data = [
|
||||
(2, 0.03125, '$\\mathdefault{2^{-5}}$'),
|
||||
(2, 1, '$\\mathdefault{2^{0}}$'),
|
||||
(2, 32, '$\\mathdefault{2^{5}}$'),
|
||||
(2, 0.0375, '$\\mathdefault{1.2\\times2^{-5}}$'),
|
||||
(2, 1.2, '$\\mathdefault{1.2\\times2^{0}}$'),
|
||||
(2, 38.4, '$\\mathdefault{1.2\\times2^{5}}$'),
|
||||
(10, -1, '$\\mathdefault{-10^{0}}$'),
|
||||
(10, 1e-05, '$\\mathdefault{10^{-5}}$'),
|
||||
(10, 1, '$\\mathdefault{10^{0}}$'),
|
||||
(10, 100000, '$\\mathdefault{10^{5}}$'),
|
||||
(10, 2e-05, '$\\mathdefault{2\\times10^{-5}}$'),
|
||||
(10, 2, '$\\mathdefault{2\\times10^{0}}$'),
|
||||
(10, 200000, '$\\mathdefault{2\\times10^{5}}$'),
|
||||
(10, 5e-05, '$\\mathdefault{5\\times10^{-5}}$'),
|
||||
(10, 5, '$\\mathdefault{5\\times10^{0}}$'),
|
||||
(10, 500000, '$\\mathdefault{5\\times10^{5}}$'),
|
||||
]
|
||||
|
||||
@pytest.mark.style('default')
|
||||
@pytest.mark.parametrize('base, value, expected', test_data)
|
||||
def test_basic(self, base, value, expected):
|
||||
formatter = mticker.LogFormatterSciNotation(base=base)
|
||||
formatter.sublabel = {1, 2, 5, 1.2}
|
||||
with matplotlib.rc_context({'text.usetex': False}):
|
||||
assert formatter(value) == expected
|
||||
|
||||
|
||||
class TestLogFormatter(object):
|
||||
pprint_data = [
|
||||
(3.141592654e-05, 0.001, '3.142e-5'),
|
||||
(0.0003141592654, 0.001, '3.142e-4'),
|
||||
(0.003141592654, 0.001, '3.142e-3'),
|
||||
(0.03141592654, 0.001, '3.142e-2'),
|
||||
(0.3141592654, 0.001, '3.142e-1'),
|
||||
(3.141592654, 0.001, '3.142'),
|
||||
(31.41592654, 0.001, '3.142e1'),
|
||||
(314.1592654, 0.001, '3.142e2'),
|
||||
(3141.592654, 0.001, '3.142e3'),
|
||||
(31415.92654, 0.001, '3.142e4'),
|
||||
(314159.2654, 0.001, '3.142e5'),
|
||||
(1e-05, 0.001, '1e-5'),
|
||||
(0.0001, 0.001, '1e-4'),
|
||||
(0.001, 0.001, '1e-3'),
|
||||
(0.01, 0.001, '1e-2'),
|
||||
(0.1, 0.001, '1e-1'),
|
||||
(1, 0.001, '1'),
|
||||
(10, 0.001, '10'),
|
||||
(100, 0.001, '100'),
|
||||
(1000, 0.001, '1000'),
|
||||
(10000, 0.001, '1e4'),
|
||||
(100000, 0.001, '1e5'),
|
||||
(3.141592654e-05, 0.015, '0'),
|
||||
(0.0003141592654, 0.015, '0'),
|
||||
(0.003141592654, 0.015, '0.003'),
|
||||
(0.03141592654, 0.015, '0.031'),
|
||||
(0.3141592654, 0.015, '0.314'),
|
||||
(3.141592654, 0.015, '3.142'),
|
||||
(31.41592654, 0.015, '31.416'),
|
||||
(314.1592654, 0.015, '314.159'),
|
||||
(3141.592654, 0.015, '3141.593'),
|
||||
(31415.92654, 0.015, '31415.927'),
|
||||
(314159.2654, 0.015, '314159.265'),
|
||||
(1e-05, 0.015, '0'),
|
||||
(0.0001, 0.015, '0'),
|
||||
(0.001, 0.015, '0.001'),
|
||||
(0.01, 0.015, '0.01'),
|
||||
(0.1, 0.015, '0.1'),
|
||||
(1, 0.015, '1'),
|
||||
(10, 0.015, '10'),
|
||||
(100, 0.015, '100'),
|
||||
(1000, 0.015, '1000'),
|
||||
(10000, 0.015, '10000'),
|
||||
(100000, 0.015, '100000'),
|
||||
(3.141592654e-05, 0.5, '0'),
|
||||
(0.0003141592654, 0.5, '0'),
|
||||
(0.003141592654, 0.5, '0.003'),
|
||||
(0.03141592654, 0.5, '0.031'),
|
||||
(0.3141592654, 0.5, '0.314'),
|
||||
(3.141592654, 0.5, '3.142'),
|
||||
(31.41592654, 0.5, '31.416'),
|
||||
(314.1592654, 0.5, '314.159'),
|
||||
(3141.592654, 0.5, '3141.593'),
|
||||
(31415.92654, 0.5, '31415.927'),
|
||||
(314159.2654, 0.5, '314159.265'),
|
||||
(1e-05, 0.5, '0'),
|
||||
(0.0001, 0.5, '0'),
|
||||
(0.001, 0.5, '0.001'),
|
||||
(0.01, 0.5, '0.01'),
|
||||
(0.1, 0.5, '0.1'),
|
||||
(1, 0.5, '1'),
|
||||
(10, 0.5, '10'),
|
||||
(100, 0.5, '100'),
|
||||
(1000, 0.5, '1000'),
|
||||
(10000, 0.5, '10000'),
|
||||
(100000, 0.5, '100000'),
|
||||
(3.141592654e-05, 5, '0'),
|
||||
(0.0003141592654, 5, '0'),
|
||||
(0.003141592654, 5, '0'),
|
||||
(0.03141592654, 5, '0.03'),
|
||||
(0.3141592654, 5, '0.31'),
|
||||
(3.141592654, 5, '3.14'),
|
||||
(31.41592654, 5, '31.42'),
|
||||
(314.1592654, 5, '314.16'),
|
||||
(3141.592654, 5, '3141.59'),
|
||||
(31415.92654, 5, '31415.93'),
|
||||
(314159.2654, 5, '314159.27'),
|
||||
(1e-05, 5, '0'),
|
||||
(0.0001, 5, '0'),
|
||||
(0.001, 5, '0'),
|
||||
(0.01, 5, '0.01'),
|
||||
(0.1, 5, '0.1'),
|
||||
(1, 5, '1'),
|
||||
(10, 5, '10'),
|
||||
(100, 5, '100'),
|
||||
(1000, 5, '1000'),
|
||||
(10000, 5, '10000'),
|
||||
(100000, 5, '100000'),
|
||||
(3.141592654e-05, 100, '0'),
|
||||
(0.0003141592654, 100, '0'),
|
||||
(0.003141592654, 100, '0'),
|
||||
(0.03141592654, 100, '0'),
|
||||
(0.3141592654, 100, '0.3'),
|
||||
(3.141592654, 100, '3.1'),
|
||||
(31.41592654, 100, '31.4'),
|
||||
(314.1592654, 100, '314.2'),
|
||||
(3141.592654, 100, '3141.6'),
|
||||
(31415.92654, 100, '31415.9'),
|
||||
(314159.2654, 100, '314159.3'),
|
||||
(1e-05, 100, '0'),
|
||||
(0.0001, 100, '0'),
|
||||
(0.001, 100, '0'),
|
||||
(0.01, 100, '0'),
|
||||
(0.1, 100, '0.1'),
|
||||
(1, 100, '1'),
|
||||
(10, 100, '10'),
|
||||
(100, 100, '100'),
|
||||
(1000, 100, '1000'),
|
||||
(10000, 100, '10000'),
|
||||
(100000, 100, '100000'),
|
||||
(3.141592654e-05, 1000000.0, '3.1e-5'),
|
||||
(0.0003141592654, 1000000.0, '3.1e-4'),
|
||||
(0.003141592654, 1000000.0, '3.1e-3'),
|
||||
(0.03141592654, 1000000.0, '3.1e-2'),
|
||||
(0.3141592654, 1000000.0, '3.1e-1'),
|
||||
(3.141592654, 1000000.0, '3.1'),
|
||||
(31.41592654, 1000000.0, '3.1e1'),
|
||||
(314.1592654, 1000000.0, '3.1e2'),
|
||||
(3141.592654, 1000000.0, '3.1e3'),
|
||||
(31415.92654, 1000000.0, '3.1e4'),
|
||||
(314159.2654, 1000000.0, '3.1e5'),
|
||||
(1e-05, 1000000.0, '1e-5'),
|
||||
(0.0001, 1000000.0, '1e-4'),
|
||||
(0.001, 1000000.0, '1e-3'),
|
||||
(0.01, 1000000.0, '1e-2'),
|
||||
(0.1, 1000000.0, '1e-1'),
|
||||
(1, 1000000.0, '1'),
|
||||
(10, 1000000.0, '10'),
|
||||
(100, 1000000.0, '100'),
|
||||
(1000, 1000000.0, '1000'),
|
||||
(10000, 1000000.0, '1e4'),
|
||||
(100000, 1000000.0, '1e5'),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('value, domain, expected', pprint_data)
|
||||
def test_pprint(self, value, domain, expected):
|
||||
fmt = mticker.LogFormatter()
|
||||
label = fmt.pprint_val(value, domain)
|
||||
assert label == expected
|
||||
|
||||
def _sub_labels(self, axis, subs=()):
|
||||
"Test whether locator marks subs to be labeled"
|
||||
fmt = axis.get_minor_formatter()
|
||||
minor_tlocs = axis.get_minorticklocs()
|
||||
fmt.set_locs(minor_tlocs)
|
||||
coefs = minor_tlocs / 10**(np.floor(np.log10(minor_tlocs)))
|
||||
label_expected = [np.round(c) in subs for c in coefs]
|
||||
label_test = [fmt(x) != '' for x in minor_tlocs]
|
||||
assert label_test == label_expected
|
||||
|
||||
@pytest.mark.style('default')
|
||||
def test_sublabel(self):
|
||||
# test label locator
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_xscale('log')
|
||||
ax.xaxis.set_major_locator(mticker.LogLocator(base=10, subs=[]))
|
||||
ax.xaxis.set_minor_locator(mticker.LogLocator(base=10,
|
||||
subs=np.arange(2, 10)))
|
||||
ax.xaxis.set_major_formatter(mticker.LogFormatter(labelOnlyBase=True))
|
||||
ax.xaxis.set_minor_formatter(mticker.LogFormatter(labelOnlyBase=False))
|
||||
# axis range above 3 decades, only bases are labeled
|
||||
ax.set_xlim(1, 1e4)
|
||||
fmt = ax.xaxis.get_major_formatter()
|
||||
fmt.set_locs(ax.xaxis.get_majorticklocs())
|
||||
show_major_labels = [fmt(x) != ''
|
||||
for x in ax.xaxis.get_majorticklocs()]
|
||||
assert np.all(show_major_labels)
|
||||
self._sub_labels(ax.xaxis, subs=[])
|
||||
|
||||
# For the next two, if the numdec threshold in LogFormatter.set_locs
|
||||
# were 3, then the label sub would be 3 for 2-3 decades and (2,5)
|
||||
# for 1-2 decades. With a threshold of 1, subs are not labeled.
|
||||
# axis range at 2 to 3 decades
|
||||
ax.set_xlim(1, 800)
|
||||
self._sub_labels(ax.xaxis, subs=[])
|
||||
|
||||
# axis range at 1 to 2 decades
|
||||
ax.set_xlim(1, 80)
|
||||
self._sub_labels(ax.xaxis, subs=[])
|
||||
|
||||
# axis range at 0.4 to 1 decades, label subs 2, 3, 4, 6
|
||||
ax.set_xlim(1, 8)
|
||||
self._sub_labels(ax.xaxis, subs=[2, 3, 4, 6])
|
||||
|
||||
# axis range at 0 to 0.4 decades, label all
|
||||
ax.set_xlim(0.5, 0.9)
|
||||
self._sub_labels(ax.xaxis, subs=np.arange(2, 10, dtype=int))
|
||||
|
||||
@pytest.mark.parametrize('val', [1, 10, 100, 1000])
|
||||
def test_LogFormatter_call(self, val):
|
||||
# test _num_to_string method used in __call__
|
||||
temp_lf = mticker.LogFormatter()
|
||||
temp_lf.axis = FakeAxis()
|
||||
assert temp_lf(val) == str(val)
|
||||
|
||||
|
||||
class TestFormatStrFormatter(object):
|
||||
def test_basic(self):
|
||||
# test % style formatter
|
||||
tmp_form = mticker.FormatStrFormatter('%05d')
|
||||
assert '00002' == tmp_form(2)
|
||||
|
||||
|
||||
class TestStrMethodFormatter(object):
|
||||
test_data = [
|
||||
('{x:05d}', (2,), '00002'),
|
||||
('{x:03d}-{pos:02d}', (2, 1), '002-01'),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('format, input, expected', test_data)
|
||||
def test_basic(self, format, input, expected):
|
||||
fmt = mticker.StrMethodFormatter(format)
|
||||
assert fmt(*input) == expected
|
||||
|
||||
|
||||
class TestEngFormatter(object):
|
||||
# (input, expected) where ''expected'' corresponds to the outputs
|
||||
# respectively returned when (places=None, places=0, places=2)
|
||||
raw_format_data = [
|
||||
(-1234.56789, ('-1.23457 k', '-1 k', '-1.23 k')),
|
||||
(-1.23456789, ('-1.23457', '-1', '-1.23')),
|
||||
(-0.123456789, ('-123.457 m', '-123 m', '-123.46 m')),
|
||||
(-0.00123456789, ('-1.23457 m', '-1 m', '-1.23 m')),
|
||||
(-0.0, ('0', '0', '0.00')),
|
||||
(-0, ('0', '0', '0.00')),
|
||||
(0, ('0', '0', '0.00')),
|
||||
(1.23456789e-6, ('1.23457 \u03bc', '1 \u03bc', '1.23 \u03bc')),
|
||||
(0.123456789, ('123.457 m', '123 m', '123.46 m')),
|
||||
(0.1, ('100 m', '100 m', '100.00 m')),
|
||||
(1, ('1', '1', '1.00')),
|
||||
(1.23456789, ('1.23457', '1', '1.23')),
|
||||
(999.9, ('999.9', '1 k', '999.90')), # places=0: corner-case rounding
|
||||
(999.9999, ('1 k', '1 k', '1.00 k')), # corner-case roudning for all
|
||||
(1000, ('1 k', '1 k', '1.00 k')),
|
||||
(1001, ('1.001 k', '1 k', '1.00 k')),
|
||||
(100001, ('100.001 k', '100 k', '100.00 k')),
|
||||
(987654.321, ('987.654 k', '988 k', '987.65 k')),
|
||||
(1.23e27, ('1230 Y', '1230 Y', '1230.00 Y')) # OoR value (> 1000 Y)
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('input, expected', raw_format_data)
|
||||
def test_params(self, input, expected):
|
||||
"""
|
||||
Test the formatting of EngFormatter for various values of the 'places'
|
||||
argument, in several cases:
|
||||
0. without a unit symbol but with a (default) space separator;
|
||||
1. with both a unit symbol and a (default) space separator;
|
||||
2. with both a unit symbol and some non default separators;
|
||||
3. without a unit symbol but with some non default separators.
|
||||
Note that cases 2. and 3. are looped over several separator strings.
|
||||
"""
|
||||
|
||||
UNIT = 's' # seconds
|
||||
DIGITS = '0123456789' # %timeit showed 10-20% faster search than set
|
||||
|
||||
# Case 0: unit='' (default) and sep=' ' (default).
|
||||
# 'expected' already corresponds to this reference case.
|
||||
exp_outputs = expected
|
||||
formatters = (
|
||||
mticker.EngFormatter(), # places=None (default)
|
||||
mticker.EngFormatter(places=0),
|
||||
mticker.EngFormatter(places=2)
|
||||
)
|
||||
for _formatter, _exp_output in zip(formatters, exp_outputs):
|
||||
assert _formatter(input) == _exp_output
|
||||
|
||||
# Case 1: unit=UNIT and sep=' ' (default).
|
||||
# Append a unit symbol to the reference case.
|
||||
# Beware of the values in [1, 1000), where there is no prefix!
|
||||
exp_outputs = (_s + " " + UNIT if _s[-1] in DIGITS # case w/o prefix
|
||||
else _s + UNIT for _s in expected)
|
||||
formatters = (
|
||||
mticker.EngFormatter(unit=UNIT), # places=None (default)
|
||||
mticker.EngFormatter(unit=UNIT, places=0),
|
||||
mticker.EngFormatter(unit=UNIT, places=2)
|
||||
)
|
||||
for _formatter, _exp_output in zip(formatters, exp_outputs):
|
||||
assert _formatter(input) == _exp_output
|
||||
|
||||
# Test several non default separators: no separator, a narrow
|
||||
# no-break space (unicode character) and an extravagant string.
|
||||
for _sep in ("", "\N{NARROW NO-BREAK SPACE}", "@_@"):
|
||||
# Case 2: unit=UNIT and sep=_sep.
|
||||
# Replace the default space separator from the reference case
|
||||
# with the tested one `_sep` and append a unit symbol to it.
|
||||
exp_outputs = (_s + _sep + UNIT if _s[-1] in DIGITS # no prefix
|
||||
else _s.replace(" ", _sep) + UNIT
|
||||
for _s in expected)
|
||||
formatters = (
|
||||
mticker.EngFormatter(unit=UNIT, sep=_sep), # places=None
|
||||
mticker.EngFormatter(unit=UNIT, places=0, sep=_sep),
|
||||
mticker.EngFormatter(unit=UNIT, places=2, sep=_sep)
|
||||
)
|
||||
for _formatter, _exp_output in zip(formatters, exp_outputs):
|
||||
assert _formatter(input) == _exp_output
|
||||
|
||||
# Case 3: unit='' (default) and sep=_sep.
|
||||
# Replace the default space separator from the reference case
|
||||
# with the tested one `_sep`. Reference case is already unitless.
|
||||
exp_outputs = (_s.replace(" ", _sep) for _s in expected)
|
||||
formatters = (
|
||||
mticker.EngFormatter(sep=_sep), # places=None (default)
|
||||
mticker.EngFormatter(places=0, sep=_sep),
|
||||
mticker.EngFormatter(places=2, sep=_sep)
|
||||
)
|
||||
for _formatter, _exp_output in zip(formatters, exp_outputs):
|
||||
assert _formatter(input) == _exp_output
|
||||
|
||||
|
||||
class TestPercentFormatter(object):
|
||||
percent_data = [
|
||||
# Check explicitly set decimals over different intervals and values
|
||||
(100, 0, '%', 120, 100, '120%'),
|
||||
(100, 0, '%', 100, 90, '100%'),
|
||||
(100, 0, '%', 90, 50, '90%'),
|
||||
(100, 0, '%', -1.7, 40, '-2%'),
|
||||
(100, 1, '%', 90.0, 100, '90.0%'),
|
||||
(100, 1, '%', 80.1, 90, '80.1%'),
|
||||
(100, 1, '%', 70.23, 50, '70.2%'),
|
||||
# 60.554 instead of 60.55: see https://bugs.python.org/issue5118
|
||||
(100, 1, '%', -60.554, 40, '-60.6%'),
|
||||
# Check auto decimals over different intervals and values
|
||||
(100, None, '%', 95, 1, '95.00%'),
|
||||
(1.0, None, '%', 3, 6, '300%'),
|
||||
(17.0, None, '%', 1, 8.5, '6%'),
|
||||
(17.0, None, '%', 1, 8.4, '5.9%'),
|
||||
(5, None, '%', -100, 0.000001, '-2000.00000%'),
|
||||
# Check percent symbol
|
||||
(1.0, 2, None, 1.2, 100, '120.00'),
|
||||
(75, 3, '', 50, 100, '66.667'),
|
||||
(42, None, '^^Foobar$$', 21, 12, '50.0^^Foobar$$'),
|
||||
]
|
||||
|
||||
percent_ids = [
|
||||
# Check explicitly set decimals over different intervals and values
|
||||
'decimals=0, x>100%',
|
||||
'decimals=0, x=100%',
|
||||
'decimals=0, x<100%',
|
||||
'decimals=0, x<0%',
|
||||
'decimals=1, x>100%',
|
||||
'decimals=1, x=100%',
|
||||
'decimals=1, x<100%',
|
||||
'decimals=1, x<0%',
|
||||
# Check auto decimals over different intervals and values
|
||||
'autodecimal, x<100%, display_range=1',
|
||||
'autodecimal, x>100%, display_range=6 (custom xmax test)',
|
||||
'autodecimal, x<100%, display_range=8.5 (autodecimal test 1)',
|
||||
'autodecimal, x<100%, display_range=8.4 (autodecimal test 2)',
|
||||
'autodecimal, x<-100%, display_range=1e-6 (tiny display range)',
|
||||
# Check percent symbol
|
||||
'None as percent symbol',
|
||||
'Empty percent symbol',
|
||||
'Custom percent symbol',
|
||||
]
|
||||
|
||||
latex_data = [
|
||||
(False, False, r'50\{t}%'),
|
||||
(False, True, r'50\\\{t\}\%'),
|
||||
(True, False, r'50\{t}%'),
|
||||
(True, True, r'50\{t}%'),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'xmax, decimals, symbol, x, display_range, expected',
|
||||
percent_data, ids=percent_ids)
|
||||
def test_basic(self, xmax, decimals, symbol,
|
||||
x, display_range, expected):
|
||||
formatter = mticker.PercentFormatter(xmax, decimals, symbol)
|
||||
with matplotlib.rc_context(rc={'text.usetex': False}):
|
||||
assert formatter.format_pct(x, display_range) == expected
|
||||
|
||||
@pytest.mark.parametrize('is_latex, usetex, expected', latex_data)
|
||||
def test_latex(self, is_latex, usetex, expected):
|
||||
fmt = mticker.PercentFormatter(symbol='\\{t}%', is_latex=is_latex)
|
||||
with matplotlib.rc_context(rc={'text.usetex': usetex}):
|
||||
assert fmt.format_pct(50, 100) == expected
|
||||
|
||||
|
||||
def test_majformatter_type():
|
||||
fig, ax = plt.subplots()
|
||||
with pytest.raises(TypeError):
|
||||
ax.xaxis.set_major_formatter(matplotlib.ticker.LogLocator())
|
||||
|
||||
|
||||
def test_minformatter_type():
|
||||
fig, ax = plt.subplots()
|
||||
with pytest.raises(TypeError):
|
||||
ax.xaxis.set_minor_formatter(matplotlib.ticker.LogLocator())
|
||||
|
||||
|
||||
def test_majlocator_type():
|
||||
fig, ax = plt.subplots()
|
||||
with pytest.raises(TypeError):
|
||||
ax.xaxis.set_major_locator(matplotlib.ticker.LogFormatter())
|
||||
|
||||
|
||||
def test_minlocator_type():
|
||||
fig, ax = plt.subplots()
|
||||
with pytest.raises(TypeError):
|
||||
ax.xaxis.set_minor_locator(matplotlib.ticker.LogFormatter())
|
||||
@@ -0,0 +1,334 @@
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.offsetbox import AnchoredOffsetbox, DrawingArea
|
||||
from matplotlib.patches import Rectangle
|
||||
|
||||
|
||||
def example_plot(ax, fontsize=12):
|
||||
ax.plot([1, 2])
|
||||
ax.locator_params(nbins=3)
|
||||
ax.set_xlabel('x-label', fontsize=fontsize)
|
||||
ax.set_ylabel('y-label', fontsize=fontsize)
|
||||
ax.set_title('Title', fontsize=fontsize)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout1'])
|
||||
def test_tight_layout1():
|
||||
'Test tight_layout for a single subplot'
|
||||
fig, ax = plt.subplots()
|
||||
example_plot(ax, fontsize=24)
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout2'])
|
||||
def test_tight_layout2():
|
||||
'Test tight_layout for multiple subplots'
|
||||
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2)
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout3'])
|
||||
def test_tight_layout3():
|
||||
'Test tight_layout for multiple subplots'
|
||||
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = plt.subplot(221)
|
||||
ax2 = plt.subplot(223)
|
||||
ax3 = plt.subplot(122)
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout4'],
|
||||
freetype_version=('2.5.5', '2.6.1'))
|
||||
def test_tight_layout4():
|
||||
'Test tight_layout for subplot2grid'
|
||||
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = plt.subplot2grid((3, 3), (0, 0))
|
||||
ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
|
||||
ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
|
||||
ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
example_plot(ax3)
|
||||
example_plot(ax4)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout5'])
|
||||
def test_tight_layout5():
|
||||
'Test tight_layout for image'
|
||||
|
||||
fig = plt.figure()
|
||||
|
||||
ax = plt.subplot(111)
|
||||
arr = np.arange(100).reshape((10, 10))
|
||||
ax.imshow(arr, interpolation="none")
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout6'])
|
||||
def test_tight_layout6():
|
||||
'Test tight_layout for gridspec'
|
||||
|
||||
# This raises warnings since tight layout cannot
|
||||
# do this fully automatically. But the test is
|
||||
# correct since the layout is manually edited
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", UserWarning)
|
||||
fig = plt.figure()
|
||||
|
||||
import matplotlib.gridspec as gridspec
|
||||
|
||||
gs1 = gridspec.GridSpec(2, 1)
|
||||
ax1 = fig.add_subplot(gs1[0])
|
||||
ax2 = fig.add_subplot(gs1[1])
|
||||
|
||||
example_plot(ax1)
|
||||
example_plot(ax2)
|
||||
|
||||
gs1.tight_layout(fig, rect=[0, 0, 0.5, 1])
|
||||
|
||||
gs2 = gridspec.GridSpec(3, 1)
|
||||
|
||||
for ss in gs2:
|
||||
ax = fig.add_subplot(ss)
|
||||
example_plot(ax)
|
||||
ax.set_title("")
|
||||
ax.set_xlabel("")
|
||||
|
||||
ax.set_xlabel("x-label", fontsize=12)
|
||||
|
||||
gs2.tight_layout(fig, rect=[0.5, 0, 1, 1], h_pad=0.45)
|
||||
|
||||
top = min(gs1.top, gs2.top)
|
||||
bottom = max(gs1.bottom, gs2.bottom)
|
||||
|
||||
gs1.tight_layout(fig, rect=[None, 0 + (bottom-gs1.bottom),
|
||||
0.5, 1 - (gs1.top-top)])
|
||||
gs2.tight_layout(fig, rect=[0.5, 0 + (bottom-gs2.bottom),
|
||||
None, 1 - (gs2.top-top)],
|
||||
h_pad=0.45)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout7'])
|
||||
def test_tight_layout7():
|
||||
# tight layout with left and right titles
|
||||
fontsize = 24
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot([1, 2])
|
||||
ax.locator_params(nbins=3)
|
||||
ax.set_xlabel('x-label', fontsize=fontsize)
|
||||
ax.set_ylabel('y-label', fontsize=fontsize)
|
||||
ax.set_title('Left Title', loc='left', fontsize=fontsize)
|
||||
ax.set_title('Right Title', loc='right', fontsize=fontsize)
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout8'])
|
||||
def test_tight_layout8():
|
||||
'Test automatic use of tight_layout'
|
||||
fig = plt.figure()
|
||||
fig.set_tight_layout({'pad': .1})
|
||||
ax = fig.add_subplot(111)
|
||||
example_plot(ax, fontsize=24)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout9'])
|
||||
def test_tight_layout9():
|
||||
# Test tight_layout for non-visible suplots
|
||||
# GH 8244
|
||||
f, axarr = plt.subplots(2, 2)
|
||||
axarr[1][1].set_visible(False)
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
# The following test is misleading when the text is removed.
|
||||
@image_comparison(baseline_images=['outward_ticks'], remove_text=False)
|
||||
def test_outward_ticks():
|
||||
'Test automatic use of tight_layout'
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(221)
|
||||
ax.xaxis.set_tick_params(tickdir='out', length=16, width=3)
|
||||
ax.yaxis.set_tick_params(tickdir='out', length=16, width=3)
|
||||
ax.xaxis.set_tick_params(
|
||||
tickdir='out', length=32, width=3, tick1On=True, which='minor')
|
||||
ax.yaxis.set_tick_params(
|
||||
tickdir='out', length=32, width=3, tick1On=True, which='minor')
|
||||
# The following minor ticks are not labelled, and they
|
||||
# are drawn over the major ticks and labels--ugly!
|
||||
ax.xaxis.set_ticks([0], minor=True)
|
||||
ax.yaxis.set_ticks([0], minor=True)
|
||||
ax = fig.add_subplot(222)
|
||||
ax.xaxis.set_tick_params(tickdir='in', length=32, width=3)
|
||||
ax.yaxis.set_tick_params(tickdir='in', length=32, width=3)
|
||||
ax = fig.add_subplot(223)
|
||||
ax.xaxis.set_tick_params(tickdir='inout', length=32, width=3)
|
||||
ax.yaxis.set_tick_params(tickdir='inout', length=32, width=3)
|
||||
ax = fig.add_subplot(224)
|
||||
ax.xaxis.set_tick_params(tickdir='out', length=32, width=3)
|
||||
ax.yaxis.set_tick_params(tickdir='out', length=32, width=3)
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
def add_offsetboxes(ax, size=10, margin=.1, color='black'):
|
||||
"""
|
||||
Surround ax with OffsetBoxes
|
||||
"""
|
||||
m, mp = margin, 1+margin
|
||||
anchor_points = [(-m, -m), (-m, .5), (-m, mp),
|
||||
(mp, .5), (.5, mp), (mp, mp),
|
||||
(.5, -m), (mp, -m), (.5, -m)]
|
||||
for point in anchor_points:
|
||||
da = DrawingArea(size, size)
|
||||
background = Rectangle((0, 0), width=size,
|
||||
height=size,
|
||||
facecolor=color,
|
||||
edgecolor='None',
|
||||
linewidth=0,
|
||||
antialiased=False)
|
||||
da.add_artist(background)
|
||||
|
||||
anchored_box = AnchoredOffsetbox(
|
||||
loc='center',
|
||||
child=da,
|
||||
pad=0.,
|
||||
frameon=False,
|
||||
bbox_to_anchor=point,
|
||||
bbox_transform=ax.transAxes,
|
||||
borderpad=0.)
|
||||
ax.add_artist(anchored_box)
|
||||
return anchored_box
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['tight_layout_offsetboxes1',
|
||||
'tight_layout_offsetboxes2'])
|
||||
def test_tight_layout_offsetboxes():
|
||||
# 1.
|
||||
# - Create 4 subplots
|
||||
# - Plot a diagonal line on them
|
||||
# - Surround each plot with 7 boxes
|
||||
# - Use tight_layout
|
||||
# - See that the squares are included in the tight_layout
|
||||
# and that the squares in the middle do not overlap
|
||||
#
|
||||
# 2.
|
||||
# - Make the squares around the right side axes invisible
|
||||
# - See that the invisible squares do not affect the
|
||||
# tight_layout
|
||||
rows = cols = 2
|
||||
colors = ['red', 'blue', 'green', 'yellow']
|
||||
x = y = [0, 1]
|
||||
|
||||
def _subplots():
|
||||
_, axs = plt.subplots(rows, cols)
|
||||
axs = axs.flat
|
||||
for ax, color in zip(axs, colors):
|
||||
ax.plot(x, y, color=color)
|
||||
add_offsetboxes(ax, 20, color=color)
|
||||
return axs
|
||||
|
||||
# 1.
|
||||
axs = _subplots()
|
||||
plt.tight_layout()
|
||||
|
||||
# 2.
|
||||
axs = _subplots()
|
||||
for ax in (axs[cols-1::rows]):
|
||||
for child in ax.get_children():
|
||||
if isinstance(child, AnchoredOffsetbox):
|
||||
child.set_visible(False)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
def test_empty_layout():
|
||||
"""Tests that tight layout doesn't cause an error when there are
|
||||
no axes.
|
||||
"""
|
||||
|
||||
fig = plt.gcf()
|
||||
fig.tight_layout()
|
||||
|
||||
|
||||
def test_verybig_decorators_horizontal():
|
||||
"Test that warning emitted when xlabel too big"
|
||||
fig, ax = plt.subplots(figsize=(3, 2))
|
||||
ax.set_xlabel('a' * 100)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
fig.tight_layout()
|
||||
assert len(w) == 1
|
||||
|
||||
|
||||
def test_verybig_decorators_vertical():
|
||||
"Test that warning emitted when xlabel too big"
|
||||
fig, ax = plt.subplots(figsize=(3, 2))
|
||||
ax.set_ylabel('a' * 100)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
fig.tight_layout()
|
||||
assert len(w) == 1
|
||||
|
||||
|
||||
def test_big_decorators_horizontal():
|
||||
"Test that warning emitted when xlabel too big"
|
||||
fig, axs = plt.subplots(1, 2, figsize=(3, 2))
|
||||
axs[0].set_xlabel('a' * 30)
|
||||
axs[1].set_xlabel('b' * 30)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
fig.tight_layout()
|
||||
assert len(w) == 1
|
||||
|
||||
|
||||
def test_big_decorators_vertical():
|
||||
"Test that warning emitted when xlabel too big"
|
||||
fig, axs = plt.subplots(2, 1, figsize=(3, 2))
|
||||
axs[0].set_ylabel('a' * 20)
|
||||
axs[1].set_ylabel('b' * 20)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
fig.tight_layout()
|
||||
assert len(w) == 1
|
||||
|
||||
|
||||
def test_badsubplotgrid():
|
||||
# test that we get warning for mismatched subplot grids rather
|
||||
# than an error
|
||||
ax1 = plt.subplot2grid((4, 5), (0, 0))
|
||||
# this is the bad entry:
|
||||
ax5 = plt.subplot2grid((5, 5), (0, 3), colspan=3, rowspan=5)
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
plt.tight_layout()
|
||||
assert len(w) == 1
|
||||
|
||||
|
||||
def test_collapsed():
|
||||
# test that if a call to tight_layout will collapes the axes that
|
||||
# it does not get applied:
|
||||
fig, ax = plt.subplots(tight_layout=True)
|
||||
ax.set_xlim([0, 1])
|
||||
ax.set_ylim([0, 1])
|
||||
|
||||
ax.annotate('BIG LONG STRING', xy=(1.25, 2), xytext=(10.5, 1.75),)
|
||||
p1 = ax.get_position()
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
plt.tight_layout()
|
||||
p2 = ax.get_position()
|
||||
assert p1.width == p2.width
|
||||
assert len(w) == 1
|
||||
@@ -0,0 +1,631 @@
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import (assert_allclose, assert_almost_equal,
|
||||
assert_array_equal, assert_array_almost_equal)
|
||||
import pytest
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as mpatches
|
||||
import matplotlib.transforms as mtransforms
|
||||
from matplotlib.path import Path
|
||||
from matplotlib.scale import LogScale
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
|
||||
def test_non_affine_caching():
|
||||
class AssertingNonAffineTransform(mtransforms.Transform):
|
||||
"""
|
||||
This transform raises an assertion error when called when it
|
||||
shouldn't be and self.raise_on_transform is True.
|
||||
|
||||
"""
|
||||
input_dims = output_dims = 2
|
||||
is_affine = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
mtransforms.Transform.__init__(self, *args, **kwargs)
|
||||
self.raise_on_transform = False
|
||||
self.underlying_transform = mtransforms.Affine2D().scale(10, 10)
|
||||
|
||||
def transform_path_non_affine(self, path):
|
||||
assert not self.raise_on_transform, \
|
||||
'Invalidated affine part of transform unnecessarily.'
|
||||
return self.underlying_transform.transform_path(path)
|
||||
transform_path = transform_path_non_affine
|
||||
|
||||
def transform_non_affine(self, path):
|
||||
assert not self.raise_on_transform, \
|
||||
'Invalidated affine part of transform unnecessarily.'
|
||||
return self.underlying_transform.transform(path)
|
||||
transform = transform_non_affine
|
||||
|
||||
my_trans = AssertingNonAffineTransform()
|
||||
ax = plt.axes()
|
||||
plt.plot(np.arange(10), transform=my_trans + ax.transData)
|
||||
plt.draw()
|
||||
# enable the transform to raise an exception if it's non-affine transform
|
||||
# method is triggered again.
|
||||
my_trans.raise_on_transform = True
|
||||
ax.transAxes.invalidate()
|
||||
plt.draw()
|
||||
|
||||
|
||||
def test_external_transform_api():
|
||||
class ScaledBy(object):
|
||||
def __init__(self, scale_factor):
|
||||
self._scale_factor = scale_factor
|
||||
|
||||
def _as_mpl_transform(self, axes):
|
||||
return (mtransforms.Affine2D().scale(self._scale_factor)
|
||||
+ axes.transData)
|
||||
|
||||
ax = plt.axes()
|
||||
line, = plt.plot(np.arange(10), transform=ScaledBy(10))
|
||||
ax.set_xlim(0, 100)
|
||||
ax.set_ylim(0, 100)
|
||||
# assert that the top transform of the line is the scale transform.
|
||||
assert_allclose(line.get_transform()._a.get_matrix(),
|
||||
mtransforms.Affine2D().scale(10).get_matrix())
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['pre_transform_data'],
|
||||
tol=0.08, remove_text=True, style='mpl20')
|
||||
def test_pre_transform_plotting():
|
||||
# a catch-all for as many as possible plot layouts which handle
|
||||
# pre-transforming the data NOTE: The axis range is important in this
|
||||
# plot. It should be x10 what the data suggests it should be
|
||||
ax = plt.axes()
|
||||
times10 = mtransforms.Affine2D().scale(10)
|
||||
|
||||
ax.contourf(np.arange(48).reshape(6, 8), transform=times10 + ax.transData)
|
||||
|
||||
ax.pcolormesh(np.linspace(0, 4, 7),
|
||||
np.linspace(5.5, 8, 9),
|
||||
np.arange(48).reshape(8, 6),
|
||||
transform=times10 + ax.transData)
|
||||
|
||||
ax.scatter(np.linspace(0, 10), np.linspace(10, 0),
|
||||
transform=times10 + ax.transData)
|
||||
|
||||
x = np.linspace(8, 10, 20)
|
||||
y = np.linspace(1, 5, 20)
|
||||
u = 2*np.sin(x) + np.cos(y[:, np.newaxis])
|
||||
v = np.sin(x) - np.cos(y[:, np.newaxis])
|
||||
|
||||
df = 25. / 30. # Compatibility factor for old test image
|
||||
ax.streamplot(x, y, u, v, transform=times10 + ax.transData,
|
||||
density=(df, df), linewidth=u**2 + v**2)
|
||||
|
||||
# reduce the vector data down a bit for barb and quiver plotting
|
||||
x, y = x[::3], y[::3]
|
||||
u, v = u[::3, ::3], v[::3, ::3]
|
||||
|
||||
ax.quiver(x, y + 5, u, v, transform=times10 + ax.transData)
|
||||
|
||||
ax.barbs(x - 3, y + 5, u**2, v**2, transform=times10 + ax.transData)
|
||||
|
||||
|
||||
def test_contour_pre_transform_limits():
|
||||
ax = plt.axes()
|
||||
xs, ys = np.meshgrid(np.linspace(15, 20, 15), np.linspace(12.4, 12.5, 20))
|
||||
ax.contourf(xs, ys, np.log(xs * ys),
|
||||
transform=mtransforms.Affine2D().scale(0.1) + ax.transData)
|
||||
|
||||
expected = np.array([[1.5, 1.24],
|
||||
[2., 1.25]])
|
||||
assert_almost_equal(expected, ax.dataLim.get_points())
|
||||
|
||||
|
||||
def test_pcolor_pre_transform_limits():
|
||||
# Based on test_contour_pre_transform_limits()
|
||||
ax = plt.axes()
|
||||
xs, ys = np.meshgrid(np.linspace(15, 20, 15), np.linspace(12.4, 12.5, 20))
|
||||
ax.pcolor(xs, ys, np.log(xs * ys),
|
||||
transform=mtransforms.Affine2D().scale(0.1) + ax.transData)
|
||||
|
||||
expected = np.array([[1.5, 1.24],
|
||||
[2., 1.25]])
|
||||
assert_almost_equal(expected, ax.dataLim.get_points())
|
||||
|
||||
|
||||
def test_pcolormesh_pre_transform_limits():
|
||||
# Based on test_contour_pre_transform_limits()
|
||||
ax = plt.axes()
|
||||
xs, ys = np.meshgrid(np.linspace(15, 20, 15), np.linspace(12.4, 12.5, 20))
|
||||
ax.pcolormesh(xs, ys, np.log(xs * ys),
|
||||
transform=mtransforms.Affine2D().scale(0.1) + ax.transData)
|
||||
|
||||
expected = np.array([[1.5, 1.24],
|
||||
[2., 1.25]])
|
||||
assert_almost_equal(expected, ax.dataLim.get_points())
|
||||
|
||||
|
||||
def test_Affine2D_from_values():
|
||||
points = np.array([[0, 0],
|
||||
[10, 20],
|
||||
[-1, 0],
|
||||
])
|
||||
|
||||
t = mtransforms.Affine2D.from_values(1, 0, 0, 0, 0, 0)
|
||||
actual = t.transform(points)
|
||||
expected = np.array([[0, 0], [10, 0], [-1, 0]])
|
||||
assert_almost_equal(actual, expected)
|
||||
|
||||
t = mtransforms.Affine2D.from_values(0, 2, 0, 0, 0, 0)
|
||||
actual = t.transform(points)
|
||||
expected = np.array([[0, 0], [0, 20], [0, -2]])
|
||||
assert_almost_equal(actual, expected)
|
||||
|
||||
t = mtransforms.Affine2D.from_values(0, 0, 3, 0, 0, 0)
|
||||
actual = t.transform(points)
|
||||
expected = np.array([[0, 0], [60, 0], [0, 0]])
|
||||
assert_almost_equal(actual, expected)
|
||||
|
||||
t = mtransforms.Affine2D.from_values(0, 0, 0, 4, 0, 0)
|
||||
actual = t.transform(points)
|
||||
expected = np.array([[0, 0], [0, 80], [0, 0]])
|
||||
assert_almost_equal(actual, expected)
|
||||
|
||||
t = mtransforms.Affine2D.from_values(0, 0, 0, 0, 5, 0)
|
||||
actual = t.transform(points)
|
||||
expected = np.array([[5, 0], [5, 0], [5, 0]])
|
||||
assert_almost_equal(actual, expected)
|
||||
|
||||
t = mtransforms.Affine2D.from_values(0, 0, 0, 0, 0, 6)
|
||||
actual = t.transform(points)
|
||||
expected = np.array([[0, 6], [0, 6], [0, 6]])
|
||||
assert_almost_equal(actual, expected)
|
||||
|
||||
|
||||
def test_clipping_of_log():
|
||||
# issue 804
|
||||
M, L, C = Path.MOVETO, Path.LINETO, Path.CLOSEPOLY
|
||||
points = [(0.2, -99), (0.4, -99), (0.4, 20), (0.2, 20), (0.2, -99)]
|
||||
codes = [M, L, L, L, C]
|
||||
path = Path(points, codes)
|
||||
|
||||
# something like this happens in plotting logarithmic histograms
|
||||
trans = mtransforms.BlendedGenericTransform(mtransforms.Affine2D(),
|
||||
LogScale.Log10Transform('clip'))
|
||||
tpath = trans.transform_path_non_affine(path)
|
||||
result = tpath.iter_segments(trans.get_affine(),
|
||||
clip=(0, 0, 100, 100),
|
||||
simplify=False)
|
||||
|
||||
tpoints, tcodes = zip(*result)
|
||||
assert_allclose(tcodes, [M, L, L, L, C])
|
||||
|
||||
|
||||
class NonAffineForTest(mtransforms.Transform):
|
||||
"""
|
||||
A class which looks like a non affine transform, but does whatever
|
||||
the given transform does (even if it is affine). This is very useful
|
||||
for testing NonAffine behaviour with a simple Affine transform.
|
||||
|
||||
"""
|
||||
is_affine = False
|
||||
output_dims = 2
|
||||
input_dims = 2
|
||||
|
||||
def __init__(self, real_trans, *args, **kwargs):
|
||||
self.real_trans = real_trans
|
||||
mtransforms.Transform.__init__(self, *args, **kwargs)
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
return self.real_trans.transform(values)
|
||||
|
||||
def transform_path_non_affine(self, path):
|
||||
return self.real_trans.transform_path(path)
|
||||
|
||||
|
||||
class BasicTransformTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
||||
self.ta1 = mtransforms.Affine2D(shorthand_name='ta1').rotate(np.pi / 2)
|
||||
self.ta2 = mtransforms.Affine2D(shorthand_name='ta2').translate(10, 0)
|
||||
self.ta3 = mtransforms.Affine2D(shorthand_name='ta3').scale(1, 2)
|
||||
|
||||
self.tn1 = NonAffineForTest(mtransforms.Affine2D().translate(1, 2),
|
||||
shorthand_name='tn1')
|
||||
self.tn2 = NonAffineForTest(mtransforms.Affine2D().translate(1, 2),
|
||||
shorthand_name='tn2')
|
||||
self.tn3 = NonAffineForTest(mtransforms.Affine2D().translate(1, 2),
|
||||
shorthand_name='tn3')
|
||||
|
||||
# creates a transform stack which looks like ((A, (N, A)), A)
|
||||
self.stack1 = (self.ta1 + (self.tn1 + self.ta2)) + self.ta3
|
||||
# creates a transform stack which looks like (((A, N), A), A)
|
||||
self.stack2 = self.ta1 + self.tn1 + self.ta2 + self.ta3
|
||||
# creates a transform stack which is a subset of stack2
|
||||
self.stack2_subset = self.tn1 + self.ta2 + self.ta3
|
||||
|
||||
# when in debug, the transform stacks can produce dot images:
|
||||
# self.stack1.write_graphviz(file('stack1.dot', 'w'))
|
||||
# self.stack2.write_graphviz(file('stack2.dot', 'w'))
|
||||
# self.stack2_subset.write_graphviz(file('stack2_subset.dot', 'w'))
|
||||
|
||||
def test_transform_depth(self):
|
||||
assert self.stack1.depth == 4
|
||||
assert self.stack2.depth == 4
|
||||
assert self.stack2_subset.depth == 3
|
||||
|
||||
def test_left_to_right_iteration(self):
|
||||
stack3 = (self.ta1 + (self.tn1 + (self.ta2 + self.tn2))) + self.ta3
|
||||
# stack3.write_graphviz(file('stack3.dot', 'w'))
|
||||
|
||||
target_transforms = [stack3,
|
||||
(self.tn1 + (self.ta2 + self.tn2)) + self.ta3,
|
||||
(self.ta2 + self.tn2) + self.ta3,
|
||||
self.tn2 + self.ta3,
|
||||
self.ta3,
|
||||
]
|
||||
r = [rh for _, rh in stack3._iter_break_from_left_to_right()]
|
||||
assert len(r) == len(target_transforms)
|
||||
|
||||
for target_stack, stack in zip(target_transforms, r):
|
||||
assert target_stack == stack
|
||||
|
||||
def test_transform_shortcuts(self):
|
||||
assert self.stack1 - self.stack2_subset == self.ta1
|
||||
assert self.stack2 - self.stack2_subset == self.ta1
|
||||
|
||||
assert self.stack2_subset - self.stack2 == self.ta1.inverted()
|
||||
assert (self.stack2_subset - self.stack2).depth == 1
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
self.stack1 - self.stack2
|
||||
|
||||
aff1 = self.ta1 + (self.ta2 + self.ta3)
|
||||
aff2 = self.ta2 + self.ta3
|
||||
|
||||
assert aff1 - aff2 == self.ta1
|
||||
assert aff1 - self.ta2 == aff1 + self.ta2.inverted()
|
||||
|
||||
assert self.stack1 - self.ta3 == self.ta1 + (self.tn1 + self.ta2)
|
||||
assert self.stack2 - self.ta3 == self.ta1 + self.tn1 + self.ta2
|
||||
|
||||
assert ((self.ta2 + self.ta3) - self.ta3 + self.ta3 ==
|
||||
self.ta2 + self.ta3)
|
||||
|
||||
def test_contains_branch(self):
|
||||
r1 = (self.ta2 + self.ta1)
|
||||
r2 = (self.ta2 + self.ta1)
|
||||
assert r1 == r2
|
||||
assert r1 != self.ta1
|
||||
assert r1.contains_branch(r2)
|
||||
assert r1.contains_branch(self.ta1)
|
||||
assert not r1.contains_branch(self.ta2)
|
||||
assert not r1.contains_branch((self.ta2 + self.ta2))
|
||||
|
||||
assert r1 == r2
|
||||
|
||||
assert self.stack1.contains_branch(self.ta3)
|
||||
assert self.stack2.contains_branch(self.ta3)
|
||||
|
||||
assert self.stack1.contains_branch(self.stack2_subset)
|
||||
assert self.stack2.contains_branch(self.stack2_subset)
|
||||
|
||||
assert not self.stack2_subset.contains_branch(self.stack1)
|
||||
assert not self.stack2_subset.contains_branch(self.stack2)
|
||||
|
||||
assert self.stack1.contains_branch((self.ta2 + self.ta3))
|
||||
assert self.stack2.contains_branch((self.ta2 + self.ta3))
|
||||
|
||||
assert not self.stack1.contains_branch((self.tn1 + self.ta2))
|
||||
|
||||
def test_affine_simplification(self):
|
||||
# tests that a transform stack only calls as much is absolutely
|
||||
# necessary "non-affine" allowing the best possible optimization with
|
||||
# complex transformation stacks.
|
||||
points = np.array([[0, 0], [10, 20], [np.nan, 1], [-1, 0]],
|
||||
dtype=np.float64)
|
||||
na_pts = self.stack1.transform_non_affine(points)
|
||||
all_pts = self.stack1.transform(points)
|
||||
|
||||
na_expected = np.array([[1., 2.], [-19., 12.],
|
||||
[np.nan, np.nan], [1., 1.]], dtype=np.float64)
|
||||
all_expected = np.array([[11., 4.], [-9., 24.],
|
||||
[np.nan, np.nan], [11., 2.]],
|
||||
dtype=np.float64)
|
||||
|
||||
# check we have the expected results from doing the affine part only
|
||||
assert_array_almost_equal(na_pts, na_expected)
|
||||
# check we have the expected results from a full transformation
|
||||
assert_array_almost_equal(all_pts, all_expected)
|
||||
# check we have the expected results from doing the transformation in
|
||||
# two steps
|
||||
assert_array_almost_equal(self.stack1.transform_affine(na_pts),
|
||||
all_expected)
|
||||
# check that getting the affine transformation first, then fully
|
||||
# transforming using that yields the same result as before.
|
||||
assert_array_almost_equal(self.stack1.get_affine().transform(na_pts),
|
||||
all_expected)
|
||||
|
||||
# check that the affine part of stack1 & stack2 are equivalent
|
||||
# (i.e. the optimization is working)
|
||||
expected_result = (self.ta2 + self.ta3).get_matrix()
|
||||
result = self.stack1.get_affine().get_matrix()
|
||||
assert_array_equal(expected_result, result)
|
||||
|
||||
result = self.stack2.get_affine().get_matrix()
|
||||
assert_array_equal(expected_result, result)
|
||||
|
||||
|
||||
class TestTransformPlotInterface(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
plt.close()
|
||||
|
||||
def test_line_extent_axes_coords(self):
|
||||
# a simple line in axes coordinates
|
||||
ax = plt.axes()
|
||||
ax.plot([0.1, 1.2, 0.8], [0.9, 0.5, 0.8], transform=ax.transAxes)
|
||||
assert_array_equal(ax.dataLim.get_points(),
|
||||
np.array([[np.inf, np.inf],
|
||||
[-np.inf, -np.inf]]))
|
||||
|
||||
def test_line_extent_data_coords(self):
|
||||
# a simple line in data coordinates
|
||||
ax = plt.axes()
|
||||
ax.plot([0.1, 1.2, 0.8], [0.9, 0.5, 0.8], transform=ax.transData)
|
||||
assert_array_equal(ax.dataLim.get_points(),
|
||||
np.array([[0.1, 0.5], [1.2, 0.9]]))
|
||||
|
||||
def test_line_extent_compound_coords1(self):
|
||||
# a simple line in data coordinates in the y component, and in axes
|
||||
# coordinates in the x
|
||||
ax = plt.axes()
|
||||
trans = mtransforms.blended_transform_factory(ax.transAxes,
|
||||
ax.transData)
|
||||
ax.plot([0.1, 1.2, 0.8], [35, -5, 18], transform=trans)
|
||||
assert_array_equal(ax.dataLim.get_points(),
|
||||
np.array([[np.inf, -5.],
|
||||
[-np.inf, 35.]]))
|
||||
plt.close()
|
||||
|
||||
def test_line_extent_predata_transform_coords(self):
|
||||
# a simple line in (offset + data) coordinates
|
||||
ax = plt.axes()
|
||||
trans = mtransforms.Affine2D().scale(10) + ax.transData
|
||||
ax.plot([0.1, 1.2, 0.8], [35, -5, 18], transform=trans)
|
||||
assert_array_equal(ax.dataLim.get_points(),
|
||||
np.array([[1., -50.], [12., 350.]]))
|
||||
plt.close()
|
||||
|
||||
def test_line_extent_compound_coords2(self):
|
||||
# a simple line in (offset + data) coordinates in the y component, and
|
||||
# in axes coordinates in the x
|
||||
ax = plt.axes()
|
||||
trans = mtransforms.blended_transform_factory(ax.transAxes,
|
||||
mtransforms.Affine2D().scale(10) + ax.transData)
|
||||
ax.plot([0.1, 1.2, 0.8], [35, -5, 18], transform=trans)
|
||||
assert_array_equal(ax.dataLim.get_points(),
|
||||
np.array([[np.inf, -50.], [-np.inf, 350.]]))
|
||||
plt.close()
|
||||
|
||||
def test_line_extents_affine(self):
|
||||
ax = plt.axes()
|
||||
offset = mtransforms.Affine2D().translate(10, 10)
|
||||
plt.plot(np.arange(10), transform=offset + ax.transData)
|
||||
expected_data_lim = np.array([[0., 0.], [9., 9.]]) + 10
|
||||
assert_array_almost_equal(ax.dataLim.get_points(), expected_data_lim)
|
||||
|
||||
def test_line_extents_non_affine(self):
|
||||
ax = plt.axes()
|
||||
offset = mtransforms.Affine2D().translate(10, 10)
|
||||
na_offset = NonAffineForTest(mtransforms.Affine2D().translate(10, 10))
|
||||
plt.plot(np.arange(10), transform=offset + na_offset + ax.transData)
|
||||
expected_data_lim = np.array([[0., 0.], [9., 9.]]) + 20
|
||||
assert_array_almost_equal(ax.dataLim.get_points(), expected_data_lim)
|
||||
|
||||
def test_pathc_extents_non_affine(self):
|
||||
ax = plt.axes()
|
||||
offset = mtransforms.Affine2D().translate(10, 10)
|
||||
na_offset = NonAffineForTest(mtransforms.Affine2D().translate(10, 10))
|
||||
pth = Path(np.array([[0, 0], [0, 10], [10, 10], [10, 0]]))
|
||||
patch = mpatches.PathPatch(pth,
|
||||
transform=offset + na_offset + ax.transData)
|
||||
ax.add_patch(patch)
|
||||
expected_data_lim = np.array([[0., 0.], [10., 10.]]) + 20
|
||||
assert_array_almost_equal(ax.dataLim.get_points(), expected_data_lim)
|
||||
|
||||
def test_pathc_extents_affine(self):
|
||||
ax = plt.axes()
|
||||
offset = mtransforms.Affine2D().translate(10, 10)
|
||||
pth = Path(np.array([[0, 0], [0, 10], [10, 10], [10, 0]]))
|
||||
patch = mpatches.PathPatch(pth, transform=offset + ax.transData)
|
||||
ax.add_patch(patch)
|
||||
expected_data_lim = np.array([[0., 0.], [10., 10.]]) + 10
|
||||
assert_array_almost_equal(ax.dataLim.get_points(), expected_data_lim)
|
||||
|
||||
def test_line_extents_for_non_affine_transData(self):
|
||||
ax = plt.axes(projection='polar')
|
||||
# add 10 to the radius of the data
|
||||
offset = mtransforms.Affine2D().translate(0, 10)
|
||||
|
||||
plt.plot(np.arange(10), transform=offset + ax.transData)
|
||||
# the data lim of a polar plot is stored in coordinates
|
||||
# before a transData transformation, hence the data limits
|
||||
# are not what is being shown on the actual plot.
|
||||
expected_data_lim = np.array([[0., 0.], [9., 9.]]) + [0, 10]
|
||||
assert_array_almost_equal(ax.dataLim.get_points(), expected_data_lim)
|
||||
|
||||
|
||||
def assert_bbox_eq(bbox1, bbox2):
|
||||
assert_array_equal(bbox1.bounds, bbox2.bounds)
|
||||
|
||||
|
||||
def test_bbox_intersection():
|
||||
bbox_from_ext = mtransforms.Bbox.from_extents
|
||||
inter = mtransforms.Bbox.intersection
|
||||
|
||||
r1 = bbox_from_ext(0, 0, 1, 1)
|
||||
r2 = bbox_from_ext(0.5, 0.5, 1.5, 1.5)
|
||||
r3 = bbox_from_ext(0.5, 0, 0.75, 0.75)
|
||||
r4 = bbox_from_ext(0.5, 1.5, 1, 2.5)
|
||||
r5 = bbox_from_ext(1, 1, 2, 2)
|
||||
|
||||
# self intersection -> no change
|
||||
assert_bbox_eq(inter(r1, r1), r1)
|
||||
# simple intersection
|
||||
assert_bbox_eq(inter(r1, r2), bbox_from_ext(0.5, 0.5, 1, 1))
|
||||
# r3 contains r2
|
||||
assert_bbox_eq(inter(r1, r3), r3)
|
||||
# no intersection
|
||||
assert inter(r1, r4) is None
|
||||
# single point
|
||||
assert_bbox_eq(inter(r1, r5), bbox_from_ext(1, 1, 1, 1))
|
||||
|
||||
|
||||
def test_bbox_as_strings():
|
||||
b = mtransforms.Bbox([[.5, 0], [.75, .75]])
|
||||
assert_bbox_eq(b, eval(repr(b), {'Bbox': mtransforms.Bbox}))
|
||||
asdict = eval(str(b), {'Bbox': dict})
|
||||
for k, v in asdict.items():
|
||||
assert getattr(b, k) == v
|
||||
fmt = '.1f'
|
||||
asdict = eval(format(b, fmt), {'Bbox': dict})
|
||||
for k, v in asdict.items():
|
||||
assert eval(format(getattr(b, k), fmt)) == v
|
||||
|
||||
|
||||
def test_transform_single_point():
|
||||
t = mtransforms.Affine2D()
|
||||
r = t.transform_affine((1, 1))
|
||||
assert r.shape == (2,)
|
||||
|
||||
|
||||
def test_log_transform():
|
||||
# Tests that the last line runs without exception (previously the
|
||||
# transform would fail if one of the axes was logarithmic).
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_yscale('log')
|
||||
ax.transData.transform((1, 1))
|
||||
|
||||
|
||||
def test_nan_overlap():
|
||||
a = mtransforms.Bbox([[0, 0], [1, 1]])
|
||||
b = mtransforms.Bbox([[0, 0], [1, np.nan]])
|
||||
assert not a.overlaps(b)
|
||||
|
||||
|
||||
def test_transform_angles():
|
||||
t = mtransforms.Affine2D() # Identity transform
|
||||
angles = np.array([20, 45, 60])
|
||||
points = np.array([[0, 0], [1, 1], [2, 2]])
|
||||
|
||||
# Identity transform does not change angles
|
||||
new_angles = t.transform_angles(angles, points)
|
||||
assert_array_almost_equal(angles, new_angles)
|
||||
|
||||
# points missing a 2nd dimension
|
||||
with pytest.raises(ValueError):
|
||||
t.transform_angles(angles, points[0:2, 0:1])
|
||||
|
||||
# Number of angles != Number of points
|
||||
with pytest.raises(ValueError):
|
||||
t.transform_angles(angles, points[0:2, :])
|
||||
|
||||
|
||||
def test_nonsingular():
|
||||
# test for zero-expansion type cases; other cases may be added later
|
||||
zero_expansion = np.array([-0.001, 0.001])
|
||||
cases = [(0, np.nan), (0, 0), (0, 7.9e-317)]
|
||||
for args in cases:
|
||||
out = np.array(mtransforms.nonsingular(*args))
|
||||
assert_array_equal(out, zero_expansion)
|
||||
|
||||
|
||||
def test_invalid_arguments():
|
||||
t = mtransforms.Affine2D()
|
||||
# There are two different exceptions, since the wrong number of
|
||||
# dimensions is caught when constructing an array_view, and that
|
||||
# raises a ValueError, and a wrong shape with a possible number
|
||||
# of dimensions is caught by our CALL_CPP macro, which always
|
||||
# raises the less precise RuntimeError.
|
||||
with pytest.raises(ValueError):
|
||||
t.transform(1)
|
||||
with pytest.raises(ValueError):
|
||||
t.transform([[[1]]])
|
||||
with pytest.raises(RuntimeError):
|
||||
t.transform([])
|
||||
with pytest.raises(RuntimeError):
|
||||
t.transform([1])
|
||||
with pytest.raises(RuntimeError):
|
||||
t.transform([[1]])
|
||||
with pytest.raises(RuntimeError):
|
||||
t.transform([[1, 2, 3]])
|
||||
|
||||
|
||||
def test_transformed_path():
|
||||
points = [(0, 0), (1, 0), (1, 1), (0, 1)]
|
||||
codes = [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]
|
||||
path = Path(points, codes)
|
||||
|
||||
trans = mtransforms.Affine2D()
|
||||
trans_path = mtransforms.TransformedPath(path, trans)
|
||||
assert_allclose(trans_path.get_fully_transformed_path().vertices, points)
|
||||
|
||||
# Changing the transform should change the result.
|
||||
r2 = 1 / np.sqrt(2)
|
||||
trans.rotate(np.pi / 4)
|
||||
assert_allclose(trans_path.get_fully_transformed_path().vertices,
|
||||
[(0, 0), (r2, r2), (0, 2 * r2), (-r2, r2)],
|
||||
atol=1e-15)
|
||||
|
||||
# Changing the path does not change the result (it's cached).
|
||||
path.points = [(0, 0)] * 4
|
||||
assert_allclose(trans_path.get_fully_transformed_path().vertices,
|
||||
[(0, 0), (r2, r2), (0, 2 * r2), (-r2, r2)],
|
||||
atol=1e-15)
|
||||
|
||||
|
||||
def test_transformed_patch_path():
|
||||
trans = mtransforms.Affine2D()
|
||||
patch = mpatches.Wedge((0, 0), 1, 45, 135, transform=trans)
|
||||
|
||||
tpatch = mtransforms.TransformedPatchPath(patch)
|
||||
points = tpatch.get_fully_transformed_path().vertices
|
||||
|
||||
# Changing the transform should change the result.
|
||||
trans.scale(2)
|
||||
assert_allclose(tpatch.get_fully_transformed_path().vertices, points * 2)
|
||||
|
||||
# Changing the path should change the result (and cancel out the scaling
|
||||
# from the transform).
|
||||
patch.set_radius(0.5)
|
||||
assert_allclose(tpatch.get_fully_transformed_path().vertices, points)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('locked_element', ['x0', 'y0', 'x1', 'y1'])
|
||||
def test_lockable_bbox(locked_element):
|
||||
other_elements = ['x0', 'y0', 'x1', 'y1']
|
||||
other_elements.remove(locked_element)
|
||||
|
||||
orig = mtransforms.Bbox.unit()
|
||||
locked = mtransforms.LockableBbox(orig, **{locked_element: 2})
|
||||
|
||||
# LockableBbox should keep its locked element as specified in __init__.
|
||||
assert getattr(locked, locked_element) == 2
|
||||
assert getattr(locked, 'locked_' + locked_element) == 2
|
||||
for elem in other_elements:
|
||||
assert getattr(locked, elem) == getattr(orig, elem)
|
||||
|
||||
# Changing underlying Bbox should update everything but locked element.
|
||||
orig.set_points(orig.get_points() + 10)
|
||||
assert getattr(locked, locked_element) == 2
|
||||
assert getattr(locked, 'locked_' + locked_element) == 2
|
||||
for elem in other_elements:
|
||||
assert getattr(locked, elem) == getattr(orig, elem)
|
||||
|
||||
# Unlocking element should revert values back to the underlying Bbox.
|
||||
setattr(locked, 'locked_' + locked_element, None)
|
||||
assert getattr(locked, 'locked_' + locked_element) is None
|
||||
assert np.all(orig.get_points() == locked.get_points())
|
||||
|
||||
# Relocking an element should change its value, but not others.
|
||||
setattr(locked, 'locked_' + locked_element, 3)
|
||||
assert getattr(locked, locked_element) == 3
|
||||
assert getattr(locked, 'locked_' + locked_element) == 3
|
||||
for elem in other_elements:
|
||||
assert getattr(locked, elem) == getattr(orig, elem)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
import matplotlib
|
||||
from matplotlib.font_manager import FontProperties
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
import os.path
|
||||
|
||||
|
||||
@image_comparison(baseline_images=["truetype-conversion"],
|
||||
extensions=["pdf"])
|
||||
def test_truetype_conversion():
|
||||
fontname = os.path.join(os.path.dirname(__file__), 'mpltest.ttf')
|
||||
fontname = os.path.abspath(fontname)
|
||||
fontprop = FontProperties(fname=fontname, size=80)
|
||||
matplotlib.rcParams['pdf.fonttype'] = 3
|
||||
fig, ax = plt.subplots()
|
||||
ax.text(0, 0, "ABCDE", fontproperties=fontprop)
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
@@ -0,0 +1,50 @@
|
||||
import matplotlib.type1font as t1f
|
||||
import os.path
|
||||
import difflib
|
||||
|
||||
|
||||
def test_Type1Font():
|
||||
filename = os.path.join(os.path.dirname(__file__), 'cmr10.pfb')
|
||||
font = t1f.Type1Font(filename)
|
||||
slanted = font.transform({'slant': 1})
|
||||
condensed = font.transform({'extend': 0.5})
|
||||
with open(filename, 'rb') as fd:
|
||||
rawdata = fd.read()
|
||||
assert font.parts[0] == rawdata[0x0006:0x10c5]
|
||||
assert font.parts[1] == rawdata[0x10cb:0x897f]
|
||||
assert font.parts[2] == rawdata[0x8985:0x8ba6]
|
||||
assert font.parts[1:] == slanted.parts[1:]
|
||||
assert font.parts[1:] == condensed.parts[1:]
|
||||
|
||||
differ = difflib.Differ()
|
||||
diff = list(differ.compare(
|
||||
font.parts[0].decode('latin-1').splitlines(),
|
||||
slanted.parts[0].decode('latin-1').splitlines()))
|
||||
for line in (
|
||||
# Removes UniqueID
|
||||
'- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup',
|
||||
'+ FontDirectory/CMR10 known{/CMR10 findfont dup',
|
||||
# Changes the font name
|
||||
'- /FontName /CMR10 def',
|
||||
'+ /FontName /CMR10_Slant_1000 def',
|
||||
# Alters FontMatrix
|
||||
'- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def',
|
||||
'+ /FontMatrix [0.001 0.0 0.001 0.001 0.0 0.0]readonly def',
|
||||
# Alters ItalicAngle
|
||||
'- /ItalicAngle 0 def',
|
||||
'+ /ItalicAngle -45.0 def'):
|
||||
assert line in diff, 'diff to slanted font must contain %s' % line
|
||||
|
||||
diff = list(differ.compare(font.parts[0].decode('latin-1').splitlines(),
|
||||
condensed.parts[0].decode('latin-1').splitlines()))
|
||||
for line in (
|
||||
# Removes UniqueID
|
||||
'- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup',
|
||||
'+ FontDirectory/CMR10 known{/CMR10 findfont dup',
|
||||
# Changes the font name
|
||||
'- /FontName /CMR10 def',
|
||||
'+ /FontName /CMR10_Extend_500 def',
|
||||
# Alters FontMatrix
|
||||
'- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def',
|
||||
'+ /FontMatrix [0.0005 0.0 0.0 0.001 0.0 0.0]readonly def'):
|
||||
assert line in diff, 'diff to condensed font must contain %s' % line
|
||||
@@ -0,0 +1,154 @@
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from matplotlib.cbook import iterable
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.units as munits
|
||||
import numpy as np
|
||||
import datetime
|
||||
import platform
|
||||
import pytest
|
||||
|
||||
|
||||
# Basic class that wraps numpy array and has units
|
||||
class Quantity(object):
|
||||
def __init__(self, data, units):
|
||||
self.magnitude = data
|
||||
self.units = units
|
||||
|
||||
def to(self, new_units):
|
||||
factors = {('hours', 'seconds'): 3600, ('minutes', 'hours'): 1 / 60,
|
||||
('minutes', 'seconds'): 60, ('feet', 'miles'): 1 / 5280.,
|
||||
('feet', 'inches'): 12, ('miles', 'inches'): 12 * 5280}
|
||||
if self.units != new_units:
|
||||
mult = factors[self.units, new_units]
|
||||
return Quantity(mult * self.magnitude, new_units)
|
||||
else:
|
||||
return Quantity(self.magnitude, self.units)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.magnitude, attr)
|
||||
|
||||
def __getitem__(self, item):
|
||||
if iterable(self.magnitude):
|
||||
return Quantity(self.magnitude[item], self.units)
|
||||
else:
|
||||
return Quantity(self.magnitude, self.units)
|
||||
|
||||
def __array__(self):
|
||||
return np.asarray(self.magnitude)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def quantity_converter():
|
||||
# Create an instance of the conversion interface and
|
||||
# mock so we can check methods called
|
||||
qc = munits.ConversionInterface()
|
||||
|
||||
def convert(value, unit, axis):
|
||||
if hasattr(value, 'units'):
|
||||
return value.to(unit).magnitude
|
||||
elif iterable(value):
|
||||
try:
|
||||
return [v.to(unit).magnitude for v in value]
|
||||
except AttributeError:
|
||||
return [Quantity(v, axis.get_units()).to(unit).magnitude
|
||||
for v in value]
|
||||
else:
|
||||
return Quantity(value, axis.get_units()).to(unit).magnitude
|
||||
|
||||
def default_units(value, axis):
|
||||
if hasattr(value, 'units'):
|
||||
return value.units
|
||||
elif np.iterable(value):
|
||||
for v in value:
|
||||
if hasattr(v, 'units'):
|
||||
return v.units
|
||||
return None
|
||||
|
||||
qc.convert = MagicMock(side_effect=convert)
|
||||
qc.axisinfo = MagicMock(side_effect=lambda u, a: munits.AxisInfo(label=u))
|
||||
qc.default_units = MagicMock(side_effect=default_units)
|
||||
return qc
|
||||
|
||||
|
||||
# Tests that the conversion machinery works properly for classes that
|
||||
# work as a facade over numpy arrays (like pint)
|
||||
@image_comparison(baseline_images=['plot_pint'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
extensions=['png'], remove_text=False, style='mpl20')
|
||||
def test_numpy_facade(quantity_converter):
|
||||
# Register the class
|
||||
munits.registry[Quantity] = quantity_converter
|
||||
|
||||
# Simple test
|
||||
y = Quantity(np.linspace(0, 30), 'miles')
|
||||
x = Quantity(np.linspace(0, 5), 'hours')
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
fig.subplots_adjust(left=0.15) # Make space for label
|
||||
ax.plot(x, y, 'tab:blue')
|
||||
ax.axhline(Quantity(26400, 'feet'), color='tab:red')
|
||||
ax.axvline(Quantity(120, 'minutes'), color='tab:green')
|
||||
ax.yaxis.set_units('inches')
|
||||
ax.xaxis.set_units('seconds')
|
||||
|
||||
assert quantity_converter.convert.called
|
||||
assert quantity_converter.axisinfo.called
|
||||
assert quantity_converter.default_units.called
|
||||
|
||||
|
||||
# Tests gh-8908
|
||||
@image_comparison(baseline_images=['plot_masked_units'],
|
||||
tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
|
||||
extensions=['png'], remove_text=True, style='mpl20')
|
||||
def test_plot_masked_units():
|
||||
data = np.linspace(-5, 5)
|
||||
data_masked = np.ma.array(data, mask=(data > -2) & (data < 2))
|
||||
data_masked_units = Quantity(data_masked, 'meters')
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(data_masked_units)
|
||||
|
||||
|
||||
def test_empty_set_limits_with_units(quantity_converter):
|
||||
# Register the class
|
||||
munits.registry[Quantity] = quantity_converter
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_xlim(Quantity(-1, 'meters'), Quantity(6, 'meters'))
|
||||
ax.set_ylim(Quantity(-1, 'hours'), Quantity(16, 'hours'))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['jpl_bar_units'], extensions=['png'],
|
||||
savefig_kwarg={'dpi': 120}, style='mpl20')
|
||||
def test_jpl_bar_units():
|
||||
from datetime import datetime
|
||||
import matplotlib.testing.jpl_units as units
|
||||
units.register()
|
||||
|
||||
day = units.Duration("ET", 24.0 * 60.0 * 60.0)
|
||||
x = [0*units.km, 1*units.km, 2*units.km]
|
||||
w = [1*day, 2*day, 3*day]
|
||||
b = units.Epoch("ET", dt=datetime(2009, 4, 25))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.bar(x, w, bottom=b)
|
||||
ax.set_ylim([b-1*day, b+w[-1]+1*day])
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['jpl_barh_units'], extensions=['png'],
|
||||
savefig_kwarg={'dpi': 120}, style='mpl20')
|
||||
def test_jpl_barh_units():
|
||||
from datetime import datetime
|
||||
import matplotlib.testing.jpl_units as units
|
||||
units.register()
|
||||
|
||||
day = units.Duration("ET", 24.0 * 60.0 * 60.0)
|
||||
x = [0*units.km, 1*units.km, 2*units.km]
|
||||
w = [1*day, 2*day, 3*day]
|
||||
b = units.Epoch("ET", dt=datetime(2009, 4, 25))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
ax.barh(x, w, left=b)
|
||||
ax.set_xlim([b-1*day, b+w[-1]+1*day])
|
||||
@@ -0,0 +1,25 @@
|
||||
import pytest
|
||||
|
||||
import matplotlib
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
@pytest.mark.skipif(not matplotlib.checkdep_usetex(True),
|
||||
reason='Missing TeX or Ghostscript or dvipng')
|
||||
@image_comparison(baseline_images=['test_usetex'],
|
||||
extensions=['pdf', 'png'],
|
||||
tol=0.3)
|
||||
def test_usetex():
|
||||
matplotlib.rcParams['text.usetex'] = True
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.text(0.1, 0.2,
|
||||
# the \LaTeX macro exercises character sizing and placement,
|
||||
# \left[ ... \right\} draw some variable-height characters,
|
||||
# \sqrt and \frac draw horizontal rules, \mathrm changes the font
|
||||
r'\LaTeX\ $\left[\int\limits_e^{2e}'
|
||||
r'\sqrt\frac{\log^3 x}{x}\,\mathrm{d}x \right\}$',
|
||||
fontsize=24)
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
Binary file not shown.
@@ -0,0 +1,455 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import matplotlib.widgets as widgets
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.testing.decorators import image_comparison
|
||||
|
||||
from numpy.testing import assert_allclose
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def get_ax():
|
||||
fig, ax = plt.subplots(1, 1)
|
||||
ax.plot([0, 200], [0, 200])
|
||||
ax.set_aspect(1.0)
|
||||
ax.figure.canvas.draw()
|
||||
return ax
|
||||
|
||||
|
||||
def do_event(tool, etype, button=1, xdata=0, ydata=0, key=None, step=1):
|
||||
"""
|
||||
*name*
|
||||
the event name
|
||||
|
||||
*canvas*
|
||||
the FigureCanvas instance generating the event
|
||||
|
||||
*guiEvent*
|
||||
the GUI event that triggered the matplotlib event
|
||||
|
||||
*x*
|
||||
x position - pixels from left of canvas
|
||||
|
||||
*y*
|
||||
y position - pixels from bottom of canvas
|
||||
|
||||
*inaxes*
|
||||
the :class:`~matplotlib.axes.Axes` instance if mouse is over axes
|
||||
|
||||
*xdata*
|
||||
x coord of mouse in data coords
|
||||
|
||||
*ydata*
|
||||
y coord of mouse in data coords
|
||||
|
||||
*button*
|
||||
button pressed None, 1, 2, 3, 'up', 'down' (up and down are used
|
||||
for scroll events)
|
||||
|
||||
*key*
|
||||
the key depressed when the mouse event triggered (see
|
||||
:class:`KeyEvent`)
|
||||
|
||||
*step*
|
||||
number of scroll steps (positive for 'up', negative for 'down')
|
||||
"""
|
||||
event = Mock()
|
||||
event.button = button
|
||||
ax = tool.ax
|
||||
event.x, event.y = ax.transData.transform([(xdata, ydata),
|
||||
(xdata, ydata)])[00]
|
||||
event.xdata, event.ydata = xdata, ydata
|
||||
event.inaxes = ax
|
||||
event.canvas = ax.figure.canvas
|
||||
event.key = key
|
||||
event.step = step
|
||||
event.guiEvent = None
|
||||
event.name = 'Custom'
|
||||
|
||||
func = getattr(tool, etype)
|
||||
func(event)
|
||||
|
||||
|
||||
def check_rectangle(**kwargs):
|
||||
ax = get_ax()
|
||||
|
||||
def onselect(epress, erelease):
|
||||
ax._got_onselect = True
|
||||
assert epress.xdata == 100
|
||||
assert epress.ydata == 100
|
||||
assert erelease.xdata == 199
|
||||
assert erelease.ydata == 199
|
||||
|
||||
tool = widgets.RectangleSelector(ax, onselect, **kwargs)
|
||||
do_event(tool, 'press', xdata=100, ydata=100, button=1)
|
||||
do_event(tool, 'onmove', xdata=199, ydata=199, button=1)
|
||||
|
||||
# purposely drag outside of axis for release
|
||||
do_event(tool, 'release', xdata=250, ydata=250, button=1)
|
||||
|
||||
if kwargs.get('drawtype', None) not in ['line', 'none']:
|
||||
assert_allclose(tool.geometry,
|
||||
[[100., 100, 199, 199, 100],
|
||||
[100, 199, 199, 100, 100]],
|
||||
err_msg=tool.geometry)
|
||||
|
||||
assert ax._got_onselect
|
||||
|
||||
|
||||
def test_rectangle_selector():
|
||||
check_rectangle()
|
||||
check_rectangle(drawtype='line', useblit=False)
|
||||
check_rectangle(useblit=True, button=1)
|
||||
check_rectangle(drawtype='none', minspanx=10, minspany=10)
|
||||
check_rectangle(minspanx=10, minspany=10, spancoords='pixels')
|
||||
check_rectangle(rectprops=dict(fill=True))
|
||||
|
||||
|
||||
def test_ellipse():
|
||||
"""For ellipse, test out the key modifiers"""
|
||||
ax = get_ax()
|
||||
|
||||
def onselect(epress, erelease):
|
||||
pass
|
||||
|
||||
tool = widgets.EllipseSelector(ax, onselect=onselect,
|
||||
maxdist=10, interactive=True)
|
||||
tool.extents = (100, 150, 100, 150)
|
||||
|
||||
# drag the rectangle
|
||||
do_event(tool, 'press', xdata=10, ydata=10, button=1,
|
||||
key=' ')
|
||||
|
||||
do_event(tool, 'onmove', xdata=30, ydata=30, button=1)
|
||||
do_event(tool, 'release', xdata=30, ydata=30, button=1)
|
||||
assert tool.extents == (120, 170, 120, 170)
|
||||
|
||||
# create from center
|
||||
do_event(tool, 'on_key_press', xdata=100, ydata=100, button=1,
|
||||
key='control')
|
||||
do_event(tool, 'press', xdata=100, ydata=100, button=1)
|
||||
do_event(tool, 'onmove', xdata=125, ydata=125, button=1)
|
||||
do_event(tool, 'release', xdata=125, ydata=125, button=1)
|
||||
do_event(tool, 'on_key_release', xdata=100, ydata=100, button=1,
|
||||
key='control')
|
||||
assert tool.extents == (75, 125, 75, 125)
|
||||
|
||||
# create a square
|
||||
do_event(tool, 'on_key_press', xdata=10, ydata=10, button=1,
|
||||
key='shift')
|
||||
do_event(tool, 'press', xdata=10, ydata=10, button=1)
|
||||
do_event(tool, 'onmove', xdata=35, ydata=30, button=1)
|
||||
do_event(tool, 'release', xdata=35, ydata=30, button=1)
|
||||
do_event(tool, 'on_key_release', xdata=10, ydata=10, button=1,
|
||||
key='shift')
|
||||
extents = [int(e) for e in tool.extents]
|
||||
assert extents == [10, 35, 10, 34]
|
||||
|
||||
# create a square from center
|
||||
do_event(tool, 'on_key_press', xdata=100, ydata=100, button=1,
|
||||
key='ctrl+shift')
|
||||
do_event(tool, 'press', xdata=100, ydata=100, button=1)
|
||||
do_event(tool, 'onmove', xdata=125, ydata=130, button=1)
|
||||
do_event(tool, 'release', xdata=125, ydata=130, button=1)
|
||||
do_event(tool, 'on_key_release', xdata=100, ydata=100, button=1,
|
||||
key='ctrl+shift')
|
||||
extents = [int(e) for e in tool.extents]
|
||||
assert extents == [70, 129, 70, 130]
|
||||
|
||||
assert tool.geometry.shape == (2, 73)
|
||||
assert_allclose(tool.geometry[:, 0], [70., 100])
|
||||
|
||||
|
||||
def test_rectangle_handles():
|
||||
ax = get_ax()
|
||||
|
||||
def onselect(epress, erelease):
|
||||
pass
|
||||
|
||||
tool = widgets.RectangleSelector(ax, onselect=onselect,
|
||||
maxdist=10, interactive=True)
|
||||
tool.extents = (100, 150, 100, 150)
|
||||
|
||||
assert tool.corners == (
|
||||
(100, 150, 150, 100), (100, 100, 150, 150))
|
||||
assert tool.extents == (100, 150, 100, 150)
|
||||
assert tool.edge_centers == (
|
||||
(100, 125.0, 150, 125.0), (125.0, 100, 125.0, 150))
|
||||
assert tool.extents == (100, 150, 100, 150)
|
||||
|
||||
# grab a corner and move it
|
||||
do_event(tool, 'press', xdata=100, ydata=100)
|
||||
do_event(tool, 'onmove', xdata=120, ydata=120)
|
||||
do_event(tool, 'release', xdata=120, ydata=120)
|
||||
assert tool.extents == (120, 150, 120, 150)
|
||||
|
||||
# grab the center and move it
|
||||
do_event(tool, 'press', xdata=132, ydata=132)
|
||||
do_event(tool, 'onmove', xdata=120, ydata=120)
|
||||
do_event(tool, 'release', xdata=120, ydata=120)
|
||||
assert tool.extents == (108, 138, 108, 138)
|
||||
|
||||
# create a new rectangle
|
||||
do_event(tool, 'press', xdata=10, ydata=10)
|
||||
do_event(tool, 'onmove', xdata=100, ydata=100)
|
||||
do_event(tool, 'release', xdata=100, ydata=100)
|
||||
assert tool.extents == (10, 100, 10, 100)
|
||||
|
||||
|
||||
def check_span(*args, **kwargs):
|
||||
ax = get_ax()
|
||||
|
||||
def onselect(vmin, vmax):
|
||||
ax._got_onselect = True
|
||||
assert vmin == 100
|
||||
assert vmax == 150
|
||||
|
||||
def onmove(vmin, vmax):
|
||||
assert vmin == 100
|
||||
assert vmax == 125
|
||||
ax._got_on_move = True
|
||||
|
||||
if 'onmove_callback' in kwargs:
|
||||
kwargs['onmove_callback'] = onmove
|
||||
|
||||
tool = widgets.SpanSelector(ax, onselect, *args, **kwargs)
|
||||
do_event(tool, 'press', xdata=100, ydata=100, button=1)
|
||||
do_event(tool, 'onmove', xdata=125, ydata=125, button=1)
|
||||
do_event(tool, 'release', xdata=150, ydata=150, button=1)
|
||||
|
||||
assert ax._got_onselect
|
||||
|
||||
if 'onmove_callback' in kwargs:
|
||||
assert ax._got_on_move
|
||||
|
||||
|
||||
def test_span_selector():
|
||||
check_span('horizontal', minspan=10, useblit=True)
|
||||
check_span('vertical', onmove_callback=True, button=1)
|
||||
check_span('horizontal', rectprops=dict(fill=True))
|
||||
|
||||
|
||||
def check_lasso_selector(**kwargs):
|
||||
ax = get_ax()
|
||||
|
||||
def onselect(verts):
|
||||
ax._got_onselect = True
|
||||
assert verts == [(100, 100), (125, 125), (150, 150)]
|
||||
|
||||
tool = widgets.LassoSelector(ax, onselect, **kwargs)
|
||||
do_event(tool, 'press', xdata=100, ydata=100, button=1)
|
||||
do_event(tool, 'onmove', xdata=125, ydata=125, button=1)
|
||||
do_event(tool, 'release', xdata=150, ydata=150, button=1)
|
||||
|
||||
assert ax._got_onselect
|
||||
|
||||
|
||||
def test_lasso_selector():
|
||||
check_lasso_selector()
|
||||
check_lasso_selector(useblit=False, lineprops=dict(color='red'))
|
||||
check_lasso_selector(useblit=True, button=1)
|
||||
|
||||
|
||||
def test_CheckButtons():
|
||||
ax = get_ax()
|
||||
check = widgets.CheckButtons(ax, ('a', 'b', 'c'), (True, False, True))
|
||||
assert check.get_status() == [True, False, True]
|
||||
check.set_active(0)
|
||||
assert check.get_status() == [False, False, True]
|
||||
|
||||
cid = check.on_clicked(lambda: None)
|
||||
check.disconnect(cid)
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['check_radio_buttons'], extensions=['png'],
|
||||
style='mpl20', remove_text=True)
|
||||
def test_check_radio_buttons_image():
|
||||
get_ax()
|
||||
plt.subplots_adjust(left=0.3)
|
||||
rax1 = plt.axes([0.05, 0.7, 0.15, 0.15])
|
||||
rax2 = plt.axes([0.05, 0.2, 0.15, 0.15])
|
||||
widgets.RadioButtons(rax1, ('Radio 1', 'Radio 2', 'Radio 3'))
|
||||
widgets.CheckButtons(rax2, ('Check 1', 'Check 2', 'Check 3'),
|
||||
(False, True, True))
|
||||
|
||||
|
||||
@image_comparison(baseline_images=['check_bunch_of_radio_buttons'],
|
||||
style='mpl20', extensions=['png'], remove_text=True)
|
||||
def test_check_bunch_of_radio_buttons():
|
||||
rax = plt.axes([0.05, 0.1, 0.15, 0.7])
|
||||
widgets.RadioButtons(rax, ('B1', 'B2', 'B3', 'B4', 'B5', 'B6',
|
||||
'B7', 'B8', 'B9', 'B10', 'B11', 'B12',
|
||||
'B13', 'B14', 'B15'))
|
||||
|
||||
|
||||
def test_slider_slidermin_slidermax_invalid():
|
||||
fig, ax = plt.subplots()
|
||||
# test min/max with floats
|
||||
with pytest.raises(ValueError):
|
||||
widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
|
||||
slidermin=10.0)
|
||||
with pytest.raises(ValueError):
|
||||
widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
|
||||
slidermax=10.0)
|
||||
|
||||
|
||||
def test_slider_slidermin_slidermax():
|
||||
fig, ax = plt.subplots()
|
||||
slider_ = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
|
||||
valinit=5.0)
|
||||
|
||||
slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
|
||||
valinit=1.0, slidermin=slider_)
|
||||
assert slider.val == slider_.val
|
||||
|
||||
slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
|
||||
valinit=10.0, slidermax=slider_)
|
||||
assert slider.val == slider_.val
|
||||
|
||||
|
||||
def test_slider_valmin_valmax():
|
||||
fig, ax = plt.subplots()
|
||||
slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
|
||||
valinit=-10.0)
|
||||
assert slider.val == slider.valmin
|
||||
|
||||
slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
|
||||
valinit=25.0)
|
||||
assert slider.val == slider.valmax
|
||||
|
||||
|
||||
def check_polygon_selector(event_sequence, expected_result, selections_count):
|
||||
"""Helper function to test Polygon Selector
|
||||
|
||||
Parameters
|
||||
----------
|
||||
event_sequence : list of tuples (etype, dict())
|
||||
A sequence of events to perform. The sequence is a list of tuples
|
||||
where the first element of the tuple is an etype (e.g., 'onmove',
|
||||
'press', etc.), and the second element of the tuple is a dictionary of
|
||||
the arguments for the event (e.g., xdata=5, key='shift', etc.).
|
||||
expected_result : list of vertices (xdata, ydata)
|
||||
The list of vertices that are expected to result from the event
|
||||
sequence.
|
||||
selections_count : int
|
||||
Wait for the tool to call its `onselect` function `selections_count`
|
||||
times, before comparing the result to the `expected_result`
|
||||
"""
|
||||
ax = get_ax()
|
||||
|
||||
ax._selections_count = 0
|
||||
|
||||
def onselect(vertices):
|
||||
ax._selections_count += 1
|
||||
ax._current_result = vertices
|
||||
|
||||
tool = widgets.PolygonSelector(ax, onselect)
|
||||
|
||||
for (etype, event_args) in event_sequence:
|
||||
do_event(tool, etype, **event_args)
|
||||
|
||||
assert ax._selections_count == selections_count
|
||||
assert ax._current_result == expected_result
|
||||
|
||||
|
||||
def polygon_place_vertex(xdata, ydata):
|
||||
return [('onmove', dict(xdata=xdata, ydata=ydata)),
|
||||
('press', dict(xdata=xdata, ydata=ydata)),
|
||||
('release', dict(xdata=xdata, ydata=ydata))]
|
||||
|
||||
|
||||
def test_polygon_selector():
|
||||
# Simple polygon
|
||||
expected_result = [(50, 50), (150, 50), (50, 150)]
|
||||
event_sequence = (polygon_place_vertex(50, 50)
|
||||
+ polygon_place_vertex(150, 50)
|
||||
+ polygon_place_vertex(50, 150)
|
||||
+ polygon_place_vertex(50, 50))
|
||||
check_polygon_selector(event_sequence, expected_result, 1)
|
||||
|
||||
# Move first vertex before completing the polygon.
|
||||
expected_result = [(75, 50), (150, 50), (50, 150)]
|
||||
event_sequence = (polygon_place_vertex(50, 50)
|
||||
+ polygon_place_vertex(150, 50)
|
||||
+ [('on_key_press', dict(key='control')),
|
||||
('onmove', dict(xdata=50, ydata=50)),
|
||||
('press', dict(xdata=50, ydata=50)),
|
||||
('onmove', dict(xdata=75, ydata=50)),
|
||||
('release', dict(xdata=75, ydata=50)),
|
||||
('on_key_release', dict(key='control'))]
|
||||
+ polygon_place_vertex(50, 150)
|
||||
+ polygon_place_vertex(75, 50))
|
||||
check_polygon_selector(event_sequence, expected_result, 1)
|
||||
|
||||
# Move first two vertices at once before completing the polygon.
|
||||
expected_result = [(50, 75), (150, 75), (50, 150)]
|
||||
event_sequence = (polygon_place_vertex(50, 50)
|
||||
+ polygon_place_vertex(150, 50)
|
||||
+ [('on_key_press', dict(key='shift')),
|
||||
('onmove', dict(xdata=100, ydata=100)),
|
||||
('press', dict(xdata=100, ydata=100)),
|
||||
('onmove', dict(xdata=100, ydata=125)),
|
||||
('release', dict(xdata=100, ydata=125)),
|
||||
('on_key_release', dict(key='shift'))]
|
||||
+ polygon_place_vertex(50, 150)
|
||||
+ polygon_place_vertex(50, 75))
|
||||
check_polygon_selector(event_sequence, expected_result, 1)
|
||||
|
||||
# Move first vertex after completing the polygon.
|
||||
expected_result = [(75, 50), (150, 50), (50, 150)]
|
||||
event_sequence = (polygon_place_vertex(50, 50)
|
||||
+ polygon_place_vertex(150, 50)
|
||||
+ polygon_place_vertex(50, 150)
|
||||
+ polygon_place_vertex(50, 50)
|
||||
+ [('onmove', dict(xdata=50, ydata=50)),
|
||||
('press', dict(xdata=50, ydata=50)),
|
||||
('onmove', dict(xdata=75, ydata=50)),
|
||||
('release', dict(xdata=75, ydata=50))])
|
||||
check_polygon_selector(event_sequence, expected_result, 2)
|
||||
|
||||
# Move all vertices after completing the polygon.
|
||||
expected_result = [(75, 75), (175, 75), (75, 175)]
|
||||
event_sequence = (polygon_place_vertex(50, 50)
|
||||
+ polygon_place_vertex(150, 50)
|
||||
+ polygon_place_vertex(50, 150)
|
||||
+ polygon_place_vertex(50, 50)
|
||||
+ [('on_key_press', dict(key='shift')),
|
||||
('onmove', dict(xdata=100, ydata=100)),
|
||||
('press', dict(xdata=100, ydata=100)),
|
||||
('onmove', dict(xdata=125, ydata=125)),
|
||||
('release', dict(xdata=125, ydata=125)),
|
||||
('on_key_release', dict(key='shift'))])
|
||||
check_polygon_selector(event_sequence, expected_result, 2)
|
||||
|
||||
# Try to move a vertex and move all before placing any vertices.
|
||||
expected_result = [(50, 50), (150, 50), (50, 150)]
|
||||
event_sequence = ([('on_key_press', dict(key='control')),
|
||||
('onmove', dict(xdata=100, ydata=100)),
|
||||
('press', dict(xdata=100, ydata=100)),
|
||||
('onmove', dict(xdata=125, ydata=125)),
|
||||
('release', dict(xdata=125, ydata=125)),
|
||||
('on_key_release', dict(key='control')),
|
||||
('on_key_press', dict(key='shift')),
|
||||
('onmove', dict(xdata=100, ydata=100)),
|
||||
('press', dict(xdata=100, ydata=100)),
|
||||
('onmove', dict(xdata=125, ydata=125)),
|
||||
('release', dict(xdata=125, ydata=125)),
|
||||
('on_key_release', dict(key='shift'))]
|
||||
+ polygon_place_vertex(50, 50)
|
||||
+ polygon_place_vertex(150, 50)
|
||||
+ polygon_place_vertex(50, 150)
|
||||
+ polygon_place_vertex(50, 50))
|
||||
check_polygon_selector(event_sequence, expected_result, 1)
|
||||
|
||||
# Try to place vertex out-of-bounds, then reset, and start a new polygon.
|
||||
expected_result = [(50, 50), (150, 50), (50, 150)]
|
||||
event_sequence = (polygon_place_vertex(50, 50)
|
||||
+ polygon_place_vertex(250, 50)
|
||||
+ [('on_key_press', dict(key='escape')),
|
||||
('on_key_release', dict(key='escape'))]
|
||||
+ polygon_place_vertex(50, 50)
|
||||
+ polygon_place_vertex(150, 50)
|
||||
+ polygon_place_vertex(50, 150)
|
||||
+ polygon_place_vertex(50, 50))
|
||||
check_polygon_selector(event_sequence, expected_result, 1)
|
||||
Reference in New Issue
Block a user