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

Revision 1116, 50.1 KB checked in by klaus, 4 years ago (diff)

more simple menu entries

  • 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
11from SeismicHandler.basics.messages import msgs, subscribe_ui_event, \
12                                            log_message, get_style#, get_runtime
13from SeismicHandler.config import get_runtime
14from SeismicHandler.core import Traces, Overlays
15from SeismicHandler.basics.codes import NAME, VERSION
16from SeismicHandler.basics.tools import AttributeBlock, timeit
17from SeismicHandler.basics.analysispar import PhaseList
18from obspy.core import read, UTCDateTime
19from obspy.sh.core import fromUTCDateTime
20
21# width of station info
22STATION_INFO = 85
23# margins (top, right, bottom, left)
24MARGINS = [10, 10, 10, 10]
25# space for time-scale
26TIMESCALE = 40
27# offset for time string
28TIMESCALE_OFFSET_TEXT = 8
29# length of minor/major ticks
30TIMESCALE_TICKS_MINOR = 3
31TIMESCALE_TICKS_MAJOR = 7
32
33# regular checking of UI data
34TIME_SLOT = .03
35
36# holds window instance
37plotter = None
38
39class traceCanvas(SP.ScrolledPanel):
40    """
41    All drawing methods for plotting traces. Include all mouse-related
42    functions.
43    """
44    #@timeit
45    def __init__(self, parent, fnames=[]):
46        SP.ScrolledPanel.__init__(self, parent, style=wx.BORDER_SIMPLE)
47
48        # class data
49        self.parent = parent
50        self.traces = Traces
51        self.ZoomWindow = None
52        self.zoomwdwwidth = 0
53        self.Scrolled = 0
54        self.mousedouble = 0
55        self.refresh = False
56        self.AllowDoubleClick = True
57        self.do_screenshot = False
58        self.pixel_duration = None
59        self.last_defined_phase = None
60        self.wheelpos = 0
61
62        # temporary layer for mouse motion
63        self.overlay_drag = wx.Overlay()
64        # layer for picks and zoom box
65        self.overlay_picks = wx.Overlay()
66
67        # application defaults
68        self.relativeAxis = True
69        self.traceOrder = 0
70        self.phasename = "P"
71        self.space = None
72        self.interactive = "trace_time"
73
74        self.ppi = wx.ScreenDC().GetPPI()
75
76        self._setup()
77
78        # event binding
79        self.Bind(wx.EVT_PAINT, self.OnPaint)
80        self.Bind(wx.EVT_SIZE, self.OnPaint)
81        self.Bind(wx.EVT_IDLE, self.OnIdle)
82        self.Bind(wx.EVT_MOTION, self.OnMouseMotion)
83        self.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
84        self.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseLeftDouble)
85        self.Bind(wx.EVT_RIGHT_DCLICK, self.OnMouseRightDouble)
86        self.Bind(wx.EVT_RIGHT_UP, self.OnMouseRightUp)
87        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)
88        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
89        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
90        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
91        self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
92
93        # messaging system
94#        msgs.subscribe(self.OnNotifyHeight, 'GUT.displayheight')
95#        msgs.subscribe(self.OnNotifyPhaseName, 'GUI.phasename')
96#        msgs.subscribe(self.OnNotifyTraceOrder, 'GUI.traceorder')
97#        msgs.subscribe(self.OnNotifyTimeAxis, 'GUT.timeaxis')
98        msgs.subscribe(self.__runtime_changer, "setruntime")
99
100        # init
101        self.OnPaint(None)
102        self.SetFocus()
103
104    def __runtime_changer(self, name, value):
105        """
106        Helper method for acting on certain runtime events. Called via message
107        system.
108        """
109        if name != "RUNTIME:styles":
110            return
111        margins = value.get("MARGINS", None)
112
113        if not margins:
114            return
115
116        # convert inch values to pixels
117        margins = [
118            margins[0] * self.ppi[1],
119            margins[1] * self.ppi[0],
120            margins[2] * self.ppi[1],
121            margins[3] * self.ppi[0]
122        ]
123
124        global MARGINS
125        if MARGINS != margins:
126            MARGINS = margins
127   
128    def closePhase( self, trace, abstime ):
129        "Return name and type of closest phase or None."
130        phaselist = PhaseList()
131        if not self.pixel_duration:
132            return None
133        toltime = 2*self.pixel_duration
134        station = "%s.%s" % (trace.stats.network,trace.stats.station)
135        for phase in phaselist.getPhaseList(station):
136            tdiff = abs( phase.picktime-abstime )
137            if tdiff < toltime:
138                return (phase.name,phaselist.picktypeName(phase.picktype))
139        return None
140
141    # event processing
142    def OnMouseWheel(self, evt):
143        maxwheelpos = 5
144        wheel = evt.GetWheelRotation()
145        if wheel > 0:
146            if self.wheelpos < maxwheelpos:
147                _sendShCommand( "zoom/rel all 2" )
148                self.wheelpos += 1
149        elif wheel < 0:
150            if self.wheelpos > -maxwheelpos:
151                _sendShCommand( "zoom/rel all 0.5" )           
152                self.wheelpos -= 1
153        evt.Skip()
154
155    def OnKeyDown(self, evt):
156        print "key"
157        kc = evt.GetKeyCode()
158        print kc
159        evt.Skip()
160#        if kc == ord("r"):
161
162    def OnMouseLeftDouble(self, evt):
163        trace, tracetime = self.ScreenToTraceAndTime(*(evt.GetPositionTuple()))
164        if trace:
165            self.mousedouble = True
166        #    print "dbg: closePhase", self.closePhase(trace,tracetime)
167        #    cphase = self.closePhase( trace, tracetime )
168        #    if cphase:
169        #        station = "%s.%s" % (trace.stats.network,trace.stats.station)
170        #        _sendShCommand(
171        #            "phase clear %s %s %s" % (cphase[0],station,cphase[1])
172        #        )
173        evt.Skip()
174
175    def OnMouseRightDouble(self, evt):
176        print "dbg: right double click"
177        self.zoomwdwwidth = 0.
178        evt.Skip()
179
180    def OnMouseLeftDown(self, evt):
181        self._captureMouse(evt)
182        trace, tracetime = self.ScreenToTraceAndTime(*(evt.GetPositionTuple()))
183        if trace:
184            #self.mousedouble = True
185            cphase = self.closePhase( trace, tracetime )
186            if cphase:
187                station = "%s.%s" % (trace.stats.network,trace.stats.station)
188                station = station.upper()
189                _sendShCommand(
190                    "@PHASE CLEAR %s %s %s" % (cphase[0],station,cphase[1]) \
191                    +"\n@PHASE DEFAULT_PHASE %s" % cphase[0]
192                )
193                self.last_defined_phase = cphase[0]
194                self._drawSinglePick( trace, tracetime,color='white' )
195            self._drawPicksAndZoom()
196        evt.Skip()
197
198    def OnMouseRightDown(self, evt):
199        self._captureMouse(evt)
200        if self.zoomwdwwidth:
201            pass
202        elif self.ZoomWindow:
203            trace, start, end = self.ZoomWindow
204            start = self.TraceAndTimeToScreen(trace, start)
205            end = self.TraceAndTimeToScreen(trace, end)
206            self.zoomwdwwidth = end[0] - start[0]
207        else:
208            self.zoomwdwwidth = 0
209
210    def OnMouseMotion(self, evt):
211        """
212        Mouse motion.
213        """
214
215        if not len(self.traces):
216            return
217
218        x, y = evt.GetPositionTuple()
219        trace, timestamp = self.ScreenToTraceAndTime(x, y)
220
221        try:
222            tid = trace.id
223        except AttributeError:
224            tid = "*"
225       
226        reltimestr = "%g" % self.ScreenToRelativeTime( x, y )
227
228        dragwidth = None
229        if evt.Dragging() and trace:
230            # new overlay for live display
231            dc = wx.ClientDC(self)
232            odc = wx.DCOverlay(self.overlay_drag, dc)
233            odc.Clear()
234            dc.SetBrush(wx.TRANSPARENT_BRUSH)
235
236            th2 = self.traceheight / 2
237            shx_graphics = trace.get_graphics()
238            if shx_graphics == None:
239                print "dbg: returned from callback, no shx_graphics"
240                return
241            mpth2 = shx_graphics.midpoint + th2
242            mmth2 = shx_graphics.midpoint - th2
243
244            # draw box
245            if evt.RightIsDown():
246                self.ZoomWindow = None
247                if self.zoomwdwwidth == 0.:
248                    self.__zoombox( dc, self.dragStart, (x, y),
249                        shx_graphics.midpoint, size=2 )
250                    dragwidth = x - self.dragStart[0]
251                else:
252                    _start = [x-self.zoomwdwwidth/2,y]
253                    _end = [x+self.zoomwdwwidth/2,y]
254                    self.__zoombox( dc, _start, _end,
255                        shx_graphics.midpoint, size=2 )
256                    dragwidth = _end[0] - _start[0]
257            # draw position line
258            elif evt.LeftIsDown():
259                dc.SetPen(wx.Pen("Blue", 1))
260                if self.traceOrder in [0, 1]:
261                    dc.DrawLine(
262                        x, mmth2 - self.Scrolled,
263                        x, mpth2 - self.Scrolled
264                    )
265                elif self.traceOrder in [2, 3]:
266                    dc.DrawLine(
267                        mmth2 - self.Scrolled, y,
268                        mpth2 - self.Scrolled, y
269                    )
270            del odc
271
272        stext = "%s - %s - %s" % (fromUTCDateTime(timestamp), reltimestr, tid)
273        if self.ZoomWindow:
274            stext += " - Z:%3.1f" % (self.ZoomWindow[2]-self.ZoomWindow[1])
275        elif dragwidth and self.pixel_duration:
276            stext += " - Z:%3.1f" % (dragwidth*self.pixel_duration)
277        self.parent.SetStatusText( stext )
278
279        # let the event propagate
280        evt.Skip()
281
282    def OnMouseRightUp(self, evt):
283        """
284        Release of right mouse button.
285        """
286        if self.HasCapture():
287            self._releaseMouse(evt)
288            x, y = evt.GetPositionTuple()
289
290            if self.zoomwdwwidth == 0:
291                trace, end = self.ScreenToTraceAndTime(x, y)
292                if self.traceOrder in [0, 1]:
293                    _, start = self.ScreenToTraceAndTime(self.dragStart[0], y)
294                elif self.traceOrder in [2, 3]:
295                    _, start = self.ScreenToTraceAndTime(x, self.dragStart[1])
296            else:
297                if self.traceOrder in [0, 1]:
298                    trace, start = \
299                        self.ScreenToTraceAndTime(x-self.zoomwdwwidth/2, y)
300                    trace, end = \
301                        self.ScreenToTraceAndTime(x+self.zoomwdwwidth/2, y)
302                elif self.traceOrder in [2, 3]:
303                    trace, start = \
304                        self.ScreenToTraceAndTime(x, y-self.zoomwdwwidth/2)
305                    trace, end = \
306                        self.ScreenToTraceAndTime(x, y+self.zoomwdwwidth/2)
307
308            if start > end:
309                end, start = start, end
310
311            self.ZoomWindow = [trace, start, end]
312            self.dragStart = None
313            self._drawPicksAndZoom()
314
315    def OnMouseLeftUp(self, evt):
316        """
317        Release of left mouse button.
318        """
319        if self.HasCapture():
320            self._releaseMouse(evt)
321            self.dragStart = None
322
323        #if self.mousedouble:
324        #    self.mousedouble = False
325
326        x, y = evt.GetPositionTuple()
327        trace, tracetime = self.ScreenToTraceAndTime(x, y)
328
329        # Send SH command to define/clear phase if not waiting for user input.
330        if self.mousedouble:
331            if self.last_defined_phase:
332                station = "%s.%s" % (trace.stats.network,trace.stats.station)
333                _sendShCommand( "@PHASE CLEAR %s %s manual" % (
334                    self.last_defined_phase,station.upper()) )
335        elif trace and self.parent._user_selection:
336            station = "%s.%s" % (trace.stats.network,trace.stats.station)
337            _sendShCommand( "@PHASE DEFINE %s %s ;;; %s" % (station.upper(),
338                fromUTCDateTime(tracetime),trace.stats.channel[-1].upper()),
339                name="mouse evt" )
340            self._drawSinglePick( trace, tracetime )
341        self.mousedouble = False
342
343        if self.interactive == "trace_time":
344            self.parent._user_selection = (trace, tracetime)
345        elif self.interactive == "relative":
346            self.parent._user_selection = self.ScreenToRelativeTime(x, y)
347        else:
348            raise NameError("unknown selection mode")
349       
350        evt.Skip()
351
352    def OnScroll(self, evt):
353        """
354        Update scroll offset.
355        """
356        if self.traceOrder in [0, 1]:
357            self.Scrolled = self.GetScrollPos(wx.VERTICAL) * \
358                                               self.GetScrollPixelsPerUnit()[1]
359        elif self.traceOrder in [2, 3]:
360            self.Scrolled = self.GetScrollPos(wx.HORIZONTAL) * \
361                                               self.GetScrollPixelsPerUnit()[0]
362        self.OnPaint(evt, renewOverlay=True)
363        evt.Skip()
364
365    def OnIdle(self, evt):
366        """
367        Idle processing. Redraw is only triggered here not on PAINT
368        event.
369        """
370        if self.refresh:
371            self._timewindow = get_runtime("timewindow")
372            self._normtype = get_runtime("normtype")
373            self._resetTimeRange()
374            self._drawTraces()
375            self._drawPicksAndZoom()
376            self.refresh = False
377
378    def _set_modes(self, name, mode):
379        """
380        Method for altering the selection mode. Intended to be called from
381        other threads using "CallAfter".
382        """
383        setattr(self, name, mode)
384
385    def OnPaint(self, evt, renewOverlay=False):
386        """
387        Called if redraw is requested.
388
389        Just set flag for redraw - execution triggered from IDLE event.
390        In case of resize renew overlay buffers.
391        """
392        # if window was resized, renew overlay buffers
393        if evt and (renewOverlay or evt.GetEventType() == wx.EVT_SIZE.typeId):
394            self._setup()
395            self.overlay_picks.Reset()
396            self.overlay_drag.Reset()
397
398        # painting is only done in idle state
399        self.refresh = True
400
401    # Usually called via message system.
402    def OnNotifyTimeAxis(self, orientation):
403        """
404        Handle changes of time axis mode (absolute vs. relative time
405        axis).
406        """
407        recent = self.relativeAxis
408        self.relativeAxis = orientation == 1
409        if recent != self.relativeAxis:
410
411            # BAD!
412            self.OnPaint(True, renewOverlay=True)
413
414    def OnNotifyTraceOrder(self, order):
415        """
416        Handle changes of trace plotting mode (horizontal vs. vertical
417        modes).
418        """
419        recent = self.traceOrder
420        self.traceOrder = order
421        if recent != self.traceOrder:
422            # reset display height
423#            msgs.sendMessage('notify.tracecount', count=len(self.traces),
424#                                                                    reset=True)
425#            msgs.sendMessage('ui.change.displayheight',
426#                                                       height=len(self.traces))
427            self._setup()
428            # BAD: setting evt=True
429            self.OnPaint(True, renewOverlay=True)
430
431    def OnNotifyHeight(self, height):
432        """
433        Handle changes of display height.
434        """
435        if self.traceOrder in [0, 1]:
436            self.space = self.GetClientSize()[1]
437        elif self.traceOrder in [2, 3]:
438            self.space = self.GetClientSize()[0]
439
440        if height != len(self.traces):
441            self.space *= len(self.traces) / float(height)
442
443        self._setup()
444        self.OnPaint(None)
445
446    def OnNotifyPhaseName(self, name):
447        self.phasename = name
448
449    # helper functions
450    #@timeit
451    def ScreenToRelativeTime(self, x, y):
452        """
453        Returns relative time inside screen.
454        """
455        x, y = self.CalcUnscrolledPosition((x,y))
456        # horizontal display
457        if self.traceOrder in [0, 1]:
458            timepos = x
459        # vertical display
460        elif self.traceOrder in [2, 3]:
461            timepos = y
462
463        # horizontal
464        if self.traceOrder in [0, 1]:
465            pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
466        # vertical
467        elif self.traceOrder in [2, 3]:
468            pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
469
470        # horizontal plotting
471        if self.traceOrder in [0, 1]:
472            timepos -= STATION_INFO + MARGINS[1]
473        # vertical plotting
474        elif self.traceOrder == 2:
475            if self.relativeAxis:
476                timepos -= length + MARGINS[0]
477            else:
478                timepos -= pixel_width + MARGINS[0]
479            timepos *= -1
480        elif self.traceOrder == 3:
481            timepos -= STATION_INFO
482
483        if self.timewindow[1] is None:
484            duration = self.relend - self.relstart
485            start = self.relstart
486        else:
487            duration = self.timewindow[1] - self.timewindow[0]
488            start = self.timewindow[0]
489
490        t = timepos * duration / pixel_width + start
491
492        return t
493
494    #@timeit
495    def ScreenToTraceAndTime(self, x, y):
496        """
497        Returns trace instance and time code derived from cursor
498        position inside graphical panel.
499        """
500        x, y = self.CalcUnscrolledPosition((x,y))
501        # horizontal display
502        if self.traceOrder in [0, 1]:
503            tracepos, timepos = y, x
504        # vertical display
505        elif self.traceOrder in [2, 3]:
506            tracepos, timepos = x, y
507
508        # get trace under cursor
509        trace = None
510        theight = self.traceheight // 2
511        for t in self.traces:
512            shx_graphics = t.get_graphics()
513            if shx_graphics == None:
514                continue
515            if 'midpoint' not in shx_graphics:
516                continue
517            if tracepos >= shx_graphics.midpoint - theight and \
518                                          tracepos <= shx_graphics.midpoint + theight:
519                trace = t
520                break
521
522        # get time code
523        if trace and self.relativeAxis:
524            start = trace.stats.starttime
525            end = trace.stats.endtime
526            shx_graphics = trace.get_graphics()
527            if shx_graphics != None:
528                pixel_width = shx_graphics.PlotPixels
529            # only needed for vertical plotting
530            length = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
531        else:
532            # global time axis
533            start = self.start
534            end = self.end
535            # horizontal
536            if self.traceOrder in [0, 1]:
537                pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
538            # vertical
539            elif self.traceOrder in [2, 3]:
540                pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
541
542        if self.timewindow[1] is None:
543            duration = end - start
544        else:
545            duration = self.timewindow[1] - self.timewindow[0]
546            start += self.timewindow[0]
547
548        # horizontal plotting
549        if self.traceOrder in [0, 1]:
550            timepos -= STATION_INFO + MARGINS[3]
551        # vertical plotting
552        elif self.traceOrder == 2:
553            if self.relativeAxis:
554                timepos -= length + MARGIN
555            else:
556                timepos -= pixel_width + MARGIN
557            timepos *= -1
558        elif self.traceOrder == 3:
559            timepos -= STATION_INFO
560
561        t = timepos * duration / pixel_width
562        timestamp = start + t
563
564        # do not return trace if timestamp is outside, region is enlarged
565        # by one pixel.
566        pixel_duration = duration/pixel_width
567        self.pixel_duration = pixel_duration
568        if trace and (trace.stats.starttime > timestamp + pixel_duration or \
569                             trace.stats.endtime + pixel_duration < timestamp):
570            trace = None
571
572        return trace, timestamp
573
574    #@timeit
575    def TraceAndTimeToScreen(self, trace, time):
576        """
577        Return x, y from given trace (midpoint) and time code.
578        """
579        if trace is None:
580            return
581        shx_graphics = trace.get_graphics()
582        if shx_graphics == None:
583            return
584
585        # horizontal
586        if self.traceOrder in [0, 1]:
587            pixel_width = self.width - STATION_INFO - MARGINS[1] - MARGINS[3]
588        # vertical
589        elif self.traceOrder in [2, 3]:
590            pixel_width = self.height - STATION_INFO - MARGINS[0] - MARGINS[2]
591
592        zerotime = trace.stats.starttime - trace.get_info("t-origin")
593        reltime = time - zerotime
594       
595        if self.timewindow[1] == None:
596            start = self.relstart
597            end = self.relend
598        else:
599            start = self.timewindow[0]
600            end = self.timewindow[1]
601       
602        # relative position inside trace window
603        relpos = (reltime - start) / (end - start) * pixel_width
604
605        # horizontal
606        if self.traceOrder in [0, 1]:
607            x, y = self.CalcScrolledPosition((
608                relpos + STATION_INFO + MARGINS[3],
609                shx_graphics.midpoint
610            ))
611            # MARGINS[1] probably wrong
612            #x, y = self.CalcScrolledPosition((
613            #    relpos + STATION_INFO + MARGINS[1] + MARGINS[3],
614            #    shx_graphics.midpoint
615            #))
616        # vertical
617        elif self.traceOrder == 2:
618            x, y = self.CalcScrolledPosition(
619                     (shx_graphics.midpoint, self.height - relpos - STATION_INFO))
620        elif self.traceOrder == 3:
621            x, y = self.CalcScrolledPosition(
622                                   (shx_graphics.midpoint, relpos + STATION_INFO))
623        return x, y
624
625    #@timeit
626    def load(self, fnames=[]):
627        """
628        Load waveform file(s).
629        """
630        testTrace = False
631        if not fnames:
632            fnames = [None]
633            testTrace = True
634
635        for fname in fnames:
636            self._debug("Loading %s..." % fname)
637
638            try:
639                print time.time(), "reading '%s'..." % fname,
640                s = time.time()
641                st = read(fname)
642                print "%.2f seconds" % (time.time() - s)
643                trace = st[0]
644                self.traces.add(trace)
645            except Exception, e:
646                msg = e.message and e.message or "File not readable!"
647                dlg = wx.MessageDialog(self, msg, "An error occured!",
648                                                         wx.OK | wx.ICON_ERROR)
649                dlg.ShowModal()
650                dlg.Destroy()
651
652                self._debug(e.message)
653
654        try:
655            if testTrace:
656                t = self.traces[0].copy()
657                t.stats.network = "XX"
658                t.stats.channel = "EHA"
659                t.trim(t.stats.starttime + 4, t.stats.endtime - 10)
660                self.traces.add(t)
661                t = self.traces[0].copy()
662                t.stats.network = "XX"
663                t.stats.channel = "EHB"
664                t.trim(t.stats.starttime + 10, t.stats.endtime - 3)
665                self.traces.add(t)
666        except Exception, inst:
667            print inst
668            pass
669
670#        msgs.sendMessage('notify.tracecount', count=len(self.traces))
671
672    #@timeit
673    def _setup(self):
674        w, h = self.GetClientSize()
675        if self.traceOrder in [0, 1]:
676            if self.space is None:
677                self.space = h
678            self.SetVirtualSizeWH(w, self.space)
679            self.SetScrollRate(0, 20)
680        elif self.traceOrder in [2, 3]:
681            if self.space is None:
682                self.space = w
683            self.SetVirtualSizeWH(self.space, h)
684            self.SetScrollRate(20, 0)
685
686    #@timeit
687    def _captureMouse(self, evt):
688        self.CaptureMouse()
689
690        ## create overlay
691        #dc = wx.ClientDC(self)
692        #odc = wx.DCOverlay(self.overlay_drag, dc)
693        #odc.Clear()
694
695        self.dragStart = evt.GetPosition()
696        self._debug("mouse captured at", self.dragStart)
697
698    #@timeit
699    def _releaseMouse(self, evt):
700        self.ReleaseMouse()
701
702        ## restore view
703        #dc = wx.ClientDC(self)
704        #odc = wx.DCOverlay(self.overlay_drag, dc)
705        #odc.Clear()
706        #del odc
707        self.overlay_drag.Reset()
708
709        self._debug("mouse released")
710
711    #@timeit
712    def _drawPicksAndZoom(self):
713        """
714        Draw picks.
715        """
716        phaselist = PhaseList()
717        _picks = {}
718        for trc in self.traces:
719            sname = "%s.%s" % (trc.stats.network,trc.stats.station)
720            for phase in phaselist.getPhaseList(sname):
721                if phase.comp and trc.stats.channel[-1] != phase.comp:
722                    continue
723                pcol = phaselist.picktypeColor( phase.picktype )
724                if pcol not in _picks.keys():
725                    _picks[pcol] = []
726                _picks[pcol].append(
727                    (phase.name,self.TraceAndTimeToScreen(trc,phase.picktime))
728                )
729
730        if not _picks and not self.ZoomWindow:
731            return
732
733        dc = wx.ClientDC(self)
734        # odc disables redraw
735        #odc = wx.DCOverlay(self.overlay_picks, dc)
736        #odc.Clear()
737        dc.SetBrush(wx.TRANSPARENT_BRUSH)
738        dc.SetFont( wx.Font( 8, wx.DEFAULT, wx.NORMAL, wx.NORMAL ) )
739
740        th2 = self.traceheight / 2 - 2
741        # draw picks
742        for pcolor in _picks.keys():
743            dc.SetPen(wx.Pen(pcolor, 1))
744            dc.SetTextForeground(pcolor)
745            for pick in _picks[pcolor]:
746                if self.traceOrder in [0, 1]:
747                    dc.DrawLine(
748                        pick[1][0], pick[1][1] - th2,
749                        pick[1][0], pick[1][1] + th2
750                    )
751                    if pick[0] != '_Mark_':
752                        dc.DrawText(pick[0], pick[1][0] + 2, pick[1][1] - th2 - 10)
753                elif self.traceOrder in [2, 3]:
754                    dc.DrawLine(
755                        pick[1][0] - th2, pick[1][1],
756                        pick[1][0] + th2, pick[1][1],
757                    )
758                    if pick[0] != '_Mark_':
759                        dc.DrawRotatedText(pick[0], pick[1][0] - th2 + 2,
760                                                            pick[1][1] - 2, 90)
761        #del odc
762
763        #draw zoom window
764        if self.ZoomWindow:
765            trace, start, end = self.ZoomWindow
766            if  trace is not None:
767                shx_graphics = trace.get_graphics()
768                if shx_graphics == None:
769                    return
770                start = self.TraceAndTimeToScreen(trace, start)
771                end = self.TraceAndTimeToScreen(trace, end)
772                self._debug("draw zoom window")
773                self.__zoombox(dc, start, end, shx_graphics.midpoint,
774                    color="Blue")
775            else:
776                self.zoomwdwwidth = 0
777                self.ZoomWindow = None
778
779
780    def _drawSinglePick( self, trace, tracetime, color=None ):
781        "Just for quick respomse to the user."
782        pname = self.last_defined_phase
783        if not pname:
784            pname = "NewPhase"
785        if color == None:
786            color = 'red'
787        dc = wx.ClientDC(self)
788        dc.SetPen(wx.Pen(color, 1))
789        dc.SetTextForeground(color)
790        dc.SetFont( wx.Font( 8, wx.DEFAULT, wx.NORMAL, wx.NORMAL ) )
791        th2 = self.traceheight / 2 - 2
792        x, y, = self.TraceAndTimeToScreen(trace,tracetime)
793        dc.DrawLine( x, y-th2, x, y+th2 )
794        if pname != '_Mark_':
795            dc.DrawText( pname, x+2, y-th2-10 )
796       
797
798    #@timeit
799    def _drawTraces(self):
800        """
801        Do the real plotting of waveforms including station text and
802        time scale.
803        """
804        # we cannot use the Screen module because of loop imports
805        if self._timewindow[1] is None:
806            timewindow = get_runtime("extend")
807        else:
808            timewindow = self._timewindow
809        width, height = self.GetVirtualSize()
810        numTraces = len(self.traces) - Overlays.numberOfOverlays()
811        globalnorm = self._normtype.startswith("A")
812
813        # compute trace height (theight) as float, otherwise trace positioning
814        # is weird when many traces on screen.
815        if self.traceOrder in [0, 1]:
816            if numTraces:
817                theight = float(height - TIMESCALE - MARGINS[0] \
818                    - MARGINS[2]) / numTraces
819            else:
820                theight = float(height - TIMESCALE)
821            pltwidth = width - STATION_INFO - MARGINS[1] - MARGINS[3]
822        elif self.traceOrder in [2, 3]:
823            if numTraces:
824                theight = (width - TIMESCALE - MARGINS[1] - MARGINS[3]) / numTraces
825            theight = width - TIMESCALE
826            pltwidth = height - STATION_INFO - MARGINS[0] - MARGINS[2]
827        else:
828            raise ValueError("unknown trace order %d" % self.traceorder)
829       
830        self.width = width
831        self.height = height
832        self.traceheight = theight
833
834        # canvas holding the final figure
835        bmap = wx.EmptyBitmap(width, height)
836        canvas = wx.MemoryDC(bmap)
837        canvas.SetBrush(wx.TRANSPARENT_BRUSH)
838        canvas.Clear()
839        canvas.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
840
841        if numTraces == 0:
842            # just clearing the screen
843            dc = wx.AutoBufferedPaintDCFactory(self)
844            dc.Clear()
845            return
846
847        if self.traceOrder == 0:
848            trcs = self.traces[::-1]
849        elif self.traceOrder in [1, 2, 3]:
850            trcs = self.traces
851       
852        # should traces overlap or stay within their y-ranges
853        overlapping_traces = get_runtime( "overlapping_traces", True )
854
855        # init overlays positions
856        Overlays.initPlot( len(self.traces), self.traceOrder )
857
858        # plot every trace
859        for i, t in enumerate(trcs):
860            if getattr(t, "shx_graphics", None) is None:
861                # trace not yet established
862                continue
863            if not self.relativeAxis:
864                start = self.start
865                end = self.end
866            else:
867                start = t.stats.starttime
868                end = start + self.maxDuration
869            try:
870                tzoom = t.stats.sh.ZOOM
871            except:
872                tzoom = 1.
873
874            # zheight is the (integer) height of the drawing box, possibly
875            # zoomed by a factor if overlapping_traces is selected.
876            if overlapping_traces:
877                # increased zheight produces overlapping of traces on display
878                zheight = int( theight * tzoom )
879                try:
880                    prepzoom = 1./tzoom
881                except:
882                    prepzoom = 1.
883            else:
884                zheight = int( theight )
885                prepzoom = 0.9  # why not 1.?
886
887            norm = globalnorm and self.maxAmplitude or self._normtype[1]
888            # zoom=1/tzoom makes trace stay within bitmap
889            t.prepare_image_data(pltwidth, zheight,
890                timewindow, zoom=prepzoom, norm=norm)
891           
892            shx_graphics = t.get_graphics()
893            if shx_graphics == None:
894                continue
895
896            # make trace graphics sketch
897            if zheight > 4:
898                if self.traceOrder in [0, 1]:
899                    bitmap = wx.EmptyBitmap(pltwidth, zheight)
900                elif self.traceOrder in [2, 3]:
901                    bitmap = wx.EmptyBitmap(zheight, pltwidth)
902            else:
903                bitmap = None
904
905            if bitmap is not None:
906                # buffer to draw in
907                dbuffer = wx.MemoryDC(bitmap)
908                #dbuffer = dbuffer
909                dbuffer.SetBrush(wx.TRANSPARENT_BRUSH)
910                dbuffer.Clear()
911                dbuffer.SetPen(wx.Pen((45,45,45), 1))
912            else:
913                dbuffer = None
914
915            _shift = t.get_info("t-origin") - timewindow[0]
916            if _shift < 0.:
917                _shift = 0.
918            # on positive shifts the bitmap is moved out of the screen
919            # to the right. On negative shifts the bitmap is recomputed
920            # and always starts at 0. Fails if the bitmap move completely
921            # out to the left. Bitmap should be cleared in this case.
922
923            # calculate offset to global time scale
924            plotStartSec = t.stats.starttime + _shift - start
925            plotoffset = 0
926            if plotStartSec or not self.relativeAxis:
927                # re-calculate offset
928                portion = plotStartSec / (timewindow[1] - timewindow[0])
929                plotoffset = portion * pltwidth
930            #ks t.shx_graphics.plotoffset = plotoffset
931
932            # For vertical plotting the data has to be transformed
933            if self.traceOrder in [0, 1]:
934                ImageData = shx_graphics.ImageData
935            if self.traceOrder == 2:
936                ImageData = []
937                # simply swap x and y
938                for line in shx_graphics.ImageData:
939                    ImageData.append([line[1], pltwidth - line[0],
940                                                  line[3], pltwidth - line[2]])
941            elif self.traceOrder == 3:
942                ImageData = []
943                # dito plus mirror x
944                for line in shx_graphics.ImageData:
945                    ImageData.append([zheight - line[1], line[0],
946                                                   zheight - line[3], line[2]])
947
948            # get color, etc. attribute
949            attrib = t.get_info("attrib")
950            try:
951                style = get_style(attrib)
952            except KeyError:
953                style = AttributeBlock()
954            cmode = None
955            try:
956                color = map(int, style.color.split(','))
957                cmode = 'i'
958            except:
959                try:
960                    color = map(float, style.color.split(','))
961                    cmode = 'f'
962                except:
963                    color = style.color
964                    cmode = 'o'
965            if cmode == 'i':
966                if 0 < max(color) <= 1:
967                    # e.g. 1,0,0 means red, multiply with 255
968                    color = [255*x for x in color]
969            elif cmode == 'f':
970                color = map( int, [255*x for x in color] )
971            linestyle = getattr(wx, style.linestyle.upper())
972            if dbuffer is not None:
973                dbuffer.SetPen(wx.Pen(color, style.linewidth, linestyle))
974                dbuffer.BeginDrawing()
975                dbuffer.DrawLineList(ImageData)
976                dbuffer.EndDrawing()
977
978            # copy trace picture to canvas
979            if dbuffer is not None:
980                if self.traceOrder in [0, 1]:
981                    if overlapping_traces:
982                        fac = i + 0.5 - 0.5*tzoom
983                    else:
984                        fac = i
985                    plotpos = Overlays.plotPos( i+1, fac * theight + MARGINS[0])
986                    canvas.Blit(
987                        plotoffset + STATION_INFO + MARGINS[3], 
988                        plotpos, 
989                        pltwidth,
990                        zheight,
991                        dbuffer,
992                        0,
993                        0,
994                        wx.AND
995                    )
996                elif self.traceOrder == 2:
997                    canvas.Blit(i * theight, -plotoffset + MARGIN, zheight,
998                                                pltwidth, dbuffer, 0, 0, wx.AND)
999                elif self.traceOrder == 3:
1000                    canvas.Blit(i * theight, STATION_INFO + plotoffset, zheight,
1001                                                pltwidth, dbuffer, 0, 0, wx.AND)
1002
1003            # trace numbering
1004            if self.traceOrder in [1, 2, 3]:
1005                idx = i + 1
1006            elif self.traceOrder == 0:
1007                idx = -i + len(self.traces)
1008            txt = "%d: %s %s" % (idx, t.stats.station, t.stats.channel[-1])
1009
1010            canvas.SetPen(wx.Pen('Grey', 1, wx.LONG_DASH))
1011
1012            # place trace names
1013            # helper lines for debugging
1014            w, h, _, _ = canvas.GetFullTextExtent(txt)
1015            shx_graphics.midpoint = tmp = i * theight + theight//2 + MARGINS[0]
1016            if self.traceOrder in [0, 1]:
1017                canvas.DrawText(txt, 5 + MARGINS[3], tmp - h // 2)
1018                canvas.DrawLine(STATION_INFO + MARGINS[3], tmp, width - MARGINS[1], tmp)
1019#                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
1020#                canvas.DrawLine(STATION_INFO, tmp + zheight // 2,
1021#                                            width - MARGIN, tmp + zheight // 2)
1022#                canvas.DrawLine(STATION_INFO, tmp - zheight // 2,
1023#                                            width - MARGIN, tmp - zheight // 2)
1024            elif self.traceOrder == 2:
1025                canvas.DrawRotatedText(txt, tmp - h // 2, height - MARGIN, 90)
1026                canvas.DrawLine(shx_graphics.midpoint, MARGIN,
1027                                                    tmp, height - STATION_INFO)
1028#                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
1029#                canvas.DrawLine(tmp + zheight // 2, MARGIN,
1030#                                     tmp + zheight // 2, height - STATION_INFO)
1031#                canvas.DrawLine(tmp - zheight // 2, MARGIN,
1032#                                     tmp - zheight // 2, height - STATION_INFO)
1033            elif self.traceOrder == 3:
1034                canvas.DrawRotatedText(txt, tmp - h // 2,
1035                                    STATION_INFO - (STATION_INFO - w) // 2, 90)
1036                canvas.DrawLine(tmp, STATION_INFO, tmp, height - MARGIN)
1037#                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
1038#                canvas.DrawLine(tmp+zheight // 2, STATION_INFO,
1039#                                           tmp + zheight // 2, height - MARGIN)
1040#                canvas.DrawLine(tmp - zheight // 2, STATION_INFO,
1041#                                           tmp - zheight // 2, height - MARGIN)
1042
1043
1044        # time axis
1045        canvas.SetPen(wx.Pen('Black', 1))
1046        if self.relativeAxis:
1047            start, end = timewindow
1048
1049        if self.traceOrder in [0, 1]:
1050            PARTS = 5.  # axis split into X parts
1051            length = width - MARGINS[1] - MARGINS[3] - STATION_INFO  # pixel length of time axis
1052            fixpos = height - TIMESCALE - MARGINS[2]  # here: y coordinate
1053            varpos_start = STATION_INFO + MARGINS[3] # here: x start
1054        elif self.traceOrder in [2, 3]:
1055            PARTS = 4.
1056            length = height - MARGIN - STATION_INFO
1057            fixpos = width - TIMESCALE + 10  # here: x coordinate
1058            if self.traceOrder == 2:
1059                varpos_start = MARGIN  # here: y start
1060            elif self.traceOrder == 3:
1061                varpos_start = STATION_INFO
1062
1063        chunk_t, chunk, labeloffset, pixoffset, PARTS \
1064            = self.niceNumbers( start, end, length, PARTS )
1065        chunk2 = chunk / 5.
1066
1067        # basic time axis line
1068        if self.traceOrder in [0, 1]:
1069            canvas.DrawLine(varpos_start, fixpos,
1070                                                 length + varpos_start, fixpos)
1071        elif self.traceOrder in [2, 3]:
1072            canvas.DrawLine(fixpos, varpos_start,
1073                                                 fixpos, varpos_start + length)
1074
1075        # sections with ticks and time string
1076
1077        try:
1078            prec = abs(int(round(1 / math.log10(chunk_t))))
1079        except:
1080            prec = 1
1081        mask = "%%.%uf" % prec
1082        for i in range(int(PARTS)):
1083            if isinstance(start, UTCDateTime):
1084                timecode = labeloffset + start + i * chunk_t
1085                txt = timecode.strftime("%H:%M:%S.") + \
1086                                             str(timecode.microsecond//1000)
1087            else:
1088                txt = mask % (labeloffset + start + i * chunk_t)
1089
1090            tw = canvas.GetFullTextExtent(txt)[0]
1091
1092            if self.traceOrder in [0, 1]:
1093                varpos_current = pixoffset + varpos_start + i * chunk
1094                # time string
1095                canvas.DrawText(txt, varpos_current - tw / 2,
1096                                                fixpos + TIMESCALE_OFFSET_TEXT)
1097                # major tick
1098                canvas.DrawLine(varpos_current, fixpos,
1099                                varpos_current, fixpos + TIMESCALE_TICKS_MAJOR)
1100                # minor ticks
1101                for j in range(1, 5):
1102                    canvas.DrawLine(
1103                        varpos_current + j * chunk2,
1104                        fixpos,
1105                        varpos_current + j * chunk2,
1106                        fixpos + TIMESCALE_TICKS_MINOR
1107                    )
1108            elif self.traceOrder in [2, 3]:
1109                if self.traceOrder == 2:
1110                    varpos_current = pixoffset \
1111                        + varpos_start + (PARTS - i) * chunk
1112                elif self.traceOrder == 3:
1113                    varpos_current = pixoffset + varpos_start + i * chunk
1114                canvas.DrawRotatedText(txt, fixpos + TIMESCALE_OFFSET_TEXT,
1115                                                   varpos_current + tw / 2, 90)
1116                canvas.DrawLine(fixpos, varpos_current,
1117                                fixpos + TIMESCALE_TICKS_MAJOR, varpos_current)
1118                for j in range(1, 5):
1119                    if self.traceOrder == 2:
1120                        j -= 5
1121                    canvas.DrawLine(
1122                        fixpos,
1123                        varpos_current + j * chunk2,
1124                        fixpos + TIMESCALE_TICKS_MINOR,
1125                        varpos_current + j * chunk2
1126                     )
1127
1128        # make it visible
1129        dc2 = wx.AutoBufferedPaintDCFactory(self)
1130        dc2.Clear()
1131        x, y = self.CalcUnscrolledPosition(0, 0)
1132        dc2.Blit(0, 0, width, height, canvas, x, y)
1133        if self.do_screenshot:
1134            self.save_shot(canvas)
1135            self.do_screenshot = False
1136        # detach
1137        canvas.SelectObject(wx.NullBitmap)
1138
1139    #@timeit
1140    def save_shot(self, dc):
1141        """
1142        Save PNG screen shot (PS later)
1143        """
1144        size = dc.Size
1145
1146        shot = wx.EmptyBitmap(size.width, size.height)
1147        shot_dc = wx.MemoryDC()
1148        shot_dc.SelectObject(shot)
1149
1150        shot_dc.Blit(0, 0, size.width, size.height, dc, 0, 0)
1151        shot_dc.SelectObject(wx.NullBitmap)
1152        img = shot.ConvertToImage()
1153        img.SaveFile(self.do_screenshot, wx.BITMAP_TYPE_PNG)
1154        log_message("info", "screen dump saved in: %s" % self.do_screenshot)
1155   
1156    #@timeit
1157    def niceNumbers( self, start, end, pixwidth, parts ):
1158        "2150 surf min"
1159        num = (end - start) / parts
1160        pixscaling = pixwidth / (end-start)
1161        tmp = 10**int(math.log10(num))  # integer power of 10
1162        nn = tmp   # best nice number is either this number or *2, *5, *10
1163        dist = abs(tmp-num)
1164        for i in (2,5,10):
1165            ndist = abs(num - i*tmp)
1166            if ndist < dist:
1167                nn = i*tmp
1168                dist = ndist
1169        labelstart = nn*int(start/nn)
1170        if (labelstart-start) < -1.e-5:
1171            labelstart += nn
1172        repeat = int((end-labelstart)/nn) + 1
1173        pixspan = nn * pixscaling
1174        pixoffset = (labelstart-start) * pixscaling
1175        labeloffset = labelstart - start
1176        #print nn, labelstart, pixoffset, repeat, '--', num, '.', start, end
1177        return (nn, pixspan, labeloffset, pixoffset, repeat)
1178
1179    @staticmethod
1180    def _debug(*args):
1181        log_message('debug.wx', " ".join([str(i) for i in args]))
1182        #print "dbg:", " ".join([str(i) for i in args])
1183
1184    #@timeit
1185    def _resetTimeRange(self, **kwargs):
1186        """
1187        Gather information about traces. Called on redraw (at idle state).
1188        """
1189        mint = UTCDateTime() # now
1190        maxt = UTCDateTime(0) # 1970
1191        minrel = 1e20
1192        maxrel = 0
1193        maxDuration = 0
1194        amplitude = 0
1195
1196        for t in self.traces:
1197            s = t.get_info("relstart")
1198            e = t.get_info("relend")
1199            if s < minrel:
1200                minrel = s
1201            if e > maxrel:
1202                maxrel = e
1203            if t.stats.starttime < mint:
1204                mint = t.stats.starttime
1205            if t.stats.endtime > maxt:
1206                maxt = t.stats.endtime
1207            # needed for relative time axis
1208            l = e - s
1209            if l > maxDuration:
1210                maxDuration = l
1211            # normation
1212            # no time window set or normtype forces full traces
1213            #if self._timewindow[1] is None or self._normtype.startswith("A"):
1214            if self._timewindow[1] is None:
1215                data = t.data
1216            else:
1217                try:
1218                    data = t.get_datawindow(*self._timewindow)
1219                except:
1220                    continue
1221            t_ampl = max( abs(data.max()), abs(data.min()) )
1222            if t_ampl > amplitude:
1223                amplitude = t_ampl
1224
1225        self.start = mint
1226        self.relstart = minrel
1227        self.end = maxt
1228        self.relend = maxrel
1229        self.maxDuration = maxDuration
1230        self.maxAmplitude = amplitude
1231        self.timewindow = get_runtime("timewindow")
1232
1233    #@timeit
1234    def __zoombox(self, dc, start, end, midpoint, color="DARKORANGE", size=1):
1235        marginfactor = 0.3
1236        mf2 = (1 - marginfactor) * 2
1237        th2 = self.traceheight / 2
1238        offset = midpoint - th2 - self.Scrolled
1239
1240        dc.SetPen(wx.Pen(color, size))
1241        if self.traceOrder in [0, 1]:
1242            dc.DrawRectangle(start[0], offset + th2 * marginfactor,
1243                                                  end[0] - start[0], th2 * mf2)
1244        elif self.traceOrder in [2, 3]:
1245            dc.DrawRectangle(offset + th2 * marginfactor, start[1],
1246                                                  th2 * mf2, end[1] - start[1])
1247
1248
1249class plotterWindow(wx.Frame):
1250    """
1251    Basic program frame.
1252    """
1253    def __init__(self, parent, title, size=(640,280), position=(100,100)):
1254        # save instance for external access
1255        global plotter
1256        plotter = self
1257        wx.Frame.__init__(self, parent, title=title, size=size,
1258              style=wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE |
1259                    wx.FRAME_FLOAT_ON_PARENT)
1260
1261        self.SetPosition(position)
1262        self.addMenuEntries()
1263        self.canvas = traceCanvas(self, [])
1264        self.CreateStatusBar()
1265        self.SetStatusText("%s %s" % (NAME, VERSION))
1266        self.Disable()
1267        self.Show()
1268        self.Enable()
1269        self._user_selection = 999.
1270        self.menu_entry_id = 10000
1271   
1272    def addMenuEntries( self ):
1273        menubar = wx.MenuBar()
1274        # file menu
1275        fileMenu = wx.Menu()
1276        self.addEntry( fileMenu, '&Quit\tCtrl+Q', 'Quit Graphics', self.OnQuit )
1277        # window menu
1278        windowMenu = wx.Menu()
1279        self.addEntry( windowMenu, '&All Norm\tCtrl+A',
1280            'Normalize All Traces Together', self.OnAllNorm )
1281        self.addEntry( windowMenu, '&Single Norm\tCtrl+S',
1282            'Normalize Traces Separately', self.OnSingleNorm )
1283        self.addEntry( windowMenu, 'Clipped Traces',
1284            'Traces are clipped when zooming amplitudes', self.OnClippedTraces )
1285        self.addEntry( windowMenu, 'Overlapping Traces',
1286            'Traces overlap when zooming', self.OnOverlappingTraces )
1287        # traces menu
1288        tracesMenu = wx.Menu()
1289        self.addEntry( tracesMenu, '&Demean\tCtrl+D',
1290            'Remove mean value from all traces', self.OnDemean )
1291        testMenu = wx.Menu()
1292        self.addEntry( testMenu, 'Read GR * BHZ of an event',
1293            'fdsnws gr * * bhz 7-aug-15_00:16:30 420', self.OnTest1 )
1294        self.addEntry( testMenu, 'Read SX,TH * BHZ of an event',
1295            'fdsnws sx,th * * bhz 7-aug-15_00:16:30 420', self.OnTest2 )
1296        # put menus and menu bar in place
1297        menubar.Append( fileMenu, '&File' )
1298        menubar.Append( windowMenu, '&Window' )
1299        menubar.Append( tracesMenu, '&Traces' )
1300        menubar.Append( testMenu, 'Test' )
1301        self.SetMenuBar( menubar )
1302        self.Centre()
1303   
1304    def addEntry( self, menutitle, entrytext, entrydescr, callback ):
1305        #self.menu_entry_id += 1
1306        #qmi = wx.MenuItem( menutitle, self.menu_entry_id, entrytext,
1307        #    help=entrydescr )
1308        #menutitle.AppendItem( qmi )
1309        #self.Bind( wx.EVT_MENU, callback, id=self.menu_entry_id )       
1310        item = menutitle.Append( wx.ID_ANY, entrytext, entrydescr )
1311        self.Bind( wx.EVT_MENU, callback, item )
1312
1313    def OnQuit( self, e ):
1314        # Cannot quit SHX, _sendShCommand waits for command to be executed
1315        # i.e. no way to terminate wx smoothly. Quit SHX after Close also not
1316        # possible, runs into communication issues between threads.
1317        #_sendShCommand( "quit y" )
1318        self.Close()
1319   
1320    def OnAllNorm( self, e ):
1321        _sendShCommand( 'norm aw' )
1322
1323    def OnSingleNorm( self, e ):
1324        _sendShCommand( 'norm sw' )
1325
1326    def OnOverlappingTraces( self, e ):
1327        _sendShCommand( 'fct overlapping_traces true' )
1328
1329    def OnClippedTraces( self, e ):
1330        _sendShCommand( 'fct overlapping_traces false' )
1331
1332    def OnDemean( self, e ):
1333        _sendShCommand( 'demean all' )
1334
1335    def OnTest1( self, e ):
1336        _sendShCommand( 'fdsnws gr * * bhz 7-aug-15_00:16:30 420' )
1337
1338    def OnTest2( self, e ):
1339        _sendShCommand( 'fdsnws sx,th * * bhz 7-aug-15_00:16:30 420' )
1340
1341    def redraw(self):
1342        self.canvas.OnPaint(None)
1343
1344
1345def _sendShCommand( cmdstring, name="default" ):
1346    "Send command to command line interpreter."
1347    msgs.sendMessage("ui.command", cmd=cmdstring, name=name )
1348
1349   
1350
1351def ui_events(func):
1352    """
1353    """
1354    def wrapper(payload, msgid):
1355        result = func(payload)
1356        if msgid is not None:
1357            msgs.sendMessage(msgid, value=result, msgid=msgid)
1358    return wrapper
1359
1360
1361@ui_events
1362def __set_cursor(mode):
1363    """
1364    Interface method for altering cursor style on canvas. Thread-safe.
1365    """
1366    mode = mode.lower()
1367    if mode == "cross":
1368        _c = wx.CURSOR_CROSS
1369    elif mode == "normal":
1370        _c = wx.CURSOR_DEFAULT
1371    elif mode == "left":
1372        _c = wx.CURSOR_POINT_LEFT
1373    elif mode == "right":
1374        _c = wx.CURSOR_POINT_RIGHT
1375    myCursor = wx.StockCursor(_c)
1376    global plotter
1377    wx.CallAfter(plotter.canvas.SetCursor, myCursor)
1378subscribe_ui_event(__set_cursor, "cursor")
1379
1380
1381@ui_events
1382def __select_gui(mode):
1383    """
1384    Interface method for selecting time and/or trace by user interaction. While
1385    UI remains responsive, everything waits for user input.
1386
1387    mode is one of the following commands:
1388    - relative (screen coordinates to relative offset) - for selecting windows
1389    - trace_time (trace under cursor and timestamp) - for picking
1390    """
1391    global plotter
1392    wx.CallAfter(plotter.canvas._set_modes, "interactive", mode)
1393    plotter._user_selection = None
1394    while plotter._user_selection is None:
1395        time.sleep(TIME_SLOT)
1396    return plotter._user_selection
1397subscribe_ui_event(__select_gui, "input")
1398
1399
1400@ui_events
1401def __screenshot(fname):
1402    """
1403    Trigger screen shot creation. Thread-safe. Image will be saved immediately.
1404    """
1405    global plotter
1406    plotter.canvas.do_screenshot = fname
1407    plotter.canvas.refresh = True
1408    wx.CallAfter(plotter.canvas.OnIdle, None)
1409subscribe_ui_event(__screenshot, "screenshot")
Note: See TracBrowser for help on using the repository browser.