conduit r1883 - in trunk: . conduit conduit/dataproviders conduit/gtkui conduit/modules/FspotModule



Author: arosenfeld
Date: Fri Feb 20 07:18:32 2009
New Revision: 1883
URL: http://svn.gnome.org/viewvc/conduit?rev=1883&view=rev

Log:
2009-02-20  Alexandre Rosenfeld  <airmind gmail com>

	* conduit/Configurator.py:
	Config Show/Hide signals.
	* conduit/dataproviders/DataProvider.py:
	Config Show/Hide signals.
	* conduit/gtkui/ConfigContainer.py:
	Tried to made it more clear why a configuration 
	might fail when the configuration value was not 
	found
	* conduit/gtkui/ConfigItems.py:
	Fixes and improved documentation.
	Changed ConfigItem to ItemBase.
	Improved public API and pythonic properties (much
	better documented in ItemBase).
	Private API for ItemBase subclasses, allows much 
	better error handler for public API.
	Reverted "list" fix.
	* conduit/modules/FspotModule/FspotModule.py:
	Fixed a few remaining config bugs (make use of
	some changes to ConfigItems, including the better
	public API).
	Improved dialog when F-Spot is not running (tags
	are still visible, but disabled).


Modified:
   trunk/ChangeLog
   trunk/conduit/Configurator.py
   trunk/conduit/dataproviders/DataProvider.py
   trunk/conduit/gtkui/ConfigContainer.py
   trunk/conduit/gtkui/ConfigItems.py
   trunk/conduit/modules/FspotModule/FspotModule.py

Modified: trunk/conduit/Configurator.py
==============================================================================
--- trunk/conduit/Configurator.py	(original)
+++ trunk/conduit/Configurator.py	Fri Feb 20 07:18:32 2009
@@ -24,6 +24,8 @@
         'changed': (gobject.SIGNAL_RUN_FIRST, None, [bool]),
         'apply': (gobject.SIGNAL_RUN_FIRST, None, []),
         'cancel': (gobject.SIGNAL_RUN_FIRST, None, []),
+        'show': (gobject.SIGNAL_RUN_FIRST, None, []),
+        'hide': (gobject.SIGNAL_RUN_FIRST, None, []),
     }
     
     def __init__(self, dataprovider, configurator):
@@ -62,12 +64,14 @@
         '''
         Show the configuration widget
         '''
+        self.emit('show')
         self.showing = True
         
     def hide(self):
         '''
         Hide the configuration widget
         '''
+        self.emit('hide')
         self.showing = False
         
     def apply_config(self):

Modified: trunk/conduit/dataproviders/DataProvider.py
==============================================================================
--- trunk/conduit/dataproviders/DataProvider.py	(original)
+++ trunk/conduit/dataproviders/DataProvider.py	Fri Feb 20 07:18:32 2009
@@ -180,21 +180,39 @@
         if hasattr(self, "configure"):
             return None
         if not self.config_container:
+            #FIXME: GtkUI is hard-coded because we dont have another interface
+            # yet, but we could make it more modular (we already import it here
+            # not to depend on it on initialization)
             import conduit.gtkui.ConfigContainer as ConfigContainer
             self.config_container = ConfigContainer.ConfigContainer(self, configurator)
             self.config_container.connect('apply', self.config_apply)
             self.config_container.connect('cancel', self.config_cancel)
+            self.config_container.connect('show', self.config_show)
+            self.config_container.connect('hide', self.config_hide)
             self.config_setup(self.config_container)
             #FIXME: This is definetely just for debugging (it prints everything
             # that is changed in the configuration dialog)
-            def print_item(config, item):
-                log.debug("%s: %s = %s" % (item.title, item.config_name, item.get_value()))
-            self.config_container.connect("item-changed", print_item)
+            #def print_item(config, item):
+            #    log.debug("%s: %s = %s" % (item.title, item.config_name, item.get_value()))
+            #self.config_container.connect("item-changed", print_item)
         return self.config_container
     
     def config_setup(self, config_container):
         '''
-        Configures the configuration controller, to show the controls needed
+        Called when the configuration container was just built. Should be 
+        implemented by subclasses that want to show their own configuration.
+        '''
+        pass
+    
+    def config_show(self, config_container):
+        '''
+        Called when the configuration is about to be shown
+        '''
+        pass
+    
+    def config_hide(self, config_container):
+        '''
+        Called when the configuration is about to be hidden
         '''
         pass
         
@@ -285,11 +303,15 @@
         for name, default, setter, getter in self._get_configuration_parameters(configuration):
             if hasattr(self, name) and callable(getattr(self, name)):
                 continue            
