Newer
Older
# -*- coding: utf-8 -*-
"""
/***************************************************************************

Benjamin Jakimow
committed
EO Time Series Viewer
-------------------
begin : 2015-08-20
git sha : $Format:%H$
copyright : (C) 2017 by HU-Berlin
email : benjamin.jakimow@geo.hu-berlin.de
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
"""
# noinspection PyPep8Naming
from eotimeseriesviewer import CursorLocationMapTool
from qgis.core import *
from qgis.gui import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtXml import QDomDocument
from .timeseries import TimeSeriesDate, SensorProxyLayer, SensorInstrument
from .externals.qps.crosshair.crosshair import CrosshairDialog, CrosshairStyle, CrosshairMapCanvasItem
from .externals.qps.maptools import *
from .labeling import quickLabelLayers, labelShortcutLayerClassificationSchemes, setQuickTSDLabelsForRegisteredLayers
from .externals.qps.classification.classificationscheme import ClassificationScheme, ClassInfo
from .externals.qps.utils import *
from .externals.qps.layerproperties import showLayerPropertiesDialog
import eotimeseriesviewer.settings
PROGRESS_TIMER = QTimer()
PROGRESS_TIMER.start(100)
def toQgsMimeDataUtilsUri(mapLayer:QgsMapLayer):
uri = QgsMimeDataUtils.Uri()
uri.name = mapLayer.name()
uri.providerKey = mapLayer.dataProvider().name()
uri.uri = mapLayer.source()
if isinstance(mapLayer, QgsRasterLayer):
uri.layerType = 'raster'
elif isinstance(mapLayer, QgsVectorLayer):
uri.layerType = 'vector'
else:
raise NotImplementedError()
return uri
class MapLoadingInfoItem(QgsMapCanvasItem):
def __init__(self, mapCanvas):
assert isinstance(mapCanvas, QgsMapCanvas)
super(MapLoadingInfoItem, self).__init__(mapCanvas)
self.mCanvas = mapCanvas
self.mProgressConnection = None
self.mCanvas.renderStarting.connect(lambda: self.showLoadingProgress(True))
#self.mCanvas.renderComplete.connect(lambda: self.showLoadingProgress(False))
PROGRESS_TIMER.timeout.connect(self.onProgressTimeOut)
self.mShowProgress = False
self.mIsVisible = True
def showLoadingProgress(self, showProgress: bool):
self.mShowProgress = showProgress
self.update()
def onProgressTimeOut(self):
if self.mShowProgress:
self.mCanvas.update()
def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
"""
Paints the crosshair
:param painter:
:param QStyleOptionGraphicsItem:
:param QWidget_widget:
:return:
"""
if True:
options = QStyleOptionProgressBar()
options.rect = QRect(0, 0, painter.window().width(), 25)
options.textAlignment = Qt.AlignCenter
options.progress = 0
options.maximum = 0
options.minimum = 0
QApplication.style().drawControl(QStyle.CE_ProgressBar, options, painter)
class MapCanvasInfoItem(QgsMapCanvasItem):

Benjamin Jakimow
committed
"""
A QgsMapCanvasItem to show text
"""
def __init__(self, mapCanvas):
assert isinstance(mapCanvas, QgsMapCanvas)
super(MapCanvasInfoItem, self).__init__(mapCanvas)
self.mCanvas = mapCanvas

Benjamin Jakimow
committed
self.mText = dict()
self.mWrapChar = '\n'
self.mTextFormat = QgsTextFormat()
self.mTextFormat.setSizeUnit(QgsUnitTypes.RenderPixels)
self.mTextFormat.setFont(QFont('Helvetica', pointSize=10))
self.mTextFormat.setColor(QColor('yellow'))
def setWrapChar(self, c:str)->str:
"""
Sets a Wrap Character
:param c:
:return:
"""
self.mWrapChar = c
return self.wrapChar()

Benjamin Jakimow
committed
def wrapChar(self)->str:
return self.mWrapChar

Benjamin Jakimow
committed
def setText(self, text:str, alignment:Qt.Alignment=Qt.AlignTop | Qt.AlignHCenter):

Benjamin Jakimow
committed
self.mText[alignment] = text
def setTextFormat(self, format:QgsTextFormat):
assert isinstance(format, QgsTextFormat)
self.mTextFormat = format
self.updateCanvas()
def textFormat(self)->QgsTextFormat:
"""
Returns the text format.
:return: QgsTextFormat
"""
return self.mTextFormat
def font(self)->QFont:
"""
Returns the font used to write text on the map canvas.
:return: QFont
"""
return self.mTextFormat.font()
def setFont(self, font:QFont):
self.mTextFormat.setFont(font)
"""
Sets the map info color
:param color: QColor
"""

Benjamin Jakimow
committed
self.mTextFormat.setColor(color)
"""
Returns the info text color
:return: QColor
"""

Benjamin Jakimow
committed
return self.mTextFormat.color()

Benjamin Jakimow
committed
def paintText(self, painter, text:str, flags, rotation=0):
padding = 5
text = text.replace('\\n', '\n')
text = text.split(self.wrapChar())

Benjamin Jakimow
committed
nl = len(text)
#text = text.split('\\n')
r = QgsTextRenderer()

Benjamin Jakimow
committed
painter.setBrush(Qt.NoBrush)
painter.setPen(Qt.NoPen)
painter.setRenderHint(QPainter.Antialiasing)

Benjamin Jakimow
committed
context = QgsRenderContext()

Benjamin Jakimow
committed
# taken from QGIS Repo src/core/qgspallabeling.cpp
m2p = QgsMapToPixel(1, 0, 0, 0, 0, 0)
context.setMapToPixel(m2p)
context.setScaleFactor(QgsApplication.desktop().logicalDpiX() / 25.4)
context.setUseAdvancedEffects(True)
context.setPainter(painter)
#context.setExtent(self.mCanvas.extent())
#context.setExpressionContext(self.mCanvas.mapSettings().expressionContext())

Benjamin Jakimow
committed
vp = QRectF(painter.viewport())
#rect = self.mCanvas.extent().toRectF()

