Skip to content

Interactive setting of savefig rcParams (Qt only). #7696

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
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Diff view
120 changes: 106 additions & 14 deletions lib/matplotlib/backends/backend_qt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__

Expand Down Expand Up @@ -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':
Expand Down Expand Up @@ -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)))
Expand All @@ -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):
Expand Down
6 changes: 2 additions & 4 deletions lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions matplotlibrc.template
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down