4356 lines
156 KiB
Python
4356 lines
156 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Created on Wed Mar 19 11:27:35 2014
|
|
|
|
@author: sebastianw
|
|
"""
|
|
|
|
import copy
|
|
import datetime
|
|
import getpass
|
|
import multiprocessing
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
import numpy as np
|
|
|
|
from matplotlib.figure import Figure
|
|
from pylot.core.util.utils import find_horizontals, identifyPhase, loopIdentifyPhase, trim_station_components, \
|
|
identifyPhaseID, check4rotated
|
|
|
|
try:
|
|
from matplotlib.backends.backend_qt5agg import FigureCanvas
|
|
except ImportError:
|
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT
|
|
from matplotlib.widgets import MultiCursor
|
|
from matplotlib.tight_layout import get_renderer, get_subplotspec_list, get_tight_layout_figure
|
|
|
|
from PySide2 import QtCore, QtGui
|
|
from PySide2.QtGui import QIcon, QPixmap, QKeySequence
|
|
from PySide2.QtWidgets import QAction, QApplication, QCheckBox, QComboBox, \
|
|
QDateTimeEdit, QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, \
|
|
QGridLayout, QLabel, QLineEdit, QMessageBox, \
|
|
QSpinBox, QTabWidget, QToolBar, QVBoxLayout, QHBoxLayout, QWidget, \
|
|
QPushButton, QFileDialog, QInputDialog
|
|
from PySide2.QtCore import QSettings, Qt, QUrl, Signal, Slot
|
|
from PySide2.QtWebEngineWidgets import QWebEngineView as QWebView
|
|
from obspy import Stream, UTCDateTime
|
|
from obspy.core.util import AttribDict
|
|
from obspy.taup import TauPyModel
|
|
from obspy.taup.utils import get_phase_names
|
|
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, getQualityFromUncertainty
|
|
from pylot.core.pick.compare import Comparison
|
|
from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, \
|
|
SetChannelComponents
|
|
from pylot.core.util.utils import prepTimeAxis, full_range, scaleWFData, \
|
|
demeanTrace, isSorted, findComboBoxIndex, clims, pick_linestyle_plt, pick_color_plt, \
|
|
check4rotated, check4doubled, check4gaps, remove_underscores
|
|
from autoPyLoT import autoPyLoT
|
|
from pylot.core.util.thread import Thread
|
|
|
|
if sys.version_info.major == 3:
|
|
import icons_rc_3 as icons_rc
|
|
elif sys.version_info.major == 2:
|
|
import icons_rc_2 as icons_rc
|
|
else:
|
|
raise ImportError('Could not determine python version.')
|
|
|
|
|
|
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)
|
|
_anno.draggable()
|
|
|
|
return _axes
|
|
|
|
|
|
def createAction(parent, text, slot=None, shortcut=None, icon=None,
|
|
tip=None, checkable=False):
|
|
"""
|
|
:rtype : ~PySide2.QtWidgets.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 ComparisonWidget(QWidget):
|
|
def __init__(self, c, parent=None, windowflag=1):
|
|
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=list(self.stations)[0], phase=list(self.phases)[0])
|
|
super(ComparisonWidget, self).__init__(parent, windowflag)
|
|
self.setupUI()
|
|
self.resize(1280, 720)
|
|
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
|
|
|
|
self._hist_checkbox = QCheckBox('Show histograms', self)
|
|
self._hist_checkbox.setObjectName('histCheckBox')
|
|
self._hist_checkbox.stateChanged.connect(self.plothist)
|
|
self.widgets = self._hist_checkbox
|
|
|
|
self._toolbar = QToolBar(self)
|
|
self._toolbar.addWidget(_stats_combobox)
|
|
self._toolbar.addWidget(_phases_combobox)
|
|
self._toolbar.addWidget(self._hist_checkbox)
|
|
|
|
_innerlayout.addWidget(self.canvas)
|
|
|
|
_outerlayout.addWidget(self._toolbar)
|
|
_outerlayout.addLayout(_innerlayout)
|
|
|
|
# finally layout the entire widget
|
|
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 showToolbar(self):
|
|
self._toolbar.show()
|
|
|
|
def hideToolbar(self):
|
|
self._toolbar.hide()
|
|
|
|
def setHistboxChecked(self, bool):
|
|
self._hist_checkbox.setChecked(bool)
|
|
|
|
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()
|
|
self.canvas.figure._tight = True
|
|
_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])
|
|
self.canvas.figure.tight_layout()
|
|
|
|
# _axes.cla()
|
|
station = self.plotprops['station']
|
|
phase = self.plotprops['phase']
|
|
if not phase in self.data.comparison[station]:
|
|
_axes.set_title('No pick found for phase {}.'.format(phase))
|
|
self.canvas.draw()
|
|
return
|
|
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()
|
|
self.canvas.figure._tight = True
|
|
_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)
|
|
_anno_std.draggable()
|
|
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)
|
|
_anno_exp.draggable()
|
|
axes_dict[phase]['exp'].set_xlabel('Time [s]')
|
|
|
|
# add colors (early, late) for expectation
|
|
ax = axes_dict[phase]['exp']
|
|
xlims = ax.get_xlim()
|
|
ylims = ax.get_ylim()
|
|
ax.fill_between([xlims[0], 0], ylims[0], ylims[1], color=(0.9, 1.0, 0.9, 0.5), label='earlier than manual')
|
|
ax.fill_between([0, xlims[1]], ylims[0], ylims[1], color=(1.0, 0.9, 0.9, 0.5), label='later than manual')
|
|
legend = ax.legend()
|
|
legend.draggable()
|
|
|
|
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(QtWidgets.QWidget):
|
|
def __init__(self, parent, title='Title'):
|
|
QtWidgets.QWidget.__init__(self, parent=parent)
|
|
self.pg = self.parent().pg
|
|
# added because adding widget to scrollArea will set scrollArea to parent
|
|
self.orig_parent = parent
|
|
# attribute plotdict is a dictionary connecting position and a name
|
|
self.plotdict = dict()
|
|
# create plot
|
|
self.main_layout = QtWidgets.QVBoxLayout()
|
|
self.label = QtWidgets.QLabel()
|
|
self.setLayout(self.main_layout)
|
|
self.plotWidget = self.pg.PlotWidget(self.parent(), 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.3)
|
|
self.plotWidget.hideAxis('bottom')
|
|
self.plotWidget.hideAxis('left')
|
|
self.wfstart, self.wfend = 0, 0
|
|
self.pen_multicursor = self.pg.mkPen(self.parent()._style['multicursor']['rgba'])
|
|
self.pen_linecolor = self.pg.mkPen(self.parent()._style['linecolor']['rgba'])
|
|
self.reinitMoveProxy()
|
|
self._proxy = self.pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
|
|
|
|
def reinitMoveProxy(self):
|
|
self.vLine = self.pg.InfiniteLine(angle=90, movable=False, pen=self.pen_multicursor)
|
|
self.hLine = self.pg.InfiniteLine(angle=0, movable=False, pen=self.pen_multicursor)
|
|
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.orig_parent.getWFID(y)
|
|
station = self.orig_parent.getStationName(wfID)
|
|
abstime = self.wfstart + x
|
|
if self.orig_parent.get_current_event():
|
|
self.label.setText("station = {}, T = {}, t = {} [s]".format(station, abstime, 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 plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None,
|
|
noiselevel=None, scaleddata=False, mapping=True,
|
|
component='*', nth_sample=1, iniPick=None, verbosity=0):
|
|
self.title = title
|
|
self.clearPlotDict()
|
|
self.wfstart, self.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(self.wfend - self.wfstart),
|
|
yMin=.5,
|
|
yMax=len(nsc) + .5)
|
|
except:
|
|
print('Warning: Could not set zoom limits')
|
|
|
|
for n, (network, station, channel) in enumerate(nsc):
|
|
n+=1
|
|
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
|
|
if verbosity:
|
|
msg = 'plotting %s channel of station %s' % (channel, station)
|
|
print(msg)
|
|
stime = trace.stats.starttime - self.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(self.wfstart)
|
|
self.ylabel = ''
|
|
self.setXLims([0, self.wfend - self.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):
|
|
pos = list(pos)
|
|
ticks = list(zip(pos, labels))
|
|
minorTicks = [(0, 0) for _ 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 PylotCanvas(FigureCanvas):
|
|
def __init__(self, figure=None, parent=None, connect_events=True, multicursor=False,
|
|
panZoomX=True, panZoomY=True):
|
|
if not figure:
|
|
figure = Figure()
|
|
# create axes
|
|
self.ax = figure.add_subplot(111)
|
|
|
|
self.axes = figure.axes
|
|
self.figure = figure
|
|
self.figure.set_facecolor(parent._style['background']['rgba_mpl'])
|
|
# attribute plotdict is a dictionary connecting position and a name
|
|
self.plotdict = dict()
|
|
# initialize super class
|
|
super(PylotCanvas, self).__init__(self.figure)
|
|
self.setParent(parent)
|
|
self.orig_parent = parent
|
|
|
|
if multicursor:
|
|
# add a cursor for station selection
|
|
self.multiCursor = MultiCursor(self.figure.canvas, self.axes,
|
|
horizOn=True, useblit=True,
|
|
color=parent._style['multicursor']['rgba_mpl'], lw=1)
|
|
|
|
# initialize panning attributes
|
|
self.press = None
|
|
self.xpress = None
|
|
self.ypress = None
|
|
self.cur_xlim = None
|
|
self.cur_ylim = None
|
|
|
|
# panZoom activated selection
|
|
self.panZoomX = panZoomX
|
|
self.panZoomY = panZoomY
|
|
|
|
self.limits = {}
|
|
|
|
for ax in self.axes:
|
|
self.limits[ax] = {'x': (-np.inf, np.inf),
|
|
'y': (-np.inf, np.inf)}
|
|
|
|
if connect_events:
|
|
self.connectEvents()
|
|
|
|
try:
|
|
self.figure.tight_layout()
|
|
except:
|
|
pass
|
|
|
|
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
|
self.setFocus()
|
|
|
|
def panPress(self, gui_event):
|
|
ax_check = False
|
|
for ax in self.axes:
|
|
if gui_event.inaxes == ax:
|
|
ax_check = True
|
|
break
|
|
if not ax_check: return
|
|
self.cur_xlim = ax.get_xlim()
|
|
self.cur_ylim = ax.get_ylim()
|
|
self.press = gui_event.xdata, gui_event.ydata
|
|
self.press_rel = gui_event.x, gui_event.y
|
|
self.xpress, self.ypress = self.press
|
|
|
|
def pan(self, gui_event):
|
|
if self.press is None:
|
|
return
|
|
if gui_event.button == 1:
|
|
self.panMotion(gui_event)
|
|
elif gui_event.button == 3:
|
|
if self.panZoomX or self.panZoomY:
|
|
self.panZoom(gui_event)
|
|
|
|
def panMotion(self, gui_event):
|
|
ax_check = False
|
|
for ax in self.axes:
|
|
if gui_event.inaxes == ax:
|
|
ax_check = True
|
|
break
|
|
if not ax_check: 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)
|
|
self.refreshPickDlgText()
|
|
ax.figure.canvas.draw()
|
|
|
|
def panRelease(self, gui_event):
|
|
self.press = None
|
|
self.press_rel = None
|
|
self.figure.canvas.draw()
|
|
|
|
def panZoom(self, gui_event, threshold=2., factor=1.1):
|
|
if not gui_event.x and not gui_event.y:
|
|
return
|
|
if not gui_event.button == 3:
|
|
return
|
|
ax_check = False
|
|
for ax in self.axes:
|
|
if gui_event.inaxes == ax:
|
|
ax_check = True
|
|
break
|
|
if not ax_check: return
|
|
|
|
#self.updateCurrentLimits() #maybe put this down to else:
|
|
|
|
# calculate delta (relative values in axis)
|
|
old_x, old_y = self.press_rel
|
|
xdiff = gui_event.x - old_x
|
|
ydiff = gui_event.y - old_y
|
|
|
|
# threshold check
|
|
if abs(xdiff) < threshold and abs(ydiff) < threshold:
|
|
return
|
|
|
|
# refresh press positions to new position
|
|
self.press = gui_event.xdata, gui_event.ydata
|
|
self.press_rel = gui_event.x, gui_event.y
|
|
self.xpress, self.ypress = self.press
|
|
|
|
if abs(xdiff) >= threshold and self.panZoomX:
|
|
x_left, x_right = self.getXLims(ax)
|
|
new_xlim = self.calcPanZoom(self.xpress, x_left, x_right, factor, (xdiff > 0))
|
|
self.setXLims(ax, new_xlim)
|
|
if abs(ydiff) >= threshold and self.panZoomY:
|
|
y_bot, y_top = self.getYLims(ax)
|
|
new_ylim = self.calcPanZoom(self.ypress, y_bot, y_top, factor, (ydiff > 0))
|
|
self.setYLims(ax, new_ylim)
|
|
|
|
self.draw()
|
|
|
|
def saveFigure(self):
|
|
if self.figure:
|
|
fd = QtWidgets.QFileDialog()
|
|
fname, filter = fd.getSaveFileName(self.parent(), filter='Images (*.png)')
|
|
if not fname:
|
|
return
|
|
if not fname.endswith('.png'):
|
|
fname += '.png'
|
|
self.figure.savefig(fname)
|
|
|
|
def calcPanZoom(self, origin, lower_b, upper_b, factor, positive):
|
|
d_lower = abs(origin - lower_b)
|
|
d_upper = abs(origin - upper_b)
|
|
|
|
if positive:
|
|
d_lower *= 1 - 1/factor
|
|
d_upper *= 1 - 1/factor
|
|
lower_b += d_lower
|
|
upper_b -= d_upper
|
|
else:
|
|
d_lower /= 1 + 1/factor
|
|
d_upper /= 1 + 1/factor
|
|
lower_b -= d_lower
|
|
upper_b += d_upper
|
|
|
|
new_lim = [lower_b, upper_b]
|
|
new_lim.sort()
|
|
|
|
return new_lim
|
|
|
|
def scrollZoom(self, gui_event, factor=2.):
|
|
if not gui_event.xdata or not gui_event.ydata:
|
|
return
|
|
ax_check = False
|
|
for ax in self.axes:
|
|
if gui_event.inaxes == ax:
|
|
ax_check = True
|
|
break
|
|
if not ax_check: return
|
|
|
|
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(ax))
|
|
new_ylim = gui_event.ydata - \
|
|
scale_factor * (gui_event.ydata - self.getYLims(ax))
|
|
|
|
new_xlim.sort()
|
|
global_x = self.getGlobalLimits(ax, 'x')
|
|
global_y = self.getGlobalLimits(ax, '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.setXLims(ax, new_xlim)
|
|
self.setYLims(ax, new_ylim)
|
|
|
|
self.refreshPickDlgText()
|
|
self.draw()
|
|
|
|
def refreshPickDlgText(self):
|
|
# TODO: Maybe decreasing performance if activated too often on move event
|
|
# refresh text for pickdlg if given
|
|
parent = self.parent()
|
|
if hasattr(parent, 'refreshArrivalsText'):
|
|
parent.refreshArrivalsText()
|
|
if hasattr(parent, 'refreshPhaseText'):
|
|
parent.refreshPhaseText()
|
|
|
|
def keyPressHandler(self, gui_event):
|
|
if gui_event.key == 'ctrl+s':
|
|
self.saveFigure()
|
|
|
|
def connectEvents(self):
|
|
self.cidscroll = self.connectScrollEvent(self.scrollZoom)
|
|
self.cidpress = self.connectPressEvent(self.panPress)
|
|
self.cidmotion = self.connectMotionEvent(self.pan)
|
|
self.cidrelease = self.connectReleaseEvent(self.panRelease)
|
|
self.cidkpress = self.connectKeyPressEvent(self.keyPressHandler)
|
|
|
|
def disconnectEvents(self):
|
|
self.disconnectScrollEvent(self.cidscroll)
|
|
self.disconnectMotionEvent(self.cidmotion)
|
|
self.disconnectPressEvent(self.cidpress)
|
|
self.disconnectReleaseEvent(self.cidrelease)
|
|
self.disconnectKeyPressEvent(self.cidkpress)
|
|
|
|
self.cidscroll = None
|
|
self.cidrelease = None
|
|
self.cidpress = None
|
|
self.cidmotion = None
|
|
self.cidkpress = None
|
|
|
|
def disconnectPressEvent(self, cid):
|
|
self.mpl_disconnect(cid)
|
|
|
|
def connectPressEvent(self, slot):
|
|
return self.mpl_connect('button_press_event', slot)
|
|
|
|
def disconnectMotionEvent(self, cid):
|
|
self.mpl_disconnect(cid)
|
|
|
|
def connectMotionEvent(self, slot):
|
|
return self.mpl_connect('motion_notify_event', slot)
|
|
|
|
def disconnectReleaseEvent(self, cid):
|
|
self.mpl_disconnect(cid)
|
|
|
|
def connectReleaseEvent(self, slot):
|
|
return self.mpl_connect('button_release_event', slot)
|
|
|
|
def disconnectScrollEvent(self, cid):
|
|
self.mpl_disconnect(cid)
|
|
|
|
def connectScrollEvent(self, slot):
|
|
return self.mpl_connect('scroll_event', slot)
|
|
|
|
def disconnectKeyPressEvent(self, cid):
|
|
self.mpl_disconnect(cid)
|
|
|
|
def connectKeyPressEvent(self, slot):
|
|
return self.mpl_connect('key_press_event', slot)
|
|
|
|
def getPlotDict(self):
|
|
return self.plotdict
|
|
|
|
def setPlotDict(self, key, value):
|
|
self.plotdict[key] = value
|
|
|
|
def clearPlotDict(self):
|
|
self.plotdict = dict()
|
|
|
|
def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None,
|
|
noiselevel=None, scaleddata=False, mapping=True,
|
|
component='*', nth_sample=1, iniPick=None, verbosity=0):
|
|
ax = self.axes[0]
|
|
ax.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()
|
|
|
|
style = self.orig_parent._style
|
|
linecolor = style['linecolor']['rgba_mpl']
|
|
|
|
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
|
|
if verbosity:
|
|
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]
|
|
ax.plot(times, data, color=linecolor, linewidth=0.7)
|
|
if noiselevel is not None:
|
|
for level in noiselevel:
|
|
ax.plot([time_ax[0], time_ax[-1]],
|
|
[level, level],
|
|
color = linecolor,
|
|
linestyle = 'dashed')
|
|
self.setPlotDict(n, (station, channel, network))
|
|
if iniPick:
|
|
ax.vlines(iniPick, ax.get_ylim()[0], ax.get_ylim()[1],
|
|
colors='m', linestyles='dashed',
|
|
linewidth=2)
|
|
xlabel = 'seconds since {0}'.format(wfstart)
|
|
ylabel = ''
|
|
self.updateWidget(xlabel, ylabel, title)
|
|
self.setXLims(ax, [0, wfend - wfstart])
|
|
self.setYLims(ax, [-0.5, nmax + 0.5])
|
|
if zoomx is not None:
|
|
self.setXLims(ax, zoomx)
|
|
if zoomy is not None:
|
|
self.setYLims(ax, zoomy)
|
|
self.draw()
|
|
|
|
def getXLims(self, ax):
|
|
return ax.get_xlim()
|
|
|
|
def getYLims(self, ax):
|
|
return ax.get_ylim()
|
|
|
|
def setXLims(self, ax, lims):
|
|
ax.set_xlim(lims)
|
|
|
|
def setYLims(self, ax, lims):
|
|
ax.set_ylim(lims)
|
|
|
|
def setYTickLabels(self, pos, labels):
|
|
self.axes[0].set_yticks(list(pos))
|
|
self.axes[0].set_yticklabels(labels)
|
|
self.draw()
|
|
|
|
def updateXLabel(self, text):
|
|
self.axes[0].set_xlabel(text)
|
|
self.draw()
|
|
|
|
def updateYLabel(self, text):
|
|
self.axes[0].set_ylabel(text)
|
|
self.draw()
|
|
|
|
def updateTitle(self, text):
|
|
self.axes[0].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.axes[0].ylim)
|
|
axann = self.axes[0].annotate(text, xy=(.03, pos),
|
|
xycoords='axes fraction')
|
|
axann.set_bbox(dict(facecolor='lightgrey', alpha=.6))
|
|
|
|
def setZoomBorders2content(self):
|
|
if not self.axes:
|
|
return
|
|
for ax in self.limits.keys():
|
|
xlims = self.getXLims(ax)
|
|
ylims = self.getYLims(ax)
|
|
|
|
self.limits[ax] = {'x': xlims,
|
|
'y': ylims}
|
|
|
|
for axis, limit in self.limits[ax].items():
|
|
self.setGlobalLimits(ax, axis, limit)
|
|
|
|
def updateCurrentLimits(self):
|
|
for ax in self.limits.keys():
|
|
self.setXLims(ax, self.getXLims(ax))
|
|
self.setYLims(ax, self.getYLims(ax))
|
|
|
|
def getGlobalLimits(self, ax, axis):
|
|
return self.limits[ax][axis]
|
|
|
|
def setGlobalLimits(self, ax, axis, limits):
|
|
self.limits[ax][axis] = limits
|
|
|
|
def resetZoom(self):
|
|
for ax in self.figure.axes:
|
|
self.setXLims(ax, self.getGlobalLimits(ax, 'x'))
|
|
self.setYLims(ax, self.getGlobalLimits(ax, 'y'))
|
|
self.draw()
|
|
|
|
|
|
class PhaseDefaults(QtWidgets.QDialog):
|
|
def __init__(self, parent=None, nrow=10,
|
|
phase_defaults=['P', 'S'],
|
|
current_phases=[]):
|
|
super(PhaseDefaults, self).__init__(parent)
|
|
self.nrow = nrow
|
|
self.checktoggle = True
|
|
self.main_layout = QtWidgets.QVBoxLayout()
|
|
self.sub_layout = QtWidgets.QGridLayout()
|
|
self.phase_names = phase_defaults
|
|
self.current_phases = current_phases
|
|
self.setButtons()
|
|
self.setupUi()
|
|
self.connectSignals()
|
|
self.setWindowTitle('Default Phases')
|
|
self.selected_phases = []
|
|
|
|
def setButtons(self):
|
|
self._check_all_button = QtWidgets.QPushButton('Check/Uncheck all')
|
|
self._buttonbox = QDialogButtonBox(QDialogButtonBox.Ok |
|
|
QDialogButtonBox.Cancel)
|
|
|
|
def setupUi(self):
|
|
self.setLayout(self.main_layout)
|
|
self.main_layout.addWidget(self._check_all_button)
|
|
self.main_layout.addLayout(self.sub_layout)
|
|
self.init_phase_layout()
|
|
self.main_layout.addWidget(self._buttonbox)
|
|
|
|
def connectSignals(self):
|
|
self._check_all_button.clicked.connect(self.toggleAllChecked)
|
|
self._buttonbox.accepted.connect(self.accept)
|
|
self._buttonbox.rejected.connect(self.reject)
|
|
|
|
def toggleAllChecked(self):
|
|
for box in self._checkboxes.values():
|
|
box.setChecked(self.checktoggle)
|
|
self.checktoggle = not self.checktoggle
|
|
|
|
def init_phase_layout(self):
|
|
self._checkboxes = {}
|
|
row = 0
|
|
column = 0
|
|
for index, phase in enumerate(self.phase_names):
|
|
if row > self.nrow:
|
|
column += 1
|
|
row = 0
|
|
checkbox = self.create_phase_box(phase)
|
|
self.sub_layout.addWidget(checkbox,
|
|
row, column)
|
|
self._checkboxes[phase] = checkbox
|
|
checkbox.setChecked(bool(phase in self.current_phases))
|
|
row += 1
|
|
|
|
def create_phase_box(self, phase_name):
|
|
checkbox = QtWidgets.QCheckBox(phase_name)
|
|
return checkbox
|
|
|
|
def update_selected_phases(self):
|
|
self.selected_phases = []
|
|
for phase in self.phase_names:
|
|
if self._checkboxes[phase].isChecked():
|
|
self.selected_phases.append(phase)
|
|
|
|
def accept(self):
|
|
self.update_selected_phases()
|
|
QtWidgets.QDialog.accept(self)
|
|
|
|
|
|
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, metadata=None,
|
|
event=None, filteroptions=None, model='iasp91'):
|
|
super(PickDlg, self).__init__(parent, 1)
|
|
self.orig_parent = parent
|
|
|
|
# initialize attributes
|
|
self.parameter = parameter
|
|
self._embedded = embedded
|
|
self.station = station
|
|
self.network = network
|
|
self.rotate = rotate
|
|
self.metadata = metadata
|
|
self.pylot_event = event
|
|
self.components = 'ZNE'
|
|
self.currentPhase = None
|
|
self.phaseText = []
|
|
self.arrivals = []
|
|
self.arrivalsText = []
|
|
self.cidpick = []
|
|
settings = QSettings()
|
|
pylot_user = getpass.getuser()
|
|
self._user = settings.value('user/Login', pylot_user)
|
|
self._dirty = False
|
|
self._style = parent._style
|
|
if picks:
|
|
self.picks = copy.deepcopy(picks)
|
|
self._init_picks = picks
|
|
else:
|
|
self.picks = {}
|
|
self._init_picks = {}
|
|
if autopicks:
|
|
self.autopicks = copy.deepcopy(autopicks)
|
|
self._init_autopicks = autopicks
|
|
else:
|
|
self.autopicks = {}
|
|
self._init_autopicks = {}
|
|
if filteroptions:
|
|
self.filteroptions = filteroptions
|
|
else:
|
|
self.filteroptions = FILTERDEFAULTS
|
|
self.pick_block = False
|
|
self.nextStation = QtWidgets.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 = PylotCanvas(parent=self, multicursor=True)
|
|
self.phaseplot = PhasePlotWidget(self)
|
|
self.phaseplot.hide()
|
|
|
|
# plot data
|
|
self.multicompfig.plotWFData(wfdata=self.getWFData(),
|
|
title=self.getStation())
|
|
|
|
self.multicompfig.setZoomBorders2content()
|
|
|
|
self.multicompfig.updateCurrentLimits()
|
|
self.multicompfig.draw()
|
|
self.multicompfig.setFocus()
|
|
|
|
# setup ui
|
|
self.setupUi()
|
|
|
|
# set plot labels
|
|
self.setPlotLabels()
|
|
|
|
# draw picks if present
|
|
self.drawAllPicks()
|
|
|
|
# init expected picks using obspy Taup
|
|
try:
|
|
if self.metadata:
|
|
self.model = TauPyModel(model)
|
|
self.get_arrivals()
|
|
self.drawArrivals()
|
|
self.activateArrivalsButton(True)
|
|
else:
|
|
self.activateArrivalsButton(False)
|
|
except Exception as e:
|
|
print('Warning: Could not init expected picks from taup: {}'.format(e))
|
|
self.activateArrivalsButton(False)
|
|
|
|
# init pick delete (with right click)
|
|
self.connect_pick_delete()
|
|
self.setWindowTitle('Pickwindow on station: {}'.format(self.getStation()))
|
|
|
|
def setupUi(self):
|
|
menuBar = QtWidgets.QMenuBar(self)
|
|
if not self._embedded:
|
|
exitMenu = menuBar.addMenu('File')
|
|
exitAction = QtWidgets.QAction('Close', self)
|
|
exitAction.triggered.connect(self.close)
|
|
exitMenu.addAction(exitAction)
|
|
|
|
self.addPickPhases(menuBar)
|
|
|
|
# create matplotlib toolbar to inherit functionality
|
|
self.figToolBar = NavigationToolbar2QT(self.multicompfig, 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.multicompfig.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] + list(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"')
|
|
|
|
self.plot_arrivals_button = QPushButton('Plot phases')
|
|
self.plot_arrivals_button.setCheckable(True)
|
|
|
|
# create accept/reject button
|
|
self.accept_button = QPushButton('&Accept')
|
|
self.reject_button = QPushButton('&Reject')
|
|
self.disable_ar_buttons()
|
|
|
|
# add hotkeys
|
|
self._shortcut_space = QtWidgets.QShortcut(QtWidgets.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:
|
|
manu_label = QLabel('Manual Onsets:')
|
|
manu_label.setStyleSheet('QLabel {'
|
|
'padding:2px;'
|
|
'padding-left:5px}')
|
|
_dialtoolbar.addWidget(manu_label)
|
|
_dialtoolbar.addWidget(self.accept_button)
|
|
_dialtoolbar.addWidget(self.reject_button)
|
|
else:
|
|
_dialtoolbar.addWidget(self.nextStation)
|
|
est_label = QLabel('Estimated onsets:')
|
|
est_label.setStyleSheet('QLabel {'
|
|
'padding:2px;'
|
|
'padding-left:5px}')
|
|
_dialtoolbar.addWidget(est_label)
|
|
_dialtoolbar.addWidget(self.plot_arrivals_button)
|
|
|
|
# layout the innermost widget
|
|
_innerlayout = QVBoxLayout()
|
|
_innerinnerlayout = QtWidgets.QHBoxLayout()
|
|
_innerinnerlayout.addWidget(self.multicompfig)
|
|
_innerinnerlayout.addWidget(self.phaseplot)
|
|
_innerlayout.addLayout(_innerinnerlayout)
|
|
|
|
# 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, 0)
|
|
_outerlayout.setStretch(2, 1)
|
|
|
|
# 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)
|
|
self.plot_arrivals_button.clicked.connect(self.toggle_arrivals_plot)
|
|
_buttonbox.accepted.connect(self.accept)
|
|
_buttonbox.rejected.connect(self.reject)
|
|
|
|
# finally layout the entire dialog
|
|
self.setLayout(_outerlayout)
|
|
self.resize(1280, 720)
|
|
|
|
def activateArrivalsButton(self, val=True):
|
|
self.plot_arrivals_button.setEnabled(val)
|
|
|
|
def toggle_arrivals_plot(self):
|
|
if self.plot_arrivals_button.isChecked():
|
|
self.plot_arrivals()
|
|
else:
|
|
self.hide_arrivals_plot()
|
|
|
|
def hide_arrivals_plot(self):
|
|
self.phaseplot.hide()
|
|
|
|
def plot_arrivals(self):
|
|
if self.phaseplot.new:
|
|
self.get_arrivals(True)
|
|
ax = self.phaseplot.ax
|
|
self.arrivals.plot(ax=ax, show=False)
|
|
ax.legend(loc=1)
|
|
self.phaseplot.new = False
|
|
self.phaseplot.draw()
|
|
self.phaseplot.show()
|
|
|
|
def setDirty(self, bool):
|
|
self._dirty = bool
|
|
|
|
def get_arrivals(self, plot=False):
|
|
if not self.metadata:
|
|
print('get_arrivals: No metadata given. Return!')
|
|
return
|
|
func = {True: self.model.get_ray_paths_geo,
|
|
False: self.model.get_travel_times_geo}
|
|
phases = self.prepare_phases()
|
|
station_id = self.data.traces[0].get_id()
|
|
parser = self.metadata[1]
|
|
station_coords = parser.get_coordinates(station_id)
|
|
origins = self.pylot_event.origins
|
|
if origins:
|
|
source_origin = origins[0]
|
|
else:
|
|
raise ValueError('No source origin given.')
|
|
arrivals = func[plot](source_origin.depth,
|
|
source_origin.latitude,
|
|
source_origin.longitude,
|
|
station_coords['latitude'],
|
|
station_coords['longitude'],
|
|
phases)
|
|
self.arrivals = arrivals
|
|
|
|
def prepare_phases(self):
|
|
settings = QtCore.QSettings()
|
|
p_phases = settings.value('p_phases')
|
|
s_phases = settings.value('s_phases')
|
|
phases = p_phases + ',' + s_phases
|
|
phases = phases.split(',')
|
|
phases = [phase.strip() for phase in phases]
|
|
return phases
|
|
|
|
def drawArrivals(self, textOnly=False):
|
|
if not self.arrivals:
|
|
return
|
|
ax = self.multicompfig.axes[0]
|
|
if not textOnly:
|
|
ylims = self.getGlobalLimits(ax, 'y')
|
|
else:
|
|
ylims = self.multicompfig.getYLims(ax)
|
|
stime = self.getStartTime()
|
|
source_origin = self.pylot_event.origins[0]
|
|
source_time = source_origin.time
|
|
for arrival in self.arrivals:
|
|
arrival_time_abs = source_time + arrival.time
|
|
time_rel = arrival_time_abs - stime
|
|
if not textOnly:
|
|
ax.plot([time_rel, time_rel], ylims, '0.3', linestyle='dashed')
|
|
self.arrivalsText.append(ax.text(time_rel, ylims[0], arrival.name, color='0.5'))
|
|
|
|
def drawArrivalsText(self):
|
|
return self.drawArrivals(True)
|
|
|
|
def refreshArrivalsText(self, event=None):
|
|
self.removeArrivalsText()
|
|
self.drawArrivalsText()
|
|
|
|
def removeArrivalsText(self):
|
|
for textItem in self.arrivalsText:
|
|
try:
|
|
textItem.remove()
|
|
except:
|
|
pass
|
|
self.arrivalsText = []
|
|
|
|
def addPickPhases(self, menuBar):
|
|
settings = QtCore.QSettings()
|
|
p_phases = settings.value('p_phases')
|
|
s_phases = settings.value('s_phases')
|
|
|
|
if p_phases:
|
|
p_phases = p_phases.split(',')
|
|
else:
|
|
p_phases = []
|
|
if s_phases:
|
|
s_phases = s_phases.split(',')
|
|
else:
|
|
s_phases = []
|
|
|
|
phases = {'P': p_phases,
|
|
'S': s_phases}
|
|
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 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_border_color(self.p_button, 'yellow')
|
|
self.activatePicking()
|
|
self.currentPhase = str(self.p_button.text())
|
|
|
|
def init_s_pick(self):
|
|
self.set_button_border_color(self.s_button, 'yellow')
|
|
self.activatePicking()
|
|
self.currentPhase = str(self.s_button.text())
|
|
|
|
def getPhaseID(self, phase):
|
|
return identifyPhaseID(phase)
|
|
|
|
def set_button_border_color(self, button, color=None):
|
|
'''
|
|
Set background color of a button.
|
|
button: type = QtWidgets.QAbstractButton
|
|
color: type = QtWidgets.QColor or type = str (RGBA)
|
|
'''
|
|
if type(color) == QtWidgets.QColor:
|
|
button.setStyleSheet({'QPushButton{background-color:transparent}'})
|
|
palette = button.palette()
|
|
role = button.backgroundRole()
|
|
palette.setColor(role, color)
|
|
button.setPalette(palette)
|
|
button.setAutoFillBackground(True)
|
|
elif type(color) == str:
|
|
button.setStyleSheet('QPushButton{border-color: %s}' % color)
|
|
elif type(color) == tuple:
|
|
button.setStyleSheet('QPushButton{border-color: rgba%s}' % str(color))
|
|
elif not color:
|
|
button.setStyleSheet(self.orig_parent._style['stylesheet'])
|
|
|
|
def reset_p_button(self):
|
|
self.set_button_border_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_border_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.multicompfig.plotWFData(wfdata=self.getWFData(),
|
|
title=self.getStation())
|
|
self.drawAllPicks()
|
|
self.drawArrivals()
|
|
self.setPlotLabels()
|
|
self.resetZoomAction.trigger()
|
|
self.deactivatePicking()
|
|
|
|
def activatePicking(self):
|
|
self.resetZoom()
|
|
if self.zoomAction.isChecked():
|
|
self.zoomAction.trigger()
|
|
self.multicompfig.disconnectEvents()
|
|
self.cidpress = self.multicompfig.connectPressEvent(self.setIniPick)
|
|
self.filterWFData()
|
|
#self.pick_block = self.togglePickBlocker()
|
|
self.disconnect_pick_delete()
|
|
|
|
def deactivatePicking(self):
|
|
self.disconnectPressEvent()
|
|
self.multicompfig.connectEvents()
|
|
self.connect_pick_delete()
|
|
|
|
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 getChannelID(self, key):
|
|
if key < 0: key = 0
|
|
return self.multicompfig.getPlotDict()[int(key)][1]
|
|
|
|
def getTraceID(self, channels):
|
|
plotDict = self.multicompfig.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[self.getPhaseID(phase)]
|
|
if type(options) == dict:
|
|
return FilterOptions(**options)
|
|
else:
|
|
return 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, ax, axis):
|
|
return self.multicompfig.getGlobalLimits(ax, axis)
|
|
|
|
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.multicompfig.disconnectEvents()
|
|
self.disconnectPressEvent()
|
|
self.cidpress = self.multicompfig.connectPressEvent(self.setPick)
|
|
|
|
if self.getPhaseID(self.currentPhase) == 'P':
|
|
self.set_button_border_color(self.p_button, 'green')
|
|
self.setIniPickP(gui_event, wfdata, trace_number)
|
|
elif self.getPhaseID(self.currentPhase) == 'S':
|
|
self.set_button_border_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(self.getPhaseID(phase)).parseFilterOptions()
|
|
if filteroptions:
|
|
try:
|
|
data.filter(**filteroptions)
|
|
wfdata.filter(**filteroptions)
|
|
except ValueError as e:
|
|
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
|
|
'Denied', 'setIniPickP: Could not filter waveform: {}'.format(e))
|
|
self.qmb.show()
|
|
|
|
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.multicompfig.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(self.getPhaseID(phase)).parseFilterOptions()
|
|
if filteroptions:
|
|
try:
|
|
data.filter(**filteroptions)
|
|
wfdata.filter(**filteroptions)
|
|
except ValueError as e:
|
|
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
|
|
'Denied', 'setIniPickS: Could not filter waveform: {}'.format(e))
|
|
self.qmb.show()
|
|
|
|
# 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.multicompfig.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.multicompfig.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(self.getPhaseID(phase)).parseFilterOptions()
|
|
|
|
# copy and filter data for earliest and latest possible picks
|
|
wfdata = self.getWFData().copy().select(channel=channel)
|
|
if filteroptions:
|
|
try:
|
|
wfdata.filter(**filteroptions)
|
|
except ValueError as e:
|
|
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
|
|
'Denied', 'setPick: Could not filter waveform: {}'.format(e))
|
|
self.qmb.show()
|
|
|
|
# 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()
|
|
self.setDirty(True)
|
|
|
|
def disconnectPressEvent(self):
|
|
self.multicompfig.mpl_disconnect(self.cidpress)
|
|
self.cidpress = None
|
|
|
|
def drawAllPicks(self):
|
|
self.removePhaseText()
|
|
self.drawPicks(picktype='manual')
|
|
self.drawPicks(picktype='auto')
|
|
|
|
def drawPicks(self, phase=None, picktype='manual', textOnly=False, picks=None):
|
|
# plotting picks
|
|
ax = self.multicompfig.axes[0]
|
|
if not textOnly:
|
|
ylims = self.multicompfig.getGlobalLimits(ax, 'y')
|
|
else:
|
|
ylims = ax.get_ylim()
|
|
if not picks:
|
|
if self.getPicks(picktype):
|
|
if phase is not None and not phase == 'SPt':
|
|
if (type(self.getPicks(picktype)[phase]) is dict
|
|
or type(self.getPicks(picktype)[phase]) is AttribDict):
|
|
picks = self.getPicks(picktype)[phase]
|
|
elif phase is None:
|
|
for phase in self.getPicks(picktype):
|
|
self.drawPicks(phase, picktype, textOnly)
|
|
return
|
|
else:
|
|
return
|
|
else:
|
|
return
|
|
|
|
# get quality classes
|
|
if self.getPhaseID(phase) == 'P':
|
|
quality = getQualityFromUncertainty(picks['spe'], self.parameter['timeerrorsP'])
|
|
phaseID = 'P'
|
|
elif self.getPhaseID(phase) == 'S':
|
|
quality = getQualityFromUncertainty(picks['spe'], self.parameter['timeerrorsS'])
|
|
phaseID = 'S'
|
|
|
|
mpp = picks['mpp'] - self.getStartTime()
|
|
if picks['epp'] and picks['lpp'] and not textOnly:
|
|
epp = picks['epp'] - self.getStartTime()
|
|
lpp = picks['lpp'] - self.getStartTime()
|
|
spe = picks['spe']
|
|
|
|
if picktype == 'manual':
|
|
color = pick_color_plt(picktype, phaseID, quality)
|
|
if not textOnly:
|
|
linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp')
|
|
ax.plot([mpp, mpp], ylims, color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
|
|
label='{}-Pick (quality: {})'.format(phase, quality), picker=5)
|
|
if spe:
|
|
ax.fill_between([mpp-spe, mpp+spe], ylims[0], ylims[1],
|
|
alpha=.25, color=color, label='{}-SPE'.format(phase))
|
|
if picks['epp']:
|
|
linestyle_epp, width_epp = pick_linestyle_plt(picktype, 'epp')
|
|
ax.plot([epp, epp], ylims, color=color, linestyle=linestyle_epp,
|
|
linewidth=width_epp, label='{}-EPP'.format(phase))
|
|
if picks['lpp']:
|
|
linestyle_lpp, width_lpp = pick_linestyle_plt(picktype, 'lpp')
|
|
ax.plot([lpp, lpp], ylims, color=color, linestyle=linestyle_lpp,
|
|
linewidth=width_lpp, label='{}-LPP'.format(phase))
|
|
# else:
|
|
# ax.plot([mpp, mpp], ylims, color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
|
|
# label='{}-Pick (NO PICKERROR)'.format(phase), picker=5)
|
|
# append phase text (if textOnly: draw with current ylims)
|
|
self.phaseText.append(ax.text(mpp, ylims[1], phase, color=color))
|
|
elif picktype == 'auto':
|
|
color = pick_color_plt(picktype, phaseID, quality)
|
|
linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp')
|
|
if not textOnly:
|
|
ax.plot(mpp, ylims[1], color=color, marker='v')
|
|
ax.plot(mpp, ylims[0], color=color, marker='^')
|
|
ax.vlines(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
|
|
picker=5, label='{}-Autopick (quality: {})'.format(phase, quality))
|
|
# append phase text (if textOnly: draw with current ylims)
|
|
self.phaseText.append(ax.text(mpp, ylims[1], phase, color=color))
|
|
else:
|
|
raise TypeError('Unknown picktype {0}'.format(picktype))
|
|
|
|
ax.legend(loc=1)
|
|
|
|
def connect_pick_delete(self):
|
|
self.cidpick = self.multicompfig.mpl_connect('pick_event', self.onpick_delete)
|
|
|
|
def disconnect_pick_delete(self):
|
|
if hasattr(self, 'cidpick'):
|
|
self.multicompfig.mpl_disconnect(self.cidpick)
|
|
|
|
def onpick_delete(self, event):
|
|
if not event.mouseevent.button == 3:
|
|
return
|
|
x = event.mouseevent.xdata
|
|
self.remove_pick_by_x(x)
|
|
self.resetPlot()
|
|
|
|
def remove_pick_by_x(self, x):
|
|
if not self.picks and not self.autopicks:
|
|
return
|
|
# init empty list and get station starttime
|
|
X = []
|
|
starttime = self.getStartTime()
|
|
# init dictionaries to iterate through and iterate over them
|
|
allpicks = {'manual': self.picks,
|
|
'auto': self.autopicks}
|
|
for picktype in allpicks.keys():
|
|
picks = allpicks[picktype]
|
|
for phase in picks:
|
|
if not type(picks[phase]) in [dict, AttribDict]:
|
|
continue
|
|
pick_rel = picks[phase]['mpp'] - starttime
|
|
# add relative pick time, phaseID and picktype index
|
|
X.append((pick_rel, phase, picktype))
|
|
# find index and value closest to x
|
|
index, value = min(enumerate([val[0] for val in X]), key=lambda y: abs(y[1] - x))
|
|
# unpack the found value
|
|
pick_rel, phase, picktype = X[index]
|
|
# delete the value from corresponding dictionary
|
|
allpicks[picktype].pop(phase)
|
|
# information output
|
|
msg = 'Deleted {} pick for phase {}, at timestamp {} (relative time: {} s)'
|
|
print(msg.format(picktype, phase, starttime+pick_rel, pick_rel))
|
|
self.setDirty(True)
|
|
|
|
def drawPhaseText(self):
|
|
self.drawPicks(picktype='manual', textOnly=True)
|
|
self.drawPicks(picktype='auto', textOnly=True)
|
|
|
|
def removePhaseText(self):
|
|
for textItem in self.phaseText:
|
|
try:
|
|
textItem.remove()
|
|
except:
|
|
pass
|
|
self.phaseText = []
|
|
|
|
def refreshPhaseText(self, event=None):
|
|
self.removePhaseText()
|
|
self.drawPhaseText()
|
|
|
|
def togglePickBlocker(self):
|
|
return not self.pick_block
|
|
|
|
def filterWFData(self):
|
|
if self.pick_block:
|
|
return
|
|
self.multicompfig.updateCurrentLimits()
|
|
data = self.getWFData().copy()
|
|
old_title = self.multicompfig.axes[0].get_title()
|
|
title = None
|
|
phase = self.currentPhase
|
|
filtoptions = None
|
|
if phase:
|
|
filtoptions = self.getFilterOptions(self.getPhaseID(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.multicompfig.plotWFData(wfdata=data, title=title,
|
|
zoomx=self.getXLims(),
|
|
zoomy=self.getYLims())
|
|
self.setPlotLabels()
|
|
self.drawAllPicks()
|
|
self.draw()
|
|
|
|
def resetPlot(self):
|
|
self.resetZoom()
|
|
data = self.getWFData().copy()
|
|
title = self.multicompfig.axes[0].get_title()
|
|
self.multicompfig.plotWFData(wfdata=data, title=title,
|
|
zoomx=self.getXLims(),
|
|
zoomy=self.getYLims())
|
|
self.setPlotLabels()
|
|
self.drawAllPicks()
|
|
self.draw()
|
|
|
|
def resetZoom(self):
|
|
ax = self.multicompfig.axes[0]
|
|
self.setXLims(self.multicompfig.getGlobalLimits(ax, 'x'))
|
|
self.setYLims(self.multicompfig.getGlobalLimits(ax, 'y'))
|
|
|
|
def setPlotLabels(self):
|
|
|
|
# get channel labels
|
|
pos = self.multicompfig.getPlotDict().keys()
|
|
labels = [self.multicompfig.getPlotDict()[key][1] for key in pos]
|
|
|
|
ax = self.multicompfig.figure.axes[0]
|
|
|
|
# set channel labels
|
|
self.multicompfig.setYTickLabels(pos, labels)
|
|
self.multicompfig.setXLims(ax, self.getXLims())
|
|
self.multicompfig.setYLims(ax, self.getYLims())
|
|
|
|
def zoom(self):
|
|
if self.zoomAction.isChecked() and self.pick_block:
|
|
self.zoomAction.setChecked(False)
|
|
elif self.zoomAction.isChecked():
|
|
self.multicompfig.disconnectEvents()
|
|
self.figToolBar.zoom()
|
|
else:
|
|
self.figToolBar.zoom()
|
|
|
|
def draw(self):
|
|
self.multicompfig.draw()
|
|
|
|
def apply(self):
|
|
picks = self.getPicks()
|
|
self.update_picks.emit(picks)
|
|
# for pick in picks:
|
|
# print(pick, picks[pick])
|
|
|
|
def discard(self):
|
|
self.picks = self._init_picks
|
|
self.autopicks = self._init_autopicks
|
|
self.update_picks.emit(self.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 = QtWidgets.QMessageBox(QtWidgets.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 = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
|
|
'Accepted', 'New picks applied!')
|
|
self.qmb.show()
|
|
|
|
|
|
class PhasePlotWidget(FigureCanvas):
|
|
def __init__(self, parent=None):
|
|
self._parent = parent
|
|
self.fig = Figure()
|
|
self.ax = self.fig.add_subplot(111, projection='polar')
|
|
self.new = True
|
|
super(PhasePlotWidget, self).__init__(self.fig)
|
|
|
|
|
|
class CanvasWidget(QWidget):
|
|
'''
|
|
'''
|
|
|
|
def __init__(self, parent, canvas):
|
|
QtWidgets.QWidget.__init__(self, parent)#, 1)
|
|
canvas = canvas
|
|
self.main_layout = QtWidgets.QVBoxLayout()
|
|
self.setLayout(self.main_layout)
|
|
self.main_layout.addWidget(canvas)
|
|
canvas.setZoomBorders2content()
|
|
|
|
|
|
class MultiEventWidget(QWidget):
|
|
start = Signal()
|
|
'''
|
|
|
|
'''
|
|
def __init__(self, options=None, parent=None, windowflag=1):
|
|
QtWidgets.QWidget.__init__(self, parent, windowflag)
|
|
|
|
self.options = options
|
|
self.setupUi()
|
|
# set initial size
|
|
self.resize(1280, 720)
|
|
|
|
def setupUi(self):
|
|
# init main layout
|
|
self.main_layout = QtWidgets.QVBoxLayout()
|
|
self.setLayout(self.main_layout)
|
|
# init main splitter
|
|
self.main_splitter = QtWidgets.QSplitter()
|
|
self.main_splitter.setChildrenCollapsible(False)
|
|
|
|
self.init_checkboxes()
|
|
|
|
self.eventbox = QtWidgets.QComboBox()
|
|
self.button_clear = QtWidgets.QPushButton('Clear')
|
|
|
|
self.main_layout.insertWidget(1, self.main_splitter)
|
|
|
|
self.main_layout.setStretch(0, 0)
|
|
self.main_layout.setStretch(1, 1)
|
|
self.main_splitter.setStretchFactor(0, 1)
|
|
self.main_splitter.setStretchFactor(1, 2)
|
|
|
|
def init_checkboxes(self):
|
|
self.rb_layout = QtWidgets.QHBoxLayout()
|
|
|
|
self.rb_dict = {}
|
|
|
|
self.start_button = QtWidgets.QPushButton('Start')
|
|
|
|
for index, (key, func, color) in enumerate(self.options):
|
|
rb = QtWidgets.QRadioButton(key)
|
|
rb.toggled.connect(self.check_rb_selection)
|
|
if color:
|
|
color = 'rgba{}'.format(color)
|
|
else:
|
|
color = 'transparent'
|
|
rb.setStyleSheet('QRadioButton{'
|
|
'background-color: %s;'
|
|
'border-style:outset;'
|
|
'border-width:1px;'
|
|
'border-radius:5px;'
|
|
'padding:5px;'
|
|
'}' % str(color))
|
|
if index == 0:
|
|
rb.setChecked(True)
|
|
self.rb_dict[key] = rb
|
|
self.rb_layout.insertWidget(index, rb)
|
|
self.rb_layout.setStretch(index, 0)
|
|
|
|
self.rb_layout.addWidget(self.start_button)
|
|
|
|
self.rb_layout.addWidget(QtWidgets.QWidget())
|
|
self.rb_layout.setStretch(len(self.options) + 1, 1)
|
|
|
|
self.main_layout.insertLayout(0, self.rb_layout)
|
|
|
|
def refresh_tooltips(self):
|
|
for key, func, color in self.options:
|
|
eventlist = func()
|
|
if not type(eventlist) == list:
|
|
eventlist = [eventlist]
|
|
tooltip=''
|
|
for index, event in enumerate(eventlist):
|
|
if not event:
|
|
continue
|
|
tooltip += '{}'.format(event.pylot_id)
|
|
if not index + 1 == len(eventlist):
|
|
tooltip += '\n'
|
|
if not tooltip:
|
|
tooltip = 'No events for this selection'
|
|
self.rb_dict[key].setToolTip(tooltip)
|
|
self.check_rb_selection()
|
|
|
|
def check_rb_selection(self):
|
|
for rb in self.rb_dict.values():
|
|
if rb.isChecked():
|
|
check_events = (rb.toolTip() == 'No events for this selection')
|
|
self.start_button.setEnabled(not(check_events))
|
|
|
|
def enable(self, bool):
|
|
for rb in self.rb_dict.values():
|
|
rb.setEnabled(bool)
|
|
self.start_button.setEnabled(bool)
|
|
self.eventbox.setEnabled(bool)
|
|
self.button_clear.setEnabled(bool)
|
|
|
|
|
|
class AutoPickWidget(MultiEventWidget):
|
|
'''
|
|
'''
|
|
|
|
def __init__(self, parent, options):
|
|
MultiEventWidget.__init__(self, options, parent, 1)
|
|
self.events2plot = {}
|
|
self.connect_buttons()
|
|
self.init_plot_layout()
|
|
self.init_log_layout()
|
|
self.reinitEvents2plot()
|
|
self.setWindowTitle('Autopick events interactively')
|
|
self.set_main_stretch()
|
|
|
|
def connect_buttons(self):
|
|
self.start_button.clicked.connect(self.run)
|
|
self.button_clear.clicked.connect(self.reinitEvents2plot)
|
|
|
|
def init_plot_layout(self):
|
|
# init tab widget
|
|
self.tab_plots = QtWidgets.QTabWidget()
|
|
self.gb_plots = QtWidgets.QGroupBox('Plots')
|
|
self.gb_plots.setMinimumSize(100, 100)
|
|
self.main_splitter.insertWidget(1, self.gb_plots)
|
|
self.plot_layout = QtWidgets.QVBoxLayout()
|
|
self.plot_layout.insertWidget(1, self.tab_plots)
|
|
self.gb_plots.setLayout(self.plot_layout)
|
|
|
|
def init_log_layout(self):
|
|
self.gb_log = QtWidgets.QGroupBox('Log')
|
|
self.gb_log.setMinimumSize(100, 100)
|
|
self.main_splitter.insertWidget(0, self.gb_log)
|
|
|
|
def insert_log_widget(self, widget):
|
|
vl = QtWidgets.QVBoxLayout()
|
|
vl.addWidget(widget)
|
|
self.gb_log.setLayout(vl)
|
|
|
|
def add_plot_widget(self, widget, key, eventID):
|
|
eventID += ' [picked: {}]'.format(time.strftime('%X %x %z'))
|
|
if not eventID in self.events2plot.keys():
|
|
self.events2plot[eventID] = {}
|
|
self.events2plot[eventID][key] = widget
|
|
|
|
def generate_combobox(self):
|
|
self.eventbox.clear()
|
|
for eventID, widgets in self.events2plot.items():
|
|
self.eventbox.addItem(str(eventID), widgets)
|
|
self.eventbox.currentIndexChanged.connect(self.draw_plots)
|
|
self.draw_plots()
|
|
|
|
def draw_plots(self, index=0):
|
|
self.refresh_plot_tabs()
|
|
widgets = self.eventbox.itemData(index)
|
|
if not widgets:
|
|
return
|
|
for key, widget in widgets.items():
|
|
self.tab_plots.addTab(widget, str(key))
|
|
|
|
def update_plots(self):
|
|
self.refresh_plot_tabs()
|
|
if len(self.events2plot) > 0:
|
|
self.eventbox_layout = QtWidgets.QHBoxLayout()
|
|
self.generate_combobox()
|
|
self.eventbox_layout.addWidget(self.eventbox)
|
|
self.eventbox_layout.addWidget(self.button_clear)
|
|
self.eventbox_layout.setStretch(0, 1)
|
|
self.plot_layout.insertLayout(0, self.eventbox_layout)
|
|
|
|
def set_main_stretch(self):
|
|
self.main_layout.setStretch(0, 0)
|
|
self.main_layout.setStretch(1, 1)
|
|
|
|
def reinitEvents2plot(self):
|
|
for eventID, eventDict in self.events2plot.items():
|
|
for widget_key, widget in eventDict.items():
|
|
widget.setParent(None)
|
|
self.events2plot = {}
|
|
self.eventbox.clear()
|
|
self.refresh_plot_tabs()
|
|
|
|
def refresh_plot_tabs(self):
|
|
self.tab_plots.clear()
|
|
|
|
def run(self):
|
|
self.refresh_plot_tabs()
|
|
self.start.emit()
|
|
|
|
|
|
class CompareEventsWidget(MultiEventWidget):
|
|
'''
|
|
'''
|
|
|
|
def __init__(self, parent, options, eventdict, comparisons):
|
|
MultiEventWidget.__init__(self, options, parent, 1)
|
|
self.eventdict = eventdict
|
|
self.comparisons = comparisons
|
|
self.compare_widget = QtWidgets.QWidget()
|
|
self.init_eventbox()
|
|
self.init_event_area()
|
|
self.fill_eventbox()
|
|
self.connect_buttons()
|
|
self.setWindowTitle('Compare events')
|
|
self.set_main_stretch()
|
|
|
|
def connect_buttons(self):
|
|
self.start_button.clicked.connect(self.run)
|
|
self.start_button.setText('Show Histograms')
|
|
|
|
def init_event_area(self):
|
|
self.event_layout = QVBoxLayout()
|
|
self.event_layout.insertWidget(0, self.eventbox)
|
|
self.event_area = QGroupBox('Single Event')
|
|
self.event_area.setLayout(self.event_layout)
|
|
self.main_layout.insertWidget(1, self.event_area)
|
|
|
|
def init_eventbox(self):
|
|
self.eventbox_layout = QtWidgets.QHBoxLayout()
|
|
self.eventbox_layout.addWidget(self.eventbox)
|
|
self.eventbox.currentIndexChanged.connect(self.update_comparison)
|
|
|
|
def fill_eventbox(self):
|
|
event_ids = list(self.eventdict.keys())
|
|
for event_id in sorted(event_ids):
|
|
self.eventbox.addItem(str(event_id))
|
|
self.update_comparison()
|
|
|
|
def update_eventbox(self):
|
|
self.eventbox.clear()
|
|
self.fill_eventbox()
|
|
|
|
def update_comparison(self, index=0):
|
|
self.compare_widget.setParent(None)
|
|
self.compare_widget = ComparisonWidget(
|
|
self.comparisons[self.eventbox.currentText()], self, 0)
|
|
self.event_layout.insertWidget(1, self.compare_widget)
|
|
self.set_main_stretch()
|
|
|
|
def set_main_stretch(self):
|
|
self.main_layout.setStretch(0, 0)
|
|
self.main_layout.setStretch(1, 1)
|
|
self.main_layout.setStretch(2, 0)
|
|
self.event_layout.setStretch(0, 0)
|
|
self.event_layout.setStretch(1, 1)
|
|
|
|
def run(self):
|
|
self.start.emit()
|
|
|
|
|
|
class TuneAutopicker(QWidget):
|
|
update = QtCore.Signal(str)
|
|
'''
|
|
QWidget used to modifiy and test picking parameters for autopicking algorithm.
|
|
|
|
:param: parent
|
|
:type: PyLoT Mainwindow
|
|
'''
|
|
|
|
def __init__(self, parent):
|
|
QtWidgets.QWidget.__init__(self, parent, 1)
|
|
self._style = parent._style
|
|
self.setWindowTitle('PyLoT - Tune Autopicker')
|
|
self.parameter = self.parent()._inputs
|
|
self.fig_dict = self.parent().fig_dict
|
|
self.data = Data()
|
|
self.pdlg_widget = None
|
|
self.pylot_picks = None
|
|
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._manual_pick_plots = []
|
|
if hasattr(self.parent(), 'metadata'):
|
|
self.metadata = self.parent().metadata
|
|
else:
|
|
self.metadata = None
|
|
# self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
|
|
# self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
|
|
|
def set_fig_dict(self, fig_dict):
|
|
for key, value in fig_dict.items():
|
|
if key is not 'mainFig':
|
|
value._tight = True
|
|
self.fig_dict = fig_dict
|
|
|
|
def init_main_layouts(self):
|
|
self.main_layout = QtWidgets.QVBoxLayout()
|
|
self.tune_layout = QtWidgets.QHBoxLayout()
|
|
self.trace_layout = QtWidgets.QHBoxLayout()
|
|
self.parameter_layout = QtWidgets.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 = QtWidgets.QComboBox()
|
|
self.stationBox.setMaxVisibleItems(42)
|
|
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)
|
|
wfdat = self.data.getWFData() # all available streams
|
|
# remove possible underscores in station names
|
|
wfdat = remove_underscores(wfdat)
|
|
# rotate misaligned stations to ZNE
|
|
# check for gaps and doubled channels
|
|
check4gaps(wfdat)
|
|
check4doubled(wfdat)
|
|
wfdat = check4rotated(wfdat, self.parent().metadata, verbosity=0)
|
|
# trim station components to same start value
|
|
trim_station_components(wfdat, trim_start=True, trim_end=False)
|
|
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 = QtWidgets.QStandardItem(network + '.' + station)
|
|
if station in self.get_current_event().pylot_picks:
|
|
item.setBackground(self.parent()._ref_test_colors['ref'])
|
|
model.appendRow(item)
|
|
|
|
def init_figure_tabs(self):
|
|
self.figure_tabs = QtWidgets.QTabWidget()
|
|
self.fill_figure_tabs()
|
|
|
|
def init_pbwidget(self):
|
|
self.pb_widget = QtWidgets.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, parent=self, windowflag=0)
|
|
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 = QtWidgets.QPushButton('Pick Trace')
|
|
self.pick_button.setStyleSheet('QPushButton{border-color: rgba(110, 200, 0, 255)}')
|
|
self.pick_button.clicked.connect(self.call_picker)
|
|
self.close_button = QtWidgets.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 = QtWidgets.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 = QtWidgets.QWidget()
|
|
v_layout = QtWidgets.QVBoxLayout()
|
|
v_layout.addWidget(canvas)
|
|
widget.setLayout(v_layout)
|
|
return widget
|
|
|
|
def gen_pick_dlg(self):
|
|
if not self.get_current_event():
|
|
if self.pdlg_widget:
|
|
self.pdlg_widget.setParent(None)
|
|
self.pdlg_widget = None
|
|
return
|
|
station = self.get_current_station()
|
|
data = self.data.getWFData()
|
|
metadata = self.parent().metadata
|
|
event = self.get_current_event()
|
|
filteroptions = self.parent().filteroptions
|
|
self.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),
|
|
metadata=metadata, event=event, filteroptions=filteroptions,
|
|
embedded=True)
|
|
self.pickDlg.update_picks.connect(self.picks_from_pickdlg)
|
|
self.pickDlg.update_picks.connect(self.fill_eventbox)
|
|
self.pickDlg.update_picks.connect(self.fill_stationbox)
|
|
self.pickDlg.update_picks.connect(lambda: self.parent().setDirty(True))
|
|
self.pickDlg.update_picks.connect(self.parent().enableSaveEventAction)
|
|
self.pickDlg.update_picks.connect(self.plot_manual_picks_to_figs)
|
|
self.pdlg_widget = QtWidgets.QWidget(self)
|
|
hl = QtWidgets.QHBoxLayout()
|
|
self.pdlg_widget.setLayout(hl)
|
|
hl.addWidget(self.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 clear_plotitem(self, plotitem):
|
|
if type(plotitem) == list:
|
|
for item in plotitem:
|
|
self.clear_plotitem(item)
|
|
return
|
|
try:
|
|
plotitem.remove()
|
|
except Exception as e:
|
|
print('Warning could not remove item {}: {}'.format(plotitem, e))
|
|
|
|
def plot_manual_picks_to_figs(self):
|
|
picks = self.get_current_event_picks(self.get_current_station())
|
|
if not picks:
|
|
return
|
|
for plotitem in self._manual_pick_plots:
|
|
self.clear_plotitem(plotitem)
|
|
self._manual_pick_plots = []
|
|
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)]
|
|
qualityPpick = getQualityFromUncertainty(picks['P']['spe'], self.parameter['timeerrorsP'])
|
|
qualitySpick = getQualityFromUncertainty(picks['S']['spe'], self.parameter['timeerrorsS'])
|
|
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_pick_to_ax(ax=ax, picks=picks, phase='P',
|
|
starttime=starttime, quality=qualityPpick)
|
|
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_pick_to_ax(ax=ax, picks=picks, phase='S',
|
|
starttime=starttime, quality=qualitySpick)
|
|
for canvas in self.parent().canvas_dict.values():
|
|
canvas.draw()
|
|
|
|
def plot_manual_pick_to_ax(self, ax, picks, phase, starttime, quality):
|
|
mpp = picks[phase]['mpp'] - starttime
|
|
color = pick_color_plt('manual', phase, quality)
|
|
|
|
y_top = 0.9 * ax.get_ylim()[1]
|
|
y_bot = 0.9 * ax.get_ylim()[0]
|
|
self._manual_pick_plots.append(ax.vlines(mpp, y_bot, y_top,
|
|
color=color, linewidth=2,
|
|
label='manual {} Onset (quality: {})'.format(phase, quality)))
|
|
self._manual_pick_plots.append(ax.plot([mpp - 0.5, mpp + 0.5],
|
|
[y_bot, y_bot], linewidth=2,
|
|
color=color))
|
|
self._manual_pick_plots.append(ax.plot([mpp - 0.5, mpp + 0.5],
|
|
[y_top, y_top], linewidth=2,
|
|
color=color))
|
|
ax.legend(loc=1)
|
|
|
|
def fill_tabs(self, event=None, picked=False):
|
|
self.clear_all()
|
|
self.gen_pick_dlg()
|
|
canvas_dict = self.parent().canvas_dict
|
|
self.overview = self.gen_tab_widget('Overview', canvas_dict['mainFig'])
|
|
id0 = self.figure_tabs.insertTab(0, self.pdlg_widget, '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:
|
|
self.fig_dict['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 = QtWidgets.QTabWidget()
|
|
self.s_tabs = QtWidgets.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,
|
|
'savexml': False}
|
|
for key in self.fig_dict.keys():
|
|
if not key == 'plot_style':
|
|
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:
|
|
msg = 'Could not execute picker:\n{}'.format(
|
|
self.ap_thread._executedError)
|
|
info = self.ap_thread._executedErrorInfo
|
|
self._warn(msg, info)
|
|
return
|
|
self.pylot_picks = self.ap_thread.data[self.get_current_event_name()]
|
|
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, 'pdlg_widget'):
|
|
if self.pdlg_widget:
|
|
self.pdlg_widget.setParent(None)
|
|
# TODO: removing widget by parent deletion raises exception when activating stationbox:
|
|
# RuntimeError: Internal C++ object (PylotCanvas) already deleted.
|
|
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, info=None):
|
|
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Warning,
|
|
'Warning', message)
|
|
self.qmb.setDetailedText(str(info))
|
|
self.qmb.show()
|
|
|
|
|
|
class PylotParaBox(QtWidgets.QWidget):
|
|
accepted = QtCore.Signal(str)
|
|
rejected = QtCore.Signal(str)
|
|
|
|
def __init__(self, parameter, parent=None, windowflag=1):
|
|
'''
|
|
Generate Widget containing parameters for PyLoT.
|
|
|
|
:param: parameter
|
|
:type: PylotParameter (object)
|
|
|
|
'''
|
|
QtWidgets.QWidget.__init__(self, parent, windowflag)
|
|
self.parameter = parameter
|
|
self.tabs = QtWidgets.QTabWidget()
|
|
self.layout = QtWidgets.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, 860)
|
|
self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
|
|
self.accepted.connect(self.params_from_gui)
|
|
self.rejected.connect(self.params_to_gui)
|
|
|
|
def _init_sublayouts(self):
|
|
self._main_layout = QtWidgets.QVBoxLayout()
|
|
self._advanced_layout = QtWidgets.QVBoxLayout()
|
|
self._create_advanced_cb()
|
|
|
|
def _init_save_buttons(self):
|
|
self._buttons_layout = QtWidgets.QHBoxLayout()
|
|
self.loadButton = QtWidgets.QPushButton('&Load settings')
|
|
self.saveButton = QtWidgets.QPushButton('&Save settings')
|
|
self.defaultsButton = QtWidgets.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 = QtWidgets.QHBoxLayout()
|
|
self._okay = QtWidgets.QPushButton('Ok')
|
|
self._close = QtWidgets.QPushButton('Close')
|
|
self._apply = QtWidgets.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.accept)
|
|
self._okay.clicked.connect(self.close)
|
|
self._apply.clicked.connect(self.accept)
|
|
self._close.clicked.connect(self.close)
|
|
self.layout.addLayout(self._dialog_buttons)
|
|
|
|
def _create_advanced_cb(self):
|
|
self._advanced_cb = QtWidgets.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 = QtWidgets.QGridLayout()
|
|
|
|
for index1, name in enumerate(parameter_names):
|
|
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
|
|
namestring = default_item['namestring']
|
|
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))
|
|
headline = default_item['namestring'][1:]
|
|
box, lower = self.create_multi_box(boxes, headline)
|
|
self.boxes[name] = boxes
|
|
namestring = default_item['namestring'][0]
|
|
text = namestring + ' [?]'
|
|
label = QtWidgets.QLabel(text)
|
|
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 = QtWidgets.QLineEdit()
|
|
elif typ == float:
|
|
box = QtWidgets.QDoubleSpinBox()
|
|
box.setDecimals(4)
|
|
box.setRange(-10e4, 10e4)
|
|
elif typ == int:
|
|
box = QtWidgets.QSpinBox()
|
|
elif typ == bool:
|
|
box = QtWidgets.QCheckBox()
|
|
else:
|
|
raise TypeError('Unrecognized type {}'.format(typ))
|
|
return box
|
|
|
|
def create_multi_box(self, boxes, headline=None):
|
|
box = QtWidgets.QWidget()
|
|
gl = QtWidgets.QGridLayout()
|
|
column = 0
|
|
if headline:
|
|
for index, item in enumerate(headline):
|
|
if not item:
|
|
continue
|
|
gl.addWidget(QtWidgets.QLabel(item), index, 0, 2)
|
|
column = 1
|
|
for index, b in enumerate(boxes):
|
|
gl.addWidget(b, index, column)
|
|
box.setLayout(gl)
|
|
return box, column
|
|
|
|
def add_tab(self, layout, name):
|
|
widget = QtWidgets.QWidget()
|
|
scrollA = QtWidgets.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, 'Filter Settings',
|
|
self.parameter.get_main_para_names()['filter'], 4)
|
|
self.add_to_layout(self._main_layout, 'Common Settings Characteristic Function',
|
|
self.parameter.get_main_para_names()['pick'], 5)
|
|
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_separator(self):
|
|
# separator = QtWidgets.QFrame()
|
|
# separator.setFrameShape(QtWidgets.QFrame.HLine)
|
|
# return separator
|
|
|
|
# def gen_headline(self, text):
|
|
# label=QtWidgets.QLabel(text)
|
|
# font=QtWidgets.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 = QtWidgets.QWidget(self, 1)
|
|
layout = QtWidgets.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 = QtWidgets.QDialog(self.parent())
|
|
layout = QtWidgets.QVBoxLayout()
|
|
dialog.setLayout(layout)
|
|
buttonbox = QtWidgets.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 = QtWidgets.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) == QtWidgets.QLineEdit:
|
|
box.setText(str(value))
|
|
elif type(box) == QtWidgets.QSpinBox or type(box) == QtWidgets.QDoubleSpinBox:
|
|
if not value:
|
|
value = 0.
|
|
box.setValue(value)
|
|
elif type(box) == QtWidgets.QCheckBox:
|
|
if value == 'True':
|
|
value = True
|
|
if value == 'False' or value is None:
|
|
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) == QtWidgets.QLineEdit:
|
|
value = str(box.text())
|
|
elif type(box) == QtWidgets.QSpinBox or type(box) == QtWidgets.QDoubleSpinBox:
|
|
value = box.value()
|
|
elif type(box) == QtWidgets.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 = QtWidgets.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 = QtWidgets.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 = []
|
|
QtWidgets.QWidget.show(self)
|
|
|
|
def close(self):
|
|
self.rejected.emit('reject')
|
|
QtWidgets.QWidget.close(self)
|
|
|
|
def accept(self):
|
|
self.accepted.emit('accept')
|
|
|
|
def _warn(self, message):
|
|
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Warning,
|
|
'Warning', message)
|
|
self.qmb.show()
|
|
|
|
|
|
class AutoPickDlg(QDialog):
|
|
def __init__(self, parent=None, sge=False):
|
|
super(AutoPickDlg, self).__init__(parent)
|
|
|
|
if not self.parent():
|
|
print('WARNING: No parent given! PyLoT parameter can not be read!')
|
|
|
|
self.pp_export = os.path.join(os.path.expanduser('~'), '.pylot', '.pylot_exported.in')
|
|
|
|
self.sge = sge
|
|
self.layout = QVBoxLayout()
|
|
self.setWindowTitle('Pick whole project...')
|
|
self.setLayout(self.layout)
|
|
|
|
self.setupUI()
|
|
self.resize(720, 0)
|
|
|
|
def setupUI(self):
|
|
self.init_sb_ncores()
|
|
self.init_gb()
|
|
|
|
self.layout.addLayout(self.layout_ncores, 2)
|
|
self.layout.addWidget(self.gb)
|
|
self.addJobWidget()
|
|
|
|
def init_sb_ncores(self):
|
|
self.layout_ncores = QHBoxLayout()
|
|
self.label_ncores = QLabel('Number of CPU cores:')
|
|
self.sb_ncores = QSpinBox()
|
|
if not self.sge:
|
|
maxcores = multiprocessing.cpu_count()
|
|
self.sb_ncores.setMinimum(1)
|
|
self.sb_ncores.setMaximum(maxcores)
|
|
self.layout_ncores.addWidget(self.label_ncores)
|
|
self.layout_ncores.addWidget(self.sb_ncores)
|
|
self.layout_ncores.setStretch(0, 1)
|
|
self.layout_ncores.setStretch(1, 0)
|
|
|
|
def init_gb(self):
|
|
title_sge = {
|
|
True: 'Submit autoPyLoT process to grid engine',
|
|
False: 'Spawn autoPyLot process on local machine'
|
|
}
|
|
|
|
self.gb = QGroupBox(title_sge[self.sge])
|
|
|
|
def addJobWidget(self):
|
|
widget_sge = {
|
|
True: Submit2Grid(),
|
|
False: SubmitLocal()
|
|
}
|
|
|
|
self.job_widget = widget_sge[self.sge]
|
|
self.job_widget.button.clicked.connect(self.accept)
|
|
|
|
self.jobLayout = QVBoxLayout()
|
|
self.jobLayout.addWidget(self.job_widget)
|
|
|
|
self.gb.setLayout(self.jobLayout)
|
|
|
|
def exportParameter(self):
|
|
self.parent().exportAllEvents()
|
|
pylot_params = self.parent()._inputs
|
|
self.addEvents2pp(pylot_params)
|
|
pylot_params.export2File(self.pp_export)
|
|
|
|
def addEvents2pp(self, pylot_parameter):
|
|
eventIDs = []
|
|
for event in self.parent().project.eventlist:
|
|
eventIDs.append(event.pylot_id)
|
|
pylot_parameter['eventID'] = eventIDs
|
|
|
|
def accept(self):
|
|
self.exportParameter()
|
|
self.job_widget.start(self.pp_export, self.sb_ncores.value())
|
|
QDialog.accept(self)
|
|
|
|
|
|
class Submit2Grid(QWidget):
|
|
def __init__(self, parent=None):
|
|
super(Submit2Grid, self).__init__(parent)
|
|
self.main_layout = QVBoxLayout()
|
|
self.sub_layout = QVBoxLayout()
|
|
self.setLayout(self.main_layout)
|
|
self.label = QLabel('Full GE command (without script name):')
|
|
self.textedit = QLineEdit()
|
|
self.button = QPushButton('Run')
|
|
|
|
self.script_fn = '.autoPyLot.sh'
|
|
|
|
self.sub_layout.addWidget(self.label)
|
|
self.sub_layout.addWidget(self.textedit)
|
|
|
|
self.main_layout.addLayout(self.sub_layout)
|
|
self.main_layout.addWidget(self.button)
|
|
|
|
self.setDefaultCommand()
|
|
|
|
def setDefaultCommand(self):
|
|
default_command = 'qsub -l low -cwd -q TARGET_MACHINE -pe mpi-fu NCORES'
|
|
self.textedit.setText(default_command)
|
|
|
|
def start(self, pp_export, ncores=None):
|
|
self.genShellScript(pp_export)
|
|
self.execute_script()
|
|
|
|
def genShellScript(self, pp_export):
|
|
outfile = open(self.script_fn, 'w')
|
|
outfile.write('#!/bin/sh\n\n')
|
|
try:
|
|
ncores = int(self.textedit.text().split()[-1])
|
|
ncores = '--ncores {}'.format(ncores)
|
|
except:
|
|
ncores = None
|
|
outfile.write('python autoPyLoT.py -i {} {}\n'.format(pp_export, ncores))
|
|
outfile.close()
|
|
|
|
def execute_script(self):
|
|
command = self.textedit.text().strip().split(' ')
|
|
command.append(self.script_fn)
|
|
p = subprocess.Popen(command)
|
|
cmd_str = str()
|
|
for item in command:
|
|
cmd_str += item + ' '
|
|
print('exec. command: {}'.format(cmd_str))
|
|
print('Spawned autoPyLoT process with pid {}'.format(p.pid))
|
|
|
|
|
|
class SubmitLocal(QWidget):
|
|
def __init__(self, parent=None):
|
|
super(SubmitLocal, self).__init__(parent)
|
|
self.main_layout = QVBoxLayout()
|
|
self.setLayout(self.main_layout)
|
|
self.button = QPushButton('Run')
|
|
|
|
self.script_fn = ['python', 'autoPyLoT.py', '-i']
|
|
|
|
self.main_layout.addWidget(self.button)
|
|
|
|
def start(self, pp_export, ncores):
|
|
self.execute_command(pp_export, ncores)
|
|
|
|
def execute_command(self, pp_export, ncores):
|
|
command = self.script_fn[:]
|
|
command.append(pp_export)
|
|
command.append('--ncores')
|
|
command.append(str(ncores))
|
|
cmd_str = str()
|
|
for item in command:
|
|
cmd_str += item + ' '
|
|
print('exec. command: {}'.format(cmd_str))
|
|
p = subprocess.Popen(command)
|
|
print('Spawned autoPyLoT process with pid {}'.format(p.pid))
|
|
|
|
|
|
class PropertiesDlg(QDialog):
|
|
def __init__(self, parent=None, infile=None, inputs=None):
|
|
super(PropertiesDlg, self).__init__(parent)
|
|
self._pylot_mainwindow = self.parent()
|
|
|
|
self.infile = infile
|
|
self.inputs = inputs
|
|
|
|
self.setWindowTitle("PyLoT Properties")
|
|
self.tabWidget = QTabWidget()
|
|
self.tabWidget.addTab(InputsTab(self), "Inputs")
|
|
# self.tabWidget.addTab(OutputsTab(self), "Outputs")
|
|
self.tabWidget.addTab(PhasesTab(self, inputs), "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(list(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, inputs=None):
|
|
super(PhasesTab, self).__init__(parent)
|
|
self.inputs = inputs
|
|
|
|
self.Pphases = 'P, Pg, Pn, PmP, P1, P2, P3'
|
|
self.Sphases = 'S, Sg, Sn, SmS, S1, S2, S3'
|
|
|
|
self.PphasesEdit = QLineEdit()
|
|
self.SphasesEdit = QLineEdit()
|
|
|
|
self.pickDefaultsButton = QtWidgets.QPushButton('Choose default phases...')
|
|
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)
|
|
|
|
self.main_layout = QtWidgets.QHBoxLayout()
|
|
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.main_layout.addLayout(layout)
|
|
self.main_layout.addWidget(self.pickDefaultsButton)
|
|
self.setLayout(self.main_layout)
|
|
|
|
self.connectSignals()
|
|
|
|
def connectSignals(self):
|
|
self.pickDefaultsButton.clicked.connect(self.get_defaults)
|
|
|
|
def get_defaults(self):
|
|
phases = [p.strip() for p in self.Pphases.split(',')] + [s.strip() for s in self.Sphases.split(',')]
|
|
p_current = [p.strip() for p in self.PphasesEdit.text().split(',')]
|
|
s_current = [s.strip() for s in self.SphasesEdit.text().split(',')]
|
|
|
|
current_phases = p_current + s_current
|
|
if self.inputs:
|
|
parameter = self.inputs
|
|
if parameter.get('extent') == 'global':
|
|
# get all default phase names known to obspy.taup
|
|
# in a list and remove duplicates
|
|
phases = list(set(get_phase_names('ttall')))
|
|
phases.sort()
|
|
|
|
phaseDefaults = PhaseDefaults(self, phase_defaults=phases,
|
|
current_phases=current_phases)
|
|
if phaseDefaults.exec_():
|
|
phase_dict = self.sortPhases(phaseDefaults.selected_phases)
|
|
p_phases = ''
|
|
s_phases = ''
|
|
for index, p in enumerate(phase_dict['P']):
|
|
p_phases += p
|
|
if not index == len(phase_dict['P']) - 1:
|
|
p_phases += ', '
|
|
for index, s in enumerate(phase_dict['S']):
|
|
s_phases += s
|
|
if not index == len(phase_dict['S']) - 1:
|
|
s_phases += ', '
|
|
self.PphasesEdit.setText(p_phases)
|
|
self.SphasesEdit.setText(s_phases)
|
|
|
|
def sortPhases(self, phases):
|
|
sorted_phases = {'P': [],
|
|
'S': []}
|
|
for phase in phases:
|
|
idf_phase = loopIdentifyPhase(phase)
|
|
if idf_phase:
|
|
sorted_phases[identifyPhase(idf_phase)].append(phase)
|
|
return sorted_phases
|
|
|
|
def getValues(self):
|
|
p_phases = self.PphasesEdit.text()
|
|
s_phases = self.SphasesEdit.text()
|
|
values = {'p_phases': p_phases,
|
|
's_phases': s_phases}
|
|
return values
|
|
|
|
def resetValues(self, infile=None):
|
|
values = {'p_phases': self.PphasesEdit.setText(self.Pphases),
|
|
's_phases': self.SphasesEdit.setText(self.Sphases)}
|
|
return values
|
|
|
|
|
|
class GraphicsTab(PropTab):
|
|
def __init__(self, parent=None):
|
|
super(GraphicsTab, self).__init__(parent)
|
|
self.pylot_mainwindow = parent._pylot_mainwindow
|
|
self.init_layout()
|
|
self.add_pg_cb()
|
|
self.add_nth_sample()
|
|
self.add_style_settings()
|
|
self.setLayout(self.main_layout)
|
|
|
|
def init_layout(self):
|
|
self.main_layout = QGridLayout()
|
|
|
|
def add_style_settings(self):
|
|
styles = self.pylot_mainwindow._styles
|
|
active_stylename = self.pylot_mainwindow._stylename
|
|
label = QtWidgets.QLabel('Application style (might require Application restart):')
|
|
self.style_cb = QComboBox()
|
|
for stylename, style in styles.items():
|
|
self.style_cb.addItem(stylename, style)
|
|
index_current_style = self.style_cb.findText(active_stylename)
|
|
self.style_cb.setCurrentIndex(index_current_style)
|
|
self.main_layout.addWidget(label, 2, 0)
|
|
self.main_layout.addWidget(self.style_cb, 2, 1)
|
|
self.style_cb.activated.connect(self.set_current_style)
|
|
|
|
def add_nth_sample(self):
|
|
settings = QSettings()
|
|
nth_sample = settings.value("nth_sample")
|
|
if not nth_sample:
|
|
nth_sample = 1
|
|
|
|
self.spinbox_nth_sample = QtWidgets.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):
|
|
try:
|
|
import pyqtgraph as pg
|
|
pg = True
|
|
except:
|
|
pg = False
|
|
|
|
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 = QtWidgets.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 set_current_style(self):
|
|
selected_style = self.style_cb.currentText()
|
|
self.pylot_mainwindow.set_style(selected_style)
|
|
|
|
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.getFilters():
|
|
self.filterOptions = parent.getFilters()
|
|
# elif filterOptions is not None:
|
|
# self.filterOptions = filterOptions
|
|
else:
|
|
self.filterOptions = {'P': FilterOptions(),
|
|
'S': FilterOptions()}
|
|
|
|
self.setWindowTitle(titleString)
|
|
self.filterOptionWidgets = {'P': FilterOptionsWidget(self.filterOptions['P']),
|
|
'S': FilterOptionsWidget(self.filterOptions['S'])}
|
|
self.setupUi()
|
|
self.updateUi()
|
|
self.connectButtons()
|
|
|
|
def setupUi(self):
|
|
self.main_layout = QtWidgets.QVBoxLayout()
|
|
self.filter_layout = QtWidgets.QHBoxLayout()
|
|
self.groupBoxes = {'P': QtWidgets.QGroupBox('P Filter'),
|
|
'S': QtWidgets.QGroupBox('S Filter')}
|
|
|
|
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok |
|
|
QDialogButtonBox.Cancel)
|
|
|
|
for key in ['P', 'S']:
|
|
groupbox = self.groupBoxes[key]
|
|
box_layout = QtWidgets.QVBoxLayout()
|
|
groupbox.setLayout(box_layout)
|
|
|
|
self.filter_layout.addWidget(groupbox)
|
|
box_layout.addWidget(self.filterOptionWidgets[key])
|
|
|
|
self.main_layout.addLayout(self.filter_layout)
|
|
self.main_layout.addWidget(self.buttonBox)
|
|
self.setLayout(self.main_layout)
|
|
|
|
def connectButtons(self):
|
|
self.buttonBox.accepted.connect(self.accept)
|
|
self.buttonBox.rejected.connect(self.reject)
|
|
|
|
def accept(self):
|
|
self.updateUi()
|
|
QDialog.accept(self)
|
|
|
|
def updateUi(self):
|
|
returnvals = []
|
|
for foWidget in self.filterOptionWidgets.values():
|
|
foWidget.updateUi()
|
|
|
|
def getFilterOptions(self):
|
|
filteroptions = {'P': self.filterOptionWidgets['P'].getFilterOptions(),
|
|
'S': self.filterOptionWidgets['S'].getFilterOptions()}
|
|
return filteroptions
|
|
|
|
|
|
class FilterOptionsWidget(QWidget):
|
|
def __init__(self, filterOptions):
|
|
super(FilterOptionsWidget, self).__init__()
|
|
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.setSingleStep(0.01)
|
|
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.setSingleStep(0.01)
|
|
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.freqminSpinBox.setValue(self.getFilterOptions().getFreq()[0])
|
|
self.freqmaxSpinBox.setValue(self.getFilterOptions().getFreq()[1])
|
|
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)
|
|
|
|
try:
|
|
self.orderSpinBox.setValue(self.getFilterOptions().getOrder())
|
|
except:
|
|
self.orderSpinBox.setValue(2)
|
|
|
|
grid = QGridLayout()
|
|
grid.addWidget(self.freqGroupBox, 0, 2, 1, 2)
|
|
grid.addLayout(self.selectTypeLayout, 1, 2, 1, 2)
|
|
|
|
self.setLayout(grid)
|
|
|
|
self.freqminSpinBox.valueChanged.connect(self.checkMin)
|
|
self.freqmaxSpinBox.valueChanged.connect(self.checkMax)
|
|
self.orderSpinBox.valueChanged.connect(self.updateUi)
|
|
self.selectTypeCombo.currentIndexChanged.connect(self.updateUi)
|
|
|
|
def checkMin(self):
|
|
if not self.freqminSpinBox.value() <= self.freqmaxSpinBox.value():
|
|
self.freqmaxSpinBox.setValue(self.freqminSpinBox.value())
|
|
|
|
def checkMax(self):
|
|
if not self.freqminSpinBox.value() <= self.freqmaxSpinBox.value():
|
|
self.freqminSpinBox.setValue(self.freqmaxSpinBox.value())
|
|
|
|
def updateUi(self):
|
|
type = self.selectTypeCombo.currentText()
|
|
_enable = type in ['bandpass', 'bandstop']
|
|
freq = [self.freqminSpinBox.value(), self.freqmaxSpinBox.value()]
|
|
self.freqmaxLabel.setEnabled(True)
|
|
self.freqmaxSpinBox.setEnabled(True)
|
|
self.freqminLabel.setEnabled(True)
|
|
self.freqminSpinBox.setEnabled(True)
|
|
self.freqminLabel.setText("minimum:")
|
|
self.freqmaxLabel.setText("maximum:")
|
|
|
|
if not _enable:
|
|
if type == 'highpass':
|
|
self.freqminLabel.setText("cutoff:")
|
|
self.freqmaxLabel.setEnabled(False)
|
|
self.freqmaxSpinBox.setEnabled(False)
|
|
elif type == 'lowpass':
|
|
self.freqmaxLabel.setText("cutoff:")
|
|
self.freqminLabel.setEnabled(False)
|
|
self.freqminSpinBox.setEnabled(False)
|
|
else:
|
|
if not isSorted(freq):
|
|
QMessageBox.warning(self, "Value error",
|
|
"Maximum frequency must be at least the "
|
|
"same value as minimum frequency (notch)! "
|
|
"Adjusted maximum frequency automatically!")
|
|
freq[1] = freq[0]
|
|
self.freqmaxSpinBox.setValue(freq[1])
|
|
self.freqmaxSpinBox.selectAll()
|
|
self.freqmaxSpinBox.setFocus()
|
|
|
|
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
|
|
|
|
|
|
class LoadDataDlg(QDialog):
|
|
def __init__(self, parent=None):
|
|
super(LoadDataDlg, self).__init__(parent)
|
|
|
|
pass
|
|
|
|
|
|
class HelpForm(QDialog):
|
|
def __init__(self, parent=None,
|
|
page=QUrl('https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/')):
|
|
super(HelpForm, self).__init__(parent, 1)
|
|
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
self.setAttribute(Qt.WA_GroupLeader)
|
|
|
|
self.home_page = page
|
|
|
|
back_icon = QIcon()
|
|
back_icon.addPixmap(QPixmap(':/icons/back.png'))
|
|
|
|
home_icon = QIcon()
|
|
home_icon.addPixmap(QPixmap(':/icons/home.png'))
|
|
|
|
backAction = QAction(back_icon, "&Back", self)
|
|
backAction.setShortcut(QKeySequence.Back)
|
|
homeAction = QAction(home_icon, "&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)
|
|
self.setLayout(layout)
|
|
|
|
backAction.triggered.connect(self.webBrowser.back)
|
|
homeAction.triggered.connect(self.home)
|
|
self.webBrowser.urlChanged.connect(self.updatePageTitle)
|
|
|
|
self.resize(1280, 720)
|
|
self.setWindowTitle("{0} Help".format(QApplication.applicationName()))
|
|
|
|
def home(self):
|
|
self.webBrowser.load(self.home_page)
|
|
|
|
def updatePageTitle(self):
|
|
self.pageLabel.setText(self.webBrowser.title())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import doctest
|
|
|
|
doctest.testmod()
|