Benjamin Jakimow
committed
textFormat = self.mTextFormat
assert isinstance(textFormat, QgsTextFormat)
th = r.textHeight(context, textFormat, text, QgsTextRenderer.Rect)
tw = r.textWidth(context, textFormat, text)

Benjamin Jakimow
committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# area to place the text inside
rect = QRectF()
x = 0.5*vp.width()
y = 0.5*vp.height()
hAlign = QgsTextRenderer.AlignCenter
# horizontal position
if bool(flags & Qt.AlignLeft):
x = padding
hAlign = QgsTextRenderer.AlignLeft
elif bool(flags & Qt.AlignHCenter):
x = 0.5 * vp.width()
hAlign = QgsTextRenderer.AlignCenter
elif bool(flags & Qt.AlignRight):
x = vp.width() - padding
hAlign = QgsTextRenderer.AlignRight
# vertical position
if bool(flags & Qt.AlignTop):
y = padding + th - 0.5* (th / nl)
elif bool(flags & Qt.AlignVCenter):
y = 0.5 * (vp.height() + th)
elif bool(flags & Qt.AlignBottom):
y = vp.height() - padding #- th
poo = QPointF(x, y)
r.drawText(poo, rotation, hAlign, text, context, textFormat)
def setUpperLeft(self, text:str):
self.setText(text, Qt.AlignTop | Qt.AlignLeft)
def setMiddleLeft(self, text: str):
self.setText(text, Qt.AlignVCenter | Qt.AlignLeft)
def setLowerLeft(self, text: str):
self.setText(text, Qt.AlignBottom | Qt.AlignLeft)
def setUpperCenter(self, text:str):
self.setText(text, Qt.AlignTop | Qt.AlignHCenter)
def setMiddleCenter(self, text: str):
self.setText(text, Qt.AlignVCenter | Qt.AlignHCenter)
def setLowerCenter(self, text: str):
self.setText(text, Qt.AlignBottom | Qt.AlignHCenter)
def setUpperRight(self, text:str):
self.setText(text, Qt.AlignTop | Qt.AlignRight)
def setMiddleRight(self, text: str):
self.setText(text, Qt.AlignVCenter | Qt.AlignRight)
def setLowerRight(self, text: str):
self.setText(text, Qt.AlignBottom | Qt.AlignRight)
def clearText(self):
self.mText.clear()
def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
"""
Paints the crosshair
:param painter:
:param QStyleOptionGraphicsItem:
:param QWidget_widget:
:return:
"""

Benjamin Jakimow
committed
for alignment, text in self.mText.items():
if isinstance(text, str) and len(text) > 0:
self.paintText(painter, text, alignment)
class MapCanvasMapTools(QObject):
def __init__(self, canvas:QgsMapCanvas, cadDock:QgsAdvancedDigitizingDockWidget):
super(MapCanvasMapTools, self).__init__(canvas)
self.mCanvas = canvas
self.mCadDock = cadDock
self.mtZoomIn = QgsMapToolZoom(canvas, False)
self.mtZoomOut = QgsMapToolZoom(canvas, True)
self.mtMoveToCenter = MapToolCenter(canvas)
self.mtPan = QgsMapToolPan(canvas)
self.mtPixelScaleExtent = PixelScaleExtentMapTool(canvas)
self.mtFullExtentMapTool = FullExtentMapTool(canvas)
self.mtCursorLocation = CursorLocationMapTool(canvas, True)

Benjamin Jakimow
committed
self.mtAddFeature = QgsMapToolAddFeature(canvas, QgsMapToolCapture.CaptureNone, cadDock)
self.mtSelectFeature = QgsMapToolSelect(canvas)
from .externals.qps.maptools import MapTools
if mapToolKey == MapTools.ZoomIn:
self.mCanvas.setMapTool(self.mtZoomIn)
elif mapToolKey == MapTools.ZoomOut:
self.mCanvas.setMapTool(self.mtZoomOut)
elif mapToolKey == MapTools.Pan:
self.mCanvas.setMapTool(self.mtPan)
elif mapToolKey == MapTools.ZoomFull:
self.mCanvas.setMapTool(self.mtFullExtentMapTool)
elif mapToolKey == MapTools.ZoomPixelScale:
self.mCanvas.setMapTool(self.mtPixelScaleExtent)
elif mapToolKey == MapTools.CursorLocation:
self.mCanvas.setMapTool(self.mtCursorLocation)
elif mapToolKey == MapTools.SpectralProfile:
pass
elif mapToolKey == MapTools.TemporalProfile:
pass
elif mapToolKey == MapTools.MoveToCenter:
self.mCanvas.setMapTool(self.mtMoveToCenter)
elif mapToolKey == MapTools.AddFeature:
self.mCanvas.setMapTool(self.mtAddFeature)
elif mapToolKey == MapTools.SelectFeature:
self.mCanvas.setMapTool(self.mtSelectFeature)
self.mtSelectFeature.setSelectionMode(QgsMapToolSelectionHandler.SelectionMode.SelectSimple)
elif mapToolKey == MapTools.SelectFeatureByPolygon:
self.mCanvas.setMapTool(self.mtSelectFeature)
self.mtSelectFeature.setSelectionMode(QgsMapToolSelectionHandler.SelectionMode.SelectPolygon)
elif mapToolKey == MapTools.SelectFeatureByFreehand:
self.mCanvas.setMapTool(self.mtSelectFeature)
self.mtSelectFeature.setSelectionMode(QgsMapToolSelectionHandler.SelectionMode.SelectFreehand)
elif mapToolKey == MapTools.SelectFeatureByRadius:
self.mCanvas.setMapTool(self.mtSelectFeature)
self.mtSelectFeature.setSelectionMode(QgsMapToolSelectionHandler.SelectionMode.SelectRadius)
else:
print('Unknown MapTool key: {}'.format(mapToolKey))
# if undefined, set a current vector layer
if mapToolKey in [MapTools.SelectFeature, MapTools.SelectFeatureByPolygon, MapTools.SelectFeatureByRadius, MapTools.SelectFeatureByFreehand] \
and self.mCanvas.currentLayer() is None:
for vl in self.mCanvas.layers():
if isinstance(vl, QgsVectorLayer):
self.mCanvas.setCurrentLayer(vl)
break
class MapCanvas(QgsMapCanvas):
"""
A widget based on QgsMapCanvas to draw spatial data
"""
"""
Canvas specific commands
"""
UpdateMapItems = 5
saveFileDirectories = dict()
#sigShowProfiles = pyqtSignal(SpatialPoint, str)
sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
#sigChangeDVRequest = pyqtSignal(QgsMapCanvas, str)
#sigChangeMVRequest = pyqtSignal(QgsMapCanvas, str)
#sigChangeSVRequest = pyqtSignal(QgsMapCanvas, QgsRasterRenderer)