-            if not config or (name in config):                
-                #if not klass:
-                klass = default.__class__
+            if not config or (name in config):
+                klass = None
+                if default is not None:            
+                    klass = default.__class__
                 if config:
-                    value = klass(config[name])
+                    value = config[name]
+                    #FIXME: Wrap a try/except clause with logging
+                    if klass:
+                        value = klass(value)
                 else:
                     value = default
                 if setter:

Modified: trunk/conduit/gtkui/ConfigContainer.py
==============================================================================
--- trunk/conduit/gtkui/ConfigContainer.py	(original)
+++ trunk/conduit/gtkui/ConfigContainer.py	Fri Feb 20 07:18:32 2009
@@ -134,7 +134,7 @@
     
     def add_item(self, title, kind, order = 0, **kwargs):
         '''
-        Add a configuration widget. Returns the Item object.
+        Add a configuration item. Returns the Item object.
         
         You can pass properties to the configuration item in kwargs.
         '''
@@ -147,16 +147,18 @@
                 self.config_values = self.dataprovider.get_configuration()
             else:
                 self.config_values = {}        
-        if 'config_name' in kwargs:
+        if kwargs.get('config_name', None):
             if kwargs['config_name'] in self.config_values:
                 kwargs['initial_value'] = self.config_values[kwargs['config_name']]
+            else:
+                raise Error("Value for %s (configuration item %s) not found in dataprovider" % (kwargs['config_name'], title))
         if 'enabled' not in kwargs:
             kwargs['enabled'] = self.section.enabled
-        try:    
-            item_cls = ConfigItems.ConfigItem.items[kind]
+        try:
+            item_cls = ConfigItems.ItemBase.items[kind]
         except KeyError:
             raise Error("Config kind %s not found" % kind)
-        item = item_cls(self, title, order, **kwargs)
+        item = item_cls(container = self, title = title, order = order, **kwargs)
         item.connect("value-changed", self._item_changed)
         self.items.append(item)
         self.section.add_item(item)

Modified: trunk/conduit/gtkui/ConfigItems.py
==============================================================================
--- trunk/conduit/gtkui/ConfigItems.py	(original)
+++ trunk/conduit/gtkui/ConfigItems.py	Fri Feb 20 07:18:32 2009
@@ -15,8 +15,12 @@
 from gettext import gettext as _ 
 import conduit
 
+class Error(Exception):
+    """Base exception for all exceptions raised in this module."""
+    pass
+
 class Section(gobject.GObject):
-    def __init__(self, container, title, order, **kwargs):
+    def __init__(self, container, title, order, enabled = True):
         '''
         A section containing items and a title
         '''
@@ -25,13 +29,13 @@
         self.title = title
         self.order = order
         self.items = []
-        self.__enabled = kwargs.get('enabled', True)
+        self.__enabled = enabled
     
     def add_item(self, item):
         ''' Adds an item to this section (this does not update the dialog) '''
         self.items.append(item)
     
-    def attach(self, table, row):        
+    def attach(self, table, row):
         if self.title:
             row += 1
             label = gtk.Label("<b>%s</b>" % (self.title))
@@ -60,6 +64,8 @@
 class ItemMeta(gobject.GObjectMeta):
     '''
     Meta class to automatically register item classes.
+    
+    Based on http://www.djangosnippets.org/snippets/542/
     '''
     def __init__(cls, name, bases, attrs):
         gobject.GObjectMeta.__init__(cls, name, bases, attrs)
@@ -76,80 +82,94 @@
             if hasattr(cls, '__item_name__'):
                 cls.items[cls.__item_name__] = cls
         
