diff --git a/pyncview/pyncview.py b/pyncview/pyncview.py index b8880d5..df6b843 100755 --- a/pyncview/pyncview.py +++ b/pyncview/pyncview.py @@ -443,10 +443,14 @@ class SliceWidget(QtWidgets.QWidget): stopAnimation = QtCore.Signal() onRecord = QtCore.Signal(object) sliceChanged = QtCore.Signal(bool) + makeSymmetric = QtCore.Signal() + changeColormap = QtCore.Signal(str) def __init__(self,parent=None,variable=None,figure=None,defaultslices=None,dimnames=None,animatecallback=None): super(SliceWidget,self).__init__(parent) + self.figure = figure + assert variable is not None,'Variable must be specified.' if defaultslices is None: defaultslices = {} @@ -511,7 +515,20 @@ def __init__(self,parent=None,variable=None,figure=None,defaultslices=None,dimna layout.addWidget(self.bnChangeAxes,2+len(dims),0,1,2) #layout.addWidget(self.bnAnimate, 3+len(dims),0,1,2) - layout.setRowStretch(4+len(dims),1) + # Add button for making the colorbar symmetric + self.bnSymmetric = QtWidgets.QPushButton('Make colorbar symmetric about 0',self) + self.bnSymmetric.clicked.connect(self.onColorbarSymmetric) + layout.addWidget(self.bnSymmetric,3+len(dims),0,1,2) + + # Add button with menu for choosing a colormap + self.bnColormap = QtWidgets.QPushButton('Choose colormap ...',self) + self.menuColormap = QtWidgets.QMenu(self) + self.bnColormap.setMenu(self.menuColormap) + layout.addWidget(self.bnColormap,4+len(dims),0,1,2) + self.colormapActions = {} + self.updateColormapMenu() + + layout.setRowStretch(6+len(dims),1) self.setLayout(layout) @@ -523,8 +540,6 @@ def __init__(self,parent=None,variable=None,figure=None,defaultslices=None,dimna self.setWindowTitle('Specify slice') - self.figure = figure - def closeEvent(self,event): # Make sure that the slice widget behaves as if no slices are specified, while # the widget is closing and after it is closed. @@ -539,6 +554,13 @@ def __init__(self,slicewidget,dim): def event(self): self.slicewidget.onAxesBounds(self.dim) + class ChangeColormapAction: + def __init__(self,slicewidget,colormap): + self.slicewidget = slicewidget + self.colormap = colormap + def event(self): + self.slicewidget.chooseColormap(self.colormap) + def getRange(self,dim): for c in self.dimcontrols: if c[0]==dim: @@ -575,6 +597,15 @@ def onAnimate(self): def onAxesBounds(self,dim=None): self.setAxesBounds.emit(dim) + def onColorbarSymmetric(self): + """Make the colorbar symmetric and choose a diverging colormap.""" + self.makeSymmetric.emit() + self.chooseColormap('RdBu_r') + + def chooseColormap(self, colormap): + self.changeColormap.emit(colormap) + self.updateColormapMenu() + def onCheckChanged(self,state=None): sliceddims = [] for (dim,checkbox,spin,bnanimate) in self.dimcontrols: @@ -614,6 +645,16 @@ def getSlices(self): slics[dim] = int(spin.value()) return slics + def updateColormapMenu(self): + """Add new entries (actions) to the colormap menu.""" + for colormap, description in self.figure.colormapQuickList.items(): + if colormap not in self.colormapActions: + action = SliceWidget.ChangeColormapAction(self, colormap) + self.menuColormap.addAction(colormap + ": " + description, action.event) + # Save action-object for colormap-event, otherwise + # the object is lost and the event cannot be triggered + self.colormapActions[colormap] = action + class NcPropertiesDialog(QtWidgets.QDialog): def __init__(self,item,parent,flags=QtCore.Qt.Dialog): QtWidgets.QDialog.__init__(self,parent,flags) @@ -860,6 +901,14 @@ def __init__(self,parent=None): self.figurepanel.figure.autosqueeze = False self.store = self.figurepanel.figure.source + # Create a collection of often used colormaps (will be extended as the user chooses other colormaps) + self.figurepanel.figure.colormapQuickList = { + 'jet': 'rainbow colormap (PyNcView default)', + 'viridis': 'sequential colormap (matplotlib default)', + 'RdBu_r': 'diverging colormap', + 'binary': 'grayscale colormap', + } + self.labelMissing = QtWidgets.QLabel('',central) self.labelMissing.setWordWrap(True) self.labelMissing.setVisible(False) @@ -1259,6 +1308,9 @@ def redraw(self,preserveproperties=True,preserveaxesbounds=True): ndim = len(varshape) - nsliced else: ndim = 1 + # Make buttons regarding colorbar visible if and only if there is a colorbar + self.slicetab.bnSymmetric.setVisible(ndim == 2) + self.slicetab.bnColormap.setVisible(ndim == 2) if ndim not in (1,2): if ndim>2: # More than 2 dimensions @@ -1471,6 +1523,23 @@ def iterdim(curslices,todoslices,progweight=1.,cumprog=0.): # Restore original cursor QtWidgets.QApplication.restoreOverrideCursor() + def makeColorbarSymmetric(self): + for axisnode in self.figurepanel.figure['Axes'].children: + if axisnode.getSecondaryId() == 'colorbar': + vamin = axisnode['Minimum'].getValue(usedefault=True) + vamax = axisnode['Maximum'].getValue(usedefault=True) + absmax = max(abs(vamax), abs(vamin)) + axisnode['Minimum'].setValue(-absmax) + axisnode['Maximum'].setValue(absmax) + + def changeColormap(self, colormap): + """Set colormap of figure to the given name and save previously used colormap.""" + figure = self.figurepanel.figure + old_colormap = figure['ColorMap'].getValue(usedefault=True) + figure['ColorMap'].setValue(colormap) + if old_colormap not in figure.colormapQuickList: + figure.colormapQuickList[old_colormap] = 'recently used' + def onRecordAnimation(self,dim): # Get the string specifying the currently selected variable (without slices applied!) varname = self.getSelectedVariable() @@ -1576,6 +1645,8 @@ def onSelectionChanged(self): if self.slicetab is not None: self.slicetab.close() self.slicetab = SliceWidget(None,var,figure=self.figurepanel.figure,defaultslices=self.defaultslices,dimnames=self.store.getVariableLongNames(),animatecallback=self.onAnimation) self.slicetab.sliceChanged.connect(self.onSliceChanged) + self.slicetab.makeSymmetric.connect(self.makeColorbarSymmetric) + self.slicetab.changeColormap.connect(self.changeColormap) self.slicetab.setAxesBounds.connect(self.setAxesBounds) self.slicetab.onRecord.connect(self.onRecordAnimation) self.slicetab.startAnimation.connect(self.figurepanel.startAnimation) diff --git a/setup.py b/setup.py index e46dd77..ddb33f3 100644 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def addtreewithwildcard(sourceroot,path,localtarget): from setuptools import setup setup(name='pyncview', - version='0.99.36', + version='0.99.37', description='NetCDF viewer written in Python', url='http://github.com/BoldingBruggeman/pyncview', author='Jorn Bruggeman', @@ -111,4 +111,4 @@ def addtreewithwildcard(sourceroot,path,localtarget): ] }, packages=['pyncview'], - package_data={'pyncview': ['pyncview.png']}) \ No newline at end of file + package_data={'pyncview': ['pyncview.png']})