source: sasview/src/sas/qtgui/Plotting/ColorMap.py @ 863ebca

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 863ebca was d6b8a1d, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

More Qt5 related fixes

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