|
21 | 21 | from matplotlib._pylab_helpers import Gcf
|
22 | 22 | from matplotlib.figure import Figure
|
23 | 23 |
|
24 |
| -from matplotlib.widgets import SubplotTool |
25 | 24 | import matplotlib.backends.qt_editor.figureoptions as figureoptions
|
26 | 25 |
|
27 | 26 | from .qt_compat import (QtCore, QtGui, QtWidgets, _getSaveFileName,
|
@@ -778,101 +777,72 @@ def save_figure(self, *args):
|
778 | 777 | QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton)
|
779 | 778 |
|
780 | 779 |
|
781 |
| -class SubplotToolQt(SubplotTool, UiSubplotTool): |
| 780 | +class SubplotToolQt(UiSubplotTool): |
782 | 781 | def __init__(self, targetfig, parent):
|
783 | 782 | UiSubplotTool.__init__(self, None)
|
784 | 783 |
|
785 |
| - self.targetfig = targetfig |
786 |
| - self.parent = parent |
787 |
| - self.donebutton.clicked.connect(self.close) |
788 |
| - self.resetbutton.clicked.connect(self.reset) |
789 |
| - self.tightlayout.clicked.connect(self.functight) |
790 |
| - |
791 |
| - # constraints |
792 |
| - self.sliderleft.valueChanged.connect(self.sliderright.setMinimum) |
793 |
| - self.sliderright.valueChanged.connect(self.sliderleft.setMaximum) |
794 |
| - self.sliderbottom.valueChanged.connect(self.slidertop.setMinimum) |
795 |
| - self.slidertop.valueChanged.connect(self.sliderbottom.setMaximum) |
796 |
| - |
797 |
| - self.defaults = {} |
798 |
| - for attr in ('left', 'bottom', 'right', 'top', 'wspace', 'hspace', ): |
799 |
| - val = getattr(self.targetfig.subplotpars, attr) |
800 |
| - self.defaults[attr] = val |
801 |
| - slider = getattr(self, 'slider' + attr) |
802 |
| - txt = getattr(self, attr + 'value') |
803 |
| - slider.setMinimum(0) |
804 |
| - slider.setMaximum(1000) |
805 |
| - slider.setSingleStep(5) |
806 |
| - # do this before hooking up the callbacks |
807 |
| - slider.setSliderPosition(int(val * 1000)) |
808 |
| - txt.setText("%.2f" % val) |
809 |
| - slider.valueChanged.connect(getattr(self, 'func' + attr)) |
810 |
| - self._setSliderPositions() |
811 |
| - |
812 |
| - def _setSliderPositions(self): |
813 |
| - for attr in ('left', 'bottom', 'right', 'top', 'wspace', 'hspace', ): |
814 |
| - slider = getattr(self, 'slider' + attr) |
815 |
| - slider.setSliderPosition(int(self.defaults[attr] * 1000)) |
816 |
| - |
817 |
| - def funcleft(self, val): |
818 |
| - if val == self.sliderright.value(): |
819 |
| - val -= 1 |
820 |
| - val /= 1000. |
821 |
| - self.targetfig.subplots_adjust(left=val) |
822 |
| - self.leftvalue.setText("%.2f" % val) |
823 |
| - if self.drawon: |
824 |
| - self.targetfig.canvas.draw_idle() |
825 |
| - |
826 |
| - def funcright(self, val): |
827 |
| - if val == self.sliderleft.value(): |
828 |
| - val += 1 |
829 |
| - val /= 1000. |
830 |
| - self.targetfig.subplots_adjust(right=val) |
831 |
| - self.rightvalue.setText("%.2f" % val) |
832 |
| - if self.drawon: |
833 |
| - self.targetfig.canvas.draw_idle() |
834 |
| - |
835 |
| - def funcbottom(self, val): |
836 |
| - if val == self.slidertop.value(): |
837 |
| - val -= 1 |
838 |
| - val /= 1000. |
839 |
| - self.targetfig.subplots_adjust(bottom=val) |
840 |
| - self.bottomvalue.setText("%.2f" % val) |
841 |
| - if self.drawon: |
842 |
| - self.targetfig.canvas.draw_idle() |
843 |
| - |
844 |
| - def functop(self, val): |
845 |
| - if val == self.sliderbottom.value(): |
846 |
| - val += 1 |
847 |
| - val /= 1000. |
848 |
| - self.targetfig.subplots_adjust(top=val) |
849 |
| - self.topvalue.setText("%.2f" % val) |
850 |
| - if self.drawon: |
851 |
| - self.targetfig.canvas.draw_idle() |
852 |
| - |
853 |
| - def funcwspace(self, val): |
854 |
| - val /= 1000. |
855 |
| - self.targetfig.subplots_adjust(wspace=val) |
856 |
| - self.wspacevalue.setText("%.2f" % val) |
857 |
| - if self.drawon: |
858 |
| - self.targetfig.canvas.draw_idle() |
859 |
| - |
860 |
| - def funchspace(self, val): |
861 |
| - val /= 1000. |
862 |
| - self.targetfig.subplots_adjust(hspace=val) |
863 |
| - self.hspacevalue.setText("%.2f" % val) |
864 |
| - if self.drawon: |
865 |
| - self.targetfig.canvas.draw_idle() |
866 |
| - |
867 |
| - def functight(self): |
868 |
| - self.targetfig.tight_layout() |
869 |
| - self._setSliderPositions() |
870 |
| - self.targetfig.canvas.draw_idle() |
871 |
| - |
872 |
| - def reset(self): |
873 |
| - self.targetfig.subplots_adjust(**self.defaults) |
874 |
| - self._setSliderPositions() |
875 |
| - self.targetfig.canvas.draw_idle() |
| 784 | + self._figure = targetfig |
| 785 | + |
| 786 | + for lower, higher in [("bottom", "top"), ("left", "right")]: |
| 787 | + self._widgets[lower].valueChanged.connect( |
| 788 | + lambda val: self._widgets[higher].setMinimum(val + .001)) |
| 789 | + self._widgets[higher].valueChanged.connect( |
| 790 | + lambda val: self._widgets[lower].setMaximum(val - .001)) |
| 791 | + |
| 792 | + self._attrs = ["top", "bottom", "left", "right", "hspace", "wspace"] |
| 793 | + self._defaults = {attr: vars(self._figure.subplotpars)[attr] |
| 794 | + for attr in self._attrs} |
| 795 | + |
| 796 | + # Set values after setting the range callbacks, but before setting up |
| 797 | + # the redraw callbacks. |
| 798 | + self._reset() |
| 799 | + |
| 800 | + for attr in self._attrs: |
| 801 | + self._widgets[attr].valueChanged.connect(self._on_value_changed) |
| 802 | + for action, method in [("Export values", self._export_values), |
| 803 | + ("Tight layout", self._tight_layout), |
| 804 | + ("Reset", self._reset), |
| 805 | + ("Close", self.close)]: |
| 806 | + self._widgets[action].clicked.connect(method) |
| 807 | + |
| 808 | + def _export_values(self): |
| 809 | + # Explicitly round to 3 decimals (which is also the spinbox precision) |
| 810 | + # to avoid numbers of the form 0.100...001. |
| 811 | + dialog = QtWidgets.QDialog() |
| 812 | + layout = QtWidgets.QVBoxLayout() |
| 813 | + dialog.setLayout(layout) |
| 814 | + text = QtWidgets.QPlainTextEdit() |
| 815 | + text.setReadOnly(True) |
| 816 | + layout.addWidget(text) |
| 817 | + text.setPlainText( |
| 818 | + ",\n".join("{}={:.3}".format(attr, self._widgets[attr].value()) |
| 819 | + for attr in self._attrs)) |
| 820 | + # Adjust the height of the text widget to fit the whole text, plus |
| 821 | + # some padding. |
| 822 | + size = text.maximumSize() |
| 823 | + size.setHeight( |
| 824 | + QtGui.QFontMetrics(text.document().defaultFont()) |
| 825 | + .size(0, text.toPlainText()).height() + 20) |
| 826 | + text.setMaximumSize(size) |
| 827 | + dialog.exec_() |
| 828 | + |
| 829 | + def _on_value_changed(self): |
| 830 | + self._figure.subplots_adjust(**{attr: self._widgets[attr].value() |
| 831 | + for attr in self._attrs}) |
| 832 | + self._figure.canvas.draw_idle() |
| 833 | + |
| 834 | + def _tight_layout(self): |
| 835 | + self._figure.tight_layout() |
| 836 | + for attr in self._attrs: |
| 837 | + widget = self._widgets[attr] |
| 838 | + widget.blockSignals(True) |
| 839 | + widget.setValue(vars(self._figure.subplotpars)[attr]) |
| 840 | + widget.blockSignals(False) |
| 841 | + self._figure.canvas.draw_idle() |
| 842 | + |
| 843 | + def _reset(self): |
| 844 | + for attr, value in self._defaults.items(): |
| 845 | + self._widgets[attr].setValue(value) |
876 | 846 |
|
877 | 847 |
|
878 | 848 | def error_msg_qt(msg, parent=None):
|
|
0 commit comments