~ubuntu-branches/ubuntu/lucid/exaile/lucid

« back to all changes in this revision

Viewing changes to xl/event.py

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Starr-Bochicchio
  • Date: 2010-02-12 19:51:01 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20100212195101-8jt3tculxcl92e6v
Tags: 0.3.1~b1-0ubuntu1
* New upstream release.
* Adjust exaile.install for new plugins.
* debian/control:
 - Drop unneeded python-dev Build-Dep.
 - Bump Standards-Version to 3.8.4 
* debian/rules: No empty po files to delete.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008-2009 Adam Olsen 
 
1
# Copyright (C) 2008-2009 Adam Olsen
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
15
15
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
16
#
17
17
#
18
 
# The developers of the Exaile media player hereby grant permission 
19
 
# for non-GPL compatible GStreamer and Exaile plugins to be used and 
20
 
# distributed together with GStreamer and Exaile. This permission is 
21
 
# above and beyond the permissions granted by the GPL license by which 
22
 
# Exaile is covered. If you modify this code, you may extend this 
23
 
# exception to your version of the code, but you are not obligated to 
24
 
# do so. If you do not wish to do so, delete this exception statement 
 
18
# The developers of the Exaile media player hereby grant permission
 
19
# for non-GPL compatible GStreamer and Exaile plugins to be used and
 
20
# distributed together with GStreamer and Exaile. This permission is
 
21
# above and beyond the permissions granted by the GPL license by which
 
22
# Exaile is covered. If you modify this code, you may extend this
 
23
# exception to your version of the code, but you are not obligated to
 
24
# do so. If you do not wish to do so, delete this exception statement
25
25
# from your version.
26
26
 
27
27
"""
28
28
Provides a signals-like system for sending and listening for 'events'
29
29
 
30
30
 
31
 
Events are kind of like signals, except they may be listened for on a 
32
 
global scale, rather than connected on a per-object basis like signals 
33
 
are. This means that ANY object can emit ANY event, and these events may 
34
 
be listened for by ANY object. Events may be emitted either syncronously 
 
31
Events are kind of like signals, except they may be listened for on a
 
32
global scale, rather than connected on a per-object basis like signals
 
33
are. This means that ANY object can emit ANY event, and these events may
 
34
be listened for by ANY object. Events may be emitted either syncronously
35
35
or asyncronously, the default is asyncronous.
36
36
 
37
37
The events module also provides an idle_add() function similar to that of
42
42
most appropriate spot is immediately before a return statement.
43
43
"""
44
44
 
45
 
from xl.nls import gettext as _
46
 
import threading, time, logging, traceback, weakref
47
 
from new import instancemethod 
48
45
from inspect import ismethod
 
46
import logging
 
47
from new import instancemethod
 
48
import re
 
49
import threading
 
50
import time
 
51
import traceback
 
52
import weakref
 
53
 
49
54
from xl import common
50
55
from xl.nls import gettext as _
51
 
import re
52
56
 
53
57
# define these here so the interperter doesn't complain about them
54
58
EVENT_MANAGER = None
109
113
    timer = EventTimer(interval, function, *args, **kwargs)
110
114
    _TIMERS.append(timer)
111
115
 
112
 
    return timer    
113
 
 
114
 
def log_event(type, object, data, async=True):
 
116
    return timer
 
117
 
 
118
def timeout_add_seconds(interval, function, *args, **kwargs):
 
119
    timer = EventTimer(interval * 1000, function, *args, **kwargs)
 
120
    _TIMERS.append(timer)
 
121
 
 
122
    return timer
 
123
 
 
124
def log_event(type, obj, data, async=True):
115
125
    """
116
126
        Sends an event.
117
127
 
118
128
        type: the 'type' or 'name' of the event. [string]
119
 
        object: the object sending the event. [object]
 
129
        obj: the object sending the event. [object]
120
130
        data: some data about the event, None if not required [object]
121
131
        async: whether or not to emit asyncronously. [bool]
122
132
    """
123
133
    global EVENT_MANAGER
124
 
    e = Event(type, object, data, time.time())
 
134
    e = Event(type, obj, data, time.time())
125
135
    if async and not _TESTING:
126
136
        EVENT_MANAGER.emit_async(e)
127
137
    else:
128
138
        EVENT_MANAGER.emit(e)
129
139
 
130
 
def add_callback(function, type=None, object=None, *args, **kwargs):
 
