pitivi r1215 - in branches/SOC_2008_BLEWIS: . pitivi pitivi/timeline pitivi/ui



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]