0001"""Common plugins for Louie."""
0002
0003from louie import dispatcher
0004from louie import error
0005
0006
0007def install_plugin(plugin):
0008 cls = plugin.__class__
0009 for p in dispatcher.plugins:
0010 if p.__class__ is cls:
0011 raise error.PluginTypeError(
0012 'Plugin of type %r already installed.' % cls)
0013 dispatcher.plugins.append(plugin)
0014
0015def remove_plugin(plugin):
0016 dispatcher.plugins.remove(plugin)
0017
0018
0019class Plugin(object):
0020 """Base class for Louie plugins.
0021
0022 Plugins are used to extend or alter the behavior of Louie
0023 in a uniform way without having to modify the Louie code
0024 itself.
0025 """
0026
0027 def is_live(self, receiver):
0028 """Return True if the receiver is still live.
0029
0030 Only called for receivers who have already been determined to
0031 be live by default Louie semantics.
0032 """
0033 return True
0034
0035 def wrap_receiver(self, receiver):
0036 """Return a callable that passes arguments to the receiver.
0037
0038 Useful when you want to change the behavior of all receivers.
0039 """
0040 return receiver
0041
0042
0043class QtWidgetPlugin(Plugin):
0044 """A Plugin for Louie that knows how to handle Qt widgets
0045 when using PyQt built with SIP 4 or higher.
0046
0047 Weak references are not useful when dealing with QWidget
0048 instances, because even after a QWidget is closed and destroyed,
0049 only the C++ object is destroyed. The Python 'shell' object
0050 remains, but raises a RuntimeError when an attempt is made to call
0051 an underlying QWidget method.
0052
0053 This plugin alleviates this behavior, and if a QWidget instance is
0054 found that is just an empty shell, it prevents Louie from
0055 dispatching to any methods on those objects.
0056 """
0057
0058 def __init__(self):
0059 try:
0060 import qt
0061 except ImportError:
0062 self.is_live = self._is_live_no_qt
0063 else:
0064 self.qt = qt
0065
0066 def is_live(self, receiver):
0067 """If receiver is a method on a QWidget, only return True if
0068 it hasn't been destroyed."""
0069 if (hasattr(receiver, 'im_self') and
0070 isinstance(receiver.im_self, self.qt.QWidget)
0071 ):
0072 try:
0073 receiver.im_self.x()
0074 except RuntimeError:
0075 return False
0076 return True
0077
0078 def _is_live_no_qt(self, receiver):
0079 return True
0080
0081
0082class TwistedDispatchPlugin(Plugin):
0083 """Plugin for Louie that wraps all receivers in callables
0084 that return Twisted Deferred objects.
0085
0086 When the wrapped receiver is called, it adds a call to the actual
0087 receiver to the reactor event loop, and returns a Deferred that is
0088 called back with the result.
0089 """
0090
0091 def __init__(self):
0092
0093
0094 from twisted import internet
0095 from twisted.internet.defer import Deferred
0096 self._internet = internet
0097 self._Deferred = Deferred
0098
0099 def wrap_receiver(self, receiver):
0100 def wrapper(*args, **kw):
0101 d = self._Deferred()
0102 def called(dummy):
0103 return receiver(*args, **kw)
0104 d.addCallback(called)
0105 self._internet.reactor.callLater(0, d.callback, None)
0106 return d
0107 return wrapper