source: sasview/src/sas/qtgui/Plotting/ColorMap.py @ 13ee4d9

ESS_GUIESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_opencl
Last change on this file since 13ee4d9 was 33c0561, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 6 years ago

Replace Apply button menu driven functionality with additional button.
Removed Cancel.
Removed the window system context help button from all affected widgets.
SASVIEW-1239

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