You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
15 KiB
362 lines
15 KiB
"""
|
|
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
|