diff --git a/QtPyLoT.py b/QtPyLoT.py index cb55c317..720a651a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -79,7 +79,8 @@ from pylot.core.util.event import Event from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ WaveformWidget, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ - getDataType, ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget + getDataType, ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \ + CompareEventsWidget from pylot.core.util.map_projection import map_projection from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.thread import Thread, Worker @@ -1029,6 +1030,8 @@ class MainWindow(QMainWindow): # if pick widget is open, refresh tooltips as well if hasattr(self, 'apw'): self.apw.refresh_tooltips() + if hasattr(self, 'cmpw'): + self.cmpw.refresh_tooltips() if not eventBox: eventBox = self.eventBox @@ -1230,14 +1233,50 @@ class MainWindow(QMainWindow): return None def comparePicks(self): - if self.check4Comparison(): - autopicks = excludeQualityClasses(self.getPicks('auto'), [4], + comparisons = {} + eventdict = {} + for event in self.project.eventlist: + if not self.comparable[event.pylot_id]: + continue + autopicks = excludeQualityClasses(event.getAutopicks(), [4], self._inputs['timeerrorsP'], self._inputs['timeerrorsS']) - manupicks = excludeQualityClasses(self.getPicks('manual'), [4], + manupicks = excludeQualityClasses(event.getPicks(), [4], self._inputs['timeerrorsP'], self._inputs['timeerrorsS']) co = Comparison(auto=autopicks, manu=manupicks) - compare_dlg = ComparisonWidget(co, self) - compare_dlg.show() + comparisons[event.pylot_id] = co + eventdict[event.pylot_id] = event + if len(eventdict) < 1: + return + + # init event selection options for autopick + self.compareoptions =[('tune events', self.get_ref_events), + ('test events', self.get_test_events), + ('all (picked) events', self.get_manu_picked_events)] + + self.cmpw = CompareEventsWidget(self, self.compareoptions, eventdict, comparisons) + self.cmpw.start.connect(self.compareMulti) + self.cmpw.refresh_tooltips() + self.cmpw.show() + + def compareMulti(self): + for key, func in self.compareoptions: + if self.cmpw.rb_dict[key].isChecked(): + # if radio button is checked break for loop and use func + break + eventlist = func() + # use only events comparable + eventlist_overlap = [event for event in eventlist if self.comparable[event.pylot_id]] + compare_widget = self.buildMultiCompareWidget(eventlist_overlap) + compare_widget.show() + + + def buildMultiCompareWidget(self, eventlist): + global_comparison = Comparison(eventlist=eventlist) + compare_widget = ComparisonWidget(global_comparison, self) + compare_widget.setWindowTitle('Histograms for all selected events') + compare_widget.hideToolbar() + compare_widget.setHistboxChecked(True) + return compare_widget def getPlotWidget(self): return self.dataPlot @@ -1488,6 +1527,7 @@ class MainWindow(QMainWindow): self.dataPlot.plotWidget.showAxis('bottom') def finishWaveformDataPlot(self): + self.comparable = self.checkEvents4comparison() if self.pg: self.finish_pg_plot() else: @@ -1521,15 +1561,31 @@ class MainWindow(QMainWindow): self.locateEvent.setEnabled(True) if event.pylot_autopicks: self.drawPicks(picktype='auto') - if event.pylot_picks and event.pylot_autopicks: - for key in event.pylot_picks: - for akey in event.pylot_autopicks: - if (akey == key) and (event.pylot_autopicks[akey]['P']['spe'] is not None \ - or event.pylot_autopicks[akey]['S']['spe'] is not None): - self.compare_action.setEnabled(True) - break + if True in self.comparable.values(): + self.compare_action.setEnabled(True) self.draw() + def checkEvent4comparison(self, event): + if event.pylot_picks and event.pylot_autopicks: + for station in event.pylot_picks: + if station in event.pylot_autopicks: + autopick_p = event.pylot_autopicks[station]['P']['spe'] + manupick_p = event.pylot_picks[station]['P']['spe'] + autopick_s = event.pylot_autopicks[station]['S']['spe'] + manupick_s = event.pylot_picks[station]['S']['spe'] + if autopick_p and manupick_p: + return True + elif autopick_s and manupick_s: + return True + return False + + def checkEvents4comparison(self): + # init dict to keep track whether event can be compared + comparable = {} + for event in self.project.eventlist: + comparable[event.pylot_id] = self.checkEvent4comparison(event) + return comparable + def clearWaveformDataPlot(self): self.disconnectWFplotEvents() if self.pg: @@ -1916,6 +1972,7 @@ class MainWindow(QMainWindow): "No autoPyLoT output declared!") return + # init event selection options for autopick self.pickoptions =[('current event', self.get_current_event), ('tune events', self.get_ref_events), ('test events', self.get_test_events), @@ -2093,7 +2150,6 @@ class MainWindow(QMainWindow): #event.picks.update(picks) MP MP idea elif type == 'auto': event.addAutopicks(picksdict['auto']) - self.check4Comparison() def drawPicks(self, station=None, picktype=None): # if picktype not specified, draw both @@ -2633,18 +2689,18 @@ class MainWindow(QMainWindow): def check4Loc(self): return self.picksNum() >= 4 - def check4Comparison(self): - mpicks = self.getPicks() - apicks = self.getPicks('auto') - for station, phases in mpicks.items(): - try: - aphases = apicks[station] - for phase in phases.keys(): - if phase in aphases.keys(): - return True - except KeyError: - continue - return False + # def check4Comparison(self): + # mpicks = self.getPicks() + # apicks = self.getPicks('auto') + # for station, phases in mpicks.items(): + # try: + # aphases = apicks[station] + # for phase in phases.keys(): + # if phase in aphases.keys(): + # return True + # except KeyError: + # continue + # return False def picksNum(self, type='manual'): num = 0 diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 24caa92c..ee908a13 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -27,16 +27,8 @@ class Comparison(object): """ def __init__(self, **kwargs): - names = list() self._pdfs = dict() - for name, fn in kwargs.items(): - if isinstance(fn, PDFDictionary): - self._pdfs[name] = fn - elif isinstance(fn, dict) or isinstance(fn, AttribDict): - self._pdfs[name] = PDFDictionary(fn) - else: - self._pdfs[name] = PDFDictionary.from_quakeml(fn) - names.append(name) + names = self.iter_kwargs(kwargs) if len(names) > 2: raise ValueError('Comparison is only defined for two ' 'arguments!') @@ -48,6 +40,40 @@ class Comparison(object): return False return True + def iter_kwargs(self, kwargs): + names = list() + for name, fn in kwargs.items(): + if name == 'eventlist': + names = self.init_by_eventlist(fn) + break + if isinstance(fn, PDFDictionary): + self._pdfs[name] = fn + elif isinstance(fn, dict) or isinstance(fn, AttribDict): + self._pdfs[name] = PDFDictionary(fn) + else: + self._pdfs[name] = PDFDictionary.from_quakeml(fn) + names.append(name) + return names + + def init_by_eventlist(self, eventlist): + # create one dictionary containing all picks for all events (therefore modify station key) + global_picksdict = {} + for event in eventlist: + automanu = {'manu': event.pylot_picks, + 'auto': event.pylot_autopicks} + for method, picksdict in automanu.items(): + if not method in global_picksdict.keys(): + global_picksdict[method] = {} + for station, picks in picksdict.items(): + new_picksdict = global_picksdict[method] + # new id combining event and station in one dictionary for all events + id = '{}_{}'.format(event.pylot_id, station) + new_picksdict[id] = picks + for method, picksdict in global_picksdict.items(): + self._pdfs[method] = PDFDictionary(picksdict) + names = list(global_picksdict.keys()) + return names + def get(self, name): return self._pdfs[name] diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index addf4121..933cf9ab 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -121,7 +121,7 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None, class ComparisonWidget(QWidget): - def __init__(self, c, parent=None): + def __init__(self, c, parent=None, windowflag=1): self._data = c self._stats = c.stations self._canvas = PlotWidget(self) @@ -130,7 +130,7 @@ class ComparisonWidget(QWidget): histCheckBox=None) self._phases = 'PS' self._plotprops = dict(station=list(self.stations)[0], phase=list(self.phases)[0]) - super(ComparisonWidget, self).__init__(parent, 1) + super(ComparisonWidget, self).__init__(parent, windowflag) self.setupUI() self.resize(1280, 720) self.plotcomparison() @@ -154,19 +154,19 @@ class ComparisonWidget(QWidget): _phases_combobox.currentIndexChanged.connect(self.prepareplot) self.widgets = _phases_combobox - _hist_checkbox = QCheckBox('Show histograms', self) - _hist_checkbox.setObjectName('histCheckBox') - _hist_checkbox.stateChanged.connect(self.plothist) - self.widgets = _hist_checkbox + self._hist_checkbox = QCheckBox('Show histograms', self) + self._hist_checkbox.setObjectName('histCheckBox') + self._hist_checkbox.stateChanged.connect(self.plothist) + self.widgets = self._hist_checkbox - _toolbar = QToolBar(self) - _toolbar.addWidget(_stats_combobox) - _toolbar.addWidget(_phases_combobox) - _toolbar.addWidget(_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(_toolbar) + _outerlayout.addWidget(self._toolbar) _outerlayout.addLayout(_innerlayout) # finally layout the entire widget @@ -232,6 +232,15 @@ class ComparisonWidget(QWidget): 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() @@ -2036,16 +2045,10 @@ class MultiEventWidget(QWidget): ''' ''' - def __init__(self, pickoptions=None, parent=None, windowflag=1): + def __init__(self, options=None, parent=None, windowflag=1): QtGui.QWidget.__init__(self, parent, windowflag) - self.pickoptions = pickoptions - # self.pickoptions =[('current event', None), - # ('tune events', None), - # ('test events', None), - # ('all (picked)', None), - # ('all events', None)] - + self.options = options self.setupUi() # set initial size self.resize(1280, 720) @@ -2077,8 +2080,9 @@ class MultiEventWidget(QWidget): self.start_button = QtGui.QPushButton('Start') - for index, (key, func) in enumerate(self.pickoptions): + for index, (key, func) in enumerate(self.options): rb = QtGui.QRadioButton(key) + rb.toggled.connect(self.check_rb_selection) if index == 0: rb.setChecked(True) self.rb_dict[key] = rb @@ -2088,69 +2092,58 @@ class MultiEventWidget(QWidget): self.rb_layout.addWidget(self.start_button) self.rb_layout.addWidget(QtGui.QWidget()) - self.rb_layout.setStretch(len(self.pickoptions)+1, 1) + self.rb_layout.setStretch(len(self.options) + 1, 1) self.main_layout.insertLayout(0, self.rb_layout) + def refresh_tooltips(self): + for key, func 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, pickoptions): - MultiEventWidget.__init__(self, pickoptions, parent, 1) + def __init__(self, parent, options): + MultiEventWidget.__init__(self, options, parent, 1) self.connect_buttons() + self.init_plot_layout() + self.init_log_layout() self.reinitEvents2plot() self.setWindowTitle('Autopick events interactively') - - def setupUi(self): - # init main layout - self.main_layout = QtGui.QVBoxLayout() - self.setLayout(self.main_layout) - # init main splitter - self.main_splitter = QtGui.QSplitter() - self.main_splitter.setChildrenCollapsible(False) - - self.init_checkboxes() - self.init_log_layout() - self.init_plot_layout() - - self.eventbox = QtGui.QComboBox() - self.button_clear = QtGui.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) + self.set_main_stretch() def connect_buttons(self): self.start_button.clicked.connect(self.run) self.button_clear.clicked.connect(self.reinitEvents2plot) - def init_checkboxes(self): - self.rb_layout = QtGui.QHBoxLayout() - - self.rb_dict = {} - - self.start_button = QtGui.QPushButton('Start') - - for index, (key, func) in enumerate(self.pickoptions): - rb = QtGui.QRadioButton(key) - 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(QtGui.QWidget()) - self.rb_layout.setStretch(len(self.pickoptions)+1, 1) - - self.main_layout.insertLayout(0, self.rb_layout) - def init_plot_layout(self): # init tab widget self.tab_plots = QtGui.QTabWidget() @@ -2202,6 +2195,10 @@ class AutoPickWidget(MultiEventWidget): 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): self.events2plot = {} self.eventbox.clear() @@ -2210,32 +2207,69 @@ class AutoPickWidget(MultiEventWidget): def refresh_plot_tabs(self): self.tab_plots.clear() - def refresh_tooltips(self): - for key, func in self.pickoptions: - 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) - def run(self): self.refresh_plot_tabs() self.start.emit() - 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 CompareEventsWidget(MultiEventWidget): + ''' + ''' + + def __init__(self, parent, options, eventdict, comparisons): + MultiEventWidget.__init__(self, options, parent, 1) + self.eventdict = eventdict + self.comparisons = comparisons + self.compare_widget = QtGui.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 = QtGui.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):