diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py index 18fa2ff04ca9..166f88b12cff 100644 --- a/lib/matplotlib/backends/backend_qt5.py +++ b/lib/matplotlib/backends/backend_qt5.py @@ -12,18 +12,17 @@ import matplotlib +from matplotlib import backend_tools from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import ( _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, - TimerBase, cursors, ToolContainerBase, StatusbarBase) -import matplotlib.backends.qt_editor.figureoptions as figureoptions -from matplotlib.backends.qt_editor.formsubplottool import UiSubplotTool -from matplotlib.figure import Figure + StatusbarBase, TimerBase, ToolContainerBase, cursors) from matplotlib.backend_managers import ToolManager -from matplotlib import backend_tools from .qt_compat import ( QtCore, QtGui, QtWidgets, _getSaveFileName, is_pyqt5, __version__, QT_API) +from .qt_editor import figureoptions, formlayout +from .qt_editor.formsubplottool import UiSubplotTool backend_version = __version__ @@ -725,6 +724,13 @@ def _init_toolbar(self): self._actions[callback] = a if callback in ['zoom', 'pan']: a.setCheckable(True) + if callback == 'save_figure': + btn = self.widgetForAction(a) + btn.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup) + menu = a.__menu = QtWidgets.QMenu() + a.setMenu(menu) + menu.addAction( + 'Options for saving', self._ui_get_save_options) if tooltip_text is not None: a.setToolTip(tooltip_text) if text == 'Subplots': @@ -838,21 +844,20 @@ def save_figure(self, *args): startpath = os.path.expanduser( matplotlib.rcParams['savefig.directory']) start = os.path.join(startpath, self.canvas.get_default_filename()) + filters = [] - selectedFilter = None + selected_filter = None for name, exts in sorted_filetypes: - exts_list = " ".join(['*.%s' % ext for ext in exts]) - filter = '%s (%s)' % (name, exts_list) + filters.append( + '{} ({})'.format(name, ' '.join(map('*.{}'.format, exts)))) if default_filetype in exts: - selectedFilter = filter - filters.append(filter) + selected_filter = filters[-1] filters = ';;'.join(filters) - fname, filter = _getSaveFileName(self.parent, - "Choose a filename to save to", - start, filters, selectedFilter) + fname, selected_filter = _getSaveFileName( + self.parent, "Choose a filename to save to", + start, filters, selected_filter) if fname: - # Save dir for next time, unless empty str (i.e., use cwd). if startpath != "": matplotlib.rcParams['savefig.directory'] = ( os.path.dirname(six.text_type(fname))) @@ -863,6 +868,93 @@ def save_figure(self, *args): self, "Error saving file", six.text_type(e), QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton) + def _ui_get_save_options(self): + fedit_arg = [] + for tab_name, options in _savefig_options: + fedit_arg.append(([], tab_name, "")) + for option in options: + if option is None: + fedit_arg[-1][0].append((None, None)) + else: + fedit_arg[-1][0].append(option.make_fedit_entry()) + fedit_res = formlayout.fedit(fedit_arg, "Options") + if not fedit_res: + return + for option, res in zip( + [option + for tab_name, options in _savefig_options + for option in options], + [res for tab in fedit_res for res in tab]): + option.setter(res) + + +class _Option(object): + + def __init__(self, name, rc_key=None, getter=None, setter=None): + if rc_key and not (getter or setter): + def make_fedit_entry(): + return (name, matplotlib.rcParams[rc_key]) + + def setter(val): + matplotlib.rcParams[rc_key] = val + + elif getter and setter and not rc_key: + def make_fedit_entry(): + return (name, getter()) + + else: + raise ValueError("Invalid entry") + + self.name = name + self.make_fedit_entry = make_fedit_entry + self.setter = setter + + +_savefig_options = [ + ("Common", [ + _Option("DPI", "savefig.dpi"), + _Option("Face color", "savefig.facecolor"), + _Option("Edge color", "savefig.edgecolor"), + _Option("Tight bounding box", + getter=lambda: matplotlib.rcParams["savefig.bbox"] == "tight", + setter=lambda val: matplotlib.rcParams.__setitem__( + "savefig.bbox", "tight" if val else "standard")), + _Option("Tight bounding box padding", "savefig.pad_inches"), + _Option("Transparent background", "savefig.transparent")]), + ("JPEG", [ + _Option("Quality", "savefig.jpeg_quality")]), + ("PDF", [ + _Option("Compression", "pdf.compression"), + _Option("Font type", + getter=lambda: + [str(matplotlib.rcParams["pdf.fonttype"]), + "3", "42"], + setter=lambda val: + matplotlib.rcParams.__setitem__("pdf.fonttype", val))]), + ("PS", [ + _Option("Paper size", + getter=lambda: + [matplotlib.rcParams["ps.papersize"]] + + ["auto", "letter", "legal", "ledger"] + + ["A{}".format(i) for i in range(11)] + + ["B{}".format(i) for i in range(11)], + setter=lambda val: + matplotlib.rcParams.__setitem__("ps.papersize", val)), + _Option("Use AFM font?", "ps.useafm"), + _Option("Distiller", + getter=lambda: + [str(matplotlib.rcParams["ps.usedistiller"])] + + ["None", "ghostscript", "xpdf"], + setter=lambda val: + matplotlib.rcParams.__setitem__("ps.usedistiller", val)), + _Option("Distiller resolution", "ps.distiller.res"), + _Option("Font type", + getter=lambda: + [str(matplotlib.rcParams["ps.fonttype"]), + "3", "42"], + setter=lambda val: + matplotlib.rcParams.__setitem__("ps.fonttype", val))])] + class SubplotToolQt(UiSubplotTool): def __init__(self, targetfig, parent): diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index a4434f1ba5d4..2c85ce4f83ad 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -509,10 +509,8 @@ def update_savefig_format(value): def validate_ps_distiller(s): if isinstance(s, six.string_types): s = s.lower() - if s in ('none', None): + if s in ('none', None, 'false', False): return None - elif s in ('false', False): - return False elif s in ('ghostscript', 'xpdf'): return s else: @@ -1337,7 +1335,7 @@ def _validate_linestyle(ls): 'ps.papersize': ['letter', validate_ps_papersize], 'ps.useafm': [False, validate_bool], # Set PYTHONINSPECT # use ghostscript or xpdf to distill ps output - 'ps.usedistiller': [False, validate_ps_distiller], + 'ps.usedistiller': [None, validate_ps_distiller], 'ps.distiller.res': [6000, validate_int], # dpi 'ps.fonttype': [3, validate_fonttype], # 3 (Type3) or 42 (Truetype) # compression level from 0 to 9; 0 to disable diff --git a/matplotlibrc.template b/matplotlibrc.template index aec7366fdd82..1ba67d214888 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -531,12 +531,12 @@ backend : $TEMPLATE_BACKEND # ps backend params #ps.papersize : letter # auto, letter, legal, ledger, A0-A10, B0-B10 #ps.useafm : False # use of afm fonts, results in small files -#ps.usedistiller : False # can be: None, ghostscript or xpdf - # Experimental: may produce smaller files. - # xpdf intended for production of publication quality files, - # but requires ghostscript, xpdf and ps2eps -#ps.distiller.res : 6000 # dpi -#ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType) +#ps.usedistiller : None # can be: None, ghostscript or xpdf + # Experimental: may produce smaller files. + # xpdf intended for production of publication quality files, + # but requires ghostscript, xpdf and ps2eps +#ps.distiller.res : 6000 # dpi +#ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType) ## pdf backend params #pdf.compression : 6 # integer from 0 to 9