Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 030cd4b

Browse files
committed
Merge remote-tracking branch 'matplotlib/master'
2 parents 8fa7011 + c29c773 commit 030cd4b

File tree

13 files changed

+141
-38
lines changed

13 files changed

+141
-38
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Preserve units with Text position
2+
`````````````````````````````````
3+
4+
Previously the 'get_position' method on Text would strip away unit information
5+
even though the units were still present. There was no inherent need to do
6+
this, so it has been changed so that unit data (if present) will be preserved.
7+
Essentially a call to 'get_position' will return the exact value from a call to
8+
'set_position'.
9+
10+
If you wish to get the old behaviour, then you can use the new method called
11+
'get_unitless_position'.

doc/mpl_toolkits/axes_grid/users/overview.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ Curvilinear Grid
464464
The motivation behind the AxisArtist module is to support curvilinear grid
465465
and ticks.
466466

467-
.. plot:: mpl_toolkits/axes_grid/examples/demo_floating_axis.py
467+
.. plot:: mpl_toolkits/axes_grid/examples/demo_curvelinear_grid.py
468468

469469
See :ref:`axisartist-manual` for more details.
470470

doc/users/pyplot_tutorial.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ some change to a figure: e.g., create a figure, create a plotting area
1111
in a figure, plot some lines in a plotting area, decorate the plot
1212
with labels, etc.... :mod:`matplotlib.pyplot` is stateful, in that it
1313
keeps track of the current figure and plotting area, and the plotting
14-
functions are directed to the current axes
14+
functions are directed to the current axes (please note that "axes" here
15+
and in most places in the documentation refers to the *axes*
16+
`part of a figure <http://matplotlib.org/faq/usage_faq.html#parts-of-a-figure>`__
17+
and not the strict mathematical term for more than one axis).
1518

1619
.. plot:: pyplots/pyplot_simple.py
1720
:include-source:
@@ -164,7 +167,7 @@ scenes. Below is a script to create two subplots.
164167

165168
The :func:`~matplotlib.pyplot.figure` command here is optional because
166169
``figure(1)`` will be created by default, just as a ``subplot(111)``
167-
will be created by default if you don't manually specify an axes. The
170+
will be created by default if you don't manually specify any axes. The
168171
:func:`~matplotlib.pyplot.subplot` command specifies ``numrows,
169172
numcols, fignum`` where ``fignum`` ranges from 1 to
170173
``numrows*numcols``. The commas in the ``subplot`` command are

lib/matplotlib/animation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,7 @@ def new_saved_frame_seq(self):
11421142
if self._save_seq:
11431143
# While iterating we are going to update _save_seq
11441144
# so make a copy to safely iterate over
1145-
self._old_saved_seq = self._save_seq.copy()
1145+
self._old_saved_seq = list(self._save_seq)
11461146
return iter(self._old_saved_seq)
11471147
else:
11481148
return itertools.islice(self.new_frame_seq(), self.save_count)

lib/matplotlib/backends/backend_qt5.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,21 @@ def stop_event_loop(self):
416416
stop_event_loop.__doc__ = FigureCanvasBase.stop_event_loop_default.__doc__
417417

418418
def draw_idle(self):
419-
self.update()
419+
# This cannot be a call to 'update', we need a slightly longer
420+
# delay, otherwise mouse releases from zooming, panning, or
421+
# lassoing might not finish processing and will not redraw properly.
422+
# We use the guard flag to prevent infinite calls to 'draw_idle' which
423+
# happens with the 'stale' figure & axes callbacks.
424+
d = self._idle
425+
self._idle = False
426+
427+
def idle_draw(*args):
428+
try:
429+
self.draw()
430+
finally:
431+
self._idle = True
432+
if d:
433+
QtCore.QTimer.singleShot(0, idle_draw)
420434

421435

422436
class MainWindow(QtWidgets.QMainWindow):

