3170 lines
114 KiB
Python
3170 lines
114 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Created on Wed Mar 19 11:27:35 2014
|
|
|
|
@author: sebastianw
|
|
"""
|
|
|
|
import os
|
|
import getpass
|
|
import warnings
|
|
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
|
|
|
|
try:
|
|
from matplotlib.backends.backend_qt4agg import FigureCanvas
|
|
except ImportError:
|
|
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
|
|
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT
|
|
from matplotlib.widgets import MultiCursor
|
|
from PySide import QtCore, QtGui
|
|
from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \
|
|
QDateTimeEdit, QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, \
|
|
QGridLayout, QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, \
|
|
QPixmap, QSpinBox, QTabWidget, QToolBar, QVBoxLayout, QWidget, \
|
|
QPushButton, QFileDialog, QInputDialog, QKeySequence
|
|
from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot
|
|
from PySide.QtWebKit import QWebView
|
|
from obspy import Stream, UTCDateTime
|
|
from obspy.core.util import AttribDict
|
|
from pylot.core.io.data import Data
|
|
from pylot.core.io.inputs import FilterOptions, PylotParameter
|
|
from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \
|
|
getResolutionWindow
|
|
from pylot.core.pick.compare import Comparison
|
|
from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, LOCTOOLS, SetChannelComponents
|
|
from pylot.core.util.utils import prepTimeAxis, full_range, scaleWFData, \
|
|
demeanTrace, isSorted, findComboBoxIndex, clims
|
|
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"])
|
|
|
|
if type[0].startswith('auto'):
|
|
type = 'auto'
|
|
else:
|
|
type = type[0]
|
|
|
|
return type
|
|
|
|
def plot_pdf(_axes, x, y, annotation, bbox_props, xlabel=None, ylabel=None,
|
|
title=None):
|
|
# try method or data
|
|
try:
|
|
_axes.plot(x, y()) # y provided as method
|
|
except:
|
|
_axes.plot(x, y) # y provided as data
|
|
|
|
if title:
|
|
_axes.set_title(title)
|
|
if xlabel:
|
|
_axes.set_xlabel(xlabel)
|
|
if ylabel:
|
|
_axes.set_ylabel(ylabel)
|
|
_anno = _axes.annotate(annotation, xy=(.05, .5), xycoords='axes fraction')
|
|
_anno.set_bbox(bbox_props)
|
|
|
|
return _axes
|
|
|
|
def createAction(parent, text, slot=None, shortcut=None, icon=None,
|
|
tip=None, checkable=False):
|
|
"""
|
|
:rtype : ~PySide.QtGui.QAction
|
|
"""
|
|
action = QAction(text, parent)
|
|
if icon is not None:
|
|
action.setIcon(icon)
|
|
if shortcut is not None:
|
|
action.setShortcut(shortcut)
|
|
if tip is not None:
|
|
action.setToolTip(tip)
|
|
if slot is not None:
|
|
action.triggered.connect(slot)
|
|
if checkable:
|
|
action.setCheckable(True)
|
|
return action
|
|
|
|
class ComparisonDialog(QDialog):
|
|
def __init__(self, c, parent=None):
|
|
self._data = c
|
|
self._stats = c.stations
|
|
self._canvas = PlotWidget(self)
|
|
self._widgets = dict(stationsComboBox=None,
|
|
phasesComboBox=None,
|
|
histCheckBox=None)
|
|
self._phases = 'PS'
|
|
self._plotprops = dict(station=self.stations[0], phase=self.phases[0])
|
|
super(ComparisonDialog, self).__init__(parent)
|
|
self.setupUI()
|
|
self.plotcomparison()
|
|
|
|
def setupUI(self):
|
|
|
|
_outerlayout = QVBoxLayout(self)
|
|
_innerlayout = QVBoxLayout()
|
|
|
|
_stats_combobox = QComboBox(self)
|
|
_stats_combobox.setObjectName('stationsComboBox')
|
|
_stats_combobox.setEditable(True)
|
|
_stats_combobox.setInsertPolicy(QComboBox.NoInsert)
|
|
_stats_combobox.addItems(sorted(self.stations))
|
|
_stats_combobox.editTextChanged.connect(self.prepareplot)
|
|
self.widgets = _stats_combobox
|
|
|
|
_phases_combobox = QComboBox(self)
|
|
_phases_combobox.setObjectName('phasesComboBox')
|
|
_phases_combobox.addItems(['P', 'S'])
|
|
_phases_combobox.currentIndexChanged.connect(self.prepareplot)
|
|
self.widgets = _phases_combobox
|
|
|
|
_hist_checkbox = QCheckBox('Show histograms', self)
|
|
_hist_checkbox.setObjectName('histCheckBox')
|
|
_hist_checkbox.stateChanged.connect(self.plothist)
|
|
self.widgets = _hist_checkbox
|
|
|
|
_toolbar = QToolBar(self)
|
|
_toolbar.addWidget(_stats_combobox)
|
|
_toolbar.addWidget(_phases_combobox)
|
|
_toolbar.addWidget(_hist_checkbox)
|
|
|
|
_buttonbox = QDialogButtonBox(QDialogButtonBox.Close)
|
|
|
|
_innerlayout.addWidget(self.canvas)
|
|
_innerlayout.addWidget(_buttonbox)
|
|
|
|
_outerlayout.addWidget(_toolbar)
|
|
_outerlayout.addLayout(_innerlayout)
|
|
|
|
_buttonbox.rejected.connect(self.reject)
|
|
|
|
# finally layout the entire dialog
|
|
self.setLayout(_outerlayout)
|
|
|
|
@property
|
|
def canvas(self):
|
|
return self._canvas
|
|
|
|
@canvas.setter
|
|
def canvas(self, canvas_obj):
|
|
self._canvas = canvas_obj
|
|
|
|
@property
|
|
def stations(self):
|
|
return self._stats
|
|
|
|
@stations.setter
|
|
def stations(self, stations):
|
|
self._stats = stations
|
|
|
|
@property
|
|
def phases(self):
|
|
return self._phases
|
|
|
|
@phases.setter
|
|
def phases(self, value):
|
|
self._phases = value
|
|
|
|
@property
|
|
def plotprops(self):
|
|
return self._plotprops
|
|
|
|
@plotprops.setter
|
|
def plotprops(self, values):
|
|
try:
|
|
key, value = values
|
|
if key not in self.plotprops.keys():
|
|
raise KeyError("'key' {0} not found in "
|
|
"ComparisonDialog.plotprops keys.".format(key))
|
|
except ValueError:
|
|
raise ValueError("Pass an iterable with two items")
|
|
else:
|
|
self._plotprops[key] = value
|
|
|
|
@property
|
|
def data(self):
|
|
return self._data
|
|
|
|
@data.setter
|
|
def data(self, data):
|
|
assert not isinstance(data, Comparison)
|
|
self.stations = data.stations
|
|
self._data = data
|
|
|
|
@property
|
|
def widgets(self):
|
|
return self._widgets
|
|
|
|
@widgets.setter
|
|
def widgets(self, widget):
|
|
name = widget.objectName()
|
|
if name in self.widgets.keys():
|
|
self._widgets[name] = widget
|
|
|
|
def clf(self):
|
|
self.canvas.figure.clf()
|
|
|
|
def hasvalue(self, sender):
|
|
text = sender.currentText()
|
|
index = sender.findText(text.upper())
|
|
return index
|
|
|
|
def prepareplot(self):
|
|
try:
|
|
_widget = self.sender()
|
|
name = _widget.objectName()
|
|
text = _widget.currentText().upper()
|
|
index = self.hasvalue(_widget)
|
|
if name == 'stationsComboBox' and index is not -1:
|
|
_widget.setCurrentIndex(index)
|
|
self.plotprops = ('station', text)
|
|
elif name == 'phasesComboBox':
|
|
self.plotprops = ('phase', text)
|
|
except ValueError:
|
|
raise ValueError('No sender widget given!')
|
|
finally:
|
|
self.plotcomparison()
|
|
|
|
def plotcomparison(self):
|
|
from matplotlib import gridspec
|
|
|
|
_gs = gridspec.GridSpec(3, 2)
|
|
self.clf()
|
|
_axes = self.canvas.figure.add_subplot(_gs[0:2, :])
|
|
_ax1 = self.canvas.figure.add_subplot(_gs[2, 0])
|
|
_ax2 = self.canvas.figure.add_subplot(_gs[2, 1])
|
|
|
|
#_axes.cla()
|
|
station = self.plotprops['station']
|
|
phase = self.plotprops['phase']
|
|
pdf = self.data.comparison[station][phase]
|
|
x, y, std, exp = pdf.axis, pdf.data, pdf.standard_deviation(), \
|
|
pdf.expectation()
|
|
|
|
annotation = "%s difference on %s\n" \
|
|
"expectation: %7.4f s\n" \
|
|
"std: %7.4f s" % (phase, station,
|
|
exp, std)
|
|
bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7)
|
|
|
|
plot_pdf(_axes, x, y, annotation, bbox_props, 'time difference [s]',
|
|
'propability density [-]', phase)
|
|
|
|
pdf_a = copy.deepcopy(self.data.get('auto')[station][phase])
|
|
pdf_m = copy.deepcopy(self.data.get('manu')[station][phase])
|
|
|
|
xauto, yauto, stdauto, expauto, alim = pdf_a.axis, pdf_a.data(), \
|
|
pdf_a.standard_deviation(), \
|
|
pdf_a.expectation(), \
|
|
pdf_a.limits()
|
|
xmanu, ymanu, stdmanu, expmanu, mlim = pdf_m.axis, pdf_m.data(), \
|
|
pdf_m.standard_deviation(), \
|
|
pdf_m.expectation(), \
|
|
pdf_m.limits()
|
|
# find common limits
|
|
lims = clims(alim, mlim)
|
|
# relative x axis
|
|
x0 = lims[0]
|
|
xmanu -= x0
|
|
xauto -= x0
|
|
lims = [lim - x0 for lim in lims]
|
|
x0 = UTCDateTime(x0)
|
|
|
|
# set annotation text
|
|
mannotation = "probability density of manual pick\n" \
|
|
"expectation: %7.4f s\n" \
|
|
"std: %7.4f s" % (expmanu-x0.timestamp, stdmanu)
|
|
|
|
aannotation = "probability density of automatic pick\n" \
|
|
"expectation: %7.4f s\n" \
|
|
"std: %7.4f s" % (expauto-x0.timestamp, stdauto)
|
|
|
|
_ax1 = plot_pdf(_ax1, xmanu, ymanu, mannotation,
|
|
bbox_props=bbox_props, xlabel='seconds since '
|
|
'{0}'.format(x0),
|
|
ylabel='probability density [-]')
|
|
_ax1.set_xlim(lims)
|
|
|
|
_ax2 = plot_pdf(_ax2, xauto, yauto, aannotation,
|
|
bbox_props=bbox_props, xlabel='seconds since '
|
|
'{0}'.format(x0))
|
|
_ax2.set_xlim(lims)
|
|
|
|
_gs.update(wspace=0.5, hspace=0.5)
|
|
|
|
self.canvas.draw()
|
|
|
|
def plothist(self):
|
|
name = self.sender().objectName()
|
|
if self.widgets[name].isChecked():
|
|
for wname, widget in self.widgets.items():
|
|
if wname != name:
|
|
self.widgets[wname].setEnabled(False)
|
|
self.canvas.figure.clf()
|
|
_axPstd, _axPexp = self.canvas.figure.add_subplot(221), self.canvas.figure.add_subplot(223)
|
|
_axSstd, _axSexp = self.canvas.figure.add_subplot(222), self.canvas.figure.add_subplot(224)
|
|
axes_dict = dict(P=dict(std=_axPstd, exp=_axPexp),
|
|
S=dict(std=_axSstd, exp=_axSexp))
|
|
bbox_props = dict(boxstyle='round', facecolor='lightgrey', alpha=.7)
|
|
for phase in self.phases:
|
|
std = self.data.get_std_array(phase)
|
|
std = std[np.isfinite(std)]
|
|
stdxlims = [0., 1.2 * max(std)]
|
|
exp = self.data.get_expectation_array(phase)
|
|
exp = exp[np.isfinite(exp)]
|
|
eps_exp = 0.05 * (max(exp) - min(exp))
|
|
expxlims = [min(exp) - eps_exp, max(exp) + eps_exp]
|
|
axes_dict[phase]['std'].hist(std, range=stdxlims, bins=20, normed=False)
|
|
axes_dict[phase]['exp'].hist(exp, range=expxlims, bins=20,
|
|
normed=False)
|
|
std_annotation = "Distribution curve for {phase} differences'\n" \
|
|
"standard deviations (all stations)\n" \
|
|
"number of samples: {nsamples}".format(phase=phase, nsamples=len(std))
|
|
_anno_std = axes_dict[phase]['std'].annotate(std_annotation, xy=(.05, .8), xycoords='axes fraction')
|
|
_anno_std.set_bbox(bbox_props)
|
|
exp_annotation = "Distribution curve for {phase} differences'\n" \
|
|
"expectations (all stations)\n" \
|
|
"number of samples: {nsamples}".format(phase=phase, nsamples=len(exp))
|
|
_anno_exp = axes_dict[phase]['exp'].annotate(exp_annotation, xy=(.05, .8), xycoords='axes fraction')
|
|
_anno_exp.set_bbox(bbox_props)
|
|
axes_dict[phase]['exp'].set_xlabel('expectation [s]')
|
|
axes_dict[phase]['std'].set_xlabel('standard deviation [s]')
|
|
|
|
for ax in axes_dict['P'].values():
|
|
ax.set_ylabel('frequency [-]')
|
|
|
|
self.canvas.draw()
|
|
else:
|
|
for wname, widget in self.widgets.items():
|
|
if wname != name:
|
|
self.widgets[wname].setEnabled(True)
|
|
self.canvas.figure.clf()
|
|
self.plotcomparison()
|
|
|
|
|
|
class PlotWidget(FigureCanvas):
|
|
def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'):
|
|
self._parent = parent
|
|
self._fig = Figure()
|
|
self._xl = xlabel
|
|
self._yl = ylabel
|
|
self._title = title
|
|
super(PlotWidget, self).__init__(self.figure)
|
|
|
|
@property
|
|
def figure(self):
|
|
return self._fig
|
|
|
|
@figure.setter
|
|
def figure(self, fig):
|
|
self._fig = fig
|
|
|
|
@property
|
|
def xlabel(self):
|
|
return self._xl
|
|
|
|
@xlabel.setter
|
|
def xlabel(self, label):
|
|
self._xl = label
|
|
|
|
@property
|
|
def ylabel(self):
|
|
return self._yl
|
|
|
|
@ylabel.setter
|
|
def ylabel(self, label):
|
|
self._yl = label
|
|
|
|
@property
|
|
def title(self):
|
|
return self._title
|
|
|
|
@title.setter
|
|
def title(self, title):
|
|
self._title = title
|
|
|
|
@property
|
|
def parent(self):
|
|
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.label = QtGui.QLabel()
|
|
self.setLayout(self.main_layout)
|
|
self.plotWidget = pg.PlotWidget(title=title, autoDownsample=True)
|
|
self.main_layout.addWidget(self.plotWidget)
|
|
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)
|
|
|
|
def reinitMoveProxy(self):
|
|
self.vLine = pg.InfiniteLine(angle=90, movable=False)
|
|
self.hLine = pg.InfiniteLine(angle=0, movable=False)
|
|
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.plotWidget.sceneBoundingRect().contains(pos):
|
|
mousePoint = self.plotWidget.getPlotItem().vb.mapSceneToView(pos)
|
|
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())
|
|
|
|
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.title = title
|
|
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])
|
|
|
|
st_select = wfdata.select(component=component)
|
|
st_select += wfdata.select(component=alter_comp)
|
|
else:
|
|
st_select = wfdata
|
|
|
|
# list containing tuples of network, station, channel (for sorting)
|
|
nsc = []
|
|
for trace in st_select:
|
|
nsc.append((trace.stats.network, trace.stats.station, trace.stats.channel))
|
|
nsc.sort()
|
|
nsc.reverse()
|
|
plots = []
|
|
|
|
try:
|
|
self.plotWidget.getPlotItem().vb.setLimits(xMin=float(0),
|
|
xMax=float(wfend-wfstart),
|
|
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 = st_select.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]
|
|
plots.append((times, data))
|
|
self.setPlotDict(n, (station, channel, network))
|
|
self.xlabel = 'seconds since {0}'.format(wfstart)
|
|
self.ylabel = ''
|
|
self.setXLims([0, wfend - wfstart])
|
|
self.setYLims([-0.5, nmax + 0.5])
|
|
return plots
|
|
|
|
# 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):
|
|
vb = self.plotWidget.getPlotItem().getViewBox()
|
|
vb.setXRange(float(lims[0]), float(lims[1]), padding=0)
|
|
|
|
def setYLims(self, lims):
|
|
vb = self.plotWidget.getPlotItem().getViewBox()
|
|
vb.setYRange(float(lims[0]), float(lims[1]), padding=0)
|
|
|
|
def setYTickLabels(self, pos, labels):
|
|
ticks = zip(pos, labels)
|
|
minorTicks = [(0, 0) for item in labels]
|
|
# leftAx.tickLength = 5
|
|
# leftAx.orientation = 'right'
|
|
self.getAxItem('left').setTicks([ticks, minorTicks])
|
|
|
|
def updateXLabel(self, text):
|
|
self.getAxItem('bottom').setLabel(text)
|
|
self.draw()
|
|
|
|
def updateYLabel(self, text):
|
|
self.getAxItem('left').setLabel(text)
|
|
self.draw()
|
|
|
|
def getAxItem(self, position):
|
|
return self.plotWidget.getPlotItem().axes[position]['item']
|
|
|
|
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 draw(self):
|
|
pass
|
|
|
|
|
|
class WaveformWidget(FigureCanvas):
|
|
def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'):
|
|
|
|
self._parent = None
|
|
self.setParent(parent)
|
|
self.figure = Figure()
|
|
self.figure.set_facecolor((.92, .92, .92))
|
|
# attribute plotdict is a dictionary connecting position and a name
|
|
self.plotdict = dict()
|
|
# create axes
|
|
self.axes = self.figure.add_subplot(111)
|
|
# initialize super class
|
|
super(WaveformWidget, self).__init__(self.figure)
|
|
# add an cursor for station selection
|
|
self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,),
|
|
horizOn=True, useblit=True,
|
|
color='m', lw=1)
|
|
# update labels of the entire widget
|
|
self.updateWidget(xlabel, ylabel, title)
|
|
|
|
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])
|
|
|
|
st_select = wfdata.select(component=component)
|
|
st_select += wfdata.select(component=alter_comp)
|
|
else:
|
|
st_select = wfdata
|
|
|
|
# list containing tuples of network, station, channel (for sorting)
|
|
nsc = []
|
|
for trace in st_select:
|
|
nsc.append((trace.stats.network, trace.stats.station, trace.stats.channel))
|
|
nsc.sort()
|
|
nsc.reverse()
|
|
|
|
for n, (network, station, channel) in enumerate(nsc):
|
|
st = st_select.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.getAxes().plot(times, data, 'k')
|
|
if noiselevel is not None:
|
|
for level in noiselevel:
|
|
self.getAxes().plot([time_ax[0], time_ax[-1]],
|
|
[level, level], '--k')
|
|
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):
|
|
self.getAxes().set_yticks(pos)
|
|
self.getAxes().set_yticklabels(labels)
|
|
self.draw()
|
|
|
|
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, verticalalignment='bottom')
|
|
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))
|
|
|
|
|
|
class PickDlg(QDialog):
|
|
update_picks = QtCore.Signal(dict)
|
|
def __init__(self, parent=None, data=None, station=None, network=None, picks=None,
|
|
autopicks=None, rotate=False, parameter=None, embedded=False):
|
|
super(PickDlg, self).__init__(parent)
|
|
|
|
# initialize attributes
|
|
self.parameter = parameter
|
|
self._embedded = embedded
|
|
self.station = station
|
|
self.network = network
|
|
self.rotate = rotate
|
|
self.components = 'ZNE'
|
|
self.currentPhase = None
|
|
settings = QSettings()
|
|
pylot_user = getpass.getuser()
|
|
self._user = settings.value('user/Login', pylot_user)
|
|
if picks:
|
|
self.picks = picks
|
|
self._init_picks = copy.deepcopy(picks)
|
|
else:
|
|
self.picks = {}
|
|
self._init_picks = {}
|
|
if autopicks:
|
|
self.autopicks = autopicks
|
|
self._init_autopicks = copy.deepcopy(autopicks)
|
|
else:
|
|
self.autopicks = {}
|
|
self._init_autopicks = {}
|
|
self.filteroptions = FILTERDEFAULTS
|
|
self.pick_block = False
|
|
self.nextStation = QtGui.QCheckBox('Continue with next station.')
|
|
|
|
# initialize panning attributes
|
|
self.press = None
|
|
self.xpress = None
|
|
self.ypress = None
|
|
self.cur_xlim = None
|
|
self.cur_ylim = None
|
|
|
|
# set attribute holding data
|
|
if data is None:
|
|
try:
|
|
data = parent.get_data().getWFData().copy()
|
|
self.data = data.select(station=station)
|
|
except AttributeError as e:
|
|
errmsg = 'You either have to put in a data or an appropriate ' \
|
|
'parent (PyLoT MainWindow) object: {0}'.format(e)
|
|
raise Exception(errmsg)
|
|
else:
|
|
self.data = data
|
|
|
|
self.stime, self.etime = full_range(self.getWFData())
|
|
|
|
# initialize plotting widget
|
|
self.multicompfig = WaveformWidget(self)
|
|
|
|
# setup ui
|
|
self.setupUi()
|
|
|
|
# plot data
|
|
self.getPlotWidget().plotWFData(wfdata=self.getWFData(),
|
|
title=self.getStation())
|
|
|
|
xlims = self.getPlotWidget().getXLims()
|
|
ylims = self.getPlotWidget().getYLims()
|
|
|
|
self.limits = {'x': xlims,
|
|
'y': ylims}
|
|
|
|
self.updateCurrentLimits()
|
|
|
|
# set plot labels
|
|
self.setPlotLabels()
|
|
|
|
# draw picks if present
|
|
self.drawAllPicks()
|
|
|
|
# connect button press event to an action
|
|
self.cidpress = self.connectPressEvent(self.panPress)
|
|
self.cidmotion = self.connectMotionEvent(self.panMotion)
|
|
self.cidrelease = self.connectReleaseEvent(self.panRelease)
|
|
self.cidscroll = self.connectScrollEvent(self.scrollZoom)
|
|
|
|
def setupUi(self):
|
|
menuBar = QtGui.QMenuBar(self)
|
|
exitMenu = menuBar.addMenu('File')
|
|
|
|
self.addPickPhases(menuBar)
|
|
|
|
exitAction = QtGui.QAction('Close', self)
|
|
exitAction.triggered.connect(self.close)
|
|
exitMenu.addAction(exitAction)
|
|
|
|
# create matplotlib toolbar to inherit functionality
|
|
self.figToolBar = NavigationToolbar2QT(self.getPlotWidget(), self)
|
|
self.figToolBar.hide()
|
|
|
|
# create icons
|
|
filter_icon = QIcon()
|
|
filter_icon.addPixmap(QPixmap(':/icons/filter.png'))
|
|
zoom_icon = QIcon()
|
|
zoom_icon.addPixmap(QPixmap(':/icons/zoom_in.png'))
|
|
home_icon = QIcon()
|
|
home_icon.addPixmap(QPixmap(':/icons/zoom_0.png'))
|
|
del_icon = QIcon()
|
|
del_icon.addPixmap(QPixmap(':/icons/delete.png'))
|
|
|
|
# create actions
|
|
self.filterAction = createAction(parent=self, text='Filter',
|
|
slot=self.filterWFData,
|
|
icon=filter_icon,
|
|
tip='Toggle filtered/original'
|
|
' waveforms')
|
|
self.zoomAction = createAction(parent=self, text='Zoom',
|
|
slot=self.zoom, icon=zoom_icon,
|
|
tip='Zoom into waveform',
|
|
checkable=True)
|
|
self.resetZoomAction = createAction(parent=self, text='Home',
|
|
slot=self.resetZoom, icon=home_icon,
|
|
tip='Reset zoom to original limits')
|
|
self.resetPicksAction = createAction(parent=self, text='Delete Picks',
|
|
slot=self.delPicks, icon=del_icon,
|
|
tip='Delete current picks.')
|
|
|
|
# create other widget elements
|
|
phaseitems = [None] + FILTERDEFAULTS.keys()
|
|
|
|
# create buttons for P and S filter and picking
|
|
self.p_button = QPushButton('P', self)
|
|
self.s_button = QPushButton('S', self)
|
|
self.p_button.setCheckable(True)
|
|
self.s_button.setCheckable(True)
|
|
# set button tooltips
|
|
# self.p_button.setToolTip('Hotkey: "1"')
|
|
# self.s_button.setToolTip('Hotkey: "2"')
|
|
|
|
# create accept/reject button
|
|
self.accept_button = QPushButton('&Accept Picks')
|
|
self.reject_button = QPushButton('&Reject Picks')
|
|
self.disable_ar_buttons()
|
|
|
|
# add hotkeys
|
|
self._shortcut_space = QtGui.QShortcut(QtGui.QKeySequence(' '), self)
|
|
self._shortcut_space.activated.connect(self.accept_button.clicked)
|
|
# button shortcuts (1 for P-button, 2 for S-button)
|
|
# self.p_button.setShortcut(QKeySequence('1'))
|
|
# self.s_button.setShortcut(QKeySequence('2'))
|
|
|
|
# layout the outermost appearance of the Pick Dialog
|
|
_outerlayout = QVBoxLayout()
|
|
_dialtoolbar = QToolBar()
|
|
|
|
# fill toolbar with content
|
|
_dialtoolbar.addAction(self.filterAction)
|
|
_dialtoolbar.addWidget(self.p_button)
|
|
_dialtoolbar.addWidget(self.s_button)
|
|
_dialtoolbar.addAction(self.zoomAction)
|
|
_dialtoolbar.addSeparator()
|
|
_dialtoolbar.addAction(self.resetZoomAction)
|
|
_dialtoolbar.addSeparator()
|
|
_dialtoolbar.addAction(self.resetPicksAction)
|
|
if self._embedded:
|
|
_dialtoolbar.addWidget(self.accept_button)
|
|
_dialtoolbar.addWidget(self.reject_button)
|
|
else:
|
|
_dialtoolbar.addWidget(self.nextStation)
|
|
|
|
# layout the innermost widget
|
|
_innerlayout = QVBoxLayout()
|
|
_innerlayout.addWidget(self.multicompfig)
|
|
|
|
# add button box to the dialog
|
|
_buttonbox = QDialogButtonBox(QDialogButtonBox.Ok |
|
|
QDialogButtonBox.Cancel)
|
|
|
|
# merge widgets and layouts to establish the dialog
|
|
if not self._embedded:
|
|
_innerlayout.addWidget(_buttonbox)
|
|
_outerlayout.addWidget(menuBar)
|
|
_outerlayout.addWidget(_dialtoolbar)
|
|
_outerlayout.addLayout(_innerlayout)
|
|
_outerlayout.setStretch(0, 0)
|
|
_outerlayout.setStretch(1, 1)
|
|
_outerlayout.setStretch(2, 100)
|
|
|
|
# connect widget element signals with slots (methods to the dialog
|
|
# object
|
|
self.p_button.clicked.connect(self.p_clicked)
|
|
self.s_button.clicked.connect(self.s_clicked)
|
|
self.accept_button.clicked.connect(self.accept)
|
|
self.reject_button.clicked.connect(self.reject)
|
|
self.accept_button.clicked.connect(self.disable_ar_buttons)
|
|
self.reject_button.clicked.connect(self.disable_ar_buttons)
|
|
_buttonbox.accepted.connect(self.accept)
|
|
_buttonbox.rejected.connect(self.reject)
|
|
|
|
# finally layout the entire dialog
|
|
self.setLayout(_outerlayout)
|
|
self.resize(1280, 720)
|
|
|
|
def addPickPhases(self, menuBar):
|
|
settings = QtCore.QSettings()
|
|
phases = {'P': settings.value('p_phases').split(','),
|
|
'S': settings.value('s_phases').split(',')}
|
|
if not 'P' in phases['P'] and not 'p' in phases['P']:
|
|
phases['P'] = ['P'] + phases['P']
|
|
if not 'S' in phases['S'] and not 's' in phases['S']:
|
|
phases['S'] = ['S'] + phases['S']
|
|
|
|
picksMenu = menuBar.addMenu('Picks')
|
|
self.picksActions = {}
|
|
|
|
# dictionary points on corresponding phase_select function
|
|
phaseSelect = {'P': self.p_phase_select,
|
|
'S': self.s_phase_select}
|
|
|
|
nHotkey = 4 # max hotkeys per phase
|
|
hotkey = 1 # start hotkey
|
|
|
|
# loop over P and S (use explicit list instead of iter over dict.keys to keep order)
|
|
for phaseIndex, phaseID in enumerate(['P', 'S']):
|
|
# loop through phases in list
|
|
for index, phase in enumerate(phases[phaseID]):
|
|
# remove zeros
|
|
phase = phase.strip()
|
|
# add hotkeys
|
|
if not index >= nHotkey:
|
|
shortcut = str(hotkey)
|
|
hotkey += 1
|
|
else:
|
|
shortcut = None
|
|
# create action and add to menu
|
|
# phase name transferred using lambda function
|
|
slot = lambda phase=phase, phaseID=phaseID: phaseSelect[phaseID](phase)
|
|
picksAction = createAction(parent=self, text=phase,
|
|
slot=slot,
|
|
shortcut=shortcut)
|
|
picksMenu.addAction(picksAction)
|
|
self.picksActions[str(phase)] = picksAction # save action in dictionary
|
|
if phaseIndex == 0:
|
|
picksMenu.addSeparator()
|
|
|
|
def disconnectPressEvent(self):
|
|
widget = self.getPlotWidget()
|
|
widget.mpl_disconnect(self.cidpress)
|
|
self.cidpress = None
|
|
|
|
def connectPressEvent(self, slot):
|
|
widget = self.getPlotWidget()
|
|
return widget.mpl_connect('button_press_event', slot)
|
|
|
|
def disconnectScrollEvent(self):
|
|
widget = self.getPlotWidget()
|
|
widget.mpl_disconnect(self.cidscroll)
|
|
self.cidscroll = None
|
|
|
|
def connectScrollEvent(self, slot):
|
|
widget = self.getPlotWidget()
|
|
return widget.mpl_connect('scroll_event', slot)
|
|
|
|
def disconnectMotionEvent(self):
|
|
widget = self.getPlotWidget()
|
|
widget.mpl_disconnect(self.cidmotion)
|
|
self.cidmotion = None
|
|
|
|
def connectMotionEvent(self, slot):
|
|
widget = self.getPlotWidget()
|
|
return widget.mpl_connect('motion_notify_event', slot)
|
|
|
|
def disconnectReleaseEvent(self):
|
|
widget = self.getPlotWidget()
|
|
widget.mpl_disconnect(self.cidrelease)
|
|
self.cidrelease = None
|
|
|
|
def connectReleaseEvent(self, slot):
|
|
widget = self.getPlotWidget()
|
|
return widget.mpl_connect('button_release_event', slot)
|
|
|
|
def disable_ar_buttons(self):
|
|
self.enable_ar_buttons(False)
|
|
|
|
def enable_ar_buttons(self, bool=True):
|
|
self.accept_button.setEnabled(bool)
|
|
self.reject_button.setEnabled(bool)
|
|
|
|
def p_phase_select(self, phase):
|
|
if not self.p_button.isChecked():
|
|
self.p_button.setChecked(True)
|
|
self.p_button.setText(phase)
|
|
else:
|
|
if str(phase) == str(self.p_button.text()):
|
|
self.reset_p_button()
|
|
else:
|
|
self.p_button.setText(phase)
|
|
self.p_clicked()
|
|
|
|
def s_phase_select(self, phase):
|
|
if not self.s_button.isChecked():
|
|
self.s_button.setChecked(True)
|
|
self.s_button.setText(phase)
|
|
else:
|
|
if str(phase) == str(self.s_button.text()):
|
|
self.reset_s_button()
|
|
else:
|
|
self.s_button.setText(phase)
|
|
self.s_clicked()
|
|
|
|
def p_clicked(self):
|
|
if self.p_button.isChecked():
|
|
self.reset_s_button()
|
|
self.s_button.setEnabled(False)
|
|
self.init_p_pick()
|
|
else:
|
|
self.leave_picking_mode()
|
|
|
|
def s_clicked(self):
|
|
if self.s_button.isChecked():
|
|
self.reset_p_button()
|
|
self.p_button.setEnabled(False)
|
|
self.init_s_pick()
|
|
else:
|
|
self.leave_picking_mode()
|
|
|
|
def init_p_pick(self):
|
|
self.set_button_color(self.p_button, 'yellow')
|
|
self.updateCurrentLimits()
|
|
self.activatePicking()
|
|
self.currentPhase = str(self.p_button.text())
|
|
|
|
def init_s_pick(self):
|
|
self.set_button_color(self.s_button, 'yellow')
|
|
self.updateCurrentLimits()
|
|
self.activatePicking()
|
|
self.currentPhase = str(self.s_button.text())
|
|
|
|
def set_button_color(self, button, color = None):
|
|
if type(color) == QtGui.QColor:
|
|
palette = button.palette()
|
|
role = button.backgroundRole()
|
|
palette.setColor(role, color)
|
|
button.setPalette(palette)
|
|
button.setAutoFillBackground(True)
|
|
elif type(color) == str or not color:
|
|
button.setStyleSheet("background-color: {}".format(color))
|
|
|
|
def reset_p_button(self):
|
|
self.set_button_color(self.p_button)
|
|
self.p_button.setEnabled(True)
|
|
self.p_button.setChecked(False)
|
|
self.p_button.setText('P')
|
|
|
|
def reset_s_button(self):
|
|
self.set_button_color(self.s_button)
|
|
self.s_button.setEnabled(True)
|
|
self.s_button.setChecked(False)
|
|
self.s_button.setText('S')
|
|
|
|
def leave_picking_mode(self):
|
|
self.currentPhase = None
|
|
self.reset_p_button()
|
|
self.reset_s_button()
|
|
self.getPlotWidget().plotWFData(wfdata=self.getWFData(),
|
|
title=self.getStation())
|
|
self.drawAllPicks()
|
|
self.setPlotLabels()
|
|
self.resetZoomAction.trigger()
|
|
self.deactivatePicking()
|
|
|
|
def activatePicking(self):
|
|
if self.zoomAction.isChecked():
|
|
self.zoomAction.trigger()
|
|
self.disconnectReleaseEvent()
|
|
self.disconnectScrollEvent()
|
|
self.disconnectMotionEvent()
|
|
self.disconnectPressEvent()
|
|
self.cidpress = self.connectPressEvent(self.setIniPick)
|
|
self.filterWFData()
|
|
self.pick_block = self.togglePickBlocker()
|
|
|
|
def deactivatePicking(self):
|
|
self.disconnectPressEvent()
|
|
self.cidpress = self.connectPressEvent(self.panPress)
|
|
self.cidmotion = self.connectMotionEvent(self.panMotion)
|
|
self.cidrelease = self.connectReleaseEvent(self.panRelease)
|
|
self.cidscroll = self.connectScrollEvent(self.scrollZoom)
|
|
|
|
def getParameter(self):
|
|
return self.parameter
|
|
|
|
def getStartTime(self):
|
|
return self.stime
|
|
|
|
def getEndTime(self):
|
|
return self.etime
|
|
|
|
def getComponents(self):
|
|
return self.components
|
|
|
|
def getStation(self):
|
|
if self.network and self.station:
|
|
return self.network+'.'+self.station
|
|
return self.station
|
|
|
|
def getPlotWidget(self):
|
|
return self.multicompfig
|
|
|
|
def getChannelID(self, key):
|
|
return self.getPlotWidget().getPlotDict()[int(key)][1]
|
|
|
|
def getTraceID(self, channels):
|
|
plotDict = self.getPlotWidget().getPlotDict()
|
|
traceIDs = []
|
|
for channel in channels:
|
|
channel = channel.upper()
|
|
for traceID, channelID in plotDict.items():
|
|
if channelID[1].upper().endswith(channel):
|
|
traceIDs.append(traceID)
|
|
return traceIDs
|
|
|
|
def getUser(self):
|
|
return self._user
|
|
|
|
def getFilterOptions(self, phase):
|
|
options = self.filteroptions[phase[0]]
|
|
return FilterOptions(**options)
|
|
|
|
def getXLims(self):
|
|
return self.cur_xlim
|
|
|
|
def getYLims(self):
|
|
return self.cur_ylim
|
|
|
|
def setXLims(self, limits):
|
|
self.cur_xlim = limits
|
|
|
|
def setYLims(self, limits):
|
|
self.cur_ylim = limits
|
|
|
|
def getGlobalLimits(self, axis):
|
|
return self.limits[axis]
|
|
|
|
def updateCurrentLimits(self):
|
|
self.setXLims(self.getPlotWidget().getXLims())
|
|
self.setYLims(self.getPlotWidget().getYLims())
|
|
|
|
def getWFData(self):
|
|
return self.data
|
|
|
|
def selectWFData(self, channel):
|
|
component = channel[-1].upper()
|
|
wfdata = Stream()
|
|
|
|
def selectTrace(tr, components):
|
|
if tr.stats.channel[-1].upper() in components:
|
|
return tr
|
|
|
|
if component == 'E' or component == 'N':
|
|
for trace in self.getWFData():
|
|
trace = selectTrace(trace, 'NE')
|
|
if trace:
|
|
wfdata.append(trace)
|
|
elif component == '1' or component == '2':
|
|
for trace in self.getWFData():
|
|
trace = selectTrace(trace, '12')
|
|
if trace:
|
|
wfdata.append(trace)
|
|
else:
|
|
wfdata = self.getWFData().select(component=component)
|
|
return wfdata
|
|
|
|
def getPicks(self, picktype='manual'):
|
|
if picktype == 'manual':
|
|
return self.picks
|
|
elif picktype == 'auto':
|
|
return self.autopicks
|
|
else:
|
|
raise TypeError('Unknown picktype {0}'.format(picktype))
|
|
|
|
def resetPicks(self):
|
|
self.picks = {}
|
|
|
|
def delPicks(self):
|
|
self.resetPicks()
|
|
self.resetPlot()
|
|
|
|
def setIniPick(self, gui_event):
|
|
|
|
trace_number = round(gui_event.ydata)
|
|
|
|
channel = self.getChannelID(trace_number)
|
|
wfdata = self.selectWFData(channel)
|
|
|
|
self.disconnectScrollEvent()
|
|
self.disconnectPressEvent()
|
|
self.disconnectReleaseEvent()
|
|
self.disconnectMotionEvent()
|
|
self.cidpress = self.connectPressEvent(self.setPick)
|
|
|
|
if self.currentPhase.startswith('P'):
|
|
self.set_button_color(self.p_button, 'green')
|
|
self.setIniPickP(gui_event, wfdata, trace_number)
|
|
elif self.currentPhase.startswith('S'):
|
|
self.set_button_color(self.s_button, 'green')
|
|
self.setIniPickS(gui_event, wfdata)
|
|
|
|
self.zoomAction.setEnabled(False)
|
|
|
|
# reset labels
|
|
self.setPlotLabels()
|
|
self.draw()
|
|
|
|
def setIniPickP(self, gui_event, wfdata, trace_number):
|
|
|
|
parameter = self.parameter
|
|
ini_pick = gui_event.xdata
|
|
|
|
nfac = parameter.get('nfacP')
|
|
twins = parameter.get('tsnrz')
|
|
noise_win = twins[0]
|
|
gap_win = twins[1]
|
|
signal_win = twins[2]
|
|
itrace = int(trace_number)
|
|
|
|
while itrace > len(wfdata) - 1:
|
|
itrace -= 1
|
|
|
|
stime = self.getStartTime()
|
|
stime_diff = wfdata[itrace].stats.starttime-stime
|
|
|
|
# copy data for plotting
|
|
data = self.getWFData().copy()
|
|
|
|
# filter data and trace on which is picked prior to determination of SNR
|
|
phase = self.currentPhase
|
|
filteroptions = self.getFilterOptions(phase[0]).parseFilterOptions()
|
|
if filteroptions:
|
|
data.filter(**filteroptions)
|
|
wfdata.filter(**filteroptions)
|
|
|
|
result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick-stime_diff, itrace)
|
|
|
|
snr = result[0]
|
|
noiselevel = result[2]
|
|
if noiselevel:
|
|
noiselevel *= nfac
|
|
else:
|
|
noiselevel = nfac
|
|
|
|
x_res = getResolutionWindow(snr, parameter.get('extent'))
|
|
|
|
# remove mean noise level from waveforms
|
|
for trace in data:
|
|
t = prepTimeAxis(trace.stats.starttime - stime, trace)
|
|
inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
|
|
trace = demeanTrace(trace=trace, window=inoise)
|
|
|
|
self.setXLims([ini_pick - x_res, ini_pick + x_res])
|
|
self.setYLims(np.array([-noiselevel * 3.5, noiselevel * 3.5]) +
|
|
trace_number)
|
|
self.getPlotWidget().plotWFData(wfdata=data,
|
|
title=self.getStation() +
|
|
' picking mode',
|
|
zoomx=self.getXLims(),
|
|
zoomy=self.getYLims(),
|
|
noiselevel=(trace_number + noiselevel,
|
|
trace_number - noiselevel),
|
|
iniPick=ini_pick)
|
|
|
|
def setIniPickS(self, gui_event, wfdata):
|
|
|
|
parameter = self.parameter
|
|
ini_pick = gui_event.xdata
|
|
|
|
nfac = parameter.get('nfacS')
|
|
twins = parameter.get('tsnrh')
|
|
noise_win = twins[0]
|
|
gap_win = twins[1]
|
|
signal_win = twins[2]
|
|
|
|
stime = self.getStartTime()
|
|
stime_diff = wfdata[0].stats.starttime-stime
|
|
|
|
# copy data for plotting
|
|
data = self.getWFData().copy()
|
|
|
|
# filter data and trace on which is picked prior to determination of SNR
|
|
phase = self.currentPhase
|
|
filteroptions = self.getFilterOptions(phase).parseFilterOptions()
|
|
if filteroptions:
|
|
data.filter(**filteroptions)
|
|
wfdata.filter(**filteroptions)
|
|
|
|
# determine SNR and noiselevel
|
|
result = getSNR(wfdata, (noise_win, gap_win, signal_win), ini_pick-stime_diff)
|
|
snr = result[0]
|
|
noiselevel = result[2]
|
|
|
|
if noiselevel:
|
|
noiselevel *= nfac
|
|
else:
|
|
noiselevel = nfac
|
|
|
|
# prepare plotting of data
|
|
for trace in data:
|
|
t = prepTimeAxis(trace.stats.starttime - stime, trace)
|
|
inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
|
|
trace = demeanTrace(trace, inoise)
|
|
|
|
# scale waveform for plotting
|
|
horiz_comp = find_horizontals(data)
|
|
data = scaleWFData(data, noiselevel * 2.5, horiz_comp)
|
|
|
|
x_res = getResolutionWindow(snr, parameter.get('extent'))
|
|
|
|
self.setXLims(tuple([ini_pick - x_res, ini_pick + x_res]))
|
|
traces = self.getTraceID(horiz_comp)
|
|
traces.sort()
|
|
self.setYLims(tuple(np.array([-1.0, +1.0]) +
|
|
np.array(traces)))
|
|
noiselevels = [trace + 1 / (2.5 * 2) for trace in traces] + \
|
|
[trace - 1 / (2.5 * 2) for trace in traces]
|
|
|
|
self.getPlotWidget().plotWFData(wfdata=data,
|
|
title=self.getStation() +
|
|
' picking mode',
|
|
zoomx=self.getXLims(),
|
|
zoomy=self.getYLims(),
|
|
noiselevel=noiselevels,
|
|
scaleddata=True,
|
|
iniPick=ini_pick)
|
|
|
|
def setPick(self, gui_event):
|
|
|
|
parameter = self.parameter
|
|
|
|
# get axes limits
|
|
self.updateCurrentLimits()
|
|
|
|
# setting pick
|
|
pick = gui_event.xdata # get pick time relative to the traces timeaxis not to the global
|
|
channel = self.getChannelID(round(gui_event.ydata))
|
|
|
|
# get name of phase actually picked
|
|
phase = self.currentPhase
|
|
|
|
# get filter parameter for the phase to be picked
|
|
filteroptions = self.getFilterOptions(phase).parseFilterOptions()
|
|
|
|
# copy and filter data for earliest and latest possible picks
|
|
wfdata = self.getWFData().copy().select(channel=channel)
|
|
if filteroptions:
|
|
wfdata.filter(**filteroptions)
|
|
|
|
# get earliest and latest possible pick and symmetric pick error
|
|
if wfdata[0].stats.channel[2] == 'Z' or wfdata[0].stats.channel[2] == '3':
|
|
nfac = parameter.get('nfacP')
|
|
TSNR = parameter.get('tsnrz')
|
|
else:
|
|
nfac = parameter.get('nfacS')
|
|
TSNR = parameter.get('tsnrh')
|
|
|
|
# return absolute time values for phases
|
|
stime = self.getStartTime()
|
|
stime_diff = wfdata[0].stats.starttime-stime
|
|
|
|
[epp, lpp, spe] = earllatepicker(wfdata, nfac, (TSNR[0], TSNR[1], TSNR[2]),
|
|
pick-stime_diff, verbosity=1)
|
|
|
|
mpp = stime + pick
|
|
if epp:
|
|
epp = stime + epp + stime_diff
|
|
if lpp:
|
|
lpp = stime + lpp + stime_diff
|
|
|
|
# save pick times for actual phase
|
|
phasepicks = dict(epp=epp, lpp=lpp, mpp=mpp, spe=spe,
|
|
picker='manual', channel=channel,
|
|
network=wfdata[0].stats.network)
|
|
|
|
try:
|
|
oldphasepick = self.picks[phase]
|
|
except KeyError:
|
|
self.picks[phase] = phasepicks
|
|
else:
|
|
self.picks[phase] = phasepicks
|
|
oepp = oldphasepick['epp']
|
|
ompp = oldphasepick['mpp']
|
|
olpp = oldphasepick['lpp']
|
|
msg = """Warning old phase information for phase {phase} has been
|
|
altered.\n
|
|
New phase times:\n
|
|
earliest possible pick: {epp}\n
|
|
most probable pick: {mpp}\n
|
|
latest possible pick: {lpp}\n
|
|
\n
|
|
Old phase times (overwritten):\n
|
|
earliest possible pick: {oepp}\n
|
|
most probable pick: {ompp}\n
|
|
latest possible pick: {olpp}\n""".format(phase=phase,
|
|
epp=epp,
|
|
mpp=pick,
|
|
lpp=lpp,
|
|
oepp=oepp,
|
|
ompp=ompp,
|
|
olpp=olpp)
|
|
|
|
self.disconnectPressEvent()
|
|
self.enable_ar_buttons()
|
|
self.zoomAction.setEnabled(True)
|
|
self.pick_block = self.togglePickBlocker()
|
|
self.leave_picking_mode()
|
|
|
|
def drawAllPicks(self):
|
|
self.drawPicks(picktype='manual')
|
|
self.drawPicks(picktype='auto')
|
|
|
|
def drawPicks(self, phase=None, picktype='manual'):
|
|
# plotting picks
|
|
ax = self.getPlotWidget().axes
|
|
ylims = self.getGlobalLimits('y')
|
|
phase_col = {
|
|
'P': ('c', 'c--', 'b-', 'bv', 'b^', 'b', 'c:'),
|
|
'S': ('m', 'm--', 'r-', 'rv', 'r^', 'r', 'm:')
|
|
}
|
|
if self.getPicks(picktype):
|
|
if phase is not None:
|
|
if (type(self.getPicks(picktype)[phase]) is dict
|
|
or type(self.getPicks(picktype)[phase]) is AttribDict):
|
|
picks = self.getPicks(picktype)[phase]
|
|
colors = phase_col[phase[0].upper()]
|
|
elif phase is None:
|
|
for phase in self.getPicks(picktype):
|
|
self.drawPicks(phase, picktype)
|
|
return
|
|
else:
|
|
return
|
|
else:
|
|
return
|
|
|
|
mpp = picks['mpp'] - self.getStartTime()
|
|
if picks['epp'] and picks['lpp']:
|
|
epp = picks['epp'] - self.getStartTime()
|
|
lpp = picks['lpp'] - self.getStartTime()
|
|
spe = picks['spe']
|
|
|
|
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))
|
|
ax.text(mpp, ylims[1], 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))
|
|
ax.legend()
|
|
|
|
|
|
def panPress(self, gui_event):
|
|
ax = self.getPlotWidget().axes
|
|
if gui_event.inaxes != ax: return
|
|
self.cur_xlim = ax.get_xlim()
|
|
self.cur_ylim = ax.get_ylim()
|
|
self.press = gui_event.xdata, gui_event.ydata
|
|
self.xpress, self.ypress = self.press
|
|
|
|
def panRelease(self, gui_event):
|
|
ax = self.getPlotWidget().axes
|
|
self.press = None
|
|
ax.figure.canvas.draw()
|
|
|
|
def panMotion(self, gui_event):
|
|
if self.press is None: return
|
|
ax = self.getPlotWidget().axes
|
|
if gui_event.inaxes != ax: return
|
|
dx = gui_event.xdata - self.xpress
|
|
dy = gui_event.ydata - self.ypress
|
|
self.cur_xlim -= dx
|
|
self.cur_ylim -= dy
|
|
ax.set_xlim(self.cur_xlim)
|
|
ax.set_ylim(self.cur_ylim)
|
|
|
|
ax.figure.canvas.draw()
|
|
|
|
def togglePickBlocker(self):
|
|
return not self.pick_block
|
|
|
|
def filterWFData(self):
|
|
if self.pick_block:
|
|
return
|
|
self.updateCurrentLimits()
|
|
data = self.getWFData().copy()
|
|
old_title = self.getPlotWidget().getAxes().get_title()
|
|
title = None
|
|
phase = self.currentPhase
|
|
filtoptions = None
|
|
if phase:
|
|
filtoptions = self.getFilterOptions(phase).parseFilterOptions()
|
|
if self.filterAction.isChecked():
|
|
if not phase:
|
|
filtoptions = FilterOptionsDialog.getFilterObject()
|
|
filtoptions = filtoptions.parseFilterOptions()
|
|
if filtoptions is not None:
|
|
data.filter(**filtoptions)
|
|
if not old_title.endswith(')'):
|
|
title = old_title + ' (filtered)'
|
|
elif not old_title.endswith(' (filtered)') and not old_title.endswith(', filtered)'):
|
|
title = old_title[:-1] + ', filtered)'
|
|
else:
|
|
if old_title.endswith(' (filtered)'):
|
|
title = old_title.replace(' (filtered)', '')
|
|
elif old_title.endswith(', filtered)'):
|
|
title = old_title.replace(', filtered)', ')')
|
|
if title is None:
|
|
title = old_title
|
|
self.getPlotWidget().plotWFData(wfdata=data, title=title,
|
|
zoomx=self.getXLims(),
|
|
zoomy=self.getYLims())
|
|
self.setPlotLabels()
|
|
self.drawAllPicks()
|
|
self.draw()
|
|
|
|
def resetPlot(self):
|
|
self.updateCurrentLimits()
|
|
data = self.getWFData().copy()
|
|
title = self.getPlotWidget().getAxes().get_title()
|
|
self.getPlotWidget().plotWFData(wfdata=data, title=title,
|
|
zoomx=self.getXLims(),
|
|
zoomy=self.getYLims())
|
|
self.setPlotLabels()
|
|
self.drawAllPicks()
|
|
self.draw()
|
|
|
|
def setPlotLabels(self):
|
|
|
|
# get channel labels
|
|
pos = self.getPlotWidget().getPlotDict().keys()
|
|
labels = [self.getPlotWidget().getPlotDict()[key][1] for key in pos]
|
|
|
|
# set channel labels
|
|
self.getPlotWidget().setYTickLabels(pos, labels)
|
|
self.getPlotWidget().setXLims(self.getXLims())
|
|
self.getPlotWidget().setYLims(self.getYLims())
|
|
|
|
def zoom(self):
|
|
if self.zoomAction.isChecked() and self.pick_block:
|
|
self.zoomAction.setChecked(False)
|
|
elif self.zoomAction.isChecked():
|
|
self.disconnectPressEvent()
|
|
self.disconnectMotionEvent()
|
|
self.disconnectReleaseEvent()
|
|
self.disconnectScrollEvent()
|
|
self.figToolBar.zoom()
|
|
else:
|
|
self.figToolBar.zoom()
|
|
self.cidpress = self.connectPressEvent(self.panPress)
|
|
self.cidmotion = self.connectMotionEvent(self.panMotion)
|
|
self.cidrelease = self.connectReleaseEvent(self.panRelease)
|
|
self.cidscroll = self.connectScrollEvent(self.scrollZoom)
|
|
|
|
def scrollZoom(self, gui_event, factor=2.):
|
|
|
|
self.updateCurrentLimits()
|
|
|
|
if gui_event.button == 'up':
|
|
scale_factor = 1 / factor
|
|
elif gui_event.button == 'down':
|
|
# deal with zoom out
|
|
scale_factor = factor
|
|
else:
|
|
# deal with something that should never happen
|
|
scale_factor = 1
|
|
print(gui_event.button)
|
|
|
|
new_xlim = gui_event.xdata - \
|
|
scale_factor * (gui_event.xdata - self.getXLims())
|
|
new_ylim = gui_event.ydata - \
|
|
scale_factor * (gui_event.ydata - self.getYLims())
|
|
|
|
new_xlim.sort()
|
|
global_x = self.getGlobalLimits('x')
|
|
global_y = self.getGlobalLimits('y')
|
|
new_xlim[0] = max(new_xlim[0], global_x[0])
|
|
new_xlim[1] = min(new_xlim[1], global_x[1])
|
|
new_ylim.sort()
|
|
new_ylim[0] = max(new_ylim[0], global_y[0])
|
|
new_ylim[1] = min(new_ylim[1], global_y[1])
|
|
|
|
self.getPlotWidget().setXLims(new_xlim)
|
|
self.getPlotWidget().setYLims(new_ylim)
|
|
self.draw()
|
|
|
|
def resetZoom(self):
|
|
self.getPlotWidget().setXLims(self.getGlobalLimits('x'))
|
|
self.getPlotWidget().setYLims(self.getGlobalLimits('y'))
|
|
self.draw()
|
|
|
|
def draw(self):
|
|
self.getPlotWidget().draw()
|
|
|
|
def apply(self):
|
|
picks = self.getPicks()
|
|
self.update_picks.emit(picks)
|
|
# for pick in picks:
|
|
# print(pick, picks[pick])
|
|
|
|
def discard(self):
|
|
picks = self._init_picks
|
|
self.picks = picks
|
|
self.update_picks.emit(picks)
|
|
# for pick in picks:
|
|
# print(pick, picks[pick])
|
|
|
|
def reject(self):
|
|
self.discard()
|
|
if not self._embedded:
|
|
QDialog.reject(self)
|
|
else:
|
|
self.resetPlot()
|
|
self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information,
|
|
'Denied', 'New picks rejected!')
|
|
self.qmb.show()
|
|
|
|
def accept(self):
|
|
self.apply()
|
|
if not self._embedded:
|
|
QDialog.accept(self)
|
|
else:
|
|
self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information,
|
|
'Accepted', 'New picks applied!')
|
|
self.qmb.show()
|
|
|
|
|
|
class TuneAutopicker(QWidget):
|
|
update = QtCore.Signal(str)
|
|
'''
|
|
QWidget used to modifiy and test picking parameters for autopicking algorithm.
|
|
|
|
:param: parent
|
|
:type: QtPyLoT Mainwindow
|
|
'''
|
|
def __init__(self, parent):
|
|
QtGui.QWidget.__init__(self, parent, 1)
|
|
self.parent = parent
|
|
self.setParent(parent)
|
|
self.setWindowTitle('PyLoT - Tune Autopicker')
|
|
self.parameter = parent._inputs
|
|
self.fig_dict = parent.fig_dict
|
|
self.data = Data()
|
|
self.init_main_layouts()
|
|
self.init_eventlist()
|
|
self.init_figure_tabs()
|
|
self.init_stationlist()
|
|
self.init_pbwidget()
|
|
self.connect_signals()
|
|
self.add_parameters()
|
|
self.add_buttons()
|
|
self.add_log()
|
|
self.set_stretch()
|
|
self.resize(1280, 720)
|
|
#self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
|
|
#self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
|
|
|
def init_main_layouts(self):
|
|
self.main_layout = QtGui.QVBoxLayout()
|
|
self.tune_layout = QtGui.QHBoxLayout()
|
|
self.trace_layout = QtGui.QHBoxLayout()
|
|
self.parameter_layout = QtGui.QVBoxLayout()
|
|
|
|
self.main_layout.addLayout(self.trace_layout)
|
|
self.main_layout.addLayout(self.tune_layout)
|
|
self.setLayout(self.main_layout)
|
|
|
|
def init_eventlist(self):
|
|
self.eventBox = self.parent.createEventBox()
|
|
self.eventBox.setMaxVisibleItems(20)
|
|
self.fill_eventbox()
|
|
self.trace_layout.addWidget(self.eventBox)
|
|
|
|
def init_stationlist(self):
|
|
self.stationBox = QtGui.QComboBox()
|
|
self.trace_layout.addWidget(self.stationBox)
|
|
self.fill_stationbox()
|
|
self.figure_tabs.setCurrentIndex(0)
|
|
|
|
def connect_signals(self):
|
|
self.eventBox.activated.connect(self.fill_stationbox)
|
|
self.eventBox.activated.connect(self.update_eventID)
|
|
self.eventBox.activated.connect(self.fill_tabs)
|
|
self.stationBox.activated.connect(self.fill_tabs)
|
|
|
|
def fill_stationbox(self):
|
|
fnames = self.parent.getWFFnames_from_eventbox(eventbox=self.eventBox)
|
|
self.data.setWFData(fnames)
|
|
self.stationBox.clear()
|
|
stations = []
|
|
for trace in self.data.getWFData():
|
|
station = trace.stats.station
|
|
network = trace.stats.network
|
|
ns_tup = (str(network), str(station))
|
|
if not ns_tup in stations:
|
|
stations.append(ns_tup)
|
|
stations.sort()
|
|
model = self.stationBox.model()
|
|
for network, station in stations:
|
|
item = QtGui.QStandardItem(network+'.'+station)
|
|
if station in self.get_current_event().pylot_picks:
|
|
item.setBackground(self.parent._colors['ref'])
|
|
model.appendRow(item)
|
|
|
|
def init_figure_tabs(self):
|
|
self.figure_tabs = QtGui.QTabWidget()
|
|
self.fill_figure_tabs()
|
|
|
|
def init_pbwidget(self):
|
|
self.pb_widget = QtGui.QWidget()
|
|
|
|
def init_tab_names(self):
|
|
self.ptb_names = ['aicFig', 'slength', 'checkZ4s', 'refPpick', 'el_Ppick', 'fm_picker']
|
|
self.stb_names = ['aicARHfig', 'refSpick', 'el_S1pick', 'el_S2pick']
|
|
|
|
def add_parameters(self):
|
|
self.paraBox = PylotParaBox(self.parameter)
|
|
self.paraBox.set_tune_mode(True)
|
|
self.update_eventID()
|
|
self.parameter_layout.addWidget(self.paraBox)
|
|
self.parameter_layout.addWidget(self.pb_widget)
|
|
self.tune_layout.insertLayout(1, self.parameter_layout)
|
|
|
|
def add_buttons(self):
|
|
self.pick_button = QtGui.QPushButton('Pick Trace')
|
|
self.pick_button.clicked.connect(self.call_picker)
|
|
self.close_button = QtGui.QPushButton('Close')
|
|
self.close_button.clicked.connect(self.hide)
|
|
self.trace_layout.addWidget(self.pick_button)
|
|
self.trace_layout.setStretch(0, 1)
|
|
self.parameter_layout.addWidget(self.close_button)
|
|
|
|
def add_log(self):
|
|
self.listWidget = QtGui.QListWidget()
|
|
self.figure_tabs.insertTab(4, self.listWidget, 'log')
|
|
|
|
def add_log_item(self, text):
|
|
self.listWidget.addItem(text)
|
|
self.listWidget.scrollToBottom()
|
|
|
|
def get_current_event(self):
|
|
path = self.eventBox.currentText()
|
|
return self.parent.project.getEventFromPath(path)
|
|
|
|
def get_current_event_name(self):
|
|
return self.eventBox.currentText().split('/')[-1]
|
|
|
|
def get_current_event_fp(self):
|
|
return self.eventBox.currentText()
|
|
|
|
def get_current_event_picks(self, station):
|
|
event = self.get_current_event()
|
|
if station in event.pylot_picks.keys():
|
|
return event.pylot_picks[station]
|
|
|
|
def get_current_event_autopicks(self, station):
|
|
event = self.get_current_event()
|
|
if event.pylot_autopicks:
|
|
return event.pylot_autopicks[station]
|
|
|
|
def get_current_station(self):
|
|
return str(self.stationBox.currentText()).split('.')[-1]
|
|
|
|
def gen_tab_widget(self, name, canvas):
|
|
widget = QtGui.QWidget()
|
|
v_layout = QtGui.QVBoxLayout()
|
|
v_layout.addWidget(canvas)
|
|
v_layout.addWidget(NavigationToolbar2QT(canvas, self))
|
|
widget.setLayout(v_layout)
|
|
return widget
|
|
|
|
def gen_pick_dlg(self):
|
|
if not self.get_current_event():
|
|
self.pickDlg = None
|
|
return
|
|
station = self.get_current_station()
|
|
data = self.data.getWFData()
|
|
pickDlg = PickDlg(self, data=data.select(station=station),
|
|
station=station, parameter=self.parameter,
|
|
picks=self.get_current_event_picks(station),
|
|
autopicks=self.get_current_event_autopicks(station),
|
|
embedded=True)
|
|
pickDlg.update_picks.connect(self.picks_from_pickdlg)
|
|
pickDlg.update_picks.connect(self.fill_eventbox)
|
|
pickDlg.update_picks.connect(self.fill_stationbox)
|
|
pickDlg.update_picks.connect(lambda: self.parent.setDirty(True))
|
|
pickDlg.update_picks.connect(self.parent.enableSaveManualPicksAction)
|
|
self.pickDlg = QtGui.QWidget()
|
|
hl = QtGui.QHBoxLayout()
|
|
self.pickDlg.setLayout(hl)
|
|
hl.addWidget(pickDlg)
|
|
|
|
def picks_from_pickdlg(self, picks=None):
|
|
station = self.get_current_station()
|
|
replot = self.parent.addPicks(station, picks)
|
|
self.get_current_event().setPick(station, picks)
|
|
if self.get_current_event() == self.parent.get_current_event():
|
|
if replot:
|
|
self.parent.plotWaveformDataThread()
|
|
self.parent.drawPicks()
|
|
else:
|
|
self.parent.drawPicks(station)
|
|
self.parent.draw()
|
|
|
|
def plot_manual_picks_to_figs(self):
|
|
picks = self.get_current_event_picks(self.get_current_station())
|
|
if not picks:
|
|
return
|
|
st = self.data.getWFData()
|
|
tr = st.select(station=self.get_current_station())[0]
|
|
starttime = tr.stats.starttime
|
|
p_axes=[
|
|
('mainFig', 0),
|
|
('aicFig', 0),
|
|
('slength', 0),
|
|
('refPpick', 0),
|
|
('el_Ppick', 0),
|
|
('fm_picker', 0),
|
|
('fm_picker', 1)]
|
|
s_axes=[
|
|
('mainFig', 1),
|
|
('mainFig', 2),
|
|
('aicARHfig', 0),
|
|
('refSpick', 0),
|
|
('el_S1pick', 0),
|
|
('el_S2pick', 0)]
|
|
for p_ax in p_axes:
|
|
axes = self.parent.fig_dict[p_ax[0]].axes
|
|
if not axes:
|
|
continue
|
|
ax = axes[p_ax[1]]
|
|
self.plot_manual_Ppick_to_ax(ax, (picks['P']['mpp'] - starttime))
|
|
for s_ax in s_axes:
|
|
axes = self.parent.fig_dict[s_ax[0]].axes
|
|
if not axes:
|
|
continue
|
|
ax = axes[s_ax[1]]
|
|
self.plot_manual_Spick_to_ax(ax, (picks['S']['mpp'] - starttime))
|
|
|
|
def plot_manual_Ppick_to_ax(self, ax, pick):
|
|
y_top = 0.9*ax.get_ylim()[1]
|
|
y_bot = 0.9*ax.get_ylim()[0]
|
|
ax.vlines(pick, y_bot, y_top,
|
|
color='teal', linewidth=2, label='manual P Onset')
|
|
ax.plot([pick-0.5, pick+0.5],
|
|
[y_bot, y_bot], linewidth=2, color='teal')
|
|
ax.plot([pick-0.5, pick+0.5],
|
|
[y_top, y_top], linewidth=2, color='teal')
|
|
ax.legend()
|
|
|
|
def plot_manual_Spick_to_ax(self, ax, pick):
|
|
y_top = 0.9*ax.get_ylim()[1]
|
|
y_bot = 0.9*ax.get_ylim()[0]
|
|
ax.vlines(pick, y_bot, y_top,
|
|
color='magenta', linewidth=2, label='manual S Onset')
|
|
ax.plot([pick-0.5, pick+0.5],
|
|
[y_bot, y_bot], linewidth=2, color='magenta')
|
|
ax.plot([pick-0.5, pick+0.5],
|
|
[y_top, y_top], linewidth=2, color='magenta')
|
|
ax.legend()
|
|
|
|
def fill_tabs(self, event=None, picked=False):
|
|
self.clear_all()
|
|
canvas_dict = self.parent.canvas_dict
|
|
self.gen_pick_dlg()
|
|
self.overview = self.gen_tab_widget('Overview', canvas_dict['mainFig'])
|
|
id0 = self.figure_tabs.insertTab(0, self.pickDlg, 'Traces Plot')
|
|
id1 = self.figure_tabs.insertTab(1, self.overview, 'Overview')
|
|
id2 = self.figure_tabs.insertTab(2, self.p_tabs, 'P')
|
|
id3 = self.figure_tabs.insertTab(3, self.s_tabs, 'S')
|
|
if picked and self.get_current_event():
|
|
self.fill_p_tabs(canvas_dict)
|
|
self.fill_s_tabs(canvas_dict)
|
|
self.toggle_autopickTabs(bool(self.fig_dict['mainFig'].axes))
|
|
self.plot_manual_picks_to_figs()
|
|
else:
|
|
self.disable_autopickTabs()
|
|
try:
|
|
main_fig.tight_layout()
|
|
except:
|
|
pass
|
|
self.figure_tabs.setCurrentIndex(0)
|
|
|
|
def fill_p_tabs(self, canvas_dict):
|
|
for name in self.ptb_names:
|
|
id = self.p_tabs.addTab(self.gen_tab_widget(name, canvas_dict[name]), name)
|
|
self.p_tabs.setTabEnabled(id, bool(self.fig_dict[name].axes))
|
|
try:
|
|
self.fig_dict[name].tight_layout()
|
|
except:
|
|
pass
|
|
|
|
def fill_s_tabs(self, canvas_dict):
|
|
for name in self.stb_names:
|
|
id = self.s_tabs.addTab(self.gen_tab_widget(name, canvas_dict[name]), name)
|
|
self.s_tabs.setTabEnabled(id, bool(self.fig_dict[name].axes))
|
|
try:
|
|
self.fig_dict[name].tight_layout()
|
|
except:
|
|
pass
|
|
|
|
def fill_figure_tabs(self):
|
|
self.clear_all()
|
|
self.p_tabs = QtGui.QTabWidget()
|
|
self.s_tabs = QtGui.QTabWidget()
|
|
self.tune_layout.insertWidget(0, self.figure_tabs)
|
|
self.init_tab_names()
|
|
|
|
def fill_eventbox(self):
|
|
project = self.parent.project
|
|
if not project:
|
|
return
|
|
# update own list
|
|
self.parent.fill_eventbox(eventBox=self.eventBox, select_events='ref')
|
|
index_start = self.parent.eventBox.currentIndex()
|
|
index = index_start
|
|
if index == -1:
|
|
index += 1
|
|
nevents = self.eventBox.model().rowCount()
|
|
path = self.eventBox.itemText(index)
|
|
if project.getEventFromPath(path).isTestEvent():
|
|
for index in range(nevents):
|
|
path = self.eventBox.itemText(index)
|
|
if project.getEventFromPath(index):
|
|
if not project.getEventFromPath(index).isTestEvent():
|
|
break
|
|
#in case all events are marked as test events and last event is reached
|
|
if index == nevents - 1:
|
|
index = -1
|
|
self.eventBox.setCurrentIndex(index)
|
|
if not index == index_start:
|
|
self.eventBox.activated.emit(index)
|
|
# update parent
|
|
self.parent.fill_eventbox()
|
|
|
|
def update_eventID(self):
|
|
self.paraBox.boxes['eventID'].setText(
|
|
self.get_current_event_name())
|
|
self.figure_tabs.setCurrentIndex(0)
|
|
|
|
def call_picker(self):
|
|
self.parameter = self.params_from_gui()
|
|
station = self.get_current_station()
|
|
if not station:
|
|
self._warn('No station selected')
|
|
return
|
|
args = {'parameter': self.parameter,
|
|
'station': station,
|
|
'fnames': 'None',
|
|
'eventid': self.get_current_event_fp(),
|
|
'iplot': 2,
|
|
'fig_dict': self.fig_dict,
|
|
'locflag': 0}
|
|
for key in self.fig_dict.keys():
|
|
self.fig_dict[key].clear()
|
|
self.ap_thread = Thread(self, autoPyLoT, arg=args,
|
|
progressText='Picking trace...',
|
|
pb_widget=self.pb_widget,
|
|
redirect_stdout=True)
|
|
self.enable(False)
|
|
self.ap_thread.message.connect(self.add_log_item)
|
|
self.ap_thread.finished.connect(self.finish_picker)
|
|
self.figure_tabs.setCurrentIndex(4)
|
|
self.ap_thread.start()
|
|
#picks = autoPyLoT(self.parameter, fnames='None', iplot=2, fig_dict=self.fig_dict)
|
|
|
|
def finish_picker(self):
|
|
self.enable(True)
|
|
if not self.ap_thread._executed:
|
|
self._warn('Could not execute picker:\n{}'.format(
|
|
self.ap_thread._executedError))
|
|
return
|
|
self.pylot_picks = self.ap_thread.data
|
|
if not self.pylot_picks:
|
|
self._warn('No picks found. See terminal output.')
|
|
return
|
|
#renew tabs
|
|
#self.fill_figure_tabs()
|
|
self.set_stretch()
|
|
self.update.emit('Update')
|
|
self.figure_tabs.setCurrentIndex(1)
|
|
|
|
def enable(self, bool):
|
|
self.pick_button.setEnabled(bool)
|
|
self.paraBox.setEnabled(bool)
|
|
self.eventBox.setEnabled(bool)
|
|
self.stationBox.setEnabled(bool)
|
|
self.overview.setEnabled(bool)
|
|
self.p_tabs.setEnabled(bool)
|
|
self.s_tabs.setEnabled(bool)
|
|
|
|
def params_from_gui(self):
|
|
parameters = self.paraBox.params_from_gui()
|
|
if self.parent:
|
|
self.parent._inputs = parameters
|
|
return parameters
|
|
|
|
def set_stretch(self):
|
|
self.tune_layout.setStretch(0, 3)
|
|
self.tune_layout.setStretch(1, 1)
|
|
|
|
def clear_all(self):
|
|
if hasattr(self, 'pickDlg'):
|
|
if self.pickDlg:
|
|
self.pickDlg.setParent(None)
|
|
del(self.pickDlg)
|
|
if hasattr(self, 'overview'):
|
|
self.overview.setParent(None)
|
|
if hasattr(self, 'p_tabs'):
|
|
self.p_tabs.clear()
|
|
self.p_tabs.setParent(None)
|
|
if hasattr(self, 's_tabs'):
|
|
self.s_tabs.clear()
|
|
self.s_tabs.setParent(None)
|
|
|
|
def disable_autopickTabs(self):
|
|
self.toggle_autopickTabs(False)
|
|
|
|
def toggle_autopickTabs(self, bool):
|
|
self.figure_tabs.setTabEnabled(1, bool)
|
|
self.figure_tabs.setTabEnabled(2, bool)
|
|
self.figure_tabs.setTabEnabled(3, bool)
|
|
|
|
def _warn(self, message):
|
|
self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,
|
|
'Warning', message)
|
|
self.qmb.show()
|
|
|
|
|
|
class PylotParaBox(QtGui.QWidget):
|
|
def __init__(self, parameter, parent=None):
|
|
'''
|
|
Generate Widget containing parameters for automatic picking algorithm.
|
|
|
|
:param: parameter
|
|
:type: PylotParameter (object)
|
|
|
|
'''
|
|
QtGui.QWidget.__init__(self, parent)
|
|
self.parameter = parameter
|
|
self.tabs = QtGui.QTabWidget()
|
|
self.layout = QtGui.QVBoxLayout()
|
|
self._init_save_buttons()
|
|
self._init_tabs()
|
|
self._init_dialog_buttons()
|
|
self.labels = {}
|
|
self.boxes = {}
|
|
self.groupboxes = {}
|
|
self._exclusive_widgets = []
|
|
self._init_sublayouts()
|
|
self.setLayout(self.layout)
|
|
self.add_main_parameters_tab()
|
|
self.add_special_pick_parameters_tab()
|
|
self.params_to_gui()
|
|
self._toggle_advanced_settings()
|
|
self.resize(720, 1280)
|
|
self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
|
|
|
|
def _init_sublayouts(self):
|
|
self._main_layout = QtGui.QVBoxLayout()
|
|
self._advanced_layout = QtGui.QVBoxLayout()
|
|
self._create_advanced_cb()
|
|
|
|
def _init_save_buttons(self):
|
|
self._buttons_layout = QtGui.QHBoxLayout()
|
|
self.loadButton = QtGui.QPushButton('&Load settings')
|
|
self.saveButton = QtGui.QPushButton('&Save settings')
|
|
self.defaultsButton = QtGui.QPushButton('&Defaults')
|
|
self._buttons_layout.addWidget(self.loadButton)
|
|
self._buttons_layout.addWidget(self.saveButton)
|
|
self._buttons_layout.addWidget(self.defaultsButton)
|
|
self.layout.addLayout(self._buttons_layout)
|
|
self.loadButton.clicked.connect(self.openFile)
|
|
self.saveButton.clicked.connect(self.saveFile)
|
|
self.defaultsButton.clicked.connect(self.restoreDefaults)
|
|
|
|
def _init_tabs(self):
|
|
self.layout.addWidget(self.tabs)
|
|
|
|
def _init_dialog_buttons(self):
|
|
self._dialog_buttons = QtGui.QHBoxLayout()
|
|
self._okay = QtGui.QPushButton('Ok')
|
|
self._close = QtGui.QPushButton('Close')
|
|
self._apply = QtGui.QPushButton('Apply')
|
|
self._dialog_buttons.addWidget(self._okay)
|
|
self._dialog_buttons.addWidget(self._close)
|
|
self._dialog_buttons.addWidget(self._apply)
|
|
self._okay.clicked.connect(self.params_from_gui)
|
|
self._okay.clicked.connect(self.close)
|
|
self._apply.clicked.connect(self.params_from_gui)
|
|
self._close.clicked.connect(self.params_to_gui)
|
|
self._close.clicked.connect(self.close)
|
|
self.layout.addLayout(self._dialog_buttons)
|
|
|
|
def _create_advanced_cb(self):
|
|
self._advanced_cb = QtGui.QCheckBox('Enable Advanced Settings')
|
|
self._advanced_layout.insertWidget(0, self._advanced_cb)
|
|
self._advanced_cb.toggled.connect(self._toggle_advanced_settings)
|
|
|
|
def _toggle_advanced_settings(self):
|
|
if self._advanced_cb.isChecked():
|
|
self._enable_advanced(True)
|
|
else:
|
|
self._enable_advanced(False)
|
|
|
|
def _enable_advanced(self, enable):
|
|
for lst in self.parameter.get_special_para_names().values():
|
|
for param in lst:
|
|
box = self.boxes[param]
|
|
if type(box) is not list:
|
|
box.setEnabled(enable)
|
|
else:
|
|
for b in box:
|
|
b.setEnabled(enable)
|
|
|
|
def set_tune_mode(self, bool):
|
|
names = ['Directories', 'NLLoc',
|
|
'Seismic Moment']
|
|
for name in names:
|
|
self.hide_groupbox(name)
|
|
if bool:
|
|
self._apply.hide()
|
|
self._okay.hide()
|
|
self._close.hide()
|
|
else:
|
|
self._apply.show()
|
|
self._okay.show()
|
|
self._close.show()
|
|
|
|
def init_boxes(self, parameter_names):
|
|
grid = QtGui.QGridLayout()
|
|
|
|
for index1, name in enumerate(parameter_names):
|
|
text = name + ' [?]'
|
|
label = QtGui.QLabel(text)
|
|
default_item = self.parameter.get_defaults()[name]
|
|
tooltip = default_item['tooltip']
|
|
tooltip += ' | type: {}'.format(default_item['type'])
|
|
if not type(default_item['type']) == tuple:
|
|
typ = default_item['type']
|
|
box = self.create_box(typ, tooltip)
|
|
self.boxes[name] = box
|
|
elif type(default_item['type']) == tuple:
|
|
boxes = []
|
|
values = self.parameter[name]
|
|
for index2, val in enumerate(values):
|
|
typ = default_item['type'][index2]
|
|
boxes.append(self.create_box(typ, tooltip))
|
|
box = self.create_multi_box(boxes)
|
|
self.boxes[name] = boxes
|
|
self.labels[name] = label
|
|
label.setToolTip(tooltip)
|
|
grid.addWidget(label, index1, 1)
|
|
grid.addWidget(box, index1, 2)
|
|
return grid
|
|
|
|
def create_box(self, typ, tooltip):
|
|
if typ == str:
|
|
box = QtGui.QLineEdit()
|
|
elif typ == float:
|
|
box = QtGui.QDoubleSpinBox()
|
|
box.setDecimals(5)
|
|
box.setRange(-10e5, 10e5)
|
|
elif typ == int:
|
|
box = QtGui.QSpinBox()
|
|
elif typ == bool:
|
|
box = QtGui.QCheckBox()
|
|
else:
|
|
raise TypeError('Unrecognized type {}'.format(typ))
|
|
return box
|
|
|
|
def create_multi_box(self, boxes):
|
|
box = QtGui.QWidget()
|
|
hl = QtGui.QVBoxLayout()
|
|
for b in boxes:
|
|
hl.addWidget(b)
|
|
box.setLayout(hl)
|
|
return box
|
|
|
|
def add_tab(self, layout, name):
|
|
widget = QtGui.QWidget()
|
|
scrollA = QtGui.QScrollArea()
|
|
scrollA.setWidgetResizable(True)
|
|
scrollA.setWidget(widget)
|
|
widget.setLayout(layout)
|
|
self.tabs.addTab(scrollA, name)
|
|
|
|
def add_main_parameters_tab(self):
|
|
self.add_to_layout(self._main_layout, 'Directories',
|
|
self.parameter.get_main_para_names()['dirs'], 0)
|
|
self.add_to_layout(self._main_layout, 'NLLoc',
|
|
self.parameter.get_main_para_names()['nlloc'], 1)
|
|
self.add_to_layout(self._main_layout, 'Seismic Moment',
|
|
self.parameter.get_main_para_names()['smoment'], 2)
|
|
self.add_to_layout(self._main_layout, 'Local Magnitude',
|
|
self.parameter.get_main_para_names()['localmag'], 3)
|
|
self.add_to_layout(self._main_layout, 'Common Settings Characteristic Function',
|
|
self.parameter.get_main_para_names()['pick'], 4)
|
|
self.add_tab(self._main_layout, 'Main Settings')
|
|
|
|
def add_special_pick_parameters_tab(self):
|
|
self.add_to_layout(self._advanced_layout, 'Z-component',
|
|
self.parameter.get_special_para_names()['z'], 1)
|
|
self.add_to_layout(self._advanced_layout, 'H-components',
|
|
self.parameter.get_special_para_names()['h'], 2)
|
|
self.add_to_layout(self._advanced_layout, 'First-motion picker',
|
|
self.parameter.get_special_para_names()['fm'], 3)
|
|
self.add_to_layout(self._advanced_layout, 'Quality assessment',
|
|
self.parameter.get_special_para_names()['quality'], 4)
|
|
self.add_tab(self._advanced_layout, 'Advanced Settings')
|
|
|
|
# def gen_h_seperator(self):
|
|
# seperator = QtGui.QFrame()
|
|
# seperator.setFrameShape(QtGui.QFrame.HLine)
|
|
# return seperator
|
|
|
|
# def gen_headline(self, text):
|
|
# label=QtGui.QLabel(text)
|
|
# font=QtGui.QFont()
|
|
# font.setBold(True)
|
|
# label.setFont(font)
|
|
# return label
|
|
|
|
def refresh(self):
|
|
for groupbox in self.groupboxes.values():
|
|
layout = groupbox._parentLayout
|
|
position = groupbox._position
|
|
layout.insertWidget(position, groupbox)
|
|
|
|
def get_groupbox_exclusive(self, name):
|
|
widget = QtGui.QWidget(self, 1)
|
|
layout = QtGui.QVBoxLayout()
|
|
widget.setLayout(layout)
|
|
layout.addWidget(self.groupboxes[name])
|
|
self._exclusive_widgets.append(widget)
|
|
return widget
|
|
|
|
def get_groupbox_dialog(self, name):
|
|
widget = self.get_groupbox_exclusive(name)
|
|
dialog = QtGui.QDialog(self.parent())
|
|
layout = QtGui.QVBoxLayout()
|
|
dialog.setLayout(layout)
|
|
buttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok |
|
|
QDialogButtonBox.Cancel)
|
|
buttonbox.accepted.connect(dialog.accept)
|
|
buttonbox.accepted.connect(self.refresh)
|
|
buttonbox.accepted.connect(self.params_from_gui)
|
|
buttonbox.rejected.connect(dialog.reject)
|
|
buttonbox.rejected.connect(self.refresh)
|
|
buttonbox.rejected.connect(self.params_to_gui)
|
|
layout.addWidget(widget)
|
|
layout.addWidget(buttonbox)
|
|
self._exclusive_dialog = dialog
|
|
return dialog
|
|
|
|
def add_to_layout(self, layout, name, items, position):
|
|
groupbox = QtGui.QGroupBox(name)
|
|
groupbox._position = position
|
|
groupbox._parentLayout = layout
|
|
self.groupboxes[name] = groupbox
|
|
groupbox.setLayout(self.init_boxes(items))
|
|
layout.insertWidget(position, groupbox)
|
|
|
|
def show_groupboxes(self):
|
|
for name in self.groupboxes.keys():
|
|
self.show_groupbox(name)
|
|
self._advanced_cb.show()
|
|
|
|
def hide_groupboxes(self):
|
|
for name in self.groupboxes.keys():
|
|
self.hide_groupbox(name)
|
|
self._advanced_cb.hide()
|
|
|
|
def show_groupbox(self, name):
|
|
if name in self.groupboxes.keys():
|
|
self.groupboxes[name].show()
|
|
else:
|
|
print('Groupbox {} not part of object.'.format(name))
|
|
|
|
def hide_groupbox(self, name):
|
|
if name in self.groupboxes.keys():
|
|
self.groupboxes[name].hide()
|
|
else:
|
|
print('Groupbox {} not part of object.'.format(name))
|
|
|
|
def show_file_buttons(self):
|
|
self.saveButton.show()
|
|
self.loadButton.show()
|
|
self.defaultsButton.show()
|
|
|
|
def hide_file_buttons(self):
|
|
self.saveButton.hide()
|
|
self.loadButton.hide()
|
|
self.defaultsButton.hide()
|
|
|
|
def show_parameter(self, name=None):
|
|
if not name:
|
|
for name in self.boxes.keys():
|
|
self.show_parameter(name)
|
|
return
|
|
if name in self.boxes.keys() and name in self.labels.keys():
|
|
# comprising case type(self.boxes[name]) == list
|
|
boxes = self.boxes[name]
|
|
if not type(boxes) == list:
|
|
boxes = [boxes]
|
|
for box in boxes:
|
|
box.show()
|
|
self.labels[name].show()
|
|
else:
|
|
print('Parameter {} not part of object.'.format(name))
|
|
|
|
def hide_parameter(self, name=None):
|
|
if not name:
|
|
for name in self.boxes.keys():
|
|
self.hide_parameter(name)
|
|
return
|
|
if name in self.boxes.keys() and name in self.labels.keys():
|
|
# comprising case type(self.boxes[name]) == list
|
|
boxes = self.boxes[name]
|
|
if not type(boxes) == list:
|
|
boxes = [boxes]
|
|
for box in boxes:
|
|
box.hide()
|
|
self.labels[name].hide()
|
|
else:
|
|
print('Parameter {} not part of object.'.format(name))
|
|
|
|
def params_from_gui(self):
|
|
for param in self.parameter.get_all_para_names():
|
|
box = self.boxes[param]
|
|
value = self.getValue(box)
|
|
self.parameter.checkValue(param, value)
|
|
self.parameter.setParamKV(param, value)
|
|
return self.parameter
|
|
|
|
def params_to_gui(self, tuneMode=False):
|
|
for param in self.parameter.get_all_para_names():
|
|
if param == 'eventID':
|
|
if tuneMode:
|
|
continue
|
|
box = self.boxes[param]
|
|
value = self.parameter[param]
|
|
#self.parameter.checkValue(param, value)
|
|
self.setValue(box, value)
|
|
|
|
def setValue(self, box, value):
|
|
if type(box) == QtGui.QLineEdit:
|
|
box.setText(str(value))
|
|
elif type(box) == QtGui.QSpinBox or type(box) == QtGui.QDoubleSpinBox:
|
|
if not value:
|
|
value = 0.
|
|
box.setValue(value)
|
|
elif type(box) == QtGui.QCheckBox:
|
|
if value == 'True':
|
|
value = True
|
|
if value == 'False':
|
|
value = False
|
|
box.setChecked(value)
|
|
elif type(box) == list:
|
|
for index, b in enumerate(box):
|
|
self.setValue(b, value[index])
|
|
|
|
def getValue(self, box):
|
|
if type(box) == QtGui.QLineEdit:
|
|
value = str(box.text())
|
|
elif type(box) == QtGui.QSpinBox or type(box) == QtGui.QDoubleSpinBox:
|
|
value = box.value()
|
|
elif type(box) == QtGui.QCheckBox:
|
|
value = box.isChecked()
|
|
elif type(box) == list:
|
|
value = []
|
|
for b in box:
|
|
value.append(self.getValue(b))
|
|
value = tuple(value)
|
|
return value
|
|
|
|
def openFile(self):
|
|
fd = QtGui.QFileDialog()
|
|
fname = fd.getOpenFileName(self, 'Browse for settings file.',
|
|
filter='PyLoT input file (*.in)')
|
|
if fname[0]:
|
|
try:
|
|
self.parameter.from_file(fname[0])
|
|
self.params_to_gui(tuneMode=True)
|
|
except Exception as e:
|
|
self._warn('Could not open file {}:\n{}'.format(fname[0], e))
|
|
return
|
|
|
|
def saveFile(self):
|
|
fd = QtGui.QFileDialog()
|
|
fname = fd.getSaveFileName(self, 'Browse for settings file.',
|
|
filter='PyLoT input file (*.in)')
|
|
if fname[0]:
|
|
try:
|
|
self.params_from_gui()
|
|
self.parameter.export2File(fname[0])
|
|
except Exception as e:
|
|
self._warn('Could not save file {}:\n{}'.format(fname[0], e))
|
|
return
|
|
|
|
def restoreDefaults(self):
|
|
try:
|
|
self.parameter.reset_defaults()
|
|
self.params_to_gui(tuneMode=True)
|
|
except Exception as e:
|
|
self._warn('Could not restore defaults:\n{}'.format(e))
|
|
return
|
|
|
|
def show(self):
|
|
self.refresh()
|
|
self.show_parameter()
|
|
if hasattr(self, '_exclusive_dialog'):
|
|
self._exclusive_dialog.close()
|
|
self._exclusive_widgets = []
|
|
QtGui.QWidget.show(self)
|
|
|
|
def _warn(self, message):
|
|
self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,
|
|
'Warning', message)
|
|
self.qmb.show()
|
|
|
|
|
|
class PropertiesDlg(QDialog):
|
|
def __init__(self, parent=None, infile=None):
|
|
super(PropertiesDlg, self).__init__(parent)
|
|
|
|
self.infile = infile
|
|
|
|
self.setWindowTitle("PyLoT Properties")
|
|
self.tabWidget = QTabWidget()
|
|
self.tabWidget.addTab(InputsTab(self), "Inputs")
|
|
self.tabWidget.addTab(OutputsTab(self), "Outputs")
|
|
self.tabWidget.addTab(PhasesTab(self), "Phases")
|
|
self.tabWidget.addTab(GraphicsTab(self), "Graphics")
|
|
#self.tabWidget.addTab(LocalisationTab(self), "Loc. Tools")
|
|
self.tabWidget.addTab(LocalisationTab(self), "NonLinLoc")
|
|
self.tabWidget.addTab(ChannelOrderTab(self), "Channel Order")
|
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok |
|
|
QDialogButtonBox.Apply |
|
|
QDialogButtonBox.Close |
|
|
QDialogButtonBox.RestoreDefaults)
|
|
|
|
layout = QVBoxLayout()
|
|
layout.addWidget(self.tabWidget)
|
|
layout.addWidget(self.buttonBox)
|
|
self.setLayout(layout)
|
|
self.setFixedWidth(700)
|
|
|
|
self.buttonBox.accepted.connect(self.accept)
|
|
self.buttonBox.rejected.connect(self.reject)
|
|
self.buttonBox.button(QDialogButtonBox.Apply).clicked.connect(self.apply)
|
|
self.buttonBox.button(QDialogButtonBox.RestoreDefaults).clicked.connect(self.restore)
|
|
|
|
def getinfile(self):
|
|
return self.infile
|
|
|
|
def accept(self, *args, **kwargs):
|
|
self.apply()
|
|
QDialog.accept(self)
|
|
|
|
def apply(self):
|
|
for widint in range(self.tabWidget.count()):
|
|
curwid = self.tabWidget.widget(widint)
|
|
values = curwid.getValues()
|
|
if values is not None:
|
|
self.setValues(values)
|
|
|
|
def close(self):
|
|
self.reset_current()
|
|
QDialog.close(self)
|
|
|
|
def show(self):
|
|
self.keep_current()
|
|
QDialog.show(self)
|
|
|
|
def restore(self):
|
|
for widint in range(self.tabWidget.count()):
|
|
curwid = self.tabWidget.widget(widint)
|
|
values = curwid.resetValues(self.getinfile())
|
|
if values is not None:
|
|
self.setValues(values)
|
|
|
|
def keep_current(self):
|
|
self._current_values = []
|
|
for widint in range(self.tabWidget.count()):
|
|
curwid = self.tabWidget.widget(widint)
|
|
values = curwid.getValues()
|
|
if values is not None:
|
|
self._current_values.append(values)
|
|
|
|
def reset_current(self):
|
|
for values in self._current_values():
|
|
self.setValues(values)
|
|
|
|
|
|
@staticmethod
|
|
def setValues(tabValues):
|
|
settings = QSettings()
|
|
compclass = settings.value('compclass')
|
|
if not compclass:
|
|
print('Warning: No settings for channel components found. Using default')
|
|
compclass = SetChannelComponents()
|
|
|
|
for setting, value in tabValues.items():
|
|
settings.setValue(setting, value)
|
|
if value is not None:
|
|
if setting.startswith('Channel Z'):
|
|
component = 'Z'
|
|
compclass.setCompPosition(value, component, False)
|
|
elif setting.startswith('Channel E'):
|
|
component = 'E'
|
|
compclass.setCompPosition(value, component, False)
|
|
elif setting.startswith('Channel N'):
|
|
component = 'N'
|
|
compclass.setCompPosition(value, component, False)
|
|
|
|
settings.sync()
|
|
|
|
|
|
class PropTab(QWidget):
|
|
def __init__(self, parent=None):
|
|
super(PropTab, self).__init__(parent)
|
|
|
|
def getValues(self):
|
|
return None
|
|
|
|
def resetValues(self, infile=None):
|
|
return None
|
|
|
|
|
|
class InputsTab(PropTab):
|
|
def __init__(self, parent, infile=None):
|
|
super(InputsTab, self).__init__(parent)
|
|
|
|
settings = QSettings()
|
|
pylot_user = getpass.getuser()
|
|
fulluser = settings.value("user/FullName")
|
|
login = settings.value("user/Login")
|
|
|
|
fullNameLabel = QLabel("Full name for user '{0}': ".format(pylot_user))
|
|
|
|
# get the full name of the actual user
|
|
self.fullNameEdit = QLineEdit()
|
|
try:
|
|
self.fullNameEdit.setText(fulluser)
|
|
except TypeError as e:
|
|
self.fullNameEdit.setText(fulluser[0])
|
|
|
|
# information about data structure
|
|
dataroot = settings.value("data/dataRoot")
|
|
curstructure = settings.value("data/Structure")
|
|
dataDirLabel = QLabel("data root directory: ")
|
|
self.dataDirEdit = QLineEdit()
|
|
self.dataDirEdit.setText(dataroot)
|
|
self.dataDirEdit.selectAll()
|
|
structureLabel = QLabel("data structure: ")
|
|
self.structureSelect = QComboBox()
|
|
|
|
from pylot.core.util.structure import DATASTRUCTURE
|
|
|
|
self.structureSelect.addItems(DATASTRUCTURE.keys())
|
|
|
|
dsind = findComboBoxIndex(self.structureSelect, curstructure)
|
|
|
|
self.structureSelect.setCurrentIndex(dsind)
|
|
|
|
layout = QGridLayout()
|
|
layout.addWidget(dataDirLabel, 0, 0)
|
|
layout.addWidget(self.dataDirEdit, 0, 1)
|
|
layout.addWidget(fullNameLabel, 1, 0)
|
|
layout.addWidget(self.fullNameEdit, 1, 1)
|
|
layout.addWidget(structureLabel, 2, 0)
|
|
layout.addWidget(self.structureSelect, 2, 1)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def getValues(self):
|
|
values = {"data/dataRoot": self.dataDirEdit.text(),
|
|
"user/FullName": self.fullNameEdit.text(),
|
|
"data/Structure": self.structureSelect.currentText()}
|
|
return values
|
|
|
|
def resetValues(self, infile):
|
|
para = PylotParameter(infile)
|
|
datstruct = para.get('datastructure')
|
|
if datstruct == 'SeisComp':
|
|
index = 0
|
|
else:
|
|
index = 2
|
|
datapath = para.get('datapath')
|
|
rootpath = para.get('rootpath')
|
|
database = para.get('database')
|
|
if isinstance(database, int):
|
|
database = str(database)
|
|
path = os.path.join(os.path.expanduser('~'), rootpath, datapath, database)
|
|
values = {"data/dataRoot": self.dataDirEdit.setText("%s" % path),
|
|
"user/FullName": self.fullNameEdit.text(),
|
|
"data/Structure": self.structureSelect.setCurrentIndex(index)}
|
|
return values
|
|
|
|
class OutputsTab(PropTab):
|
|
def __init__(self, parent=None, infile=None):
|
|
super(OutputsTab, self).__init__(parent)
|
|
|
|
settings = QSettings()
|
|
curval = settings.value("output/Format", None)
|
|
|
|
eventOutputLabel = QLabel("event/picks output format")
|
|
self.eventOutputComboBox = QComboBox()
|
|
eventoutputformats = OUTPUTFORMATS.keys()
|
|
self.eventOutputComboBox.addItems(eventoutputformats)
|
|
|
|
ind = findComboBoxIndex(self.eventOutputComboBox, curval)
|
|
|
|
self.eventOutputComboBox.setCurrentIndex(ind)
|
|
layout = QGridLayout()
|
|
layout.addWidget(eventOutputLabel, 0, 0)
|
|
layout.addWidget(self.eventOutputComboBox, 0, 1)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def getValues(self):
|
|
values = {"output/Format": self.eventOutputComboBox.currentText()}
|
|
return values
|
|
|
|
def resetValues(self, infile):
|
|
values = {"output/Format": self.eventOutputComboBox.setCurrentIndex(1)}
|
|
return values
|
|
|
|
|
|
class PhasesTab(PropTab):
|
|
def __init__(self, parent=None):
|
|
super(PhasesTab, self).__init__(parent)
|
|
|
|
self.PphasesEdit = QLineEdit()
|
|
self.SphasesEdit = QLineEdit()
|
|
|
|
PphasesLabel = QLabel("P Phases to pick")
|
|
SphasesLabel = QLabel("S Phases to pick")
|
|
|
|
settings = QSettings()
|
|
Pphases = settings.value('p_phases')
|
|
Sphases = settings.value('s_phases')
|
|
|
|
self.PphasesEdit.setText("%s" % Pphases)
|
|
self.SphasesEdit.setText("%s" % Sphases)
|
|
|
|
layout = QGridLayout()
|
|
layout.addWidget(PphasesLabel, 0, 0)
|
|
layout.addWidget(SphasesLabel, 1, 0)
|
|
|
|
layout.addWidget(self.PphasesEdit, 0, 1)
|
|
layout.addWidget(self.SphasesEdit, 1, 1)
|
|
self.setLayout(layout)
|
|
|
|
def getValues(self):
|
|
values = {'p_phases': self.PphasesEdit.text(),
|
|
's_phases': self.SphasesEdit.text()}
|
|
return values
|
|
|
|
def resetValues(self, infile=None):
|
|
Pphases = 'P, Pg, Pn, PmP, P1, P2, P3'
|
|
Sphases = 'S, Sg, Sn, SmS, S1, S2, S3'
|
|
values = {'p_phases': self.PphasesEdit.setText(Pphases),
|
|
's_phases': self.SphasesEdit.setText(Sphases)}
|
|
return values
|
|
|
|
|
|
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)
|
|
|
|
def init_layout(self):
|
|
self.main_layout = QGridLayout()
|
|
|
|
def add_nth_sample(self):
|
|
settings = QSettings()
|
|
nth_sample = settings.value("nth_sample")
|
|
if not nth_sample:
|
|
nth_sample = 1
|
|
|
|
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))
|
|
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(),
|
|
'pyqtgraphic': self.checkbox_pg.isChecked()}
|
|
return values
|
|
|
|
def resetValues(self, infile=None):
|
|
values = {'nth_sample': self.spinbox_nth_sample.setValue(1),
|
|
'pyqtgraphic': self.checkbox_pg.setChecked(True)}
|
|
return values
|
|
|
|
|
|
class ChannelOrderTab(PropTab):
|
|
def __init__(self, parent=None, infile=None):
|
|
super(ChannelOrderTab, self).__init__(parent)
|
|
|
|
settings = QSettings()
|
|
compclass = settings.value('compclass')
|
|
if not compclass:
|
|
print('Warning: No settings for channel components found. Using default')
|
|
compclass = SetChannelComponents()
|
|
|
|
ChannelOrderLabelZ = QLabel("Channel Z [up/down, default=3]")
|
|
ChannelOrderLabelN = QLabel("Channel N [north/south, default=1]")
|
|
ChannelOrderLabelE = QLabel("Channel E [east/west, default=2]")
|
|
self.ChannelOrderZEdit = QLineEdit()
|
|
self.ChannelOrderZEdit.setMaxLength(1)
|
|
self.ChannelOrderZEdit.setFixedSize(20, 20)
|
|
self.ChannelOrderNEdit = QLineEdit()
|
|
self.ChannelOrderNEdit.setMaxLength(1)
|
|
self.ChannelOrderNEdit.setFixedSize(20, 20)
|
|
self.ChannelOrderEEdit = QLineEdit()
|
|
self.ChannelOrderEEdit.setMaxLength(1)
|
|
self.ChannelOrderEEdit.setFixedSize(20, 20)
|
|
# get channel order settings
|
|
zcomp = compclass.getCompPosition('Z')
|
|
ncomp = compclass.getCompPosition('N')
|
|
ecomp = compclass.getCompPosition('E')
|
|
self.ChannelOrderZEdit.setText("%s" % zcomp)
|
|
self.ChannelOrderNEdit.setText("%s" % ncomp)
|
|
self.ChannelOrderEEdit.setText("%s" % ecomp)
|
|
|
|
layout = QGridLayout()
|
|
layout.addWidget(ChannelOrderLabelZ, 0, 0)
|
|
layout.addWidget(ChannelOrderLabelN, 1, 0)
|
|
layout.addWidget(ChannelOrderLabelE, 2, 0)
|
|
layout.addWidget(self.ChannelOrderZEdit, 0, 1)
|
|
layout.addWidget(self.ChannelOrderNEdit, 1, 1)
|
|
layout.addWidget(self.ChannelOrderEEdit, 2, 1)
|
|
|
|
self.setLayout(layout)
|
|
self.connectSignals()
|
|
|
|
def connectSignals(self):
|
|
self.ChannelOrderZEdit.textEdited.connect(self.checkDoubleZ)
|
|
self.ChannelOrderNEdit.textEdited.connect(self.checkDoubleN)
|
|
self.ChannelOrderEEdit.textEdited.connect(self.checkDoubleE)
|
|
|
|
def checkDoubleZ(self, text):
|
|
self.checkDouble(text, 'Z')
|
|
|
|
def checkDoubleN(self, text):
|
|
self.checkDouble(text, 'N')
|
|
|
|
def checkDoubleE(self, text):
|
|
self.checkDouble(text, 'E')
|
|
|
|
def checkDouble(self, text, comp):
|
|
channelOrderEdits = {
|
|
'Z': self.ChannelOrderZEdit,
|
|
'N': self.ChannelOrderNEdit,
|
|
'E': self.ChannelOrderEEdit
|
|
}
|
|
for key in channelOrderEdits.keys():
|
|
if key == comp:
|
|
continue
|
|
if str(channelOrderEdits[key].text()) == str(text):
|
|
channelOrderEdits[key].setText('')
|
|
|
|
def getValues(self):
|
|
values = {"Channel Z [up/down, default=3]": int(self.ChannelOrderZEdit.text()),
|
|
"Channel N [north/south, default=1]": int(self.ChannelOrderNEdit.text()),
|
|
"Channel E [east/west, default=2]": int(self.ChannelOrderEEdit.text())}
|
|
return values
|
|
|
|
def resetValues(self, infile=None):
|
|
Zdefault = 3
|
|
Ndefault = 1
|
|
Edefault = 2
|
|
values = {"Channel Z [up/down, default=3]": self.ChannelOrderZEdit.setText("%d" % Zdefault),
|
|
"Channel N [north/south, default=1]": self.ChannelOrderNEdit.setText("%d" % Ndefault),
|
|
"Channel E [east/west, default=2]": self.ChannelOrderEEdit.setText("%d" % Edefault)}
|
|
return values
|
|
|
|
# MP MP: No idea why this function exists!?
|
|
# def getComponents(self):
|
|
# self.CompName = dict(Z='10', N='11', E='12')
|
|
|
|
|
|
class LocalisationTab(PropTab):
|
|
def __init__(self, parent=None, infile=None):
|
|
super(LocalisationTab, self).__init__(parent)
|
|
|
|
settings = QSettings()
|
|
curtool = settings.value("loc/tool", None)
|
|
|
|
#loctoollabel = QLabel("location tool")
|
|
self.locToolComboBox = QComboBox()
|
|
#loctools = LOCTOOLS.keys()
|
|
#self.locToolComboBox.addItems(loctools)
|
|
|
|
#toolind = findComboBoxIndex(self.locToolComboBox, curtool)
|
|
|
|
#self.locToolComboBox.setCurrentIndex(toolind)
|
|
|
|
curroot = settings.value("{0}/rootPath".format(curtool), None)
|
|
curbin = settings.value("{0}/binPath".format(curtool), None)
|
|
|
|
self.rootlabel = QLabel("root directory")
|
|
self.binlabel = QLabel("bin directory")
|
|
|
|
self.rootedit = QLineEdit('')
|
|
self.binedit = QLineEdit('')
|
|
|
|
if curroot is not None:
|
|
self.rootedit.setText(curroot)
|
|
if curbin is not None:
|
|
self.binedit.setText(curbin)
|
|
|
|
rootBrowse = QPushButton('...', self)
|
|
rootBrowse.clicked.connect(lambda: self.selectDirectory(self.rootedit))
|
|
|
|
binBrowse = QPushButton('...', self)
|
|
binBrowse.clicked.connect(lambda: self.selectDirectory(self.binedit))
|
|
|
|
#self.locToolComboBox.currentIndexChanged.connect(self.updateUi)
|
|
|
|
self.updateUi()
|
|
|
|
layout = QGridLayout()
|
|
#layout.addWidget(loctoollabel, 0, 0)
|
|
#layout.addWidget(self.locToolComboBox, 0, 1)
|
|
layout.addWidget(self.rootlabel, 1, 0)
|
|
layout.addWidget(self.rootedit, 1, 1)
|
|
layout.addWidget(rootBrowse, 1, 2)
|
|
layout.addWidget(self.binlabel, 2, 0)
|
|
layout.addWidget(self.binedit, 2, 1)
|
|
layout.addWidget(binBrowse, 2, 2)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def updateUi(self):
|
|
curtool = self.locToolComboBox.currentText()
|
|
#if curtool is not None:
|
|
self.rootlabel.setText("{0} root directory".format(curtool))
|
|
self.binlabel.setText("{0} bin directory".format(curtool))
|
|
|
|
def selectDirectory(self, edit):
|
|
selected_directory = QFileDialog.getExistingDirectory()
|
|
# check if string is empty
|
|
if selected_directory:
|
|
edit.setText(selected_directory)
|
|
|
|
def getValues(self):
|
|
loctool = self.locToolComboBox.currentText()
|
|
values = {"{0}/rootPath".format(loctool): self.rootedit.text(),
|
|
"{0}/binPath".format(loctool): self.binedit.text()}
|
|
#"loc/tool": loctool}
|
|
return values
|
|
|
|
def resetValues(self, infile):
|
|
para = PylotParameter(infile)
|
|
nllocroot = para.get('nllocroot')
|
|
nllocbin = para.get('nllocbin')
|
|
loctool = self.locToolComboBox.setCurrentIndex(3)
|
|
values = {"nll/rootPath": self.rootedit.setText("%s" % nllocroot),
|
|
"nll/binPath": self.binedit.setText("%s" % nllocbin)}
|
|
|
|
|
|
class NewEventDlg(QDialog):
|
|
def __init__(self, parent=None, titleString="Create a new event"):
|
|
"""
|
|
QDialog object utilized to create a new event manually.
|
|
"""
|
|
super(NewEventDlg, self).__init__()
|
|
|
|
self.setupUI()
|
|
|
|
now = datetime.datetime.now()
|
|
self.eventTimeEdit.setDateTime(now)
|
|
# event dates in the future are forbidden
|
|
self.eventTimeEdit.setMaximumDateTime(now)
|
|
|
|
self.latEdit.setText("51.0000")
|
|
self.lonEdit.setText("7.0000")
|
|
self.depEdit.setText("10.0")
|
|
|
|
self.buttonBox.accepted.connect(self.accept)
|
|
self.buttonBox.rejected.connect(self.reject)
|
|
|
|
def getValues(self):
|
|
return {'origintime': self.eventTimeEdit.dateTime().toPython(),
|
|
'latitude': self.latEdit.text(),
|
|
'longitude': self.lonEdit.text(),
|
|
'depth': self.depEdit.text()}
|
|
|
|
def setupUI(self):
|
|
# create widget objects
|
|
timeLabel = QLabel()
|
|
timeLabel.setText("Select time: ")
|
|
self.eventTimeEdit = QDateTimeEdit()
|
|
latLabel = QLabel()
|
|
latLabel.setText("Latitude: ")
|
|
self.latEdit = QLineEdit()
|
|
lonLabel = QLabel()
|
|
lonLabel.setText("Longitude: ")
|
|
self.lonEdit = QLineEdit()
|
|
depLabel = QLabel()
|
|
depLabel.setText("Depth: ")
|
|
self.depEdit = QLineEdit()
|
|
|
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok |
|
|
QDialogButtonBox.Cancel)
|
|
|
|
grid = QGridLayout()
|
|
grid.addWidget(timeLabel, 0, 0)
|
|
grid.addWidget(self.eventTimeEdit, 0, 1)
|
|
grid.addWidget(latLabel, 1, 0)
|
|
grid.addWidget(self.latEdit, 1, 1)
|
|
grid.addWidget(lonLabel, 2, 0)
|
|
grid.addWidget(self.lonEdit, 2, 1)
|
|
grid.addWidget(depLabel, 3, 0)
|
|
grid.addWidget(self.depEdit, 3, 1)
|
|
grid.addWidget(self.buttonBox, 4, 1)
|
|
|
|
self.setLayout(grid)
|
|
|
|
|
|
class FilterOptionsDialog(QDialog):
|
|
def __init__(self, parent=None, titleString="Filter options",
|
|
filterOptions=None):
|
|
"""
|
|
PyLoT widget FilterOptionsDialog is a QDialog object. It is an UI to
|
|
adjust parameters for filtering seismic data.
|
|
"""
|
|
super(FilterOptionsDialog, self).__init__()
|
|
|
|
if parent is not None and parent.getFilterOptions():
|
|
self.filterOptions = parent.getFilterOptions()
|
|
elif filterOptions is not None:
|
|
self.filterOptions = FilterOptions(filterOptions)
|
|
else:
|
|
self.filterOptions = FilterOptions()
|
|
|
|
_enable = True
|
|
if self.getFilterOptions().getFilterType() is None:
|
|
_enable = False
|
|
|
|
self.freqminLabel = QLabel()
|
|
self.freqminLabel.setText("minimum:")
|
|
self.freqminSpinBox = QDoubleSpinBox()
|
|
self.freqminSpinBox.setRange(5e-7, 1e6)
|
|
self.freqminSpinBox.setDecimals(2)
|
|
self.freqminSpinBox.setSuffix(' Hz')
|
|
self.freqminSpinBox.setEnabled(_enable)
|
|
|
|
self.freqmaxLabel = QLabel()
|
|
self.freqmaxLabel.setText("maximum:")
|
|
self.freqmaxSpinBox = QDoubleSpinBox()
|
|
self.freqmaxSpinBox.setRange(5e-7, 1e6)
|
|
self.freqmaxSpinBox.setDecimals(2)
|
|
self.freqmaxSpinBox.setSuffix(' Hz')
|
|
|
|
if _enable:
|
|
self.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0])
|
|
if self.getFilterOptions().getFilterType() in ['bandpass',
|
|
'bandstop']:
|
|
self.freqmaxSpinBox.setValue(
|
|
self.getFilterOptions().getFreq()[1])
|
|
else:
|
|
try:
|
|
self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq())
|
|
self.freqminSpinBox.setValue(self.getFilterOptions().getFreq())
|
|
except TypeError as e:
|
|
print(e)
|
|
self.freqmaxSpinBox.setValue(1.)
|
|
self.freqminSpinBox.setValue(.1)
|
|
|
|
typeOptions = [None, "bandpass", "bandstop", "lowpass", "highpass"]
|
|
|
|
self.orderLabel = QLabel()
|
|
self.orderLabel.setText("Order:")
|
|
self.orderSpinBox = QSpinBox()
|
|
self.orderSpinBox.setRange(2, 10)
|
|
self.orderSpinBox.setEnabled(_enable)
|
|
self.selectTypeLabel = QLabel()
|
|
self.selectTypeLabel.setText("Select filter type:")
|
|
self.selectTypeCombo = QComboBox()
|
|
self.selectTypeCombo.addItems(typeOptions)
|
|
self.selectTypeCombo.setCurrentIndex(typeOptions.index(self.getFilterOptions().getFilterType()))
|
|
self.selectTypeLayout = QVBoxLayout()
|
|
self.selectTypeLayout.addWidget(self.orderLabel)
|
|
self.selectTypeLayout.addWidget(self.orderSpinBox)
|
|
self.selectTypeLayout.addWidget(self.selectTypeLabel)
|
|
self.selectTypeLayout.addWidget(self.selectTypeCombo)
|
|
|
|
self.freqGroupBox = QGroupBox("Frequency range")
|
|
self.freqGroupLayout = QGridLayout()
|
|
self.freqGroupLayout.addWidget(self.freqminLabel, 0, 0)
|
|
self.freqGroupLayout.addWidget(self.freqminSpinBox, 0, 1)
|
|
self.freqGroupLayout.addWidget(self.freqmaxLabel, 1, 0)
|
|
self.freqGroupLayout.addWidget(self.freqmaxSpinBox, 1, 1)
|
|
self.freqGroupBox.setLayout(self.freqGroupLayout)
|
|
|
|
self.freqmaxSpinBox.setEnabled(_enable)
|
|
|
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok |
|
|
QDialogButtonBox.Cancel)
|
|
|
|
grid = QGridLayout()
|
|
grid.addWidget(self.freqGroupBox, 0, 2, 1, 2)
|
|
grid.addLayout(self.selectTypeLayout, 1, 2, 1, 2)
|
|
grid.addWidget(self.buttonBox, 2, 2, 1, 2)
|
|
|
|
self.setLayout(grid)
|
|
|
|
self.freqminSpinBox.valueChanged.connect(self.updateUi)
|
|
self.freqmaxSpinBox.valueChanged.connect(self.updateUi)
|
|
self.orderSpinBox.valueChanged.connect(self.updateUi)
|
|
self.selectTypeCombo.currentIndexChanged.connect(self.updateUi)
|
|
self.buttonBox.accepted.connect(self.accept)
|
|
self.buttonBox.rejected.connect(self.reject)
|
|
|
|
def updateUi(self):
|
|
type = self.selectTypeCombo.currentText()
|
|
_enable = type in ['bandpass', 'bandstop']
|
|
freq = [self.freqminSpinBox.value(), self.freqmaxSpinBox.value()]
|
|
self.freqmaxLabel.setEnabled(_enable)
|
|
self.freqmaxSpinBox.setEnabled(_enable)
|
|
|
|
if not _enable:
|
|
self.freqminLabel.setText("cutoff:")
|
|
self.freqmaxSpinBox.setValue(freq[0])
|
|
freq.remove(freq[1])
|
|
else:
|
|
self.freqminLabel.setText("minimum:")
|
|
if not isSorted(freq):
|
|
QMessageBox.warning(self, "Value error",
|
|
"Maximum frequency must be at least the "
|
|
"same value as minimum frequency (notch)!")
|
|
self.freqmaxSpinBox.setValue(freq[0])
|
|
self.freqmaxSpinBox.selectAll()
|
|
self.freqmaxSpinBox.setFocus()
|
|
return
|
|
|
|
self.getFilterOptions().setFilterType(type)
|
|
self.getFilterOptions().setFreq(freq)
|
|
self.getFilterOptions().setOrder(self.orderSpinBox.value())
|
|
|
|
def getFilterOptions(self):
|
|
return self.filterOptions
|
|
|
|
@staticmethod
|
|
def getFilterObject():
|
|
dlg = FilterOptionsDialog()
|
|
if dlg.exec_():
|
|
return dlg.getFilterOptions()
|
|
return None
|
|
|
|
def accept(self):
|
|
self.updateUi()
|
|
QDialog.accept(self)
|
|
|
|
|
|
class LoadDataDlg(QDialog):
|
|
def __init__(self, parent=None):
|
|
super(LoadDataDlg, self).__init__(parent)
|
|
|
|
pass
|
|
|
|
|
|
class HelpForm(QDialog):
|
|
def __init__(self, page=QUrl('https://ariadne.geophysik.rub.de/trac/PyLoT'),
|
|
parent=None):
|
|
super(HelpForm, self).__init__(parent)
|
|
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
self.setAttribute(Qt.WA_GroupLeader)
|
|
|
|
backAction = QAction(QIcon(":/back.png"), "&Back", self)
|
|
backAction.setShortcut(QKeySequence.Back)
|
|
homeAction = QAction(QIcon(":/home.png"), "&Home", self)
|
|
homeAction.setShortcut("Home")
|
|
self.pageLabel = QLabel()
|
|
|
|
toolBar = QToolBar()
|
|
toolBar.addAction(backAction)
|
|
toolBar.addAction(homeAction)
|
|
toolBar.addWidget(self.pageLabel)
|
|
self.webBrowser = QWebView()
|
|
self.webBrowser.load(page)
|
|
|
|
layout = QVBoxLayout()
|
|
layout.addWidget(toolBar)
|
|
layout.addWidget(self.webBrowser, 1)
|
|
self.setLayout(layout)
|
|
|
|
self.connect(backAction, Signal("triggered()"),
|
|
self.webBrowser, Slot("backward()"))
|
|
self.connect(homeAction, Signal("triggered()"),
|
|
self.webBrowser, Slot("home()"))
|
|
self.connect(self.webBrowser, Signal("sourceChanged(QUrl)"),
|
|
self.updatePageTitle)
|
|
|
|
self.resize(400, 600)
|
|
self.setWindowTitle("{0} Help".format(QApplication.applicationName()))
|
|
|
|
def updatePageTitle(self):
|
|
self.pageLabel.setText(self.webBrowser.documentTitle())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import doctest
|
|
|
|
doctest.testmod()
|