diff --git a/make/deploy.py b/make/deploy.py index 1bad3118e24b0b849559f3eb41b7ceb4c69931a2..bc37bbaa85f5d1e7daf4821acbe5b3cfba211235 100644 --- a/make/deploy.py +++ b/make/deploy.py @@ -40,7 +40,8 @@ app = initQgisApplication() DIR_BUILD = jp(DIR_REPO, 'build') DIR_DEPLOY = jp(DIR_REPO, 'deploy') - +QGIS_MIN = '3.4' +QGIS_MAX = '3.99' REPO = git.Repo(DIR_REPO) currentBranch = REPO.active_branch.name timestamp = ''.join(np.datetime64(datetime.datetime.now()).astype(str).split(':')[0:-1]).replace('-','') @@ -166,6 +167,8 @@ def build(): lines = f.readlines() f.close() lines = re.sub('version=.*\n', 'version={}\n'.format(buildID), ''.join(lines)) + lines = re.sub('qgisMinimumVersion=.*\n', 'qgisMinimumVersion={}\n'.format(QGIS_MIN), ''.join(lines)) + lines = re.sub('qgisMaximumVersion=.*\n', 'qgisMaximumVersion={}\n'.format(QGIS_MAX), ''.join(lines)) f = open(pathMetadata, 'w') f.write(lines) f.flush() @@ -237,8 +240,8 @@ def updateRepositoryXML(path:str=None): <about></about> <version></version> <trusted>True</trusted> - <qgis_minimum_version>3.3.0</qgis_minimum_version> - <qgis_maximum_version>3.99.0</qgis_maximum_version> + <qgis_minimum_version>dummy</qgis_minimum_version> + <qgis_maximum_version>dummy</qgis_maximum_version> <homepage></homepage> <file_name></file_name> <icon></icon> @@ -264,11 +267,11 @@ def updateRepositoryXML(path:str=None): plugin = ET.SubElement(root, 'pyqgis_plugin') plugin.attrib['name'] = "{} (develop version)".format(timeseriesviewer.TITLE) plugin.attrib['version'] = '{}'.format(version) - ET.SubElement(plugin, 'description').text = r'EnMAP-Box development version' + ET.SubElement(plugin, 'description').text = r'EO Time Series Viewer (Development Version)' ET.SubElement(plugin, 'about').text = 'Preview' ET.SubElement(plugin, 'version').text = version - ET.SubElement(plugin, 'qgis_minimum_version').text = '3.4' - ET.SubElement(plugin, 'qgis_maximum_version').text = '3.99' + ET.SubElement(plugin, 'qgis_minimum_version').text = QGIS_MIN + ET.SubElement(plugin, 'qgis_maximum_version').text = QGIS_MAX ET.SubElement(plugin, 'homepage').text = timeseriesviewer.HOMEPAGE ET.SubElement(plugin, 'file_name').text = bn ET.SubElement(plugin, 'icon').text = 'icon.png' diff --git a/metadata.txt b/metadata.txt index a7c3715c906abb59809dd73f27f5800b196ea74a..00cbd66e273254317932f6f4d32368041c3116ae 100644 --- a/metadata.txt +++ b/metadata.txt @@ -1,8 +1,9 @@ [general] name=EO Time Series Viewer description=Visualization of multi-sensor Earth observation time series data. -version=0.7.201806201143 -qgisMinimumVersion=3.0 +version=dummy +qgisMinimumVersion=dummy +qgisMaximumVersion=dummy author=Benjamin Jakimow, Geomatics Lab, Humboldt-Universität zu Berlin about=Highlights<ul><li>Spatially synchronized maps for each observation date</li><li>Multiple band combinations in parallel, e.g. True Color <i>and</i> SWIR bands</li><li>Multi-sensor support: separate render settings for separate senors/image products</li><li>Visualization of Spectral and Temporal Profiles</li></ul>Usage<ol> <li>"Files" > "Add images" to define the time series</li> <li>"View" > "Add Map View" to create a new row of maps, e.g. for SWIR visualization</li> <li>For each map view specifiy how each sensor should be rendered, e.g. RGB or single band color range</li> <li>Optimize render settings, e.g. via map canvas context menu</li></ol>Dependencies<ul> <li>pyqtgraph (required)</li> <li>PyOpenGL (optional)</li></ul><p> Install depencencies with <code>python -m pip pyqtgraph</code></p> email=benjamin.jakimow@geo.hu-berlin.de diff --git a/pb_tool.cfg b/pb_tool.cfg index 197acd240fe622a72a4081e022ccb5109b91471a..0125506782f6a463228c06cbb1f940385cee0ecc 100644 --- a/pb_tool.cfg +++ b/pb_tool.cfg @@ -38,6 +38,8 @@ extras: LICENSE.md README.md CHANGES.txt + ABOUT.html + ABOUT_Plugin.html contributors.txt # Other directories to be deployed with the plugin. diff --git a/test/test_mapvisualization.py b/test/test_mapvisualization.py index 220d3ad5b7beac3d4e94383e3506cbeea4fa0dd7..2ae3de9cba1422969c1cc0ea76d3202fec8b8208 100644 --- a/test/test_mapvisualization.py +++ b/test/test_mapvisualization.py @@ -185,7 +185,7 @@ class testclassDialogTest(unittest.TestCase): lyr = QgsRasterLayer(Img_2014_01_15_LC82270652014015LGN00_BOA) doc = QDomDocument() err = '' - lyr.exportNamedStyle(doc, err) + lyr.exportNamedStyle(doc) xml0 = doc.toString() self.assertEqual(err, '') diff --git a/timeseriesviewer/__init__.py b/timeseriesviewer/__init__.py index 8d6a5ce32268dea025f2db1e062435779374dfa7..49bfc8019ff38f9fd85e3939211147efb5dd8c1d 100644 --- a/timeseriesviewer/__init__.py +++ b/timeseriesviewer/__init__.py @@ -80,13 +80,14 @@ DIR_QGIS_RESOURCES = jp(DIR_REPO, 'qgisresources') site.addsitedir(DIR_SITE_PACKAGES) OPENGL_AVAILABLE = False - try: import OpenGL OPENGL_AVAILABLE = True except: pass + + def messageLog(msg, level=None): """ Writes a log message to the QGIS EO TimeSeriesViewer log @@ -105,6 +106,29 @@ try: except: pass +# make the EnMAP-Box resources available +if not 'images' in sys.modules.keys(): + import timeseriesviewer.resourcemockup + sys.modules['images'] = timeseriesviewer.resourcemockup + + +#see https://github.com/pyqtgraph/pyqtgraph/issues/774 +WORKAROUND_PYTGRAPH_ISSUE_774 = True +if WORKAROUND_PYTGRAPH_ISSUE_774: + from pyqtgraph.graphicsItems.GraphicsObject import GraphicsObject + + from qgis.PyQt.QtCore import QVariant + untouched = GraphicsObject.itemChange + + def newFunc(cls, change, value): + if value != QVariant(None): + return untouched(cls, change, value) + else: + return untouched(cls, change, None) + + GraphicsObject.itemChange = newFunc + + def initSettings(): def setIfNone(key, value): if SETTINGS.value(key) is None: diff --git a/timeseriesviewer/__main__.py b/timeseriesviewer/__main__.py index c1d9fb083aea21821e70e6b512c01c4f6556489b..12450fede6cc5a7e2cb5b865611a411443ac3966 100644 --- a/timeseriesviewer/__main__.py +++ b/timeseriesviewer/__main__.py @@ -17,19 +17,17 @@ *************************************************************************** """ -import sys, os - def run(): - pkg = os.path.dirname(__file__) - repo = os.path.dirname(pkg) - if repo not in sys.path: - if __name__ == '__main__': - sys.path.append(repo) - #for p in sorted(sys.path): print(p) - import timeseriesviewer.main - timeseriesviewer.main.DEBUG = True - timeseriesviewer.main.main() + # add site-packages to sys.path as done by enmapboxplugin.py + from timeseriesviewer.utils import initQgisApplication + qgsApp = initQgisApplication() + + from timeseriesviewer.main import TimeSeriesViewer + ts = TimeSeriesViewer(None) + ts.run() + qgsApp.exec_() + qgsApp.exitQgis() if __name__ == '__main__': run() diff --git a/timeseriesviewer/main.py b/timeseriesviewer/main.py index 4fb1017f35307fc2496bf39d19c065d48ca9c67f..63d48b4aef6ef2cce89bd3e141bf4d25912d1245 100644 --- a/timeseriesviewer/main.py +++ b/timeseriesviewer/main.py @@ -788,18 +788,3 @@ def disconnect_signal(signal): signal.disconnect() except TypeError: break - - -def main(): - # add site-packages to sys.path as done by enmapboxplugin.py - from timeseriesviewer.utils import initQgisApplication - qgsApp = initQgisApplication() - ts = TimeSeriesViewer(None) - ts.run() - qgsApp.exec_() - qgsApp.exitQgis() - -if __name__ == '__main__': - - import timeseriesviewer.__main__ as m - m.run() \ No newline at end of file diff --git a/timeseriesviewer/mapvisualization.py b/timeseriesviewer/mapvisualization.py index 53324cda18d0d221ab21a645485d53239cac66ba..3e6e832edc3bd2ddadeb2b3468571f35432e2875 100644 --- a/timeseriesviewer/mapvisualization.py +++ b/timeseriesviewer/mapvisualization.py @@ -546,9 +546,20 @@ class RendererWidgetModifications(object): minMaxWidget.layout().itemAt(0).widget().collapsedStateChanged.connect(self.onCollapsed) - def initWidgetNames(self): - for c in self.children(): - setattr(self, c.objectName(), c) + def initWidgetNames(self, parent=None): + """ + Create a python variables to access QObjects which are child of parent + :param parent: QObject, self by default + """ + if parent is None: + parent = self + + for c in parent.children(): + setattr(parent, c.objectName(), c) + + + + def onCollapsed(self, b): hint = self.sizeHint() @@ -609,10 +620,10 @@ class RendererWidgetModifications(object): s = "" - def comboBoxWithNotSetItem(self, cb): + def comboBoxWithNotSetItem(self, cb)->bool: assert isinstance(cb, QComboBox) data = cb.itemData(0, role=Qt.DisplayRole) - return str(data) in ['not set', 'None', 'NoneType'] + return re.search(r'^(not set|none|nonetype)$',str(data).strip(), re.I) is not None def setLayoutItemVisibility(self, grid, isVisible): assert isinstance(self, QgsRasterRendererWidget) @@ -797,9 +808,9 @@ class SingleBandPseudoColorRendererWidget(QgsSingleBandPseudoColorRendererWidget def __init__(self, layer, extent): super(SingleBandPseudoColorRendererWidget, self).__init__(layer, extent) - self.mColormapTreeWidget.setMinimumSize(QSize(1,1)) + #self.mColormapTreeWidget.setMinimumSize(QSize(1,1)) - self.gridLayout = self.layout() + self.gridLayout = self.layout().children()[0] assert isinstance(self.gridLayout, QGridLayout) for i in range(self.gridLayout.count()): w = self.gridLayout.itemAt(i) @@ -1575,7 +1586,7 @@ def rendererToXml(renderer): lyr = QgsRasterLayer(path) assert lyr.isValid() lyr.setRenderer(renderer.clone()) - lyr.exportNamedStyle(doc, err) + lyr.exportNamedStyle(doc) #remove dummy raster layer lyr = None drv.Delete(path) @@ -1584,7 +1595,7 @@ def rendererToXml(renderer): #todo: distinguish vector type from requested renderer lyr = QgsVectorLayer('Point?crs=epsg:4326&field=id:integer', 'dummy', 'memory') lyr.setRenderer(renderer.clone()) - lyr.exportNamedStyle(doc, err) + lyr.exportNamedStyle(doc) lyr = None else: raise NotImplementedError() diff --git a/timeseriesviewer/resourcemockup.py b/timeseriesviewer/resourcemockup.py new file mode 100644 index 0000000000000000000000000000000000000000..fb26d5ce053560fa2841320565c83acc5a659678 --- /dev/null +++ b/timeseriesviewer/resourcemockup.py @@ -0,0 +1 @@ +#mockup to use QGIS ressources \ No newline at end of file diff --git a/timeseriesviewer/utils.py b/timeseriesviewer/utils.py index 54d89efd40beac30126314c52737bc1b797d9969..d00318ac80cc547e3c86d93d3096f9a938c4cfd4 100644 --- a/timeseriesviewer/utils.py +++ b/timeseriesviewer/utils.py @@ -1091,18 +1091,19 @@ def zipdir(pathDir, pathZip): zip.write(filename, arcname) - def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgisResourceDir=None): """ Initializes the QGIS Environment :return: QgsApplication instance of local QGIS installation """ - import site if pythonPlugins is None: pythonPlugins = [] assert isinstance(pythonPlugins, list) - if isinstance(qgisResourceDir, str) and os.path.isdir(qgisResourceDir): + if os.path.exists(os.path.join(DIR_REPO, 'qgisresources')): + qgisResourceDir = os.path.join(DIR_REPO, 'qgisresources') + if isinstance(qgisResourceDir, str): + assert os.path.isdir(qgisResourceDir) import importlib, re modules = [m for m in os.listdir(qgisResourceDir) if re.search(r'[^_].*\.py', m)] modules = [m[0:-3] for m in modules] @@ -1111,7 +1112,6 @@ def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgi if "qInitResources" in dir(mod): mod.qInitResources() - envVar = os.environ.get('QGIS_PLUGINPATH', None) if isinstance(envVar, list): pythonPlugins.extend(re.split('[;:]', envVar)) @@ -1153,20 +1153,56 @@ def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgi qgsApp = QgsApplication([], True) qgsApp.setPrefixPath(PATH_QGIS, True) qgsApp.initQgis() - qgsApp.registerOgrDrivers() - - from qgis.gui import QgsGui - QgsGui.editorWidgetRegistry().initEditors() - def printQgisLog(tb, error, level): - if error not in ['Python warning']: - print(tb) + def printQgisLog(msg, tag, level): + if tag not in ['Processing']: + if tag in ['Python warning', 'warning']: + import re + if re.search('(Deprecation|Import)Warning', msg) is not None: + return + else: + return + print(msg) QgsApplication.instance().messageLog().messageReceived.connect(printQgisLog) + #initiate a PythonRunner instance if None exists + if not QgsPythonRunner.isValid(): + r = PythonRunnerImpl() + QgsPythonRunner.setInstance(r) return qgsApp +class PythonRunnerImpl(QgsPythonRunner): + """ + A Qgs PythonRunner implementation + """ + + def __init__(self): + super(PythonRunnerImpl, self).__init__() + + + def evalCommand(self, cmd:str, result:str): + try: + o = compile(cmd) + except Exception as ex: + result = str(ex) + return False + return True + + def runCommand(self, command, messageOnError=''): + try: + o = compile(command, 'fakemodule', 'exec') + exec(o) + except Exception as ex: + messageOnError = str(ex) + command = ['{}:{}'.format(i+1, l) for i,l in enumerate(command.splitlines())] + print('\n'.join(command), file=sys.stderr) + raise ex + return False + return True + + def createCRSTransform(src, dst): assert isinstance(src, QgsCoordinateReferenceSystem) assert isinstance(dst, QgsCoordinateReferenceSystem)