├── .gitattributes ├── tests ├── unittests │ ├── __init__.py │ └── test_qgis.py ├── testdata │ ├── tri-time-test.nc │ ├── ipf-borehole │ │ ├── id_3.txt │ │ ├── id_0.txt │ │ ├── id_1.txt │ │ ├── id_2.txt │ │ └── boreholes.ipf │ └── ipf-timeseries │ │ ├── timeseries.ipf │ │ ├── B30F0059001.txt │ │ ├── B30F0217001.txt │ │ └── B30F0222001.txt └── test_pytest.py ├── imodqgis ├── dependencies │ └── pyqtgraph_0_12_3 │ │ ├── util │ │ ├── __init__.py │ │ ├── colorama │ │ │ ├── __init__.py │ │ │ └── LICENSE.txt │ │ ├── numba_helper.py │ │ ├── cupy_helper.py │ │ ├── get_resolution.py │ │ ├── garbage_collector.py │ │ └── pil_fix.py │ │ ├── widgets │ │ ├── __init__.py │ │ ├── FileDialog.py │ │ ├── BusyCursor.py │ │ ├── MatplotlibWidget.py │ │ ├── HistogramLUTWidget.py │ │ ├── PathButton.py │ │ ├── GraphicsLayoutWidget.py │ │ ├── JoystickButton.py │ │ └── ValueLabel.py │ │ ├── Qt │ │ ├── QtCore │ │ │ └── __init__.py │ │ ├── QtGui │ │ │ └── __init__.py │ │ └── QtWidgets │ │ │ └── __init__.py │ │ ├── opengl │ │ ├── items │ │ │ ├── __init__.py │ │ │ ├── GLBarGraphItem.py │ │ │ ├── GLAxisItem.py │ │ │ ├── GLBoxItem.py │ │ │ ├── GLGridItem.py │ │ │ └── GLGradientLegendItem.py │ │ ├── glInfo.py │ │ └── __init__.py │ │ ├── metaarray │ │ ├── __init__.py │ │ ├── license.txt │ │ └── readMeta.m │ │ ├── GraphicsScene │ │ ├── __init__.py │ │ ├── exportDialogTemplate.ui │ │ ├── exportDialogTemplate_pyqt6.py │ │ └── exportDialogTemplate_pyqt5.py │ │ ├── console │ │ ├── __init__.py │ │ └── CmdInput.py │ │ ├── dockarea │ │ └── __init__.py │ │ ├── graphicsItems │ │ ├── ViewBox │ │ │ └── __init__.py │ │ ├── PlotItem │ │ │ └── __init__.py │ │ ├── ItemGroup.py │ │ ├── __init__.py │ │ ├── ButtonItem.py │ │ ├── GraphicsObject.py │ │ ├── GraphicsWidget.py │ │ ├── ScaleBar.py │ │ ├── MultiPlotItem.py │ │ └── FillBetweenItem.py │ │ ├── canvas │ │ ├── __init__.py │ │ ├── TransformGuiTemplate.ui │ │ ├── CanvasManager.py │ │ ├── TransformGuiTemplate_pyqt5.py │ │ ├── TransformGuiTemplate_pyqt6.py │ │ ├── TransformGuiTemplate_pyside2.py │ │ └── TransformGuiTemplate_pyside6.py │ │ ├── icons │ │ ├── auto.png │ │ ├── ctrl.png │ │ ├── lock.png │ │ ├── default.png │ │ ├── __init__.py │ │ └── invisibleEye.svg │ │ ├── flowchart │ │ ├── __init__.py │ │ ├── library │ │ │ └── __init__.py │ │ ├── FlowchartGraphicsView.py │ │ ├── FlowchartTemplate_pyside2.py │ │ ├── FlowchartTemplate_pyqt5.py │ │ ├── FlowchartTemplate_pyqt6.py │ │ ├── FlowchartTemplate.ui │ │ ├── FlowchartTemplate_pyside6.py │ │ ├── NodeLibrary.py │ │ ├── FlowchartCtrlTemplate_pyqt5.py │ │ ├── FlowchartCtrlTemplate_pyqt6.py │ │ └── FlowchartCtrlTemplate_pyside2.py │ │ ├── imageview │ │ └── __init__.py │ │ ├── parametertree │ │ ├── __init__.py │ │ └── parameterTypes │ │ │ ├── bool.py │ │ │ ├── str.py │ │ │ ├── progress.py │ │ │ ├── text.py │ │ │ ├── color.py │ │ │ ├── colormap.py │ │ │ ├── font.py │ │ │ ├── action.py │ │ │ ├── calendar.py │ │ │ ├── numeric.py │ │ │ ├── __init__.py │ │ │ └── qtenum.py │ │ ├── exporters │ │ ├── __init__.py │ │ ├── HDF5Exporter.py │ │ └── PrintExporter.py │ │ ├── colors │ │ └── maps │ │ │ ├── PAL-relaxed.hex │ │ │ └── PAL-relaxed_bright.hex │ │ ├── ordereddict.py │ │ ├── multiprocess │ │ ├── __init__.py │ │ └── bootstrap.py │ │ ├── ptime.py │ │ ├── functions_numba.py │ │ ├── PlotData.py │ │ ├── ThreadsafeTimer.py │ │ ├── units.py │ │ ├── frozenSupport.py │ │ └── Transform3D.py ├── icon.png ├── arrow │ ├── __init__.py │ └── reading.py ├── utils │ ├── __init__.py │ ├── pathing.py │ ├── temporal.py │ ├── color.py │ ├── user_communication.py │ └── layers.py ├── resources.qrc ├── idf │ ├── __init__.py │ └── layer_styling.py ├── about │ ├── __init__.py │ ├── about_widget.py │ └── about.md ├── viewer │ └── __init__.py ├── nhi_data │ └── __init__.py ├── timeseries │ └── __init__.py ├── netcdf_manager │ └── __init__.py ├── cross_section │ └── __init__.py ├── __init__.py ├── gef │ └── __init__.py ├── ipf │ └── __init__.py ├── widgets │ ├── dock_widget.py │ ├── __init__.py │ └── colors_dialog.py └── icons │ ├── deltares.svg │ ├── time-series.svg │ └── enabling-delta-life.svg ├── pyproject.toml ├── scripts ├── deploy.bat ├── package.bat ├── compile_resources.bat └── add_spdx.py ├── setup.cfg ├── .gitignore ├── CONTRIBUTING.md ├── .github ├── running_tests_locally.md └── workflows │ └── package.yml └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unittests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/Qt/QtCore/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/Qt/QtGui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/Qt/QtWidgets/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/opengl/items/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/util/colorama/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imodqgis/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/imod-qgis/main/imodqgis/icon.png -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/metaarray/__init__.py: -------------------------------------------------------------------------------- 1 | from .MetaArray import * 2 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/GraphicsScene/__init__.py: -------------------------------------------------------------------------------- 1 | from .GraphicsScene import * 2 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/console/__init__.py: -------------------------------------------------------------------------------- 1 | from .Console import ConsoleWidget 2 | -------------------------------------------------------------------------------- /imodqgis/arrow/__init__.py: -------------------------------------------------------------------------------- 1 | from imodqgis.arrow.reading import read_arrow 2 | 3 | __all__ = ["read_arrow"] -------------------------------------------------------------------------------- /imodqgis/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | -------------------------------------------------------------------------------- /tests/testdata/tri-time-test.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/imod-qgis/main/tests/testdata/tri-time-test.nc -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/dockarea/__init__.py: -------------------------------------------------------------------------------- 1 | from .DockArea import DockArea 2 | from .Dock import Dock 3 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/ViewBox/__init__.py: -------------------------------------------------------------------------------- 1 | from .ViewBox import ViewBox 2 | 3 | __all__ = ['ViewBox'] 4 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/canvas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .Canvas import * 3 | from .CanvasItem import * 4 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/PlotItem/__init__.py: -------------------------------------------------------------------------------- 1 | from .PlotItem import PlotItem 2 | 3 | __all__ = ['PlotItem'] 4 | -------------------------------------------------------------------------------- /tests/test_pytest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test using pytest 3 | """ 4 | 5 | def test_pytest(): 6 | print("Hello world") 7 | assert True is True 8 | -------------------------------------------------------------------------------- /imodqgis/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icon.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/testdata/ipf-borehole/id_3.txt: -------------------------------------------------------------------------------- 1 | 4 2 | 2,2 3 | top,1e+20 4 | lithology,1e+20 5 | -30,"sand" 6 | -100,"sandy loam" 7 | -210,"clay" 8 | -300,"silt" 9 | -------------------------------------------------------------------------------- /tests/testdata/ipf-borehole/id_0.txt: -------------------------------------------------------------------------------- 1 | 4 2 | 2,2 3 | top,1e+20 4 | lithology,1e+20 5 | 0,"silt" 6 | -100,"sandy loam" 7 | -200,"clay" 8 | -300,"silty clay" 9 | -------------------------------------------------------------------------------- /tests/testdata/ipf-borehole/id_1.txt: -------------------------------------------------------------------------------- 1 | 4 2 | 2,2 3 | top,1e+20 4 | lithology,1e+20 5 | 50,"sandy loam" 6 | -150,"silty clay" 7 | -250,"clay" 8 | -400,"silt" 9 | -------------------------------------------------------------------------------- /tests/testdata/ipf-borehole/id_2.txt: -------------------------------------------------------------------------------- 1 | 4 2 | 2,2 3 | top,1e+20 4 | lithology,1e+20 5 | 30,"sand" 6 | -50,"silty clay" 7 | -150,"silt" 8 | -200,"sandy loam" 9 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/icons/auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/imod-qgis/main/imodqgis/dependencies/pyqtgraph_0_12_3/icons/auto.png -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/icons/ctrl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/imod-qgis/main/imodqgis/dependencies/pyqtgraph_0_12_3/icons/ctrl.png -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/icons/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/imod-qgis/main/imodqgis/dependencies/pyqtgraph_0_12_3/icons/lock.png -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/icons/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/imod-qgis/main/imodqgis/dependencies/pyqtgraph_0_12_3/icons/default.png -------------------------------------------------------------------------------- /tests/testdata/ipf-timeseries/timeseries.ipf: -------------------------------------------------------------------------------- 1 | 3 2 | 3 3 | x 4 | y 5 | id 6 | 3,txt 7 | 92530,463930,"B30F0059001" 8 | 93100,464580,"B30F0217001" 9 | 92840,463680,"B30F0222001" 10 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .Flowchart import * 3 | 4 | from .library import getNodeType, registerNodeType, getNodeTree 5 | -------------------------------------------------------------------------------- /tests/testdata/ipf-borehole/boreholes.ipf: -------------------------------------------------------------------------------- 1 | 4 2 | 3 3 | x 4 | y 5 | id 6 | 3,txt 7 | 139354,376437,"id_0" 8 | 146343,390891,"id_1" 9 | 144914,386603,"id_2" 10 | 141419,381043,"id_3" 11 | -------------------------------------------------------------------------------- /imodqgis/idf/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.idf.idf_dialog import ImodIdfDialog 5 | 6 | __all__ = ["ImodIdfDialog"] 7 | -------------------------------------------------------------------------------- /imodqgis/about/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.about.about_widget import ImodAboutDialog 5 | 6 | __all__ = ["ImodAboutDialog"] -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff] 2 | select = ["E", "F", "NPY", "PD", "C4", "I"] 3 | ignore = ["E501", "PD901"] 4 | fixable = ["I"] 5 | ignore-init-module-imports = true 6 | exclude = ["imodqgis/dependencies/*.py"] -------------------------------------------------------------------------------- /imodqgis/viewer/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.viewer.viewer_widget import ImodViewerWidget 5 | 6 | __all__ = ["ImodViewerWidget"] -------------------------------------------------------------------------------- /imodqgis/nhi_data/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.nhi_data.nhi_data_dialog import ImodNhiDataDialog 5 | 6 | __all__ = ["ImodNhiDataDialog"] 7 | -------------------------------------------------------------------------------- /imodqgis/timeseries/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.timeseries.timeseries_widget import ImodTimeSeriesWidget 5 | 6 | __all__ = ["ImodTimeSeriesWidget"] 7 | -------------------------------------------------------------------------------- /imodqgis/netcdf_manager/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.netcdf_manager.netcdf_widget import ImodNetcdfManagerWidget 5 | 6 | __all__ = ["ImodNetcdfManagerWidget"] -------------------------------------------------------------------------------- /imodqgis/cross_section/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.cross_section.cross_section_widget import ImodCrossSectionWidget 5 | 6 | __all__ = ["ImodCrossSectionWidget"] -------------------------------------------------------------------------------- /scripts/deploy.bat: -------------------------------------------------------------------------------- 1 | set plugin_dir=%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins 2 | 3 | rem Remove plugin folder before copying 4 | rmdir %plugin_dir%\imodqgis /S /Q 5 | rem Copy 6 | robocopy %~dp0\..\imodqgis %plugin_dir%\imodqgis /E 7 | -------------------------------------------------------------------------------- /imodqgis/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | def classFactory(iface): # pylint: disable=invalid-name 5 | from imodqgis.imod_plugin import ImodPlugin 6 | 7 | return ImodPlugin(iface) 8 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [options] 2 | packages = find: 3 | python_requires = >=3.7 4 | install_requires = 5 | pandas 6 | declxml 7 | pyqtgraph 8 | 9 | [options.extras_require] 10 | docs = 11 | pydata-sphinx-theme 12 | sphinx 13 | sphinx-gallery 14 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/imageview/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Widget used for display and analysis of 2D and 3D image data. 3 | Includes ROI plotting over time and image normalization. 4 | """ 5 | 6 | from .ImageView import ImageView 7 | 8 | __all__ = ['ImageView'] 9 | -------------------------------------------------------------------------------- /imodqgis/arrow/reading.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from osgeo import ogr 3 | 4 | 5 | def read_arrow(path): 6 | dataset = ogr.Open(path) 7 | layer = dataset.GetLayer(0) 8 | stream = layer.GetArrowStreamAsNumPy() 9 | data = stream.GetNextRecordBatch() 10 | return pd.DataFrame(data=data) 11 | -------------------------------------------------------------------------------- /imodqgis/gef/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.gef.gef_dialog import ImodGefDialog 5 | from imodqgis.gef.reading import CptGefFile, GefType 6 | 7 | __all__ = [ 8 | "ImodGefDialog", 9 | "CptGefFile", 10 | "GefType", 11 | ] -------------------------------------------------------------------------------- /scripts/package.bat: -------------------------------------------------------------------------------- 1 | 2 | rem powershell equivalent to https://github.com/lutraconsulting/qgis-crayfish-plugin/blob/master/package.bash 3 | 4 | %systemroot%\System32\WindowsPowerShell\v1.0\powershell.exe -command "cd ..; rm -r -fo imodqgis.zip; cd imodqgis; git archive --prefix=imodqgis/ -o ../imodqgis.zip HEAD" 5 | 6 | pause -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/__init__.py: -------------------------------------------------------------------------------- 1 | from .Parameter import Parameter, registerParameterType, registerParameterItemType 2 | from .ParameterTree import ParameterTree 3 | from .ParameterItem import ParameterItem 4 | from .ParameterSystem import ParameterSystem, SystemSolver 5 | from . import parameterTypes as types 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | *.pyc 3 | __pycache__/ 4 | 5 | # Unit test / coverage reports 6 | htmlcov/ 7 | .tox/ 8 | .coverage 9 | .coverage.* 10 | .cache 11 | .pytest_cache 12 | nosetests.xml 13 | coverage.xml 14 | *.cover 15 | .hypothesis/ 16 | 17 | # Sphinx documentation 18 | docs/_build/ 19 | 20 | # Locally packaged plugin 21 | imodqgis.zip 22 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/exporters/__init__.py: -------------------------------------------------------------------------------- 1 | from .Exporter import Exporter 2 | from .ImageExporter import * 3 | from .SVGExporter import * 4 | from .Matplotlib import * 5 | from .CSVExporter import * 6 | from .PrintExporter import * 7 | from .HDF5Exporter import * 8 | 9 | def listExporters(): 10 | return Exporter.Exporters[:] 11 | 12 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/colors/maps/PAL-relaxed.hex: -------------------------------------------------------------------------------- 1 | ; PyQtGraph's "relaxed" plot color palette 2 | ; This is the darker variant for plotting on a light background ("light mode") 3 | ; 4 | #f97f10 ; orange 5 | #e5bb00 ; yellow 6 | #94ab00 ; grass 7 | #12a12a ; green 8 | #007c8c ; sea 9 | #0e56c2 ; blue 10 | #813be3 ; indigo 11 | #c01188 ; purple 12 | #e23512 ; red 13 | #f97f10 ; orange 14 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/colors/maps/PAL-relaxed_bright.hex: -------------------------------------------------------------------------------- 1 | ; PyQtGraph's "relaxed" plot color palette 2 | ; This is the brighter variant for plotting on a dark background ("dark mode") 3 | ; 4 | #ff9d47 ; orange 5 | #f7e100 ; yellow 6 | #b3cf00 ; grass 7 | #1ec23a ; green 8 | #00a0b5 ; sea 9 | #1f78ff ; blue 10 | #a54dff ; indigo 11 | #e22ca8 ; purple 12 | #ff532b ; red 13 | #ff9d47 ; orange 14 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/ordereddict.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import warnings 3 | 4 | 5 | class OrderedDict(collections.OrderedDict): 6 | def __init__(self, *args, **kwds): 7 | warnings.warn( 8 | "OrderedDict is in the standard library for supported versions of Python. Will be removed in 0.13", 9 | DeprecationWarning, 10 | stacklevel=2, 11 | ) 12 | super().__init__(*args, **kwds) 13 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/bool.py: -------------------------------------------------------------------------------- 1 | from ...Qt import QtWidgets 2 | from .basetypes import WidgetParameterItem 3 | 4 | 5 | class BoolParameterItem(WidgetParameterItem): 6 | """ 7 | Registered parameter type which displays a QCheckBox 8 | """ 9 | def makeWidget(self): 10 | w = QtWidgets.QCheckBox() 11 | w.sigChanged = w.toggled 12 | w.value = w.isChecked 13 | w.setValue = w.setChecked 14 | self.hideWidget = False 15 | return w 16 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/util/numba_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from warnings import warn 3 | 4 | from .. import getConfigOption 5 | 6 | def getNumbaFunctions(): 7 | if getConfigOption("useNumba"): 8 | try: 9 | import numba # noqa 10 | except ImportError: 11 | warn("numba library could not be loaded, but 'useNumba' is set.") 12 | return None 13 | 14 | from .. import functions_numba 15 | return functions_numba 16 | else: 17 | return None 18 | -------------------------------------------------------------------------------- /imodqgis/ipf/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.ipf.ipf_dialog import ImodIpfDialog 5 | from imodqgis.ipf.reading import ( 6 | IpfType, 7 | read_associated_borehole, 8 | read_associated_header, 9 | read_associated_timeseries, 10 | read_ipf_header, 11 | ) 12 | 13 | __all__ = [ 14 | "ImodIpfDialog", 15 | "IpfType", 16 | "read_associated_borehole", 17 | "read_associated_header", 18 | "read_associated_timeseries", 19 | "read_ipf_header", 20 | ] -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/str.py: -------------------------------------------------------------------------------- 1 | from ...Qt import QtWidgets 2 | from .basetypes import WidgetParameterItem 3 | 4 | 5 | class StrParameterItem(WidgetParameterItem): 6 | """Registered parameter type which displays a QLineEdit""" 7 | 8 | def makeWidget(self): 9 | w = QtWidgets.QLineEdit() 10 | w.setStyleSheet('border: 0px') 11 | w.sigChanged = w.editingFinished 12 | w.value = w.text 13 | w.setValue = w.setText 14 | w.sigChanging = w.textChanged 15 | return w 16 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/opengl/glInfo.py: -------------------------------------------------------------------------------- 1 | from ..Qt import QtWidgets 2 | from OpenGL.GL import * 3 | 4 | class GLTest(QtWidgets.QOpenGLWidget): 5 | def initializeGL(self): 6 | print("GL version:" + glGetString(GL_VERSION).decode("utf-8")) 7 | print("MAX_TEXTURE_SIZE: %d" % glGetIntegerv(GL_MAX_TEXTURE_SIZE)) 8 | print("MAX_3D_TEXTURE_SIZE: %d" % glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE)) 9 | print("Extensions: " + glGetString(GL_EXTENSIONS).decode("utf-8").replace(" ", "\n")) 10 | 11 | app = QtWidgets.QApplication([]) 12 | GLTest().show() 13 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/FileDialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtGui 3 | import sys 4 | 5 | __all__ = ['FileDialog'] 6 | 7 | class FileDialog(QtGui.QFileDialog): 8 | ## Compatibility fix for OSX: 9 | ## For some reason the native dialog doesn't show up when you set AcceptMode to AcceptSave on OS X, so we don't use the native dialog 10 | 11 | def __init__(self, *args): 12 | QtGui.QFileDialog.__init__(self, *args) 13 | 14 | if sys.platform == 'darwin': 15 | self.setOption(QtGui.QFileDialog.Option.DontUseNativeDialog) 16 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/progress.py: -------------------------------------------------------------------------------- 1 | from ...Qt import QtWidgets 2 | from ..Parameter import Parameter 3 | from .basetypes import WidgetParameterItem 4 | 5 | 6 | class ProgressBarParameterItem(WidgetParameterItem): 7 | def makeWidget(self): 8 | w = QtWidgets.QProgressBar() 9 | w.setMaximumHeight(20) 10 | w.sigChanged = w.valueChanged 11 | self.hideWidget = False 12 | return w 13 | 14 | 15 | class ProgressBarParameter(Parameter): 16 | """ 17 | Displays a progress bar whose value can be set between 0 and 100 18 | """ 19 | itemClass = ProgressBarParameterItem 20 | -------------------------------------------------------------------------------- /tests/unittests/test_qgis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Test using the qgis testing framework. 4 | 5 | explained here: 6 | https://github.com/qgis/QGIS/tree/master/.docker 7 | """ 8 | 9 | import sys 10 | 11 | from qgis.testing import unittest 12 | 13 | 14 | class TestTest(unittest.TestCase): 15 | 16 | def test_passes(self): 17 | self.assertTrue(True) 18 | 19 | def run_all(): 20 | """Default function that is called by the runner if nothing else is specified""" 21 | suite = unittest.TestSuite() 22 | suite.addTests(unittest.makeSuite(TestTest, 'test')) 23 | unittest.TextTestRunner(verbosity=3, stream=sys.stdout).run(suite) -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/util/cupy_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from warnings import warn 4 | 5 | from .. import getConfigOption 6 | 7 | 8 | def getCupy(): 9 | if getConfigOption("useCupy"): 10 | try: 11 | import cupy 12 | except ImportError: 13 | warn("cupy library could not be loaded, but 'useCupy' is set.") 14 | return None 15 | if os.name == "nt" and cupy.cuda.runtime.runtimeGetVersion() < 11000: 16 | warn("In Windows, CUDA toolkit should be version 11 or higher, or some functions may misbehave.") 17 | return cupy 18 | else: 19 | return None 20 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/ItemGroup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtCore 3 | from .GraphicsObject import GraphicsObject 4 | 5 | __all__ = ['ItemGroup'] 6 | class ItemGroup(GraphicsObject): 7 | """ 8 | Replacement for QGraphicsItemGroup 9 | """ 10 | 11 | def __init__(self, *args): 12 | GraphicsObject.__init__(self, *args) 13 | self.setFlag(self.GraphicsItemFlag.ItemHasNoContents) 14 | 15 | def boundingRect(self): 16 | return QtCore.QRectF() 17 | 18 | def paint(self, *args): 19 | pass 20 | 21 | def addItem(self, item): 22 | item.setParentItem(self) 23 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/__init__.py: -------------------------------------------------------------------------------- 1 | ### just import everything from sub-modules 2 | 3 | #import os 4 | 5 | #d = os.path.split(__file__)[0] 6 | #files = [] 7 | #for f in os.listdir(d): 8 | #if os.path.isdir(os.path.join(d, f)): 9 | #files.append(f) 10 | #elif f[-3:] == '.py' and f != '__init__.py': 11 | #files.append(f[:-3]) 12 | 13 | #for modName in files: 14 | #mod = __import__(modName, globals(), locals(), fromlist=['*']) 15 | #if hasattr(mod, '__all__'): 16 | #names = mod.__all__ 17 | #else: 18 | #names = [n for n in dir(mod) if n[0] != '_'] 19 | #for k in names: 20 | ##print modName, k 21 | #globals()[k] = getattr(mod, k) 22 | -------------------------------------------------------------------------------- /imodqgis/utils/pathing.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | import os 5 | import platform 6 | from pathlib import Path 7 | 8 | 9 | def get_configdir() -> Path: 10 | """ 11 | Get the location of the imod-qgis plugin settings. 12 | 13 | The location differs per OS. Configdir is created if not exists. 14 | 15 | Returns 16 | ------- 17 | configdir: pathlib.Path 18 | """ 19 | if platform.system() == "Windows": 20 | configdir = Path(os.environ["APPDATA"]) / "imod-qgis" 21 | else: 22 | configdir = Path(os.environ["HOME"]) / ".imod-qgis" 23 | 24 | # If not present, make directory 25 | configdir.mkdir(exist_ok=True, mode=766) 26 | 27 | return configdir 28 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/util/get_resolution.py: -------------------------------------------------------------------------------- 1 | from .. import mkQApp 2 | from ..Qt import QtGui 3 | 4 | def test_screenInformation(): 5 | # a qApp is still needed, otherwise screen is None 6 | qApp = mkQApp() 7 | screen = QtGui.QGuiApplication.primaryScreen() 8 | screens = QtGui.QGuiApplication.screens() 9 | resolution = screen.size() 10 | availableResolution = screen.availableSize() 11 | print("Screen resolution: {}x{}".format(resolution.width(), resolution.height())) 12 | print("Available geometry: {}x{}".format(availableResolution.width(), availableResolution.height())) 13 | print("Number of Screens: {}".format(len(screens))) 14 | return None 15 | 16 | 17 | if __name__ == "__main__": 18 | test_screenInformation() 19 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/text.py: -------------------------------------------------------------------------------- 1 | from .basetypes import WidgetParameterItem 2 | from ..Parameter import Parameter 3 | from ...Qt import QtWidgets, QtCore 4 | 5 | 6 | class TextParameterItem(WidgetParameterItem): 7 | """ParameterItem displaying a QTextEdit widget.""" 8 | 9 | def makeWidget(self): 10 | self.hideWidget = False 11 | self.asSubItem = True 12 | self.textBox = w = QtWidgets.QTextEdit() 13 | w.sizeHint = lambda: QtCore.QSize(300, 100) 14 | w.value = w.toPlainText 15 | w.setValue = w.setPlainText 16 | w.sigChanged = w.textChanged 17 | return w 18 | 19 | 20 | class TextParameter(Parameter): 21 | """Editable string, displayed as large text box in the tree.""" 22 | itemClass = TextParameterItem 23 | -------------------------------------------------------------------------------- /scripts/compile_resources.bat: -------------------------------------------------------------------------------- 1 | rem Set the OSGeo4W environmental variables 2 | rem See if OSGeo4W batch file exist in an OSGeo4W dir 3 | rem If that doesn't work, try to find the QGIS dir (version numbers may differ) 4 | if exist c:\OSGeo4W64\OSGeo4W.bat ( 5 | call c:\OSGeo4W64\bin\o4w_env.bat 6 | call c:\OSGeo4W64\bin\qt5_env.bat 7 | call c:\OSGeo4W64\bin\py3_env.bat 8 | ) else ( 9 | rem You have to use a for loop to set the output as a variable ... 10 | for /f "tokens=*" %%f in ('dir /b "c:\Program Files" ^| find "QGIS 3.1"') do ( 11 | set qgis=%%f 12 | ) 13 | call "c:\Program Files\%%qgis%%\bin\o4w_env.bat" 14 | call "c:\Program Files\%%qgis%%\bin\qt5_env.bat" 15 | call "c:\Program Files\%%qgis%%\bin\py3_env.bat" 16 | ) 17 | 18 | cd ..\imodqgis 19 | call pyrcc5 resources.qrc -o resources.py 20 | pause -------------------------------------------------------------------------------- /imodqgis/widgets/dock_widget.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from qgis.gui import QgsDockWidget 5 | from qgis.PyQt.QtCore import Qt 6 | 7 | FLAGS = ( 8 | Qt.CustomizeWindowHint 9 | | Qt.Window 10 | | Qt.WindowMinimizeButtonHint 11 | | Qt.WindowMaximizeButtonHint 12 | | Qt.WindowCloseButtonHint 13 | ) 14 | 15 | 16 | class ImodDockWidget(QgsDockWidget): 17 | """ 18 | This gives a minimize and maximize button to a DockWidget when detached. 19 | """ 20 | 21 | def __init__(self, parent=None): 22 | QgsDockWidget.__init__(self, parent) 23 | self.topLevelChanged.connect(self.onTopLevelChanged) 24 | 25 | def onTopLevelChanged(self): 26 | sender = self.sender() 27 | if sender.isFloating(): 28 | sender.setWindowFlags(FLAGS) 29 | sender.show() 30 | -------------------------------------------------------------------------------- /imodqgis/utils/temporal.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from qgis.core import ( 5 | QgsMeshDatasetIndex, 6 | ) 7 | 8 | 9 | def get_group_is_temporal(layer): 10 | """Returns list of booleans of which meshdataset groups are temporal""" 11 | indexes = layer.datasetGroupsIndexes() 12 | qgs_indexes = [QgsMeshDatasetIndex(group=i, dataset=0) for i in indexes] 13 | return [layer.datasetGroupMetadata(i).isTemporal() for i in qgs_indexes] 14 | 15 | 16 | def is_temporal_meshlayer(layer): 17 | """ 18 | Return whether layer is temporal, 19 | 20 | There currently does not seem to exist a qgis mesh layer attribute that 21 | indicates whether there is a temporal dataset stored in a layer. 22 | If there is one, this function is obsolete. 23 | """ 24 | 25 | return any(get_group_is_temporal(layer)) 26 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/BusyCursor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from contextlib import contextmanager 3 | 4 | from ..Qt import QtGui, QtCore 5 | 6 | __all__ = ["BusyCursor"] 7 | 8 | 9 | @contextmanager 10 | def BusyCursor(): 11 | """ 12 | Display a busy mouse cursor during long operations. 13 | Usage:: 14 | 15 | with BusyCursor(): 16 | doLongOperation() 17 | 18 | May be nested. If called from a non-gui thread, then the cursor will not be affected. 19 | """ 20 | app = QtCore.QCoreApplication.instance() 21 | in_gui_thread = (app is not None) and (QtCore.QThread.currentThread() == app.thread()) 22 | try: 23 | if in_gui_thread: 24 | QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor)) 25 | yield 26 | finally: 27 | if in_gui_thread: 28 | QtGui.QApplication.restoreOverrideCursor() 29 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/library/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from collections import OrderedDict 3 | import os, types 4 | from ...debug import printExc 5 | from ..NodeLibrary import NodeLibrary, isNodeClass 6 | from ... import reload as reload 7 | 8 | 9 | # Build default library 10 | LIBRARY = NodeLibrary() 11 | 12 | # For backward compatibility, expose the default library's properties here: 13 | NODE_LIST = LIBRARY.nodeList 14 | NODE_TREE = LIBRARY.nodeTree 15 | registerNodeType = LIBRARY.addNodeType 16 | getNodeTree = LIBRARY.getNodeTree 17 | getNodeType = LIBRARY.getNodeType 18 | 19 | # Add all nodes to the default library 20 | from . import Data, Display, Filters, Operators 21 | for mod in [Data, Display, Filters, Operators]: 22 | nodes = [getattr(mod, name) for name in dir(mod) if isNodeClass(getattr(mod, name))] 23 | for node in nodes: 24 | LIBRARY.addNodeType(node, [(mod.__name__.split('.')[-1],)]) 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Useful links 2 | ============ 3 | 4 | The PyQt5 and PyQgis documentation is quite scattered, and often we have to 5 | resort to reading the C++ API or calling ``dir()``. Here is a list of useful 6 | links which provide some information or examples on how to do things. Below are 7 | some useful links that helped us develop the plugin and its' test bench. 8 | 9 | PyQgis 10 | ------ 11 | 12 | * [PyQgis API](https://qgis.org/pyqgis/3.16/) 13 | * [QGIS 3 Cheatsheet](https://github.com/All4Gis/QGIS-cheat-sheet/blob/master/QGIS3.md) 14 | 15 | Testing PyQgis 16 | -------------- 17 | 18 | * [QGIS plugin testing demo](https://github.com/opengisch/qgis_plugins_test_demo) 19 | * [Python tests in QGIS repository](https://github.com/qgis/QGIS/tree/master/tests/src/python) 20 | * [GUI testing PyQt5 with signalspy](https://sharplydescribed.wordpress.com/2020/09/17/gui-qt5-testing-with-python-qsignalspy-qtest/) 21 | * [QTest in QGIS](https://gis.stackexchange.com/questions/250234/qtest-interactions-with-the-qgis-map-canvas) 22 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/multiprocess/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Multiprocessing utility library 3 | (parallelization done the way I like it) 4 | 5 | Luke Campagnola 6 | 2012.06.10 7 | 8 | This library provides: 9 | 10 | - simple mechanism for starting a new python interpreter process that can be controlled from the original process 11 | (this allows, for example, displaying and manipulating plots in a remote process 12 | while the parent process is free to do other work) 13 | - proxy system that allows objects hosted in the remote process to be used as if they were local 14 | - Qt signal connection between processes 15 | - very simple in-line parallelization (fork only; does not work on windows) for number-crunching 16 | 17 | TODO: 18 | allow remote processes to serve as rendering engines that pass pixmaps back to the parent process for display 19 | (RemoteGraphicsView class) 20 | """ 21 | 22 | from .processes import * 23 | from .parallelizer import Parallelize, CanceledError 24 | from .remoteproxy import proxy, ClosedError, NoResultError 25 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/color.py: -------------------------------------------------------------------------------- 1 | from ...widgets.ColorButton import ColorButton 2 | from ... import functions as fn 3 | from .basetypes import WidgetParameterItem, SimpleParameter 4 | 5 | 6 | class ColorParameterItem(WidgetParameterItem): 7 | """Registered parameter type which displays a :class:`ColorButton ` """ 8 | def makeWidget(self): 9 | w = ColorButton() 10 | w.sigChanged = w.sigColorChanged 11 | w.sigChanging = w.sigColorChanging 12 | w.value = w.color 13 | w.setValue = w.setColor 14 | self.hideWidget = False 15 | w.setFlat(True) 16 | return w 17 | 18 | 19 | class ColorParameter(SimpleParameter): 20 | itemClass = ColorParameterItem 21 | 22 | def _interpretValue(self, v): 23 | return fn.mkColor(v) 24 | 25 | def value(self): 26 | return fn.mkColor(super().value()) 27 | 28 | def saveState(self, filter=None): 29 | state = super().saveState(filter) 30 | state['value'] = self.value().getRgb() 31 | return state 32 | -------------------------------------------------------------------------------- /imodqgis/widgets/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from imodqgis.widgets.colors_dialog import PSEUDOCOLOR, UNIQUE_COLOR, ColorsDialog 5 | from imodqgis.widgets.dataset_variable_widget import ( 6 | MultipleVariablesWidget, 7 | VariablesWidget, 8 | ) 9 | from imodqgis.widgets.dock_widget import ImodDockWidget 10 | from imodqgis.widgets.maptools import ( 11 | LineGeometryPickerWidget, 12 | MultipleLineGeometryPickerWidget, 13 | PointGeometryPickerWidget, 14 | RectangleMapTool, 15 | ) 16 | from imodqgis.widgets.pseudocolor_widget import ImodPseudoColorWidget 17 | from imodqgis.widgets.unique_color_widget import ImodUniqueColorWidget 18 | 19 | __all__ = [ 20 | 'PSEUDOCOLOR', 21 | 'UNIQUE_COLOR', 22 | 'ColorsDialog', 23 | 'ImodDockWidget', 24 | 'ImodPseudoColorWidget', 25 | 'ImodUniqueColorWidget', 26 | 'LineGeometryPickerWidget', 27 | 'MultipleLineGeometryPickerWidget', 28 | 'MultipleVariablesWidget', 29 | 'PointGeometryPickerWidget', 30 | 'RectangleMapTool', 31 | 'VariablesWidget', 32 | ] -------------------------------------------------------------------------------- /imodqgis/utils/color.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtGui import QColor 2 | 3 | from qgis.core import ( 4 | QgsGradientStop, 5 | QgsGradientColorRamp 6 | ) 7 | import numpy as np 8 | 9 | from typing import List 10 | 11 | 12 | def create_colorramp( 13 | boundaries: List[float], 14 | colors: List[QColor], 15 | discrete: bool = False, 16 | ): 17 | """ 18 | Manually construct colorramp from boundaries and colors. The stops 19 | determined by the createColorRamp method appear to be broken. 20 | """ 21 | 22 | # For some reason discrete colormaps require the last color also added as 23 | # stop 24 | if discrete: 25 | indices_stops = slice(1, None) 26 | else: 27 | indices_stops = slice(1, -1) 28 | 29 | bound_arr = np.array(boundaries) 30 | boundaries_norm = (bound_arr-bound_arr[0])/(bound_arr[-1]-bound_arr[0]) 31 | stops = [ 32 | QgsGradientStop(stop, color) for stop, color in zip( 33 | boundaries_norm[indices_stops], colors[indices_stops] 34 | ) 35 | ] 36 | return QgsGradientColorRamp(colors[0], colors[-1], discrete, stops) 37 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/colormap.py: -------------------------------------------------------------------------------- 1 | from .basetypes import WidgetParameterItem, SimpleParameter 2 | from ...Qt import QtCore 3 | from ...colormap import ColorMap 4 | from ...widgets.GradientWidget import GradientWidget 5 | 6 | 7 | class ColorMapParameterItem(WidgetParameterItem): 8 | """Registered parameter type which displays a :class:`GradientWidget `""" 9 | def makeWidget(self): 10 | w = GradientWidget(orientation='bottom') 11 | w.sizeHint = lambda: QtCore.QSize(300, 35) 12 | w.sigChanged = w.sigGradientChangeFinished 13 | w.sigChanging = w.sigGradientChanged 14 | w.value = w.colorMap 15 | w.setValue = w.setColorMap 16 | self.hideWidget = False 17 | self.asSubItem = True 18 | return w 19 | 20 | 21 | class ColorMapParameter(SimpleParameter): 22 | itemClass = ColorMapParameterItem 23 | 24 | def _interpretValue(self, v): 25 | if v is not None and not isinstance(v, ColorMap): 26 | raise TypeError("Cannot set colormap parameter from object %r" % v) 27 | return v 28 | -------------------------------------------------------------------------------- /imodqgis/about/about_widget.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | import os 5 | 6 | from PyQt5.QtWidgets import ( 7 | QDialog, 8 | QLabel, 9 | QVBoxLayout, 10 | ) 11 | 12 | path_text = os.path.abspath(os.path.join(os.path.dirname(__file__), "about.md")) 13 | 14 | with open(path_text) as file: 15 | TEXT = file.read() 16 | 17 | 18 | class ImodAboutDialog(QDialog): 19 | def __init__(self, iface, parent=None): 20 | QDialog.__init__(self, parent) 21 | self.setWindowTitle("About the iMOD plugin") 22 | self.iface = iface 23 | 24 | # Set minimum width of Dialog 25 | self.setMinimumWidth(800) 26 | 27 | self.text = QLabel() 28 | 29 | # Set MarkdownText as TextFormat 30 | # https://doc.qt.io/qt-5/qt.html#TextFormat-enum 31 | self.text.setTextFormat(3) 32 | 33 | # Allow hyperlinks to open a browser tab 34 | self.text.setOpenExternalLinks(True) 35 | 36 | self.text.setText(TEXT) 37 | 38 | layout = QVBoxLayout() 39 | layout.addWidget(self.text) 40 | self.setLayout(layout) 41 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/metaarray/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Luke Campagnola ('luke.campagnola@%s.com' % 'gmail') 2 | The MIT License 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/opengl/items/GLBarGraphItem.py: -------------------------------------------------------------------------------- 1 | from .GLMeshItem import GLMeshItem 2 | from ..MeshData import MeshData 3 | import numpy as np 4 | 5 | class GLBarGraphItem(GLMeshItem): 6 | def __init__(self, pos, size): 7 | """ 8 | pos is (...,3) array of the bar positions (the corner of each bar) 9 | size is (...,3) array of the sizes of each bar 10 | """ 11 | nCubes = np.prod(pos.shape[:-1]) 12 | cubeVerts = np.mgrid[0:2,0:2,0:2].reshape(3,8).transpose().reshape(1,8,3) 13 | cubeFaces = np.array([ 14 | [0,1,2], [3,2,1], 15 | [4,5,6], [7,6,5], 16 | [0,1,4], [5,4,1], 17 | [2,3,6], [7,6,3], 18 | [0,2,4], [6,4,2], 19 | [1,3,5], [7,5,3]]).reshape(1,12,3) 20 | size = size.reshape((nCubes, 1, 3)) 21 | pos = pos.reshape((nCubes, 1, 3)) 22 | verts = cubeVerts * size + pos 23 | faces = cubeFaces + (np.arange(nCubes) * 8).reshape(nCubes,1,1) 24 | md = MeshData(verts.reshape(nCubes*8,3), faces.reshape(nCubes*12,3)) 25 | 26 | GLMeshItem.__init__(self, meshdata=md, shader='shaded', smooth=False) 27 | -------------------------------------------------------------------------------- /scripts/add_spdx.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from glob import glob 3 | from pathlib import Path 4 | 5 | SPDX = """\ 6 | # Copyright © 2021 Deltares 7 | # SPDX-License-Identifier: GPL-2.0-or-later 8 | # 9 | """ 10 | 11 | wdir = (Path(sys.path[0]) / ".." / "imodqgis").resolve() 12 | 13 | files = glob(str(wdir / "**" / "*.py"), recursive=True) 14 | files = [Path(file) for file in files] 15 | 16 | # Filter default scripts that were added by the QGIS plugin builder 17 | files = [file for file in files if not file.match("imodqgis/plugin_upload.py")] 18 | files = [file for file in files if not file.match("imodqgis/resources.py")] 19 | 20 | # Filter dependencies, we are not copyrighting those 21 | files = [file for file in files if "dependencies" not in list(file.parts)] 22 | 23 | for file in files: 24 | with open(file, "r+", encoding="utf-8") as fd: 25 | contents = fd.readlines() 26 | if contents[0:3] != SPDX.splitlines(True): # Only add SPDX if not yet in file 27 | contents.insert(0, SPDX) # new_string should end in a newline 28 | fd.seek(0) # readlines consumes the iterator, so we need to start over 29 | fd.writelines(contents) # No need to truncate as we are increasing filesize 30 | -------------------------------------------------------------------------------- /imodqgis/utils/user_communication.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from qgis.core import Qgis, QgsMessageLog 5 | from qgis.PyQt.QtWidgets import QMessageBox 6 | 7 | 8 | class UserCommunication: 9 | """Class for communication with user""" 10 | 11 | def __init__(self, iface, context): 12 | self.iface = iface 13 | self.context = context 14 | 15 | def show_info(self, msg): 16 | QMessageBox.information(self.iface.mainWindow(), self.context, msg) 17 | 18 | def show_warn(self, msg): 19 | QMessageBox.warning(self.iface.mainWindow(), self.context, msg) 20 | 21 | def log_info(self, msg): 22 | QgsMessageLog.logMessage(msg, self.context, QgsMessageLog.INFO) 23 | 24 | def bar_error(self, msg): 25 | self.iface.messageBar().pushMessage(self.context, msg, level=Qgis.Critical) 26 | 27 | def bar_warn(self, msg, dur=5): 28 | self.iface.messageBar().pushMessage( 29 | self.context, msg, level=Qgis.Warning, duration=dur 30 | ) 31 | 32 | def bar_info(self, msg, dur=5): 33 | self.iface.messageBar().pushMessage(self.context, msg, duration=dur) 34 | 35 | def clear_bar_messages(self): 36 | self.iface.messageBar().clearWidgets() 37 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/MatplotlibWidget.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtGui 3 | 4 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas 5 | from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar 6 | 7 | from matplotlib.figure import Figure 8 | 9 | __all__ = ['MatplotlibWidget'] 10 | 11 | class MatplotlibWidget(QtGui.QWidget): 12 | """ 13 | Implements a Matplotlib figure inside a QWidget. 14 | Use getFigure() and redraw() to interact with matplotlib. 15 | 16 | Example:: 17 | 18 | mw = MatplotlibWidget() 19 | subplot = mw.getFigure().add_subplot(111) 20 | subplot.plot(x,y) 21 | mw.draw() 22 | """ 23 | 24 | def __init__(self, size=(5.0, 4.0), dpi=100): 25 | QtGui.QWidget.__init__(self) 26 | self.fig = Figure(size, dpi=dpi) 27 | self.canvas = FigureCanvas(self.fig) 28 | self.canvas.setParent(self) 29 | self.toolbar = NavigationToolbar(self.canvas, self) 30 | 31 | self.vbox = QtGui.QVBoxLayout() 32 | self.vbox.addWidget(self.toolbar) 33 | self.vbox.addWidget(self.canvas) 34 | 35 | self.setLayout(self.vbox) 36 | 37 | def getFigure(self): 38 | return self.fig 39 | 40 | def draw(self): 41 | self.canvas.draw() 42 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/console/CmdInput.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtCore, QtGui 3 | 4 | class CmdInput(QtGui.QLineEdit): 5 | 6 | sigExecuteCmd = QtCore.Signal(object) 7 | 8 | def __init__(self, parent): 9 | QtGui.QLineEdit.__init__(self, parent) 10 | self.history = [""] 11 | self.ptr = 0 12 | 13 | def keyPressEvent(self, ev): 14 | if ev.key() == QtCore.Qt.Key.Key_Up: 15 | if self.ptr < len(self.history) - 1: 16 | self.setHistory(self.ptr+1) 17 | ev.accept() 18 | return 19 | elif ev.key() == QtCore.Qt.Key.Key_Down: 20 | if self.ptr > 0: 21 | self.setHistory(self.ptr-1) 22 | ev.accept() 23 | return 24 | elif ev.key() == QtCore.Qt.Key.Key_Return: 25 | self.execCmd() 26 | else: 27 | super().keyPressEvent(ev) 28 | self.history[0] = self.text() 29 | 30 | def execCmd(self): 31 | cmd = self.text() 32 | if len(self.history) == 1 or cmd != self.history[1]: 33 | self.history.insert(1, cmd) 34 | self.history[0] = "" 35 | self.setHistory(0) 36 | self.sigExecuteCmd.emit(cmd) 37 | 38 | def setHistory(self, num): 39 | self.ptr = num 40 | self.setText(self.history[self.ptr]) 41 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/font.py: -------------------------------------------------------------------------------- 1 | from .basetypes import WidgetParameterItem 2 | from ..Parameter import Parameter 3 | from ...Qt import QtWidgets, QtGui 4 | 5 | 6 | class FontParameterItem(WidgetParameterItem): 7 | def makeWidget(self): 8 | w = QtWidgets.QFontComboBox() 9 | w.setMaximumHeight(20) 10 | w.sigChanged = w.currentFontChanged 11 | w.value = w.currentFont 12 | w.setValue = w.setCurrentFont 13 | self.hideWidget = False 14 | return w 15 | 16 | def updateDisplayLabel(self, value=None): 17 | if value is None: 18 | value = self.widget.currentText() 19 | super().updateDisplayLabel(value) 20 | 21 | 22 | class FontParameter(Parameter): 23 | """ 24 | Creates and controls a QFont value. Be careful when selecting options from the font dropdown. since not all 25 | fonts are available on all systems 26 | """ 27 | itemClass = FontParameterItem 28 | 29 | def _interpretValue(self, v): 30 | if isinstance(v, str): 31 | newVal = QtGui.QFont() 32 | if not newVal.fromString(v): 33 | raise ValueError(f'Error parsing font "{v}"') 34 | v = newVal 35 | return v 36 | 37 | def saveState(self, filter=None): 38 | state = super().saveState(filter) 39 | state['value'] = state['value'].toString() 40 | return state 41 | -------------------------------------------------------------------------------- /.github/running_tests_locally.md: -------------------------------------------------------------------------------- 1 | 2 | Running workflow on Windows 3 | ========================== 4 | This document describes how to run the github workflow on your windows machine. 5 | This makes writing tests doable. 6 | 7 | 1: Enable WSL2 8 | ------------------- 9 | [Follow the instructions 10 | here.](https://docs.microsoft.com/en-us/windows/wsl/install-win10#manual-installation-steps) 11 | 12 | 2: Install Docker 13 | --------------------- 14 | [Follow the instructions here.]( 15 | https://docs.docker.com/docker-for-windows/install/) 16 | 17 | 3: Install ``act`` 18 | -------------------- 19 | [Follow the instructions here.](https://github.com/nektos/act) 20 | 21 | 4: Manually modify 1 line in tests.yml 22 | --------------------------------------- 23 | Change the line under of code in tests.yml, for the step: "Install qgis image" 24 | Change the absolute path if your working directory is not ``c:/src/imod-qgis``! 25 | 26 | 5: Run ``act`` 27 | ------------------- 28 | Open up a prompt and run the command ``act``. This will fetch the QGIS docker 29 | image, and run the tests on it. **Note that you have to delete the running qgis 30 | container in order to run the complete workflow again!** 31 | 32 | 6: Re-run specific tests 33 | --------------------------- 34 | If you want to run specific tests on a running docker container, in your 35 | prompt, execute the following command: 36 | ``docker exec -t qgis-testing-environment sh -c "cd /tests_directory/tests && qgis_testrunner.sh unittests.test_maptools"`` -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/ptime.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ptime.py - Precision time function made os-independent (should have been taken care of by python) 4 | Copyright 2010 Luke Campagnola 5 | Distributed under MIT/X11 license. See license.txt for more information. 6 | """ 7 | 8 | 9 | import sys 10 | import warnings 11 | 12 | from time import perf_counter as clock 13 | from time import time as system_time 14 | 15 | START_TIME = None 16 | time = None 17 | 18 | def winTime(): 19 | """Return the current time in seconds with high precision (windows version, use Manager.time() to stay platform independent).""" 20 | warnings.warn( 21 | "'pg.time' will be removed from the library in the first release following January, 2022. Use time.perf_counter instead", 22 | DeprecationWarning, stacklevel=2 23 | ) 24 | return clock() + START_TIME 25 | 26 | def unixTime(): 27 | """Return the current time in seconds with high precision (unix version, use Manager.time() to stay platform independent).""" 28 | warnings.warn( 29 | "'pg.time' will be removed from the library in the first release following January, 2022. Use time.perf_counter instead", 30 | DeprecationWarning, stacklevel=2 31 | ) 32 | return system_time() 33 | 34 | 35 | if sys.platform.startswith('win'): 36 | cstart = clock() ### Required to start the clock in windows 37 | START_TIME = system_time() - cstart 38 | 39 | time = winTime 40 | else: 41 | time = unixTime 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iMOD-QGIS plugin 2 | This plugin aids exploring 4D geospatial data and links to the iMOD 3D viewer. 3 | It is part of a larger software suite, named [iMOD Suite](https://deltares.github.io/iMOD-Documentation/index.html). 4 | 5 | Primary components are: 6 | 7 | * Timeseries visualization 8 | * Cross-section visualization 9 | * Connecting to the iMOD 3D viewer 10 | * Reading .IPF files 11 | * Connecting to the NHI Data portal 12 | 13 | Mesh data can be used to store data with a time, z, y, 14 | and x dimension. 15 | Currently the z-dimension is only scarcely supported by MDAL. 16 | Therefore, for each vertical layer, 17 | we require a mesh dataset with the following variables: 18 | 19 | * `{var}_layer_{nr}` 20 | * `top_layer_{nr}` 21 | * `bottom_layer_{nr}` 22 | 23 | To prepare your datasets for the iMOD QGIS plugin, [we advice using this utility 24 | function in iMOD 25 | Python](https://deltares.github.io/imod-python/api/generated/util/imod.util.mdal_compliant_ugrid2d.html#imod.util.mdal_compliant_ugrid2d). 26 | If you'd like to prepare data yourself for the plugin, [follow this 27 | example.](https://deltares.github.io/iMOD-Documentation/tutorial_wq.html#convert-output-data). 28 | We expect to make this less specific in the future. 29 | 30 | ## Further reading 31 | 32 | * [Installation instructions](https://deltares.github.io/iMOD-Documentation/qgis_install.html) 33 | 34 | * [User manual](https://deltares.github.io/iMOD-Documentation/qgis_user_manual.html) 35 | 36 | * [Known issues](https://deltares.github.io/iMOD-Documentation/qgis_known_issues.html) 37 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartGraphicsView.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtGui, QtCore 3 | from ..widgets.GraphicsView import GraphicsView 4 | from ..graphicsItems.ViewBox import ViewBox 5 | 6 | translate = QtCore.QCoreApplication.translate 7 | 8 | class FlowchartGraphicsView(GraphicsView): 9 | 10 | sigHoverOver = QtCore.Signal(object) 11 | sigClicked = QtCore.Signal(object) 12 | 13 | def __init__(self, widget, *args): 14 | GraphicsView.__init__(self, *args, useOpenGL=False) 15 | self._vb = FlowchartViewBox(widget, lockAspect=True, invertY=True) 16 | self.setCentralItem(self._vb) 17 | self.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True) 18 | 19 | def viewBox(self): 20 | return self._vb 21 | 22 | 23 | class FlowchartViewBox(ViewBox): 24 | 25 | def __init__(self, widget, *args, **kwargs): 26 | ViewBox.__init__(self, *args, **kwargs) 27 | self.widget = widget 28 | 29 | def getMenu(self, ev): 30 | ## called by ViewBox to create a new context menu 31 | self._fc_menu = QtGui.QMenu() 32 | self._subMenus = self.getContextMenus(ev) 33 | for menu in self._subMenus: 34 | self._fc_menu.addMenu(menu) 35 | return self._fc_menu 36 | 37 | def getContextMenus(self, ev): 38 | ## called by scene to add menus on to someone else's context menu 39 | menu = self.widget.buildMenu(ev.scenePos()) 40 | menu.setTitle(translate("Context Menu", "Add node")) 41 | return [menu, ViewBox.getMenu(self, ev)] 42 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/util/colorama/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Jonathan Hartley 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the copyright holders, nor those of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/HistogramLUTWidget.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Widget displaying an image histogram along with gradient editor. Can be used to adjust 4 | the appearance of images. This is a wrapper around HistogramLUTItem 5 | """ 6 | 7 | from ..Qt import QtGui, QtCore 8 | from .GraphicsView import GraphicsView 9 | from ..graphicsItems.HistogramLUTItem import HistogramLUTItem 10 | 11 | __all__ = ['HistogramLUTWidget'] 12 | 13 | 14 | class HistogramLUTWidget(GraphicsView): 15 | """QWidget wrapper for :class:`~pyqtgraph.HistogramLUTItem`. 16 | 17 | All parameters are passed along in creating the HistogramLUTItem. 18 | """ 19 | 20 | def __init__(self, parent=None, *args, **kargs): 21 | background = kargs.pop('background', 'default') 22 | GraphicsView.__init__(self, parent, useOpenGL=False, background=background) 23 | self.item = HistogramLUTItem(*args, **kargs) 24 | self.setCentralItem(self.item) 25 | 26 | self.orientation = kargs.get('orientation', 'vertical') 27 | if self.orientation == 'vertical': 28 | self.setSizePolicy(QtGui.QSizePolicy.Policy.Preferred, QtGui.QSizePolicy.Policy.Expanding) 29 | self.setMinimumWidth(95) 30 | else: 31 | self.setSizePolicy(QtGui.QSizePolicy.Policy.Expanding, QtGui.QSizePolicy.Policy.Preferred) 32 | self.setMinimumHeight(95) 33 | 34 | def sizeHint(self): 35 | if self.orientation == 'vertical': 36 | return QtCore.QSize(115, 200) 37 | else: 38 | return QtCore.QSize(200, 115) 39 | 40 | def __getattr__(self, attr): 41 | return getattr(self.item, attr) 42 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/functions_numba.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import numba 3 | 4 | rescale_functions = {} 5 | 6 | def rescale_clip_source(xx, scale, offset, vmin, vmax, yy): 7 | for i in range(xx.size): 8 | val = (xx[i] - offset) * scale 9 | yy[i] = min(max(val, vmin), vmax) 10 | 11 | def rescaleData(data, scale, offset, dtype, clip): 12 | data_out = np.empty_like(data, dtype=dtype) 13 | key = (data.dtype.name, data_out.dtype.name) 14 | func = rescale_functions.get(key) 15 | if func is None: 16 | func = numba.guvectorize( 17 | [f'{key[0]}[:],f8,f8,f8,f8,{key[1]}[:]'], 18 | '(n),(),(),(),()->(n)', 19 | nopython=True)(rescale_clip_source) 20 | rescale_functions[key] = func 21 | func(data, scale, offset, clip[0], clip[1], out=data_out) 22 | return data_out 23 | 24 | @numba.jit(nopython=True) 25 | def _rescale_and_lookup1d_function(data, scale, offset, lut, out): 26 | vmin, vmax = 0, lut.shape[0] - 1 27 | for r in range(data.shape[0]): 28 | for c in range(data.shape[1]): 29 | val = (data[r, c] - offset) * scale 30 | val = min(max(val, vmin), vmax) 31 | out[r, c] = lut[int(val)] 32 | 33 | def rescale_and_lookup1d(data, scale, offset, lut): 34 | # data should be floating point and 2d 35 | # lut is 1d 36 | data_out = np.empty_like(data, dtype=lut.dtype) 37 | _rescale_and_lookup1d_function(data, float(scale), float(offset), lut, data_out) 38 | return data_out 39 | 40 | @numba.jit(nopython=True) 41 | def numba_take(lut, data): 42 | # numba supports only the 1st two arguments of np.take 43 | return np.take(lut, data) 44 | -------------------------------------------------------------------------------- /.github/workflows/package.yml: -------------------------------------------------------------------------------- 1 | # Modified from https://github.com/GispoCoding/qaava-qgis-plugin/blob/master/.github/workflows/tests.yml 2 | # workflow name 3 | name: Package 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the wanted branches 7 | on: 8 | push: 9 | branches: 10 | - main 11 | tags: 12 | - 'v*' # or 'v*' if you only want tags starting with 'v' 13 | pull_request: 14 | branches: 15 | - main 16 | 17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 18 | jobs: 19 | package: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@v3 26 | 27 | - name: Copy plugin to directory for packaging 28 | run: | 29 | rm -rf imodqgis_packaged 30 | mkdir -m 755 imodqgis_packaged 31 | cp -R imodqgis imodqgis_packaged/imodqgis 32 | shell: bash 33 | 34 | - name: Create zip for release 35 | if: startsWith(github.ref, 'refs/tags/') 36 | run: | 37 | cd imodqgis_packaged 38 | zip -r ../imodqgis_${{ github.ref_name }}.zip . 39 | cd .. 40 | 41 | - name: Upload imodqgis to Github artifact 42 | if: always() 43 | uses: actions/upload-artifact@v4 44 | with: 45 | path: imodqgis_packaged 46 | name: imodqgis_${{ github.sha }} 47 | 48 | - name: Upload imodqgis to release 49 | if: startsWith(github.ref, 'refs/tags/') 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | run: | 53 | gh release upload ${{ github.ref_name }} imodqgis_${{ github.ref_name }}.zip --clobber 54 | -------------------------------------------------------------------------------- /imodqgis/icons/deltares.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/PlotData.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class PlotData(object): 5 | """ 6 | Class used for managing plot data 7 | - allows data sharing between multiple graphics items (curve, scatter, graph..) 8 | - each item may define the columns it needs 9 | - column groupings ('pos' or x, y, z) 10 | - efficiently appendable 11 | - log, fft transformations 12 | - color mode conversion (float/byte/qcolor) 13 | - pen/brush conversion 14 | - per-field cached masking 15 | - allows multiple masking fields (different graphics need to mask on different criteria) 16 | - removal of nan/inf values 17 | - option for single value shared by entire column 18 | - cached downsampling 19 | - cached min / max / hasnan / isuniform 20 | """ 21 | def __init__(self): 22 | self.fields = {} 23 | 24 | self.maxVals = {} ## cache for max/min 25 | self.minVals = {} 26 | 27 | def addFields(self, **fields): 28 | for f in fields: 29 | if f not in self.fields: 30 | self.fields[f] = None 31 | 32 | def hasField(self, f): 33 | return f in self.fields 34 | 35 | def __getitem__(self, field): 36 | return self.fields[field] 37 | 38 | def __setitem__(self, field, val): 39 | self.fields[field] = val 40 | 41 | def max(self, field): 42 | mx = self.maxVals.get(field, None) 43 | if mx is None: 44 | mx = np.max(self[field]) 45 | self.maxVals[field] = mx 46 | return mx 47 | 48 | def min(self, field): 49 | mn = self.minVals.get(field, None) 50 | if mn is None: 51 | mn = np.min(self[field]) 52 | self.minVals[field] = mn 53 | return mn 54 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/opengl/__init__.py: -------------------------------------------------------------------------------- 1 | from .GLViewWidget import GLViewWidget 2 | 3 | ## dynamic imports cause too many problems. 4 | #from .. import importAll 5 | #importAll('items', globals(), locals()) 6 | 7 | from .items.GLGraphItem import * 8 | from .items.GLGridItem import * 9 | from .items.GLBarGraphItem import * 10 | from .items.GLScatterPlotItem import * 11 | from .items.GLMeshItem import * 12 | from .items.GLLinePlotItem import * 13 | from .items.GLAxisItem import * 14 | from .items.GLImageItem import * 15 | from .items.GLSurfacePlotItem import * 16 | from .items.GLBoxItem import * 17 | from .items.GLVolumeItem import * 18 | from .items.GLTextItem import * 19 | from .items.GLGradientLegendItem import * 20 | 21 | from .MeshData import MeshData 22 | ## for backward compatibility: 23 | #MeshData.MeshData = MeshData ## breaks autodoc. 24 | 25 | from . import shaders 26 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/ThreadsafeTimer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .Qt import QtCore 3 | 4 | __all__ = ['ThreadsafeTimer'] 5 | 6 | class ThreadsafeTimer(QtCore.QObject): 7 | """ 8 | Thread-safe replacement for QTimer. 9 | """ 10 | 11 | timeout = QtCore.Signal() 12 | sigTimerStopRequested = QtCore.Signal() 13 | sigTimerStartRequested = QtCore.Signal(object) 14 | 15 | def __init__(self): 16 | QtCore.QObject.__init__(self) 17 | self.timer = QtCore.QTimer() 18 | self.timer.timeout.connect(self.timerFinished) 19 | self.timer.moveToThread(QtCore.QCoreApplication.instance().thread()) 20 | self.moveToThread(QtCore.QCoreApplication.instance().thread()) 21 | self.sigTimerStopRequested.connect(self.stop, QtCore.Qt.ConnectionType.QueuedConnection) 22 | self.sigTimerStartRequested.connect(self.start, QtCore.Qt.ConnectionType.QueuedConnection) 23 | 24 | 25 | def start(self, timeout): 26 | isGuiThread = QtCore.QThread.currentThread() == QtCore.QCoreApplication.instance().thread() 27 | if isGuiThread: 28 | #print "start timer", self, "from gui thread" 29 | self.timer.start(int(timeout)) 30 | else: 31 | #print "start timer", self, "from remote thread" 32 | self.sigTimerStartRequested.emit(timeout) 33 | 34 | def stop(self): 35 | isGuiThread = QtCore.QThread.currentThread() == QtCore.QCoreApplication.instance().thread() 36 | if isGuiThread: 37 | #print "stop timer", self, "from gui thread" 38 | self.timer.stop() 39 | else: 40 | #print "stop timer", self, "from remote thread" 41 | self.sigTimerStopRequested.emit() 42 | 43 | def timerFinished(self): 44 | self.timeout.emit() 45 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/util/garbage_collector.py: -------------------------------------------------------------------------------- 1 | import gc 2 | 3 | from ..Qt import QtCore 4 | 5 | class GarbageCollector(object): 6 | ''' 7 | Disable automatic garbage collection and instead collect manually 8 | on a timer. 9 | 10 | This is done to ensure that garbage collection only happens in the GUI 11 | thread, as otherwise Qt can crash. 12 | 13 | Credit: Erik Janssens 14 | Source: http://pydev.blogspot.com/2014/03/should-python-garbage-collector-be.html 15 | ''' 16 | 17 | def __init__(self, interval=1.0, debug=False): 18 | self.debug = debug 19 | if debug: 20 | gc.set_debug(gc.DEBUG_LEAK) 21 | 22 | self.timer = QtCore.QTimer() 23 | self.timer.timeout.connect(self.check) 24 | 25 | self.threshold = gc.get_threshold() 26 | gc.disable() 27 | self.timer.start(interval * 1000) 28 | 29 | def check(self): 30 | #return self.debug_cycles() # uncomment to just debug cycles 31 | l0, l1, l2 = gc.get_count() 32 | if self.debug: 33 | print('gc_check called:', l0, l1, l2) 34 | if l0 > self.threshold[0]: 35 | num = gc.collect(0) 36 | if self.debug: 37 | print('collecting gen 0, found: %d unreachable' % num) 38 | if l1 > self.threshold[1]: 39 | num = gc.collect(1) 40 | if self.debug: 41 | print('collecting gen 1, found: %d unreachable' % num) 42 | if l2 > self.threshold[2]: 43 | num = gc.collect(2) 44 | if self.debug: 45 | print('collecting gen 2, found: %d unreachable' % num) 46 | 47 | def debug_cycles(self): 48 | gc.collect() 49 | for obj in gc.garbage: 50 | print(obj, repr(obj), type(obj)) 51 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/PathButton.py: -------------------------------------------------------------------------------- 1 | from ..Qt import QtGui, QtCore 2 | from .. import functions as fn 3 | 4 | __all__ = ['PathButton'] 5 | 6 | 7 | class PathButton(QtGui.QPushButton): 8 | """Simple PushButton extension that paints a QPainterPath centered on its face. 9 | """ 10 | def __init__(self, parent=None, path=None, pen='default', brush=None, size=(30,30), margin=7): 11 | QtGui.QPushButton.__init__(self, parent) 12 | self.margin = margin 13 | self.path = None 14 | if pen == 'default': 15 | pen = 'k' 16 | self.setPen(pen) 17 | self.setBrush(brush) 18 | if path is not None: 19 | self.setPath(path) 20 | if size is not None: 21 | self.setFixedWidth(size[0]) 22 | self.setFixedHeight(size[1]) 23 | 24 | def setBrush(self, brush): 25 | self.brush = fn.mkBrush(brush) 26 | 27 | def setPen(self, *args, **kwargs): 28 | self.pen = fn.mkPen(*args, **kwargs) 29 | 30 | def setPath(self, path): 31 | self.path = path 32 | self.update() 33 | 34 | def paintEvent(self, ev): 35 | super().paintEvent(ev) 36 | margin = self.margin 37 | geom = QtCore.QRectF(0, 0, self.width(), self.height()).adjusted(margin, margin, -margin, -margin) 38 | rect = self.path.boundingRect() 39 | scale = min(geom.width() / float(rect.width()), geom.height() / float(rect.height())) 40 | 41 | p = QtGui.QPainter(self) 42 | p.setRenderHint(p.RenderHint.Antialiasing) 43 | p.translate(geom.center()) 44 | p.scale(scale, scale) 45 | p.translate(-rect.center()) 46 | p.setPen(self.pen) 47 | p.setBrush(self.brush) 48 | p.drawPath(self.path) 49 | p.end() 50 | 51 | 52 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/ButtonItem.py: -------------------------------------------------------------------------------- 1 | from ..Qt import QtGui, QtCore 2 | from .GraphicsObject import GraphicsObject 3 | 4 | __all__ = ['ButtonItem'] 5 | class ButtonItem(GraphicsObject): 6 | """Button graphicsItem displaying an image.""" 7 | 8 | clicked = QtCore.Signal(object) 9 | 10 | def __init__(self, imageFile=None, width=None, parentItem=None, pixmap=None): 11 | self.enabled = True 12 | GraphicsObject.__init__(self) 13 | if imageFile is not None: 14 | self.setImageFile(imageFile) 15 | elif pixmap is not None: 16 | self.setPixmap(pixmap) 17 | 18 | if width is not None: 19 | s = float(width) / self.pixmap.width() 20 | self.setScale(s) 21 | if parentItem is not None: 22 | self.setParentItem(parentItem) 23 | self.setOpacity(0.7) 24 | 25 | def setImageFile(self, imageFile): 26 | self.setPixmap(QtGui.QPixmap(imageFile)) 27 | 28 | def setPixmap(self, pixmap): 29 | self.pixmap = pixmap 30 | self.update() 31 | 32 | def mouseClickEvent(self, ev): 33 | if self.enabled: 34 | self.clicked.emit(self) 35 | 36 | def mouseHoverEvent(self, ev): 37 | if not self.enabled: 38 | return 39 | if ev.isEnter(): 40 | self.setOpacity(1.0) 41 | else: 42 | self.setOpacity(0.7) 43 | 44 | def disable(self): 45 | self.enabled = False 46 | self.setOpacity(0.4) 47 | 48 | def enable(self): 49 | self.enabled = True 50 | self.setOpacity(0.7) 51 | 52 | def paint(self, p, *args): 53 | p.setRenderHint(p.RenderHint.Antialiasing) 54 | p.drawPixmap(0, 0, self.pixmap) 55 | 56 | def boundingRect(self): 57 | return QtCore.QRectF(self.pixmap.rect()) 58 | 59 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/units.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Very simple unit support: 3 | # - creates variable names like 'mV' and 'kHz' 4 | # - the value assigned to the variable corresponds to the scale prefix 5 | # (mV = 0.001) 6 | # - the actual units are purely cosmetic for making code clearer: 7 | # 8 | # x = 20*pA is identical to x = 20*1e-12 9 | # 10 | # No unicode variable names (μ,Ω) allowed until python 3, but just assigning 11 | # them to the globals dict doesn't error in python 2. 12 | import unicodedata 13 | 14 | # All unicode identifiers get normalized automatically 15 | SI_PREFIXES = unicodedata.normalize("NFKC", u"yzafpnµm kMGTPEZY") 16 | UNITS = unicodedata.normalize("NFKC", u"m,s,g,W,J,V,A,F,T,Hz,Ohm,Ω,S,N,C,px,b,B,Pa").split(",") 17 | allUnits = {} 18 | 19 | 20 | def addUnit(prefix, val): 21 | g = globals() 22 | for u in UNITS: 23 | g[prefix + u] = val 24 | allUnits[prefix + u] = val 25 | 26 | 27 | for pre in SI_PREFIXES: 28 | v = SI_PREFIXES.index(pre) - 8 29 | if pre == " ": 30 | pre = "" 31 | addUnit(pre, 1000 ** v) 32 | 33 | addUnit("c", 0.01) 34 | addUnit("d", 0.1) 35 | addUnit("da", 10) 36 | addUnit("h", 100) 37 | # py2 compatibility 38 | addUnit("u", 1e-6) 39 | 40 | 41 | def evalUnits(unitStr): 42 | """ 43 | Evaluate a unit string into ([numerators,...], [denominators,...]) 44 | Examples: 45 | N m/s^2 => ([N, m], [s, s]) 46 | A*s / V => ([A, s], [V,]) 47 | """ 48 | pass 49 | 50 | 51 | def formatUnits(units): 52 | """ 53 | Format a unit specification ([numerators,...], [denominators,...]) 54 | into a string (this is the inverse of evalUnits) 55 | """ 56 | pass 57 | 58 | 59 | def simplify(units): 60 | """ 61 | Cancel units that appear in both numerator and denominator, then attempt to replace 62 | groups of units with single units where possible (ie, J/s => W) 63 | """ 64 | pass 65 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/multiprocess/bootstrap.py: -------------------------------------------------------------------------------- 1 | """For starting up remote processes""" 2 | import sys, pickle, os 3 | import importlib 4 | 5 | if __name__ == '__main__': 6 | if hasattr(os, 'setpgrp'): 7 | os.setpgrp() ## prevents signals (notably keyboard interrupt) being forwarded from parent to this process 8 | if sys.version[0] == '3': 9 | #name, port, authkey, ppid, targetStr, path, pyside = pickle.load(sys.stdin.buffer) 10 | opts = pickle.load(sys.stdin.buffer) 11 | else: 12 | #name, port, authkey, ppid, targetStr, path, pyside = pickle.load(sys.stdin) 13 | opts = pickle.load(sys.stdin) 14 | #print "key:", ' '.join([str(ord(x)) for x in authkey]) 15 | path = opts.pop('path', None) 16 | if path is not None: 17 | if isinstance(path, str): 18 | # if string, just insert this into the path 19 | sys.path.insert(0, path) 20 | else: 21 | # if list, then replace the entire sys.path 22 | ## modify sys.path in place--no idea who already has a reference to the existing list. 23 | while len(sys.path) > 0: 24 | sys.path.pop() 25 | sys.path.extend(path) 26 | 27 | pyqtapis = opts.pop('pyqtapis', None) 28 | if pyqtapis is not None: 29 | try: 30 | from PyQt5 import sip 31 | except ImportError: 32 | import sip 33 | for k,v in pyqtapis.items(): 34 | sip.setapi(k, v) 35 | 36 | qt_lib = opts.pop('qt_lib', None) 37 | if qt_lib is not None: 38 | globals()[qt_lib] = importlib.import_module(qt_lib) 39 | 40 | targetStr = opts.pop('targetStr') 41 | try: 42 | target = pickle.loads(targetStr) ## unpickling the target should import everything we need 43 | except: 44 | print("Current sys.path:", sys.path) 45 | raise 46 | target(**opts) ## Send all other options to the target function 47 | sys.exit(0) 48 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/GraphicsObject.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtGui, QT_LIB 3 | if QT_LIB.startswith('PyQt'): 4 | from ..Qt import sip 5 | from .GraphicsItem import GraphicsItem 6 | 7 | __all__ = ['GraphicsObject'] 8 | class GraphicsObject(GraphicsItem, QtGui.QGraphicsObject): 9 | """ 10 | **Bases:** :class:`GraphicsItem `, :class:`QtGui.QGraphicsObject` 11 | 12 | Extension of QGraphicsObject with some useful methods (provided by :class:`GraphicsItem `) 13 | """ 14 | _qtBaseClass = QtGui.QGraphicsObject 15 | def __init__(self, *args): 16 | self.__inform_view_on_changes = True 17 | QtGui.QGraphicsObject.__init__(self, *args) 18 | self.setFlag(self.GraphicsItemFlag.ItemSendsGeometryChanges) 19 | GraphicsItem.__init__(self) 20 | 21 | def itemChange(self, change, value): 22 | ret = super().itemChange(change, value) 23 | if change in [self.GraphicsItemChange.ItemParentHasChanged, self.GraphicsItemChange.ItemSceneHasChanged]: 24 | self.parentChanged() 25 | try: 26 | inform_view_on_change = self.__inform_view_on_changes 27 | except AttributeError: 28 | # It's possible that the attribute was already collected when the itemChange happened 29 | # (if it was triggered during the gc of the object). 30 | pass 31 | else: 32 | if inform_view_on_change and change in [self.GraphicsItemChange.ItemPositionHasChanged, self.GraphicsItemChange.ItemTransformHasChanged]: 33 | self.informViewBoundsChanged() 34 | 35 | ## workaround for pyqt bug: 36 | ## http://www.riverbankcomputing.com/pipermail/pyqt/2012-August/031818.html 37 | if QT_LIB == 'PyQt5' and change == self.GraphicsItemChange.ItemParentChange and isinstance(ret, QtGui.QGraphicsItem): 38 | ret = sip.cast(ret, QtGui.QGraphicsItem) 39 | 40 | return ret 41 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/frozenSupport.py: -------------------------------------------------------------------------------- 1 | ## Definitions helpful in frozen environments (eg py2exe) 2 | import os, sys, zipfile 3 | 4 | def listdir(path): 5 | """Replacement for os.listdir that works in frozen environments.""" 6 | if not hasattr(sys, 'frozen'): 7 | return os.listdir(path) 8 | 9 | (zipPath, archivePath) = splitZip(path) 10 | if archivePath is None: 11 | return os.listdir(path) 12 | 13 | with zipfile.ZipFile(zipPath, "r") as zipobj: 14 | contents = zipobj.namelist() 15 | results = set() 16 | for name in contents: 17 | # components in zip archive paths are always separated by forward slash 18 | if name.startswith(archivePath) and len(name) > len(archivePath): 19 | name = name[len(archivePath):].split('/')[0] 20 | results.add(name) 21 | return list(results) 22 | 23 | def isdir(path): 24 | """Replacement for os.path.isdir that works in frozen environments.""" 25 | if not hasattr(sys, 'frozen'): 26 | return os.path.isdir(path) 27 | 28 | (zipPath, archivePath) = splitZip(path) 29 | if archivePath is None: 30 | return os.path.isdir(path) 31 | with zipfile.ZipFile(zipPath, "r") as zipobj: 32 | contents = zipobj.namelist() 33 | archivePath = archivePath.rstrip('/') + '/' ## make sure there's exactly one '/' at the end 34 | for c in contents: 35 | if c.startswith(archivePath): 36 | return True 37 | return False 38 | 39 | 40 | def splitZip(path): 41 | """Splits a path containing a zip file into (zipfile, subpath). 42 | If there is no zip file, returns (path, None)""" 43 | components = os.path.normpath(path).split(os.sep) 44 | for index, component in enumerate(components): 45 | if component.endswith('.zip'): 46 | zipPath = os.sep.join(components[0:index+1]) 47 | archivePath = ''.join([x+'/' for x in components[index+1:]]) 48 | return (zipPath, archivePath) 49 | else: 50 | return (path, None) 51 | 52 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/metaarray/readMeta.m: -------------------------------------------------------------------------------- 1 | function f = readMeta(file) 2 | info = hdf5info(file); 3 | f = readMetaRecursive(info.GroupHierarchy.Groups(1)); 4 | end 5 | 6 | 7 | function f = readMetaRecursive(root) 8 | typ = 0; 9 | for i = 1:length(root.Attributes) 10 | if strcmp(root.Attributes(i).Shortname, '_metaType_') 11 | typ = root.Attributes(i).Value.Data; 12 | break 13 | end 14 | end 15 | if typ == 0 16 | printf('group has no _metaType_') 17 | typ = 'dict'; 18 | end 19 | 20 | list = 0; 21 | if strcmp(typ, 'list') || strcmp(typ, 'tuple') 22 | data = {}; 23 | list = 1; 24 | elseif strcmp(typ, 'dict') 25 | data = struct(); 26 | else 27 | printf('Unrecognized meta type %s', typ); 28 | data = struct(); 29 | end 30 | 31 | for i = 1:length(root.Attributes) 32 | name = root.Attributes(i).Shortname; 33 | if strcmp(name, '_metaType_') 34 | continue 35 | end 36 | val = root.Attributes(i).Value; 37 | if isa(val, 'hdf5.h5string') 38 | val = val.Data; 39 | end 40 | if list 41 | ind = str2num(name)+1; 42 | data{ind} = val; 43 | else 44 | data.(name) = val; 45 | end 46 | end 47 | 48 | for i = 1:length(root.Datasets) 49 | fullName = root.Datasets(i).Name; 50 | name = stripName(fullName); 51 | file = root.Datasets(i).Filename; 52 | data2 = hdf5read(file, fullName); 53 | if list 54 | ind = str2num(name)+1; 55 | data{ind} = data2; 56 | else 57 | data.(name) = data2; 58 | end 59 | end 60 | 61 | for i = 1:length(root.Groups) 62 | name = stripName(root.Groups(i).Name); 63 | data2 = readMetaRecursive(root.Groups(i)); 64 | if list 65 | ind = str2num(name)+1; 66 | data{ind} = data2; 67 | else 68 | data.(name) = data2; 69 | end 70 | end 71 | f = data; 72 | return; 73 | end 74 | 75 | 76 | function f = stripName(str) 77 | inds = strfind(str, '/'); 78 | if isempty(inds) 79 | f = str; 80 | else 81 | f = str(inds(length(inds))+1:length(str)); 82 | end 83 | end 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/Transform3D.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .Qt import QtGui 3 | from . import functions as fn 4 | from .Vector import Vector 5 | import numpy as np 6 | 7 | 8 | class Transform3D(QtGui.QMatrix4x4): 9 | """ 10 | Extension of QMatrix4x4 with some helpful methods added. 11 | """ 12 | def __init__(self, *args): 13 | if len(args) == 1: 14 | if isinstance(args[0], (list, tuple, np.ndarray)): 15 | args = [x for y in args[0] for x in y] 16 | if len(args) != 16: 17 | raise TypeError("Single argument to Transform3D must have 16 elements.") 18 | elif isinstance(args[0], QtGui.QMatrix4x4): 19 | args = list(args[0].copyDataTo()) 20 | 21 | QtGui.QMatrix4x4.__init__(self, *args) 22 | 23 | def matrix(self, nd=3): 24 | if nd == 3: 25 | return np.array(self.copyDataTo()).reshape(4,4) 26 | elif nd == 2: 27 | m = np.array(self.copyDataTo()).reshape(4,4) 28 | m[2] = m[3] 29 | m[:,2] = m[:,3] 30 | return m[:3,:3] 31 | else: 32 | raise Exception("Argument 'nd' must be 2 or 3") 33 | 34 | def map(self, obj): 35 | """ 36 | Extends QMatrix4x4.map() to allow mapping (3, ...) arrays of coordinates 37 | """ 38 | if isinstance(obj, np.ndarray) and obj.shape[0] in (2,3): 39 | if obj.ndim >= 2: 40 | return fn.transformCoordinates(self, obj) 41 | elif obj.ndim == 1: 42 | v = QtGui.QMatrix4x4.map(self, Vector(obj)) 43 | return np.array([v.x(), v.y(), v.z()])[:obj.shape[0]] 44 | elif isinstance(obj, (list, tuple)): 45 | v = QtGui.QMatrix4x4.map(self, Vector(obj)) 46 | return type(obj)([v.x(), v.y(), v.z()])[:len(obj)] 47 | else: 48 | return QtGui.QMatrix4x4.map(self, obj) 49 | 50 | def inverted(self): 51 | inv, b = QtGui.QMatrix4x4.inverted(self) 52 | return Transform3D(inv), b 53 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/opengl/items/GLAxisItem.py: -------------------------------------------------------------------------------- 1 | from OpenGL.GL import * 2 | from .. GLGraphicsItem import GLGraphicsItem 3 | from ... import QtGui 4 | 5 | __all__ = ['GLAxisItem'] 6 | 7 | class GLAxisItem(GLGraphicsItem): 8 | """ 9 | **Bases:** :class:`GLGraphicsItem ` 10 | 11 | Displays three lines indicating origin and orientation of local coordinate system. 12 | 13 | """ 14 | 15 | def __init__(self, size=None, antialias=True, glOptions='translucent'): 16 | GLGraphicsItem.__init__(self) 17 | if size is None: 18 | size = QtGui.QVector3D(1,1,1) 19 | self.antialias = antialias 20 | self.setSize(size=size) 21 | self.setGLOptions(glOptions) 22 | 23 | def setSize(self, x=None, y=None, z=None, size=None): 24 | """ 25 | Set the size of the axes (in its local coordinate system; this does not affect the transform) 26 | Arguments can be x,y,z or size=QVector3D(). 27 | """ 28 | if size is not None: 29 | x = size.x() 30 | y = size.y() 31 | z = size.z() 32 | self.__size = [x,y,z] 33 | self.update() 34 | 35 | def size(self): 36 | return self.__size[:] 37 | 38 | 39 | def paint(self): 40 | 41 | #glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 42 | #glEnable( GL_BLEND ) 43 | #glEnable( GL_ALPHA_TEST ) 44 | self.setupGLState() 45 | 46 | if self.antialias: 47 | glEnable(GL_LINE_SMOOTH) 48 | glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) 49 | 50 | glBegin( GL_LINES ) 51 | 52 | x,y,z = self.size() 53 | glColor4f(0, 1, 0, .6) # z is green 54 | glVertex3f(0, 0, 0) 55 | glVertex3f(0, 0, z) 56 | 57 | glColor4f(1, 1, 0, .6) # y is yellow 58 | glVertex3f(0, 0, 0) 59 | glVertex3f(0, y, 0) 60 | 61 | glColor4f(0, 0, 1, .6) # x is blue 62 | glVertex3f(0, 0, 0) 63 | glVertex3f(x, 0, 0) 64 | glEnd() 65 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/action.py: -------------------------------------------------------------------------------- 1 | from ..ParameterItem import ParameterItem 2 | from ..Parameter import Parameter 3 | from ...Qt import QtWidgets, QtCore 4 | 5 | 6 | class ActionParameterItem(ParameterItem): 7 | """ParameterItem displaying a clickable button.""" 8 | def __init__(self, param, depth): 9 | ParameterItem.__init__(self, param, depth) 10 | self.layoutWidget = QtWidgets.QWidget() 11 | self.layout = QtWidgets.QHBoxLayout() 12 | self.layout.setContentsMargins(0, 0, 0, 0) 13 | self.layoutWidget.setLayout(self.layout) 14 | self.button = QtWidgets.QPushButton() 15 | #self.layout.addSpacing(100) 16 | self.layout.addWidget(self.button) 17 | self.layout.addStretch() 18 | self.button.clicked.connect(self.buttonClicked) 19 | self.titleChanged() 20 | self.optsChanged(self.param, self.param.opts) 21 | 22 | def treeWidgetChanged(self): 23 | ParameterItem.treeWidgetChanged(self) 24 | tree = self.treeWidget() 25 | if tree is None: 26 | return 27 | 28 | self.setFirstColumnSpanned(True) 29 | tree.setItemWidget(self, 0, self.layoutWidget) 30 | 31 | def titleChanged(self): 32 | self.button.setText(self.param.title()) 33 | self.setSizeHint(0, self.button.sizeHint()) 34 | 35 | def optsChanged(self, param, opts): 36 | ParameterItem.optsChanged(self, param, opts) 37 | 38 | if 'enabled' in opts: 39 | self.button.setEnabled(opts['enabled']) 40 | 41 | if 'tip' in opts: 42 | self.button.setToolTip(opts['tip']) 43 | 44 | def buttonClicked(self): 45 | self.param.activate() 46 | 47 | 48 | class ActionParameter(Parameter): 49 | """Used for displaying a button within the tree. 50 | 51 | ``sigActivated(self)`` is emitted when the button is clicked. 52 | """ 53 | itemClass = ActionParameterItem 54 | sigActivated = QtCore.Signal(object) 55 | 56 | def activate(self): 57 | self.sigActivated.emit(self) 58 | self.emitStateChanged('activated', None) 59 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/canvas/TransformGuiTemplate.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 224 10 | 117 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | PyQtGraph 21 | 22 | 23 | 24 | 1 25 | 26 | 27 | 0 28 | 29 | 30 | 31 | 32 | Translate: 33 | 34 | 35 | 36 | 37 | 38 | 39 | Rotate: 40 | 41 | 42 | 43 | 44 | 45 | 46 | Scale: 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Mirror 59 | 60 | 61 | 62 | 63 | 64 | 65 | Reflect 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/calendar.py: -------------------------------------------------------------------------------- 1 | from ...Qt import QtWidgets, QtCore 2 | from ..Parameter import Parameter 3 | from .basetypes import WidgetParameterItem 4 | 5 | 6 | class CalendarParameterItem(WidgetParameterItem): 7 | def makeWidget(self): 8 | self.asSubItem = True 9 | w = QtWidgets.QCalendarWidget() 10 | w.setMaximumHeight(200) 11 | w.sigChanged = w.selectionChanged 12 | w.value = w.selectedDate 13 | w.setValue = w.setSelectedDate 14 | self.hideWidget = False 15 | self.param.opts.setdefault('default', QtCore.QDate.currentDate()) 16 | return w 17 | 18 | 19 | class CalendarParameter(Parameter): 20 | """ 21 | Displays a Qt calendar whose date is specified by a 'format' option. 22 | 23 | ============== ======================================================== 24 | **Options:** 25 | format Format for displaying the date and converting from a string. Can be any value accepted by 26 | `QDate.toString` and `fromString`, or a stringified version of a QDateFormat enum, i.e. 'ISODate', 27 | 'TextDate' (default), etc. 28 | ============== ======================================================== 29 | """ 30 | 31 | itemClass = CalendarParameterItem 32 | 33 | def __init__(self, **opts): 34 | opts.setdefault('format', 'TextDate') 35 | super().__init__(**opts) 36 | 37 | def _interpretFormat(self, fmt=None): 38 | fmt = fmt or self.opts.get('format') 39 | if hasattr(QtCore.Qt.DateFormat, fmt): 40 | fmt = getattr(QtCore.Qt.DateFormat, fmt) 41 | return fmt 42 | 43 | def _interpretValue(self, v): 44 | if isinstance(v, str): 45 | fmt = self._interpretFormat() 46 | if fmt is None: 47 | raise ValueError('Cannot parse date string without a set format') 48 | v = QtCore.QDate.fromString(v, fmt) 49 | return v 50 | 51 | def saveState(self, filter=None): 52 | state = super().saveState(filter) 53 | fmt = self._interpretFormat() 54 | state['value'] = state['value'].toString(fmt) 55 | return state 56 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/icons/__init__.py: -------------------------------------------------------------------------------- 1 | import os.path as op 2 | import warnings 3 | 4 | from ..Qt import QtGui 5 | 6 | __all__ = ['getGraphIcon', 'getGraphPixmap'] 7 | 8 | _ICON_REGISTRY = {} 9 | 10 | 11 | class GraphIcon: 12 | """An icon place holder for lazy loading of QIcons 13 | 14 | The icon must reside in the icons folder and the path refers to the full 15 | name including suffix of the icon file, e.g.: 16 | 17 | tiny = GraphIcon("tiny.png") 18 | 19 | Icons can be later retrieved via the function `getGraphIcon` and providing 20 | the name: 21 | 22 | tiny = getGraphIcon("tiny") 23 | """ 24 | 25 | def __init__(self, path): 26 | self._path = path 27 | name = path.split('.')[0] 28 | _ICON_REGISTRY[name] = self 29 | self._icon = None 30 | 31 | def _build_qicon(self): 32 | icon = QtGui.QIcon(op.join(op.dirname(__file__), self._path)) 33 | name = self._path.split('.')[0] 34 | _ICON_REGISTRY[name] = icon 35 | self._icon = icon 36 | 37 | @property 38 | def qicon(self): 39 | if self._icon is None: 40 | self._build_qicon() 41 | 42 | return self._icon 43 | 44 | 45 | def getGraphIcon(name): 46 | """Return a `PyQtGraph` icon from the registry by `name`""" 47 | icon = _ICON_REGISTRY[name] 48 | if isinstance(icon, GraphIcon): 49 | icon = icon.qicon 50 | _ICON_REGISTRY[name] = icon 51 | 52 | return icon 53 | 54 | 55 | def getGraphPixmap(name, size=(20, 20)): 56 | """Return a `QPixmap` from the registry by `name`""" 57 | icon = getGraphIcon(name) 58 | 59 | return icon.pixmap(*size) 60 | 61 | 62 | def getPixmap(name, size=(20, 20)): 63 | """Historic `getPixmap` function 64 | 65 | (eg. getPixmap('auto') loads pyqtgraph/icons/auto.png) 66 | """ 67 | warnings.warn( 68 | "'getPixmap' is deprecated and will be removed soon, " 69 | "please use `getGraphPixmap` in the future", 70 | DeprecationWarning, stacklevel=2) 71 | return getGraphPixmap(name, size=size) 72 | 73 | 74 | # Note: List all graph icons here ... 75 | auto = GraphIcon("auto.png") 76 | ctrl = GraphIcon("ctrl.png") 77 | default = GraphIcon("default.png") 78 | invisibleEye = GraphIcon("invisibleEye.svg") 79 | lock = GraphIcon("lock.png") 80 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/numeric.py: -------------------------------------------------------------------------------- 1 | from .basetypes import WidgetParameterItem 2 | from ...widgets.SpinBox import SpinBox 3 | 4 | class NumericParameterItem(WidgetParameterItem): 5 | """ 6 | Subclasses `WidgetParameterItem` to provide the following types: 7 | 8 | ========================== ============================================================= 9 | **Registered Types:** 10 | int Displays a :class:`SpinBox ` in integer 11 | mode. 12 | float Displays a :class:`SpinBox `. 13 | ========================== ============================================================= 14 | """ 15 | def makeWidget(self): 16 | opts = self.param.opts 17 | t = opts['type'] 18 | defs = { 19 | 'value': 0, 'min': None, 'max': None, 20 | 'step': 1.0, 'dec': False, 21 | 'siPrefix': False, 'suffix': '', 'decimals': 3, 22 | } 23 | if t == 'int': 24 | defs['int'] = True 25 | defs['minStep'] = 1.0 26 | for k in defs: 27 | if k in opts: 28 | defs[k] = opts[k] 29 | if 'limits' in opts: 30 | defs['min'], defs['max'] = opts['limits'] 31 | w = SpinBox() 32 | w.setOpts(**defs) 33 | w.sigChanged = w.sigValueChanged 34 | w.sigChanging = w.sigValueChanging 35 | return w 36 | 37 | def updateDisplayLabel(self, value=None): 38 | if value is None: 39 | value = self.widget.lineEdit().text() 40 | super().updateDisplayLabel(value) 41 | 42 | def showEditor(self): 43 | super().showEditor() 44 | self.widget.selectNumber() # select the numerical portion of the text for quick editing 45 | 46 | def limitsChanged(self, param, limits): 47 | self.widget.setOpts(bounds=limits) 48 | 49 | def optsChanged(self, param, opts): 50 | super().optsChanged(param, opts) 51 | sbOpts = {} 52 | if 'units' in opts and 'suffix' not in opts: 53 | sbOpts['suffix'] = opts['units'] 54 | for k, v in opts.items(): 55 | if k in self.widget.opts: 56 | sbOpts[k] = v 57 | self.widget.setOpts(**sbOpts) 58 | self.updateDisplayLabel() 59 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/GraphicsWidget.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtGui 3 | from .GraphicsItem import GraphicsItem 4 | 5 | __all__ = ['GraphicsWidget'] 6 | 7 | class GraphicsWidget(GraphicsItem, QtGui.QGraphicsWidget): 8 | 9 | _qtBaseClass = QtGui.QGraphicsWidget 10 | def __init__(self, *args, **kargs): 11 | """ 12 | **Bases:** :class:`GraphicsItem `, :class:`QtGui.QGraphicsWidget` 13 | 14 | Extends QGraphicsWidget with several helpful methods and workarounds for PyQt bugs. 15 | Most of the extra functionality is inherited from :class:`GraphicsItem `. 16 | """ 17 | QtGui.QGraphicsWidget.__init__(self, *args, **kargs) 18 | GraphicsItem.__init__(self) 19 | 20 | ## done by GraphicsItem init 21 | #GraphicsScene.registerObject(self) ## workaround for pyqt bug in graphicsscene.items() 22 | 23 | # Removed due to https://bugreports.qt-project.org/browse/PYSIDE-86 24 | #def itemChange(self, change, value): 25 | ## BEWARE: Calling QGraphicsWidget.itemChange can lead to crashing! 26 | ##ret = QtGui.QGraphicsWidget.itemChange(self, change, value) ## segv occurs here 27 | ## The default behavior is just to return the value argument, so we'll do that 28 | ## without calling the original method. 29 | #ret = value 30 | #if change in [self.ItemParentHasChanged, self.ItemSceneHasChanged]: 31 | #self._updateView() 32 | #return ret 33 | 34 | def setFixedHeight(self, h): 35 | self.setMaximumHeight(h) 36 | self.setMinimumHeight(h) 37 | 38 | def setFixedWidth(self, h): 39 | self.setMaximumWidth(h) 40 | self.setMinimumWidth(h) 41 | 42 | def height(self): 43 | return self.geometry().height() 44 | 45 | def width(self): 46 | return self.geometry().width() 47 | 48 | def boundingRect(self): 49 | br = self.mapRectFromParent(self.geometry()).normalized() 50 | #print "bounds:", br 51 | return br 52 | 53 | def shape(self): ## No idea why this is necessary, but rotated items do not receive clicks otherwise. 54 | p = QtGui.QPainterPath() 55 | p.addRect(self.boundingRect()) 56 | #print "shape:", p.boundingRect() 57 | return p 58 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/icons/invisibleEye.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 9 | 16 | 23 | 27 | 28 | 35 | 36 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/util/pil_fix.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Importing this module installs support for 16-bit images in PIL. 4 | This works by patching objects in the PIL namespace; no files are 5 | modified. 6 | """ 7 | 8 | import warnings 9 | warnings.warn( 10 | "Not used in pyqtgraph. Will be removed in 0.13", 11 | DeprecationWarning, stacklevel=2 12 | ) 13 | 14 | from PIL import Image 15 | 16 | if Image.VERSION == '1.1.7': 17 | Image._MODE_CONV["I;16"] = ('%su2' % Image._ENDIAN, None) 18 | Image._fromarray_typemap[((1, 1), " ndmax: 62 | raise ValueError("Too many dimensions.") 63 | 64 | size = shape[:2][::-1] 65 | if strides is not None: 66 | obj = obj.tostring() 67 | 68 | return frombuffer(mode, size, obj, "raw", mode, 0, 1) 69 | 70 | Image.fromarray=fromarray 71 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/__init__.py: -------------------------------------------------------------------------------- 1 | from .action import ActionParameter, ActionParameterItem 2 | from .basetypes import WidgetParameterItem, SimpleParameter, GroupParameter, GroupParameterItem 3 | from .bool import BoolParameterItem 4 | from .calendar import CalendarParameter, CalendarParameterItem 5 | from .checklist import ChecklistParameter, ChecklistParameterItem 6 | from .color import ColorParameter, ColorParameterItem 7 | from .colormap import ColorMapParameter, ColorMapParameterItem 8 | from .file import FileParameter, FileParameterItem 9 | from .font import FontParameter, FontParameterItem 10 | from .list import ListParameter, ListParameterItem 11 | from .numeric import NumericParameterItem 12 | from .pen import PenParameter, PenParameterItem 13 | from .progress import ProgressBarParameter, ProgressBarParameterItem 14 | from .qtenum import QtEnumParameter 15 | from .slider import SliderParameter, SliderParameterItem 16 | from .str import StrParameterItem 17 | from .text import TextParameter, TextParameterItem 18 | from ..Parameter import registerParameterType, registerParameterItemType 19 | 20 | registerParameterItemType('bool', BoolParameterItem, SimpleParameter, override=True) 21 | registerParameterItemType('float', NumericParameterItem, SimpleParameter, override=True) 22 | registerParameterItemType('int', NumericParameterItem, SimpleParameter, override=True) 23 | registerParameterItemType('str', StrParameterItem, SimpleParameter, override=True) 24 | 25 | registerParameterType('group', GroupParameter, override=True) 26 | 27 | registerParameterType('action', ActionParameter, override=True) 28 | registerParameterType('calendar', CalendarParameter, override=True) 29 | registerParameterType('checklist', ChecklistParameter, override=True) 30 | registerParameterType('color', ColorParameter, override=True) 31 | registerParameterType('colormap', ColorMapParameter, override=True) 32 | registerParameterType('file', FileParameter, override=True) 33 | registerParameterType('font', FontParameter, override=True) 34 | registerParameterType('list', ListParameter, override=True) 35 | registerParameterType('pen', PenParameter, override=True) 36 | registerParameterType('progress', ProgressBarParameter, override=True) 37 | # qtenum is a bit specific, hold off on registering for now 38 | registerParameterType('slider', SliderParameter, override=True) 39 | registerParameterType('text', TextParameter, override=True) 40 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/canvas/CanvasManager.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtCore, QtGui 3 | if not hasattr(QtCore, 'Signal'): 4 | QtCore.Signal = QtCore.pyqtSignal 5 | import weakref 6 | 7 | class CanvasManager(QtCore.QObject): 8 | SINGLETON = None 9 | 10 | sigCanvasListChanged = QtCore.Signal() 11 | 12 | def __init__(self): 13 | if CanvasManager.SINGLETON is not None: 14 | raise Exception("Can only create one canvas manager.") 15 | CanvasManager.SINGLETON = self 16 | QtCore.QObject.__init__(self) 17 | self.canvases = weakref.WeakValueDictionary() 18 | 19 | @classmethod 20 | def instance(cls): 21 | return CanvasManager.SINGLETON 22 | 23 | def registerCanvas(self, canvas, name): 24 | n2 = name 25 | i = 0 26 | while n2 in self.canvases: 27 | n2 = "%s_%03d" % (name, i) 28 | i += 1 29 | self.canvases[n2] = canvas 30 | self.sigCanvasListChanged.emit() 31 | return n2 32 | 33 | def unregisterCanvas(self, name): 34 | c = self.canvases[name] 35 | del self.canvases[name] 36 | self.sigCanvasListChanged.emit() 37 | 38 | def listCanvases(self): 39 | return list(self.canvases.keys()) 40 | 41 | def getCanvas(self, name): 42 | return self.canvases[name] 43 | 44 | 45 | manager = CanvasManager() 46 | 47 | 48 | class CanvasCombo(QtGui.QComboBox): 49 | def __init__(self, parent=None): 50 | QtGui.QComboBox.__init__(self, parent) 51 | man = CanvasManager.instance() 52 | man.sigCanvasListChanged.connect(self.updateCanvasList) 53 | self.hostName = None 54 | self.updateCanvasList() 55 | 56 | def updateCanvasList(self): 57 | canvases = CanvasManager.instance().listCanvases() 58 | canvases.insert(0, "") 59 | if self.hostName in canvases: 60 | canvases.remove(self.hostName) 61 | 62 | sel = self.currentText() 63 | if sel in canvases: 64 | self.blockSignals(True) ## change does not affect current selection; block signals during update 65 | self.clear() 66 | for i in canvases: 67 | self.addItem(i) 68 | if i == sel: 69 | self.setCurrentIndex(self.count()) 70 | 71 | self.blockSignals(False) 72 | 73 | def setHostName(self, name): 74 | self.hostName = name 75 | self.updateCanvasList() 76 | 77 | -------------------------------------------------------------------------------- /tests/testdata/ipf-timeseries/B30F0059001.txt: -------------------------------------------------------------------------------- 1 | 95 2 | 2,1 3 | time,1e+20 4 | head,1e+20 5 | 19520814000000,-2.1 6 | 19520828000000,-2.03 7 | 19520915000000,-1.92 8 | 19520929000000,-1.65 9 | 19521014000000,-1.67 10 | 19521114000000,-1.7 11 | 19521128000000,-1.49 12 | 19521215000000,-1.41 13 | 19521229000000,-1.28 14 | 19530114000000,-1.5 15 | 19530128000000,-1.57 16 | 19530214000000,-1.43 17 | 19530228000000,-1.62 18 | 19530314000000,-1.73 19 | 19530328000000,-1.78 20 | 19530414000000,-1.48 21 | 19530515000000,-1.87 22 | 19530528000000,-2.02 23 | 19530615000000,-1.8 24 | 19530629000000,-1.95 25 | 19530714000000,-2.13 26 | 19530728000000,-2.04 27 | 19530814000000,-2.19 28 | 19530914000000,-1.96 29 | 19531128000000,-1.83 30 | 19531214000000,-1.86 31 | 19531228000000,-1.66 32 | 19540103000000,-1.54 33 | 19540114000000,-1.64 34 | 19540414000000,-1.75 35 | 19540514000000,-1.88 36 | 19540528000000,-2.0 37 | 19540628000000,-1.91 38 | 19540714000000,-2.09 39 | 19540728000000,-1.76 40 | 19540814000000,-1.63 41 | 19540914000000,-1.85 42 | 19540928000000,-1.81 43 | 19541115000000,-1.61 44 | 19541214000000,-1.6 45 | 19550128000000,-1.24 46 | 19550328000000,-1.59 47 | 19550628000000,-1.99 48 | 19550714000000,-2.08 49 | 19550815000000,-1.98 50 | 19550829000000,-2.17 51 | 19551028000000,-1.68 52 | 19551114000000,-1.74 53 | 19551228000000,-1.58 54 | 19560414000000,-1.72 55 | 19560628000000,-1.93 56 | 19560728000000,-1.89 57 | 19560914000000,-1.71 58 | 19561114000000,-1.69 59 | 19561228000000,-1.56 60 | 19570314000000,-2.01 61 | 19570628000000,-2.15 62 | 19570928000000,-1.45 63 | 19571228000000,-1.47 64 | 19580114000000,-1.51 65 | 19580128000000,-1.42 66 | 19580314000000,-1.4 67 | 19580328000000,-1.53 68 | 19580614000000,-1.77 69 | 19581128000000,-2.21 70 | 19590615000000,-1.94 71 | 19590814000000,-2.06 72 | 19590914000000,-2.07 73 | 19590928000000,-2.05 74 | 19591228000000,-1.84 75 | 19600215000000,-1.82 76 | 19600928000000,-1.79 77 | 19601128000000,-1.35 78 | 19601214000000,-1.37 79 | 19601228000000,-1.39 80 | 19610414000000,-1.46 81 | 19610828000000,-1.9 82 | 19611214000000,-1.34 83 | 19620115000000,-1.33 84 | 19620214000000,-1.31 85 | 19630429000000,-1.55 86 | 19630628000000,-1.97 87 | 19641214000000,-1.38 88 | 19650118000000,-1.3 89 | 19730205000000,-1.44 90 | 19731214000000,-1.14 91 | 19740429000000,-1.36 92 | 19741014000000,-1.11 93 | 19741216000000,-1.04 94 | 19750428000000,-1.17 95 | 19750827000000,-1.22 96 | 19751217000000,-1.1 97 | 19761214000000,-1.05 98 | 19770428000000,-1.29 99 | 19771214000000,-1.25 100 | -------------------------------------------------------------------------------- /tests/testdata/ipf-timeseries/B30F0217001.txt: -------------------------------------------------------------------------------- 1 | 96 2 | 2,1 3 | time,1e+20 4 | head,1e+20 5 | 19550628000000,-2.1 6 | 19550714000000,-2.25 7 | 19550728000000,-2.31 8 | 19550815000000,-2.19 9 | 19550914000000,-2.21 10 | 19550928000000,-2.17 11 | 19551014000000,-2.09 12 | 19551028000000,-1.73 13 | 19551114000000,-1.86 14 | 19551214000000,-1.82 15 | 19551228000000,-1.77 16 | 19560114000000,-1.69 17 | 19560214000000,-1.68 18 | 19560228000000,-1.79 19 | 19560314000000,-1.87 20 | 19560328000000,-1.92 21 | 19560414000000,-1.91 22 | 19560428000000,-1.9 23 | 19560514000000,-2.11 24 | 19560528000000,-2.13 25 | 19560614000000,-2.16 26 | 19560628000000,-2.12 27 | 19560828000000,-1.8 28 | 19560928000000,-2.04 29 | 19561015000000,-1.81 30 | 19561029000000,-1.71 31 | 19561228000000,-1.76 32 | 19570114000000,-1.7 33 | 19570228000000,-1.97 34 | 19570314000000,-2.33 35 | 19570415000000,-2.53 36 | 19570514000000,-2.15 37 | 19570528000000,-2.2 38 | 19570628000000,-2.36 39 | 19570814000000,-2.27 40 | 19571114000000,-1.88 41 | 19571214000000,-1.95 42 | 19580114000000,-1.61 43 | 19580128000000,-1.66 44 | 19580228000000,-1.62 45 | 19580314000000,-1.59 46 | 19580328000000,-1.67 47 | 19580628000000,-2.01 48 | 19580728000000,-1.99 49 | 19580814000000,-2.05 50 | 19580828000000,-2.28 51 | 19580915000000,-2.59 52 | 19581028000000,-2.34 53 | 19581114000000,-2.39 54 | 19581128000000,-2.8 55 | 19581229000000,-2.55 56 | 19590114000000,-2.45 57 | 19590214000000,-2.3 58 | 19590319000000,-2.58 59 | 19590814000000,-2.23 60 | 19590928000000,-2.32 61 | 19591128000000,-2.54 62 | 19591214000000,-2.64 63 | 19600114000000,-2.56 64 | 19600215000000,-2.35 65 | 19600314000000,-2.47 66 | 19600328000000,-2.52 67 | 19600414000000,-1.98 68 | 19600614000000,-2.07 69 | 19600628000000,-2.26 70 | 19610108000000,-2.03 71 | 19610114000000,-1.42 72 | 19610414000000,-1.75 73 | 19610428000000,-1.78 74 | 19610614000000,-1.96 75 | 19610714000000,-2.14 76 | 19611128000000,-1.74 77 | 19611214000000,-1.54 78 | 19620120000000,-1.53 79 | 19620129000000,-1.89 80 | 19620214000000,-1.57 81 | 19620414000000,-1.58 82 | 19630328000000,-1.65 83 | 19631216000000,-1.94 84 | 19640214000000,-1.85 85 | 19640414000000,-1.93 86 | 19641028000000,-1.6 87 | 19650114000000,-1.48 88 | 19651228000000,-1.52 89 | 19660228000000,-1.56 90 | 19660428000000,-1.63 91 | 19660719000000,-1.83 92 | 19661214000000,-1.51 93 | 19670328000000,-1.84 94 | 19670828000000,-2.06 95 | 19680115000000,-1.5 96 | 19690627000000,-2.18 97 | 19691014000000,-2.22 98 | 19691128000000,-2.02 99 | 19700615000000,-2.29 100 | 19700728000000,-2.99 101 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartTemplate_pyside2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'FlowchartTemplate.ui' 4 | # 5 | # Created: Sun Sep 18 19:16:03 2016 6 | # by: pyside2-uic running on PySide2 2.0.0~alpha0 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide2 import QtCore, QtGui, QtWidgets 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(529, 329) 16 | self.selInfoWidget = QtWidgets.QWidget(Form) 17 | self.selInfoWidget.setGeometry(QtCore.QRect(260, 10, 264, 222)) 18 | self.selInfoWidget.setObjectName("selInfoWidget") 19 | self.gridLayout = QtWidgets.QGridLayout(self.selInfoWidget) 20 | self.gridLayout.setContentsMargins(0, 0, 0, 0) 21 | self.gridLayout.setObjectName("gridLayout") 22 | self.selDescLabel = QtWidgets.QLabel(self.selInfoWidget) 23 | self.selDescLabel.setText("") 24 | self.selDescLabel.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) 25 | self.selDescLabel.setWordWrap(True) 26 | self.selDescLabel.setObjectName("selDescLabel") 27 | self.gridLayout.addWidget(self.selDescLabel, 0, 0, 1, 1) 28 | self.selNameLabel = QtWidgets.QLabel(self.selInfoWidget) 29 | font = QtGui.QFont() 30 | font.setWeight(75) 31 | font.setBold(True) 32 | self.selNameLabel.setFont(font) 33 | self.selNameLabel.setText("") 34 | self.selNameLabel.setObjectName("selNameLabel") 35 | self.gridLayout.addWidget(self.selNameLabel, 0, 1, 1, 1) 36 | self.selectedTree = DataTreeWidget(self.selInfoWidget) 37 | self.selectedTree.setObjectName("selectedTree") 38 | self.selectedTree.headerItem().setText(0, "1") 39 | self.gridLayout.addWidget(self.selectedTree, 1, 0, 1, 2) 40 | self.hoverText = QtWidgets.QTextEdit(Form) 41 | self.hoverText.setGeometry(QtCore.QRect(0, 240, 521, 81)) 42 | self.hoverText.setObjectName("hoverText") 43 | self.view = FlowchartGraphicsView(Form) 44 | self.view.setGeometry(QtCore.QRect(0, 0, 256, 192)) 45 | self.view.setObjectName("view") 46 | 47 | self.retranslateUi(Form) 48 | QtCore.QMetaObject.connectSlotsByName(Form) 49 | 50 | def retranslateUi(self, Form): 51 | Form.setWindowTitle(QtWidgets.QApplication.translate("Form", "Form", None, -1)) 52 | 53 | from ..flowchart.FlowchartGraphicsView import FlowchartGraphicsView 54 | from ..widgets.DataTreeWidget import DataTreeWidget 55 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/ScaleBar.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtGui, QtCore 3 | from .GraphicsObject import * 4 | from .GraphicsWidgetAnchor import * 5 | from .TextItem import TextItem 6 | from .. import functions as fn 7 | from .. import getConfigOption 8 | from ..Point import Point 9 | 10 | __all__ = ['ScaleBar'] 11 | 12 | class ScaleBar(GraphicsObject, GraphicsWidgetAnchor): 13 | """ 14 | Displays a rectangular bar to indicate the relative scale of objects on the view. 15 | """ 16 | def __init__(self, size, width=5, brush=None, pen=None, suffix='m', offset=None): 17 | GraphicsObject.__init__(self) 18 | GraphicsWidgetAnchor.__init__(self) 19 | self.setFlag(self.GraphicsItemFlag.ItemHasNoContents) 20 | self.setAcceptedMouseButtons(QtCore.Qt.MouseButton.NoButton) 21 | 22 | if brush is None: 23 | brush = getConfigOption('foreground') 24 | self.brush = fn.mkBrush(brush) 25 | self.pen = fn.mkPen(pen) 26 | self._width = width 27 | self.size = size 28 | if offset == None: 29 | offset = (0,0) 30 | self.offset = offset 31 | 32 | self.bar = QtGui.QGraphicsRectItem() 33 | self.bar.setPen(self.pen) 34 | self.bar.setBrush(self.brush) 35 | self.bar.setParentItem(self) 36 | 37 | self.text = TextItem(text=fn.siFormat(size, suffix=suffix), anchor=(0.5,1)) 38 | self.text.setParentItem(self) 39 | 40 | def parentChanged(self): 41 | view = self.parentItem() 42 | if view is None: 43 | return 44 | view.sigRangeChanged.connect(self.updateBar) 45 | self.updateBar() 46 | 47 | 48 | def updateBar(self): 49 | view = self.parentItem() 50 | if view is None: 51 | return 52 | p1 = view.mapFromViewToItem(self, QtCore.QPointF(0,0)) 53 | p2 = view.mapFromViewToItem(self, QtCore.QPointF(self.size,0)) 54 | w = (p2-p1).x() 55 | self.bar.setRect(QtCore.QRectF(-w, 0, w, self._width)) 56 | self.text.setPos(-w/2., 0) 57 | 58 | def boundingRect(self): 59 | return QtCore.QRectF() 60 | 61 | def setParentItem(self, p): 62 | ret = GraphicsObject.setParentItem(self, p) 63 | if self.offset is not None: 64 | offset = Point(self.offset) 65 | anchorx = 1 if offset[0] <= 0 else 0 66 | anchory = 1 if offset[1] <= 0 else 0 67 | anchor = (anchorx, anchory) 68 | self.anchor(itemPos=anchor, parentPos=anchor, offset=offset) 69 | return ret 70 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartTemplate_pyqt5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file './pyqtgraph/flowchart/FlowchartTemplate.ui' 4 | # 5 | # Created: Wed Mar 26 15:09:28 2014 6 | # by: PyQt5 UI code generator 5.0.1 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(529, 329) 16 | self.selInfoWidget = QtWidgets.QWidget(Form) 17 | self.selInfoWidget.setGeometry(QtCore.QRect(260, 10, 264, 222)) 18 | self.selInfoWidget.setObjectName("selInfoWidget") 19 | self.gridLayout = QtWidgets.QGridLayout(self.selInfoWidget) 20 | self.gridLayout.setContentsMargins(0, 0, 0, 0) 21 | self.gridLayout.setObjectName("gridLayout") 22 | self.selDescLabel = QtWidgets.QLabel(self.selInfoWidget) 23 | self.selDescLabel.setText("") 24 | self.selDescLabel.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) 25 | self.selDescLabel.setWordWrap(True) 26 | self.selDescLabel.setObjectName("selDescLabel") 27 | self.gridLayout.addWidget(self.selDescLabel, 0, 0, 1, 1) 28 | self.selNameLabel = QtWidgets.QLabel(self.selInfoWidget) 29 | font = QtGui.QFont() 30 | font.setBold(True) 31 | font.setWeight(75) 32 | self.selNameLabel.setFont(font) 33 | self.selNameLabel.setText("") 34 | self.selNameLabel.setObjectName("selNameLabel") 35 | self.gridLayout.addWidget(self.selNameLabel, 0, 1, 1, 1) 36 | self.selectedTree = DataTreeWidget(self.selInfoWidget) 37 | self.selectedTree.setObjectName("selectedTree") 38 | self.selectedTree.headerItem().setText(0, "1") 39 | self.gridLayout.addWidget(self.selectedTree, 1, 0, 1, 2) 40 | self.hoverText = QtWidgets.QTextEdit(Form) 41 | self.hoverText.setGeometry(QtCore.QRect(0, 240, 521, 81)) 42 | self.hoverText.setObjectName("hoverText") 43 | self.view = FlowchartGraphicsView(Form) 44 | self.view.setGeometry(QtCore.QRect(0, 0, 256, 192)) 45 | self.view.setObjectName("view") 46 | 47 | self.retranslateUi(Form) 48 | QtCore.QMetaObject.connectSlotsByName(Form) 49 | 50 | def retranslateUi(self, Form): 51 | _translate = QtCore.QCoreApplication.translate 52 | Form.setWindowTitle(_translate("Form", "PyQtGraph")) 53 | 54 | from ..widgets.DataTreeWidget import DataTreeWidget 55 | from ..flowchart.FlowchartGraphicsView import FlowchartGraphicsView 56 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartTemplate_pyqt6.py: -------------------------------------------------------------------------------- 1 | # Form implementation generated from reading ui file '../pyqtgraph/flowchart/FlowchartTemplate.ui' 2 | # 3 | # Created by: PyQt6 UI code generator 6.1.0 4 | # 5 | # WARNING: Any manual changes made to this file will be lost when pyuic6 is 6 | # run again. Do not edit this file unless you know what you are doing. 7 | 8 | 9 | from PyQt6 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(529, 329) 16 | self.selInfoWidget = QtWidgets.QWidget(Form) 17 | self.selInfoWidget.setGeometry(QtCore.QRect(260, 10, 264, 222)) 18 | self.selInfoWidget.setObjectName("selInfoWidget") 19 | self.gridLayout = QtWidgets.QGridLayout(self.selInfoWidget) 20 | self.gridLayout.setContentsMargins(0, 0, 0, 0) 21 | self.gridLayout.setObjectName("gridLayout") 22 | self.selDescLabel = QtWidgets.QLabel(self.selInfoWidget) 23 | self.selDescLabel.setText("") 24 | self.selDescLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop) 25 | self.selDescLabel.setWordWrap(True) 26 | self.selDescLabel.setObjectName("selDescLabel") 27 | self.gridLayout.addWidget(self.selDescLabel, 0, 0, 1, 1) 28 | self.selNameLabel = QtWidgets.QLabel(self.selInfoWidget) 29 | font = QtGui.QFont() 30 | font.setBold(True) 31 | self.selNameLabel.setFont(font) 32 | self.selNameLabel.setText("") 33 | self.selNameLabel.setObjectName("selNameLabel") 34 | self.gridLayout.addWidget(self.selNameLabel, 0, 1, 1, 1) 35 | self.selectedTree = DataTreeWidget(self.selInfoWidget) 36 | self.selectedTree.setObjectName("selectedTree") 37 | self.selectedTree.headerItem().setText(0, "1") 38 | self.gridLayout.addWidget(self.selectedTree, 1, 0, 1, 2) 39 | self.hoverText = QtWidgets.QTextEdit(Form) 40 | self.hoverText.setGeometry(QtCore.QRect(0, 240, 521, 81)) 41 | self.hoverText.setObjectName("hoverText") 42 | self.view = FlowchartGraphicsView(Form) 43 | self.view.setGeometry(QtCore.QRect(0, 0, 256, 192)) 44 | self.view.setObjectName("view") 45 | 46 | self.retranslateUi(Form) 47 | QtCore.QMetaObject.connectSlotsByName(Form) 48 | 49 | def retranslateUi(self, Form): 50 | _translate = QtCore.QCoreApplication.translate 51 | Form.setWindowTitle(_translate("Form", "PyQtGraph")) 52 | from ..flowchart.FlowchartGraphicsView import FlowchartGraphicsView 53 | from ..widgets.DataTreeWidget import DataTreeWidget 54 | -------------------------------------------------------------------------------- /imodqgis/idf/layer_styling.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | from typing import List 5 | 6 | from qgis.core import ( 7 | QgsColorRampShader, 8 | QgsRasterBandStats, 9 | QgsRasterShader, 10 | QgsSingleBandPseudoColorRenderer, 11 | QgsStyle, 12 | ) 13 | 14 | 15 | def color_ramp_items( 16 | colormap: str, minimum: float, maximum: float, nclass: int 17 | ) -> List[QgsColorRampShader.ColorRampItem]: 18 | """ 19 | Parameters 20 | ---------- 21 | colormap: str 22 | Name of QGIS colormap 23 | minimum: float 24 | maximum: float 25 | nclass: int 26 | Number of colormap classes to create 27 | 28 | Returns 29 | ------- 30 | color_ramp: QgsGradientColorRamp 31 | color_ramp_items: List[QgsColorRampShader.ColorRampItem] 32 | Can be used directly by the QgsColorRampShader 33 | """ 34 | delta = maximum - minimum 35 | fractional_steps = [i / nclass for i in range(nclass + 1)] 36 | ramp = QgsStyle().defaultStyle().colorRamp(colormap) 37 | colors = [ramp.color(f) for f in fractional_steps] 38 | steps = [minimum + f * delta for f in fractional_steps] 39 | return ramp, [ 40 | QgsColorRampShader.ColorRampItem(step, color, str(step)) 41 | for step, color in zip(steps, colors) 42 | ] 43 | 44 | 45 | def pseudocolor_renderer( 46 | layer, band: int, colormap: str, nclass: int 47 | ) -> QgsSingleBandPseudoColorRenderer: 48 | """ 49 | Parameters 50 | ---------- 51 | layer: QGIS map layer 52 | band: int 53 | band number of the raster to create a renderer for 54 | colormap: str 55 | Name of QGIS colormap 56 | nclass: int 57 | Number of colormap classes to create 58 | 59 | Returns 60 | ------- 61 | renderer: QgsSingleBandPseudoColorRenderer 62 | """ 63 | stats = layer.dataProvider().bandStatistics(band, QgsRasterBandStats.All) 64 | minimum = stats.minimumValue 65 | maximum = stats.maximumValue 66 | 67 | ramp, ramp_items = color_ramp_items(colormap, minimum, maximum, nclass) 68 | shader_function = QgsColorRampShader() 69 | shader_function.setMinimumValue(minimum) 70 | shader_function.setMaximumValue(maximum) 71 | shader_function.setSourceColorRamp(ramp) 72 | shader_function.setColorRampType(QgsColorRampShader.Interpolated) 73 | shader_function.setClassificationMode(QgsColorRampShader.EqualInterval) 74 | shader_function.setColorRampItemList(ramp_items) 75 | 76 | raster_shader = QgsRasterShader() 77 | raster_shader.setRasterShaderFunction(shader_function) 78 | 79 | return QgsSingleBandPseudoColorRenderer(layer.dataProvider(), band, raster_shader) 80 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartTemplate.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 529 10 | 329 11 | 12 | 13 | 14 | PyQtGraph 15 | 16 | 17 | 18 | 19 | 260 20 | 10 21 | 264 22 | 222 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 33 | 34 | 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 1 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 0 66 | 240 67 | 521 68 | 81 69 | 70 | 71 | 72 | 73 | 74 | 75 | 0 76 | 0 77 | 256 78 | 192 79 | 80 | 81 | 82 | 83 | 84 | 85 | DataTreeWidget 86 | QTreeWidget 87 |
..widgets.DataTreeWidget
88 |
89 | 90 | FlowchartGraphicsView 91 | QGraphicsView 92 |
..flowchart.FlowchartGraphicsView
93 |
94 |
95 | 96 | 97 |
98 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/MultiPlotItem.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | MultiPlotItem.py - Graphics item used for displaying an array of PlotItems 4 | Copyright 2010 Luke Campagnola 5 | Distributed under MIT/X11 license. See license.txt for more information. 6 | """ 7 | from . import GraphicsLayout 8 | from ..metaarray import * 9 | 10 | __all__ = ['MultiPlotItem'] 11 | 12 | 13 | class MultiPlotItem(GraphicsLayout.GraphicsLayout): 14 | """ 15 | :class:`~pyqtgraph.GraphicsLayout` that automatically generates a grid of 16 | plots from a MetaArray. 17 | 18 | .. seealso:: :class:`~pyqtgraph.MultiPlotWidget`: Widget containing a MultiPlotItem 19 | """ 20 | 21 | def __init__(self, *args, **kwds): 22 | GraphicsLayout.GraphicsLayout.__init__(self, *args, **kwds) 23 | self.plots = [] 24 | 25 | def plot(self, data, **plotArgs): 26 | """Plot the data from a MetaArray with each array column as a separate 27 | :class:`~pyqtgraph.PlotItem`. 28 | 29 | Axis labels are automatically extracted from the array info. 30 | 31 | ``plotArgs`` are passed to :meth:`PlotItem.plot 32 | `. 33 | """ 34 | #self.layout.clear() 35 | 36 | if hasattr(data, 'implements') and data.implements('MetaArray'): 37 | if data.ndim != 2: 38 | raise Exception("MultiPlot currently only accepts 2D MetaArray.") 39 | ic = data.infoCopy() 40 | ax = 0 41 | for i in [0, 1]: 42 | if 'cols' in ic[i]: 43 | ax = i 44 | break 45 | #print "Plotting using axis %d as columns (%d plots)" % (ax, data.shape[ax]) 46 | for i in range(data.shape[ax]): 47 | pi = self.addPlot() 48 | self.nextRow() 49 | sl = [slice(None)] * 2 50 | sl[ax] = i 51 | pi.plot(data[tuple(sl)], **plotArgs) 52 | #self.layout.addItem(pi, i, 0) 53 | self.plots.append((pi, i, 0)) 54 | info = ic[ax]['cols'][i] 55 | title = info.get('title', info.get('name', None)) 56 | units = info.get('units', None) 57 | pi.setLabel('left', text=title, units=units) 58 | info = ic[1-ax] 59 | title = info.get('title', info.get('name', None)) 60 | units = info.get('units', None) 61 | pi.setLabel('bottom', text=title, units=units) 62 | else: 63 | raise Exception("Data type %s not (yet?) supported for MultiPlot." % type(data)) 64 | 65 | def close(self): 66 | for p in self.plots: 67 | p[0].close() 68 | self.plots = None 69 | self.clear() 70 | -------------------------------------------------------------------------------- /imodqgis/about/about.md: -------------------------------------------------------------------------------- 1 | # iMOD-QGIS plugin 2 | 3 | ## Functionality 4 | The iMOD QGIS plugin provides ways to visualize 4D unstructured subsurface data [time, layer, y, x]. 5 | 6 | Primary components are: 7 | 8 | * An IPF reader to read .IPF files into Qgis, which is a Deltares point format that supports assocciated depth or time data. 9 | * Connecting to the iMOD 3D viewer 10 | * Timeseries visualization 11 | * Cross-section visualization 12 | * Connecting to the NHI data portal, which provides subsurface data for the Netherlands 13 | 14 | ## Data Requirements 15 | 3D data needs to be provided as an unstructured file readable by MDAL, 16 | preferably an 17 | [UGRID file](https://ugrid-conventions.github.io/ugrid-conventions/). 18 | 19 | Currently MDAL does not support the reading of 3D layered unstructured UGRID files. 20 | 21 | Therefore, for each vertical layer, 22 | we require a mesh dataset with the following variables: 23 | 24 | * `{var}_layer_{nr}` 25 | * `top_layer_{nr}` 26 | * `bottom_layer_{nr}` 27 | 28 | Here is an example script how to do this with 29 | [iMOD-python](https://gitlab.com/deltares/imod/imod-python/-/snippets/2111702). 30 | 31 | Point data with timeseries or borelogs need to be provided as 32 | [IPF](https://content.oss.deltares.nl/imod/iMOD_Manual_actual/imod-um-IPF-files.html#autosec-591) file. 33 | The IPF file is a Deltares point format that supports assocciated depth or 34 | time data. 35 | 36 | ## Dependencies 37 | To do 3D viewing, the iMOD 3D Viewer is required, 38 | [for which downloading and installing instructions can be found here](https://deltares.github.io/iMOD-Documentation/viewer_install.html). 39 | 40 | This plugin uses: 41 | 42 | * [PyQtGraph 12.2](https://www.pyqtgraph.org/) for graph visualization 43 | * [declxml](https://declxml.readthedocs.io/en/latest/) to create xml files 44 | * [pandas](https://pandas.pydata.org/) to read and process csv files 45 | 46 | PyQtGraph and declxml are pure python packages distributed with the plugin, 47 | pandas comes with the QGIS installation 48 | 49 | NOTE: If you installed QGIS with OSGeo4W, make sure pandas is installed as an extra dependency. 50 | See the [installation instructions](https://deltares.github.io/iMOD-Documentation/qgis_install.html). 51 | 52 | This package was inspired by the [Crayfish plugin](https://github.com/lutraconsulting/qgis-crayfish-plugin/tree/master/crayfish). 53 | 54 | ## Documentation 55 | 56 | * [General iMOD documentation](https://deltares.github.io/iMOD-Documentation/index.html) 57 | * [Plugin Installation Instructions](https://deltares.github.io/iMOD-Documentation/qgis_install.html) 58 | * [Plugin User Manual](https://deltares.github.io/iMOD-Documentation/qgis_index.html) 59 | 60 | ## License 61 | The iMOD plugin is a product of Deltares, and published under the GPLv2 license. 62 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/canvas/TransformGuiTemplate_pyqt5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'pyqtgraph/canvas/TransformGuiTemplate.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.5.1 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_Form(object): 12 | def setupUi(self, Form): 13 | Form.setObjectName("Form") 14 | Form.resize(224, 117) 15 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 16 | sizePolicy.setHorizontalStretch(0) 17 | sizePolicy.setVerticalStretch(0) 18 | sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) 19 | Form.setSizePolicy(sizePolicy) 20 | self.verticalLayout = QtWidgets.QVBoxLayout(Form) 21 | self.verticalLayout.setContentsMargins(0, 0, 0, 0) 22 | self.verticalLayout.setSpacing(1) 23 | self.verticalLayout.setObjectName("verticalLayout") 24 | self.translateLabel = QtWidgets.QLabel(Form) 25 | self.translateLabel.setObjectName("translateLabel") 26 | self.verticalLayout.addWidget(self.translateLabel) 27 | self.rotateLabel = QtWidgets.QLabel(Form) 28 | self.rotateLabel.setObjectName("rotateLabel") 29 | self.verticalLayout.addWidget(self.rotateLabel) 30 | self.scaleLabel = QtWidgets.QLabel(Form) 31 | self.scaleLabel.setObjectName("scaleLabel") 32 | self.verticalLayout.addWidget(self.scaleLabel) 33 | self.horizontalLayout = QtWidgets.QHBoxLayout() 34 | self.horizontalLayout.setObjectName("horizontalLayout") 35 | self.mirrorImageBtn = QtWidgets.QPushButton(Form) 36 | self.mirrorImageBtn.setToolTip("") 37 | self.mirrorImageBtn.setObjectName("mirrorImageBtn") 38 | self.horizontalLayout.addWidget(self.mirrorImageBtn) 39 | self.reflectImageBtn = QtWidgets.QPushButton(Form) 40 | self.reflectImageBtn.setObjectName("reflectImageBtn") 41 | self.horizontalLayout.addWidget(self.reflectImageBtn) 42 | self.verticalLayout.addLayout(self.horizontalLayout) 43 | 44 | self.retranslateUi(Form) 45 | QtCore.QMetaObject.connectSlotsByName(Form) 46 | 47 | def retranslateUi(self, Form): 48 | _translate = QtCore.QCoreApplication.translate 49 | Form.setWindowTitle(_translate("Form", "PyQtGraph")) 50 | self.translateLabel.setText(_translate("Form", "Translate:")) 51 | self.rotateLabel.setText(_translate("Form", "Rotate:")) 52 | self.scaleLabel.setText(_translate("Form", "Scale:")) 53 | self.mirrorImageBtn.setText(_translate("Form", "Mirror")) 54 | self.reflectImageBtn.setText(_translate("Form", "Reflect")) 55 | 56 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/parametertree/parameterTypes/qtenum.py: -------------------------------------------------------------------------------- 1 | from ...Qt import QtCore, QT_LIB 2 | from .list import ListParameter 3 | 4 | 5 | class QtEnumParameter(ListParameter): 6 | def __init__(self, enum, searchObj=QtCore.Qt, **opts): 7 | """ 8 | Constructs a list of allowed enum values from the enum class provided 9 | `searchObj` is only needed for PyQt5 compatibility, where it must be the module holding the enum. 10 | For instance, if making a QtEnumParameter out of QtWidgets.QFileDialog.Option, `searchObj` would 11 | be QtWidgets.QFileDialog 12 | """ 13 | self.enum = enum 14 | self.searchObj = searchObj 15 | opts.setdefault('name', enum.__name__) 16 | self.enumMap = self._getAllowedEnums(enum) 17 | 18 | opts.update(limits=self.formattedLimits()) 19 | super().__init__(**opts) 20 | 21 | def setValue(self, value, blockSignal=None): 22 | if isinstance(value, str): 23 | value = self.enumMap[value] 24 | super().setValue(value, blockSignal) 25 | 26 | def formattedLimits(self): 27 | # Title-cased words without the ending substring for brevity 28 | substringEnd = None 29 | mapping = self.enumMap 30 | shortestName = min(len(name) for name in mapping) 31 | names = list(mapping) 32 | cmpName, *names = names 33 | for ii in range(-1, -shortestName-1, -1): 34 | if any(cmpName[ii] != curName[ii] for curName in names): 35 | substringEnd = ii+1 36 | break 37 | # Special case of 0: Set to none to avoid null string 38 | if substringEnd == 0: 39 | substringEnd = None 40 | limits = {} 41 | for kk, vv in self.enumMap.items(): 42 | limits[kk[:substringEnd]] = vv 43 | return limits 44 | 45 | def saveState(self, filter=None): 46 | state = super().saveState(filter) 47 | reverseMap = dict(zip(self.enumMap.values(), self.enumMap)) 48 | state['value'] = reverseMap[state['value']] 49 | return state 50 | 51 | def _getAllowedEnums(self, enum): 52 | """Pyside provides a dict for easy evaluation""" 53 | if 'PySide' in QT_LIB: 54 | vals = enum.values 55 | elif 'PyQt5' in QT_LIB: 56 | vals = {} 57 | for key in dir(self.searchObj): 58 | value = getattr(self.searchObj, key) 59 | if isinstance(value, enum): 60 | vals[key] = value 61 | elif 'PyQt6' in QT_LIB: 62 | vals = {e.name: e for e in enum} 63 | else: 64 | raise RuntimeError(f'Cannot find associated enum values for qt lib {QT_LIB}') 65 | # Remove "M" since it's not a real option 66 | vals.pop(f'M{enum.__name__}', None) 67 | return vals 68 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/opengl/items/GLBoxItem.py: -------------------------------------------------------------------------------- 1 | from OpenGL.GL import * 2 | from .. GLGraphicsItem import GLGraphicsItem 3 | from ...Qt import QtGui 4 | from ... import functions as fn 5 | 6 | __all__ = ['GLBoxItem'] 7 | 8 | class GLBoxItem(GLGraphicsItem): 9 | """ 10 | **Bases:** :class:`GLGraphicsItem ` 11 | 12 | Displays a wire-frame box. 13 | """ 14 | def __init__(self, size=None, color=None, glOptions='translucent'): 15 | GLGraphicsItem.__init__(self) 16 | if size is None: 17 | size = QtGui.QVector3D(1,1,1) 18 | self.setSize(size=size) 19 | if color is None: 20 | color = (255,255,255,80) 21 | self.setColor(color) 22 | self.setGLOptions(glOptions) 23 | 24 | def setSize(self, x=None, y=None, z=None, size=None): 25 | """ 26 | Set the size of the box (in its local coordinate system; this does not affect the transform) 27 | Arguments can be x,y,z or size=QVector3D(). 28 | """ 29 | if size is not None: 30 | x = size.x() 31 | y = size.y() 32 | z = size.z() 33 | self.__size = [x,y,z] 34 | self.update() 35 | 36 | def size(self): 37 | return self.__size[:] 38 | 39 | def setColor(self, *args): 40 | """Set the color of the box. Arguments are the same as those accepted by functions.mkColor()""" 41 | self.__color = fn.mkColor(*args) 42 | 43 | def color(self): 44 | return self.__color 45 | 46 | def paint(self): 47 | #glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 48 | #glEnable( GL_BLEND ) 49 | #glEnable( GL_ALPHA_TEST ) 50 | ##glAlphaFunc( GL_ALWAYS,0.5 ) 51 | #glEnable( GL_POINT_SMOOTH ) 52 | #glDisable( GL_DEPTH_TEST ) 53 | self.setupGLState() 54 | 55 | glBegin( GL_LINES ) 56 | 57 | glColor4f(*self.color().getRgbF()) 58 | x,y,z = self.size() 59 | glVertex3f(0, 0, 0) 60 | glVertex3f(0, 0, z) 61 | glVertex3f(x, 0, 0) 62 | glVertex3f(x, 0, z) 63 | glVertex3f(0, y, 0) 64 | glVertex3f(0, y, z) 65 | glVertex3f(x, y, 0) 66 | glVertex3f(x, y, z) 67 | 68 | glVertex3f(0, 0, 0) 69 | glVertex3f(0, y, 0) 70 | glVertex3f(x, 0, 0) 71 | glVertex3f(x, y, 0) 72 | glVertex3f(0, 0, z) 73 | glVertex3f(0, y, z) 74 | glVertex3f(x, 0, z) 75 | glVertex3f(x, y, z) 76 | 77 | glVertex3f(0, 0, 0) 78 | glVertex3f(x, 0, 0) 79 | glVertex3f(0, y, 0) 80 | glVertex3f(x, y, 0) 81 | glVertex3f(0, 0, z) 82 | glVertex3f(x, 0, z) 83 | glVertex3f(0, y, z) 84 | glVertex3f(x, y, z) 85 | 86 | glEnd() 87 | 88 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartTemplate_pyside6.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'FlowchartTemplate.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.1.0 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import * 12 | from PySide6.QtGui import * 13 | from PySide6.QtWidgets import * 14 | 15 | from ..widgets.DataTreeWidget import DataTreeWidget 16 | from ..flowchart.FlowchartGraphicsView import FlowchartGraphicsView 17 | 18 | 19 | class Ui_Form(object): 20 | def setupUi(self, Form): 21 | if not Form.objectName(): 22 | Form.setObjectName(u"Form") 23 | Form.resize(529, 329) 24 | self.selInfoWidget = QWidget(Form) 25 | self.selInfoWidget.setObjectName(u"selInfoWidget") 26 | self.selInfoWidget.setGeometry(QRect(260, 10, 264, 222)) 27 | self.gridLayout = QGridLayout(self.selInfoWidget) 28 | self.gridLayout.setObjectName(u"gridLayout") 29 | self.selDescLabel = QLabel(self.selInfoWidget) 30 | self.selDescLabel.setObjectName(u"selDescLabel") 31 | self.selDescLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop) 32 | self.selDescLabel.setWordWrap(True) 33 | 34 | self.gridLayout.addWidget(self.selDescLabel, 0, 0, 1, 1) 35 | 36 | self.selNameLabel = QLabel(self.selInfoWidget) 37 | self.selNameLabel.setObjectName(u"selNameLabel") 38 | font = QFont() 39 | font.setBold(True) 40 | self.selNameLabel.setFont(font) 41 | 42 | self.gridLayout.addWidget(self.selNameLabel, 0, 1, 1, 1) 43 | 44 | self.selectedTree = DataTreeWidget(self.selInfoWidget) 45 | __qtreewidgetitem = QTreeWidgetItem() 46 | __qtreewidgetitem.setText(0, u"1"); 47 | self.selectedTree.setHeaderItem(__qtreewidgetitem) 48 | self.selectedTree.setObjectName(u"selectedTree") 49 | 50 | self.gridLayout.addWidget(self.selectedTree, 1, 0, 1, 2) 51 | 52 | self.hoverText = QTextEdit(Form) 53 | self.hoverText.setObjectName(u"hoverText") 54 | self.hoverText.setGeometry(QRect(0, 240, 521, 81)) 55 | self.view = FlowchartGraphicsView(Form) 56 | self.view.setObjectName(u"view") 57 | self.view.setGeometry(QRect(0, 0, 256, 192)) 58 | 59 | self.retranslateUi(Form) 60 | 61 | QMetaObject.connectSlotsByName(Form) 62 | # setupUi 63 | 64 | def retranslateUi(self, Form): 65 | Form.setWindowTitle(QCoreApplication.translate("Form", u"PyQtGraph", None)) 66 | self.selDescLabel.setText("") 67 | self.selNameLabel.setText("") 68 | # retranslateUi 69 | 70 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/canvas/TransformGuiTemplate_pyqt6.py: -------------------------------------------------------------------------------- 1 | # Form implementation generated from reading ui file '../pyqtgraph/canvas/TransformGuiTemplate.ui' 2 | # 3 | # Created by: PyQt6 UI code generator 6.1.0 4 | # 5 | # WARNING: Any manual changes made to this file will be lost when pyuic6 is 6 | # run again. Do not edit this file unless you know what you are doing. 7 | 8 | 9 | from PyQt6 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(224, 117) 16 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) 17 | sizePolicy.setHorizontalStretch(0) 18 | sizePolicy.setVerticalStretch(0) 19 | sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) 20 | Form.setSizePolicy(sizePolicy) 21 | self.verticalLayout = QtWidgets.QVBoxLayout(Form) 22 | self.verticalLayout.setContentsMargins(0, 0, 0, 0) 23 | self.verticalLayout.setSpacing(1) 24 | self.verticalLayout.setObjectName("verticalLayout") 25 | self.translateLabel = QtWidgets.QLabel(Form) 26 | self.translateLabel.setObjectName("translateLabel") 27 | self.verticalLayout.addWidget(self.translateLabel) 28 | self.rotateLabel = QtWidgets.QLabel(Form) 29 | self.rotateLabel.setObjectName("rotateLabel") 30 | self.verticalLayout.addWidget(self.rotateLabel) 31 | self.scaleLabel = QtWidgets.QLabel(Form) 32 | self.scaleLabel.setObjectName("scaleLabel") 33 | self.verticalLayout.addWidget(self.scaleLabel) 34 | self.horizontalLayout = QtWidgets.QHBoxLayout() 35 | self.horizontalLayout.setObjectName("horizontalLayout") 36 | self.mirrorImageBtn = QtWidgets.QPushButton(Form) 37 | self.mirrorImageBtn.setToolTip("") 38 | self.mirrorImageBtn.setObjectName("mirrorImageBtn") 39 | self.horizontalLayout.addWidget(self.mirrorImageBtn) 40 | self.reflectImageBtn = QtWidgets.QPushButton(Form) 41 | self.reflectImageBtn.setObjectName("reflectImageBtn") 42 | self.horizontalLayout.addWidget(self.reflectImageBtn) 43 | self.verticalLayout.addLayout(self.horizontalLayout) 44 | 45 | self.retranslateUi(Form) 46 | QtCore.QMetaObject.connectSlotsByName(Form) 47 | 48 | def retranslateUi(self, Form): 49 | _translate = QtCore.QCoreApplication.translate 50 | Form.setWindowTitle(_translate("Form", "PyQtGraph")) 51 | self.translateLabel.setText(_translate("Form", "Translate:")) 52 | self.rotateLabel.setText(_translate("Form", "Rotate:")) 53 | self.scaleLabel.setText(_translate("Form", "Scale:")) 54 | self.mirrorImageBtn.setText(_translate("Form", "Mirror")) 55 | self.reflectImageBtn.setText(_translate("Form", "Reflect")) 56 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/exporters/HDF5Exporter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import QtCore 3 | from .Exporter import Exporter 4 | from ..parametertree import Parameter 5 | from .. import PlotItem 6 | 7 | import numpy 8 | try: 9 | import h5py 10 | HAVE_HDF5 = True 11 | except ImportError: 12 | HAVE_HDF5 = False 13 | 14 | translate = QtCore.QCoreApplication.translate 15 | 16 | __all__ = ['HDF5Exporter'] 17 | 18 | 19 | class HDF5Exporter(Exporter): 20 | Name = "HDF5 Export: plot (x,y)" 21 | windows = [] 22 | allowCopy = False 23 | 24 | def __init__(self, item): 25 | Exporter.__init__(self, item) 26 | self.params = Parameter(name='params', type='group', children=[ 27 | {'name': 'Name', 'title': translate("Exporter", 'Name'), 'type': 'str', 'value': 'Export', }, 28 | {'name': 'columnMode', 'title': translate("Exporter", 'columnMode'), 'type': 'list', 29 | 'limits': ['(x,y) per plot', '(x,y,y,y) for all plots']}, 30 | ]) 31 | 32 | def parameters(self): 33 | return self.params 34 | 35 | def export(self, fileName=None): 36 | if not HAVE_HDF5: 37 | raise RuntimeError("This exporter requires the h5py package, " 38 | "but it was not importable.") 39 | 40 | if not isinstance(self.item, PlotItem): 41 | raise Exception("Must have a PlotItem selected for HDF5 export.") 42 | 43 | if fileName is None: 44 | self.fileSaveDialog(filter=["*.h5", "*.hdf", "*.hd5"]) 45 | return 46 | dsname = self.params['Name'] 47 | fd = h5py.File(fileName, 'a') # forces append to file... 'w' doesn't seem to "delete/overwrite" 48 | data = [] 49 | 50 | appendAllX = self.params['columnMode'] == '(x,y) per plot' 51 | # Check if the arrays are ragged 52 | len_first = len(self.item.curves[0].getData()[0]) if self.item.curves[0] else None 53 | ragged = any(len(i.getData()[0]) != len_first for i in self.item.curves) 54 | 55 | if ragged: 56 | dgroup = fd.create_group(dsname) 57 | for i, c in enumerate(self.item.curves): 58 | d = c.getData() 59 | fdata = numpy.array([d[0], d[1]]).astype('double') 60 | cname = c.name() if c.name() is not None else str(i) 61 | dset = dgroup.create_dataset(cname, data=fdata) 62 | else: 63 | for i, c in enumerate(self.item.curves): 64 | d = c.getData() 65 | if appendAllX or i == 0: 66 | data.append(d[0]) 67 | data.append(d[1]) 68 | 69 | fdata = numpy.array(data).astype('double') 70 | dset = fd.create_dataset(dsname, data=fdata) 71 | 72 | fd.close() 73 | 74 | if HAVE_HDF5: 75 | HDF5Exporter.register() 76 | -------------------------------------------------------------------------------- /imodqgis/icons/time-series.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 21 | 23 | image/svg+xml 24 | 26 | 27 | 28 | 29 | 30 | 32 | 53 | 59 | 64 | 65 | 70 | 71 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/canvas/TransformGuiTemplate_pyside2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'TransformGuiTemplate.ui' 4 | # 5 | # Created: Sun Sep 18 19:18:41 2016 6 | # by: pyside2-uic running on PySide2 2.0.0~alpha0 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide2 import QtCore, QtGui, QtWidgets 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(224, 117) 16 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 17 | sizePolicy.setHorizontalStretch(0) 18 | sizePolicy.setVerticalStretch(0) 19 | sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) 20 | Form.setSizePolicy(sizePolicy) 21 | self.verticalLayout = QtWidgets.QVBoxLayout(Form) 22 | self.verticalLayout.setSpacing(1) 23 | self.verticalLayout.setContentsMargins(0, 0, 0, 0) 24 | self.verticalLayout.setObjectName("verticalLayout") 25 | self.translateLabel = QtWidgets.QLabel(Form) 26 | self.translateLabel.setObjectName("translateLabel") 27 | self.verticalLayout.addWidget(self.translateLabel) 28 | self.rotateLabel = QtWidgets.QLabel(Form) 29 | self.rotateLabel.setObjectName("rotateLabel") 30 | self.verticalLayout.addWidget(self.rotateLabel) 31 | self.scaleLabel = QtWidgets.QLabel(Form) 32 | self.scaleLabel.setObjectName("scaleLabel") 33 | self.verticalLayout.addWidget(self.scaleLabel) 34 | self.horizontalLayout = QtWidgets.QHBoxLayout() 35 | self.horizontalLayout.setObjectName("horizontalLayout") 36 | self.mirrorImageBtn = QtWidgets.QPushButton(Form) 37 | self.mirrorImageBtn.setToolTip("") 38 | self.mirrorImageBtn.setObjectName("mirrorImageBtn") 39 | self.horizontalLayout.addWidget(self.mirrorImageBtn) 40 | self.reflectImageBtn = QtWidgets.QPushButton(Form) 41 | self.reflectImageBtn.setObjectName("reflectImageBtn") 42 | self.horizontalLayout.addWidget(self.reflectImageBtn) 43 | self.verticalLayout.addLayout(self.horizontalLayout) 44 | 45 | self.retranslateUi(Form) 46 | QtCore.QMetaObject.connectSlotsByName(Form) 47 | 48 | def retranslateUi(self, Form): 49 | Form.setWindowTitle(QtWidgets.QApplication.translate("Form", "Form", None, -1)) 50 | self.translateLabel.setText(QtWidgets.QApplication.translate("Form", "Translate:", None, -1)) 51 | self.rotateLabel.setText(QtWidgets.QApplication.translate("Form", "Rotate:", None, -1)) 52 | self.scaleLabel.setText(QtWidgets.QApplication.translate("Form", "Scale:", None, -1)) 53 | self.mirrorImageBtn.setText(QtWidgets.QApplication.translate("Form", "Mirror", None, -1)) 54 | self.reflectImageBtn.setText(QtWidgets.QApplication.translate("Form", "Reflect", None, -1)) 55 | 56 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/NodeLibrary.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from .Node import Node 3 | 4 | def isNodeClass(cls): 5 | try: 6 | if not issubclass(cls, Node): 7 | return False 8 | except: 9 | return False 10 | return hasattr(cls, 'nodeName') 11 | 12 | 13 | 14 | class NodeLibrary: 15 | """ 16 | A library of flowchart Node types. Custom libraries may be built to provide 17 | each flowchart with a specific set of allowed Node types. 18 | """ 19 | 20 | def __init__(self): 21 | self.nodeList = OrderedDict() 22 | self.nodeTree = OrderedDict() 23 | 24 | def addNodeType(self, nodeClass, paths, override=False): 25 | """ 26 | Register a new node type. If the type's name is already in use, 27 | an exception will be raised (unless override=True). 28 | 29 | ============== ========================================================= 30 | **Arguments:** 31 | 32 | nodeClass a subclass of Node (must have typ.nodeName) 33 | paths list of tuples specifying the location(s) this 34 | type will appear in the library tree. 35 | override if True, overwrite any class having the same name 36 | ============== ========================================================= 37 | """ 38 | if not isNodeClass(nodeClass): 39 | raise Exception("Object %s is not a Node subclass" % str(nodeClass)) 40 | 41 | name = nodeClass.nodeName 42 | if not override and name in self.nodeList: 43 | raise Exception("Node type name '%s' is already registered." % name) 44 | 45 | self.nodeList[name] = nodeClass 46 | for path in paths: 47 | root = self.nodeTree 48 | for n in path: 49 | if n not in root: 50 | root[n] = OrderedDict() 51 | root = root[n] 52 | root[name] = nodeClass 53 | 54 | def getNodeType(self, name): 55 | try: 56 | return self.nodeList[name] 57 | except KeyError: 58 | raise Exception("No node type called '%s'" % name) 59 | 60 | def getNodeTree(self): 61 | return self.nodeTree 62 | 63 | def copy(self): 64 | """ 65 | Return a copy of this library. 66 | """ 67 | lib = NodeLibrary() 68 | lib.nodeList = self.nodeList.copy() 69 | lib.nodeTree = self.treeCopy(self.nodeTree) 70 | return lib 71 | 72 | @staticmethod 73 | def treeCopy(tree): 74 | copy = OrderedDict() 75 | for k,v in tree.items(): 76 | if isNodeClass(v): 77 | copy[k] = v 78 | else: 79 | copy[k] = NodeLibrary.treeCopy(v) 80 | return copy 81 | 82 | def reload(self): 83 | """ 84 | Reload Node classes in this library. 85 | """ 86 | raise NotImplementedError() 87 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/opengl/items/GLGridItem.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from OpenGL.GL import * 4 | from .. GLGraphicsItem import GLGraphicsItem 5 | from ... import QtGui 6 | from ... import functions as fn 7 | 8 | __all__ = ['GLGridItem'] 9 | 10 | class GLGridItem(GLGraphicsItem): 11 | """ 12 | **Bases:** :class:`GLGraphicsItem ` 13 | 14 | Displays a wire-frame grid. 15 | """ 16 | 17 | def __init__(self, size=None, color=(255, 255, 255, 76.5), antialias=True, glOptions='translucent'): 18 | GLGraphicsItem.__init__(self) 19 | self.setGLOptions(glOptions) 20 | self.antialias = antialias 21 | if size is None: 22 | size = QtGui.QVector3D(20,20,1) 23 | self.setSize(size=size) 24 | self.setSpacing(1, 1, 1) 25 | self.setColor(color) 26 | 27 | def setSize(self, x=None, y=None, z=None, size=None): 28 | """ 29 | Set the size of the axes (in its local coordinate system; this does not affect the transform) 30 | Arguments can be x,y,z or size=QVector3D(). 31 | """ 32 | if size is not None: 33 | x = size.x() 34 | y = size.y() 35 | z = size.z() 36 | self.__size = [x,y,z] 37 | self.update() 38 | 39 | def size(self): 40 | return self.__size[:] 41 | 42 | def setSpacing(self, x=None, y=None, z=None, spacing=None): 43 | """ 44 | Set the spacing between grid lines. 45 | Arguments can be x,y,z or spacing=QVector3D(). 46 | """ 47 | if spacing is not None: 48 | x = spacing.x() 49 | y = spacing.y() 50 | z = spacing.z() 51 | self.__spacing = [x,y,z] 52 | self.update() 53 | 54 | def spacing(self): 55 | return self.__spacing[:] 56 | 57 | def setColor(self, color): 58 | """Set the color of the grid. Arguments are the same as those accepted by functions.mkColor()""" 59 | self.__color = fn.mkColor(color) 60 | self.update() 61 | 62 | def color(self): 63 | return self.__color 64 | 65 | def paint(self): 66 | self.setupGLState() 67 | 68 | if self.antialias: 69 | glEnable(GL_LINE_SMOOTH) 70 | glEnable(GL_BLEND) 71 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 72 | glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) 73 | 74 | glBegin( GL_LINES ) 75 | 76 | x,y,z = self.size() 77 | xs,ys,zs = self.spacing() 78 | xvals = np.arange(-x/2., x/2. + xs*0.001, xs) 79 | yvals = np.arange(-y/2., y/2. + ys*0.001, ys) 80 | glColor4f(*self.color().getRgbF()) 81 | for x in xvals: 82 | glVertex3f(x, yvals[0], 0) 83 | glVertex3f(x, yvals[-1], 0) 84 | for y in yvals: 85 | glVertex3f(xvals[0], y, 0) 86 | glVertex3f(xvals[-1], y, 0) 87 | 88 | glEnd() 89 | -------------------------------------------------------------------------------- /tests/testdata/ipf-timeseries/B30F0222001.txt: -------------------------------------------------------------------------------- 1 | 113 2 | 2,1 3 | time,1e+20 4 | head,1e+20 5 | 19571214000000,-1.55 6 | 19571228000000,-1.53 7 | 19580114000000,-1.24 8 | 19580128000000,-1.28 9 | 19580215000000,-1.49 10 | 19580228000000,-1.42 11 | 19580314000000,-1.37 12 | 19580328000000,-1.43 13 | 19580414000000,-1.62 14 | 19580514000000,-1.72 15 | 19580528000000,-1.85 16 | 19580614000000,-1.81 17 | 19580628000000,-1.79 18 | 19580714000000,-1.84 19 | 19580728000000,-1.77 20 | 19580828000000,-1.91 21 | 19580915000000,-2.14 22 | 19581014000000,-1.93 23 | 19581028000000,-1.9 24 | 19581114000000,-2.0 25 | 19581128000000,-2.24 26 | 19581215000000,-2.01 27 | 19581229000000,-1.98 28 | 19590128000000,-1.83 29 | 19590214000000,-1.8 30 | 19590228000000,-2.04 31 | 19590319000000,-2.1 32 | 19590328000000,-1.89 33 | 19590414000000,-1.73 34 | 19590428000000,-1.69 35 | 19590528000000,-1.82 36 | 19590728000000,-2.11 37 | 19590828000000,-2.05 38 | 19590928000000,-2.15 39 | 19591014000000,-2.13 40 | 19591028000000,-1.94 41 | 19591128000000,-2.07 42 | 19591214000000,-2.09 43 | 19600314000000,-1.96 44 | 19600528000000,-1.92 45 | 19600714000000,-2.02 46 | 19600829000000,-1.59 47 | 19600914000000,-1.7 48 | 19600928000000,-1.76 49 | 19601014000000,-1.64 50 | 19601128000000,-1.39 51 | 19601214000000,-1.45 52 | 19601228000000,-1.52 53 | 19610114000000,-1.58 54 | 19610214000000,-1.4 55 | 19610314000000,-1.61 56 | 19610714000000,-1.99 57 | 19610828000000,-1.97 58 | 19611027000000,-1.5 59 | 19611114000000,-1.25 60 | 19611214000000,-1.33 61 | 19620201000000,-1.44 62 | 19620214000000,-1.38 63 | 19620228000000,-1.41 64 | 19620312000000,-1.56 65 | 19620314000000,-1.63 66 | 19620328000000,-1.68 67 | 19620430000000,-1.65 68 | 19620528000000,-1.6 69 | 19620628000000,-1.75 70 | 19620727000000,-1.71 71 | 19620814000000,-1.74 72 | 19620929000000,-1.67 73 | 19621214000000,-1.51 74 | 19630416000000,-1.46 75 | 19630814000000,-1.95 76 | 19640928000000,-1.86 77 | 19641014000000,-1.57 78 | 19641228000000,-1.29 79 | 19650103000000,-1.47 80 | 19650114000000,-1.26 81 | 19650128000000,-1.31 82 | 19651214000000,-1.3 83 | 19660114000000,-1.35 84 | 19660329000000,-1.48 85 | 19660516000000,-1.34 86 | 19660831000000,-1.87 87 | 19670113000000,-1.32 88 | 19680416000000,-1.54 89 | 19680927000000,-1.88 90 | 19690228000000,-1.18 91 | 19690714000000,-2.03 92 | 19690728000000,-2.08 93 | 19690814000000,-2.26 94 | 19691028000000,-2.12 95 | 19700528000000,-2.2 96 | 19720914000000,-1.36 97 | 19721128000000,-1.23 98 | 19731114000000,-1.17 99 | 19731214000000,-1.15 100 | 19740114000000,-1.14 101 | 19740128000000,-1.13 102 | 19740228000000,-1.22 103 | 19740314000000,-1.2 104 | 19740327000000,-1.21 105 | 19741014000000,-1.16 106 | 19741028000000,-1.07 107 | 19741114000000,-1.11 108 | 19741129000000,-1.06 109 | 19741213000000,-1.09 110 | 19750128000000,-1.05 111 | 19750314000000,-1.19 112 | 19751215000000,-1.27 113 | 19770214000000,-1.1 114 | 19781228000000,-1.04 115 | 19791214000000,-1.12 116 | 19810428000000,-1.08 117 | 19880128000000,-1.03 118 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/GraphicsLayoutWidget.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ..Qt import mkQApp 3 | from ..graphicsItems.GraphicsLayout import GraphicsLayout 4 | from .GraphicsView import GraphicsView 5 | 6 | __all__ = ['GraphicsLayoutWidget'] 7 | class GraphicsLayoutWidget(GraphicsView): 8 | """ 9 | Convenience class consisting of a :class:`GraphicsView 10 | ` with a single :class:`GraphicsLayout 11 | ` as its central item. 12 | 13 | This widget is an easy starting point for generating multi-panel figures. 14 | Example:: 15 | 16 | w = pg.GraphicsLayoutWidget() 17 | p1 = w.addPlot(row=0, col=0) 18 | p2 = w.addPlot(row=0, col=1) 19 | v = w.addViewBox(row=1, col=0, colspan=2) 20 | 21 | ========= ================================================================= 22 | parent (QWidget or None) The parent widget. 23 | show (bool) If True, then immediately show the widget after it is 24 | created. If the widget has no parent, then it will be shown 25 | inside a new window. 26 | size (width, height) tuple. Optionally resize the widget. Note: if 27 | this widget is placed inside a layout, then this argument has no 28 | effect. 29 | title (str or None) If specified, then set the window title for this 30 | widget. 31 | kargs All extra arguments are passed to 32 | :meth:`GraphicsLayout.__init__ 33 | ` 34 | ========= ================================================================= 35 | 36 | 37 | This class wraps several methods from its internal GraphicsLayout: 38 | :func:`nextRow ` 39 | :func:`nextColumn ` 40 | :func:`addPlot ` 41 | :func:`addViewBox ` 42 | :func:`addItem ` 43 | :func:`getItem ` 44 | :func:`addLabel ` 45 | :func:`addLayout ` 46 | :func:`removeItem ` 47 | :func:`itemIndex ` 48 | :func:`clear ` 49 | """ 50 | def __init__(self, parent=None, show=False, size=None, title=None, **kargs): 51 | mkQApp() 52 | GraphicsView.__init__(self, parent) 53 | self.ci = GraphicsLayout(**kargs) 54 | for n in ['nextRow', 'nextCol', 'nextColumn', 'addPlot', 'addViewBox', 'addItem', 'getItem', 'addLayout', 'addLabel', 'removeItem', 'itemIndex', 'clear']: 55 | setattr(self, n, getattr(self.ci, n)) 56 | self.setCentralItem(self.ci) 57 | 58 | if size is not None: 59 | self.resize(*size) 60 | 61 | if title is not None: 62 | self.setWindowTitle(title) 63 | 64 | if show is True: 65 | self.show() 66 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/GraphicsScene/exportDialogTemplate.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 241 10 | 367 11 | 12 | 13 | 14 | Export 15 | 16 | 17 | 18 | 0 19 | 20 | 21 | 22 | 23 | Item to export: 24 | 25 | 26 | 27 | 28 | 29 | 30 | false 31 | 32 | 33 | 34 | 1 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Export format 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Export 53 | 54 | 55 | 56 | 57 | 58 | 59 | Close 60 | 61 | 62 | 63 | 64 | 65 | 66 | 2 67 | 68 | 69 | false 70 | 71 | 72 | 73 | 1 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Export options 82 | 83 | 84 | 85 | 86 | 87 | 88 | Copy 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | ParameterTree 97 | QTreeWidget 98 |
..parametertree
99 |
100 |
101 | 102 | 103 |
104 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/exporters/PrintExporter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .Exporter import Exporter 3 | from ..parametertree import Parameter 4 | from ..Qt import QtGui, QtCore 5 | 6 | translate = QtCore.QCoreApplication.translate 7 | 8 | __all__ = ['PrintExporter'] 9 | #__all__ = [] ## Printer is disabled for now--does not work very well. 10 | 11 | class PrintExporter(Exporter): 12 | Name = "Printer" 13 | def __init__(self, item): 14 | Exporter.__init__(self, item) 15 | tr = self.getTargetRect() 16 | self.params = Parameter(name='params', type='group', children=[ 17 | {'name': 'width', 'title': translate("Exporter", 'width'), 'type': 'float', 'value': 0.1, 18 | 'limits': (0, None), 'suffix': 'm', 'siPrefix': True}, 19 | {'name': 'height', 'title': translate("Exporter", 'height'), 'type': 'float', 20 | 'value': (0.1 * tr.height()) / tr.width(), 'limits': (0, None), 'suffix': 'm', 'siPrefix': True}, 21 | ]) 22 | self.params.param('width').sigValueChanged.connect(self.widthChanged) 23 | self.params.param('height').sigValueChanged.connect(self.heightChanged) 24 | 25 | def widthChanged(self): 26 | sr = self.getSourceRect() 27 | ar = sr.height() / sr.width() 28 | self.params.param('height').setValue(self.params['width'] * ar, blockSignal=self.heightChanged) 29 | 30 | def heightChanged(self): 31 | sr = self.getSourceRect() 32 | ar = sr.width() / sr.height() 33 | self.params.param('width').setValue(self.params['height'] * ar, blockSignal=self.widthChanged) 34 | 35 | def parameters(self): 36 | return self.params 37 | 38 | def export(self, fileName=None): 39 | printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution) 40 | dialog = QtGui.QPrintDialog(printer) 41 | dialog.setWindowTitle(translate('Exporter', "Print Document")) 42 | if dialog.exec_() != QtGui.QDialog.DialogCode.Accepted: 43 | return 44 | 45 | #dpi = QtGui.QDesktopWidget().physicalDpiX() 46 | 47 | #self.svg.setSize(QtCore.QSize(100,100)) 48 | #self.svg.setResolution(600) 49 | #res = printer.resolution() 50 | sr = self.getSourceRect() 51 | #res = sr.width() * .4 / (self.params['width'] * 100 / 2.54) 52 | res = QtGui.QGuiApplication.primaryScreen().physicalDotsPerInchX() 53 | printer.setResolution(res) 54 | rect = printer.pageRect() 55 | center = rect.center() 56 | h = self.params['height'] * res * 100. / 2.54 57 | w = self.params['width'] * res * 100. / 2.54 58 | x = center.x() - w/2. 59 | y = center.y() - h/2. 60 | 61 | targetRect = QtCore.QRect(x, y, w, h) 62 | sourceRect = self.getSourceRect() 63 | painter = QtGui.QPainter(printer) 64 | try: 65 | self.setExportMode(True, {'painter': painter}) 66 | self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect)) 67 | finally: 68 | self.setExportMode(False) 69 | painter.end() 70 | 71 | 72 | #PrintExporter.register() 73 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/JoystickButton.py: -------------------------------------------------------------------------------- 1 | from math import hypot 2 | from ..Qt import QtGui, QtCore, mkQApp 3 | 4 | 5 | __all__ = ['JoystickButton'] 6 | 7 | class JoystickButton(QtGui.QPushButton): 8 | sigStateChanged = QtCore.Signal(object, object) ## self, state 9 | 10 | def __init__(self, parent=None): 11 | QtGui.QPushButton.__init__(self, parent) 12 | self.radius = 200 13 | self.setCheckable(True) 14 | self.state = None 15 | self.setState(0, 0) 16 | self.setFixedWidth(50) 17 | self.setFixedHeight(50) 18 | 19 | 20 | def mousePressEvent(self, ev): 21 | self.setChecked(True) 22 | lpos = ev.position() if hasattr(ev, 'position') else ev.localPos() 23 | self.pressPos = lpos 24 | ev.accept() 25 | 26 | def mouseMoveEvent(self, ev): 27 | lpos = ev.position() if hasattr(ev, 'position') else ev.localPos() 28 | dif = lpos - self.pressPos 29 | self.setState(dif.x(), -dif.y()) 30 | 31 | def mouseReleaseEvent(self, ev): 32 | self.setChecked(False) 33 | self.setState(0,0) 34 | 35 | def wheelEvent(self, ev): 36 | ev.accept() 37 | 38 | 39 | def doubleClickEvent(self, ev): 40 | ev.accept() 41 | 42 | def getState(self): 43 | return self.state 44 | 45 | def setState(self, *xy): 46 | xy = list(xy) 47 | d = hypot(xy[0], xy[1]) # length 48 | nxy = [0, 0] 49 | for i in [0,1]: 50 | if xy[i] == 0: 51 | nxy[i] = 0 52 | else: 53 | nxy[i] = xy[i] / d 54 | 55 | if d > self.radius: 56 | d = self.radius 57 | d = (d / self.radius) ** 2 58 | xy = [nxy[0] * d, nxy[1] * d] 59 | 60 | w2 = self.width() / 2 61 | h2 = self.height() / 2 62 | self.spotPos = QtCore.QPoint( 63 | int(w2 * (1 + xy[0])), 64 | int(h2 * (1 - xy[1])) 65 | ) 66 | self.update() 67 | if self.state == xy: 68 | return 69 | self.state = xy 70 | self.sigStateChanged.emit(self, self.state) 71 | 72 | def paintEvent(self, ev): 73 | super().paintEvent(ev) 74 | p = QtGui.QPainter(self) 75 | p.setBrush(QtGui.QBrush(QtGui.QColor(0,0,0))) 76 | p.drawEllipse( 77 | self.spotPos.x() - 3, 78 | self.spotPos.y() - 3, 79 | 6, 80 | 6 81 | ) 82 | 83 | def resizeEvent(self, ev): 84 | self.setState(*self.state) 85 | super().resizeEvent(ev) 86 | 87 | 88 | 89 | if __name__ == '__main__': 90 | app = mkQApp() 91 | w = QtGui.QMainWindow() 92 | b = JoystickButton() 93 | w.setCentralWidget(b) 94 | w.show() 95 | w.resize(100, 100) 96 | 97 | def fn(b, s): 98 | print("state changed:", s) 99 | 100 | b.sigStateChanged.connect(fn) 101 | 102 | app.exec() if hasattr(app, 'exec') else app.exec_() 103 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/widgets/ValueLabel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from time import perf_counter 3 | from ..Qt import QtGui 4 | from .. import functions as fn 5 | 6 | __all__ = ['ValueLabel'] 7 | 8 | class ValueLabel(QtGui.QLabel): 9 | """ 10 | QLabel specifically for displaying numerical values. 11 | Extends QLabel adding some extra functionality: 12 | 13 | - displaying units with si prefix 14 | - built-in exponential averaging 15 | """ 16 | 17 | def __init__(self, parent=None, suffix='', siPrefix=False, averageTime=0, formatStr=None): 18 | """ 19 | ============== ================================================================================== 20 | **Arguments:** 21 | suffix (str or None) The suffix to place after the value 22 | siPrefix (bool) Whether to add an SI prefix to the units and display a scaled value 23 | averageTime (float) The length of time in seconds to average values. If this value 24 | is 0, then no averaging is performed. As this value increases 25 | the display value will appear to change more slowly and smoothly. 26 | formatStr (str) Optionally, provide a format string to use when displaying text. The text 27 | will be generated by calling formatStr.format(value=, avgValue=, suffix=) 28 | (see Python documentation on str.format) 29 | This option is not compatible with siPrefix 30 | ============== ================================================================================== 31 | """ 32 | QtGui.QLabel.__init__(self, parent) 33 | self.values = [] 34 | self.averageTime = averageTime ## no averaging by default 35 | self.suffix = suffix 36 | self.siPrefix = siPrefix 37 | if formatStr is None: 38 | formatStr = '{avgValue:0.2g} {suffix}' 39 | self.formatStr = formatStr 40 | 41 | def setValue(self, value): 42 | now = perf_counter() 43 | self.values.append((now, value)) 44 | cutoff = now - self.averageTime 45 | while len(self.values) > 0 and self.values[0][0] < cutoff: 46 | self.values.pop(0) 47 | self.update() 48 | 49 | def setFormatStr(self, text): 50 | self.formatStr = text 51 | self.update() 52 | 53 | def setAverageTime(self, t): 54 | self.averageTime = t 55 | 56 | def averageValue(self): 57 | return sum(v[1] for v in self.values) / float(len(self.values)) 58 | 59 | 60 | def paintEvent(self, ev): 61 | self.setText(self.generateText()) 62 | return super().paintEvent(ev) 63 | 64 | def generateText(self): 65 | if len(self.values) == 0: 66 | return '' 67 | avg = self.averageValue() 68 | val = self.values[-1][1] 69 | if self.siPrefix: 70 | return fn.siFormat(avg, suffix=self.suffix) 71 | else: 72 | return self.formatStr.format(value=val, avgValue=avg, suffix=self.suffix) 73 | 74 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/GraphicsScene/exportDialogTemplate_pyqt6.py: -------------------------------------------------------------------------------- 1 | # Form implementation generated from reading ui file '../pyqtgraph/GraphicsScene/exportDialogTemplate.ui' 2 | # 3 | # Created by: PyQt6 UI code generator 6.1.0 4 | # 5 | # WARNING: Any manual changes made to this file will be lost when pyuic6 is 6 | # run again. Do not edit this file unless you know what you are doing. 7 | 8 | 9 | from PyQt6 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(241, 367) 16 | self.gridLayout = QtWidgets.QGridLayout(Form) 17 | self.gridLayout.setSpacing(0) 18 | self.gridLayout.setObjectName("gridLayout") 19 | self.label = QtWidgets.QLabel(Form) 20 | self.label.setObjectName("label") 21 | self.gridLayout.addWidget(self.label, 0, 0, 1, 3) 22 | self.itemTree = QtWidgets.QTreeWidget(Form) 23 | self.itemTree.setObjectName("itemTree") 24 | self.itemTree.headerItem().setText(0, "1") 25 | self.itemTree.header().setVisible(False) 26 | self.gridLayout.addWidget(self.itemTree, 1, 0, 1, 3) 27 | self.label_2 = QtWidgets.QLabel(Form) 28 | self.label_2.setObjectName("label_2") 29 | self.gridLayout.addWidget(self.label_2, 2, 0, 1, 3) 30 | self.formatList = QtWidgets.QListWidget(Form) 31 | self.formatList.setObjectName("formatList") 32 | self.gridLayout.addWidget(self.formatList, 3, 0, 1, 3) 33 | self.exportBtn = QtWidgets.QPushButton(Form) 34 | self.exportBtn.setObjectName("exportBtn") 35 | self.gridLayout.addWidget(self.exportBtn, 6, 1, 1, 1) 36 | self.closeBtn = QtWidgets.QPushButton(Form) 37 | self.closeBtn.setObjectName("closeBtn") 38 | self.gridLayout.addWidget(self.closeBtn, 6, 2, 1, 1) 39 | self.paramTree = ParameterTree(Form) 40 | self.paramTree.setColumnCount(2) 41 | self.paramTree.setObjectName("paramTree") 42 | self.paramTree.headerItem().setText(0, "1") 43 | self.paramTree.header().setVisible(False) 44 | self.gridLayout.addWidget(self.paramTree, 5, 0, 1, 3) 45 | self.label_3 = QtWidgets.QLabel(Form) 46 | self.label_3.setObjectName("label_3") 47 | self.gridLayout.addWidget(self.label_3, 4, 0, 1, 3) 48 | self.copyBtn = QtWidgets.QPushButton(Form) 49 | self.copyBtn.setObjectName("copyBtn") 50 | self.gridLayout.addWidget(self.copyBtn, 6, 0, 1, 1) 51 | 52 | self.retranslateUi(Form) 53 | QtCore.QMetaObject.connectSlotsByName(Form) 54 | 55 | def retranslateUi(self, Form): 56 | _translate = QtCore.QCoreApplication.translate 57 | Form.setWindowTitle(_translate("Form", "Export")) 58 | self.label.setText(_translate("Form", "Item to export:")) 59 | self.label_2.setText(_translate("Form", "Export format")) 60 | self.exportBtn.setText(_translate("Form", "Export")) 61 | self.closeBtn.setText(_translate("Form", "Close")) 62 | self.label_3.setText(_translate("Form", "Export options")) 63 | self.copyBtn.setText(_translate("Form", "Copy")) 64 | from ..parametertree import ParameterTree 65 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartCtrlTemplate_pyqt5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file './pyqtgraph/flowchart/FlowchartCtrlTemplate.ui' 4 | # 5 | # Created: Wed Mar 26 15:09:28 2014 6 | # by: PyQt5 UI code generator 5.0.1 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PyQt5 import QtCore, QtGui, QtWidgets 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(217, 499) 16 | self.gridLayout = QtWidgets.QGridLayout(Form) 17 | self.gridLayout.setContentsMargins(0, 0, 0, 0) 18 | self.gridLayout.setVerticalSpacing(0) 19 | self.gridLayout.setObjectName("gridLayout") 20 | self.loadBtn = QtWidgets.QPushButton(Form) 21 | self.loadBtn.setObjectName("loadBtn") 22 | self.gridLayout.addWidget(self.loadBtn, 1, 0, 1, 1) 23 | self.saveBtn = FeedbackButton(Form) 24 | self.saveBtn.setObjectName("saveBtn") 25 | self.gridLayout.addWidget(self.saveBtn, 1, 1, 1, 2) 26 | self.saveAsBtn = FeedbackButton(Form) 27 | self.saveAsBtn.setObjectName("saveAsBtn") 28 | self.gridLayout.addWidget(self.saveAsBtn, 1, 3, 1, 1) 29 | self.reloadBtn = FeedbackButton(Form) 30 | self.reloadBtn.setCheckable(False) 31 | self.reloadBtn.setFlat(False) 32 | self.reloadBtn.setObjectName("reloadBtn") 33 | self.gridLayout.addWidget(self.reloadBtn, 4, 0, 1, 2) 34 | self.showChartBtn = QtWidgets.QPushButton(Form) 35 | self.showChartBtn.setCheckable(True) 36 | self.showChartBtn.setObjectName("showChartBtn") 37 | self.gridLayout.addWidget(self.showChartBtn, 4, 2, 1, 2) 38 | self.ctrlList = TreeWidget(Form) 39 | self.ctrlList.setObjectName("ctrlList") 40 | self.ctrlList.headerItem().setText(0, "1") 41 | self.ctrlList.header().setVisible(False) 42 | self.ctrlList.header().setStretchLastSection(False) 43 | self.gridLayout.addWidget(self.ctrlList, 3, 0, 1, 4) 44 | self.fileNameLabel = QtWidgets.QLabel(Form) 45 | font = QtGui.QFont() 46 | font.setBold(True) 47 | font.setWeight(75) 48 | self.fileNameLabel.setFont(font) 49 | self.fileNameLabel.setText("") 50 | self.fileNameLabel.setAlignment(QtCore.Qt.AlignCenter) 51 | self.fileNameLabel.setObjectName("fileNameLabel") 52 | self.gridLayout.addWidget(self.fileNameLabel, 0, 1, 1, 1) 53 | 54 | self.retranslateUi(Form) 55 | QtCore.QMetaObject.connectSlotsByName(Form) 56 | 57 | def retranslateUi(self, Form): 58 | _translate = QtCore.QCoreApplication.translate 59 | Form.setWindowTitle(_translate("Form", "PyQtGraph")) 60 | self.loadBtn.setText(_translate("Form", "Load..")) 61 | self.saveBtn.setText(_translate("Form", "Save")) 62 | self.saveAsBtn.setText(_translate("Form", "As..")) 63 | self.reloadBtn.setText(_translate("Form", "Reload Libs")) 64 | self.showChartBtn.setText(_translate("Form", "Flowchart")) 65 | 66 | from ..widgets.FeedbackButton import FeedbackButton 67 | from ..widgets.TreeWidget import TreeWidget 68 | -------------------------------------------------------------------------------- /imodqgis/widgets/colors_dialog.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | import numpy as np 5 | from PyQt5.QtWidgets import ( 6 | QComboBox, 7 | QDialog, 8 | QHBoxLayout, 9 | QLabel, 10 | QPushButton, 11 | QVBoxLayout, 12 | ) 13 | 14 | PSEUDOCOLOR = 0 15 | UNIQUE_COLOR = 1 16 | 17 | 18 | class ColorsDialog(QDialog): 19 | def __init__( 20 | self, pseudocolor_widget, unique_color_widget, default_to, data, parent 21 | ): 22 | QDialog.__init__(self, parent) 23 | self.pseudocolor_widget = pseudocolor_widget 24 | self.unique_color_widget = unique_color_widget 25 | self.data = data 26 | 27 | self.render_type_box = QComboBox() 28 | self.render_type_box.insertItems(0, ["Pseudocolor", "Unique values"]) 29 | self.render_type_box.setCurrentIndex(default_to) 30 | self.render_type_box.currentIndexChanged.connect(self.on_render_type_changed) 31 | 32 | # Check if data is a number dtype, if not: only unique coloring works properly 33 | if not np.issubdtype(data.dtype, np.number): 34 | self.render_type_box.setCurrentIndex(UNIQUE_COLOR) 35 | self.render_type_box.setEnabled(False) 36 | else: 37 | self.render_type_box.setEnabled(True) 38 | 39 | apply_button = QPushButton("Apply") 40 | cancel_button = QPushButton("Cancel") 41 | apply_button.clicked.connect(self.accept) 42 | cancel_button.clicked.connect(self.reject) 43 | 44 | first_row = QHBoxLayout() 45 | first_row.addWidget(QLabel("Render type:")) 46 | first_row.addWidget(self.render_type_box) 47 | first_row.addStretch() 48 | 49 | second_row = QHBoxLayout() 50 | second_row.addWidget(apply_button) 51 | second_row.addWidget(cancel_button) 52 | layout = QVBoxLayout() 53 | layout.addLayout(first_row) 54 | layout.addWidget(self.pseudocolor_widget) 55 | layout.addWidget(self.unique_color_widget) 56 | layout.addLayout(second_row) 57 | self.setLayout(layout) 58 | self.on_render_type_changed() 59 | 60 | def on_render_type_changed(self): 61 | if self.render_type_box.currentIndex() == PSEUDOCOLOR: 62 | self.pseudocolor_widget.setVisible(True) 63 | self.unique_color_widget.setVisible(False) 64 | self.pseudocolor_widget.set_data(self.data) 65 | else: 66 | self.pseudocolor_widget.setVisible(False) 67 | self.unique_color_widget.setVisible(True) 68 | self.unique_color_widget.set_data(self.data) 69 | 70 | def detach(self): 71 | self.pseudocolor_widget.setParent(self.parent()) 72 | self.unique_color_widget.setParent(self.parent()) 73 | 74 | # NOTA BENE: detach() and these overloaded methods are required, otherwise 75 | # the color_widget is garbage collected when the dialog closes. 76 | def closeEvent(self, e): 77 | self.detach() 78 | QDialog.closeEvent(self, e) 79 | 80 | def reject(self): 81 | self.detach() 82 | QDialog.reject(self) 83 | 84 | def accept(self): 85 | self.detach() 86 | QDialog.accept(self) 87 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/GraphicsScene/exportDialogTemplate_pyqt5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '.\exportDialogTemplate.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.4 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_Form(object): 15 | def setupUi(self, Form): 16 | Form.setObjectName("Form") 17 | Form.resize(241, 367) 18 | self.gridLayout = QtWidgets.QGridLayout(Form) 19 | self.gridLayout.setSpacing(0) 20 | self.gridLayout.setObjectName("gridLayout") 21 | self.label = QtWidgets.QLabel(Form) 22 | self.label.setObjectName("label") 23 | self.gridLayout.addWidget(self.label, 0, 0, 1, 3) 24 | self.itemTree = QtWidgets.QTreeWidget(Form) 25 | self.itemTree.setObjectName("itemTree") 26 | self.itemTree.headerItem().setText(0, "1") 27 | self.itemTree.header().setVisible(False) 28 | self.gridLayout.addWidget(self.itemTree, 1, 0, 1, 3) 29 | self.label_2 = QtWidgets.QLabel(Form) 30 | self.label_2.setObjectName("label_2") 31 | self.gridLayout.addWidget(self.label_2, 2, 0, 1, 3) 32 | self.formatList = QtWidgets.QListWidget(Form) 33 | self.formatList.setObjectName("formatList") 34 | self.gridLayout.addWidget(self.formatList, 3, 0, 1, 3) 35 | self.exportBtn = QtWidgets.QPushButton(Form) 36 | self.exportBtn.setObjectName("exportBtn") 37 | self.gridLayout.addWidget(self.exportBtn, 6, 1, 1, 1) 38 | self.closeBtn = QtWidgets.QPushButton(Form) 39 | self.closeBtn.setObjectName("closeBtn") 40 | self.gridLayout.addWidget(self.closeBtn, 6, 2, 1, 1) 41 | self.paramTree = ParameterTree(Form) 42 | self.paramTree.setColumnCount(2) 43 | self.paramTree.setObjectName("paramTree") 44 | self.paramTree.headerItem().setText(0, "1") 45 | self.paramTree.header().setVisible(False) 46 | self.gridLayout.addWidget(self.paramTree, 5, 0, 1, 3) 47 | self.label_3 = QtWidgets.QLabel(Form) 48 | self.label_3.setObjectName("label_3") 49 | self.gridLayout.addWidget(self.label_3, 4, 0, 1, 3) 50 | self.copyBtn = QtWidgets.QPushButton(Form) 51 | self.copyBtn.setObjectName("copyBtn") 52 | self.gridLayout.addWidget(self.copyBtn, 6, 0, 1, 1) 53 | 54 | self.retranslateUi(Form) 55 | QtCore.QMetaObject.connectSlotsByName(Form) 56 | 57 | def retranslateUi(self, Form): 58 | _translate = QtCore.QCoreApplication.translate 59 | Form.setWindowTitle(_translate("Form", "Export")) 60 | self.label.setText(_translate("Form", "Item to export:")) 61 | self.label_2.setText(_translate("Form", "Export format")) 62 | self.exportBtn.setText(_translate("Form", "Export")) 63 | self.closeBtn.setText(_translate("Form", "Close")) 64 | self.label_3.setText(_translate("Form", "Export options")) 65 | self.copyBtn.setText(_translate("Form", "Copy")) 66 | from ..parametertree import ParameterTree 67 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartCtrlTemplate_pyqt6.py: -------------------------------------------------------------------------------- 1 | # Form implementation generated from reading ui file '../pyqtgraph/flowchart/FlowchartCtrlTemplate.ui' 2 | # 3 | # Created by: PyQt6 UI code generator 6.1.0 4 | # 5 | # WARNING: Any manual changes made to this file will be lost when pyuic6 is 6 | # run again. Do not edit this file unless you know what you are doing. 7 | 8 | 9 | from PyQt6 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(217, 499) 16 | self.gridLayout = QtWidgets.QGridLayout(Form) 17 | self.gridLayout.setContentsMargins(0, 0, 0, 0) 18 | self.gridLayout.setVerticalSpacing(0) 19 | self.gridLayout.setObjectName("gridLayout") 20 | self.loadBtn = QtWidgets.QPushButton(Form) 21 | self.loadBtn.setObjectName("loadBtn") 22 | self.gridLayout.addWidget(self.loadBtn, 1, 0, 1, 1) 23 | self.saveBtn = FeedbackButton(Form) 24 | self.saveBtn.setObjectName("saveBtn") 25 | self.gridLayout.addWidget(self.saveBtn, 1, 1, 1, 2) 26 | self.saveAsBtn = FeedbackButton(Form) 27 | self.saveAsBtn.setObjectName("saveAsBtn") 28 | self.gridLayout.addWidget(self.saveAsBtn, 1, 3, 1, 1) 29 | self.reloadBtn = FeedbackButton(Form) 30 | self.reloadBtn.setCheckable(False) 31 | self.reloadBtn.setFlat(False) 32 | self.reloadBtn.setObjectName("reloadBtn") 33 | self.gridLayout.addWidget(self.reloadBtn, 4, 0, 1, 2) 34 | self.showChartBtn = QtWidgets.QPushButton(Form) 35 | self.showChartBtn.setCheckable(True) 36 | self.showChartBtn.setObjectName("showChartBtn") 37 | self.gridLayout.addWidget(self.showChartBtn, 4, 2, 1, 2) 38 | self.ctrlList = TreeWidget(Form) 39 | self.ctrlList.setObjectName("ctrlList") 40 | self.ctrlList.headerItem().setText(0, "1") 41 | self.ctrlList.header().setVisible(False) 42 | self.ctrlList.header().setStretchLastSection(False) 43 | self.gridLayout.addWidget(self.ctrlList, 3, 0, 1, 4) 44 | self.fileNameLabel = QtWidgets.QLabel(Form) 45 | font = QtGui.QFont() 46 | font.setBold(True) 47 | self.fileNameLabel.setFont(font) 48 | self.fileNameLabel.setText("") 49 | self.fileNameLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) 50 | self.fileNameLabel.setObjectName("fileNameLabel") 51 | self.gridLayout.addWidget(self.fileNameLabel, 0, 1, 1, 1) 52 | 53 | self.retranslateUi(Form) 54 | QtCore.QMetaObject.connectSlotsByName(Form) 55 | 56 | def retranslateUi(self, Form): 57 | _translate = QtCore.QCoreApplication.translate 58 | Form.setWindowTitle(_translate("Form", "PyQtGraph")) 59 | self.loadBtn.setText(_translate("Form", "Load..")) 60 | self.saveBtn.setText(_translate("Form", "Save")) 61 | self.saveAsBtn.setText(_translate("Form", "As..")) 62 | self.reloadBtn.setText(_translate("Form", "Reload Libs")) 63 | self.showChartBtn.setText(_translate("Form", "Flowchart")) 64 | from ..widgets.FeedbackButton import FeedbackButton 65 | from ..widgets.TreeWidget import TreeWidget 66 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/graphicsItems/FillBetweenItem.py: -------------------------------------------------------------------------------- 1 | from ..Qt import QtGui 2 | from .. import functions as fn 3 | from .PlotDataItem import PlotDataItem 4 | from .PlotCurveItem import PlotCurveItem 5 | 6 | __all__ = ['FillBetweenItem'] 7 | 8 | class FillBetweenItem(QtGui.QGraphicsPathItem): 9 | """ 10 | GraphicsItem filling the space between two PlotDataItems. 11 | """ 12 | def __init__(self, curve1=None, curve2=None, brush=None, pen=None): 13 | QtGui.QGraphicsPathItem.__init__(self) 14 | self.curves = None 15 | if curve1 is not None and curve2 is not None: 16 | self.setCurves(curve1, curve2) 17 | elif curve1 is not None or curve2 is not None: 18 | raise Exception("Must specify two curves to fill between.") 19 | 20 | if brush is not None: 21 | self.setBrush(brush) 22 | self.setPen(pen) 23 | self.updatePath() 24 | 25 | def setBrush(self, *args, **kwds): 26 | """Change the fill brush. Acceps the same arguments as pg.mkBrush()""" 27 | QtGui.QGraphicsPathItem.setBrush(self, fn.mkBrush(*args, **kwds)) 28 | 29 | def setPen(self, *args, **kwds): 30 | QtGui.QGraphicsPathItem.setPen(self, fn.mkPen(*args, **kwds)) 31 | 32 | def setCurves(self, curve1, curve2): 33 | """Set the curves to fill between. 34 | 35 | Arguments must be instances of PlotDataItem or PlotCurveItem. 36 | 37 | Added in version 0.9.9 38 | """ 39 | if self.curves is not None: 40 | for c in self.curves: 41 | try: 42 | c.sigPlotChanged.disconnect(self.curveChanged) 43 | except (TypeError, RuntimeError): 44 | pass 45 | 46 | curves = [curve1, curve2] 47 | for c in curves: 48 | if not isinstance(c, PlotDataItem) and not isinstance(c, PlotCurveItem): 49 | raise TypeError("Curves must be PlotDataItem or PlotCurveItem.") 50 | self.curves = curves 51 | curve1.sigPlotChanged.connect(self.curveChanged) 52 | curve2.sigPlotChanged.connect(self.curveChanged) 53 | self.setZValue(min(curve1.zValue(), curve2.zValue())-1) 54 | self.curveChanged() 55 | 56 | def curveChanged(self): 57 | self.updatePath() 58 | 59 | def updatePath(self): 60 | if self.curves is None: 61 | self.setPath(QtGui.QPainterPath()) 62 | return 63 | paths = [] 64 | for c in self.curves: 65 | if isinstance(c, PlotDataItem): 66 | paths.append(c.curve.getPath()) 67 | elif isinstance(c, PlotCurveItem): 68 | paths.append(c.getPath()) 69 | 70 | path = QtGui.QPainterPath() 71 | transform = QtGui.QTransform() 72 | ps1 = paths[0].toSubpathPolygons(transform) 73 | ps2 = paths[1].toReversed().toSubpathPolygons(transform) 74 | ps2.reverse() 75 | if len(ps1) == 0 or len(ps2) == 0: 76 | self.setPath(QtGui.QPainterPath()) 77 | return 78 | 79 | 80 | for p1, p2 in zip(ps1, ps2): 81 | path.addPolygon(p1 + p2) 82 | self.setPath(path) 83 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/canvas/TransformGuiTemplate_pyside6.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'TransformGuiTemplate.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.1.0 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import * 12 | from PySide6.QtGui import * 13 | from PySide6.QtWidgets import * 14 | 15 | 16 | class Ui_Form(object): 17 | def setupUi(self, Form): 18 | if not Form.objectName(): 19 | Form.setObjectName(u"Form") 20 | Form.resize(224, 117) 21 | sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) 22 | sizePolicy.setHorizontalStretch(0) 23 | sizePolicy.setVerticalStretch(0) 24 | sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) 25 | Form.setSizePolicy(sizePolicy) 26 | self.verticalLayout = QVBoxLayout(Form) 27 | self.verticalLayout.setSpacing(1) 28 | self.verticalLayout.setContentsMargins(0, 0, 0, 0) 29 | self.verticalLayout.setObjectName(u"verticalLayout") 30 | self.translateLabel = QLabel(Form) 31 | self.translateLabel.setObjectName(u"translateLabel") 32 | 33 | self.verticalLayout.addWidget(self.translateLabel) 34 | 35 | self.rotateLabel = QLabel(Form) 36 | self.rotateLabel.setObjectName(u"rotateLabel") 37 | 38 | self.verticalLayout.addWidget(self.rotateLabel) 39 | 40 | self.scaleLabel = QLabel(Form) 41 | self.scaleLabel.setObjectName(u"scaleLabel") 42 | 43 | self.verticalLayout.addWidget(self.scaleLabel) 44 | 45 | self.horizontalLayout = QHBoxLayout() 46 | self.horizontalLayout.setObjectName(u"horizontalLayout") 47 | self.mirrorImageBtn = QPushButton(Form) 48 | self.mirrorImageBtn.setObjectName(u"mirrorImageBtn") 49 | 50 | self.horizontalLayout.addWidget(self.mirrorImageBtn) 51 | 52 | self.reflectImageBtn = QPushButton(Form) 53 | self.reflectImageBtn.setObjectName(u"reflectImageBtn") 54 | 55 | self.horizontalLayout.addWidget(self.reflectImageBtn) 56 | 57 | 58 | self.verticalLayout.addLayout(self.horizontalLayout) 59 | 60 | 61 | self.retranslateUi(Form) 62 | 63 | QMetaObject.connectSlotsByName(Form) 64 | # setupUi 65 | 66 | def retranslateUi(self, Form): 67 | Form.setWindowTitle(QCoreApplication.translate("Form", u"PyQtGraph", None)) 68 | self.translateLabel.setText(QCoreApplication.translate("Form", u"Translate:", None)) 69 | self.rotateLabel.setText(QCoreApplication.translate("Form", u"Rotate:", None)) 70 | self.scaleLabel.setText(QCoreApplication.translate("Form", u"Scale:", None)) 71 | #if QT_CONFIG(tooltip) 72 | self.mirrorImageBtn.setToolTip("") 73 | #endif // QT_CONFIG(tooltip) 74 | self.mirrorImageBtn.setText(QCoreApplication.translate("Form", u"Mirror", None)) 75 | self.reflectImageBtn.setText(QCoreApplication.translate("Form", u"Reflect", None)) 76 | # retranslateUi 77 | 78 | -------------------------------------------------------------------------------- /imodqgis/utils/layers.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Deltares 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | # 4 | """ 5 | Layer utilities, different layers are currently stored 6 | in datasets as individual variables ("group_names"). 7 | 8 | When MDAL supports layers for UGRID, these utilities become unnecessary 9 | """ 10 | 11 | import re 12 | from collections import defaultdict 13 | 14 | from qgis.core import ( 15 | QgsMeshDatasetIndex, 16 | ) 17 | 18 | NO_LAYERS = ["0"] 19 | 20 | def natural_sort_key(pair, _nsre=re.compile("([0-9]+)")): 21 | # From: https://stackoverflow.com/questions/4836710/is-there-a-built-in-function-for-string-natural-sort 22 | s = pair[0] 23 | return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)] 24 | 25 | 26 | def get_group_names(layer): 27 | indexes = layer.datasetGroupsIndexes() 28 | qgs_indexes = [QgsMeshDatasetIndex(group=i, dataset=0) for i in indexes] 29 | group_names = [layer.datasetGroupMetadata(i).name() for i in qgs_indexes] 30 | # Sort the entries by name 31 | sorted_pairs = sorted(zip(group_names, indexes), key=natural_sort_key) 32 | group_names, indexes = [list(tup) for tup in zip(*sorted_pairs)] 33 | return indexes, group_names 34 | 35 | 36 | def groupby_variable(group_names, dataset_indexes): 37 | EXCEPTED_VARIABLE_NAMES = ["face_x", "face_y"] # Might be further expanded with other UGRID groups. 38 | grouped = defaultdict(dict) 39 | for group_name, dataset_idx in zip(group_names, dataset_indexes): 40 | if "_layer_" in group_name: 41 | parts = group_name.split("_layer_") 42 | name, layer_number = parts 43 | grouped[name][layer_number] = dataset_idx 44 | elif group_name not in EXCEPTED_VARIABLE_NAMES: 45 | layer_number = NO_LAYERS[0] 46 | name = group_name 47 | grouped[name][layer_number] = dataset_idx 48 | 49 | return grouped 50 | 51 | 52 | def groupby_layer(group_names): 53 | """ 54 | Groupby layer, provided by a list variable names ("group names", in MDAL terms). 55 | 56 | Parameters 57 | ---------- 58 | group_names : list of str 59 | list with dataset group names 60 | 61 | 62 | Returns 63 | ------- 64 | gb : dict 65 | A dictionary with "layer_{\d+}" as key and 66 | a list with all full dataset names 67 | as value 68 | """ 69 | prog = re.compile("(.+)_(layer_\d+)") 70 | groups = [prog.match(group_name) for group_name in group_names] 71 | # Filter None from list, as to filter variables without "layer" in name, e.g. 'faces_x' 72 | groups = list(filter(None.__ne__, groups)) 73 | # Convert to list of tuples: [('layer_1', 'bottom_layer_1'), ...] 74 | # the .group confusingly is a regex method here. 75 | groups = [(g.group(2), g.group(0)) for g in groups] 76 | gb = defaultdict(list) 77 | for key, group in groups: 78 | gb[key].append(group) 79 | return gb 80 | 81 | 82 | def get_layer_idx(layer_key): 83 | """ 84 | Extract layer number from a key such as 'layer_1' 85 | 86 | Parameters 87 | ---------- 88 | key : str 89 | layer key like 'layer_1' 90 | 91 | Returns 92 | ------- 93 | int 94 | """ 95 | return int(re.match("layer_(\d+)", layer_key).group(1)) 96 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/flowchart/FlowchartCtrlTemplate_pyside2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'FlowchartCtrlTemplate.ui' 4 | # 5 | # Created: Sun Sep 18 19:16:46 2016 6 | # by: pyside2-uic running on PySide2 2.0.0~alpha0 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from PySide2 import QtCore, QtGui, QtWidgets 11 | 12 | class Ui_Form(object): 13 | def setupUi(self, Form): 14 | Form.setObjectName("Form") 15 | Form.resize(217, 499) 16 | self.gridLayout = QtWidgets.QGridLayout(Form) 17 | self.gridLayout.setContentsMargins(0, 0, 0, 0) 18 | self.gridLayout.setVerticalSpacing(0) 19 | self.gridLayout.setObjectName("gridLayout") 20 | self.loadBtn = QtWidgets.QPushButton(Form) 21 | self.loadBtn.setObjectName("loadBtn") 22 | self.gridLayout.addWidget(self.loadBtn, 1, 0, 1, 1) 23 | self.saveBtn = FeedbackButton(Form) 24 | self.saveBtn.setObjectName("saveBtn") 25 | self.gridLayout.addWidget(self.saveBtn, 1, 1, 1, 2) 26 | self.saveAsBtn = FeedbackButton(Form) 27 | self.saveAsBtn.setObjectName("saveAsBtn") 28 | self.gridLayout.addWidget(self.saveAsBtn, 1, 3, 1, 1) 29 | self.reloadBtn = FeedbackButton(Form) 30 | self.reloadBtn.setCheckable(False) 31 | self.reloadBtn.setFlat(False) 32 | self.reloadBtn.setObjectName("reloadBtn") 33 | self.gridLayout.addWidget(self.reloadBtn, 4, 0, 1, 2) 34 | self.showChartBtn = QtWidgets.QPushButton(Form) 35 | self.showChartBtn.setCheckable(True) 36 | self.showChartBtn.setObjectName("showChartBtn") 37 | self.gridLayout.addWidget(self.showChartBtn, 4, 2, 1, 2) 38 | self.ctrlList = TreeWidget(Form) 39 | self.ctrlList.setObjectName("ctrlList") 40 | self.ctrlList.headerItem().setText(0, "1") 41 | self.ctrlList.header().setVisible(False) 42 | self.ctrlList.header().setStretchLastSection(False) 43 | self.gridLayout.addWidget(self.ctrlList, 3, 0, 1, 4) 44 | self.fileNameLabel = QtWidgets.QLabel(Form) 45 | font = QtGui.QFont() 46 | font.setWeight(75) 47 | font.setBold(True) 48 | self.fileNameLabel.setFont(font) 49 | self.fileNameLabel.setText("") 50 | self.fileNameLabel.setAlignment(QtCore.Qt.AlignCenter) 51 | self.fileNameLabel.setObjectName("fileNameLabel") 52 | self.gridLayout.addWidget(self.fileNameLabel, 0, 1, 1, 1) 53 | 54 | self.retranslateUi(Form) 55 | QtCore.QMetaObject.connectSlotsByName(Form) 56 | 57 | def retranslateUi(self, Form): 58 | Form.setWindowTitle(QtWidgets.QApplication.translate("Form", "Form", None, -1)) 59 | self.loadBtn.setText(QtWidgets.QApplication.translate("Form", "Load..", None, -1)) 60 | self.saveBtn.setText(QtWidgets.QApplication.translate("Form", "Save", None, -1)) 61 | self.saveAsBtn.setText(QtWidgets.QApplication.translate("Form", "As..", None, -1)) 62 | self.reloadBtn.setText(QtWidgets.QApplication.translate("Form", "Reload Libs", None, -1)) 63 | self.showChartBtn.setText(QtWidgets.QApplication.translate("Form", "Flowchart", None, -1)) 64 | 65 | from ..widgets.FeedbackButton import FeedbackButton 66 | from ..widgets.TreeWidget import TreeWidget 67 | -------------------------------------------------------------------------------- /imodqgis/dependencies/pyqtgraph_0_12_3/opengl/items/GLGradientLegendItem.py: -------------------------------------------------------------------------------- 1 | from ... Qt import QtCore, QtGui 2 | from ... import functions as fn 3 | from ... colormap import ColorMap 4 | from ..GLGraphicsItem import GLGraphicsItem 5 | 6 | __all__ = ['GLGradientLegendItem'] 7 | 8 | class GLGradientLegendItem(GLGraphicsItem): 9 | """ 10 | Displays legend colorbar on the screen. 11 | """ 12 | 13 | def __init__(self, **kwds): 14 | """ 15 | Arguments: 16 | pos: position of the colorbar on the screen, from the top left corner, in pixels 17 | size: size of the colorbar without the text, in pixels 18 | gradient: a pg.ColorMap used to color the colorbar 19 | labels: a dict of "text":value to display next to the colorbar. 20 | The value corresponds to a position in the gradient from 0 to 1. 21 | fontColor: sets the color of the texts. Accepts any single argument accepted by 22 | :func:`~pyqtgraph.mkColor` 23 | #Todo: 24 | size as percentage 25 | legend title 26 | """ 27 | GLGraphicsItem.__init__(self) 28 | glopts = kwds.pop("glOptions", "additive") 29 | self.setGLOptions(glopts) 30 | self.pos = (10, 10) 31 | self.size = (10, 100) 32 | self.fontColor = QtGui.QColor(QtCore.Qt.GlobalColor.white) 33 | # setup a default trivial gradient 34 | stops = (0.0, 1.0) 35 | self.gradient = ColorMap(pos=stops, color=(0.0, 1.0)) 36 | self._gradient = None 37 | self.labels = {str(x) : x for x in stops} 38 | self.setData(**kwds) 39 | 40 | def setData(self, **kwds): 41 | args = ["size", "pos", "gradient", "labels", "fontColor"] 42 | for k in kwds.keys(): 43 | if k not in args: 44 | raise Exception( 45 | "Invalid keyword argument: %s (allowed arguments are %s)" 46 | % (k, str(args)) 47 | ) 48 | 49 | self.antialias = False 50 | 51 | for key in kwds: 52 | value = kwds[key] 53 | if key == 'fontColor': 54 | value = fn.mkColor(value) 55 | elif key == 'gradient': 56 | self._gradient = None 57 | setattr(self, key, value) 58 | 59 | ##todo: add title 60 | ##todo: take more gradient types 61 | self.update() 62 | 63 | def paint(self): 64 | self.setupGLState() 65 | 66 | if self._gradient is None: 67 | self._gradient = self.gradient.getGradient() 68 | 69 | barRect = QtCore.QRectF(self.pos[0], self.pos[1], self.size[0], self.size[1]) 70 | self._gradient.setStart(barRect.bottomLeft()) 71 | self._gradient.setFinalStop(barRect.topLeft()) 72 | 73 | painter = QtGui.QPainter(self.view()) 74 | painter.fillRect(barRect, self._gradient) 75 | painter.setPen(self.fontColor) 76 | for labelText, labelPosition in self.labels.items(): 77 | ## todo: draw ticks, position text properly 78 | x = 1.1 * self.size[0] + self.pos[0] 79 | y = self.size[1] - labelPosition * self.size[1] + self.pos[1] + 8 80 | ##todo: fonts 81 | painter.drawText(QtCore.QPointF(x, y), labelText) 82 | painter.end() 83 | 84 | -------------------------------------------------------------------------------- /imodqgis/icons/enabling-delta-life.svg: -------------------------------------------------------------------------------- 1 | Artboard 1 --------------------------------------------------------------------------------