8000 backend_wx.py: ReleaseMouse() when the capture is lost by newville · Pull Request #7652 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

backend_wx.py: ReleaseMouse() when the capture is lost #7652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
eac63b7
remove all CaptureMouse() and ReleaseMouse() calls
newville Dec 21, 2016
b4a083e
Merge branch 'master' of github.com:matplotlib/matplotlib
newville Dec 22, 2016
a8437a9
avoiding wx C++ assertions by handling Capture Lost and Changed events
newville Dec 22, 2016
331e6f7
Merge branch 'master' of github.com:matplotlib/matplotlib
newville Dec 24, 2016
0d5d1d5
now with _set_capture() method so that Capture is always released bef…
newville Dec 24, 2016
6135b14
Decode error messages from image converters.
QuLogic Dec 22, 2016
c96d50e
Also decode stdout from image verifiers.
QuLogic Dec 22, 2016
2d7b735
Make style change examples consistent
dstansby Dec 23, 2016
75a4986
Make plot titles consistent
dstansby Dec 23, 2016
8e45f4d
Allow to store metadata in png files
Xarthisius Oct 25, 2016
afaa4c4
Ensure that metadata pointer is initially set to NULL
Xarthisius Oct 25, 2016
26a8af8
Allow to pass custom infoDict to images created with pdf backend
Xarthisius Oct 25, 2016
15999fe
Allow to pass custom metadata to png images created with agg backend
Xarthisius Oct 25, 2016
80b2361
Allow to pass custom Creator to images created with ps backend
Xarthisius Oct 25, 2016
095db35
'Software' starts with capital letter as per PNG specification
Xarthisius Oct 25, 2016
19e9ad8
fix pep8 issue
Xarthisius Oct 25, 2016
17e8231
Drop debug statements
Xarthisius Oct 25, 2016
9fea731
Preserve the default values of the metadata. Allow user to update if …
Xarthisius Oct 25, 2016
cdc2185
Revert accidental changes and fix indentation
Xarthisius Oct 27, 2016
fa42ced
Handle unicode/bytes directly in the C extension
Xarthisius Oct 27, 2016
f0681de
Use 'latin_1' encoding instead of ascii
Xarthisius Oct 29, 2016
7912f00
Fix numpydoc format issues
Xarthisius Oct 29, 2016
2a5e3b0
Add example info dictionary
Xarthisius Oct 29, 2016
2ba99e1
Add a description for the 'metadata' keyword to the docstring
Xarthisius Oct 31, 2016
ad1dad9
Explicitly define 'metadata' kwarg in '_print_figure'
Xarthisius Oct 31, 2016
46afd9f
Define 'metadata' as kwarg in _print_figure_tex for consistency
Xarthisius Oct 31, 2016
7c2a94d
Use default value for invalid key instead of NULL
Xarthisius Oct 31, 2016
a499650
Use OrderedDict for metadata
Xarthisius Nov 17, 2016
5afb67a
Add 'what is new' entry for metadata kwarg in matplotlib.pyplot.savefig
Xarthisius Nov 17, 2016
5302ff7
Fix merge
Xarthisius Dec 25, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
8000
Diff view
18 changes: 9 additions & 9 deletions doc/users/dflt_style_changes.rst
10000
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,9 @@ cycle.
return [ax.fill_between(th, np.sin((j / N) * np.pi + th), alpha=.5, **extra_kwargs)
for j in range(N)]

demo(ax1, {}, '2.x')
demo(ax2, {'facecolor': 'C0'}, 'non-cycled')
demo(ax1, {'facecolor': 'C0'}, 'classic')
demo(ax2, {}, 'v2.0')


If the facecolor is set via the ``facecolors`` or ``color`` keyword argument,
then the color is not cycled.
Expand Down Expand Up @@ -588,14 +589,13 @@ The default value of the ``align`` kwarg for both
**bar_kwargs)


ax1.set_title('2.0')

ax2.set_title("classic alignment")
ax1.set_title("classic")
ax2.set_title('v2.0')

demo(ax1.bar, {})
demo(ax2.bar, {'align': 'edge'})
demo(ax3.barh, {})
demo(ax4.barh, {'align': 'edge'})
demo(ax1.bar, {'align': 'edge'})
demo(ax2.bar, {})
demo(ax3.barh, {'align': 'edge'})
demo(ax4.barh, {})


