Skip to content

Commit 225c8d3

Browse files
committed
Interactive setting of savefig rcParams (Qt only).
An additional window pops after selecting the filename to allow setting of savefig-related rcParams. For simplicity and consistency, the "ps.usedistiller" rcParam is now normalized to `None` when not set.
1 parent 74b6913 commit 225c8d3

File tree

3 files changed

+112
-24
lines changed

3 files changed

+112
-24
lines changed

lib/matplotlib/backends/backend_qt5.py

Lines changed: 104 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,17 @@
1212

1313
import matplotlib
1414

15+
from matplotlib import backend_tools
1516
from matplotlib._pylab_helpers import Gcf
1617
from matplotlib.backend_bases import (
1718
_Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2,
18-
TimerBase, cursors, ToolContainerBase, StatusbarBase)
19-
import matplotlib.backends.qt_editor.figureoptions as figureoptions
20-
from matplotlib.backends.qt_editor.formsubplottool import UiSubplotTool
21-
from matplotlib.figure import Figure
19+
TimerBase, ToolContainerBase, cursors)
2220
from matplotlib.backend_managers import ToolManager
23-
from matplotlib import backend_tools
2421

2522
from .qt_compat import (
2623
QtCore, QtGui, QtWidgets, _getSaveFileName, is_pyqt5, __version__, QT_API)
24+
from .qt_editor import figureoptions, formlayout
25+
from .qt_editor.formsubplottool import UiSubplotTool
2726

2827
backend_version = __version__
2928

@@ -838,24 +837,42 @@ def save_figure(self, *args):
838837
startpath = os.path.expanduser(
839838
matplotlib.rcParams['savefig.directory'])
840839
start = os.path.join(startpath, self.canvas.get_default_filename())
840+
841841
filters = []
842-
selectedFilter = None
842+
selected_filter = None
843843
for name, exts in sorted_filetypes:
844-
exts_list = " ".join(['*.%s' % ext for ext in exts])
845-
filter = '%s (%s)' % (name, exts_list)
844+
filters.append(
845+
'{} ({})'.format(name, ' '.join(map('*.{}'.format, exts))))
846846
if default_filetype in exts:
847-
selectedFilter = filter
848-
filters.append(filter)
847+
selected_filter = filters[-1]
849848
filters = ';;'.join(filters)
850849

851-
fname, filter = _getSaveFileName(self.parent,
852-
"Choose a filename to save to",
853-
start, filters, selectedFilter)
850+
fname, selected_filter = _getSaveFileName(
851+
self.parent, "Choose a filename to save to",
852+
start, filters, selected_filter)
854853
if fname:
855-
# Save dir for next time, unless empty str (i.e., use cwd).
856854
if startpath != "":
857855
matplotlib.rcParams['savefig.directory'] = (
858856
os.path.dirname(six.text_type(fname)))
857+
options = _default_savefig_options
858+
try:
859+
options = (options
860+
+ [None]
861+
+ _extra_savefig_options[
862+
os.path.splitext(fname)[1].lower()])
863+
except KeyError:
864+
pass
865+
fedit_arg = []
866+
for option in options:
867+
if option is None:
868+
fedit_arg.append((None, None))
869+
else:
870+
fedit_arg.append(option.make_fedit_entry())
871+
fedit_res = formlayout.fedit(fedit_arg, "Options")
872+
if not fedit_res:
873+
return
874+
for option, res in zip(filter(None, options), fedit_res):
875+
option.setter(res)
859876
try:
860877
self.canvas.figure.savefig(six.text_type(fname))
861878
except Exception as e:
@@ -864,6 +881,79 @@ def save_figure(self, *args):
864881
QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton)
865882

866883

