"""
Module that contains the StretchLayout, RuleLayout
and StretchDefaultsDialog classes
"""
# This file is part of 'TuiView' - a simple Raster viewer
# Copyright (C) 2012 Sam Gillingham
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import os
import json
import traceback
from PySide6.QtWidgets import QDialog, QFormLayout, QGridLayout, QVBoxLayout
from PySide6.QtWidgets import QHBoxLayout, QComboBox, QToolBar, QLabel
from PySide6.QtWidgets import QPushButton, QGroupBox, QDockWidget, QFileDialog
from PySide6.QtWidgets import QTabWidget, QWidget, QSpinBox, QDoubleSpinBox, QCheckBox
from PySide6.QtWidgets import QToolButton, QColorDialog, QMessageBox
from PySide6.QtGui import QIcon, QPixmap, QColor, QAction, QGuiApplication
from PySide6.QtCore import QSettings, Qt, Signal
from . import viewerstretch
from . import pseudocolor
from .viewerstrings import MESSAGE_TITLE
from . import viewerwindow
SHOW_TRACEBACK = os.getenv('TUIVIEW_SHOW_TRACEBACK', '0') == '1'
# strings for the combo boxes and their values
MODE_DATA = (("Color Table", viewerstretch.VIEWER_MODE_COLORTABLE),
("Greyscale", viewerstretch.VIEWER_MODE_GREYSCALE),
("PseudoColor", viewerstretch.VIEWER_MODE_PSEUDOCOLOR),
("RGB", viewerstretch.VIEWER_MODE_RGB),
("RGBA", viewerstretch.VIEWER_MODE_RGBA))
STRETCH_DATA = (("None", viewerstretch.VIEWER_STRETCHMODE_NONE),
("Linear", viewerstretch.VIEWER_STRETCHMODE_LINEAR),
("Linear bands vary", viewerstretch.VIEWER_STRETCHMODE_LINEAR_VAR),
("Standard Deviation", viewerstretch.VIEWER_STRETCHMODE_STDDEV),
("Standard Deviation bands vary", viewerstretch.VIEWER_STRETCHMODE_STDDEV_VAR),
("Histogram", viewerstretch.VIEWER_STRETCHMODE_HIST),
("Histogram bands vary", viewerstretch.VIEWER_STRETCHMODE_HIST_VAR))
DEFAULT_STRETCH_KEY = 'DefaultStretch'
MAX_BAND_NUMBER = 100 # for spin boxes
STRETCH_FILTER = ".stretch Files (*.stretch)"
[docs]class StretchLayout(QFormLayout):
"""
Layout that contains the actual stretch information
"""
def __init__(self, parent, stretch, gdaldataset=None):
QFormLayout.__init__(self)
# the mode
self.modeCombo = QComboBox(parent)
for text, code in MODE_DATA:
self.modeCombo.addItem(text, code)
# callback so we can set the state of other items when changed
self.modeCombo.currentIndexChanged.connect(self.modeChanged)
self.rampCombo = QComboBox(parent)
# make sure the pseudocolor has the extra ramps loaded
try:
pseudocolor.loadExtraRamps()
except Exception as e:
if SHOW_TRACEBACK:
traceback.print_exc()
else:
QMessageBox.critical(parent, MESSAGE_TITLE, str(e))
# populate combo - sort by type
for (name, display) in pseudocolor.getRampsForDisplay():
self.rampCombo.addItem(display, name)
self.modeLayout = QHBoxLayout()
self.modeLayout.addWidget(self.modeCombo)
self.modeLayout.addWidget(self.rampCombo)
self.addRow("Mode", self.modeLayout)
if gdaldataset is None:
# we don't have a dateset - is a rule
# create spin boxes for the bands
self.createSpinBands(parent)
else:
# we have a dataset. create combo
# boxes with the band names
self.createComboBands(gdaldataset, parent)
self.addRow("Bands", self.bandLayout)
# the parameters of the stretch
self.paramsLayout = QVBoxLayout()
self.paramsMinLayout = QHBoxLayout()
self.paramsMinList = []
for _ in range(4):
spin_box = QDoubleSpinBox(parent)
spin_box.setDecimals(3)
self.paramsMinLayout.addWidget(spin_box)
stats = QCheckBox(parent)
stats.setText("Statistics Min")
stats.stateChanged.connect(self.statsChanged)
self.paramsMinLayout.addWidget(stats)
self.paramsMinList.append((spin_box, stats))
self.paramsMaxLayout = QHBoxLayout()
self.paramsMaxList = []
for _ in range(4):
spin_box = QDoubleSpinBox(parent)
spin_box.setDecimals(3)
self.paramsMaxLayout.addWidget(spin_box)
stats = QCheckBox(parent)
stats.setText("Statistics Max")
stats.stateChanged.connect(self.statsChanged)
self.paramsMaxLayout.addWidget(stats)
self.paramsMaxList.append((spin_box, stats))
self.paramsLayout.addLayout(self.paramsMinLayout)
self.paramsLayout.addLayout(self.paramsMaxLayout)
self.addRow("Stretch Params", self.paramsLayout)
# create the combo for the type of stretch
self.stretchLayout = QHBoxLayout()
self.stretchCombo = QComboBox(parent)
for text, code in STRETCH_DATA:
self.stretchCombo.addItem(text, code)
# callback so we can set the state of other items when changed
self.stretchCombo.currentIndexChanged.connect(self.stretchChanged)
self.stretchLayout.addWidget(self.stretchCombo)
# now for no data, background and NaN
self.nodataLabel = QLabel(parent)
self.nodataLabel.setText("No Data")
self.stretchLayout.addWidget(self.nodataLabel)
self.stretchLayout.setAlignment(self.nodataLabel, Qt.AlignRight)
self.nodataButton = ColorButton(parent)
self.stretchLayout.addWidget(self.nodataButton)
self.backgroundLabel = QLabel(parent)
self.backgroundLabel.setText("Background")
self.stretchLayout.addWidget(self.backgroundLabel)
self.stretchLayout.setAlignment(self.backgroundLabel, Qt.AlignRight)
self.backgroundButton = ColorButton(parent)
self.stretchLayout.addWidget(self.backgroundButton)
self.NaNLabel = QLabel(parent)
self.NaNLabel.setText("NaN")
self.stretchLayout.addWidget(self.NaNLabel)
self.stretchLayout.setAlignment(self.NaNLabel, Qt.AlignRight)
self.NaNButton = ColorButton(parent)
self.stretchLayout.addWidget(self.NaNButton)
self.addRow("Stretch", self.stretchLayout)
# set state of GUI for this stretch
self.updateStretch(stretch)
[docs] def statsChanged(self, state):
"""
Called when the 'Statistics Min' or Max box is checked
"""
for spin_box, stats in self.paramsMinList:
if stats.isEnabled():
spin_box.setEnabled(stats.checkState() != Qt.Checked)
for spin_box, stats in self.paramsMaxList:
if stats.isEnabled():
spin_box.setEnabled(stats.checkState() != Qt.Checked)
[docs] def updateStretch(self, stretch):
"""
Change the state of the GUI to match the given stretch
"""
# the mode
idx = self.modeCombo.findData(stretch.mode)
if idx != -1:
self.modeCombo.setCurrentIndex(idx)
# ramp
if stretch.rampName is not None:
idx = self.rampCombo.findData(stretch.rampName)
if idx != -1:
self.rampCombo.setCurrentIndex(idx)
# set ramp state depending on if we are pseudo color or not
state = stretch.mode == viewerstretch.VIEWER_MODE_PSEUDOCOLOR
self.rampCombo.setEnabled(state)
# set the bands depending on if we are RGB/RGBA or not
if stretch.mode == viewerstretch.VIEWER_MODE_RGB:
self.redWidget.setToolTip("Red")
self.greenWidget.setToolTip("Green")
self.blueWidget.setToolTip("Blue")
(r, g, b) = stretch.bands
if isinstance(self.redWidget, QSpinBox):
self.redWidget.setValue(r)
self.greenWidget.setValue(g)
self.blueWidget.setValue(b)
else:
self.redWidget.setCurrentIndex(r - 1)
self.greenWidget.setCurrentIndex(g - 1)
self.blueWidget.setCurrentIndex(b - 1)
self.alphaWidget.setEnabled(False)
elif stretch.mode == viewerstretch.VIEWER_MODE_RGBA:
self.redWidget.setToolTip("Red")
self.greenWidget.setToolTip("Green")
self.blueWidget.setToolTip("Blue")
self.alphaWidget.setToolTip("Alpha")
(r, g, b, a) = stretch.bands
if isinstance(self.redWidget, QSpinBox):
self.redWidget.setValue(r)
self.greenWidget.setValue(g)
self.blueWidget.setValue(b)
self.alphaWidget.setValue(a)
else:
self.redWidget.setCurrentIndex(r - 1)
self.greenWidget.setCurrentIndex(g - 1)
self.blueWidget.setCurrentIndex(b - 1)
self.alphaWidget.setCurrentIndex(a - 1)
self.alphaWidget.setEnabled(True)
else:
self.redWidget.setToolTip("Displayed Band")
self.greenWidget.setEnabled(False)
self.blueWidget.setEnabled(False)
self.alphaWidget.setEnabled(False)
if isinstance(self.redWidget, QSpinBox):
self.redWidget.setValue(stretch.bands[0])
else:
self.redWidget.setCurrentIndex(stretch.bands[0] - 1)
# stretch mode
idx = self.stretchCombo.findData(stretch.stretchmode)
if idx != -1:
self.stretchCombo.setCurrentIndex(idx)
state = stretch.mode != viewerstretch.VIEWER_MODE_COLORTABLE
self.stretchCombo.setEnabled(state)
# Set up GUI
self.setStretchMode(stretch.stretchmode, stretch.stretchparam)
# nodata etc
self.nodataButton.setColorAsRGBATuple(stretch.nodata_rgba)
self.backgroundButton.setColorAsRGBATuple(stretch.background_rgba)
self.NaNButton.setColorAsRGBATuple(stretch.nan_rgba)
[docs] def setStretchMode(self, stretchmode, stretchparam=None):
"""
Used by updateStretch() and stretchChanged() to update the GUI for the stretch
if stretchparam is None, then the stretch defaults are used
"""
mode = self.modeCombo.currentData()
nbands = 1
if mode == viewerstretch.VIEWER_MODE_RGB:
nbands = 3
elif mode == viewerstretch.VIEWER_MODE_RGBA:
nbands = 4
# Note: set enabled on stats tick box first - changing checkState
# triggers statsChanged, but this ignores tick boxes that have
# been disabled
if stretchmode == viewerstretch.VIEWER_STRETCHMODE_STDDEV:
spin_box, stats = self.paramsMinList[0]
spin_box.setEnabled(True)
spin_box.setRange(0, 10)
spin_box.setSingleStep(0.1)
stats.setCheckState(Qt.Unchecked)
stats.setEnabled(False)
if stretchparam is not None:
spin_box.setValue(stretchparam[0])
else:
spin_box.setValue(viewerstretch.VIEWER_DEFAULT_STDDEV)
spin_box.setToolTip("Number of Standard Deviations")
for spin_box, stats in self.paramsMinList[1:]:
spin_box.setEnabled(False)
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
for spin_box, stats in self.paramsMaxList:
spin_box.setEnabled(False)
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
elif stretchmode == viewerstretch.VIEWER_STRETCHMODE_STDDEV_VAR:
if stretchparam is None:
stretchparam = [None] * len(self.paramsMinList)
for idx, (stddev, (spin_box, stats)) in enumerate(zip(stretchparam, self.paramsMinList)):
spin_box.setEnabled(idx < nbands)
spin_box.setRange(0, 10)
spin_box.setSingleStep(0.1)
spin_box.setToolTip("Number of Standard Deviations")
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
if stddev is not None:
spin_box.setValue(stddev)
else:
spin_box.setValue(viewerstretch.VIEWER_DEFAULT_STDDEV)
for spin_box, stats in self.paramsMaxList:
spin_box.setEnabled(False)
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
elif stretchmode == viewerstretch.VIEWER_STRETCHMODE_HIST:
spin_box, stats = self.paramsMinList[0]
spin_box.setEnabled(True)
spin_box.setRange(0, 1)
spin_box.setSingleStep(0.005)
if stretchparam is not None:
spin_box.setValue(stretchparam[0])
else:
spin_box.setValue(viewerstretch.VIEWER_DEFAULT_HISTMIN)
spin_box.setToolTip("Minimum Proportion of Histogram")
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
for spin_box, stats in self.paramsMinList[1:]:
spin_box.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
stats.setEnabled(False)
spin_box, stats = self.paramsMaxList[0]
spin_box.setEnabled(True)
spin_box.setRange(0, 1)
spin_box.setSingleStep(0.005)
if stretchparam is not None:
spin_box.setValue(stretchparam[1])
else:
spin_box.setValue(viewerstretch.VIEWER_DEFAULT_HISTMAX)
spin_box.setToolTip("Maximum Proportion of Histogram")
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
for spin_box, stats in self.paramsMaxList[1:]:
spin_box.setEnabled(False)
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
elif stretchmode == viewerstretch.VIEWER_STRETCHMODE_HIST_VAR:
if stretchparam is None:
stretchparam = [(None, None)] * len(self.paramsMinList)
for idx, ((minVal, maxVal), (spin_box, stats)) in enumerate(zip(stretchparam, self.paramsMinList)):
spin_box.setEnabled(idx < nbands)
spin_box.setRange(0, 10)
spin_box.setSingleStep(0.1)
spin_box.setToolTip("Minimum Proportion of Histogram")
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
if minVal is not None:
spin_box.setValue(minVal)
else:
spin_box.setValue(viewerstretch.VIEWER_DEFAULT_HISTMIN)
for idx, ((minVal, maxVal), (spin_box, stats)) in enumerate(zip(stretchparam, self.paramsMaxList)):
spin_box.setEnabled(idx < nbands)
spin_box.setRange(0, 10)
spin_box.setSingleStep(0.1)
spin_box.setToolTip("Maximum Proportion of Histogram")
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
if maxVal is not None:
spin_box.setValue(maxVal)
else:
spin_box.setValue(viewerstretch.VIEWER_DEFAULT_HISTMAX)
elif stretchmode == viewerstretch.VIEWER_STRETCHMODE_LINEAR:
spin_box, stats = self.paramsMinList[0]
spin_box.setRange(-2**32, 2**32)
spin_box.setSingleStep(1)
if stretchparam is not None:
spin_box.setValue(stretchparam[0])
spin_box.setEnabled(True)
stats.setCheckState(Qt.Unchecked)
else:
spin_box.setValue(0)
spin_box.setEnabled(False)
stats.setCheckState(Qt.Checked)
spin_box.setToolTip("Minimum value")
stats.setEnabled(True)
for spin_box, stats in self.paramsMinList[1:]:
spin_box.setEnabled(False)
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
spin_box, stats = self.paramsMaxList[0]
spin_box.setRange(-2**32, 2**32)
spin_box.setSingleStep(1)
if stretchparam is not None:
spin_box.setValue(stretchparam[1])
stats.setCheckState(Qt.Unchecked)
spin_box.setEnabled(True)
else:
spin_box.setValue(0)
stats.setCheckState(Qt.Checked)
spin_box.setEnabled(False)
stats.setEnabled(True)
spin_box.setToolTip("Maximum value")
for spin_box, stats in self.paramsMaxList[1:]:
spin_box.setEnabled(False)
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
elif stretchmode == viewerstretch.VIEWER_STRETCHMODE_LINEAR_VAR:
if stretchparam is None:
stretchparam = [(None, None)] * len(self.paramsMinList)
for idx, ((minVal, maxVal), (spin_box, stats)) in enumerate(zip(stretchparam, self.paramsMinList)):
spin_box.setRange(-2**32, 2**32)
spin_box.setSingleStep(1)
spin_box.setToolTip("Minimum value")
stats.setEnabled(idx < nbands)
if minVal is not None:
spin_box.setEnabled(idx < nbands)
spin_box.setValue(minVal)
stats.setCheckState(Qt.Unchecked)
else:
spin_box.setValue(0)
stats.setCheckState(Qt.Checked)
spin_box.setEnabled(False)
for idx, ((minVal, maxVal), (spin_box, stats)) in enumerate(zip(stretchparam, self.paramsMaxList)):
spin_box.setRange(-2**32, 2**32)
spin_box.setSingleStep(1)
spin_box.setToolTip("Maximum value")
stats.setCheckState(Qt.Unchecked)
stats.setEnabled(idx < nbands)
if maxVal is not None:
spin_box.setValue(maxVal)
stats.setCheckState(Qt.Unchecked)
spin_box.setEnabled(idx < nbands)
else:
spin_box.setValue(0)
stats.setCheckState(Qt.Checked)
spin_box.setEnabled(False)
else:
# no stretch
for spin_box, stats in self.paramsMinList:
spin_box.setEnabled(False)
spin_box.setToolTip("")
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
for spin_box, stats in self.paramsMaxList:
spin_box.setEnabled(False)
spin_box.setToolTip("")
stats.setEnabled(False)
stats.setCheckState(Qt.Unchecked)
[docs] def createSpinBands(self, parent):
"""
For the case where we are creating a rule
we have no band names so create spin boxes
"""
# create the 3 band spin boxes
self.bandLayout = QHBoxLayout()
self.redWidget = QSpinBox(parent)
self.redWidget.setRange(1, MAX_BAND_NUMBER)
self.bandLayout.addWidget(self.redWidget)
self.greenWidget = QSpinBox(parent)
self.greenWidget.setRange(1, MAX_BAND_NUMBER)
self.bandLayout.addWidget(self.greenWidget)
self.blueWidget = QSpinBox(parent)
self.blueWidget.setRange(1, MAX_BAND_NUMBER)
self.bandLayout.addWidget(self.blueWidget)
self.alphaWidget = QSpinBox(parent)
self.alphaWidget.setRange(1, MAX_BAND_NUMBER)
self.bandLayout.addWidget(self.alphaWidget)
[docs] def createComboBands(self, gdaldataset, parent):
"""
We have a dataset - create combo boxes with the band names
"""
self.bandLayout = QHBoxLayout()
self.redWidget = QComboBox(parent)
self.bandLayout.addWidget(self.redWidget)
self.greenWidget = QComboBox(parent)
self.bandLayout.addWidget(self.greenWidget)
self.blueWidget = QComboBox(parent)
self.bandLayout.addWidget(self.blueWidget)
self.alphaWidget = QComboBox(parent)
self.bandLayout.addWidget(self.alphaWidget)
self.populateComboFromDataset(self.redWidget, gdaldataset)
self.populateComboFromDataset(self.greenWidget, gdaldataset)
self.populateComboFromDataset(self.blueWidget, gdaldataset)
self.populateComboFromDataset(self.alphaWidget, gdaldataset)
[docs] def populateComboFromDataset(self, combo, gdaldataset):
"""
Go through all the bands in the dataset and add a combo
item for each one. Set the current index to the currentBand
"""
for count in range(gdaldataset.RasterCount):
bandnum = count + 1
gdalband = gdaldataset.GetRasterBand(bandnum)
name = gdalband.GetDescription()
if name == '':
# make up a name so the user can still choose
name = 'Band %d' % bandnum
combo.addItem(name, bandnum)
[docs] @staticmethod
def getBandValue(widget):
"""
Depending on whether widget it a spinbox
or a combo box extract the current value for it.
"""
if isinstance(widget, QSpinBox):
value = widget.value()
else:
index = widget.currentIndex()
var = widget.itemData(index)
value = var
return value
[docs] def getStretch(self):
"""
Return a ViewerStretch object that reflects
the current state of the GUI
"""
obj = viewerstretch.ViewerStretch()
obj.mode = self.modeCombo.currentData()
value = self.getBandValue(self.redWidget)
bands = [value]
if obj.mode == viewerstretch.VIEWER_MODE_RGB:
value = self.getBandValue(self.greenWidget)
bands.append(value)
value = self.getBandValue(self.blueWidget)
bands.append(value)
elif obj.mode == viewerstretch.VIEWER_MODE_RGBA:
value = self.getBandValue(self.greenWidget)
bands.append(value)
value = self.getBandValue(self.blueWidget)
bands.append(value)
value = self.getBandValue(self.alphaWidget)
bands.append(value)
obj.setBands(tuple(bands))
if obj.mode == viewerstretch.VIEWER_MODE_PSEUDOCOLOR:
idx = self.rampCombo.currentIndex()
rampName = self.rampCombo.itemData(idx)
obj.setPseudoColor(str(rampName))
obj.stretchmode = self.stretchCombo.currentData()
if obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_STDDEV:
spin_box, stats = self.paramsMinList[0]
value = spin_box.value()
obj.setStdDevStretch(value)
elif obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_STDDEV_VAR:
values = []
for idx, (spin_box, stats) in enumerate(self.paramsMinList):
if idx >= len(bands):
break
value = spin_box.value()
values.append(value)
obj.setStdDevStretchVar(values)
elif obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_HIST:
spin_box, stats = self.paramsMinList[0]
histmin = spin_box.value()
spin_box, stats = self.paramsMaxList[0]
histmax = spin_box.value()
obj.setHistStretch(histmin, histmax)
elif obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_HIST_VAR:
values = []
for idx, ((spin_box_min, _), (spin_box_max, _)) in enumerate(zip(self.paramsMinList, self.paramsMaxList)):
if idx >= len(bands):
break
minVal = spin_box_min.value()
maxVal = spin_box_max.value()
values.append((minVal, maxVal))
obj.setHistStretchVar(values)
elif obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_LINEAR:
# need to do something cleverer here with the special value
spin_box, stats = self.paramsMinList[0]
minVal = spin_box.value()
if stats.checkState() == Qt.Checked:
minVal = None
spin_box, stats = self.paramsMaxList[0]
maxVal = spin_box.value()
if stats.checkState() == Qt.Checked:
maxVal = None
obj.setLinearStretch(minVal, maxVal)
elif obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_LINEAR_VAR:
# need to do something cleverer here with the special value
values = []
for idx, ((spin_box_min, stats_min), (spin_box_max, stats_max)) in enumerate(zip(self.paramsMinList, self.paramsMaxList)):
if idx >= len(bands):
break
minVal = spin_box_min.value()
if stats_min.checkState() == Qt.Checked:
minVal = None
maxVal = spin_box_max.value()
if stats_max.checkState() == Qt.Checked:
maxVal = None
values.append((minVal, maxVal))
obj.setLinearStretchVar(values)
obj.setNoDataRGBA(self.nodataButton.getColorAsRGBATuple())
obj.setBackgroundRGBA(self.backgroundButton.getColorAsRGBATuple())
obj.setNaNRGBA(self.NaNButton.getColorAsRGBATuple())
return obj
[docs] def modeChanged(self, index):
"""
Called when user changed the mode.
Updates other GUI elements as needed
"""
mode = self.modeCombo.itemData(index)
greenredEnabled = mode in [viewerstretch.VIEWER_MODE_RGB,
viewerstretch.VIEWER_MODE_RGBA]
alphaEnabled = mode == viewerstretch.VIEWER_MODE_RGBA
self.greenWidget.setEnabled(greenredEnabled)
self.blueWidget.setEnabled(greenredEnabled)
self.alphaWidget.setEnabled(alphaEnabled)
if greenredEnabled:
self.redWidget.setToolTip("Red")
self.greenWidget.setToolTip("Green")
self.blueWidget.setToolTip("Blue")
else:
self.redWidget.setToolTip("Displayed Band")
self.greenWidget.setToolTip("")
self.blueWidget.setToolTip("")
if alphaEnabled:
self.alphaWidget.setToolTip("Alpha")
else:
self.alphaWidget.setToolTip("")
if mode == viewerstretch.VIEWER_MODE_COLORTABLE:
# need to set stretch to none
self.stretchCombo.setCurrentIndex(0)
state = mode != viewerstretch.VIEWER_MODE_COLORTABLE
self.stretchCombo.setEnabled(state)
state = mode == viewerstretch.VIEWER_MODE_PSEUDOCOLOR
self.rampCombo.setEnabled(state)
# to ensure the parameter items are correct for number of bands
stretchmode = self.stretchCombo.currentData()
self.setStretchMode(stretchmode)
[docs] def stretchChanged(self, index):
"""
Called when user changed the stretch.
Updates other GUI elements as needed
"""
stretchmode = self.stretchCombo.itemData(index)
self.setStretchMode(stretchmode)
RULE_DATA = (("Number of Bands Less than", viewerstretch.VIEWER_COMP_LT),
("Number of Bands Greater than", viewerstretch.VIEWER_COMP_GT),
("Number of Bands Equal to", viewerstretch.VIEWER_COMP_EQ))
[docs]class RuleLayout(QGridLayout):
"""
Layout that contains the 'rules'. These are
the number of bands, the comparison with the
number of bands and the check for a color table
"""
def __init__(self, parent, rule):
QGridLayout.__init__(self)
# the comaprison combo
self.compCombo = QComboBox(parent)
index = 0
for text, code in RULE_DATA:
self.compCombo.addItem(text, code)
if code == rule.comp:
self.compCombo.setCurrentIndex(index)
index += 1
self.addWidget(self.compCombo, 0, 0)
# the number of bands spinbox
self.numberBox = QSpinBox(parent)
self.numberBox.setRange(1, 100)
self.numberBox.setValue(rule.value)
self.addWidget(self.numberBox, 0, 1)
# the label for the color table rule
self.colorTableLabel = QLabel(parent)
self.colorTableLabel.setText("Color Table in Band")
self.addWidget(self.colorTableLabel, 1, 0)
# color table band spinbox
self.colorTableBox = QSpinBox(parent)
self.colorTableBox.setRange(0, 100)
self.colorTableBox.setSpecialValueText('No color table required')
if rule.ctband is None:
self.colorTableBox.setValue(0)
else:
self.colorTableBox.setValue(rule.ctband)
self.addWidget(self.colorTableBox, 1, 1)
[docs] def getRule(self):
"""
Return a StretchRule instance for the current GUI
settings
Note: the stretch field will be None
"""
index = self.compCombo.currentIndex()
comp = self.compCombo.itemData(index)
value = self.numberBox.value()
ctband = self.colorTableBox.value()
if ctband == 0:
ctband = None # no color table required
obj = viewerstretch.StretchRule(comp, value, ctband, None)
return obj
[docs]class StretchDefaultsDialog(QDialog):
"""
Dialog that contains a Tabs, each one describing a rule
and is a combination of RuleLayout and StretchLayout
"""
def __init__(self, parent):
QDialog.__init__(self, parent)
# create a tab widget with its tabs on the left
self.tabWidget = QTabWidget(self)
self.tabWidget.setTabPosition(QTabWidget.West)
# grab the rules from the setting
# this supplies some default rules if none
ruleList = self.fromSettings()
count = 1
# go through each rule
for rule in ruleList:
# create a widget for it
widget = self.createWidget(rule, rule.stretch)
# add the widget as a new tab
name = "Rule %d" % count
self.tabWidget.addTab(widget, name)
count += 1
# now sort out the rest of the dialog
self.mainLayout = QVBoxLayout(self)
self.mainLayout.addWidget(self.tabWidget)
# new and delete buttons
self.newBeforeButton = QPushButton(self)
self.newBeforeButton.setText("New Rule Before")
self.newBeforeButton.clicked.connect(self.onNewBefore)
self.newAfterButton = QPushButton(self)
self.newAfterButton.setText("New Rule After")
self.newAfterButton.clicked.connect(self.onNewAfter)
self.deleteRuleButton = QPushButton(self)
self.deleteRuleButton.setText("Delete This Rule")
if len(ruleList) <= 1:
self.deleteRuleButton.setEnabled(False)
self.deleteRuleButton.clicked.connect(self.onDelete)
self.newDeleteLayout = QHBoxLayout()
self.newDeleteLayout.addWidget(self.newBeforeButton)
self.newDeleteLayout.addWidget(self.newAfterButton)
self.newDeleteLayout.addWidget(self.deleteRuleButton)
self.mainLayout.addLayout(self.newDeleteLayout)
# ok and cancel buttons
self.okButton = QPushButton(self)
self.okButton.setText("OK")
self.okButton.setDefault(True)
self.okButton.clicked.connect(self.onOK)
self.cancelButton = QPushButton(self)
self.cancelButton.setText("Cancel")
self.cancelButton.clicked.connect(self.reject)
self.buttonLayout = QHBoxLayout()
self.buttonLayout.addWidget(self.okButton)
self.buttonLayout.addWidget(self.cancelButton)
self.mainLayout.addLayout(self.buttonLayout)
self.setLayout(self.mainLayout)
self.setWindowTitle("Default Stretch")
self.setSizeGripEnabled(True)
self.resize(600, 400)
[docs] @staticmethod
def fromSettings():
"""
Read the default stretch rules from the
settings and return a list of StretchRules.
Supplies a default set of rules if none found.
"""
settings = QSettings()
settings.beginGroup('Stretch')
ruleList = []
defaultRulesJSON = settings.value(DEFAULT_STRETCH_KEY)
if defaultRulesJSON is None or defaultRulesJSON == '':
# there isn't one, construct some defaults
stretch = viewerstretch.ViewerStretch()
# single band with color table
stretch.setColorTable()
stretch.setBands((1,))
# must be one band and band one must have a color table
rule = viewerstretch.StretchRule(
viewerstretch.VIEWER_COMP_EQ, 1, 1, stretch)
ruleList.append(rule)
# single band without color table
stretch.setGreyScale()
stretch.setStdDevStretch()
rule = viewerstretch.StretchRule(
viewerstretch.VIEWER_COMP_EQ, 1, None, stretch)
ruleList.append(rule)
# 2 bands
rule = viewerstretch.StretchRule(
viewerstretch.VIEWER_COMP_EQ, 2, None, stretch)
ruleList.append(rule)
# 3 bands
stretch.setRGB()
stretch.setBands((1, 2, 3))
rule = viewerstretch.StretchRule(
viewerstretch.VIEWER_COMP_EQ, 3, None, stretch)
ruleList.append(rule)
# < 6 bands
stretch.setBands((4, 3, 2))
rule = viewerstretch.StretchRule(
viewerstretch.VIEWER_COMP_LT, 6, None, stretch)
ruleList.append(rule)
# > 5 bands
stretch.setBands((5, 4, 2))
rule = viewerstretch.StretchRule(
viewerstretch.VIEWER_COMP_GT, 5, None, stretch)
ruleList.append(rule)
else:
# is a list of json strings
for string in json.loads(defaultRulesJSON):
# go through each one (which is itself a json string)
# and decode into a StretchRule
rule = viewerstretch.StretchRule.fromString(string)
ruleList.append(rule)
settings.endGroup()
return ruleList
[docs] def toSettings(self):
"""
Write the contents of the dialog as the
default rules to be remembered for next time.
"""
settings = QSettings()
settings.beginGroup('Stretch')
# go through each tab and turn
# rules into JSON string and append to list
defaultRulesList = []
nwidgets = self.tabWidget.count()
for index in range(nwidgets):
widget = self.tabWidget.widget(index)
stretch = widget.stretchLayout.getStretch()
rule = widget.ruleLayout.getRule()
rule.stretch = stretch
string = rule.toString()
defaultRulesList.append(string)
# turn list into a json string and write to settings
JSONstring = json.dumps(defaultRulesList)
settings.setValue(DEFAULT_STRETCH_KEY, JSONstring)
settings.endGroup()
[docs] def renumberTabs(self):
"""
A tab has been added or deleted so renumber
the tabs
"""
ntabs = self.tabWidget.count()
for index in range(ntabs):
name = "Rule %d" % (index + 1)
self.tabWidget.setTabText(index, name)
[docs] def onOK(self):
"""
OK button pressed. Save settings
"""
self.toSettings()
QDialog.accept(self)
[docs] def onNewBefore(self):
"""
The 'add new page before' button pressed.
Add a new page in with the rule/stretch
same as current page
"""
# get the current page and rule/stretch
currentWidget = self.tabWidget.currentWidget()
currentIndex = self.tabWidget.currentIndex()
rule = currentWidget.ruleLayout.getRule()
stretch = currentWidget.stretchLayout.getStretch()
# create a new tab
newWidget = self.createWidget(rule, stretch)
self.tabWidget.setUpdatesEnabled(False) # reduce flicker
self.tabWidget.insertTab(currentIndex, newWidget, "new rule")
self.deleteRuleButton.setEnabled(True)
self.renumberTabs() # make sure the numbers in order
self.tabWidget.setUpdatesEnabled(True) # reduce flicker
[docs] def onNewAfter(self):
"""
The 'add new page after' button pressed.
Add a new page in with the rule/stretch
same as current page
"""
# get the current page and rule/stretch
currentWidget = self.tabWidget.currentWidget()
currentIndex = self.tabWidget.currentIndex()
rule = currentWidget.ruleLayout.getRule()
stretch = currentWidget.stretchLayout.getStretch()
# create a new tab
newWidget = self.createWidget(rule, stretch)
self.tabWidget.setUpdatesEnabled(False) # reduce flicker
self.tabWidget.insertTab(currentIndex + 1, newWidget, "new rule")
self.deleteRuleButton.setEnabled(True)
self.renumberTabs() # make sure the numbers in order
self.tabWidget.setUpdatesEnabled(True) # reduce flicker
[docs] def onDelete(self):
"""
Delete the current page.
"""
currentIndex = self.tabWidget.currentIndex()
self.tabWidget.setUpdatesEnabled(False) # reduce flicker
self.tabWidget.removeTab(currentIndex)
if self.tabWidget.count() <= 1:
self.deleteRuleButton.setEnabled(False)
self.renumberTabs()
self.tabWidget.setUpdatesEnabled(True) # reduce flicker