From 3cfd2371fc31585726a2e1bed0deed63058f36e8 Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 23 Aug 2017 12:17:51 +0200 Subject: [PATCH 01/11] [change] scroll zoom now part of WFwidget --- pylot/core/util/widgets.py | 68 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 47d97057..b0aaebb9 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -611,6 +611,11 @@ class WaveformWidget(FigureCanvas): color='m', lw=1) # update labels of the entire widget self.updateWidget(xlabel, ylabel, title) + + self.limits = {'x': None, + 'y': None} + + self.cidscroll = self.connectScrollEvent(self.scrollZoom) try: self.figure.tight_layout() except: @@ -622,6 +627,9 @@ class WaveformWidget(FigureCanvas): def setPlotDict(self, key, value): self.plotdict[key] = value + def connectScrollEvent(self, slot): + return self.mpl_connect('scroll_event', slot) + def clearPlotDict(self): self.plotdict = dict() @@ -746,6 +754,51 @@ class WaveformWidget(FigureCanvas): xycoords='axes fraction') axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) + def scrollZoom(self, gui_event, factor=2.): + + if not gui_event.xdata or not gui_event.ydata: + 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()) + new_ylim = gui_event.ydata - \ + scale_factor * (gui_event.ydata - self.getYLims()) + + new_xlim.sort() + global_x = self.getGlobalLimits('x') + global_y = self.getGlobalLimits('y') + new_xlim[0] = max(new_xlim[0], global_x[0]) + new_xlim[1] = min(new_xlim[1], global_x[1]) + new_ylim.sort() + new_ylim[0] = max(new_ylim[0], global_y[0]) + new_ylim[1] = min(new_ylim[1], global_y[1]) + + self.setXLims(new_xlim) + self.setYLims(new_ylim) + self.draw() + + def updateCurrentLimits(self): + self.setXLims(self.getXLims()) + self.setYLims(self.getYLims()) + + def getGlobalLimits(self, axis): + return self.limits[axis] + + def setGlobalLimits(self, axis, limits): + self.limits[axis] = limits + class PhaseDefaults(QtGui.QDialog): def __init__(self, parent=None, nrow=10, @@ -897,6 +950,9 @@ class PickDlg(QDialog): self.limits = {'x': xlims, 'y': ylims} + for ax, limit in self.limits.items(): + self.getPlotWidget().setGlobalLimits(ax, limit) + self.updateCurrentLimits() # set plot labels @@ -909,7 +965,9 @@ class PickDlg(QDialog): self.cidpress = self.connectPressEvent(self.panPress) self.cidmotion = self.connectMotionEvent(self.panMotion) self.cidrelease = self.connectReleaseEvent(self.panRelease) - self.cidscroll = self.connectScrollEvent(self.scrollZoom) + self.cidscroll = self.connectScrollEvent(self.multicompfig.scrollZoom) + self.cidscroll_arr = self.connectScrollEvent(self.refreshArrivalsText) + self.cidscroll_ph = self.connectScrollEvent(self.refreshPhaseText) # init expected picks using obspy Taup try: @@ -1127,7 +1185,7 @@ class PickDlg(QDialog): def drawArrivalsText(self): return self.drawArrivals(True) - def refreshArrivalsText(self): + def refreshArrivalsText(self, event=None): self.removeArrivalsText() self.drawArrivalsText() @@ -1205,7 +1263,11 @@ class PickDlg(QDialog): def disconnectScrollEvent(self): widget = self.getPlotWidget() widget.mpl_disconnect(self.cidscroll) + widget.mpl_disconnect(self.cidscroll_arr) + widget.mpl_disconnect(self.cidscroll_ph) self.cidscroll = None + self.cidscroll_arr = None + self.cidscroll_ph = None def connectScrollEvent(self, slot): widget = self.getPlotWidget() @@ -1817,7 +1879,7 @@ class PickDlg(QDialog): pass self.phaseText = [] - def refreshPhaseText(self): + def refreshPhaseText(self, event=None): self.removePhaseText() self.drawPhaseText() From 1bbb686778686f7134272d3afac3c73eee3d6595 Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 23 Aug 2017 14:45:46 +0200 Subject: [PATCH 02/11] [updates] first usage of PylotCanvas for other fig --- QtPyLoT.py | 10 ++-- autoPyLoT.py | 5 +- pylot/core/util/widgets.py | 118 +++++++++++++++---------------------- 3 files changed, 56 insertions(+), 77 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 0d596133..4f3222d2 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -78,7 +78,7 @@ from pylot.core.util.utils import fnConstructor, getLogin, \ 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, \ + PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ getDataType, ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget from pylot.core.util.map_projection import map_projection from pylot.core.util.structure import DATASTRUCTURE @@ -619,8 +619,8 @@ class MainWindow(QMainWindow): self.disconnectWFplotEvents() if str(settings.value('pyqtgraphic')) == 'false' or not pg: self.pg = False - self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, - title=plottitle) + self.dataPlot = PylotCanvas(parent=self, connect_events=False) + self.dataPlot.updateWidget(xlab, None, plottitle) else: self.pg = True self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, @@ -1853,7 +1853,7 @@ class MainWindow(QMainWindow): def init_canvas_dict(self): self.canvas_dict = {} for key in self.fig_keys: - self.canvas_dict[key] = FigureCanvas(self.fig_dict[key]) + self.canvas_dict[key] = PylotCanvas(self.fig_dict[key]) def init_fig_dict_wadatijack(self, eventIDs): self.fig_dict_wadatijack = {} @@ -1872,7 +1872,7 @@ class MainWindow(QMainWindow): for eventID in self.fig_dict_wadatijack.keys(): self.canvas_dict_wadatijack[eventID] = {} for key in self.fig_keys_wadatijack: - self.canvas_dict_wadatijack[eventID][key] = FigureCanvas(self.fig_dict_wadatijack[eventID][key]) + self.canvas_dict_wadatijack[eventID][key] = PylotCanvas(self.fig_dict_wadatijack[eventID][key]) def tune_autopicker(self): ''' diff --git a/autoPyLoT.py b/autoPyLoT.py index d55129d6..88e09c8b 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -264,8 +264,11 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even print(wfdat) ########################################################## # !automated picking starts here! + fdwj = None + if fig_dict_wadatijack: + fdwj = fig_dict_wadatijack[evID] picks = autopickevent(wfdat, parameter, iplot=iplot, fig_dict=fig_dict, - fig_dict_wadatijack=fig_dict_wadatijack[evID], + fig_dict_wadatijack=fdwj, ncores=ncores, metadata=metadata, origin=data.get_evt_data().origins) ########################################################## # locating diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b0aaebb9..46edfc87 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -593,34 +593,46 @@ class WaveformWidgetPG(QtGui.QWidget): pass -class WaveformWidget(FigureCanvas): - def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): +class PylotCanvas(FigureCanvas): + def __init__(self, figure=None, parent=None, connect_events=True): self._parent = parent - self.figure = Figure() + if not figure: + figure = Figure() + # create axes + self.axes = figure.add_subplot(111) + self.figure = figure self.figure.set_facecolor((.92, .92, .92)) # attribute plotdict is a dictionary connecting position and a name self.plotdict = dict() - # create axes - self.axes = self.figure.add_subplot(111) # initialize super class - super(WaveformWidget, self).__init__(self.figure) - # add an cursor for station selection + super(PylotCanvas, self).__init__(self.figure) + # add a cursor for station selection self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), horizOn=True, useblit=True, color='m', lw=1) # update labels of the entire widget - self.updateWidget(xlabel, ylabel, title) + #self.updateWidget(xlabel, ylabel, title) - self.limits = {'x': None, - 'y': None} + self.limits = {'x': (-np.inf, np.inf), + 'y': (-np.inf, np.inf)} + + if connect_events: + self.connectEvents() - self.cidscroll = self.connectScrollEvent(self.scrollZoom) try: self.figure.tight_layout() except: pass + def connectEvents(self): + self.cidscroll = self.connectScrollEvent(self.scrollZoom) + + def disconnectEvents(self): + self.mpl_disconnect(self.cidscroll) + + self.cidscroll = None + def getPlotDict(self): return self.plotdict @@ -789,6 +801,16 @@ class WaveformWidget(FigureCanvas): self.setYLims(new_ylim) self.draw() + def setZoomBorders2content(self): + xlims = self.getXLims() + ylims = self.getYLims() + + self.limits = {'x': xlims, + 'y': ylims} + + for ax, limit in self.limits.items(): + self.setGlobalLimits(ax, limit) + def updateCurrentLimits(self): self.setXLims(self.getXLims()) self.setYLims(self.getYLims()) @@ -933,7 +955,7 @@ class PickDlg(QDialog): self.stime, self.etime = full_range(self.getWFData()) # initialize plotting widget - self.multicompfig = WaveformWidget(self) + self.multicompfig = PylotCanvas(parent=self, connect_events=False) self.phaseplot = PhasePlotWidget(self) self.phaseplot.hide() @@ -944,14 +966,7 @@ class PickDlg(QDialog): self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) - xlims = self.getPlotWidget().getXLims() - ylims = self.getPlotWidget().getYLims() - - self.limits = {'x': xlims, - 'y': ylims} - - for ax, limit in self.limits.items(): - self.getPlotWidget().setGlobalLimits(ax, limit) + self.getPlotWidget().setZoomBorders2content() self.updateCurrentLimits() @@ -962,10 +977,7 @@ class PickDlg(QDialog): self.drawAllPicks() # connect button press event to an action - self.cidpress = self.connectPressEvent(self.panPress) - self.cidmotion = self.connectMotionEvent(self.panMotion) - self.cidrelease = self.connectReleaseEvent(self.panRelease) - self.cidscroll = self.connectScrollEvent(self.multicompfig.scrollZoom) + self.connectEvents() self.cidscroll_arr = self.connectScrollEvent(self.refreshArrivalsText) self.cidscroll_ph = self.connectScrollEvent(self.refreshPhaseText) @@ -1399,10 +1411,7 @@ class PickDlg(QDialog): def deactivatePicking(self): self.disconnectPressEvent() - self.cidpress = self.connectPressEvent(self.panPress) - self.cidmotion = self.connectMotionEvent(self.panMotion) - self.cidrelease = self.connectReleaseEvent(self.panRelease) - self.cidscroll = self.connectScrollEvent(self.scrollZoom) + self.connectEvents() self.connect_pick_delete() def getParameter(self): @@ -1461,7 +1470,7 @@ class PickDlg(QDialog): self.cur_ylim = limits def getGlobalLimits(self, axis): - return self.limits[axis] + return self.getPlotWidget().getGlobalLimits(axis) def updateCurrentLimits(self): self.setXLims(self.getPlotWidget().getXLims()) @@ -1759,7 +1768,7 @@ class PickDlg(QDialog): # plotting picks ax = self.getPlotWidget().axes if not textOnly: - ylims = self.getGlobalLimits('y') + ylims = self.getPlotWidget().getGlobalLimits('y') else: ylims = self.getPlotWidget().getYLims() if self.getPicks(picktype): @@ -1971,6 +1980,12 @@ class PickDlg(QDialog): self.getPlotWidget().setXLims(self.getXLims()) self.getPlotWidget().setYLims(self.getYLims()) + def connectEvents(self): + self.cidpress = self.connectPressEvent(self.panPress) + self.cidmotion = self.connectMotionEvent(self.panMotion) + self.cidrelease = self.connectReleaseEvent(self.panRelease) + self.cidscroll = self.connectScrollEvent(self.multicompfig.scrollZoom) + def zoom(self): if self.zoomAction.isChecked() and self.pick_block: self.zoomAction.setChecked(False) @@ -1982,47 +1997,6 @@ class PickDlg(QDialog): self.figToolBar.zoom() else: self.figToolBar.zoom() - self.cidpress = self.connectPressEvent(self.panPress) - self.cidmotion = self.connectMotionEvent(self.panMotion) - self.cidrelease = self.connectReleaseEvent(self.panRelease) - self.cidscroll = self.connectScrollEvent(self.scrollZoom) - - def scrollZoom(self, gui_event, factor=2.): - - if not gui_event.xdata or not gui_event.ydata: - 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()) - new_ylim = gui_event.ydata - \ - scale_factor * (gui_event.ydata - self.getYLims()) - - new_xlim.sort() - global_x = self.getGlobalLimits('x') - global_y = self.getGlobalLimits('y') - new_xlim[0] = max(new_xlim[0], global_x[0]) - new_xlim[1] = min(new_xlim[1], global_x[1]) - new_ylim.sort() - new_ylim[0] = max(new_ylim[0], global_y[0]) - new_ylim[1] = min(new_ylim[1], global_y[1]) - - self.getPlotWidget().setXLims(new_xlim) - self.getPlotWidget().setYLims(new_ylim) - self.refreshArrivalsText() - self.refreshPhaseText() - self.draw() def resetZoom(self): self.getPlotWidget().setXLims(self.getGlobalLimits('x')) @@ -2080,9 +2054,11 @@ class CanvasWidget(QWidget): def __init__(self, parent, canvas): QtGui.QWidget.__init__(self, parent)#, 1) + canvas = canvas self.main_layout = QtGui.QVBoxLayout() self.setLayout(self.main_layout) self.main_layout.addWidget(canvas) + canvas.setZoomBorders2content() class AutoPickWidget(QWidget): From 070c32e0073546011b4e17ff92333baf3eef5fad Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 23 Aug 2017 15:18:24 +0200 Subject: [PATCH 03/11] [change] disable multicursor by default --- QtPyLoT.py | 2 +- pylot/core/util/widgets.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 4f3222d2..e1129f3e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -619,7 +619,7 @@ class MainWindow(QMainWindow): self.disconnectWFplotEvents() if str(settings.value('pyqtgraphic')) == 'false' or not pg: self.pg = False - self.dataPlot = PylotCanvas(parent=self, connect_events=False) + self.dataPlot = PylotCanvas(parent=self, connect_events=False, multicursor=True) self.dataPlot.updateWidget(xlab, None, plottitle) else: self.pg = True diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 46edfc87..e027eda6 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -594,7 +594,7 @@ class WaveformWidgetPG(QtGui.QWidget): class PylotCanvas(FigureCanvas): - def __init__(self, figure=None, parent=None, connect_events=True): + def __init__(self, figure=None, parent=None, connect_events=True, multicursor=False): self._parent = parent if not figure: @@ -607,10 +607,11 @@ class PylotCanvas(FigureCanvas): self.plotdict = dict() # initialize super class super(PylotCanvas, self).__init__(self.figure) - # add a cursor for station selection - self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), - horizOn=True, useblit=True, - color='m', lw=1) + if multicursor: + # add a cursor for station selection + self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), + horizOn=True, useblit=True, + color='m', lw=1) # update labels of the entire widget #self.updateWidget(xlabel, ylabel, title) @@ -955,7 +956,7 @@ class PickDlg(QDialog): self.stime, self.etime = full_range(self.getWFData()) # initialize plotting widget - self.multicompfig = PylotCanvas(parent=self, connect_events=False) + self.multicompfig = PylotCanvas(parent=self, connect_events=False, multicursor=True) self.phaseplot = PhasePlotWidget(self) self.phaseplot.hide() From c83671f14032680eb41e16075d65b8e6bb15a668 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 24 Aug 2017 11:35:53 +0200 Subject: [PATCH 04/11] [update] canvas used for tune-fig, not yet pan --- QtPyLoT.py | 2 ++ pylot/core/util/widgets.py | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e1129f3e..2c36ef61 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1903,6 +1903,8 @@ class MainWindow(QMainWindow): ''' self.init_canvas_dict() self.tap.fill_tabs(picked=True) + for canvas in self.canvas_dict.values(): + canvas.setZoomBorders2content() def autoPick(self): autosave = self.get_current_event_path() diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e027eda6..68ec58d5 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -600,7 +600,9 @@ class PylotCanvas(FigureCanvas): if not figure: figure = Figure() # create axes - self.axes = figure.add_subplot(111) + self.ax = figure.add_subplot(111) + + self.axes = figure.axes self.figure = figure self.figure.set_facecolor((.92, .92, .92)) # attribute plotdict is a dictionary connecting position and a name @@ -609,7 +611,7 @@ class PylotCanvas(FigureCanvas): super(PylotCanvas, self).__init__(self.figure) if multicursor: # add a cursor for station selection - self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), + self.multiCursor = MultiCursor(self.figure.canvas, self.axes, horizOn=True, useblit=True, color='m', lw=1) # update labels of the entire widget @@ -725,7 +727,7 @@ class PylotCanvas(FigureCanvas): self.draw() def getAxes(self): - return self.axes + return self.axes[0] def getXLims(self): return self.getAxes().get_xlim() @@ -803,6 +805,8 @@ class PylotCanvas(FigureCanvas): self.draw() def setZoomBorders2content(self): + if not self.axes: + return xlims = self.getXLims() ylims = self.getYLims() @@ -1894,7 +1898,7 @@ class PickDlg(QDialog): self.drawPhaseText() def panPress(self, gui_event): - ax = self.getPlotWidget().axes + ax = self.getPlotWidget().axes[0] if gui_event.inaxes != ax: return self.cur_xlim = ax.get_xlim() self.cur_ylim = ax.get_ylim() @@ -1902,15 +1906,15 @@ class PickDlg(QDialog): self.xpress, self.ypress = self.press def panRelease(self, gui_event): - ax = self.getPlotWidget().axes + figure = self.getPlotWidget().figure self.press = None self.refreshPhaseText() self.refreshArrivalsText() - ax.figure.canvas.draw() + figure.canvas.draw() def panMotion(self, gui_event): if self.press is None: return - ax = self.getPlotWidget().axes + ax = self.getPlotWidget().axes[0] if gui_event.inaxes != ax: return dx = gui_event.xdata - self.xpress dy = gui_event.ydata - self.ypress From 726544bf3ba18b92f375456d3909bcfbccb334dd Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 24 Aug 2017 17:16:21 +0200 Subject: [PATCH 05/11] [update] canvas finally working again --- pylot/core/util/widgets.py | 448 +++++++++++++++++++------------------ 1 file changed, 227 insertions(+), 221 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 68ec58d5..4da7968b 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -614,11 +614,19 @@ class PylotCanvas(FigureCanvas): self.multiCursor = MultiCursor(self.figure.canvas, self.axes, horizOn=True, useblit=True, color='m', lw=1) - # update labels of the entire widget - #self.updateWidget(xlabel, ylabel, title) - self.limits = {'x': (-np.inf, np.inf), - 'y': (-np.inf, np.inf)} + # initialize panning attributes + self.press = None + self.xpress = None + self.ypress = None + self.cur_xlim = None + self.cur_ylim = None + + 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() @@ -628,23 +636,124 @@ class PylotCanvas(FigureCanvas): except: pass + 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.xpress, self.ypress = self.press + + def panMotion(self, gui_event): + if self.press is None: return + 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) + ax.figure.canvas.draw() + + def panRelease(self, gui_event): + self.press = None + self.figure.canvas.draw() + + 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.draw() + def connectEvents(self): self.cidscroll = self.connectScrollEvent(self.scrollZoom) + self.cidpress = self.connectPressEvent(self.panPress) + self.cidmotion = self.connectMotionEvent(self.panMotion) + self.cidrelease = self.connectReleaseEvent(self.panRelease) def disconnectEvents(self): - self.mpl_disconnect(self.cidscroll) + self.disconnectScrollEvent() + self.disconnectMotionEvent() + self.disconnectPressEvent() + self.disconnectReleaseEvent() + def disconnectPressEvent(self): + self.mpl_disconnect(self.cidpress) + self.cidpress = None + + def connectPressEvent(self, slot): + return self.mpl_connect('button_press_event', slot) + + def disconnectMotionEvent(self): + self.mpl_disconnect(self.cidmotion) + self.cidmotion = None + + def connectMotionEvent(self, slot): + return self.mpl_connect('motion_notify_event', slot) + + def disconnectReleaseEvent(self): + self.mpl_disconnect(self.cidrelease) + self.cidrelease = None + + def connectReleaseEvent(self, slot): + return self.mpl_connect('button_release_event', slot) + + def disconnectScrollEvent(self): + self.mpl_disconnect(self.cidscroll) self.cidscroll = None + def connectScrollEvent(self, slot): + return self.mpl_connect('scroll_event', slot) + def getPlotDict(self): return self.plotdict def setPlotDict(self, key, value): self.plotdict[key] = value - def connectScrollEvent(self, slot): - return self.mpl_connect('scroll_event', slot) - def clearPlotDict(self): self.plotdict = dict() @@ -657,7 +766,9 @@ class PylotCanvas(FigureCanvas): 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.getAxes().cla() + ax = self.axes[0] + ax.cla() + self.clearPlotDict() wfstart, wfend = full_range(wfdata) nmax = 0 @@ -704,58 +815,54 @@ class PylotCanvas(FigureCanvas): trace.normalize(np.max(np.abs(trace.data)) * 2) times = [time for index, time in enumerate(time_ax) if not index % nth_sample] data = [datum + n for index, datum in enumerate(trace.data) if not index % nth_sample] - self.getAxes().plot(times, data, 'k', linewidth=0.7) + ax.plot(times, data, 'k', linewidth=0.7) if noiselevel is not None: for level in noiselevel: - self.getAxes().plot([time_ax[0], time_ax[-1]], + ax.plot([time_ax[0], time_ax[-1]], [level, level], '--k') - if iniPick: - ax = self.getAxes() - ax.vlines(iniPick, ax.get_ylim()[0], ax.get_ylim()[1], - colors='m', linestyles='dashed', - linewidth=2) self.setPlotDict(n, (station, channel, network)) + 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([0, wfend - wfstart]) - self.setYLims([-0.5, nmax + 0.5]) + self.setXLims(ax, [0, wfend - wfstart]) + self.setYLims(ax, [-0.5, nmax + 0.5]) if zoomx is not None: - self.setXLims(zoomx) + self.setXLims(ax, zoomx) if zoomy is not None: - self.setYLims(zoomy) + self.setYLims(ax, zoomy) self.draw() - def getAxes(self): - return self.axes[0] + def getXLims(self, ax): + return ax.get_xlim() - def getXLims(self): - return self.getAxes().get_xlim() + def getYLims(self, ax): + return ax.get_ylim() - def getYLims(self): - return self.getAxes().get_ylim() + def setXLims(self, ax, lims): + ax.set_xlim(lims) - def setXLims(self, lims): - self.getAxes().set_xlim(lims) - - def setYLims(self, lims): - self.getAxes().set_ylim(lims) + def setYLims(self, ax, lims): + ax.set_ylim(lims) def setYTickLabels(self, pos, labels): - self.getAxes().set_yticks(list(pos)) - self.getAxes().set_yticklabels(labels) + self.axes[0].set_yticks(list(pos)) + self.axes[0].set_yticklabels(labels) self.draw() def updateXLabel(self, text): - self.getAxes().set_xlabel(text) + self.axes[0].set_xlabel(text) self.draw() def updateYLabel(self, text): - self.getAxes().set_ylabel(text) + self.axes[0].set_ylabel(text) self.draw() def updateTitle(self, text): - self.getAxes().set_title(text, verticalalignment='bottom') + self.axes[0].set_title(text, verticalalignment='bottom') self.draw() def updateWidget(self, xlabel, ylabel, title): @@ -764,67 +871,40 @@ class PylotCanvas(FigureCanvas): self.updateTitle(title) def insertLabel(self, pos, text): - pos = pos / max(self.getAxes().ylim) - axann = self.getAxes().annotate(text, xy=(.03, pos), + 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 scrollZoom(self, gui_event, factor=2.): - - if not gui_event.xdata or not gui_event.ydata: - 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()) - new_ylim = gui_event.ydata - \ - scale_factor * (gui_event.ydata - self.getYLims()) - - new_xlim.sort() - global_x = self.getGlobalLimits('x') - global_y = self.getGlobalLimits('y') - new_xlim[0] = max(new_xlim[0], global_x[0]) - new_xlim[1] = min(new_xlim[1], global_x[1]) - new_ylim.sort() - new_ylim[0] = max(new_ylim[0], global_y[0]) - new_ylim[1] = min(new_ylim[1], global_y[1]) - - self.setXLims(new_xlim) - self.setYLims(new_ylim) - self.draw() - def setZoomBorders2content(self): if not self.axes: return - xlims = self.getXLims() - ylims = self.getYLims() + for ax in self.limits.keys(): + xlims = self.getXLims(ax) + ylims = self.getYLims(ax) - self.limits = {'x': xlims, - 'y': ylims} + self.limits[ax] = {'x': xlims, + 'y': ylims} - for ax, limit in self.limits.items(): - self.setGlobalLimits(ax, limit) + for axis, limit in self.limits[ax].items(): + self.setGlobalLimits(ax, axis, limit) def updateCurrentLimits(self): - self.setXLims(self.getXLims()) - self.setYLims(self.getYLims()) + for ax in self.limits.keys(): + self.setXLims(ax, self.getXLims(ax)) + self.setYLims(ax, self.getYLims(ax)) - def getGlobalLimits(self, axis): - return self.limits[axis] + def getGlobalLimits(self, ax, axis): + return self.limits[ax][axis] - def setGlobalLimits(self, axis, limits): - self.limits[axis] = limits + 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(QtGui.QDialog): @@ -960,20 +1040,22 @@ class PickDlg(QDialog): self.stime, self.etime = full_range(self.getWFData()) # initialize plotting widget - self.multicompfig = PylotCanvas(parent=self, connect_events=False, multicursor=True) + self.multicompfig = PylotCanvas(parent=self, multicursor=True) self.phaseplot = PhasePlotWidget(self) self.phaseplot.hide() - # setup ui - self.setupUi() - # plot data - self.getPlotWidget().plotWFData(wfdata=self.getWFData(), + self.multicompfig.plotWFData(wfdata=self.getWFData(), title=self.getStation()) - self.getPlotWidget().setZoomBorders2content() + self.multicompfig.setZoomBorders2content() - self.updateCurrentLimits() + self.multicompfig.updateCurrentLimits() + + self.connectScrollEvent() + + # setup ui + self.setupUi() # set plot labels self.setPlotLabels() @@ -981,11 +1063,6 @@ class PickDlg(QDialog): # draw picks if present self.drawAllPicks() - # connect button press event to an action - self.connectEvents() - self.cidscroll_arr = self.connectScrollEvent(self.refreshArrivalsText) - self.cidscroll_ph = self.connectScrollEvent(self.refreshPhaseText) - # init expected picks using obspy Taup try: if self.parent().metadata: @@ -1013,7 +1090,7 @@ class PickDlg(QDialog): self.addPickPhases(menuBar) # create matplotlib toolbar to inherit functionality - self.figToolBar = NavigationToolbar2QT(self.getPlotWidget(), self) + self.figToolBar = NavigationToolbar2QT(self.multicompfig, self) self.figToolBar.hide() # create icons @@ -1037,7 +1114,7 @@ class PickDlg(QDialog): tip='Zoom into waveform', checkable=True) self.resetZoomAction = createAction(parent=self, text='Home', - slot=self.resetZoom, icon=home_icon, + 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, @@ -1184,11 +1261,11 @@ class PickDlg(QDialog): def drawArrivals(self, textOnly=False): if not self.arrivals: return - ax = self.getPlotWidget().axes + ax = self.multicompfig.axes[0] if not textOnly: ylims = self.getGlobalLimits('y') else: - ylims = self.getPlotWidget().getYLims() + ylims = self.multicompfig.getYLims(ax) stime = self.getStartTime() source_origin = self.parent().get_current_event().origins[0] source_time = source_origin.time @@ -1268,45 +1345,16 @@ class PickDlg(QDialog): if phaseIndex == 0: picksMenu.addSeparator() - def disconnectPressEvent(self): - widget = self.getPlotWidget() - widget.mpl_disconnect(self.cidpress) - self.cidpress = None - - def connectPressEvent(self, slot): - widget = self.getPlotWidget() - return widget.mpl_connect('button_press_event', slot) - def disconnectScrollEvent(self): - widget = self.getPlotWidget() - widget.mpl_disconnect(self.cidscroll) + widget = self.multicompfig widget.mpl_disconnect(self.cidscroll_arr) widget.mpl_disconnect(self.cidscroll_ph) - self.cidscroll = None self.cidscroll_arr = None self.cidscroll_ph = None - def connectScrollEvent(self, slot): - widget = self.getPlotWidget() - return widget.mpl_connect('scroll_event', slot) - - def disconnectMotionEvent(self): - widget = self.getPlotWidget() - widget.mpl_disconnect(self.cidmotion) - self.cidmotion = None - - def connectMotionEvent(self, slot): - widget = self.getPlotWidget() - return widget.mpl_connect('motion_notify_event', slot) - - def disconnectReleaseEvent(self): - widget = self.getPlotWidget() - widget.mpl_disconnect(self.cidrelease) - self.cidrelease = None - - def connectReleaseEvent(self, slot): - widget = self.getPlotWidget() - return widget.mpl_connect('button_release_event', slot) + def connectScrollEvent(self): + self.cidscroll_arr = self.multicompfig.connectScrollEvent(self.refreshArrivalsText) + self.cidscroll_ph = self.multicompfig.connectScrollEvent(self.refreshPhaseText) def disable_ar_buttons(self): self.enable_ar_buttons(False) @@ -1355,13 +1403,11 @@ class PickDlg(QDialog): def init_p_pick(self): self.set_button_color(self.p_button, 'yellow') - self.updateCurrentLimits() self.activatePicking() self.currentPhase = str(self.p_button.text()) def init_s_pick(self): self.set_button_color(self.s_button, 'yellow') - self.updateCurrentLimits() self.activatePicking() self.currentPhase = str(self.s_button.text()) @@ -1394,7 +1440,7 @@ class PickDlg(QDialog): self.currentPhase = None self.reset_p_button() self.reset_s_button() - self.getPlotWidget().plotWFData(wfdata=self.getWFData(), + self.multicompfig.plotWFData(wfdata=self.getWFData(), title=self.getStation()) self.drawAllPicks() self.drawArrivals() @@ -1403,20 +1449,20 @@ class PickDlg(QDialog): self.deactivatePicking() def activatePicking(self): + self.resetZoom() if self.zoomAction.isChecked(): self.zoomAction.trigger() - self.disconnectReleaseEvent() + self.multicompfig.disconnectEvents() self.disconnectScrollEvent() - self.disconnectMotionEvent() - self.disconnectPressEvent() - self.cidpress = self.connectPressEvent(self.setIniPick) + self.cidpress = self.multicompfig.connectPressEvent(self.setIniPick) self.filterWFData() - self.pick_block = self.togglePickBlocker() + #self.pick_block = self.togglePickBlocker() self.disconnect_pick_delete() def deactivatePicking(self): self.disconnectPressEvent() - self.connectEvents() + self.connectScrollEvent() + self.multicompfig.connectEvents() self.connect_pick_delete() def getParameter(self): @@ -1436,14 +1482,11 @@ class PickDlg(QDialog): return self.network + '.' + self.station return self.station - def getPlotWidget(self): - return self.multicompfig - def getChannelID(self, key): - return self.getPlotWidget().getPlotDict()[int(key)][1] + return self.multicompfig.getPlotDict()[int(key)][1] def getTraceID(self, channels): - plotDict = self.getPlotWidget().getPlotDict() + plotDict = self.multicompfig.getPlotDict() traceIDs = [] for channel in channels: channel = channel.upper() @@ -1475,11 +1518,7 @@ class PickDlg(QDialog): self.cur_ylim = limits def getGlobalLimits(self, axis): - return self.getPlotWidget().getGlobalLimits(axis) - - def updateCurrentLimits(self): - self.setXLims(self.getPlotWidget().getXLims()) - self.setYLims(self.getPlotWidget().getYLims()) + return self.multicompfig.getGlobalLimits(axis) def getWFData(self): return self.data @@ -1528,11 +1567,9 @@ class PickDlg(QDialog): channel = self.getChannelID(trace_number) wfdata = self.selectWFData(channel) - self.disconnectScrollEvent() + self.multicompfig.disconnectEvents() self.disconnectPressEvent() - self.disconnectReleaseEvent() - self.disconnectMotionEvent() - self.cidpress = self.connectPressEvent(self.setPick) + self.cidpress = self.multicompfig.connectPressEvent(self.setPick) if self.getPhaseID(self.currentPhase) == 'P': self.set_button_color(self.p_button, 'green') @@ -1600,7 +1637,7 @@ class PickDlg(QDialog): self.setXLims([ini_pick - x_res, ini_pick + x_res]) self.setYLims(np.array([-noiselevel * 3.5, noiselevel * 3.5]) + trace_number) - self.getPlotWidget().plotWFData(wfdata=data, + self.multicompfig.plotWFData(wfdata=data, title=self.getStation() + ' picking mode', zoomx=self.getXLims(), @@ -1668,7 +1705,7 @@ class PickDlg(QDialog): noiselevels = [trace + 1 / (2.5 * 2) for trace in traces] + \ [trace - 1 / (2.5 * 2) for trace in traces] - self.getPlotWidget().plotWFData(wfdata=data, + self.multicompfig.plotWFData(wfdata=data, title=self.getStation() + ' picking mode', zoomx=self.getXLims(), @@ -1682,7 +1719,7 @@ class PickDlg(QDialog): parameter = self.parameter # get axes limits - self.updateCurrentLimits() + self.multicompfig.updateCurrentLimits() # setting pick pick = gui_event.xdata # get pick time relative to the traces timeaxis not to the global @@ -1760,10 +1797,14 @@ class PickDlg(QDialog): self.disconnectPressEvent() self.enable_ar_buttons() self.zoomAction.setEnabled(True) - self.pick_block = self.togglePickBlocker() + #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') @@ -1771,11 +1812,11 @@ class PickDlg(QDialog): def drawPicks(self, phase=None, picktype='manual', textOnly=False): # plotting picks - ax = self.getPlotWidget().axes + ax = self.multicompfig.axes[0] if not textOnly: - ylims = self.getPlotWidget().getGlobalLimits('y') + ylims = self.multicompfig.getGlobalLimits(ax, 'y') else: - ylims = self.getPlotWidget().getYLims() + ylims = ax.get_ylim() if self.getPicks(picktype): if phase is not None and not phase == 'SPt': if (type(self.getPicks(picktype)[phase]) is dict @@ -1842,11 +1883,11 @@ class PickDlg(QDialog): ax.legend(loc=1) def connect_pick_delete(self): - self.cidpick = self.getPlotWidget().mpl_connect('pick_event', self.onpick_delete) + self.cidpick = self.multicompfig.mpl_connect('pick_event', self.onpick_delete) def disconnect_pick_delete(self): if hasattr(self, 'cidpick'): - self.getPlotWidget().mpl_disconnect(self.cidpick) + self.multicompfig.mpl_disconnect(self.cidpick) def onpick_delete(self, event): if not event.mouseevent.button == 3: @@ -1897,43 +1938,15 @@ class PickDlg(QDialog): self.removePhaseText() self.drawPhaseText() - def panPress(self, gui_event): - ax = self.getPlotWidget().axes[0] - if gui_event.inaxes != ax: return - self.cur_xlim = ax.get_xlim() - self.cur_ylim = ax.get_ylim() - self.press = gui_event.xdata, gui_event.ydata - self.xpress, self.ypress = self.press - - def panRelease(self, gui_event): - figure = self.getPlotWidget().figure - self.press = None - self.refreshPhaseText() - self.refreshArrivalsText() - figure.canvas.draw() - - def panMotion(self, gui_event): - if self.press is None: return - ax = self.getPlotWidget().axes[0] - if gui_event.inaxes != ax: return - dx = gui_event.xdata - self.xpress - dy = gui_event.ydata - self.ypress - self.cur_xlim -= dx - self.cur_ylim -= dy - ax.set_xlim(self.cur_xlim) - ax.set_ylim(self.cur_ylim) - - ax.figure.canvas.draw() - def togglePickBlocker(self): return not self.pick_block def filterWFData(self): if self.pick_block: return - self.updateCurrentLimits() + self.multicompfig.updateCurrentLimits() data = self.getWFData().copy() - old_title = self.getPlotWidget().getAxes().get_title() + old_title = self.multicompfig.axes[0].get_title() title = None phase = self.currentPhase filtoptions = None @@ -1956,7 +1969,7 @@ class PickDlg(QDialog): title = old_title.replace(', filtered)', ')') if title is None: title = old_title - self.getPlotWidget().plotWFData(wfdata=data, title=title, + self.multicompfig.plotWFData(wfdata=data, title=title, zoomx=self.getXLims(), zoomy=self.getYLims()) self.setPlotLabels() @@ -1964,52 +1977,45 @@ class PickDlg(QDialog): self.draw() def resetPlot(self): - self.updateCurrentLimits() + self.resetZoom() data = self.getWFData().copy() - title = self.getPlotWidget().getAxes().get_title() - self.getPlotWidget().plotWFData(wfdata=data, title=title, + 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.getPlotWidget().getPlotDict().keys() - labels = [self.getPlotWidget().getPlotDict()[key][1] for key in pos] + 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.getPlotWidget().setYTickLabels(pos, labels) - self.getPlotWidget().setXLims(self.getXLims()) - self.getPlotWidget().setYLims(self.getYLims()) - - def connectEvents(self): - self.cidpress = self.connectPressEvent(self.panPress) - self.cidmotion = self.connectMotionEvent(self.panMotion) - self.cidrelease = self.connectReleaseEvent(self.panRelease) - self.cidscroll = self.connectScrollEvent(self.multicompfig.scrollZoom) + 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.disconnectPressEvent() - self.disconnectMotionEvent() - self.disconnectReleaseEvent() - self.disconnectScrollEvent() + self.multicompfig.disconnectEvents() self.figToolBar.zoom() else: self.figToolBar.zoom() - def resetZoom(self): - self.getPlotWidget().setXLims(self.getGlobalLimits('x')) - self.getPlotWidget().setYLims(self.getGlobalLimits('y')) - self.draw() - def draw(self): - self.getPlotWidget().draw() + self.multicompfig.draw() def apply(self): picks = self.getPicks() From 34ae5dddd258c6fb48d38fd1874ec12184bde2ef Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 29 Aug 2017 16:32:42 +0200 Subject: [PATCH 06/11] [update] phaseText refreshing working (maybe slow) if replotting slows down user interaction too much, change replot to pan-release instead of -move action, or add refresh timer --- pylot/core/util/widgets.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 6d95c241..376d1a66 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -664,6 +664,7 @@ class PylotCanvas(FigureCanvas): 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): @@ -708,8 +709,19 @@ class PylotCanvas(FigureCanvas): 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 connectEvents(self): self.cidscroll = self.connectScrollEvent(self.scrollZoom) self.cidpress = self.connectPressEvent(self.panPress) @@ -1054,8 +1066,6 @@ class PickDlg(QDialog): self.multicompfig.updateCurrentLimits() - self.connectScrollEvent() - # setup ui self.setupUi() @@ -1347,17 +1357,6 @@ class PickDlg(QDialog): if phaseIndex == 0: picksMenu.addSeparator() - def disconnectScrollEvent(self): - widget = self.multicompfig - widget.mpl_disconnect(self.cidscroll_arr) - widget.mpl_disconnect(self.cidscroll_ph) - self.cidscroll_arr = None - self.cidscroll_ph = None - - def connectScrollEvent(self): - self.cidscroll_arr = self.multicompfig.connectScrollEvent(self.refreshArrivalsText) - self.cidscroll_ph = self.multicompfig.connectScrollEvent(self.refreshPhaseText) - def disable_ar_buttons(self): self.enable_ar_buttons(False) @@ -1455,7 +1454,6 @@ class PickDlg(QDialog): if self.zoomAction.isChecked(): self.zoomAction.trigger() self.multicompfig.disconnectEvents() - self.disconnectScrollEvent() self.cidpress = self.multicompfig.connectPressEvent(self.setIniPick) self.filterWFData() #self.pick_block = self.togglePickBlocker() @@ -1463,7 +1461,6 @@ class PickDlg(QDialog): def deactivatePicking(self): self.disconnectPressEvent() - self.connectScrollEvent() self.multicompfig.connectEvents() self.connect_pick_delete() From 9a61a7f27d151fc108923b8bc6e994b0c12489d8 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 31 Aug 2017 15:41:59 +0200 Subject: [PATCH 07/11] [change] fix legend locations to prevent them from bouncing on zoom --- pylot/core/pick/autopick.py | 8 ++++---- pylot/core/pick/picker.py | 8 ++++---- pylot/core/pick/utils.py | 12 ++++++------ pylot/core/util/map_projection.py | 2 +- pylot/core/util/widgets.py | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 6932f620..a4607d63 100644 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -330,7 +330,7 @@ def autopickstation(wfstream, pickparam, verbose=False, for ax in fig.axes: ax.vlines(pstart, ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed', label='P start') ax.vlines(pstop, ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed', label='P stop') - ax.legend() + ax.legend(loc=1) ############################################################## if aicpick.getpick() is not None: # check signal length to detect spuriously picked noise peaks @@ -872,7 +872,7 @@ def autopickstation(wfstream, pickparam, verbose=False, ax1.set_title('%s, %s, P Weight=%d' % (tr_filt.stats.station, tr_filt.stats.channel, Pweight)) - ax1.legend() + ax1.legend(loc=1) ax1.set_yticks([]) ax1.set_ylim([-1.5, 1.5]) ax1.set_ylabel('Normalized Counts') @@ -930,7 +930,7 @@ def autopickstation(wfstream, pickparam, verbose=False, else: ax2.set_title('%s, S Weight=%d, SNR=None, SNRdB=None' % ( trH1_filt.stats.channel, Sweight)) - ax2.legend() + ax2.legend(loc=1) ax2.set_yticks([]) ax2.set_ylim([-1.5, 1.5]) ax2.set_ylabel('Normalized Counts') @@ -973,7 +973,7 @@ def autopickstation(wfstream, pickparam, verbose=False, [-1.3, -1.3], 'g', linewidth=2) ax3.plot([lpickS, lpickS], [-1.1, 1.1], 'g--', label='lpp') ax3.plot([epickS, epickS], [-1.1, 1.1], 'g--', label='epp') - ax3.legend() + ax3.legend(loc=1) ax3.set_yticks([]) ax3.set_ylim([-1.5, 1.5]) ax3.set_xlabel('Time [s] after %s' % tr_filt.stats.starttime) diff --git a/pylot/core/pick/picker.py b/pylot/core/pick/picker.py index 1715746c..6be3d7d2 100644 --- a/pylot/core/pick/picker.py +++ b/pylot/core/pick/picker.py @@ -272,7 +272,7 @@ class AICPicker(AutoPicker): x = self.Data[0].data ax.plot(self.Tcf, x / max(x), 'k', label='(HOS-/AR-) Data') ax.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', label='Smoothed AIC-CF') - ax.legend() + ax.legend(loc=1) ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) ax.set_yticks([]) ax.set_title(self.Data[0].stats.station) @@ -313,7 +313,7 @@ class AICPicker(AutoPicker): ax1.plot([self.Pick, self.Pick], [-0.1, 0.5], 'b', linewidth=2, label='AIC-Pick') ax1.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) ax1.set_yticks([]) - ax1.legend() + ax1.legend(loc=1) if self.Pick is not None: ax2 = fig.add_subplot(2, 1, 2, sharex=ax1) @@ -336,7 +336,7 @@ class AICPicker(AutoPicker): ax2.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) ax2.set_ylabel('Counts') ax2.set_yticks([]) - ax2.legend() + ax2.legend(loc=1) if plt_flag == 1: fig.show() try: input() @@ -480,7 +480,7 @@ class PragPicker(AutoPicker): ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) ax.set_yticks([]) ax.set_title(self.Data[0].stats.station) - ax.legend() + ax.legend(loc=1) if plt_flag == 1: fig.show() try: input() diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 006f9837..9de01ec1 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -150,7 +150,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None): ax.set_title( 'Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) - ax.legend() + ax.legend(loc=1) if plt_flag == 1: fig.show() try: input() @@ -330,7 +330,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None): ax1.plot(t[islope1], xraw[islope1], label='Slope Window') ax1.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14, label='Zero Crossings') ax1.plot(t[islope1], datafit1, '--g', linewidth=2) - ax1.legend() + ax1.legend(loc=1) ax1.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) ax1.set_yticks([]) ax1.set_title('First-Motion Determination, %s, Unfiltered Data' % Xraw[ @@ -678,7 +678,7 @@ def wadaticheck(pickdic, dttolerance, iplot=0, fig_dict=None): ax.plot(checkedPpicks, wdfit2, 'g', label='Wadati 2') ax.set_title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) - ax.legend() + ax.legend(loc=1) else: ax.set_title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) @@ -789,7 +789,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot=0, fi ax.plot([t[isignal[0]], t[isignal[len(isignal) - 1]]], [minsiglevel, minsiglevel], 'g', linewidth=2, label='Minimum Signal Level') ax.plot([pick, pick], [min(rms), max(rms)], 'b', linewidth=2, label='Onset') - ax.legend() + ax.legend(loc=1) ax.set_xlabel('Time [s] since %s' % X[0].stats.starttime) ax.set_ylabel('Counts') ax.set_title('Check for Signal Length, Station %s' % X[0].stats.station) @@ -901,7 +901,7 @@ def checkPonsets(pickdic, dttolerance, jackfactor=5, iplot=0, fig_dict=None): ax.set_xlabel('Number of P Picks') ax.set_ylabel('Onset Time [s] from 1.1.1970') - ax.legend() + ax.legend(loc=1) ax.set_title('Jackknifing and Median Tests on P Onsets') if plt_flag: fig.show() @@ -1108,7 +1108,7 @@ def checkZ4S(X, pick, zfac, checkwin, iplot, fig=None): ax.set_ylabel('Normalized Counts') ax.axvspan(pick, pick + checkwin, color='c', alpha=0.2, lw=0) - ax.legend() + ax.legend(loc=1) ax.set_xlabel('Time [s] since %s' % zdat[0].stats.starttime) if plt_flag == 1: fig.show() diff --git a/pylot/core/util/map_projection.py b/pylot/core/util/map_projection.py index a7e78f64..bb334570 100644 --- a/pylot/core/util/map_projection.py +++ b/pylot/core/util/map_projection.py @@ -272,7 +272,7 @@ class map_projection(QtGui.QWidget): for index, name in enumerate(self.station_names): self.annotations.append(self.main_ax.annotate(' %s' % name, xy=(self.x[index], self.y[index]), fontsize='x-small', color='white', zorder=12)) - self.legend = self.main_ax.legend() + self.legend = self.main_ax.legend(loc=1) def add_cbar(self, label): cbar = self.main_ax.figure.colorbar(self.sc_picked, fraction=0.025) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 3575eb1e..9b9c1e5d 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1245,7 +1245,7 @@ class PickDlg(QDialog): self.get_arrivals(True) ax = self.phaseplot.ax self.arrivals.plot(ax=ax, show=False) - ax.legend() + ax.legend(loc=1) self.phaseplot.new = False self.phaseplot.draw() self.phaseplot.show() @@ -2553,7 +2553,7 @@ class TuneAutopicker(QWidget): [y_bot, y_bot], linewidth=2, color='teal') ax.plot([pick - 0.5, pick + 0.5], [y_top, y_top], linewidth=2, color='teal') - ax.legend() + ax.legend(loc=1) def plot_manual_Spick_to_ax(self, ax, pick): y_top = 0.9 * ax.get_ylim()[1] @@ -2564,7 +2564,7 @@ class TuneAutopicker(QWidget): [y_bot, y_bot], linewidth=2, color='magenta') ax.plot([pick - 0.5, pick + 0.5], [y_top, y_top], linewidth=2, color='magenta') - ax.legend() + ax.legend(loc=1) def fill_tabs(self, event=None, picked=False): self.clear_all() From 39b450b005d5cf11d05b5bcea9392565a7040cf0 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 1 Sep 2017 15:43:16 +0200 Subject: [PATCH 08/11] [add] panZoom implemented --- pylot/core/util/widgets.py | 83 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 9b9c1e5d..7f5f35e4 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -605,7 +605,8 @@ class WaveformWidgetPG(QtGui.QWidget): class PylotCanvas(FigureCanvas): - def __init__(self, figure=None, parent=None, connect_events=True, multicursor=False): + def __init__(self, figure=None, parent=None, connect_events=True, multicursor=False, + panZoomX=True, panZoomY=True): self._parent = parent if not figure: @@ -633,6 +634,10 @@ class PylotCanvas(FigureCanvas): self.cur_xlim = None self.cur_ylim = None + # panZoom activated selection + self.panZoomX = panZoomX + self.panZoomY = panZoomY + self.limits = {} for ax in self.axes: @@ -657,10 +662,19 @@ class PylotCanvas(FigureCanvas): 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): - if self.press is None: return ax_check = False for ax in self.axes: if gui_event.inaxes == ax: @@ -678,8 +692,69 @@ class PylotCanvas(FigureCanvas): 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 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 @@ -734,7 +809,7 @@ class PylotCanvas(FigureCanvas): def connectEvents(self): self.cidscroll = self.connectScrollEvent(self.scrollZoom) self.cidpress = self.connectPressEvent(self.panPress) - self.cidmotion = self.connectMotionEvent(self.panMotion) + self.cidmotion = self.connectMotionEvent(self.pan) self.cidrelease = self.connectReleaseEvent(self.panRelease) def disconnectEvents(self): @@ -1922,6 +1997,8 @@ class PickDlg(QDialog): for picktype in allpicks.keys(): picks = allpicks[picktype] for phase in picks: + if not type(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)) From c6105330e0749c21f4e2d6e294661433c0d3a1e2 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 4 Sep 2017 15:03:30 +0200 Subject: [PATCH 09/11] [add] Ctrl+S to save fig (has to be clicked firsts) --- pylot/core/util/widgets.py | 64 ++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 7f5f35e4..4abbf85a 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -31,6 +31,8 @@ except ImportError: from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT from matplotlib.widgets import MultiCursor +from matplotlib.tight_layout import get_renderer, get_subplotspec_list, get_tight_layout_figure + from PySide import QtCore, QtGui from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \ QDateTimeEdit, QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, \ @@ -607,7 +609,6 @@ class WaveformWidgetPG(QtGui.QWidget): class PylotCanvas(FigureCanvas): def __init__(self, figure=None, parent=None, connect_events=True, multicursor=False, panZoomX=True, panZoomY=True): - self._parent = parent if not figure: figure = Figure() @@ -616,11 +617,12 @@ class PylotCanvas(FigureCanvas): self.axes = figure.axes self.figure = figure - self.figure.set_facecolor((.92, .92, .92)) + self.figure.set_facecolor((1., 1., 1.)) # attribute plotdict is a dictionary connecting position and a name self.plotdict = dict() # initialize super class super(PylotCanvas, self).__init__(self.figure) + if multicursor: # add a cursor for station selection self.multiCursor = MultiCursor(self.figure.canvas, self.axes, @@ -652,6 +654,9 @@ class PylotCanvas(FigureCanvas): except: pass + self.setFocusPolicy(QtCore.Qt.StrongFocus) + self.setFocus() + def panPress(self, gui_event): ax_check = False for ax in self.axes: @@ -734,6 +739,15 @@ class PylotCanvas(FigureCanvas): self.draw() + def saveFigure(self): + if self.figure: + fd = QtGui.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) @@ -806,46 +820,60 @@ class PylotCanvas(FigureCanvas): 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.disconnectMotionEvent() - self.disconnectPressEvent() - self.disconnectReleaseEvent() + self.disconnectScrollEvent(self.cidscroll) + self.disconnectMotionEvent(self.cidmotion) + self.disconnectPressEvent(self.cidpress) + self.disconnectReleaseEvent(self.cidrelease) + self.disconnectKeyPressEvent(self.cidkpress) - def disconnectPressEvent(self): - self.mpl_disconnect(self.cidpress) + 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): - self.mpl_disconnect(self.cidmotion) - self.cidmotion = None + def disconnectMotionEvent(self, cid): + self.mpl_disconnect(cid) def connectMotionEvent(self, slot): return self.mpl_connect('motion_notify_event', slot) - def disconnectReleaseEvent(self): - self.mpl_disconnect(self.cidrelease) - self.cidrelease = None + def disconnectReleaseEvent(self, cid): + self.mpl_disconnect(cid) def connectReleaseEvent(self, slot): return self.mpl_connect('button_release_event', slot) - def disconnectScrollEvent(self): - self.mpl_disconnect(self.cidscroll) - self.cidscroll = None + 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 @@ -1152,6 +1180,8 @@ class PickDlg(QDialog): self.multicompfig.setZoomBorders2content() self.multicompfig.updateCurrentLimits() + self.multicompfig.draw() + self.multicompfig.setFocus() # setup ui self.setupUi() From 88bf4c8f67bd1e585f1337a88d3d2eb897cd9470 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 5 Sep 2017 15:03:03 +0200 Subject: [PATCH 10/11] [bugfix] delete picks after jk/wadati not working --- pylot/core/util/map_projection.py | 4 ++++ pylot/core/util/widgets.py | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/map_projection.py b/pylot/core/util/map_projection.py index bb334570..e89f4c86 100644 --- a/pylot/core/util/map_projection.py +++ b/pylot/core/util/map_projection.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + import matplotlib.pyplot as plt import numpy as np import obspy @@ -13,6 +16,7 @@ plt.interactive(False) class map_projection(QtGui.QWidget): def __init__(self, parent, figure=None): ''' + :param: picked, can be False, auto, manual :value: str ''' diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 4abbf85a..b764c9d0 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2027,7 +2027,7 @@ class PickDlg(QDialog): for picktype in allpicks.keys(): picks = allpicks[picktype] for phase in picks: - if not type(phase) in [dict, AttribDict]: + if not type(picks[phase]) in [dict, AttribDict]: continue pick_rel = picks[phase]['mpp'] - starttime # add relative pick time, phaseID and picktype index @@ -2039,8 +2039,8 @@ class PickDlg(QDialog): # delete the value from corresponding dictionary allpicks[picktype].pop(phase) # information output - msg = 'Deleted {} pick for phase {}, {}[s] from starttime {}' - print(msg.format(picktype, phase, pick_rel, starttime)) + 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): From bf36e5e6a57d9d2c2d64682ce6d417685d2ce508 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 5 Sep 2017 15:31:02 +0200 Subject: [PATCH 11/11] [bugfix] shift traces on wfPG plot --- pylot/core/util/widgets.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b764c9d0..e038bb3c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -524,12 +524,13 @@ class WaveformWidgetPG(QtGui.QWidget): try: self.plotWidget.getPlotItem().vb.setLimits(xMin=float(0), xMax=float(self.wfend - self.wfstart), - yMin=-0.5, - yMax=len(nsc) + 0.5) + 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: @@ -554,7 +555,7 @@ class WaveformWidgetPG(QtGui.QWidget): self.xlabel = 'seconds since {0}'.format(self.wfstart) self.ylabel = '' self.setXLims([0, self.wfend - self.wfstart]) - self.setYLims([-0.5, nmax + 0.5]) + self.setYLims([0.5, nmax + 0.5]) return plots # def getAxes(self):