source: SHX/trunk/sandbox/traceplotter.py @ 425

Revision 425, 34.4 KB checked in by marcus, 12 years ago (diff)

reformatting after PEP8

  • Property svn:eol-style set to native
Line 
1# -*- coding: utf-8 -*-
2
3import time
4print time.time(), "start-up"
5
6import wx
7import sys
8import os
9import wx.lib.scrolledpanel as SP
10from obspy.core import UTCDateTime
11from obspy.core.util import AttribDict
12from SeismicHandler.core import read
13from SeismicHandler.utils.pubsub import pub as msgs
14from obspy.sh.core import fromUTCDateTime
15
16# width of station info
17STATION_INFO = 85
18# margin at end of trace
19MARGIN = 15
20# space for time-scale
21TIMESCALE = 40
22# offset for time string
23TIMESCALE_OFFSET_TEXT = 8
24# length of minor/major ticks
25TIMESCALE_TICKS_MINOR = 3
26TIMESCALE_TICKS_MAJOR = 7
27
28class Traces(object):
29    """
30    Object for trace administration.
31    """
32    def __init__(self):
33        self.traces = []
34        self.ids = []
35
36    def add(self, trace):
37        self.traces.append(trace)
38
39    def remove(self, trace):
40        pass
41   
42    def pop(self, trace):
43        pass
44
45    def __iter__(self):
46        for t in self.traces:
47            yield t
48
49    def __len__(self):
50        return len(self.traces)
51
52    def __getitem__(self, item):
53        return self.traces[item]
54
55class traceCanvas(SP.ScrolledPanel):
56    """
57    All drawing methods for plotting traces. Include all mouse-related
58    functions.
59    """
60    def __init__(self, parent, fnames=[]):
61        SP.ScrolledPanel.__init__(self, parent, style=wx.BORDER_SIMPLE)
62
63        # class data
64        self.parent = parent
65        self.traces = Traces()
66        self.ZoomWindow = None
67        self.Scrolled = 0
68        self.mousedouble = 0
69        self.refresh = False
70        self.AllowDoubleClick = True
71
72        # temporary layer for mouse motion
73        self.overlay_drag = wx.Overlay()
74        # layer for picks and zoom box
75        self.overlay_picks = wx.Overlay()
76
77        # application defaults
78        self.relativeAxis = False
79        self.traceOrder = 0
80        self.phasename = "P"
81        self.space = None
82
83        self._setup()
84
85        # event binding
86        self.Bind(wx.EVT_PAINT, self.OnPaint)
87        self.Bind(wx.EVT_SIZE, self.OnPaint)
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_UP, self.OnMouseRightUp)
93        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)
94        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
95        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
96        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
97
98        # messaging system
99        msgs.subscribe(self.OnNotifyHeight, 'ui.change.displayheight')
100        msgs.subscribe(self.OnNotifyPhaseName, 'ui.change.phasename')
101        msgs.subscribe(self.OnNotifyTraceOrder, 'ui.change.traceorder')
102        msgs.subscribe(self.OnNotifyTimeAxis, 'ui.change.timeaxis')
103        msgs.subscribe(self._resetTimeRange, 'notify.tracecount')
104
105        # init
106        self.load(fnames)
107        self.OnPaint(None)
108        self.SetFocus()
109
110    # event processing
111    def OnMouseWheel(self, evt):
112        pass
113
114    def OnMouseLeftDouble(self, evt):
115        trace, _ = self.ScreenToTraceAndTime(*(evt.GetPositionTuple()))
116        if trace:
117            self.mousedouble = True
118
119        evt.Skip()
120
121    def OnMouseLeftDown(self, evt):
122#        print "down", evt.ButtonDClick(wx.MOUSE_BTN_LEFT)
123        print "down", evt.ButtonDClick()
124        self._captureMouse(evt)
125        evt.Skip()
126
127    def OnMouseRightDown(self, evt):
128        self._captureMouse(evt)
129
130    def OnMouseMotion(self, evt):
131        """
132        Mouse motion.
133        """
134        x, y = evt.GetPositionTuple()
135        trace, timestamp = self.ScreenToTraceAndTime(x, y)
136
137        try:
138            id = trace.id
139        except AttributeError:
140            id = "*"
141
142        self.parent.SetStatusText(
143            "%s - %s" % (fromUTCDateTime(timestamp), id)
144        )
145
146        if evt.Dragging() and trace:
147            # new overlay for live display
148            dc = wx.ClientDC(self)
149            odc = wx.DCOverlay(self.overlay_drag, dc)
150            odc.Clear()
151            dc.SetBrush(wx.TRANSPARENT_BRUSH)
152
153            th2 = self.traceheight / 2
154            mpth2 = trace.shx.midpoint + th2
155            mmth2 = trace.shx.midpoint - th2
156
157            # draw box
158            if evt.RightIsDown():
159                self.ZoomWindow = None
160                self.__zoombox(dc, self.dragStart, (x, y),
161                                                    trace.shx.midpoint, size=2)
162            # draw position line
163            elif evt.LeftIsDown():
164                dc.SetPen(wx.Pen("Blue", 1))
165                if self.traceOrder in [0, 1]:
166                    dc.DrawLine(
167                        x, mmth2 - self.Scrolled,
168                        x, mpth2 - self.Scrolled
169                    )
170                elif self.traceOrder in [2, 3]:
171                    dc.DrawLine(
172                        mmth2 - self.Scrolled, y,
173                        mpth2 - self.Scrolled, y
174                    )
175            del odc
176
177        # let the event propagate
178        evt.Skip()
179
180    def OnMouseRightUp(self, evt):
181        """
182        Release of right mouse button.
183        """
184        if self.HasCapture():
185            self._releaseMouse(evt)
186            x, y = evt.GetPositionTuple()
187
188            trace, end = self.ScreenToTraceAndTime(x, y)
189            if self.traceOrder in [0, 1]:
190                _, start = self.ScreenToTraceAndTime(self.dragStart[0], y)
191            elif self.traceOrder in [2, 3]:
192                _, start = self.ScreenToTraceAndTime(x, self.dragStart[1])
193
194            if start > end:
195                end, start = start, end
196
197            self.ZoomWindow = [trace, start, end,]
198            self.dragStart = None
199            self._drawPicksAndZoom()
200
201    def OnMouseLeftUp(self, evt):
202        """
203        Release of left mouse button.
204        """
205        if self.HasCapture():
206            self._releaseMouse(evt)
207            self.dragStart = None
208
209        print "up", self.mousedouble
210        if self.mousedouble:
211            self.mousedouble = False
212
213        x, y = evt.GetPositionTuple()
214        trace, timestamp = self.ScreenToTraceAndTime(x, y)
215        if not trace:
216            evt.Skip()
217            return
218
219        if "picks" not in trace.shx:
220            trace.shx.picks = AttribDict()
221
222        if "manual" not in trace.shx.picks:
223            trace.shx.picks.manual = {}
224
225        trace.shx.picks.manual[self.phasename] = timestamp
226        msg = "phase '%s' associated to %s at %s" % (
227                self.phasename,
228                trace.id,
229                fromUTCDateTime(timestamp)
230        )
231
232        self.parent.SetStatusText(msg)
233        self._debug(msg)
234        self._drawPicksAndZoom()
235
236    def OnScroll(self, evt):
237        """
238        Update scroll offset.
239        """
240        if self.traceOrder in [0, 1]:
241            self.Scrolled = self.GetScrollPos(wx.VERTICAL) * \
242                                               self.GetScrollPixelsPerUnit()[1]
243        elif self.traceOrder in [2, 3]:
244            self.Scrolled = self.GetScrollPos(wx.HORIZONTAL) * \
245                                               self.GetScrollPixelsPerUnit()[0]
246        self.OnPaint(evt, renewOverlay=True)
247        evt.Skip()
248
249    def OnIdle(self, evt):
250        """
251        Idle processing. Redraw is only triggered here not on PAINT
252        event.
253        """
254        if self.refresh:
255            self._drawTraces()
256            self._drawPicksAndZoom()
257            self.refresh = False
258
259    def OnPaint(self, evt, renewOverlay=False):
260        """
261        Called if redraw is requested.
262
263        Just set flag for redraw - execution triggered from IDLE event.
264        In case of resize renew overlay buffers.
265        """
266        # if window was resized, renew overlay buffers
267        if evt and (renewOverlay or evt.GetEventType() == wx.EVT_SIZE.typeId):
268            self._setup()
269            self.overlay_picks.Reset()
270            self.overlay_drag.Reset()
271
272        # painting is only done in idle state
273        self.refresh = True
274
275    # Usually called via message system.
276    def OnNotifyTimeAxis(self, orientation):
277        """
278        Handle changes of time axis mode (absolute vs. relative time
279        axis).
280        """
281        recent = self.relativeAxis
282        self.relativeAxis = orientation == 1
283        if recent != self.relativeAxis:
284
285            # BAD!
286            self.OnPaint(True, renewOverlay=True)
287
288    def OnNotifyTraceOrder(self, order):
289        """
290        Handle changes of trace plotting mode (horizontal vs. vertical
291        modes).
292        """
293        recent = self.traceOrder
294        self.traceOrder = order
295        if recent != self.traceOrder:
296            # reset display height
297            msgs.sendMessage('notify.tracecount', count=len(self.traces),
298                                                                    reset=True)
299            msgs.sendMessage('ui.change.displayheight',
300                                                       height=len(self.traces))
301            self._setup()
302            # BAD: setting evt=True
303            self.OnPaint(True, renewOverlay=True)
304
305    def OnNotifyHeight(self, height):
306        """
307        Handle changes of display height.
308        """
309        if self.traceOrder in [0, 1]:
310            self.space = self.GetClientSize()[1]
311        elif self.traceOrder in [2, 3]:
312            self.space = self.GetClientSize()[0]
313
314        if height != len(self.traces):
315            self.space *= len(self.traces) / float(height)
316
317        self._setup()
318        self.OnPaint(None)
319
320    def OnNotifyPhaseName(self, name):
321        self.phasename = name
322
323    # helper functions
324    def ScreenToTraceAndTime(self, x, y):
325        """
326        Returns trace instance and time code derived from cursor
327        position inside graphical panel.
328        """
329        x, y = self.CalcUnscrolledPosition((x,y))
330        # horizontal display
331        if self.traceOrder in [0, 1]:
332            tracepos, timepos = y, x
333        # vertical display
334        elif self.traceOrder in [2, 3]:
335            tracepos, timepos = x, y
336
337        # get trace under cursor
338        trace = None
339        theight = self.traceheight // 2
340        for t in self.traces:
341            if tracepos >= t.shx.midpoint - theight and \
342                                          tracepos <= t.shx.midpoint + theight:
343                trace = t
344                break
345
346        # get time code
347        if trace and self.relativeAxis:
348            start = trace.stats.starttime
349            end = trace.stats.endtime
350            pixel_width = trace.shx.PlotPixels
351            # only needed for vertical plotting
352            length = self.height - STATION_INFO - MARGIN
353        else:
354            # global time axis
355            start = self.start
356            end = self.end
357            # horizontal
358            if self.traceOrder in [0, 1]:
359                pixel_width = self.width - STATION_INFO - MARGIN
360            # vertical
361            elif self.traceOrder in [2, 3]:
362                pixel_width = self.height - STATION_INFO - MARGIN
363
364        duration = end - start
365        timestamp = None
366
367        # horizontal plotting
368        if self.traceOrder in [0, 1]:
369            timepos -= STATION_INFO
370        # vertical plotting
371        elif self.traceOrder == 2:
372            if self.relativeAxis:
373                timepos -= length + MARGIN
374            else:
375                timepos -= pixel_width + MARGIN
376            timepos *= -1
377        elif self.traceOrder == 3:
378            timepos -= STATION_INFO
379
380        t = timepos * duration / pixel_width
381        timestamp = start + t
382
383        # do not return trace if timestamp is outside, region is enlarged
384        # by one pixel.
385        pixel_duration = duration/pixel_width
386        if trace and (trace.stats.starttime > timestamp + pixel_duration or \
387                             trace.stats.endtime + pixel_duration < timestamp):
388            trace = None
389
390        return trace, timestamp
391
392    def TraceAndTimeToScreen(self, trace, time):
393        """
394        Return x, y from given trace (midpoint) and time code.
395        """
396        if self.relativeAxis:
397            start = trace.stats.starttime
398            end = trace.stats.endtime
399            pixel_width = trace.shx.PlotPixels
400        else:
401            start = self.start
402            end = self.end
403
404            # horizontal
405            if self.traceOrder in [0, 1]:
406                pixel_width = self.width - STATION_INFO - MARGIN
407            # vertical
408            elif self.traceOrder in [2, 3]:
409                pixel_width = self.height - STATION_INFO - MARGIN
410
411        # relative position inside trace window
412        relpos = (time - start) / (end - start) * pixel_width
413
414        # horizontal
415        if self.traceOrder in [0, 1]:
416            x, y = self.CalcScrolledPosition(
417                                   (relpos + STATION_INFO, trace.shx.midpoint))
418        # vertical
419        elif self.traceOrder == 2:
420            x, y = self.CalcScrolledPosition(
421                     (trace.shx.midpoint, self.height - relpos - STATION_INFO))
422        elif self.traceOrder == 3:
423            x, y = self.CalcScrolledPosition(
424                                   (trace.shx.midpoint, relpos + STATION_INFO))
425        return x, y
426
427    def load(self, fnames=[]):
428        """
429        Load waveform file(s).
430        """
431        if not fnames:
432            fnames = [None]
433            testTrace = True
434
435        for fname in fnames:
436            self._debug("Loading %s..." % fname)
437
438            try:
439                st = read(fname)
440                trace = st[0]
441                self.traces.add(trace)
442            except Exception, e:
443                msg = e.message and e.message or "File not readable!"
444                dlg = wx.MessageDialog(self, msg, "An error occured!",
445                                                         wx.OK | wx.ICON_ERROR)
446                dlg.ShowModal()
447                dlg.Destroy()
448
449                self._debug(e.message)
450
451        try:
452            if testTrace:
453                t = self.traces[0].copy()
454                t.stats.network = "XX"
455                t.stats.channel = "EHA"
456                t.trim(t.stats.starttime + 4, t.stats.endtime - 10)
457                self.traces.add(t)
458                t = self.traces[0].copy()
459                t.stats.network = "XX"
460                t.stats.channel = "EHB"
461                t.trim(t.stats.starttime + 10, t.stats.endtime - 3)
462                self.traces.add(t)
463        except Exception, inst:
464            print inst
465            pass
466
467        msgs.sendMessage('notify.tracecount', count=len(self.traces))
468
469    def _setup(self):
470        w, h = self.GetClientSize()
471        if self.traceOrder in [0, 1]:
472            if self.space is None:
473                self.space = h
474            self.SetVirtualSizeWH(w, self.space)
475            self.SetScrollRate(0, 20)
476        elif self.traceOrder in [2, 3]:
477            if self.space is None:
478                self.space = w
479            self.SetVirtualSizeWH(self.space, h)
480            self.SetScrollRate(20, 0)
481
482    def _captureMouse(self, evt):
483        self._debug("_captureMouse")
484
485        self.CaptureMouse()
486
487        dc = wx.ClientDC(self)
488        odc = wx.DCOverlay(self.overlay_drag, dc)
489        odc.Clear()
490
491        self.dragStart = evt.GetPosition()
492
493    def _releaseMouse(self, evt):
494        self._debug("_releaseMouse")
495       
496        self.ReleaseMouse()
497
498        # restore view
499        dc = wx.ClientDC(self)
500        odc = wx.DCOverlay(self.overlay_drag, dc)
501        odc.Clear()
502        del odc
503        self.overlay_drag.Reset()
504
505    def _drawPicksAndZoom(self):
506        """
507        Draw picks.
508        """
509        _picks = []
510        for t in self.traces:
511            try:
512                picks = t.shx.picks
513            except KeyError:
514                continue
515
516            for mode in picks:
517                for pick in picks[mode]:
518                    _picks.append(
519                        [mode, pick,
520                         self.TraceAndTimeToScreen(t, picks[mode][pick])]
521                    )
522
523        if not _picks and not self.ZoomWindow:
524            return
525
526        dc = wx.ClientDC(self)
527        odc = wx.DCOverlay(self.overlay_picks, dc)
528        odc.Clear()
529        dc.SetBrush(wx.TRANSPARENT_BRUSH)
530
531        th2 = self.traceheight / 2
532        # draw picks
533        for pick in _picks:
534            color = "Red"
535            dc.SetPen(wx.Pen(color, 1))
536            dc.SetTextForeground(color)
537            if self.traceOrder in [0, 1]:
538                dc.DrawLine(
539                    pick[2][0], pick[2][1] - th2,
540                    pick[2][0], pick[2][1] + th2
541                )
542                dc.DrawText(pick[1], pick[2][0] + 2, pick[2][1] - th2 + 2)
543            elif self.traceOrder in [2, 3]:
544                dc.DrawLine(
545                    pick[2][0] - th2, pick[2][1],
546                    pick[2][0] + th2, pick[2][1],
547                )
548                dc.DrawRotatedText(pick[1], pick[2][0] - th2 + 2,
549                                                            pick[2][1] - 2, 90)
550
551        #draw zoom window
552        if self.ZoomWindow:
553            trace, start, end = self.ZoomWindow
554            start = self.TraceAndTimeToScreen(trace, start)
555            end = self.TraceAndTimeToScreen(trace, end)
556
557            self._debug("draw zoom window")
558            self.__zoombox(dc, start, end, trace.shx.midpoint, color="Blue")
559
560        del odc
561
562    def _drawTraces(self):
563        """
564        Do the real plotting of waveforms including station text and
565        time scale.
566        """
567        width, height = self.GetVirtualSize()
568        numTraces = len(self.traces)
569
570        if self.traceOrder in [0, 1]:
571            theight = (height - TIMESCALE) // numTraces
572            pltwidth = width - STATION_INFO - MARGIN
573        elif self.traceOrder in [2, 3]:
574            theight = (width - TIMESCALE) // numTraces
575            pltwidth = height - STATION_INFO - MARGIN
576        else:
577            raise ValueError("unknown trace order %d" % self.traceorder)
578
579        self.width = width
580        self.height = height
581        self.traceheight = theight
582
583        # canvas holding the final figure
584        bmap = wx.EmptyBitmap(width, height)
585        canvas = wx.MemoryDC(bmap)
586        canvas.SetBrush(wx.TRANSPARENT_BRUSH)
587        canvas.Clear()
588        canvas.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
589
590        if self.traceOrder == 0:
591            trcs = self.traces[::-1]
592        elif self.traceOrder in [1, 2, 3]:
593            trcs = self.traces
594
595        # plot every trace
596        for i, t in enumerate(trcs):
597            if not self.relativeAxis:
598                start = self.start
599                end = self.end
600            else:
601                start = t.stats.starttime
602                end = start + self.maxDuration
603
604            t._shxPrepareImageData(pltwidth, theight, (start, end), zoom=0.9)
605
606            # make trace graphics sketch
607            if self.traceOrder in [0, 1]:
608                bitmap = wx.EmptyBitmap(pltwidth, theight)
609            elif self.traceOrder in [2, 3]:
610                bitmap = wx.EmptyBitmap(theight, pltwidth)
611
612            # buffer to draw in
613            buffer = wx.MemoryDC(bitmap)
614            buffer = buffer
615            buffer.SetBrush(wx.TRANSPARENT_BRUSH)
616            buffer.Clear()
617
618            buffer.SetPen(wx.Pen((45,45,45), 1))
619
620            # calculate offset to global time scale
621            plotStartSec = t.stats.starttime - start
622            plotoffset = 0
623            if plotStartSec and not self.relativeAxis:
624                # re-calculate offset
625                portion = plotStartSec / (end - start)
626                plotoffset = portion * pltwidth
627            t.shx.plotoffset = plotoffset
628
629            # For vertical plotting the data has to be transformed
630            if self.traceOrder in [0, 1]:
631                ImageData = t.shx.ImageData
632            if self.traceOrder == 2:
633                ImageData = []
634                # simply swap x and y
635                for line in t.shx.ImageData:
636                    ImageData.append([line[1], pltwidth - line[0],
637                                                  line[3], pltwidth - line[2]])
638            elif self.traceOrder == 3:
639                ImageData = []
640                # dito plus mirror x
641                for line in t.shx.ImageData:
642                    ImageData.append([theight - line[1], line[0],
643                                                   theight - line[3], line[2]])
644
645            buffer.BeginDrawing()
646            buffer.DrawLineList(ImageData)
647            buffer.EndDrawing()
648
649            # copy trace picture to canvas
650            if self.traceOrder in [0, 1]:
651                canvas.Blit(plotoffset + STATION_INFO, i * theight, pltwidth,
652                                                 theight, buffer, 0, 0, wx.AND)
653            elif self.traceOrder == 2:
654                canvas.Blit(i * theight, -plotoffset + MARGIN, theight,
655                                                pltwidth, buffer, 0, 0, wx.AND)
656            elif self.traceOrder == 3:
657                canvas.Blit(i * theight, STATION_INFO + plotoffset, theight,
658                                                pltwidth, buffer, 0, 0, wx.AND)
659
660            # trace numbering
661            if self.traceOrder in [1, 2, 3]:
662                idx = i+1
663            elif self.traceOrder == 0:
664                idx = -i+len(self.traces)
665            txt = "%d: %s %s" % (idx, t.stats.station, t.stats.channel[-1])
666
667            canvas.SetPen(wx.Pen('Grey', 1, wx.LONG_DASH))
668
669            # place trace names
670            # helper lines for debugging
671            w, h, _, _ = canvas.GetFullTextExtent(txt)
672            t.shx.midpoint = tmp = i * theight + theight//2
673            if self.traceOrder in [0, 1]:
674                canvas.DrawText(txt, 5, tmp - h // 2)
675                canvas.DrawLine(STATION_INFO, tmp, width - MARGIN, tmp)
676                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
677                canvas.DrawLine(STATION_INFO, tmp + theight // 2,
678                                            width - MARGIN, tmp + theight // 2)
679                canvas.DrawLine(STATION_INFO, tmp - theight // 2,
680                                            width - MARGIN, tmp - theight // 2)
681            elif self.traceOrder == 2:
682                canvas.DrawRotatedText(txt, tmp - h // 2, height - MARGIN, 90)
683                canvas.DrawLine(t.shx.midpoint, MARGIN,
684                                                    tmp, height - STATION_INFO)
685                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
686                canvas.DrawLine(tmp + theight // 2, MARGIN,
687                                     tmp + theight // 2, height - STATION_INFO)
688                canvas.DrawLine(tmp - theight // 2, MARGIN,
689                                     tmp - theight // 2, height - STATION_INFO)
690            elif self.traceOrder == 3:
691                canvas.DrawRotatedText(txt, tmp - h // 2,
692                                    STATION_INFO - (STATION_INFO - w) // 2, 90)
693                canvas.DrawLine(tmp, STATION_INFO, tmp, height - MARGIN)
694                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
695                canvas.DrawLine(tmp+theight // 2, STATION_INFO,
696                                           tmp + theight // 2, height - MARGIN)
697                canvas.DrawLine(tmp - theight // 2, STATION_INFO,
698                                           tmp - theight // 2, height - MARGIN)
699
700        # time axis
701        canvas.SetPen(wx.Pen('Black', 1))
702        if self.relativeAxis:
703            end = end - start
704            start = 0
705
706        if self.traceOrder in [0, 1]:
707            PARTS = 5.  # axis split into X parts
708            length = width - MARGIN - STATION_INFO  # pixel length of time axis
709            fixpos = height - TIMESCALE + 10  # here: y coordinate
710            varpos_start = STATION_INFO  # here: x start
711        elif self.traceOrder in [2, 3]:
712            PARTS = 4.
713            length = height - MARGIN - STATION_INFO
714            fixpos = width - TIMESCALE + 10  # here: x coordinate
715            if self.traceOrder == 2:
716                varpos_start = MARGIN  # here: y start
717            elif self.traceOrder == 3:
718                varpos_start = STATION_INFO
719
720        chunk = length / PARTS
721        chunk2 = chunk / 5.
722        chunk_t = (end - start) / PARTS
723
724        # basic time axis line
725        if self.traceOrder in [0, 1]:
726            canvas.DrawLine(varpos_start, fixpos,
727                                                 length + varpos_start, fixpos)
728        elif self.traceOrder in [2, 3]:
729            canvas.DrawLine(fixpos, varpos_start,
730                                                 fixpos, varpos_start + length)
731
732        # sections with ticks and time string
733        for i in range(int(PARTS)):
734            if isinstance(start, UTCDateTime):
735                timecode = start + i*chunk_t
736                txt = timecode.strftime("%H:%M:%S.") + \
737                                             str(timecode.microsecond//1000)
738            else:
739                txt = str(start + i*chunk_t)
740
741            tw = canvas.GetFullTextExtent(txt)[0]
742
743            if self.traceOrder in [0, 1]:
744                varpos_current = varpos_start + i * chunk
745                # time string
746                canvas.DrawText(txt, varpos_current - tw / 2,
747                                                fixpos + TIMESCALE_OFFSET_TEXT)
748                # major tick
749                canvas.DrawLine(varpos_current, fixpos,
750                                varpos_current, fixpos + TIMESCALE_TICKS_MAJOR)
751                # minor ticks
752                for j in range(1, 5):
753                    canvas.DrawLine(
754                        varpos_current + j * chunk2,
755                        fixpos,
756                        varpos_current + j * chunk2,
757                        fixpos + TIMESCALE_TICKS_MINOR
758                    )
759            elif self.traceOrder in [2, 3]:
760                if self.traceOrder == 2:
761                    varpos_current = varpos_start + (PARTS - i) * chunk
762                elif self.traceOrder == 3:
763                    varpos_current = varpos_start + i * chunk
764                canvas.DrawRotatedText(txt, fixpos + TIMESCALE_OFFSET_TEXT,
765                                                   varpos_current + tw / 2, 90)
766                canvas.DrawLine(fixpos, varpos_current,
767                                fixpos + TIMESCALE_TICKS_MAJOR, varpos_current)
768                for j in range(1, 5):
769                    if self.traceOrder == 2:
770                        j -= 5
771                    canvas.DrawLine(
772                        fixpos,
773                        varpos_current + j * chunk2,
774                        fixpos + TIMESCALE_TICKS_MINOR,
775                        varpos_current + j * chunk2
776                     )
777
778        # make it visible
779        dc2 = wx.AutoBufferedPaintDCFactory(self)
780        dc2.Clear()
781        x, y = self.CalcUnscrolledPosition(0, 0)
782        dc2.Blit(0, 0, width, height, canvas, x, y)
783
784    def _debug(self, *args):
785        msgs.sendMessage('log.debug', message=" ".join([str(i) for i in args]))
786
787    def _resetTimeRange(self, **kwargs):
788        """
789        Gather information about traces. Called via message system.
790        """
791        min = UTCDateTime() # now
792        max = UTCDateTime(0) # 1970
793        maxDuration = 0
794        amplitude = 0
795
796        for t in self.traces:
797            if t.stats.starttime < min:
798                min = t.stats.starttime
799            if t.stats.endtime > max:
800                max = t.stats.endtime
801            # needed for relative time axis
802            l = t.stats.endtime - t.stats.starttime
803            if l > maxDuration:
804                maxDuration = l
805            # used for normation
806            t_ampl = abs(t.max() * t.stats.calib)
807            if t_ampl > amplitude:
808                amplitude = t_ampl
809
810        self.start = min
811        self.end = max
812        self.maxDuration = maxDuration
813        self.maxAmplitude = amplitude
814
815    def __zoombox(self, dc, start, end, midpoint, color="DARKORANGE", size=1):
816        marginfactor = 0.3
817        mf2 = (1 - marginfactor) * 2
818        th2 = self.traceheight / 2
819        offset = midpoint - th2 - self.Scrolled
820
821        dc.SetPen(wx.Pen(color, size))
822        if self.traceOrder in [0, 1]:
823            dc.DrawRectangle(start[0], offset + th2 * marginfactor,
824                                                  end[0] - start[0], th2 * mf2)
825        elif self.traceOrder in [2, 3]:
826            dc.DrawRectangle(offset + th2 * marginfactor, start[1],
827                                                  th2 * mf2, end[1] - start[1])
828
829class tracePlotter(wx.Frame):
830    """
831    Basic program frame.
832    """
833    def __init__(self, parent, title, size=(640,280)):
834        wx.Frame.__init__(self, parent, title=title, size=size,
835                      style=wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE)
836
837        files = []
838        for fname in sys.argv[1:]:
839            files.append(fname)
840
841        try:
842            self.dirname, self.filename = os.path.split(files[-1])
843        except IndexError:
844            fname = None
845
846        # menu
847        menuBar = wx.MenuBar()
848        firstmenu = wx.Menu()
849        readdb = wx.MenuItem(firstmenu, wx.NewId(), 'Re&ad...\tr')
850        readdb.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN,
851                                                       wx.ART_OTHER, (16, 16)))
852        self.Bind(wx.EVT_MENU, self.OnReadDb, readdb)
853        firstmenu.AppendItem(readdb)
854
855        read = wx.MenuItem(firstmenu, wx.NewId(), 'Read &file...\tCTRL+R')
856        read.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,
857                                                       wx.ART_OTHER, (16, 16)))
858        self.Bind(wx.EVT_MENU, self.OnRead, read)
859        firstmenu.AppendItem(read)
860
861        quit = wx.MenuItem(firstmenu, wx.NewId(), '&Quit\tCtrl+Q')
862        quit.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_QUIT,
863                                                       wx.ART_OTHER, (16, 16)))
864        self.Bind(wx.EVT_MENU, self.OnQuit, quit)
865        firstmenu.AppendItem(quit)
866
867        menuBar.Append(firstmenu, "&File");
868        self.SetMenuBar(menuBar)
869
870        # key board shortcuts / not working in GTK!!
871        self.Accelerators = wx.AcceleratorTable(
872            [(wx.ACCEL_CTRL, ord('r'), read.GetId()),
873             (wx.ACCEL_NORMAL, ord('x'), read.GetId()),
874             (wx.ACCEL_NORMAL, wx.WXK_LEFT, read.GetId()),
875            ])
876        self.SetAcceleratorTable(self.Accelerators)
877
878#        self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
879
880        # toolbar
881        self.toolbar = tb = self.CreateToolBar(wx.TB_TEXT | wx.TB_NOICONS, -1)
882
883        msg = ' time-axis '
884        tb.AddControl(wx.StaticText(tb, -1, msg))
885        timeaxis = wx.Choice(tb, wx.NewId(), wx.DefaultPosition, wx.DefaultSize,
886                                                      ["absolute", "relative"])
887        tb.AddControl(timeaxis)
888        self.Bind(wx.EVT_CHOICE, self.OnTimeAxisChange, timeaxis)
889
890        msg = ' trace order/direction '
891        tb.AddControl(wx.StaticText(tb, -1, msg))
892        traceorder = wx.Choice(
893            tb, wx.NewId(), wx.DefaultPosition, wx.DefaultSize,
894            ["left-right, first at bottom", "left-right, first at top",
895             "bottom-top, first left", "top-bottom, first left"]
896        )
897        tb.AddControl(traceorder)
898        self.Bind(wx.EVT_CHOICE, self.OnTraceOrderChange, traceorder)
899
900        msg = ' phase '
901        tb.AddControl(wx.StaticText(tb, -1, msg))
902        phase = wx.TextCtrl(tb, wx.NewId(), 'P')
903        tb.AddControl(phase)
904        self.Bind(wx.EVT_TEXT, self.OnPhaseNameChange, phase)
905
906        msg = ' height '
907        tb.AddControl(wx.StaticText(tb, -1, msg))
908        height = wx.Slider(tb, wx.NewId(), 2, 1, 2, wx.DefaultPosition,
909                                     (70, -1), wx.SL_HORIZONTAL | wx.SL_LABELS)
910        tb.AddControl(height)
911        self.height = height
912        self.Bind(wx.EVT_SCROLL_CHANGED, self.OnHeightChange, height)
913
914        tb.Realize()
915
916        # messaging system
917        msgs.subscribe(self.OnNofityHeight, 'notify.tracecount')
918
919        # init canvas
920        self.canvas = traceCanvas(self, files)
921        self.CreateStatusBar()
922        self.SetStatusText("Seimic Handler wxWidgets version")
923
924        self.Show()
925
926    # event processing
927    def OnPhaseNameChange(self, evt):
928        msgs.sendMessage('ui.change.phasename', name=evt.GetString())
929
930    def OnTraceOrderChange(self, evt):
931        msgs.sendMessage('ui.change.traceorder', order=evt.GetSelection())
932
933    def OnTimeAxisChange(self, evt):
934        msgs.sendMessage('ui.change.timeaxis', orientation=evt.GetSelection())
935
936    def OnKeyDown(self, evt):
937        kc = evt.GetKeyCode()
938        if kc == ord("r"):
939            self.onRead(evt)
940        else:
941            print msgs.sendMessage('log.debug', message=("key code %d" % kc))
942
943    def OnQuit(self, evt):
944        self.Close()
945
946    def OnRead(self, evt):
947        """ Open a file"""
948        self.dirname = getattr(self, "dirname", "")
949        self.filename = getattr(self, "filename", "")
950       
951        dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*",
952                                                      wx.OPEN | wx.FD_MULTIPLE)
953
954        files = []
955        if dlg.ShowModal() == wx.ID_OK:
956            files = dlg.GetFilenames()
957            self.filename = dlg.GetFilename()
958            self.dirname = dlg.GetDirectory()
959        dlg.Destroy()
960
961        for fname in files:
962            fqpn = os.path.join(self.dirname, fname)
963
964            self.SetStatusText(fqpn)
965            self.canvas.load([fqpn])
966
967    def OnReadDb(self, evt):
968        dlg = wx.MessageDialog(self, "not implemented yet",
969                                                    "An error occured!", wx.OK)
970        dlg.ShowModal()
971        dlg.Destroy()
972
973    def OnHeightChange(self, evt):
974        msgs.sendMessage('ui.change.displayheight', height=evt.GetPosition())
975
976    def OnNofityHeight(self, count, reset=False):
977        """
978        Alter style of control in respect to number of traces.
979        """
980        if count < 2:
981            self.height.Disable()
982            return
983        else:
984            self.height.Enable()
985
986        # new traces -> full display?
987        if reset or self.height.GetValue() == self.height.GetMax():
988            pos = count
989        else:
990            pos = self.height.GetValue()
991
992        if reset or count != self.height.GetMax():
993            self.height.SetMax(count)
994            self.height.SetValue(pos)
995
996def __pubmaster(topic=msgs.AUTO_TOPIC, **kwargs):
997    print "%.2f" % time.time(), "_pubmaster_", topic.getName(), kwargs
998#msgs.subscribe(__pubmaster, msgs.ALL_TOPICS)
999
1000def __debug(message, topic=msgs.AUTO_TOPIC):
1001    if type(message) == list:
1002        message = " ".join(map(str, message))
1003
1004    print "%.2f" % time.time(), "[%s]" % topic.getName(), message
1005msgs.subscribe(__debug, 'log.debug')
1006
1007def main():
1008    app = wx.App(False)
1009    appwidth = int(wx.Display().GetGeometry()[2]*.8)
1010    appheight = int(wx.GetClientDisplayRect()[3]*.6)
1011    _ = tracePlotter(None, 'Trace Plotter Test', (appwidth, appheight))
1012    app.MainLoop()
1013
1014if __name__ == '__main__':
1015    main()
Note: See TracBrowser for help on using the repository browser.