Re: [jokosher-devel] Six weeks till the freeze



On 9/19/06, John Green <john thegreens co uk> wrote:
On Tue, Sep 19, 2006 at 11:13:36AM +0200, Jens Geiregat wrote:
>
> I tried to do that for the timeline some weeks ago. In stead of
> drawing a savedLine the size of the visible area, I prepared a
> savedLine three times the size of the visible area and used that as
> long as possible. I never got it to work 100% right, but if you want
> it, I'll send you the code.

Excellent :) That would be great. You can mail it to me at this address.

--
John Green

Hi,

I attached my modified TimeLine.py. I don't remember what the problems
with it were, but I hope its usefull for you.


Jens
import gtk
import pango
import gobject
import gst
import time

#=========================================================================

class TimeLine(gtk.DrawingArea):

	""" This class handles drawing the time line display.
	"""

	__gtype_name__ = 'TimeLine'
	
	_NUM_LINES = 5 # Number of 'short' lines + 1

	#_____________________________________________________________________

	def __init__(self, project, timelinebar, mainview):
		""" project - reference to the active project
		"""
		
		gtk.DrawingArea.__init__(self)
	
		self.project = project
		self.timelinebar = timelinebar
		self.mainview = mainview
		self.project.transport.AddListener(self)
		self.project.AddListener(self)
		self.height = 44
		self.buttonDown = False
		self.dragging = False
	
		self.set_events(gtk.gdk.POINTER_MOTION_MASK |
								gtk.gdk.BUTTON_PRESS_MASK |
								gtk.gdk.BUTTON_RELEASE_MASK)
		self.connect("expose-event", self.OnDraw)
		self.connect("button_release_event", self.onMouseUp)
		self.connect("button_press_event", self.onMouseDown)
		self.connect("motion_notify_event", self.onMouseMove)
		self.connect("size_allocate", self.OnAllocate)
		self.savedLine = None
		self.savedLinePiece = None
		
		self.scrollTime = 0
		self.pixelsBefore = 0
		self.startTime = 0
	#_____________________________________________________________________

	def OnAllocate(self, widget, allocation):
		self.allocation = allocation
		#Redraw timeline
		self.DrawLine(widget)
		
	#_____________________________________________________________________
		
	def OnDraw(self, widget, event):
		""" Fires off the drawing operation. """
		viewStart = self.project.viewStart
		viewScale = self.project.viewScale
		width = self.get_allocation().width
		position = self.project.transport.position
		x = int(round((position - viewStart) * viewScale))
		
		newtime = time.time()

		if x >= (width * 0.8):
			if newtime > (self.scrollTime + 1/30.):
				self.scrollTime = newtime
				self.project.SetViewStart(position - ((width * 0.8)/viewScale))
				#return
		
		if self.savedLine == None:
			self.DrawLine(widget)
