[Deskbar] Att. async module loaders: Handy test module
- From: Mikkel Kamstrup Erlandsen <kamstrup daimi au dk>
- To: deskbar-applet-list gnome org
- Subject: [Deskbar] Att. async module loaders: Handy test module
- Date: Tue, 11 Oct 2005 09:39:33 +0200
I was discussing with Raphael on IRC last night whether the async
loading/initializing was working. I wrote the attached test handler to
verify this. Async loading does indeed work, as you'll se if you stick
it in deskbar/handlers/.
Not that I don't expect you to have your own test handlers; this one was
just handy in putting Raphaels concerns to rest ;-P
The concerning fact was that the window was slow to come up. Digging
through the code I tracked this down to the signals emitted from the
ModuleLoader. I gave them the same priority as default GTK events.
Seemingly this was way to aggressive. Changing this to
gobject.PRIORITY_DEFAULT_IDLE did the trick.
The attached patch is a modified version of Raphaels modification of my
initial patch. Ie. apply the *-fix_priority.patch after applying
*.patch.
Raphael:
You modifications to my initial patch are most welcome. They work out
fine for me... I didn't know that python had a thread safe Queue ready
to plug in. That is a valuable trick!
Cheers
Mikkel
from time import sleep
from handler import Handler
from handler import Match
NAME = "Test Module"
EXPORTED_CLASS = "TestModule"
class TestMatch (Match):
def __init__(self, handler, name, icon=None):
Match.__init__ (self, handler, name)
def get_handler(self):
"""
Returns the handler owning this match.
"""
return self._handler
def get_name(self, text=None):
return {"name": self._name}
def get_verb(self):
return "%(name)s - %(text)s"
def action(self, text=None):
pass
class TestModule (Handler):
INIT_TIME = 5
def __init__ (self):
Handler.__init__ (self, None)
def initialize (self):
print NAME + "initializing ... This takes %s secons." % self.INIT_TIME
for i in range(self.INIT_TIME):
print (i+1)*"."
sleep (1)
def query (self, qstring, max):
if max > 0:
return [TestMatch(self, "TestMatch")]
else: return []
def stop (self):
pass
def get_priority(self):
return 300
if __name__ == "__main__":
print "Implement me."
Index: deskbar/applet.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/applet.py,v
retrieving revision 1.13
diff -u -p -r1.13 applet.py
--- deskbar/applet.py 10 Oct 2005 17:53:21 -0000 1.13
+++ deskbar/applet.py 10 Oct 2005 21:30:07 -0000
@@ -1,14 +1,21 @@
import os, time
import deskbar, deskbar.deskbarentry, deskbar.about, deskbar.preferences, deskbar.applet_keybinder
+from deskbar.module_list import ModuleLoader
# WARNING: Load gnome.ui before gnomeapplet or we have a nasty warning.
import gnome.ui
import gnomeapplet, gtk, gtk.gdk, gconf
+module_dirs = [deskbar.HANDLERS_DIR, "~/.gnome2/deskbar-applet"]
+
class DeskbarApplet:
def __init__(self, applet):
self.applet = applet
-
- self.entry = deskbar.deskbarentry.DeskbarEntry()
+
+ self.loader = ModuleLoader (module_dirs)
+ self.loader.connect ("module-loaded", self.loader.initialize_module_async_cb)
+ self.loader.load_all_async ()
+
+ self.entry = deskbar.deskbarentry.DeskbarEntry(self.loader)
self.entry.get_evbox().connect("button-press-event", self.on_icon_button_press)
self.entry.get_entry().connect("button-press-event", self.on_entry_button_press)
Index: deskbar/deskbar-applet
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/deskbar-applet,v
retrieving revision 1.6
diff -u -p -r1.6 deskbar-applet
--- deskbar/deskbar-applet 6 Oct 2005 16:36:17 -0000 1.6
+++ deskbar/deskbar-applet 10 Oct 2005 21:30:07 -0000
@@ -3,7 +3,11 @@
# (C) 2005 Nigel Tao.
# Licensed under the GNU GPL.
-import gtk, gnomeapplet
+import gtk
+gtk.threads_init()
+
+import gnomeapplet
+
import getopt, sys
from os.path import *
@@ -18,15 +22,6 @@ if _check(name):
else:
print "Running installed deskbar, using normal PYTHONPATH"
-# Init twisted
-#from twisted.python import threadable
-#threadable.init()
-
-#from twisted.internet import gtk2reactor
-#gtk2reactor.install()
-
-#from twisted.internet import reactor
-
# Now the path is set, import our applet
import deskbar.applet
@@ -36,18 +31,17 @@ def applet_factory(applet, iid):
# Return a standalone window that holds the applet
def build_window():
- win = gtk.Window(gtk.WINDOW_TOPLEVEL)
- win.set_title("Deskbar Applet")
- #win.connect("destroy", lambda x: reactor.stop())
- win.connect("destroy", gtk.main_quit)
+ app = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ app.set_title("Deskbar Applet")
+ app.connect("destroy", gtk.main_quit)
applet = gnomeapplet.Applet()
applet_factory(applet, None)
- applet.reparent(win)
+ applet.reparent(app)
- win.show_all()
+ app.show_all()
- return win
+ return app
def usage():
@@ -61,7 +55,7 @@ OPTIONS:
"""
sys.exit()
-if __name__ == "__main__":
+if __name__ == "__main__":
standalone = False
try:
@@ -79,17 +73,18 @@ if __name__ == "__main__":
print "No problems so far."
elif o in ("-w", "--window"):
standalone = True
-
+
if standalone:
build_window()
+ gtk.threads_enter()
gtk.main()
+ gtk.threads_leave()
else:
+ gtk.threads_enter()
gnomeapplet.bonobo_factory(
"OAFIID:Deskbar_Applet_Factory",
gnomeapplet.Applet.__gtype__,
"deskbar-applet",
"0",
applet_factory)
-
- #reactor.suggestThreadPoolSize(3)
- #reactor.run()
+ gtk.threads_leave()
Index: deskbar/deskbarentry.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/deskbarentry.py,v
retrieving revision 1.11
diff -u -p -r1.11 deskbarentry.py
--- deskbar/deskbarentry.py 10 Oct 2005 17:53:21 -0000 1.11
+++ deskbar/deskbarentry.py 10 Oct 2005 21:30:07 -0000
@@ -3,14 +3,10 @@ import cgi
import deskbar
import deskbar.iconentry
+from deskbar.module_list import ModuleList
import gtk, gobject
-from module_list import ModuleList
-from module_list import ModuleLoader
-module_dirs = [deskbar.HANDLERS_DIR, "~/.gnome2/deskbar-applet"]
-
-
# The liststore columns
HANDLER_PRIO_COL = 0
MATCH_PRIO_COL = 1
@@ -31,12 +27,13 @@ MOVE_UP = -1
MOVE_DOWN = +1
class DeskbarEntry(deskbar.iconentry.IconEntry):
- def __init__(self):
+ def __init__(self, loader):
deskbar.iconentry.IconEntry.__init__(self)
# Set up the Handlers
self._handlers = ModuleList ()
- self._load_handlers()
+ loader.connect ("module-loaded", self._handlers.update_row_cb)
+ loader.connect ("module-initialized", self._handlers.module_toggled_cb)
self._completion_model = None
self._selected_match_index = -1
@@ -103,12 +100,7 @@ class DeskbarEntry(deskbar.iconentry.Ico
def get_history(self):
return self._history
-
- def _load_handlers(self):
- loader = ModuleLoader (self._handlers, module_dirs, ".py")
- loader.load_all ()
- return
-
+
def _on_sort_matches(self, treemodel, iter1, iter2):
# First compare global handler priority
diff = treemodel[iter1][HANDLER_PRIO_COL] - treemodel[iter2][HANDLER_PRIO_COL]
Index: deskbar/handler.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/handler.py,v
retrieving revision 1.1
diff -u -p -r1.1 handler.py
--- deskbar/handler.py 6 Oct 2005 14:01:06 -0000 1.1
+++ deskbar/handler.py 10 Oct 2005 21:30:07 -0000
@@ -73,10 +73,14 @@ class Match:
class Handler:
def __init__(self, iconfile):
+ """
+ The constructor of the Handler should generally not block.
+ Heavy duty tasks such as indexing should be done in the initialize() method.
+ """
# We load the icon file, and if it fails load an empty one
try:
- self._icon = gtk.gdk.pixbuf_new_from_file_at_size(join(deskbar.ART_DATA_DIR, iconfile), -1, deskbar.ICON_SIZE)
- except gobject.GError:
+ self._icon = gtk.gdk.pixbuf_new_from_file_at_size(join(deskbar.ART_DATA_DIR, iconfile), deskbar.ICON_SIZE, deskbar.ICON_SIZE)
+ except Exception:
self._icon = None
def get_priority(self):
@@ -91,6 +95,25 @@ class Handler:
Returns None if there is no associated icon.
"""
return self._icon
+
+ def initialize(self):
+ """
+ The constructor of the Handler should generally not block.
+ Heavy duty tasks such as indexing should be done in this method.
+
+ Handler.initialize() is guarantied to be called before the handler
+ is queried.
+ """
+ pass
+
+ def stop(self):
+ """
+ If the handler needs any cleaning up before it is unloaded, do it here.
+
+ Handler.stop() is guarantied to be called before the handler is
+ unloaded.
+ """
+ pass
def query(self, query, max=5):
"""
Index: deskbar/module_list.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/module_list.py,v
retrieving revision 1.3
diff -u -p -r1.3 module_list.py
--- deskbar/module_list.py 9 Oct 2005 20:58:36 -0000 1.3
+++ deskbar/module_list.py 10 Oct 2005 21:30:07 -0000
@@ -5,23 +5,27 @@ import sys
import pydoc
from os.path import join, basename, normpath, abspath
from os.path import split, expanduser, exists, isfile, dirname
+from threading import Thread
+import Queue
class ModuleContext:
"""A generic wrapper for any object stored in a ModuleList.
settings is unused at the moment.
- """
- def __init__ (self, icon, enabled, name, module, settings, filename):
+ """
+
+ def __init__ (self, icon, enabled, module, settings, filename, name, exported_class):
"""The icon should be a gtk.gdk.Pixbuf"""
self.icon = icon
self.enabled = enabled
- self.name = name
self.module = module
self.settings = settings
self.filename = filename
+ self.name = name
+ self.exported_class = exported_class
class ModuleListIter :
- """An iter type to iterate over the modules contexts in a ModuleList object.
+ """An iter type to iterate over the of *enabled* module contexts in a ModuleList object.
This object is (typically) not used directly. See the documentation for ModuleList.
For documentation on iters see: http://docs.python.org/lib/typeiter.html
@@ -37,15 +41,12 @@ class ModuleListIter :
return self
def next (self):
- """Return the next module in the ModuleList."""
+ """Return the next *enabled* module in the ModuleList."""
try:
- mod = ModuleContext ( self.owner.get_value (self.owner_iter, self.owner.ICON_COL),
- self.owner.get_value (self.owner_iter, self.owner.ENABLED_COL),
- self.owner.get_value (self.owner_iter, self.owner.NAME_COL),
- self.owner.get_value (self.owner_iter, self.owner.MODULE_COL),
- self.owner.get_value (self.owner_iter, self.owner.SETTINGS_COL),
- self.owner.get_value (self.owner_iter, self.owner.FILENAME_COL))
+ mod = self.owner.get_context_from_iter (self.owner_iter)
self.owner_iter = self.owner.iter_next (self.owner_iter)
+ if not mod.enabled:
+ return self.next()
except TypeError:
raise StopIteration
return mod
@@ -58,7 +59,7 @@ class ModuleList (gtk.ListStore):
for modctx in modlist:
do_something (modctx)
- From this perspective the ModuleList stores ModuleContext (it actually doesnt),
+ From this perspective the ModuleList stores ModuleContexts (it actually doesnt),
so to utilize the modules you'll have to acces modctx.module.
Note that the gtk.ListView extends the following classes:
@@ -68,32 +69,94 @@ class ModuleList (gtk.ListStore):
http://www.pygtk.org/pygtk2reference/class-gtkliststore.html
Note that
"""
+
ICON_COL = 0
ENABLED_COL = 1
- NAME_COL = 2
- MODULE_COL = 3
- SETTINGS_COL = 4
- FILENAME_COL = 5
+ MODULE_COL = 2
+ SETTINGS_COL = 3
+ FILENAME_COL = 4
+ NAME_COL = 5
+ EXP_CLASS_COL = 6
def __init__ (self):
gtk.ListStore.__init__ (self, gtk.gdk.Pixbuf,
bool,
- gobject.TYPE_STRING,
gobject.TYPE_PYOBJECT,
gobject.TYPE_PYOBJECT,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
gobject.TYPE_STRING)
def __iter__ (self):
return ModuleListIter (self)
- def add (self, mod_context):
- it = self.append ()
- self.set (it, self.ICON_COL, mod_context.icon)
- self.set (it, self.ENABLED_COL, mod_context.enabled)
- self.set (it, self.NAME_COL, mod_context.name)
- self.set (it, self.MODULE_COL, mod_context.module)
- self.set (it, self.SETTINGS_COL, mod_context.settings)
- self.set (it, self.FILENAME_COL, mod_context.filename)
+ def add (self, context):
+ """Appends the module context to the list."""
+ self.update_row (context, iter)
+
+
+ def get_iter_from_context (self, modctx):
+ """Returns a gtk.TreeIter pointing to the row containing the filename
+ modctx.filename. This should be uniqualy determined by the context.
+
+ If the filename is not found return None.
+
+ INVARIANT: ModuleContexts are uniquely determined by their .filename
+ """
+
+ iter = self.get_iter_first ()
+ while (iter is not None):
+ if self.get_value (iter, self.FILENAME_COL) == modctx.filename:
+ break
+ iter = self.iter_next (iter)
+ return iter
+
+ def get_context_from_iter (self, iter):
+ """Return a ModuleContext representing the row pointed to by iter."""
+ modctx = ModuleContext ( self.get_value (iter, self.ICON_COL),
+ self.get_value (iter, self.ENABLED_COL),
+ self.get_value (iter, self.MODULE_COL),
+ self.get_value (iter, self.SETTINGS_COL),
+ self.get_value (iter, self.FILENAME_COL),
+ self.get_value (iter, self.NAME_COL),
+ self.get_value (iter, self.EXP_CLASS_COL))
+ return modctx
+
+ def update_row (self, context, iter=None):
+ """If iter is set this method updates the row pointed to by iter with the
+ values of context.
+
+ If iter is not set it will try to obtain an iter pointing
+ to the row containg context.filename. If there's no such row, it will append it.
+ """
+
+ if (iter is None):
+ iter = self.get_iter_from_context (context)
+ if (iter is None):
+ iter = self.append ()
+
+ self.set_value(iter, self.ICON_COL, context.icon)
+ self.set_value(iter, self.ENABLED_COL, context.enabled)
+ self.set_value(iter, self.MODULE_COL, context.module)
+ self.set_value(iter, self.SETTINGS_COL, context.settings)
+ self.set_value(iter, self.FILENAME_COL, context.filename)
+ self.set_value(iter, self.NAME_COL, context.name)
+ self.set_value(iter, self.EXP_CLASS_COL, context.exported_class)
+
+ def update_row_cb (self, sender, context, iter=None):
+ """
+ Callback for updating the row containing context.
+ If iter is set the row to which it points to will be
+ updated with the context.
+ """
+ self.update_row (context, iter)
+
+ def module_toggled_cb (self, sender, context):
+ """
+ Callback to toggle the enabled state of the context.
+ """
+ iter = self.get_iter_from_context (context)
+ self.set_value(iter, self.ENABLED_COL, context.enabled)
class ModuleListView (gtk.TreeView):
"""A versatile list widget that displays the contents of a ModuleList.
@@ -107,6 +170,11 @@ class ModuleListView (gtk.TreeView):
This will construct a list showing the module names and a checkbox on whether or
not they are enabled.
"""
+
+ __gsignals__ = {
+ "row-toggled" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_PYOBJECT])
+ }
+
def __init__ (self, model, columns):
gtk.TreeView.__init__ (self, model)
@@ -119,7 +187,7 @@ class ModuleListView (gtk.TreeView):
if (model.ENABLED_COL in columns):
cell_enabled = gtk.CellRendererToggle ()
cell_enabled.set_property ("activatable", True)
- cell_enabled.connect('toggled', self.toggle_enable, model)
+ cell_enabled.connect('toggled', self.emit_row_toggled, model)
self.column_enabled = gtk.TreeViewColumn ("Enabled", cell_enabled, active=model.ENABLED_COL)
if (model.NAME_COL in columns):
@@ -139,30 +207,67 @@ class ModuleListView (gtk.TreeView):
self.set_headers_visible(True)
- def toggle_enable (self, cell, path, model):
- iter = model.get_iter(path)
- model.set_value(iter, model.ENABLED_COL, not cell.get_active())
- for mod in model:
- print mod.name +" is enabled: " + str(mod.enabled)
-
-class ModuleLoader:
+ def emit_row_toggled (self, cell, path, model):
+ """Callback for the toggle buttons in the ModuleList.ENABLED_COL.
+ Emits a 'row-toggled' signal passing the context in the row as argument."""
+ context = model.get_context_from_iter (model.get_iter(path))
+ self.emit ("row-toggled", context)
+
+
+
+class ModuleLoader (gobject.GObject):
"""An auxilary class to ModuleList. Create an instance of ModuleLoader by
specifying the which directories to search and what extension to accept.
The load_all() method will load all qualified modules into the ModuleList
specified in the constructor.
+
+ Most methods have a _async variant. These methods emits signals that is handled
+ by the *main* thread. This ensures that locking mechanisms are unlikely to be
+ needed.
+
+ Hint: If you pass None as the dirs argument the ModuleLoader will not search
+ for modules at all. This is useful if you want to reload a single module for
+ which you know the path.
+
+ Important: Remember to do gtk.gdk.threads_init() or gobject.threads_init() before
+ using any of the _async methods or else it WON'T WORK. Caveat emptor!
"""
- def __init__ (self, modlist, dirs, extension=".py"):
- """modlist: The ModuleList to store all succesfully loaded modules
+
+ __gsignals__ = {
+ "module-loaded" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_PYOBJECT]),
+ "module-initialized" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_PYOBJECT]),
+ "module-stopped" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_PYOBJECT])
+ }
+
+ def __init__ (self, dirs, extension=".py"):
+ """
dirs: A list of directories to search. Relative pathnames and paths
- containing ~ will be expanded.
+ containing ~ will be expanded. If dirs is None the
+ ModuleLoader will not search for modules.
extension: What extension should this ModuleLoader accept (string).
"""
- self.modlist = modlist
- self.dirs = map (lambda s: abspath(expanduser(s)), dirs)
+ gobject.GObject.__init__ (self)
self.ext = extension
- self.filelist = self.build_filelist ()
-
-
+ if (dirs):
+ self.dirs = map (lambda s: abspath(expanduser(s)), dirs)
+ self.filelist = self.build_filelist ()
+ else:
+ self.dirs = None
+ self.filelist = []
+
+ self.task_queue = Queue.Queue(0)
+ thread = Thread(None, self.consume_queue)
+ thread.setDaemon(True)
+ thread.start()
+
+ def consume_queue(self):
+ while True:
+ print '------ In thread, getting task queue'
+ task = self.task_queue.get()
+ print '------ Got:', task
+ task()
+
+
def build_filelist (self):
"""Returns a list containing the filenames of all qualified modules.
This method is automatically invoked by the constructor.
@@ -182,18 +287,11 @@ class ModuleLoader:
if (filename[-len(self.ext):] == self.ext):
return True
return False
-
- def load_all (self):
- """Tries to load all qualified modules detected by the ModuleLoader.
- All succesfully loaded modules are stored in the ModuleList.
- """
- for f in self.filelist:
- self.load (f)
-
- def load (self, filename):
- """Tries to load the specified file as a module and stores it in the ModuleList."""
+
+ def import_module (self, filename):
+ """Tries to import the specified file. Returns the python module on succes.
+ Primarily for internal use."""
try:
- print "Importing %s" % filename
mod = pydoc.importfile (filename)
except IOError, err:
print >> sys.stderr, "Error loading the file: %s\nThe file probably doesn't exist." % filename
@@ -203,7 +301,7 @@ class ModuleLoader:
print >> sys.stderr, "Unknown error loading the file: %s." % filename
print >> sys.stderr, str(err)
return
-
+
try:
if (mod.EXPORTED_CLASS): pass
if (mod.NAME): pass
@@ -217,42 +315,159 @@ class ModuleLoader:
return
try:
- mod_init = getattr (mod, mod.EXPORTED_CLASS)
+ if (getattr (mod, mod.EXPORTED_CLASS)) : pass
except AttributeError:
print >> sys.stderr, "Class %s not found in file %s. Skipping." % (mod.EXPORTED_CLASS, filename)
return
-
+
try:
- mod_instance = mod_init()
- except Exception, err:
- print >> sys.stderr, "Error in file: %s" % filename
- print >> sys.stderr, "There was an error initializing the class: %s" % str(mod_init)
- print >> sys.stderr, str(err)
+ if (getattr(mod, mod.EXPORTED_CLASS).initialize) : pass
+ except AttributeError:
+ print >> sys.stderr, "Class %s in file %s does not have an initialize(self) method. Skipping." % (mod.EXPORTED_CLASS, filename)
return
-
- context = ModuleContext (None, True, mod.NAME, mod_instance, None, filename)
- self.modlist.add(context)
+ return mod
+
+ def load_all (self):
+ """Tries to load all qualified modules detected by the ModuleLoader.
+ Each time a module is loaded it will emit a 'module-loaded' signal
+ passing a corresponding module context.
+ """
+ if self.dirs is None:
+ print >> sys.stderr, "The ModuleLoader at %s has no filelist!" % str(id(self))
+ print >> sys.stderr, "It was probably initialized with dirs=None."
+ return
+
+ for f in self.filelist:
+ self.load (f)
+
+ def load (self, filename):
+ """Loads the given file as a module and emits a 'module-loaded' signal
+ passing a corresponding ModuleContext as argument.
+
+ Returns the context as added to the list.
+ """
+ mod = self.import_module (filename)
+ if mod is None : return
+ mod_instance = getattr (mod, mod.EXPORTED_CLASS) ()
+
+ context = ModuleContext (mod_instance.get_icon(), False, mod_instance,
+ None, filename, mod.NAME, mod.EXPORTED_CLASS)
+ gobject.idle_add (self.__emit_module_loaded, context, priority=gobject.PRIORITY_DEFAULT)
+ print "Loaded module '%s' from file %s." % (mod.NAME, filename)
+ return context
+
+ def __emit_module_loaded (self, context):
+ """Idle method. Internal use only."""
+ self.emit ("module-loaded", context)
+ return False
+
+ def initialize_module (self, context):
+ """
+ Initializes the module in the given context. Emits a 'module-initialized' signal
+ when done, passing the (now enabled) contextas argument.
+ """
+
+ print "Initializing '%s'" % context.name
+ context.module.initialize ()
+
+ context.enabled = True
+ gobject.idle_add (self.__emit_module_initialized, context, priority=gobject.PRIORITY_DEFAULT)
+
+ def initialize_module_async_cb (self, sender, context):
+ """
+ Convenience callback used to initialize a module when it's loaded.
+ Use like:
+ loader.connect ("module-loaded", loader.initialize_module_async_cb)
+ """
+ self.initialize_module_async (context)
+
+ def __emit_module_initialized (self, context):
+ """Idle method. Internal use only."""
+ self.emit ("module-initialized", context)
+ return False
+
+ def stop_module (self, context):
+ """
+ Stops the module an sets context.enabled = False. Furthermore the context.module
+ instance is also set to None. Emits a 'context-stopped' signal when done passing
+ the stopped context as argument.
+ """
+
+ print "Stopping '%s'" % context.name
+ context.module.stop ()
+
+ context.enabled = False
+ context.module = None
+ gobject.idle_add (self.__emit_module_stopped, context, priority=gobject.PRIORITY_DEFAULT)
+
+ def __emit_module_stopped (self, context):
+ """Idle method. Internal use only."""
+ self.emit ("module-stopped", context)
+ return False
+
+ def load_all_async (self):
+ """
+ Same as load_all() except the loading is done in a separate thread.
+ """
+ self.task_queue.put(self.load_all)
+
+ def load_async (self, filename):
+ """
+ Invokes load() in a new thread.
+ """
+ self.task_queue.put( lambda: self.load(filename) )
+
+ def initialize_module_async (self, context):
+ """
+ Invokes initialize_module in a new thread.
+ """
+ self.task_queue.put( lambda: self.initialize_module(context) )
+
+ def stop_module_async (self, context):
+ """
+ Invokes stop_module in a new thread.
+ """
+ self.task_queue.put( lambda: self.stop_module(context) )
+
+def toggle_module (sender, context, ml):
+ """Test function"""
+ if (context.enabled):
+ ml.stop_module_async (context)
+ else:
+ ml.initialize_module_async (context)
+
if __name__ == "__main__":
- """A test suite for the Module* classes. Run from top level dir,
+
+ """A test suite for the Module* classes. Run from top level dir,
ie. from deskbar-applet/ run 'python deskbar/module_list.py'."""
+ gtk.threads_init()
+
name = join(dirname(__file__), '..')
print 'Changing PYTHONPATH'
sys.path.insert(0, abspath(name))
l = ModuleList ()
- ml = ModuleLoader (l, ["deskbar/handlers"], ".py")
+ ml = ModuleLoader (["deskbar/handlers"], ".py")
+ ml.connect ("module-loaded", l.update_row_cb)
+ ml.connect ("module-initialized", l.module_toggled_cb)
+ ml.connect ("module-stopped", l.module_toggled_cb)
# Load all or just the directories handler. Uncomment to your liking
- #ml.load_all ()
- ml.load (abspath(expanduser("deskbar/handlers/directories.py")))
+ ml.load_all_async ()
+ #ml.load_async (abspath(expanduser("deskbar/testmod.py")))
+
lw = ModuleListView (l, [ModuleList.FILENAME_COL, ModuleList.NAME_COL, ModuleList.ENABLED_COL])
+ lw.connect ("row-toggled", toggle_module, ml)
+
win = gtk.Window ()
win.connect ("destroy", gtk.main_quit)
win.add (lw)
win.show ()
lw.show ()
-
+
+ gtk.threads_enter()
gtk.main ()
+ gtk.threads_leave()
Index: deskbar/handlers/epiphany.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/handlers/epiphany.py,v
retrieving revision 1.13
diff -u -p -r1.13 epiphany.py
--- deskbar/handlers/epiphany.py 10 Oct 2005 18:15:18 -0000 1.13
+++ deskbar/handlers/epiphany.py 10 Oct 2005 21:30:07 -0000
@@ -50,7 +50,8 @@ class EpiphanySmartMatch(EpiphanyMatch):
class EpiphanyHandler(deskbar.handler.Handler):
def __init__(self):
deskbar.handler.Handler.__init__(self, "web-bookmark.png")
-
+
+ def initialize(self):
parser = EpiphanyBookmarksParser(self)
self._indexer = parser.get_indexer()
self._smart_bookmarks = parser.get_smart_bookmarks()
Index: deskbar/handlers/galago.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/handlers/galago.py,v
retrieving revision 1.2
diff -u -p -r1.2 galago.py
--- deskbar/handlers/galago.py 6 Oct 2005 14:01:06 -0000 1.2
+++ deskbar/handlers/galago.py 10 Oct 2005 21:30:07 -0000
@@ -4,8 +4,12 @@ import gnomevfs
import deskbar, deskbar.indexer
import deskbar.handler
-EXPORTED_CLASS = "GalagoHandler"
-NAME = _("Email and Address Book")
+#EXPORTED_CLASS = "GalagoHandler"
+#NAME = _("Email and Address Book")
+
+# FIXME: Waiting for python bindings of galago.
+EXPORTED_CLASS = None
+NAME = "Waiting for python bindings of galago. Should allow to send IM by typing name."
PRIORITY = 150
@@ -22,18 +26,17 @@ class GalagoMatch(deskbar.handler.Match)
def get_verb(self):
return _("Send Email to <b>%(name)s</b>")
-
-
class GalagoHandler(deskbar.handler.Handler):
def __init__(self):
- deskbar.handler.Handler.__init__(self, "mail.png")
-
+ deskbar.handler.Handler.__init__(self, "mail.png")
self._indexer = deskbar.indexer.Index()
-
+
+ def initialize(self):
# FIXME: Dummy entries
- self._indexer.add("William Gates III <billg microsoft com>", GalagoMatch(self, "William Gates III", "billg microsoft com"))
- self._indexer.add("Steve Ballmer <steve microsoft com>", GalagoMatch(self, "Steve Ballmer", "steve microsoft com"))
- self._indexer.add("Bill Joy <bjoy sun com>", GalagoMatch(self, "Bill Joy", "bjoy sun com"))
+ #self._indexer.add("William Gates III <billg microsoft com>", GalagoMatch(self, "William Gates III", "billg microsoft com"))
+ #self._indexer.add("Steve Ballmer <steve microsoft com>", GalagoMatch(self, "Steve Ballmer", "steve microsoft com"))
+ #self._indexer.add("Bill Joy <bjoy sun com>", GalagoMatch(self, "Bill Joy", "bjoy sun com"))
+ pass
def get_priority(self):
return PRIORITY
Index: deskbar/handlers/gtkbookmarks.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/handlers/gtkbookmarks.py,v
retrieving revision 1.4
diff -u -p -r1.4 gtkbookmarks.py
--- deskbar/handlers/gtkbookmarks.py 6 Oct 2005 14:01:06 -0000 1.4
+++ deskbar/handlers/gtkbookmarks.py 10 Oct 2005 21:30:07 -0000
@@ -27,10 +27,10 @@ class GtkBookmarkHandler(deskbar.handler
def __init__(self):
deskbar.handler.Handler.__init__(self, "folder-bookmark.png")
- print 'Starting .gtkbookmarks file indexation'
self._locations = {}
+
+ def initialize(self):
self._scan_bookmarks_files()
- print '\tDone !'
def get_priority(self):
return PRIORITY
Index: deskbar/handlers/mozilla.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/handlers/mozilla.py,v
retrieving revision 1.13
diff -u -p -r1.13 mozilla.py
--- deskbar/handlers/mozilla.py 10 Oct 2005 18:15:18 -0000 1.13
+++ deskbar/handlers/mozilla.py 10 Oct 2005 21:30:07 -0000
@@ -66,6 +66,10 @@ class MozillaHandler(deskbar.handler.Han
def __init__(self):
deskbar.handler.Handler.__init__(self, "web-bookmark.png")
+ self._indexer = None
+ self._smart_bookmarks = None
+
+ def initialize(self):
parser = MozillaBookmarksParser(self)
self._indexer = parser.get_indexer()
@@ -108,13 +112,11 @@ class MozillaBookmarksParser(HTMLParser.
self._indexer = deskbar.indexer.Index()
- print 'Starting mozilla/ff bookmarks indexation'
if USING_FIREFOX:
self._index_firefox()
else:
self._index_mozilla()
self.close()
- print '\tDone !'
def get_indexer(self):
"""
Index: deskbar/handlers/networkplaces.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/handlers/networkplaces.py,v
retrieving revision 1.1
diff -u -p -r1.1 networkplaces.py
--- deskbar/handlers/networkplaces.py 7 Oct 2005 19:54:45 -0000 1.1
+++ deskbar/handlers/networkplaces.py 10 Oct 2005 21:30:07 -0000
@@ -8,7 +8,6 @@ NAME = _("Network Places")
PRIORITY = 150
-GCONF_CLIENT = gconf.client_get_default()
NETWORK_PLACES_GCONF = '/desktop/gnome/connected_servers'
icon_theme = gtk.icon_theme_get_default()
@@ -30,10 +29,10 @@ class NetworkPlacesHandler(deskbar.handl
def __init__(self):
deskbar.handler.Handler.__init__(self, "folder-bookmark.png")
- print 'Starting Network places file indexation'
self._indexer = deskbar.indexer.Index()
+
+ def initialize(self):
self._scan_network_places()
- print '\tDone !'
def get_priority(self):
return PRIORITY
@@ -42,18 +41,19 @@ class NetworkPlacesHandler(deskbar.handl
return self._indexer.look_up(query)[:max]
def _scan_network_places(self):
- if not GCONF_CLIENT.dir_exists(NETWORK_PLACES_GCONF):
+ client = gconf.client_get_default()
+ if not client.dir_exists(NETWORK_PLACES_GCONF):
return
-
- dirs = GCONF_CLIENT.all_dirs(NETWORK_PLACES_GCONF)
+
+ dirs = client.all_dirs(NETWORK_PLACES_GCONF)
for place in dirs:
try:
- name = GCONF_CLIENT.get_string(place+"/display_name")
- uri = GCONF_CLIENT.get_string(place+"/uri")
+ name = client.get_string(place+"/display_name")
+ uri = client.get_string(place+"/uri")
pixbuf = None
try:
- icon = GCONF_CLIENT.get_string(place+"/icon")
+ icon = client.get_string(place+"/icon")
pixbuf = icon_theme.load_icon(icon, deskbar.ICON_SIZE, gtk.ICON_LOOKUP_USE_BUILTIN)
except Exception, msg:
print 'Error:_scan_network_places:Cannot retreive icon:', msg
Index: deskbar/handlers/pathprograms.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/handlers/pathprograms.py,v
retrieving revision 1.2
diff -u -p -r1.2 pathprograms.py
--- deskbar/handlers/pathprograms.py 9 Oct 2005 20:14:21 -0000 1.2
+++ deskbar/handlers/pathprograms.py 10 Oct 2005 21:30:07 -0000
@@ -37,10 +37,10 @@ class PathProgramsHandler(deskbar.handle
deskbar.handler.Handler.__init__(self, "generic.png")
self._programs = {}
- print 'Starting PATH programs indexation'
+
+ def initialize(self):
self._desktop_programs = self._scan_desktop_files()
self._scan_path()
- print '\tDone !'
def get_priority(self):
return PRIORITY
Index: deskbar/handlers/programs.py
===================================================================
RCS file: /cvs/gnome/deskbar-applet/deskbar/handlers/programs.py,v
retrieving revision 1.13
diff -u -p -r1.13 programs.py
--- deskbar/handlers/programs.py 9 Oct 2005 23:38:46 -0000 1.13
+++ deskbar/handlers/programs.py 10 Oct 2005 21:30:07 -0000
@@ -76,9 +76,9 @@ class ProgramsHandler(deskbar.handler.Ha
deskbar.handler.Handler.__init__(self, "generic.png")
self._indexer = deskbar.indexer.Index()
- print 'Starting .desktop file indexation'
+
+ def initialize(self):
self._scan_desktop_files()
- print '\tDone !'
def get_priority(self):
return PRIORITY
diff -ruN ../deskbar-applet/deskbar/module_list.py deskbar/module_list.py
--- ../deskbar-applet/deskbar/module_list.py 2005-10-11 09:25:26.506141800 +0200
+++ deskbar/module_list.py 2005-10-11 09:27:56.511337544 +0200
@@ -239,6 +239,8 @@
"module-stopped" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_PYOBJECT])
}
+ SIGNAL_PRIORITY = gobject.PRIORITY_DEFAULT_IDLE
+
def __init__ (self, dirs, extension=".py"):
"""
dirs: A list of directories to search. Relative pathnames and paths
@@ -353,7 +355,7 @@
context = ModuleContext (mod_instance.get_icon(), False, mod_instance,
None, filename, mod.NAME, mod.EXPORTED_CLASS)
- gobject.idle_add (self.__emit_module_loaded, context, priority=gobject.PRIORITY_DEFAULT)
+ gobject.idle_add (self.__emit_module_loaded, context, priority=self.SIGNAL_PRIORITY)
print "Loaded module '%s' from file %s." % (mod.NAME, filename)
return context
@@ -372,7 +374,7 @@
context.module.initialize ()
context.enabled = True
- gobject.idle_add (self.__emit_module_initialized, context, priority=gobject.PRIORITY_DEFAULT)
+ gobject.idle_add (self.__emit_module_initialized, context, priority=self.SIGNAL_PRIORITY)
def initialize_module_async_cb (self, sender, context):
"""
@@ -399,7 +401,7 @@
context.enabled = False
context.module = None
- gobject.idle_add (self.__emit_module_stopped, context, priority=gobject.PRIORITY_DEFAULT)
+ gobject.idle_add (self.__emit_module_stopped, context, priority=self.SIGNAL_PRIORITY)
def __emit_module_stopped (self, context):
"""Idle method. Internal use only."""
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]