""" | |
File: | |
JetSegGraph.py | |
Contents and purpose: | |
Draws the event graph and progress bar | |
Copyright (c) 2008 Android Open Source Project | |
Licensed under the Apache License, Version 2.0 (the "License"); | |
you may not use this file except in compliance with the License. | |
You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
""" | |
import wx | |
import logging | |
from JetUtils import * | |
from JetDefs import * | |
GRAPH_COLORS = [ | |
'#C0E272', | |
'#85CF89', | |
'#CF9683', | |
'#749EDE', | |
'#9FB5B1', | |
'#B095BF', | |
'#FE546D', | |
'#B3BB97', | |
'#FFFFB8', | |
] | |
PROGRESS_BAR = '#0000CC' | |
EOS_BAR = '#095000' | |
APP_BAR = '#B3BB97' | |
class Marker(): | |
""" Defines portions of the graph for events """ | |
def __init__(self, sEventType, iEventId, sName, sStartMbt, sEndMbt, iStartMeasure, ppqn): | |
self.sEventType = sEventType | |
self.iEventId = iEventId | |
self.sName = sName | |
self.StartMbt = ConvertStrTimeToTuple(sStartMbt) | |
self.EndMbt = ConvertStrTimeToTuple(sEndMbt) | |
self.iStartMeasure = iStartMeasure | |
self.iStart = 0 | |
self.iEnd = 0 | |
self.iWidth = 0 | |
self.iHeight = 0 | |
self.iTop = 0 | |
self.iUpdate = False | |
self.sColor = '#FFFFB8' | |
self.ppqn = ppqn | |
self.isDirty = False | |
def CalcCoord(self, step, height, ColorFct): | |
""" Calculates the coordinates in pixels for graphing the shaded regions """ | |
#measures | |
iStartM = self.StartMbt[0] - self.iStartMeasure | |
iEndM = self.EndMbt[0] - self.iStartMeasure | |
self.iStart = step * iStartM | |
self.iEnd = step * iEndM | |
#beats | |
self.iStart = self.iStart + ((step / 4.0) * (self.StartMbt[1]-1)) | |
self.iEnd = self.iEnd + ((step / 4.0) * (self.EndMbt[1]-1)) | |
#ticks | |
pctTickOfBeat = (float(self.StartMbt[2]) / float(self.ppqn)) | |
self.iStart = self.iStart + ((pctTickOfBeat * (step / 4.0))) | |
pctTickOfBeat = (float(self.EndMbt[2]) / float(self.ppqn)) | |
self.iEnd = self.iEnd + ((pctTickOfBeat * (step / 4.0))) | |
self.iWidth = self.iEnd - self.iStart | |
self.iHeight = height | |
self.sColor = ColorFct() | |
self.iUpdate = False | |
class SegmentGraph(wx.Panel): | |
""" Draws the player graph bar """ | |
def __init__(self, parent, pos=wx.DefaultPosition, size=wx.DefaultSize, ClickCallbackFct=None, showLabels=True, showClips=True, showAppEvts=True): | |
wx.Panel.__init__(self, parent, -1, pos=pos, size=size, style=wx.BORDER_STATIC) | |
self.iLocationInMs = 0 | |
self.iLengthInMs = 0 | |
self.iLengthInMeasures = 0 | |
self.iMarkerTop = 15 | |
self.iScaleTop = 0 | |
self.iEdges = 5 | |
self.iStartMeasure = 0 | |
self.iMidiMode = False | |
self.ClickCallbackFct = ClickCallbackFct | |
self.iColor = 0 | |
self.showLabels = showLabels | |
self.showClips = showClips | |
self.showAppEvts = showAppEvts | |
self.font = wx.Font(8, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Courier') | |
self.Markers = [] | |
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) | |
self.Bind(wx.EVT_PAINT, self.OnPaint) | |
self.Bind(wx.EVT_SIZE, self.OnSize) | |
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) | |
#initialize buffer | |
self.OnSize(None) | |
def ClearGraph(self): | |
""" Clears the graph values """ | |
self.iLocationInMs = 0 | |
self.iLengthInMs = 0 | |
self.iLengthInMeasures = 0 | |
self.iMarkerTop = 15 | |
self.iScaleTop = 0 | |
self.iEdges = 5 | |
self.iStartMeasure = 0 | |
self.iMidiMode = False | |
self.iColor = 0 | |
self.Markers = [] | |
self.iLocationInMs = 0 | |
self.DoDrawing() | |
def LoadSegment(self, segment, segMarker=None, iMidiMode=False, showLabels=True, showClips=True, showAppEvts=True): | |
""" Loads up the segment drawing the graph """ | |
if segment is None: | |
self.ClearGraph() | |
return None | |
self.iMidiMode = iMidiMode | |
self.showLabels = showLabels | |
self.showClips = showClips | |
self.showAppEvts = showAppEvts | |
self.Markers = [] | |
self.iLocationInMs = 0 | |
info = MidiSegInfo(segment) | |
#disable graph for debugging | |
#return info | |
self.iLengthInMs = info.iLengthInMs | |
self.ppqn = info.ppqn | |
self.StartMbt = mbtFct(ConvertStrTimeToTuple(segment.start), 1) | |
self.EndMbt = mbtFct(ConvertStrTimeToTuple(segment.end), 1) | |
self.LengthMbt = None | |
self.iStartMeasure = self.StartMbt[0] | |
self.iLengthInMeasures = self.EndMbt[0] - self.StartMbt[0] | |
for jet_event in segment.jetevents: | |
if self.showClips and jet_event.event_type == JetDefs.E_CLIP: | |
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) | |
elif jet_event.event_type == JetDefs.E_EOS: | |
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) | |
elif self.showAppEvts and jet_event.event_type == JetDefs.E_APP: | |
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) | |
if segMarker is not None: | |
self.AddMarker(JetDefs.E_CLIP, 0, segMarker[0], mbtFct(segMarker[1],1), mbtFct(segMarker[2],1), self.iStartMeasure, self.ppqn) | |
self.DoDrawing() | |
return info | |
def AddMarker(self, sEventType, iEventId, sName, sStartMbt, sEndMbt, iStartMeasure, ppqn): | |
""" Adds a marker to the list """ | |
if not CompareMbt(sStartMbt, sEndMbt): | |
sEndMbt = sStartMbt | |
self.Markers.append(Marker(sEventType, iEventId, sName, sStartMbt, sEndMbt, iStartMeasure, ppqn)) | |
def OnLeftDown(self, event): | |
""" Calls the function assicated with an event """ | |
pt = event.GetPosition() | |
for Marker in self.Markers: | |
if pt[0] >= Marker.iStart and pt[0] <= Marker.iEnd and pt[1] >= Marker.iTop and pt[1] <= Marker.iTop + Marker.iHeight: | |
if self.ClickCallbackFct != None: | |
self.ClickCallbackFct(Marker.sName, Marker.iEventId) | |
def GetAColor(self): | |
""" Gets a color """ | |
color = GRAPH_COLORS[self.iColor] | |
self.iColor = self.iColor + 1 | |
if self.iColor >= len(GRAPH_COLORS): | |
self.iColor = 0 | |
return color | |
def OnSize(self, event=None): | |
""" Repaints for resizing of screen """ | |
if OsWindows(): | |
# The Buffer init is done here, to make sure the buffer is always | |
# the same size as the Window | |
Size = self.GetClientSizeTuple() | |
# Make new offscreen bitmap: this bitmap will always have the | |
# current drawing in it, so it can be used to save the image to | |
# a file, or whatever. | |
self._Buffer = wx.EmptyBitmap(*Size) | |
self.DoDrawing(None) | |
if event is not None: | |
event.Skip() | |
def OnPaint(self, event=None): | |
""" Painting of windows """ | |
if OsWindows(): | |
dc = wx.BufferedPaintDC(self, self._Buffer) | |
else: | |
dc = wx.AutoBufferedPaintDC(self) | |
dc.Background = wx.Brush(wx.WHITE) | |
self.DoDrawing(dc) | |
def DoDrawing(self, dc=None): | |
""" Does the actual drawing of the control """ | |
if dc is None: | |
if OsWindows(): | |
dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer) | |
else: | |
dc = wx.AutoBufferedPaintDC(self) | |
dc.Background = wx.Brush(wx.WHITE) | |
dc.Clear() | |
self.iColor = 0 | |
gWidth, gHeight = self.GetSize() | |
gWidth = gWidth - (self.iEdges * 2) | |
step = int(gWidth / (self.iLengthInMeasures + .01)) | |
for Marker in self.Markers: | |
Marker.CalcCoord(step, gHeight, self.GetAColor) | |
""" eliminate overlaps; establish colors """ | |
iClips = 0 | |
iMarkers = 0 | |
for index, Marker in enumerate(self.Markers): | |
if Marker.sEventType == JetDefs.E_CLIP: | |
iClips = iClips + 1 | |
iOverlaps = 1 | |
for index1, Marker1 in enumerate(self.Markers): | |
if Marker.sEventType == JetDefs.E_CLIP: | |
if index != index1 and not Marker1.iUpdate: | |
if Marker.iStart <= Marker1.iStart and Marker.iEnd <= Marker1.iEnd and Marker.iEnd >= Marker1.iStart: | |
iOverlaps = iOverlaps + 1 | |
Marker.iUpdate = True | |
Marker1.iUpdate = True | |
if not Marker.iUpdate and Marker.iStart >= Marker1.iStart and Marker.iEnd >= Marker1.iEnd and Marker.iStart <= Marker1.iEnd: | |
iOverlaps = iOverlaps + 1 | |
Marker.iUpdate = True | |
Marker1.iUpdate = True | |
if iOverlaps > 1: | |
iTop = 0 | |
for index1, Marker1 in enumerate(self.Markers): | |
if Marker.sEventType == JetDefs.E_CLIP: | |
if Marker1.iUpdate: | |
Marker1.iHeight = gHeight / iOverlaps | |
Marker1.iTop = iTop * Marker1.iHeight | |
iTop = iTop + 1 | |
elif Marker.sEventType == JetDefs.E_APP: | |
iMarkers = iMarkers + 1 | |
for Marker in self.Markers: | |
if Marker.sEventType == JetDefs.E_CLIP: | |
dc.SetPen(wx.Pen(Marker.sColor)) | |
dc.SetBrush(wx.Brush(Marker.sColor)) | |
dc.DrawRectangle(Marker.iStart + self.iEdges, Marker.iTop, Marker.iWidth, Marker.iHeight) | |
width, height = dc.GetTextExtent(Marker.sName) | |
k = ((Marker.iStart + Marker.iEnd) / 2) - (width/2) + self.iEdges | |
if self.showLabels or self.iMidiMode: | |
dc.DrawText(Marker.sName, k, ((Marker.iTop+Marker.iHeight/2) - (height*.5))) | |
if self.iMidiMode: | |
self.iMidiModeStart = Marker.iStart | |
elif Marker.sEventType == JetDefs.E_EOS: | |
dc.SetPen(wx.Pen(EOS_BAR)) | |
dc.SetBrush(wx.Brush(EOS_BAR)) | |
dc.DrawRectangle(Marker.iStart + self.iEdges, Marker.iTop, 1, Marker.iHeight) | |
width, height = dc.GetTextExtent(Marker.sName) | |
k = Marker.iStart - (width/2) + self.iEdges | |
dc.DrawText(Marker.sName, k, ((Marker.iTop+Marker.iHeight/2) - (height*.5))) | |
elif Marker.sEventType == JetDefs.E_APP: | |
dc.SetPen(wx.Pen(APP_BAR)) | |
dc.SetBrush(wx.Brush(APP_BAR)) | |
dc.DrawRectangle(Marker.iStart + self.iEdges, Marker.iTop, 1, Marker.iHeight) | |
width, height = dc.GetTextExtent(Marker.sName) | |
k = Marker.iStart - (width/2) + self.iEdges | |
if self.showLabels or self.iMidiMode: | |
dc.DrawText(Marker.sName, k, ((Marker.iTop+Marker.iHeight/2) - (height*.5))) | |
""" Draw scale """ | |
if gWidth == 0: | |
iDiv = 50 | |
else: | |
iDiv = (gWidth)/18 | |
if iDiv == 0: | |
iDiv = 50 | |
scale = ((self.iLengthInMeasures / iDiv) + 1) | |
if scale == 0: | |
scale = 1 | |
beatStep = step / 4.0 | |
dc.SetFont(self.font) | |
j = 0 | |
lastEnd = 0 | |
num = range(self.iStartMeasure, self.iStartMeasure + self.iLengthInMeasures + 1, 1) | |
dc.SetPen(wx.Pen('#5C5142')) | |
for i in range(0, (self.iLengthInMeasures+1)*step, step): | |
k = i + self.iEdges | |
dc.DrawLine(k, self.iScaleTop, k, self.iScaleTop+8) | |
if i != (self.iLengthInMeasures)*step: | |
for iBeat in range(1,4): | |
k = i+(iBeat * beatStep) + self.iEdges | |
dc.DrawLine(k, self.iScaleTop, k, self.iScaleTop+4) | |
width, height = dc.GetTextExtent(str(num[j])) | |
k = i-(width/2) + self.iEdges | |
if k > lastEnd: | |
if j == 0 or (j % scale) == 0: | |
dc.DrawText(str(num[j]), k, self.iScaleTop+8) | |
lastEnd = k + width | |
j = j + 1 | |
""" Updates the location bar in case screen moved or resized """ | |
if self.iLocationInMs > 0 and self.iLengthInMs > 0: | |
iOffset = 0 | |
if self.iMidiMode: | |
iOffset = self.iMidiModeStart | |
till = gWidth * (self.iLocationInMs / self.iLengthInMs) | |
dc.SetPen(wx.Pen(PROGRESS_BAR)) | |
dc.SetBrush(wx.Brush(PROGRESS_BAR)) | |
dc.DrawRectangle(self.iEdges + iOffset, gHeight-6, till, 3) | |
def UpdateLocation(self, iLocationInMs): | |
""" Updates the location bar """ | |
#disable graph for debugging | |
#return info | |
self.iLocationInMs = iLocationInMs | |
if self.iLocationInMs > 0 and self.iLengthInMs > 0: | |
if OsWindows(): | |
dc = wx.BufferedDC(wx.ClientDC(self), self._Buffer) | |
else: | |
dc = wx.AutoBufferedPaintDC(self) | |
dc.Background = wx.Brush(wx.WHITE) | |
iOffset = 0 | |
if self.iMidiMode: | |
iOffset = self.iMidiModeStart | |
gWidth, gHeight = self.GetSize() | |
gWidth = gWidth - (self.iEdges * 2) | |
till = gWidth * (self.iLocationInMs / self.iLengthInMs) | |
dc.SetPen(wx.Pen(PROGRESS_BAR)) | |
dc.SetBrush(wx.Brush(PROGRESS_BAR)) | |
dc.DrawRectangle(self.iEdges + iOffset, gHeight-6, till, 3) | |
self.isDirty = True | |
else: | |
if self.isDirty: | |
self.DoDrawing() | |
self.isDirty = False |