Skip to content

Commit c27e185

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 0084e4a commit c27e185

File tree

3 files changed

+112
-23
lines changed

3 files changed

+112
-23
lines changed

lib/matplotlib/backends/backend_qt5.py

Lines changed: 104 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@
1515
from matplotlib.backend_bases import (
1616
_Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2,
1717
TimerBase, cursors)
18-
import matplotlib.backends.qt_editor.figureoptions as figureoptions
19-
from matplotlib.backends.qt_editor.formsubplottool import UiSubplotTool
20-
from matplotlib.figure import Figure
2118

22-
from .qt_compat import (
23-
QtCore, QtGui, QtWidgets, _getSaveFileName, is_pyqt5, __version__, QT_API)
19+
from .qt_compat import (QtCore, QtGui, QtWidgets, _getSaveFileName,
20+
__version__, is_pyqt5, QT_API)
21+
from .qt_editor import figureoptions, formlayout
22+
from .qt_editor.formsubplottool import UiSubplotTool
2423

2524
backend_version = __version__
2625

@@ -726,24 +725,43 @@ def save_figure(self, *args):
726725
startpath = os.path.expanduser(
727726
matplotlib.rcParams['savefig.directory'])
728727
start = os.path.join(startpath, self.canvas.get_default_filename())
728+
729729
filters = []
730-
selectedFilter = None
730+
selected_filter = None
731731
for name, exts in sorted_filetypes:
732-
exts_list = " ".join(['*.%s' % ext for ext in exts])
733-
filter = '%s (%s)' % (name, exts_list)
732+
filters.append(
733+
'{} ({})'.format(name, ' '.join(map('*.{}'.format, exts))))
734734
if default_filetype in exts:
735-
selectedFilter = filter
736-
filters.append(filter)
735+
selected_filter = filters[-1]
737736
filters = ';;'.join(filters)
738737

739-
fname, filter = _getSaveFileName(self.parent,
740-
"Choose a filename to save to",
741-
start, filters, selectedFilter)
738+
fname, _ = _getSaveFileName(
739+
self.parent, "Choose a filename to save to",
740+
start, filters, selected_filter)
742741
if fname:
743742
# Save dir for next time, unless empty str (i.e., use cwd).
744743
if startpath != "":
745744
matplotlib.rcParams['savefig.directory'] = (
746745
os.path.dirname(six.text_type(fname)))
746+
options = _default_savefig_options
747+
try:
748+
options = (options
749+
+ [None]
750+
+ _extra_savefig_options[
751+
os.path.splitext(fname)[1].lower()])
752+
except KeyError:
753+
pass
754+
fedit_arg = []
755+
for option in options:
756+
if option is None:
757+
fedit_arg.append((None, None))
758+
else:
759+
fedit_arg.append(option.make_fedit_entry())
760+
fedit_res = formlayout.fedit(fedit_arg, "Options")
761+
if not fedit_res:
762+
return
763+
for option, res in zip(filter(None, options), fedit_res):
764+
option.setter(res)
747765
try:
748766
self.canvas.figure.savefig(six.text_type(fname))
749767
except Exception as e:
@@ -752,6 +770,79 @@ def save_figure(self, *args):
752770
QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton)
753771

754772

773+
class _Option(object):
774+
775+
def __init__(self, name, rc_key=None, getter=None, setter=None):
776+
if rc_key and not (getter or setter):
777+
def make_fedit_entry():
778+
return (name, matplotlib.rcParams[rc_key])
779+
780+
def setter(val):
781+
matplotlib.rcParams[rc_key] = val
782+
783+
elif getter and setter and not rc_key:
784+
def make_fedit_entry():
785+
return (name, getter())
786+
787+
else:
788+
raise ValueError("Invalid entry")
789+
790+
self.name = name
791+
self.make_fedit_entry = make_fedit_entry
792+
self.setter = setter
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+
755846
class SubplotToolQt(UiSubplotTool):
756847
def __init__(self, targetfig, parent):
757848
UiSubplotTool.__init__(self, None)

lib/matplotlib/rcsetup.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -500,10 +500,8 @@ def update_savefig_format(value):
500500
def validate_ps_distiller(s):
501501
if isinstance(s, six.string_types):
502502
s = s.lower()
503-
if s in ('none', None):
503+
if s in ('none', None, 'false', False):
504504
return None
505-
elif s in ('false', False):
506-
return False
507505
elif s in ('ghostscript', 'xpdf'):
508506
return s
509507
else:
@@ -1330,7 +1328,7 @@ def _validate_linestyle(ls):
13301328
'ps.papersize': ['letter', validate_ps_papersize],
13311329
'ps.useafm': [False, validate_bool], # Set PYTHONINSPECT
13321330
# use ghostscript or xpdf to distill ps output
1333-
'ps.usedistiller': [False, validate_ps_distiller],
1331+
'ps.usedistiller': [None, validate_ps_distiller],
13341332
'ps.distiller.res': [6000, validate_int], # dpi
13351333
'ps.fonttype': [3, validate_fonttype], # 3 (Type3) or 42 (Truetype)
13361334
# 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
@@ -532,12 +532,12 @@ backend : $TEMPLATE_BACKEND
532532
# ps backend params
533533
#ps.papersize : letter # auto, letter, legal, ledger, A0-A10, B0-B10
534534
#ps.useafm : False # use of afm fonts, results in small files
535-
#ps.usedistiller : False # can be: None, ghostscript or xpdf
536-
# Experimental: may produce smaller files.
537-
# xpdf intended for production of publication quality files,
538-
# but requires ghostscript, xpdf and ps2eps
539-
#ps.distiller.res : 6000 # dpi
540-
#ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType)
535+
#ps.usedistiller : None # can be: None, ghostscript or xpdf
536+
# Experimental: may produce smaller files.
537+
# xpdf intended for production of publication quality files,
538+
# but requires ghostscript, xpdf and ps2eps
539+
#ps.distiller.res : 6000 # dpi
540+
#ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType)
541541

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

0 commit comments

Comments
 (0)