140
def add_callback(function, type=None, obj=None, *args, **kwargs):
131
141
    """
132
142
        Sets an Event callback
133
143
 
134
 
        You should ALWAYS specify one of the two options on what to listen 
135
 
        for. While not forbidden to listen to all events, doing so will 
136
 
        cause your callback to be called very frequently, and possibly may 
 
144
        You should ALWAYS specify one of the two options on what to listen
 
145
        for. While not forbidden to listen to all events, doing so will
 
146
        cause your callback to be called very frequently, and possibly may
137
147
        cause slowness within the player itself.
138
148
 
139
149
        @param function: the function to call when the event happens [function]
140
 
        @param type: the 'type' or 'name' of the event to listen for, eg 
141
 
                "track_added",  "cover_changed". Defaults to any event if 
 
150
        @param type: the 'type' or 'name' of the event to listen for, eg
 
151
                "track_added",  "cover_changed". Defaults to any event if
142
152
                not specified. [string]
143
 
        @param object: the object to listen to events from, eg exaile.collection, 
144
 
                exaile.cover_manager. Defaults to any object if not 
 
153
        @param obj: the object to listen to events from, eg exaile.collection,
 
154
                exaile.cover_manager. Defaults to any object if not
145
155
                specified. [object]
146
156
        Any additional paramaters will be passed to the callback.
147
157
    """
148
158
    global EVENT_MANAGER
149
 
    EVENT_MANAGER.add_callback(function, type, object, args, kwargs)
 
159
    EVENT_MANAGER.add_callback(function, type, obj, args, kwargs)
150
160
 
151
 
def remove_callback(function, type=None, object=None):
 
161
def remove_callback(function, type=None, obj=None):
152
162
    """
153
163
        Removes a callback
154
164
 
156
166
        the callback
157
167
    """
158
168
    global EVENT_MANAGER
159
 
    EVENT_MANAGER.remove_callback(function, type, object)
 
169
    EVENT_MANAGER.remove_callback(function, type, obj)
160
170
 
161
171
def idle_add(func, *args):
162
172
    """
163
173
        Adds a function to run when there is spare processor time.
164
 
        
 
174
 
165
175
        func: the function to call [function]
166
 
        
167
 
        any additional arguments to idle_add will be passed on to the 
 
176
 
 
177
        any additional arguments to idle_add will be passed on to the
168
178
        called function.
169
179
 
170
180
        do not use for long-running tasks, so as to avoid blocking other
172
182
    """
173
183
    global IDLE_MANAGER
174
184
    IDLE_MANAGER.add(func, *args)
175
 
    
 
185
 
176
186
def events_pending():
177
187
    """
178
188
        Returns true if there are any events pending in the IdleManager.
179
189
    """
180
190
    global IDLE_MANAGER
181
191
    return IDLE_MANAGER.events_pending()
182
 
    
 
192
 
183
193
def event_iteration():
184
194
    """
185
195
        Explicitly processes one event in the IdleManager.
186
196
    """
187
197
    global IDLE_MANAGER
188
198
    IDLE_MANAGER.event_iteration()
189
 
    
 
199
 
190
200
def wait_for_pending_events():
191
201
    """
192
202
        Blocks until there are no pending events in the IdleManager.
198
208
    """
199
209
        Represents an Event
200
210
    """
201
 
    def __init__(self, type, object, data, time):
 
211
    def __init__(self, type, obj, data, time):
202
212
        """
203
213
            type: the 'type' or 'name' for this Event [string]
204
 
            object: the object emitting the Event [object]
 
214
            obj: the object emitting the Event [object]
205
215
            data: some piece of data relevant to the Event [object]
206
216
        """
207
217
        self.type = type
208
 
        self.object = object
 
218
        self.object = obj
209
219
        self.data = data
210
220
        self.time = time
211
221
 
228
238
        self.valid = False
229
239
 
230
240
class _WeakMethod:
231
 
    """Represent a weak bound method, i.e. a method doesn't keep alive the 
232
 
    object that it is bound to. It uses WeakRef which, used on its own, 
233
 
    produces weak methods that are dead on creation, not very useful. 
 
241
    """Represent a weak bound method, i.e. a method doesn't keep alive the
 
242
    object that it is bound to. It uses WeakRef which, used on its own,
 
243
    produces weak methods that are dead on creation, not very useful.
234
244
    Typically, you will use the getRef() function instead of using
235
245
    this class directly. """
236
 
    
 
246
 
237
247
    def __init__(self, method, notifyDead = None):