-class ConfigItem(gobject.GObject):
+class ItemBase(gobject.GObject):
     '''
     A config item is basically a wrapper to a widget.
     
     It works by exposing a value that should be translated to the underlying
     widget. The type of the value depends on the item.
-
-    A few properties are supported to customize their behaviours. They
-    should be passed in ``kwargs``:
-        :config_name: Used to save/load the configuration value from the 
-            dataprovider.
-        :config_type: ``function(value)`` that converts the config value into
-            something a dataprovider will accept. This could be something 
-            like int, str, etc., or a custom function.
-        :initial_value: When the item is created or cancel is called,
-            the item returns to this value. Changes to the current value
-            when apply is called.
-        :initial_value_callback: It's a function that should return a value
-            to initial value, called when the item is created or when cancel
-            is called. It is especially useful for items that keep their state
-            somewhere else.
-        :choices: Valid when the user needs to select a value from a list.
-            It has to be a tuple with ``(value, label)``.
-        :needs_label: If True, the widget will have a label with title as
-            the text.
-        :needs_space: If ``needs_label`` is False, but the widget still wants 
-            to be aligned to the right in the window, set this to True.
-        :enabled: If the widget can be edited by the user.
-        :save_callback: A ``function(item, value)`` called when apply is 
-    selected and the value must be saved.
-        
+    
+    Subclasses should implement _build_widget, _set_value and _get_value.
+    If they include choices, they also must implement _set_choices or 
+    _clear_choices and _build_choices.
+    
     Signals emitted:
-        :value-changed: Emitted everytime the value changes, emitting two 
-            parameters, the first is a bool wheter the value is the same
-            as the initial value, and the second is the value.
+        :value-changed: Emitted everytime the value changes. It's signature
+            is ``function(is_initial_value, value)`` or 
+            ``method(self, is_initial_value, value)``. See the is_initial_value
+            function below.
     '''
-    
-    #Automatically registers item descendants
     __metaclass__ = ItemMeta
     
     __gsignals__ = {
         'value-changed' : (gobject.SIGNAL_RUN_FIRST, None, [bool, object]),
-        #'reset' : (gobject.SIGNAL_RUN_LAST, None, []),
-        #'initial-state' : (gobject.SIGNAL_RUN_FIRST, None, []),
     }
     
     def __init__(self, container, title, order, config_name=None, 
-            config_type=None, choices=None, needs_label=True, 
+            config_type=None, choices=[], needs_label=True, 
             needs_space=False, initial_value=None, initial_value_callback=None,
-            save_callback=None, fill=False, enabled=True, **kwargs):
+            save_callback=None, fill=False, enabled=True):
         '''
-        Create a config item.
+        Creates a config item.
+        
+        The parameters can customize how the item behaves:
+        @param config_name: Used to save/load the configuration value from the 
+            dataprovider.
+        @param config_type: ``function(value)`` that converts the config value into
+            something a dataprovider will accept. This could be something 
+            like int, str, etc., or a custom function.
+        @param initial_value: When the item is created or cancel is called,
+            the item returns to this value. Changes to the current value
+            when apply is called.
+        @param initial_value_callback: It's a function that should return a value
+            to initial value, called when the item is created or when cancel
+            is called. It is especially useful for items that keep their state
+            somewhere else.
+        @param choices: Valid when the user needs to select a value from a list.
+            It has to be a tuple with ``(value, label)``.
+        @param needs_label: If True, the widget will have a label with title as
+            the text. Items such as list sets this to False.
+        @param needs_space: If ``needs_label`` is False, but the widget still wants 
+            to be aligned to the right in the window, set this to True.
+        @param enabled: If the widget can be edited by the user.
+        @param save_callback: A ``function(item, value)`` called when apply is 
+            selected and the value must be saved.        
         '''
         gobject.GObject.__init__(self)
+        
+        # These properties should not be changed
         self.container = container
+        self.read_only = False
+        
+        # Properties that take in effect while the configuration is running
+        # Access then using with their public attributes (as implemented
+        # with properties below), such as ``item.enabled = False``
         self.__widget = None
         self.__label = None
+        self.__enabled = enabled
+        self.__choices = choices
+
+        # These properties do not need any special processing when changed, 
+        # they can probably be directly assigned to another value
+        self.config_name = config_name 
+        self.config_type = config_type 
+        self.save_callback = save_callback 
+        self.initial_value = initial_value 
+        self.initial_value_callback = initial_value_callback
+        
+        # These properties takes no effect while the configuration is running,
+        # unless the widgets are rebuilt (there are no provisions to make that
+        # happen at the moment)
         self.title = title
         self.order = order
-        self.__choices = choices #kwargs.get('choices', None)
-        self.needs_label = needs_label #kwargs.get('needs_label', True)
-        self.needs_space = needs_space #kwargs.get('needs_space', False)
-        self.config_name = config_name #kwargs.get('config_name', None)
-        self.config_type = config_type #kwargs.get('config_type', None)
-        self.save_callback = save_callback #kwargs.get('save_callback', None)
-        self.initial_value = initial_value #kwargs.get('initial_value', None)
-        self.initial_value_callback = initial_value_callback #kwargs.get('initial_value_callback', None)
-        
-        self.fill = fill #kwargs.get('fill', False)
-        self.enabled = enabled #kwargs.get('enabled', True)
-
+        self.needs_label = needs_label
+        self.needs_space = needs_space
+        self.fill = fill
+    
     def _value_changed(self, *args):
         '''
-        Called everytime the value changes. Emits the value-changed signal.
+        Should be called everytime the value changes. Emits the value-changed 
+        signal.
         
         This method can be chained into widget signals. It will safely ignore
         any argument passed to it.
@@ -161,8 +181,6 @@
     def _build_choices(self):
         '''
         Implement this when you need to build the choices of a widget.
