110259SAndrew.Bardsley@arm.com# Copyright (c) 2013 ARM Limited 210259SAndrew.Bardsley@arm.com# All rights reserved 310259SAndrew.Bardsley@arm.com# 410259SAndrew.Bardsley@arm.com# The license below extends only to copyright in the software and shall 510259SAndrew.Bardsley@arm.com# not be construed as granting a license to any other intellectual 610259SAndrew.Bardsley@arm.com# property including but not limited to intellectual property relating 710259SAndrew.Bardsley@arm.com# to a hardware implementation of the functionality of the software 810259SAndrew.Bardsley@arm.com# licensed hereunder. You may use the software subject to the license 910259SAndrew.Bardsley@arm.com# terms below provided that you ensure that this notice is replicated 1010259SAndrew.Bardsley@arm.com# unmodified and in its entirety in all distributions of the software, 1110259SAndrew.Bardsley@arm.com# modified or unmodified, in source code or in binary form. 1210259SAndrew.Bardsley@arm.com# 1310259SAndrew.Bardsley@arm.com# Redistribution and use in source and binary forms, with or without 1410259SAndrew.Bardsley@arm.com# modification, are permitted provided that the following conditions are 1510259SAndrew.Bardsley@arm.com# met: redistributions of source code must retain the above copyright 1610259SAndrew.Bardsley@arm.com# notice, this list of conditions and the following disclaimer; 1710259SAndrew.Bardsley@arm.com# redistributions in binary form must reproduce the above copyright 1810259SAndrew.Bardsley@arm.com# notice, this list of conditions and the following disclaimer in the 1910259SAndrew.Bardsley@arm.com# documentation and/or other materials provided with the distribution; 2010259SAndrew.Bardsley@arm.com# neither the name of the copyright holders nor the names of its 2110259SAndrew.Bardsley@arm.com# contributors may be used to endorse or promote products derived from 2210259SAndrew.Bardsley@arm.com# this software without specific prior written permission. 2310259SAndrew.Bardsley@arm.com# 2410259SAndrew.Bardsley@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2510259SAndrew.Bardsley@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2610259SAndrew.Bardsley@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2710259SAndrew.Bardsley@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2810259SAndrew.Bardsley@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2910259SAndrew.Bardsley@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3010259SAndrew.Bardsley@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3110259SAndrew.Bardsley@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3210259SAndrew.Bardsley@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3310259SAndrew.Bardsley@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3410259SAndrew.Bardsley@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3510259SAndrew.Bardsley@arm.com# 3610259SAndrew.Bardsley@arm.com# Authors: Andrew Bardsley 3710259SAndrew.Bardsley@arm.com 3810259SAndrew.Bardsley@arm.comimport pygtk 3910259SAndrew.Bardsley@arm.compygtk.require('2.0') 4010259SAndrew.Bardsley@arm.comimport gtk 4110259SAndrew.Bardsley@arm.comimport gobject 4210259SAndrew.Bardsley@arm.comimport cairo 4310259SAndrew.Bardsley@arm.comimport re 4410259SAndrew.Bardsley@arm.com 4510259SAndrew.Bardsley@arm.comfrom point import Point 4610259SAndrew.Bardsley@arm.comimport parse 4710259SAndrew.Bardsley@arm.comimport colours 4810259SAndrew.Bardsley@arm.comimport model 4910259SAndrew.Bardsley@arm.comfrom model import Id, BlobModel, BlobDataSelect, special_state_chars 5010259SAndrew.Bardsley@arm.comimport blobs 5110259SAndrew.Bardsley@arm.com 5210259SAndrew.Bardsley@arm.comclass BlobView(object): 5310259SAndrew.Bardsley@arm.com """The canvas view of the pipeline""" 5410259SAndrew.Bardsley@arm.com def __init__(self, model): 5510259SAndrew.Bardsley@arm.com # A unit blob will appear at size blobSize inside a space of 5610259SAndrew.Bardsley@arm.com # size pitch. 5710259SAndrew.Bardsley@arm.com self.blobSize = Point(45.0, 45.0) 5810259SAndrew.Bardsley@arm.com self.pitch = Point(60.0, 60.0) 5910259SAndrew.Bardsley@arm.com self.origin = Point(50.0, 50.0) 6010259SAndrew.Bardsley@arm.com # Some common line definitions to cut down on arbitrary 6110259SAndrew.Bardsley@arm.com # set_line_widths 6210259SAndrew.Bardsley@arm.com self.thickLineWidth = 10.0 6310259SAndrew.Bardsley@arm.com self.thinLineWidth = 4.0 6410259SAndrew.Bardsley@arm.com self.midLineWidth = 6.0 6510259SAndrew.Bardsley@arm.com # The scale from the units of pitch to device units (nominally 6610259SAndrew.Bardsley@arm.com # pixels for 1.0 to 1.0 6710259SAndrew.Bardsley@arm.com self.masterScale = Point(1.0,1.0) 6810259SAndrew.Bardsley@arm.com self.model = model 6910259SAndrew.Bardsley@arm.com self.fillColour = colours.emptySlotColour 7010259SAndrew.Bardsley@arm.com self.timeIndex = 0 7110259SAndrew.Bardsley@arm.com self.time = 0 7210259SAndrew.Bardsley@arm.com self.positions = [] 7310259SAndrew.Bardsley@arm.com self.controlbar = None 7410259SAndrew.Bardsley@arm.com # The sequence number selector state 7510259SAndrew.Bardsley@arm.com self.dataSelect = BlobDataSelect() 7610259SAndrew.Bardsley@arm.com # Offset of this view's time from self.time used for miniviews 7710259SAndrew.Bardsley@arm.com # This is actually an offset of the index into the array of times 7810259SAndrew.Bardsley@arm.com # seen in the event file) 7910259SAndrew.Bardsley@arm.com self.timeOffset = 0 8010259SAndrew.Bardsley@arm.com # Maximum view size for initial window mapping 8110259SAndrew.Bardsley@arm.com self.initialHeight = 600.0 8210259SAndrew.Bardsley@arm.com 8310259SAndrew.Bardsley@arm.com # Overlays are speech bubbles explaining blob data 8410259SAndrew.Bardsley@arm.com self.overlays = [] 8510259SAndrew.Bardsley@arm.com 8610259SAndrew.Bardsley@arm.com self.da = gtk.DrawingArea() 8710259SAndrew.Bardsley@arm.com def draw(arg1, arg2): 8810259SAndrew.Bardsley@arm.com self.redraw() 8910259SAndrew.Bardsley@arm.com self.da.connect('expose_event', draw) 9010259SAndrew.Bardsley@arm.com 9110259SAndrew.Bardsley@arm.com # Handy offsets from the blob size 9210259SAndrew.Bardsley@arm.com self.blobIndent = (self.pitch - self.blobSize).scale(0.5) 9310259SAndrew.Bardsley@arm.com self.blobIndentFactor = self.blobIndent / self.pitch 9410259SAndrew.Bardsley@arm.com 9510259SAndrew.Bardsley@arm.com def add_control_bar(self, controlbar): 9610259SAndrew.Bardsley@arm.com """Add a BlobController to this view""" 9710259SAndrew.Bardsley@arm.com self.controlbar = controlbar 9810259SAndrew.Bardsley@arm.com 9910259SAndrew.Bardsley@arm.com def draw_to_png(self, filename): 10010259SAndrew.Bardsley@arm.com """Draw the view to a PNG file""" 10110259SAndrew.Bardsley@arm.com surface = cairo.ImageSurface( 10210259SAndrew.Bardsley@arm.com cairo.FORMAT_ARGB32, 10310259SAndrew.Bardsley@arm.com self.da.get_allocation().width, 10410259SAndrew.Bardsley@arm.com self.da.get_allocation().height) 10510259SAndrew.Bardsley@arm.com cr = gtk.gdk.CairoContext(cairo.Context(surface)) 10610259SAndrew.Bardsley@arm.com self.draw_to_cr(cr) 10710259SAndrew.Bardsley@arm.com surface.write_to_png(filename) 10810259SAndrew.Bardsley@arm.com 10910259SAndrew.Bardsley@arm.com def draw_to_cr(self, cr): 11010259SAndrew.Bardsley@arm.com """Draw to a given CairoContext""" 11110259SAndrew.Bardsley@arm.com cr.set_source_color(colours.backgroundColour) 11210259SAndrew.Bardsley@arm.com cr.set_line_width(self.thickLineWidth) 11310259SAndrew.Bardsley@arm.com cr.paint() 11410259SAndrew.Bardsley@arm.com cr.save() 11510259SAndrew.Bardsley@arm.com cr.scale(*self.masterScale.to_pair()) 11610259SAndrew.Bardsley@arm.com cr.translate(*self.origin.to_pair()) 11710259SAndrew.Bardsley@arm.com 11810259SAndrew.Bardsley@arm.com positions = [] # {} 11910259SAndrew.Bardsley@arm.com 12010259SAndrew.Bardsley@arm.com # Draw each blob 12110259SAndrew.Bardsley@arm.com for blob in self.model.blobs: 12210259SAndrew.Bardsley@arm.com blob_event = self.model.find_unit_event_by_time( 12310259SAndrew.Bardsley@arm.com blob.unit, self.time) 12410259SAndrew.Bardsley@arm.com 12510259SAndrew.Bardsley@arm.com cr.save() 12610259SAndrew.Bardsley@arm.com pos = blob.render(cr, self, blob_event, self.dataSelect, 12710259SAndrew.Bardsley@arm.com self.time) 12810259SAndrew.Bardsley@arm.com cr.restore() 12910259SAndrew.Bardsley@arm.com if pos is not None: 13010259SAndrew.Bardsley@arm.com (centre, size) = pos 13110259SAndrew.Bardsley@arm.com positions.append((blob, centre, size)) 13210259SAndrew.Bardsley@arm.com 13310259SAndrew.Bardsley@arm.com # Draw all the overlays over the top 13410259SAndrew.Bardsley@arm.com for overlay in self.overlays: 13510259SAndrew.Bardsley@arm.com overlay.show(cr) 13610259SAndrew.Bardsley@arm.com 13710259SAndrew.Bardsley@arm.com cr.restore() 13810259SAndrew.Bardsley@arm.com 13910259SAndrew.Bardsley@arm.com return positions 14010259SAndrew.Bardsley@arm.com 14110259SAndrew.Bardsley@arm.com def redraw(self): 14210259SAndrew.Bardsley@arm.com """Redraw the whole view""" 14310259SAndrew.Bardsley@arm.com buffer = cairo.ImageSurface( 14410259SAndrew.Bardsley@arm.com cairo.FORMAT_ARGB32, 14510259SAndrew.Bardsley@arm.com self.da.get_allocation().width, 14610259SAndrew.Bardsley@arm.com self.da.get_allocation().height) 14710259SAndrew.Bardsley@arm.com 14810259SAndrew.Bardsley@arm.com cr = gtk.gdk.CairoContext(cairo.Context(buffer)) 14910259SAndrew.Bardsley@arm.com positions = self.draw_to_cr(cr) 15010259SAndrew.Bardsley@arm.com 15110259SAndrew.Bardsley@arm.com # Assume that blobs are in order for depth so we want to 15210259SAndrew.Bardsley@arm.com # hit the frontmost blob first if we search by position 15310259SAndrew.Bardsley@arm.com positions.reverse() 15410259SAndrew.Bardsley@arm.com self.positions = positions 15510259SAndrew.Bardsley@arm.com 15610259SAndrew.Bardsley@arm.com # Paint the drawn buffer onto the DrawingArea 15710259SAndrew.Bardsley@arm.com dacr = self.da.window.cairo_create() 15810259SAndrew.Bardsley@arm.com dacr.set_source_surface(buffer, 0.0, 0.0) 15910259SAndrew.Bardsley@arm.com dacr.paint() 16010259SAndrew.Bardsley@arm.com 16110259SAndrew.Bardsley@arm.com buffer.finish() 16210259SAndrew.Bardsley@arm.com 16310259SAndrew.Bardsley@arm.com def set_time_index(self, time): 16410259SAndrew.Bardsley@arm.com """Set the time index for the view. A time index is an index into 16510259SAndrew.Bardsley@arm.com the model's times array of seen event times""" 16610259SAndrew.Bardsley@arm.com self.timeIndex = time + self.timeOffset 16710259SAndrew.Bardsley@arm.com if len(self.model.times) != 0: 16810259SAndrew.Bardsley@arm.com if self.timeIndex >= len(self.model.times): 16910259SAndrew.Bardsley@arm.com self.time = self.model.times[len(self.model.times) - 1] 17010259SAndrew.Bardsley@arm.com else: 17110259SAndrew.Bardsley@arm.com self.time = self.model.times[self.timeIndex] 17210259SAndrew.Bardsley@arm.com else: 17310259SAndrew.Bardsley@arm.com self.time = 0 17410259SAndrew.Bardsley@arm.com 17510259SAndrew.Bardsley@arm.com def get_pic_size(self): 17610259SAndrew.Bardsley@arm.com """Return the size of ASCII-art picture of the pipeline scaled by 17710259SAndrew.Bardsley@arm.com the blob pitch""" 17810259SAndrew.Bardsley@arm.com return (self.origin + self.pitch * 17910259SAndrew.Bardsley@arm.com (self.model.picSize + Point(1.0,1.0))) 18010259SAndrew.Bardsley@arm.com 18110259SAndrew.Bardsley@arm.com def set_da_size(self): 18210259SAndrew.Bardsley@arm.com """Set the DrawingArea size after scaling""" 18310259SAndrew.Bardsley@arm.com self.da.set_size_request(10 , int(self.initialHeight)) 18410259SAndrew.Bardsley@arm.com 18510259SAndrew.Bardsley@arm.comclass BlobController(object): 18610259SAndrew.Bardsley@arm.com """The controller bar for the viewer""" 18710259SAndrew.Bardsley@arm.com def __init__(self, model, view, 18810259SAndrew.Bardsley@arm.com defaultEventFile="", defaultPictureFile=""): 18910259SAndrew.Bardsley@arm.com self.model = model 19010259SAndrew.Bardsley@arm.com self.view = view 19110259SAndrew.Bardsley@arm.com self.playTimer = None 19210259SAndrew.Bardsley@arm.com self.filenameEntry = gtk.Entry() 19310259SAndrew.Bardsley@arm.com self.filenameEntry.set_text(defaultEventFile) 19410259SAndrew.Bardsley@arm.com self.pictureEntry = gtk.Entry() 19510259SAndrew.Bardsley@arm.com self.pictureEntry.set_text(defaultPictureFile) 19610259SAndrew.Bardsley@arm.com self.timeEntry = None 19710259SAndrew.Bardsley@arm.com self.defaultEventFile = defaultEventFile 19810259SAndrew.Bardsley@arm.com self.startTime = None 19910259SAndrew.Bardsley@arm.com self.endTime = None 20010259SAndrew.Bardsley@arm.com 20110259SAndrew.Bardsley@arm.com self.otherViews = [] 20210259SAndrew.Bardsley@arm.com 20310259SAndrew.Bardsley@arm.com def make_bar(elems): 20410259SAndrew.Bardsley@arm.com box = gtk.HBox(homogeneous=False, spacing=2) 20510259SAndrew.Bardsley@arm.com box.set_border_width(2) 20610259SAndrew.Bardsley@arm.com for widget, signal, handler in elems: 20710259SAndrew.Bardsley@arm.com if signal is not None: 20810259SAndrew.Bardsley@arm.com widget.connect(signal, handler) 20910259SAndrew.Bardsley@arm.com box.pack_start(widget, False, True, 0) 21010259SAndrew.Bardsley@arm.com return box 21110259SAndrew.Bardsley@arm.com 21210259SAndrew.Bardsley@arm.com self.timeEntry = gtk.Entry() 21310259SAndrew.Bardsley@arm.com 21410259SAndrew.Bardsley@arm.com t = gtk.ToggleButton('T') 21510259SAndrew.Bardsley@arm.com t.set_active(False) 21610259SAndrew.Bardsley@arm.com s = gtk.ToggleButton('S') 21710259SAndrew.Bardsley@arm.com s.set_active(True) 21810259SAndrew.Bardsley@arm.com p = gtk.ToggleButton('P') 21910259SAndrew.Bardsley@arm.com p.set_active(True) 22010259SAndrew.Bardsley@arm.com l = gtk.ToggleButton('L') 22110259SAndrew.Bardsley@arm.com l.set_active(True) 22210259SAndrew.Bardsley@arm.com f = gtk.ToggleButton('F') 22310259SAndrew.Bardsley@arm.com f.set_active(True) 22410259SAndrew.Bardsley@arm.com e = gtk.ToggleButton('E') 22510259SAndrew.Bardsley@arm.com e.set_active(True) 22610259SAndrew.Bardsley@arm.com 22710259SAndrew.Bardsley@arm.com # Should really generate this from above 22810259SAndrew.Bardsley@arm.com self.view.dataSelect.ids = set("SPLFE") 22910259SAndrew.Bardsley@arm.com 23010259SAndrew.Bardsley@arm.com self.bar = gtk.VBox() 23110259SAndrew.Bardsley@arm.com self.bar.set_homogeneous(False) 23210259SAndrew.Bardsley@arm.com 23310259SAndrew.Bardsley@arm.com row1 = make_bar([ 23410259SAndrew.Bardsley@arm.com (gtk.Button('Start'), 'clicked', self.time_start), 23510259SAndrew.Bardsley@arm.com (gtk.Button('End'), 'clicked', self.time_end), 23610259SAndrew.Bardsley@arm.com (gtk.Button('Back'), 'clicked', self.time_back), 23710259SAndrew.Bardsley@arm.com (gtk.Button('Forward'), 'clicked', self.time_forward), 23810259SAndrew.Bardsley@arm.com (gtk.Button('Play'), 'clicked', self.time_play), 23910259SAndrew.Bardsley@arm.com (gtk.Button('Stop'), 'clicked', self.time_stop), 24010259SAndrew.Bardsley@arm.com (self.timeEntry, 'activate', self.time_set), 24110259SAndrew.Bardsley@arm.com (gtk.Label('Visible ids:'), None, None), 24210259SAndrew.Bardsley@arm.com (t, 'clicked', self.toggle_id('T')), 24310259SAndrew.Bardsley@arm.com (gtk.Label('/'), None, None), 24410259SAndrew.Bardsley@arm.com (s, 'clicked', self.toggle_id('S')), 24510259SAndrew.Bardsley@arm.com (gtk.Label('.'), None, None), 24610259SAndrew.Bardsley@arm.com (p, 'clicked', self.toggle_id('P')), 24710259SAndrew.Bardsley@arm.com (gtk.Label('/'), None, None), 24810259SAndrew.Bardsley@arm.com (l, 'clicked', self.toggle_id('L')), 24910259SAndrew.Bardsley@arm.com (gtk.Label('/'), None, None), 25010259SAndrew.Bardsley@arm.com (f, 'clicked', self.toggle_id('F')), 25110259SAndrew.Bardsley@arm.com (gtk.Label('.'), None, None), 25210259SAndrew.Bardsley@arm.com (e, 'clicked', self.toggle_id('E')), 25310259SAndrew.Bardsley@arm.com (self.filenameEntry, 'activate', self.load_events), 25410259SAndrew.Bardsley@arm.com (gtk.Button('Reload'), 'clicked', self.load_events) 25510259SAndrew.Bardsley@arm.com ]) 25610259SAndrew.Bardsley@arm.com 25710259SAndrew.Bardsley@arm.com self.bar.pack_start(row1, False, True, 0) 25810259SAndrew.Bardsley@arm.com self.set_time_index(0) 25910259SAndrew.Bardsley@arm.com 26010259SAndrew.Bardsley@arm.com def toggle_id(self, id): 26110259SAndrew.Bardsley@arm.com """One of the sequence number selector buttons has been toggled""" 26210259SAndrew.Bardsley@arm.com def toggle(button): 26310259SAndrew.Bardsley@arm.com if button.get_active(): 26410259SAndrew.Bardsley@arm.com self.view.dataSelect.ids.add(id) 26510259SAndrew.Bardsley@arm.com else: 26610259SAndrew.Bardsley@arm.com self.view.dataSelect.ids.discard(id) 26710259SAndrew.Bardsley@arm.com 26810259SAndrew.Bardsley@arm.com # Always leave one thing visible 26910259SAndrew.Bardsley@arm.com if len(self.view.dataSelect.ids) == 0: 27010259SAndrew.Bardsley@arm.com self.view.dataSelect.ids.add(id) 27110259SAndrew.Bardsley@arm.com button.set_active(True) 27210259SAndrew.Bardsley@arm.com self.view.redraw() 27310259SAndrew.Bardsley@arm.com return toggle 27410259SAndrew.Bardsley@arm.com 27510259SAndrew.Bardsley@arm.com def set_time_index(self, time): 27610259SAndrew.Bardsley@arm.com """Set the time index in the view""" 27710259SAndrew.Bardsley@arm.com self.view.set_time_index(time) 27810259SAndrew.Bardsley@arm.com 27910259SAndrew.Bardsley@arm.com for view in self.otherViews: 28010259SAndrew.Bardsley@arm.com view.set_time_index(time) 28110259SAndrew.Bardsley@arm.com view.redraw() 28210259SAndrew.Bardsley@arm.com 28310259SAndrew.Bardsley@arm.com self.timeEntry.set_text(str(self.view.time)) 28410259SAndrew.Bardsley@arm.com 28510259SAndrew.Bardsley@arm.com def time_start(self, button): 28610259SAndrew.Bardsley@arm.com """Start pressed""" 28710259SAndrew.Bardsley@arm.com self.set_time_index(0) 28810259SAndrew.Bardsley@arm.com self.view.redraw() 28910259SAndrew.Bardsley@arm.com 29010259SAndrew.Bardsley@arm.com def time_end(self, button): 29110259SAndrew.Bardsley@arm.com """End pressed""" 29210259SAndrew.Bardsley@arm.com self.set_time_index(len(self.model.times) - 1) 29310259SAndrew.Bardsley@arm.com self.view.redraw() 29410259SAndrew.Bardsley@arm.com 29510259SAndrew.Bardsley@arm.com def time_forward(self, button): 29610259SAndrew.Bardsley@arm.com """Step forward pressed""" 29710259SAndrew.Bardsley@arm.com self.set_time_index(min(self.view.timeIndex + 1, 29810259SAndrew.Bardsley@arm.com len(self.model.times) - 1)) 29910259SAndrew.Bardsley@arm.com self.view.redraw() 30010259SAndrew.Bardsley@arm.com gtk.gdk.flush() 30110259SAndrew.Bardsley@arm.com 30210259SAndrew.Bardsley@arm.com def time_back(self, button): 30310259SAndrew.Bardsley@arm.com """Step back pressed""" 30410259SAndrew.Bardsley@arm.com self.set_time_index(max(self.view.timeIndex - 1, 0)) 30510259SAndrew.Bardsley@arm.com self.view.redraw() 30610259SAndrew.Bardsley@arm.com 30710259SAndrew.Bardsley@arm.com def time_set(self, entry): 30810259SAndrew.Bardsley@arm.com """Time dialogue changed. Need to find a suitable time 30910259SAndrew.Bardsley@arm.com <= the entry's time""" 31010259SAndrew.Bardsley@arm.com newTime = self.model.find_time_index(int(entry.get_text())) 31110259SAndrew.Bardsley@arm.com self.set_time_index(newTime) 31210259SAndrew.Bardsley@arm.com self.view.redraw() 31310259SAndrew.Bardsley@arm.com 31410259SAndrew.Bardsley@arm.com def time_step(self): 31510259SAndrew.Bardsley@arm.com """Time step while playing""" 31610259SAndrew.Bardsley@arm.com if not self.playTimer \ 31710259SAndrew.Bardsley@arm.com or self.view.timeIndex == len(self.model.times) - 1: 31810259SAndrew.Bardsley@arm.com self.time_stop(None) 31910259SAndrew.Bardsley@arm.com return False 32010259SAndrew.Bardsley@arm.com else: 32110259SAndrew.Bardsley@arm.com self.time_forward(None) 32210259SAndrew.Bardsley@arm.com return True 32310259SAndrew.Bardsley@arm.com 32410259SAndrew.Bardsley@arm.com def time_play(self, play): 32510259SAndrew.Bardsley@arm.com """Automatically advance time every 100 ms""" 32610259SAndrew.Bardsley@arm.com if not self.playTimer: 32710259SAndrew.Bardsley@arm.com self.playTimer = gobject.timeout_add(100, self.time_step) 32810259SAndrew.Bardsley@arm.com 32910259SAndrew.Bardsley@arm.com def time_stop(self, play): 33010259SAndrew.Bardsley@arm.com """Stop play pressed""" 33110259SAndrew.Bardsley@arm.com if self.playTimer: 33210259SAndrew.Bardsley@arm.com gobject.source_remove(self.playTimer) 33310259SAndrew.Bardsley@arm.com self.playTimer = None 33410259SAndrew.Bardsley@arm.com 33510259SAndrew.Bardsley@arm.com def load_events(self, button): 33610259SAndrew.Bardsley@arm.com """Reload events file""" 33710259SAndrew.Bardsley@arm.com self.model.load_events(self.filenameEntry.get_text(), 33810259SAndrew.Bardsley@arm.com startTime=self.startTime, endTime=self.endTime) 33910259SAndrew.Bardsley@arm.com self.set_time_index(min(len(self.model.times) - 1, 34010259SAndrew.Bardsley@arm.com self.view.timeIndex)) 34110259SAndrew.Bardsley@arm.com self.view.redraw() 34210259SAndrew.Bardsley@arm.com 34310259SAndrew.Bardsley@arm.comclass Overlay(object): 34410259SAndrew.Bardsley@arm.com """An Overlay is a speech bubble explaining the data in a blob""" 34510259SAndrew.Bardsley@arm.com def __init__(self, model, view, point, blob): 34610259SAndrew.Bardsley@arm.com self.model = model 34710259SAndrew.Bardsley@arm.com self.view = view 34810259SAndrew.Bardsley@arm.com self.point = point 34910259SAndrew.Bardsley@arm.com self.blob = blob 35010259SAndrew.Bardsley@arm.com 35110259SAndrew.Bardsley@arm.com def find_event(self): 35210259SAndrew.Bardsley@arm.com """Find the event for a changing time and a fixed blob""" 35310259SAndrew.Bardsley@arm.com return self.model.find_unit_event_by_time(self.blob.unit, 35410259SAndrew.Bardsley@arm.com self.view.time) 35510259SAndrew.Bardsley@arm.com 35610259SAndrew.Bardsley@arm.com def show(self, cr): 35710259SAndrew.Bardsley@arm.com """Draw the overlay""" 35810259SAndrew.Bardsley@arm.com event = self.find_event() 35910259SAndrew.Bardsley@arm.com 36010259SAndrew.Bardsley@arm.com if event is None: 36110259SAndrew.Bardsley@arm.com return 36210259SAndrew.Bardsley@arm.com 36310259SAndrew.Bardsley@arm.com insts = event.find_ided_objects(self.model, self.blob.picChar, 36410259SAndrew.Bardsley@arm.com False) 36510259SAndrew.Bardsley@arm.com 36610259SAndrew.Bardsley@arm.com cr.set_line_width(self.view.thinLineWidth) 36710259SAndrew.Bardsley@arm.com cr.translate(*(Point(0.0,0.0) - self.view.origin).to_pair()) 36810259SAndrew.Bardsley@arm.com cr.scale(*(Point(1.0,1.0) / self.view.masterScale).to_pair()) 36910259SAndrew.Bardsley@arm.com 37010259SAndrew.Bardsley@arm.com # Get formatted data from the insts to format into a table 37110259SAndrew.Bardsley@arm.com lines = list(inst.table_line() for inst in insts) 37210259SAndrew.Bardsley@arm.com 37310259SAndrew.Bardsley@arm.com text_size = 10.0 37410259SAndrew.Bardsley@arm.com cr.set_font_size(text_size) 37510259SAndrew.Bardsley@arm.com 37610259SAndrew.Bardsley@arm.com def text_width(str): 37710259SAndrew.Bardsley@arm.com xb, yb, width, height, dx, dy = cr.text_extents(str) 37810259SAndrew.Bardsley@arm.com return width 37910259SAndrew.Bardsley@arm.com 38010259SAndrew.Bardsley@arm.com # Find the maximum number of columns and the widths of each column 38110259SAndrew.Bardsley@arm.com num_columns = 0 38210259SAndrew.Bardsley@arm.com for line in lines: 38310259SAndrew.Bardsley@arm.com num_columns = max(num_columns, len(line)) 38410259SAndrew.Bardsley@arm.com 38510259SAndrew.Bardsley@arm.com widths = [0] * num_columns 38610259SAndrew.Bardsley@arm.com for line in lines: 38710259SAndrew.Bardsley@arm.com for i in xrange(0, len(line)): 38810259SAndrew.Bardsley@arm.com widths[i] = max(widths[i], text_width(line[i])) 38910259SAndrew.Bardsley@arm.com 39010259SAndrew.Bardsley@arm.com # Calculate the size of the speech bubble 39110259SAndrew.Bardsley@arm.com column_gap = 1 * text_size 39210259SAndrew.Bardsley@arm.com id_width = 6 * text_size 39310259SAndrew.Bardsley@arm.com total_width = sum(widths) + id_width + column_gap * (num_columns + 1) 39410259SAndrew.Bardsley@arm.com gap_step = Point(1.0, 0.0).scale(column_gap) 39510259SAndrew.Bardsley@arm.com 39610259SAndrew.Bardsley@arm.com text_point = self.point 39710259SAndrew.Bardsley@arm.com text_step = Point(0.0, text_size) 39810259SAndrew.Bardsley@arm.com 39910259SAndrew.Bardsley@arm.com size = Point(total_width, text_size * len(insts)) 40010259SAndrew.Bardsley@arm.com 40110259SAndrew.Bardsley@arm.com # Draw the speech bubble 40210259SAndrew.Bardsley@arm.com blobs.speech_bubble(cr, self.point, size, text_size) 40310259SAndrew.Bardsley@arm.com cr.set_source_color(colours.backgroundColour) 40410259SAndrew.Bardsley@arm.com cr.fill_preserve() 40510259SAndrew.Bardsley@arm.com cr.set_source_color(colours.black) 40610259SAndrew.Bardsley@arm.com cr.stroke() 40710259SAndrew.Bardsley@arm.com 40810259SAndrew.Bardsley@arm.com text_point += Point(1.0,1.0).scale(2.0 * text_size) 40910259SAndrew.Bardsley@arm.com 41010259SAndrew.Bardsley@arm.com id_size = Point(id_width, text_size) 41110259SAndrew.Bardsley@arm.com 41210259SAndrew.Bardsley@arm.com # Draw the rows in the table 41310259SAndrew.Bardsley@arm.com for i in xrange(0, len(insts)): 41410259SAndrew.Bardsley@arm.com row_point = text_point 41510259SAndrew.Bardsley@arm.com inst = insts[i] 41610259SAndrew.Bardsley@arm.com line = lines[i] 41710259SAndrew.Bardsley@arm.com blobs.striped_box(cr, row_point + id_size.scale(0.5), 41810259SAndrew.Bardsley@arm.com id_size, inst.id.to_striped_block(self.view.dataSelect)) 41910259SAndrew.Bardsley@arm.com cr.set_source_color(colours.black) 42010259SAndrew.Bardsley@arm.com 42110259SAndrew.Bardsley@arm.com row_point += Point(1.0, 0.0).scale(id_width) 42210259SAndrew.Bardsley@arm.com row_point += text_step 42310259SAndrew.Bardsley@arm.com # Draw the columns of each row 42410259SAndrew.Bardsley@arm.com for j in xrange(0, len(line)): 42510259SAndrew.Bardsley@arm.com row_point += gap_step 42610259SAndrew.Bardsley@arm.com cr.move_to(*row_point.to_pair()) 42710259SAndrew.Bardsley@arm.com cr.show_text(line[j]) 42810259SAndrew.Bardsley@arm.com row_point += Point(1.0, 0.0).scale(widths[j]) 42910259SAndrew.Bardsley@arm.com 43010259SAndrew.Bardsley@arm.com text_point += text_step 43110259SAndrew.Bardsley@arm.com 43210259SAndrew.Bardsley@arm.comclass BlobWindow(object): 43310259SAndrew.Bardsley@arm.com """The top-level window and its mouse control""" 43410259SAndrew.Bardsley@arm.com def __init__(self, model, view, controller): 43510259SAndrew.Bardsley@arm.com self.model = model 43610259SAndrew.Bardsley@arm.com self.view = view 43710259SAndrew.Bardsley@arm.com self.controller = controller 43810259SAndrew.Bardsley@arm.com self.controlbar = None 43910259SAndrew.Bardsley@arm.com self.window = None 44010259SAndrew.Bardsley@arm.com self.miniViewCount = 0 44110259SAndrew.Bardsley@arm.com 44210259SAndrew.Bardsley@arm.com def add_control_bar(self, controlbar): 44310259SAndrew.Bardsley@arm.com self.controlbar = controlbar 44410259SAndrew.Bardsley@arm.com 44510259SAndrew.Bardsley@arm.com def show_window(self): 44610259SAndrew.Bardsley@arm.com self.window = gtk.Window() 44710259SAndrew.Bardsley@arm.com 44810259SAndrew.Bardsley@arm.com self.vbox = gtk.VBox() 44910259SAndrew.Bardsley@arm.com self.vbox.set_homogeneous(False) 45010259SAndrew.Bardsley@arm.com if self.controlbar: 45110259SAndrew.Bardsley@arm.com self.vbox.pack_start(self.controlbar, False, True, 0) 45210259SAndrew.Bardsley@arm.com self.vbox.add(self.view.da) 45310259SAndrew.Bardsley@arm.com 45410259SAndrew.Bardsley@arm.com if self.miniViewCount > 0: 45510259SAndrew.Bardsley@arm.com self.miniViews = [] 45610259SAndrew.Bardsley@arm.com self.miniViewHBox = gtk.HBox(homogeneous=True, spacing=2) 45710259SAndrew.Bardsley@arm.com 45810259SAndrew.Bardsley@arm.com # Draw mini views 45910259SAndrew.Bardsley@arm.com for i in xrange(1, self.miniViewCount + 1): 46010259SAndrew.Bardsley@arm.com miniView = BlobView(self.model) 46110259SAndrew.Bardsley@arm.com miniView.set_time_index(0) 46210259SAndrew.Bardsley@arm.com miniView.masterScale = Point(0.1, 0.1) 46310259SAndrew.Bardsley@arm.com miniView.set_da_size() 46410259SAndrew.Bardsley@arm.com miniView.timeOffset = i + 1 46510259SAndrew.Bardsley@arm.com self.miniViews.append(miniView) 46610259SAndrew.Bardsley@arm.com self.miniViewHBox.pack_start(miniView.da, False, True, 0) 46710259SAndrew.Bardsley@arm.com 46810259SAndrew.Bardsley@arm.com self.controller.otherViews = self.miniViews 46910259SAndrew.Bardsley@arm.com self.vbox.add(self.miniViewHBox) 47010259SAndrew.Bardsley@arm.com 47110259SAndrew.Bardsley@arm.com self.window.add(self.vbox) 47210259SAndrew.Bardsley@arm.com 47310259SAndrew.Bardsley@arm.com def show_event(picChar, event): 47410259SAndrew.Bardsley@arm.com print '**** Comments for', event.unit, \ 47510259SAndrew.Bardsley@arm.com 'at time', self.view.time 47610259SAndrew.Bardsley@arm.com for name, value in event.pairs.iteritems(): 47710259SAndrew.Bardsley@arm.com print name, '=', value 47810259SAndrew.Bardsley@arm.com for comment in event.comments: 47910259SAndrew.Bardsley@arm.com print comment 48010259SAndrew.Bardsley@arm.com if picChar in event.visuals: 48110259SAndrew.Bardsley@arm.com # blocks = event.visuals[picChar].elems() 48210259SAndrew.Bardsley@arm.com print '**** Colour data' 48310259SAndrew.Bardsley@arm.com objs = event.find_ided_objects(self.model, picChar, True) 48410259SAndrew.Bardsley@arm.com for obj in objs: 48510259SAndrew.Bardsley@arm.com print ' '.join(obj.table_line()) 48610259SAndrew.Bardsley@arm.com 48710259SAndrew.Bardsley@arm.com def clicked_da(da, b): 48810259SAndrew.Bardsley@arm.com point = Point(b.x, b.y) 48910259SAndrew.Bardsley@arm.com 49010259SAndrew.Bardsley@arm.com overlay = None 49110259SAndrew.Bardsley@arm.com for blob, centre, size in self.view.positions: 49210259SAndrew.Bardsley@arm.com if point.is_within_box((centre, size)): 49310259SAndrew.Bardsley@arm.com event = self.model.find_unit_event_by_time(blob.unit, 49410259SAndrew.Bardsley@arm.com self.view.time) 49510259SAndrew.Bardsley@arm.com if event is not None: 49610259SAndrew.Bardsley@arm.com if overlay is None: 49710259SAndrew.Bardsley@arm.com overlay = Overlay(self.model, self.view, point, 49810259SAndrew.Bardsley@arm.com blob) 49910259SAndrew.Bardsley@arm.com show_event(blob.picChar, event) 50010259SAndrew.Bardsley@arm.com if overlay is not None: 50110259SAndrew.Bardsley@arm.com self.view.overlays = [overlay] 50210259SAndrew.Bardsley@arm.com else: 50310259SAndrew.Bardsley@arm.com self.view.overlays = [] 50410259SAndrew.Bardsley@arm.com 50510259SAndrew.Bardsley@arm.com self.view.redraw() 50610259SAndrew.Bardsley@arm.com 50710259SAndrew.Bardsley@arm.com # Set initial size and event callbacks 50810259SAndrew.Bardsley@arm.com self.view.set_da_size() 50910259SAndrew.Bardsley@arm.com self.view.da.add_events(gtk.gdk.BUTTON_PRESS_MASK) 51010259SAndrew.Bardsley@arm.com self.view.da.connect('button-press-event', clicked_da) 51110259SAndrew.Bardsley@arm.com self.window.connect('destroy', lambda(widget): gtk.main_quit()) 51210259SAndrew.Bardsley@arm.com 51310259SAndrew.Bardsley@arm.com def resize(window, event): 51410259SAndrew.Bardsley@arm.com """Resize DrawingArea to match new window size""" 51510259SAndrew.Bardsley@arm.com size = Point(float(event.width), float(event.height)) 51610259SAndrew.Bardsley@arm.com proportion = size / self.view.get_pic_size() 51710259SAndrew.Bardsley@arm.com # Preserve aspect ratio 51810259SAndrew.Bardsley@arm.com daScale = min(proportion.x, proportion.y) 51910259SAndrew.Bardsley@arm.com self.view.masterScale = Point(daScale, daScale) 52010259SAndrew.Bardsley@arm.com self.view.overlays = [] 52110259SAndrew.Bardsley@arm.com 52210259SAndrew.Bardsley@arm.com self.view.da.connect('configure-event', resize) 52310259SAndrew.Bardsley@arm.com 52410259SAndrew.Bardsley@arm.com self.window.show_all() 525