#		if self.project.transport.RedrawTimeLine:
#			self.project.transport.RedrawTimeLine = False
#			self.DrawLine(widget)
#		if self.project.RedrawTimeLine:
#			self.project.RedrawTimeLine = False
#			self.DrawLine(widget)
		d = widget.window

		gc = d.new_gc()
		
		# redraw area from saved image
		ix = ((viewStart-self.startTime) * viewScale) + event.area.x# + self.pixelsBefore
		if ix > width:
			print "RELOAD! ix = %d, width = %d"%(ix, width)
			self.DrawLine(widget)#, viewStart=self.project.transport.position)
			ix = (viewStart * viewScale) + event.area.x + self.pixelsBefore
		d.draw_image(gc, self.savedLine, ix,
						event.area.y, event.area.x, event.area.y,
						event.area.width, event.area.height)

		# Draw play cursor position
		col = gc.get_colormap().alloc_color("#FF0000")
		gc.set_foreground(col)
		 
		
		d.draw_line(gc, x, 0, x, self.get_allocation().height)	
	
	#_____________________________________________________________________
		
	def DrawLine(self, widget, viewStart=None):
		""" Draws the timeline and saves it to memory
		    - Must be called initially and to redraw the timeline
				  after moving the project start
		"""
		d = widget.window
		# 0.75 + 1 + 0.75 = 2.5
		d = gtk.gdk.Pixmap(d, self.get_allocation().width * 2.5, self.get_allocation().height)
		gc = d.new_gc()
		
		y = 0
		
		col = gc.get_colormap().alloc_color("#FFFFFF")
		gc.set_foreground(col)
		gc.set_fill(gtk.gdk.SOLID)
		
		d.draw_rectangle(	gc, True, 
							0, 
							0, 
							*(d.get_size()))
							
		col = gc.get_colormap().alloc_color("#555555")
		gc.set_foreground(col)
							
		d.draw_rectangle(	gc, False, 
							0, 
							0, 
							*(d.get_size()))
		
		transport = self.project.transport

		x = 0
		if transport.mode == transport.MODE_BARS_BEATS:
		
			# Calculate our scroll offset
			pos = (self.project.viewStart / 60.) * transport.bpm
			beat = int(pos)
			offset = pos - beat
			
			if offset > 0.:
				x -= offset * ((self.project.viewScale * 60.) / transport.bpm)
				x += (self.project.viewScale * 60.) / transport.bpm
				beat += 1
		
			while x < self.get_allocation().width:
				# Draw the beat/bar divisions
				ix = int(x)
				if beat % transport.meter_nom:
					d.draw_line(gc, ix, int(self.get_allocation().height/1.2), ix, self.get_allocation().height)
				else:
					d.draw_line(gc, ix, int(self.get_allocation().height/2), ix, self.get_allocation().height)
					
					# Draw the bar number
					l = pango.Layout(self.create_pango_context())
					l.set_text(str((beat / transport.meter_nom)+1))
					d.draw_layout(gc, ix, 5, l)
					
				beat += 1
				
				x += (60. / transport.bpm ) * self.project.viewScale
		else:
			# Working in milliseconds here. Using seconds gives modulus problems because they're floats
			viewScale = self.project.viewScale / 1000.