-        It's usually called on build_widget. If implemented together with 
-        _clear_choices, there is no need to implement set_choices
         '''
         pass
     
@@ -172,64 +190,60 @@
         '''
         pass
     
-    choices = property(lambda self: self.__choices, 
-                       lambda self, v: self.set_choices(v))
-
-    def set_choices(self, choices):
+    def _set_choices(self, choices):
         '''
-        Set choices and automatically rebuild a widget containing a set of choices.
+        Should set choices and reassign it's old value.
+        
+        Subclasses do not need no implement this, they should implement 
+        _build_choices and _clear_choices. If they do implement it, they should
+        not call this method, unless they know what they are doing.
         '''
         value = self.get_value()
         self.__choices = choices
         self._clear_choices()
         self._build_choices()
         self.set_value(value)
-    
-    def is_initial_value(self):
-        '''
-        Returns True if the current value is the initial value.
-        '''
-        return self.initial_value == self.value
-        
-    def set_enabled(self, enabled):
-        '''
-        Set the widget sensibility.
-        '''
-        self.enabled = enabled
-        if self.widget:
-            self.widget.set_sensitive(enabled)
-        
-    def reset(self):
+
+    def set_choices(self, choices):
         '''
-        Resets the widget to it's initial value.
+        Set the choices and recovers the old state if possible.
         '''
-        if self.__widget:
-            #self.emit('reset')
-            self.widget.set_sensitive(self.enabled)            
-            if self.initial_value_callback:
-                self.initial_value = self.initial_value_callback()
-            self.value = self.initial_value
+        self._set_choices(choices)
     
-    def save_state(self):
-        '''
-        Save the current value as the initial value.
-        '''
-        value = self.get_value()
-        self.initial_value = value
-        if self.save_callback:
-            self.save_callback(self, value)
+    choices = property(lambda self: self.__choices, set_choices)
         
-    def apply(self):
+    def _attach(self, table, row, in_section):
         '''
-        Save the current value as the initial value and calls the dataprovider
-        to save the current value
+        Attach this item's widget to a table.
         '''
-        self.save_state()
-        self.container.apply_config([self])
+        widget = self.get_widget()
+        label = self.get_label()
+        row += 1
+        table.resize(row, 2)
+        align = gtk.Alignment(0.5, 0.5, 1.0, 1.0)
+        if in_section:
+            align.set_padding(0, 0, 12, 0)
+        #FIXME: This would allow the configurator widget to shrink more then
+        # it's original size. It might be useful for PaneConfigurator, but
+        # it feels weird. And it screws the size requisition, so it's smaller
+        # then it should be.
+        #if label:
+        #    label.set_ellipsize(pango.ELLIPSIZE_END)
+        right_align = label or self.needs_space
+        if self.fill:
+            yoptions = gtk.FILL | gtk.EXPAND
+        else:
+            yoptions = 0
+        if right_align:
+            if label:
+                align.add(label)
+                table.attach(align, 0, 1, row - 1, row, xoptions = gtk.SHRINK | gtk.FILL, yoptions = 0)
+            table.attach(widget, 1, 2, row - 1, row, xoptions = gtk.FILL | gtk.EXPAND, yoptions = yoptions)
+        else:
+            align.add(widget)
+            table.attach(align, 0, 2, row - 1, row, xoptions = gtk.FILL | gtk.EXPAND, yoptions = yoptions)    
+        return row        
 
-    label = property(lambda self: self.get_label(), 
-                     lambda self, v: self.set_label(v))        
-                
     def get_label(self):
         '''
         Returns the gtk.Label to this item (if needed)
@@ -247,16 +261,18 @@
         Sets the label widget
         '''
         self.__label = label        
+        
+    label = property(lambda self: self.get_label(), 
+                     lambda self, v: self.set_label(v))        
 
-    widget = property(lambda self: self.get_widget(), 
-                      lambda self, v: self.set_widget(v))        
-    
     def get_widget(self):
         '''
         Return the widget, building it as needed.
         '''
         if not self.__widget:
-            self.build_widget()
+            self._build_widget()
+            if not self.__widget:
+                raise Error("Widget could not be built")            
             self.reset()
         return self.__widget
 