Benjamin Jakimow
committed
sigCrosshairPositionChanged = pyqtSignal(SpatialPoint)
sigCrosshairVisibilityChanged = pyqtSignal(bool)
sigDestinationCrsChanged = pyqtSignal(QgsCoordinateReferenceSystem)

Benjamin Jakimow
committed
sigCrosshairStyleChanged = pyqtSignal(CrosshairStyle)
def __init__(self, parent=None):
super(MapCanvas, self).__init__(parent=parent)
self.mMapLayerStore = QgsProject.instance()
self.mMapTools = None
self.initMapTools()

benjamin.jakimow@geo.hu-berlin.de
committed
self.mCrosshairItem = CrosshairMapCanvasItem(self)
self.mInfoItem = MapCanvasInfoItem(self)
self.mProgressItem = MapLoadingInfoItem(self)
self.mUserInputWidget = QgsUserInputWidget(self)
self.mUserInputWidget.setObjectName('UserInputDockWidget')
self.mUserInputWidget.setAnchorWidget(self)
self.mUserInputWidget.setAnchorWidgetPoint(QgsFloatingWidget.TopRight)
self.mUserInputWidget.setAnchorPoint(QgsFloatingWidget.TopRight)
#self.mProgressBar = QProgressBar()
#self.mUserInputWidget.addUserInputWidget(self.mProgressBar)
self.mNeedsRefresh = False
self.mRenderingFinished = True
self.mIsRefreshing = False
t2 = time.time()
dt = t2 - self.mRefreshStartTime
self.sigMapRefreshed[float].emit(dt)
self.sigMapRefreshed[float, float].emit(self.mRefreshStartTime, t2)

benjamin.jakimow@geo.hu-berlin.de
committed
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
bg = eotimeseriesviewer.settings.value(eotimeseriesviewer.settings.Keys.MapBackgroundColor, default=QColor(0, 0, 0))
self.setContextMenuPolicy(Qt.DefaultContextMenu)
self.extentsChanged.connect(lambda : self.sigSpatialExtentChanged.emit(self.spatialExtent()))
self.destinationCrsChanged.connect(lambda : self.sigDestinationCrsChanged.emit(self.crs()))
def userInputWidget(self)->QgsUserInputWidget:
"""
Returns the mapcanvas QgsUserInputWidget
:return: QgsUserInputWidget
"""
return self.mUserInputWidget
def infoItem(self)->MapCanvasInfoItem:
"""
Returns the MapCanvasInfoItem, e.g. to plot text on top of the map canvas
:return: MapCanvasInfoItem
"""
return self.mInfoItem
def mapView(self):
"""
Returns the MapView this MapCanvas is linked to
:return:
"""
return self.mMapView
def mapTools(self)->MapCanvasMapTools:
"""
Returns the map tools of this MapCanvas
:return: MapCanvasMapTools
"""
return self.mMapTools
def initMapTools(self):
self.mCadDock = QgsAdvancedDigitizingDockWidget(self)
self.mCadDock.setVisible(False)
self.mMapTools = MapCanvasMapTools(self, self.mCadDock)
def setMapLayerStore(self, store):
"""
Sets the QgsMapLayerStore or QgsProject instance that is used to register map layers
:param store: QgsMapLayerStore | QgsProject
"""
assert isinstance(store, (QgsMapLayerStore, QgsProject))
self.mMapLayerStore = store
def renderingFinished(self)->bool:
"""
Returns whether the MapCanvas is processing a rendering task
:return: bool
"""
return self.mRenderingFinished

Benjamin Jakimow
committed
def mousePressEvent(self, event:QMouseEvent):
b = event.button() == Qt.LeftButton
if b and isinstance(self.mapTool(), QgsMapTool):
b = isinstance(self.mapTool(), (QgsMapToolIdentify,
CursorLocationMapTool,
SpectralProfileMapTool, TemporalProfileMapTool))
super(MapCanvas, self).mousePressEvent(event)
if b:
ms = self.mapSettings()
pointXY = ms.mapToPixel().toMapCoordinates(event.x(), event.y())
spatialPoint = SpatialPoint(ms.destinationCrs(), pointXY)
self.setCrosshairPosition(spatialPoint)
"""
Sets the map canvas MapView
:param mapView: MapView
"""
from eotimeseriesviewer.mapvisualization import MapView

Benjamin Jakimow
committed
self.mInfoItem.setTextFormat(mapView.mapTextFormat())
self.addToRefreshPipeLine(mapView.mapBackgroundColor())
self.addToRefreshPipeLine(MapCanvas.Command.UpdateMapItems)
def setTSD(self, tsd:TimeSeriesDate):
Sets the TimeSeriesDate this map-canvas is linked to
if self.mTSD == tsd:
return
# disconnect old TSD
if isinstance(self.mTSD, TimeSeriesDate):
self.mTSD.sensor().sigNameChanged.disconnect(self.updateScope)
self.mTSD = tsd
if isinstance(tsd, TimeSeriesDate):
self.mTSD.sensor().sigNameChanged.connect(self.updateScope)
self.updateScope()
def updateScope(self):
"""
Updates map-canvas TSD variables
"""

Benjamin Jakimow
committed
scope = self.expressionContextScope()
tsd = self.tsd()
if isinstance(tsd, TimeSeriesDate):
scope.setVariable('map_date', str(tsd.date()), isStatic=False)
scope.setVariable('map_doy', tsd.doy(), isStatic=False)
scope.setVariable('map_sensor', tsd.sensor().name(), isStatic=False)
else:
scope.setVariable('map_date', None, isStatic=False)
scope.setVariable('map_doy', None, isStatic=False)
scope.setVariable('map_sensor', None, isStatic=False)
def tsd(self)->TimeSeriesDate:
Returns the TimeSeriesDate
:return: TimeSeriesDate

