[pitivi/ges: 181/287] utils/timeline: Move interfaces used in the Timeline to the utils folder
- From: Jean-FranÃois Fortin Tam <jfft src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi/ges: 181/287] utils/timeline: Move interfaces used in the Timeline to the utils folder
- Date: Thu, 15 Mar 2012 16:41:03 +0000 (UTC)
commit d60346f83797b7da8ea4877150f4952a05116ec7
Author: Thibault Saunier <thibault saunier collabora com>
Date: Tue Jan 10 14:23:55 2012 -0300
utils/timeline: Move interfaces used in the Timeline to the utils folder
Move:
+ Zoomable
+ View
+ Controller
pitivi/ui/Makefile.am | 5 +-
pitivi/ui/controller.py | 241 -------------------
pitivi/ui/curve.py | 5 +-
pitivi/ui/mainwindow.py | 6 +-
pitivi/ui/preview.py | 2 +-
pitivi/ui/previewer.py | 2 +-
pitivi/ui/ruler.py | 4 +-
pitivi/ui/timeline.py | 14 +-
pitivi/ui/timelinecanvas.py | 13 +-
pitivi/ui/track.py | 5 +-
pitivi/ui/trackobject.py | 8 +-
pitivi/ui/view.py | 24 --
pitivi/ui/zoominterface.py | 149 ------------
pitivi/utils/timeline.py | 548 +++++++++++++++++++++++++++++++++++--------
tests/test_integration.py | 2 +-
15 files changed, 484 insertions(+), 544 deletions(-)
---
diff --git a/pitivi/ui/Makefile.am b/pitivi/ui/Makefile.am
index 4c1acd9..0a403ce 100644
--- a/pitivi/ui/Makefile.am
+++ b/pitivi/ui/Makefile.am
@@ -3,7 +3,6 @@ uidir = $(libdir)/pitivi/python/pitivi/ui
ui_PYTHON = \
__init__.py \
alignmentprogress.py \
- controller.py \
curve.py \
depsmanager.py \
filelisterrordialog.py \
@@ -22,10 +21,8 @@ ui_PYTHON = \
trackobject.py \
track.py \
viewer.py \
- view.py \
clipproperties.py \
- filechooserpreview.py \
- zoominterface.py
+ filechooserpreview.py
clean-local:
rm -rf *.pyc *.pyo
diff --git a/pitivi/ui/curve.py b/pitivi/ui/curve.py
index 7fa4549..d160532 100644
--- a/pitivi/ui/curve.py
+++ b/pitivi/ui/curve.py
@@ -27,10 +27,7 @@ import gobject
import gtk
from pitivi.utils.receiver import receiver, handler
-from pitivi.ui.zoominterface import Zoomable
-import pitivi.ui.previewer as previewer
-from pitivi.ui.view import View
-from pitivi.ui.controller import Controller
+from pitivi.utils.timeline import View, Controller, Zoomable
from pitivi.utils.ui import LAYER_HEIGHT_EXPANDED, roundedrec, Point
from pitivi.utils.misc import between
diff --git a/pitivi/ui/mainwindow.py b/pitivi/ui/mainwindow.py
index 5b8d4b4..5d24d70 100644
--- a/pitivi/ui/mainwindow.py
+++ b/pitivi/ui/mainwindow.py
@@ -37,17 +37,17 @@ from gtk import RecentManager
from pitivi.utils.loggable import Loggable
from pitivi.settings import GlobalSettings
+from pitivi.effects import EffectListWidget
+from pitivi.medialibrary import MediaLibraryWidget, MediaLibraryError
from pitivi.utils.misc import show_user_manual
from pitivi.utils.ui import SPACING, info_name, FILESOURCE_TUPLE, URI_TUPLE, \
TYPE_URI_LIST, TYPE_PITIVI_FILESOURCE
+from pitivi.utils.timeline import Zoomable
from pitivi.ui.timeline import Timeline
from pitivi.ui.basetabs import BaseTabs
from pitivi.ui.viewer import PitiviViewer
-from pitivi.medialibrary import MediaLibraryWidget, MediaLibraryError
-from pitivi.effects import EffectListWidget
-from pitivi.ui.zoominterface import Zoomable
from pitivi.ui.clipproperties import ClipProperties
from pitivi.ui.filechooserpreview import PreviewWidget
diff --git a/pitivi/ui/preview.py b/pitivi/ui/preview.py
index 605b613..1822fe4 100644
--- a/pitivi/ui/preview.py
+++ b/pitivi/ui/preview.py
@@ -28,7 +28,7 @@ import goocanvas
import gobject
from pitivi.utils.receiver import receiver, handler
-from pitivi.ui.zoominterface import Zoomable
+from pitivi.utils.timeline import Zoomable
import pitivi.ui.previewer as previewer
diff --git a/pitivi/ui/previewer.py b/pitivi/ui/previewer.py
index 43fa8ec..e8ead1d 100644
--- a/pitivi/ui/previewer.py
+++ b/pitivi/ui/previewer.py
@@ -32,7 +32,7 @@ import pitivi.utils as utils
from pitivi.configure import get_pixmap_dir
from pitivi.utils.signal import Signallable
from pitivi.settings import GlobalSettings
-from pitivi.ui.zoominterface import Zoomable
+from pitivi.utils.timeline import Zoomable
from pitivi.utils.loggable import Loggable
from pitivi.thumbnailcache import ThumbnailCache
from pitivi.ui.prefs import PreferencesDialog
diff --git a/pitivi/ui/ruler.py b/pitivi/ui/ruler.py
index 9c5ab79..1ac12f6 100644
--- a/pitivi/ui/ruler.py
+++ b/pitivi/ui/ruler.py
@@ -28,9 +28,9 @@ import gtk
import gst
from pitivi.utils.playback import Seeker
-
-from pitivi.ui.zoominterface import Zoomable
+from pitivi.utils.timeline import Zoomable
from pitivi.utils.loggable import Loggable
+
from pitivi.utils.ui import time_to_string
diff --git a/pitivi/ui/timeline.py b/pitivi/ui/timeline.py
index e386ddb..9c3ba4b 100644
--- a/pitivi/ui/timeline.py
+++ b/pitivi/ui/timeline.py
@@ -24,25 +24,25 @@ Timeline widgets for the complex view
"""
import gtk
-
-import ruler
import gst
-import gobject
import ges
+import ruler
+import gobject
from gettext import gettext as _
-from zoominterface import Zoomable
from pitivi.check import soft_deps
-from pitivi.utils.loggable import Loggable
+from pitivi.effects import AUDIO_EFFECT, VIDEO_EFFECT
+
from timelinecanvas import TimelineCanvas
from timelinecontrols import TimelineControls
-from pitivi.effects import AUDIO_EFFECT, VIDEO_EFFECT
-from pitivi.utils.timeline import MoveContext, SELECT
from pitivi.ui.depsmanager import DepsManager
from pitivi.ui.filelisterrordialog import FileListErrorDialog
from pitivi.ui.alignmentprogress import AlignmentProgressDialog
+
+from pitivi.utils.loggable import Loggable
+from pitivi.utils.timeline import MoveContext, SELECT, Zoomable
from pitivi.utils.ui import SPACING, TRACK_SPACING, LAYER_HEIGHT_EXPANDED,\
LAYER_SPACING, TYPE_PITIVI_FILESOURCE, VIDEO_EFFECT_TUPLE, \
AUDIO_EFFECT_TUPLE, EFFECT_TUPLE, FILESOURCE_TUPLE, TYPE_PITIVI_EFFECT
diff --git a/pitivi/ui/timelinecanvas.py b/pitivi/ui/timelinecanvas.py
index 3242945..b4b966a 100644
--- a/pitivi/ui/timelinecanvas.py
+++ b/pitivi/ui/timelinecanvas.py
@@ -23,17 +23,18 @@ import gtk
import goocanvas
from gettext import gettext as _
-from pitivi.utils.loggable import Loggable
-from pitivi.utils.receiver import receiver, handler
+from pitivi.settings import GlobalSettings
+
from pitivi.ui.track import Track
from pitivi.ui.trackobject import TrackObject
-from pitivi.ui.zoominterface import Zoomable
-from pitivi.settings import GlobalSettings
from pitivi.ui.prefs import PreferencesDialog
+from pitivi.ui.curve import KW_LABEL_Y_OVERFLOW
+
+from pitivi.utils.loggable import Loggable
+from pitivi.utils.receiver import receiver, handler
from pitivi.utils.ui import TRACK_SPACING, unpack_cairo_pattern, \
LAYER_HEIGHT_EXPANDED, LAYER_SPACING, Point
-from pitivi.ui.controller import Controller
-from pitivi.ui.curve import KW_LABEL_Y_OVERFLOW
+from pitivi.utils.timeline import Controller, Zoomable
from pitivi.utils.ui import SPACING
# cursors to be used for resizing objects
diff --git a/pitivi/ui/track.py b/pitivi/ui/track.py
index 12f9a6f..b5198e0 100644
--- a/pitivi/ui/track.py
+++ b/pitivi/ui/track.py
@@ -25,12 +25,13 @@ import ges
import gobject
from pitivi.utils.loggable import Loggable
-from pitivi.ui.zoominterface import Zoomable
+from pitivi.utils.timeline import Zoomable
from pitivi.utils.receiver import receiver, handler
-from pitivi.ui.trackobject import TrackObject
from pitivi.utils.ui import LAYER_HEIGHT_EXPANDED,\
LAYER_HEIGHT_COLLAPSED, LAYER_SPACING
+from pitivi.ui.trackobject import TrackObject
+
class Transition(goocanvas.Rect, Zoomable):
diff --git a/pitivi/ui/trackobject.py b/pitivi/ui/trackobject.py
index a57b761..034ab32 100644
--- a/pitivi/ui/trackobject.py
+++ b/pitivi/ui/trackobject.py
@@ -5,11 +5,8 @@ import pango
import cairo
import pitivi.configure as configure
-import controller
-from view import View
from gettext import gettext as _
-from zoominterface import Zoomable
from pitivi.utils.ui import LAYER_SPACING, unpack_cairo_pattern, \
unpack_cairo_gradient, LAYER_HEIGHT_EXPANDED, LAYER_HEIGHT_COLLAPSED
@@ -20,7 +17,8 @@ from pitivi.utils.receiver import receiver, handler
from pitivi.ui.prefs import PreferencesDialog
from pitivi.utils.signal import Signallable
from pitivi.utils.timeline import SELECT, SELECT_ADD, UNSELECT, \
- SELECT_BETWEEN, MoveContext, TrimStartContext, TrimEndContext
+ SELECT_BETWEEN, MoveContext, TrimStartContext, TrimEndContext, Controller, \
+ View, Zoomable
#--------------------------------------------------------------#
@@ -126,7 +124,7 @@ class Selected (Signallable):
selected = property(getSelected, setSelected)
-class TimelineController(controller.Controller):
+class TimelineController(Controller):
_cursor = ARROW
_context = None
diff --git a/pitivi/utils/timeline.py b/pitivi/utils/timeline.py
index 07eed83..6964288 100644
--- a/pitivi/utils/timeline.py
+++ b/pitivi/utils/timeline.py
@@ -21,12 +21,14 @@
# Boston, MA 02110-1301, USA.
import ges
-
-from gst import SECOND
+import gtk
+import gst
from pitivi.utils.misc import infinity
from pitivi.utils.loggable import Loggable
from pitivi.utils.signal import Signallable
+from pitivi.utils.receiver import receiver, handler
+from pitivi.utils.ui import Point
#from pitivi.utils.align import AutoAligner
@@ -42,7 +44,12 @@ SELECT_BETWEEN = 3
#------------------------------------------------------------------------------#
-# Private utililities #
+# Timeline Object management helper #
+class TimelineError(Exception):
+ """Base Exception for errors happening in L{Timeline}s or L{TimelineObject}s"""
+ pass
+
+
def previous_track_source(focus, layer, start):
"""
Get the source before @start in @track
@@ -213,13 +220,121 @@ class SmallestGapsFinder(object):
return gap_objects.issubset(self.internal_objects)
-#-----------------------------------------------------------------------------#
-# Public Classes #
-class TimelineError(Exception):
- """Base Exception for errors happening in L{Timeline}s or L{TimelineObject}s"""
- pass
+class Selection(Signallable):
+ """
+ A collection of L{ges.TimelineObject}.
+
+ Signals:
+ - C{selection-changed} : The contents of the L{ges.Selection} changed.
+
+ @ivar selected: Set of selected L{ges.TrackObject}
+ @type selected: C{list}
+ """
+
+ __signals__ = {
+ "selection-changed": []}
+
+ def __init__(self):
+ self.selected = set([])
+ self.last_single_obj = None
+
+ def setToObj(self, obj, mode):
+ """
+ Convenience method for calling L{setSelection} with a single L{ges.TimelineObject}
+
+ @see: L{setSelection}
+ """
+ self.setSelection(set([obj]), mode)
+
+ def addTimelineObject(self, timeline_object):
+ """
+ Add the given timeline_object to the selection.
+
+ @param timeline_object: The object to add
+ @type timeline_object: L{ges.TimelineObject}
+ @raises TimelineError: If the object is already controlled by this
+ Selection.
+ """
+ if timeline_object in self.timeline_objects:
+ raise TimelineError("TrackObject already in this selection")
+
+ def setSelection(self, objs, mode):
+ """
+ Update the current selection.
+ Depending on the value of C{mode}, the selection will be:
+ - L{SELECT} : set to the provided selection.
+ - L{UNSELECT} : the same minus the provided selection.
+ - L{SELECT_ADD} : extended with the provided selection.
+ @param selection: The list of timeline objects to update the selection with.
+ @param mode: The type of update to apply. Can be C{SELECT},C{UNSELECT} or C{SELECT_ADD}
+
+ @see: L{setToObj}
+ """
+ # get a list of timeline objects
+ selection = set()
+ for obj in objs:
+ # FIXME GES break, handle the fact that we have unlinked objects in GES
+ if isinstance(obj, ges.TrackObject):
+ selection.add(obj.get_timeline_object())
+ else:
+ selection.add(obj)
+
+ old_selection = self.selected
+ if mode == SELECT_ADD:
+ selection = self.selected | selection
+ elif mode == UNSELECT:
+ selection = self.selected - selection
+ self.selected = selection
+
+ if len(self.selected) == 1:
+ self.last_single_obj = iter(selection).next()
+
+ for obj in self.selected - old_selection:
+ for tckobj in obj.get_track_objects():
+ if not isinstance(tckobj, ges.TrackEffect):
+ tckobj.selected.selected = True
+
+ for obj in old_selection - self.selected:
+ for tckobj in obj.get_track_objects():
+ if not isinstance(tckobj, ges.TrackEffect):
+ tckobj.selected.selected = False
+
+ # FIXME : shouldn't we ONLY emit this IFF the selection has changed ?
+ self.emit("selection-changed")
+
+ def getSelectedTrackObjs(self):
+ """
+ Returns the list of L{TrackObject} contained in this selection.
+ """
+ objects = []
+ for timeline_object in self.selected:
+ objects.extend(timeline_object.get_track_objects())
+
+ return set(objects)
+
+ def getSelectedTrackEffects(self):
+ """
+ Returns the list of L{TrackEffect} contained in this selection.
+ """
+ track_effects = []
+ for timeline_object in self.selected:
+ for track in timeline_object.get_track_objects():
+ if isinstance(track, ges.TrackEffect):
+ track_effects.append(track)
+
+ return track_effects
+
+ def __len__(self):
+ return len(self.selected)
+
+ def __iter__(self):
+ return iter(self.selected)
+
+
+#-----------------------------------------------------------------------------#
+# Timeline edition modes helpers #
class EditingContext(object):
DEFAULT = 0
@@ -551,7 +666,7 @@ class MoveContext(EditingContext, Loggable):
if prev:
prev_end = prev.get_start() + prev.get_duration()
- if abs(start - prev_end) < SECOND:
+ if abs(start - prev_end) < gst.SECOND:
self.debug("Snaping to edge frontward, diff=%d",
abs(start - prev_end))
return prev_end
@@ -561,7 +676,7 @@ class MoveContext(EditingContext, Loggable):
tckobj.get_timeline_object().get_layer(), start,
end - start)
- if next and abs(end - next.get_start()) < SECOND:
+ if next and abs(end - next.get_start()) < gst.SECOND:
self.debug("Snaping to edge backward, diff=%d",
abs(end - next.get_start()))
return next.get_start() - (end - start)
@@ -837,114 +952,359 @@ class TrimEndContext(EditingContext):
self._defaultTo(duration, obj.props.priority)
-class Selection(Signallable):
- """
- A collection of L{ges.TimelineObject}.
+#-------------------------- Interfaces ----------------------------------------#
- Signals:
- - C{selection-changed} : The contents of the L{ges.Selection} changed.
+ARROW = gtk.gdk.Cursor(gtk.gdk.ARROW)
- @ivar selected: Set of selected L{ges.TrackObject}
- @type selected: C{list}
- """
- __signals__ = {
- "selection-changed": []}
+class Controller(Loggable):
+
+ """A controller which implements drag-and-drop bahavior on connected view
+ objects in the timeline. Subclasses may override the drag_start, drag_end,
+ pos, and set_pos methods"""
+
+ # note we SHOULD be using the gtk function for this, but it doesn't appear
+ # to be exposed in pygtk
+ __DRAG_THRESHOLD__ = Point(0, 0)
+
+ _view = receiver()
+
+ _dragging = None
+ _canvas = None
+ _cursor = None
+ _ptr_within = False
+ _last_click = None
+ _initial = None
+ _mousedown = None
+ _last_event = None
+ _pending_drag_start = None
+ _pending_drag_end = False
+ _shift_down = False
+ _control_down = False
+ _handle_enter_leave = True
+ _handle_mouse_up_down = True
+ _handle_motion_notify = True
+
+ def __init__(self, view=None):
+ object.__init__(self)
+ self._view = view
+ Loggable.__init__(self)
+
+## convenience functions
+
+ def from_event(self, event):
+ """returns the coordinates of an event"""
+ return Point(*self._canvas.convert_from_pixels(event.x, event.y))
+
+ def from_item_event(self, item, event):
+ return Point(*self._canvas.convert_from_item_space(item,
+ *self.from_event(event)))
+
+ def to_item_space(self, item, point):
+ return Point(*self._canvas.convert_to_item_space(item, *point))
+
+ def pos(self, item):
+ bounds = item.get_bounds()
+ return Point(bounds.x1, bounds.y1)
+
+## signal handlers
+
+ @handler(_view, "enter_notify_event")
+ def enter_notify_event(self, item, target, event):
+ self._event_common(item, target, event)
+ self._canvas.grab_focus(item)
+ if self._cursor and item is target:
+ event.window.set_cursor(self._cursor)
+ if not self._dragging:
+ self.enter(item, target)
+ self._ptr_within = True
+ return self._handle_enter_leave or self._dragging
+
+ @handler(_view, "leave_notify_event")
+ def leave_notify_event(self, item, target, event):
+ self._event_common(item, target, event)
+ self._canvas.keyboard_ungrab(item, event.time)
+ self._ptr_within = False
+ if not self._dragging:
+ self.leave(item, target)
+ event.window.set_cursor(ARROW)
+ return self._handle_enter_leave or self._dragging
+
+ @handler(_view, "button_press_event")
+ def button_press_event(self, item, target, event):
+ self._event_common(item, target, event)
+ if not self._canvas:
+ self._canvas = item.get_canvas()
+ self._mousedown = self.pos(item) - self.transform(self.from_item_event(
+ item, event))
+ self._dragging = target
+ self._initial = self.pos(target)
+ self._pending_drag_start = (item, target, event)
+ return self._handle_mouse_up_down
+
+ @handler(_view, "motion_notify_event")
+ def motion_notify_event(self, item, target, event):
+ self._event_common(item, target, event)
+ if self._dragging:
+ if self._pending_drag_start is not None:
+ pending_drag_start, self._pending_drag_start = \
+ self._pending_drag_start, None
+ self._pending_drag_end = True
+ self._drag_start(*pending_drag_start)
+
+ self.set_pos(self._dragging,
+ self.transform(self._mousedown + self.from_item_event(item,
+ event)))
+ return self._handle_motion_notify
+ else:
+ self.hover(item, target, event)
+ return False
+
+ @handler(_view, "button_release_event")
+ def button_release_event(self, item, target, event):
+ self._event_common(item, target, event)
+ self._drag_end(item, self._dragging, event)
+ self._dragging = None
+ return self._handle_mouse_up_down
+
+ @handler(_view, "key_press_event")
+ def key_press_event(self, item, target, event):
+ self._event_common(item, target, event)
+ kv = event.keyval
+ if kv in (gtk.keysyms.Shift_L, gtk.keysyms.Shift_R):
+ self._shift_down = True
+ elif kv in (gtk.keysyms.Control_L, gtk.keysyms.Control_R):
+ self._control_down = True
+ return self.key_press(kv)
+
+ @handler(_view, "key_release_event")
+ def key_release_event(self, item, target, event):
+ self._event_common(item, target, event)
+ kv = event.keyval
+ if kv in (gtk.keysyms.Shift_L, gtk.keysyms.Shift_R):
+ self._shift_down = False
+ elif kv in (gtk.keysyms.Control_L, gtk.keysyms.Control_R):
+ self._control_down = False
+ return self.key_release(kv)
+
+## internal callbacks
+
+ def _event_common(self, item, target, event):
+ if not self._canvas:
+ self._canvas = item.get_canvas()
+ # might there be a better way to do this?
+ self._hadj = self._canvas.app.gui.timeline.hadj
+ self._vadj = self._canvas.app.gui.timeline.vadj
+ self._last_event = event
+ s = event.get_state()
+ self._shift_down = s & gtk.gdk.SHIFT_MASK
+ self._control_down = s & gtk.gdk.CONTROL_MASK
+
+ def _drag_start(self, item, target, event):
+ self.drag_start(item, target, event)
+
+ def _drag_end(self, item, target, event):
+ self._pending_drag_start = None
+ pending_drag_end, self._pending_drag_end = self._pending_drag_end, False
+ if pending_drag_end:
+ self.drag_end(item, target, event)
+
+ if self._ptr_within and self._drag_threshold():
+ point = self.from_item_event(item, event)
+ if self._last_click and (event.time - self._last_click < 400):
+ self.double_click(point)
+ else:
+ self.click(point)
+ self._last_click = event.time
+ event.window.set_cursor(self._cursor)
+ else:
+ event.window.set_cursor(ARROW)
+
+ def _drag_threshold(self):
+ last = self.pos(self._dragging)
+ difference = abs(self._initial - last)
+ if abs(self._initial - last) > self.__DRAG_THRESHOLD__:
+ return False
+ return True
+
+## protected interface for subclasses
+
+ def click(self, pos):
+ pass
+
+ def double_click(self, pos):
+ pass
+
+ def drag_start(self, item, target, event):
+ pass
+
+ def drag_end(self, item, target, event):
+ pass
+
+ def set_pos(self, obj, pos):
+ obj.props.x, obj.props.y = pos
+
+ def transform(self, pos):
+ return pos
+
+ def enter(self, item, target):
+ pass
+
+ def leave(self, item, target):
+ pass
+
+ def key_press(self, keyval):
+ pass
+
+ def key_release(self, keyval):
+ pass
+
+ def hover(self, item, target, event):
+ pass
+
+
+class View(object):
+
+ Controller = Controller
def __init__(self):
- self.selected = set([])
- self.last_single_obj = None
+ object.__init__(self)
+ self._controller = self.Controller(view=self)
- def setToObj(self, obj, mode):
- """
- Convenience method for calling L{setSelection} with a single L{ges.TimelineObject}
+## public interface
- @see: L{setSelection}
- """
- self.setSelection(set([obj]), mode)
+ def focus(self):
+ pass
- def addTimelineObject(self, timeline_object):
- """
- Add the given timeline_object to the selection.
+ def select(self):
+ pass
- @param timeline_object: The object to add
- @type timeline_object: L{ges.TimelineObject}
- @raises TimelineError: If the object is already controlled by this
- Selection.
- """
- if timeline_object in self.timeline_objects:
- raise TimelineError("TrackObject already in this selection")
+ def activate(self):
+ pass
- def setSelection(self, objs, mode):
- """
- Update the current selection.
+ def normal(self):
+ pass
- Depending on the value of C{mode}, the selection will be:
- - L{SELECT} : set to the provided selection.
- - L{UNSELECT} : the same minus the provided selection.
- - L{SELECT_ADD} : extended with the provided selection.
- @param selection: The list of timeline objects to update the selection with.
- @param mode: The type of update to apply. Can be C{SELECT},C{UNSELECT} or C{SELECT_ADD}
+class Zoomable(object):
+ """
+ Interface for managing tranformation between timeline timestamps and UI
+ pixels.
+
+ Complex Timeline interfaces v2 (01 Jul 2008)
+
+ Zoomable
+ -----------------------
+ Interface for the Complex Timeline widgets for setting, getting,
+ distributing and modifying the zoom ratio and the size of the widget.
+
+ A zoomratio is the number of pixels per second
+ ex : 10.0 = 10 pixels for a second
+ ex : 0.1 = 1 pixel for 10 seconds
+ ex : 1.0 = 1 pixel for a second
+ Class Methods
+ . pixelToNs(pixels)
+ . nsToPixels(time)
+ . setZoomRatio
+ Instance Methods
+ . zoomChanged()
+ """
- @see: L{setToObj}
- """
- # get a list of timeline objects
- selection = set()
- for obj in objs:
- # FIXME GES break, handle the fact that we have unlinked objects in GES
- if isinstance(obj, ges.TrackObject):
- selection.add(obj.get_timeline_object())
- else:
- selection.add(obj)
+ sigid = None
+ _instances = []
+ max_zoom = 1000.0
+ min_zoom = 0.25
+ zoom_steps = 100
+ zoom_range = max_zoom - min_zoom
+ _cur_zoom = 2
+ zoomratio = None
- old_selection = self.selected
- if mode == SELECT_ADD:
- selection = self.selected | selection
- elif mode == UNSELECT:
- selection = self.selected - selection
- self.selected = selection
+ def __init__(self):
+ # FIXME: ideally we should deprecate this
+ Zoomable.addInstance(self)
+ if Zoomable.zoomratio is None:
+ Zoomable.zoomratio = self.computeZoomRatio(self._cur_zoom)
- if len(self.selected) == 1:
- self.last_single_obj = iter(selection).next()
+ def __del__(self):
+ if self in Zoomable._instances:
+ # FIXME: ideally we should deprecate this and spit a warning here
+ self._instances.remove(self)
- for obj in self.selected - old_selection:
- for tckobj in obj.get_track_objects():
- if not isinstance(tckobj, ges.TrackEffect):
- tckobj.selected.selected = True
+ @classmethod
+ def addInstance(cls, instance):
+ cls._instances.append(instance)
- for obj in old_selection - self.selected:
- for tckobj in obj.get_track_objects():
- if not isinstance(tckobj, ges.TrackEffect):
- tckobj.selected.selected = False
+ @classmethod
+ def removeInstance(cls, instance):
+ cls._instances.remove(instance)
- # FIXME : shouldn't we ONLY emit this IFF the selection has changed ?
- self.emit("selection-changed")
+ @classmethod
+ def setZoomRatio(cls, ratio):
+ if cls.zoomratio != ratio:
+ cls.zoomratio = min(cls.max_zoom, max(cls.min_zoom, ratio))
+ cls._zoomChanged()
- def getSelectedTrackObjs(self):
+ @classmethod
+ def setZoomLevel(cls, level):
+ level = min(cls.zoom_steps, max(0, level))
+ if level != cls._cur_zoom:
+ cls._cur_zoom = level
+ cls.setZoomRatio(cls.computeZoomRatio(level))
+
+ @classmethod
+ def getCurrentZoomLevel(cls):
+ return cls._cur_zoom
+
+ @classmethod
+ def zoomIn(cls):
+ cls.setZoomLevel(cls._cur_zoom + 1)
+
+ @classmethod
+ def zoomOut(cls):
+ cls.setZoomLevel(cls._cur_zoom - 1)
+
+ @classmethod
+ def computeZoomRatio(cls, x):
+ return ((((float(x) / cls.zoom_steps) ** 3) * cls.zoom_range) +
+ cls.min_zoom)
+
+ @classmethod
+ def computeZoomLevel(cls, ratio):
+ return int((
+ (max(0, ratio - cls.min_zoom) /
+ cls.zoom_range) ** (1.0 / 3.0)) *
+ cls.zoom_steps)
+
+ @classmethod
+ def pixelToNs(cls, pixel):
"""
- Returns the list of L{TrackObject} contained in this selection.
+ Returns the pixel equivalent in nanoseconds according to the zoomratio
"""
- objects = []
- for timeline_object in self.selected:
- objects.extend(timeline_object.get_track_objects())
+ return long(pixel * gst.SECOND / cls.zoomratio)
- return set(objects)
-
- def getSelectedTrackEffects(self):
+ @classmethod
+ def pixelToNsAt(cls, pixel, ratio):
"""
- Returns the list of L{TrackEffect} contained in this selection.
+ Returns the pixel equivalent in nanoseconds according to the zoomratio
"""
- track_effects = []
- for timeline_object in self.selected:
- for track in timeline_object.get_track_objects():
- if isinstance(track, ges.TrackEffect):
- track_effects.append(track)
+ return long(pixel * gst.SECOND / ratio)
- return track_effects
+ @classmethod
+ def nsToPixel(cls, duration):
+ """
+ Returns the pixel equivalent of the given duration, according to the
+ set zoom ratio
+ """
+ ## DIE YOU CUNTMUNCH CLOCK_TIME_NONE UBER STUPIDITY OF CRACK BINDINGS !!!!!!
+ if duration == 18446744073709551615 or \
+ long(duration) == long(gst.CLOCK_TIME_NONE):
+ return 0
+ return int((float(duration) / gst.SECOND) * cls.zoomratio)
- def __len__(self):
- return len(self.selected)
+ @classmethod
+ def _zoomChanged(cls):
+ for inst in cls._instances:
+ inst.zoomChanged()
- def __iter__(self):
- return iter(self.selected)
+ def zoomChanged(self):
+ pass
diff --git a/tests/test_integration.py b/tests/test_integration.py
index b6476ce..0cbe029 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -307,7 +307,7 @@ class InstanceRunner(Signallable):
if self.no_ui:
self.instance.run(["--no-ui"])
else:
- from pitivi.ui.zoominterface import Zoomable
+ from pitivi.utils.timeline import Zoomable
# set a common zoom ratio so that things like edge snapping values
# are consistent
Zoomable.setZoomLevel((3 * Zoomable.zoom_steps) / 4)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]