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

Revision 1139, 73.6 KB checked in by klaus, 4 years ago (diff)

phase menu; BP filter; bugfix in recover; quick save and recover

  • Property svn:eol-style set to native
Line 
1# -*- coding: utf-8 -*-
2
3#    This file is part of Seismic Handler eXtended (SHX). For terms of use and
4#    license information please see license.txt and visit
5#    http://www.seismic-handler.org/portal/wiki/Shx/LicenseTerms
6
7import wx
8import wx.lib.scrolledpanel as SP
9import time
10import math
11import os
12import numpy as np
13from functools import partial
14from SeismicHandler.basics.messages import msgs, subscribe_ui_event, \
15                                ui_event, log_message, get_style#, get_runtime
16from SeismicHandler.config import get_runtime
17from SeismicHandler.core import Traces, Overlays
18from SeismicHandler.basics.codes import NAME, VERSION
19from SeismicHandler.basics.tools import get_default_attrib, timeit, timestore
20from SeismicHandler.basics.analysispar import PhaseList, AnalysisPar
21from obspy.core import read, UTCDateTime
22from obspy.sh.core import fromUTCDateTime, toUTCDateTime
23
24# width of station info
25STATION_INFO = 85
26# margins (top, right, bottom, left)
27MARGINS = [10, 10, 10, 10]
28# space for time-scale
29TIMESCALE = 40
30# offset for time string
31TIMESCALE_OFFSET_TEXT = 8
32# length of minor/major ticks
33TIMESCALE_TICKS_MINOR = 3
34TIMESCALE_TICKS_MAJOR = 7
35
36# regular checking of UI data
37TIME_SLOT = .03
38
39# holds window instance
40plotter = None
41
42class traceCanvas(SP.ScrolledPanel):
43    """
44    All drawing methods for plotting traces. Include all mouse-related
45    functions.
46    """
47    #@timeit
48    def __init__(self, parent, fnames=[]):
49        SP.ScrolledPanel.__init__(self, parent,
50            style=wx.BORDER_SIMPLE|wx.NO_FULL_REPAINT_ON_RESIZE)
51
52        # class data
53        self._bitmap = None
54        self.parent = parent
55        self.traces = Traces
56        self.ZoomWindow = None
57        self.zoomwdwwidth = 0
58        self.Scrolled = 0
59        self.mousedouble = 0
60        self.refresh = False
61        self.AllowDoubleClick = True
62        self.do_screenshot = False
63        self.pixel_duration = None
64        self.last_defined_phase = None
65        self.wheelpos = 0
66        self.timewindow = (None,None)
67        self.last_number_of_traces = 0
68        self._xor_line = None
69        self._xor_rect = None
70
71        # temporary layer for mouse motion
72        self.overlay_drag = wx.Overlay()
73        # layer for picks and zoom box
74        self.overlay_picks = wx.Overlay()
75
76        # application defaults
77        self.relativeAxis = True
78        self.traceOrder = 0
79        self.phasename = "P"
80        self.space = None
81        self.interactive = "trace_time"
82
83        self.ppi = wx.ScreenDC().GetPPI()
84
85        self._setup()
86
87        def disable_event(*pargs,**kwargs):
88            pass # the sauce, please
89
90        # event binding
91        self.Bind(wx.EVT_PAINT, self.OnPaint)
92        self.Bind(wx.EVT_SIZE, self.OnSize)
93        self.Bind(wx.EVT_IDLE, self.OnIdle)
94        self.Bind(wx.EVT_MOTION, self.OnMouseMotion)
95        self.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
96        self.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseLeftDouble)
97        self.Bind(wx.EVT_RIGHT_DCLICK, self.OnMouseRightDouble)
98        self.Bind(wx.EVT_RIGHT_UP, self.OnMouseRightUp)
99        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)
100        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
101        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
102        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
103        self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
104        self.Bind(wx.EVT_ERASE_BACKGROUND, disable_event)
105
106        # messaging system
107#        msgs.subscribe(self.OnNotifyHeight, 'GUT.displayheight')
108#        msgs.subscribe(self.OnNotifyPhaseName, 'GUI.phasename')
109#        msgs.subscribe(self.OnNotifyTraceOrder, 'GUI.traceorder')
110#        msgs.subscribe(self.OnNotifyTimeAxis, 'GUT.timeaxis')
111        msgs.subscribe(self.__runtime_changer, "setruntime")
112
113        # init
114        self.OnSize(None)
115        self.SetFocus()
116
117    def __runtime_changer(self, name, value):
118        """
119        Helper method for acting on certain runtime events. Called via message
120        system.
121        """
122        if name != "RUNTIME:styles":
123            return
124        margins = value.get("MARGINS", None)
125
126        if not margins:
127            return
128
129        # convert inch values to pixels
130        margins = [
131            margins[0] * self.ppi[1],
132            margins[1] * self.ppi[0],
133            margins[2] * self.ppi[1],
134            margins[3] * self.ppi[0]
135        ]
136
137        global MARGINS
138        if MARGINS != margins:
139            MARGINS = margins
140   
141    def closePhase( self, trace, abstime ):
142        "Return name and type of closest phase or None."
143        phaselist = PhaseList()
144        if not self.pixel_duration:
145            return None
146        toltime = 2*self.pixel_duration
147        station = "%s.%s" % (trace.stats.network,trace.stats.station)
148        for phase in phaselist.getPhaseList(station):
149            tdiff = abs( phase.picktime-abstime )
150            if tdiff < toltime:
151                return (phase.name,phaselist.picktypeName(phase.picktype))
152        return None
153   
154    def _drawXor( self, mode, coo=(None,None,None,None) ):
155        """Draws line (len(coo)==2) or rectangle (len(coo) == 4) in XOR mode.
156        Deletes previous line/rectangle. To only clear old element, pass
157        None-tuples.
158        Should also draw into backing store bitmap (self._bitmap). Now,
159        a repaint doesn't restore the XOR drawing, resulting in a doubled
160        element.
161        """
162        if mode == 'clear':
163            self._xor_line = None
164            self._xor_rect = None
165            return
166        color = 'orange'
167        # Line or rectangle?
168        islinemode = (mode == 'line')
169        # Setup dc.
170        if islinemode:
171            size = 1
172        else:
173            size = 2
174        dc = wx.ClientDC(self)
175        dc.SetLogicalFunction( wx.XOR )
176        dc.SetPen( wx.Pen(color,size,wx.SOLID) )
177        dc.SetBrush( wx.Brush(color,wx.TRANSPARENT) )
178        #dc.SetBrush( wx.Brush(color,wx.SOLID) )
179        if islinemode:
180            if self._xor_line != None:
181                # Clear old rectangle.
182                dc.DrawLine( *self._xor_line )
183            if coo[0] == None:
184                self._xor_line = None
185            else:
186                # Draw new rectangle.
187                dc.DrawLine( *coo )
188                self._xor_line = coo
189        else:
190            if self._xor_rect != None:
191                # Clear old rectangle.
192                dc.DrawRectangle( *self._xor_rect )
193            if coo[0] == None:
194                self._xor_rect = None
195            else:
196                # Draw new rectangle.
197                dc.DrawRectangle( *coo )
198                self._xor_rect = coo
199
200    # event processing
201    def OnMouseWheel(self, evt):
202        maxwheelpos = 10
203        wheel = evt.GetWheelRotation()
204        if wheel > 0:
205            if self.wheelpos < maxwheelpos:
206                _sendShCommand( "zoom/rel all 2" )
207                self.wheelpos += 1
208        elif wheel < 0:
209            if self.wheelpos > -maxwheelpos:
210                _sendShCommand( "zoom/rel all 0.5" )           
211                self.wheelpos -= 1
212        evt.Skip()
213
214    def OnKeyDown(self, evt):
215        print "key"
216        kc = evt.GetKeyCode()
217        print kc
218        evt.Skip()
219#        if kc == ord("r"):
220
221    def OnMouseLeftDouble(self, evt):
222        trace, tracetime = self.ScreenToTraceAndTime(*(evt.GetPositionTuple()))
223        if trace:
224            self.mousedouble = True
225        #    print "dbg: closePhase", self.closePhase(trace,tracetime)
226        #    cphase = self.closePhase( trace, tracetime )
227        #    if cphase:
228        #        station = "%s.%s" % (trace.stats.network,trace.stats.station)
229        #        _sendShCommand(
230        #            "phase clear %s %s %s" % (cphase[0],station,cphase[1])
231        #        )
232        evt.Skip()
233
234    def OnMouseRightDouble(self, evt):
235        print "dbg: right double click"
236        self.zoomwdwwidth = 0.
237        evt.Skip()
238
239    def OnMouseLeftDown(self, evt):
240        self._captureMouse(evt)
241        trace, tracetime = self.ScreenToTraceAndTime(*(evt.GetPositionTuple()))
242        if trace:
243            #self.mousedouble = True
244            cphase = self.closePhase( trace, tracetime )
245            if cphase:
246                station = "%s.%s" % (trace.stats.network,trace.stats.station)
247                station = station.upper()
248                _sendShCommand(
249                    "@PHASE CLEAR %s %s %s" % (cphase[0],station,cphase[1]) \
250                    +"\n@PHASE DEFAULT_PHASE %s" % cphase[0]
251                )
252                self.last_defined_phase = cphase[0]
253                self._drawSinglePick( trace, tracetime,color='white' )
254            self._drawPicksAndZoom()
255        evt.Skip()
256
257    def OnMouseRightDown(self, evt):
258        self._captureMouse(evt)
259        if self.zoomwdwwidth:
260            pass
261        elif self.ZoomWindow:
262            trace, start, end = self.ZoomWindow
263            start = self.TraceAndTimeToScreen(trace, start)
264            end = self.TraceAndTimeToScreen(trace, end)
265            self.zoomwdwwidth = end[0] - start[0]
266        else:
267            self.zoomwdwwidth = 0
268
269    def OnMouseMotion(self, evt):
270        """
271        Mouse motion.
272        """
273
274        if not len(self.traces):
275            return
276
277        x, y = evt.GetPositionTuple()
278        trace, timestamp = self.ScreenToTraceAndTime(x, y)
279
280        try:
281            tid = trace.id
282        except AttributeError:
283            tid = "*"
284       
285        reltimestr = "%g" % self.ScreenToRelativeTime( x, y )
286
287        dragwidth = None
288        if evt.Dragging() and trace:
289            th2 = self.traceheight / 2
290            shx_graphics = trace.get_graphics()
291            if shx_graphics == None:
292                print "dbg: returned from callback, no shx_graphics"
293                return
294            mpth2 = shx_graphics.midpoint + th2
295            mmth2 = shx_graphics.midpoint - th2
296
297            # draw box
298            if evt.RightIsDown():
299                self.ZoomWindow = None
300                if plotter:
301                    plotter.setZoomWindow( None, None, None )
302                if self.zoomwdwwidth == 0.:
303                    self.__zoombox( self.dragStart, (x, y),
304                        shx_graphics.midpoint, size=2 )
305                    dragwidth = x - self.dragStart[0]
306                else:
307                    _start = [x-self.zoomwdwwidth/2,y]
308                    _end = [x+self.zoomwdwwidth/2,y]
309                    self.__zoombox( _start, _end,
310                        shx_graphics.midpoint, size=2 )
311                    dragwidth = _end[0] - _start[0]
312            # draw position line
313            elif evt.LeftIsDown():
314                if self.traceOrder in [0, 1]:
315                    self._drawXor( 'line', (
316                        x, mmth2 - self.Scrolled,
317                        x, mpth2 - self.Scrolled
318                    ) )
319                elif self.traceOrder in [2, 3]:
320                    self._drawXor( 'line', (
321                        mmth2 - self.Scrolled, y,
322                        mpth2 - self.Scrolled, y
323                    ) )
324
325        stext = "%s - %s - %s" % (fromUTCDateTime(timestamp), reltimestr, tid)
326        if self.ZoomWindow:
327            stext += " - Z:%3.1f" % (self.ZoomWindow[2]-self.ZoomWindow[1])
328        elif dragwidth and self.pixel_duration:
329            stext += " - Z:%3.1f" % (dragwidth*self.pixel_duration)
330        self.parent.SetStatusText( stext )
331
332        # let the event propagate
333        evt.Skip()
334
335    def OnMouseRightUp(self, evt):
336        """
337        Release of right mouse button.
338        """
339        if self.HasCapture():
340            self._releaseMouse(evt)
341            x, y = evt.GetPositionTuple()
342
343            if self.zoomwdwwidth == 0:
344                trace, end = self.ScreenToTraceAndTime(x, y)
345                if self.traceOrder in [0, 1]:
346                    _, start = self.ScreenToTraceAndTime(self.dragStart[0], y)
347                elif self.traceOrder in [2, 3]:
348                    _, start = self.ScreenToTraceAndTime(x, self.dragStart[1])
349            else:
350                if self.traceOrder in [0, 1]:
351                    trace, start = \
352                        self.ScreenToTraceAndTime(x-self.zoomwdwwidth/2, y)
353                    trace, end = \
354                        self.ScreenToTraceAndTime(x+self.zoomwdwwidth/2, y)
355                elif self.traceOrder in [2, 3]:
356                    trace, start = \
357                        self.ScreenToTraceAndTime(x, y-self.zoomwdwwidth/2)
358                    trace, end = \
359                        self.ScreenToTraceAndTime(x, y+self.zoomwdwwidth/2)
360
361            if start > end:
362                end, start = start, end
363
364            self.ZoomWindow = [trace, start, end]
365            self.dragStart = None
366            self._drawPicksAndZoom()
367            if plotter and trace:
368                # store relative times for possible menu call to stw.
369                srel = start - trace.stats.starttime \
370                    + trace.get_info('t-origin')
371                erel = end - trace.stats.starttime + trace.get_info('t-origin')
372                plotter.setZoomWindow( trace.index(True), srel, erel )
373
374    def OnMouseLeftUp(self, evt):
375        """
376        Release of left mouse button.
377        """
378        if self.HasCapture():
379            self._releaseMouse(evt)
380            self.dragStart = None
381           
382        #print "dbg: double buffered", self.IsDoubleBuffered()
383
384        #if self.mousedouble:
385        #    self.mousedouble = False
386
387        x, y = evt.GetPositionTuple()
388        trace, tracetime = self.ScreenToTraceAndTime(x, y)
389
390        # Send SH command to define/clear phase if not waiting for user input.
391        if self.mousedouble:
392            if self.last_defined_phase:
393                station = "%s.%s" % (trace.stats.network,trace.stats.station)
394                _sendShCommand( "@PHASE CLEAR %s %s manual" % (
395                    self.last_defined_phase,station.upper()) )
396        elif trace and self.parent._user_selection:
397            station = "%s.%s" % (trace.stats.network,trace.stats.station)
398            _sendShCommand( "@PHASE DEFINE %s %s ;;; %s" % (station.upper(),
399                fromUTCDateTime(tracetime),trace.stats.channel[-1].upper()),
400                name="mouse evt" )
401            self._drawSinglePick( trace, tracetime )
402        self.mousedouble = False
403
404        if self.interactive == "trace_time":
405            self.parent._user_selection = (trace, tracetime)
406        elif self.interactive == "relative":
407            self.parent._user_selection = self.ScreenToRelativeTime(x, y)
408        else:
409            raise NameError("unknown selection mode")
410       
411        evt.Skip()
412
413    def OnScroll(self, evt):
414        """
415        Update scroll offset.
416        """
417        if self.traceOrder in [0, 1]:
418            self.Scrolled = self.GetScrollPos(wx.VERTICAL) * \
419                                               self.GetScrollPixelsPerUnit()[1]
420        elif self.traceOrder in [2, 3]:
421            self.Scrolled = self.GetScrollPos(wx.HORIZONTAL) * \
422                                               self.GetScrollPixelsPerUnit()[0]
423        self.OnSize(evt, renewOverlay=True)
424        evt.Skip()
425
426    def OnIdle(self, evt):
427        """
428        Idle processing. Redraw is done here.
429        """
430        if self.refresh:
431            self._timewindow = get_runtime("timewindow")
432            self._normtype = get_runtime("normtype")
433            wx.BeginBusyCursor( cursor=wx.StockCursor(_cursorNameToID("wait")) )
434            self._resetTimeRange()
435            self._drawTraces()
436            self._drawPicksAndZoom()
437            wx.EndBusyCursor()
438            self.refresh = False
439
440    def _set_modes(self, name, mode):
441        """
442        Method for altering the selection mode. Intended to be called from
443        other threads using "CallAfter".
444        """
445        setattr(self, name, mode)
446
447    def OnSize(self, evt, renewOverlay=False):
448        """
449        Called if redraw is requested.
450
451        Just set flag for redraw - execution triggered from IDLE event.
452        In case of resize renew overlay buffers.
453        """
454        # if window was resized, renew overlay buffers
455        if evt and (renewOverlay or evt.GetEventType() == wx.EVT_SIZE.typeId):
456            self._setup()
457            self.overlay_picks.Reset()
458            self.overlay_drag.Reset()
459
460        # painting is only done in idle state
461        self.refresh = True
462   
463    def OnPaint( self, evt ):
464        #ks dc2 = wx.AutoBufferedPaintDCFactory(self)
465        #ks dc2.Clear()
466        #ks x, y = self.CalcUnscrolledPosition(0, 0)
467        #ks dc2.Blit(0, 0, width, height, canvas, x, y)
468        if self._bitmap:
469            dc = wx.BufferedPaintDC(self, self._bitmap)
470
471    # Usually called via message system.
472    def OnNotifyTimeAxis(self, orientation):
473        """
474        Handle changes of time axis mode (absolute vs. relative time
475        axis).
476        """
477        recent = self.relativeAxis
478        self.relativeAxis = orientation == 1
479        if recent != self.relativeAxis:
480            # BAD!
481            self.OnSize(True, renewOverlay=True)
482
483    def OnNotifyTraceOrder(self, order):
484        """
485        Handle changes of trace plotting mode (horizontal vs. vertical
486        modes).
487        """
488        recent = self.traceOrder
489        self.traceOrder = order
490        if recent != self.traceOrder:
491            # reset display height
492#            msgs.sendMessage('notify.tracecount', count=len(self.traces),
493#                                                                    reset=True)
494#            msgs.sendMessage('ui.change.displayheight',
495#                                                       height=len(self.traces))
496            self._setup()
497            # BAD: setting evt=True
498            self.OnSize(True, renewOverlay=True)
499
500    def OnNotifyHeight(self, height):
501        """
502        Handle changes of display height.
503        """
504        if self.traceOrder in [0, 1]:
505            self.space = self.GetClientSize()[1]
506        elif self.traceOrder in [2, 3]:
507            self.space = self.GetClientSize()[0]
508
509        if height != len(self.traces):
510            self.space *= len(self.traces) / float(height)
511
512        self._setup()
513        self.OnSize(None)
514
515    def OnNotifyPhaseName(self, name):
516        self.phasename = name
517
518    # helper functions
519    #@timeit
520    def ScreenToRelativeTime(self, x, y):
521        """
522        Returns relative time inside screen.
523        """
524        x, y = self.CalcUnscrolledPosition((x,y))
525        # horizontal display
526        if self.traceOrder in [0, 1]:
527            timepos = x
528        # vertical display
529        elif self.traceOrder in [2, 3]:
530            timepos = y
531
532        # horizontal
533        if self.traceOrder in [0, 1]:
534            pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
535        # vertical
536        elif self.traceOrder in [2, 3]:
537            pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
538
539        # horizontal plotting
540        if self.traceOrder in [0, 1]:
541            timepos -= STATION_INFO + MARGINS[1]
542        # vertical plotting
543        elif self.traceOrder == 2:
544            if self.relativeAxis:
545                timepos -= length + MARGINS[0]
546            else:
547                timepos -= pixel_width + MARGINS[0]
548            timepos *= -1
549        elif self.traceOrder == 3:
550            timepos -= STATION_INFO
551
552        if self.timewindow[1] is None:
553            duration = self.relend - self.relstart
554            start = self.relstart
555        else:
556            duration = self.timewindow[1] - self.timewindow[0]
557            start = self.timewindow[0]
558
559        t = timepos * duration / pixel_width + start
560
561        return t
562
563    #@timeit
564    def ScreenToTraceAndTime(self, x, y):
565        """
566        Returns trace instance and time code derived from cursor
567        position inside graphical panel.
568        """
569        x, y = self.CalcUnscrolledPosition((x,y))
570        # horizontal display
571        if self.traceOrder in [0, 1]:
572            tracepos, timepos = y, x
573        # vertical display
574        elif self.traceOrder in [2, 3]:
575            tracepos, timepos = x, y
576
577        # get trace under cursor
578        trace = None
579        theight = self.traceheight // 2
580        for t in self.traces:
581            shx_graphics = t.get_graphics()
582            if shx_graphics == None:
583                continue
584            if 'midpoint' not in shx_graphics:
585                continue
586            if tracepos >= shx_graphics.midpoint - theight and \
587                                          tracepos <= shx_graphics.midpoint + theight:
588                trace = t
589                break
590
591        # get time code
592        if trace and self.relativeAxis:
593            start = trace.stats.starttime
594            end = trace.stats.endtime
595            shx_graphics = trace.get_graphics()
596            if shx_graphics != None:
597                pixel_width = shx_graphics.PlotPixels
598            # only needed for vertical plotting
599            length = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
600        else:
601            # global time axis
602            start = self.start
603            end = self.end
604            # horizontal
605            if self.traceOrder in [0, 1]:
606                pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
607            # vertical
608            elif self.traceOrder in [2, 3]:
609                pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
610
611        if self.timewindow[1] is None:
612            duration = end - start
613        else:
614            duration = self.timewindow[1] - self.timewindow[0]
615            start += self.timewindow[0]
616
617        # horizontal plotting
618        if self.traceOrder in [0, 1]:
619            timepos -= STATION_INFO + MARGINS[3]
620        # vertical plotting
621        elif self.traceOrder == 2:
622            if self.relativeAxis:
623                timepos -= length + MARGIN
624            else:
625                timepos -= pixel_width + MARGIN
626            timepos *= -1
627        elif self.traceOrder == 3:
628            timepos -= STATION_INFO
629
630        t = timepos * duration / pixel_width
631        timestamp = start + t
632
633        # do not return trace if timestamp is outside, region is enlarged
634        # by one pixel.
635        pixel_duration = duration/pixel_width
636        self.pixel_duration = pixel_duration
637        if trace and (trace.stats.starttime > timestamp + pixel_duration or \
638                             trace.stats.endtime + pixel_duration < timestamp):
639            trace = None
640
641        return trace, timestamp
642
643    #@timeit
644    def TraceAndTimeToScreen(self, trace, time):
645        """
646        Return x, y from given trace (midpoint) and time code.
647        """
648        if trace is None:
649            return
650        shx_graphics = trace.get_graphics()
651        if shx_graphics == None:
652            return
653
654        # horizontal
655        if self.traceOrder in [0, 1]:
656            pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
657        # vertical
658        elif self.traceOrder in [2, 3]:
659            pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
660
661        zerotime = trace.stats.starttime - trace.get_info("t-origin")
662        reltime = time - zerotime
663       
664        if self.timewindow[1] == None:
665            start = self.relstart
666            end = self.relend
667        else:
668            start = self.timewindow[0]
669            end = self.timewindow[1]
670       
671        # relative position inside trace window
672        relpos = (reltime - start) / (end - start) * pixel_width
673
674        # horizontal
675        if self.traceOrder in [0, 1]:
676            x, y = self.CalcScrolledPosition((
677                relpos + STATION_INFO + MARGINS[3],
678                shx_graphics.midpoint
679            ))
680        # vertical
681        elif self.traceOrder == 2:
682            x, y = self.CalcScrolledPosition(
683                     (shx_graphics.midpoint, self.height - relpos - STATION_INFO))
684        elif self.traceOrder == 3:
685            x, y = self.CalcScrolledPosition(
686                                   (shx_graphics.midpoint, relpos + STATION_INFO))
687        return x, y
688   
689
690    #@timeit
691    def _setup(self):
692        w, h = self.GetClientSize()
693        if self.traceOrder in [0, 1]:
694            if self.space is None:
695                self.space = h
696            self.SetVirtualSizeWH(w, self.space)
697            self.SetScrollRate(0, 20)
698        elif self.traceOrder in [2, 3]:
699            if self.space is None:
700                self.space = w
701            self.SetVirtualSizeWH(self.space, h)
702            self.SetScrollRate(20, 0)
703
704    #@timeit
705    def _captureMouse(self, evt):
706        self.CaptureMouse()
707
708        ## create overlay
709        #dc = wx.ClientDC(self)
710        #odc = wx.DCOverlay(self.overlay_drag, dc)
711        #odc.Clear()
712
713        self.dragStart = evt.GetPosition()
714        self._debug("mouse captured at", self.dragStart)
715
716    #@timeit
717    def _releaseMouse(self, evt):
718        self.ReleaseMouse()
719
720        ## restore view
721        #dc = wx.ClientDC(self)
722        #odc = wx.DCOverlay(self.overlay_drag, dc)
723        #odc.Clear()
724        #del odc
725        self.overlay_drag.Reset()
726
727        self._debug("mouse released")
728
729    #@timeit
730    def _drawPicksAndZoom(self):
731        """
732        Draw picks.
733        """
734        phaselist = PhaseList()
735        _picks = {}
736        for trc in self.traces:
737            sname = "%s.%s" % (trc.stats.network,trc.stats.station)
738            for phase in phaselist.getPhaseList(sname):
739                if phase.comp and trc.stats.channel[-1] != phase.comp:
740                    continue
741                pcol = phaselist.picktypeColor( phase.picktype )
742                if pcol not in _picks.keys():
743                    _picks[pcol] = []
744                _picks[pcol].append(
745                    (phase.name,self.TraceAndTimeToScreen(trc,phase.picktime))
746                )
747
748        if not _picks and not self.ZoomWindow:
749            return
750
751        dc = wx.ClientDC(self)
752        # odc disables redraw
753        #odc = wx.DCOverlay(self.overlay_picks, dc)
754        #odc.Clear()
755        dc.SetBrush(wx.TRANSPARENT_BRUSH)
756        dc.SetFont( wx.Font( 8, wx.DEFAULT, wx.NORMAL, wx.NORMAL ) )
757
758        th2 = self.traceheight / 2 - 2
759        # draw picks
760        for pcolor in _picks.keys():
761            dc.SetPen(wx.Pen(pcolor, 1))
762            dc.SetTextForeground(pcolor)
763            for pick in _picks[pcolor]:
764                if self.traceOrder in [0, 1]:
765                    dc.DrawLine(
766                        pick[1][0], pick[1][1] - th2,
767                        pick[1][0], pick[1][1] + th2
768                    )
769                    if pick[0] != '_Mark_':
770                        dc.DrawText(pick[0], pick[1][0] + 2, pick[1][1] - th2 - 10)
771                elif self.traceOrder in [2, 3]:
772                    dc.DrawLine(
773                        pick[1][0] - th2, pick[1][1],
774                        pick[1][0] + th2, pick[1][1],
775                    )
776                    if pick[0] != '_Mark_':
777                        dc.DrawRotatedText(pick[0], pick[1][0] - th2 + 2,
778                                                            pick[1][1] - 2, 90)
779        #del odc
780
781        #draw zoom window
782        if self.ZoomWindow:
783            trace, start, end = self.ZoomWindow
784            if  trace is not None:
785                shx_graphics = trace.get_graphics()
786                if shx_graphics == None:
787                    return
788                start = self.TraceAndTimeToScreen(trace, start)
789                end = self.TraceAndTimeToScreen(trace, end)
790                self._debug("draw zoom window")
791                self.__zoombox( start, end, shx_graphics.midpoint,
792                    color="Blue")
793            else:
794                self.zoomwdwwidth = 0
795                self.ZoomWindow = None
796                if plotter:
797                    plotter.setZoomWindow( None, None, None )
798                self.__zoombox( None, None, None )
799
800
801    def _drawSinglePick( self, trace, tracetime, color=None ):
802        "Just for quick respomse to the user."
803        pname = self.last_defined_phase
804        if not pname:
805            pname = "NewPhase"
806        if color == None:
807            color = 'red'
808        dc = wx.ClientDC(self)
809        dc.SetPen(wx.Pen(color, 1))
810        dc.SetTextForeground(color)
811        dc.SetFont( wx.Font( 8, wx.DEFAULT, wx.NORMAL, wx.NORMAL ) )
812        th2 = self.traceheight / 2 - 2
813        x, y, = self.TraceAndTimeToScreen(trace,tracetime)
814        dc.DrawLine( x, y-th2, x, y+th2 )
815        if pname != '_Mark_':
816            dc.DrawText( pname, x+2, y-th2-10 )
817       
818
819    #@timeit
820    def _drawTraces(self):
821        """
822        Do the real plotting of waveforms including station text and
823        time scale.
824        """
825        # Get dimensions of the drawing area and number of traces to plot.
826        # We cannot use the Screen module because of loop imports.
827        if self._timewindow[1] is None:
828            timewindow = get_runtime("extend")
829        else:
830            timewindow = self._timewindow
831        self.width, self.height = self.GetVirtualSize()
832        numTraces = len(self.traces) - Overlays.numberOfOverlays()
833        globalnorm = self._normtype.startswith("A")
834
835        # Delete zoom window if number of traces has changed.
836        # -> i.e. not drawn in this redraw.
837        if self.last_number_of_traces != numTraces and self.ZoomWindow:
838            self.ZoomWindow = None
839            if plotter:
840                plotter.setZoomWindow( None, None, None )
841        self.last_number_of_traces = numTraces
842        self._drawXor( 'clear' )
843
844        # How much space is left for each trace:
845        #    theight: height of trace space in pixel units
846        #    pltwidth: width of trace space in pixel units
847        # Compute trace height (theight) as float, otherwise trace positioning
848        # is weird when many traces on screen.
849        if self.traceOrder in [0, 1]:
850            if numTraces:
851                theight = float(self.height - TIMESCALE - MARGINS[0] \
852                    - MARGINS[2]) / numTraces
853            else:
854                theight = float(self.height - TIMESCALE)
855            pltwidth = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
856        elif self.traceOrder in [2, 3]:
857            if numTraces:
858                theight = (self.width - TIMESCALE - MARGINS[1] \
859                    - MARGINS[3]) / numTraces
860            theight = self.width - TIMESCALE
861            pltwidth = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
862        else:
863            raise ValueError("unknown trace order %d" % self.traceorder)
864       
865        # Trace height used in scaling routines,
866        #   make it visible to other methods.
867        self.traceheight = theight
868
869        # Bitmap holding the final figure, canvas->DC drawing to this bitmap.
870        self._bitmap = wx.EmptyBitmap(self.width, self.height)
871        canvas = wx.MemoryDC(self._bitmap)
872        canvas.SetBrush(wx.TRANSPARENT_BRUSH)
873        canvas.Clear()
874        canvas.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
875
876        # If nothing to display, clear eand exit.
877        if numTraces == 0:
878            # just clearing the screen
879            dc = wx.AutoBufferedPaintDCFactory(self)
880            dc.Clear()
881            return
882
883        if self.traceOrder == 0:
884            trcs = self.traces[::-1]
885        elif self.traceOrder in [1, 2, 3]:
886            trcs = self.traces
887       
888        # Should traces overlap or stay within their y-ranges?
889        # Overlapping traces: create taller bitmaps than computed before
890        # in theight (zoomed up by a zoom factor). When the trace-bitmaps
891        # are copied to the screen buffer, they overlap each other.
892        overlapping_traces = get_runtime( "overlapping_traces", True )
893
894        # Init overlays positions. Overlays change the trace counting, since
895        # we need less trace positions.
896        Overlays.initPlot( len(self.traces), self.traceOrder )
897
898        # Loop over traces.
899        for i, t in enumerate(trcs):
900            if getattr(t, "shx_graphics", None) is None:
901                # Trace not yet established.
902                continue
903            if not self.relativeAxis:
904                start = self.start
905                end = self.end
906            else:
907                start = t.stats.starttime
908                end = start + self.maxDuration
909            try:
910                tzoom = t.stats.sh.ZOOM
911            except:
912                tzoom = 1.
913
914            # zheight is the (integer) height of the drawing box, possibly
915            # zoomed by a factor if overlapping_traces is selected.
916            if overlapping_traces:
917                # Increased zheight produces overlapping of traces on display.
918                zheight = int( theight * tzoom )
919                try:
920                    prepzoom = 1./tzoom
921                except:
922                    prepzoom = 1.
923            else:
924                zheight = int( theight )
925                prepzoom = 0.9  # why not 1.?
926           
927            # Limit zheight to screen height, otherwise we run into memory
928            # problems.
929            if zheight > self.height:
930                zheight = self.height
931
932            # Amplitude scaling factor within bitmap.
933            norm = globalnorm and self.maxAmplitude or self._normtype[1]
934           
935            # After eliminating t.prepare_image_data (too slow), this
936            # shx_graphics-stuff is probably not needed any more.
937            shx_graphics = t.get_graphics()
938            if shx_graphics == None:
939                continue
940
941            # Create an emtpy bitmap for this trace, but only if the pixel
942            # height is larger than 4. Doesn't make sense to put time and
943            # CPU into figures with only 2 pixels height. Draw simple line
944            # instead.
945            if zheight > 4:
946                if self.traceOrder in [0, 1]:
947                    bitmap = wx.EmptyBitmap(pltwidth, zheight)
948                elif self.traceOrder in [2, 3]:
949                    bitmap = wx.EmptyBitmap(zheight, pltwidth)
950            else:
951                bitmap = None
952            if bitmap is not None:
953                # buffer to draw in
954                dbuffer = wx.MemoryDC(bitmap)
955                #dbuffer = dbuffer
956                dbuffer.SetBrush(wx.TRANSPARENT_BRUSH)
957                dbuffer.Clear()
958                dbuffer.SetPen(wx.Pen((45,45,45), 1))
959            else:
960                dbuffer = None
961
962            # Time position of trace.
963            _shift = t.get_info("t-origin") - timewindow[0]
964            if _shift < 0.:
965                _shift = 0.
966            # On positive shifts the bitmap is moved out of the screen
967            # to the right. On negative shifts the bitmap is recomputed
968            # and always starts at 0. Fails if the bitmap move completely
969            # out to the left. Bitmap should be cleared in this case.
970
971            # calculate offset to global time scale
972            plotStartSec = t.stats.starttime + _shift - start
973            plotoffset = 0
974            if plotStartSec or not self.relativeAxis:
975                # re-calculate offset
976                portion = plotStartSec / (timewindow[1] - timewindow[0])
977                plotoffset = portion * pltwidth
978
979            # Get drawing attributes, color and other styles.
980            attrib = t.get_info("attrib")
981            try:
982                style = get_style(attrib)
983            except KeyError:
984                style = get_default_attrib(t.get_info("METASTATUS")) #AttributeBlock()
985            cmode = None
986            try:
987                color = map(int, style.color.split(','))
988                cmode = 'i'
989            except:
990                try:
991                    color = map(float, style.color.split(','))
992                    cmode = 'f'
993                except:
994                    color = style.color
995                    cmode = 'o'
996            if cmode == 'i':
997                if 0 < max(color) <= 1:
998                    # e.g. 1,0,0 means red, multiply with 255
999                    color = [255*x for x in color]
1000            elif cmode == 'f':
1001                color = map( int, [255*x for x in color] )
1002            linestyle = getattr(wx, style.linestyle.upper())
1003
1004            # Draw seismogram into trace bitmap.
1005            if dbuffer is not None:
1006                dbuffer.SetPen(wx.Pen(color, style.linewidth, linestyle))
1007                dbuffer.BeginDrawing()
1008                dbuffer.DrawLines(
1009                    self.prepImage(t,pltwidth,zheight,timewindow,prepzoom,norm)
1010                )
1011                dbuffer.EndDrawing()
1012
1013            # Copy trace bitmap to canvas
1014            if dbuffer is not None:
1015                if self.traceOrder in [0, 1]:
1016                    if overlapping_traces:
1017                        fac = i + 0.5 - 0.5*tzoom
1018                    else:
1019                        fac = i
1020                    plotpos = Overlays.plotPos( i+1, fac * theight + MARGINS[0])
1021                    canvas.Blit(
1022                        plotoffset + STATION_INFO + MARGINS[3], 
1023                        plotpos, 
1024                        pltwidth,
1025                        zheight,
1026                        dbuffer,
1027                        0,
1028                        0,
1029                        wx.AND
1030                    )
1031                elif self.traceOrder == 2:
1032                    canvas.Blit(i * theight, -plotoffset + MARGIN, zheight,
1033                                                pltwidth, dbuffer, 0, 0, wx.AND)
1034                elif self.traceOrder == 3:
1035                    canvas.Blit(i * theight, STATION_INFO + plotoffset, zheight,
1036                                                pltwidth, dbuffer, 0, 0, wx.AND)
1037
1038            # Put labels on traces.
1039            # Trace numbering
1040            if self.traceOrder in [1, 2, 3]:
1041                idx = i + 1
1042            elif self.traceOrder == 0:
1043                idx = -i + len(self.traces)
1044            # Currently no choice on type of label, just station and component.
1045            txt = "%d: %s %s" % (idx, t.stats.station, t.stats.channel[-1])
1046            canvas.SetPen(wx.Pen('Grey', 1, wx.LONG_DASH))
1047            # Draw labels and helper lines (zero lines)
1048            w, h, _, _ = canvas.GetFullTextExtent(txt)
1049            shx_graphics.midpoint = tmp = i * theight + theight//2 + MARGINS[0]
1050            if self.traceOrder in [0, 1]:
1051                canvas.DrawText(txt, 5 + MARGINS[3], tmp - h // 2)
1052                canvas.DrawLine(STATION_INFO + MARGINS[3], tmp,
1053                    self.width - MARGINS[1], tmp)
1054            elif self.traceOrder == 2:
1055                canvas.DrawRotatedText(txt, tmp - h // 2, self.height - MARGIN, 90)
1056                canvas.DrawLine(shx_graphics.midpoint, MARGIN,
1057                    tmp, self.height - STATION_INFO)
1058            elif self.traceOrder == 3:
1059                canvas.DrawRotatedText(txt, tmp - h // 2,
1060                                    STATION_INFO - (STATION_INFO - w) // 2, 90)
1061                canvas.DrawLine(tmp, STATION_INFO, tmp, self.height - MARGIN)
1062
1063            # End of trace loop
1064
1065        # Draw time axis.
1066        canvas.SetPen(wx.Pen('Black', 1))
1067        if self.relativeAxis:
1068            start, end = timewindow
1069
1070        if self.traceOrder in [0, 1]:
1071            PARTS = 5.  # axis split into X parts
1072            length = self.width - MARGINS[1] - MARGINS[3] - STATION_INFO  # pixel length of time axis
1073            fixpos = self.height - TIMESCALE - MARGINS[2]  # here: y coordinate
1074            varpos_start = STATION_INFO + MARGINS[3] # here: x start
1075        elif self.traceOrder in [2, 3]:
1076            PARTS = 4.
1077            length = self.height - MARGIN - STATION_INFO
1078            fixpos = self.width - TIMESCALE + 10  # here: x coordinate
1079            if self.traceOrder == 2:
1080                varpos_start = MARGIN  # here: y start
1081            elif self.traceOrder == 3:
1082                varpos_start = STATION_INFO
1083
1084        chunk_t, chunk, labeloffset, pixoffset, PARTS \
1085            = self.niceNumbers( start, end, length, PARTS )
1086        chunk2 = chunk / 5.
1087
1088        # basic time axis line
1089        if self.traceOrder in [0, 1]:
1090            canvas.DrawLine(varpos_start, fixpos,
1091                                                 length + varpos_start, fixpos)
1092        elif self.traceOrder in [2, 3]:
1093            canvas.DrawLine(fixpos, varpos_start,
1094                                                 fixpos, varpos_start + length)
1095
1096        # sections with ticks and time string
1097
1098        try:
1099            prec = abs(int(round(1 / math.log10(chunk_t))))
1100        except:
1101            prec = 1
1102        mask = "%%.%uf" % prec
1103        for i in range(int(PARTS)):
1104            if isinstance(start, UTCDateTime):
1105                timecode = labeloffset + start + i * chunk_t
1106                txt = timecode.strftime("%H:%M:%S.") + \
1107                                             str(timecode.microsecond//1000)
1108            else:
1109                txt = mask % (labeloffset + start + i * chunk_t)
1110
1111            tw = canvas.GetFullTextExtent(txt)[0]
1112
1113            if self.traceOrder in [0, 1]:
1114                varpos_current = pixoffset + varpos_start + i * chunk
1115                # time string
1116                canvas.DrawText(txt, varpos_current - tw / 2,
1117                                                fixpos + TIMESCALE_OFFSET_TEXT)
1118                # major tick
1119                canvas.DrawLine(varpos_current, fixpos,
1120                                varpos_current, fixpos + TIMESCALE_TICKS_MAJOR)
1121                # minor ticks
1122                for j in range(1, 5):
1123                    canvas.DrawLine(
1124                        varpos_current + j * chunk2,
1125                        fixpos,
1126                        varpos_current + j * chunk2,
1127                        fixpos + TIMESCALE_TICKS_MINOR
1128                    )
1129            elif self.traceOrder in [2, 3]:
1130                if self.traceOrder == 2:
1131                    varpos_current = pixoffset \
1132                        + varpos_start + (PARTS - i) * chunk
1133                elif self.traceOrder == 3:
1134                    varpos_current = pixoffset + varpos_start + i * chunk
1135                canvas.DrawRotatedText(txt, fixpos + TIMESCALE_OFFSET_TEXT,
1136                                                   varpos_current + tw / 2, 90)
1137                canvas.DrawLine(fixpos, varpos_current,
1138                                fixpos + TIMESCALE_TICKS_MAJOR, varpos_current)
1139                for j in range(1, 5):
1140                    if self.traceOrder == 2:
1141                        j -= 5
1142                    canvas.DrawLine(
1143                        fixpos,
1144                        varpos_current + j * chunk2,
1145                        fixpos + TIMESCALE_TICKS_MINOR,
1146                        varpos_current + j * chunk2
1147                     )
1148
1149        if self.do_screenshot:
1150            self.save_shot(canvas)
1151            self.do_screenshot = False
1152        # detach
1153        #ks canvas.SelectObject(wx.NullBitmap)
1154        del canvas
1155        self.Refresh()
1156        self.Update()
1157
1158    def prepImage( self, trc, width, height, timewindow, zoom, norm ):
1159        "Return polygon ready for plotting via wx."
1160
1161        windowstart, windowend = timewindow
1162        try:
1163            pt = trc.slice_relative(windowstart, windowend)
1164        except:
1165            return []
1166           
1167        # scaling in time direction
1168        duration_total = windowend - windowstart
1169        duration_trace = pt.stats.endtime - pt.stats.starttime
1170        pixel_width = duration_trace * width / duration_total
1171       
1172        # save pixel_width (but dont' really understand)
1173        shx_graphics = trc.get_graphics()
1174        if shx_graphics != None:
1175            shx_graphics.PlotPixels = pixel_width
1176
1177        # make sure, norm is a number
1178        if hasattr(norm, "lower"):
1179            norm = norm.upper()[0]
1180            if norm == "W":
1181                norm = abs(pt.max())
1182            elif norm == "T":
1183                norm = self.max()
1184            else:
1185                raise ValueError("Invalid input for normation!")
1186       
1187        # scaling in amplitude direction
1188        try:
1189            trczoom = trc.stats.sh.ZOOM
1190        except:
1191            trczoom = 1.0
1192        amplscale = 1. / norm * height / 2 * zoom * trczoom
1193        return zip(
1194            np.linspace( 0., pixel_width, len(pt.data) ),
1195            pt.data*amplscale + height/2
1196        )
1197
1198    #@timeit
1199    def save_shot(self, dc):
1200        """
1201        Save PNG screen shot (PS later)
1202        """
1203        size = dc.Size
1204
1205        shot = wx.EmptyBitmap(size.width, size.height)
1206        shot_dc = wx.MemoryDC()
1207        shot_dc.SelectObject(shot)
1208
1209        shot_dc.Blit(0, 0, size.width, size.height, dc, 0, 0)
1210        shot_dc.SelectObject(wx.NullBitmap)
1211        img = shot.ConvertToImage()
1212        img.SaveFile(self.do_screenshot, wx.BITMAP_TYPE_PNG)
1213        log_message("info", "screen dump saved in: %s" % self.do_screenshot)
1214   
1215    #@timeit
1216    def niceNumbers( self, start, end, pixwidth, parts ):
1217        "2150 surf min"
1218        num = (end - start) / parts
1219        pixscaling = pixwidth / (end-start)
1220        tmp = 10**int(math.log10(num))  # integer power of 10
1221        nn = tmp   # best nice number is either this number or *2, *5, *10
1222        dist = abs(tmp-num)
1223        for i in (2,5,10):
1224            ndist = abs(num - i*tmp)
1225            if ndist < dist:
1226                nn = i*tmp
1227                dist = ndist
1228        labelstart = nn*int(start/nn)
1229        if (labelstart-start) < -1.e-5:
1230            labelstart += nn
1231        repeat = int((end-labelstart)/nn) + 1
1232        pixspan = nn * pixscaling
1233        pixoffset = (labelstart-start) * pixscaling
1234        labeloffset = labelstart - start
1235        #print nn, labelstart, pixoffset, repeat, '--', num, '.', start, end
1236        return (nn, pixspan, labeloffset, pixoffset, repeat)
1237
1238    @staticmethod
1239    def _debug(*args):
1240        log_message('debug.wx', " ".join([str(i) for i in args]))
1241        #print "dbg:", " ".join([str(i) for i in args])
1242
1243    #@timeit
1244    def _resetTimeRange(self, **kwargs):
1245        """
1246        Gather information about traces. Called on redraw (at idle state).
1247        """
1248        mint = UTCDateTime() # now
1249        maxt = UTCDateTime(0) # 1970
1250        minrel = 1e20
1251        maxrel = 0
1252        maxDuration = 0
1253        amplitude = 0
1254
1255        for t in self.traces:
1256            s = t.get_info("relstart")
1257            e = t.get_info("relend")
1258            if s < minrel:
1259                minrel = s
1260            if e > maxrel:
1261                maxrel = e
1262            if t.stats.starttime < mint:
1263                mint = t.stats.starttime
1264            if t.stats.endtime > maxt:
1265                maxt = t.stats.endtime
1266            # needed for relative time axis
1267            l = e - s
1268            if l > maxDuration:
1269                maxDuration = l
1270            # normation
1271            # no time window set or normtype forces full traces
1272            #if self._timewindow[1] is None or self._normtype.startswith("A"):
1273            if self._timewindow[1] is None:
1274                data = t.data
1275            else:
1276                try:
1277                    data = t.get_datawindow(*self._timewindow)
1278                except:
1279                    continue
1280            t_ampl = max( abs(data.max()), abs(data.min()) )
1281            if t_ampl > amplitude:
1282                amplitude = t_ampl
1283
1284        self.start = mint
1285        self.relstart = minrel
1286        self.end = maxt
1287        self.relend = maxrel
1288        self.maxDuration = maxDuration
1289        self.maxAmplitude = amplitude
1290        recent = self.timewindow
1291        self.timewindow = get_runtime("timewindow")
1292        if recent != self.timewindow:
1293            # time window changed, delete zoom box
1294            self.ZoomWindow = None
1295            if plotter:
1296                plotter.setZoomWindow( None, None, None )
1297
1298   
1299    #@timeit
1300    def __zoombox(self, start, end, midpoint, color="DARKORANGE", size=1):
1301        if start == None:
1302            self._drawXor( 'rect', (None,None,None,None) )
1303            return
1304        marginfactor = 0.3
1305        mf2 = (1 - marginfactor) * 2
1306        th2 = self.traceheight / 2
1307        offset = midpoint - th2 - self.Scrolled
1308        if self.traceOrder in [0, 1]:
1309            self._drawXor( 'rect', (start[0], offset + th2 * marginfactor,
1310                end[0] - start[0], th2 * mf2) )
1311        elif self.traceOrder in [2, 3]:
1312            self._drawXor( 'rect', (offset + th2 * marginfactor, start[1],
1313                th2 * mf2, end[1] - start[1]) )
1314
1315
1316class plotterWindow(wx.Frame):
1317    """
1318    Basic program frame including menu.
1319   
1320    Shortcuts
1321           b(Beam) d(DelTimeWdw) g(PhasePg) n(PhasePn) p(PhaseP) s(SetTimeWdw)
1322           u(Undo Filter) 0(PrevEvent) 1(NextEvent) 2(PhaseSg) 8(Filter 1-8Hz)
1323    Ctrl-  f(FK) l(FilterSROLP) n(Norm) o(Overlapping) q(Quit) s(SaveT&P)
1324           w(FilterWWSSNSP) y(SortByDistance)
1325    """
1326    def __init__(self, parent, title, size=(640,280), position=(100,100)):
1327        # save instance for external access
1328        global plotter
1329        plotter = self
1330        wx.Frame.__init__(self, parent, title=title, size=size,
1331              style=wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE |
1332                    wx.FRAME_FLOAT_ON_PARENT)
1333
1334        self.SetPosition(position)
1335        self.addMenuEntries()
1336        self.canvas = traceCanvas(self, [])
1337        self.CreateStatusBar()
1338        self.SetStatusText("%s %s" % (NAME, VERSION))
1339        self.Disable()
1340        self.Show()
1341        self.Enable()
1342        self._user_selection = 999.
1343        self._zoomwdw = None
1344        self._seltrace = None
1345        self.flag_beam = False
1346        self.flag_norm = False
1347        self.flag_overlap = True
1348        self.pardialog = None
1349   
1350    def showMessage( self, msg, error=False ):
1351        mode = wx.OK
1352        if error:
1353            mode |= wx.ICON_ERROR
1354        dlg = wx.MessageDialog(self, msg, "An error occured!", mode )
1355        dlg.ShowModal()
1356        dlg.Destroy()
1357       
1358    def addMenuEntries( self ):
1359        menubar = wx.MenuBar()
1360        # file menu
1361        fileMenu = wx.Menu()
1362        self.addEntry( fileMenu, 'Read Data File(s)',
1363            'Read data file(s) in one of various formats', self.OnReadDataFile )
1364        self.addEntry( fileMenu, 'Write Data File',
1365            'Write all traces into file. Specify format as extension.',
1366            self.OnWriteDataFile )
1367        fileMenu.AppendSeparator()
1368        self.addEntry( fileMenu, 'Read FDSN net/station list',
1369            'Read station list from FDSN webserver', self.OnReadFdsnStations )
1370        self.addEntry( fileMenu, 'Read FDSN circle area',
1371            'Read stations in circle area from FDSN webserver',
1372            self.OnReadFdsnCircle )
1373        fileMenu.AppendSeparator()
1374        self.addEntry( fileMenu, 'Complete Metadata (FDSN)',
1375            'Complete metadata of traces using FDSN webserver',
1376            self.OnCompleteMeta )
1377        fileMenu.AppendSeparator()
1378        self.addEntry( fileMenu, '&Quit\tCtrl+Q', 'Quit Graphics', self.OnQuit )
1379        # window menu
1380        windowMenu = wx.Menu()
1381        self.addEntry( windowMenu, '&Set Time Window\tS',
1382            'Set Zoom window as new time window', self.OnSetTimeWindow )
1383        self.addEntry( windowMenu, '&Del Time Window\tD',
1384            'Delete time window', self.OnDelTimeWindow )
1385        self.addEntry( windowMenu, '&Norm All/Single\tCtrl+N',
1386            'Normalize All Traces Together / Normalize Traces Separately',
1387            self.OnNorm )
1388        self.addEntry( windowMenu, '&Overlapping/Clipped Traces\tCtrl+O',
1389            'Traces overlap when zooming / Traces are clipped when zooming '\
1390            +'amplitudes', self.OnOverlappingTraces )
1391        # traces menu
1392        tracesMenu = wx.Menu()
1393        self.addEntry( tracesMenu, '&Demean\tCtrl+D',
1394            'Remove mean value from all traces', self.OnDemean )
1395        self.addEntry( tracesMenu, 'Sort by Distance\tCtrl+Y',
1396            'Sort traces by distance', self.OnSortByDistance )
1397        tracesMenu.AppendSeparator()
1398        self.addEntry( tracesMenu, 'Delete Selected Trace',
1399            'Delete trace with zoom window', self.OnDeleteTrace )
1400        self.addEntry( tracesMenu, 'Delete Selected And Above',
1401            'Delete trace with zoom window and all above',
1402            self.OnDeleteTraceAndAbove )
1403        # array menu
1404        arrayMenu = wx.Menu()
1405        self.addEntry( arrayMenu, 'FK',
1406            'Compute FK for all traces on selected time window', self.OnFK )
1407        self.addEntry( arrayMenu, '&FK (Auto Freq)\tCtrl+F',
1408            'Compute FK with automatic freq window', self.OnFKAutoFreq )
1409        self.addEntry( arrayMenu, 'Beam / Del Beam\tB',
1410            'Compute/delete beam for all traces', self.OnBeam )
1411        # simulate menu
1412        simulateMenu = wx.Menu()
1413        self.addEntry( simulateMenu, 'Simulate &WWSSN-SP\tCtrl+W',
1414            'Simulate WWSSN-SP instrument on all traces', self.OnSimulateWWSSNSP )
1415        self.addEntry( simulateMenu, 'Simulate WWSSN-LP',
1416            'Simulate WWSSN-LP instrument on all traces', self.OnSimulateWWSSNLP )
1417        self.addEntry( simulateMenu, 'Simulate LRSM-SP',
1418            'Simulate WWSSN-SP instrument on all traces', self.OnSimulateLRSMSP )
1419        self.addEntry( simulateMenu, 'Simulate LRSM-LP',
1420            'Simulate WWSSN-LP instrument on all traces', self.OnSimulateLRSMLP )
1421        self.addEntry( simulateMenu, 'Simulate KIRNOS',
1422            'Simulate KIRNOS instrument on all traces', self.OnSimulateKIRNOS )
1423        self.addEntry( simulateMenu, 'Simulate SRO-&LP\tCtrl+L',
1424            'Simulate SRO-LP instrument on all traces', self.OnSimulateSROLP )
1425        simulateMenu.AppendSeparator()
1426        self.addEntry( simulateMenu, 'Filter BP 1-8Hz\t8',
1427            'Apply Butterworth bandpass 1-8Hz', self.OnFilterBP_1_8 )
1428        simulateMenu.AppendSeparator()
1429        self.addEntry( simulateMenu, 'Undo Filter\tu',
1430            'Return to original traces', self.OnSimulateUndo )
1431        # events menu
1432        eventsMenu = wx.Menu()
1433        self.addEntry( eventsMenu, 'Get Eventlist',
1434            'Retrieve event list from FDSN server', self.OnGetEventlist )
1435        self.addEntry( eventsMenu, 'Next Event\t1',
1436            'Prepare next event time from list', self.OnNextEvent )
1437        self.addEntry( eventsMenu, 'Previous Event\t0',
1438            'Prepare previous event time from list', self.OnPrevEvent )
1439        phaseMenu = wx.Menu()
1440        self.addEntry( phaseMenu, 'Pg\tg',
1441            'Set phase for picking to Pg', self.OnSetPhase_Pg )
1442        self.addEntry( phaseMenu, 'Pn\tn',
1443            'Set phase for picking to Pn', self.OnSetPhase_Pn )
1444        self.addEntry( phaseMenu, 'Sg\t2',
1445            'Set phase for picking to Sg', self.OnSetPhase_Sg )
1446        self.addEntry( phaseMenu, 'Sn',
1447            'Set phase for picking to Sn', self.OnSetPhase_Sn )
1448        self.addEntry( phaseMenu, 'P\tp',
1449            'Set phase for picking to P', self.OnSetPhase_P )
1450        self.addEntry( phaseMenu, 'pP',
1451            'Set phase for picking to pP', self.OnSetPhase_pP )
1452        self.addEntry( phaseMenu, 'sP',
1453            'Set phase for picking to sP', self.OnSetPhase_sP )
1454        self.addEntry( phaseMenu, 'PP',
1455            'Set phase for picking to PP', self.OnSetPhase_PP )
1456        self.addEntry( phaseMenu, 'S',
1457            'Set phase for picking to S', self.OnSetPhase_S )
1458        self.addEntry( phaseMenu, 'PKP',
1459            'Set phase for picking to PKP', self.OnSetPhase_PKP )
1460        self.addEntry( phaseMenu, 'PKPdf',
1461            'Set phase for picking to PKPdf', self.OnSetPhase_PKPdf )
1462        self.addEntry( phaseMenu, 'PKPbc',
1463            'Set phase for picking to PKPbc', self.OnSetPhase_PKPbc )
1464        self.addEntry( phaseMenu, 'PKPab',
1465            'Set phase for picking to PKPab', self.OnSetPhase_PKPab )
1466        self.addEntry( phaseMenu, 'pPKPdf',
1467            'Set phase for picking to pPKPdf', self.OnSetPhase_pPKPdf )
1468        self.addEntry( phaseMenu, 'pPKPbc',
1469            'Set phase for picking to pPKPbc', self.OnSetPhase_pPKPbc )
1470        self.addEntry( phaseMenu, 'pPKPab',
1471            'Set phase for picking to pPKPab', self.OnSetPhase_pPKPab )
1472        self.addEntry( phaseMenu, 'Use ev_phase',
1473            'Use phase name from parameter ev_phase',
1474            self.OnSetPhaseUseEvPhase )
1475        # control menu
1476        controlMenu = wx.Menu()
1477        self.addEntry( controlMenu, 'Save Traces/Params\tCtrl+S',
1478            'Save trace info and parameters into a recovery command file',
1479            self.OnSaveTracesAndParamsQuick )
1480        self.addEntry( controlMenu, 'Save Traces/Params as ...',
1481            'Save trace info and parameters into a recovery command file',
1482            self.OnSaveTracesAndParams )
1483        self.addEntry( controlMenu, 'Recover Traces/Params',
1484            'Recover traces and parameters from default recovery',
1485            self.OnRecoverTracesAndParamsQuick )
1486        self.addEntry( controlMenu, 'Recover Traces/Params from ...',
1487            'Recover traces and parameters from a recovery command file',
1488            self.OnRecoverTracesAndParams )
1489        tracesMenu.AppendSeparator()
1490        self.addEntry( controlMenu, 'Open Parameter Window',
1491            'Open parameter dialog', self.OnOpenParams )
1492        # test menu
1493        testMenu = wx.Menu()
1494        self.addEntry( testMenu, 'Read GR * BHZ of an event',
1495            'fdsnws gr * * bhz 7-aug-15_00:16:30 420', self.OnTest1 )
1496        self.addEntry( testMenu, 'Read SX,TH * BHZ of an event',
1497            'fdsnws sx,th * * bhz 7-aug-15_00:16:30 420', self.OnTest2 )
1498        # put menus and menu bar in place
1499        menubar.Append( fileMenu, 'File' )
1500        menubar.Append( windowMenu, 'Display' )
1501        menubar.Append( tracesMenu, 'Traces' )
1502        menubar.Append( arrayMenu, 'Array' )
1503        menubar.Append( simulateMenu, 'Filter' )
1504        menubar.Append( eventsMenu, 'Events' )
1505        menubar.Append( phaseMenu, 'Phases' )
1506        menubar.Append( controlMenu, 'Control' )
1507        menubar.Append( testMenu, 'Test' )
1508        self.SetMenuBar( menubar )
1509        self.Centre()
1510   
1511    def addEntry( self, menutitle, entrytext, entrydescr, callback ):
1512        #self.menu_entry_id += 1
1513        #qmi = wx.MenuItem( menutitle, self.menu_entry_id, entrytext,
1514        #    help=entrydescr )
1515        #menutitle.AppendItem( qmi )
1516        #self.Bind( wx.EVT_MENU, callback, id=self.menu_entry_id )       
1517        item = menutitle.Append( wx.ID_ANY, entrytext, entrydescr )
1518        self.Bind( wx.EVT_MENU, callback, item )
1519
1520    def OnQuit( self, e ):
1521        # Cannot quit SHX, _sendShCommand waits for command to be executed
1522        # i.e. no way to terminate wx smoothly. Quit SHX after Close also not
1523        # possible, runs into communication issues between threads.
1524        #_sendShCommand( "quit y" )
1525        self.Close()
1526   
1527    def OnNorm( self, e ):
1528        if self.flag_norm:
1529            _sendShCommand( 'norm aw' )
1530        else:
1531            _sendShCommand( 'norm sw' )
1532        self.flag_norm = not self.flag_norm
1533
1534    def OnOverlappingTraces( self, e ):
1535        if self.flag_overlap:
1536            _sendShCommand( 'fct overlapping_traces false' )
1537        else:
1538            _sendShCommand( 'fct overlapping_traces true' )
1539        self.flag_overlap = not self.flag_overlap
1540
1541    def OnDemean( self, e ):
1542        _sendShCommand( 'demean all' )
1543   
1544    def OnDeleteTrace( self, e ):
1545        if self._seltrace == None:
1546            self.showMessage( "no trace selected" )
1547        else:
1548            _sendShCommand( "del %d" % self._seltrace )
1549
1550    def OnDeleteTraceAndAbove( self, e ):
1551        if self._seltrace == None:
1552            self.showMessage( "no trace selected" )
1553        else:
1554            _sendShCommand( "del |%d|-|$dsptrcs|" % self._seltrace )
1555
1556    def OnTest1( self, e ):
1557        _sendShCommand( 'fdsnws gr * * bhz 7-aug-15_00:16:30 420' )
1558
1559    def OnTest2( self, e ):
1560        _sendShCommand( 'fdsnws sx,th * * bhz 7-aug-15_00:16:30 420' )
1561
1562    def OnSetTimeWindow( self, e ):
1563        _sendShCommand( 'stw %g %g' % self._zoomwdw )
1564
1565    def OnDelTimeWindow( self, e ):
1566        _sendShCommand( 'dtw' )
1567
1568    def OnReadDataFile( self, e ):
1569        "Read one or more data files."
1570        style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
1571        dlg = wx.FileDialog( self, "Select Data File", defaultDir=os.getcwd(),
1572            style=style )
1573        dlg.ShowModal()
1574        filelist = dlg.GetPaths()
1575        retcode = dlg.GetReturnCode()
1576        dlg.Destroy()
1577        if retcode != wx.ID_OK:
1578            return
1579        cmd = '\n'.join(["@READO %s ALL" % fname.replace('/','\\') \
1580            for fname in filelist])
1581        _sendShCommand( cmd )
1582   
1583    def OnWriteDataFile( self, e ):
1584        "Writes all traces to file."
1585        legal_formats = ('mseed','q','gse','gse2','sac')
1586        style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
1587        dlg = wx.FileDialog( self, "Select Output File", defaultDir=os.getcwd(),
1588            style=style )
1589        dlg.ShowModal()
1590        outfile = dlg.GetPath()
1591        retcode = dlg.GetReturnCode()
1592        dlg.Destroy()
1593        if retcode != wx.ID_OK:
1594            return
1595        format = os.path.splitext(outfile)[1].lstrip('.').lower()
1596        if format not in legal_formats:
1597            msg = "Format '%s' not supported. Nothing written." % format
1598            self.showMessage( msg, error=True )
1599            return
1600        cmd = "@WRITEO %s ALL %s" % (outfile.replace('/','\\'),format.upper())
1601        _sendShCommand( cmd )
1602   
1603    def OnReadFdsnStations( self, e ):
1604        ap = AnalysisPar()
1605        cmd = "fdsnws %s %s %s %s %s %s /addr=%s" % (
1606            ap.getValueAsString( 'readws_net' ),
1607            ap.getValueAsString( 'readws_station' ),
1608            ap.getValueAsString( 'readws_loc' ),
1609            ap.getValueAsString( 'readws_chan' ),
1610            ap.getValueAsString( 'read_time' ),
1611            ap.getValueAsString( 'read_length' ),
1612            ap.getValueAsString( 'readws_server' ),
1613        )
1614        cmd += "\nmeta all complete /addr=%s" \
1615            % ap.getValueAsString('readws_server')
1616        _sendShCommand( cmd )       
1617
1618    def OnReadFdsnCircle( self, e ):
1619        ap = AnalysisPar()
1620        cmd = "fdsnws %s,%s,%s * * %s %s %s /addr=%s" % (
1621            ap.getValueAsString( 'sta_latitude' ),
1622            ap.getValueAsString( 'sta_longitude' ),
1623            ap.getValueAsString( 'sta_radius' ),
1624            ap.getValueAsString( 'readws_chan' ),
1625            ap.getValueAsString( 'read_time' ),
1626            ap.getValueAsString( 'read_length' ),
1627            ap.getValueAsString( 'readws_server' ),
1628        )
1629        cmd += "\nmeta all complete /addr=%s" \
1630            % ap.getValueAsString('readws_server')
1631        _sendShCommand( cmd )
1632   
1633    def OnCompleteMeta( self, e ):
1634        ap = AnalysisPar()
1635        _sendShCommand(
1636            "meta all complete /addr=%s" % ap.getValueAsString('readws_server')
1637        )
1638
1639    def OnSortByDistance( self, e ):
1640        ap = AnalysisPar()
1641        lat = ap.getValueAsString( 'epi_latitude' )
1642        lon = ap.getValueAsString( 'epi_longitude' )
1643        if not lat or not lon:
1644            self.showMessage( "no epicenter defined" )
1645            return
1646        _sendShCommand( "sort_by_distance %s %s" % (lat,lon) )
1647   
1648    def OnFK( self, e ):
1649        if not self._zoomwdw:
1650            return
1651        timea, timeb = self._zoomwdw
1652        if timea == None or timeb == None:
1653            return
1654        ap = AnalysisPar()
1655        cmd = "shx_menu_fk %g %g %s %s %s" % (timea,timeb,
1656            ap.getValueAsString('fk_min_freq'),
1657            ap.getValueAsString('fk_max_freq'),
1658            ap.getValueAsString('fk_max_slowness'))
1659        _sendShCommand( cmd )
1660   
1661    def OnFKAutoFreq( self, e ):
1662        if not self._zoomwdw:
1663            return
1664        timea, timeb = self._zoomwdw
1665        if timea == None or timeb == None:
1666            return
1667        frq = 1./(timeb-timea)
1668        ap = AnalysisPar()
1669        cmd = "shx_menu_fk %g %g %s %s %s" % (timea,timeb,frq,(10.*frq),
1670            ap.getValueAsString('fk_max_slowness'))
1671        print "dbg: fkcmd:", cmd
1672        _sendShCommand( cmd )
1673    def OnBeam( self, e ):
1674        if self.flag_beam:
1675            _sendShCommand( "shx_menu_delbeam" )
1676        else:
1677            _sendShCommand( "shx_menu_beam" )
1678        self.flag_beam = not self.flag_beam
1679   
1680    def OnSimulateWWSSNSP( self, e ):
1681        _sendShCommand( 'shx_menu_simulate wwssn-sp' )
1682
1683    def OnSimulateWWSSNLP( self, e ):
1684        _sendShCommand( 'shx_menu_simulate wwssn-lp' )
1685
1686    def OnSimulateLRSMSP( self, e ):
1687        _sendShCommand( 'shx_menu_simulate lrsm-sp' )
1688
1689    def OnSimulateLRSMLP( self, e ):
1690        _sendShCommand( 'shx_menu_simulate lrsm-lp' )
1691
1692    def OnSimulateKIRNOS( self, e ):
1693        _sendShCommand( 'shx_menu_simulate kirnos' )
1694
1695    def OnSimulateSROLP( self, e ):
1696        _sendShCommand( 'shx_menu_simulate sro-lp' )
1697
1698    def OnFilterBP_1_8( self, e ):
1699        _sendShCommand( 'shx_menu_filter bp_1hz_8hz_4' )
1700
1701    def OnSimulateUndo( self, e ):
1702        _sendShCommand( 'shx_menu_simulate undo' )
1703   
1704    def OnOpenParams( self, e ):
1705        self.pardialog = ParamDialog( self )
1706        self.pardialog.Show()
1707
1708    def OnGetEventlist( self, e ):
1709        _sendShCommand( 'shx_menu_get_eventlist' )
1710
1711    def OnNextEvent( self, e ):
1712        _sendShCommand( 'shx_menu_next_event;;' )
1713
1714    def OnPrevEvent( self, e ):
1715        _sendShCommand( 'shx_menu_next_event -1' )
1716       
1717    def OnSaveTracesAndParamsQuick( self, e ):
1718        sloutfile = 'TRACES_PARAMS_RECOVER.SHC'
1719        cmd = "param savetraces %s\n" % sloutfile\
1720            +"param saveascmd %s /append" % sloutfile
1721        print "dbg: cmd:", cmd
1722        _sendShCommand( cmd )
1723   
1724    def OnSaveTracesAndParams( self, e ):
1725        style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
1726        dlg = wx.FileDialog( self, "Select Save File", defaultDir=os.getcwd(),
1727            style=style, wildcard='*.SHC' )
1728        dlg.ShowModal()
1729        outfile = dlg.GetFilename()
1730        retcode = dlg.GetReturnCode()
1731        dlg.Destroy()
1732        if retcode != wx.ID_OK:
1733            return
1734        sloutfile = outfile.replace("/","\\")
1735        cmd = "param savetraces %s\n" % sloutfile\
1736            +"param saveascmd %s /append" % sloutfile
1737        print "dbg: cmd:", cmd
1738        _sendShCommand( cmd )
1739   
1740    def OnRecoverTracesAndParamsQuick( self, e ):
1741        _sendShCommand( 'TRACES_PARAMS_RECOVER' )
1742   
1743    def OnRecoverTracesAndParams( self, e ):
1744        style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
1745        dlg = wx.FileDialog( self, "Select Recovery File",
1746            defaultDir=os.getcwd(), style=style, wildcard='*.SHC' )
1747        dlg.ShowModal()
1748        recfile = dlg.GetFilename()
1749        retcode = dlg.GetReturnCode()
1750        dlg.Destroy()
1751        if retcode != wx.ID_OK:
1752            return
1753        slrecfile = recfile.replace('/','\\')
1754        if slrecfile.endswith('.SHC'):
1755            slrecfile = os.path.splitext(slrecfile)[0]
1756        _sendShCommand( slrecfile )
1757
1758    def OnSetPhase_Pg( self, e ):
1759        _sendShCommand( "@PHASE DEFAULT_PHASE Pg" )
1760    def OnSetPhase_Pn( self, e ):
1761        _sendShCommand( "@PHASE DEFAULT_PHASE Pn" )
1762    def OnSetPhase_Sg( self, e ):
1763        _sendShCommand( "@PHASE DEFAULT_PHASE Sg" )
1764    def OnSetPhase_Sn( self, e ):
1765        _sendShCommand( "@PHASE DEFAULT_PHASE Sn" )
1766    def OnSetPhase_P( self, e ):
1767        _sendShCommand( "@PHASE DEFAULT_PHASE P" )
1768    def OnSetPhase_pP( self, e ):
1769        _sendShCommand( "@PHASE DEFAULT_PHASE pP" )
1770    def OnSetPhase_sP( self, e ):
1771        _sendShCommand( "@PHASE DEFAULT_PHASE sP" )
1772    def OnSetPhase_PP( self, e ):
1773        _sendShCommand( "@PHASE DEFAULT_PHASE PP" )
1774    def OnSetPhase_S( self, e ):
1775        _sendShCommand( "@PHASE DEFAULT_PHASE S" )
1776    def OnSetPhase_PKP( self, e ):
1777        _sendShCommand( "@PHASE DEFAULT_PHASE PKP" )
1778    def OnSetPhase_PKPdf( self, e ):
1779        _sendShCommand( "@PHASE DEFAULT_PHASE PKPdf" )
1780    def OnSetPhase_PKPbc( self, e ):
1781        _sendShCommand( "@PHASE DEFAULT_PHASE PKPbc" )
1782    def OnSetPhase_PKPab( self, e ):
1783        _sendShCommand( "@PHASE DEFAULT_PHASE PKPab" )
1784    def OnSetPhase_pPKPdf( self, e ):
1785        _sendShCommand( "@PHASE DEFAULT_PHASE pPKPdf" )
1786    def OnSetPhase_pPKPbc( self, e ):
1787        _sendShCommand( "@PHASE DEFAULT_PHASE pPKPbc" )
1788    def OnSetPhase_pPKPab( self, e ):
1789        _sendShCommand( "@PHASE DEFAULT_PHASE pPKPab" )
1790
1791    def OnSetPhaseUseEvPhase( self, e ):
1792        _sendShCommand( "@PHASE DEFAULT_PHASE %s"
1793            % AnalysisPar().getValueAsString('ev_phase'))
1794
1795    def redraw(self):
1796        self.canvas.OnSize(None)
1797   
1798    def setZoomWindow( self, trace, start, end ):
1799        self._seltrace = trace
1800        self._zoomwdw = (start,end)
1801
1802
1803class ParamDialog(wx.Dialog):
1804
1805    def __init__( self, parent ):
1806        wx.Dialog.__init__( self, parent, -1, "Parameters", size=(350,750) )
1807        #sizer =  self.CreateTextSizer('Param X')
1808       
1809        ypos = 10
1810        height = 26
1811        textwidth = 270
1812        btnwidth = 70
1813        space = 11
1814       
1815        self.SetBackgroundColour( 'white' )
1816        self.vtext = {}
1817        self.vbutton = {}
1818
1819        self.ap = AnalysisPar()
1820        for pname in self.ap.sortedParams():
1821            pvalue = self.ap.getValueAsString( pname )
1822            ptext = "%s : %s" % (pname,pvalue)
1823            self.vbutton[pname] = wx.Button( self, label='Change',
1824                pos=(space,ypos-4), size=(btnwidth,height) )
1825            self.vtext[pname] = wx.StaticText( self, label=ptext,
1826                pos=(btnwidth+2*space,ypos), size=(textwidth,height) )
1827            #self.vtext[pname].SetBackgroundColour( 'red' )
1828            self.Bind( wx.EVT_BUTTON, partial(self.OnButton,pname),
1829                self.vbutton[pname] )
1830            ypos += 28
1831   
1832    def OnButton( self, pname, e, *args ):
1833        oldvalue = self.ap.getValueAsString( pname )
1834        ptype = self.ap.getType( pname )
1835        dlg = QueryString( self, "Change %s" % pname,
1836            "New value for '%s' (type %s):" % (pname,ptype), oldvalue )
1837        btn = dlg.ShowModal()
1838        result = dlg.getValue()
1839        dlg.Destroy()
1840        if result == None:
1841            # Clicked on Cancel
1842            return
1843        if self.validate( result, ptype ):
1844            if result == "":
1845                cmd = "param set %s ;;" % pname
1846            else:
1847                # phase names need capcnv switched off
1848                cmd = "@PARAM SET %s %s" % (pname,result)
1849            _sendShCommand( cmd )
1850        else:
1851            msg = "Illegal input: got '%s' but need type %s" % (result,ptype)
1852            dlg = wx.MessageDialog(self, msg, "Illegal input",
1853                                                     wx.OK | wx.ICON_ERROR)
1854            dlg.ShowModal()
1855            dlg.Destroy()
1856        #self.updateVariables() # done via command line
1857   
1858    def updateVariables( self, *args ):
1859        for pname in self.ap.sortedParams():
1860            pvalue = self.ap.getValueAsString( pname )
1861            self.vtext[pname].SetLabel( "%s : %s" % (pname,pvalue) )
1862   
1863    def validate( self, result, ptype ):
1864        if ptype == 'int':
1865            try:
1866                i = int( result )
1867                return True
1868            except:
1869                return False
1870        elif ptype in ('float','floatN'):
1871            if ptype == 'floatN' and result == "":
1872                return True
1873            try:
1874                i = float( result )
1875                return True
1876            except:
1877                return False
1878        elif ptype == 'datetime':
1879            try:
1880                r = toUTCDateTime( result )
1881                return (r != None)
1882            except:
1883                return False
1884        elif ptype in ('string','file'):
1885            return (result != "")
1886        elif ptype == 'fileN':
1887            return True
1888        else:
1889            print "Program bug, unknown type '%s'" % ptype
1890            return False
1891
1892
1893class QueryString(wx.Dialog):
1894
1895    def __init__( self, parent, title, prompt, oldvalue ):
1896        wx.Dialog.__init__( self, parent, -1, title, size=(350,120) )
1897        self.SetBackgroundColour( 'white' )
1898        wx.StaticText( self, label=prompt, pos=(10,10) )
1899        self.text = wx.TextCtrl( self, value=oldvalue, pos=(10,30),
1900            size=(200,26) )
1901        tbtn = wx.Button( self, label='OK', pos=(50,70) )
1902        self.Bind( wx.EVT_BUTTON, self.OnTextOk, tbtn )
1903        cbtn = wx.Button( self, label='Cancel', pos=(150,70) )
1904        self.Bind( wx.EVT_BUTTON, self.OnTextCancel, cbtn )
1905        self.result = None
1906   
1907    def OnTextOk( self, e ):
1908        self.result = self.text.GetValue()
1909        self.Destroy()
1910   
1911    def OnTextCancel( self, e ):
1912        self.result = None
1913        self.Destroy()
1914   
1915    def getValue( self ):
1916        return self.result
1917       
1918
1919
1920
1921def _sendShCommand( cmdstring, name="default" ):
1922    "Send command to command line interpreter."
1923    wx.BeginBusyCursor()
1924    try:
1925        msgs.sendMessage("ui.command", cmd=cmdstring, name=name )
1926        exc = None
1927    except Exception as exc:
1928        pass
1929    wx.EndBusyCursor()
1930    if exc:
1931        raise exc
1932
1933
1934def ui_events(func):
1935    """
1936    """
1937    def wrapper(payload, msgid):
1938        result = func(payload)
1939        if msgid is not None:
1940            msgs.sendMessage(msgid, value=result, msgid=msgid)
1941    return wrapper
1942
1943
1944def _cursorNameToID( mode ):
1945    mode = mode.lower()
1946    if mode == "cross":
1947        _c = wx.CURSOR_CROSS
1948    elif mode == "normal":
1949        _c = wx.CURSOR_DEFAULT
1950    elif mode == "left":
1951        _c = wx.CURSOR_POINT_LEFT
1952    elif mode == "right":
1953        _c = wx.CURSOR_POINT_RIGHT
1954    elif mode == "busy":
1955        _c = wx.CURSOR_WAIT
1956    elif mode == "wait":
1957        _c = wx.CURSOR_ARROWWAIT
1958    return _c
1959
1960
1961@ui_events
1962def __set_cursor(mode):
1963    """
1964    Interface method for altering cursor style on canvas. Thread-safe.
1965    """
1966    _c = _cursorNameToID( mode )
1967    myCursor = wx.StockCursor(_c)
1968    global plotter
1969    wx.CallAfter(plotter.canvas.SetCursor, myCursor)
1970subscribe_ui_event(__set_cursor, "cursor")
1971
1972
1973@ui_events
1974def __select_gui(mode):
1975    """
1976    Interface method for selecting time and/or trace by user interaction. While
1977    UI remains responsive, everything waits for user input.
1978
1979    mode is one of the following commands:
1980    - relative (screen coordinates to relative offset) - for selecting windows
1981    - trace_time (trace under cursor and timestamp) - for picking
1982    """
1983    global plotter
1984    wx.CallAfter(plotter.canvas._set_modes, "interactive", mode)
1985    plotter._user_selection = None
1986    while plotter._user_selection is None:
1987        time.sleep(TIME_SLOT)
1988    return plotter._user_selection
1989subscribe_ui_event(__select_gui, "input")
1990
1991
1992@ui_events
1993def __screenshot(fname):
1994    """
1995    Trigger screen shot creation. Thread-safe. Image will be saved immediately.
1996    """
1997    global plotter
1998    plotter.canvas.do_screenshot = fname
1999    plotter.canvas.refresh = True
2000    wx.CallAfter(plotter.canvas.OnIdle, None)
2001subscribe_ui_event(__screenshot, "screenshot")
2002
2003
2004@ui_events
2005def __updateparams(unused):
2006    """
2007    Trigger redraw of parameter dialog.
2008    """
2009    global plotter
2010    if plotter and plotter.pardialog != None:
2011        wx.CallAfter(plotter.pardialog.updateVariables, None)
2012subscribe_ui_event(__updateparams, "updateparams")
2013
2014@ui_events
2015def __openparams(unused):
2016    """
2017    Open parameter dialog box.
2018    """
2019    global plotter
2020    if plotter and plotter.pardialog == None:
2021        wx.CallAfter(plotter.OnOpenParams, None)
2022subscribe_ui_event(__openparams, "openparams")
2023
Note: See TracBrowser for help on using the repository browser.