# -*- coding: utf-8 -*-
### Copyright (C) 2008-2011 Antonio Valentino <a_valentino@users.sf.net>
### This file is part of GSDView.
### GSDView 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.
### GSDView 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 GSDView; if not, write to the Free Software
### Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
'''Widgets and dialogs for GSDView.'''
import os
import logging
from ConfigParser import ConfigParser
import numpy as np
from osgeo import gdal
from qt import QtCore, QtGui
from .. import utils
from .. import qt4support
from ..widgets import get_filedialog, FileEntryWidget
from . import gdalsupport
__author__ = '$Author$'
__date__ = '$Date$'
__revision__ = '$Revision$'
GDALInfoWidgetBase = qt4support.getuiform('gdalinfo', __name__)
GDALPreferencesPageBase = qt4support.getuiform('gdalpage', __name__)
[docs]class GDALPreferencesPage(QtGui.QWidget, GDALPreferencesPageBase):
def __init__(self, parent=None, flags=QtCore.Qt.WindowFlags(0), **kwargs):
super(GDALPreferencesPage, self).__init__(parent, flags, **kwargs)
self.setupUi(self)
self.infoButton.setIcon(qt4support.geticon('info.svg', 'gsdview'))
# Avoid promoted widgets
DirectoryOnly = QtGui.QFileDialog.DirectoryOnly
self.gdalDataDirEntryWidget = FileEntryWidget(mode=DirectoryOnly,
enabled=False)
self.optionsGridLayout.addWidget(self.gdalDataDirEntryWidget, 1, 1)
self.gdalDataCheckBox.toggled.connect(
self.gdalDataDirEntryWidget.setEnabled)
self.gdalDriverPathEntryWidget = FileEntryWidget(mode=DirectoryOnly,
enabled=False)
self.optionsGridLayout.addWidget(self.gdalDriverPathEntryWidget, 3, 1)
self.gdalDriverPathCheckBox.toggled.connect(
self.gdalDriverPathEntryWidget.setEnabled)
self.ogrDriverPathEntryWidget = FileEntryWidget(mode=DirectoryOnly,
enabled=False)
self.optionsGridLayout.addWidget(self.ogrDriverPathEntryWidget, 4, 1)
self.ogrDriverPathCheckBox.toggled.connect(
self.ogrDriverPathEntryWidget.setEnabled)
# info button
self.infoButton.clicked.connect(self.showinfo)
# Context menu actions
qt4support.setViewContextActions(self.extraOptTableWidget)
# standard options
cachesize = gdal.GetCacheMax()
self.cacheSpinBox.setValue(cachesize // 1024 ** 2)
dialog = get_filedialog(self)
for name in ('gdalDataDir', 'gdalDriverPath', 'ogrDriverPath'):
widget = getattr(self, name + 'EntryWidget')
widget.dialog = dialog
widget.mode = QtGui.QFileDialog.Directory
# extra options
self._extraoptions = {}
stdoptions = set(('GDAL_DATA', 'GDAL_SKIP', 'GDAL_DRIVER_PATH',
'OGR_DRIVER_PATH', 'GDAL_CACHEMAX', ''))
extraoptions = gdalsupport.GDAL_CONFIG_OPTIONS.splitlines()
extraoptions = [opt for opt in extraoptions if opt not in stdoptions]
self.extraOptTableWidget.setRowCount(len(extraoptions))
for row, key in enumerate(extraoptions):
item = QtGui.QTableWidgetItem(key)
item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
self.extraOptTableWidget.setItem(row, 0, item)
value = gdal.GetConfigOption(key, '')
item = QtGui.QTableWidgetItem(value)
self.extraOptTableWidget.setItem(row, 1, item)
if value:
self._extraoptions[key] = value
hheader = self.extraOptTableWidget.horizontalHeader()
hheader.resizeSections(QtGui.QHeaderView.ResizeToContents)
@QtCore.Slot()
[docs] def showinfo(self):
dialog = QtGui.QDialog(self)
dialog.setWindowTitle(self.tr('GDAL info'))
layout = QtGui.QVBoxLayout()
layout.addWidget(GDALInfoWidget())
buttonbox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Close,
accepted=dialog.accept,
rejected=dialog.reject)
layout.addWidget(buttonbox)
dialog.setLayout(layout)
buttonbox.setFocus()
dialog.exec_()
[docs] def load(self, settings):
settings.beginGroup('gdal')
try:
# cache size
cachesize = settings.value('GDAL_CACHEMAX')
if cachesize is not None:
self.cacheCheckBox.setChecked(True)
self.cacheSpinBox.setValue(int(cachesize) // 1024 ** 2)
else:
# show the current value and disable the control
cachesize = gdal.GetCacheMax()
self.cacheSpinBox.setValue(cachesize // 1024 ** 2)
self.cacheCheckBox.setChecked(False)
# GDAL data dir
datadir = settings.value('GDAL_DATA')
if datadir:
self.gdalDataCheckBox.setChecked(True)
self.gdalDataDirEntryWidget.setText(datadir)
else:
# show the current value and disable the control
datadir = gdal.GetConfigOption('GDAL_DATA', '')
self.gdalDataDirEntryWidget.setText(datadir)
self.gdalDataCheckBox.setChecked(False)
# GDAL_SKIP
gdalskip = settings.value('GDAL_SKIP')
if gdalskip:
self.skipCheckBox.setChecked(True)
self.skipLineEdit.setText(gdalskip)
else:
# show the current value and disable the control
gdalskip = gdal.GetConfigOption('GDAL_SKIP', '')
self.skipLineEdit.setText(gdalskip)
self.skipCheckBox.setChecked(False)
# GDAL driver path
gdaldriverpath = settings.value('GDAL_DRIVER_PATH')
if gdaldriverpath:
self.gdalDriverPathCheckBox.setChecked(True)
self.gdalDriverPathEntryWidget.setText(gdaldriverpath)
else:
# show the current value and disable the control
gdaldriverpath = gdal.GetConfigOption('GDAL_DRIVER_PATH', '')
self.gdalDriverPathEntryWidget.setText(gdaldriverpath)
self.gdalDriverPathCheckBox.setChecked(False)
# OGR driver path
ogrdriverpath = settings.value('OGR_DRIVER_PATH')
if ogrdriverpath:
self.ogrDriverPathCheckBox.setChecked(True)
self.ogrDriverPathEntryWidget.setText(ogrdriverpath)
else:
# show the current value and disable the control
ogrdriverpath = gdal.GetConfigOption('OGR_DRIVER_PATH', '')
self.ogrDriverPathEntryWidget.setText(ogrdriverpath)
self.ogrDriverPathCheckBox.setChecked(False)
# @TODO: complete
#~ gdal.GetConfigOption('CPL_DEBUG', 'OFF')
#~ gdal.GetConfigOption('GDAL_PAM_ENABLED', "NULL")
# extra options
# @TODO
finally:
settings.endGroup()
[docs] def save(self, settings):
settings.beginGroup('gdal')
try:
# cache
if self.cacheCheckBox.isChecked():
value = self.cacheSpinBox.value() * 1024 ** 2
settings.setValue('GDAL_CACHEMAX', value)
else:
settings.remove('GDAL_CACHEMAX')
# GDAL data dir
if self.gdalDataCheckBox.isChecked():
value = self.gdalDataDirEntryWidget.text()
settings.setValue('GDAL_DATA', value)
else:
settings.remove('GDAL_DATA')
# GDAL_SKIP
if self.skipCheckBox.isChecked():
settings.setValue('GDAL_SKIP', self.skipLineEdit.text())
else:
settings.remove('GDAL_SKIP')
# GDAL driver path
if self.gdalDriverPathCheckBox.isChecked():
value = self.gdalDriverPathEntryWidget.text()
settings.setValue('GDAL_DRIVER_PATH', value)
else:
settings.remove('GDAL_DRIVER_PATH')
# OGR driver path
if self.ogrDriverPathCheckBox.isChecked():
value = self.ogrDriverPathEntryWidget.text()
settings.setValue('OGR_DRIVER_PATH', value)
else:
settings.remove('OGR_DRIVER_PATH')
# @TODO: complete
#~ gdal.GetConfigOption('CPL_DEBUG', 'OFF')
#~ gdal.GetConfigOption('GDAL_PAM_ENABLED', "NULL")
# extra options
# @TODO
finally:
settings.endGroup()
[docs]class BackendPreferencesPage(GDALPreferencesPage):
def __init__(self, parent=None, flags=QtCore.Qt.WindowFlags(0), **kwargs):
super(BackendPreferencesPage, self).__init__(parent, flags, **kwargs)
#self.setupUi(self)
# GDAL backend
msg = 'Show overview items in the tree view.'
tip = msg + "\nNOTE: this setting doesn't affects items already open."
checkbox = QtGui.QCheckBox(self.tr(msg), toolTip=self.tr(tip))
self.showOverviewCheckbox = checkbox
layout = QtGui.QVBoxLayout()
layout.addWidget(checkbox)
#~ layout.addSpacerItem(QtGui.QSpacerItem(0, 20))
self.groupbox = QtGui.QGroupBox(self.tr('GDAL Backend Preferences'))
self.groupbox.setLayout(layout)
self.verticalLayout.insertWidget(1, self.groupbox)
[docs] def load(self, settings):
settings.beginGroup('gdalbackend')
try:
# show overviews in the treeview
value = settings.value('visible_overview_items')
if value is not None:
# @COMPATIBILITY: presumably a bug in PyQt4 (4.7.2)
if isinstance(value, basestring):
value = True if value in ('true', 'True') else False
self.showOverviewCheckbox.setChecked(value)
finally:
settings.endGroup()
super(BackendPreferencesPage, self).load(settings)
[docs] def save(self, settings):
settings.beginGroup('gdalbackend')
try:
# show overviews in the treeview
value = self.showOverviewCheckbox.isChecked()
settings.setValue('visible_overview_items', bool(value))
finally:
settings.endGroup()
super(BackendPreferencesPage, self).save(settings)
[docs]class KeyPressEater(QtCore.QObject):
def __init__(self, keys=None, parent=None):
super(KeyPressEater, self).__init__(parent)
if keys is not None:
self.keys = keys
else:
self.keys = [QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]
[docs] def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress:
if event.key() in self.keys:
return True
# standard event processing
return super(KeyPressEater, self).eventFilter(obj, event)
MetadataWidgetBase = qt4support.getuiform('metadata', __name__)
OverviewWidgetBase = qt4support.getuiform('overview', __name__)
[docs]class OverviewDialog(QtGui.QDialog):
'''Dialog for overview management.
Display existing overview levels and allow to to sibmit overview
compitation requests.
:SIGNALS:
* :attr:`overviewComputationRequest`
'''
#: SIGNAL: it is emitted when a time expensive computation of overviews
#: is required
#:
#: :C++ signature: `void overviewComputationRequest(QtGui.QStandardItem)`
overviewComputationRequest = QtCore.Signal(QtGui.QStandardItem) # @TODO: check backward compatibility
#overviewComputationRequest = QtCore.Signal(object)
def __init__(self, item=None, parent=None, flags=QtCore.Qt.WindowFlags(0),
**kargs):
super(OverviewDialog, self).__init__(parent, flags)
self.setWindowTitle(self.tr('Overview computation'))
label = QtGui.QLabel(self.tr('Dataset:'))
#: dataset label
self.description = QtGui.QLineEdit()
self.description.setReadOnly(True)
hlayout = QtGui.QHBoxLayout()
hlayout.addWidget(label)
hlayout.addWidget(self.description)
#: overview widget
self.overviewWidget = SpecialOverviewWidget()
buttonbox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Close)
buttonbox.rejected.connect(self.reject)
layout = QtGui.QVBoxLayout()
layout.addLayout(hlayout)
layout.addWidget(self.overviewWidget)
layout.addWidget(buttonbox)
self.setLayout(layout)
self._item = None
if item:
self.setItem(item)
else:
self.reset()
def _emitComputationRequest(self):
assert self._item, 'item not set'
self.overviewComputationRequest.emit(self._item)
[docs] def reset(self):
self.overviewWidget.reset()
self.description.setText('')
if self._item:
self.overviewWidget.overviewComputationRequest.disconnect(
self._emitComputationRequest)
self._item = None
[docs] def setItem(self, item):
if item:
if not hasattr(item, 'GetRasterBand'):
band = item
while not hasattr(item, 'GetRasterBand'):
band = item
item = item.parent()
else:
band = item.GetRasterBand(1)
self.overviewWidget.setItem(band)
self._item = item
self.description.setText(self._item.GetDescription())
self.description.setCursorPosition(0)
self.overviewWidget.overviewComputationRequest.connect(
self._emitComputationRequest)
else:
self.reset()
[docs] def updateOverviewInfo(self):
if self._item:
self.setItem(self._item)
else:
self.reset()
[docs]class MajorObjectInfoDialog(QtGui.QDialog):
def __init__(self, gdalobj, parent=None, flags=QtCore.Qt.WindowFlags(0),
**kwargs):
super(MajorObjectInfoDialog, self).__init__(parent, flags, **kwargs)
if hasattr(self, 'setupUi'):
self.setupUi(self)
self._obj = gdalobj
self.metadataWidget = MetadataWidget(self)
layout = self.tabWidget.widget(1).layout()
layout.addWidget(self.metadataWidget)
self.metadataWidget.domainChanged.connect(self.updateMetadata)
action = self.metadataWidget.findChild(QtGui.QAction, 'saveAsAction')
action.triggered.disconnect()
action.triggered.connect(self.saveMetadata)
action = self.metadataWidget.findChild(QtGui.QAction, 'printAction')
action.triggered.disconnect()
action.triggered.connect(self.printMetadata)
# Init tabs
self.updateMetadata()
def _checkgdalobj(self):
if not self._obj:
raise ValueError('no GDAL object attached (self._obj is None).')
@QtCore.Slot(str)
[docs] def reset(self):
self.resetMetadata()
[docs] def update(self):
if self._obj is not None:
self.updateMetadata()
else:
self.reset()
def _metadataToCfg(self, cfg=None):
if cfg is None:
# @TODO: use ordered dict if available
cfg = ConfigParser()
combobox = self.metadataWidget.domainComboBox
domains = [combobox.itemText(i) for i in range(combobox.count())]
for domain in domains:
# @NOTE: preserve order
metadatalist = self._obj.GetMetadata_List(str(domain))
if metadatalist:
if domain:
section = '_'.join([domain, 'DOMAIN'])
else:
section = 'DEFAULT_DOMAIN'
cfg.add_section(section)
metadata = [line.split('=', 1) for line in metadatalist]
for name, value in metadata:
cfg.set(section, name, value)
return cfg
# @TODO: move to metadata widget
@QtCore.Slot()
@QtCore.Slot()
def _setupImageStructureInfo(widget, metadata):
widget.compressionValue.setText(metadata.get('COMPRESSION', ''))
widget.nbitsValue.setText(metadata.get('NBITS', ''))
widget.interleaveValue.setText(metadata.get('INTERLEAVE', ''))
widget.pixelTypeValue.setText(metadata.get('PIXELTYPE', ''))
HistogramConfigDialogBase = qt4support.getuiform('histoconfig', __name__)
[docs]class HistogramConfigDialog(QtGui.QDialog, HistogramConfigDialogBase):
def __init__(self, parent=None, flags=QtCore.Qt.WindowFlags(0), **kwargs):
super(HistogramConfigDialog, self).__init__(parent, flags, **kwargs)
self.setupUi(self)
# Make it not resizable
w = self.maximumSize().width()
h = self.size().height()
self.setMaximumSize(w, h)
# Colors
self._default_palette = self.minSpinBox.palette()
self._error_palette = QtGui.QPalette(self._default_palette)
color = QtGui.QColor(QtCore.Qt.red)
self._error_palette.setColor(QtGui.QPalette.Text, color)
color.setAlpha(50)
self._error_palette.setColor(QtGui.QPalette.Base, color)
self.minSpinBox.editingFinished.connect(self.validate)
self.maxSpinBox.editingFinished.connect(self.validate)
@QtCore.Slot()
[docs] def validate(self):
if self.minSpinBox.value() >= self.maxSpinBox.value():
self.minSpinBox.lineEdit().setPalette(self._error_palette)
self.maxSpinBox.lineEdit().setPalette(self._error_palette)
return False
self.minSpinBox.lineEdit().setPalette(self._default_palette)
self.maxSpinBox.lineEdit().setPalette(self._default_palette)
return True
[docs] def setLimits(self, dtype):
vmin = -2 ** 15 - 0.5
vmax = 2 ** 16 - 0.5
if dtype in (np.uint8, np.uint16, np.uint32, np.uint64):
# Unsigned
vmin = -0.5
if dtype == np.uint8:
vmax = 255.5
else:
vmax = 2 ** 16 - 0.5
elif dtype == np.int8:
vmin = -128.5
vmax = 127.5
elif dtype == np.int16:
vmax = 2 ** 15 + 0.5
self.minSpinBox.setMinimum(vmin)
self.minSpinBox.setMaximum(vmax)
self.maxSpinBox.setMinimum(vmin)
self.maxSpinBox.setMaximum(vmax)
BandInfoDialogBase = qt4support.getuiform('banddialog', __name__)
[docs]class BandInfoDialog(MajorObjectInfoDialog, BandInfoDialogBase):
'''Info dialog for GDAL raster bands.
:SIGNALS:
* :attr:`statsComputationRequest`
* :attr:`histogramComputationRequest`
* :attr:`overviewComputationRequest`
'''
#: SIGNAL: it is emitted when a time expensive computation of statistics
#: is required
#:
#: :C++ signature: `void statsComputationRequest(QtGui.QStandardItem)`
statsComputationRequest = QtCore.Signal(QtGui.QStandardItem) # @TODO: check backward compatibility
#: SIGNAL: it is emitted when a time expensive computation of an histogram
#: is required
#:
#: :C++ signature: `void histogramComputationRequest(QtGui.QStandardItem)`
histogramComputationRequest = QtCore.Signal(QtGui.QStandardItem) # @TODO: check backward compatibility
# @TODO: check
#self.emit(QtCore.SIGNAL(
# 'histogramComputationRequest(QtGui.QStandardItem, int, int, int)'),
# band, hmin, nmax, nbuckets)
#: SIGNAL: it is emitted when overview computation is required
#:
#: :C++ signature: `void overviewComputationRequest()`
overviewComputationRequest = QtCore.Signal(QtGui.QStandardItem)
def __init__(self, band=None, parent=None, flags=QtCore.Qt.WindowFlags(0),
**kwargs):
super(BandInfoDialog, self).__init__(band, parent, flags, **kwargs)
# Set tab icons
geticon = qt4support.geticon
self.tabWidget.setTabIcon(0, geticon('info.svg', 'gsdview'))
self.tabWidget.setTabIcon(1, geticon('metadata.svg', __name__))
self.tabWidget.setTabIcon(2, geticon('statistics.svg', __name__))
self.tabWidget.setTabIcon(3, geticon('color.svg', __name__))
# Overview page
#: overview widget
self.overviewWidget = SpecialOverviewWidget(parent=self)
self.overviewWidget.addLevelButton.setIcon(geticon('add.svg',
'gsdview'))
self.tabWidget.addTab(self.overviewWidget,
geticon('overview.svg', __name__),
self.tr('Overviews'))
# Context menu actions
qt4support.setViewContextActions(self.histogramTableWidget)
qt4support.setViewContextActions(self.colorTableWidget)
if not hasattr(gdal.Band, 'GetDefaultHistogram'):
self.histogramGroupBox.hide()
self.statisticsVerticalLayout.addStretch()
# @TODO: remove.
# Tempoorary disable the button for custom histogram configuration
self.customHistogramCheckBox.setEnabled(False)
# Tabs
if band:
self._connect_signals()
self.update()
def _connect_signals(self):
# @TODO: check
self.computeStatsButton.clicked.connect(self._computeStats)
self.approxStatsCheckBox.toggled.connect(
self.computeStatsButton.setEnabled)
# @TODO: check
self.computeHistogramButton.clicked.connect(self._computeHistogram)
self.customHistogramCheckBox.toggled.connect(
self.computeHistogramButton.setEnabled)
self.overviewWidget.overviewComputationRequest.connect(
self._computeOverviews)
def _disconnect_signals(self):
# @TODO: check
self.computeStatsButton.clicked.disconnect(self._computeStats)
self.approxStatsCheckBox.toggled.disconnect(
self.computeStatsButton.setEnabled)
# @TODO: check
self.computeHistogramButton.clicked.dosconnect(self._computeHistogram)
self.customHistogramCheckBox.toggled.disconnect(
self.computeHistogramButton.setEnabled)
self.overviewWidget.overviewComputationRequest.disconnect(
self._computeOverviews)
@property
[docs] def band(self):
return self._obj
[docs] def setBand(self, band):
self._obj = band
if band is not None:
# @TODO: check type
self._connect_signals()
self.update()
else:
self._disconnect_signals()
self.reset()
[docs] def reset(self):
super(BandInfoDialog, self).reset()
self.resetInfoTab()
self.resetStatistics()
self.resetHistogram()
self.resetColorTable()
self.overviewWidget.reset()
[docs] def update(self):
super(BandInfoDialog, self).update()
self.updateInfoTab()
self.updateStatistics()
self.updateHistogram()
self.updateColorTable()
self.updateOverviewInfo()
[docs] def resetImageStructure(self):
_setupImageStructureInfo(self, {})
[docs] def setImageStructure(self, metadata):
if metadata is None:
self.resetImageStructure()
_setupImageStructureInfo(self, metadata)
[docs] def updateImageStructure(self):
if self.band is not None:
metadata = self.band.GetMetadata('IMAGE_STRUCTURE')
else:
metadata = {}
self.setImageStructure(metadata)
[docs] def resetInfoTab(self):
# Info
self.descriptionValue.setText('')
self.bandNumberValue.setText('')
self.colorInterpretationValue.setText('')
self.overviewCountValue.setText('0')
self.hasArbitraryOverviewsValue.setText('')
# @TODO: checksum
#~ band.Checksum ??
# Data
self.xSizeValue.setText('0')
self.ySizeValue.setText('0')
self.blockSizeValue.setText('0')
self.noDataValue.setText('')
self.dataTypeValue.setText('')
self.unitTypeValue.setText('')
self.offsetValue.setText('0')
self.scaleValue.setText('1')
self.resetImageStructure()
[docs] def updateInfoTab(self):
if self.band is None:
self.resetInfoTab()
return
band = self.band
# Color interpretaion
colorint = band.GetRasterColorInterpretation()
colorint = gdal.GetColorInterpretationName(colorint)
# Info
self.descriptionValue.setText(band.GetDescription().strip())
# @COMPATIBILITY: GetBand requires GDAL >= 1.7
try:
bandno = band.GetBand()
except AttributeError:
bandno = self.tr('Unknown')
self.bandNumberValue.setText(str(bandno))
self.colorInterpretationValue.setText(colorint)
self.overviewCountValue.setText(str(band.GetOverviewCount()))
# @COMPATIBILITY: HasArbitraryOverviews requires GDAL >= 1.7
if hasattr(gdal.Band, 'HasArbitraryOverviews'):
hasArbitaryOvr = band.HasArbitraryOverviews()
self.hasArbitraryOverviewsValue.setText(str(hasArbitaryOvr))
else:
self.hasArbitraryOverviewsValue.setText('')
# @TODO: checksum
#~ band.Checksum ??
# Data
self.xSizeValue.setText(str(band.XSize))
self.ySizeValue.setText(str(band.YSize))
self.blockSizeValue.setText(str(band.GetBlockSize()))
self.noDataValue.setText(str(band.GetNoDataValue()))
self.dataTypeValue.setText(gdal.GetDataTypeName(band.DataType))
# @COMPATIBILITY: GetUnitType requires GDAL >= 1.7
if hasattr(gdal.Band, 'GetUnitType'):
unitType = band.GetUnitType()
self.unitTypeValue.setText(str(unitType))
else:
self.unitTypeValue.setText('')
self.offsetValue.setText(str(band.GetOffset()))
self.scaleValue.setText(str(band.GetScale()))
self.updateImageStructure()
[docs] def resetStatistics(self):
'''Reset statistics.'''
value = self.tr('Not computed')
self.minimumValue.setText(value)
self.maximumValue.setText(value)
self.meanValue.setText(value)
self.stdValue.setText(value)
[docs] def setStatistics(self, vmin, vmax, mean, stddev):
self.minimumValue.setText(str(vmin))
self.maximumValue.setText(str(vmax))
self.meanValue.setText(str(mean))
self.stdValue.setText(str(stddev))
@QtCore.Slot()
@qt4support.overrideCursor
[docs] def updateStatistics(self):
if self.band is None:
self.resetStatistics()
return
# @NOTE: the band.GetStatistics method called with the second argument
# set to False (no image rescanning) has been fixed in
# r19666_ (1.6 branch) and r19665_ (1.7 branch)
# see `ticket #3572` on `GDAL Trac`_.
#
# .. _r19666: http://trac.osgeo.org/gdal/changeset/19666
# .. _r19665: http://trac.osgeo.org/gdal/changeset/19665
# .. _`ticket #3572`: http://trac.osgeo.org/gdal/ticket/3572
# .. _`GDAL Trac`: http://trac.osgeo.org/gdal
if gdalsupport.hasFastStats(self.band):
vmin, vmax, mean, stddev = self.band.GetStatistics(True, True)
self.setStatistics(vmin, vmax, mean, stddev)
self.computeStatsButton.setEnabled(False)
else:
self.resetStatistics()
[docs] def resetHistogram(self):
tablewidget = self.histogramTableWidget
self.numberOfClassesValue.setText('0')
qt4support.clearTable(tablewidget)
[docs] def setHistogram(self, vmin, vmax, nbuckets, hist):
self.numberOfClassesValue.setText(str(nbuckets))
w = (vmax - vmin) / nbuckets
tablewidget = self.histogramTableWidget
tablewidget.setRowCount(nbuckets)
for row in range(nbuckets):
start = vmin + row * w
stop = start + w
tablewidget.setItem(row, 0,
QtGui.QTableWidgetItem(str(start)))
tablewidget.setItem(row, 1,
QtGui.QTableWidgetItem(str(stop)))
tablewidget.setItem(row, 2,
QtGui.QTableWidgetItem(str(hist[row])))
# @TODO: plotting
[docs] def updateHistogram(self):
if self.band is None:
self.resetHistogram()
if gdal.DataTypeIsComplex(self.band.DataType):
self.computeHistogramButton.setEnabled(False)
return
else:
self.computeHistogramButton.setEnabled(True)
if gdal.VersionInfo() < '1700':
# @TODO: check
if self.computeHistogramButton.isEnabled() == False:
# Histogram already computed
hist = self.band.GetDefaultHistogram()
else:
hist = None
else:
# @WARNING: causes a crash in GDAL < 1.7.0 (r18405)
# @SEEALSO: http://trac.osgeo.org/gdal/ticket/3304
hist = self.band.GetDefaultHistogram(force=False)
if hist:
self.setHistogram(*hist)
self.computeHistogramButton.setEnabled(False)
else:
self.resetHistogram()
@staticmethod
def _rgb2qcolor(red, green, blue, alpha=255):
qcolor = QtGui.QColor()
qcolor.setRgb(red, green, blue, alpha)
return qcolor
@staticmethod
def _gray2qcolor(gray):
qcolor = QtGui.QColor()
qcolor.setRgb(gray, gray, gray)
return qcolor
@staticmethod
def _cmyk2qcolor(cyan, magenta, yellow, black=255):
qcolor = QtGui.QColor()
qcolor.setCmyk(cyan, magenta, yellow, black)
return qcolor
@staticmethod
def _hsv2qcolor(hue, lightness, saturation, a=0):
qcolor = QtGui.QColor()
qcolor.setHsv(hue, lightness, saturation, a)
return qcolor
[docs] def resetColorTable(self):
self.ctInterpretationValue.setText('')
self.colorsNumberValue.setText('')
qt4support.clearTable(self.colorTableWidget)
[docs] def setColorTable(self, colortable):
if colortable is None:
self.resetColorTable()
return
ncolors = colortable.GetCount()
colorint = colortable.GetPaletteInterpretation()
label = gdalsupport.colorinterpretations[colorint]['label']
self.ctInterpretationValue.setText(label)
self.colorsNumberValue.setText(str(ncolors))
mapping = gdalsupport.colorinterpretations[colorint]['inverse']
labels = [mapping[k] for k in sorted(mapping.keys())]
labels.append('Color')
tablewidget = self.colorTableWidget
tablewidget.setRowCount(ncolors)
tablewidget.setColumnCount(len(labels))
tablewidget.setHorizontalHeaderLabels(labels)
tablewidget.setVerticalHeaderLabels([str(i) for i in range(ncolors)])
colors = gdalsupport.colortable2numpy(colortable)
if colorint == gdal.GPI_RGB:
func = BandInfoDialog._rgb2qcolor
elif colorint == gdal.GPI_Gray:
func = BandInfoDialog._gray2qcolor
elif colorint == gdal.GPI_CMYK:
func = BandInfoDialog._cmyk2qcolor
elif colorint == gdal.GPI_HLS:
func = BandInfoDialog._hsv2qcolor
else:
raise ValueError('invalid color intepretatin: "%s"' % colorint)
brush = QtGui.QBrush()
brush.setStyle(QtCore.Qt.SolidPattern)
for row, color in enumerate(colors):
for chan, value in enumerate(color):
tablewidget.setItem(row, chan,
QtGui.QTableWidgetItem(str(value)))
qcolor = func(*color)
brush.setColor(qcolor)
item = QtGui.QTableWidgetItem()
item.setBackground(brush)
tablewidget.setItem(row, chan + 1, item)
hheader = tablewidget.horizontalHeader()
hheader.resizeSections(QtGui.QHeaderView.ResizeToContents)
[docs] def updateColorTable(self):
if self.band is None:
self.resetColorTable()
else:
colortable = self.band.GetRasterColorTable()
if colortable is None:
# Disable the color table tab
self.tabWidget.setTabEnabled(3, False)
else:
self.tabWidget.setTabEnabled(3, True)
self.setColorTable(colortable)
[docs] def updateOverviewInfo(self):
if self.band is not None:
self.overviewWidget.setItem(self.band)
else:
self.overviewWidget.reset()
# @TODO: check
@QtCore.Slot()
def _computeStats(self):
self._checkgdalobj()
self.statsComputationRequest.emit(self.band)
#~ logging.info('start statistics computation')
#~ band = self.band
#~ approx = self.approxStatsCheckBox.isChecked()
#~ band.ComputeStatistics(approx)#, callback=None, callback_data=None)
#~ # @COMPATIBILITY: workaround fo flagging statistics as computed
#~ # @SEALSO: ticket #3572 on GDAL Trac
#~ stats = band.GetStatistics(True, True)
#~ for name, value in zip(gdalsupport.GDAL_STATS_KEYS, stats):
#~ band.SetMetadataItem(name, str(value))
#~ # @TODO: check
#~ #if self.domainComboBox.currentText() == '':
#~ # self.updateMetadata()
#~ logging.debug('statistics computation completed')
#~ self.updateStatistics()
# @TODO: check
@QtCore.Slot()
def _computeHistogram(self):
self._checkgdalobj()
self.histogramComputationRequest.emit(self.band)
#~ band = self.band
#~ approx = self.approxStatsCheckBox.isChecked()
#~ if self.customHistogramCheckBox.isChecked():
#~ dialog = HistogramConfigDialog(self)
#~ # @COMPATIBILITY: bug in GDAL 1.6.x line
#~ # @WARNING: causes a crash in GDAL < 1.6.4 (r18405)
#~ # @SEEALSO: http://trac.osgeo.org/gdal/ticket/3304
#~ if gdal.VersionInfo() < '1640':
#~ dialog.approxCheckBox.setChecked(True)
#~ dialog.approxCheckBox.setEnabled(False)
#~ from osgeo.gdal_array import GDALTypeCodeToNumericTypeCode
#~ try:
#~ dtype = GDALTypeCodeToNumericTypeCode(band.DataType)
#~ except KeyError:
#~ pass
#~ else:
#~ dialog.setLimits(dtype)
#~ tablewidget = self.histogramTableWidget
#~ if tablewidget.rowCount() > 0:
#~ item = tablewidget.item(0, 0)
#~ vmin = float(item.text())
#~ item = tablewidget.item(tablewidget.rowCount() - 1 , 1)
#~ vmax = float(item.text())
#~ dialog.minSpinBox.setValue(vmin)
#~ dialog.maxSpinBox.setValue(vmax)
#~ dialog.nBucketsSpinBox.setValue(tablewidget.rowCount())
#~ done = False
#~ while not done:
#~ ret = dialog.exec_()
#~ if ret == QtGui.QDialog.Rejected:
#~ return
#~ if dialog.validate() is False:
#~ msg = self.tr('The histogram minimum have been set to a '
#~ 'value that is greater or equal of the '
#~ 'histogram maximum.\n'
#~ 'Please fix it.')
#~ QtGui.QMessageBox.warning(self, self.tr('WARNING!'), msg)
#~ else:
#~ done = True
#~ vmin = dialog.minSpinBox.value()
#~ vmax = dialog.maxSpinBox.value()
#~ nbuckets = dialog.nBucketsSpinBox.value()
#~ include_out_of_range = dialog.outOfRangeCheckBox.isChecked()
#~ approx = dialog.approxCheckBox.isChecked()
#~ # @TODO: use callback for progress reporting
#~ hist = qt4support.callExpensiveFunc(
#~ band.GetHistogram,
#~ vmin, vmax, nbuckets,
#~ include_out_of_range, approx)
#~ #callback=None, callback_data=None)
#~ else:
#~ # @TODO: use callback for progress reporting
#~ hist = qt4support.callExpensiveFunc(band.GetDefaultHistogram)
#~ #callback=None,
#~ #callback_data=None)
#~ vmin, vmax, nbuckets, hist = hist
#~ self.computeHistogramButton.setEnabled(False)
#~ self.setHistogram(vmin, vmax, nbuckets, hist)
#~ self.updateStatistics() # @TODO: check
@QtCore.Slot()
def _computeOverviews(self):
self._checkgdalobj()
self.overviewComputationRequest.emit(self.band)
DatasetInfoDialogBase = qt4support.getuiform('datasetdialog', __name__)
[docs]class DatasetInfoDialog(MajorObjectInfoDialog, DatasetInfoDialogBase):
def __init__(self, dataset=None, parent=None,
flags=QtCore.Qt.WindowFlags(0), **kwargs):
super(DatasetInfoDialog, self).__init__(dataset, parent, flags,
**kwargs)
# Set icons
geticon = qt4support.geticon
self.tabWidget.setTabIcon(0, geticon('info.svg', 'gsdview'))
self.tabWidget.setTabIcon(1, geticon('metadata.svg', __name__))
self.tabWidget.setTabIcon(2, geticon('gcp.svg', __name__))
self.tabWidget.setTabIcon(3, geticon('driver.svg', __name__))
self.tabWidget.setTabIcon(4, geticon('multiple-documents.svg',
__name__))
self.driverMetadataWidget = MetadataWidget(self)
self.driverMetadataWidget.enableDomains(False)
self.driverMetadataWidget.enableButtons(False)
layout = self.driverMetadataGroupBox.layout()
layout.addWidget(self.driverMetadataWidget)
# Context menu actions
qt4support.setViewContextActions(self.gcpsTableWidget)
qt4support.setViewContextActions(self.fileListWidget)
if not hasattr(gdal.Dataset, 'GetFileList'):
self.tabWidget.setTabEnabled(4, False)
# Setup Tabs
if dataset:
self.update()
@property
[docs] def dataset(self):
return self._obj
[docs] def setDataset(self, dataset):
self._obj = dataset
if dataset is not None:
# @TODO: check type
self.update()
else:
self.reset()
[docs] def reset(self):
super(DatasetInfoDialog, self).reset()
self.resetInfoTab()
self.resetDriverTab()
self.resetGCPs()
self.resetFilesTab()
[docs] def update(self):
super(DatasetInfoDialog, self).update()
self.updateInfoTab()
self.updateDriverTab()
self.updateGCPs()
self.updateFilesTab()
[docs] def resetImageStructure(self):
_setupImageStructureInfo(self, {})
[docs] def setImageStructure(self, metadata):
if metadata is None:
self.resetImageStructure()
_setupImageStructureInfo(self, metadata)
[docs] def updateImageStructure(self):
if self.dataset is not None:
metadata = self.dataset.GetMetadata('IMAGE_STRUCTURE')
else:
metadata = {}
self.setImageStructure(metadata)
[docs] def resetInfoTab(self):
self.descriptionValue.setText('')
self.rasterCountValue.setText('0')
self.xSizeValue.setText('0')
self.ySizeValue.setText('0')
self.projectionValue.setText('')
self.projectionRefValue.setText('')
self.resetImageStructure()
self.xOffsetValue.setText('0')
self.yOffsetValue.setText('0')
self.a11Value.setText('1')
self.a12Value.setText('0')
self.a21Value.setText('0')
self.a22Value.setText('1')
[docs] def updateInfoTab(self):
if self.dataset is None:
self.resetInfoTab()
return
dataset = self.dataset
description = os.path.basename(dataset.GetDescription())
self.descriptionValue.setText(description)
self.descriptionValue.setCursorPosition(0)
self.rasterCountValue.setText(str(dataset.RasterCount))
self.xSizeValue.setText(str(dataset.RasterXSize))
self.ySizeValue.setText(str(dataset.RasterYSize))
self.projectionValue.setText(dataset.GetProjection())
self.projectionRefValue.setText(dataset.GetProjectionRef())
self.updateImageStructure()
xoffset, a11, a12, yoffset, a21, a22 = dataset.GetGeoTransform()
self.xOffsetValue.setText(str(xoffset))
self.yOffsetValue.setText(str(yoffset))
self.a11Value.setText(str(a11))
self.a12Value.setText(str(a12))
self.a21Value.setText(str(a21))
self.a22Value.setText(str(a22))
[docs] def resetDriverTab(self):
self.driverShortNameValue.setText('')
self.driverLongNameValue.setText('')
self.driverDescriptionValue.setText('')
self.driverHelpTopicValue.setText('')
self.driverMetadataWidget.resetMetadata()
[docs] def setDriverTab(self, driver):
self.resetDriverTab()
self.driverShortNameValue.setText(driver.ShortName)
self.driverLongNameValue.setText(driver.LongName)
self.driverDescriptionValue.setText(driver.GetDescription())
if driver.HelpTopic:
link = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.gdal.org/%s"><span style=" text-decoration: underline; color:#0000ff;">%s</span></a></p></body></html>''' % (driver.HelpTopic, driver.HelpTopic)
else:
link = str(driver.HelpTopic)
self.driverHelpTopicValue.setText(link)
metadatalist = driver.GetMetadata_List()
if metadatalist:
self.driverMetadataWidget.setMetadata(metadatalist)
[docs] def updateDriverTab(self):
if self.dataset is None:
self.resetDriverTab()
return
driver = self.dataset.GetDriver()
self.setDriverTab(driver)
[docs] def resetGCPs(self):
tablewidget = self.gcpsTableWidget
qt4support.clearTable(tablewidget)
self.gcpsNumValue.setText('')
self.gcpsProjectionValue.setText('')
[docs] def setGCPs(self, gcplist, projection):
self.resetGCPs()
tablewidget = self.gcpsTableWidget
self.gcpsProjectionValue.setText(projection)
self.gcpsNumValue.setText(str(len(gcplist)))
tablewidget.setRowCount(len(gcplist))
tablewidget.setVerticalHeaderLabels([str(gcp.Id) for gcp in gcplist])
sortingenabled = tablewidget.isSortingEnabled()
tablewidget.setSortingEnabled(False)
Item = QtGui.QTableWidgetItem
for row, gcp in enumerate(gcplist):
tablewidget.setItem(row, 0, Item(str(gcp.GCPPixel)))
tablewidget.setItem(row, 1, Item(str(gcp.GCPLine)))
tablewidget.setItem(row, 2, Item(str(gcp.GCPX)))
tablewidget.setItem(row, 3, Item(str(gcp.GCPY)))
tablewidget.setItem(row, 4, Item(str(gcp.GCPZ)))
tablewidget.setItem(row, 5, Item(gcp.Info))
#~ item.setToolTip(1, gcp.Info)
# Fix table header behaviour
tablewidget.setSortingEnabled(sortingenabled)
[docs] def updateGCPs(self):
if self.dataset is None:
self.resetGCPs()
else:
# It seems there is a bug in GDAL that causes incorrect GCPs
# handling when a subdatast is opened (a dataset is aready open)
# @TODO: check and, if the case, file a ticket on
# http://www.gdal.org
#self.setGCPs(dataset.GetGCPs(), dataset.GetGCPProjection())
try:
gcplist = self.dataset.GetGCPs()
except SystemError:
logging.debug('unable to read GCPs from dataset %s' %
self.dataset.GetDescription())
#, exc_info=True)
else:
if not gcplist:
# Disable the GCPs tab
self.tabWidget.setTabEnabled(2, False)
else:
self.tabWidget.setTabEnabled(2, True)
self.setGCPs(gcplist, self.dataset.GetGCPProjection())
[docs] def resetFilesTab(self):
#qt4support.clearTable(self.fileListWidget) # @TODO: check
self.fileListWidget.clear()
[docs] def setFiles(self, files):
self.tabWidget.setTabEnabled(4, True)
for filename in files:
self.fileListWidget.addItem(filename)
[docs] def updateFilesTab(self):
if self.dataset is not None:
self.tabWidget.setTabEnabled(4, True)
self.setFiles(self.dataset.GetFileList())
else:
self.resetFilesTab()
self.tabWidget.setTabEnabled(4, False)
#~ class SubDatasetInfoDialog(DatasetInfoDialog):
#~ def __init__(self, subdataset, parent=None,
#~ flags=QtCore.Qt.WindowFlags(0)):
#~ assert dataset, 'a valid GDAL dataset expected'
#~ DatasetInfoDialog.__init__(self, subdataset, parent, flags)