source: sasview/src/sas/qtgui/Plotting/ColorMap.py @ 8fad50b

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 8fad50b was 8fad50b, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 5 years ago

Replaced pylab.cm with mpl.cm (PK's code review)

  • Property mode set to 100644
File size: 8.9 KB
Line 
1"""
2Allows users to change the range of the current graph
3"""
4from PyQt5 import QtCore
5from PyQt5 import QtGui
6from PyQt5 import QtWidgets
7import sas.qtgui.path_prepare
8
9import matplotlib as mpl
10import numpy
11
12from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
13from sas.qtgui.Plotting.PlotterData import Data2D
14from sas.qtgui.Utilities.GuiUtils import formatNumber, DoubleValidator
15from .rangeSlider import RangeSlider
16
17DEFAULT_MAP = 'jet'
18
19# Local UI
20from sas.qtgui.UI import main_resources_rc
21from sas.qtgui.Plotting.UI.ColorMapUI import Ui_ColorMapUI
22
23class ColorMap(QtWidgets.QDialog, Ui_ColorMapUI):
24    apply_signal = QtCore.pyqtSignal(tuple, str)
25    def __init__(self, parent=None, cmap=None, vmin=0.0, vmax=100.0, data=None):
26        super(ColorMap, self).__init__()
27
28        self.setupUi(self)
29        assert(isinstance(data, Data2D))
30
31        self.data = data
32        self._cmap_orig = self._cmap = cmap if cmap is not None else DEFAULT_MAP
33        self.all_maps = [m for m in mpl.cm.datad]
34        self.maps = sorted(m for m in self.all_maps if not m.endswith("_r"))
35        self.rmaps = sorted(set(self.all_maps) - set(self.maps))
36
37        self.vmin = self.vmin_orig = vmin
38        self.vmax = self.vmax_orig = vmax
39
40        # Initialize detector labels
41        self.initDetectorData()
42
43        # Initialize the combo box
44        self.initMapCombobox()
45
46        self.initRangeSlider()
47
48        # Add the color map component
49        self.initColorMap()
50
51        # Initialize validators on amplitude textboxes
52        validator_min = DoubleValidator(self.txtMinAmplitude)
53        validator_min.setNotation(0)
54        self.txtMinAmplitude.setValidator(validator_min)
55        validator_max = DoubleValidator(self.txtMaxAmplitude)
56        validator_max.setNotation(0)
57        self.txtMaxAmplitude.setValidator(validator_max)
58
59        # Set the initial amplitudes
60        self.txtMinAmplitude.setText(formatNumber(self.vmin))
61        self.txtMaxAmplitude.setText(formatNumber(self.vmax))
62
63        # Enforce constant size on the widget
64        self.setFixedSize(self.minimumSizeHint())
65
66        # Handle combobox changes
67        self.cbColorMap.currentIndexChanged.connect(self.onMapIndexChange)
68
69        # Handle checkbox changes
70        self.chkReverse.stateChanged.connect(self.onColorMapReversed)
71
72        # Handle the Reset button click
73        self.buttonBox.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect(self.onReset)
74
75        # Handle the Apply button click
76        self.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.onApply)
77
78        # Handle the amplitude setup
79        self.txtMinAmplitude.editingFinished.connect(self.onAmplitudeChange)
80        self.txtMaxAmplitude.editingFinished.connect(self.onAmplitudeChange)
81
82    def cmap(self):
83        """
84        Getter for the color map
85        """
86        return self._cmap
87
88    def norm(self):
89        """
90        Getter for the color map norm
91        """
92        return (self._norm.vmin, self._norm.vmax)
93
94    def onReset(self):
95        """
96        Respond to the Reset button click
97        """
98        # Go back to original settings
99        self._cmap = self._cmap_orig
100        self.vmin = self.vmin_orig
101        self.vmax = self.vmax_orig
102        self._norm = mpl.colors.Normalize(vmin=self.vmin, vmax=self.vmax)
103        self.txtMinAmplitude.setText(formatNumber(self.vmin))
104        self.txtMaxAmplitude.setText(formatNumber(self.vmax))
105        self.initMapCombobox()
106        self.slider.setMinimum(self.vmin)
107        self.slider.setMaximum(self.vmax)
108        self.slider.setLowValue(self.vmin)
109        self.slider.setHighValue(self.vmax)
110        # Redraw the widget
111        self.redrawColorBar()
112        self.canvas.draw()
113
114    def onApply(self):
115        """
116        Respond to the Apply button click.
117        Send a signal to the plotter with vmin/vmax/cmap for chart update
118        """
119        self.apply_signal.emit(self.norm(), self.cmap())
120
121    def initDetectorData(self):
122        """
123        Fill out the Detector labels
124        """
125        xnpts = len(self.data.x_bins)
126        ynpts = len(self.data.y_bins)
127        self.lblWidth.setText(formatNumber(xnpts))
128        self.lblHeight.setText(formatNumber(ynpts))
129        xmax = max(self.data.xmin, self.data.xmax)
130        ymax = max(self.data.ymin, self.data.ymax)
131        qmax = numpy.sqrt(numpy.power(xmax, 2) + numpy.power(ymax, 2))
132        self.lblQmax.setText(formatNumber(qmax))
133        self.lblStopRadius.setText(formatNumber(self.data.xmin))
134
135    def initMapCombobox(self):
136        """
137        Fill out the combo box with all available color maps
138        """
139        if self._cmap in self.rmaps:
140            maps = self.rmaps
141            # Assure correct state of the checkbox
142            self.chkReverse.setChecked(True)
143        else:
144            maps = self.maps
145            # Assure correct state of the checkbox
146            self.chkReverse.setChecked(False)
147
148        self.cbColorMap.addItems(maps)
149        # Set the default/passed map
150        self.cbColorMap.setCurrentIndex(self.cbColorMap.findText(self._cmap))
151
152    def initRangeSlider(self):
153        """
154        Create and display the double slider for data range mapping.
155        """
156        self.slider = RangeSlider()
157        self.slider.setMinimum(self.vmin)
158        self.slider.setMaximum(self.vmax)
159        self.slider.setLowValue(self.vmin)
160        self.slider.setHighValue(self.vmax)
161        self.slider.setOrientation(QtCore.Qt.Horizontal)
162
163        self.slider_label = QtWidgets.QLabel()
164        self.slider_label.setText("Drag the sliders to adjust color range.")
165
166        def set_vmin(value):
167            self.vmin = value
168            self.txtMinAmplitude.setText(str(value))
169            self.updateMap()
170        def set_vmax(value):
171            self.vmax = value
172            self.txtMaxAmplitude.setText(str(value))
173            self.updateMap()
174
175        self.slider.lowValueChanged.connect(set_vmin)
176        self.slider.highValueChanged.connect(set_vmin)
177
178    def updateMap(self):
179        self._norm = mpl.colors.Normalize(vmin=self.vmin, vmax=self.vmax)
180        self.redrawColorBar()
181        self.canvas.draw()
182
183    def initColorMap(self):
184        """
185        Prepare and display the color map
186        """
187        self.fig = mpl.figure.Figure(figsize=(4, 1))
188        self.ax1 = self.fig.add_axes([0.05, 0.65, 0.9, 0.15])
189
190        self._norm = mpl.colors.Normalize(vmin=self.vmin, vmax=self.vmax)
191        self.redrawColorBar()
192        self.canvas = FigureCanvas(self.fig)
193
194        layout = QtWidgets.QVBoxLayout()
195        layout.addWidget(self.slider_label)
196        layout.addWidget(self.slider)
197        layout.addWidget(self.canvas)
198
199        self.widget.setLayout(layout)
200
201    def onMapIndexChange(self, index):
202        """
203        Respond to the color map change event
204        """
205        new_map = str(self.cbColorMap.itemText(index))
206        self._cmap = new_map
207        self.redrawColorBar()
208        self.canvas.draw()
209
210    def redrawColorBar(self):
211        """
212        Call ColorbarBase with current values, effectively redrawing the widget
213        """
214        self.cb = mpl.colorbar.ColorbarBase(self.ax1, cmap=self._cmap,
215                                            norm=self._norm,
216                                            orientation='horizontal')
217        self.cb.set_label('Color map range')
218
219    def onColorMapReversed(self, isChecked):
220        """
221        Respond to ticking/unticking the color map reverse checkbox
222        """
223        current_map = str(self.cbColorMap.currentText())
224        if isChecked:
225            # Add "_r" to map name for the reversed version
226            new_map = current_map + "_r"
227            maps = self.rmaps
228            # Assure the reversed map exists.
229            if new_map not in maps:
230                new_map = maps[0]
231        else:
232            new_map = current_map[:-2] # "_r" = last two chars
233            maps = self.maps
234            # Base map for the reversed map should ALWAYS exist,
235            # but let's be paranoid here
236            if new_map not in maps:
237                new_map = maps[0]
238
239        self._cmap = new_map
240        # Clear the content of the combobox.
241        # Needs signal blocking, or else onMapIndexChange() spoils it all
242        self.cbColorMap.blockSignals(True)
243        self.cbColorMap.clear()
244        # Add the new set of maps
245        self.cbColorMap.addItems(maps)
246        # Remove the signal block before the proper index set
247        self.cbColorMap.blockSignals(False)
248        self.cbColorMap.setCurrentIndex(self.cbColorMap.findText(new_map))
249
250    def onAmplitudeChange(self):
251        """
252        Respond to editing the amplitude fields
253        """
254        min_amp = self.vmin
255        max_amp = self.vmax
256
257        try:
258            min_amp = float(self.txtMinAmplitude.text())
259        except ValueError:
260            pass
261        try:
262            max_amp = float(self.txtMaxAmplitude.text())
263        except ValueError:
264            pass
265
266        self._norm = mpl.colors.Normalize(vmin=min_amp, vmax=max_amp)
267        self.redrawColorBar()
268        self.canvas.draw()
Note: See TracBrowser for help on using the repository browser.