Benjamin Jakimow
committed
def setSpatialExtent(self, extent:SpatialExtent):
"""
Sets the spatial extent
:param extent: SpatialExtent
"""
assert isinstance(extent, SpatialExtent)
extent = extent.toCrs(self.crs())
self.setExtent(extent)
def setSpatialCenter(self, center:SpatialPoint):
"""
Sets the SpatialCenter
:param center: SpatialPoint
"""
assert isinstance(center, SpatialPoint)
center = center.toCrs(self.crs())
self.setCenter(center)
def setFixedSize(self, size:QSize):
"""
Changes the map-canvas size
:param size: QSize
"""
assert isinstance(size, QSize)
if self.size() != size:
super(MapCanvas, self).setFixedSize(size)
if self.crs() != crs:
self.setDestinationCrs(crs)
"""
Shortcut to return self.mapSettings().destinationCrs()
:return: QgsCoordinateReferenceSystem
"""
return self.mapSettings().destinationCrs()
def setLayers(self, mapLayers):
"""
Set the map layers and, if necessary, registers the in a QgsMapLayerStore
:param mapLayers:
"""
self.mMapLayerStore.addMapLayers(mapLayers)
super(MapCanvas, self).setLayers(mapLayers)
def isVisibleToViewport(self)->bool:
"""
Returns whether the MapCanvas is visible to a user and not hidden behind the invisible regions of a scroll area.
:return: bool
"""
return self.visibleRegion().boundingRect().isValid()
def addToRefreshPipeLine(self, arguments: list):
"""
Adds commands or other arguments to a pipeline which will be handled during the next timed refresh.
:param arguments: argument | [list-of-arguments]
"""
if not isinstance(arguments, list):
arguments = [arguments]
if isinstance(a, SpatialExtent):
self.mTimedRefreshPipeLine[SpatialPoint] = a
elif isinstance(a, QColor):
self.mTimedRefreshPipeLine[QColor] = a
elif isinstance(a, MapCanvas.Command):
if not MapCanvas.Command in self.mTimedRefreshPipeLine.keys():
self.mTimedRefreshPipeLine[MapCanvas.Command] = []
# remove previous commands of same type, append command to end
while a in self.mTimedRefreshPipeLine[MapCanvas.Command]:
self.mTimedRefreshPipeLine[MapCanvas.Command].remove(a)
self.mTimedRefreshPipeLine[MapCanvas.Command].append(a)
else:
raise NotImplementedError('Unsupported argument: {}'.format(str(a)))
def timedRefresh(self):
"""
Called to refresh the map canvas with all things needed to be done with lazy evaluation
expected = []
existing = self.layers()
existingSources = [l.source() for l in existing]
if self.mapView() is None or self.tsd() is None:
self.setLayers([])
self.mInfoItem.clearText()
self.update()
return
for lyr in self.mMapView.layers():
assert isinstance(lyr, QgsMapLayer)
if isinstance(lyr, SensorProxyLayer):
if self.tsd().sensor() == lyr.sensor():
for source in self.tsd().sourceUris():
sourceLayer = None
if source in existingSources:
sourceLayer = existing[existingSources.index(source)]
else:
sourceLayer = SensorProxyLayer(source, sensor=self.tsd().sensor())
sourceLayer.setName(lyr.name())
sourceLayer.setCustomProperty('eotsv/sensorid', self.tsd().sensor().id())
try:
renderer = lyr.renderer()
if isinstance(renderer, QgsRasterRenderer):
sourceLayer.setRenderer(renderer.clone())
except Exception as exR:
s = ""
assert isinstance(sourceLayer, QgsRasterLayer)
expected.append(sourceLayer)
else:
# skip any other SensorProxyLayer that relates to another sensor
pass
else:
expected.append(lyr)
if len(self.mTimedRefreshPipeLine) == 0 and self.layers() == expected:
# there is nothing to do.
# look for new layers
lyrs = self.layers()
if lyrs != expected:
self.setLayers(expected)
if True:
# set sources first
keys = self.mTimedRefreshPipeLine.keys()
if QgsCoordinateReferenceSystem in keys:
self.setDestinationCrs(self.mTimedRefreshPipeLine[QgsCoordinateReferenceSystem])
if SpatialExtent in keys:
self.setSpatialExtent(self.mTimedRefreshPipeLine[SpatialExtent])
if SpatialPoint in keys:
self.setSpatialCenter(self.mTimedRefreshPipeLine[SpatialPoint])
if QColor in keys:
self.setCanvasColor(self.mTimedRefreshPipeLine[QColor])
if MapCanvas.Command in keys:
commands = self.mTimedRefreshPipeLine[MapCanvas.Command]
for command in commands:
assert isinstance(command, MapCanvas.Command)
if command == MapCanvas.Command.RefreshRenderer:
for px in [px for px in self.mMapView.layers() if isinstance(px, SensorProxyLayer)]:
for l in self.layers():
if isinstance(l, SensorProxyLayer) and l.sensor() == px.sensor():
try:
renderer = px.renderer().clone()
renderer.setInput(l.dataProvider())
l.setRenderer(renderer)
except Exception as ex:
s = ""
if command == MapCanvas.Command.UpdateMapItems:
#self.update()
self.mTimedRefreshPipeLine.clear()
def setLayerVisibility(self, cls, isVisible:bool):
"""
:param cls: type of layer, e.g. QgsRasterLayer to set visibility of all layers of same type
QgsMapLayer instance to the visibility of a specific layer
:param isVisible: bool
"""
self.mMapLayerModel.setLayerVisibility(cls, isVisible)
self.addToRefreshPipeLine(MapCanvas.Command.RefreshVisibility)
def setCrosshairStyle(self, crosshairStyle:CrosshairStyle, emitSignal=True):
"""
Sets the CrosshairStyle
:param crosshairStyle: CrosshairStyle
:param emitSignal: Set to Fals to no emit a signal.
"""
from eotimeseriesviewer import CrosshairStyle
if crosshairStyle is None:

Benjamin Jakimow
committed
self.mCrosshairItem.crosshairStyle.setShow(False)
self.mCrosshairItem.update()
else:
assert isinstance(crosshairStyle, CrosshairStyle)

Benjamin Jakimow
committed
self.mCrosshairItem.setCrosshairStyle(crosshairStyle)

Benjamin Jakimow
committed
if emitSignal:
self.sigCrosshairStyleChanged.emit(self.mCrosshairItem.crosshairStyle())

Benjamin Jakimow
committed
def crosshairStyle(self)->CrosshairStyle:
"""
Returns the style of the Crosshair.
:return: CrosshairStyle
"""

Benjamin Jakimow
committed
return self.mCrosshairItem.crosshairStyle
def setCrosshairPosition(self, spatialPoint:SpatialPoint):

Benjamin Jakimow
committed
"""
Sets the position of the Crosshair.
:param spatialPoint: SpatialPoint
:param emitSignal: True (default). Set False to avoid emitting sigCrosshairPositionChanged
:return:
"""

Benjamin Jakimow
committed
point = spatialPoint.toCrs(self.mapSettings().destinationCrs())
if self.mCrosshairItem.mPosition != point:
self.mCrosshairItem.setPosition(point)

Benjamin Jakimow
committed
self.sigCrosshairPositionChanged.emit(point)
def crosshairPosition(self)->SpatialPoint:
"""Returns the last crosshair position"""
return self.mCrosshairItem.mPosition
def setCrosshairVisibility(self, b:bool, emitSignal=True):
"""
Sets the Crosshair visbility
:param b: bool
"""
if b and self.mCrosshairItem.mPosition is None:
self.mCrosshairItem.setPosition(self.spatialCenter())
self.sigCrosshairPositionChanged.emit(self.spatialCenter())
if b != self.mCrosshairItem.visibility():
self.mCrosshairItem.setVisibility(b)
if emitSignal:
self.sigCrosshairVisibilityChanged.emit(b)
def layerPaths(self):
:return: [list-of-str]

benjamin.jakimow@geo.hu-berlin.de
committed
return [str(l.source()) for l in self.layers()]
def pixmap(self):
"""
Returns the current map image as pixmap
return self.grab()
def contextMenu(self, pos:QPoint)->QMenu:
"""
Create the MapCanvas context menu with options relevant for pixel position ``pos``.
:param pos: QPoint
:return: QMenu
"""
mapSettings = self.mapSettings()
assert isinstance(mapSettings, QgsMapSettings)
pointGeo = mapSettings.mapToPixel().toMapCoordinates(pos.x(), pos.y())
assert isinstance(pointGeo, QgsPointXY)
from .main import TimeSeriesViewer
eotsv = TimeSeriesViewer.instance()