238
248
        """
239
 
            The method must be bound. notifyDead will be called when 
 
249
            The method must be bound. notifyDead will be called when
240
250
            object that method is bound to dies.
241
251
        """
242
252
        assert ismethod(method)
248
258
            self.objRef = weakref.ref(method.im_self, notifyDead)
249
259
        self.fun = method.im_func
250
260
        self.cls = method.im_class
251
 
        
 
261
 
252
262
    def __call__(self):
253
263
        if self.objRef() is None:
254
264
            return None
255
265
        else:
256
266
            return instancemethod(self.fun, self.objRef(), self.cls)
257
 
        
 
267
 
258
268
    def __eq__(self, method2):
259
269
        if not isinstance(method2, _WeakMethod):
260
 
            return False 
 
270
            return False
261
271
        return      self.fun      is method2.fun \
262
272
                and self.objRef() is method2.objRef() \
263
273
                and self.objRef() is not None
264
 
    
 
274
 
265
275
    def __hash__(self):
266
276
        return hash(self.fun)
267
 
    
 
277
 
268
278
    def __repr__(self):
269
279
        dead = ''
270
 
        if self.objRef() is None: 
 
280
        if self.objRef() is None:
271
281
            dead = '; DEAD'
272
282
        obj = '<%s at %s%s>' % (self.__class__, id(self), dead)
273
283
        return obj
274
 
        
 
284
 
275
285
    def refs(self, weakRef):
276
286
        """Return true if we are storing same object referred to by weakRef."""
277
287
        return self.objRef == weakRef
287
297
        createRef = _WeakMethod
288
298
    else:
289
299
        createRef = weakref.ref
290
 
        
 
300
 
291
301
    if notifyDead is None:
292
302
        return createRef(obj)
293
303
    else:
330
340
        # This is quite simple. If we have a job, wake up and run it.
331
341
        # If we run out of jobs, sleep until we have another one to do.
332
342
        while True:
333
 
            if self._stopped: return 
 
343
            if self._stopped: return
334
344
            while len(self.queue) == 0:
335
345
                self.event.wait()
336
346
                self.event.clear()
337
347
            func, args = self.queue[0]
338
348
            self.queue = self.queue[1:]
339
 
            
340
 
            if self._stopped: return 
 
349
 
 
350
            if self._stopped: return
341
351
            self._call_function(func, args)
342
 
                
 
352
 
343
353
    def events_pending(self):
344
 
        """ 
345
 
            Returns true if there are events pending in the event queue.    
 
354
        """
 
355
            Returns true if there are events pending in the event queue.
346
356
        """
347
357
        if len(self.queue) > 0:
348
358
            return True
349
359
        else:
350
360
            return False
351
 
            
 
361
 
352
362
    def event_iteration(self):
353
363
        """
354
364
            Forces an event from the event queue to be processed.
355
365
        """
356
366
        self.event.set()
357
 
        
 
367
 
358
368
    def wait_for_pending_events(self):
359
 
        """ 
 
369
        """
360
370
            Blocks until the event queue is empty.
361
371
        """
362
372
        while self.events_pending():
367
377
            Adds a function to be executed.
368
378
 
369
379
            func: the function to execute [function]
370
 
            
 
380
 
371
381
            any additional arguments will be passed on to the called
372
382
            function
373
383
        """
392
402
 
393
403
            event: the Event to emit [Event]
394
404
        """
395
 
        if not _TESTING: 
 
405
        if not _TESTING:
396
406
            self.lock.acquire()
397
407
 
398
408
        callbacks = []
412
422
            if not self.logger_filter or re.search(self.logger_filter,
413
423
                event.type):
414
424
                logger.debug("Sent '%(type)s' event from "
415
 
                    "'%(object)s' with data '%(data)s'." % 
416
 
                        {'type' : event.type, 'object' : repr(event.object), 
 
425
                    "'%(object)s' with data '%(data)s'." %
 
426
                        {'type' : event.type, 'object' : repr(event.object),
417
427
                        'data' : repr(event.data)})
418
428
 
419
429
        # now call them
437
447
                                "to %(event)s." % {
438
448
                                    'function': cb.wfunction(),
439
449
                                    'event': event.type})
