1""" 2 File: 3 JetSegGraph.py 4 5 Contents and purpose: 6 Draws the event graph and progress bar 7 8 Copyright (c) 2008 Android Open Source Project 9 10 Licensed under the Apache License, Version 2.0 (the "License"); 11 you may not use this file except in compliance with the License. 12 You may obtain a copy of the License at 13 14 http://www.apache.org/licenses/LICENSE-2.0 15 16 Unless required by applicable law or agreed to in writing, software 17 distributed under the License is distributed on an "AS IS" BASIS, 18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 See the License for the specific language governing permissions and 20 limitations under the License. 21""" 22 23 24import wx 25import logging 26 27from JetUtils import * 28from JetDefs import * 29 30GRAPH_COLORS = [ 31 '#C0E272', 32 '#85CF89', 33 '#CF9683', 34 '#749EDE', 35 '#9FB5B1', 36 '#B095BF', 37 '#FE546D', 38 '#B3BB97', 39 '#FFFFB8', 40 41 ] 42 43PROGRESS_BAR = '#0000CC' 44EOS_BAR = '#095000' 45APP_BAR = '#B3BB97' 46 47 48class Marker(): 49 """ Defines portions of the graph for events """ 50 def __init__(self, sEventType, iEventId, sName, sStartMbt, sEndMbt, iStartMeasure, ppqn): 51 self.sEventType = sEventType 52 self.iEventId = iEventId 53 self.sName = sName 54 self.StartMbt = ConvertStrTimeToTuple(sStartMbt) 55 self.EndMbt = ConvertStrTimeToTuple(sEndMbt) 56 self.iStartMeasure = iStartMeasure 57 self.iStart = 0 58 self.iEnd = 0 59 self.iWidth = 0 60 self.iHeight = 0 61 self.iTop = 0 62 self.iUpdate = False 63 self.sColor = '#FFFFB8' 64 self.ppqn = ppqn 65 self.isDirty = False 66 67 def CalcCoord(self, step, height, ColorFct): 68 """ Calculates the coordinates in pixels for graphing the shaded regions """ 69 #measures 70 iStartM = self.StartMbt[0] - self.iStartMeasure 71 iEndM = self.EndMbt[0] - self.iStartMeasure 72 self.iStart = step * iStartM 73 self.iEnd = step * iEndM 74 #beats 75 self.iStart = self.iStart + ((step / 4.0) * (self.StartMbt[1]-1)) 76 self.iEnd = self.iEnd + ((step / 4.0) * (self.EndMbt[1]-1)) 77 #ticks 78 pctTickOfBeat = (float(self.StartMbt[2]) / float(self.ppqn)) 79 self.iStart = self.iStart + ((pctTickOfBeat * (step / 4.0))) 80 pctTickOfBeat = (float(self.EndMbt[2]) / float(self.ppqn)) 81 self.iEnd = self.iEnd + ((pctTickOfBeat * (step / 4.0))) 82 83 self.iWidth = self.iEnd - self.iStart 84 85 self.iHeight = height 86 self.sColor = ColorFct() 87 self.iUpdate = False 88 89class SegmentGraph(wx.Panel): 90 """ Draws the player graph bar """ 91 def __init__(self, parent, pos=wx.DefaultPosition, size=wx.DefaultSize, ClickCallbackFct=None, showLabels=True, showClips=True, showAppEvts=True): 92 wx.Panel.__init__(self, parent, -1, pos=pos, size=size, style=wx.BORDER_STATIC) 93 self.iLocationInMs = 0 94 self.iLengthInMs = 0 95 self.iLengthInMeasures = 0 96 self.iMarkerTop = 15 97 self.iScaleTop = 0 98 self.iEdges = 5 99 self.iStartMeasure = 0 100 self.iMidiMode = False 101 self.ClickCallbackFct = ClickCallbackFct 102 self.iColor = 0 103 self.showLabels = showLabels 104 self.showClips = showClips 105 self.showAppEvts = showAppEvts 106 107 self.font = wx.Font(8, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Courier') 108 109 self.Markers = [] 110 self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) 111 self.Bind(wx.EVT_PAINT, self.OnPaint) 112 self.Bind(wx.EVT_SIZE, self.OnSize) 113 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) 114 115 #initialize buffer 116 self.OnSize(None) 117 118 def ClearGraph(self): 119 """ Clears the graph values """ 120 self.iLocationInMs = 0 121 self.iLengthInMs = 0 122 self.iLengthInMeasures = 0 123 self.iMarkerTop = 15 124 self.iScaleTop = 0 125 self.iEdges = 5 126 self.iStartMeasure = 0 127 self.iMidiMode = False 128 self.iColor = 0 129 self.Markers = [] 130 self.iLocationInMs = 0 131 self.DoDrawing() 132 133 def LoadSegment(self, segment, segMarker=None, iMidiMode=False, showLabels=True, showClips=True, showAppEvts=True): 134 """ Loads up the segment drawing the graph """ 135 if segment is None: 136 self.ClearGraph() 137 return None 138 self.iMidiMode = iMidiMode 139 self.showLabels = showLabels 140 self.showClips = showClips 141 self.showAppEvts = showAppEvts 142 self.Markers = [] 143 self.iLocationInMs = 0 144 info = MidiSegInfo(segment) 145 #disable graph for debugging 146 #return info 147 self.iLengthInMs = info.iLengthInMs 148 self.ppqn = info.ppqn 149 self.StartMbt = mbtFct(ConvertStrTimeToTuple(segment.start), 1) 150 self.EndMbt = mbtFct(ConvertStrTimeToTuple(segment.end), 1) 151 self.LengthMbt = None 152 self.iStartMeasure = self.StartMbt[0] 153 self.iLengthInMeasures = self.EndMbt[0] - self.StartMbt[0] 154 155 for jet_event in segment.jetevents: 156 if self.showClips and jet_event.event_type == JetDefs.E_CLIP: 157 self.AddMarker(JetDefs.E_CLIP, jet_event.event_id, jet_event.event_name, mbtFct(jet_event.event_start,1), mbtFct(jet_event.event_end,1), self.iStartMeasure, self.ppqn) 158 elif jet_event.event_type == JetDefs.E_EOS: 159 self.AddMarker(JetDefs.E_EOS, jet_event.event_id, jet_event.event_name, mbtFct(jet_event.event_end,1), mbtFct(jet_event.event_end,1), self.iStartMeasure, self.ppqn) 160 elif self.showAppEvts and jet_event.event_type == JetDefs.E_APP: 161 self.AddMarker(JetDefs.E_APP, jet_event.event_id, jet_event.event_name, mbtFct(jet_event.event_start,1), mbtFct(jet_event.event_end,1), self.iStartMeasure, self.ppqn) 162 163 if segMarker is not None: 164 self.AddMarker(JetDefs.E_CLIP, 0, segMarker[0], mbtFct(segMarker[1],1), mbtFct(segMarker[2],1), self.iStartMeasure, self.ppqn) 165 166 self.DoDrawing() 167 return info 168 169 def AddMarker(self, sEventType, iEventId, sName, sStartMbt, sEndMbt, iStartMeasure, ppqn): 170 """ Adds a marker to the list """ 171 if not CompareMbt(sStartMbt, sEndMbt): 172 sEndMbt = sStartMbt 173 self.Markers.append(Marker(sEventType, iEventId, sName, sStartMbt, sEndMbt, iStartMeasure, ppqn)) 174 175 def OnLeftDown(self, event): 176 """ Calls the function assicated with an event """ 177 pt = event.GetPosition() 178 for Marker in self.Markers: 179 if pt[0] >= Marker.iStart and pt[0] <= Marker.iEnd and pt[1] >= Marker.iTop and pt[1] <= Marker.iTop + Marker.iHeight: 180 if self.ClickCallbackFct != None: 181 self.ClickCallbackFct(Marker.sName, Marker.iEventId) 182 183 def GetAColor(self): 184 """ Gets a color """ 185 color = GRAPH_COLORS[self.iColor] 186 self.iColor = self.iColor + 1 187 if self.iColor >= len(GRAPH_COLORS): 188 self.iColor = 0 189 return color 190 191 def OnSize(self, event=None): 192 """ Repaints for resizing of screen """ 193 if OsWindows(): 194 # The Buffer init is done here, to make sure the buffer is always 195 # the same size as the Window 196 Size = self.GetClientSizeTuple() 197 198 # Make new offscreen bitmap: this bitmap will always have the 199 # current drawing in it, so it can be used to save the image to 200 # a file, or whatever. 201 self._Buffer = wx.EmptyBitmap(*Size) 202 self.DoDrawing(None) 203 if event is not None: 204 event.Skip() 205 206 def OnPaint(self, event=None): 207 """ Painting of windows """ 208 if OsWindows(): 209 dc = wx.BufferedPaintDC(self, self._Buffer) 210 else: 211 dc = wx.AutoBufferedPaintDC(self) 212 dc.Background = wx.Brush(wx.WHITE) 213 self.DoDrawing(dc) 214 215 def DoDrawing(self, dc=None): 216 """ Does the actual drawing of the control """ 217 if dc is None: 218 if OsWindows(): 219 dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer) 220 else: 221 dc = wx.AutoBufferedPaintDC(self) 222 dc.Background = wx.Brush(wx.WHITE) 223 224 dc.Clear() 225 226 self.iColor = 0 227 gWidth, gHeight = self.GetSize() 228 gWidth = gWidth - (self.iEdges * 2) 229 step = int(gWidth / (self.iLengthInMeasures + .01)) 230 231 for Marker in self.Markers: 232 Marker.CalcCoord(step, gHeight, self.GetAColor) 233 234 """ eliminate overlaps; establish colors """ 235 iClips = 0 236 iMarkers = 0 237 for index, Marker in enumerate(self.Markers): 238 if Marker.sEventType == JetDefs.E_CLIP: 239 iClips = iClips + 1 240 iOverlaps = 1 241 for index1, Marker1 in enumerate(self.Markers): 242 if Marker.sEventType == JetDefs.E_CLIP: 243 if index != index1 and not Marker1.iUpdate: 244 if Marker.iStart <= Marker1.iStart and Marker.iEnd <= Marker1.iEnd and Marker.iEnd >= Marker1.iStart: 245 iOverlaps = iOverlaps + 1 246 Marker.iUpdate = True 247 Marker1.iUpdate = True 248 if not Marker.iUpdate and Marker.iStart >= Marker1.iStart and Marker.iEnd >= Marker1.iEnd and Marker.iStart <= Marker1.iEnd: 249 iOverlaps = iOverlaps + 1 250 Marker.iUpdate = True 251 Marker1.iUpdate = True 252 if iOverlaps > 1: 253 iTop = 0 254 for index1, Marker1 in enumerate(self.Markers): 255 if Marker.sEventType == JetDefs.E_CLIP: 256 if Marker1.iUpdate: 257 Marker1.iHeight = gHeight / iOverlaps 258 Marker1.iTop = iTop * Marker1.iHeight 259 iTop = iTop + 1 260 elif Marker.sEventType == JetDefs.E_APP: 261 iMarkers = iMarkers + 1 262 263 for Marker in self.Markers: 264 if Marker.sEventType == JetDefs.E_CLIP: 265 dc.SetPen(wx.Pen(Marker.sColor)) 266 dc.SetBrush(wx.Brush(Marker.sColor)) 267 dc.DrawRectangle(Marker.iStart + self.iEdges, Marker.iTop, Marker.iWidth, Marker.iHeight) 268 width, height = dc.GetTextExtent(Marker.sName) 269 k = ((Marker.iStart + Marker.iEnd) / 2) - (width/2) + self.iEdges 270 if self.showLabels or self.iMidiMode: 271 dc.DrawText(Marker.sName, k, ((Marker.iTop+Marker.iHeight/2) - (height*.5))) 272 if self.iMidiMode: 273 self.iMidiModeStart = Marker.iStart 274 elif Marker.sEventType == JetDefs.E_EOS: 275 dc.SetPen(wx.Pen(EOS_BAR)) 276 dc.SetBrush(wx.Brush(EOS_BAR)) 277 dc.DrawRectangle(Marker.iStart + self.iEdges, Marker.iTop, 1, Marker.iHeight) 278 width, height = dc.GetTextExtent(Marker.sName) 279 k = Marker.iStart - (width/2) + self.iEdges 280 dc.DrawText(Marker.sName, k, ((Marker.iTop+Marker.iHeight/2) - (height*.5))) 281 elif Marker.sEventType == JetDefs.E_APP: 282 dc.SetPen(wx.Pen(APP_BAR)) 283 dc.SetBrush(wx.Brush(APP_BAR)) 284 dc.DrawRectangle(Marker.iStart + self.iEdges, Marker.iTop, 1, Marker.iHeight) 285 width, height = dc.GetTextExtent(Marker.sName) 286 k = Marker.iStart - (width/2) + self.iEdges 287 if self.showLabels or self.iMidiMode: 288 dc.DrawText(Marker.sName, k, ((Marker.iTop+Marker.iHeight/2) - (height*.5))) 289 290 291 """ Draw scale """ 292 if gWidth == 0: 293 iDiv = 50 294 else: 295 iDiv = (gWidth)/18 296 if iDiv == 0: 297 iDiv = 50 298 scale = ((self.iLengthInMeasures / iDiv) + 1) 299 if scale == 0: 300 scale = 1 301 beatStep = step / 4.0 302 dc.SetFont(self.font) 303 j = 0 304 lastEnd = 0 305 num = range(self.iStartMeasure, self.iStartMeasure + self.iLengthInMeasures + 1, 1) 306 dc.SetPen(wx.Pen('#5C5142')) 307 for i in range(0, (self.iLengthInMeasures+1)*step, step): 308 k = i + self.iEdges 309 dc.DrawLine(k, self.iScaleTop, k, self.iScaleTop+8) 310 if i != (self.iLengthInMeasures)*step: 311 for iBeat in range(1,4): 312 k = i+(iBeat * beatStep) + self.iEdges 313 dc.DrawLine(k, self.iScaleTop, k, self.iScaleTop+4) 314 width, height = dc.GetTextExtent(str(num[j])) 315 k = i-(width/2) + self.iEdges 316 if k > lastEnd: 317 if j == 0 or (j % scale) == 0: 318 dc.DrawText(str(num[j]), k, self.iScaleTop+8) 319 lastEnd = k + width 320 j = j + 1 321 322 """ Updates the location bar in case screen moved or resized """ 323 if self.iLocationInMs > 0 and self.iLengthInMs > 0: 324 iOffset = 0 325 if self.iMidiMode: 326 iOffset = self.iMidiModeStart 327 328 till = gWidth * (self.iLocationInMs / self.iLengthInMs) 329 dc.SetPen(wx.Pen(PROGRESS_BAR)) 330 dc.SetBrush(wx.Brush(PROGRESS_BAR)) 331 dc.DrawRectangle(self.iEdges + iOffset, gHeight-6, till, 3) 332 333 def UpdateLocation(self, iLocationInMs): 334 """ Updates the location bar """ 335 #disable graph for debugging 336 #return info 337 338 self.iLocationInMs = iLocationInMs 339 if self.iLocationInMs > 0 and self.iLengthInMs > 0: 340 if OsWindows(): 341 dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer) 342 else: 343 dc = wx.AutoBufferedPaintDC(self) 344 dc.Background = wx.Brush(wx.WHITE) 345 346 iOffset = 0 347 if self.iMidiMode: 348 iOffset = self.iMidiModeStart 349 350 gWidth, gHeight = self.GetSize() 351 gWidth = gWidth - (self.iEdges * 2) 352 353 till = gWidth * (self.iLocationInMs / self.iLengthInMs) 354 dc.SetPen(wx.Pen(PROGRESS_BAR)) 355 dc.SetBrush(wx.Brush(PROGRESS_BAR)) 356 dc.DrawRectangle(self.iEdges + iOffset, gHeight-6, till, 3) 357 self.isDirty = True 358 else: 359 if self.isDirty: 360 self.DoDrawing() 361 self.isDirty = False 362