Skip to content

Commit 173a197

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 ab98852 commit 173a197

File tree

3 files changed

+106
-19
lines changed

3 files changed

+106
-19
lines changed

lib/matplotlib/backends/backend_qt5.py

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
unicode_literals)
33
import six
44

5+
from collections import namedtuple
56
import os
67
import re
78
import signal
@@ -27,7 +28,8 @@
2728

2829
from .qt_compat import (QtCore, QtGui, QtWidgets, _getSaveFileName,
2930
__version__, is_pyqt5)
30-
from matplotlib.backends.qt_editor.formsubplottool import UiSubplotTool
31+
from .qt_editor import formlayout
32+
from .qt_editor.formsubplottool import UiSubplotTool
3133

3234
backend_version = __version__
3335

@@ -725,19 +727,19 @@ def save_figure(self, *args):
725727
startpath = matplotlib.rcParams.get('savefig.directory', '')
726728
startpath = os.path.expanduser(startpath)
727729
start = os.path.join(startpath, self.canvas.get_default_filename())
730+
728731
filters = []
729-
selectedFilter = None
732+
selected_filter = None
730733
for name, exts in sorted_filetypes:
731-
exts_list = " ".join(['*.%s' % ext for ext in exts])
732-
filter = '%s (%s)' % (name, exts_list)
734+
filters.append(
735+
'{} ({})'.format(name, ' '.join(map('*.{}'.format, exts))))
733736
if default_filetype in exts:
734-
selectedFilter = filter
735-
filters.append(filter)
737+
selected_filter = filters[-1]
736738
filters = ';;'.join(filters)
737739

738-
fname, filter = _getSaveFileName(self.parent,
739-
"Choose a filename to save to",
740-
start, filters, selectedFilter)
740+
fname, selected_filter = _getSaveFileName(
741+
self.parent, "Choose a filename to save to",
742+
start, filters, selected_filter)
741743
if fname:
742744
if startpath == '':
743745
# explicitly missing key or empty str signals to use cwd
@@ -746,6 +748,26 @@ def save_figure(self, *args):
746748
# save dir for next time
747749
savefig_dir = os.path.dirname(six.text_type(fname))
748750
matplotlib.rcParams['savefig.directory'] = savefig_dir
751+
options = _default_savefig_options
752+
try:
753+
options = (
754+
options
755+
+ [None]
756+
+ _extra_savefig_options[
757+
os.path.splitext(fname)[1].lower()])
758+
except KeyError:
759+
pass
760+
fedit_arg = []
761+
for option in options:
762+
if option == None:
763+
fedit_arg.append((None, None))
764+
else:
765+
fedit_arg.append(option.gen_fedit_entry())
766+
fedit_res = formlayout.fedit(fedit_arg, "Options")
767+
if not fedit_res:
768+
return
769+
for option, res in zip(filter(None, options), fedit_res):
770+
option.setter(res)
749771
try:
750772
self.canvas.print_figure(six.text_type(fname))
751773
except Exception as e:
@@ -754,6 +776,73 @@ def save_figure(self, *args):
754776
QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton)
755777

756778