884+
class _Option(object):
885+
886+
def __init__(self, name, rc_key=None, getter=None, setter=None):
887+
if rc_key and not (getter or setter):
888+
def make_fedit_entry():
889+
return (name, matplotlib.rcParams[rc_key])
890+
891+
def setter(val):
892+
matplotlib.rcParams[rc_key] = val
893+
894+
elif getter and setter and not rc_key:
895+
def make_fedit_entry():
896+
return (name, getter())
897+
898+
else:
899+
raise ValueError("Invalid entry")
900+
901+
self.name = name
902+
self.make_fedit_entry = make_fedit_entry
903+
self.setter = setter
904+
905+
906+
_default_savefig_options = [
907+
_Option("DPI", "savefig.dpi"),
908+
_Option("Face color", "savefig.facecolor"),
909+
_Option("Edge color", "savefig.edgecolor"),
910+
_Option("Tight bounding box",
911+
getter=lambda: matplotlib.rcParams["savefig.bbox"] == "tight",
912+
setter=lambda val: matplotlib.rcParams.__setitem__(
913+
"savefig.bbox", "tight" if val else "standard")),
914+
_Option("Tight bounding box padding", "savefig.pad_inches"),
915+
_Option("Transparent background", "savefig.transparent")]
916+
917+
918+
_extra_savefig_options = {
919+
".jpg": [
920+
_Option("JPEG quality", "savefig.jpeg_quality")],
921+
".jpeg": [
922+
_Option("JPEG quality", "savefig.jpeg_quality")],
923+
".pdf": [
924+
_Option("PDF compression", "pdf.compression"),
925+
_Option("PDF font type",
926+
getter=lambda:
927+
[str(matplotlib.rcParams["pdf.fonttype"]),
928+
"3", "42"],
929+
setter=lambda val:
930+
matplotlib.rcParams.__setitem__("pdf.fonttype", val))],
931+
".ps": [
932+
_Option("PS paper size",
933+
getter=lambda:
934+
[matplotlib.rcParams["ps.papersize"]]
935+
+ ["auto", "letter", "legal", "ledger"]
936+
+ ["A{}".format(i) for i in range(11)]
937+
+ ["B{}".format(i) for i in range(11)],
938+
setter=lambda val:
939+
matplotlib.rcParams.__setitem__("ps.papersize", val)),
940+
_Option("PS use AFM font", "ps.useafm"),
941+
_Option("PS distiller",
942+
getter=lambda:
943+
[str(matplotlib.rcParams["ps.usedistiller"])]
944+
+ ["None", "ghostscript", "xpdf"],
945+
setter=lambda val:
946+
matplotlib.rcParams.__setitem__("ps.usedistiller", val)),
947+
_Option("PS distiller resolution", "ps.distiller.res"),
948+
_Option("PS font type",
949+
getter=lambda:
950+
[str(matplotlib.rcParams["ps.fonttype"]),
951+
"3", "42"],
952+
setter=lambda val:
953+
matplotlib.rcParams.__setitem__("ps.fonttype", val))]
954+
}
955+
956+
867957
class SubplotToolQt(UiSubplotTool):
868958
def __init__(self, targetfig, parent):
869959
UiSubplotTool.__init__(self, None)

lib/matplotlib/rcsetup.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,10 +509,8 @@ def update_savefig_format(value):
509509
def validate_ps_distiller(s):
510510
if isinstance(s, six.string_types):
511511
s = s.lower()
512-
if s in ('none', None):
512+
if s in ('none', None, 'false', False):
513513
return None
514-
elif s in ('false', False):
515-
return False
516514
elif s in ('ghostscript', 'xpdf'):
517515
return s
518516
else:
@@ -1337,7 +1335,7 @@ def _validate_linestyle(ls):
13371335
'ps.papersize': ['letter', validate_ps_papersize],
13381336
'ps.useafm': [False, validate_bool], # Set PYTHONINSPECT
13391337
# use ghostscript or xpdf to distill ps output
1340-
'ps.usedistiller': [False, validate_ps_distiller],
1338+
'ps.usedistiller': [None, validate_ps_distiller],
13411339
'ps.distiller.res': [6000, validate_int], # dpi
13421340
'ps.fonttype': [3, validate_fonttype], # 3 (Type3) or 42 (Truetype)
13431341
# compression level from 0 to 9; 0 to disable

matplotlibrc.template

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -531,12 +531,12 @@ backend : $TEMPLATE_BACKEND
531531
# ps backend params
532532
#ps.papersize : letter # auto, letter, legal, ledger, A0-A10, B0-B10
533533
#ps.useafm : False # use of afm fonts, results in small files
534-
#ps.usedistiller : False # can be: None, ghostscript or xpdf
535-
# Experimental: may produce smaller files.
536-
# xpdf intended for production of publication quality files,
537-
# but requires ghostscript, xpdf and ps2eps
538-
#ps.distiller.res : 6000 # dpi
539-
#ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType)
534+
#ps.usedistiller : None # can be: None, ghostscript or xpdf
535+
# Experimental: may produce smaller files.
536+
# xpdf intended for production of publication quality files,
537+
# but requires ghostscript, xpdf and ps2eps
538+
#ps.distiller.res : 6000 # dpi
539+
#ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType)
540540

541541
## pdf backend params
542542
#pdf.compression : 6 # integer from 0 to 9

0 commit comments

Comments
 (0)