@@ -265,17 +281,53 @@
         Sets the widget
         '''
         self.__widget = widget
+        
+    widget = property(lambda self: self.get_widget(), 
+                      lambda self, v: self.set_widget(v))
 
-    def build_widget(self):
+    def get_value(self):
         '''
-        Implement this to build the underlying widget as needed. Has to assign
-        the widget to self.widget (no need to return it)
+        Gets the value from the widget. If the widget does not exist yet
+        (the container was not built) the initial_value is returned instead.
+        
+        This is a public interface method, should not be overriden by 
+        descendants. Implement _get_value instead.
+        
+        Note that this method is expected to be cheap. Take care of not having
+        heavy processing in this method. 
+        It is called every time the user changes the value.
         '''
-        pass
+        if not self.__widget:
+            return self.initial_value
+        return self._get_value()
+    
+    def set_value(self, value):
+        '''
+        Sets the value of the widget.
+        
+        This is a public interface method, should not be overriden by 
+        descendants. Implement _set_value instead.
+        '''
+        #FIXME: We should probably check for exceptions here, to avoid not 
+        # showing the configuration dialog because a value was invalid,
+        # which could occur with invalid config values.
+        # We should probably assign the initial value here in case of an 
+        # Exception. In case of another Exception, then it's the module fault,
+        # and no exception handling should be done.        
+        self.initial_value = value
+        if not self.__widget:
+            return
+        self._set_value(value)
+
+    #Set value as a property
+    value = property(get_value, set_value)
     
     def get_config_value(self):
         '''
-        Return a value suitable to a dataprovider
+        Returns a dict suitable to a dataprovider set_configuration.
+        
+        Returning a dict allows subclasses to provide more then one configuration
+        value if needed.
         '''
         if not self.config_name:
             return None
@@ -287,83 +339,68 @@
             log.warning("Value %s could not be translated with %s" % (value, self.config_type))
             #raise TypeError()
         else:
-            return {self.config_name: value}
-    
-    #Set value as a property and make sure get_value and set_value can be 
-    #overriden in descendants without conflicting with this property
-    value = property(lambda self: self.get_value(),
-                     lambda self, v: self.set_value(v))
+            return {self.config_name: value}      
         
-    def get_value(self):
+    def is_initial_value(self):
         '''
-        Gets the value from the widget.
+        Returns True if the current value is the initial value.
+        '''
+        return self.initial_value == self.value
         
-        This is a public interface method, should not be overriden by 
-        descendants. Implement _get_value instead.
+    def _set_enabled(self, enabled):
+        self.widget.set_sensitive(enabled)
+        
+    def set_enabled(self, enabled):
         '''
-        return self._get_value()
-    
-    def set_value(self, value):
+        Set the widget sensibility.
         '''
-        Sets the value from the widget.
+        self.__enabled = enabled
+        if self.__widget:
+            self._set_enabled(enabled)
+            #self.widget.set_sensitive(enabled)
+    
+    enabled = property(lambda self: self.__enabled, lambda self, enabled: self.set_enabled(enabled))
         
-        This is a public interface method, should not be overriden by 
-        descendants. Implement _set_value instead.
+    def reset(self):
         '''
-        #FIXME: We should probably check for exceptions here, to avoid not 
-        # showing the configuration dialog because a value was invalid,
-        # which could occur with invalid config values.
-        # We should probably assign the initial value here in case of an 
-        # Exception. In case of another Exception, then it's the module fault,
-        # and no exception handling should be done.
-        self._set_value(value)
-
-    def _attach(self, table, row, in_section):
+        Resets the widget to it's initial value.
         '''
-        Attach this item's widget to a table.
+        if self.__widget:
+            #self.emit('reset')
+            #self.widget.set_sensitive(self.enabled)
+            self.set_enabled(self.enabled)
+            if self.initial_value_callback:
+                self.initial_value = self.initial_value_callback()
+            self.value = self.initial_value
+    
+    def save_state(self):
         '''