Benjamin Jakimow
committed
viewPortMapLayers = [l for l in self.layers() if isinstance(l, QgsMapLayer)]

Benjamin Jakimow
committed

Benjamin Jakimow
committed
viewPortRasterLayers = [l for l in viewPortMapLayers if isinstance(l, QgsRasterLayer) and SpatialExtent.fromLayer(l).toCrs(self.crs()).contains(pointGeo)]
viewPortSensorLayers = [l for l in viewPortRasterLayers if isinstance(l, SensorProxyLayer)]
viewPortVectorLayers = [l for l in viewPortMapLayers if isinstance(l, QgsVectorLayer)]

Benjamin Jakimow
committed
refSensorLayer = None
refRasterLayer = None
if len(viewPortRasterLayers) > 0:
refRasterLayer = viewPortRasterLayers[0]
if len(viewPortSensorLayers) > 0:
refSensorLayer = viewPortSensorLayers[0]
menu = QMenu()
if isinstance(self.tsd(), TimeSeriesDate):

Benjamin Jakimow
committed
tss = None
sourceUris = self.tsd().sourceUris()
for sl in viewPortSensorLayers:
if sl.source() in sourceUris:
tss = self.tsd()[sourceUris.index(sl.source())]
break
lyrWithSelectedFeatures = [l for l in quickLabelLayers() if l.isEditable() and l.selectedFeatureCount() > 0]
layerNames = ', '.join([l.name() for l in lyrWithSelectedFeatures])
m = menu.addMenu('Quick Labels'.format(self.tsd().date()))
nQuickLabelLayers = len(lyrWithSelectedFeatures)
m.setEnabled(nQuickLabelLayers > 0)
a = m.addAction('Set Date/Sensor attributes')
a.setToolTip('Writes the date ate and sensor quick labels of selected features in {}.'.format(layerNames))

Benjamin Jakimow
committed
a.triggered.connect(lambda *args, tsd = self.tsd(), tss=tss: setQuickTSDLabelsForRegisteredLayers(tsd, tss))
from .labeling import EDITOR_WIDGET_REGISTRY_KEY as QUICK_LABEL_KEY
from .labeling import CONFKEY_CLASSIFICATIONSCHEME, layerClassSchemes, setQuickClassInfo
for layer in lyrWithSelectedFeatures:
assert isinstance(layer, QgsVectorLayer)
csf = layerClassSchemes(layer)
if len(csf) > 0:
m.addSection(layer.name())
for (cs, field) in csf:
assert isinstance(cs, ClassificationScheme)
assert isinstance(field, QgsField)
classMenu = m.addMenu('"{}" ({})'.format(field.name(), field.typeName()))
for classInfo in cs:
assert isinstance(classInfo, ClassInfo)
a = classMenu.addAction('{} "{}"'.format(classInfo.label(), classInfo.name()))
a.setIcon(classInfo.icon())
a.triggered.connect(lambda _, vl=layer, f=field, c=classInfo: setQuickClassInfo(vl, f, c))

