pitivi r1215 - in branches/SOC_2008_BLEWIS: . pitivi pitivi/timeline pitivi/ui
- From: blewis svn gnome org
- To: svn-commits-list gnome org
- Subject: pitivi r1215 - in branches/SOC_2008_BLEWIS: . pitivi pitivi/timeline pitivi/ui
- Date: Thu, 24 Jul 2008 20:27:11 +0000 (UTC)
Author: blewis
Date: Thu Jul 24 20:27:11 2008
New Revision: 1215
URL: http://svn.gnome.org/viewvc/pitivi?rev=1215&view=rev
Log:
* pitivi/timeline/timeline.py:
added getDuration() method
* pitivi/ui/complexinterface.py:
ZoomableWidgetInterface has been replaced by Zoomable, which is
completely rewritten and based on sharing a single gtk.Adjustment()
with mutliple observers.
* pitivi/ui/complextimeline.py:
complex timeline has been adjusted to use the new interface. a single
gtk.Adjustment is created in ComplexTimelineWidget.py.
* pitivi/ui/ruler.py:
ruler code has been updated to use the new interface, as well as the
timeline's new getDuration() method.
* pitivi/utils.py:
added argmax()
Fixed zoom support after breaking it for layout changes
Modified:
branches/SOC_2008_BLEWIS/ChangeLog
branches/SOC_2008_BLEWIS/pitivi/timeline/timeline.py
branches/SOC_2008_BLEWIS/pitivi/ui/complexinterface.py
branches/SOC_2008_BLEWIS/pitivi/ui/complextimeline.py
branches/SOC_2008_BLEWIS/pitivi/ui/ruler.py
branches/SOC_2008_BLEWIS/pitivi/utils.py
Modified: branches/SOC_2008_BLEWIS/pitivi/timeline/timeline.py
==============================================================================
--- branches/SOC_2008_BLEWIS/pitivi/timeline/timeline.py (original)
+++ branches/SOC_2008_BLEWIS/pitivi/timeline/timeline.py Thu Jul 24 20:27:11 2008
@@ -113,6 +113,9 @@
s.audiodepth = a.audiodepth
return s
+ def getDuration(self):
+ return max(self.audiocomp.duration, self.videocomp.duration)
+
# Serializable methods
def toDataFormat(self):
Modified: branches/SOC_2008_BLEWIS/pitivi/ui/complexinterface.py
==============================================================================
--- branches/SOC_2008_BLEWIS/pitivi/ui/complexinterface.py (original)
+++ branches/SOC_2008_BLEWIS/pitivi/ui/complexinterface.py Thu Jul 24 20:27:11 2008
@@ -27,10 +27,9 @@
import gst
#
-# Complex Timeline interfaces v1 (01 Feb 2006)
+# Complex Timeline interfaces v2 (01 Jul 2008)
#
-#
-# ZoomableWidgetInterface
+# Zoomable
# -----------------------
# Interface for the Complex Timeline widgets for setting, getting,
# distributing and modifying the zoom ratio and the size of the widget.
@@ -41,41 +40,49 @@
# ex : 1.0 = 1 pixel for a second
#
# Methods:
+# . setZoomAdjustment(adj)
+# . getZoomAdjustment()
+# . setChildZoomAdjustment()
+# . zoomChanged()
# . setZoomRatio(ratio)
# . getZoomRatio(ratio)
# . pixelToNs(pixels)
# . nsToPixels(time)
-# . getPixelWidth()
-#
-#
-class ZoomableWidgetInterface:
+class Zoomable:
+
+ zoomratio = 0
+ zoom_adj = None
+
+ def setZoomAdjustment(self, adjustment):
+ if self.zoom_adj:
+ self.zoom_adj.disconnect(self._zoom_changed_sigid)
+ self.zoom_adj = adjustment
+ if adjustment:
+ self._zoom_changed_sigid = adjustment.connect("value-changed",
+ self._zoom_changed_cb)
+ self.zoomratio = adjustment.get_value()
+ self.setChildZoomAdjustment(adjustment)
+ self.zoomChanged()
- def getPixelWidth(self):
- """
- Returns the width in pixels corresponding to the duration of the object
- """
- dur = self.getDuration()
- width = self.nsToPixel(dur)
- gst.log("Got time %s, returning width : %d" % (gst.TIME_ARGS(dur), width))
- return width
+ def getZoomAdjustment(self):
+ return self.zoom_adj
- def getPixelPosition(self):
- """
- Returns the pixel offset of the widget in it's container, according to:
- _ the start position of the object in it's timeline container,
- _ and the set zoom ratio
- """
- start = self.getStartTime()
- pos = self.nsToPixel(start)
- gst.log("Got start time %s, returning offset %d" % (gst.TIME_ARGS(start), pos))
- return pos
+ def _zoom_changed_cb(self, adjustment):
+ self.zoomratio = adjustment.get_value()
+ self.zoomChanged()
+
+ def getZoomRatio(self):
+ return self.zoomratio
+
+ def setZoomRatio(self, ratio):
+ self.zoom_adj.set_value(ratio)
def pixelToNs(self, pixel):
"""
Returns the pixel equivalent in nanoseconds according to the zoomratio
"""
- return long(pixel * gst.SECOND / self.getZoomRatio())
+ return long(pixel * gst.SECOND / self.zoomratio)
def nsToPixel(self, duration):
"""
@@ -84,53 +91,59 @@
"""
if duration == gst.CLOCK_TIME_NONE:
return 0
- return int((float(duration) / gst.SECOND) * self.getZoomRatio())
+ return int((float(duration) / gst.SECOND) * self.zoomratio)
- ## Methods to implement in subclasses
-
- def getDuration(self):
- """
- Return the duration in nanoseconds of the object
- To be implemented by subclasses
- """
- raise NotImplementedError
-
- def getStartTime(self):
- """
- Return the start time in nanosecond of the object
- To be implemented by subclasses
- """
- raise NotImplementedError
+ # Override in subclasses
def zoomChanged(self):
- raise NotImplementedError
+ pass
- def durationChanged(self):
- self.queue_resize()
+ def setChildZoomAdjustment(self, adj):
+ pass
- def startChanged(self):
- self.queue_resize()
+# ZoomableObject(Zoomable)
+# -----------------------
+# Interface for UI widgets which wrap PiTiVi timeline objects.
+#
+# Methods:
+# . setObject
+# . getObject
+# . startDurationChanged
+# . getPixelPosition
+# . getPixelWidth
+
+class ZoomableObject(Zoomable):
+
+ object = None
+ width = None
+ position = None
+
+ def setTimelineObject(self, object):
+ if self.object:
+ self.object.disconnect(self._start_duration_changed_sigid)
+ self.object = object
+ if object:
+ self.start_duration_changed_sigid = object.connect(
+ "start-duration-changed", self._start_duration_changed_cb)
+
+ def getTimelineObject(self):
+ return self.object
+
+ def _start_duration_changed(self, object, start, duration):
+ self.width = self.nsToPixel(duration)
+ self.position = self.nsToPixel(start)
+ self.startDurationChanged()
def startDurationChanged(self):
- gst.info("start/duration changed")
- self.queue_resize()
+ """Overriden by subclasses"""
+ pass
- def getZoomRatio(self):
- # either the current object is the top-level object that contains the zoomratio
- if hasattr(self, 'zoomratio'):
- return self.zoomratio
- # chain up to the parent
- parent = self.parent
- while not hasattr(parent, 'getZoomRatio'):
- parent = parent.parent
- return parent.getZoomRatio()
-
- def setZoomRatio(self, zoomratio):
- if hasattr(self, 'zoomratio'):
- if self.zoomratio == zoomratio:
- return
- gst.debug("Changing zoomratio to %f" % zoomratio)
- self.zoomratio = zoomratio
- self.zoomChanged()
- else:
- self.parent.setZoomRatio(zoomratio)
+ def zoomChanged(self):
+ self._start_duration_changed(self.object, self.object.start,
+ self.object.duration)
+
+ def getPixelPosition(self):
+ return self.position
+
+ def getPixelWidth(self):
+ return self.width
Modified: branches/SOC_2008_BLEWIS/pitivi/ui/complextimeline.py
==============================================================================
--- branches/SOC_2008_BLEWIS/pitivi/ui/complextimeline.py (original)
+++ branches/SOC_2008_BLEWIS/pitivi/ui/complextimeline.py Thu Jul 24 20:27:11 2008
@@ -31,15 +31,14 @@
from pitivi.bin import SmartTimelineBin
from pitivi.timeline.source import TimelineFileSource
from complexlayer import LayerInfoList
-from layerwidgets import TimelineToolBar
import ruler
-from complexinterface import ZoomableWidgetInterface
+from complexinterface import Zoomable
import goocanvas
from util import *
import os.path
from urllib import unquote
from pitivi.timeline.objects import MEDIA_TYPE_AUDIO, MEDIA_TYPE_VIDEO
-from pitivi.utils import closest_item
+from pitivi.utils import closest_item, argmax
from gettext import gettext as _
@@ -167,7 +166,7 @@
</ui>
'''
-class ComplexTrack(SmartGroup):
+class ComplexTrack(SmartGroup, Zoomable):
__gtype_name__ = 'ComplexTrack'
def __init__(self, *args, **kwargs):
@@ -176,24 +175,8 @@
self.elements = {}
self.sig_ids = None
self.comp = None
- self._zoom_adjustment = gtk.Adjustment()
- self._zoom_ratio = 0.0
- self._zoom_adjustment.lower = 0.1
- self._zoom_adjustment.upper = 1000
- self._zoom_adjustment.connect("value-changed", self._adjust_zoom)
- self.set_zoom_ratio(10.0)
self.object_style = None
- def get_zoom_adjustment(self):
- return self._zoom_adjustment
-
- def _adjust_zoom(self, adjustment):
- self._zoom_ratio = adjustment.get_value()
- self._zoom()
-
- def set_zoom_ratio(self, ratio):
- self._zoom_adjustment.set_value(ratio)
-
def set_composition(self, comp):
if self.sig_ids:
for sig in self.sig_ids:
@@ -218,6 +201,7 @@
element.connect("start-duration-changed", self.start_duration_cb, w)
self.widgets[element] = w
self.elements[w] = element
+ element.set_data("widget", w)
self.start_duration_cb(element, element.start, element.duration, w)
self.add_child(w)
make_selectable(self.canvas, w.bg)
@@ -235,17 +219,9 @@
del self.widgets[element]
del self.elements[w]
- def ns_to_pixel(self, time):
- if time == gst.CLOCK_TIME_NONE:
- return 0
- return (float(time) / gst.SECOND) * self._zoom_ratio
-
- def pixel_to_ns(self, pixel):
- return long(pixel * gst.SECOND / self._zoom_ratio)
-
def start_duration_cb(self, obj, start, duration, widget):
- widget.props.width = self.ns_to_pixel(duration)
- self.set_child_pos(widget, (self.ns_to_pixel(start), 0))
+ widget.props.width = self.nsToPixel(duration)
+ self.set_child_pos(widget, (self.nsToPixel(start), 0))
def _start_drag(self, item):
item.raise_(None)
@@ -259,7 +235,7 @@
def _move_source_cb(self, item, pos):
element = item.element
element.setStartDurationTime(max(self.canvas.snap_obj_to_edit(element,
- self.pixel_to_ns(pos[0])), 0))
+ self.pixelToNs(pos[0])), 0))
def _trim_source_start_cb(self, item, pos):
element = item.element
@@ -271,7 +247,7 @@
# min(start) = end - element.factory.getDuration()
new_start = max(0,
cur_end - element.factory.getDuration(),
- self.canvas.snap_time_to_edit(self.pixel_to_ns(pos[0])))
+ self.canvas.snap_time_to_edit(self.pixelToNs(pos[0])))
new_duration = cur_end - new_start
new_media_start = element.media_start + (new_start - element.media_start)
element.setStartDurationTime(new_start, new_duration)
@@ -284,14 +260,14 @@
new_end = min(cur_start + element.factory.getDuration(),
max(cur_start,
self.canvas.snap_time_to_edit(
- self.pixel_to_ns(pos[0] + width(item)))))
+ self.pixelToNs(pos[0] + width(item)))))
new_duration = new_end - element.start
element.setStartDurationTime(gst.CLOCK_TIME_NONE, new_duration)
#FIXME: only for sources
element.setMediaStartDurationTime(gst.CLOCK_TIME_NONE, new_duration)
- def _zoom(self):
+ def zoomChanged(self):
"""Force resize if zoom ratio changes"""
for child in self.elements:
element = self.elements[child]
@@ -380,22 +356,25 @@
self.r_handle.props.height = height
self._size_spacer()
-class CompositionLayers(goocanvas.Canvas, ZoomableWidgetInterface):
+class CompositionLayers(goocanvas.Canvas, Zoomable):
""" Souped-up VBox that contains the timeline's CompositionLayer """
- def __init__(self, leftsizegroup, layerinfolist):
+ def __init__(self, layerinfolist):
goocanvas.Canvas.__init__(self)
- self._selected_sources = set()
+ self._selected_sources = []
self._editpoints = []
self._deadband = 0
+ self._timeline_position = 0
+
self._block_size_request = False
- self.leftSizeGroup = leftsizegroup
+ self.props.integer_layout = True
+ self.props.automatic_bounds = False
+
self.layerInfoList = layerinfolist
self.layerInfoList.connect('layer-added', self._layerAddedCb)
self.layerInfoList.connect('layer-removed', self._layerRemovedCb)
+
self._createUI()
- self.props.automatic_bounds = False
- self.props.integer_layout = True
self.connect("size_allocate", self._size_allocate)
def _createUI(self):
@@ -469,6 +448,11 @@
return res
return time
+ def snap_time_to_playhead(self, time):
+ if abs(time - self._timeline_position) <= self._deadband:
+ return self._timeline_position
+ return time
+
def snap_obj_to_edit(self, obj, time):
# need to find the closest edge to both the left and right sides of
# the object we are draging.
@@ -499,7 +483,7 @@
if obj.comp:
obj.comp.removeSource(obj.element, remove_linked=True,
collapse_neighbours=False)
- self._selected_sources = set()
+ set_selection(self, set())
return True
def activateRazor(self, unused_action):
@@ -515,7 +499,8 @@
def _razorMovedCb(self, canvas, event):
x, y = event_coords(self, event)
- self._razor.props.x = x
+ self._razor.props.x = self.nsToPixel(self.snap_time_to_playhead(
+ self.pixelToNs(x)))
return True
def _razorClickedCb(self, canvas, event):
@@ -531,17 +516,19 @@
# selectable should be sources
x, y = event_coords(self, event)
items = self.get_items_at(x, y, True)
+ if not items:
+ return True
for item in items:
if item.get_data("selectable"):
parent = item.get_parent()
gst.log("attempting to split source at position %d" % x)
- self._splitSource(parent, x)
+ self._splitSource(parent, self.snap_time_to_playhead(
+ self.pixelToNs(x)))
return True
- def _splitSource(self, obj, x):
+ def _splitSource(self, obj, editpoint):
comp = obj.comp
element = obj.element
- editpoint = self.pixelToNs(x)
# we want to divide element in elementA, elementB at the
# edit point.
@@ -554,6 +541,9 @@
# start/duration.
a_dur = a_end - a_start
b_dur = b_end - b_start
+ if not (a_dur and b_dur):
+ gst.Log("cannot cut at existing edit point, aborting")
+ return
# and finally, we need the media-start/duration for both sources.
# in this case, media-start = media-duration, but this would not be
@@ -581,47 +571,56 @@
pass
def selectAfterCurrent(self, unused_action):
+ ## helper function
+ #def source_pos(ui_obj):
+ # return ui_obj.comp.getSimpleSourcePosition(ui_obj.element)
+
+ ## mapping from composition -> (source1, ... sourceN)
+ #comps = dict()
+ #for source in self._selected_sources:
+ # if not source.comp in comps:
+ # comps[source.comp] = []
+ # comps[source.comp].append(source)
+
+ ## find the latest source in each compostion, and all sources which
+ ## occur after it. then select them.
+ #to_select = set()
+ #for comp, sources in comps.items():
+ # # source positions start at 1, not 0.
+ # latest = max((source_pos(source) for source in sources)) - 1
+ # # widget is available in "widget" data member of object.
+ # # we add the background of the widget, not the widget itself.
+ # objs = [obj.get_data("widget").bg for obj in comp.condensed[latest:]]
+ # to_select.update(set(objs))
+ #set_selection(self, to_select)
pass
-
-## ZoomableWidgetInterface overrides
-
def _selection_changed_cb(self, selected, deselected):
-
# TODO: filter this list for things other than sources, and put them
# into appropriate lists
for item in selected:
item.props.fill_color_rgba = item.get_data("selected_color")
parent = item.get_parent()
- self._selected_sources.add(parent)
+ self._selected_sources.append(parent)
for item in deselected:
item.props.fill_color_rgba = item.get_data("normal_color")
parent = item.get_parent()
self._selected_sources.remove(parent)
-## ZoomableWidgetInterface overrides
+ def timelinePositionChanged(self, value, frame):
+ self._timeline_position = value
- def getDuration(self):
- return max([layer.composition.duration for layer in
- self.layerInfoList])
-
- def getStartTime(self):
- # the start time is always 0 (for display reason)
- return 0
+## Zoomable Override
- def zoomChanged(self):
- ratio = self.getZoomRatio()
- self._deadband = self.pixelToNs(DEADBAND)
+ def setChildZoomAdjustment(self, adj):
for layer in self.layers:
- layer.set_zoom_ratio(ratio)
-
- def timelinePositionChanged(self, value, frame):
- pass
+ layer.setZoomAdjustment(adj)
## LayerInfoList callbacks
def _layerAddedCb(self, unused_infolist, layer, position):
track = ComplexTrack()
+ track.setZoomAdjustment(self.getZoomAdjustment())
track.set_composition(layer.composition)
track.set_canvas(self)
self.layers.insert_child(track, position)
@@ -652,19 +651,22 @@
# +--Status Bar ??
#
-class ComplexTimelineWidget(gtk.VBox, ZoomableWidgetInterface):
+class ComplexTimelineWidget(gtk.VBox):
def __init__(self):
gst.log("Creating ComplexTimelineWidget")
gtk.VBox.__init__(self)
- self.zoomratio = 10.0
-
+ self._zoom_adj = gtk.Adjustment()
+ self._zoom_adj.lower = 0.1
+ self._zoom_adj.upper = 1000
+ self._zoom_adj.set_value(10)
+
# common LayerInfoList
self.layerInfoList = LayerInfoList()
instance.PiTiVi.playground.connect('position',
- self._playgroundPositionCb)
+ self._playgroundPositionCb)
# project signals
instance.PiTiVi.connect("new-project-loading",
self._newProjectLoadingCb)
@@ -681,23 +683,18 @@
self.leftSizeGroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
self.hadj = gtk.Adjustment()
self.ruler = ruler.ScaleRuler(self.hadj)
- #FIXME: this is crack
- self.ruler.getDuration = self.getDuration
- self.ruler.getStartTime = self.getStartTime
- self.ruler.zoomChanged = self.zoomChanged
+ self.ruler.setZoomAdjustment(self._zoom_adj)
self.ruler.set_size_request(0, 35)
self.pack_start(self.ruler, expand=False, fill=True)
# List of CompositionLayers
- self.compositionLayers = CompositionLayers(self.leftSizeGroup,
- self.layerInfoList)
+ self.compositionLayers = CompositionLayers(self.layerInfoList)
+ self.compositionLayers.setZoomAdjustment(self._zoom_adj)
self.scrolledWindow = gtk.ScrolledWindow(self.hadj)
self.scrolledWindow.set_policy(gtk.POLICY_ALWAYS, gtk.POLICY_AUTOMATIC)
self.scrolledWindow.add(self.compositionLayers)
#FIXME: remove padding between scrollbar and scrolled window
self.pack_start(self.scrolledWindow, expand=True)
- # force update of the zoom ratio, which hasn't been set yet.
- self.compositionLayers.zoomChanged()
# toolbar actions
actions = (
@@ -733,20 +730,6 @@
def _layerStartDurationChanged(self, layer):
self.ruler.startDurationChanged()
-## ZoomableWidgetInterface overrides
-## * we send everything to self.compositionLayers
-## * topLayer's function calls will also go there
-
- def getDuration(self):
- return self.compositionLayers.getDuration()
-
- def getStartTime(self):
- return self.compositionLayers.getStartTime()
-
- def zoomChanged(self):
- self.compositionLayers.zoomChanged()
- self.ruler.zoomChanged()
-
## ToolBar callbacks
## override show()/hide() methods to take care of actions
@@ -762,13 +745,13 @@
self.actiongroup.set_visible(False)
super(ComplexTimelineWidget, self).hide()
- def _zoomInCb(self, unused_action):
- zoomratio = 10
- self.setZoomRatio(zoomratio)
-
def _zoomOutCb(self, unused_action):
- zoomratio = 10
- self.setZoomRatio(zoomratio)
+ r = self._zoom_adj.get_value() * 0.5
+ self._zoom_adj.set_value(r)
+
+ def _zoomInCb(self, unused_action):
+ r = self._zoom_adj.get_value() * 2
+ self._zoom_adj.set_value(r)
## PlayGround timeline position callback
Modified: branches/SOC_2008_BLEWIS/pitivi/ui/ruler.py
==============================================================================
--- branches/SOC_2008_BLEWIS/pitivi/ui/ruler.py (original)
+++ branches/SOC_2008_BLEWIS/pitivi/ui/ruler.py Thu Jul 24 20:27:11 2008
@@ -27,9 +27,9 @@
import gtk
import gst
import pitivi.instance as instance
-from complexinterface import ZoomableWidgetInterface
+from complexinterface import Zoomable
-class ScaleRuler(gtk.Layout, ZoomableWidgetInterface):
+class ScaleRuler(gtk.Layout, Zoomable):
__gsignals__ = {
"expose-event":"override",
@@ -56,16 +56,12 @@
self.currentlySeeking = False
self.pressed = False
-## ZoomableWidgetInterface methods are handled by the container (LayerStack)
-## Except for ZoomChanged
+## Zoomable interface override
def zoomChanged(self):
self.doPixmap()
self.queue_draw()
- def getPixelWidth(self):
- return ZoomableWidgetInterface.getPixelWidth(self) + 2 * self.border
-
## timeline position changed method
def timelinePositionChanged(self, value, unused_frame):
@@ -172,24 +168,35 @@
self.drawBackground(context, rect)
self.drawRuler(context, rect)
+ def getDuration(self):
+ return instance.PiTiVi.current.timeline.getDuration()
+
+ def getPixelWidth(self):
+ return self.nsToPixel(self.getDuration())
+
+ def getPixelPosition(self):
+ return 0
+
def drawBackground(self, context, allocation):
context.save()
context.set_source_rgb(0.5, 0.5, 0.5)
- context.rectangle(0, 0,
- allocation.width, allocation.height)
+ context.rectangle(0, 0, allocation.width, allocation.height)
context.fill()
context.stroke()
if self.getDuration() > 0:
context.set_source_rgb(0.8, 0.8, 0.8)
- context.rectangle(0, 0,
- self.getPixelWidth(), allocation.height)
+ context.rectangle(0, 0, self.getPixelWidth(), allocation.height)
context.fill()
context.stroke()
context.restore()
+ def startDurationChanged(self):
+ gst.info("start/duration changed")
+ self.queue_resize()
+
def drawRuler(self, context, allocation):
context.save()
Modified: branches/SOC_2008_BLEWIS/pitivi/utils.py
==============================================================================
--- branches/SOC_2008_BLEWIS/pitivi/utils.py (original)
+++ branches/SOC_2008_BLEWIS/pitivi/utils.py Thu Jul 24 20:27:11 2008
@@ -93,3 +93,12 @@
diff = diff_a
return res, diff
+def argmax(func, seq):
+ """return the element of seq that gives max(map(func, seq))"""
+ def compare(a, b):
+ if a[0] > b[0]:
+ return a
+ return b
+ # using a generator expression here should save memory
+ objs = ((func(x), x) for x in seq)
+ return reduce(compare, objs)[1]
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]