-        widget = self.get_widget()
-        label = self.get_label()
-        row += 1
-        table.resize(row, 2)
-        align = gtk.Alignment(0.5, 0.5, 1.0, 1.0)
-        if in_section:
-            align.set_padding(0, 0, 12, 0)
-        #if label:
-        #    label.set_ellipsize(pango.ELLIPSIZE_END)
-        right_align = label or self.needs_space
-        if self.fill:
-            yoptions = gtk.FILL | gtk.EXPAND
-        else:
-            yoptions = 0
-        if right_align:
-            if label:
-                align.add(label)
-                table.attach(align, 0, 1, row - 1, row, xoptions = gtk.SHRINK | gtk.FILL, yoptions = 0)
-            table.attach(widget, 1, 2, row - 1, row, xoptions = gtk.FILL | gtk.EXPAND, yoptions = yoptions)
-        else:
-            align.add(widget)
-            table.attach(align, 0, 2, row - 1, row, xoptions = gtk.FILL | gtk.EXPAND, yoptions = yoptions)    
-        return row
-
-#FIXME: Finish the CustomWidget implementation (if we need the custom widget support)
-#class ConfigCustomWidget(ConfigItem):
-#    def _get_value(self):
-#        return self.load_callback(self.widget)
-#    
-#    def _set_value(self, value):
-#        self.widget_callback(self.widget)
+        Seve the current value as the initial value.
+        '''
+        value = self.get_value()
+        self.initial_value = value
+        if self.save_callback:
+            self.save_callback(self, value)
+        
+    def apply(self):
+        '''
+        Seve the current value as the initial value and calls the dataprovider
+        to save the current value.
+        '''
+        self.save_state()
+        self.container.apply_config([self])          
 
-class ConfigLabel(ConfigItem):
+class ConfigLabel(ItemBase):
     __item_name__ = 'label'
     
-    def __init__(self, *args, **kwargs):
-        ConfigItem.__init__(self, *args, **kwargs)
-        self.xalignment = kwargs.get('xalignment', 0.0)
-        self.yalignment = kwargs.get('yalignment', 0.5)
-        self.use_markup = kwargs.get('use_markup', False)
+    def __init__(self, xalignment = 0.0, yalignment = 0.5, use_markup = False, **kwargs):
+        ItemBase.__init__(self, **kwargs)
+        self.xalignment = xalignment #kwargs.get('xalignment', 0.0)
+        self.yalignment = yalignment #kwargs.get('yalignment', 0.5)
+        self.use_markup = use_markup #kwargs.get('use_markup', False)
+        self.read_only = True
     
-    def build_widget(self):
+    def _build_widget(self):
         self.widget = gtk.Label()
         self.widget.set_alignment(self.xalignment, self.yalignment)
         self.widget.set_use_markup(self.use_markup)
@@ -377,19 +414,21 @@
         else:
             self.widget.set_text(str(value))
             
-class ConfigButton(ConfigItem):
+class ConfigButton(ItemBase):
     __item_name__ = 'button'
     
     def __init__(self, *args, **kwargs):
-        ConfigItem.__init__(self, *args, **kwargs)
+        action = kwargs.pop('action', None)
+        ItemBase.__init__(self, *args, **kwargs)
         self.callback_id = None
         self.callback = None
         self.needs_space = kwargs.get('needs_space', True)
         self.needs_label = kwargs.get('needs_label', False)
-        if 'action' in kwargs:
-            self.initial_value = kwargs['action']
+        if action:
+            self.initial_value = action
+        self.read_only = True
     
-    def build_widget(self):
+    def _build_widget(self):
         self.widget = gtk.Button(self.title)
         
     def _set_value(self, value):
@@ -406,12 +445,12 @@
     def _get_value(self):
         return self.callback
     
-class ConfigFileButton(ConfigItem):
+class ConfigFileButton(ItemBase):
     __item_name__ = 'filebutton'
     
     def __init__(self, *args, **kwargs):
-        ConfigItem.__init__(self, *args, **kwargs)        
-        self.directory = kwargs.get('directory', False)
+        self.directory = kwargs.pop('directory', False)
+        ItemBase.__init__(self, *args, **kwargs)        
         self._current_filename = None
     
     def _selection_changed(self, filechooser):
@@ -419,7 +458,7 @@
             self._current_filename = filechooser.get_filename()
             self._value_changed()            
     
-    def build_widget(self):
+    def _build_widget(self):
         self.widget = gtk.FileChooserButton(self.title)
         self.widget.connect("selection-changed", self._selection_changed)
         if self.directory:
@@ -433,11 +472,11 @@
     def _get_value(self):
         return self._current_filename
 
-class ConfigRadio(ConfigItem):
+class ConfigRadio(ItemBase):
     __item_name__ = 'radio'
     
     def __init__(self, container, title, order, **kwargs):
-        ConfigItem.__init__(self, container, title, order, **kwargs)
+        ItemBase.__init__(self, container, title, order, **kwargs)
         self.needs_label = title is not None
         self.buttons = {}
         self._active_button = None
@@ -461,7 +500,7 @@
             self.buttons[value] = last_button
             self.widget.pack_start(last_button)
     
-    def build_widget(self):
+    def _build_widget(self):
         self.widget = gtk.VBox()
         self._build_choices()
     
@@ -477,16 +516,16 @@
         else:
             log.warn("Value %s could not be applied to config %s" % (repr(self.title), new_value))
 
-class ConfigSpin(ConfigItem):
+class ConfigSpin(ItemBase):
     __item_name__ = 'spin'
     
     def __init__(self, *args, **kwargs):
-        ConfigItem.__init__(self, *args, **kwargs)
-        self.maximum = kwargs.get('maximum', sys.maxint)
-        self.minimum = kwargs.get('minimum', 0)
-        self.step = kwargs.get('step', 1)
+        self.maximum = kwargs.pop('maximum', sys.maxint)
+        self.minimum = kwargs.pop('minimum', 0)
+        self.step = kwargs.pop('step', 1)        
+        ItemBase.__init__(self, *args, **kwargs)
     
-    def build_widget(self):
+    def _build_widget(self):
         self.adjust = gtk.Adjustment(lower = self.minimum, upper = self.maximum, step_incr = self.step)
         self.widget = gtk.SpinButton(self.adjust)
         self.widget.connect("value-changed", self._value_changed)
@@ -501,7 +540,7 @@
         except:
             log.warn("Value %s could not be applied to config %s" % (repr(self.title), value))        
 
-class ConfigCombo(ConfigItem):
+class ConfigCombo(ItemBase):
     '''
     A box where the user can select one value from several
     