Benjamin Jakimow
committed
if isinstance(refSensorLayer, SensorProxyLayer):
m = menu.addMenu('Raster stretch...')
action = m.addAction('Linear')
action.triggered.connect(lambda *args, lyr=refSensorLayer: self.stretchToExtent(self.spatialExtent(), 'linear_minmax', layer=lyr, p=0.0))
action = m.addAction('Linear 5%')
action.triggered.connect(lambda *args, lyr=refSensorLayer: self.stretchToExtent(self.spatialExtent(), 'linear_minmax', layer=lyr, p=0.05))
action = m.addAction('Gaussian')
action.triggered.connect(lambda *args, lyr=refSensorLayer: self.stretchToExtent(self.spatialExtent(), 'gaussian', layer=lyr, n=3))
menu.addSeparator()
from .externals.qps.layerproperties import pasteStyleFromClipboard, pasteStyleToClipboard
b = isinstance(refRasterLayer, QgsRasterLayer)
a = menu.addAction('Copy Style')
a.setEnabled(b)
a.setToolTip('Copy the current layer style to clipboard')
a.triggered.connect(lambda *args, lyr=refRasterLayer: pasteStyleToClipboard(lyr))

Benjamin Jakimow
committed
a = menu.addAction('Paste Style')
a.setEnabled(b)
a.setEnabled('application/qgis.style' in QApplication.clipboard().mimeData().formats())
a.triggered.connect(lambda *args, lyr=refRasterLayer: self.onPasteStyleFromClipboard(lyr))
menu.addSeparator()
m = menu.addMenu('Layers...')
visibleLayers = viewPortRasterLayers + viewPortVectorLayers
for mapLayer in visibleLayers:
#sub = m.addMenu(mapLayer.name())
sub = m.addMenu(os.path.basename(mapLayer.source()))
if isinstance(mapLayer, SensorProxyLayer):
sub.setIcon(QIcon(':/timeseriesviewer/icons/icon.svg'))
elif isinstance(mapLayer, QgsRasterLayer):
sub.setIcon(QIcon(''))
elif isinstance(mapLayer, QgsVectorLayer):
wkbType = QgsWkbTypes.displayString(int(mapLayer.wkbType()))
if re.search('polygon', wkbType, re.I):
sub.setIcon(QIcon(r':/images/themes/default/mIconPolygonLayer.svg'))
elif re.search('line', wkbType, re.I):
sub.setIcon(QIcon(r':/images/themes/default/mIconLineLayer.svg'))
elif re.search('point', wkbType, re.I):
sub.setIcon(QIcon(r':/images/themes/default/mIconPointLayer.svg'))
a = sub.addAction('Properties...')
a.triggered.connect(lambda *args,
lyr = mapLayer,
c = self,
b = isinstance(mapLayer, SensorProxyLayer) == False:
showLayerPropertiesDialog(lyr, c, useQGISDialog=b))
a = sub.addAction('Zoom to Layer')
a.setIcon(QIcon(':/images/themes/default/mActionZoomToLayer.svg'))
a.triggered.connect(lambda *args, lyr=mapLayer: self.setSpatialExtent(SpatialExtent.fromLayer(lyr)))

Benjamin Jakimow
committed
a = sub.addAction('Copy Style')
a.setToolTip('Copy layer style to clipboard')
a.triggered.connect(lambda *args, lyr=mapLayer: pasteStyleToClipboard(lyr))
a = sub.addAction('Paste Style')
a.setToolTip('Paster layer style from clipboard')
a.setEnabled('application/qgis.style' in QApplication.clipboard().mimeData().formats())
a.triggered.connect(lambda *args, lyr=mapLayer: self.onPasteStyleFromClipboard(lyr))
menu.addSeparator()
action = menu.addAction('Zoom to full extent')
action.setIcon(QIcon(':/images/themes/default/mActionZoomFullExtent.svg'))
action.triggered.connect(lambda: self.setExtent(self.fullExtent()))
menu.addSeparator()
m = menu.addMenu('Crosshair...')
action = m.addAction('Show')
action.setCheckable(True)
action.setChecked(self.mCrosshairItem.visibility())
action.toggled.connect(self.setCrosshairVisibility)

Benjamin Jakimow
committed
def onCrosshairChange(*args):
style = CrosshairDialog.getCrosshairStyle(parent=self,
mapCanvas=self,
crosshairStyle=self.mCrosshairItem.crosshairStyle)

Benjamin Jakimow
committed
if isinstance(style, CrosshairStyle):
self.setCrosshairStyle(style)
action.triggered.connect(onCrosshairChange)
if isinstance(tsd, TimeSeriesDate):
menu.addSeparator()
m = menu.addMenu('Copy...')
action = m.addAction('Date')
action.triggered.connect(lambda: QApplication.clipboard().setText(str(tsd.date())))
action.setToolTip('Sends "{}" to the clipboard.'.format(str(tsd.date())))
action = m.addAction('Sensor')
action.triggered.connect(lambda: QApplication.clipboard().setText(tsd.sensor().name()))
action.setToolTip('Sends "{}" to the clipboard.'.format(tsd.sensor().name()))

Benjamin Jakimow
committed
action = m.addAction('Path')

Benjamin Jakimow
committed
paths = [QDir.toNativeSeparators(p) for p in tsd.sourceUris()]
action.triggered.connect(lambda _, paths=paths: QApplication.clipboard().setText('\n'.join(paths)))
action.setToolTip('Sends {} source URI(s) to the clipboard.'.format(len(tsd)))
extent = self.extent()
assert isinstance(extent, QgsRectangle)
action = m.addAction('Extent')
action.triggered.connect(lambda _, extent=extent: QApplication.clipboard().setText(extent.toString()))
action.setToolTip('Sends the map extent to the clipboard.')
action = m.addAction('Map')
action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.pixmap()))
action.setToolTip('Copies this map into the clipboard.')

benjamin.jakimow@geo.hu-berlin.de
committed
from .utils import findParent
from .mapvisualization import MapWidget
mw = findParent(self, MapWidget)
if isinstance(mw, MapWidget):
action = m.addAction('All Maps')
action.triggered.connect(lambda: QApplication.clipboard().setPixmap(mw.grab()))
action.setToolTip('Copies all maps into the clipboard.')

benjamin.jakimow@geo.hu-berlin.de
committed
m = menu.addMenu('Map Coordinates...')
ext = self.spatialExtent()
center = self.spatialExtent().spatialCenter()
action = m.addAction('Extent (WKT Coordinates)')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktCoordinates()))
action = m.addAction('Extent (WKT Polygon)')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktPolygon()))
m.addSeparator()
action = m.addAction('Map Center (WKT)')
action.triggered.connect(lambda: QApplication.clipboard().setText(center.asWkt()))

