home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / share / pycentral / deskbar-applet / site-packages / deskbar / Handler.py < prev    next >
Encoding:
Python Source  |  2006-08-29  |  10.3 KB  |  342 lines

  1. import sys
  2. import gtk, gobject
  3. import deskbar.Utils, deskbar
  4.  
  5. # Most Handlers will be HANDLER_IS_HAPPY, and this the assumed default state.
  6. # Some Handlers will require configuration, or need to notify the user in some
  7. # way of some message.  For example, the evolution-data-server backed Handler
  8. # should give an indication if none of the user's address books have auto-
  9. # complete enabled (thus rendering the Handler unable to function).  This would
  10. # be represented by HANDLER_IS_CONFIGURABLE.
  11. # Other Handlers shouldn't be shown to the user at all.  For example, if the
  12. # user's preferred web browser is Epiphany, then the Firefox handler should
  13. # be hidden: HANDLER_IS_NOT_APPLICABLE.
  14. HANDLER_IS_HAPPY          = 0
  15. HANDLER_IS_CONFIGURABLE   = 1
  16. HANDLER_HAS_REQUIREMENTS  = 2
  17. HANDLER_IS_NOT_APPLICABLE = 3
  18.         
  19. class Handler:
  20.     def __init__(self, iconfile):
  21.         """
  22.         The constructor of the Handler should not block. 
  23.         Heavy duty tasks such as indexing should be done in the initialize() method.
  24.         Under all circumstances, the constructor should not raise ANY exception
  25.         """
  26.         if type(iconfile) == tuple:
  27.             for icon in iconfile:
  28.                 self._icon = deskbar.Utils.load_icon(icon)
  29.                 if self._icon != None:
  30.                     break
  31.         else:
  32.             self._icon = deskbar.Utils.load_icon(iconfile)
  33.             
  34.         self._priority = 0
  35.     
  36.     def set_priority(self, prio):
  37.         self._priority = prio
  38.         
  39.     def get_priority(self):
  40.         """
  41.         Returns the global priority (against other handlers) of this handler as int
  42.         """
  43.         return self._priority
  44.     
  45.     def deserialize(self, class_name, serialized):
  46.         try:
  47.             match = getattr(sys.modules[self.__module__], class_name)(self, **serialized)
  48.             if match.is_valid():
  49.                 return match
  50.         except Exception, msg:
  51.             print 'Warning:Error while deserializing match:', class_name, serialized, msg
  52.  
  53.         return None
  54.         
  55.     def get_icon(self):
  56.         """
  57.         Returns a GdkPixbuf hat represents this handler.
  58.         Returns None if there is no associated icon.
  59.         """
  60.         return self._icon
  61.     
  62.     def initialize(self):
  63.         """
  64.         The initialize of the Handler should not block. 
  65.         Heavy duty tasks such as indexing should be done in this method, it 
  66.         will be called with a low priority in the mainloop.
  67.         
  68.         Handler.initialize() is guarantied to be called before the handler
  69.         is queried.
  70.         
  71.         If an exception is thrown in this method, the module will be ignored and will
  72.         not receive any query.
  73.         """
  74.         pass
  75.     
  76.     def stop(self):
  77.         """
  78.         If the handler needs any cleaning up before it is unloaded, do it here.
  79.         
  80.         Handler.stop() is guarantied to be called before the handler is 
  81.         unloaded.
  82.         """
  83.         pass
  84.         
  85.     def query(self, query):
  86.         """
  87.         Searches the handler for the given query string.
  88.         Returns a list of
  89.             (string, match object) tuple or
  90.             match object, when the string is the same as the passed one.
  91.         """
  92.         raise NotImplementedError
  93.  
  94.     def on_key_press(self, query, shortcut):
  95.         """
  96.         Called when the user presses a special trigger combination, like alt-foo
  97.         The query text and text press gtk event are passed.
  98.         
  99.         The handler must return None if it didn't handle the key press, or a Match instance if
  100.         it handled the keypress, and the returned match will be executed with the query text
  101.         """
  102.         return None
  103.         
  104.     def is_async (self):
  105.         """
  106.         AsyncHandler overwrites this method and returns True.
  107.         It is used to determine whether we should call some async specific methods/signals.
  108.         """
  109.         return False
  110.  
  111. class SignallingHandler (Handler, gobject.GObject):
  112.     """
  113.     This handler is an asynchronous handler using natural glib libraries, like
  114.     libebook, or galago, or twisted.
  115.     """
  116.     __gsignals__ = {
  117.         "query-ready" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_STRING, gobject.TYPE_PYOBJECT])
  118.     }
  119.  
  120.     def __init__ (self, iconfile=None):
  121.         Handler.__init__ (self, iconfile)
  122.         gobject.GObject.__init__ (self)
  123.         self.__last_query = None
  124.         self.__start_query_id = 0
  125.         self.__delay = 0
  126.  
  127.     def set_delay (self, timeout):
  128.         self.__delay = timeout
  129.  
  130.     def query_async (self, qstring):
  131.         if self.__delay == 0:
  132.             self.__query_async_real (qstring)
  133.             return
  134.         # Check if there is a query delayed, and remove it if so
  135.         if self.__start_query_id != 0:
  136.             gobject.source_remove(self.__start_query_id)
  137.  
  138.         self.__start_query_id = gobject.timeout_add(self.__delay, self.__query_async_real, qstring) 
  139.  
  140.     def __query_async_real (self, qstring):
  141.         """
  142.         When we receive an async call, we first register the most current search string.
  143.         Then we call with a little delay the actual query() method, implemented by the handler.
  144.         """
  145.         self.__last_query = qstring
  146.         try:
  147.             self.query (qstring)
  148.         except TypeError:
  149.             self.query (qstring, deskbar.DEFAULT_RESULTS_PER_HANDLER)
  150.  
  151.     def emit_query_ready (self, qstring, matches):
  152.         if qstring == self.__last_query:
  153.             self.emit ("query-ready", qstring, matches)
  154.  
  155.     def stop_query (self):
  156.         if self.__start_query_id != 0:
  157.             gobject.source_remove(self.__start_query_id)
  158.         self.__last_query = None
  159.         
  160.     def is_async (self):
  161.         return True
  162.         
  163. if gtk.pygtk_version < (2,8,0):
  164.     gobject.type_register(SignallingHandler)
  165.     
  166. # Here begins the Nastyness
  167. from Queue import Queue
  168. from Queue import Empty
  169. from threading import Thread
  170.  
  171. class NoArgs :
  172.     pass
  173.  
  174. class QueryStopped (Exception):
  175.     pass    
  176.  
  177. class QueryChanged (Exception):
  178.     def __init__ (self, new_query):
  179.         self.new_query = new_query
  180.                 
  181. class AsyncHandler (Handler, gobject.GObject):
  182.     """
  183.     This class can do asynchronous queries. To implement an AsyncHandler just write it
  184.     like you would an ordinary (sync) Handler. Ie. you main concern is to implement a
  185.     query() method.
  186.     
  187.     In doing this you should regularly call check_query_changed() which will restart
  188.     the query if the query string has changed. This method can handle clean up methods
  189.     and timeouts/delays if you want to check for rapidly changing queries.
  190.     
  191.     To return a list of Matches either just return it normally from query(), or use
  192.     emit_query_ready(matches) to emit partial results.
  193.     
  194.     There will at all times only be at maximum one thread per AsyncHandler.
  195.     """
  196.  
  197.     __gsignals__ = {
  198.         "query-ready" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_STRING, gobject.TYPE_PYOBJECT]),
  199.     }
  200.  
  201.     QUERY_PRIORITY = gobject.PRIORITY_DEFAULT_IDLE
  202.  
  203.     def __init__ (self, iconfile=None):
  204.         Handler.__init__ (self, iconfile)
  205.         gobject.GObject.__init__ (self)
  206.         self.__query_queue = Queue ()
  207.         self.is_running = False
  208.     
  209.     def query_async (self, qstring):
  210.         """
  211.         This method is the one to be called by the object wanting to start a new query.
  212.         If there's an already running query that one will be cancelled if possible.
  213.         
  214.         Each time there is matches ready there will be a "query-ready" signal emitted
  215.         which will be handled in the main thread. A list of Match objects will be passed
  216.         argument to this signal.
  217.         
  218.         Note: An AsyncHandler may signal on partial results. The thread need not have
  219.         exited because there's a 'query-ready' signal emitted. Read: Don't assume that the
  220.         handler only return Matches one time.
  221.         """
  222.         if not self.is_running:
  223.             self.is_running = True
  224.             Thread (None, self.__query_async, args=(qstring,)).start ()
  225.             #print "AsyncHandler: Thread created for %s" % self.__class__ # DEBUG
  226.         else:
  227.             self.__query_queue.put (qstring, False)
  228.     
  229.     def stop_query (self):
  230.         """
  231.         Instructs the handler to stop the query the next time it does check_query_changed().
  232.         """
  233.         if self.is_running:
  234.             self.__query_queue.put (QueryStopped)
  235.     
  236.     def emit_query_ready (self, qstring, matches):
  237.         """
  238.         Use this method to emit partial results. matches should be a list of Match objects.
  239.         
  240.         Note: returning a list of Match objects from the query() method automatically
  241.         emits a 'query-ready' signal for this list. 
  242.         """
  243.         gobject.idle_add (self.__emit_query_ready, qstring, matches)
  244.         
  245.     def check_query_changed (self, clean_up=None, args=NoArgs, timeout=None):
  246.         """
  247.         Checks if the query has changed. If it has it will execute clean_up(args)
  248.         and raise a QueryChanged exception. DO NOT catch this exception. This should
  249.         only be done by __async_query().
  250.         
  251.         If you pass a timeout argument this call will not return before the query
  252.         has been unchanged for timeout seconds.
  253.         """
  254.         qstring = None
  255.         try:
  256.             qstring = self.__get_last_query (timeout)
  257.         except QueryStopped:
  258.             if clean_up:
  259.                 if args == NoArgs:
  260.                     clean_up ()
  261.                 else:
  262.                     clean_up (args)
  263.             #print "AsyncHandler: Query stopped", self.__class__ # DEBUG
  264.             raise QueryStopped()
  265.         if qstring:
  266.             # There's a query queued
  267.             # cancel the current query.
  268.             if clean_up:
  269.                 if args == NoArgs:
  270.                     clean_up ()
  271.                 else:
  272.                     clean_up (args)
  273.             #print "AsyncHandler: Query changed", self.__class__ # DEBUG
  274.             raise QueryChanged (qstring)
  275.         
  276.     def __emit_query_ready (self, qstring, matches):
  277.         """Idle handler to emit a 'query-ready' signal to the main loop."""
  278.         self.emit ("query-ready", qstring, matches)
  279.         return False
  280.  
  281.     def __query_async (self, qstring):
  282.         """
  283.         The magic happens here.
  284.         """
  285.         try:
  286.             try:
  287.                 res = self.query (qstring)
  288.             except TypeError:
  289.                 res = self.query (qstring, deskbar.DEFAULT_RESULTS_PER_HANDLER)
  290.                 
  291.             if (res and res != []):
  292.                 self.emit_query_ready (qstring, res)
  293.             self.is_running = False
  294.             
  295.         except QueryChanged, query_change:
  296.             try:
  297.                 self.__query_async (query_change.new_query)
  298.             except QueryStopped:
  299.                 self.is_running = False
  300.                 #print "AsyncHandler: %s thread terminated." % str(self.__class__) # DEBUG
  301.                 
  302.         except QueryStopped:
  303.             self.is_running = False
  304.             #print "AsyncHandler: %s thread terminated." % str(self.__class__) # DEBUG
  305.  
  306.     def __get_last_query (self, timeout=None):
  307.         """
  308.         Returns the query to be put on the query queue. We don't wan't to
  309.         do all the intermediate ones... They're obsolete.
  310.         
  311.         If there's a QueryStopped class somewhere in the queue 
  312.         (put there by stop_query()) raise a QueryStopped exeption.
  313.         This exception will be caught by __query_async()
  314.         
  315.         If timeout is passed then wait timeout seconds to see if new queries
  316.         are put on the queue.
  317.         """
  318.         tmp = None
  319.         last_query = None
  320.         try:
  321.             while True:
  322.                 # Get a query without blocking (or only block
  323.                 # timeout seconds).
  324.                 # The get() call raises an Empty exception
  325.                 # if there's no element to get()
  326.                 if timeout:
  327.                     tmp = self.__query_queue.get (True, timeout)
  328.                 else:
  329.                     tmp = self.__query_queue.get (False)
  330.                 last_query = tmp
  331.                 if last_query == QueryStopped:
  332.                     raise QueryStopped ()
  333.         except Empty:
  334.             return last_query
  335.  
  336.     def is_async (self):
  337.         """Well what do you think?"""
  338.         return True
  339.  
  340. if gtk.pygtk_version < (2,8,0):
  341.     gobject.type_register(AsyncHandler)
  342.