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

Revision 1191, 132.2 KB checked in by klaus, 4 years ago (diff)

changes due to new obspy version

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