benjamin.jakimow@geo.hu-berlin.de
committed

benjamin.jakimow@geo.hu-berlin.de
committed
action.triggered.connect(lambda: QApplication.clipboard().setText(center.toString()))
action = m.addAction('Map Extent (WKT)')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktPolygon()))
action = m.addAction('Map Extent')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.toString()))

benjamin.jakimow@geo.hu-berlin.de
committed
m.addSeparator()
action = m.addAction('CRS (EPSG)')
action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().authid()))
action = m.addAction('CRS (WKT)')
action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().toWkt()))
action = m.addAction('CRS (Proj4)')
action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().toProj4()))
action = m.addAction('PNG')
action.triggered.connect(lambda: self.saveMapImageDialog('PNG'))
action = m.addAction('JPEG')
action.triggered.connect(lambda: self.saveMapImageDialog('JPG'))

benjamin.jakimow@geo.hu-berlin.de
committed

Benjamin Jakimow
committed
classSchemes = []
for layer in lyrWithSelectedFeaturs:
for classScheme in layerClassSchemes(layer):
assert isinstance(classScheme, ClassificationScheme)
if classScheme in classSchemes:
continue
classMenu = m.addMenu('Classification "{}"'.format(classScheme.name()))
assert isinstance(classMenu, QMenu)
for classInfo in classScheme:
assert isinstance(classInfo, ClassInfo)
a = classMenu.addAction(classInfo.name())
a.setIcon(classInfo.icon())
a.setToolTip('Write "{}" or "{}" to connected vector field attributes'.format(classInfo.name(), classInfo.label()))

Benjamin Jakimow
committed
a.triggered.connect(

Benjamin Jakimow
committed
applyShortcutsToRegisteredLayers(tsd, [ci]))
classSchemes.append(classScheme)
if isinstance(self.tsd(), TimeSeriesDate) and isinstance(eotsv, TimeSeriesViewer):
ts = eotsv.timeSeries()
action = menu.addAction('Focus on Spatial Extent')
action.triggered.connect(ts.focusVisibilityToExtent)
action = menu.addAction('Hide Date')
action.triggered.connect(lambda *args: ts.hideTSDs([tsd]))
action = menu.addAction('Remove Date')
action.triggered.connect(lambda *args, : ts.removeTSDs([tsd]))
return menu

Benjamin Jakimow
committed
def onPasteStyleFromClipboard(self, lyr):
from .externals.qps.layerproperties import pasteStyleFromClipboard
pasteStyleFromClipboard(lyr)
if isinstance(lyr, SensorProxyLayer):
self.mMapView.sensorProxyLayer(lyr.sensor()).setRenderer(lyr.renderer().clone())
def contextMenuEvent(self, event:QContextMenuEvent):
"""
Create and shows the MapCanvas context menu.
:param event: QEvent
"""
assert isinstance(event, QContextMenuEvent)
menu = self.contextMenu(event.pos())
menu.exec_(event.globalPos())
from eotimeseriesviewer.utils import qgisInstance
iface = qgisInstance()
if isinstance(iface, QgisInterface):
grpNode= iface.layerTreeView().currentGroupNode()
assert isinstance(grpNode, QgsLayerTreeGroup)
for l in mapLayers:
if isinstance(l, QgsRasterLayer):
lqgis = iface.addRasterLayer(l.source(), l.name())
lqgis.setRenderer(l.renderer().clone())
lqgis = iface.addVectorLayer(l.source(), l.name(), 'ogr')
def stretchToCurrentExtent(self):
se = self.spatialExtent()
self.stretchToExtent(se, stretchType='linear_minmax', p=0.05)

Benjamin Jakimow
committed
def stretchToExtent(self, spatialExtent:SpatialExtent, stretchType='linear_minmax', layer:QgsRasterLayer=None, **stretchArgs):
"""
:param spatialExtent: rectangle to get the image statistics for
:param stretchType: ['linear_minmax' (default), 'gaussian']
:param stretchArgs:
linear_minmax: 'p' percentage from min/max, e.g. +- 5 %
gaussian: 'n' mean +- n* standard deviations
:return:
"""

Benjamin Jakimow
committed
if not isinstance(layer, QgsRasterLayer):
layers = [l for l in self.layers() if isinstance(l, SensorProxyLayer)]
if len(layers) > 0:
layer = layers[0]
else:
layers = [l for l in self.layers() if isinstance(l, SensorProxyLayer)]
if len(layers) > 0:
layer = layers[0]

Benjamin Jakimow
committed
if not isinstance(layer, QgsRasterLayer):
return

Benjamin Jakimow
committed
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
r = layer.renderer()
dp = layer.dataProvider()
newRenderer = None
extent = spatialExtent.toCrs(layer.crs())
assert isinstance(dp, QgsRasterDataProvider)
def getCE(band):
stats = dp.bandStatistics(band, QgsRasterBandStats.All, extent, 256)
ce = QgsContrastEnhancement(dp.dataType(band))
d = (stats.maximumValue - stats.minimumValue)
if stretchType == 'linear_minmax':
ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
ce.setMinimumValue(stats.minimumValue + d * stretchArgs.get('p', 0))
ce.setMaximumValue(stats.maximumValue - d * stretchArgs.get('p', 0))
elif stretchType == 'gaussian':
ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
ce.setMinimumValue(stats.mean - stats.stdDev * stretchArgs.get('n', 3))
ce.setMaximumValue(stats.mean + stats.stdDev * stretchArgs.get('n', 3))
else:
# stretchType == 'linear_minmax':
ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
ce.setMinimumValue(stats.minimumValue)
ce.setMaximumValue(stats.maximumValue)

Benjamin Jakimow
committed
return ce

Benjamin Jakimow
committed
if isinstance(r, QgsMultiBandColorRenderer):
newRenderer = r.clone()

Benjamin Jakimow
committed
ceR = getCE(r.redBand())
ceG = getCE(r.greenBand())
ceB = getCE(r.blueBand())

Benjamin Jakimow
committed
newRenderer.setRedContrastEnhancement(ceR)
newRenderer.setGreenContrastEnhancement(ceG)
newRenderer.setBlueContrastEnhancement(ceB)

