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

Revision 1129, 64.5 KB checked in by klaus, 4 years ago (diff)

first working GUI for parameter set

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