From a07162d668eeb39623e347dfc675d77826760bfa Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 7 Jun 2017 17:05:48 +0200 Subject: [PATCH 1/6] first semi-working plot of all traces --- QtPyLoT.py | 38 ++++++-- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 178 +++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 998a6c72..b653bfe8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -42,6 +42,11 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ import numpy as np from obspy import UTCDateTime +try: + import pyqtgraph as pg +except: + pg = None + try: from matplotlib.backends.backend_qt4agg import FigureCanvas except ImportError: @@ -66,7 +71,7 @@ from pylot.core.util.utils import fnConstructor, getLogin, \ full_range from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ - WaveformWidget, PropertiesDlg, HelpForm, createAction, PickDlg, \ + WaveformWidget, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ getDataType, ComparisonDialog, TuneAutopicker, AutoPickParaBox from pylot.core.util.map_projection import map_projection from pylot.core.util.structure import DATASTRUCTURE @@ -210,8 +215,14 @@ class MainWindow(QMainWindow): # create central matplotlib figure canvas widget plottitle = "Overview: {0} components ".format(self.getComponent()) - self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, - title=plottitle) + if pg: + self.pg = True + self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) + else: + self.pg = False + self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) self.dataPlot.setCursor(Qt.CrossCursor) # add scroll area used in case number of traces gets too high @@ -1187,6 +1198,12 @@ class MainWindow(QMainWindow): ''' Connect signals refering to WF-Dataplot (select station, tutor_user, scrolling) ''' + if self.pg: + pass + else: + self.connect_mpl() + + def connect_mpl(self): if not self.poS_id: self.poS_id = self.dataPlot.mpl_connect('button_press_event', self.pickOnStation) @@ -1198,11 +1215,16 @@ class MainWindow(QMainWindow): self.scroll_id = self.dataPlot.mpl_connect('scroll_event', self.scrollPlot) - def disconnectWFplotEvents(self): ''' Disconnect all signals refering to WF-Dataplot (select station, tutor_user, scrolling) ''' + if self.pg: + pass + else: + self.disconnect_mpl() + + def disconnect_mpl(self): if self.poS_id: self.dataPlot.mpl_disconnect(self.poS_id) if self.ae_id: @@ -1237,7 +1259,10 @@ class MainWindow(QMainWindow): def clearWaveformDataPlot(self): self.disconnectWFplotEvents() - self.dataPlot.getAxes().cla() + if self.pg: + self.dataPlot.plotitem.clear() + else: + self.dataPlot.getAxes().cla() self.loadlocationaction.setEnabled(False) self.auto_tune.setEnabled(False) self.auto_pick.setEnabled(False) @@ -1254,6 +1279,7 @@ class MainWindow(QMainWindow): ''' Open a modal thread to plot current waveform data. ''' + #self.plotWaveformData() wfp_thread = Thread(self, self.plotWaveformData, progressText='Plotting waveform data...') wfp_thread.finished.connect(self.finishWaveformDataPlot) @@ -1284,7 +1310,7 @@ class MainWindow(QMainWindow): plotWidget.figure.tight_layout() except: pass - self._max_xlims = self.dataPlot.getXLims() + #self._max_xlims = self.dataPlot.getXLims() def adjustPlotHeight(self): height_need = len(self.data.getWFData())*self.height_factor diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 8fc5044c..b603f317 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0af79-dirty +be8035-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index f28d2679..68b637dd 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -12,6 +12,11 @@ import copy import datetime import numpy as np +try: + import pyqtgraph as pg +except: + pg = None + from matplotlib.figure import Figure from pylot.core.util.utils import find_horizontals @@ -42,6 +47,12 @@ from autoPyLoT import autoPyLoT from pylot.core.util.thread import Thread import icons_rc +if pg: + pg.setConfigOption('background', 'w') + pg.setConfigOption('foreground', 'k') + pg.setConfigOptions(antialias=True) + #pg.setConfigOption('leftButtonPan', False) + def getDataType(parent): type = QInputDialog().getItem(parent, "Select phases type", "Type:", ["manual", "automatic"]) @@ -393,6 +404,173 @@ class PlotWidget(FigureCanvas): return self._parent +class WaveformWidgetPG(QtGui.QWidget): + def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): + QtGui.QWidget.__init__(self, parent)#, 1) + self.setParent(parent) + self._parent = parent + # attribute plotdict is a dictionary connecting position and a name + self.plotdict = dict() + # create plot + self.main_layout = QtGui.QVBoxLayout() + self.setLayout(self.main_layout) + #self.win = pg.GraphicsWindow(title="Window") + self.plot = pg.PlotWidget(title=title) + self.main_layout.addWidget(self.plot) + #self.plot = self.win.addPlot(title=title) + self.plot.setMouseEnabled(False, False) + self.plot.showGrid(x=False, y=True, alpha=0.2) + # update labels of the entire widget + #self.updateWidget(xlabel, ylabel, title) + + self.vLine = pg.InfiniteLine(angle=90, movable=False) + self.hLine = pg.InfiniteLine(angle=0, movable=False) + self.plot.addItem(self.vLine, ignoreBounds=True) + self.plot.addItem(self.hLine, ignoreBounds=True) + self._proxy = pg.SignalProxy(self.plot.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) + + def mouseMoved(self, evt): + pos = evt[0] ## using signal proxy turns original arguments into a tuple + if self.plot.sceneBoundingRect().contains(pos): + mousePoint = self.plot.getPlotItem().vb.mapSceneToView(pos) + index = int(mousePoint.x()) + # if index > 0 and index < len(data1): + # label.setText("x=%0.1f, y1=%0.1f, y2=%0.1f" % (mousePoint.x(), data1[index], data2[index])) + self.vLine.setPos(mousePoint.x()) + self.hLine.setPos(mousePoint.y()) + + def getPlotDict(self): + return self.plotdict + + def setPlotDict(self, key, value): + self.plotdict[key] = value + + def clearPlotDict(self): + self.plotdict = dict() + + def getParent(self): + return self._parent + + def setParent(self, parent): + self._parent = parent + + def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, + noiselevel=None, scaleddata=False, mapping=True, + component='*', nth_sample=1, iniPick=None): + #self.getAxes().cla() + self.clearPlotDict() + wfstart, wfend = full_range(wfdata) + nmax = 0 + + settings = QSettings() + compclass = settings.value('compclass') + if not compclass: + print('Warning: No settings for channel components found. Using default') + compclass = SetChannelComponents() + + if not component == '*': + alter_comp = compclass.getCompPosition(component) + #alter_comp = str(alter_comp[0]) + + wfdata = wfdata.select(component=component) + wfdata += wfdata.select(component=alter_comp) + + # list containing tuples of network, station, channel (for sorting) + nsc = [] + for trace in wfdata: + nsc.append((trace.stats.network, trace.stats.station, trace.stats.channel)) + nsc.sort() + nsc.reverse() + + for n, (network, station, channel) in enumerate(nsc): + st = wfdata.select(network=network, station=station, channel=channel) + trace = st[0] + if mapping: + comp = channel[-1] + n = compclass.getPlotPosition(str(comp)) + #n = n[0] + if n > nmax: + nmax = n + msg = 'plotting %s channel of station %s' % (channel, station) + print(msg) + stime = trace.stats.starttime - wfstart + time_ax = prepTimeAxis(stime, trace) + if time_ax is not None: + if not scaleddata: + trace.detrend('constant') + trace.normalize(np.max(np.abs(trace.data)) * 2) + times = [time for index, time in enumerate(time_ax) if not index%nth_sample] + data = [datum + n for index, datum in enumerate(trace.data) if not index%nth_sample] + self.plot.plot(times, data, pen=(0, 0, 0)) + if noiselevel is not None: + for level in noiselevel: + self.plot.plot([time_ax[0], time_ax[-1]], + [level, level], pen=(0, 0, 0)) + # if iniPick: + # ax = self.getAxes() + # ax.vlines(iniPick, ax.get_ylim()[0], ax.get_ylim()[1], + # colors='m', linestyles='dashed', + # linewidth=2) + self.setPlotDict(n, (station, channel, network)) + xlabel = 'seconds since {0}'.format(wfstart) + ylabel = '' + #self.updateWidget(xlabel, ylabel, title) + # self.setXLims([0, wfend - wfstart]) + # self.setYLims([-0.5, nmax + 0.5]) + # if zoomx is not None: + # self.setXLims(zoomx) + # if zoomy is not None: + # self.setYLims(zoomy) + # self.draw() + + # def getAxes(self): + # return self.axes + + # def getXLims(self): + # return self.getAxes().get_xlim() + + # def getYLims(self): + # return self.getAxes().get_ylim() + + # def setXLims(self, lims): + # self.getAxes().set_xlim(lims) + + # def setYLims(self, lims): + # self.getAxes().set_ylim(lims) + + def setYTickLabels(self, pos, labels): + ticks = zip(pos, labels) + leftAx = self.plot.getPlotItem().axes['left']['item'] + # leftAx.tickLength = 5 + # leftAx.orientation = 'right' + leftAx.setTicks([ticks, []]) + + # def updateXLabel(self, text): + # self.getAxes().set_xlabel(text) + # self.draw() + + # def updateYLabel(self, text): + # self.getAxes().set_ylabel(text) + # self.draw() + + # def updateTitle(self, text): + # self.getAxes().set_title(text) + # self.draw() + + # def updateWidget(self, xlabel, ylabel, title): + # self.updateXLabel(xlabel) + # self.updateYLabel(ylabel) + # self.updateTitle(title) + + # def insertLabel(self, pos, text): + # pos = pos / max(self.getAxes().ylim) + # axann = self.getAxes().annotate(text, xy=(.03, pos), + # xycoords='axes fraction') + # axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) + def draw(self): + pass + + class WaveformWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): From 909e2241a7209f6d6608a5c975afc68080ad3e22 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 8 Jun 2017 14:09:01 +0200 Subject: [PATCH 2/6] [change] working version of pyqtgraph for dataPlot, WIP: fill not working on versions lower 0.9.9 --- QtPyLoT.py | 119 +++++++++++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 98 ++++++++++++++---------------- 3 files changed, 135 insertions(+), 84 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index b653bfe8..b47bbbd2 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1009,10 +1009,7 @@ class MainWindow(QMainWindow): return self.dataPlot @staticmethod - def getWFID(gui_event): - - ycoord = gui_event.ydata - + def getWFID(ycoord): try: statID = int(round(ycoord)) except TypeError as e: @@ -1199,10 +1196,13 @@ class MainWindow(QMainWindow): Connect signals refering to WF-Dataplot (select station, tutor_user, scrolling) ''' if self.pg: - pass + self.connect_pg() else: self.connect_mpl() + def connect_pg(self): + self.poS_id = self.dataPlot.plotWidget.scene().sigMouseClicked.connect(self.pickOnStation) + def connect_mpl(self): if not self.poS_id: self.poS_id = self.dataPlot.mpl_connect('button_press_event', @@ -1220,10 +1220,14 @@ class MainWindow(QMainWindow): Disconnect all signals refering to WF-Dataplot (select station, tutor_user, scrolling) ''' if self.pg: - pass + self.disconnect_pg() else: self.disconnect_mpl() + def disconnect_pg(self): + if self.poS_id: + self.dataPlot.plotWidget.scene().sigMouseClicked.disconnect(self.poS_id) + def disconnect_mpl(self): if self.poS_id: self.dataPlot.mpl_disconnect(self.poS_id) @@ -1236,6 +1240,8 @@ class MainWindow(QMainWindow): self.scroll_id = None def finishWaveformDataPlot(self): + if pg: + self.getPlotWidget().updateWidget() self.connectWFplotEvents() self.loadlocationaction.setEnabled(True) self.auto_tune.setEnabled(True) @@ -1260,7 +1266,7 @@ class MainWindow(QMainWindow): def clearWaveformDataPlot(self): self.disconnectWFplotEvents() if self.pg: - self.dataPlot.plotitem.clear() + self.dataPlot.plotWidgetitem.clear() else: self.dataPlot.getAxes().cla() self.loadlocationaction.setEnabled(False) @@ -1313,6 +1319,8 @@ class MainWindow(QMainWindow): #self._max_xlims = self.dataPlot.getXLims() def adjustPlotHeight(self): + if self.pg: + return height_need = len(self.data.getWFData())*self.height_factor plotWidget = self.getPlotWidget() if self.tabs.widget(0).frameSize().height() < height_need: @@ -1408,7 +1416,9 @@ class MainWindow(QMainWindow): return self.seismicPhase def getStationName(self, wfID): - return self.getPlotWidget().getPlotDict()[wfID][0] + plot_dict = self.getPlotWidget().getPlotDict() + if wfID in plot_dict.keys(): + return plot_dict[wfID][0] def alterPhase(self): pass @@ -1455,14 +1465,25 @@ class MainWindow(QMainWindow): self.dataPlot.draw() def pickOnStation(self, gui_event): - if not gui_event.button == 1: - return - - wfID = self.getWFID(gui_event) + if self.pg: + if not gui_event.button() == 1: + return + else: + if not gui_event.button == 1: + return + + if self.pg: + ycoord = self.dataPlot.plotWidget.getPlotItem().vb.mapSceneToView(gui_event.scenePos()).y() + else: + ycoord = gui_event.ydata + + wfID = self.getWFID(ycoord) if wfID is None: return station = self.getStationName(wfID) + if not station: + return self.update_status('picking on station {0}'.format(station)) data = self.get_data().getWFData() pickDlg = PickDlg(self, parameter=self._inputs, @@ -1629,12 +1650,23 @@ class MainWindow(QMainWindow): plotID = self.getStationID(station) if plotID is None: return - ax = self.getPlotWidget().axes + if pg: + pw = self.getPlotWidget().plotWidget + else: + ax = self.getPlotWidget().axes ylims = np.array([-.5, +.5]) + plotID - phase_col = { - 'P': ('c', 'c--', 'b-', 'bv', 'b^', 'b'), - 'S': ('m', 'm--', 'r-', 'rv', 'r^', 'r') - } + if pg: + dashed = QtCore.Qt.DashLine + dotted = QtCore.Qt.DotLine + phase_col = { + 'P': (pg.mkPen('c'), pg.mkPen((0, 255, 255, 100), style=dashed), pg.mkPen('b', style=dashed), pg.mkPen('b', style=dotted)), + 'S': (pg.mkPen('m'), pg.mkPen((255, 0, 255, 100), style=dashed), pg.mkPen('r', style=dashed), pg.mkPen('r', style=dotted)) + } + else: + phase_col = { + 'P': ('c', 'c--', 'b-', 'bv', 'b^', 'b'), + 'S': ('m', 'm--', 'r-', 'rv', 'r^', 'r') + } stat_picks = self.getPicks(type=picktype)[station] @@ -1655,22 +1687,47 @@ class MainWindow(QMainWindow): if not spe and epp and lpp: spe = symmetrize_error(mpp - epp, lpp - mpp) - if picktype == 'manual': - if picks['epp'] and picks['lpp']: - ax.fill_between([epp, lpp], ylims[0], ylims[1], - alpha=.25, color=colors[0], label='EPP, LPP') - if spe: - ax.plot([mpp - spe, mpp - spe], ylims, colors[1], label='{}-SPE'.format(phase)) - ax.plot([mpp + spe, mpp + spe], ylims, colors[1]) - ax.plot([mpp, mpp], ylims, colors[2], label='{}-Pick'.format(phase)) + if pg: + if picktype == 'manual': + if picks['epp'] and picks['lpp']: + pw.plot([epp, epp], ylims, + alpha=.25, pen=colors[0], name='EPP') + pw.plot([lpp, lpp], ylims, + alpha=.25, pen=colors[0], name='LPP') + if spe: + spe_l = pg.PlotDataItem([mpp - spe, mpp - spe], ylims, pen=colors[1], name='{}-SPE'.format(phase)) + spe_r = pg.PlotDataItem([mpp + spe, mpp + spe], ylims, pen=colors[1]) + pw.addItem(spe_l) + pw.addItem(spe_r) + try: + fill = pg.FillBetweenItem(spe_l, spe_r, brush=colors[1].brush()) + fb = pw.addItem(fill) + except: + print('Could not create fill for SPE.') + pw.plot([mpp, mpp], ylims, pen=colors[2], name='{}-Pick'.format(phase)) + else: + pw.plot([mpp, mpp], ylims, pen=colors[0], name='{}-Pick (NO PICKERROR)'.format(phase)) + elif picktype == 'auto': + pw.plot([mpp, mpp], ylims, pen=colors[3]) else: - ax.plot([mpp, mpp], ylims, colors[6], label='{}-Pick (NO PICKERROR)'.format(phase)) - elif picktype == 'auto': - ax.plot(mpp, ylims[1], colors[3], - mpp, ylims[0], colors[4]) - ax.vlines(mpp, ylims[0], ylims[1], colors[5], linestyles='dotted') + raise TypeError('Unknown picktype {0}'.format(picktype)) else: - raise TypeError('Unknown picktype {0}'.format(picktype)) + if picktype == 'manual': + if picks['epp'] and picks['lpp']: + ax.fill_between([epp, lpp], ylims[0], ylims[1], + alpha=.25, color=colors[0], label='EPP, LPP') + if spe: + ax.plot([mpp - spe, mpp - spe], ylims, colors[1], label='{}-SPE'.format(phase)) + ax.plot([mpp + spe, mpp + spe], ylims, colors[1]) + ax.plot([mpp, mpp], ylims, colors[2], label='{}-Pick'.format(phase)) + else: + ax.plot([mpp, mpp], ylims, colors[6], label='{}-Pick (NO PICKERROR)'.format(phase)) + elif picktype == 'auto': + ax.plot(mpp, ylims[1], colors[3], + mpp, ylims[0], colors[4]) + ax.vlines(mpp, ylims[0], ylims[1], colors[5], linestyles='dotted') + else: + raise TypeError('Unknown picktype {0}'.format(picktype)) def locate_event(self): """ diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index b603f317..47c1eedd 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -be8035-dirty +a071-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 68b637dd..c0b285eb 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -414,26 +414,23 @@ class WaveformWidgetPG(QtGui.QWidget): # create plot self.main_layout = QtGui.QVBoxLayout() self.setLayout(self.main_layout) - #self.win = pg.GraphicsWindow(title="Window") - self.plot = pg.PlotWidget(title=title) - self.main_layout.addWidget(self.plot) - #self.plot = self.win.addPlot(title=title) - self.plot.setMouseEnabled(False, False) - self.plot.showGrid(x=False, y=True, alpha=0.2) - # update labels of the entire widget - #self.updateWidget(xlabel, ylabel, title) + self.plotWidget = pg.PlotWidget(title=title, autoDownsample=True) + self.main_layout.addWidget(self.plotWidget) + #self.plotWidget.setMouseEnabled(False, False) + self.plotWidget.showGrid(x=False, y=True, alpha=0.2) + self._proxy = pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) + def reinitMoveProxy(self): self.vLine = pg.InfiniteLine(angle=90, movable=False) self.hLine = pg.InfiniteLine(angle=0, movable=False) - self.plot.addItem(self.vLine, ignoreBounds=True) - self.plot.addItem(self.hLine, ignoreBounds=True) - self._proxy = pg.SignalProxy(self.plot.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) - + self.plotWidget.addItem(self.vLine, ignoreBounds=True) + self.plotWidget.addItem(self.hLine, ignoreBounds=True) + def mouseMoved(self, evt): pos = evt[0] ## using signal proxy turns original arguments into a tuple - if self.plot.sceneBoundingRect().contains(pos): - mousePoint = self.plot.getPlotItem().vb.mapSceneToView(pos) - index = int(mousePoint.x()) + if self.plotWidget.sceneBoundingRect().contains(pos): + mousePoint = self.plotWidget.getPlotItem().vb.mapSceneToView(pos) + # index = int(mousePoint.x()) # if index > 0 and index < len(data1): # label.setText("x=%0.1f, y1=%0.1f, y2=%0.1f" % (mousePoint.x(), data1[index], data2[index])) self.vLine.setPos(mousePoint.x()) @@ -457,7 +454,8 @@ class WaveformWidgetPG(QtGui.QWidget): def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, noiselevel=None, scaleddata=False, mapping=True, component='*', nth_sample=1, iniPick=None): - #self.getAxes().cla() + self.title = title + self.plotWidget.getPlotItem().clear() self.clearPlotDict() wfstart, wfend = full_range(wfdata) nmax = 0 @@ -501,10 +499,10 @@ class WaveformWidgetPG(QtGui.QWidget): trace.normalize(np.max(np.abs(trace.data)) * 2) times = [time for index, time in enumerate(time_ax) if not index%nth_sample] data = [datum + n for index, datum in enumerate(trace.data) if not index%nth_sample] - self.plot.plot(times, data, pen=(0, 0, 0)) + self.plotWidget.plot(times, data, pen=(0, 0, 0)) if noiselevel is not None: for level in noiselevel: - self.plot.plot([time_ax[0], time_ax[-1]], + self.plotWidget.plot([time_ax[0], time_ax[-1]], [level, level], pen=(0, 0, 0)) # if iniPick: # ax = self.getAxes() @@ -512,16 +510,12 @@ class WaveformWidgetPG(QtGui.QWidget): # colors='m', linestyles='dashed', # linewidth=2) self.setPlotDict(n, (station, channel, network)) - xlabel = 'seconds since {0}'.format(wfstart) - ylabel = '' - #self.updateWidget(xlabel, ylabel, title) - # self.setXLims([0, wfend - wfstart]) - # self.setYLims([-0.5, nmax + 0.5]) - # if zoomx is not None: - # self.setXLims(zoomx) - # if zoomy is not None: - # self.setYLims(zoomy) - # self.draw() + self.reinitMoveProxy() + self.xlabel = 'seconds since {0}'.format(wfstart) + self.ylabel = '' + self.setXLims([0, wfend - wfstart]) + self.setYLims([-0.5, nmax + 0.5]) + self.draw() # def getAxes(self): # return self.axes @@ -532,41 +526,41 @@ class WaveformWidgetPG(QtGui.QWidget): # def getYLims(self): # return self.getAxes().get_ylim() - # def setXLims(self, lims): - # self.getAxes().set_xlim(lims) + def setXLims(self, lims): + vb = self.plotWidget.getPlotItem().getViewBox() + vb.setXRange(lims[0], lims[1], padding=0) - # def setYLims(self, lims): - # self.getAxes().set_ylim(lims) + def setYLims(self, lims): + vb = self.plotWidget.getPlotItem().getViewBox() + vb.setYRange(lims[0], lims[1], padding=0) def setYTickLabels(self, pos, labels): ticks = zip(pos, labels) - leftAx = self.plot.getPlotItem().axes['left']['item'] + minorTicks = [(0, 0) for item in labels] # leftAx.tickLength = 5 # leftAx.orientation = 'right' - leftAx.setTicks([ticks, []]) + self.getAxItem('left').setTicks([ticks, minorTicks]) - # def updateXLabel(self, text): - # self.getAxes().set_xlabel(text) - # self.draw() + def updateXLabel(self, text): + self.getAxItem('bottom').setLabel(text) + self.draw() - # def updateYLabel(self, text): - # self.getAxes().set_ylabel(text) - # self.draw() + def updateYLabel(self, text): + self.getAxItem('left').setLabel(text) + self.draw() - # def updateTitle(self, text): - # self.getAxes().set_title(text) - # self.draw() + def getAxItem(self, position): + return self.plotWidget.getPlotItem().axes[position]['item'] - # def updateWidget(self, xlabel, ylabel, title): - # self.updateXLabel(xlabel) - # self.updateYLabel(ylabel) - # self.updateTitle(title) + def updateTitle(self, text): + self.plotWidget.getPlotItem().setTitle(text) + self.draw() + + def updateWidget(self):#, xlabel, ylabel, title): + self.updateXLabel(self.xlabel) + self.updateYLabel(self.ylabel) + self.updateTitle(self.title) - # def insertLabel(self, pos, text): - # pos = pos / max(self.getAxes().ylim) - # axann = self.getAxes().annotate(text, xy=(.03, pos), - # xycoords='axes fraction') - # axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) def draw(self): pass From d6dd8bfaa8ffec0100865fb5414aeb14b63aa3bb Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 8 Jun 2017 16:14:37 +0200 Subject: [PATCH 3/6] [bugfix] several bugfixes mostly refering to plotting from plotWaveformDataThread, now only plot data are generated from within thread --- QtPyLoT.py | 58 +++++++++++++++++++++----------------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 18 ++++-------- 3 files changed, 38 insertions(+), 40 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index b47bbbd2..cc00fc74 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1201,7 +1201,7 @@ class MainWindow(QMainWindow): self.connect_mpl() def connect_pg(self): - self.poS_id = self.dataPlot.plotWidget.scene().sigMouseClicked.connect(self.pickOnStation) + self.poS_id = self.dataPlot.plotWidget.scene().sigMouseClicked.connect(self.pickOnStation) def connect_mpl(self): if not self.poS_id: @@ -1226,7 +1226,7 @@ class MainWindow(QMainWindow): def disconnect_pg(self): if self.poS_id: - self.dataPlot.plotWidget.scene().sigMouseClicked.disconnect(self.poS_id) + self.dataPlot.plotWidget.scene().sigMouseClicked.disconnect() def disconnect_mpl(self): if self.poS_id: @@ -1240,8 +1240,22 @@ class MainWindow(QMainWindow): self.scroll_id = None def finishWaveformDataPlot(self): - if pg: - self.getPlotWidget().updateWidget() + if self.pg: + self.getPlotWidget().updateWidget() + plots = self.wfp_thread.data + for times, data in plots: + self.dataPlot.plotWidget.getPlotItem().plot(times, data, pen='k') + self.dataPlot.reinitMoveProxy() + plotWidget = self.getPlotWidget() + plotDict = plotWidget.getPlotDict() + pos = plotDict.keys() + labels = [plotDict[n][2]+'.'+plotDict[n][0] for n in pos] + plotWidget.setYTickLabels(pos, labels) + try: + plotWidget.figure.tight_layout() + except: + pass + #self._max_xlims = self.dataPlot.getXLims() self.connectWFplotEvents() self.loadlocationaction.setEnabled(True) self.auto_tune.setEnabled(True) @@ -1266,7 +1280,7 @@ class MainWindow(QMainWindow): def clearWaveformDataPlot(self): self.disconnectWFplotEvents() if self.pg: - self.dataPlot.plotWidgetitem.clear() + self.dataPlot.plotWidget.getPlotItem().clear() else: self.dataPlot.getAxes().cla() self.loadlocationaction.setEnabled(False) @@ -1285,11 +1299,11 @@ class MainWindow(QMainWindow): ''' Open a modal thread to plot current waveform data. ''' - #self.plotWaveformData() - wfp_thread = Thread(self, self.plotWaveformData, - progressText='Plotting waveform data...') - wfp_thread.finished.connect(self.finishWaveformDataPlot) - wfp_thread.start() + self.clearWaveformDataPlot() + self.wfp_thread = Thread(self, self.plotWaveformData, + progressText='Plotting waveform data...') + self.wfp_thread.finished.connect(self.finishWaveformDataPlot) + self.wfp_thread.start() def plotWaveformData(self): ''' @@ -1307,16 +1321,8 @@ class MainWindow(QMainWindow): # wfst += self.get_data().getWFData().select(component=alter_comp) plotWidget = self.getPlotWidget() self.adjustPlotHeight() - plotWidget.plotWFData(wfdata=wfst, title=title, mapping=False, component=comp, nth_sample=int(nth_sample)) - plotDict = plotWidget.getPlotDict() - pos = plotDict.keys() - labels = [plotDict[n][2]+'.'+plotDict[n][0] for n in pos] - plotWidget.setYTickLabels(pos, labels) - try: - plotWidget.figure.tight_layout() - except: - pass - #self._max_xlims = self.dataPlot.getXLims() + plots = plotWidget.plotWFData(wfdata=wfst, title=title, mapping=False, component=comp, nth_sample=int(nth_sample)) + return plots def adjustPlotHeight(self): if self.pg: @@ -1331,20 +1337,20 @@ class MainWindow(QMainWindow): def plotZ(self): self.setComponent('Z') self.plotWaveformDataThread() - self.drawPicks() - self.draw() + # self.drawPicks() + # self.draw() def plotN(self): self.setComponent('N') self.plotWaveformDataThread() - self.drawPicks() - self.draw() + # self.drawPicks() + # self.draw() def plotE(self): self.setComponent('E') self.plotWaveformDataThread() - self.drawPicks() - self.draw() + # self.drawPicks() + # self.draw() def pushFilterWF(self, param_args): self.get_data().filterWFData(param_args) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 47c1eedd..df648b9f 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -a071-dirty +909e-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index c0b285eb..bb4bbbe8 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -418,6 +418,7 @@ class WaveformWidgetPG(QtGui.QWidget): self.main_layout.addWidget(self.plotWidget) #self.plotWidget.setMouseEnabled(False, False) self.plotWidget.showGrid(x=False, y=True, alpha=0.2) + self.reinitMoveProxy() self._proxy = pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) def reinitMoveProxy(self): @@ -455,7 +456,7 @@ class WaveformWidgetPG(QtGui.QWidget): noiselevel=None, scaleddata=False, mapping=True, component='*', nth_sample=1, iniPick=None): self.title = title - self.plotWidget.getPlotItem().clear() + #self.plotWidget.clear() self.clearPlotDict() wfstart, wfend = full_range(wfdata) nmax = 0 @@ -479,6 +480,7 @@ class WaveformWidgetPG(QtGui.QWidget): nsc.append((trace.stats.network, trace.stats.station, trace.stats.channel)) nsc.sort() nsc.reverse() + plots = [] for n, (network, station, channel) in enumerate(nsc): st = wfdata.select(network=network, station=station, channel=channel) @@ -499,23 +501,13 @@ class WaveformWidgetPG(QtGui.QWidget): trace.normalize(np.max(np.abs(trace.data)) * 2) times = [time for index, time in enumerate(time_ax) if not index%nth_sample] data = [datum + n for index, datum in enumerate(trace.data) if not index%nth_sample] - self.plotWidget.plot(times, data, pen=(0, 0, 0)) - if noiselevel is not None: - for level in noiselevel: - self.plotWidget.plot([time_ax[0], time_ax[-1]], - [level, level], pen=(0, 0, 0)) - # if iniPick: - # ax = self.getAxes() - # ax.vlines(iniPick, ax.get_ylim()[0], ax.get_ylim()[1], - # colors='m', linestyles='dashed', - # linewidth=2) + plots.append((times, data)) self.setPlotDict(n, (station, channel, network)) - self.reinitMoveProxy() self.xlabel = 'seconds since {0}'.format(wfstart) self.ylabel = '' self.setXLims([0, wfend - wfstart]) self.setYLims([-0.5, nmax + 0.5]) - self.draw() + return plots # def getAxes(self): # return self.axes From b061c81b2cc7de4b2e90a8da9d3d8f19f9bad661 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 8 Jun 2017 16:58:44 +0200 Subject: [PATCH 4/6] [change] removed axes on startup --- QtPyLoT.py | 22 ++++++++++++++-------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 23 ++++++++++++++++++----- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index cc00fc74..16039abd 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -214,7 +214,7 @@ class MainWindow(QMainWindow): self.tabs.currentChanged.connect(self.refreshTabs) # create central matplotlib figure canvas widget - plottitle = "Overview: {0} components ".format(self.getComponent()) + plottitle = None#"Overview: {0} components ".format(self.getComponent()) if pg: self.pg = True self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, @@ -1239,13 +1239,20 @@ class MainWindow(QMainWindow): self.ae_id = None self.scroll_id = None + def finish_pg_plot(self): + self.getPlotWidget().updateWidget() + plots = self.wfp_thread.data + for times, data in plots: + self.dataPlot.plotWidget.getPlotItem().plot(times, data, pen='k') + self.dataPlot.reinitMoveProxy() + self.dataPlot.plotWidget.showAxis('left') + self.dataPlot.plotWidget.showAxis('bottom') + def finishWaveformDataPlot(self): if self.pg: - self.getPlotWidget().updateWidget() - plots = self.wfp_thread.data - for times, data in plots: - self.dataPlot.plotWidget.getPlotItem().plot(times, data, pen='k') - self.dataPlot.reinitMoveProxy() + self.finish_pg_plot() + else: + self._max_xlims = self.dataPlot.getXLims() plotWidget = self.getPlotWidget() plotDict = plotWidget.getPlotDict() pos = plotDict.keys() @@ -1255,7 +1262,6 @@ class MainWindow(QMainWindow): plotWidget.figure.tight_layout() except: pass - #self._max_xlims = self.dataPlot.getXLims() self.connectWFplotEvents() self.loadlocationaction.setEnabled(True) self.auto_tune.setEnabled(True) @@ -1709,7 +1715,7 @@ class MainWindow(QMainWindow): fill = pg.FillBetweenItem(spe_l, spe_r, brush=colors[1].brush()) fb = pw.addItem(fill) except: - print('Could not create fill for SPE.') + print('Warning: drawPicks: Could not create fill for symmetric pick error.') pw.plot([mpp, mpp], ylims, pen=colors[2], name='{}-Pick'.format(phase)) else: pw.plot([mpp, mpp], ylims, pen=colors[0], name='{}-Pick (NO PICKERROR)'.format(phase)) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index df648b9f..603fa123 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -909e-dirty +d6dd-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index bb4bbbe8..ee426643 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -413,11 +413,14 @@ class WaveformWidgetPG(QtGui.QWidget): self.plotdict = dict() # create plot self.main_layout = QtGui.QVBoxLayout() + self.label = QtGui.QLabel() self.setLayout(self.main_layout) self.plotWidget = pg.PlotWidget(title=title, autoDownsample=True) self.main_layout.addWidget(self.plotWidget) - #self.plotWidget.setMouseEnabled(False, False) + self.main_layout.addWidget(self.label) self.plotWidget.showGrid(x=False, y=True, alpha=0.2) + self.plotWidget.hideAxis('bottom') + self.plotWidget.hideAxis('left') self.reinitMoveProxy() self._proxy = pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) @@ -431,9 +434,12 @@ class WaveformWidgetPG(QtGui.QWidget): pos = evt[0] ## using signal proxy turns original arguments into a tuple if self.plotWidget.sceneBoundingRect().contains(pos): mousePoint = self.plotWidget.getPlotItem().vb.mapSceneToView(pos) - # index = int(mousePoint.x()) - # if index > 0 and index < len(data1): - # label.setText("x=%0.1f, y1=%0.1f, y2=%0.1f" % (mousePoint.x(), data1[index], data2[index])) + x, y, = (mousePoint.x(), mousePoint.y()) + #if x > 0:# and index < len(data1): + wfID = self._parent.getWFID(y) + station = self._parent.getStationName(wfID) + if self._parent.get_current_event(): + self.label.setText("station = {}, t = {} [s]".format(station, x)) self.vLine.setPos(mousePoint.x()) self.hLine.setPos(mousePoint.y()) @@ -456,7 +462,6 @@ class WaveformWidgetPG(QtGui.QWidget): noiselevel=None, scaleddata=False, mapping=True, component='*', nth_sample=1, iniPick=None): self.title = title - #self.plotWidget.clear() self.clearPlotDict() wfstart, wfend = full_range(wfdata) nmax = 0 @@ -482,6 +487,14 @@ class WaveformWidgetPG(QtGui.QWidget): nsc.reverse() plots = [] + try: + self.plotWidget.getPlotItem().vb.setLimits(xMin=wfstart, + xMax=wfend, + yMin=-0.5, + yMax=len(nsc)+0.5) + except: + print('Warning: Could not set zoom limits') + for n, (network, station, channel) in enumerate(nsc): st = wfdata.select(network=network, station=station, channel=channel) trace = st[0] From 8e839df718bd6197e0b6c768bda4de0189da0e07 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 9 Jun 2017 13:23:16 +0200 Subject: [PATCH 5/6] [add] pyqtgraph now part of Settings object; if installed will be used by default but can be deactivated --- QtPyLoT.py | 51 ++++++++++++++++++++++---------------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 23 ++++++++++++++--- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 16039abd..1136a29d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -45,6 +45,7 @@ from obspy import UTCDateTime try: import pyqtgraph as pg except: + print('QtPyLoT: Could not import pyqtgraph.') pg = None try: @@ -144,9 +145,6 @@ class MainWindow(QMainWindow): self.dirty = False - # setup UI - self.setupUi() - if settings.value("user/FullName", None) is None: fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") settings.setValue("user/FullName", fulluser) @@ -170,6 +168,9 @@ class MainWindow(QMainWindow): settings.setValue('compclass', SetChannelComponents()) settings.sync() + # setup UI + self.setupUi() + self.filteroptions = {} self.pickDlgs = {} self.picks = {} @@ -189,8 +190,6 @@ class MainWindow(QMainWindow): self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(pylot_icon) - xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') - _widget = QWidget() self._main_layout = QVBoxLayout() @@ -213,21 +212,13 @@ class MainWindow(QMainWindow): self._main_layout.addWidget(self.tabs) self.tabs.currentChanged.connect(self.refreshTabs) - # create central matplotlib figure canvas widget - plottitle = None#"Overview: {0} components ".format(self.getComponent()) - if pg: - self.pg = True - self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, - title=plottitle) - else: - self.pg = False - self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, - title=plottitle) - self.dataPlot.setCursor(Qt.CrossCursor) - # add scroll area used in case number of traces gets too high self.wf_scroll_area = QtGui.QScrollArea() + # create central matplotlib figure canvas widget + self.pg = pg + self.init_wfWidget() + # init main widgets for main tabs wf_tab = QtGui.QWidget() array_tab = QtGui.QWidget() @@ -247,7 +238,6 @@ class MainWindow(QMainWindow): self.tabs.addTab(events_tab, 'Eventlist') self.wf_layout.addWidget(self.wf_scroll_area) - self.wf_scroll_area.setWidget(self.dataPlot) self.wf_scroll_area.setWidgetResizable(True) self.init_array_tab() self.init_event_table() @@ -521,6 +511,24 @@ class MainWindow(QMainWindow): self.setCentralWidget(_widget) + def init_wfWidget(self): + settings = QSettings() + xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') + plottitle = None#"Overview: {0} components ".format(self.getComponent()) + self.disconnectWFplotEvents() + if str(settings.value('pyqtgraphic')) == 'false' or not pg: + self.pg = False + self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) + else: + self.pg = True + self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) + self.dataPlot.setCursor(Qt.CrossCursor) + self.wf_scroll_area.setWidget(self.dataPlot) + if self.get_current_event(): + self.plotWaveformDataThread() + def init_ref_test_buttons(self): ''' Initiate/create buttons for assigning events containing manual picks to reference or test set. @@ -1662,12 +1670,12 @@ class MainWindow(QMainWindow): plotID = self.getStationID(station) if plotID is None: return - if pg: + if self.pg: pw = self.getPlotWidget().plotWidget else: ax = self.getPlotWidget().axes ylims = np.array([-.5, +.5]) + plotID - if pg: + if self.pg: dashed = QtCore.Qt.DashLine dotted = QtCore.Qt.DotLine phase_col = { @@ -1699,7 +1707,7 @@ class MainWindow(QMainWindow): if not spe and epp and lpp: spe = symmetrize_error(mpp - epp, lpp - mpp) - if pg: + if self.pg: if picktype == 'manual': if picks['epp'] and picks['lpp']: pw.plot([epp, epp], ylims, @@ -2252,6 +2260,7 @@ class MainWindow(QMainWindow): self._props = PropertiesDlg(self, infile=self.infile) if self._props.exec_(): + self.init_wfWidget() return def helpHelp(self): diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 603fa123..dc5ac6e8 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -d6dd-dirty +b061-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index ee426643..33f4a79c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2373,6 +2373,7 @@ class PropTab(QWidget): def resetValues(self, infile=None): return None + class InputsTab(PropTab): def __init__(self, parent, infile=None): @@ -2484,6 +2485,7 @@ class GraphicsTab(PropTab): def __init__(self, parent=None): super(GraphicsTab, self).__init__(parent) self.init_layout() + self.add_pg_cb() self.add_nth_sample() self.setLayout(self.main_layout) @@ -2498,15 +2500,28 @@ class GraphicsTab(PropTab): self.spinbox_nth_sample = QtGui.QSpinBox() label = QLabel('nth sample') + label.setToolTip('Plot every nth sample (to speed up plotting)') self.spinbox_nth_sample.setMinimum(1) self.spinbox_nth_sample.setMaximum(10e3) self.spinbox_nth_sample.setValue(int(nth_sample)) - label.setToolTip('Plot every nth sample (to speed up plotting)') - self.main_layout.addWidget(label, 0, 0) - self.main_layout.addWidget(self.spinbox_nth_sample, 0, 1) + self.main_layout.addWidget(label, 1, 0) + self.main_layout.addWidget(self.spinbox_nth_sample, 1, 1) + def add_pg_cb(self): + text = {True: 'Use pyqtgraphic library for plotting', + False: 'Cannot use library: pyqtgraphic not found on system'} + label = QLabel('PyQt graphic') + label.setToolTip(text[bool(pg)]) + label.setEnabled(bool(pg)) + self.checkbox_pg = QtGui.QCheckBox() + self.checkbox_pg.setEnabled(bool(pg)) + self.checkbox_pg.setChecked(bool(pg)) + self.main_layout.addWidget(label, 0, 0) + self.main_layout.addWidget(self.checkbox_pg, 0, 1) + def getValues(self): - values = {'nth_sample': self.spinbox_nth_sample.value()} + values = {'nth_sample': self.spinbox_nth_sample.value(), + 'pyqtgraphic': self.checkbox_pg.isChecked()} return values From 01c556562e617431f338ca3e6450ae3fb4e485c2 Mon Sep 17 00:00:00 2001 From: marcel Date: Fri, 9 Jun 2017 13:51:20 +0200 Subject: [PATCH 6/6] [bugfix] newer version of pyqtgraph could not cope with UTCDatetime, now min/max_range for axes working --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index dc5ac6e8..9515eb1e 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -b061-dirty +8e83-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 33f4a79c..60257c90 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -488,8 +488,8 @@ class WaveformWidgetPG(QtGui.QWidget): plots = [] try: - self.plotWidget.getPlotItem().vb.setLimits(xMin=wfstart, - xMax=wfend, + self.plotWidget.getPlotItem().vb.setLimits(xMin=float(0), + xMax=float(wfend-wfstart), yMin=-0.5, yMax=len(nsc)+0.5) except: @@ -533,11 +533,11 @@ class WaveformWidgetPG(QtGui.QWidget): def setXLims(self, lims): vb = self.plotWidget.getPlotItem().getViewBox() - vb.setXRange(lims[0], lims[1], padding=0) + vb.setXRange(float(lims[0]), float(lims[1]), padding=0) def setYLims(self, lims): vb = self.plotWidget.getPlotItem().getViewBox() - vb.setYRange(lims[0], lims[1], padding=0) + vb.setYRange(float(lims[0]), float(lims[1]), padding=0) def setYTickLabels(self, pos, labels): ticks = zip(pos, labels)