Benjamin Jakimow
committed
elif isinstance(r, QgsSingleBandPseudoColorRenderer):
newRenderer = r.clone()
ce = getCE(newRenderer.band())

Benjamin Jakimow
committed
# stats = dp.bandStatistics(newRenderer.band(), QgsRasterBandStats.All, extent, 500)

Benjamin Jakimow
committed
shader = newRenderer.shader()
newRenderer.setClassificationMax(ce.maximumValue())
newRenderer.setClassificationMin(ce.minimumValue())
shader.setMaximumValue(ce.maximumValue())
shader.setMinimumValue(ce.minimumValue())

Benjamin Jakimow
committed
elif isinstance(r, QgsSingleBandGrayRenderer):

Benjamin Jakimow
committed
newRenderer = r.clone()
ce = getCE(newRenderer.grayBand())
newRenderer.setContrastEnhancement(ce)

Benjamin Jakimow
committed
elif isinstance(r, QgsPalettedRasterRenderer):

Benjamin Jakimow
committed
newRenderer = r.clone()

Benjamin Jakimow
committed
if newRenderer is not None:

Benjamin Jakimow
committed
if isinstance(layer, SensorProxyLayer):
self.mMapView.sensorProxyLayer(layer.sensor()).setRenderer(newRenderer)
elif isinstance(layer, QgsRasterLayer):
layer.setRenderer(layer)
def saveMapImageDialog(self, fileType):
"""
Opens a dialog to save the map as local file
:param fileType:
:return:
"""
import eotimeseriesviewer.settings
lastDir = eotimeseriesviewer.settings.value(eotimeseriesviewer.settings.Keys.ScreenShotDirectory, os.path.expanduser('~'))
from eotimeseriesviewer.utils import filenameFromString
from eotimeseriesviewer.mapvisualization import MapView
if isinstance(self.mTSD, TimeSeriesDate) and isinstance(self.mMapView, MapView):
path = filenameFromString('{}.{}'.format(self.mTSD.date(), self.mMapView.title()))
else:
path = 'mapcanvas'
path = jp(lastDir, '{}.{}'.format(path, fileType.lower()))
path, _ = QFileDialog.getSaveFileName(self, 'Save map as {}'.format(fileType), path)
if len(path) > 0:
self.saveAsImage(path, None, fileType)
eotimeseriesviewer.settings.setValue(eotimeseriesviewer.settings.Keys.ScreenShotDirectory, os.path.dirname(path))
def setSpatialExtent(self, spatialExtent: SpatialExtent):
"""
Sets the SpatialExtent to be shown.
:param spatialExtent: SpatialExtent
"""
assert isinstance(spatialExtent, SpatialExtent)
if self.spatialExtent() != spatialExtent:
spatialExtent = spatialExtent.toCrs(self.crs())
if isinstance(spatialExtent, SpatialExtent):
self.setExtent(spatialExtent)
def setSpatialCenter(self, spatialPoint: SpatialPoint):
"""
Sets the map center
:param spatialPoint: SpatialPoint
"""
center = spatialPoint.toCrs(self.crs())
if isinstance(center, SpatialPoint):
self.setCenter(center)

Benjamin Jakimow
committed
def spatialExtent(self)->SpatialExtent:
"""
Returns the map extent as SpatialExtent (extent + CRS)
:return: SpatialExtent
"""
return SpatialExtent.fromMapCanvas(self)

Benjamin Jakimow
committed
def spatialCenter(self)->SpatialPoint:
"""
Returns the map center as SpatialPoint (QgsPointXY + CRS)
:return: SpatialPoint
"""
return SpatialPoint.fromMapCanvasCenter(self)
def spatialExtentHint(self)->SpatialExtent:
"""
Returns a hint for a SpatialExtent, derived from the first raster layer
:return: SpatialExtent
"""
layers = self.layers()
if len(layers) > 0:
e = self.fullExtent()
ext = SpatialExtent(crs, e)
else:
ext = SpatialExtent.world()
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
class CanvasBoundingBoxItem(QgsGeometryRubberBand):
def __init__(self, mapCanvas):
assert isinstance(mapCanvas, QgsMapCanvas)
super(CanvasBoundingBoxItem, self).__init__(mapCanvas)
self.canvas = mapCanvas
self.mCanvasExtents = dict()
self.mShow = True
self.mShowTitles = True
self.setIconType(QgsGeometryRubberBand.ICON_NONE)
def connectCanvas(self, canvas):
assert isinstance(canvas, QgsMapCanvas)
assert canvas != self.canvas
if canvas not in self.mCanvasExtents.keys():
self.mCanvasExtents[canvas] = None
canvas.extentsChanged.connect(lambda : self.onExtentsChanged(canvas))
canvas.destroyed.connect(lambda : self.disconnectCanvas(canvas))
self.onExtentsChanged(canvas)
def disconnectCanvas(self, canvas):
self.mCanvasExtents.pop(canvas)
def onExtentsChanged(self, canvas):
assert isinstance(canvas, QgsMapCanvas)
ext = SpatialExtent.fromMapCanvas(canvas)
ext = ext.toCrs(self.canvas.mapSettings().destinationCrs())
assert geom.fromWkt(ext.asWktPolygon())
self.mCanvasExtents[canvas] = (ext, geom)
self.refreshExtents()
def refreshExtents(self):
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
if self.mShow:
for canvas, t in self.mCanvasExtents.items():
ext, geom = t
multi.addGeometry(geom.clone())
self.setGeometry(multi)
def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
super(CanvasBoundingBoxItem, self).paint(painter)
if self.mShowTitles and self.mShow:
painter.setPen(Qt.blue);
painter.setFont(QFont("Arial", 30))
for canvas, t in self.mCanvasExtents.items():
ext, geom = t
ULpx = self.toCanvasCoordinates(ext.center())
txt = canvas.windowTitle()
painter.drawLine(0, 0, 200, 200);
painter.drawText(ULpx, txt)
def setShow(self, b):
assert isinstance(b, bool)
self.mShow = b
def setShowTitles(self, b):
assert isinstance(b, bool)
self.mShowTitles = b