To restore the previous behavior explicitly pass the keyword argument
Expand Down
20 changes: 20 additions & 0 deletions doc/users/whats_new/metadata_savefig_kwarg.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Metadata savefig kwarg
----------------------

:func:`~matplotlib.pyplot.savefig` now accepts `metadata` as a keyword argument.
It can be used to store key/value pairs in the image metadata.

Supported formats and backends
``````````````````````````````
* 'png' with Agg backend
* 'pdf' with PDF backend (see
:func:`~matplotlib.backends.backend_pdf.PdfFile.writeInfoDict` for a list of
supported keywords)
* 'eps' and 'ps' with PS backend (only 'Creator' key is accepted)

Example
```````
::

plt.savefig('test.png', metadata={'Software': 'My awesome software'})

13 changes: 11 additions & 2 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@

import threading
import numpy as np
from collections import OrderedDict
from math import radians, cos, sin
from matplotlib import verbose, rcParams
from matplotlib import verbose, rcParams, __version__
from matplotlib.backend_bases import (RendererBase, FigureManagerBase,
FigureCanvasBase)
from matplotlib.cbook import is_string_like, maxdict, restrict_dict
Expand Down Expand Up @@ -554,8 +555,16 @@ def print_png(self, filename_or_obj, *args, **kwargs):
else:
close = False

version_str = 'matplotlib version ' + __version__ + \
', http://matplotlib.org/'
metadata = OrderedDict({'Software': version_str})
user_metadata = kwargs.pop("metadata", None)
if user_metadata is not None:
metadata.update(user_metadata)

try:
_png.write_png(renderer._renderer, filename_or_obj, self.figure.dpi)
_png.write_png(renderer._renderer, filename_or_obj,
self.figure.dpi, metadata=metadata)
finally:
if close:
filename_or_obj.close()
Expand Down
23 changes: 15 additions & 8 deletions lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ def _flush(self):
class PdfFile(object):
"""PDF file object."""

def __init__(self, filename):
def __init__(self, filename, metadata=None):
self.nextObject = 1 # next free object id
self.xrefTable = [[0, 65535, 'the zero object']]
self.passed_in_file_object = False
Expand Down Expand Up @@ -486,7 +486,9 @@ def __init__(self, filename):
'Creator': 'matplotlib %s, http://matplotlib.org' % __version__,
'Producer': 'matplotlib pdf backend%s' % revision,
'CreationDate': source_date
}
}
if metadata is not None:
self.infoDict.update(metadata)

self.fontNames = {} # maps filenames to internal font names
self.nextFont = 1 # next free internal font name
Expand Down Expand Up @@ -2438,22 +2440,27 @@ class PdfPages(object):
"""
__slots__ = ('_file', 'keep_empty')

def __init__(self, filename, keep_empty=True):
def __init__(self, filename, keep_empty=True, metadata=None):
"""
Create a new PdfPages object.

Parameters
----------

filename: str
filename : str
Plots using :meth:`PdfPages.savefig` will be written to a file at
this location. The file is opened at once and any older file with
the same name is overwritten.
keep_empty: bool, optional
keep_empty : bool, optional
If set to False, then empty pdf files will be delet 6D47 ed automatically
when closed.
metadata : dictionary, optional
Information dictionary object (see PDF reference section 10.2.1
'Document Information Dictionary'), e.g.:
`{'Creator': 'My software', 'Author': 'Me',
'Title': 'Awesome fig'}`
"""
self._file = PdfFile(filename)
self._file = PdfFile(filename, metadata=metadata)
self.keep_empty = keep_empty

def __enter__(self):
Expand Down Expand Up @@ -2492,7 +2499,7 @@ def savefig(self, figure=None, **kwargs):
Parameters
----------

figure: :class:`~matplotlib.figure.Figure` or int, optional
figure : :class:`~matplotlib.figure.Figure` or int, optional
Specifies what figure is saved to file. If not specified, the
active figure is saved. If a :class:`~matplotlib.figure.Figure`
instance is provided, this figure is saved. If an int is specified,
Expand Down Expand Up @@ -2556,7 +2563,7 @@ def print_pdf(self, filename, **kwargs):
if isinstance(filename, PdfPages):
file = filename._file
else:
file = PdfFile(filename)
file = PdfFile(filename, metadata=kwargs.pop("metadata", None))
try:
file.newPage(width, height)
_bbox_inches_restore = kwargs.pop("bbox_inches_restore", None)
Expand Down
29 changes: 23 additions & 6 deletions lib/matplotlib/backends/backend_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@ def _print_ps(self, outfile, format, *args, **kwargs):

def _print_figure(self, outfile, format, dpi=72, facecolor='w', edgecolor='w',
orientation='portrait', isLandscape=False, papertype=None,
**kwargs):
metadata=None, **kwargs):
"""
Render the figure to hardcopy. Set the figure patch face and
edge colors. This is useful because some of the GUIs have a
Expand All @@ -978,6 +978,9 @@ def _print_figure(self, outfile, format, dpi=72, facecolor='w', edgecolor='w',

If outfile is a file object, a stand-alone PostScript file is
written into this file object.

metadata must be a dictionary. Currently, only the value for
the key 'Creator' is used.
"""
isEPSF = format == 'eps'
passed_in_file_object = False
Expand Down Expand Up @@ -1059,13 +1062,18 @@ def write(self, *kl, **kwargs):
self.figure.set_facecolor(origfacecolor)
self.figure.set_edgecolor(origedgecolor)

# check for custom metadata
if metadata is not None and 'Creator' in metadata:
creator_str = metadata['Creator']
else:
creator_str = "matplotlib version " + __version__ + \
", http://matplotlib.org/"
def print_figure_impl():
# write the PostScript headers
if isEPSF: print("%!PS-Adobe-3.0 EPSF-3.0", file=fh)
else: print("%!PS-Adobe-3.0", file=fh)
F438 if title: print("%%Title: "+title, file=fh)
print(("%%Creator: matplotlib version "
+__version__+", http://matplotlib.org/"), file=fh)
print("%%Creator: " + creator_str, file=fh)
# get source date from SOURCE_DATE_EPOCH, if set
# See https://reproducible-builds.org/specs/source-date-epoch/
source_date_epoch = os.getenv("SOURCE_DATE_EPOCH")
Expand Down Expand Up @@ -1189,12 +1197,15 @@ def do_nothing():
os.chmod(outfile, mode)

def _print_figure_tex(self, outfile, format, dpi, facecolor, edgecolor,
orientation, isLandscape, papertype,
orientation, isLandscape, papertype, metadata=None,
**kwargs):
"""
If text.usetex is True in rc, a temporary pair of tex/eps files
are created to allow tex to manage the text layout via the PSFrags
package. These files are processed to yield the final ps or eps file.

metadata must be a dictionary. Currently, only the value for
the key 'Creator' is used.
"""
isEPSF = format == 'eps'
if is_string_like(outfile):
Expand Down Expand Up @@ -1249,14 +1260,20 @@ def write(self, *kl, **kwargs):
self.figure.set_facecolor(origfacecolor)
self.figure.set_edgecolor(origedgecolor)

# check for custom metadata
if metadata is not None and 'Creator' in metadata:
creator_str = metadata['Creator']
else:
creator_str = "matplotlib version " + __version__ + \
", http://matplotlib.org/"

# write to a temp file, we'll move it to outfile when done
fd, tmpfile = mkstemp()
with io.open(fd, 'w', encoding='latin-1') as fh:
# write the Encapsulated PostScript headers
print("%!PS-Adobe-3.0 EPSF-3.0", file=fh)
if title: print("%%Title: "+title, file=fh)
print(("%%Creator: matplotlib version "
+__version__+", http://matplotlib.org/"), file=fh)
print("%%Creator: " + creator_str, file=fh)
# get source date from SOURCE_DATE_EPOCH, if set
# See https://reproducible-builds.org/specs/source-date-epoch/
source_date_epoch = os.getenv("SOURCE_DATE_EPOCH")
Expand Down
35 changes: 23 additions & 12 deletions lib/matplotlib/backends/backend_wx.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,9 @@ def do_nothing(*args, **kwargs):
self.Bind(wx.EVT_MIDDLE_DCLICK, self._onMiddleButtonDClick)
self.Bind(wx.EVT_MIDDLE_UP, self._onMiddleButtonUp)

self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self._onCaptureLost)
self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self._onCaptureLost)

if wx.VERSION_STRING < "2.9":
# only needed in 2.8 to reduce flicker
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
Expand Down Expand Up @@ -1023,20 +1026,31 @@ def _onKeyUp(self, evt):
evt.Skip()
FigureCanvasBase.key_release_event(self, key, guiEvent=evt)

def _set_capture(self, capture=True):
"""control wx mouse capture """
if self.HasCapture():
self.ReleaseMouse()
if capture:
self.CaptureMouse()

def _onCaptureLost(self, evt):
"""Capture changed or lost"""
self._set_capture(False)

def _onRightButtonDown(self, evt):
"""Start measuring on an axis."""
x = evt.GetX()
y = self.figure.bbox.height - evt.GetY()
evt.Skip()
self.CaptureMouse()
self._set_capture(True)
FigureCanvasBase.button_press_event(self, x, y, 3, guiEvent=evt)

def _onRightButtonDClick(self, evt):
"""Start measuring on an axis."""
x = evt.GetX()
y = self.figure.bbox.height - evt.GetY()
evt.Skip()
self.CaptureMouse()
self._set_capture(True)
FigureCanvasBase.button_press_event(self, x, y, 3,
dblclick=True, guiEvent=evt)

Expand All @@ -1045,24 +1059,23 @@ def _onRightButtonUp(self, evt):
x = evt.GetX()
y = self.figure.bbox.height - evt.GetY()
evt.Skip()
if self.HasCapture():
self.ReleaseMouse()
self._set_capture(False)
FigureCanvasBase.button_release_event(self, x, y, 3, guiEvent=evt)

def _onLeftButtonDown(self, evt):
"""Start measuring on an axis."""
x = evt.GetX()
y = self.figure.bbox.height - evt.GetY()
evt.Skip()
self.CaptureMouse()
self._set_capture(True)
FigureCanvasBase.button_press_event(self, x, y, 1, guiEvent=evt)

def _onLeftButtonDClick(self, evt):
"""Start measuring on an axis."""
x = evt.GetX()
y = self.figure.bbox.height - evt.GetY()
evt.Skip()
self.CaptureMouse()
self._set_capture(True)
FigureCanvasBase.button_press_event(self, x, y, 1,
dblclick=True, guiEvent=evt)

Expand All @@ -1072,8 +1085,7 @@ def _onLeftButtonUp(self, evt):
y = self.figure.bbox.height - evt.GetY()
# print 'release button', 1
evt.Skip()
if self.HasCapture():
self.ReleaseMouse()
self._set_capture(False)
FigureCanvasBase.button_release_event(self, x, y, 1, guiEvent=evt)

# Add middle button events
Expand All @@ -1082,15 +1094,15 @@ def _onMiddleButtonDown(self, evt):
x = evt.GetX()
y = self.figure.bbox.height - evt.GetY()
evt.Skip()
self.CaptureMouse()
self._set_capture(True)
FigureCanvasBase.button_press_event(self, x, y, 2, guiEvent=evt)

def _onMiddleButtonDClick(self, evt):
"""Start measuring on an axis."""
x = evt.GetX()
y = self.figure.bbox.height - evt.GetY()
evt.Skip()
self.CaptureMouse()
self._set_capture(True)
FigureCanvasBase.button_press_event(self, x, y, 2,
dblclick=True, guiEvent=evt)

Expand All @@ -1100,8 +1112,7 @@ def _onMiddleButtonUp(self, evt):
y = self.figure.bbox.height - evt.GetY()
# print 'release button', 1
evt.Skip()
if self.HasCapture():
self.ReleaseMouse()
self._set_capture(False)
FigureCanvasBase.button_release_event(self, x, y, 2, guiEvent=evt)

def _onMouseWheel(self, evt):
Expand Down
8 changes: 4 additions & 4 deletions lib/matplotlib/testing/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ def get_file_hash(path, block_size=2 ** 20):
def make_external_conversion_command(cmd):
def convert(old, new):
cmdline = cmd(old, new)
pipe = subprocess.Popen(
cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pipe = subprocess.Popen(cmdline, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = pipe.communicate()
errcode = pipe.wait()
if not os.path.exists(new) or errcode:
Expand Down Expand Up @@ -224,8 +224,8 @@ def verify(filename):
verifier = verifiers.get(extension, None)
if verifier is not None:
cmd = verifier(filename)
pipe = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pipe = subprocess.Popen(cmd, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = pipe.communicate()
errcode = pipe.wait()
if errcode != 0:
Expand Down
Loading
0