lib/matplotlib/backends/backend_qt5agg.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ def paintEvent(self, e):
7171
In Qt, all drawing should be done inside of here when a widget is
7272
shown onscreen.
7373
"""
74-
FigureCanvasAgg.draw(self)
74+
# If we have not rendered the Agg backend yet, do so now.
75+
if not hasattr(self, 'renderer'):
76+
FigureCanvasAgg.draw(self)
7577

7678
# FigureCanvasQT.paintEvent(self, e)
7779
if DEBUG:

lib/matplotlib/collections.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,7 @@ def __init__(self, segments, # Can be None.
10531053
cmap=None,
10541054
pickradius=5,
10551055
zorder=2,
1056+
facecolors='none',
10561057
**kwargs
10571058
):
10581059
"""
@@ -1106,6 +1107,11 @@ def __init__(self, segments, # Can be None.
11061107
*zorder*
11071108
The zorder of the LineCollection. Default is 2
11081109
1110+
*facecolors*
1111+
The facecolors of the LineCollection. Default is 'none'
1112+
Setting to a value other than 'none' will lead to a filled
1113+
polygon being drawn between points on each line.
1114+
11091115
The use of :class:`~matplotlib.cm.ScalarMappable` is optional.
11101116
If the :class:`~matplotlib.cm.ScalarMappable` array
11111117
:attr:`~matplotlib.cm.ScalarMappable._A` is not None (i.e., a call to
@@ -1124,7 +1130,7 @@ def __init__(self, segments, # Can be None.
11241130
Collection.__init__(
11251131
self,
11261132
edgecolors=colors,
1127-
facecolors='none',
1133+
facecolors=facecolors,
11281134
linewidths=linewidths,
11291135
linestyles=linestyles,
11301136
antialiaseds=antialiaseds,

lib/matplotlib/font_manager.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,15 +1099,18 @@ def score_family(self, families, family2):
10991099
Returns a match score between the list of font families in
11001100
*families* and the font family name *family2*.
11011101
1102-
An exact match anywhere in the list returns 0.0.
1102+
An exact match at the head of the list returns 0.0.
11031103
1104-
A match by generic font name will return 0.1.
1104+
A match further down the list will return between 0 and 1.
11051105
11061106
No match will return 1.0.
11071107
"""
11081108
if not isinstance(families, (list, tuple)):
11091109
families = [families]
1110+
elif len(families) == 0:
1111+
return 1.0
11101112
family2 = family2.lower()
1113+
step = 1 / len(families)
11111114
for i, family1 in enumerate(families):
11121115
family1 = family1.lower()
11131116
if family1 in font_family_aliases:
@@ -1117,12 +1120,11 @@ def score_family(self, families, family2):
11171120
options = [x.lower() for x in options]
11181121
if family2 in options:
11191122
idx = options.index(family2)
1120-
return ((0.1 * (idx / len(options))) *
1121-
((i + 1) / len(families)))
1123+
return (i + (idx / len(options))) * step
11221124
elif family1 == family2:
11231125
# The score should be weighted by where in the
11241126
# list the font was found.
1125-
return i / len(families)
1127+
return i * step
11261128
return 1.0
11271129

11281130
def score_style(self, style1, style2):

lib/matplotlib/image.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -718,11 +718,15 @@ def get_cursor_data(self, event):
718718
arr = self.get_array()
719719
data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]])
720720
array_extent = mtransforms.Bbox([[0, 0], arr.shape[:2]])
721-
trans = mtransforms. BboxTransform(boxin=data_extent,
722-
boxout=array_extent)
721+
trans = mtransforms.BboxTransform(boxin=data_extent,
722+
boxout=array_extent)
723723
y, x = event.ydata, event.xdata
724724
i, j = trans.transform_point([y, x]).astype(int)
725-
return arr[i, j]
725+
# Clip the coordinates at array bounds
726+
if not (0 <= i < arr.shape[0]) or not (0 <= j < arr.shape[1]):
727+
return None
728+
else:
729+
return arr[i, j]
726730

727731

728732
class NonUniformImage(AxesImage):

lib/matplotlib/lines.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -568,13 +568,14 @@ def get_window_extent(self, renderer):
568568
def axes(self, ax):
569569
# call the set method from the base-class property
570570
Artist.axes.fset(self, ax)
571-
# connect unit-related callbacks
572-
if ax.xaxis is not None:
573-
self._xcid = ax.xaxis.callbacks.connect('units',
574-
self.recache_always)
575-
if ax.yaxis is not None:
576-
self._ycid = ax.yaxis.callbacks.connect('units',
577-
self.recache_always)
571+
if ax is not None:
572+
# connect unit-related callbacks
573+
if ax.xaxis is not None:
574+
self._xcid = ax.xaxis.callbacks.connect('units',
575+
self.recache_always)
576+
if ax.yaxis is not None:
577+
self._ycid = ax.yaxis.callbacks.connect('units',
578+
self.recache_always)
578579

579580
def set_data(self, *args):
580581
"""

lib/matplotlib/tests/test_artist.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,21 +150,27 @@ def test_cull_markers():
150150
def test_remove():
151151
fig, ax = plt.subplots()
152152
im = ax.imshow(np.arange(36).reshape(6, 6))
153+
ln, = ax.plot(range(5))
153154

154155
assert_true(fig.stale)
155156
assert_true(ax.stale)
156157

157158
fig.canvas.draw()
158159
assert_false(fig.stale)
159160
assert_false(ax.stale)
161+
assert_false(ln.stale)
160162

161163
assert_true(im in ax.mouseover_set)
164+
assert_true(ln not in ax.mouseover_set)
162165
assert_true(im.axes is ax)
163166

164167
im.remove()
168+
ln.remove()
169+
170+
for art in [im, ln]:
171+
assert_true(art.axes is None)
172+
assert_true(art.figure is None)
165173

166-
assert_true(im.axes is None)
167-
assert_true(im.figure is None)
168174
assert_true(im not in ax.mouseover_set)
169175
assert_true(fig.stale)
170176
assert_true(ax.stale)

lib/matplotlib/tests/test_image.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,16 +170,36 @@ def test_cursor_data():
170170
xdisp, ydisp = ax.transData.transform_point([x, y])
171171

172172
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
173-
assert im.get_cursor_data(event) == 44
173+
z = im.get_cursor_data(event)
174+
assert z == 44, "Did not get 44, got %d" % z
175+
176+
# Now try for a point outside the image
177+
# Tests issue #4957
178+
x, y = 10.1, 4
179+
xdisp, ydisp = ax.transData.transform_point([x, y])
180+
181+
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
182+
z = im.get_cursor_data(event)
183+
assert z is None, "Did not get None, got %d" % z
184+
185+
# Hmm, something is wrong here... I get 0, not None...
186+
# But, this works further down in the tests with extents flipped
187+
#x, y = 0.1, -0.1
188+
#xdisp, ydisp = ax.transData.transform_point([x, y])
189+
#event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
190+
#z = im.get_cursor_data(event)
191+
#assert z is None, "Did not get None, got %d" % z
174192

175193
ax.clear()
194+
# Now try with the extents flipped.
176195
im = ax.imshow(np.arange(100).reshape(10, 10), origin='lower')
177196

178197
x, y = 4, 4
179198
xdisp, ydisp = ax.transData.transform_point([x, y])
180199

181200
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
182-
assert im.get_cursor_data(event) == 44
201+
z = im.get_cursor_data(event)
202+
assert z == 44, "Did not get 44, got %d" % z
183203

184204
fig, ax = plt.subplots()
185205
im = ax.imshow(np.arange(100).reshape(10, 10), extent=[0, 0.5, 0, 0.5])
@@ -188,7 +208,24 @@ def test_cursor_data():
188208
xdisp, ydisp = ax.transData.transform_point([x, y])
189209

190210
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
191-
assert im.get_cursor_data(event) == 55
211+
z = im.get_cursor_data(event)
212+
assert z == 55, "Did not get 55, got %d" % z
213+
214+
# Now try for a point outside the image
215+
# Tests issue #4957
216+
x, y = 0.75, 0.25
217+
xdisp, ydisp = ax.transData.transform_point([x, y])
218+
219+
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
220+
z = im.get_cursor_data(event)
221+
assert z is None, "Did not get None, got %d" % z
222+
223+
x, y = 0.01, -0.01
224+
xdisp, ydisp = ax.transData.transform_point([x, y])
225+
226+
event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
227+
z = im.get_cursor_data(event)
228+
assert z is None, "Did not get None, got %d" % z
192229

193230

194231
@image_comparison(baseline_images=['image_clip'])

lib/matplotlib/text.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,8 @@ def update(self, kwargs):
240240
"""
241241
bbox = kwargs.pop('bbox', None)
242242
super(Text, self).update(kwargs)
243-
self.set_bbox(bbox) # depends on font properties
243+
if bbox:
244+
self.set_bbox(bbox) # depends on font properties
244245

245246
def __getstate__(self):
246247
d = super(Text, self).__getstate__()
@@ -280,7 +281,7 @@ def contains(self, mouseevent):
280281

281282
def _get_xy_display(self):
282283
'get the (possibly unit converted) transformed x, y in display coords'
283-
x, y = self.get_position()
284+
x, y = self.get_unitless_position()
284285
return self.get_transform().transform_point((x, y))
285286

286287
def _get_multialignment(self):
@@ -536,8 +537,8 @@ def update_bbox_position_size(self, renderer):
536537

537538
trans = self.get_transform()
538539

539-
# don't use self.get_position here, which refers to text position
540-
# in Text, and dash position in TextWithDash:
540+
# don't use self.get_unitless_position here, which refers to text
541+
# position in Text, and dash position in TextWithDash:
541542
posx = float(self.convert_xunits(self._x))
542543
posy = float(self.convert_yunits(self._y))
543544

@@ -877,12 +878,20 @@ def get_horizontalalignment(self):
877878
"""
878879
return self._horizontalalignment
879880

880-
def get_position(self):
881-
"Return the position of the text as a tuple (*x*, *y*)"
881+
def get_unitless_position(self):
882+
"Return the unitless position of the text as a tuple (*x*, *y*)"
883+
# This will get the position with all unit information stripped away.
884+
# This is here for convienience since it is done in several locations.
882885
x = float(self.convert_xunits(self._x))
883886
y = float(self.convert_yunits(self._y))
884887
return x, y
885888

889+
def get_position(self):
890+
"Return the position of the text as a tuple (*x*, *y*)"
891+
# This should return the same data (possible unitized) as was
892+
# specified with 'set_x' and 'set_y'.
893+
return self._x, self._y
894+
886895
def get_prop_tup(self):
887896
"""
888897
Return a hashable tuple of properties.
@@ -891,7 +900,7 @@ def get_prop_tup(self):
891900
want to cache derived information about text (e.g., layouts) and
892901
need to know if the text has changed.
893902
"""
894-
x, y = self.get_position()
903+
x, y = self.get_unitless_position()
895904
return (x, y, self.get_text(), self._color,
896905
self._verticalalignment, self._horizontalalignment,
897906
hash(self._fontproperties),
@@ -950,7 +959,7 @@ def get_window_extent(self, renderer=None, dpi=None):
950959
raise RuntimeError('Cannot get window extent w/o renderer')
951960

952961
bbox, info, descent = self._get_layout(self._renderer)
953-
x, y = self.get_position()
962+
x, y = self.get_unitless_position()
954963
x, y = self.get_transform().transform_point((x, y))
955964
bbox = bbox.translated(x, y)
956965
if dpi is not None:
@@ -1365,12 +1374,20 @@ def __init__(self,
13651374

13661375
#self.set_bbox(dict(pad=0))
13671376

1368-
def get_position(self):
1369-
"Return the position of the text as a tuple (*x*, *y*)"
1377+
def get_unitless_position(self):
1378+
"Return the unitless position of the text as a tuple (*x*, *y*)"
1379+
# This will get the position with all unit information stripped away.
1380+
# This is here for convienience since it is done in several locations.
13701381
x = float(self.convert_xunits(self._dashx))
13711382
y = float(self.convert_yunits(self._dashy))
13721383
return x, y
13731384

1385+
def get_position(self):
1386+
"Return the position of the text as a tuple (*x*, *y*)"
1387+
# This should return the same data (possibly unitized) as was
1388+
# specified with set_x and set_y
1389+
return self._dashx, self._dashy
1390+
13741391
def get_prop_tup(self):
13751392
"""
13761393
Return a hashable tuple of properties.
@@ -1402,7 +1419,7 @@ def update_coords(self, renderer):
14021419
with respect to the actual canvas's coordinates we need to map
14031420
back and forth.
14041421
"""
1405-
dashx, dashy = self.get_position()
1422+
dashx, dashy = self.get_unitless_position()
14061423
dashlength = self.get_dashlength()
14071424
# Shortcircuit this process if we don't have a dash
14081425
if dashlength == 0.0:

0 commit comments

Comments
 (0)