From aba592ab0127964697ae2ed70525303d38a2ad4c Mon Sep 17 00:00:00 2001 From: "benjamin.jakimow" <benjamin.jakimow@geo.hu-berlin.de> Date: Thu, 4 Jul 2019 08:04:03 +0200 Subject: [PATCH] MapCanvas context menu lists layers under cursor position only refactoring Loading Progress dialog and TimeSeriesTreeView context menu increased version to 1.5 --- CHANGELOG | 3 + eotimeseriesviewer/__init__.py | 2 +- eotimeseriesviewer/main.py | 56 +++++-------- eotimeseriesviewer/mapcanvas.py | 10 ++- eotimeseriesviewer/mapviewscrollarea.py | 6 ++ eotimeseriesviewer/timeseries.py | 98 ++++++++++++++--------- eotimeseriesviewer/ui/timeseriesdock.ui | 4 +- eotimeseriesviewer/ui/timeseriesviewer.ui | 14 ++-- 8 files changed, 104 insertions(+), 89 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a718c9f6..fd3d1614 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ ============== Changelog ============== +2019-07-04 (version 1.5): + * + 2019-07-02 (version 1.4): * adding vector layers with sublayers will add all sublayers * map canvas context menu "Focus on Spatial Extent" will hide maps without time series data for the current spatial extent diff --git a/eotimeseriesviewer/__init__.py b/eotimeseriesviewer/__init__.py index dc91c11e..92e93bd1 100644 --- a/eotimeseriesviewer/__init__.py +++ b/eotimeseriesviewer/__init__.py @@ -21,7 +21,7 @@ # noinspection PyPep8Naming -__version__ = '1.4' # sub-subversion number is added automatically +__version__ = '1.5' # sub-subversion number is added automatically LICENSE = 'GNU GPL-3' TITLE = 'EO Time Series Viewer' DESCRIPTION = 'Visualization of multi-sensor Earth observation time series data.' diff --git a/eotimeseriesviewer/main.py b/eotimeseriesviewer/main.py index 0241097a..74433728 100644 --- a/eotimeseriesviewer/main.py +++ b/eotimeseriesviewer/main.py @@ -140,12 +140,13 @@ class TimeSeriesViewerUI(QMainWindow, area = None - def addDockWidget(dock): + def addDockWidget(dock:QDockWidget): """ shortcut to add a created dock and return it :param dock: :return: """ + dock.setParent(self) self.addDockWidget(area, dock) return dock @@ -179,8 +180,8 @@ class TimeSeriesViewerUI(QMainWindow, area = Qt.BottomDockWidgetArea - panel = SpectralLibraryPanel(None) - panel.setParent(self) + panel = SpectralLibraryPanel(self) + self.dockSpectralLibrary = addDockWidget(panel) self.tabifyDockWidget(self.dockTimeSeries, self.dockSpectralLibrary) @@ -415,7 +416,7 @@ class TimeSeriesViewer(QgisInterface, QObject): self.ui.actionLoadTS.triggered.connect(self.loadTimeSeriesDefinition) self.ui.actionClearTS.triggered.connect(self.clearTimeSeries) self.ui.actionSaveTS.triggered.connect(self.saveTimeSeriesDefinition) - self.ui.actionAddTSExample.triggered.connect(self.loadExampleTimeSeries) + self.ui.actionAddTSExample.triggered.connect(lambda : self.loadExampleTimeSeries(loadAsync=False)) self.ui.actionLoadTimeSeriesStack.triggered.connect(self.loadTimeSeriesStack) self.ui.actionShowCrosshair.toggled.connect(self.spatialTemporalVis.setCrosshairVisibility) self.ui.actionExportMapsToImages.triggered.connect(lambda: self.exportMapsToImages()) @@ -896,25 +897,8 @@ class TimeSeriesViewer(QgisInterface, QObject): """ return self.ui.messageBar - def loadImageFiles(self, files:list): - """ - Loads image files to the time series. - :param files: [list-of-file-paths] - """ - assert isinstance(files, list) - - progressDialog = QProgressDialog(parent=self.ui) - progressDialog.setWindowTitle('Load data') - progressDialog.setWindowFlag(Qt.WindowContextHelpButtonHint, False) - progressDialog.show() - QApplication.processEvents() - self.mTimeSeries.addSources(files, progressDialog=progressDialog) - - progressDialog.hide() - progressDialog.setParent(None) - - def loadTimeSeriesDefinition(self, path:str=None, n_max:int=None, progressDialog:QProgressDialog=None): + def loadTimeSeriesDefinition(self, path:str=None, n_max:int=None): """ Loads a time series definition file :param path: @@ -936,21 +920,12 @@ class TimeSeriesViewer(QgisInterface, QObject): if path is not None and os.path.exists(path): s.setValue('file_ts_definition', path) - - b = isinstance(progressDialog, QProgressDialog) - - if not b: - progressDialog = QProgressDialog(parent=self.ui) - progressDialog.setWindowTitle('Load data') - progressDialog.setWindowFlag(Qt.WindowContextHelpButtonHint, False) - progressDialog.show() - self.clearTimeSeries() + progressDialog = QProgressDialog(self.ui) + progressDialog.setWindowTitle('Image Loading') + progressDialog.setMinimumDuration(2000) self.mTimeSeries.loadFromFile(path, n_max=n_max, progressDialog=progressDialog) - if not b: - progressDialog.hide() - progressDialog.setParent(None) def createMapView(self, name:str=None): """ @@ -1192,14 +1167,19 @@ class TimeSeriesViewer(QgisInterface, QObject): dn = os.path.dirname(files[0]) s.setValue('dir_datasources', dn) - if files: + progressDialog = QProgressDialog('Images Loading', 'Cancel', 0, len(files), parent=self.ui) + progressDialog.setLabelText('Start loading {} images....'.format(len(files))) + progressDialog.setMinimumDuration(2000) + progressDialog.setValue(0) + if loadAsync: - self.mTimeSeries.addSourcesAsync(files) + self.mTimeSeries.addSourcesAsync(files, progressDialog=progressDialog) else: - self.mTimeSeries.addSources(files) + self.mTimeSeries.addSources(files, progressDialog=progressDialog) + - QCoreApplication.processEvents() + #QCoreApplication.processEvents() #self.mTimeSeries.addSources(files) def clearTimeSeries(self): diff --git a/eotimeseriesviewer/mapcanvas.py b/eotimeseriesviewer/mapcanvas.py index 5d72a19f..aba98887 100644 --- a/eotimeseriesviewer/mapcanvas.py +++ b/eotimeseriesviewer/mapcanvas.py @@ -603,6 +603,8 @@ class MapCanvas(QgsMapCanvas): and SpatialExtent.fromLayer(l).toCrs(self.crs()).contains(pointGeo)] viewPortSensorLayers = [l for l in viewPortRasterLayers if isinstance(l, SensorProxyLayer)] + viewPortVectorLayers = [l for l in self.layers() if isinstance(l, QgsVectorLayer)] + refSensorLayer = None refRasterLayer = None @@ -678,8 +680,12 @@ class MapCanvas(QgsMapCanvas): menu.addSeparator() m = menu.addMenu('Layers...') - for mapLayer in self.layers(): - sub = m.addMenu(mapLayer.name()) + 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')) diff --git a/eotimeseriesviewer/mapviewscrollarea.py b/eotimeseriesviewer/mapviewscrollarea.py index 0eeaca69..e3f5e7df 100644 --- a/eotimeseriesviewer/mapviewscrollarea.py +++ b/eotimeseriesviewer/mapviewscrollarea.py @@ -45,3 +45,9 @@ class MapViewScrollArea(QScrollArea): diff = centerInParent - centerViewPort return diff.manhattanLength() + + def sizeHint(self): + parent = self.parent() + hint = super(MapViewScrollArea, self).sizeHint() + + return hint diff --git a/eotimeseriesviewer/timeseries.py b/eotimeseriesviewer/timeseries.py index 2eda86b1..9ed15d65 100644 --- a/eotimeseriesviewer/timeseries.py +++ b/eotimeseriesviewer/timeseries.py @@ -861,6 +861,12 @@ class TimeSeriesTreeView(QTreeView): tsd = self.model().data(idx, role=Qt.UserRole) menu = QMenu(self) + if isinstance(tsd, TimeSeriesDate): + a = menu.addAction('Show map for {}'.format(tsd.date())) + a.setToolTip('Shows the map related to this time series date.') + a.triggered.connect(lambda _, tsd=tsd: self.sigMoveToDateRequest.emit(tsd)) + menu.addSeparator() + a = menu.addAction('Copy value(s)') a.triggered.connect(lambda: self.onCopyValues()) a = menu.addAction('Hide date(s)') @@ -869,10 +875,7 @@ class TimeSeriesTreeView(QTreeView): a = menu.addAction('Show date(s)') a.setToolTip('Shows the selected time series dates.') a.triggered.connect(lambda: self.onSetCheckState(Qt.Unchecked)) - if isinstance(tsd, TimeSeriesDate): - a = menu.addAction('Show {} in map.'.format(tsd.date())) - a.setToolTip('Shows the map related to this time series date.') - a.triggered.connect(lambda _, tsd=tsd: self.sigMoveToDateRequest.emit(tsd)) + menu.popup(QCursor.pos()) @@ -950,7 +953,7 @@ class TimeSeries(QAbstractItemModel): sigTimeSeriesDatesAdded = pyqtSignal(list) sigTimeSeriesDatesRemoved = pyqtSignal(list) - sigLoadingProgress = pyqtSignal(int, int, str) + #sigLoadingProgress = pyqtSignal(int, int, str) sigSensorAdded = pyqtSignal(SensorInstrument) @@ -970,6 +973,8 @@ class TimeSeries(QAbstractItemModel): self.mShape = None self.mDateTimePrecision = DateTimePrecision.Original + self.mLoadingProgressDialog = None + self.mCurrentDates = [] self.mCurrentSpatialExtent = None @@ -1080,12 +1085,19 @@ class TimeSeries(QAbstractItemModel): if len(parts) > 1: masks.append(parts[1]) - if n_max: n_max = min([len(images), n_max]) - self.addSourcesAsync(images[0:n_max], progressDialog=progressDialog) - else: - self.addSourcesAsync(images, progressDialog=progressDialog) + images = images[0:n_max] + + if isinstance(progressDialog, QProgressDialog): + progressDialog.setMaximum(len(images)) + progressDialog.setMinimum(0) + progressDialog.setValue(0) + progressDialog.setLabelText('Start loading {} images....'.format(len(images))) + + self.addSourcesAsync(images, progressDialog=progressDialog) + + #self.addMasks(masks) @@ -1215,7 +1227,14 @@ class TimeSeries(QAbstractItemModel): removed = list() for tsd in tsds: + assert isinstance(tsd, TimeSeriesDate) + + tsd.sigSourcesRemoved.disconnect() + tsd.sigSourcesAdded.disconnect() + tsd.sigVisibilityChanged.disconnect() + tsd.sigRemoveMe.disconnect() + row = self.mTSDs.index(tsd) self.beginRemoveRows(self.mRootIndex, row, row) self.mTSDs.remove(tsd) @@ -1302,6 +1321,8 @@ class TimeSeries(QAbstractItemModel): assert isinstance(tm, QgsTaskManager) assert isinstance(nWorkers, int) and nWorkers >= 1 + self.mLoadingProgressDialog = progressDialog + # see https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks def chunks(l, n): @@ -1311,10 +1332,9 @@ class TimeSeries(QAbstractItemModel): n = int(len(sources) / nWorkers) for subset in chunks(sources, 50): - #for source in sources: - #subset = [source] + dump = pickle.dumps(subset) - #taskDescription = 'Load EOTSV {} sources {}'.format(len(subset), uuid.uuid4()) + taskDescription = 'Load {} images'.format(len(subset)) qgsTask = QgsTask.fromFunction(taskDescription, doLoadTimeSeriesSourcesTask, dump, on_finished=self.onAddSourcesAsyncFinished) tid = id(qgsTask) @@ -1322,7 +1342,7 @@ class TimeSeries(QAbstractItemModel): qgsTask.taskCompleted.connect(lambda *args, tid=tid: self.onRemoveTask(tid)) qgsTask.taskTerminated.connect(lambda *args, tid=tid: self.onRemoveTask(tid)) - if False: # for debugging + if False: # for debugging only resultDump = doLoadTimeSeriesSourcesTask(qgsTask, dump) self.onAddSourcesAsyncFinished(None, resultDump) else: @@ -1340,6 +1360,9 @@ class TimeSeries(QAbstractItemModel): dump = args[1] sources = pickle.loads(dump) for source in sources: + if isinstance(self.mLoadingProgressDialog, QProgressDialog): + self.increaseProgressBar() + newTSD = self._addSource(source) if isinstance(newTSD, TimeSeriesDate): addedDates.append(newTSD) @@ -1347,22 +1370,25 @@ class TimeSeries(QAbstractItemModel): if len(addedDates) > 0: self.sigTimeSeriesDatesAdded.emit(addedDates) - except Exception as ex: s = "" else: s = "" - #self._cleanTasks() - def _cleanTasks(self): - toRemove = [] - for task in self.mTasks: - if isinstance(task, QgsTask): - if task.status() in [QgsTask.Complete, QgsTask.Terminated]: - toRemove.append(task) + if isinstance(self.mLoadingProgressDialog, QProgressDialog): + if self.mLoadingProgressDialog.wasCanceled() or self.mLoadingProgressDialog.value() == -1: + self.mLoadingProgressDialog = None + + def increaseProgressBar(self): + if isinstance(self.mLoadingProgressDialog, QProgressDialog): + v = self.mLoadingProgressDialog.value() + 1 + self.mLoadingProgressDialog.setValue(v) + self.mLoadingProgressDialog.setLabelText('{}/{}'.format(v, self.mLoadingProgressDialog.maximum())) + + if v == 1 or v % 25 == 0: + QApplication.processEvents() + - for t in toRemove: - self.mTasks.remove(t) def addSources(self, sources:list, progressDialog:QProgressDialog=None): """ @@ -1370,15 +1396,8 @@ class TimeSeries(QAbstractItemModel): :param sources: [list-of-TimeSeriesSources] """ assert isinstance(sources, list) - + self.mLoadingProgressDialog = progressDialog nMax = len(sources) - #self.sigTimeSeriesSourcesAboutToBeChanged.emit() - - self.sigLoadingProgress.emit(0, nMax, 'Start loading {} sources...'.format(nMax)) - - if isinstance(progressDialog, QProgressDialog): - progressDialog.setRange(0, nMax) - progressDialog.setLabelText('Load rasters...'.format(nMax)) # 1. read sources # this could be excluded into a parallel process addedDates = [] @@ -1394,17 +1413,16 @@ class TimeSeries(QAbstractItemModel): msg = 'Unable to add: {}\n{}'.format(str(source), str(ex)) print(msg, file=sys.stderr) - if isinstance(progressDialog, QProgressDialog): - if progressDialog.wasCanceled(): + if isinstance(self.mLoadingProgressDialog, QProgressDialog): + if self.mLoadingProgressDialog.wasCanceled(): break - progressDialog.setValue(i) - progressDialog.setLabelText('{}/{}'.format(i+1, nMax)) + self.increaseProgressBar() - if (i+1) % 10 == 0: - self.sigLoadingProgress.emit(i+1, nMax, msg) + #if (i+1) % 10 == 0: + # self.sigLoadingProgress.emit(i+1, nMax, msg) - if (i+1) % 50 == 0: - QGuiApplication.processEvents() + #if (i+1) % 50 == 0: + # QGuiApplication.processEvents() if isinstance(newTSD, TimeSeriesDate): addedDates.append(newTSD) @@ -1416,6 +1434,8 @@ class TimeSeries(QAbstractItemModel): if len(addedDates) > 0: self.sigTimeSeriesDatesAdded.emit(addedDates) + self.mLoadingProgressDialog = None + def _addSource(self, source:TimeSeriesSource)->TimeSeriesDate: """ diff --git a/eotimeseriesviewer/ui/timeseriesdock.ui b/eotimeseriesviewer/ui/timeseriesdock.ui index a0856d09..b2635172 100644 --- a/eotimeseriesviewer/ui/timeseriesdock.ui +++ b/eotimeseriesviewer/ui/timeseriesdock.ui @@ -18,8 +18,8 @@ </property> <property name="minimumSize"> <size> - <width>152</width> - <height>139</height> + <width>0</width> + <height>0</height> </size> </property> <property name="windowTitle"> diff --git a/eotimeseriesviewer/ui/timeseriesviewer.ui b/eotimeseriesviewer/ui/timeseriesviewer.ui index 615c5981..f79f1b3c 100644 --- a/eotimeseriesviewer/ui/timeseriesviewer.ui +++ b/eotimeseriesviewer/ui/timeseriesviewer.ui @@ -35,8 +35,8 @@ <widget class="QWidget" name="mCentralWidget"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>1</horstretch> - <verstretch>1</verstretch> + <horstretch>3</horstretch> + <verstretch>3</verstretch> </sizepolicy> </property> <property name="minimumSize"> @@ -74,9 +74,9 @@ <item> <widget class="MapViewScrollArea" name="scrollAreaSubsets"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>1</horstretch> - <verstretch>1</verstretch> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>3</horstretch> + <verstretch>3</verstretch> </sizepolicy> </property> <property name="minimumSize"> @@ -108,8 +108,8 @@ </property> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> + <horstretch>2</horstretch> + <verstretch>2</verstretch> </sizepolicy> </property> <property name="minimumSize"> -- GitLab