@@ -521,7 +560,7 @@
     def _clear_choices(self):
         self.widget.get_model().clear()
     
-    def build_widget(self):
+    def _build_widget(self):
         self.widget = gtk.combo_box_new_text()
         self._build_choices()
         self.widget.connect("changed", self._value_changed)
@@ -544,7 +583,7 @@
 class ConfigComboText(ConfigCombo):
     __item_name__ = 'combotext'
 
-    def build_widget(self):
+    def _build_widget(self):
         self.widget = gtk.combo_box_entry_new_text()
         self._build_choices()
         self.widget.connect("changed", self._value_changed)
@@ -555,14 +594,14 @@
     def _set_value(self, value):
         self.widget.child.set_text(str(value))
     
-class ConfigText(ConfigItem):
+class ConfigText(ItemBase):
     __item_name__ = 'text'
     
-    def __init__(self, *args, **kwargs):
-        ConfigItem.__init__(self, *args, **kwargs)
-        self.password = kwargs.get('password', False)
+    def __init__(self, password = False, **kwargs):
+        self.password = password #kwargs.pop('password', False)
+        ItemBase.__init__(self, **kwargs)        
     
-    def build_widget(self):
+    def _build_widget(self):
         self.widget = gtk.Entry()
         self.widget.connect("notify::text", self._value_changed)
         self.widget.set_visibility(not self.password)
@@ -573,30 +612,49 @@
     def _set_value(self, value):
         self.widget.set_text(str(value))
             
-class ConfigList(ConfigItem):
+class ConfigList(ItemBase):
     __item_name__ = 'list'
     
     def __init__(self, *args, **kwargs):
-        ConfigItem.__init__(self, *args, **kwargs)
+        ItemBase.__init__(self, *args, **kwargs)
         self.needs_label = kwargs.get('needs_label', False)
-        self.initial_value = sorted(self.initial_value)
+        if self.initial_value:
+            #FIXME: Sorted should be optional
+            try:
+                self.initial_value = sorted(self.initial_value)
+            except TypeError:
+                raise Error("List only supports iterables as value (%s is not)" % (self.initial_value))
         self.fill = kwargs.get('fill', True)
         self._checked_items = None
         self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
     
-    def cellcheck_cb(self, cell, path, model):
+    def _cellcheck_cb(self, cell, path, model):
         model[path][1] = not cell.get_active()
         self._checked_items = None        
         self._value_changed()
     
     def _build_choices(self):
-        for value, label in self.choices:
-            self.model.append((label, value))
+        try:
+            for choice in self.choices:
+                if isinstance(choice, tuple):
+                    if len(choice) != 2:
+                        raise ValueError
+                    value, label = choice
+                else:
+                    label = choice
+                #Set's the list text and initial (unchecked) value, it will be
+                #checked or unchecked later by set_value
+                self.model.append((str(label), False))
+        except (ValueError, TypeError):
+            raise Error("Choices is not valid, it should be a (value, label) list or a list of labels (%s is not)" % self.choices)
 
     def _clear_choices(self):
         self.model.clear()