#			if not viewStart:
#				viewStart = int(self.project.viewStart * 1000)
#			else:
#				print "got: %f, real: %f"%(viewStart, self.project.viewStart)
#				viewStart = int(viewStart * 1000)
			viewStart = int(self.project.transport.position * 1000)
			factor, displayMilliseconds = self.GetZoomFactor(viewScale)
			
			pixelsBefore = self.get_allocation().width * 0.75
			timeBefore = pixelsBefore / viewScale
			viewStart = int(viewStart - timeBefore)
			firstBlock = viewStart % (self._NUM_LINES * factor)
			if firstBlock > 0:
				firstBlock -= self._NUM_LINES * factor # Time where the block with the viewStart starts.
			
			x = firstBlock * viewScale
			self.pixelsBefore = pixelsBefore + x
			msec = viewStart - firstBlock
			self.startTime = msec / 1000.
			
			# Draw ticks up to the end of our display
			if not self.savedLinePiece:
				self.DrawLinePiece(widget, viewScale, factor)
			l = pango.Layout(self.create_pango_context())
			x=0
			while x < self.get_allocation().width * 10:
				ix = int(x)
				width = int(self._NUM_LINES * factor * viewScale)
				d.draw_image(gc, self.savedLinePiece, 0,
								0, ix, 0,
								width, self.get_allocation().height)
				# Draw the bar number
				if msec >=0:
					if displayMilliseconds:
						#Should use transportmanager for this...
						l.set_text("%d:%02d:%03d"%((msec/1000) / 60, (msec/1000) % 60, msec%1000) )
					else:
						l.set_text("%d:%02d"%((msec/1000) / 60, (msec/1000) % 60))
					d.draw_layout(gc, ix, 5, l)
				msec += self._NUM_LINES * factor
				x += self._NUM_LINES * viewScale * factor
		self.savedLine = d.get_image(0, 0, *(d.get_size()))
	
	#_____________________________________________________________________
	
	def DrawLinePiece(self, widget, viewScale, factor):
		"""
			Draws a piece of the timeline:
			>                        |<
			>    |    |    |    |    |<
			This piece can then be painted repeatedly to self.savedLine.
		"""
		window = widget.window
		window = gtk.gdk.Pixmap(window, self._NUM_LINES * factor * viewScale, self.get_allocation().height)
		gc = window.new_gc()
		col = gc.get_colormap().alloc_color("#FFFFFF")
		gc.set_foreground(col)
		window.draw_rectangle(	gc, True, 
							0, 
							0, 
							*(window.get_size()))
		col = gc.get_colormap().alloc_color("#555555")
		gc.set_foreground(col)
		x = 0
		window.draw_line(gc, x, int(self.get_allocation().height/2.), x, self.get_allocation().height)
		for i in range(self._NUM_LINES - 1):
			x += viewScale * factor
			ix = int(x)
			window.draw_line(gc, ix, int(self.get_allocation().height/1.2), ix, self.get_allocation().height)
		self.savedLinePiece = window.get_image(0, 0, *(window.get_size()))
	
	#_____________________________________________________________________
	
	def do_size_request(self, requisition):
		requisition.width = self.get_allocation().width
		requisition.height = self.height
		
	#_____________________________________________________________________
	
	def OnStateChanged(self, obj, change=None):
		""" 
		Called when there is a change fo state in transport
		manager.Could be one of
		 *  Mode changed from bars/beats to minutes or vice versa
		    (requires a complete redraw of timeline - flag set)
		 *  Change in playing position -only needs partial redraw
		 *  Project change e.g. a scroll or zoom change
		    (requires a complete redraw of timeline - flag set)
		"""
		if self.project.transport.RedrawTimeLine or self.project.RedrawTimeLine:
			self.queue_draw()
			return
		x1 = round((self.project.transport.PrevPosition - self.project.viewStart) * self.project.viewScale)
		x2 = round((self.project.transport.position - self.project.viewStart) * self.project.viewScale)
		
		self.queue_draw_area(int(x1)-1, 0, 3, self.get_allocation().height)
		self.queue_draw_area(int(x2)-1, 0, 3, self.get_allocation().height)
		
	#_____________________________________________________________________
		
	def onMouseDown(self, widget, event):
		self.buttonDown = True
		self.dragging = False
		self.moveHead(event.x)
		return True

	#_____________________________________________________________________

	def onMouseMove(self, widget, event):
		if not self.buttonDown:
			return
		self.dragging = True
		
		self.moveHead(event.x)
		
	#_____________________________________________________________________
		
	def onMouseUp(self, widget, event):
		self.dragging = False
		self.buttonDown = False
		
	#_____________________________________________________________________
		
	def moveHead(self, xpos):
		pos = self.project.viewStart + xpos/ self.project.viewScale
		self.project.transport.SeekTo(pos)
		
	#_____________________________________________________________________
	
	def GetZoomFactor(self, viewScale):
		"""
			To be used for drawing the MODE_HOURS_MINS_SECS timeline
			
			Returns:
				- an integer factor to be multiplied with the viewScale to zoom the timeline in/out
				- a boolean indicating if milliseconds should be displayed
			The default factor is 1000, meaning that the distance between the short lines of the timeline
			symbolizes 1000 milliseconds. The code will increase of decrease this factor to keep the
			timeline readable. The factors can be set with the zoomLevels array. This array
			contains zoom levels that support precision from 20 ms to 1 minute. More extreme zoom
			levels could be added, but would never be reached because the viewScale is limited.
		"""
		shortTextWidth = 28 # for '0:00' notation
		longTextWidth = 56 # for '0:00:000' notation
		textWidth = shortTextWidth
		whiteSpace = 50
		factor = 1000 # Default factor is 1 second for 1 line
		zoomLevels = [20, 100, 200, 1000, 4000, 12000, 60000]
		if (textWidth + whiteSpace) > (self._NUM_LINES * factor * viewScale):
			factor = zoomLevels[zoomLevels.index(factor) + 1]
			while (textWidth + whiteSpace) > (self._NUM_LINES * factor * viewScale) and factor != zoomLevels[-1]:
				factor = zoomLevels[zoomLevels.index(factor) + 1]
		else:
			while (textWidth + whiteSpace) < (factor * viewScale) and factor != zoomLevels[0]:
				factor = zoomLevels[zoomLevels.index(factor) - 1]
				if factor == 200:
					textWidth = longTextWidth
		return factor, (factor < 200) # 0.2 * 5 = 1.0 second, if the interval is smaller, milliseconds are needed
	
#=========================================================================


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]