779+
_Option = namedtuple("_Option", "name gen_fedit_entry setter")
780+
def _option(name, rc_key=None, getter=None, setter=None):
781+
if rc_key and not (getter or setter):
782+
def gen_fedit_entry():
783+
return (name, matplotlib.rcParams[rc_key])
784+
def setter(val):
785+
matplotlib.rcParams[rc_key] = val
786+
return _Option(name, gen_fedit_entry, setter)
787+
elif getter and setter and not rc_key:
788+
def gen_fedit_entry():
789+
return (name, getter())
790+
return _Option(name, gen_fedit_entry, setter)
791+
else:
792+
raise ValueError("Invalid entry")
793+
794+
795+
_default_savefig_options = [
796+
_option("DPI", "savefig.dpi"),
797+
_option("Face color", "savefig.facecolor"),
798+
_option("Edge color", "savefig.edgecolor"),
799+
_option("Tight bounding box",
800+
getter=lambda: matplotlib.rcParams["savefig.bbox"] == "tight",
801+
setter=lambda val: matplotlib.rcParams.__setitem__(
802+
"savefig.bbox", "tight" if val else "standard")),
803+
_option("Tight bounding box padding", "savefig.pad_inches"),
804+
_option("Transparent background", "savefig.transparent")]
805+
806+
807+
_extra_savefig_options = {
808+
".jpg": [
809+
_option("JPEG quality", "savefig.jpeg_quality")],
810+
".jpeg": [
811+
_option("JPEG quality", "savefig.jpeg_quality")],
812+
".pdf": [
813+
_option("PDF compression", "pdf.compression"),
814+
_option("PDF font type",
815+
getter=lambda:
816+
[str(matplotlib.rcParams["pdf.fonttype"]),
817+
"3", "42"],
818+
setter=lambda val:
819+
matplotlib.rcParams.__setitem__("pdf.fonttype", val))],
820+
".ps": [
821+
_option("PS paper size",
822+
getter=lambda:
823+
[matplotlib.rcParams["ps.papersize"]]
824+
+ ["auto", "letter", "legal", "ledger"]
825+
+ ["A{}".format(i) for i in range(11)]
826+
+ ["B{}".format(i) for i in range(11)],
827+
setter=lambda val:
828+
matplotlib.rcParams.__setitem__("ps.papersize", val)),
829+
_option("PS use AFM font", "ps.useafm"),
830+
_option("PS distiller",
831+
getter=lambda:
832+
[str(matplotlib.rcParams["ps.usedistiller"])]
833+
+ ["None", "ghostscript", "xpdf"],
834+
setter=lambda val:
835+
matplotlib.rcParams.__setitem__("ps.usedistiller", val)),
836+
_option("PS distiller resolution", "ps.distiller.res"),
837+
_option("PS font type",
838+
getter=lambda:
839+
[str(matplotlib.rcParams["ps.fonttype"]),
840+
"3", "42"],
841+
setter=lambda val:
842+
matplotlib.rcParams.__setitem__("ps.fonttype", val))]
843+
}
844+
845+
757846
class SubplotToolQt(SubplotTool, UiSubplotTool):
758847
def __init__(self, targetfig, parent):
759848
UiSubplotTool.__init__(self, None)

lib/matplotlib/rcsetup.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -504,10 +504,8 @@ def update_savefig_format(value):
504504
def validate_ps_distiller(s):
505505
if isinstance(s, six.string_types):
506506
s = s.lower()
507-
if s in ('none', None):
507+
if s in ('none', None, 'false', False):
508508
return None
509-
elif s in ('false', False):
510-
return False
511509
elif s in ('ghostscript', 'xpdf'):
512510
return s
513511
else:
@@ -1269,7 +1267,7 @@ def validate_animation_writer_path(p):
12691267
'ps.papersize': ['letter', validate_ps_papersize],
12701268
'ps.useafm': [False, validate_bool], # Set PYTHONINSPECT
12711269
# use ghostscript or xpdf to distill ps output
1272-
'ps.usedistiller': [False, validate_ps_distiller],
1270+
'ps.usedistiller': [None, validate_ps_distiller],
12731271
'ps.distiller.res': [6000, validate_int], # dpi
12741272
'ps.fonttype': [3, validate_fonttype], # 3 (Type3) or 42 (Truetype)
12751273
# 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
@@ -538,12 +538,12 @@ backend : $TEMPLATE_BACKEND
538538
# ps backend params
539539
#ps.papersize : letter # auto, letter, legal, ledger, A0-A10, B0-B10
540540
#ps.useafm : False # use of afm fonts, results in small files
541-
#ps.usedistiller : False # can be: None, ghostscript or xpdf
542-
# Experimental: may produce smaller files.
543-
# xpdf intended for production of publication quality files,
544-
# but requires ghostscript, xpdf and ps2eps
545-
#ps.distiller.res : 6000 # dpi
546-
#ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType)
541+
#ps.usedistiller : None # can be: None, ghostscript or xpdf
542+
# Experimental: may produce smaller files.
543+
# xpdf intended for production of publication quality files,
544+
# but requires ghostscript, xpdf and ps2eps
545+
#ps.distiller.res : 6000 # dpi
546+
#ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType)
547547

548548
# pdf backend params
549549
#pdf.compression : 6 # integer from 0 to 9

0 commit comments

Comments
 (0)