-        
-    def build_widget(self):
+    
+    def _set_enabled(self, enabled):
+        self.list.set_sensitive(enabled)
+    
+    def _build_widget(self):
         self.vbox = gtk.VBox()
         self.vbox.set_spacing(4)
         self.scrolled_window = gtk.ScrolledWindow()        
@@ -611,16 +669,20 @@
         self.list.set_model(self.model)        
         check_renderer = gtk.CellRendererToggle()
         check_renderer.set_property('activatable', True)
-        check_renderer.connect( 'toggled', self.cellcheck_cb, self.model )
-        self.list.append_column(gtk.TreeViewColumn(_("Enabled"), check_renderer, active=1))                    
-        self.list.append_column(gtk.TreeViewColumn(_("Text"), gtk.CellRendererText(), text=0))     
+        check_renderer.connect( 'toggled', self._cellcheck_cb, self.model )
+        #FIXME: We could probably support more columns, maybe by automatically
+        # detecting if choices include tuples, and which types are inside the 
+        # tuple.
+        self.list.append_column(gtk.TreeViewColumn("Enabled", check_renderer, active=1))                    
+        self.list.append_column(gtk.TreeViewColumn("Label", gtk.CellRendererText(), text=0))   
+        self._clear_choices()  
         self._build_choices()
         self.scrolled_window.add(self.list)
         self.widget = self.vbox
         self.widget.set_size_request(-1, 150)
     
     def _update_total(self):
-        self.total_label.set_text("Total: %d" % len(self._checked_items))
+        self.total_label.set_text(_("Total: %d") % len(self._checked_items))
     
     def _get_value(self):
         if not self._checked_items:
@@ -639,14 +701,14 @@
             log.warn("Value %s could not be applied to config %s" % (value, repr(self.title)))
         self._update_total()
 
-class ConfigCheckBox(ConfigItem):
+class ConfigCheckBox(ItemBase):
     __item_name__ = 'check'
     
     def __init__(self, *args, **kwargs):
-        ConfigItem.__init__(self, *args, **kwargs)
+        ItemBase.__init__(self, *args, **kwargs)
         self.needs_label = False
         
-    def build_widget(self):
+    def _build_widget(self):
         self.widget = gtk.CheckButton()
         self.widget.set_label(self.title)
         self.widget.connect("toggled", self._value_changed)

Modified: trunk/conduit/modules/FspotModule/FspotModule.py
==============================================================================
--- trunk/conduit/modules/FspotModule/FspotModule.py	(original)
+++ trunk/conduit/modules/FspotModule/FspotModule.py	Fri Feb 20 07:18:32 2009
@@ -215,8 +215,8 @@
         self.tag_remote = None
 
     def config_setup(self, config):
-        RUNNING_MESSAGE = "F-Spot is running"
-        STOPPED_MESSAGE = "Please start F-Spot or activate the D-Bus Extension"
+        RUNNING_MESSAGE = _("F-Spot is running")
+        STOPPED_MESSAGE = _("Please start F-Spot or activate the D-Bus Extension")
 
         def start_fspot(button):
             #would be cleaner if we could autostart using dbus,
@@ -228,35 +228,27 @@
 
         def watch(name):
             connected = bool(name and self._connect_to_fspot())
-            if connected:            
-                tags_config.set_choices([(tag, tag) for tag in self._get_all_tags()])
+            start_fspot_config.enabled = not connected
+            tags_config.enabled = connected
+            if connected:
+                tags_config.choices = self._get_all_tags()
             else:
-                tags_config.set_choices([])
-            add_tags_section.set_enabled(connected)
-            if config.showing:
-                if connected:
-                    status_label.set_value(RUNNING_MESSAGE)
-                else:
-                    status_label.set_value(STOPPED_MESSAGE)
-
-        if self._connect_to_fspot():
-            tags = [(tag, tag) for tag in self._get_all_tags()]
-            message = RUNNING_MESSAGE
-        else:
-            tags = []
-            message = STOPPED_MESSAGE
+                tags_config.choices = tags_config.value
+            add_tags_section.enabled = connected            
+            if connected:
+                status_label.value = RUNNING_MESSAGE
+            else:
+                status_label.value = STOPPED_MESSAGE
 
-        status_label = config.add_item("Status", "label",
-            initial_value = message
-        )
-        config.add_item("Start F-Spot", "button",
+        status_label = config.add_item("Status", "label")
+        start_fspot_config = config.add_item("Start F-Spot", "button",
             initial_value = start_fspot
         )
 
         config.add_section("Tags")
         tags_config = config.add_item("Tags", "list",
             config_name = 'tags',
-            choices = tags,
+            choices = self.enabledTags,
         )
 
         def add_tag_cb(button):



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]