440
 
                    cb.wfunction().__call__(event.type, event.object, 
 
450
                    cb.wfunction().__call__(event.type, event.object,
441
451
                            event.data, *cb.args, **cb.kwargs)
442
452
            except:
443
453
                traceback.print_exc()
444
454
                # something went wrong inside the function we're calling
445
 
                if not _TESTING: 
446
 
                    common.log_exception(logger, 
 
455
                if not _TESTING:
 
456
                    common.log_exception(logger,
447
457
                            message="Event callback exception caught!")
448
458
                else:
449
459
                    traceback.print_exc()
450
460
 
451
 
        if not _TESTING: 
 
461
        if not _TESTING:
452
462
            self.lock.release()
453
463
 
454
464
    def emit_async(self, event):
457
467
        """
458
468
        self.idle.add(self.emit, event)
459
469
 
460
 
    def add_callback(self, function, type, object, args, kwargs):
 
470
    def add_callback(self, function, type, obj, args, kwargs):
461
471
        """
462
472
            Registers a callback.
463
473
            You should always specify at least one of type or object.
464
474
 
465
475
            @param function: The function to call [function]
466
 
            @param type:     The 'type' or 'name' of event to listen for. Defaults
 
476
            @param type: The 'type' or 'name' of event to listen for. Defaults
467
477
                to any. [string]
468
 
            @param object:   The object to listen to events from. Defaults
 
478
            @param obj: The object to listen to events from. Defaults
469
479
                to any. [string]
470
480
        """
471
481
        # add the specified categories if needed.
472
482
        if not self.callbacks.has_key(type):
473
483
            self.callbacks[type] = weakref.WeakKeyDictionary()
474
 
        if object is None:
475
 
            object = _NONE
476
 
        if not self.callbacks[type].has_key(object):
477
 
            self.callbacks[type][object] = []
 
484
        if obj is None:
 
485
            obj = _NONE
 
486
        try:
 
487
            callbacks = self.callbacks[type][obj]
 
488
        except KeyError:
 
489
            callbacks = self.callbacks[type][obj] = []
478
490
 
479
491
        # add the actual callback
480
 
        self.callbacks[type][object].append(
481
 
                Callback(function, time.time(), args, kwargs))
 
492
        callbacks.append(Callback(function, time.time(), args, kwargs))
482
493
 
483
494
        if self.use_logger:
484
495
            if not self.logger_filter or re.search(self.logger_filter, type):
485
 
                logger.debug("Added callback %s for [%s, %s]" % 
486
 
                        (function, type, object))
487
 
    
488
 
    def remove_callback(self, function, type=None, object=None):
 
496
                logger.debug("Added callback %s for [%s, %s]" %
 
497
                        (function, type, obj))
 
498
 
 
499
    def remove_callback(self, function, type=None, obj=None):
489
500
        """
490
501
            Unsets a callback
491
502
 
492
503
            The parameters must match those given when the callback was
493
504
            registered. (minus any additional args)
494
505
        """
495
 
        self.idle.add(self._remove_callback, function, type, object)
 
506
        self.idle.add(self._remove_callback, function, type, obj)
496
507
 
497
 
    def _remove_callback(self, function, type, object):
 
508
    def _remove_callback(self, function, type, obj):
498
509
        """
499
 
            Unsets a callback. 
 
510
            Unsets a callback.
500
511
 
501
512
            The parameters must match those given when the callback was
502
513
            registered.
503
514
        """
504
 
        if object is None:
505
 
            object = _NONE
 
515
        if obj is None:
 
516
            obj = _NONE
506
517
        remove = []
507
518
        try:
508
 
            for cb in self.callbacks[type][object]:
 
519
            callbacks = self.callbacks[type][obj]
 
520
            for cb in callbacks:
509
521
                if cb.wfunction() == function:
510
522
                    remove.append(cb)
511
523
        except KeyError:
514
526
            return
515
527
 
516
528
        for cb in remove:
517
 
            self.callbacks[type][object].remove(cb)
 
529
            callbacks.remove(cb)
518
530
 
519
531
        if self.use_logger:
520
532
            if not self.logger_filter or re.search(self.logger_filter, type):
521
 
                logger.debug("Removed callback %s for [%s, %s]" % 
522
 
                        (function, type, object))
 
533
                logger.debug("Removed callback %s for [%s, %s]" %
 
534
                        (function, type, obj))
523
535
 
524
536
 
525
537
class Waiter(threading.Thread):
550
562
 
551
563
    def _call_function(self):
552
564
        try:
553
 
            self.func.__call__(*self.args, **self.kwargs)
 
565
            self.function(*self.args, **self.kwargs)
554
566
        except:
555
567
            common.log_exception()
556
568