Commit 6ada12a9 authored by Benjamin Jakimow's avatar Benjamin Jakimow
Browse files

updates 3d plotting (WIP)

parent 7d411161
......@@ -47,13 +47,6 @@ import numpy as np
DEBUG = False
OPENGL_AVAILABLE = False
MATPLOTLIB_AVAILABLE = False
try:
import timeseriesviewer.temporalprofiles3dMPL
MATPLOTLIB_AVAILABLE = True
except Exception as ex:
print('unable to import matlotlib based 3d plotting:\n{}'.format(ex))
try:
......@@ -459,7 +452,7 @@ class PlotSettingsModel3DWidgetDelegate(QStyledItemDelegate):
else:
s = ""
#print(('Delegate commit failed',w.asExpression()))
if isinstance(w, PlotStyleButton):
if isinstance(w, TemporalProfile3DPlotStyleButton):
self.commitData.emit(w)
......@@ -600,7 +593,6 @@ class PlotSettingsModel3D(QAbstractTableModel):
self.cnStyle = 'Style'
self.cnSensor = 'Sensor'
self.columnNames = [self.cnTemporalProfile, self.cnSensor, self.cnStyle, self.cnExpression]
self.mPlotSettings = []
#assert isinstance(plotWidget, DateTimePlotWidget)
......@@ -619,14 +611,6 @@ class PlotSettingsModel3D(QAbstractTableModel):
return False
def createStyle(self, sensor):
if not self.hasStyleForSensor(sensor):
s = TemporalProfile3DPlotStyle()
#use another color for the new sensor
if len(self) > 0:
color = self[-1].color()
s.setColor(nextColor(color))
self.insertPlotStyles([s])
def onSensorRemoved(self, sensor):
assert isinstance(sensor, SensorInstrument)
......@@ -824,7 +808,17 @@ class PlotSettingsModel3D(QAbstractTableModel):
plotStyle.setTemporalProfile(value)
result = True
elif columnName == self.cnStyle:
#set the style and trigger an update
lastItemType = plotStyle.itemType()
lastExpression = plotStyle.expression()
plotStyle.copyFrom(value)
if lastItemType != plotStyle.itemType() or \
lastExpression != plotStyle.expression():
plotStyle.updateDataProperties()
else:
plotStyle.updateStyleProperties()
result = True
return result
......@@ -1205,8 +1199,6 @@ class ProfileViewDockUI(QgsDockWidget, loadUI('profileviewdock.ui')):
mode = 'No3D'
if OPENGL_AVAILABLE:
self.init3DWidgets('gl')
elif MATPLOTLIB_AVAILABLE:
self.init3DWidgets('mpl')
#pi = self.plotWidget2D.plotItem
......@@ -1252,17 +1244,6 @@ class ProfileViewDockUI(QgsDockWidget, loadUI('profileviewdock.ui')):
self.plotWidget3D.setBaseSize(size)
self.splitter3D.setSizes([100, 100])
elif MATPLOTLIB_AVAILABLE and mode == 'mpl':
from timeseriesviewer.temporalprofiles3dMPL import MyMplCanvas3D
self.plotWidget3DMPL = MyMplCanvas3D()
self.plotWidget3DMPL.setObjectName('plotWidget3DMPL')
size = self.labelDummy3D.size()
l.addWidget(self.plotWidget3D)
self.plotWidget3D.setSizePolicy(self.labelDummy3D.sizePolicy())
self.labelDummy3D.setVisible(False)
l.removeWidget(self.labelDummy3D)
self.plotWidget3D.setBaseSize(size)
self.splitter3D.setSizes([100, 100])
def onStackPageChanged(self, i):
w = self.stackedWidget.currentWidget()
......@@ -1402,10 +1383,12 @@ class SpectralTemporalVisualization(QObject):
self.plot2D.getPlotItem().removeItem(pi)
def on3DPlotStyleRemoved(plotStyles):
toRemove = []
for plotStyle in plotStyles:
assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
for pi in plotStyle.mPlotItems:
self.plot3D.removeItem(pi)
toRemove.append(plotStyle.mPlotItems)
self.plot3D.removeItems(toRemove)
def onMaxProfilesChanged(n):
v = self.ui.sbMaxTP.value()
......@@ -1418,7 +1401,7 @@ class SpectralTemporalVisualization(QObject):
self.plotSettingsModel2D.sigPlotStylesRemoved.connect(on2DPlotStyleRemoved)
self.plotSettingsModel3D.sigPlotStylesRemoved.connect(on3DPlotStyleRemoved)
#initialize the update loop
self.updateRequested = True
......@@ -1546,19 +1529,24 @@ class SpectralTemporalVisualization(QObject):
if not OPENGL_AVAILABLE:
return
plotStyle = TemporalProfile3DPlotStyle()
plotStyle.sigExpressionUpdated.connect(self.updatePlot3D)
if len(self.tpCollection) > 0:
temporalProfile = self.tpCollection[0]
plotStyle.setTemporalProfile(temporalProfile)
color = self.plotSettingsModel3D[-1].color()
plotStyle.setColor(nextColor(color))
sensors = list(self.TS.Sensors.keys())
if len(sensors) > 0:
plotStyle.setSensor(sensors[0])
self.plotSettingsModel3D.insertPlotStyles([plotStyle], i=0) # latest to the top
self.updatePlot3D()
plotItems = plotStyle.createPlotItem()
self.plot3D.addItems(plotItems)
#self.updatePlot3D()
def onProfileClicked2D(self, pdi):
if isinstance(pdi, TemporalProfilePlotDataItem):
......@@ -1972,6 +1960,7 @@ class SpectralTemporalVisualization(QObject):
# 1. ensure that data from all bands will be loaded
# new loaded values will be shown in the next updatePlot3D call
coordinates = []
allPlotItems = []
for plotStyle3D in self.plotSettingsModel3D:
assert isinstance(plotStyle3D, TemporalProfile3DPlotStyle)
if plotStyle3D.isPlotable():
......@@ -1980,9 +1969,13 @@ class SpectralTemporalVisualization(QObject):
if len(coordinates) > 0:
self.loadCoordinate(coordinates, mode='all')
# 2. remove old plot items
self.plot3D.clearItems()
toRemove = [item for item in self.plot3D.items if item not in allPlotItems]
self.plot3D.removeItems(toRemove)
toAdd = [item for item in allPlotItems if item not in self.plot3D.items]
self.plot3D.addItems(toAdd)
"""
# 3. add new plot items
plotItems = []
for plotStyle3D in self.plotSettingsModel3D:
......@@ -1994,15 +1987,11 @@ class SpectralTemporalVisualization(QObject):
self.plot3D.addItems(plotItems)
self.plot3D.updateDataRanges()
self.plot3D.resetScaling()
#self.plot3D.resetCamera()
"""
@QtCore.pyqtSlot()
def updatePlot2D(self):
if isinstance(self.plotSettingsModel2D, PlotSettingsModel2D):
if DEBUG:
print('Update plot...')
pi = self.plot2D.getPlotItem()
locations = set()
for plotStyle in self.plotSettingsModel2D:
......
......@@ -564,6 +564,8 @@ class DateTimeViewBox(pg.ViewBox):
class TemporalProfilePlotStyleBase(PlotStyle):
sigStyleUpdated = pyqtSignal()
sigDataUpdated = pyqtSignal()
sigExpressionUpdated = pyqtSignal()
sigSensorChanged = pyqtSignal(SensorInstrument)
......
......@@ -17,7 +17,7 @@
***************************************************************************
"""
# noinspection PyPep8Naming
import sys, os, re, collections
import sys, os, re, collections, copy
from qgis import *
from qgis.core import *
from qgis.gui import *
......@@ -138,6 +138,8 @@ class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
ITEM_TYPES.addOption(Option('ScatterPlotItem', name='3D Scatter Plot'))
ITEM_TYPES.addOption(Option('MeshItem', name='3D Mesh'))
sigStyleUpdated = pyqtSignal()
sigUpdated = pyqtSignal()
sigExpressionUpdated = pyqtSignal()
sigSensorChanged = pyqtSignal(SensorInstrument)
......@@ -146,8 +148,6 @@ class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
super(TemporalProfile3DPlotStyle, self).__init__(temporalProfile=temporalProfile)
#assert isinstance(temporalProfile, TemporalProfile)
#TemporalProfilePlotStyleBase.__init__(self, None)
# get some good defaults
self.setExpression('b')
self.mItemType = 'LinePlotItem'
......@@ -160,17 +160,27 @@ class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
def setItemKwds(self, kwds):
self.m3DItemKWDS = kwds
#self.updateStyleProperties()
def itemKwds(self):
return self.m3DItemKWDS.copy()
def updateStyleProperties(self):
"""
Updates changes in coloring and visibility
:return:
"""
for pdi in self.mPlotItems:
s = ""
def updateDataProperties(self):
"""
Updates changes in the underlying data or item type
"""
plotDataItems = self.mPlotItems[:]
for pdi in self.mPlotItems:
s = ""
......@@ -178,6 +188,8 @@ class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
def setItemType(self, itemType):
assert itemType in TemporalProfile3DPlotStyle.ITEM_TYPES.optionValues()
self.mItemType = itemType
self.sigDataUpdated.emit()
#self.updateDataProperties()
def itemType(self):
return self.mItemType
......@@ -189,8 +201,6 @@ class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
self.setItemType(plotStyle.itemType())
self.setItemKwds(plotStyle.itemKwds())
s = ""
def update(self):
......@@ -247,11 +257,10 @@ class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
return icon
def createPlotItem(self, plotWidget):
def createPlotItem(self):
"""
Returns the PlotItem
:param plotWidget:
:return:
Returns the list of PlotItem related to the current settings
:return: [list-of plotitems]
"""
if not OPENGL_AVAILABLE:
return None
......@@ -335,9 +344,8 @@ class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
# arr[:, i] = (arr[:, i] - m0) / (m1 - m0)
# degug pyqtgraph
import copy
kwds = copy.copy(self.m3DItemKWDS)
kwds = copy.copy(self.m3DItemKWDS)
for k, v in list(kwds.items()):
if isinstance(v, QColor):
kwds[k] = fn.glColor(v)
......@@ -349,6 +357,7 @@ class TemporalProfile3DPlotStyle(TemporalProfilePlotStyleBase):
raise NotImplementedError(self.mItemType)
self.mPlotItems.append(plotItems)
return plotItems
......
......@@ -411,6 +411,16 @@ class ViewWidget3D(GLViewWidget):
self.updateDataRanges()
self.update()
def removeItems(self, items):
for item in items:
if item in self.items:
self.items.remove(item)
item._setView(None)
self.updateDataRanges()
self.update()
def mouseMoveEvent(self, ev):
assert isinstance(ev, QMouseEvent)
""" Allow Shift to Move and Ctrl to Pan.
......
# embedding_in_qt5.py --- Simple Qt5 application embedding matplotlib canvases
#
# Copyright (C) 2005 Florent Rougon
# 2006 Darren Dale
# 2015 Jens H Nielsen
#
# This file is an example program for matplotlib. It may be used and
# modified with no restriction; raw copies as well as modified versions
# may be distributed without limitation.
import sys
import os
import random
import matplotlib
# Make sure that we are using QT5
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from numpy import arange, sin, pi
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from mpl_toolkits.mplot3d import Axes3D # @UnusedImport
from matplotlib.figure import Figure
#from matplotlib.figure import Figure
import numpy
#import mpl_toolkits.mplot3d
from mpl_toolkits.mplot3d import proj3d
import matplotlib.pyplot as plt
plt.style.use('dark_background')
def orthogonal_proj(zfront, zback):
a = (zfront + zback) / (zfront - zback)
b = -2 * (zfront * zback) / (zfront - zback)
# -0.0001 added for numerical stability as suggested in:
# http://stackoverflow.com/questions/23840756
return numpy.array([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, a, b],
[0, 0, -0.0001, zback]])
# Later in your plotting code ...
proj3d.persp_transformation = orthogonal_proj
progname = os.path.basename(sys.argv[0])
progversion = "0.1"
class MyMplCanvas3D(FigureCanvas):
"""Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
#self.axes = fig.add_subplot(111)
self.axes = fig.add_subplot(111, projection='3d')
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.axes.mouse_init()
def compute_initial_figure(self):
self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], [0, 1, 2, 3], 'r')
self.axes.mouse_init()
def update_figure(self):
# Build a list of 4 random integers between 0 and 10 (both inclusive)
return
class MyMplCanvas(FigureCanvas):
"""Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
#self.axes = fig.add_subplot(111)
self.axes = fig.add_subplot(111, projection='3d')
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyStaticMplCanvas(MyMplCanvas):
"""Simple canvas with a sine plot."""
def compute_initial_figure(self):
t = arange(0.0, 3.0, 0.01)
s = sin(2*pi*t)
self.axes.plot(t, s)
class MyDynamicMplCanvas(MyMplCanvas):
"""A canvas that updates itself every second with a new plot."""
def __init__(self, *args, **kwargs):
MyMplCanvas.__init__(self, *args, **kwargs)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.update_figure)
timer.start(1000)
self.axes.mouse_init()
def compute_initial_figure(self):
self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'r')
self.axes.mouse_init()
def update_figure(self):
# Build a list of 4 random integers between 0 and 10 (both inclusive)
l = [random.randint(0, 10) for i in range(4)]
self.axes.cla()
self.axes.plot([0, 1, 2, 3], l, 'r')
self.draw()
self.axes.mouse_init()
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setWindowTitle("application main window")
self.file_menu = QtWidgets.QMenu('&File', self)
self.file_menu.addAction('&Quit', self.fileQuit,
QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
self.menuBar().addMenu(self.file_menu)
self.help_menu = QtWidgets.QMenu('&Help', self)
self.menuBar().addSeparator()
self.menuBar().addMenu(self.help_menu)
self.help_menu.addAction('&About', self.about)
self.main_widget = QtWidgets.QWidget(self)
l = QtWidgets.QVBoxLayout(self.main_widget)
if False:
sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100)
dc = MyDynamicMplCanvas(self.main_widget, width=5, height=4, dpi=100)
l.addWidget(sc)
l.addWidget(dc)
else:
sc = MyMplCanvas3D(self.main_widget, width=5, height=3, dpi = 200)
l.addWidget(sc)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
self.statusBar().showMessage("All hail matplotlib!", 2000)
def fileQuit(self):
self.close()
def closeEvent(self, ce):
self.fileQuit()
def about(self):
QtWidgets.QMessageBox.about(self, "About",
"""embedding_in_qt5.py example
Copyright 2005 Florent Rougon, 2006 Darren Dale, 2015 Jens H Nielsen
This program is a simple example of a Qt5 application embedding matplotlib
canvases.
It may be used and modified with no restriction; raw copies as well as
modified versions may be distributed without limitation.
This is modified from the embedding in qt4 example to show the difference
between qt4 and qt5"""
)
if __name__ == '__main__':
from timeseriesviewer.utils import initQgisApplication
qApp = initQgisApplication()
aw = ApplicationWindow()
aw.setWindowTitle("%s" % progname)
aw.show()
qApp.exec_()
#qApp.exec_()
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment