source: SHX/trunk/SeismicHandler/modules/wx_.py @ 1162

Revision 1162, 120.7 KB checked in by klaus, 4 years ago (diff)

locate tele command (to be checked)

  • Property svn:eol-style set to native
Line 
1# -*- coding: utf-8 -*-
2
3#    This file is part of Seismic Handler eXtended (SHX). For terms of use and
4#    license information please see license.txt and visit
5#    http://www.seismic-handler.org/portal/wiki/Shx/LicenseTerms
6
7import wx
8import wx.lib.scrolledpanel as SP
9import time
10import math
11import os
12import numpy as np
13from functools import partial
14from SeismicHandler.basics.messages import msgs, subscribe_ui_event, \
15                                ui_event, log_message, get_style#, get_runtime
16from SeismicHandler.config import get_runtime, set_runtime
17from SeismicHandler.core import Traces, Overlays, traces_from_list
18from SeismicHandler.basics.codes import NAME, VERSION
19from SeismicHandler.basics.tools import get_default_attrib, timeit, timestore,\
20    EventTextParser
21from SeismicHandler.basics.analysispar import PhaseList, AnalysisPar
22from obspy.core import read, UTCDateTime
23from obspy.sh.core import fromUTCDateTime, toUTCDateTime
24
25# width of station info
26STATION_INFO = 85
27# margins (top, right, bottom, left)
28MARGINS = [10, 10, 10, 10]
29# space for time-scale
30TIMESCALE = 40
31# offset for time string
32TIMESCALE_OFFSET_TEXT = 8
33# length of minor/major ticks
34TIMESCALE_TICKS_MINOR = 3
35TIMESCALE_TICKS_MAJOR = 7
36
37# regular checking of UI data
38TIME_SLOT = .03
39
40shxcolor1 = '#ddeeff'
41shxcolor2 = '#bccdff'
42
43# holds window instance
44plotter = None
45
46# status information
47statusinfotext = ""
48
49displayselect = None
50
51class magnifyCanvas(wx.Panel):
52    """
53    A drawing canvas for the magnification trace. It has methods similar to
54    the traceCanvas class, but the methods are simpler due the fact that there
55    is only one trace displayed. On mouse operations there is no need for the
56    y (vertical) coordinate to be evaluated. Subclassing therefore is difficult
57    or the code must be reorganized. It is not advisable to run this in a
58    separate thread, the message communication could be too slow for immediate
59    reactions to mouse input.
60    """
61    def __init__( self, parent ):
62        "magnifyCanvas: init method."
63        wx.Panel.__init__( self, parent,
64            style=wx.BORDER_SIMPLE|wx.NO_FULL_REPAINT_ON_RESIZE|wx.WANTS_CHARS)
65        self.Bind(wx.EVT_PAINT, self.OnPaint)
66        self.Bind(wx.EVT_SIZE, self.OnSize)
67        self.Bind(wx.EVT_MOTION, self.OnMouseMotion)
68        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
69        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)
70        self.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
71        self.Bind(wx.EVT_RIGHT_UP, self.OnMouseRightUp)
72        self.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseLeftDouble)
73        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
74        #self.Bind(wx.EVT_KEY_UP, self.OnKeyPress )
75        self.Bind(wx.EVT_CHAR, self.OnKeyPress )
76        self.SetFocus()
77        self._magn_bitmap = None
78        self.trc = None
79        self.pixwidth = None
80        self.pixheight = None
81        self.pixel_duration = None
82        self._xor_line = None
83        self._last_defined_phase = None
84        self._mousedouble = None
85        self._dragstarttime = None
86        self._s3_showalltraces = False
87        self._s3_origtrcname = None
88        self._s3_othertraces = []
89        self.SetBackgroundColour( "white" )
90        self.clearWindow()
91   
92    def OnPaint( self, e ):
93        "magnifyCanvas: redraw event."
94        if self._magn_bitmap:
95            dc = wx.BufferedPaintDC(self, self._magn_bitmap)
96            #self._drawPicks()
97   
98    def OnSize( self, e ):
99        "magnifyCanvas: resize event."
100        self.pixwidth, self.pixheight = self.GetVirtualSize()
101        self.refresh()
102   
103    def OnMouseMotion(self, evt):
104        "magnifyCanvas: mouse motion event."
105        if not self.trc:
106            return
107        x, y = evt.GetPositionTuple()
108        abstime, reltime = self._fromScreenCoo( x,  mode='both' )
109        # draw XOR marker
110        if evt.LeftIsDown():
111            self._drawXorLine( x )
112        # Put text in status line of plotter window
113        stext = "%s    %g    %s    Z:%3.1f" % (fromUTCDateTime(abstime),reltime,
114            self.trc.id,(self.trc.stats.endtime-self.trc.stats.starttime))
115        plotter._setStatusText( stext )
116        if evt.RightIsDown() and self._dragstarttime:
117            difftime = self._dragstarttime - abstime
118            plotter.canvas.moveZoomWindow( 'right', span=difftime )
119            #self._dragstarttime -= 0.05*difftime
120        # let the event propagate
121        evt.Skip()
122
123    def OnMouseLeftDown(self, evt):
124        "magnifyCanvas: Left moune button pressed down."
125        x, y = evt.GetPositionTuple()
126        abstime = self._fromScreenCoo( x,  mode='abs' )
127        if self.trc:
128            cphase = self._closePhase( abstime )
129            if cphase:
130                station = "%s.%s" % (self.trc.stats.network,
131                    self.trc.stats.station)
132                station = station.upper()
133                _sendShCommand(
134                    "@PHASE CLEAR %s %s %s" % (cphase[0],station,cphase[1]) \
135                    +"\n@PHASE DEFAULT_PHASE %s" % cphase[0]
136                )
137                self._last_defined_phase = cphase[0]
138                #self._drawSinglePick( trace, tracetime,color='white' )
139                self.refresh()
140        evt.Skip()
141   
142    def OnMouseRightDown( self, evt ):
143        "magnifyCanvas: right mouse button pressed."
144        x, y = evt.GetPositionTuple()
145        self._dragstarttime = self._fromScreenCoo( x,  mode='abs' )
146        evt.Skip()
147
148    def OnMouseRightUp( self, evt ):
149        "magnifyCanvas: right mouse button released."
150        self._dragstarttime = None
151        evt.Skip()
152
153    def OnMouseLeftUp(self, evt):
154        "magnifyCanvas: left mouse button released."
155        if not self.trc:
156            return
157        self._drawXorLine( None )  # clear possibly existing XOR line.
158        x, y = evt.GetPositionTuple()
159        abstime, reltime = self._fromScreenCoo( x,  mode='both' )
160        station = "%s.%s" % (self.trc.stats.network,self.trc.stats.station)
161
162        # Send SH command to define/clear phase if not waiting for user input.
163        # Redraw of main window only if less than 50 traces.
164        if displayselect or len(Traces) < 50:
165            qredraw = '/REDRAW'
166        else:
167            qredraw = ''
168        if self._mousedouble:
169            if self._last_defined_phase:
170                _sendShCommand( "@PHASE CLEAR %s %s manual %s" % (
171                    self._last_defined_phase,station.upper(),qredraw) )
172        else:
173            _sendShCommand( "@PHASE DEFINE %s %s ;;; %s %s" % (station.upper(),
174                fromUTCDateTime(abstime),self.trc.stats.channel[-1].upper(),
175                qredraw), name="mouse evt" )
176        self.refresh()
177        self._mousedouble = False
178        evt.Skip()
179   
180    def OnMouseLeftDouble( self, evt ):
181        "magnifyCanvas: left double click."
182        self._mousedouble = True
183
184    def OnMouseWheel(self, evt):
185        "magnifyCanvas: mouse wheel event."
186        wheel = evt.GetWheelRotation()
187        if wheel > 0:
188            plotter.canvas.moveZoomWindow( 'up' )
189        elif wheel < 0:
190            plotter.canvas.moveZoomWindow( 'down' )
191        evt.Skip()
192   
193    def OnKeyPress( self, evt ):
194        "magnifyCanvas: key event: emulate all shortcuts of main window."
195        localtrans = {
196            314 : '<-',
197            315 : '-^',
198            316 : '->',
199            317 : '-v',
200        }
201        try:
202            char = chr( evt.GetUniChar() ).lower()
203        except:
204            try:
205                char = localtrans[evt.GetUniChar()]
206            except:
207                print "dbg: unichar not recognized", repr(evt.GetUniChar())
208                return
209        shortcut = {
210            'a': plotter.OnSimulateWoodAnderson,
211            'b': plotter.OnBeam,
212            'd': plotter.OnDelTimeWindow,
213            'g': plotter.OnSetPhase_Pg,
214            'l': plotter.OnLocateEvent,
215            'm': plotter.OnMagnMlAnalyticSingle,
216            'n': plotter.OnSetPhase_Pn,
217            'p': plotter.OnSetPhase_P,
218            's': plotter.OnSetTimeWindow,
219            'u': plotter.OnSimulateUndo,
220            'w': plotter.OnPlaneWave,
221            '>': plotter.OnMoveZoomRight,
222            '<': plotter.OnMoveZoomLeft,
223            '+': plotter.OnAmplZoomUp,
224            '-': plotter.OnAmplZoomDown,
225            '0': plotter.OnPrevEvent,
226            '1': plotter.OnNextEvent,
227            '2': plotter.OnSetPhase_Sg,
228            '3': plotter.OnShowAllInMagnify,
229            '8': plotter.OnFilterBP_1_8,
230            '-^': self.keyZoomUp,
231            '-v': self.keyZoomDown,
232            '->': self.keyZoomGrow,
233            '<-': self.keyZoomShrink,
234        }
235        shortcut_control = {
236            'd': plotter.OnDemean,
237            'f': plotter.OnFKAutoFreq,
238            'n': plotter.OnNorm,
239            'o': plotter.OnOverlappingTraces,
240            'q': plotter.OnQuit,
241            's': plotter.OnSaveTracesAndParamsQuick,
242            'w': plotter.OnSimulateWWSSNSP,
243            'l': plotter.OnSimulateSROLP,
244            'y': plotter.OnSortByDistance,
245        }
246        if evt.ControlDown():
247            if char in shortcut_control.keys():
248                shortcut_control[char](None)
249        else:
250            if char in shortcut.keys():
251                shortcut[char](None)
252   
253    def keyZoomGrow( self, e ):
254        plotter.canvas.moveZoomWindow( 'grow' )
255    def keyZoomShrink( self, e ):
256        plotter.canvas.moveZoomWindow( 'shrink' )
257    def keyZoomUp( self, e ):
258        plotter.canvas.moveZoomWindow( 'up' )
259    def keyZoomDown( self, e ):
260        plotter.canvas.moveZoomWindow( 'down' )
261
262    def refresh( self ):
263        "magnifyCanvas: redraw content of window."
264        self._drawMagnifyTrace()
265        self._drawPicks()
266
267    def _closePhase( self, abstime ):
268        "magnifyCanvas: return name and type of closest phase or None."
269        if not self.trc:
270            return None
271        phaselist = PhaseList()
272        if not self.pixel_duration:
273            return None
274        toltime = 3*self.pixel_duration
275        station = "%s.%s" % (self.trc.stats.network,self.trc.stats.station)
276        for phase in phaselist.getPhaseList(station):
277            tdiff = abs( phase.picktime-abstime )
278            if tdiff < toltime:
279                return (phase.name,phaselist.picktypeName(phase.picktype))
280        return None
281   
282    def _drawMagnifyTrace( self, clear=True, othertrace=None, yoffset=0.,
283        color=(0,0,0), norm=None ):
284        "magnifyCanvas: does the drawing work."
285        # Bitmap holding the final figure, canvas->DC drawing to this bitmap.
286        if self.trc == None:
287            self.clearWindow()
288            return
289        if norm == None:
290            try:
291                norm = max( abs(max(self.trc.data)), abs(min(self.trc.data)) )
292            except:
293                self.clearWindow()
294                return
295        if othertrace == None:
296            drawtrc = self.trc
297        else:
298            drawtrc = othertrace
299        amplscale = 1./norm * self.pixheight/2.
300        self._magn_bitmap = wx.EmptyBitmap(self.pixwidth, self.pixheight)
301        mdc = wx.MemoryDC(self._magn_bitmap)
302        mdc.SetBrush(wx.TRANSPARENT_BRUSH)
303        if clear:
304            mdc.Clear()
305        mdc.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
306        mdc.SetPen(wx.Pen(color, 1))
307        mdc.BeginDrawing()
308        mdc.DrawLines(
309            zip(
310                np.linspace( 0., self.pixwidth, len(drawtrc.data) ),
311                drawtrc.data*amplscale + self.pixheight/2 + yoffset
312            )
313        )
314        # station label and amplitudes
315        mindat = self.trc.data.min()
316        maxdat = self.trc.data.max()
317        if abs(mindat) > 10 or abs(maxdat) > 10:
318            valtext = "%3.1f,%3.1f" % (mindat,maxdat)
319        else:
320            valtext = "%g,%g" % (mindat,maxdat)
321        labtext = "%s (%s)" % (self.trc.id,valtext)
322        mdc.DrawText( labtext, 4, 4 ) # top left corner addressed
323        mdc.EndDrawing()
324        del mdc
325        self.Refresh()
326        self.Update()
327   
328    def _drawPicks( self ):
329        "magnifyCanvas: add picks to waveform."
330        if self.trc == None:
331            return
332        phaselist = PhaseList()
333        _picks = {}
334        sname = "%s.%s" % (self.trc.stats.network,self.trc.stats.station)
335        for phase in phaselist.getPhaseList(sname):
336            if phase.comp and self.trc.stats.channel[-1] != phase.comp:
337                continue
338            pcol = phaselist.picktypeColor( phase.picktype )
339            if pcol not in _picks.keys():
340                _picks[pcol] = []
341            _picks[pcol].append(
342                ( phase.name, self._toScreenCoo(phase.picktime) )
343            )
344        dc = wx.MemoryDC(self._magn_bitmap)  # mdc
345        #dc = wx.ClientDC(self)
346        dc.SetBrush(wx.TRANSPARENT_BRUSH)
347        dc.SetFont( wx.Font( 8, wx.DEFAULT, wx.NORMAL, wx.NORMAL ) )
348        pickborder = 10
349        yup = self.pixheight - pickborder
350        ydn = pickborder
351        # draw picks
352        for pcolor in _picks.keys():
353            dc.SetPen(wx.Pen(pcolor, 1))
354            dc.SetTextForeground(pcolor)
355            for pick in _picks[pcolor]:
356                pname, xcoo = pick
357                dc.DrawLine( xcoo, yup, xcoo, ydn )
358                if pname != '_Mark_':
359                    dc.DrawText(pname, xcoo + 2, ydn - 10)
360        del dc          # mdc
361        self.Refresh()  # mdc
362        self.Update()   # mdc
363   
364    def _drawXorLine( self, xcoo=None ):
365        """
366        magnifyCanvas:
367        Draws line in XOR mode. Deletes previous line. To only clear old
368        element, pass None.
369        Should also draw into backing store bitmap (self._bitmap). Now,
370        a repaint doesn't restore the XOR drawing, resulting in a doubled
371        element.
372        """
373        color = 'orange'
374        size = 1
375        pickborder = 10
376        yup = self.pixheight - pickborder
377        ydn = pickborder
378        if xcoo == None:
379            coo = (None,None,None,None)
380        else:
381            coo = (xcoo,ydn,xcoo,yup)
382        dc = wx.ClientDC(self)
383        dc.SetLogicalFunction( wx.XOR )
384        dc.SetPen( wx.Pen(color,size,wx.SOLID) )
385        dc.SetBrush( wx.Brush(color,wx.TRANSPARENT) )
386        if self._xor_line != None:
387            # Clear old line.
388            dc.DrawLine( *self._xor_line )
389        if coo[0] == None:
390            self._xor_line = None
391        else:
392            # Draw new rectangle.
393            dc.DrawLine( *coo )
394            self._xor_line = coo
395
396    def magnifyTrace( self, trc, boxwidth=None, boxcenter=None, boxend=None ):
397        "magnifyCanvas: top level redraw routine for magnify content."
398        if trc == None:
399            self.clearWindow()
400            return
401        if self._s3_showalltraces:
402            s3_recent = self._s3_origtrcname
403            self._s3_origtrcname = "%s.%s.%s.%s" % (trc.stats.network,
404                trc.stats.station,trc.stats.location,trc.stats.channel)
405            if s3_recent != self._s3_origtrcname:
406                self._s3_othertraces = self._s3_findOtherTraces()
407        self.pixwidth, self.pixheight = self.GetVirtualSize()
408        self.pixel_duration = self._fromScreenCoo( 1, mode='rel' )
409        if boxcenter != None:
410            if not boxwidth:
411                return
412            w2 = boxwidth/2
413            torig = trc.get_info('t-origin')
414            if torig:
415                boxcenter -= torig
416            ta = boxcenter - w2
417            tb = boxcenter + w2
418            self.trc = trc.slice( ta, tb )
419        elif boxend != None:
420            if not boxwidth:
421                return
422            torig = trc.get_info('t-origin')
423            if torig:
424                boxend -= torig
425            ta = boxend - boxwidth
426            if ta > boxend:
427                self.trc = trc.slice( boxend, ta )
428            else:
429                self.trc = trc.slice( ta, boxend )
430        else:
431            self.trc = trc
432        clearwdw = True
433        if self._s3_showalltraces and len(self._s3_othertraces) > 0:
434            try:
435                norm = max( abs(max(self.trc.data)), abs(min(self.trc.data)) )
436            except:
437                return
438            self._s3_showOtherTraces( norm )
439            clearwdw = False
440        self._drawMagnifyTrace( clearwdw )
441        self._drawPicks()
442   
443    def clearWindow( self ):
444        "magnifyCanvas: clear window content."
445        dc = wx.AutoBufferedPaintDCFactory(self)
446        dc.Clear()
447        return
448   
449    def _toScreenCoo( self, abstime ):
450        "magnifyCanvas: helper for coo transformation."
451        if not self.trc or not self.pixwidth:
452            return 0.
453        tdiff = self.trc.stats.endtime-self.trc.stats.starttime
454        if tdiff == 0.:
455            return 0.
456        return ((abstime-self.trc.stats.starttime) / tdiff * self.pixwidth)
457   
458    def _fromScreenCoo( self, xpix, mode='abs' ):
459        "magnifyCanvas: helper for coo transformation."
460        if not self.trc or not self.pixwidth:
461            return 0.
462        reltime = (float(xpix)/float(self.pixwidth)) \
463            * (self.trc.stats.endtime-self.trc.stats.starttime)
464        if mode == 'abs':
465            return self.trc.stats.starttime + reltime
466        elif mode == 'rel':
467            return reltime
468        else:
469            return (self.trc.stats.starttime + reltime,reltime)
470   
471    def _s3_findOtherTraces( self ):
472        "magnifyCanvas: find other components of main trace."
473        tlist = []
474        for trc in traces_from_list('all'):
475            sname = "%s.%s.%s.%s" % (trc.stats.network,
476                trc.stats.station,trc.stats.location,trc.stats.channel)
477            if sname == self._s3_origtrcname:
478                # not the same trace again
479                continue
480            # take away last char (component)
481            tname = self._s3_origtrcname[:-1]
482            if sname.startswith(tname):
483                tlist.append( trc )
484        return tlist
485   
486    def _s3_showOtherTraces( self, norm ):
487        """
488        magnifyCanvas:
489        show other components of main traces using plot method
490        _drawMagnifyTrace.
491        """
492        yoffset = -self.pixheight/5
493        ystep = 2*self.pixheight/5
494        clearwdw = True
495        for trc in self._s3_othertraces:
496            otrc = trc.slice(self.trc.stats.starttime,self.trc.stats.endtime)
497            if len(otrc) < 2:
498                print "dbg: s3: short other trace"
499                continue
500            if abs(otrc.stats.starttime-self.trc.stats.starttime) \
501                > self.trc.stats.delta:
502                print "dbg: s3: other trace stange start time"
503                continue
504            self._drawMagnifyTrace( clearwdw, otrc, yoffset,
505                color=(200,200,200), norm=norm )
506            yoffset += ystep
507            clearwdw = False
508   
509    def toggleShowAllTraces( self ):
510        "magnifyCanvas: toggle show-all-traces flag. Called from menu."
511        if self._s3_showalltraces:
512            self._s3_showalltraces = False
513            self._s3_origtrcname = None
514            self._s3_othertraces = []
515        else:
516            self._s3_showalltraces = True
517       
518
519class traceCanvas(SP.ScrolledPanel):
520    """
521    All drawing methods for plotting traces. Include all mouse-related
522    functions.
523    """
524    #@timeit
525    def __init__(self, parent, fnames=[]):
526        "Trace canvas init method."
527        SP.ScrolledPanel.__init__(self, parent,
528            style=wx.BORDER_SIMPLE|wx.NO_FULL_REPAINT_ON_RESIZE)
529
530        # class data
531        self._bitmap = None
532        self.parent = parent
533        self.traces = Traces
534        self.ZoomWindow = None
535        self.zoomwdwwidth = 0
536        self.Scrolled = 0
537        self.mousedouble = 0
538        self.refresh = False
539        self.AllowDoubleClick = True
540        self.do_screenshot = False
541        self.pixel_duration = None
542        self.last_defined_phase = None
543        self.wheelpos = 0
544        self.timewindow = (None,None)
545        self.last_number_of_traces = 0
546        self._xor_line = None
547        self._xor_rect = None
548
549        # temporary layer for mouse motion
550        self.overlay_drag = wx.Overlay()
551        # layer for picks and zoom box
552        self.overlay_picks = wx.Overlay()
553
554        # application defaults
555        self.relativeAxis = True
556        self.traceOrder = 0
557        self.phasename = "P"
558        self.space = None
559        self.interactive = "trace_time"
560
561        self.ppi = wx.ScreenDC().GetPPI()
562
563        self._setup()
564
565        def disable_event(*pargs,**kwargs):
566            "traceCanvas: from some website."
567            pass # the sauce, please
568
569        # event binding
570        self.Bind(wx.EVT_PAINT, self.OnPaint)
571        self.Bind(wx.EVT_SIZE, self.OnSize)
572        self.Bind(wx.EVT_IDLE, self.OnIdle)
573        self.Bind(wx.EVT_MOTION, self.OnMouseMotion)
574        self.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
575        self.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseLeftDouble)
576        self.Bind(wx.EVT_RIGHT_DCLICK, self.OnMouseRightDouble)
577        self.Bind(wx.EVT_RIGHT_UP, self.OnMouseRightUp)
578        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)
579        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
580        #self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
581        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
582        #self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
583        self.Bind(wx.EVT_ERASE_BACKGROUND, disable_event)
584
585        # messaging system
586#        msgs.subscribe(self.OnNotifyHeight, 'GUT.displayheight')
587#        msgs.subscribe(self.OnNotifyPhaseName, 'GUI.phasename')
588#        msgs.subscribe(self.OnNotifyTraceOrder, 'GUI.traceorder')
589#        msgs.subscribe(self.OnNotifyTimeAxis, 'GUT.timeaxis')
590        msgs.subscribe(self.__runtime_changer, "setruntime")
591
592        # init
593        self.OnSize(None)
594        self.SetFocus()
595
596    def __runtime_changer(self, name, value):
597        """
598        traceCanvas:
599        Helper method for acting on certain runtime events. Called via message
600        system.
601        """
602        if name != "RUNTIME:styles":
603            return
604        margins = value.get("MARGINS", None)
605
606        if not margins:
607            return
608
609        # convert inch values to pixels
610        margins = [
611            margins[0] * self.ppi[1],
612            margins[1] * self.ppi[0],
613            margins[2] * self.ppi[1],
614            margins[3] * self.ppi[0]
615        ]
616
617        global MARGINS
618        if MARGINS != margins:
619            MARGINS = margins
620   
621    def closePhase( self, trace, abstime ):
622        "traceCanvas: Return name and type of closest phase or None."
623        phaselist = PhaseList()
624        if not self.pixel_duration:
625            return None
626        toltime = 2*self.pixel_duration
627        station = "%s.%s" % (trace.stats.network,trace.stats.station)
628        for phase in phaselist.getPhaseList(station):
629            tdiff = abs( phase.picktime-abstime )
630            if tdiff < toltime:
631                return (phase.name,phaselist.picktypeName(phase.picktype))
632        return None
633   
634    def _drawXor( self, mode, coo=(None,None,None,None), dc=None ):
635        """
636        traceCanvas:
637        Draws line (len(coo)==2) or rectangle (len(coo) == 4) in XOR mode.
638        Deletes previous line/rectangle. To only clear old element, pass
639        None-tuples.
640        Should also draw into backing store bitmap (self._bitmap). Now,
641        a repaint doesn't restore the XOR drawing, resulting in a doubled
642        element.
643        """
644        if mode == 'clear':
645            self._xor_line = None
646            self._xor_rect = None
647            return
648        color = 'orange'
649        # Line or rectangle?
650        islinemode = (mode == 'line')
651        # Setup dc.
652        if islinemode:
653            size = 1
654        else:
655            size = 2
656        if dc == None:
657            dc = wx.ClientDC(self)
658        dc.SetLogicalFunction( wx.XOR )
659        dc.SetPen( wx.Pen(color,size,wx.SOLID) )
660        dc.SetBrush( wx.Brush(color,wx.TRANSPARENT) )
661        #dc.SetBrush( wx.Brush(color,wx.SOLID) )
662        if islinemode:
663            if self._xor_line != None:
664                # Clear old rectangle.
665                dc.DrawLine( *self._xor_line )
666            if coo[0] == None:
667                self._xor_line = None
668            else:
669                # Draw new rectangle.
670                dc.DrawLine( *coo )
671                self._xor_line = coo
672        else:
673            if self._xor_rect != None:
674                # Clear old rectangle.
675                dc.DrawRectangle( *self._xor_rect )
676            if coo[0] == None:
677                self._xor_rect = None
678            else:
679                # Draw new rectangle.
680                dc.DrawRectangle( *coo )
681                self._xor_rect = coo
682
683    # event processing
684    def OnMouseWheel(self, evt):
685        "traceCanvas: mouse wheel callback."
686        maxwheelpos = 10
687        wheel = evt.GetWheelRotation()
688        if displayselect:
689            if wheel > 0:
690                _scrollTraces( 'up' )
691            elif wheel < 0:
692                _scrollTraces( 'down' )
693        else:
694            if wheel > 0:
695                #self.moveZoomWindow( 'up' )
696                self.moveZoomWindow( 'grow' )
697            elif wheel < 0:
698                #self.moveZoomWindow( 'down' )
699                self.moveZoomWindow( 'shrink' )
700        #else:
701        #    if wheel > 0:
702        #        if self.wheelpos < maxwheelpos:
703        #            _sendShCommand( "zoom/rel all 2" )
704        #            self.wheelpos += 1
705        #    elif wheel < 0:
706        #        if self.wheelpos > -maxwheelpos:
707        #            _sendShCommand( "zoom/rel all 0.5" )           
708        #            self.wheelpos -= 1
709        evt.Skip()
710
711    def OnKeyDown(self, evt):
712        "traceCanvas: key events not used, just menu shortcuts."
713        print "key"
714        kc = evt.GetKeyCode()
715        print kc
716        evt.Skip()
717#        if kc == ord("r"):
718
719    def OnMouseLeftDouble(self, evt):
720        "traceCanvas: left double click."
721        trace, tracetime = self.ScreenToTraceAndTime(*(evt.GetPositionTuple()))
722        if trace:
723            self.mousedouble = True
724        #    print "dbg: closePhase", self.closePhase(trace,tracetime)
725        #    cphase = self.closePhase( trace, tracetime )
726        #    if cphase:
727        #        station = "%s.%s" % (trace.stats.network,trace.stats.station)
728        #        _sendShCommand(
729        #            "phase clear %s %s %s" % (cphase[0],station,cphase[1])
730        #        )
731        evt.Skip()
732
733    def OnMouseRightDouble(self, evt):
734        "traceCanvas: right double click."
735        self.ZoomWindow = None
736        if plotter:
737            plotter.setZoomWindow( None, None, None )
738        self.zoomwdwwidth = 0.
739        self.__zoombox( None, None, None )
740        evt.Skip()
741
742    def OnMouseLeftDown(self, evt):
743        "traceCanvas: press left mouse button."
744        self._captureMouse(evt)
745        trace, tracetime = self.ScreenToTraceAndTime(*(evt.GetPositionTuple()))
746        if trace:
747            #self.mousedouble = True
748            cphase = self.closePhase( trace, tracetime )
749            if cphase:
750                station = "%s.%s" % (trace.stats.network,trace.stats.station)
751                station = station.upper()
752                _sendShCommand(
753                    "@PHASE CLEAR %s %s %s" % (cphase[0],station,cphase[1]) \
754                    +"\n@PHASE DEFAULT_PHASE %s" % cphase[0]
755                )
756                self.last_defined_phase = cphase[0]
757                self._drawSinglePick( trace, tracetime,color='white' )
758                if plotter.magnify:
759                    plotter.magnify.canvas.refresh()
760            self._drawPicksM()
761        evt.Skip()
762
763    def OnMouseRightDown(self, evt):
764        "traceCanvas: press right mouse button callback."
765        self._captureMouse(evt)
766        if self.zoomwdwwidth:
767            pass
768        elif self.ZoomWindow:
769            trace, start, end = self.ZoomWindow
770            start = self.TraceAndTimeToScreen(trace, start)
771            end = self.TraceAndTimeToScreen(trace, end)
772            if start[0] == None or end[0] == None:
773                self.ZoomWindow = None
774                return
775            self.zoomwdwwidth = end[0] - start[0]
776        else:
777            self.zoomwdwwidth = 0
778        # draw zoom box
779        if self.zoomwdwwidth > 0:
780            x, y = evt.GetPositionTuple()
781            trace, start = \
782                self.ScreenToTraceAndTime(x-self.zoomwdwwidth/2, y)
783            trace, end = \
784                self.ScreenToTraceAndTime(x+self.zoomwdwwidth/2, y)
785            self.ZoomWindow = [trace, start, end]
786            self._drawZoomWindow()
787       
788
789    def OnMouseMotion(self, evt):
790        """
791        traceCanvas: Mouse motion.
792        """
793
794        if not len(self.traces):
795            return
796
797        x, y = evt.GetPositionTuple()
798        trace, timestamp = self.ScreenToTraceAndTime(x, y)
799        if timestamp == None:
800            return
801
802        try:
803            tid = trace.id
804        except AttributeError:
805            tid = "*"
806       
807        reltimestr = "%g" % self.ScreenToRelativeTime( x, y )
808
809        dragwidth = None
810        if evt.Dragging() and trace:
811            th2 = self.traceheight / 2
812            shx_graphics = trace.get_graphics()
813            if shx_graphics == None:
814                print "dbg: returned from callback, no shx_graphics"
815                return
816            mpth2 = shx_graphics.midpoint + th2
817            mmth2 = shx_graphics.midpoint - th2
818
819            # draw box
820            if evt.RightIsDown():
821                self.ZoomWindow = None
822                if plotter:
823                    plotter.setZoomWindow( None, None, None )
824                if self.zoomwdwwidth == 0.:
825                    self.__zoombox( self.dragStart, (x, y),
826                        shx_graphics.midpoint, size=2 )
827                    dragwidth = x - self.dragStart[0]
828                    # data for magnify trace
829                    if plotter and plotter.magnify:
830                        plotter.magnify.canvas.magnifyTrace( trace,
831                            boxend=timestamp,
832                            boxwidth=dragwidth*self.pixel_duration )
833                else:
834                    _start = [x-self.zoomwdwwidth/2,y]
835                    _end = [x+self.zoomwdwwidth/2,y]
836                    self.__zoombox( _start, _end,
837                        shx_graphics.midpoint, size=2 )
838                    dragwidth = _end[0] - _start[0]
839                    if plotter and plotter.magnify:
840                        plotter.magnify.canvas.magnifyTrace( trace,
841                            boxcenter=timestamp,
842                            boxwidth=dragwidth*self.pixel_duration )
843            # draw position line
844            elif evt.LeftIsDown():
845                if self.traceOrder in [0, 1]:
846                    self._drawXor( 'line', (
847                        x, mmth2 - self.Scrolled,
848                        x, mpth2 - self.Scrolled
849                    ) )
850                elif self.traceOrder in [2, 3]:
851                    self._drawXor( 'line', (
852                        mmth2 - self.Scrolled, y,
853                        mpth2 - self.Scrolled, y
854                    ) )
855
856        stext = "%s    %s    %s" % (fromUTCDateTime(timestamp), reltimestr, tid)
857        if self.ZoomWindow:
858            stext += "    Z:%3.1f" % (self.ZoomWindow[2]-self.ZoomWindow[1])
859        elif dragwidth and self.pixel_duration:
860            stext += "    Z:%3.1f" % (dragwidth*self.pixel_duration)
861        self.parent._setStatusText( stext )
862
863        # let the event propagate
864        evt.Skip()
865
866    def OnMouseRightUp(self, evt):
867        """
868        traceCanvas: Release of right mouse button.
869        """
870        if self.HasCapture():
871            self._releaseMouse(evt)
872            x, y = evt.GetPositionTuple()
873
874            if self.zoomwdwwidth == 0:
875                trace, end = self.ScreenToTraceAndTime(x, y)
876                if end == None:
877                    return
878                if self.traceOrder in [0, 1]:
879                    _, start = self.ScreenToTraceAndTime(self.dragStart[0], y)
880                elif self.traceOrder in [2, 3]:
881                    _, start = self.ScreenToTraceAndTime(x, self.dragStart[1])
882            else:
883                if self.traceOrder in [0, 1]:
884                    trace, start = \
885                        self.ScreenToTraceAndTime(x-self.zoomwdwwidth/2, y)
886                    trace, end = \
887                        self.ScreenToTraceAndTime(x+self.zoomwdwwidth/2, y)
888                elif self.traceOrder in [2, 3]:
889                    trace, start = \
890                        self.ScreenToTraceAndTime(x, y-self.zoomwdwwidth/2)
891                    trace, end = \
892                        self.ScreenToTraceAndTime(x, y+self.zoomwdwwidth/2)
893
894            if start > end:
895                end, start = start, end
896
897            self.ZoomWindow = [trace, start, end]
898            self.dragStart = None
899            self._drawZoomWindow()
900            if plotter and trace:
901                # store relative times for possible menu call to stw.
902                srel = start - trace.stats.starttime \
903                    + trace.get_info('t-origin')
904                erel = end - trace.stats.starttime + trace.get_info('t-origin')
905                plotter.setZoomWindow( trace.index(True), srel, erel )
906                # content of magnify window
907                if plotter.magnify:
908                    plotter.magnify.canvas.magnifyTrace(trace.slice(start,end))
909            elif plotter.magnify:
910                plotter.magnify.canvas.magnifyTrace( None )  # clear
911
912    def OnMouseLeftUp(self, evt):
913        """
914        traceCanvas: Release of left mouse button.
915        """
916        if self.HasCapture():
917            self._releaseMouse(evt)
918            self.dragStart = None
919           
920        #print "dbg: double buffered", self.IsDoubleBuffered()
921
922        #if self.mousedouble:
923        #    self.mousedouble = False
924
925        x, y = evt.GetPositionTuple()
926        trace, tracetime = self.ScreenToTraceAndTime(x, y)
927        if tracetime == None:
928            return
929
930        # Send SH command to define/clear phase if not waiting for user input.
931        # Redraw of main window only if less than 50 traces on display.
932        if displayselect or len(Traces) < 50:
933            qredraw = "/REDRAW"
934        else:
935            qredraw = ""
936        if self.mousedouble:
937            if self.last_defined_phase:
938                station = "%s.%s" % (trace.stats.network,trace.stats.station)
939                _sendShCommand( "@PHASE CLEAR %s %s manual %s" % (
940                    self.last_defined_phase,station.upper(),qredraw) )
941        elif trace and self.parent._user_selection:
942            station = "%s.%s" % (trace.stats.network,trace.stats.station)
943            _sendShCommand( "@PHASE DEFINE %s %s ;;; %s %s" % (station.upper(),
944                fromUTCDateTime(tracetime),trace.stats.channel[-1].upper(),
945                qredraw), name="mouse evt" )
946            self._drawSinglePick( trace, tracetime )
947            if plotter.magnify:
948                plotter.magnify.canvas.refresh()
949        self.mousedouble = False
950
951        if self.interactive == "trace_time":
952            self.parent._user_selection = (trace, tracetime)
953        elif self.interactive == "relative":
954            self.parent._user_selection = self.ScreenToRelativeTime(x, y)
955        else:
956            raise NameError("unknown selection mode")
957       
958        evt.Skip()
959
960    def OnScroll(self, evt):
961        """
962        traceCanvas: Update scroll offset. Event disabled.
963        """
964        print "dbg: on scroll"
965        if self.traceOrder in [0, 1]:
966            self.Scrolled = self.GetScrollPos(wx.VERTICAL) * \
967                                               self.GetScrollPixelsPerUnit()[1]
968        elif self.traceOrder in [2, 3]:
969            self.Scrolled = self.GetScrollPos(wx.HORIZONTAL) * \
970                                               self.GetScrollPixelsPerUnit()[0]
971        self.OnSize(evt, renewOverlay=True)
972        evt.Skip()
973
974    def OnIdle(self, evt):
975        """
976        traceCanvas: idle processing. Redraw is done here.
977        """
978        if self.refresh:
979            self._timewindow = get_runtime("timewindow")
980            self._normtype = get_runtime("normtype")
981            wx.BeginBusyCursor( cursor=wx.StockCursor(_cursorNameToID("cross")) )
982            self._resetTimeRange()
983            self._drawTraces()
984            self._drawPicksM()
985            self._drawZoomWindow()
986            if plotter.magnify:
987                plotter.magnify.canvas.refresh()
988            wx.EndBusyCursor()
989            self.refresh = False
990
991    def _set_modes(self, name, mode):
992        """
993        traceCanvas:
994        Method for altering the selection mode. Intended to be called from
995        other threads using "CallAfter".
996        """
997        setattr(self, name, mode)
998
999    def OnSize(self, evt, renewOverlay=False):
1000        """
1001        traceCanvas: Called if redraw is requested.
1002
1003        Just set flag for redraw - execution triggered from IDLE event.
1004        In case of resize renew overlay buffers.
1005        """
1006        # if window was resized, renew overlay buffers
1007        if evt and (renewOverlay or evt.GetEventType() == wx.EVT_SIZE.typeId):
1008            self._setup()
1009            self.overlay_picks.Reset()
1010            self.overlay_drag.Reset()
1011
1012        # painting is only done in idle state
1013        self.refresh = True
1014   
1015    def OnPaint( self, evt ):
1016        "traceCnvas: redraw event."
1017        if self._bitmap:
1018            dc = wx.BufferedPaintDC(self, self._bitmap)
1019
1020    # Usually called via message system.
1021    def OnNotifyTimeAxis(self, orientation):
1022        """
1023        traceCanvas:
1024        Handle changes of time axis mode (absolute vs. relative time
1025        axis).
1026        """
1027        recent = self.relativeAxis
1028        self.relativeAxis = orientation == 1
1029        if recent != self.relativeAxis:
1030            # BAD!
1031            self.OnSize(True, renewOverlay=True)
1032
1033    def OnNotifyTraceOrder(self, order):
1034        """
1035        traceCanvas:
1036        Handle changes of trace plotting mode (horizontal vs. vertical
1037        modes).
1038        """
1039        recent = self.traceOrder
1040        self.traceOrder = order
1041        if recent != self.traceOrder:
1042            # reset display height
1043#            msgs.sendMessage('notify.tracecount', count=len(self.traces),
1044#                                                                    reset=True)
1045#            msgs.sendMessage('ui.change.displayheight',
1046#                                                       height=len(self.traces))
1047            self._setup()
1048            # BAD: setting evt=True
1049            self.OnSize(True, renewOverlay=True)
1050
1051    def OnNotifyHeight(self, height):
1052        """
1053        traceCanvas: handle changes of display height.
1054        """
1055        if self.traceOrder in [0, 1]:
1056            self.space = self.GetClientSize()[1]
1057        elif self.traceOrder in [2, 3]:
1058            self.space = self.GetClientSize()[0]
1059
1060        if height != len(self.traces):
1061            self.space *= len(self.traces) / float(height)
1062
1063        self._setup()
1064        self.OnSize(None)
1065
1066    def OnNotifyPhaseName(self, name):
1067        "traceCanvas: ?"
1068        self.phasename = name
1069
1070    # helper functions
1071    #@timeit
1072    def ScreenToRelativeTime(self, x, y):
1073        """
1074        traceCanvas: Returns relative time inside screen.
1075        """
1076        x, y = self.CalcUnscrolledPosition((x,y))
1077        # horizontal display
1078        if self.traceOrder in [0, 1]:
1079            timepos = x
1080        # vertical display
1081        elif self.traceOrder in [2, 3]:
1082            timepos = y
1083
1084        # horizontal
1085        if self.traceOrder in [0, 1]:
1086            pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
1087        # vertical
1088        elif self.traceOrder in [2, 3]:
1089            pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
1090
1091        # horizontal plotting
1092        if self.traceOrder in [0, 1]:
1093            timepos -= STATION_INFO + MARGINS[1]
1094        # vertical plotting
1095        elif self.traceOrder == 2:
1096            if self.relativeAxis:
1097                timepos -= length + MARGINS[0]
1098            else:
1099                timepos -= pixel_width + MARGINS[0]
1100            timepos *= -1
1101        elif self.traceOrder == 3:
1102            timepos -= STATION_INFO
1103
1104        if self.timewindow[1] is None:
1105            duration = self.relend - self.relstart
1106            start = self.relstart
1107        else:
1108            duration = self.timewindow[1] - self.timewindow[0]
1109            start = self.timewindow[0]
1110
1111        t = timepos * duration / pixel_width + start
1112
1113        return t
1114
1115    #@timeit
1116    def ScreenToTraceAndTime(self, x, y):
1117        """
1118        traceCanvas:
1119        Returns trace instance and time code derived from cursor
1120        position inside graphical panel.
1121        """
1122        x, y = self.CalcUnscrolledPosition((x,y))
1123        # horizontal display
1124        if self.traceOrder in [0, 1]:
1125            tracepos, timepos = y, x
1126        # vertical display
1127        elif self.traceOrder in [2, 3]:
1128            tracepos, timepos = x, y
1129
1130        # get trace under cursor
1131        trace = None
1132        theight = self.traceheight // 2
1133        for t in self.traces:
1134            shx_graphics = t.get_graphics()
1135            if shx_graphics == None:
1136                continue
1137            if 'midpoint' not in shx_graphics:
1138                continue
1139            if shx_graphics.midpoint == None:
1140                continue
1141            if tracepos >= shx_graphics.midpoint - theight and \
1142                tracepos <= shx_graphics.midpoint + theight:
1143                trace = t
1144                break
1145
1146        # get time code
1147        if trace and self.relativeAxis:
1148            start = trace.stats.starttime
1149            end = trace.stats.endtime
1150            shx_graphics = trace.get_graphics()
1151            if shx_graphics != None:
1152                try:
1153                    pixel_width = shx_graphics.PlotPixels
1154                except:
1155                    # during redraw PlotPixels may not be set yet
1156                    return (None,None)
1157            # only needed for vertical plotting
1158            length = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
1159        else:
1160            # global time axis
1161            start = self.start
1162            end = self.end
1163            # horizontal
1164            if self.traceOrder in [0, 1]:
1165                pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
1166            # vertical
1167            elif self.traceOrder in [2, 3]:
1168                pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
1169
1170        if self.timewindow[1] is None:
1171            duration = end - start
1172        else:
1173            duration = self.timewindow[1] - self.timewindow[0]
1174            start += self.timewindow[0]
1175
1176        # horizontal plotting
1177        if self.traceOrder in [0, 1]:
1178            timepos -= STATION_INFO + MARGINS[3]
1179        # vertical plotting
1180        elif self.traceOrder == 2:
1181            if self.relativeAxis:
1182                timepos -= length + MARGIN
1183            else:
1184                timepos -= pixel_width + MARGIN
1185            timepos *= -1
1186        elif self.traceOrder == 3:
1187            timepos -= STATION_INFO
1188
1189        t = timepos * duration / pixel_width
1190        timestamp = start + t
1191       
1192        # if trace is shifted, take t-origin into account
1193        if trace:
1194            timestamp -= trace.get_info('t-origin')
1195
1196        # do not return trace if timestamp is outside, region is enlarged
1197        # by one pixel.
1198        pixel_duration = duration/pixel_width
1199        self.pixel_duration = pixel_duration
1200        if trace and (trace.stats.starttime > timestamp + pixel_duration or \
1201                             trace.stats.endtime + pixel_duration < timestamp):
1202            trace = None
1203
1204        return trace, timestamp
1205
1206    #@timeit
1207    def TraceAndTimeToScreen(self, trace, time):
1208        """
1209        traceCanvas:
1210        Return x, y from given trace (midpoint) and time code.
1211        """
1212        if trace is None:
1213            return (None,None)
1214        shx_graphics = trace.get_graphics()
1215        if shx_graphics == None:
1216            return (None,None)
1217        if shx_graphics.midpoint == None:
1218            return (None,None)
1219
1220        # horizontal
1221        if self.traceOrder in [0, 1]:
1222            pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
1223        # vertical
1224        elif self.traceOrder in [2, 3]:
1225            pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
1226
1227        zerotime = trace.stats.starttime - trace.get_info("t-origin")
1228        reltime = time - zerotime
1229       
1230        if self.timewindow[1] == None:
1231            start = self.relstart
1232            end = self.relend
1233        else:
1234            start = self.timewindow[0]
1235            end = self.timewindow[1]
1236       
1237        # relative position inside trace window
1238        relpos = (reltime - start) / (end - start) * pixel_width
1239
1240        # horizontal
1241        if self.traceOrder in [0, 1]:
1242            x, y = self.CalcScrolledPosition((
1243                relpos + STATION_INFO + MARGINS[3],
1244                shx_graphics.midpoint
1245            ))
1246        # vertical
1247        elif self.traceOrder == 2:
1248            x, y = self.CalcScrolledPosition(
1249                     (shx_graphics.midpoint, self.height - relpos - STATION_INFO))
1250        elif self.traceOrder == 3:
1251            x, y = self.CalcScrolledPosition(
1252                                   (shx_graphics.midpoint, relpos + STATION_INFO))
1253        return x, y
1254   
1255
1256    #@timeit
1257    def _setup(self):
1258        "traceCanvas: helper routine."
1259        w, h = self.GetClientSize()
1260        if self.traceOrder in [0, 1]:
1261            if self.space is None:
1262                self.space = h
1263            self.SetVirtualSizeWH(w, self.space)
1264            self.SetScrollRate(0, 20)
1265        elif self.traceOrder in [2, 3]:
1266            if self.space is None:
1267                self.space = w
1268            self.SetVirtualSizeWH(self.space, h)
1269            self.SetScrollRate(20, 0)
1270
1271    #@timeit
1272    def _captureMouse(self, evt):
1273        "traceCanvas: helper."
1274        self.CaptureMouse()
1275
1276        ## create overlay
1277        #dc = wx.ClientDC(self)
1278        #odc = wx.DCOverlay(self.overlay_drag, dc)
1279        #odc.Clear()
1280
1281        self.dragStart = evt.GetPosition()
1282        self._debug("mouse captured at", self.dragStart)
1283
1284    #@timeit
1285    def _releaseMouse(self, evt):
1286        "traceCanvas: helper."
1287        self.ReleaseMouse()
1288
1289        ## restore view
1290        #dc = wx.ClientDC(self)
1291        #odc = wx.DCOverlay(self.overlay_drag, dc)
1292        #odc.Clear()
1293        #del odc
1294        self.overlay_drag.Reset()
1295
1296        self._debug("mouse released")
1297
1298    #@timeit
1299    def _drawPicksM(self):
1300        """
1301        traceCanvas: draw picks.
1302        """
1303        phaselist = PhaseList()
1304        _picks = {}
1305        for trc in self.traces:
1306            sname = "%s.%s" % (trc.stats.network,trc.stats.station)
1307            for phase in phaselist.getPhaseList(sname):
1308                if phase.comp and trc.stats.channel[-1] != phase.comp:
1309                    continue
1310                pcol = phaselist.picktypeColor( phase.picktype )
1311                if pcol not in _picks.keys():
1312                    _picks[pcol] = []
1313                ppos = self.TraceAndTimeToScreen(trc,phase.picktime)
1314                if ppos[0] != None:
1315                    _picks[pcol].append( (phase.name,ppos) )
1316
1317        if not _picks and not self.ZoomWindow:
1318            return
1319
1320        dc = wx.MemoryDC(self._bitmap)   # mdc
1321        #dc = wx.ClientDC(self)
1322        dc.SetBrush(wx.TRANSPARENT_BRUSH)
1323        dc.SetFont( wx.Font( 8, wx.DEFAULT, wx.NORMAL, wx.NORMAL ) )
1324
1325        th2 = self.traceheight / 2 - 2
1326        # draw picks
1327        for pcolor in _picks.keys():
1328            dc.SetPen(wx.Pen(pcolor, 1))
1329            dc.SetTextForeground(pcolor)
1330            for pick in _picks[pcolor]:
1331                if self.traceOrder in [0, 1]:
1332                    dc.DrawLine(
1333                        pick[1][0], pick[1][1] - th2,
1334                        pick[1][0], pick[1][1] + th2
1335                    )
1336                    if pick[0] != '_Mark_':
1337                        dc.DrawText(pick[0], pick[1][0] + 2, pick[1][1] - th2 - 10)
1338                elif self.traceOrder in [2, 3]:
1339                    dc.DrawLine(
1340                        pick[1][0] - th2, pick[1][1],
1341                        pick[1][0] + th2, pick[1][1],
1342                    )
1343                    if pick[0] != '_Mark_':
1344                        dc.DrawRotatedText(pick[0], pick[1][0] - th2 + 2,
1345                                                            pick[1][1] - 2, 90)
1346        del dc          # mdc
1347        self.Refresh()  # mdc
1348        self.Update()   # mdc
1349
1350    def _drawZoomWindow( self ):
1351        #draw zoom window
1352        if self.ZoomWindow:
1353            trace, start, end = self.ZoomWindow
1354            if  trace is not None:
1355                shx_graphics = trace.get_graphics()
1356                if shx_graphics == None:
1357                    return
1358                start = self.TraceAndTimeToScreen(trace, start)
1359                end = self.TraceAndTimeToScreen(trace, end)
1360                self._debug("draw zoom window")
1361                self.__zoombox( start, end, shx_graphics.midpoint,
1362                    color="Blue")
1363            else:
1364                self.zoomwdwwidth = 0
1365                self.ZoomWindow = None
1366                if plotter:
1367                    plotter.setZoomWindow( None, None, None )
1368                self.__zoombox( None, None, None )
1369
1370
1371    def _drawSinglePick( self, trace, tracetime, color=None ):
1372        "traceCanvas: Just for quick response to the user."
1373        pname = self.last_defined_phase
1374        if not pname:
1375            pname = "NewPhase"
1376        if color == None:
1377            color = 'red'
1378        dc = wx.ClientDC(self)
1379        dc.SetPen(wx.Pen(color, 1))
1380        dc.SetTextForeground(color)
1381        dc.SetFont( wx.Font( 8, wx.DEFAULT, wx.NORMAL, wx.NORMAL ) )
1382        th2 = self.traceheight / 2 - 2
1383        x, y, = self.TraceAndTimeToScreen(trace,tracetime)
1384        dc.DrawLine( x, y-th2, x, y+th2 )
1385        if pname != '_Mark_':
1386            dc.DrawText( pname, x+2, y-th2-10 )
1387       
1388
1389    #@timeit
1390    def _drawTraces(self):
1391        """
1392        traceCanvas:
1393        Do the real plotting of waveforms including station text and
1394        time scale.
1395        """
1396        # Get dimensions of the drawing area and number of traces to plot.
1397        # We cannot use the Screen module because of loop imports.
1398        if self._timewindow[1] is None:
1399            timewindow = get_runtime("extend")
1400        else:
1401            timewindow = self._timewindow
1402        self.width, self.height = self.GetVirtualSize()
1403        numTraces = len(self.traces) - Overlays.numberOfOverlays()
1404        if displayselect:
1405            dsptmp = displayselect[1]-displayselect[0]+1
1406            if dsptmp < numTraces:
1407                numTraces = dsptmp
1408        globalnorm = self._normtype.startswith("A")
1409
1410        # Delete zoom window if number of traces has changed.
1411        # -> i.e. not drawn in this redraw.
1412        if self.last_number_of_traces != numTraces and self.ZoomWindow:
1413            self.ZoomWindow = None
1414            if plotter:
1415                plotter.setZoomWindow( None, None, None )
1416        self.last_number_of_traces = numTraces
1417        self._drawXor( 'clear' )
1418
1419        # How much space is left for each trace:
1420        #    theight: height of trace space in pixel units
1421        #    pltwidth: width of trace space in pixel units
1422        # Compute trace height (theight) as float, otherwise trace positioning
1423        # is weird when many traces on screen.
1424        if self.traceOrder in [0, 1]:
1425            if numTraces:
1426                theight = float(self.height - TIMESCALE - MARGINS[0] \
1427                    - MARGINS[2]) / numTraces
1428            else:
1429                theight = float(self.height - TIMESCALE)
1430            pltwidth = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
1431        elif self.traceOrder in [2, 3]:
1432            if numTraces:
1433                theight = (self.width - TIMESCALE - MARGINS[1] \
1434                    - MARGINS[3]) / numTraces
1435            theight = self.width - TIMESCALE
1436            pltwidth = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
1437        else:
1438            raise ValueError("unknown trace order %d" % self.traceorder)
1439       
1440        # Trace height used in scaling routines,
1441        #   make it visible to other methods.
1442        self.traceheight = theight
1443
1444        # Bitmap holding the final figure, canvas->DC drawing to this bitmap.
1445        self._bitmap = wx.EmptyBitmap(self.width, self.height)
1446        canvas = wx.MemoryDC(self._bitmap)
1447        canvas.SetBrush(wx.TRANSPARENT_BRUSH)
1448        canvas.Clear()
1449        canvas.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
1450
1451        # If nothing to display, clear eand exit.
1452        if numTraces == 0:
1453            # just clearing the screen
1454            dc = wx.AutoBufferedPaintDCFactory(self)
1455            dc.Clear()
1456            return
1457
1458        if self.traceOrder == 0:
1459            trcs = self.traces[::-1]
1460        elif self.traceOrder in [1, 2, 3]:
1461            trcs = self.traces
1462       
1463        # Should traces overlap or stay within their y-ranges?
1464        # Overlapping traces: create taller bitmaps than computed before
1465        # in theight (zoomed up by a zoom factor). When the trace-bitmaps
1466        # are copied to the screen buffer, they overlap each other.
1467        overlapping_traces = get_runtime( "overlapping_traces", True )
1468
1469        # Init overlays positions. Overlays change the trace counting, since
1470        # we need less trace positions.
1471        Overlays.initPlot( len(self.traces), self.traceOrder )
1472
1473        # Loop over traces.
1474        plotidx = 0
1475        for trccnt,t in enumerate(trcs):
1476            if getattr(t, "shx_graphics", None) is None:
1477                # Trace not yet established.
1478                continue
1479            if displayselect:
1480                trcidx = t.index(True)
1481                if trcidx < displayselect[0] or trcidx > displayselect[1]:
1482                    shx_graphics = t.get_graphics()
1483                    if shx_graphics != None:
1484                        shx_graphics.midpoint = None
1485                    continue
1486            if not self.relativeAxis:
1487                start = self.start
1488                end = self.end
1489            else:
1490                start = t.stats.starttime
1491                end = start + self.maxDuration
1492            try:
1493                tzoom = t.stats.sh.ZOOM
1494            except:
1495                tzoom = 1.
1496
1497            # zheight is the (integer) height of the drawing box, possibly
1498            # zoomed by a factor if overlapping_traces is selected.
1499            if overlapping_traces:
1500                # Increased zheight produces overlapping of traces on display.
1501                zheight = int( theight * tzoom )
1502                try:
1503                    prepzoom = 1./tzoom
1504                except:
1505                    prepzoom = 1.
1506            else:
1507                zheight = int( theight )
1508                prepzoom = 0.9  # why not 1.?
1509           
1510            # Limit zheight to screen height, otherwise we run into memory
1511            # problems.
1512            if zheight > self.height:
1513                zheight = self.height
1514
1515            # Amplitude scaling factor within bitmap.
1516            norm = globalnorm and self.maxAmplitude or self._normtype[1]
1517           
1518            # After eliminating t.prepare_image_data (too slow), this
1519            # shx_graphics-stuff is probably not needed any more.
1520            shx_graphics = t.get_graphics()
1521            if shx_graphics == None:
1522                continue
1523
1524            # Create an emtpy bitmap for this trace, but only if the pixel
1525            # height is larger than 4. Doesn't make sense to put time and
1526            # CPU into figures with only 2 pixels height. Draw simple line
1527            # instead.
1528            if zheight > 4:
1529                if self.traceOrder in [0, 1]:
1530                    bitmap = wx.EmptyBitmap(pltwidth, zheight)
1531                elif self.traceOrder in [2, 3]:
1532                    bitmap = wx.EmptyBitmap(zheight, pltwidth)
1533            else:
1534                bitmap = None
1535            if bitmap is not None:
1536                # buffer to draw in
1537                dbuffer = wx.MemoryDC(bitmap)
1538                #dbuffer = dbuffer
1539                dbuffer.SetBrush(wx.TRANSPARENT_BRUSH)
1540                dbuffer.Clear()
1541                dbuffer.SetPen(wx.Pen((45,45,45), 1))
1542            else:
1543                dbuffer = None
1544
1545            # Time position of trace.
1546            _shift = t.get_info("t-origin") - timewindow[0]
1547            if _shift < 0.:
1548                _shift = 0.
1549            # On positive shifts the bitmap is moved out of the screen
1550            # to the right. On negative shifts the bitmap is recomputed
1551            # and always starts at 0. Fails if the bitmap move completely
1552            # out to the left. Bitmap should be cleared in this case.
1553
1554            # calculate offset to global time scale
1555            plotStartSec = t.stats.starttime + _shift - start
1556            plotoffset = 0
1557            if plotStartSec or not self.relativeAxis:
1558                # re-calculate offset
1559                portion = plotStartSec / (timewindow[1] - timewindow[0])
1560                plotoffset = portion * pltwidth
1561
1562            # Get drawing attributes, color and other styles.
1563            attrib = t.get_info("attrib")
1564            try:
1565                style = get_style(attrib)
1566            except KeyError:
1567                style = get_default_attrib(t.get_info("METASTATUS"))
1568            cmode = None
1569            try:
1570                color = map(int, style.color.split(','))
1571                cmode = 'i'
1572            except:
1573                try:
1574                    color = map(float, style.color.split(','))
1575                    cmode = 'f'
1576                except:
1577                    color = style.color
1578                    cmode = 'o'
1579            if cmode == 'i':
1580                if 0 < max(color) <= 1:
1581                    # e.g. 1,0,0 means red, multiply with 255
1582                    color = [255*x for x in color]
1583            elif cmode == 'f':
1584                color = map( int, [255*x for x in color] )
1585            linestyle = getattr(wx, style.linestyle.upper())
1586
1587            # Draw seismogram into trace bitmap.
1588            if dbuffer is not None:
1589                dbuffer.SetPen(wx.Pen(color, style.linewidth, linestyle))
1590                dbuffer.BeginDrawing()
1591                dbuffer.DrawLines(
1592                    self.prepImage(t,pltwidth,zheight,timewindow,prepzoom,norm)
1593                )
1594                dbuffer.EndDrawing()
1595
1596            # Copy trace bitmap to canvas
1597            if dbuffer is not None:
1598                if self.traceOrder in [0, 1]:
1599                    if overlapping_traces:
1600                        fac = plotidx + 0.5 - 0.5*tzoom
1601                    else:
1602                        fac = plotidx
1603                    plotpos = Overlays.plotPos( plotidx+1, fac * theight + MARGINS[0])
1604                    canvas.Blit(
1605                        plotoffset + STATION_INFO + MARGINS[3], 
1606                        plotpos, 
1607                        pltwidth,
1608                        zheight,
1609                        dbuffer,
1610                        0,
1611                        0,
1612                        wx.AND
1613                    )
1614                elif self.traceOrder == 2:
1615                    canvas.Blit(plotidx * theight, -plotoffset + MARGIN, zheight,
1616                                                pltwidth, dbuffer, 0, 0, wx.AND)
1617                elif self.traceOrder == 3:
1618                    canvas.Blit(plotidx * theight, STATION_INFO + plotoffset, zheight,
1619                                                pltwidth, dbuffer, 0, 0, wx.AND)
1620
1621            # Put labels on traces.
1622            # Trace numbering
1623            if self.traceOrder in [1, 2, 3]:
1624                idx = trccnt + 1
1625            elif self.traceOrder == 0:
1626                idx = -trccnt + len(self.traces)
1627            # Currently no choice on type of label, just station and component.
1628            txt = "%d: %s %s" % (idx, t.stats.station, t.stats.channel[-1])
1629            canvas.SetPen(wx.Pen('Grey', 1, wx.LONG_DASH))
1630            # Draw labels and helper lines (zero lines)
1631            w, h, _, _ = canvas.GetFullTextExtent(txt)
1632            shx_graphics.midpoint = tmp = plotidx * theight + theight//2 + MARGINS[0]
1633            if self.traceOrder in [0, 1]:
1634                canvas.DrawText(txt, 5 + MARGINS[3], tmp - h // 2)
1635                canvas.DrawLine(STATION_INFO + MARGINS[3], tmp,
1636                    self.width - MARGINS[1], tmp)
1637            elif self.traceOrder == 2:
1638                canvas.DrawRotatedText(txt, tmp - h // 2, self.height - MARGIN, 90)
1639                canvas.DrawLine(shx_graphics.midpoint, MARGIN,
1640                    tmp, self.height - STATION_INFO)
1641            elif self.traceOrder == 3:
1642                canvas.DrawRotatedText(txt, tmp - h // 2,
1643                                    STATION_INFO - (STATION_INFO - w) // 2, 90)
1644                canvas.DrawLine(tmp, STATION_INFO, tmp, self.height - MARGIN)
1645
1646            plotidx += 1
1647            # End of trace loop
1648
1649        # Draw time axis.
1650        canvas.SetPen(wx.Pen('Black', 1))
1651        if self.relativeAxis:
1652            start, end = timewindow
1653
1654        if self.traceOrder in [0, 1]:
1655            PARTS = 5.  # axis split into X parts
1656            length = self.width - MARGINS[1] - MARGINS[3] - STATION_INFO  # pixel length of time axis
1657            fixpos = self.height - TIMESCALE - MARGINS[2]  # here: y coordinate
1658            varpos_start = STATION_INFO + MARGINS[3] # here: x start
1659        elif self.traceOrder in [2, 3]:
1660            PARTS = 4.
1661            length = self.height - MARGIN - STATION_INFO
1662            fixpos = self.width - TIMESCALE + 10  # here: x coordinate
1663            if self.traceOrder == 2:
1664                varpos_start = MARGIN  # here: y start
1665            elif self.traceOrder == 3:
1666                varpos_start = STATION_INFO
1667
1668        chunk_t, chunk, labeloffset, pixoffset, PARTS \
1669            = self.niceNumbers( start, end, length, PARTS )
1670        chunk2 = chunk / 5.
1671
1672        # basic time axis line
1673        if self.traceOrder in [0, 1]:
1674            canvas.DrawLine(varpos_start, fixpos,
1675                                                 length + varpos_start, fixpos)
1676        elif self.traceOrder in [2, 3]:
1677            canvas.DrawLine(fixpos, varpos_start,
1678                                                 fixpos, varpos_start + length)
1679
1680        # sections with ticks and time string
1681
1682        try:
1683            prec = abs(int(round(1 / math.log10(chunk_t))))
1684        except:
1685            prec = 1
1686        mask = "%%.%uf" % prec
1687        for i in range(int(PARTS)):
1688            if isinstance(start, UTCDateTime):
1689                timecode = labeloffset + start + i * chunk_t
1690                txt = timecode.strftime("%H:%M:%S.") + \
1691                                             str(timecode.microsecond//1000)
1692            else:
1693                txt = mask % (labeloffset + start + i * chunk_t)
1694
1695            tw = canvas.GetFullTextExtent(txt)[0]
1696
1697            if self.traceOrder in [0, 1]:
1698                varpos_current = pixoffset + varpos_start + i * chunk
1699                # time string
1700                canvas.DrawText(txt, varpos_current - tw / 2,
1701                                                fixpos + TIMESCALE_OFFSET_TEXT)
1702                # major tick
1703                canvas.DrawLine(varpos_current, fixpos,
1704                                varpos_current, fixpos + TIMESCALE_TICKS_MAJOR)
1705                # minor ticks
1706                for j in range(1, 5):
1707                    canvas.DrawLine(
1708                        varpos_current + j * chunk2,
1709                        fixpos,
1710                        varpos_current + j * chunk2,
1711                        fixpos + TIMESCALE_TICKS_MINOR
1712                    )
1713            elif self.traceOrder in [2, 3]:
1714                if self.traceOrder == 2:
1715                    varpos_current = pixoffset \
1716                        + varpos_start + (PARTS - i) * chunk
1717                elif self.traceOrder == 3:
1718                    varpos_current = pixoffset + varpos_start + i * chunk
1719                canvas.DrawRotatedText(txt, fixpos + TIMESCALE_OFFSET_TEXT,
1720                                                   varpos_current + tw / 2, 90)
1721                canvas.DrawLine(fixpos, varpos_current,
1722                                fixpos + TIMESCALE_TICKS_MAJOR, varpos_current)
1723                for j in range(1, 5):
1724                    if self.traceOrder == 2:
1725                        j -= 5
1726                    canvas.DrawLine(
1727                        fixpos,
1728                        varpos_current + j * chunk2,
1729                        fixpos + TIMESCALE_TICKS_MINOR,
1730                        varpos_current + j * chunk2
1731                     )
1732
1733        if self.do_screenshot:
1734            self.save_shot(canvas)
1735            self.do_screenshot = False
1736        # detach
1737        #ks canvas.SelectObject(wx.NullBitmap)
1738        del canvas
1739        self.Refresh()
1740        self.Update()
1741
1742    def prepImage( self, trc, width, height, timewindow, zoom, norm ):
1743        "traceCanvas: return polygon ready for plotting via wx."
1744
1745        windowstart, windowend = timewindow
1746        try:
1747            pt = trc.slice_relative(windowstart, windowend)
1748        except:
1749            return []
1750           
1751        # scaling in time direction
1752        duration_total = windowend - windowstart
1753        duration_trace = pt.stats.endtime - pt.stats.starttime
1754        pixel_width = duration_trace * width / duration_total
1755       
1756        # save pixel_width (but dont' really understand)
1757        shx_graphics = trc.get_graphics()
1758        if shx_graphics != None:
1759            shx_graphics.PlotPixels = pixel_width
1760
1761        # make sure, norm is a number
1762        if hasattr(norm, "lower"):
1763            norm = norm.upper()[0]
1764            if norm == "W":
1765                norm = abs(pt.max())
1766            elif norm == "T":
1767                norm = self.max()
1768            else:
1769                raise ValueError("Invalid input for normation!")
1770       
1771        # scaling in amplitude direction
1772        try:
1773            trczoom = trc.stats.sh.ZOOM
1774        except:
1775            trczoom = 1.0
1776        amplscale = 1. / norm * height / 2 * zoom * trczoom
1777        return zip(
1778            np.linspace( 0., pixel_width, len(pt.data) ),
1779            pt.data*amplscale + height/2
1780        )
1781
1782    #@timeit
1783    def save_shot(self, dc):
1784        """
1785        traceCanvas: Save PNG screen shot (PS later)
1786        """
1787        size = dc.Size
1788
1789        shot = wx.EmptyBitmap(size.width, size.height)
1790        shot_dc = wx.MemoryDC()
1791        shot_dc.SelectObject(shot)
1792
1793        shot_dc.Blit(0, 0, size.width, size.height, dc, 0, 0)
1794        shot_dc.SelectObject(wx.NullBitmap)
1795        img = shot.ConvertToImage()
1796        img.SaveFile(self.do_screenshot, wx.BITMAP_TYPE_PNG)
1797        log_message("info", "screen dump saved in: %s" % self.do_screenshot)
1798   
1799    #@timeit
1800    def niceNumbers( self, start, end, pixwidth, parts ):
1801        "traceCanvas: nice numbers for time axis."
1802        num = (end - start) / parts
1803        pixscaling = pixwidth / (end-start)
1804        tmp = 10**int(math.log10(num))  # integer power of 10
1805        nn = tmp   # best nice number is either this number or *2, *5, *10
1806        dist = abs(tmp-num)
1807        for i in (2,5,10):
1808            ndist = abs(num - i*tmp)
1809            if ndist < dist:
1810                nn = i*tmp
1811                dist = ndist
1812        labelstart = nn*int(start/nn)
1813        if (labelstart-start) < -1.e-5:
1814            labelstart += nn
1815        repeat = int((end-labelstart)/nn) + 1
1816        pixspan = nn * pixscaling
1817        pixoffset = (labelstart-start) * pixscaling
1818        labeloffset = labelstart - start
1819        #print nn, labelstart, pixoffset, repeat, '--', num, '.', start, end
1820        return (nn, pixspan, labeloffset, pixoffset, repeat)
1821
1822    @staticmethod
1823    def _debug(*args):
1824        "traceCanvas: helper."
1825        log_message('debug.wx', " ".join([str(i) for i in args]))
1826        #print "dbg:", " ".join([str(i) for i in args])
1827
1828    #@timeit
1829    def _resetTimeRange(self, **kwargs):
1830        """
1831        traceCanvas:
1832        Gather information about traces. Called on redraw (at idle state).
1833        """
1834        mint = UTCDateTime() # now
1835        maxt = UTCDateTime(0) # 1970
1836        minrel = 1e20
1837        maxrel = 0
1838        maxDuration = 0
1839        amplitude = 0
1840
1841        for t in self.traces:
1842            s = t.get_info("relstart")
1843            e = t.get_info("relend")
1844            if s < minrel:
1845                minrel = s
1846            if e > maxrel:
1847                maxrel = e
1848            if t.stats.starttime < mint:
1849                mint = t.stats.starttime
1850            if t.stats.endtime > maxt:
1851                maxt = t.stats.endtime
1852            # needed for relative time axis
1853            l = e - s
1854            if l > maxDuration:
1855                maxDuration = l
1856            # normation
1857            # no time window set or normtype forces full traces
1858            #if self._timewindow[1] is None or self._normtype.startswith("A"):
1859            if self._timewindow[1] is None:
1860                data = t.data
1861            else:
1862                try:
1863                    data = t.get_datawindow(*self._timewindow)
1864                except:
1865                    continue
1866            t_ampl = max( abs(data.max()), abs(data.min()) )
1867            if t_ampl > amplitude:
1868                amplitude = t_ampl
1869
1870        self.start = mint
1871        self.relstart = minrel
1872        self.end = maxt
1873        self.relend = maxrel
1874        self.maxDuration = maxDuration
1875        self.maxAmplitude = amplitude
1876        recent = self.timewindow
1877        self.timewindow = get_runtime("timewindow")
1878        if recent != self.timewindow:
1879            # time window changed, delete zoom box
1880            self.ZoomWindow = None
1881            if plotter:
1882                plotter.setZoomWindow( None, None, None )
1883
1884   
1885    #@timeit
1886    def __zoombox(self, start, end, midpoint, color="DARKORANGE", size=1,
1887        dc=None):
1888        "traceCanvas: draw zoom box in XOR mode."
1889        if start == None:
1890            self._drawXor( 'rect', (None,None,None,None), dc )
1891            return
1892        if midpoint == None:
1893            return
1894        marginfactor = 0.3
1895        mf2 = (1 - marginfactor) * 2
1896        th2 = self.traceheight / 2 + 1
1897        offset = midpoint - th2 - self.Scrolled
1898        if self.traceOrder in [0, 1]:
1899            self._drawXor( 'rect', (start[0], offset + th2 * marginfactor,
1900                end[0] - start[0], th2 * mf2 + 1), dc )
1901        elif self.traceOrder in [2, 3]:
1902            self._drawXor( 'rect', (offset + th2 * marginfactor, start[1],
1903                th2 * mf2, end[1] - start[1]), dc )
1904   
1905    def moveZoomWindow( self, direction, span=None ):
1906        "traceCanvas: move zoom window."
1907        if not self.ZoomWindow:
1908            return
1909        trace, start, end = self.ZoomWindow
1910        boxwidth = end - start
1911        if direction == 'right':
1912            if span == None:
1913                tmove = boxwidth/2.
1914            else:
1915                tmove = span
1916            start += tmove
1917            end += tmove
1918        elif direction == 'left':
1919            tmove = boxwidth/2.
1920            start -= tmove
1921            end -= tmove
1922        elif direction == 'shrink':
1923            if self.timewindow[1] is None:
1924                duration = self.relend - self.relstart
1925            else:
1926                duration = self.timewindow[1] - self.timewindow[0]
1927            limit = duration/100.
1928            if boxwidth > limit:
1929                boxwidth *= 0.9
1930                end = start + boxwidth
1931                self.zoomwdwwidth = boxwidth/self.pixel_duration
1932        elif direction == 'grow':
1933            if self.timewindow[1] is None:
1934                duration = self.relend - self.relstart
1935            else:
1936                duration = self.timewindow[1] - self.timewindow[0]
1937            limit = duration
1938            if boxwidth < limit:
1939                boxwidth *= 1.1
1940                end = start + boxwidth
1941                self.zoomwdwwidth = boxwidth/self.pixel_duration
1942        elif direction == 'up':
1943            newtrc = traces_from_list( "%d" % (trace.index(True)+1) )
1944            if newtrc and len(newtrc) == 1:
1945                trace = newtrc[0]
1946            else:
1947                return
1948        elif direction == 'down':
1949            newidx = trace.index(True)-1
1950            if newidx == 0:
1951                return
1952            newtrc = traces_from_list( "%d" % newidx )
1953            if newtrc and len(newtrc) == 1:
1954                trace = newtrc[0]
1955            else:
1956                return
1957        else:
1958            return
1959        self.ZoomWindow = trace, start, end
1960        plotter.setZoomWindow( trace.index(True), _absToRelTime(trace,start),
1961            _absToRelTime(trace,end) )
1962        self._drawZoomWindow()
1963        plotter.magnify.canvas.magnifyTrace( trace, boxwidth=boxwidth,
1964            boxend=end )
1965
1966
1967class magnifyWindow(wx.Frame):
1968
1969    def __init__( self, parent, title, size=None, position=None ):
1970        "magnifyWindow: init method."
1971        if size == None or position == None:
1972            px, py = plotter.GetPosition()
1973            pw, ph = plotter.GetSize()
1974            if size == None:
1975                size = (pw,ph/5)
1976            if position == None:
1977                position = (px,0)
1978        wx.Frame.__init__(self, parent, title=title, size=size,
1979            style=wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE |
1980            wx.FRAME_FLOAT_ON_PARENT)
1981        #self.SetPosition( position )
1982        self.Move( get_runtime("wdwpos_magnify") )
1983        self.canvas = magnifyCanvas( self )
1984        self.Disable()
1985        self.Show()
1986        self.Enable()
1987
1988
1989class plotterWindow(wx.Frame):
1990    """
1991    Basic program frame including menu.
1992   
1993    Shortcuts
1994           a(FilterWA) b(Beam) d(DelTimeWdw) g(PhasePg) l(locate) m(mlSingle)
1995           n(PhasePn) p(PhaseP) s(SetTimeWdw) u(Undo Filter) w(planewave)
1996           0(PrevEvent) 1(NextEvent) 2(PhaseSg) 8(Filter 1-8Hz) >(zoom->right)
1997           <(zoom->left)
1998    Ctrl-  f(FK) l(FilterSROLP) n(Norm) o(Overlapping) q(Quit) s(SaveT&P)
1999           w(FilterWWSSNSP) y(SortByDistance)
2000    """
2001    def __init__(self, parent, title, size=(640,280), position=(50,100)):
2002        "plotterWindow: init method."
2003        # save instance for external access
2004        global plotter
2005        plotter = self
2006        wx.Frame.__init__(self, parent, title=title, size=size,
2007              style=wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE |
2008                    wx.FRAME_FLOAT_ON_PARENT)
2009
2010        #self.SetPosition(position) # positioning doesn't work
2011        self._menuitems = {}
2012        self.addMenuEntries()
2013        self.canvas = traceCanvas(self, [])
2014        statusbar = self.CreateStatusBar()
2015        statusbar.SetBackgroundColour( shxcolor1 )
2016        self.SetStatusText("%s %s" % (NAME, VERSION))
2017        self.Disable()
2018        self.Show()
2019        self.Enable()
2020        self._user_selection = 999.
2021        self._zoomwdw = None
2022        self._seltrace = None
2023        self.flag_beam = False
2024        self.flag_norm = False
2025        self.flag_overlap = True
2026        self.pardialog = None
2027        self.magnify = None
2028        set_runtime( "wdwpos_main", position )
2029        set_runtime( "wdwsize_main", size )
2030        self.Move( position )   # this works rather than SetPosition
2031        # set defaults for magnify and parameter window
2032        mh = 151
2033        set_runtime( "wdwpos_magnify", (position[0],position[1]-mh-30) )
2034        set_runtime( "wdwsize_magnify", (size[0],mh) )
2035        set_runtime( "wdwpos_params", (position[0]+size[0]+2,position[1]-mh-30) )
2036   
2037    def _setStatusText( self, localtext ):
2038        "plotterWindow: set local status text + global statusinfotext."
2039        self.SetStatusText( localtext + 10*'\t' + statusinfotext )
2040   
2041    def openMagnifyWindow( self, e ):
2042        "plotterWindow: open magnify window."
2043        self.magnify = magnifyWindow( self, 'SHX magnify' )
2044   
2045    def showMessage( self, msg, error=False ):
2046        "plotterWindow: show message in a dialog."
2047        mode = wx.OK
2048        if error:
2049            mode |= wx.ICON_ERROR
2050        dlg = wx.MessageDialog(self, msg, "An error occured!", mode )
2051        dlg.ShowModal()
2052        dlg.Destroy()
2053       
2054    def redraw(self):
2055        "plotterWindow: redraw procedure."
2056        self.canvas.OnSize(None)
2057   
2058    def setZoomWindow( self, trace, start, end ):
2059        "plotterWindow: get new zoom window (called from other class instances."
2060        self._seltrace = trace
2061        self._zoomwdw = (start,end)
2062
2063    def addMenuEntries( self ):
2064        "plotterWindow: definition of menu entries."
2065        menubar = wx.MenuBar()
2066        menubar.SetBackgroundColour( shxcolor2 )
2067        # file menu
2068        fileMenu = wx.Menu()
2069        self.addEntry( fileMenu, 'Read Data File(s)',
2070            'Read data file(s) in one of various formats', self.OnReadDataFile )
2071        self.addEntry( fileMenu, 'Write Data File',
2072            'Write all traces into file. Specify format as extension.',
2073            self.OnWriteDataFile )
2074        fileMenu.AppendSeparator()
2075        self.addEntry( fileMenu, 'Read FDSN Webservice',
2076            'Read network and stations network/station list '\
2077            +'+ circle area from FDSN webserver',
2078            self.OnReadFdsnCombi )
2079        self.addEntry( fileMenu, 'Add stations around epicenter (FDSN-ws)',
2080            'Read stations in circle area around epicener from FDSN webserver',
2081            self.OnReadFdsnCircle )
2082        fileMenu.AppendSeparator()
2083        self.addEntry( fileMenu, 'Complete Metadata (FDSN)',
2084            'Complete metadata of traces using FDSN webserver',
2085            self.OnCompleteMeta )
2086        fileMenu.AppendSeparator()
2087        self.addEntry( fileMenu, '&Quit\tCtrl+Q', 'Quit Graphics', self.OnQuit )
2088        # display menu
2089        windowMenu = wx.Menu()
2090        self.addEntry( windowMenu, '&Set Time Window\tS',
2091            'Set Zoom window as new time window', self.OnSetTimeWindow )
2092        self.addEntry( windowMenu, '&Del Time Window\tD',
2093            'Delete time window.', self.OnDelTimeWindow )
2094        windowMenu.AppendSeparator()
2095        self.addEntry( windowMenu, '&Move Zoom Right\t>',
2096            'Move zoom window to the right (50%).', self.OnMoveZoomRight )
2097        self.addEntry( windowMenu, 'Move Zoom Left\t<',
2098            'Move zoom window to the left (50%).', self.OnMoveZoomLeft )
2099        self.addEntry( windowMenu, 'Move Zoom Up',
2100            'Move zoom window upward.', self.OnMoveZoomUp, accel=wx.WXK_UP )
2101        self.addEntry( windowMenu, 'Move Zoom Down',
2102            'Move zoom window downward.', self.OnMoveZoomDown )
2103        self.addEntry( windowMenu, 'Zoom Amplitudes Up\t+',
2104            'Zoom amplitudes up (factor 2).', self.OnAmplZoomUp )
2105        self.addEntry( windowMenu, 'Zoom Amplitudes Down\t-',
2106            'Zoom amplitudes down (factor 0.5).', self.OnAmplZoomDown )
2107        windowMenu.AppendSeparator()
2108        m = self.addEntry( windowMenu, 'Trace Scroll On',
2109            'Display at max 21 traces and enable scrolling.',
2110            self.OnTraceScrollOn )
2111        self._menuitems['TraceScrollOn'] = m
2112        m = self.addEntry( windowMenu, 'Trace Scroll Off',
2113            'Display all traces, switches off scrolling.',
2114            self.OnTraceScrollOff )
2115        self._menuitems['TraceScrollOff'] = m
2116        m = self.addEntry( windowMenu, 'Trace Scroll Up',
2117            'Scroll traces up. Use mouse wheel.', self.OnTraceScrollUp )
2118        self._menuitems['TraceScrollUp'] = m
2119        m = self.addEntry( windowMenu, 'Trace Scroll Down',
2120            'Scroll traces down. Use mouse wheel', self.OnTraceScrollDown )
2121        self._menuitems['TraceScrollDown'] = m
2122        self.addEntry( windowMenu, 'Show All Components in Magnify\t3',
2123            'Show other components in magnify window.',
2124            self.OnShowAllInMagnify )
2125        windowMenu.AppendSeparator()
2126        self.addEntry( windowMenu, '&Norm All/Single\tCtrl+N',
2127            'Normalize All Traces Together / Normalize Traces Separately',
2128            self.OnNorm )
2129        self.addEntry( windowMenu, '&Overlapping/Clipped Traces\tCtrl+O',
2130            'Traces overlap when zooming / Traces are clipped when zooming '\
2131            +'amplitudes', self.OnOverlappingTraces )
2132        # traces menu
2133        tracesMenu = wx.Menu()
2134        self.addEntry( tracesMenu, '&Demean\tCtrl+D',
2135            'Remove mean value from all traces', self.OnDemean )
2136        self.addEntry( tracesMenu, 'Sort by Distance\tCtrl+Y',
2137            'Sort traces by distance', self.OnSortByDistance )
2138        tracesMenu.AppendSeparator()
2139        self.addEntry( tracesMenu, 'Delete Selected Trace',
2140            'Delete trace with zoom window', self.OnDeleteTrace )
2141        self.addEntry( tracesMenu, 'Delete Selected Station',
2142            'Delete all traces of this station', self.OnDeleteStation )
2143        self.addEntry( tracesMenu, 'Delete Selected And Above',
2144            'Delete trace with zoom window and all above',
2145            self.OnDeleteTraceAndAbove )
2146        tracesMenu.AppendSeparator()
2147        self.addEntry( tracesMenu, 'Spectrogram',
2148            'Compute spectrogram', self.OnSpectrogram )
2149        # array menu
2150        arrayMenu = wx.Menu()
2151        self.addEntry( arrayMenu, 'FK',
2152            'Compute FK for all traces on selected time window', self.OnFK )
2153        self.addEntry( arrayMenu, '&FK (Auto Freq)\tCtrl+F',
2154            'Compute FK with automatic freq window', self.OnFKAutoFreq )
2155        self.addEntry( arrayMenu, 'Beam / Del Beam\tB',
2156            'Compute/delete beam for all traces', self.OnBeam )
2157        self.addEntry( arrayMenu, 'Plane Wave\tW',
2158            'Compute slowness and azimuth using picks', self.OnPlaneWave )
2159        self.addEntry( arrayMenu, 'Correlation Picker',
2160            'Crosscorrelate all traces with selected wavelet and pick',
2161            self.OnCorrPick )
2162        # simulate menu
2163        simulateMenu = wx.Menu()
2164        self.addEntry( simulateMenu, 'Simulate &Wood-Anderson\tA',
2165            'Simulate Wood-Anderson instrument on all traces',
2166            self.OnSimulateWoodAnderson )
2167        self.addEntry( simulateMenu, 'Simulate Wood-Anderson-1Hz',
2168            'Simulate Wood-Anderson instrument + 1Hz-HP on all traces',
2169            self.OnSimulateWoodAnderson1Hz )
2170        self.addEntry( simulateMenu, 'Simulate WWSSN-SP\tCtrl+W',
2171            'Simulate WWSSN-SP instrument on all traces', self.OnSimulateWWSSNSP )
2172        self.addEntry( simulateMenu, 'Simulate WWSSN-LP',
2173            'Simulate WWSSN-LP instrument on all traces', self.OnSimulateWWSSNLP )
2174        self.addEntry( simulateMenu, 'Simulate LRSM-SP',
2175            'Simulate WWSSN-SP instrument on all traces', self.OnSimulateLRSMSP )
2176        self.addEntry( simulateMenu, 'Simulate LRSM-LP',
2177            'Simulate WWSSN-LP instrument on all traces', self.OnSimulateLRSMLP )
2178        self.addEntry( simulateMenu, 'Simulate KIRNOS',
2179            'Simulate KIRNOS instrument on all traces', self.OnSimulateKIRNOS )
2180        self.addEntry( simulateMenu, 'Simulate SRO-&LP\tCtrl+L',
2181            'Simulate SRO-LP instrument on all traces', self.OnSimulateSROLP )
2182        simulateMenu.AppendSeparator()
2183        self.addEntry( simulateMenu, 'Filter BP 1-8Hz\t8',
2184            'Apply Butterworth bandpass 1-8Hz', self.OnFilterBP_1_8 )
2185        simulateMenu.AppendSeparator()
2186        self.addEntry( simulateMenu, 'Undo Filter\tu',
2187            'Return to original traces', self.OnSimulateUndo )
2188        # events menu
2189        eventsMenu = wx.Menu()
2190        self.addEntry( eventsMenu, 'Get Eventlist',
2191            'Retrieve event list from FDSN server', self.OnGetEventlist )
2192        self.addEntry( eventsMenu, 'Next Event\t1',
2193            'Prepare next event time from list', self.OnNextEvent )
2194        self.addEntry( eventsMenu, 'Previous Event\t0',
2195            'Prepare previous event time from list', self.OnPrevEvent )
2196        eventsMenu.AppendSeparator()
2197        self.addEntry( eventsMenu, 'Event Info Parser ...',
2198            'Read event from text line', self.OnEventInfoParser )
2199        eventsMenu.AppendSeparator()
2200        self.addEntry( eventsMenu, 'Locate Local Event\tl',
2201            'Use phase list for locating event', self.OnLocateEvent )
2202        self.addEntry( eventsMenu, 'Locate Tele Event with P',
2203            'Use P arrivals for teleseismic location', self.OnLocateTeleEvent )
2204        self.addEntry( eventsMenu, 'Plot Location',
2205            'Simple map with stations and epicenter', self.OnPlotLocation )
2206        # phase menu
2207        phaseMenu = wx.Menu()
2208        self.addEntry( phaseMenu, 'Pg\tg',
2209            'Set phase for picking to Pg', self.OnSetPhase_Pg )
2210        self.addEntry( phaseMenu, 'Pn\tn',
2211            'Set phase for picking to Pn', self.OnSetPhase_Pn )
2212        self.addEntry( phaseMenu, 'Sg\t2',
2213            'Set phase for picking to Sg', self.OnSetPhase_Sg )
2214        self.addEntry( phaseMenu, 'Sn',
2215            'Set phase for picking to Sn', self.OnSetPhase_Sn )
2216        self.addEntry( phaseMenu, 'P\tp',
2217            'Set phase for picking to P', self.OnSetPhase_P )
2218        self.addEntry( phaseMenu, 'pP',
2219            'Set phase for picking to pP', self.OnSetPhase_pP )
2220        self.addEntry( phaseMenu, 'sP',
2221            'Set phase for picking to sP', self.OnSetPhase_sP )
2222        self.addEntry( phaseMenu, 'PP',
2223            'Set phase for picking to PP', self.OnSetPhase_PP )
2224        self.addEntry( phaseMenu, 'S',
2225            'Set phase for picking to S', self.OnSetPhase_S )
2226        self.addEntry( phaseMenu, 'PKP',
2227            'Set phase for picking to PKP', self.OnSetPhase_PKP )
2228        self.addEntry( phaseMenu, 'PKPdf',
2229            'Set phase for picking to PKPdf', self.OnSetPhase_PKPdf )
2230        self.addEntry( phaseMenu, 'PKPbc',
2231            'Set phase for picking to PKPbc', self.OnSetPhase_PKPbc )
2232        self.addEntry( phaseMenu, 'PKPab',
2233            'Set phase for picking to PKPab', self.OnSetPhase_PKPab )
2234        self.addEntry( phaseMenu, 'pPKPdf',
2235            'Set phase for picking to pPKPdf', self.OnSetPhase_pPKPdf )
2236        self.addEntry( phaseMenu, 'pPKPbc',
2237            'Set phase for picking to pPKPbc', self.OnSetPhase_pPKPbc )
2238        self.addEntry( phaseMenu, 'pPKPab',
2239            'Set phase for picking to pPKPab', self.OnSetPhase_pPKPab )
2240        self.addEntry( phaseMenu, 'Use ev_phase',
2241            'Use phase name from parameter ev_phase',
2242            self.OnSetPhaseUseEvPhase )
2243        phaseMenu.AppendSeparator()
2244        self.addEntry( phaseMenu, 'Theo Phases (local)',
2245            'Compute theoretical phases for a local event',
2246            self.OnTheoPhaseLocal )
2247        self.addEntry( phaseMenu, 'Theo Phases (tele)',
2248            'Compute theoretical phases for a teleseismic event',
2249            self.OnTheoPhaseTele )
2250        self.addEntry( phaseMenu, 'Delete all Theo Phases',
2251            'Delete all theoretical phases', self.OnDeleteTheoPhase )
2252        phaseMenu.AppendSeparator()
2253        self.addEntry( phaseMenu, 'Plot Phases',
2254            'Create distance vs travel time plot', self.OnPhasePlot )
2255        # magnitude menu
2256        magnMenu = wx.Menu()
2257        self.addEntry( magnMenu, 'ml on all Trc. (analytic)',
2258            'Computes magnitude ml around picked phases at all traces up to selected',
2259            self.OnMagnMlAnalytic )
2260        self.addEntry( magnMenu, '&ml on single Trc. (analytic)\tM',
2261            'Computes magnitude ml within selection window',
2262            self.OnMagnMlAnalyticSingle )
2263        self.addEntry( magnMenu, 'ml Delete all',
2264            'Deletes all ml magnitudes', self.OnMagnMlDeleteAll )
2265        self.addEntry( magnMenu, 'ml Delete selected',
2266            'Deletes magnitude ml at selected trace',
2267            self.OnMagnMlDeleteSingle )
2268        self.addEntry( magnMenu, 'ml Plot magnitudes',
2269            'Create and show a magnitude vs distance plot.', self.OnMagnMlPlot )
2270        # control menu
2271        controlMenu = wx.Menu()
2272        self.addEntry( controlMenu, 'Save Traces/Params\tCtrl+S',
2273            'Save trace info and parameters into a recovery command file',
2274            self.OnSaveTracesAndParamsQuick )
2275        self.addEntry( controlMenu, 'Save Traces/Params as ...',
2276            'Save trace info and parameters into a recovery command file',
2277            self.OnSaveTracesAndParams )
2278        self.addEntry( controlMenu, 'Recover Traces/Params',
2279            'Recover traces and parameters from default recovery',
2280            self.OnRecoverTracesAndParamsQuick )
2281        self.addEntry( controlMenu, 'Recover Traces/Params from ...',
2282            'Recover traces and parameters from a recovery command file',
2283            self.OnRecoverTracesAndParams )
2284        controlMenu.AppendSeparator()
2285        self.addEntry( controlMenu, 'Clear All Parameters and Traces',
2286            'Deletes traces, phase, magnitudes and resets parameters.',
2287            self.OnClearAllParameters )
2288        controlMenu.AppendSeparator()
2289        self.addEntry( controlMenu, 'Open Parameter Window',
2290            'Open parameter dialog', self.OnOpenParams )
2291        self.addEntry( controlMenu, 'Open Magnify Window',
2292            'Open magnification window for traces', self.OnOpenMagnify )
2293        self.addEntry( controlMenu, 'Refresh',
2294            'Refresh display', self.OnUserRefresh )
2295        # test menu
2296        testMenu = wx.Menu()
2297        self.addEntry( testMenu, 'Read GR * BHZ of an event',
2298            'fdsnws gr * * bhz 7-aug-15_00:16:30 420', self.OnTest1 )
2299        self.addEntry( testMenu, 'Read SX,TH * BHZ of an event',
2300            'fdsnws sx,th * * bhz 7-aug-15_00:16:30 420', self.OnTest2 )
2301        # put menus and menu bar in place
2302        menubar.Append( fileMenu, 'File' )
2303        menubar.Append( windowMenu, 'Display' )
2304        menubar.Append( tracesMenu, 'Traces' )
2305        menubar.Append( arrayMenu, 'Array' )
2306        menubar.Append( simulateMenu, 'Filter' )
2307        menubar.Append( eventsMenu, 'Events' )
2308        menubar.Append( phaseMenu, 'Phases' )
2309        menubar.Append( magnMenu, 'Magnitude' )
2310        menubar.Append( controlMenu, 'Control' )
2311        menubar.Append( testMenu, 'Test' )
2312        self._menuitems['TraceScrollOff'].Enable(False)
2313        self._menuitems['TraceScrollUp'].Enable(False)
2314        self._menuitems['TraceScrollDown'].Enable(False)
2315        self.SetMenuBar( menubar )
2316        self.Centre()
2317   
2318    def addEntry( self, menutitle, entrytext, entrydescr, callback,
2319        accel=None ):
2320        "plotterWindow: method to add a menu entry."
2321        #self.menu_entry_id += 1
2322        #qmi = wx.MenuItem( menutitle, self.menu_entry_id, entrytext,
2323        #    help=entrydescr )
2324        #menutitle.AppendItem( qmi )
2325        #self.Bind( wx.EVT_MENU, callback, id=self.menu_entry_id )       
2326        item = menutitle.Append( wx.ID_ANY, entrytext, entrydescr )
2327        item.SetBackgroundColour( shxcolor1 )
2328        self.Bind( wx.EVT_MENU, callback, item )
2329        # doesn't work:
2330        if accel:
2331            item.SetAccel( wx.AcceleratorEntry(keyCode=accel) )
2332        return item
2333
2334    def OnQuit( self, e ):
2335        "plotterWindow: file menu entry."
2336        # Cannot quit SHX, _sendShCommand waits for command to be executed
2337        # i.e. no way to terminate wx smoothly. Quit SHX after Close also not
2338        # possible, runs into communication issues between threads.
2339        #_sendShCommand( "quit y" )
2340        self.Close()
2341   
2342    def OnNorm( self, e ):
2343        "plotterWindow: display menu entry."
2344        if self.flag_norm:
2345            _sendShCommand( 'norm aw' )
2346        else:
2347            _sendShCommand( 'norm sw' )
2348        self.flag_norm = not self.flag_norm
2349
2350    def OnOverlappingTraces( self, e ):
2351        "plotterWindow: traces menu entry."
2352        if self.flag_overlap:
2353            _sendShCommand( 'fct overlapping_traces false' )
2354        else:
2355            _sendShCommand( 'fct overlapping_traces true' )
2356        self.flag_overlap = not self.flag_overlap
2357
2358    def OnDemean( self, e ):
2359        "plotterWindow: traces menu entry."
2360        _sendShCommand( 'demean all' )
2361   
2362    def OnDeleteTrace( self, e ):
2363        "plotterWindow: traces menu entry."
2364        if self._seltrace == None:
2365            self.showMessage( "no trace selected" )
2366        else:
2367            _sendShCommand( "del %d" % self._seltrace )
2368   
2369    def OnDeleteStation( self, e ):
2370        "plotterWindow: traces menu entry."
2371        if self._seltrace == None:
2372            self.showMessage( "no trace selected" )
2373            return
2374        trclist = traces_from_list( "%d" % self._seltrace )
2375        if len(trclist) != 1:
2376            self.showMessage( "Program bug: should select only one trace" )
2377            return
2378        trc = trclist[0]
2379        _sendShCommand( "del _station(%s)" % trc.stats.station )
2380
2381    def OnDeleteTraceAndAbove( self, e ):
2382        "plotterWindow: traces menu entry."
2383        if self._seltrace == None:
2384            self.showMessage( "no trace selected" )
2385        else:
2386            _sendShCommand( "del |%d|-|$dsptrcs|" % self._seltrace )
2387   
2388    def OnSpectrogram( self, e ):
2389        "plotterWindow: traces menu entry."
2390        if self._seltrace == None:
2391            self.showMessage( "no trace selected" )
2392            return
2393        try:
2394            timea, timeb = self._zoomwdw
2395        except:
2396            timea = timeb = None
2397        if timea and timeb:
2398            cmd = "spectrogram %d %g %g" % (self._seltrace,timea,timeb)
2399        else:
2400            cmd = "spectrogram %d" % self._seltrace
2401        _sendShCommand( cmd )
2402       
2403
2404    def OnTest1( self, e ):
2405        "plotterWindow: test menu entry."
2406        _sendShCommand( 'fdsnws gr * * bhz 7-aug-15_00:16:30 420' )
2407
2408    def OnTest2( self, e ):
2409        "plotterWindow: test menu entry."
2410        _sendShCommand( 'fdsnws sx,th * * bhz 7-aug-15_00:16:30 420' )
2411
2412    def OnSetTimeWindow( self, e ):
2413        "plotterWindow: display menu entry."
2414        _sendShCommand( 'stw %g %g' % self._zoomwdw )
2415
2416    def OnDelTimeWindow( self, e ):
2417        "plotterWindow: display menu entry."
2418        _sendShCommand( 'dtw' )
2419
2420    def OnMoveZoomRight( self, e ):
2421        "plotterWindow: display menu entry."
2422        plotter.canvas.moveZoomWindow( 'right' )
2423
2424    def OnMoveZoomLeft( self, e ):
2425        "plotterWindow: display menu entry."
2426        plotter.canvas.moveZoomWindow( 'left' )
2427
2428    def OnMoveZoomUp( self, e ):
2429        "plotterWindow: display menu entry."
2430        plotter.canvas.moveZoomWindow( 'up' )
2431
2432    def OnMoveZoomDown( self, e ):
2433        "plotterWindow: display menu entry."
2434        plotter.canvas.moveZoomWindow( 'down' )
2435
2436    def OnAmplZoomUp( self, e ):
2437        "plotterWindow: display menu entry."
2438        _sendShCommand( "zoom/rel all 2" )
2439
2440    def OnAmplZoomDown( self, e ):
2441        "plotterWindow: display menu entry."
2442        _sendShCommand( "zoom/rel all 0.5" )
2443
2444    def OnTraceScrollOn( self, e ):
2445        "plotterWindow: display menu entry."
2446        global displayselect
2447        displayselect = (1,21)
2448        plotter.canvas.refresh = True
2449        self._menuitems['TraceScrollOn'].Enable(False)
2450        self._menuitems['TraceScrollOff'].Enable()
2451        self._menuitems['TraceScrollUp'].Enable()
2452        self._menuitems['TraceScrollDown'].Enable()
2453
2454    def OnTraceScrollOff( self, e ):
2455        "plotterWindow: display menu entry."
2456        global displayselect
2457        displayselect = None
2458        plotter.canvas.refresh = True
2459        self._menuitems['TraceScrollOn'].Enable()
2460        self._menuitems['TraceScrollOff'].Enable(False)
2461        self._menuitems['TraceScrollUp'].Enable(False)
2462        self._menuitems['TraceScrollDown'].Enable(False)
2463       
2464    def OnTraceScrollUp( self, e ):
2465        "plotterWindow: display menu entry."
2466        _scrollTraces( 'up' )
2467
2468    def OnTraceScrollDown( self, e ):
2469        "plotterWindow: display menu entry."
2470        _scrollTraces( 'down' )
2471
2472    def OnShowAllInMagnify( self, e ):
2473        "plotterWindow: display menu entry."
2474        plotter.magnify.canvas.toggleShowAllTraces()
2475
2476    def OnReadDataFile( self, e ):
2477        "plotterWindow: file menu entry,  read one or more data files."
2478        style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
2479        dlg = wx.FileDialog( self, "Select Data File", defaultDir=os.getcwd(),
2480            style=style )
2481        dlg.ShowModal()
2482        filelist = dlg.GetPaths()
2483        retcode = dlg.GetReturnCode()
2484        dlg.Destroy()
2485        if retcode != wx.ID_OK:
2486            return
2487        cmd = '\n'.join(["@READO %s ALL" % fname.replace('/','\\') \
2488            for fname in filelist])
2489        _sendShCommand( cmd )
2490   
2491    def OnWriteDataFile( self, e ):
2492        "plotterWindow: file menue entry, writes all traces to file."
2493        legal_formats = ('mseed','q','gse','gse2','sac')
2494        style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
2495        dlg = wx.FileDialog( self, "Select Output File", defaultDir=os.getcwd(),
2496            style=style )
2497        dlg.ShowModal()
2498        outfile = dlg.GetPath()
2499        retcode = dlg.GetReturnCode()
2500        dlg.Destroy()
2501        if retcode != wx.ID_OK:
2502            return
2503        format = os.path.splitext(outfile)[1].lstrip('.').lower()
2504        if format not in legal_formats:
2505            msg = "Format '%s' not supported. Nothing written." % format
2506            self.showMessage( msg, error=True )
2507            return
2508        cmd = "@WRITEO %s ALL %s" % (outfile.replace('/','\\'),format.upper())
2509        _sendShCommand( cmd )
2510   
2511    def OnReadFdsnCombi( self, e ):
2512        "plotterWindow: file menu entry."
2513        ap = AnalysisPar()
2514        cmds = ['switch uniquechannel on']
2515        if ap.getValueAsString('readws_net'):
2516            cmd = "fdsnws %s %s %s %s %s %s /addr=%s" % (
2517                ap.getValueAsString( 'readws_net' ),
2518                ap.getValueAsString( 'readws_station' ),
2519                ap.getValueAsString( 'readws_loc' ),
2520                ap.getValueAsString( 'readws_chan' ),
2521                ap.getValueAsString( 'read_time' ),
2522                ap.getValueAsString( 'read_length' ),
2523                ap.getValueAsString( 'readws_server' ),
2524            )
2525            cmds.append( cmd )
2526        circle = ap.getValue('sta_radius')
2527        if circle != None and circle > 0.:
2528            cmd = "fdsnws %s,%s,%s * * %s %s %s /addr=%s" % (
2529                ap.getValueAsString( 'sta_latitude' ),
2530                ap.getValueAsString( 'sta_longitude' ),
2531                ap.getValueAsString( 'sta_radius' ),
2532                ap.getValueAsString( 'readws_chan' ),
2533                ap.getValueAsString( 'read_time' ),
2534                ap.getValueAsString( 'read_length' ),
2535                ap.getValueAsString( 'readws_server' ),
2536            )
2537            cmds.append( cmd )
2538        cmds.append( "meta all complete /addr=%s" \
2539            % ap.getValueAsString('readws_server') )
2540        _sendShCommand( '\n'.join(cmds) )
2541   
2542    def OnReadFdsnCircle( self, e ):
2543        "plotterWindow: file menu entry."
2544        ap = AnalysisPar()
2545        cmds = ['switch uniquechannel on']
2546        cmd = "fdsnws %s,%s,%s * * %s %s %s /addr=%s" % (
2547            ap.getValueAsString( 'epi_latitude' ),
2548            ap.getValueAsString( 'epi_longitude' ),
2549            ap.getValueAsString( 'sta_radius' ),
2550            ap.getValueAsString( 'readws_chan' ),
2551            ap.getValueAsString( 'read_time' ),
2552            ap.getValueAsString( 'read_length' ),
2553            ap.getValueAsString( 'readws_server' ),
2554        )
2555        cmds.append( cmd )
2556        cmds.append( "meta all complete /addr=%s" \
2557            % ap.getValueAsString('readws_server') )
2558        _sendShCommand( '\n'.join(cmds) )
2559   
2560    def OnCompleteMeta( self, e ):
2561        "plotterWindow: file menu entry."
2562        ap = AnalysisPar()
2563        _sendShCommand(
2564            "meta all complete /addr=%s" % ap.getValueAsString('readws_server')
2565        )
2566
2567    def OnSortByDistance( self, e ):
2568        "plotterWindow: traces menu entry."
2569        ap = AnalysisPar()
2570        lat = ap.getValueAsString( 'epi_latitude' )
2571        lon = ap.getValueAsString( 'epi_longitude' )
2572        if not lat or not lon:
2573            self.showMessage( "no epicenter defined" )
2574            return
2575        _sendShCommand( "sort_by_distance %s %s" % (lat,lon) )
2576   
2577    def OnFK( self, e ):
2578        "plotterWindow: array menu entry."
2579        if not self._zoomwdw:
2580            return
2581        timea, timeb = self._zoomwdw
2582        if timea == None or timeb == None:
2583            return
2584        ap = AnalysisPar()
2585        cmd = "shx_menu_fk %g %g %s %s %s" % (timea,timeb,
2586            ap.getValueAsString('fk_min_freq'),
2587            ap.getValueAsString('fk_max_freq'),
2588            ap.getValueAsString('fk_max_slowness'))
2589        _sendShCommand( cmd )
2590   
2591    def OnFKAutoFreq( self, e ):
2592        "plotterWindow: array menu entry."
2593        if not self._zoomwdw:
2594            return
2595        timea, timeb = self._zoomwdw
2596        if timea == None or timeb == None:
2597            return
2598        frq = 1./(timeb-timea)
2599        ap = AnalysisPar()
2600        cmd = "shx_menu_fk %g %g %s %s %s" % (timea,timeb,frq,(10.*frq),
2601            ap.getValueAsString('fk_max_slowness'))
2602        _sendShCommand( cmd )
2603
2604    def OnBeam( self, e ):
2605        "plotterWindow: menu entry."
2606        if self.flag_beam:
2607            _sendShCommand( "shx_menu_delbeam" )
2608        else:
2609            _sendShCommand( "shx_menu_beam" )
2610        self.flag_beam = not self.flag_beam
2611   
2612    def OnPlaneWave( self, e ):
2613        _sendShCommand( "shx_menu_plane_wave" )
2614   
2615    def OnCorrPick( self, e ):
2616        if not self._zoomwdw:
2617            self.showMessage( "No selection window found." )
2618            return
2619        timea, timeb = self._zoomwdw
2620        if timea == None or timeb == None:
2621            return
2622        _sendShCommand( "corrpick %d %g %g %g" % (self._seltrace,
2623            timea,timeb,((timeb-timea)*4.)) )
2624   
2625    def OnSimulateWoodAnderson( self, e ):
2626        "plotterWindow: filter menu entry."
2627        _sendShCommand( 'shx_menu_simulate wood-anderson ;; 5.' )
2628
2629    def OnSimulateWoodAnderson1Hz( self, e ):
2630        "plotterWindow: filter menu entry."
2631        _sendShCommand( 'shx_menu_simulate wood-anderson 1.0 5.' )
2632
2633    def OnSimulateWWSSNSP( self, e ):
2634        "plotterWindow: filter menu entry."
2635        _sendShCommand( 'shx_menu_simulate wwssn-sp ;; 5.' )
2636
2637    def OnSimulateWWSSNLP( self, e ):
2638        "plotterWindow: filter menu entry."
2639        _sendShCommand( 'shx_menu_simulate wwssn-lp ;; 20.' )
2640
2641    def OnSimulateLRSMSP( self, e ):
2642        "plotterWindow: filter menu entry."
2643        _sendShCommand( 'shx_menu_simulate lrsm-sp ;; 5.' )
2644
2645    def OnSimulateLRSMLP( self, e ):
2646        "plotterWindow: filter menu entry."
2647        _sendShCommand( 'shx_menu_simulate lrsm-lp ;; 20.' )
2648
2649    def OnSimulateKIRNOS( self, e ):
2650        "plotterWindow: filter menu entry."
2651        _sendShCommand( 'shx_menu_simulate kirnos ;; 20' )
2652
2653    def OnSimulateSROLP( self, e ):
2654        "plotterWindow: filter menu entry."
2655        _sendShCommand( 'shx_menu_simulate sro-lp ;; 30.' )
2656
2657    def OnFilterBP_1_8( self, e ):
2658        "plotterWindow: filter menu entry."
2659        _sendShCommand( 'shx_menu_filter bp_1hz_8hz_4 5.' )
2660
2661    def OnSimulateUndo( self, e ):
2662        "plotterWindow: filter menu entry."
2663        _sendShCommand( 'shx_menu_simulate undo' )
2664   
2665    def OnOpenParams( self, e ):
2666        "plotterWindow: control menu entry."
2667        self.pardialog = ParamDialog( self )
2668        self.pardialog.Show()
2669        self.pardialog.Move( get_runtime("wdwpos_params") )
2670   
2671    def OnOpenMagnify( self, e ):
2672        "plotterWindow: control menu entry."
2673        plotter.openMagnifyWindow(None)
2674   
2675    def OnUserRefresh( self, e ):
2676        self.redraw()
2677       
2678    def OnGetEventlist( self, e ):
2679        "plotterWindow: events menu entry."
2680        _sendShCommand( 'shx_menu_get_eventlist' )
2681
2682    def OnNextEvent( self, e ):
2683        "plotterWindow: events menu entry."
2684        _sendShCommand( 'shx_menu_next_event;;' )
2685
2686    def OnPrevEvent( self, e ):
2687        "plotterWindow: events menu entry."
2688        _sendShCommand( 'shx_menu_next_event -1' )
2689   
2690    def OnEventInfoParser( self, e ):
2691        "plotterWindow: events menu entry."
2692        dlg = EventParseDialog( self )
2693        dlg.ShowModal()
2694        dlg.Destroy()
2695
2696    def OnLocateEvent( self, e ):
2697        "plotterWindow: events menu entry."
2698        _sendShCommand( 'locate shxvar /showresult' )
2699   
2700    def OnLocateTeleEvent( self, e ):
2701        "plotterWindow: events menu entry."
2702        _sendShCommand( "shx_menu_locate_tele" )
2703       
2704    def OnPlotLocation( self, e ):
2705        "plotterWindow: events menu entry."
2706        ap = AnalysisPar()
2707        lat = ap.getValue( 'epi_latitude' )
2708        lon = ap.getValue( 'epi_longitude' )
2709        dep = ap.getValue( 'source_depth' )
2710        orig = ap.getValueAsString( 'origin_time' )
2711        title = "loc:_%s_%g,%g_dep:_%g_(%s)" % (orig,lat,lon,dep,
2712            ap.getValue('depth_type'))
2713        _sendShCommand( '@PLOT_STATIONS WITHPHASES LOCAL /ADDLOC=%g,%g /TITLE=%s' \
2714            % (lat,lon,title) )
2715       
2716    def OnSaveTracesAndParamsQuick( self, e ):
2717        "plotterWindow: control menu entry."
2718        sloutfile = 'TRACES_PARAMS_RECOVER.SHC'
2719        cmd = "param savetraces %s\n" % sloutfile\
2720            +"param saveascmd %s /append" % sloutfile
2721        _sendShCommand( cmd )
2722   
2723    def OnSaveTracesAndParams( self, e ):
2724        "plotterWindow: control menu entry."
2725        style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
2726        dlg = wx.FileDialog( self, "Select Save File", defaultDir=os.getcwd(),
2727            style=style, wildcard='*.SHC' )
2728        dlg.ShowModal()
2729        outfile = dlg.GetFilename()
2730        retcode = dlg.GetReturnCode()
2731        dlg.Destroy()
2732        if retcode != wx.ID_OK:
2733            return
2734        sloutfile = outfile.replace("/","\\")
2735        cmd = "param savetraces %s\n" % sloutfile\
2736            +"param saveascmd %s /append" % sloutfile
2737        _sendShCommand( cmd )
2738   
2739    def OnRecoverTracesAndParamsQuick( self, e ):
2740        "plotterWindow: control menu entry."
2741        _sendShCommand( 'TRACES_PARAMS_RECOVER' )
2742       
2743    def OnClearAllParameters( self, e ):
2744        _sendShCommand( 'shx_menu_reset_all' )
2745       
2746    def OnRecoverTracesAndParams( self, e ):
2747        "plotterWindow: control menu entry."
2748        style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
2749        dlg = wx.FileDialog( self, "Select Recovery File",
2750            defaultDir=os.getcwd(), style=style, wildcard='*.SHC' )
2751        dlg.ShowModal()
2752        recfile = dlg.GetFilename()
2753        retcode = dlg.GetReturnCode()
2754        dlg.Destroy()
2755        if retcode != wx.ID_OK:
2756            return
2757        slrecfile = recfile.replace('/','\\')
2758        if slrecfile.endswith('.SHC'):
2759            slrecfile = os.path.splitext(slrecfile)[0]
2760        _sendShCommand( slrecfile )
2761
2762    def OnSetPhase_Pg( self, e ):
2763        "plotterWindow: phase menu entry."
2764        _sendShCommand( "@PHASE DEFAULT_PHASE Pg" )
2765    def OnSetPhase_Pn( self, e ):
2766        "plotterWindow: phase menu entry."
2767        _sendShCommand( "@PHASE DEFAULT_PHASE Pn" )
2768    def OnSetPhase_Sg( self, e ):
2769        "plotterWindow: phase menu entry."
2770        _sendShCommand( "@PHASE DEFAULT_PHASE Sg" )
2771    def OnSetPhase_Sn( self, e ):
2772        "plotterWindow: phase menu entry."
2773        _sendShCommand( "@PHASE DEFAULT_PHASE Sn" )
2774    def OnSetPhase_P( self, e ):
2775        "plotterWindow: phase menu entry."
2776        _sendShCommand( "@PHASE DEFAULT_PHASE P" )
2777    def OnSetPhase_pP( self, e ):
2778        "plotterWindow: phase menu entry."
2779        _sendShCommand( "@PHASE DEFAULT_PHASE pP" )
2780    def OnSetPhase_sP( self, e ):
2781        "plotterWindow: phase menu entry."
2782        _sendShCommand( "@PHASE DEFAULT_PHASE sP" )
2783    def OnSetPhase_PP( self, e ):
2784        "plotterWindow: phase menu entry."
2785        _sendShCommand( "@PHASE DEFAULT_PHASE PP" )
2786    def OnSetPhase_S( self, e ):
2787        "plotterWindow: phase menu entry."
2788        _sendShCommand( "@PHASE DEFAULT_PHASE S" )
2789    def OnSetPhase_PKP( self, e ):
2790        "plotterWindow: phase menu entry."
2791        _sendShCommand( "@PHASE DEFAULT_PHASE PKP" )
2792    def OnSetPhase_PKPdf( self, e ):
2793        "plotterWindow: phase menu entry."
2794        _sendShCommand( "@PHASE DEFAULT_PHASE PKPdf" )
2795    def OnSetPhase_PKPbc( self, e ):
2796        "plotterWindow: phase menu entry."
2797        _sendShCommand( "@PHASE DEFAULT_PHASE PKPbc" )
2798    def OnSetPhase_PKPab( self, e ):
2799        "plotterWindow: phase menu entry."
2800        _sendShCommand( "@PHASE DEFAULT_PHASE PKPab" )
2801    def OnSetPhase_pPKPdf( self, e ):
2802        "plotterWindow: phase menu entry."
2803        _sendShCommand( "@PHASE DEFAULT_PHASE pPKPdf" )
2804    def OnSetPhase_pPKPbc( self, e ):
2805        "plotterWindow: phase menu entry."
2806        _sendShCommand( "@PHASE DEFAULT_PHASE pPKPbc" )
2807    def OnSetPhase_pPKPab( self, e ):
2808        "plotterWindow: phase menu entry."
2809        _sendShCommand( "@PHASE DEFAULT_PHASE pPKPab" )
2810
2811    def OnSetPhaseUseEvPhase( self, e ):
2812        "plotterWindow: phase menu entry."
2813        _sendShCommand( "@PHASE DEFAULT_PHASE %s"
2814            % AnalysisPar().getValueAsString('ev_phase'))
2815
2816    def OnTheoPhaseLocal( self, e ):
2817        "plotterWindow: phase menu entry."
2818        ap = AnalysisPar()
2819        for vname in ('epi_latitude','epi_longitude','source_depth',
2820            'origin_time'):
2821            val = ap.getValue(vname)
2822            if val == None or val == '':
2823                self.showMessage( "No location determined yet." )
2824                return
2825        plist = ap.getValue('phaselist')
2826        if plist == None or plist == "":
2827            plist = "Pg,Sg,Pn,Sn"
2828        _sendShCommand( "phase clear ;;; theo\n@THEOPHASE local all %s" % plist )
2829
2830    def OnTheoPhaseTele( self, e ):
2831        "plotterWindow: phase menu entry."
2832        _sendShCommand( "phase clear ;;; theo\ntheophase iasp91 all" )
2833   
2834    def OnPhasePlot( self, e ):
2835        _sendShCommand( "phase plot" )
2836
2837    def OnDeleteTheoPhase( self, e ):
2838        "plotterWindow: phase menu entry."
2839        _sendShCommand( "phase clear ;;; theo /redraw" )
2840
2841    def OnMagnMlAnalytic( self, e ):
2842        "plotterWindow: magnitude menu entry."
2843        if self._seltrace:
2844            cmd = "@MAGNITUDE determine ml 1-%d Pg,Sg,Pn,Sn 8" % self._seltrace
2845        else:
2846            cmd = "@MAGNITUDE determine ml all Pg,Sg,Pn,Sn 8"
2847        _sendShCommand( cmd )
2848   
2849    def OnMagnMlAnalyticSingle( self, e ):
2850        "plotterWindow: magnitude menu entry."
2851        try:
2852            timea, timeb = self._zoomwdw
2853        except:
2854            timea = timeb = None
2855        if timea == None or not self._seltrace:
2856            self.showMessage( "No trace selected" )
2857            return
2858        cmd = "magnitude determine ml %d %g %g" % (self._seltrace,timea,timeb)
2859        _sendShCommand( cmd )
2860   
2861    def OnMagnMlDeleteAll( self, e ):
2862        "plotterWindow: magnitude menu entry."
2863        _sendShCommand( "magnitude clear ml" )
2864
2865    def OnMagnMlDeleteSingle( self, e ):
2866        "plotterWindow: magnitude menu entry."
2867        if not self._seltrace:
2868            self.showMessage( "No trace selected" )
2869            return
2870        cmd = "magnitude clear ml %d" % self._seltrace
2871        _sendShCommand( cmd )
2872
2873    def OnMagnMlPlot( self, e ):
2874        "plotterWindow: magnitude menu entry."
2875        _infodisplayStatus( 'ml Plot magnitudes' )
2876        _sendShCommand( "magnitude plot ml" )
2877
2878
2879class ParamDialog(wx.Dialog):
2880
2881    def __init__( self, parent ):
2882        "ParamDialog: init method."
2883        wx.Dialog.__init__( self, parent, -1, title="Parameters",
2884            style=wx.SUNKEN_BORDER )       
2885        self.SetBackgroundColour( shxcolor1 )
2886        height = 26
2887        btnwidth = 70
2888        vbox = wx.BoxSizer(wx.VERTICAL)
2889        vbox.Add( (5,5), 0, wx.ALL, 1 )
2890        self.vtext = {}
2891        self.vbutton = {}
2892
2893        self.ap = AnalysisPar()
2894        lastgroup = self.ap.getGroup(self.ap.sortedParams()[0])
2895        for pname in self.ap.sortedParams():
2896            pvalue = self.ap.getValueAsString( pname )
2897            if self.ap.getGroup(pname) != lastgroup:
2898                # add spacer
2899                sline = wx.StaticLine(self, name='group', size=(300,1))
2900                #sline.SetForegroundColour( shxcolor2 )
2901                vbox.Add( sline, 0, wx.ALL, 5 )
2902                lastgroup = self.ap.getGroup(pname)
2903            ptext = "%s : %s" % (pname,pvalue)
2904            hbox = wx.BoxSizer(wx.HORIZONTAL)
2905            self.vbutton[pname] = wx.Button( self, label='Change',
2906                size=(btnwidth,height) )
2907            self.vbutton[pname].SetBackgroundColour( 'white' )
2908            self.vtext[pname] = wx.StaticText( self, label=ptext )
2909            hbox.Add( (3,3), 0, wx.ALL, 1 )
2910            hbox.Add( self.vbutton[pname], 0, wx.RIGHT, 1 )
2911            hbox.Add( self.vtext[pname], 1, wx.ALIGN_CENTER_VERTICAL, 1 )
2912            self.Bind( wx.EVT_BUTTON, partial(self.OnButton,pname),
2913                self.vbutton[pname] )
2914            vbox.Add( hbox, 0, wx.LEFT, 1 )
2915        vbox.Add( (5,5), 0, wx.ALL, 1 )
2916        vbox.SetSizeHints(self)
2917        self.SetAutoLayout(True)
2918        self.SetSizer( vbox )
2919        self.Layout()
2920   
2921    def OnButton( self, pname, e, *args ):
2922        "ParamDialog: button event."
2923        oldvalue = self.ap.getValueAsString( pname )
2924        ptype = self.ap.getType( pname )
2925        dlg = QueryString( self, "Change %s" % pname,
2926            "New value for '%s' (type %s):" % (pname,ptype), oldvalue )
2927        btn = dlg.ShowModal()
2928        result = dlg.getValue()
2929        dlg.Destroy()
2930        if result == None:
2931            # Clicked on Cancel
2932            return
2933        if self.validate( result, ptype ):
2934            if result == "":
2935                cmd = "param set %s ;;" % pname
2936            else:
2937                # phase names need capcnv switched off
2938                if result.startswith("$"):
2939                    cmd = "@PARAM SET %s |$DOLLAR|%s|" % (pname,result[1:])
2940                else:
2941                    cmd = "@PARAM SET %s %s" % (pname,result)
2942            _sendShCommand( cmd )
2943        else:
2944            msg = "Illegal input: got '%s' but need type %s" % (result,ptype)
2945            dlg = wx.MessageDialog(self, msg, "Illegal input",
2946                                                     wx.OK | wx.ICON_ERROR)
2947            dlg.ShowModal()
2948            dlg.Destroy()
2949        #self.updateVariables() # done via command line
2950   
2951    def updateVariables( self, *args ):
2952        "ParamDialog: update all values (strings) in dialog box."
2953        for pname in self.ap.sortedParams():
2954            pvalue = self.ap.getValueAsString( pname )
2955            self.vtext[pname].SetLabel( "%s : %s" % (pname,pvalue) )
2956   
2957    def validate( self, result, ptype ):
2958        "ParamDialog: validate user input according to data type."
2959        if ptype == 'int':
2960            try:
2961                i = int( result )
2962                return True
2963            except:
2964                return False
2965        elif ptype in ('float','floatN'):
2966            if ptype == 'floatN' and result == "":
2967                return True
2968            try:
2969                i = float( result )
2970                return True
2971            except:
2972                return False
2973        elif ptype == 'datetime':
2974            try:
2975                r = toUTCDateTime( result )
2976                return (r != None)
2977            except:
2978                return False
2979        elif ptype in ('string','file'):
2980            return (result != "")
2981        elif ptype in ('stringN','fileN'):
2982            return True
2983        else:
2984            print "Program bug, unknown type '%s'" % ptype
2985            return False
2986
2987
2988class QueryString(wx.Dialog):
2989
2990    def __init__( self, parent, title, prompt, oldvalue ):
2991        "QueryString init method."
2992        wx.Dialog.__init__( self, parent, -1, title )
2993        self.SetBackgroundColour( shxcolor1 )
2994        vbox = wx.BoxSizer( wx.VERTICAL )
2995        prompt = wx.StaticText( self, label=prompt )
2996        vbox.Add( prompt, 0, wx.ALIGN_CENTER_HORIZONTAL, 10 )
2997        vbox.Add( (10,5), 0 )
2998        self.text = wx.TextCtrl( self, value=oldvalue, size=(200,26) )
2999        vbox.Add( self.text, 0, wx.ALIGN_CENTER_HORIZONTAL, 10 )
3000        hbox = wx.BoxSizer( wx.HORIZONTAL )
3001        tbtn = wx.Button( self, label='OK', pos=(50,70) )
3002        self.Bind( wx.EVT_BUTTON, self.OnTextOk, tbtn )
3003        hbox.Add( tbtn, 0, wx.ALL, 10 )
3004        cbtn = wx.Button( self, label='Cancel', pos=(150,70) )
3005        self.Bind( wx.EVT_BUTTON, self.OnTextCancel, cbtn )
3006        hbox.Add( cbtn, 0, wx.ALL, 10 )
3007        tbox = wx.BoxSizer( wx.VERTICAL )
3008        tbox.Add( vbox, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 10 )
3009        tbox.Add( hbox, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 )
3010        tbox.SetSizeHints(self)
3011        self.SetAutoLayout(True)
3012        self.SetSizer( tbox )
3013        self.Layout()       
3014        self.result = None
3015   
3016    def OnTextOk( self, e ):
3017        "QueryString: OK button."
3018        self.result = self.text.GetValue()
3019        self.Destroy()
3020   
3021    def OnTextCancel( self, e ):
3022        "QueryString: cancel button."
3023        self.result = None
3024        self.Destroy()
3025   
3026    def getValue( self ):
3027        "QueryString: access to result."
3028        return self.result
3029       
3030
3031class EventParseDialog(wx.Dialog):
3032
3033    def __init__( self, parent ):
3034        "EventParseDialog: init method."
3035        wx.Dialog.__init__( self, parent, -1, title="Event Info Parser",
3036            style=wx.SUNKEN_BORDER )       
3037        self.SetBackgroundColour( shxcolor1 )
3038        height = 26
3039        btnwidth = 70
3040        vspace = [(15,15), 0, wx.ALL, 1]
3041
3042        hbox = wx.BoxSizer( wx.HORIZONTAL )
3043        okbtn = wx.Button( self, label='OK', pos=(50,70) )
3044        self.Bind( wx.EVT_BUTTON, self.OnTextOk, okbtn )
3045        hbox.Add( okbtn, 0, wx.ALL, 10 )
3046        cbtn = wx.Button( self, label='Cancel', pos=(150,70) )
3047        self.Bind( wx.EVT_BUTTON, self.OnTextCancel, cbtn )
3048        hbox.Add( cbtn, 0, wx.ALL, 10 )
3049
3050        vbox = wx.BoxSizer(wx.VERTICAL)
3051        vbox.Add( *vspace )
3052        vbox.Add( wx.StaticText(self,label="Reference Station:"), 0,
3053            wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 3 )
3054        self.text_refstat = wx.TextCtrl( self, value='gr.gra1', size=(60,height) )
3055        vbox.Add( self.text_refstat, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 10 )
3056        vbox.Add( *vspace )
3057        vbox.Add( wx.StaticText(self,label="Event Info Text:"), 0,
3058            wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 3 )
3059        self.text_evinfo = wx.TextCtrl( self, value='', size=(400,height) )
3060        vbox.Add( self.text_evinfo, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 10 )
3061        vbox.Add( *vspace )
3062        vbox.Add( hbox, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 3 )
3063        vbox.Add( *vspace )
3064        vbox.SetSizeHints(self)
3065        self.SetAutoLayout(True)
3066        self.SetSizer( vbox )
3067        self.Layout()
3068   
3069    def OnTextOk( self, e ):
3070        refstat = self.text_refstat.GetValue()
3071        evinfo = EventTextParser(self.text_evinfo.GetValue()).parse()
3072        if evinfo == None:
3073            plotter.showMessage( "Could not parse event line")
3074            self.Destroy()
3075            return
3076        origin, lat, lon = evinfo
3077        self.Destroy()
3078        cmd = "shx_util_eventinfo %s %s %g %g" % (refstat,
3079            fromUTCDateTime(origin),lat,lon)
3080        _sendShCommand( cmd )
3081       
3082    def OnTextCancel( self, e ):
3083        self.Destroy()
3084   
3085
3086def _absToRelTime( trc, abstime ):
3087    "Convert absolute (trace) time to relative (time axis) time."
3088    return (abstime - trc.stats.starttime + trc.get_info('t-origin'))
3089
3090def _relToAbsTime( trc, reltime ):
3091    "Convert relative (time axis) time into absolute (trace) time."
3092    return (trc.stats.starttime + reltime - trc.get_info('t-origin'))
3093
3094
3095def _sendShCommand( cmdstring, name="default" ):
3096    "Send command to command line interpreter."
3097    wx.BeginBusyCursor()
3098    try:
3099        msgs.sendMessage("ui.command", cmd=cmdstring, name=name )
3100        exc = None
3101    except Exception as exc:
3102        pass
3103    wx.EndBusyCursor()
3104    if exc:
3105        raise exc
3106
3107def _infodisplayStatus( text ):
3108    "Local routine for info display status text."
3109    global statusinfotext
3110    statusinfotext = text
3111    if plotter:
3112        plotter._setStatusText( 'Message' )
3113
3114
3115def ui_events(func):
3116    """
3117    """
3118    def wrapper(payload, msgid):
3119        result = func(payload)
3120        if msgid is not None:
3121            msgs.sendMessage(msgid, value=result, msgid=msgid)
3122    return wrapper
3123
3124
3125def _cursorNameToID( mode ):
3126    mode = mode.lower()
3127    if mode == "cross":
3128        _c = wx.CURSOR_CROSS
3129    elif mode == "normal":
3130        _c = wx.CURSOR_DEFAULT
3131    elif mode == "left":
3132        _c = wx.CURSOR_POINT_LEFT
3133    elif mode == "right":
3134        _c = wx.CURSOR_POINT_RIGHT
3135    elif mode == "busy":
3136        _c = wx.CURSOR_WAIT
3137    elif mode == "wait":
3138        _c = wx.CURSOR_ARROWWAIT
3139    return _c
3140
3141def _scrollTraces( direction ):
3142    global displayselect
3143    increment = 6
3144    if displayselect == None:
3145        return
3146    t1, t2 = displayselect
3147    trcno = len(Traces)
3148    if direction == 'up':
3149        t1 += increment
3150        t2 += increment
3151        while t2 > trcno:
3152            t2 -= 1
3153            t1 -= 1
3154    else:
3155        t1 -= increment
3156        t2 -= increment
3157        while t1 < 1:
3158            t1 += 1
3159            t2 += 1
3160    displayselect = (t1,t2)
3161    plotter.canvas.refresh = True
3162
3163@ui_events
3164def __set_cursor(mode):
3165    """
3166    Interface method for altering cursor style on canvas. Thread-safe.
3167    """
3168    _c = _cursorNameToID( mode )
3169    myCursor = wx.StockCursor(_c)
3170    global plotter
3171    wx.CallAfter(plotter.canvas.SetCursor, myCursor)
3172subscribe_ui_event(__set_cursor, "cursor")
3173
3174
3175@ui_events
3176def __select_gui(mode):
3177    """
3178    Interface method for selecting time and/or trace by user interaction. While
3179    UI remains responsive, everything waits for user input.
3180
3181    mode is one of the following commands:
3182    - relative (screen coordinates to relative offset) - for selecting windows
3183    - trace_time (trace under cursor and timestamp) - for picking
3184    """
3185    global plotter
3186    wx.CallAfter(plotter.canvas._set_modes, "interactive", mode)
3187    plotter._user_selection = None
3188    while plotter._user_selection is None:
3189        time.sleep(TIME_SLOT)
3190    return plotter._user_selection
3191subscribe_ui_event(__select_gui, "input")
3192
3193
3194@ui_events
3195def __screenshot(fname):
3196    """
3197    Trigger screen shot creation. Thread-safe. Image will be saved immediately.
3198    """
3199    global plotter
3200    plotter.canvas.do_screenshot = fname
3201    plotter.canvas.refresh = True
3202    wx.CallAfter(plotter.canvas.OnIdle, None)
3203subscribe_ui_event(__screenshot, "screenshot")
3204
3205
3206@ui_events
3207def __updateparams(unused):
3208    """
3209    Trigger redraw of parameter dialog.
3210    """
3211    global plotter
3212    if plotter and plotter.pardialog != None:
3213        wx.CallAfter(plotter.pardialog.updateVariables, None)
3214subscribe_ui_event(__updateparams, "updateparams")
3215
3216@ui_events
3217def __openparams(unused):
3218    """
3219    Open parameter dialog box.
3220    """
3221    global plotter
3222    if plotter and plotter.pardialog == None:
3223        wx.CallAfter(plotter.OnOpenParams, None)
3224subscribe_ui_event(__openparams, "openparams")
3225
3226@ui_events
3227def __openmagnify(unused):
3228    """
3229    Open magnify window.
3230    """
3231    global plotter
3232    if plotter and plotter.magnify == None:
3233        wx.CallAfter(plotter.openMagnifyWindow, None)
3234subscribe_ui_event(__openmagnify, "openmagnify")
3235
3236@ui_events
3237def __infodisplay(infotext):
3238    """
3239    Set text for infodisplay (in status line).
3240    """
3241    global statusinfotext
3242    statusinfotext = infotext
3243    if plotter:
3244        wx.CallAfter(plotter._setStatusText, 'Message')
3245subscribe_ui_event(__infodisplay, "infodisplay")
3246
Note: See TracBrowser for help on using the repository browser.