"""
Module that contains the LayerWindow class
"""
# 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.
from PyQt5.QtGui import QIcon, QColor
from PyQt5.QtWidgets import QDockWidget, QListView, QMenu, QAbstractItemView
from PyQt5.QtWidgets import QAction, QInputDialog, QColorDialog
from PyQt5.QtCore import QAbstractListModel, Qt, pyqtSignal
from . import viewerlayers
from . import stretchdialog
from . import propertieswindow
from .viewerstrings import MESSAGE_TITLE
[docs]class LayerItemModel(QAbstractListModel):
"""
This class provides the data to the list view by
reading the list of layers provided by the LayerManager
"""
def __init__(self, viewwidget, viewwindow, parent):
QAbstractListModel.__init__(self, parent)
self.viewwidget = viewwidget
self.viewwindow = viewwindow
self.rasterIcon = QIcon(":/viewer/images/rasterlayer.png")
self.vectorIcon = QIcon(":/viewer/images/vectorlayer.png")
[docs] def rowCount(self, parent):
"Just the number of layers"
return len(self.viewwidget.layers.layers)
[docs] def flags(self, index):
"Have to override to make it checkable"
f = QAbstractListModel.flags(self, index)
return f | Qt.ItemIsUserCheckable
[docs] def getLayer(self, index):
"""
Because we are showing the layers in the opposite
order (last layer is top) we have a helper function
to get the right layer
"""
row = index.row()
layerNum = len(self.viewwidget.layers.layers) - row - 1
return self.viewwidget.layers.layers[layerNum]
[docs] def data(self, index, role):
"""
Get the data associated with an item
"""
if not index.isValid():
return None
layer = self.getLayer(index)
if role == Qt.DisplayRole:
# name
return layer.title
elif role == Qt.DecorationRole:
# icon
layer = self.getLayer(index)
if isinstance(layer, viewerlayers.ViewerRasterLayer):
return self.rasterIcon
else:
return self.vectorIcon
elif role == Qt.CheckStateRole:
# whether displayed or not
if layer.displayed:
return Qt.Checked
else:
return Qt.Unchecked
else:
return None
[docs] def setData(self, index, value, role):
"""
Set the data back. Only bother with CheckStateRole
"""
if role == Qt.CheckStateRole:
state = value
layer = self.getLayer(index)
state = state == Qt.Checked
self.viewwidget.layers.setDisplayedState(layer, state)
# redraw
self.viewwidget.viewport().update()
return True
return False
[docs]class LayerListView(QListView):
"""
Our own QListView derived class so we can handle the context menu event
"""
def __init__(self):
QListView.__init__(self)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setupActions()
self.setupMenu()
[docs] def setupActions(self):
"Set up the actions for the popup menu"
self.layerExtentAct = QAction(self, triggered=self.zoomLayer)
self.layerExtentAct.setText("&Zoom to Layer Extent")
self.layerExtentAct.setStatusTip("Zoom to Layer Extent")
self.layerExtentAct.setIcon(QIcon(":/viewer/images/zoomlayer.png"))
self.layerExtentAct.setIconVisibleInMenu(True)
self.removeLayerAct = QAction(self, triggered=self.removeLayers)
self.removeLayerAct.setText("&Remove Selected Layer(s)")
self.removeLayerAct.setStatusTip("Remove selected layer(s)")
self.removeLayerAct.setIcon(QIcon(":/viewer/images/removelayer.png"))
self.removeLayerAct.setIconVisibleInMenu(True)
self.moveUpAct = QAction(self, triggered=self.moveUp)
self.moveUpAct.setText("Move &Up")
self.moveUpAct.setStatusTip("Move selected layer up in list")
self.moveUpAct.setIcon(QIcon(":/viewer/images/arrowup.png"))
self.moveUpAct.setIconVisibleInMenu(True)
self.moveDownAct = QAction(self, triggered=self.moveDown)
self.moveDownAct.setText("Move &Down")
self.moveDownAct.setStatusTip("Move selected layer down in list")
self.moveDownAct.setIcon(QIcon(":/viewer/images/arrowdown.png"))
self.moveDownAct.setIconVisibleInMenu(True)
self.moveToTopAct = QAction(self, triggered=self.moveToTop)
self.moveToTopAct.setText("Move To &Top")
self.moveToTopAct.setStatusTip("Move selected layer top top")
self.changeColorAct = QAction(self, triggered=self.changeColor)
self.changeColorAct.setText("Change &Color")
self.changeColorAct.setStatusTip("Change color of vector layer")
self.setSQLAct = QAction(self, triggered=self.setSQL)
self.setSQLAct.setText("Set &attribute filter")
self.setSQLAct.setStatusTip("Set attribute filter via SQL")
self.setLineWidthAct = QAction(self, triggered=self.setLineWidth)
self.setLineWidthAct.setText("Set &Line width")
self.setLineWidthAct.setStatusTip("Set line width of rendered features")
self.setPointCrossSizeAct = QAction(self, triggered=self.setPointCrossSize)
self.setPointCrossSizeAct.setText("Set P&oint cross size")
self.setPointCrossSizeAct.setStatusTip("Set the size of the point crosshair")
self.setFillAct = QAction(self, toggled=self.toggleFill)
self.setFillAct.setText("Fill Polygons")
self.setFillAct.setStatusTip("Toggle the fill status of polygons")
self.setFillAct.setCheckable(True)
self.setLabelAct = QAction(self, triggered=self.setLabel)
self.setLabelAct.setText("Set Label")
self.setLabelAct.setStatusTip("Set the attribute to show as a label")
self.setLabelColorAct = QAction(self, triggered=self.setLabelColor)
self.setLabelColorAct.setText("Set Label Color")
self.setLabelColorAct.setStatusTip("Set the color of the labels")
self.editStretchAct = QAction(self, triggered=self.editStretch)
self.editStretchAct.setText("&Edit Stretch")
self.editStretchAct.setStatusTip("Edit Stretch of raster layer")
self.propertiesAct = QAction(self, triggered=self.properties)
self.propertiesAct.setText("&Properties")
self.propertiesAct.setStatusTip("Show properties of file")
self.propertiesAct.setIcon(QIcon(":/viewer/images/properties.png"))
self.propertiesAct.setIconVisibleInMenu(True)
[docs] def zoomLayer(self):
"zoom to the extents of the selected layer"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
extent = layer.coordmgr.getFullWorldExtent()
layer.coordmgr.setWorldExtent(extent)
model.viewwidget.layers.makeLayersConsistent(layer)
model.viewwidget.layers.updateImages()
model.viewwidget.viewport().update()
[docs] def removeLayers(self):
"remove the selected layer"
try:
selected = self.selectedIndexes()
except AttributeError:
# Layer windows is not open
selected = []
if len(selected) > 0:
model = self.model()
layers = [model.getLayer(layer) for layer in selected]
model.viewwidget.removeLayers(layers)
[docs] def moveUp(self):
"Move the selected layer up in order"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
model.viewwidget.layers.moveLayerUp(layer)
model.viewwidget.viewport().update()
[docs] def moveDown(self):
"Move the selected layer down in order"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
model.viewwidget.layers.moveLayerDown(layer)
model.viewwidget.viewport().update()
[docs] def moveToTop(self):
"Move the selected layer to the top"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
model.viewwidget.layers.moveLayerToTop(layer)
model.viewwidget.viewport().update()
[docs] def changeColor(self):
"Change the color of the vector layer"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
rgba = layer.getColorAsRGBATuple()
init = QColor(rgba[0], rgba[1], rgba[2], rgba[3])
newCol = QColorDialog.getColor(init, self, "Choose Layer Color",
QColorDialog.ShowAlphaChannel)
if newCol.isValid():
rgba = (newCol.red(), newCol.green(), newCol.blue(),
newCol.alpha())
layer.updateColor(rgba)
model.viewwidget.viewport().update()
[docs] def setSQL(self):
"Set the attribute filter for vector layers"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
oldsql = ""
if layer.hasSQL():
oldsql = layer.getSQL()
sql, ok = QInputDialog.getText(self, MESSAGE_TITLE,
"Enter SQL attribute filter", text=oldsql)
if ok:
if sql == "":
sql = None
else:
sql = str(sql)
layer.setSQL(sql)
layer.getImage()
model.viewwidget.viewport().update()
[docs] def setLineWidth(self):
"Set the line width for vector layers"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
linewidth = layer.getLineWidth()
linewidth, ok = QInputDialog.getInt(self, MESSAGE_TITLE,
"Enter line width", value=linewidth, min=0, max=100)
if ok:
layer.setLineWidth(linewidth)
layer.getImage()
model.viewwidget.viewport().update()
[docs] def setPointCrossSize(self):
"Set the cross hair size for points"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
crossHairSize = layer.getHalfCrossSize()
crossHairSize, ok = QInputDialog.getInt(self, MESSAGE_TITLE,
"Enter half crosshair size", value=crossHairSize, min=0, max=100)
if ok:
layer.setHalfCrossSize(crossHairSize)
layer.getImage()
model.viewwidget.viewport().update()
[docs] def toggleFill(self, state):
"toggle the fill state"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
layer.setFill(state)
layer.getImage()
model.viewwidget.viewport().update()
[docs] def setLabel(self):
"Set the label"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
all_attributes = layer.getAvailableAttributes()
attribute_list = ["<None>"]
attribute_list.extend(all_attributes)
idx = 0
field = layer.getFieldToLabel()
if field is not None:
idx = attribute_list.index(field)
att, ok = QInputDialog.getItem(self, MESSAGE_TITLE,
"Select Attribute", attribute_list, idx, False)
if ok:
if att == "<None>":
att = None
layer.setFieldToLabel(att)
layer.getImage() # need to redraw whole thing so old labels aren't seen
model.viewwidget.viewport().update()
[docs] def setLabelColor(self):
"Color for label"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
color = layer.getLabelColorAsRGBATuple()
init = QColor(color[0], color[1], color[2], color[3])
newCol = QColorDialog.getColor(init, self, "Choose Label Color",
QColorDialog.ShowAlphaChannel)
if newCol.isValid():
rgba = (newCol.red(), newCol.green(), newCol.blue(),
newCol.alpha())
layer.updateLabelColor(rgba)
model.viewwidget.viewport().update()
[docs] def editStretch(self):
"Edit the stretch for the layer"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
stretchDock = stretchdialog.StretchDockWidget(self,
model.viewwidget, layer)
model.viewwindow.addDockWidget(Qt.TopDockWidgetArea, stretchDock)
[docs] def properties(self):
"Show the properties for the layer"
selected = self.selectedIndexes()
if len(selected) > 0:
index = selected[0]
model = self.model()
layer = model.getLayer(index)
info = layer.getPropertiesInfo()
dlg = propertieswindow.PropertiesWindow(self, info)
dlg.setWindowTitle(layer.title)
dlg.show()
[docs]class LayerWindow(QDockWidget):
"""
Our dock window that shows the layers.
Contains a list view
"""
# signals
layerWindowClosed = pyqtSignal('QDockWidget', name='layerWindowClosed')
"signal emitted when window closed"
def __init__(self, parent, viewwidget):
QDockWidget.__init__(self, "Layers", parent)
self.viewwidget = viewwidget
self.parent = parent
# create the list view
self.listView = LayerListView()
# set our item model
model = LayerItemModel(viewwidget, parent, self)
self.listView.setModel(model)
self.setWidget(self.listView)
# connect so we get told when layers added and removed
viewwidget.layers.layersChanged.connect(self.layersChanged)
[docs] def layersChanged(self):
"""
Called when a layer has been added or removed to/from the LayerManager
"""
# the only way I can get the whole thing redrawn
# is to pretend I have a new model. Any suggestions?
model = LayerItemModel(self.viewwidget, self.parent, self)
self.listView.setModel(model)
[docs] def closeEvent(self, event):
"""
Window is being closed - inform parent window
"""
self.layerWindowClosed.emit(self)