~timo-jyrinki/ubuntu/trusty/pitivi/backport_utopic_fixes

« back to all changes in this revision

Viewing changes to pitivi/utils/ripple_update_group.py

  • Committer: Package Import Robot
  • Author(s): Sebastian Dröge
  • Date: 2014-04-05 15:28:16 UTC
  • mfrom: (6.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20140405152816-6lijoax4cngiz5j5
Tags: 0.93-3
* debian/control:
  + Depend on python-gi (>= 3.10), older versions do not work
    with pitivi (Closes: #732813).
  + Add missing dependency on gir1.2-clutter-gst-2.0 (Closes: #743692).
  + Add suggests on gir1.2-notify-0.7 and gir1.2-gnomedesktop-3.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Pitivi video editor
 
2
#
 
3
#       pitivi/utils/ripple_update_group.py
 
4
#
 
5
# Copyright (c) 2010, Brandon Lewis <brandon.lewis@collabora.co.uk>
 
6
#
 
7
# This program is free software; you can redistribute it and/or
 
8
# modify it under the terms of the GNU Lesser General Public
 
9
# License as published by the Free Software Foundation; either
 
10
# version 2.1 of the License, or (at your option) any later version.
 
11
#
 
12
# This program is distributed in the hope that it will be useful,
 
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
# Lesser General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU Lesser General Public
 
18
# License along with this program; if not, write to the
 
19
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 
20
# Boston, MA 02110-1301, USA.
 
21
 
 
22
 
 
23
class RippleUpdateGroup(object):
 
24
    """Allows for event-driven spreadsheet-like ripple updates without
 
25
    infinite loops.
 
26
 
 
27
    This class allows you to express an event-driven sequence of operations in
 
28
    terms of a directed graph. It is not a constraint solver: The goal is to
 
29
    allow the programmer to reduce complex logic to a set of simple functions
 
30
    and predicates combined declaratively.
 
31
 
 
32
    Events propagate through the graph in breadth first order. During an
 
33
    update cycle, each vertex is visited only once, so cycles can exist in the
 
34
    graph without creating infinite loops.
 
35
 
 
36
    Each vertex represents a unique object. The following may also be
 
37
    associated with a vertex:
 
38
 
 
39
        - the name of a signal on the object. when this signal fires, it
 
40
          triggers an update cycle beginning at this object. during an update
 
41
          cycle, further signal emissions from this or any other vertex will
 
42
          be ignored to prevent infinite loops.
 
43
 
 
44
        - an update function, which will be called when the vertex is visited
 
45
          as part of an update cycle. It will not be called when the object
 
46
          emits a signal.
 
47
 
 
48
        - zero or more user-specified arguments, passed to the
 
49
          update_function.
 
50
 
 
51
    An edge between two verticies represents a sequence of operations. If an
 
52
    edge exists from object A to object B, then whenever A is perfomred, B
 
53
    should be performed too -- unless it has already been visited as part of
 
54
    this update cycle.
 
55
 
 
56
    In addition to a a pair of objects, each edge also has the following
 
57
    assoicated with it:
 
58
 
 
59
        - a predicate function. called during an update cycle when this edge
 
60
          is reached, and before any other processing is done. If this
 
61
          function returns false, it will be as if this edge otherwise did not
 
62
          exist.
 
63
 
 
64
        - a function to be called whenver the edge is visited during an update
 
65
          cycle. this function will not be called if the condition function
 
66
          returns False.
 
67
 
 
68
    @ivar arcs: A map from widget to a list of edges originating in the widget.
 
69
    @ivar update_funcs: A map from widget to a (callable, args) tuple.
 
70
    """
 
71
 
 
72
    def __init__(self):
 
73
        self.arcs = {}
 
74
        self.update_funcs = {}
 
75
        self.ignore_new_signals = False
 
76
 
 
77
    def addVertex(self, widget, signal=None, update_func=None,
 
78
            update_func_args=()):
 
79
        """Add a widget to the list of vertexes.
 
80
 
 
81
        @param widget: The vertex to be added.
 
82
        @type widget: Gtk.Widget
 
83
        @param signal: A signal of the widget to be monitored.
 
84
        @type signal: str
 
85
        @param update_func: A callable object called when the vertex is visited.
 
86
        @type update_func: function
 
87
        @param update_func_args: The arguments for calling update_func.
 
88
        @type update_func_args: tuple
 
89
        """
 
90
        if signal:
 
91
            widget.connect(signal, self._widgetValueChanged)
 
92
        self.update_funcs[widget] = (update_func, update_func_args)
 
93
        self.arcs[widget] = []
 
94
 
 
95
    def addEdge(self, widget_a, widget_b, predicate=None, edge_func=None):
 
96
        """Add a directional edge from widget_a to widget_b.
 
97
 
 
98
        @param widget_a: The source vertex.
 
99
        @type widget_a: Gtk.Widget
 
100
        @param widget_b: The target vertex.
 
101
        @type widget_b: Gtk.Widget
 
102
        @param predicate: A callable object returning whether the edge may be
 
103
            traversed.
 
104
        @type predicate: function
 
105
        @param edge_func: A callable object called when the edge is traversed.
 
106
        @type edge_func: function
 
107
        """
 
108
        self.arcs[widget_a].append((widget_b, predicate, edge_func))
 
109
 
 
110
    def addBiEdge(self, widget_a, widget_b, predicate=None, edge_func=None):
 
111
        """Add a bidirectional edge between the specified vertexes.
 
112
 
 
113
        @see: addEdge
 
114
        """
 
115
        self.addEdge(widget_a, widget_b, predicate, edge_func)
 
116
        self.addEdge(widget_b, widget_a, predicate, edge_func)
 
117
 
 
118
    def _widgetValueChanged(self, widget, *unused):
 
119
        """Handle an event generated by the specified widget."""
 
120
        if self.ignore_new_signals:
 
121
            return
 
122
 
 
123
        self.ignore_new_signals = True
 
124
        try:
 
125
            self._updateValues(widget)
 
126
        finally:
 
127
            self.ignore_new_signals = False
 
128
 
 
129
    def _updateValues(self, widget):
 
130
        """Traverse the graph starting from the specified widget."""
 
131
        # Initialize the list of (source_widget, arc) to be traversed.
 
132
        queue = [(widget, arc) for arc in self.arcs[widget]]
 
133
        visited = set([widget])
 
134
        while queue:
 
135
            source_widget, arc = queue.pop(0)
 
136
            target_widget, predicate, edge_func = arc
 
137
 
 
138
            # ignore nodes we've seen
 
139
            if target_widget in visited:
 
140
                continue
 
141
 
 
142
            # check whether conditions permit this edge to be followed
 
143
            if predicate and not predicate():
 
144
                continue
 
145
 
 
146
            # traverse the edge
 
147
            if edge_func:
 
148
                edge_func()
 
149
 
 
150
            # visit the target node
 
151
            update_func, update_func_args = self.update_funcs[target_widget]
 
152
            if update_func:
 
153
                update_func(source_widget, target_widget, *update_func_args)
 
154
            visited.add(target_widget)
 
155
 
 
156
            # enqueue children
 
157
            for arc in self.arcs[target_widget]:
 
158
                queue.append((target_widget, arc))