From 185cc14e38a195729899cced630e46c7b437240c Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 12 Jan 2018 10:07:14 +0100 Subject: [PATCH 01/18] [add] P and S Filter options for main window [bugfix] wforiginal not pre-processed --- PyLoT.py | 107 +++++++++++++++++++++++++------------- pylot/RELEASE-VERSION | 2 +- pylot/core/io/data.py | 21 +++++++- pylot/core/util/thread.py | 2 +- 4 files changed, 91 insertions(+), 41 deletions(-) diff --git a/PyLoT.py b/PyLoT.py index 063a89ca..0edd102a 100755 --- a/PyLoT.py +++ b/PyLoT.py @@ -274,6 +274,10 @@ class MainWindow(QMainWindow): print_icon.addPixmap(QPixmap(':/icons/printer.png')) self.filter_icon = QIcon() self.filter_icon.addPixmap(QPixmap(':/icons/filter.png')) + self.filter_icon_p = QIcon() + self.filter_icon_p.addPixmap(QPixmap(':/icons/filter_p.png')) + self.filter_icon_s = QIcon() + self.filter_icon_s.addPixmap(QPixmap(':/icons/filter_s.png')) z_icon = QIcon() z_icon.addPixmap(QPixmap(':/icons/key_Z.png')) n_icon = QIcon() @@ -368,12 +372,20 @@ class MainWindow(QMainWindow): self.setParameter, None, paraIcon, "Modify Parameter") - self.filterAction = self.createAction(self, "&Filter ...", - self.filterWaveformData, - "Ctrl+F", self.filter_icon, - """Toggle un-/filtered waveforms - to be displayed, according to the - desired seismic phase.""", True) + self.filterActionP = createAction(parent=self, text='Apply P Filter', + slot=self.filterP, + icon=self.filter_icon_p, + tip='Toggle filtered/original' + ' waveforms', + checkable=True, + shortcut='Ctrl+F') + self.filterActionS = createAction(parent=self, text='Apply S Filter', + slot=self.filterS, + icon=self.filter_icon_s, + tip='Toggle filtered/original' + ' waveforms', + checkable=True, + shortcut='Shift+F') filterEditAction = self.createAction(self, "&Filter parameter ...", self.adjustFilterOptions, "Alt+F", self.filter_icon, @@ -502,8 +514,8 @@ class MainWindow(QMainWindow): self.updateFileMenu() self.editMenu = self.menuBar().addMenu('&Edit') - editActions = (self.filterAction, filterEditAction, None, - self.selectPAction, self.selectSAction, None, + editActions = (self.filterActionP, self.filterActionS, filterEditAction, None, + #self.selectPAction, self.selectSAction, None, self.inventoryAction, self.initMapAction, None, prefsEventAction) #printAction) #TODO: print event? @@ -1415,7 +1427,8 @@ class MainWindow(QMainWindow): return None def getStime(self): - return self._stime + if self.get_data(): + return full_range(self.get_data().getWFData())[0] def addActions(self, target, actions): for action in actions: @@ -1589,18 +1602,9 @@ class MainWindow(QMainWindow): # else: # ans = False self.fnames = self.getWFFnames_from_eventbox() - self.data.setWFData(self.fnames) - wfdat = self.data.getWFData() # all available streams - # remove possible underscores in station names - wfdat = remove_underscores(wfdat) - # check for gaps and doubled channels - check4gaps(wfdat) - check4doubled(wfdat) - # check for stations with rotated components - wfdat = check4rotated(wfdat, self.metadata, verbosity=0) - # trim station components to same start value - trim_station_components(wfdat, trim_start=True, trim_end=False) - self._stime = full_range(self.get_data().getWFData())[0] + self.data.setWFData(self.fnames, + checkRotated=True, + metadata=self.metadata) def connectWFplotEvents(self): ''' @@ -1759,18 +1763,19 @@ class MainWindow(QMainWindow): self.disableSaveEventAction() self.draw() - def plotWaveformDataThread(self): + def plotWaveformDataThread(self, filter=True): ''' Open a modal thread to plot current waveform data. ''' self.clearWaveformDataPlot() self.wfp_thread = Thread(self, self.plotWaveformData, + arg=filter, progressText='Plotting waveform data...', pb_widget=self.mainProgressBarWidget) self.wfp_thread.finished.connect(self.finishWaveformDataPlot) self.wfp_thread.start() - def plotWaveformData(self): + def plotWaveformData(self, filter=True): ''' Plot waveform data to current plotWidget. ''' @@ -1782,8 +1787,10 @@ class MainWindow(QMainWindow): comp = self.getComponent() title = 'section: {0} components'.format(zne_text[comp]) wfst = self.get_data().getWFData() - if self.filterAction.isChecked(): - self.filterWaveformData(plot=False) + if self.filterActionP.isChecked() and filter: + self.filterWaveformData(plot=False, phase='P') + elif self.filterActionS.isChecked() and filter: + self.filterWaveformData(plot=False, phase='S') # wfst = self.get_data().getWFData().select(component=comp) # wfst += self.get_data().getWFData().select(component=alter_comp) plotWidget = self.getPlotWidget() @@ -1823,19 +1830,45 @@ class MainWindow(QMainWindow): def pushFilterWF(self, param_args): self.get_data().filterWFData(param_args) - def filterWaveformData(self, plot=True): + def filterP(self): + self.filterActionS.setChecked(False) + if self.filterActionP.isChecked(): + self.filterWaveformData(phase='P') + else: + self.resetWFData() + + def filterS(self): + self.filterActionP.setChecked(False) + if self.filterActionS.isChecked(): + self.filterWaveformData(phase='S') + else: + self.resetWFData() + + def resetWFData(self): + self.get_data().resetWFData() + self.plotWaveformDataThread() + + def filterWaveformData(self, plot=True, phase=None): if self.get_data(): - if self.getFilterOptions() and self.filterAction.isChecked(): - kwargs = self.getFilterOptions()[self.getSeismicPhase()].parseFilterOptions() - self.pushFilterWF(kwargs) - elif self.filterAction.isChecked(): + if not phase: + if self.filterActionP.isChecked(): + phase = 'P' + elif self.filterActionS.isChecked(): + phase = 'S' + if self.getFilterOptions(): + if (phase == 'P' and self.filterActionP.isChecked()) or (phase == 'S' and self.filterActionS.isChecked()): + kwargs = self.getFilterOptions()[phase].parseFilterOptions() + self.pushFilterWF(kwargs) + else: + self.get_data().resetWFData() + elif self.filterActionP.isChecked() or self.filterActionS.isChecked(): self.adjustFilterOptions() else: self.get_data().resetWFData() - if plot: - self.plotWaveformDataThread() - self.drawPicks() - self.draw() + if plot: + self.plotWaveformDataThread(filter=False) + #self.drawPicks() + #self.draw() def adjustFilterOptions(self): fstring = "Filter Options" @@ -1844,7 +1877,7 @@ class MainWindow(QMainWindow): if self.filterDlg.exec_(): filteroptions = self.filterDlg.getFilterOptions() self.setFilterOptions(filteroptions) - if self.filterAction.isChecked(): + if self.filterActionP.isChecked() or self.filterActionS.isChecked(): kwargs = self.getFilterOptions()[self.getSeismicPhase()].parseFilterOptions() self.pushFilterWF(kwargs) self.plotWaveformDataThread() @@ -1925,7 +1958,7 @@ class MainWindow(QMainWindow): # '[{0}: {1} Hz]'.format( # self.getFilterOptions().getFilterType(), # self.getFilterOptions().getFreq())) - # if self.filterAction.isChecked(): + # if self.filterActionP.isChecked() or self.filterActionS.isChecked(): # self.filterWaveformData() def getSeismicPhase(self): @@ -2018,7 +2051,7 @@ class MainWindow(QMainWindow): autopicks=self.getPicksOnStation(station, 'auto'), metadata=self.metadata, event=event, filteroptions=self.filteroptions) - if self.filterAction.isChecked(): + if self.filterActionP.isChecked() or self.filterActionS.isChecked(): pickDlg.currentPhase = self.getSeismicPhase() pickDlg.filterWFData() pickDlg.nextStation.setChecked(nextStation) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index b775f1a3..18025107 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0ebc-dirty +c7dc-dirty diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index eef5ef88..60d017d1 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -12,7 +12,8 @@ from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \ picksdict_from_pilot, merge_picks from pylot.core.util.errors import FormatError, OverwriteError from pylot.core.util.event import Event -from pylot.core.util.utils import fnConstructor, full_range +from pylot.core.util.utils import fnConstructor, full_range, remove_underscores, check4gaps, check4doubled, \ + check4rotated, trim_station_components import pylot.core.loc.velest as velest @@ -367,7 +368,7 @@ class Data(object): data.filter(**kwargs) self.dirty = True - def setWFData(self, fnames): + def setWFData(self, fnames, checkRotated=False, metadata=None): """ Clear current waveform data and set given waveform data :param fnames: waveform data names to append @@ -379,10 +380,26 @@ class Data(object): self.appendWFData(fnames) else: return False + + # various pre-processing steps: + # remove possible underscores in station names + self.wfdata = remove_underscores(self.wfdata) + # check for gaps and doubled channels + check4gaps(self.wfdata) + check4doubled(self.wfdata) + # check for stations with rotated components + if checkRotated and metadata is not None: + self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0) + # trim station components to same start value + trim_station_components(self.wfdata, trim_start=True, trim_end=False) + + # make a copy of original data self.wforiginal = self.getWFData().copy() self.dirty = False return True + + def appendWFData(self, fnames): """ Read waveform data from fnames and append it to current wf data diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index ae56c697..0a8151d0 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -24,7 +24,7 @@ class Thread(QThread): if self.redirect_stdout: sys.stdout = self try: - if self.arg: + if self.arg is not None: self.data = self.func(self.arg) else: self.data = self.func() From 23ed5ceb5cb55e2960389fd4ddeceed49bb77023 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 12 Jan 2018 10:10:51 +0100 Subject: [PATCH 02/18] [update] filter hotkeys changed to P, S, Ctrl+F --- PyLoT.py | 6 +++--- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/PyLoT.py b/PyLoT.py index 0edd102a..d0070a23 100755 --- a/PyLoT.py +++ b/PyLoT.py @@ -378,17 +378,17 @@ class MainWindow(QMainWindow): tip='Toggle filtered/original' ' waveforms', checkable=True, - shortcut='Ctrl+F') + shortcut='P') self.filterActionS = createAction(parent=self, text='Apply S Filter', slot=self.filterS, icon=self.filter_icon_s, tip='Toggle filtered/original' ' waveforms', checkable=True, - shortcut='Shift+F') + shortcut='S') filterEditAction = self.createAction(self, "&Filter parameter ...", self.adjustFilterOptions, - "Alt+F", self.filter_icon, + "Ctrl+F", self.filter_icon, """Adjust filter parameters.""") self.inventoryAction = self.createAction(self, "Select &Inventory ...", self.get_new_metadata, diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 18025107..a28bf6ea 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -c7dc-dirty +185c-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 16ead10a..8c2c81c5 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1264,14 +1264,14 @@ class PickDlg(QDialog): tip='Toggle filtered/original' ' waveforms', checkable=True, - shortcut='Ctrl+F') + shortcut='P') self.filterActionS = createAction(parent=self, text='Apply S Filter', slot=self.filterS, icon=filter_icon_s, tip='Toggle filtered/original' ' waveforms', checkable=True, - shortcut='Shift+F') + shortcut='S') self.autoFilterAction = createAction(parent=self, text='Automatic Filtering', slot=self.toggleAutoFilter, icon=key_a_icon, @@ -1540,7 +1540,7 @@ class PickDlg(QDialog): filterOptionsAction = createAction(parent=self, text="&Filter parameter ...", slot=self.filterOptions, - shortcut='Alt+F', + shortcut='Ctrl+F', icon=self.orig_parent.filter_icon) filterMenu = menuBar.addMenu('Filter') filterMenu.addAction(self.filterActionP) From ffa30e92e96089ca6f9bcb3bb60097afcbc06daf Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 12 Jan 2018 10:15:30 +0100 Subject: [PATCH 03/18] [bugfix] tried to filter without active event --- PyLoT.py | 3 +++ pylot/RELEASE-VERSION | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/PyLoT.py b/PyLoT.py index d0070a23..ef88c010 100755 --- a/PyLoT.py +++ b/PyLoT.py @@ -1849,6 +1849,9 @@ class MainWindow(QMainWindow): self.plotWaveformDataThread() def filterWaveformData(self, plot=True, phase=None): + if not self.get_current_event(): + return + if self.get_data(): if not phase: if self.filterActionP.isChecked(): diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index a28bf6ea..ce33fbd0 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -185c-dirty +23ed-dirty From 5aef50f922b3a1355e02069f2a8433b2f2cddd87 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 12 Jan 2018 14:07:36 +0100 Subject: [PATCH 04/18] [new] save filteroptions as strings in XML (untested) --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/phases.py | 13 +++++++++++-- pylot/core/util/utils.py | 32 ++++++++++++++++++++++++++++++++ pylot/core/util/widgets.py | 16 +++++++--------- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index ce33fbd0..237b9555 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -23ed-dirty +ffa3-dirty diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index b5650fe6..9391e68f 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -16,7 +16,8 @@ from pylot.core.io.inputs import PylotParameter from pylot.core.io.location import create_event, \ create_magnitude from pylot.core.pick.utils import select_for_phase -from pylot.core.util.utils import getOwner, full_range, four_digits +from pylot.core.util.utils import getOwner, full_range, four_digits, transformFilteroptions2String, \ + transformFilterString4Export, backtransformFilterString def add_amplitudes(event, amplitudes): @@ -235,6 +236,7 @@ def picksdict_from_picks(evt): network = pick.waveform_id.network_code mpp = pick.time spe = pick.time_errors.uncertainty + filter_id = backtransformFilterString(str(pick.filter_id.id)) try: picker = str(pick.method_id) if picker.startswith('smi:local/'): @@ -261,6 +263,7 @@ def picksdict_from_picks(evt): phase['channel'] = channel phase['network'] = network phase['picker'] = picker + phase['filter_id'] = filter_id if filter_id is not None else '' onsets[pick.phase_hint] = phase.copy() picksdict[picker][station] = onsets.copy() @@ -312,6 +315,13 @@ def picks_from_picksdict(picks, creation_info=None): pick.waveform_id = ope.WaveformStreamID(station_code=station, channel_code=ccode, network_code=ncode) + try: + filter_id = phase['filteroptions'] + filter_id = transformFilterString4Export(filter_id) + except KeyError as e: + warnings.warn(e.message, RuntimeWarning) + filter_id = '' + pick.filter_id = filter_id try: polarity = phase['fm'] if polarity == 'U' or '+': @@ -328,7 +338,6 @@ def picks_from_picksdict(picks, creation_info=None): picks_list.append(pick) return picks_list - def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0): import glob diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 6a91c844..9185f971 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -391,6 +391,38 @@ def full_range(stream): return min_start, max_end +def transformFilteroptions2String(filtopts): + st = '' + if not filtopts: + return st + if 'type' in filtopts.keys(): + st += '{}'.format(filtopts['type']) + if 'freq' in filtopts.keys(): + st += ' | freq: {}'.format(filtopts['freq']) + elif 'freqmin' in filtopts.keys() and 'freqmax' in filtopts.keys(): + st += ' | freqmin: {} | freqmax: {}'.format(filtopts['freqmin'], filtopts['freqmax']) + for key, value in filtopts.items(): + if key in ['type', 'freq', 'freqmin', 'freqmax']: + continue + st += ' | {}: {}'.format(key, value) + return st + + +def transformFilterString4Export(st): + st = st.replace('|', '//') + st = st.replace(':', '/') + st = st.replace(' ', '') + return st + + +def backtransformFilterString(st): + st = st.split('smi:local/') + st = st[1] if len(st) > 1 else st[0] + st = st.replace('//', ' | ') + st = st.replace('/', ': ') + return st + + def getHash(time): """ takes a time object and returns the corresponding SHA1 hash of the formatted date string diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8c2c81c5..fb64e3ea 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -17,8 +17,6 @@ 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, real_Bool, pick_color try: from matplotlib.backends.backend_qt4agg import FigureCanvas @@ -49,7 +47,9 @@ 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 + check4rotated, check4doubled, check4gaps, remove_underscores, find_horizontals, identifyPhase, \ + loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \ + identifyPhaseID, real_Bool, pick_color from autoPyLoT import autoPyLoT from pylot.core.util.thread import Thread @@ -1989,7 +1989,8 @@ class PickDlg(QDialog): # 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) + network=wfdata[0].stats.network, + filteroptions=transformFilteroptions2String(filteroptions)) try: oldphasepick = self.picks[phase] @@ -2202,11 +2203,8 @@ class PickDlg(QDialog): data.detrend('linear') data.taper(0.02, type='cosine') data.filter(**filtoptions) - title += ' | {} filtered |'.format(filtoptions['type']) - for key, value in filtoptions.items(): - if key == 'type': - continue - title += ' {}: {} |'.format(key, value) + filtops_str = transformFilteroptions2String(filtoptions) + title += ' | Filteroptions: {}'.format(filtops_str) self.multicompfig.plotWFData(wfdata=data, title=title, zoomx=self.getXLims(), zoomy=self.getYLims()) From bb395d25146a2f2ae8defdb98ef63aa5c60549be Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 12 Jan 2018 14:42:16 +0100 Subject: [PATCH 05/18] [new] preparation for pick info on click --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 43 +++++++++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 237b9555..2f9654ee 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -ffa3-dirty +5aef5-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index fb64e3ea..ca75e363 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2109,12 +2109,33 @@ class PickDlg(QDialog): ax.legend(loc=1) def connect_pick_delete(self): - self.cidpick = self.multicompfig.mpl_connect('pick_event', self.onpick_delete) + self.cidpick = self.multicompfig.mpl_connect('pick_event', self.onpick) def disconnect_pick_delete(self): if hasattr(self, 'cidpick'): self.multicompfig.mpl_disconnect(self.cidpick) + def onpick(self, event): + if event.mouseevent.button == 1: + self.onpick_info(event) + elif event.mouseevent.button == 3: + self.onpick_delete(event) + + def onpick_info(self, event): + if not event.mouseevent.button == 1: + return + x = event.mouseevent.xdata + allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x) + pick = allpicks[picktype][phase] + message = '{} {}-pick'.format(picktype, phase) + if 'mpp' in pick: + message += ', mpp: {}'.format(pick['mpp']) + if 'spe' in pick: + message += ', spe: {}'.format(pick['spe']) + if 'filteroptions' in pick: + message += ', filter: {}'.format(pick['filteroptions']) + print(message) + def onpick_delete(self, event): if not event.mouseevent.button == 3: return @@ -2125,7 +2146,16 @@ class PickDlg(QDialog): def remove_pick_by_x(self, x): if not self.picks and not self.autopicks: return - # init empty list and get station starttime + allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x) + # 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, self.getStartTime()+pick_rel, pick_rel)) + self.setDirty(True) + + def identify_selected_picks(self, x): + # init empty list and get stat5ion starttime X = [] starttime = self.getStartTime() # init dictionaries to iterate through and iterate over them @@ -2143,12 +2173,9 @@ class PickDlg(QDialog): 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) + return allpicks, pick_rel, phase, picktype + + def drawPhaseText(self): self.drawPicks(picktype='manual', textOnly=True) From 796eea87a1adeb6bcb809652b9b9be8e72159e69 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 12 Jan 2018 15:14:57 +0100 Subject: [PATCH 06/18] [new] show time and pick info on left click in 3 comp window! --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 29 ++++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 2f9654ee..a2d38bef 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -5aef5-dirty +bb39-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index ca75e363..3c28c209 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1228,6 +1228,7 @@ class PickDlg(QDialog): # init pick delete (with right click) self.connect_pick_delete() + self.connect_mouse_motion() self.setWindowTitle('Pickwindow on station: {}'.format(self.getStation())) self.setWindowState(QtCore.Qt.WindowMaximized) @@ -1316,6 +1317,8 @@ class PickDlg(QDialog): self.reject_button = QPushButton('&Reject') self.disable_ar_buttons() + self.statusbar = QtGui.QStatusBar(self) + # add hotkeys self._shortcut_space = QtGui.QShortcut(QtGui.QKeySequence(' '), self) self._shortcut_space.activated.connect(self.accept_button.clicked) @@ -1360,9 +1363,12 @@ class PickDlg(QDialog): # layout the innermost widget _innerlayout = QVBoxLayout() _innerinnerlayout = QtGui.QHBoxLayout() + _lowerlayout = QHBoxLayout() _innerinnerlayout.addWidget(self.multicompfig) _innerinnerlayout.addWidget(self.phaseplot) _innerlayout.addLayout(_innerinnerlayout) + _innerlayout.addLayout(_lowerlayout) + _lowerlayout.addWidget(self.statusbar) # add button box to the dialog _buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | @@ -1370,13 +1376,17 @@ class PickDlg(QDialog): # merge widgets and layouts to establish the dialog if not self._embedded: - _innerlayout.addWidget(_buttonbox) + _lowerlayout.addWidget(_buttonbox) _outerlayout.addWidget(menuBar) _outerlayout.addWidget(_dialtoolbar) _outerlayout.addLayout(_innerlayout) _outerlayout.setStretch(0, 0) _outerlayout.setStretch(1, 0) _outerlayout.setStretch(2, 1) + _lowerlayout.setStretch(0, 5) + _lowerlayout.setStretch(1, 1) + _innerlayout.setStretch(0, 1) + _innerlayout.setStretch(1, 0) # connect widget element signals with slots (methods to the dialog # object @@ -2108,6 +2118,10 @@ class PickDlg(QDialog): ax.legend(loc=1) + def connect_mouse_motion(self): + self.cidmotion = self.multicompfig.mpl_connect( + 'motion_notify_event', self.on_motion) + def connect_pick_delete(self): self.cidpick = self.multicompfig.mpl_connect('pick_event', self.onpick) @@ -2115,6 +2129,11 @@ class PickDlg(QDialog): if hasattr(self, 'cidpick'): self.multicompfig.mpl_disconnect(self.cidpick) + def on_motion(self, event): + x = event.xdata + if x is not None: + self.statusbar.showMessage('T = {}, t = {} [s]'.format(self.stime+x, x)) + def onpick(self, event): if event.mouseevent.button == 1: self.onpick_info(event) @@ -2129,12 +2148,12 @@ class PickDlg(QDialog): pick = allpicks[picktype][phase] message = '{} {}-pick'.format(picktype, phase) if 'mpp' in pick: - message += ', mpp: {}'.format(pick['mpp']) + message += ', MPP: {}'.format(pick['mpp']) if 'spe' in pick: - message += ', spe: {}'.format(pick['spe']) + message += ', SPE: {}'.format(pick['spe']) if 'filteroptions' in pick: - message += ', filter: {}'.format(pick['filteroptions']) - print(message) + message += ', FILTER: {}'.format(pick['filteroptions']) + self.statusbar.showMessage(message, 10e3) def onpick_delete(self, event): if not event.mouseevent.button == 3: From 534222e241e7e855161a922843561dcb371ca978 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 11:16:26 +0100 Subject: [PATCH 07/18] [bugfix] Exception when pick.filter_id was None --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/phases.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index a2d38bef..a729ded2 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -bb39-dirty +796e-dirty diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 9391e68f..55a496e2 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -236,7 +236,10 @@ def picksdict_from_picks(evt): network = pick.waveform_id.network_code mpp = pick.time spe = pick.time_errors.uncertainty - filter_id = backtransformFilterString(str(pick.filter_id.id)) + if pick.filter_id: + filter_id = backtransformFilterString(str(pick.filter_id.id)) + else: + filter_id = None try: picker = str(pick.method_id) if picker.startswith('smi:local/'): From 0013d099f392e4d6d970d4df65d67ae4ec3d77a1 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 11:21:43 +0100 Subject: [PATCH 08/18] [new] tooltip for phases on mouseover (untested) --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index a729ded2..98c9f0b1 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -796e-dirty +5342-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 3c28c209..6294192f 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -375,8 +375,8 @@ class ComparisonWidget(QWidget): 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') + #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() @@ -1140,6 +1140,7 @@ class PickDlg(QDialog): self.components = 'ZNE' self.currentPhase = None self.phaseText = [] + self.phaseLines = [] self.arrivals = [] self.arrivalsText = [] self.cidpick = [] @@ -2085,8 +2086,9 @@ class PickDlg(QDialog): 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) + vl = ax.axvline(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp, + label='{}-Pick (quality: {})'.format(phase, quality), picker=5) + self.phaseLines.append(vl) if spe: ax.fill_between([mpp-spe, mpp+spe], ylims[0], ylims[1], alpha=.25, color=color, label='{}-SPE'.format(phase)) @@ -2109,8 +2111,9 @@ class PickDlg(QDialog): 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)) + vl = ax.vlines(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp, + picker=5, label='{}-Autopick (quality: {})'.format(phase, quality)) + self.phaseLines.append(vl) # append phase text (if textOnly: draw with current ylims) self.phaseText.append(ax.text(mpp, ylims[1], phase, color=color)) else: @@ -2124,6 +2127,7 @@ class PickDlg(QDialog): def connect_pick_delete(self): self.cidpick = self.multicompfig.mpl_connect('pick_event', self.onpick) + self.cidpick = self.multicompfig.mpl_connect('motion_notify_event', self.on_hover_info) def disconnect_pick_delete(self): if hasattr(self, 'cidpick'): @@ -2140,6 +2144,27 @@ class PickDlg(QDialog): elif event.mouseevent.button == 3: self.onpick_delete(event) + def on_hover_info(self, event): + if not any([phase.contains(event)[0] for phase in self.phaseLines]): + return + x = event.xdata + if not x: + return + allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x) + pick = allpicks[picktype][phase] + message = '{} {}-pick'.format(picktype, phase) + if 'mpp' in pick: + message += ', MPP: {}'.format(pick['mpp']) + if 'spe' in pick: + message += ', SPE: {} [s]'.format(pick['spe']) + if 'filteroptions' in pick: + message += ', FILTER: {}'.format(pick['filteroptions']) + x = event.x + y = event.y + y = self.size().height() - y + pt = self.mapToGlobal(QtCore.QPoint(x, y)) + QtGui.QToolTip.showText(pt, message) + def onpick_info(self, event): if not event.mouseevent.button == 1: return From d6400562d63396776b9af13e5be89b21365a7fc8 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 11:22:49 +0100 Subject: [PATCH 09/18] [change] DELETE PICK WITH MIDDLE MOUSE BUTTON (else deleted using zoom) --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 6294192f..e40430a4 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2141,7 +2141,7 @@ class PickDlg(QDialog): def onpick(self, event): if event.mouseevent.button == 1: self.onpick_info(event) - elif event.mouseevent.button == 3: + elif event.mouseevent.button == 2: self.onpick_delete(event) def on_hover_info(self, event): From a1ee8d408c14bdf96882519f3845f602f3551dff Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 11:31:30 +0100 Subject: [PATCH 10/18] [add] user_help to status bar [bugfix] missing change to mouseevent button --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 98c9f0b1..1ddb9ca5 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -5342-dirty +d640-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e40430a4..0b4c7463 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2136,7 +2136,10 @@ class PickDlg(QDialog): def on_motion(self, event): x = event.xdata if x is not None: - self.statusbar.showMessage('T = {}, t = {} [s]'.format(self.stime+x, x)) + time_code = 'T = {}, t = {} [s]'.format(self.stime+x, x) + user_help = ' - Left-Click to Drag | Right-Click to Pan-Zoom |' \ + ' Mousewheel to Zoom | Middle-Click to Delete Pick' + self.statusbar.showMessage(time_code + user_help) def onpick(self, event): if event.mouseevent.button == 1: @@ -2181,7 +2184,7 @@ class PickDlg(QDialog): self.statusbar.showMessage(message, 10e3) def onpick_delete(self, event): - if not event.mouseevent.button == 3: + if not event.mouseevent.button == 2: return x = event.mouseevent.xdata self.remove_pick_by_x(x) From 44f63f816378c3d4fc9ddf4860b5b538a3993fdd Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 11:38:07 +0100 Subject: [PATCH 11/18] [change] ax.vlines -> ax.axvline (infinite) --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 1ddb9ca5..4b1bdc80 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -d640-dirty +a1ee-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0b4c7463..a5369484 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2094,12 +2094,12 @@ class PickDlg(QDialog): 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)) + ax.axvline(epp, ylims[0], ylims[1], 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)) + ax.axvline(lpp, ylims[0], ylims[1], 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) @@ -2111,8 +2111,8 @@ class PickDlg(QDialog): if not textOnly: ax.plot(mpp, ylims[1], color=color, marker='v') ax.plot(mpp, ylims[0], color=color, marker='^') - vl = ax.vlines(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp, - picker=5, label='{}-Autopick (quality: {})'.format(phase, quality)) + vl = ax.axvline(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp, + picker=5, label='{}-Autopick (quality: {})'.format(phase, quality)) self.phaseLines.append(vl) # append phase text (if textOnly: draw with current ylims) self.phaseText.append(ax.text(mpp, ylims[1], phase, color=color)) @@ -2920,9 +2920,9 @@ class TuneAutopicker(QWidget): 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.axvline(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)) From 4589f92a1a84cbfeaabe756428e17edc965c9a45 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 13:52:31 +0100 Subject: [PATCH 12/18] [new] renaming of phases (untested) --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 4b1bdc80..d6e8b031 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -a1ee-dirty +44f63-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a5369484..5595c1b5 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1258,6 +1258,8 @@ class PickDlg(QDialog): home_icon.addPixmap(QPixmap(':/icons/zoom_0.png')) del_icon = QIcon() del_icon.addPixmap(QPixmap(':/icons/delete.png')) + sync_icon = QIcon() + sync_icon.addPixmap(QPixmap(':/icons/sync.png')) # create actions self.filterActionP = createAction(parent=self, text='Apply P Filter', @@ -1290,6 +1292,9 @@ class PickDlg(QDialog): self.resetPicksAction = createAction(parent=self, text='Delete Picks', slot=self.delPicks, icon=del_icon, tip='Delete current picks.') + self.renamePhaseAction = createAction(parent=self, text='Rename Phase', + slot=self.initRenamePhase, icon=sync_icon, + tip='Rename a Phase.', checkable=True) self.addPickPhases(menuBar) @@ -1342,6 +1347,7 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.resetZoomAction) _dialtoolbar.addSeparator() _dialtoolbar.addAction(self.resetPicksAction) + _dialtoolbar.addAction(self.renamePhaseAction) _dialtoolbar.addSeparator() if self._embedded: manu_label = QLabel('Manual Onsets:') @@ -1795,6 +1801,17 @@ class PickDlg(QDialog): self.resetPicks() self.refreshPlot() + def initRenamePhase(self): + if self.renamePhaseAction.isChecked(): + self.multicompfig.disconnectEvents() + self.multicompfig.set_frame_color('orange') + self.draw() + self.statusbar.showMessage('Click on a phase you want to rename.') + else: + self.multicompfig.set_frame_color() + self.multicompfig.connectEvents() + self.draw() + def setIniPick(self, gui_event): self.multicompfig.set_frame_color('green') trace_number = round(gui_event.ydata) @@ -2045,6 +2062,7 @@ class PickDlg(QDialog): self.removePhaseText() self.drawPicks(picktype='manual') self.drawPicks(picktype='auto') + self.draw() def drawPicks(self, phase=None, picktype='manual', textOnly=False, picks=None): # plotting picks @@ -2181,6 +2199,10 @@ class PickDlg(QDialog): message += ', SPE: {}'.format(pick['spe']) if 'filteroptions' in pick: message += ', FILTER: {}'.format(pick['filteroptions']) + + if self.renamePhaseAction.isChecked(): + self.renamePhase(picktype, phase) + self.statusbar.showMessage(message, 10e3) def onpick_delete(self, event): @@ -2190,6 +2212,30 @@ class PickDlg(QDialog): self.remove_pick_by_x(x) self.refreshPlot() + def renamePhase(self, picktype, phase): + allpicks = {'manual': self.picks, + 'auto': self.autopicks} + picks = allpicks[picktype] + dialog = QtGui.QInputDialog(parent=self) + new_phase, executed = dialog.getText(self, 'Rename phase', 'Rename phase {} to:'.format(phase)) + if executed: + try: + self.renamePhaseInDict(picks, phase, new_phase) + except KeyError as e: + QtGui.QMessageBox.warning(self, 'Could not rename phase', + 'Could not rename phase {} to {}: {}'.format(phase, new_phase, e)) + self.renamePhaseAction.setChecked(False) + self.multicompfig.set_frame_color() + self.multicompfig.connectEvents() + self.refreshPlot() + + def renamePhaseInDict(self, picks, phase_old, phase_new): + if phase_new in picks: + raise KeyError('New phase ID already assigned.') + picks[phase_new] = picks[phase_old].copy() + picks.pop(phase_old) + self.setDirty(True) + def remove_pick_by_x(self, x): if not self.picks and not self.autopicks: return From b191bd8cf44d723425e65ccf28317718334223b7 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 14:17:14 +0100 Subject: [PATCH 13/18] [update] warn/do not save picks with unknown phase ID --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/utils.py | 2 ++ pylot/core/util/widgets.py | 58 ++++++++++++++++++-------------------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index d6e8b031..427df7f4 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -44f63-dirty +4589-dirty diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 9185f971..6de1e8e4 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -1169,6 +1169,8 @@ def identifyPhase(phase): # common phase suffix for P and S common_P = ['P', 'p', 'R'] common_S = ['S', 's'] + if phase is None: + return False if phase[-1] in common_P: return 'P' if phase[-1] in common_S: diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 5595c1b5..e942dae3 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1144,6 +1144,7 @@ class PickDlg(QDialog): self.arrivals = [] self.arrivalsText = [] self.cidpick = [] + self.cidpress = None settings = QSettings() pylot_user = getpass.getuser() self._user = settings.value('user/Login', pylot_user) @@ -1678,7 +1679,12 @@ class PickDlg(QDialog): def activatePicking(self): phase = self.currentPhase - color = pick_color_plt('manual', self.getPhaseID(phase)) + phaseID = self.getPhaseID(phase) + if not phaseID: + self.warn_unknown_phase(phase) + self.leave_picking_mode() + return + color = pick_color_plt('manual', phaseID) self.multicompfig.set_frame_color(color) self.multicompfig.set_frame_linewidth(1.5) if self.zoomAction.isChecked(): @@ -2020,39 +2026,27 @@ class PickDlg(QDialog): network=wfdata[0].stats.network, filteroptions=transformFilteroptions2String(filteroptions)) - 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) + saved = self.savePick(phase, phasepicks, pick, epp, lpp) + if saved: + self.setDirty(True) self.disconnectPressEvent() self.enable_ar_buttons() self.zoomAction.setEnabled(True) #self.pick_block = self.togglePickBlocker() self.leave_picking_mode() - self.setDirty(True) + + def savePick(self, phase, phasepicks): + if not self.getPhaseID(phase): + self.warn_unknown_phase(phase) + return + + self.picks[phase] = phasepicks + return True + + def warn_unknown_phase(self, phase=None): + QtGui.QMessageBox.warning(self, 'Unknown phase ID', + 'Could not identify phase ID: {}.'.format(phase)) def disconnectPressEvent(self): self.multicompfig.mpl_disconnect(self.cidpress) @@ -2232,9 +2226,11 @@ class PickDlg(QDialog): def renamePhaseInDict(self, picks, phase_old, phase_new): if phase_new in picks: raise KeyError('New phase ID already assigned.') - picks[phase_new] = picks[phase_old].copy() - picks.pop(phase_old) - self.setDirty(True) + picks_new = picks[phase_old].copy() + saved = self.savePick(phase_new, picks_new) + if saved: + picks.pop(phase_old) + self.setDirty(True) def remove_pick_by_x(self, x): if not self.picks and not self.autopicks: From 49de0fb22e352797a17f08cdaa49d617511a0d2a Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 14:20:48 +0100 Subject: [PATCH 14/18] [add] Hotkey for renaming --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 427df7f4..ddf27fb8 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -4589-dirty +b191b-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e942dae3..1fe72cc2 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1295,7 +1295,8 @@ class PickDlg(QDialog): tip='Delete current picks.') self.renamePhaseAction = createAction(parent=self, text='Rename Phase', slot=self.initRenamePhase, icon=sync_icon, - tip='Rename a Phase.', checkable=True) + tip='Rename a Phase.', checkable=True, + shortcut='R') self.addPickPhases(menuBar) From 1c51ed12fc092b1c40fa90a277f33851096782b5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 14:21:10 +0100 Subject: [PATCH 15/18] [bugfix] missing paranthesis --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 1fe72cc2..b18ebd93 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2357,7 +2357,7 @@ class PickDlg(QDialog): self.filterActionS.setChecked(False) data = self.getWFData().copy() title = self.getStation() - filter = self.filterActionP.isChecked or self.filterActionS.isChecked() + filter = self.filterActionP.isChecked() or self.filterActionS.isChecked() self.plotWFData(filter=filter) def resetZoom(self): From 5b5902a32994a8fe39480b44202ba19cfca8e607 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 14:28:08 +0100 Subject: [PATCH 16/18] [update] keep filter corresponding to phase when refreshing plot --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index ddf27fb8..7d063375 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -b191b-dirty +1c51-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b18ebd93..4a98435f 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2355,10 +2355,17 @@ class PickDlg(QDialog): if self.autoFilterAction.isChecked(): self.filterActionP.setChecked(False) self.filterActionS.setChecked(False) - data = self.getWFData().copy() - title = self.getStation() - filter = self.filterActionP.isChecked() or self.filterActionS.isChecked() - self.plotWFData(filter=filter) + # data = self.getWFData().copy() + # title = self.getStation() + filter = False + phase = None + if self.filterActionP.isChecked(): + phase = 'P' + filter = True + if self.filterActionS.isChecked(): + phase = 'S' + filter = True + self.plotWFData(phase=phase, filter=filter) def resetZoom(self): ax = self.multicompfig.axes[0] From f4839bb609516fe20aeffa32009f04ed82014c52 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 14:31:42 +0100 Subject: [PATCH 17/18] [bugfix] too many arguments in function (outdated) --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 4a98435f..c2d34800 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2027,7 +2027,7 @@ class PickDlg(QDialog): network=wfdata[0].stats.network, filteroptions=transformFilteroptions2String(filteroptions)) - saved = self.savePick(phase, phasepicks, pick, epp, lpp) + saved = self.savePick(phase, phasepicks) if saved: self.setDirty(True) From de8886b73b4ff4c3e063f22379f5fe947b52a6f5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Jan 2018 14:37:07 +0100 Subject: [PATCH 18/18] [update] leave rename phase mode when picking initiated --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 7d063375..878d172c 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -1c51-dirty +f483-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index c2d34800..9a2c9411 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1679,6 +1679,8 @@ class PickDlg(QDialog): self.deactivatePicking() def activatePicking(self): + self.leave_rename_phase() + self.renamePhaseAction.setEnabled(False) phase = self.currentPhase phaseID = self.getPhaseID(phase) if not phaseID: @@ -1709,6 +1711,7 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.multicompfig.connectEvents() + self.renamePhaseAction.setEnabled(True) self.connect_pick_delete() self.draw() @@ -2034,7 +2037,7 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.enable_ar_buttons() self.zoomAction.setEnabled(True) - #self.pick_block = self.togglePickBlocker() + #self.pick_block = self.togglPickBlocker() self.leave_picking_mode() def savePick(self, phase, phasepicks): @@ -2219,9 +2222,7 @@ class PickDlg(QDialog): except KeyError as e: QtGui.QMessageBox.warning(self, 'Could not rename phase', 'Could not rename phase {} to {}: {}'.format(phase, new_phase, e)) - self.renamePhaseAction.setChecked(False) - self.multicompfig.set_frame_color() - self.multicompfig.connectEvents() + self.leave_rename_phase() self.refreshPlot() def renamePhaseInDict(self, picks, phase_old, phase_new): @@ -2233,6 +2234,11 @@ class PickDlg(QDialog): picks.pop(phase_old) self.setDirty(True) + def leave_rename_phase(self): + self.renamePhaseAction.setChecked(False) + self.multicompfig.set_frame_color() + self.multicompfig.connectEvents() + def remove_pick_by_x(self, x): if not self.picks and not self.autopicks: return