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

Revision 424, 34.0 KB checked in by marcus, 12 years ago (diff)

two small comments

  • 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), trace.shx.midpoint, \
161                                                                         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 event.
252        """
253        if self.refresh:
254            self._drawTraces()
255            self._drawPicksAndZoom()
256            self.refresh = False
257
258    def OnPaint(self, evt, renewOverlay=False):
259        """
260        Called if redraw is requested.
261
262        Just set flag for redraw - execution triggered from IDLE event. In case
263        of resize renew overlay buffers.
264        """
265        # if window was resized, renew overlay buffers
266        if evt and (renewOverlay or evt.GetEventType() == wx.EVT_SIZE.typeId):
267            self._setup()
268            self.overlay_picks.Reset()
269            self.overlay_drag.Reset()
270
271        # painting is only done in idle state
272        self.refresh = True
273
274    # Usually called via message system.
275    def OnNotifyTimeAxis(self, orientation):
276        """
277        Handle changes of time axis mode (absolute vs. relative time axis).
278        """
279        recent = self.relativeAxis
280        self.relativeAxis = orientation == 1
281        if recent != self.relativeAxis:
282
283            # BAD!
284            self.OnPaint(True, renewOverlay=True)
285
286    def OnNotifyTraceOrder(self, order):
287        """
288        Handle changes of trace plotting mode (horizontal vs. vertical modes).
289        """
290        recent = self.traceOrder
291        self.traceOrder = order
292        if recent != self.traceOrder:
293            # reset display height
294            msgs.sendMessage('notify.tracecount', count=len(self.traces), reset=True)
295            msgs.sendMessage('ui.change.displayheight', height=len(self.traces))
296            self._setup()
297            # BAD: setting evt=True
298            self.OnPaint(True, renewOverlay=True)
299
300    def OnNotifyHeight(self, height):
301        """
302        Handle changes of display height.
303        """
304        if self.traceOrder in [0, 1]:
305            self.space = self.GetClientSize()[1]
306        elif self.traceOrder in [2, 3]:
307            self.space = self.GetClientSize()[0]
308
309        if height != len(self.traces):
310            self.space *= len(self.traces) / float(height)
311
312        self._setup()
313        self.OnPaint(None)
314
315    def OnNotifyPhaseName(self, name):
316        self.phasename = name
317
318    # helper functions
319    def ScreenToTraceAndTime(self, x, y):
320        """
321        Returns trace instance and time code derived from cursor position inside
322        graphical panel.
323        """
324        x, y = self.CalcUnscrolledPosition((x,y))
325        # horizontal display
326        if self.traceOrder in [0, 1]:
327            tracepos, timepos = y, x
328        # vertical display
329        elif self.traceOrder in [2, 3]:
330            tracepos, timepos = x, y
331
332        # get trace under cursor
333        trace = None
334        theight = self.traceheight // 2
335        for t in self.traces:
336            if tracepos >= t.shx.midpoint - theight and \
337                                           tracepos <= t.shx.midpoint + theight:
338                trace = t
339                break
340
341        # get time code
342        if trace and self.relativeAxis:
343            start = trace.stats.starttime
344            end = trace.stats.endtime
345            pixel_width = trace.shx.PlotPixels
346            # only needed for vertical plotting
347            length = self.height - STATION_INFO - MARGIN
348        else:
349            # global time axis
350            start = self.start
351            end = self.end
352            # horizontal
353            if self.traceOrder in [0, 1]:
354                pixel_width = self.width - STATION_INFO - MARGIN
355            # vertical
356            elif self.traceOrder in [2, 3]:
357                pixel_width = self.height - STATION_INFO - MARGIN
358
359        duration = end - start
360        timestamp = None
361
362        # horizontal plotting
363        if self.traceOrder in [0, 1]:
364            timepos -= STATION_INFO
365        # vertical plotting
366        elif self.traceOrder == 2:
367            if self.relativeAxis:
368                timepos -= length + MARGIN
369            else:
370                timepos -= pixel_width + MARGIN
371            timepos *= -1
372        elif self.traceOrder == 3:
373            timepos -= STATION_INFO
374
375        t = timepos * duration / pixel_width
376        timestamp = start + t
377
378        # do not return trace if timestamp is outside, region is enlarged by one
379        # pixel
380        pixel_duration = duration/pixel_width
381        if trace and (trace.stats.starttime > timestamp + pixel_duration or \
382                              trace.stats.endtime + pixel_duration < timestamp):
383            trace = None
384
385        return trace, timestamp
386
387    def TraceAndTimeToScreen(self, trace, time):
388        """
389        Return x, y from given trace (midpoint) and time code.
390        """
391        if self.relativeAxis:
392            start = trace.stats.starttime
393            end = trace.stats.endtime
394            pixel_width = trace.shx.PlotPixels
395        else:
396            start = self.start
397            end = self.end
398
399            # horizontal
400            if self.traceOrder in [0, 1]:
401                pixel_width = self.width - STATION_INFO - MARGIN
402            # vertical
403            elif self.traceOrder in [2, 3]:
404                pixel_width = self.height - STATION_INFO - MARGIN
405
406        # relative position inside trace window
407        relpos = (time - start) / (end - start) * pixel_width
408
409        # horizontal
410        if self.traceOrder in [0, 1]:
411            x, y = self.CalcScrolledPosition((relpos + STATION_INFO, trace.shx.midpoint))
412        # vertical
413        elif self.traceOrder == 2:
414            x, y = self.CalcScrolledPosition((trace.shx.midpoint, self.height - relpos - STATION_INFO))
415        elif self.traceOrder == 3:
416            x, y = self.CalcScrolledPosition((trace.shx.midpoint, relpos + STATION_INFO))
417        return x, y
418
419    def load(self, fnames=[]):
420        """
421        Load waveform file(s).
422        """
423        if not fnames:
424            fnames = [None]
425            testTrace = True
426
427        for fname in fnames:
428            self._debug("Loading %s..." % fname)
429
430            try:
431                st = read(fname)
432                trace = st[0]
433                self.traces.add(trace)
434            except Exception, e:
435                msg = e.message and e.message or "File not readable!"
436                dlg = wx.MessageDialog(self, msg, "An error occured!", \
437                                                          wx.OK | wx.ICON_ERROR)
438                dlg.ShowModal()
439                dlg.Destroy()
440
441                self._debug(e.message)
442
443        try:
444            if testTrace:
445                t = self.traces[0].copy()
446                t.stats.network = "XX"
447                t.stats.channel = "EHA"
448                t.trim(t.stats.starttime+4, t.stats.endtime-10)
449                self.traces.add(t)
450                t = self.traces[0].copy()
451                t.stats.network = "XX"
452                t.stats.channel = "EHB"
453                t.trim(t.stats.starttime+10, t.stats.endtime-3)
454                self.traces.add(t)
455        except Exception, inst:
456            print inst
457            pass
458
459        msgs.sendMessage('notify.tracecount', count=len(self.traces))
460
461    def _setup(self):
462        w, h = self.GetClientSize()
463        if self.traceOrder in [0, 1]:
464            if self.space is None:
465                self.space = h
466            self.SetVirtualSizeWH(w, self.space)
467            self.SetScrollRate(0, 20)
468        elif self.traceOrder in [2, 3]:
469            if self.space is None:
470                self.space = w
471            self.SetVirtualSizeWH(self.space, h)
472            self.SetScrollRate(20, 0)
473
474    def _captureMouse(self, evt):
475        self._debug("_captureMouse")
476
477        self.CaptureMouse()
478
479        dc = wx.ClientDC(self)
480        odc = wx.DCOverlay(self.overlay_drag, dc)
481        odc.Clear()
482
483        self.dragStart = evt.GetPosition()
484
485    def _releaseMouse(self, evt):
486        self._debug("_releaseMouse")
487       
488        self.ReleaseMouse()
489
490        # restore view
491        dc = wx.ClientDC(self)
492        odc = wx.DCOverlay(self.overlay_drag, dc)
493        odc.Clear()
494        del odc
495        self.overlay_drag.Reset()
496
497    def _drawPicksAndZoom(self):
498        """
499        Draw picks.
500        """
501        _picks = []
502        for t in self.traces:
503            try:
504                picks = t.shx.picks
505            except KeyError:
506                continue
507
508            for mode in picks:
509                for pick in picks[mode]:
510                    _picks.append(
511                        [mode, pick,
512                         self.TraceAndTimeToScreen(t, picks[mode][pick])]
513                    )
514
515        if not _picks and not self.ZoomWindow:
516            return
517
518        dc = wx.ClientDC(self)
519        odc = wx.DCOverlay(self.overlay_picks, dc)
520        odc.Clear()
521        dc.SetBrush(wx.TRANSPARENT_BRUSH)
522
523        th2 = self.traceheight / 2
524        # draw picks
525        for pick in _picks:
526            color = "Red"
527            dc.SetPen(wx.Pen(color, 1))
528            dc.SetTextForeground(color)
529            if self.traceOrder in [0, 1]:
530                dc.DrawLine(
531                    pick[2][0], pick[2][1] - th2,
532                    pick[2][0], pick[2][1] + th2
533                )
534                dc.DrawText(pick[1], pick[2][0] + 2, pick[2][1] - th2 + 2)
535            elif self.traceOrder in [2, 3]:
536                dc.DrawLine(
537                    pick[2][0] - th2, pick[2][1],
538                    pick[2][0] + th2, pick[2][1],
539                )
540                dc.DrawRotatedText(pick[1], pick[2][0]-th2+2, pick[2][1]-2, 90)
541
542        #draw zoom window
543        if self.ZoomWindow:
544            trace, start, end = self.ZoomWindow
545            start = self.TraceAndTimeToScreen(trace, start)
546            end = self.TraceAndTimeToScreen(trace, end)
547
548            self._debug("draw zoom window")
549            self.__zoombox(dc, start, end, trace.shx.midpoint, color="Blue")
550
551        del odc
552
553    def _drawTraces(self):
554        """
555        Do the real plotting of waveforms including station text and time scale.
556        """
557        width, height = self.GetVirtualSize()
558        numTraces = len(self.traces)
559
560        if self.traceOrder in [0, 1]:
561            theight = (height - TIMESCALE) // numTraces
562            pltwidth = width - STATION_INFO - MARGIN
563        elif self.traceOrder in [2, 3]:
564            theight = (width - TIMESCALE) // numTraces
565            pltwidth = height - STATION_INFO - MARGIN
566        else:
567            raise ValueError("unknown trace order %d" % self.traceorder)
568
569        self.width = width
570        self.height = height
571        self.traceheight = theight
572
573        # canvas holding the final figure
574        bmap = wx.EmptyBitmap(width, height)
575        canvas = wx.MemoryDC(bmap)
576        canvas.SetBrush(wx.TRANSPARENT_BRUSH)
577        canvas.Clear()
578        canvas.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
579
580        if self.traceOrder == 0:
581            trcs = self.traces[::-1]
582        elif self.traceOrder in [1, 2, 3]:
583            trcs = self.traces
584
585        # plot every trace
586        for i, t in enumerate(trcs):
587            if not self.relativeAxis:
588                start = self.start
589                end = self.end
590            else:
591                start = t.stats.starttime
592                end = start + self.maxDuration
593
594            t._shxPrepareImageData(pltwidth, theight, (start, end), zoom=0.9)
595
596            # make trace graphics sketch
597            if self.traceOrder in [0, 1]:
598                bitmap = wx.EmptyBitmap(pltwidth, theight)
599            elif self.traceOrder in [2, 3]:
600                bitmap = wx.EmptyBitmap(theight, pltwidth)
601
602            # buffer to draw in
603            buffer = wx.MemoryDC(bitmap)
604            buffer = buffer
605            buffer.SetBrush(wx.TRANSPARENT_BRUSH)
606            buffer.Clear()
607
608            buffer.SetPen(wx.Pen((45,45,45), 1))
609
610            # calculate offset to global time scale
611            plotStartSec = t.stats.starttime - start
612            plotoffset = 0
613            if plotStartSec and not self.relativeAxis:
614                # re-calculate offset
615                portion = plotStartSec / (end - start)
616                plotoffset = portion * pltwidth
617            t.shx.plotoffset = plotoffset
618
619            # For vertical plotting the data has to be transformed
620            if self.traceOrder in [0, 1]:
621                ImageData = t.shx.ImageData
622            if self.traceOrder == 2:
623                ImageData = []
624                # simply swap x and y
625                for line in t.shx.ImageData:
626                    ImageData.append([line[1], pltwidth - line[0], line[3], \
627                                                            pltwidth - line[2]])
628            elif self.traceOrder == 3:
629                ImageData = []
630                # dito plus mirror x
631                for line in t.shx.ImageData:
632                    ImageData.append([theight - line[1], line[0], \
633                                                    theight - line[3], line[2]])
634
635            buffer.BeginDrawing()
636            buffer.DrawLineList(ImageData)
637            buffer.EndDrawing()
638
639            # copy trace picture to canvas
640            if self.traceOrder in [0, 1]:
641                canvas.Blit(plotoffset + STATION_INFO, i * theight, pltwidth, \
642                                                  theight, buffer, 0, 0, wx.AND)
643            elif self.traceOrder == 2:
644                canvas.Blit(i * theight, -plotoffset + MARGIN, theight, \
645                                                 pltwidth, buffer, 0, 0, wx.AND)
646            elif self.traceOrder == 3:
647                canvas.Blit(i * theight, STATION_INFO + plotoffset, theight, \
648                                                 pltwidth, buffer, 0, 0, wx.AND)
649
650            # trace numbering
651            if self.traceOrder in [1, 2, 3]:
652                idx = i+1
653            elif self.traceOrder == 0:
654                idx = -i+len(self.traces)
655            txt = "%d: %s %s" % (idx, t.stats.station, t.stats.channel[-1])
656
657            canvas.SetPen(wx.Pen('Grey', 1, wx.LONG_DASH))
658
659            # place trace names
660            # helper lines for debugging
661            w, h, _, _ = canvas.GetFullTextExtent(txt)
662            t.shx.midpoint = tmp = i * theight + theight//2
663            if self.traceOrder in [0, 1]:
664                canvas.DrawText(txt, 5, tmp - h//2)
665                canvas.DrawLine(STATION_INFO, tmp, width-MARGIN, tmp)
666                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
667                canvas.DrawLine(STATION_INFO, tmp+theight//2, width-MARGIN, \
668                                                                 tmp+theight//2)
669                canvas.DrawLine(STATION_INFO, tmp-theight//2, width-MARGIN, \
670                                                                 tmp-theight//2)
671            elif self.traceOrder == 2:
672                canvas.DrawRotatedText(txt, tmp - h//2, height - MARGIN, 90)
673                canvas.DrawLine(t.shx.midpoint, MARGIN, tmp, height-STATION_INFO)
674                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
675                canvas.DrawLine(tmp+theight//2, MARGIN, tmp+theight//2, \
676                                                            height-STATION_INFO)
677                canvas.DrawLine(tmp-theight//2, MARGIN, tmp-theight//2, \
678                                                            height-STATION_INFO)
679            elif self.traceOrder == 3:
680                canvas.DrawRotatedText(txt, tmp - h//2, \
681                                         STATION_INFO - (STATION_INFO-w)//2, 90)
682                canvas.DrawLine(tmp, STATION_INFO, tmp, height-MARGIN)
683                canvas.SetPen(wx.Pen('Red', 1, wx.LONG_DASH))
684                canvas.DrawLine(tmp+theight//2, STATION_INFO, tmp+theight//2, \
685                                                                  height-MARGIN)
686                canvas.DrawLine(tmp-theight//2, STATION_INFO, tmp-theight//2, \
687                                                                  height-MARGIN)
688
689        # time axis
690        canvas.SetPen(wx.Pen('Black', 1))
691        if self.relativeAxis:
692            end = end - start
693            start = 0
694
695        if self.traceOrder in [0, 1]:
696            PARTS = 5.  # axis split into X parts
697            length = width - MARGIN - STATION_INFO  # pixel length of time axis
698            fixpos = height - TIMESCALE + 10  # here: y coordinate
699            varpos_start = STATION_INFO  # here: x start
700        elif self.traceOrder in [2, 3]:
701            PARTS = 4.
702            length = height - MARGIN - STATION_INFO
703            fixpos = width - TIMESCALE + 10  # here: x coordinate
704            if self.traceOrder == 2:
705                varpos_start = MARGIN  # here: y start
706            elif self.traceOrder == 3:
707                varpos_start = STATION_INFO
708
709        chunk = length / PARTS
710        chunk2 = chunk / 5.
711        chunk_t = (end - start) / PARTS
712
713        # basic time axis line
714        if self.traceOrder in [0, 1]:
715            canvas.DrawLine(varpos_start, fixpos, length + varpos_start, fixpos)
716        elif self.traceOrder in [2, 3]:
717            canvas.DrawLine(fixpos, varpos_start, fixpos, varpos_start + length)
718
719        # sections with ticks and time string
720        for i in range(int(PARTS)):
721            if isinstance(start, UTCDateTime):
722                timecode = start + i*chunk_t
723                txt = timecode.strftime("%H:%M:%S.") + \
724                                             str(timecode.microsecond//1000)
725            else:
726                txt = str(start + i*chunk_t)
727
728            tw = canvas.GetFullTextExtent(txt)[0]
729
730            if self.traceOrder in [0, 1]:
731                varpos_current = varpos_start + i * chunk
732                # time string
733                canvas.DrawText(txt, varpos_current - tw / 2,
734                                                 fixpos + TIMESCALE_OFFSET_TEXT)
735                # major tick
736                canvas.DrawLine(varpos_current, fixpos,
737                                 varpos_current, fixpos + TIMESCALE_TICKS_MAJOR)
738                # minor ticks
739                for j in range(1, 5):
740                    canvas.DrawLine(
741                        varpos_current + j * chunk2,
742                        fixpos,
743                        varpos_current + j * chunk2,
744                        fixpos + TIMESCALE_TICKS_MINOR
745                    )
746            elif self.traceOrder in [2, 3]:
747                if self.traceOrder == 2:
748                    varpos_current = varpos_start + (PARTS - i) * chunk
749                elif self.traceOrder == 3:
750                    varpos_current = varpos_start + i * chunk
751                canvas.DrawRotatedText(txt, fixpos + TIMESCALE_OFFSET_TEXT,
752                                                    varpos_current + tw / 2, 90)
753                canvas.DrawLine(fixpos, varpos_current,
754                                 fixpos + TIMESCALE_TICKS_MAJOR, varpos_current)
755                for j in range(1, 5):
756                    if self.traceOrder == 2:
757                        j -= 5
758                    canvas.DrawLine(
759                        fixpos,
760                        varpos_current + j * chunk2,
761                         fixpos + TIMESCALE_TICKS_MINOR,
762                         varpos_current + j * chunk2
763                     )
764
765        # make it visible
766        dc2 = wx.AutoBufferedPaintDCFactory(self)
767        dc2.Clear()
768        x, y = self.CalcUnscrolledPosition(0, 0)
769        dc2.Blit(0, 0, width, height, canvas, x, y)
770
771    def _debug(self, *args):
772        msgs.sendMessage('log.debug', message=" ".join([str(i) for i in args]))
773
774    def _resetTimeRange(self, **kwargs):
775        """
776        Gather information about traces. Called via message bus.
777        """
778        min = UTCDateTime() # now
779        max = UTCDateTime(0) # 1970
780        maxDuration = 0
781        amplitude = 0
782
783        for t in self.traces:
784            if t.stats.starttime < min:
785                min = t.stats.starttime
786            if t.stats.endtime > max:
787                max = t.stats.endtime
788            # needed for relative time axis
789            l = t.stats.endtime - t.stats.starttime
790            if l > maxDuration:
791                maxDuration = l
792            # used for normation
793            t_ampl = abs(t.max() * t.stats.calib)
794            if t_ampl > amplitude:
795                amplitude = t_ampl
796
797        self.start = min
798        self.end = max
799        self.maxDuration = maxDuration
800        self.maxAmplitude = amplitude
801
802    def __zoombox(self, dc, start, end, midpoint, color="DARKORANGE", size=1):
803        marginfactor = 0.3
804        mf2 = (1-marginfactor)*2
805        th2 = self.traceheight / 2
806        offset = midpoint - th2 - self.Scrolled
807
808        dc.SetPen(wx.Pen(color, size))
809        if self.traceOrder in [0, 1]:
810            dc.DrawRectangle(start[0], offset + th2*marginfactor, \
811                                                       end[0]-start[0], th2*mf2)
812        elif self.traceOrder in [2, 3]:
813            dc.DrawRectangle(offset + th2*marginfactor, start[1], \
814                                                       th2*mf2, end[1]-start[1])
815
816class tracePlotter(wx.Frame):
817    """
818    Basic program frame.
819    """
820    def __init__(self, parent, title, size=(640,280)):
821        wx.Frame.__init__(self, parent, title=title, size=size, \
822                       style=wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE)
823
824        files = []
825        for fname in sys.argv[1:]:
826            files.append(fname)
827
828        try:
829            self.dirname, self.filename = os.path.split(files[-1])
830        except IndexError:
831            fname = None
832
833        # menu
834        menuBar = wx.MenuBar()
835        firstmenu = wx.Menu()
836        readdb = wx.MenuItem(firstmenu, wx.NewId(), 'Re&ad...\tr')
837        readdb.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, \
838                                                        wx.ART_OTHER, (16, 16)))
839        self.Bind(wx.EVT_MENU, self.OnReadDb, readdb)
840        firstmenu.AppendItem(readdb)
841
842        read = wx.MenuItem(firstmenu, wx.NewId(), 'Read &file...\tCTRL+R')
843        read.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, \
844                                                        wx.ART_OTHER, (16, 16)))
845        self.Bind(wx.EVT_MENU, self.OnRead, read)
846        firstmenu.AppendItem(read)
847
848        quit = wx.MenuItem(firstmenu, wx.NewId(), '&Quit\tCtrl+Q')
849        quit.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_QUIT,
850                                                        wx.ART_OTHER, (16, 16)))
851        self.Bind(wx.EVT_MENU, self.OnQuit, quit)
852        firstmenu.AppendItem(quit)
853
854        menuBar.Append(firstmenu, "&File");
855        self.SetMenuBar(menuBar)
856
857        # key board shortcuts / not working in GTK!!
858        self.Accelerators = wx.AcceleratorTable(
859            [(wx.ACCEL_CTRL, ord('r'), read.GetId()),
860             (wx.ACCEL_NORMAL, ord('x'), read.GetId()),
861             (wx.ACCEL_NORMAL, wx.WXK_LEFT, read.GetId()),
862            ])
863        self.SetAcceleratorTable(self.Accelerators)
864
865#        self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
866
867        # toolbar
868        self.toolbar = tb = self.CreateToolBar(wx.TB_TEXT | wx.TB_NOICONS, -1)
869
870        msg = ' time-axis '
871        tb.AddControl(wx.StaticText(tb, -1, msg))
872        timeaxis = wx.Choice(tb, wx.NewId(), wx.DefaultPosition, wx.DefaultSize,
873                                                       ["absolute", "relative"])
874        tb.AddControl(timeaxis)
875        self.Bind(wx.EVT_CHOICE, self.OnTimeAxisChange, timeaxis)
876
877        msg = ' trace order/direction '
878        tb.AddControl(wx.StaticText(tb, -1, msg))
879        traceorder = wx.Choice(tb, wx.NewId(), wx.DefaultPosition, wx.DefaultSize, \
880            ["left-right, first at bottom", "left-right, first at top",
881             "bottom-top, first left", "top-bottom, first left"]
882        )
883        tb.AddControl(traceorder)
884        self.Bind(wx.EVT_CHOICE, self.OnTraceOrderChange, traceorder)
885
886        msg = ' phase '
887        tb.AddControl(wx.StaticText(tb, -1, msg))
888        phase = wx.TextCtrl(tb, wx.NewId(), 'P')
889        tb.AddControl(phase)
890        self.Bind(wx.EVT_TEXT, self.OnPhaseNameChange, phase)
891
892        msg = ' height '
893        tb.AddControl(wx.StaticText(tb, -1, msg))
894        height = wx.Slider(tb, wx.NewId(), 2, 1, 2, wx.DefaultPosition, (70, -1), wx.SL_HORIZONTAL | wx.SL_LABELS)
895        tb.AddControl(height)
896        self.height = height
897        self.Bind(wx.EVT_SCROLL_CHANGED, self.OnHeightChange, height)
898
899        tb.Realize()
900
901        # messaging system
902        msgs.subscribe(self.OnNofityHeight, 'notify.tracecount')
903
904        # init canvas
905        self.canvas = traceCanvas(self, files)
906        self.CreateStatusBar()
907        self.SetStatusText("Seimic Handler wxWidgets version")
908
909        self.Show()
910
911    # event processing
912    def OnPhaseNameChange(self, evt):
913        msgs.sendMessage('ui.change.phasename', name=evt.GetString())
914
915    def OnTraceOrderChange(self, evt):
916        msgs.sendMessage('ui.change.traceorder', order=evt.GetSelection())
917
918    def OnTimeAxisChange(self, evt):
919        msgs.sendMessage('ui.change.timeaxis', orientation=evt.GetSelection())
920
921    def OnKeyDown(self, evt):
922        kc = evt.GetKeyCode()
923        if kc == ord("r"):
924            self.onRead(evt)
925        else:
926            print msgs.sendMessage('log.debug', message=("key code %d" % kc))
927
928    def OnQuit(self, evt):
929        self.Close()
930
931    def OnRead(self, evt):
932        """ Open a file"""
933        self.dirname = getattr(self, "dirname", "")
934        self.filename = getattr(self, "filename", "")
935       
936        dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", \
937                                                       wx.OPEN | wx.FD_MULTIPLE)
938
939        files = []
940        if dlg.ShowModal() == wx.ID_OK:
941            files = dlg.GetFilenames()
942            self.filename = dlg.GetFilename()
943            self.dirname = dlg.GetDirectory()
944        dlg.Destroy()
945
946        for fname in files:
947            fqpn = os.path.join(self.dirname, fname)
948
949            self.SetStatusText(fqpn)
950            self.canvas.load([fqpn])
951
952    def OnReadDb(self, evt):
953        dlg = wx.MessageDialog(self, "not implemented yet", \
954                                                     "An error occured!", wx.OK)
955        dlg.ShowModal()
956        dlg.Destroy()
957
958    def OnHeightChange(self, evt):
959        msgs.sendMessage('ui.change.displayheight', height=evt.GetPosition())
960
961    def OnNofityHeight(self, count, reset=False):
962        """
963        Alter style of control in respect to number of traces.
964        """
965        if count < 2:
966            self.height.Disable()
967            return
968        else:
969            self.height.Enable()
970
971        # new traces -> full display?
972        if reset or self.height.GetValue() == self.height.GetMax():
973            pos = count
974        else:
975            pos = self.height.GetValue()
976
977        if reset or count != self.height.GetMax():
978            self.height.SetMax(count)
979            self.height.SetValue(pos)
980
981def __pubmaster(topic=msgs.AUTO_TOPIC, **kwargs):
982    print "%.2f" % time.time(), "_pubmaster_", topic.getName(), kwargs
983#msgs.subscribe(__pubmaster, msgs.ALL_TOPICS)
984
985def __debug(message, topic=msgs.AUTO_TOPIC):
986    if type(message) == list:
987        message = " ".join(map(str, message))
988
989    print "%.2f" % time.time(), "[%s]" % topic.getName(), message
990msgs.subscribe(__debug, 'log.debug')
991
992def main():
993    app = wx.App(False)
994    appwidth = int(wx.Display().GetGeometry()[2]*.8)
995    appheight = int(wx.GetClientDisplayRect()[3]*.6)
996    _ = tracePlotter(None, 'Trace Plotter Test', (appwidth, appheight))
997    app.MainLoop()
998
999if __name__ == '__main__':
1000    main()
Note: See TracBrowser for help on using the repository browser.