|
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