From ed849ee7922dbb94031f596d0d751263716386c4 Mon Sep 17 00:00:00 2001 From: Benjamin Jakimow <no.email> Date: Thu, 22 Dec 2016 10:25:41 +0100 Subject: [PATCH] refactoring minor UI changes --- timeseriesviewer/main.py | 397 ++++++++++++-- timeseriesviewer/tests.py | 2 +- timeseriesviewer/timeseries.py | 22 +- timeseriesviewer/ui/bandview.ui | 161 +++--- timeseriesviewer/ui/imagechipviewsettings.ui | 16 +- timeseriesviewer/ui/timseriesviewer.ui | 514 +++++++++---------- timeseriesviewer/ui/widgets.py | 169 +++++- 7 files changed, 880 insertions(+), 401 deletions(-) diff --git a/timeseriesviewer/main.py b/timeseriesviewer/main.py index 09d5184c..d6842424 100644 --- a/timeseriesviewer/main.py +++ b/timeseriesviewer/main.py @@ -47,11 +47,10 @@ except: import numpy as np -import multiprocessing, site +import sys, bisect, multiprocessing, site from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4.uic.Compiler.qtproxies import QtGui, QtCore -import sys import code import codecs @@ -218,13 +217,147 @@ class TimeSeriesItemModel(QAbstractItemModel): return 1 +class TimeSeriesDatumViewManager(QObject): + + def __init__(self, timeSeriesViewer): + assert isinstance(timeSeriesViewer, TimeSeriesViewer) + super(TimeSeriesDatumViewManager, self).__init__() + + self.TSV = timeSeriesViewer + + self.TSDViews = list() + self.bandViewMananger = self.TSV.bandViewManager + self.bandViewMananger.sigBandViewAdded.connect(self.addBandView) + self.bandViewMananger.sigBandViewRemoved.connect(self.removeBandView) + self.setExtent(self.TSV.TS.getMaxExtent()) + self.setMaxTSDViews() + self.setTimeSeries(self.TSV.TS) + self.L = self.TSV.ui.scrollAreaSubsetContent.layout() + self.setSubsetSize(QSize(100,50)) + + def setSubsetSize(self, size): + assert isinstance(size, QSize) + self.subsetSize = size + for tsdv in self.TSDViews: + tsdv.setSubsetSize(size) + self.adjustScrollArea() + + + def adjustScrollArea(self): + + m = self.L.contentsMargins() + n = len(self.TSDViews) + if n > 0: + refTSDView = self.TSDViews[0] + size = refTSDView.ui.size() + + w = n * size.width() + (n-1) * (m.left()+ m.right()) + h = max([refTSDView.ui.minimumHeight() + m.top() + m.bottom(), + self.TSV.ui.scrollAreaSubsets.height()-25]) + + self.L.parentWidget().setFixedSize(w,h) + + + def setTimeSeries(self,TS): + assert isinstance(TS, TimeSeries) + self.TS = TS + self.TS.sigTimeSeriesDatumAdded.connect(self.createTSDView) + + + def setMaxTSDViews(self, n=-1): + self.nMaxTSDViews = n + #todo: remove views + + def setExtent(self, extent): + self.extent = extent + assert isinstance(extent, QgsRectangle) + tsdviews = sorted(self.TSDViews, key=lambda t:t.TSD) + for tsdview in tsdviews: + tsdview.setSpatialExtent(extent) + + def navToDOI(self, TSD): + assert isinstance(TSD, TimeSeriesDatum) + #get widget related to TSD + tsdviews = [t for t in self.TSDViews if t.TSD == TSD] + if len(tsdviews) > 0: + i = self.TSDViews.index(tsdviews[0])+1.5 + n = len(self.TSDViews) + + scrollBar = self.TSV.ui.scrollAreaSubsets.horizontalScrollBar() + smin = scrollBar.minimum() + smax = scrollBar.maximum() + v = smin + (smax - smin) * float(i) / n + scrollBar.setValue(int(round(v))) + + def addBandView(self, bandView): + assert isinstance(bandView, BandView) + + w = self.L.parentWidget() + w.setUpdatesEnabled(False) + + for tsdv in self.TSDViews: + tsdv.ui.setUpdatesEnabled(False) + + for tsdv in self.TSDViews: + tsdv.insertBandView(bandView) + + for tsdv in self.TSDViews: + tsdv.ui.setUpdatesEnabled(True) + + w.setUpdatesEnabled(True) + + + + def removeBandView(self, bandView): + assert isinstance(bandView, BandView) + for tsdv in self.TSDViews: + tsdv.removeBandView(bandView) + + + + def createTSDView(self, TSD): + assert isinstance(TSD, TimeSeriesDatum) + tsdView = TimeSeriesDatumView(TSD) + tsdView.setSubsetSize(self.subsetSize) + tsdView.setSpatialExtent(self.extent) + self.addTSDView(tsdView) + + + def removeTSD(self, TSD): + assert isinstance(TSDV, TimeSeriesDatum) + tsdvs = [tsdv for tsdv in self.TSDViews if tsdv.TSD == TSD] + assert len(tsdvs) == 1 + self.removeTSDView(tsdvs[0]) + + def removeTSDView(self, TSDV): + assert isinstance(TSDV, TimeSeriesDatumView) + self.TSDViews.remove(TSDV) + + def addTSDView(self, TSDV): + assert isinstance(TSDV, TimeSeriesDatumView) + + if len(self.TSDViews) < 10: + pass + + bisect.insort(self.TSDViews, TSDV) + + TSDV.ui.setParent(self.L.parentWidget()) + self.L.addWidget(TSDV.ui) + + self.adjustScrollArea() + #self.TSV.ui.scrollAreaSubsetContent.update() + #self.TSV.ui.scrollAreaSubsets.update() + s = "" + + + class BandView(QObject): sigAddBandView = pyqtSignal(object) sigRemoveBandView = pyqtSignal(object) - def __init__(self, TS, recommended_bands=None, parent=None, showSensorNames=True): + def __init__(self, recommended_bands=None, parent=None, showSensorNames=True): super(BandView, self).__init__() self.ui = widgets.BandViewUI(parent) self.ui.create() @@ -233,17 +366,9 @@ class BandView(QObject): self.ui.actionAddBandView.triggered.connect(lambda : self.sigAddBandView.emit(self)) self.ui.actionRemoveBandView.triggered.connect(lambda: self.sigRemoveBandView.emit(self)) - assert type(TS) is TimeSeries self.sensorViews = collections.OrderedDict() - self.TS = TS - self.TS.sigSensorAdded.connect(self.addSensor) - self.TS.sigChanged.connect(self.removeSensor) - self.mShowSensorNames = showSensorNames - for sensor in self.TS.Sensors: - self.addSensor(sensor) - def setTitle(self, title): self.ui.labelViewName.setText(title) @@ -257,9 +382,16 @@ class BandView(QObject): def removeSensor(self, sensor): assert type(sensor) is SensorInstrument - assert sensor in self.sensorViews.keys() - self.sensorViews[sensor].close() - self.sensorViews.pop(sensor) + if sensor in self.sensorViews.keys(): + self.sensorViews[sensor].close() + self.sensorViews.pop(sensor) + return True + else: + return False + + def hasSensor(self, sensor): + assert type(sensor) is SensorInstrument + return sensor in self.sensorViews.keys() def addSensor(self, sensor): """ @@ -271,14 +403,173 @@ class BandView(QObject): w = widgets.ImageChipViewSettings(sensor) w.showSensorName(self.mShowSensorNames) self.sensorViews[sensor] = w - i = self.ui.mainLayout.count()-1 - self.ui.mainLayout.insertWidget(i, w.ui) - s = "" + l = self.ui.sensorList.layout() + i = l.count() #last widget is a spacer + l.insertWidget(i-1, w.ui) + def getSensorWidget(self, sensor): assert type(sensor) is SensorInstrument return self.sensorViews[sensor] + + +class BandViewManager(QObject): + + sigSensorAdded = pyqtSignal(SensorInstrument) + sigSensorRemoved = pyqtSignal(SensorInstrument) + sigBandViewAdded = pyqtSignal(BandView) + sigBandViewRemoved = pyqtSignal(BandView) + + def __init__(self, timeSeriesViewer): + assert isinstance(timeSeriesViewer, TimeSeriesViewer) + super(BandViewManager, self).__init__() + + self.TSV = timeSeriesViewer + self.bandViews = [] + + def removeSensor(self, sensor): + assert isinstance(sensor, SensorInstrument) + + removed = False + for view in self.bandViews: + removed = removed and view.removeSensor(sensor) + + if removed: + self.sigSensorRemoved(sensor) + + + def createBandView(self): + bandView = BandView(parent=self.TSV.ui.scrollAreaBandViewsContent, showSensorNames=False) + bandView.sigAddBandView.connect(self.createBandView) + bandView.sigRemoveBandView.connect(self.removeBandView) + + self.addBandView(bandView) + + def addBandView(self, bandView): + assert isinstance(bandView, BandView) + l = self.TSV.BVP + self.bandViews.append(bandView) + l.addWidget(bandView.ui) + n = len(self) + + for sensor in self.TSV.TS.Sensors: + bandView.addSensor(sensor) + + bandView.showSensorNames(n == 1) + bandView.setTitle('#{}'.format(n)) + self.sigBandViewAdded.emit(bandView) + + def removeBandView(self, bandView): + assert isinstance(bandView, BandView) + self.bandViews.remove(bandView) + self.TSV.BVP.removeWidget(bandView.ui) + bandView.ui.close() + #self.TSV.ui.scrollAreaBandViewsContent.update() + + if len(self.bandViews) > 0: + self.bandViews[0].showSensorNames(True) + for i, bv in enumerate(self.bandViews): + bv.setTitle('#{}'.format(i + 1)) + + self.sigBandViewRemoved.emit(bandView) + + def __len__(self): + return len(self.bandViews) + + def __iter__(self): + return iter(self.bandViews) + + def __getitem__(self, key): + return self.bandViews[key] + + def __contains__(self, bandView): + return bandView in self.bandViews + + +class TimeSeriesDatumView(QObject): + def __init__(self, TSD, parent=None): + + super(TimeSeriesDatumView, self).__init__() + self.ui = widgets.TimeSeriesDatumViewUI(parent) + self.ui.create() + + self.TSD = None + self.setTimeSeriesDatum(TSD) + self.bandViewCanvases = [] + self.L = self.ui.layout() + self.wOffset = self.L.count()-1 + self.setSubsetSize(QSize(300,300)) + + + def setSpatialExtent(self, extent): + + assert isinstance(extent, QgsRectangle) + for t in self.bandViewCanvases: + c = t[1] + c.setExtent(extent) + + def setSubsetSize(self, size): + assert isinstance(size, QSize) + assert size.width() > 5 and size.height() > 5 + self.subsetSize = size + m = self.L.contentsMargins() + + self.ui.labelTitle.setFixedWidth(size.width()) + self.ui.line.setFixedWidth(size.width()) + + #apply new subset size to existing canvases + for c in self.ui.findChildren(widgets.BandViewMapCanvas): + c.setFixedSize(size) + + self.ui.setFixedWidth(size.width() + 2*(m.left() + m.right())) + n = len(self.bandViewCanvases) + #todo: improve size forecast + self.ui.setMinimumHeight((n+1) * size.height()) + + + def setTimeSeriesDatum(self, TSD): + assert isinstance(TSD, TimeSeriesDatum) + self.TSD = TSD + self.ui.labelTitle.setText(str(TSD.date)) + + for c in self.ui.findChildren(widgets.BandViewMapCanvas): + c.setLayer(self.TSD.pathImg) + + def removeBandView(self, bandView): + toRemove = self.findItems(bandView) + for t in toRemove: + self.bandViewCanvases.remove(t) + bandView, canvas = t + self.L.removeWidget(canvas) + canvas.close() + + + def findItems(self, item): + if isinstance(item, BandView): + return [t for t in self.bandViewCanvases if t[0] == item] + if isinstance(item, widgets.BandViewMapCanvas): + return [t for t in self.bandViewCanvases if t[1] == item] + + + def insertBandView(self, bandView, i=-1): + assert isinstance(bandView, BandView) + assert len(self.findItems(bandView)) == 0 + assert i >= -1 and i <= len(self.bandViewCanvases) + if i == -1: + i = len(self.bandViewCanvases) + + canvas = widgets.BandViewMapCanvas(self.ui) + canvas.setLayer(self.TSD.pathImg) + canvas.setFixedSize(self.subsetSize) + self.bandViewCanvases.insert(i, (bandView, canvas)) + self.L.insertWidget(self.wOffset + i, canvas) + + def __lt__(self, other): + + return self.TSD < other.TSD + + class RenderJob(object): def __init__(self, TSD, renderer, destinationId=None): @@ -739,10 +1030,16 @@ class TimeSeriesViewer: #init TS model TSM = TimeSeriesTableModel(self.TS) D = self.ui + self.ICP = D.scrollAreaSubsetContent.layout() + D.scrollAreaBandViewsContent.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) + self.BVP = self.ui.scrollAreaBandViewsContent.layout() + D.tableView_TimeSeries.setModel(TSM) D.tableView_TimeSeries.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) - self.BAND_VIEWS = list() + self.bandViewManager = BandViewManager(self) + self.timeSeriesViewManager = TimeSeriesDatumViewManager(self) + self.ImageChipBuffer = ImageChipBuffer() self.PIXMAPS = PixmapBuffer() self.PIXMAPS.sigPixmapCreated.connect(self.showSubset) @@ -757,8 +1054,7 @@ class TimeSeriesViewer: D.actionSelectCenter.triggered.connect(self.ua_selectByCoordinate) D.actionSelectArea.triggered.connect(self.ua_selectByRectangle) - D.actionAddBandView.triggered.connect(self.ua_addBandView) - #D.actionRemoveBandView.triggered.connect(self.ua_removeBandView) + D.actionAddBandView.triggered.connect(self.bandViewManager.createBandView) D.actionAddTSD.triggered.connect(self.ua_addTSImages) D.actionRemoveTSD.triggered.connect(self.ua_removeTSD) @@ -780,8 +1076,10 @@ class TimeSeriesViewer: D.sliderDOI.valueChanged.connect(self.setDOI) - D.spinBox_ncpu.setRange(0, multiprocessing.cpu_count()) + D.actionSetSubsetSize.triggered.connect(lambda : self.timeSeriesViewManager.setSubsetSize( + self.ui.subsetSize())) + D.actionSetExtent.triggered.connect(lambda: self.timeSeriesViewManager.setExtent(self.ui.extent())) self.RectangleMapTool = None self.PointMapTool = None @@ -798,9 +1096,6 @@ class TimeSeriesViewer: D.btnSelectArea.setEnabled(False) #self.RectangleMapTool.connect(self.ua_selectByRectangle_Done) - self.ICP = D.scrollAreaSubsetContent.layout() - D.scrollAreaBandViewsContent.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) - self.BVP = self.ui.scrollAreaBandViewsContent.layout() self.check_enabled() s = "" @@ -820,9 +1115,19 @@ class TimeSeriesViewer: ui.sliderDOI.setValue(v) def setDOI(self, i): - TSD = self.TS.data[i-1] - self.ui.labelDOI.setText(str(TSD.date)) - s = "" + + TSD = None + + if i is None or i > len(self.TS): + text = '<empty timeseries>' + else: + TSD = self.TS.data[i - 1] + text = str(TSD.date) + + self.ui.labelDOI.setText(text) + + if TSD: + self.timeSeriesViewManager.navToDOI(TSD) @staticmethod def icon(): @@ -841,8 +1146,14 @@ class TimeSeriesViewer: self.hasInitialCenterPoint = True if len(self.TS.data) == 0: self.hasInitialCenterPoint = False + else: + if len(self.bandViewManager) == 0: + # add two empty band-views by default + self.ua_addBandView() + self.ua_addBandView() - def ua_loadTSFile(self, path=None): + + def ua_loadTSFile(self, path=None, n_max=None): if path is None or path is False: path = QFileDialog.getOpenFileName(self.ui, 'Open Time Series file', '') @@ -852,7 +1163,7 @@ class TimeSeriesViewer: M = self.ui.tableView_TimeSeries.model() M.beginResetModel() self.ua_clear_TS() - self.TS.loadFromFile(path) + self.TS.loadFromFile(path, n_max=n_max) M.endResetModel() self.check_enabled() @@ -941,7 +1252,7 @@ class TimeSeriesViewer: self.setCanvasSRS(TSD.lyrImg.crs()) if self.ui.spinBox_coordinate_x.value() == 0.0 and \ self.ui.spinBox_coordinate_y.value() == 0.0: - bbox = self.TS.getMaxExtent(srs=self.canvas_srs) + bbox = self.TS.getMaxExtent(srs=self.canvasCrs) self.ui.spinBox_coordinate_x.setRange(bbox.xMinimum(), bbox.xMaximum()) self.ui.spinBox_coordinate_y.setRange(bbox.yMinimum(), bbox.yMaximum()) @@ -957,7 +1268,7 @@ class TimeSeriesViewer: def check_enabled(self): D = self.ui hasTS = len(self.TS) > 0 or DEBUG - hasTSV = len(self.BAND_VIEWS) > 0 + hasTSV = len(self.bandViewManager) > 0 hasQGIS = qgis_available #D.tabWidget_viewsettings.setEnabled(hasTS) @@ -1086,7 +1397,7 @@ class TimeSeriesViewer: self.ICP.addWidget(textLabel, 0, cnt_chips) viewList = list() j = 1 - for view in self.BAND_VIEWS: + for view in self.bandViewManager: viewWidget = view.getSensorWidget(TSD.sensor) layerRenderer = viewWidget.layerRenderer() @@ -1123,7 +1434,7 @@ class TimeSeriesViewer: #(TSD, [renderers] in order of views) LUT_RENDERER = {} - for view in self.BAND_VIEWS: + for view in self.bandViewManager: for sensor in view.Sensors.keys(): if sensor not in LUT_RENDERER.keys(): LUT_RENDERER[sensor] = [] @@ -1196,26 +1507,10 @@ class TimeSeriesViewer: def ua_addBandView(self): - - bandView = BandView(self.TS, showSensorNames=len(self.BAND_VIEWS)==0) - bandView.sigAddBandView.connect(self.ua_addBandView) - bandView.sigRemoveBandView.connect(self.ua_removeBandView) - - #bandView.removeView.connect(self.ua_removeBandView) - self.BAND_VIEWS.append(bandView) - self.BVP.addWidget(bandView.ui) - - bandView.setTitle('#{}'.format(len(self.BAND_VIEWS))) + self.bandViewManager.createBandView() def ua_removeBandView(self, bandView): - #bandView.ui.setHidden(True) - self.BAND_VIEWS.remove(bandView) - self.BVP.removeWidget(bandView.ui) - bandView.ui.close() - self.BAND_VIEWS[0].showSensorNames(True) - for i, bv in enumerate(self.BAND_VIEWS): - bv.setTitle('#{}'.format(i+1)) - #self.ui.scrollAreaBandViewsContent.update() + self.bandViewManager.removeBandView(bandView) diff --git a/timeseriesviewer/tests.py b/timeseriesviewer/tests.py index 00255569..3773ad65 100644 --- a/timeseriesviewer/tests.py +++ b/timeseriesviewer/tests.py @@ -48,7 +48,7 @@ def test_gui(): S = TimeSeriesViewer(None) S.run() - S.ua_loadTSFile(path=PATH_EXAMPLE_TIMESERIES) + S.ua_loadTSFile(path=PATH_EXAMPLE_TIMESERIES, n_max=10) pass def test_component(): diff --git a/timeseriesviewer/timeseries.py b/timeseriesviewer/timeseries.py index 2c1903f6..8753788b 100644 --- a/timeseriesviewer/timeseries.py +++ b/timeseriesviewer/timeseries.py @@ -400,8 +400,14 @@ class TimeSeriesDatum(QObject): def __cmp__(self, other): return cmp(str((self.date, self.sensor)), str((other.date, other.sensor))) - def __eq__(self, other): - return self.date == other.date and self.sensor == other.sensor + #def __eq__(self, other): + # return self.date == other.date and self.sensor == other.sensor + + def __lt__(self, other): + if self.date < other.date: + return True + else: + return self.sensor.sensorName < other.sensor.sensorName def __hash__(self): return hash((self.date,self.sensor.sensorName)) @@ -445,7 +451,7 @@ class TimeSeries(QObject): _sep = ';' - def loadFromFile(self, path): + def loadFromFile(self, path, n_max=None): images = [] masks = [] @@ -461,8 +467,12 @@ class TimeSeries(QObject): if len(parts) > 1: masks.append(parts[1]) - self.addFiles(images) - self.addMasks(masks) + if n_max: + n_max = min([len(images), n_max]) + self.addFiles(images[0:n_max]) + else: + self.addFiles(images) + #self.addMasks(masks) def saveToFile(self, path): @@ -490,7 +500,7 @@ class TimeSeries(QObject): def getMaxExtent(self, srs=None): if len(self.data) == 0: - return None + return QgsRectangle() if srs is None: srs = self.data[0].crs() diff --git a/timeseriesviewer/ui/bandview.ui b/timeseriesviewer/ui/bandview.ui index c6350804..8bc4779d 100644 --- a/timeseriesviewer/ui/bandview.ui +++ b/timeseriesviewer/ui/bandview.ui @@ -6,10 +6,15 @@ <rect> <x>0</x> <y>0</y> - <width>206</width> - <height>140</height> + <width>530</width> + <height>175</height> </rect> </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> + </property> <property name="windowTitle"> <string>Frame</string> </property> @@ -19,7 +24,7 @@ <property name="frameShadow"> <enum>QFrame::Plain</enum> </property> - <layout class="QHBoxLayout" name="mainLayout" stretch="0,0"> + <layout class="QVBoxLayout" name="verticalLayout"> <property name="spacing"> <number>0</number> </property> @@ -27,78 +32,89 @@ <number>0</number> </property> <item> - <layout class="QVBoxLayout" name="headerColumnLayout"> - <property name="spacing"> - <number>1</number> + <widget class="QFrame" name="sensorList"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> </property> - <item> - <widget class="QLabel" name="labelViewName"> - <property name="text"> - <string>#1</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnAddBandView"> - <property name="text"> - <string>...</string> - </property> - <property name="icon"> - <iconset resource="resources.qrc"> - <normaloff>:/timseriesviewer/icons/mActionAddBandView.png</normaloff>:/timseriesviewer/icons/mActionAddBandView.png</iconset> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnRemoveBandView"> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="icon"> - <iconset resource="resources.qrc"> - <normaloff>:/timseriesviewer/icons/mActionRemoveBandView.png</normaloff>:/timseriesviewer/icons/mActionRemoveBandView.png</iconset> - </property> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>0</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <widget class="QFrame" name="sensorRowLayout"> <property name="autoFillBackground"> <bool>true</bool> </property> <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> + <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> - <enum>QFrame::Raised</enum> + <enum>QFrame::Plain</enum> </property> <layout class="QHBoxLayout" name="horizontalLayout"> <property name="spacing"> - <number>1</number> + <number>5</number> </property> <property name="margin"> - <number>1</number> + <number>0</number> </property> + <item> + <layout class="QVBoxLayout" name="headerColumnLayout"> + <property name="spacing"> + <number>1</number> + </property> + <item> + <widget class="QLabel" name="labelViewName"> + <property name="text"> + <string>#1</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnAddBandView"> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timseriesviewer/icons/mActionAddBandView.png</normaloff>:/timseriesviewer/icons/mActionAddBandView.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnRemoveBandView"> + <property name="autoFillBackground"> + <bool>false</bool> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timseriesviewer/icons/mActionRemoveBandView.png</normaloff>:/timseriesviewer/icons/mActionRemoveBandView.png</iconset> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> <item> <spacer name="horizontalSpacer"> <property name="orientation"> @@ -106,7 +122,7 @@ </property> <property name="sizeHint" stdset="0"> <size> - <width>171</width> + <width>494</width> <height>20</height> </size> </property> @@ -115,6 +131,25 @@ </layout> </widget> </item> + <item> + <widget class="Line" name="bottomLine"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> </layout> <action name="actionRemoveBandView"> <property name="icon"> diff --git a/timeseriesviewer/ui/imagechipviewsettings.ui b/timeseriesviewer/ui/imagechipviewsettings.ui index 8e4a65eb..7ccb3646 100644 --- a/timeseriesviewer/ui/imagechipviewsettings.ui +++ b/timeseriesviewer/ui/imagechipviewsettings.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>285</width> - <height>137</height> + <width>263</width> + <height>154</height> </rect> </property> <property name="sizePolicy"> @@ -18,27 +18,27 @@ </property> <property name="font"> <font> - <pointsize>10</pointsize> + <pointsize>11</pointsize> </font> </property> <property name="windowTitle"> <string>GroupBox</string> </property> <property name="title"> - <string/> + <string><Sensor></string> </property> <property name="flat"> - <bool>false</bool> + <bool>true</bool> </property> <property name="checkable"> <bool>false</bool> </property> <layout class="QVBoxLayout" name="verticalLayout"> <property name="spacing"> - <number>0</number> + <number>1</number> </property> <property name="margin"> - <number>0</number> + <number>1</number> </property> <item> <layout class="QGridLayout" name="bandSelectionGridLayout" rowstretch="0,0,0,0,0,0" columnstretch="0,0,0,0"> @@ -147,7 +147,7 @@ </size> </property> <property name="text"> - <string/> + <string><RGB Bands></string> </property> </widget> </item> diff --git a/timeseriesviewer/ui/timseriesviewer.ui b/timeseriesviewer/ui/timseriesviewer.ui index abb8bde9..43c21c63 100644 --- a/timeseriesviewer/ui/timseriesviewer.ui +++ b/timeseriesviewer/ui/timseriesviewer.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>1095</width> - <height>760</height> + <height>820</height> </rect> </property> <property name="windowTitle"> @@ -41,10 +41,16 @@ <widget class="QScrollArea" name="scrollAreaSubsets"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> + <horstretch>1</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>200</width> + <height>200</height> + </size> + </property> <property name="frameShape"> <enum>QFrame::StyledPanel</enum> </property> @@ -55,23 +61,40 @@ <enum>Qt::ScrollBarAsNeeded</enum> </property> <property name="widgetResizable"> - <bool>false</bool> + <bool>true</bool> </property> <widget class="QWidget" name="scrollAreaSubsetContent"> <property name="geometry"> <rect> - <x>-110</x> + <x>0</x> <y>0</y> - <width>723</width> - <height>491</height> + <width>613</width> + <height>595</height> </rect> </property> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>200</width> + <height>200</height> + </size> + </property> + <layout class="QHBoxLayout" name="scrollAreaLayout" stretch=""> + <property name="spacing"> + <number>1</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetNoConstraint</enum> + </property> + <property name="margin"> + <number>0</number> + </property> + </layout> </widget> </widget> </item> @@ -99,7 +122,7 @@ <bool>true</bool> </property> <property name="currentIndex"> - <number>1</number> + <number>0</number> </property> <widget class="QWidget" name="tab_timeseries"> <attribute name="title"> @@ -228,16 +251,22 @@ <item> <widget class="QScrollArea" name="scrollAreaBandViews"> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <horstretch>1</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>100</width> + <height>100</height> + </size> + </property> <property name="autoFillBackground"> <bool>true</bool> </property> <property name="verticalScrollBarPolicy"> - <enum>Qt::ScrollBarAlwaysOn</enum> + <enum>Qt::ScrollBarAsNeeded</enum> </property> <property name="horizontalScrollBarPolicy"> <enum>Qt::ScrollBarAsNeeded</enum> @@ -250,7 +279,7 @@ <rect> <x>0</x> <y>0</y> - <width>1068</width> + <width>1083</width> <height>120</height> </rect> </property> @@ -432,7 +461,7 @@ </widget> </item> <item> - <widget class="QDoubleSpinBox" name="spinBox_coordinate_x"> + <widget class="QDoubleSpinBox" name="spinBoxExtentCenterX"> <property name="maximumSize"> <size> <width>100</width> @@ -464,7 +493,7 @@ </widget> </item> <item> - <widget class="QDoubleSpinBox" name="spinBox_coordinate_y"> + <widget class="QDoubleSpinBox" name="spinBoxExtentCenterY"> <property name="maximumSize"> <size> <width>100</width> @@ -528,7 +557,7 @@ </widget> </item> <item> - <widget class="QDoubleSpinBox" name="doubleSpinBox_subset_size_x"> + <widget class="QDoubleSpinBox" name="spinBoxExtentWidth"> <property name="minimumSize"> <size> <width>100</width> @@ -566,7 +595,7 @@ </widget> </item> <item> - <widget class="QDoubleSpinBox" name="doubleSpinBox_subset_size_y"> + <widget class="QDoubleSpinBox" name="spinBoxExtentHeight"> <property name="minimumSize"> <size> <width>100</width> @@ -624,7 +653,7 @@ </widget> </item> <item row="3" column="1"> - <widget class="QPlainTextEdit" name="tb_bb_srs"> + <widget class="QPlainTextEdit" name="textBoxCRSInfo"> <property name="enabled"> <bool>true</bool> </property> @@ -885,6 +914,8 @@ <addaction name="actionLoadTS"/> <addaction name="actionSaveTS"/> <addaction name="actionClearTS"/> + <addaction name="separator"/> + <addaction name="actionAddTSExample"/> </widget> <widget class="QMenu" name="menuAbout"> <property name="title"> @@ -905,265 +936,168 @@ <widget class="QDockWidget" name="dockRendering"> <property name="minimumSize"> <size> - <width>281</width> - <height>216</height> + <width>324</width> + <height>232</height> </size> </property> <property name="features"> <set>QDockWidget::AllDockWidgetFeatures</set> </property> + <property name="windowTitle"> + <string>Rendering</string> + </property> <attribute name="dockWidgetArea"> <number>1</number> </attribute> <widget class="QWidget" name="dockWidgetContents"> <layout class="QVBoxLayout" name="verticalLayout_5"> + <property name="spacing"> + <number>1</number> + </property> + <property name="margin"> + <number>1</number> + </property> <item> - <widget class="QGroupBox" name="groupBox_rendering"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> + <widget class="QFrame" name="frame_5"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> </property> - <property name="title"> - <string>Rendering</string> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> </property> - <layout class="QFormLayout" name="formLayout"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::AllNonFixedFieldsGrow</enum> - </property> - <property name="horizontalSpacing"> - <number>2</number> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <property name="spacing"> + <number>1</number> </property> - <property name="verticalSpacing"> - <number>5</number> + <property name="margin"> + <number>0</number> </property> - <property name="bottomMargin"> - <number>6</number> - </property> - <item row="0" column="0" colspan="2"> - <widget class="QRadioButton" name="rb_showEntireTS"> - <property name="text"> - <string>Entire Time Series</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QRadioButton" name="rb_showTimeWindow"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>Select </string> - </property> - <property name="text"> - <string>Time Window</string> - </property> - <property name="checked"> - <bool>false</bool> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QFrame" name="frame_timewindow"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="minimumSize"> - <size> - <width>50</width> - <height>0</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <property name="spacing"> - <number>2</number> - </property> - <property name="margin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="label_8"> - <property name="text"> - <string>before</string> - </property> - </widget> - </item> - <item> - <widget class="QFrame" name="frame_4"> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <property name="margin"> - <number>0</number> - </property> - </layout> - </widget> - </item> - <item> - <widget class="QSpinBox" name="sb_ndates_before"> - <property name="maximum"> - <number>9999</number> - </property> - <property name="value"> - <number>1</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_9"> - <property name="text"> - <string>after</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="sb_ndates_after"> - <property name="maximum"> - <number>9999</number> - </property> - <property name="value"> - <number>1</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_14"> - <property name="text"> - <string>DOI</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - </item> - <item row="3" column="0" colspan="2"> - <widget class="QFrame" name="frame_5"> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_9"> - <property name="margin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="label_11"> - <property name="text"> - <string>max. size </string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBoxMaxPixmapSize"> - <property name="minimumSize"> - <size> - <width>50</width> - <height>0</height> - </size> - </property> - <property name="toolTip"> - <string>Max. length of an image chip on screen.</string> - </property> - <property name="suffix"> - <string>px</string> - </property> - <property name="minimum"> - <number>20</number> - </property> - <property name="maximum"> - <number>1000</number> - </property> - <property name="singleStep"> - <number>50</number> - </property> - <property name="value"> - <number>300</number> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="text"> - <string>#cpu</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spinBox_ncpu"> - <property name="toolTip"> - <string>Number of CPUs used for parallel image chip calculation</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - </item> </layout> </widget> </item> <item> - <widget class="QGroupBox" name="groupBox"> - <property name="minimumSize"> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <property name="spacing"> + <number>1</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_11"> + <property name="text"> + <string>Subset size x</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinBoxSubsetSizeX"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="toolTip"> + <string>Max. length of an image chip on screen.</string> + </property> + <property name="specialValueText"> + <string/> + </property> + <property name="suffix"> + <string>px</string> + </property> + <property name="minimum"> + <number>20</number> + </property> + <property name="maximum"> + <number>1000</number> + </property> + <property name="singleStep"> + <number>25</number> + </property> + <property name="value"> + <number>100</number> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinBoxSubsetSizeY"> + <property name="suffix"> + <string>px</string> + </property> + <property name="prefix"> + <string/> + </property> + <property name="minimum"> + <number>20</number> + </property> + <property name="maximum"> + <number>1000</number> + </property> + <property name="singleStep"> + <number>25</number> + </property> + <property name="value"> + <number>50</number> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="checkBoxLockSubsetAspect"> + <property name="text"> + <string>lock aspect</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Progress</string> + </property> + </widget> + </item> + <item> + <widget class="QProgressBar" name="progressBar"> + <property name="value"> + <number>0</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> <size> - <width>0</width> - <height>0</height> + <width>20</width> + <height>40</height> </size> </property> - <property name="title"> - <string>Progress</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_10"> - <item> - <widget class="QProgressBar" name="progressBar"> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - </layout> - </widget> + </spacer> </item> </layout> </widget> @@ -1362,24 +1296,82 @@ <string>Remove all images from time series</string> </property> </action> + <action name="actionSetSubsetSize"> + <property name="text"> + <string>setSubsetSize</string> + </property> + </action> + <action name="actionSetExtent"> + <property name="text"> + <string>setExtent</string> + </property> + </action> </widget> <resources> <include location="resources.qrc"/> </resources> <connections> <connection> - <sender>rb_showTimeWindow</sender> - <signal>toggled(bool)</signal> - <receiver>frame_timewindow</receiver> - <slot>setEnabled(bool)</slot> + <sender>spinBoxExtentCenterX</sender> + <signal>valueChanged(double)</signal> + <receiver>actionSetExtent</receiver> + <slot>trigger()</slot> + <hints> + <hint type="sourcelabel"> + <x>125</x> + <y>150</y> + </hint> + <hint type="destinationlabel"> + <x>-1</x> + <y>-1</y> + </hint> + </hints> + </connection> + <connection> + <sender>spinBoxExtentCenterY</sender> + <signal>valueChanged(double)</signal> + <receiver>actionSetExtent</receiver> + <slot>trigger()</slot> + <hints> + <hint type="sourcelabel"> + <x>234</x> + <y>150</y> + </hint> + <hint type="destinationlabel"> + <x>-1</x> + <y>-1</y> + </hint> + </hints> + </connection> + <connection> + <sender>spinBoxExtentHeight</sender> + <signal>valueChanged(double)</signal> + <receiver>actionSetExtent</receiver> + <slot>trigger()</slot> + <hints> + <hint type="sourcelabel"> + <x>234</x> + <y>175</y> + </hint> + <hint type="destinationlabel"> + <x>-1</x> + <y>-1</y> + </hint> + </hints> + </connection> + <connection> + <sender>spinBoxExtentWidth</sender> + <signal>valueChanged(double)</signal> + <receiver>actionSetExtent</receiver> + <slot>trigger()</slot> <hints> <hint type="sourcelabel"> - <x>101</x> - <y>437</y> + <x>125</x> + <y>175</y> </hint> <hint type="destinationlabel"> - <x>451</x> - <y>445</y> + <x>-1</x> + <y>-1</y> </hint> </hints> </connection> diff --git a/timeseriesviewer/ui/widgets.py b/timeseriesviewer/ui/widgets.py index b086bbe4..c37ace76 100644 --- a/timeseriesviewer/ui/widgets.py +++ b/timeseriesviewer/ui/widgets.py @@ -34,6 +34,7 @@ PATH_MAIN_UI = jp(DIR_UI, 'timseriesviewer.ui') PATH_BANDVIEWSETTINGS_UI = jp(DIR_UI, 'bandviewsettings.ui') PATH_IMAGECHIPVIEWSETTINGS_UI = jp(DIR_UI, 'imagechipviewsettings.ui') PATH_BANDVIEW_UI = jp(DIR_UI, 'bandview.ui') +PATH_TSDVIEW_UI = jp(DIR_UI, 'timeseriesdatumview.ui') class TimeSeriesViewerUI(QMainWindow, loadUIFormClass(PATH_MAIN_UI)): @@ -47,9 +48,11 @@ class TimeSeriesViewerUI(QMainWindow, # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) + self.mCrs = None - #connect buttons with actions - self.btnCRS.setDefaultAction(self.actionSelectCRS) + #set button default actions -> this will show the action icons as well + #I don't know why this is not possible in the QDesigner when QToolButtons are + #placed outside a toolbar self.btnSelectArea.setDefaultAction(self.actionSelectArea) self.btnSelectCenterCoordinate.setDefaultAction(self.actionSelectCenter) @@ -64,6 +67,94 @@ class TimeSeriesViewerUI(QMainWindow, self.btnSaveTS.setDefaultAction(self.actionSaveTS) self.btnClearTS.setDefaultAction(self.actionClearTS) + #define subset-size behaviour + self.spinBoxSubsetSizeX.valueChanged.connect(lambda: self.onSubsetValueChanged('X')) + self.spinBoxSubsetSizeY.valueChanged.connect(lambda: self.onSubsetValueChanged('Y')) + self.lastSubsetSizeX = self.spinBoxSubsetSizeX.value() + self.lastSubsetSizeY = self.spinBoxSubsetSizeY.value() + + + def crs(self): + return self.mCrs + pass + + sigCrsChanged = pyqtSignal(QgsCoordinateReferenceSystem) + def setCrs(self, crs): + assert isinstance(crs, QgsCoordinateReferenceSystem) + old = self.mCrs + self.mCrs = crs + self.textBoxCRSInfo.setText(crs.toWkt()) + if self.mCrs != old: + self.sigCrsChanged.emit(crs) + + + def extent(self): + width = QgsVector(self.spinBoxExtentWidth.value(), 0.0) + height = QgsVector(0.0, self.spinBoxExtentHeight.value()) + + Center = QgsPoint(self.spinBoxExtentCenterX.value(), self.spinBoxExtentCenterY.value()) + UL = Center - (width * 0.5) + (height * 0.5) + LR = Center + (width * 0.5) - (height * 0.5) + return QgsRectangle(UL, LR) + + sigExtentChanged = pyqtSignal(QgsRectangle) + def setExtent(self, extent): + old = self.extent() + assert isinstance(extent, QgsRectangle) + center = extent.center() + self.spinBoxExtentCenterX.setValue(center.x()) + self.spinBoxExtentCenterY.setValue(center.y()) + self.spinBoxExtentWidth.setValue(extent.width()) + self.spinBoxExtentHeight.setValue(extent.height()) + + if old != extent: + self.sigExtentChanged.emit(extent) + + sigSubsetSizeChanged = pyqtSignal(QSize) + + def setSubsetSize(self, size): + old = self.subsetSize() + self.spinBoxSubsetSizeX.setValue(size.width()) + self.spinBoxSubsetSizeY.setValue(size.height()) + + if old != size: + self.sigSubsetSizeChanged(size) + + def subsetSize(self): + return QSize(self.spinBoxSubsetSizeX.value(), + self.spinBoxSubsetSizeY.value()) + + + def setProgress(self, value, valueMax=None, valueMin=0): + p = self.progressBar + if valueMin is not None and valueMin != self.progessBar.minimum(): + p.setMinimum(valueMin) + if valueMax is not None and valueMax != self.progessBar.maximum(): + p.setMaximum(valueMax) + self.progressBar.setValue(value) + + + def onSubsetValueChanged(self, key): + if self.checkBoxLockSubsetAspect.isChecked(): + + if key == 'X': + v_old = self.lastSubsetSizeX + v_new = self.spinBoxSubsetSizeX.value() + s = self.spinBoxSubsetSizeY + elif key == 'Y': + v_old = self.lastSubsetSizeY + v_new = self.spinBoxSubsetSizeY.value() + s = self.spinBoxSubsetSizeX + + oldState = s.blockSignals(True) + s.setValue(int(round(float(v_new) / v_old * s.value()))) + s.blockSignals(oldState) + + self.lastSubsetSizeX = self.spinBoxSubsetSizeX.value() + self.lastSubsetSizeY = self.spinBoxSubsetSizeY.value() + + self.actionSetSubsetSize.activate(QAction.Trigger) + class VerticalLabel(QLabel): def __init__(self, text, orientation='vertical', forceWidth=True): QLabel.__init__(self, text) @@ -136,7 +227,33 @@ class BandViewUI(QFrame, loadUIFormClass(PATH_BANDVIEW_UI)): self.btnAddBandView.setDefaultAction(self.actionAddBandView) +class TimeSeriesDatumViewUI(QFrame, loadUIFormClass(PATH_TSDVIEW_UI)): + def __init__(self, title='<#>', parent=None): + super(TimeSeriesDatumViewUI, self).__init__(parent) + + self.emptyHeight = self.height() + self.setupUi(self) + + def sizeHint(self): + w = self.minimumWidth() + canvases = self.findChildren(BandViewMapCanvas) + h = self.emptyHeight + len(canvases) * w + return QSize(w,h) + +class LineWidget(QFrame): + + def __init__(self, parent=None, orientation='horizontal'): + super(LineWidget, self).__init__(parent) + + self.setFrameShadow(QFrame.Sunken) + self.setFixedHeight(3) + self.setStyleSheet("background-color: #c0c0c0;") + self.orientation = orientation + if self.orientation == 'horizontal': + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + else: + self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) class ImageChipViewSettingsUI(QGroupBox, loadUIFormClass(PATH_IMAGECHIPVIEWSETTINGS_UI)): @@ -158,6 +275,35 @@ class ImageChipViewSettingsUI(QGroupBox, self.btn453.setDefaultAction(self.actionSet453) +class BandViewMapCanvas(QgsMapCanvas): + + def __init__(self, parent=None): + super(BandViewMapCanvas, self).__init__(parent) + self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.lyr = None + self.renderer = None + self.registry = QgsMapLayerRegistry.instance() + + def setLayer(self, uri): + assert isinstance(uri, str) + + self.setLayerSet([]) + if self.lyr is not None: + #de-register layer + self.registry.removeMapLayer(self.lyr) + + self.lyr = QgsRasterLayer(uri) + self.lyr.setRenderer(self.renderer) + self.registry.addMapLayer(self.lyr, False) + + lset = [QgsMapCanvasLayer(self.lyr)] + self.setLayerSet(lset) + + def setRenderer(self, renderer): + s = "" + self.renderer = renderer.clone() + + class ImageChipViewSettings(QObject): @@ -165,8 +311,8 @@ class ImageChipViewSettings(QObject): #define signals - - removeView = pyqtSignal() + sigRendererChanged = pyqtSignal(QgsRasterRenderer) + sigRemoveView = pyqtSignal() def __init__(self, sensor, parent=None): """Constructor.""" @@ -187,7 +333,7 @@ class ImageChipViewSettings(QObject): for sl in self.sliders: sl.setMinimum(1) sl.setMaximum(sensor.nb) - sl.valueChanged.connect(self.bandSelectionChanged) + sl.valueChanged.connect(self.layerRendererChanged) self.ceAlgs = [("No enhancement", QgsContrastEnhancement.NoEnhancement), ("Stretch to MinMax", QgsContrastEnhancement.StretchToMinimumMaximum), @@ -239,13 +385,14 @@ class ImageChipViewSettings(QObject): for i, b in enumerate(bands): self.sliders[i].setValue(b) + #slider value change emits signal -> no emit required here def rgb(self): return [self.ui.sliderRed.value(), self.ui.sliderGreen.value(), self.ui.sliderBlue.value()] - def bandSelectionChanged(self, *args): + def setRenderInfo(self, *args): rgb = self.rgb() text = 'RGB {}-{}-{}'.format(*rgb) @@ -253,10 +400,7 @@ class ImageChipViewSettings(QObject): text += ' ({} {})'.format( ','.join(['{:0.2f}'.format(self.sensor.wavelengths[b-1]) for b in rgb]), self.sensor.wavelengthUnits) - - self.ui.labelBands.setText(text) - s = "" def setLayerRenderer(self, renderer): ui = self.ui @@ -273,8 +417,11 @@ class ImageChipViewSettings(QObject): algs = [i[1] for i in self.ceAlgs] ui.comboBoxContrastEnhancement.setCurrentIndex(algs.index(ceRed.contrastEnhancementAlgorithm())) + self.layerRendererChanged() - + def layerRendererChanged(self): + self.setRenderInfo() + self.sigRendererChanged.emit(self.layerRenderer()) def layerRenderer(self): ui = self.ui @@ -310,7 +457,7 @@ class ImageChipViewSettings(QObject): #add general options action = menu.addAction('Remove Band View') action.setToolTip('Removes this band view') - action.triggered.connect(lambda : self.removeView.emit()) + action.triggered.connect(lambda : self.sigRemoveView.emit()) #add QGIS specific options txt = QApplication.clipboard().text() if re.search('<!DOCTYPE(.|\n)*rasterrenderer.*type="multibandcolor"', txt) is not None: -- GitLab