conduit r1854 - in trunk: . conduit conduit/dataproviders conduit/gtkui
- From: arosenfeld svn gnome org
- To: svn-commits-list gnome org
- Subject: conduit r1854 - in trunk: . conduit conduit/dataproviders conduit/gtkui
- Date: Tue, 10 Feb 2009 04:03:45 +0000 (UTC)
Author: arosenfeld
Date: Tue Feb 10 04:03:44 2009
New Revision: 1854
URL: http://svn.gnome.org/viewvc/conduit?rev=1854&view=rev
Log:
2009-02-10 Alexandre Rosenfeld <airmind gmail com>
New configuration system commit. Only the core classes are
commited, no dataprovider is changed yet. These will be
commited when ready.
* conduit/Configurator.py:
Base configuration classes
* conduit/Module.py:
Log an error when not able to load a module
* conduit/dataproviders/DataProvider.py:
Added update_configuration as an alternative to
implementing get/set_configuration in data
providers.
Added new configuration system methods
* conduit/gtkui/Canvas.py:
Use the new configuration system if availiable in a data
provider, or logs a critical message if not, and runs
the old configure method.
* conduit/gtkui/ConfigContainer.py:
* conduit/gtkui/ConfigWidgets.py:
* conduit/gtkui/EmbedConfigurator.py:
* conduit/gtkui/WindowConfigurator.py:
New configuration system. Temporarily documented in
http://wiki.arosenfeld.net/Conduit
Added:
trunk/conduit/Configurator.py
trunk/conduit/gtkui/ConfigContainer.py
trunk/conduit/gtkui/ConfigWidgets.py
trunk/conduit/gtkui/EmbedConfigurator.py
trunk/conduit/gtkui/WindowConfigurator.py
Modified:
trunk/ChangeLog
trunk/conduit/Module.py
trunk/conduit/dataproviders/DataProvider.py
trunk/conduit/gtkui/Canvas.py
Added: trunk/conduit/Configurator.py
==============================================================================
--- (empty file)
+++ trunk/conduit/Configurator.py Tue Feb 10 04:03:44 2009
@@ -0,0 +1,87 @@
+"""
+Configuration interfaces
+
+Copyright: Alexandre Rosenfeld, 2009
+License: GPLv2
+"""
+import os.path
+import gobject
+import logging
+log = logging.getLogger("conduit.Configutator")
+
+from gettext import gettext as _
+import conduit
+
+class BaseConfigContainer(gobject.GObject):
+ """
+ Base configuration container class.
+ """
+
+ __gsignals__ = {
+ # Changed is called when the user modifies anything in the
+ # configuration dialog. The bool property is True if the values
+ # are in their initial state
+ 'changed': (gobject.SIGNAL_RUN_FIRST, None, [bool]),
+ 'apply': (gobject.SIGNAL_RUN_FIRST, None, []),
+ 'cancel': (gobject.SIGNAL_RUN_FIRST, None, []),
+ }
+
+ def __init__(self, dataprovider, configurator):
+ gobject.GObject.__init__(self)
+ self.showing = False
+ self.dataprovider = dataprovider
+ self.configurator = configurator
+ self.name = None
+ self.icon = None
+
+ def get_name(self):
+ '''
+ Returns the name for this container
+ '''
+ #FIXME: This doesnt work, the dataprovider does not have a name, it's
+ # module wrapper does. This is fixed by a hack inside the Canvas, by
+ # assigning the name there, which is not ideal.
+ return self.name or self.dataprovider and self.dataprovider.get_name()
+
+ def get_icon(self):
+ '''
+ Returns a path to this configurator icon
+ '''
+ #FIXME: This doesnt work, the dataprovider does not have an icon, it's
+ # module wrapper does. This is fixed by a hack inside the Canvas, by
+ # assigning the icon there, which is not ideal.
+ return self.icon or self.dataprovider and self.dataprovider.get_icon()
+
+ def get_config_widget(self):
+ '''
+ Returns the root configuration widget
+ '''
+ pass
+
+ def show(self):
+ '''
+ Show the configuration widget
+ '''
+ self.showing = True
+
+ def hide(self):
+ '''
+ Hide the configuration widget
+ '''
+ self.showing = False
+
+ def apply_config(self):
+ '''
+ Save the current configuration state to the dataprovider.
+ '''
+ self.emit('apply')
+
+ def cancel_config(self):
+ '''
+ Cancel the configuration, reverting any changes the user might have done
+ '''
+ self.emit('cancel')
+
+ def is_modified(self):
+ return False
+
Modified: trunk/conduit/Module.py
==============================================================================
--- trunk/conduit/Module.py (original)
+++ trunk/conduit/Module.py Tue Feb 10 04:03:44 2009
@@ -201,7 +201,7 @@
log.warn("Error loading the file: %s\n%s" % (filename, "".join(traceback.format_exception(e.exc,e.value,e.tb))))
self.invalidFiles.append(os.path.basename(filename))
except Exception, e:
- log.warn("Error loading the file: %s\n%s" % (filename, "".join(traceback.format_exception(e.exc,e.value,e.tb))))
+ log.error("Error loading the file: %s\n%s" % (filename, traceback.format_exc()))
self.invalidFiles.append(os.path.basename(filename))
def load_all(self, whitelist, blacklist):
Modified: trunk/conduit/dataproviders/DataProvider.py
==============================================================================
--- trunk/conduit/dataproviders/DataProvider.py (original)
+++ trunk/conduit/dataproviders/DataProvider.py Tue Feb 10 04:03:44 2009
@@ -57,6 +57,8 @@
self.pendingChangeDetected = False
self.icon = None
self.status = STATUS_NONE
+ self.config_container = None
+ self.configuration = {}
def __emit_status_changed(self):
"""
@@ -168,13 +170,46 @@
else:
return False
- def configure(self, window):
+ def get_config_container(self, configurator):
"""
- Show a configuration box for configuring the dataprovider instance.
- @param window: The parent gtk.Window (to show a modal dialog)
+ Retrieves the configuration container
+ @param configurator: The configurator object
"""
+ # If the dataprovider is using the old system, returns None (a message
+ # will be thrown in the Canvas module)
+ if hasattr(self, "configure"):
+ return None
+ if not self.config_container:
+ 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_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)
+ return self.config_container
+
+ def config_setup(self, config_container):
+ '''
+ Configures the configuration controller, to show the controls needed
+ '''
pass
-
+
+ def config_apply(self, config_container):
+ '''
+ Called when the configuration was applied
+ '''
+ pass
+
+ def config_cancel(self, config_container):
+ '''
+ Called when the configuration was cancelled
+ '''
+ pass
+
def is_configured(self, isSource, isTwoWay):
"""
Checks if the dp has been configured or not (and if it needs to be)
@@ -191,6 +226,19 @@
@returns: Dictionary of strings containing application settings
@rtype: C{dict(string)}
"""
+ if self.configuration:
+ ret = {}
+ for name, default, setter, getter in self._get_configuration_parameters(self.configuration):
+ if getter:
+ value = getter()
+ elif hasattr(self, name):
+ value = getattr(self, name)
+ else:
+ value = default
+ ret[name] = value
+ return ret
+ else:
+ log.critical("No configuration set (probably old-style module)")
return {}
def get_configuration_xml(self):
@@ -207,7 +255,7 @@
vtype = Settings.TYPE_TO_TYPE_NAME[ type(configDict[config]) ]
value = Settings.TYPE_TO_STRING[ type(configDict[config]) ](configDict[config])
except KeyError:
- log.warn("Cannot convert %s to string. Value of %s not saved" % (type(value), config))
+ log.warn("Cannot convert %s to string. Value of %s not saved" % (type(configDict[config]), config))
vtype = Settings.TYPE_TO_TYPE_NAME[str]
value = Settings.TYPE_TO_STRING[str](configDict[config])
configxml.setAttribute("type", vtype)
@@ -218,22 +266,90 @@
return doc.toxml()
+ def _get_configuration_parameters(self, configuration):
+ '''
+ Normalize the configuration dict to 3 params and yields the name plus
+ the parameters. See update_configuration for more information.
+ '''
+ for name, params in configuration.iteritems():
+ if not isinstance(params, tuple):
+ params = (params,)
+ # Normalize to 3 parameters plus name
+ yield (name,) + params + (None,) * (3 - len(params))
+
+ def _set_configuration_values(self, configuration, config = None):
+ '''
+ Set attributes according to configuration. See update_configuration
+ for more information.
+ '''
+ 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 config:
+ value = klass(config[name])
+ else:
+ value = default
+ if setter:
+ if not hasattr(self, name):
+ setattr(self, name, value)
+ setter(value)
+ else:
+ setattr(self, name, value)
+
+ def update_configuration(self, **kwargs):
+ '''
+ Set the configuration values to be automatically saved and loaded.
+
+ The keys to kwargs are the attribute names that will be used. The values
+ to kwargs may be a default value or a tuple, containing the default
+ value, a setter and a getter. Not all values must exist in the
+ tuple. In the case the tuple's length is smaller then 3, the later
+ properties are defaulted to None.
+ The default value is immediately applied to the attribute if no other
+ value is set to that attribute. So calling this function on
+ initialization already initializes all attributes.
+ The getter and setter are functions to get and set the value. They are
+ very simple, getter should return the value to an attribute, so that
+ value can be saved, while setter, which receives a value trough it's
+ arguments, should propably set an attribute with that value.
+ Notice that if the setter is used, the value is not automatically set
+ as an attribute, unless that attribute does not exist. This allows a
+ setter to compare the current attribute value to new value to be set,
+ and only set the new value if it wishes so.
+
+ Note if the dataprovider overrides set_configuration or
+ get_configuration without calling the implementations in this class,
+ then the properties defined here have no affect. Either do not override
+ those functions, or call them like
+ DataProviderBase.get_configuration(self).
+ '''
+ #FIXME: Rewrite and clarify the documentation above
+
+ self.configuration.update(kwargs)
+ self._set_configuration_values(kwargs)
+
def set_configuration(self, config):
"""
Restores applications settings
@param config: dictionary of dataprovider settings to restore
"""
- for c in config:
- #Perform these checks to stop malformed xml from stomping on
- #unintended variables or posing a security risk by overwriting methods
- if getattr(self, c, None) != None and callable(getattr(self, c, None)) == False:
- setattr(self,c,config[c])
- else:
- log.warn("Not restoring %s setting: Exists=%s Callable=%s" % (
- c,
- getattr(self, c, False),
- callable(getattr(self, c, None)))
- )
+ if self.configuration:
+ self._set_configuration_values(self.configuration, config)
+ else:
+ for c in config:
+ #Perform these checks to stop malformed xml from stomping on
+ #unintended variables or posing a security risk by overwriting methods
+ if getattr(self, c, None) != None and callable(getattr(self, c, None)) == False:
+ setattr(self,c,config[c])
+ else:
+ log.warn("Not restoring %s setting: Exists=%s Callable=%s" % (
+ c,
+ getattr(self, c, False),
+ callable(getattr(self, c, None)))
+ )
def set_configuration_xml(self, xmltext):
"""
Modified: trunk/conduit/gtkui/Canvas.py
==============================================================================
--- trunk/conduit/gtkui/Canvas.py (original)
+++ trunk/conduit/gtkui/Canvas.py Tue Feb 10 04:03:44 2009
@@ -22,6 +22,7 @@
import conduit.gtkui.Tree
import conduit.gtkui.Util as GtkUtil
import conduit.dataproviders.DataProvider as DataProvider
+import conduit.gtkui.WindowConfigurator as WindowConfigurator
log.info("Module Information: %s" % Utils.get_module_information(goocanvas, "pygoocanvas_version"))
@@ -169,6 +170,8 @@
self.parentWindow = parentWindow
self.msg = msg
+ self.configurator = WindowConfigurator.WindowConfigurator(self.parentWindow)
+
self._setup_popup_menus(dataproviderMenu, conduitMenu)
#set up DND from the treeview
@@ -351,12 +354,50 @@
for i in self._get_child_conduit_canvas_items():
i.set_width(allocation.width)
+
+ def _update_configuration(self, selectedItem):
+ if not selectedItem:
+ self.configurator.set_containers([])
+ return
+
+ dps = []
+ for dp in selectedItem.model.get_all_dataproviders():
+ if not dp.module:
+ continue
+ container = dp.module.get_config_container(self.configurator)
+ if container:
+ #FIXME: Should be moved inside the container
+ container.icon = dp.get_icon()
+ container.name = dp.get_name()
+ dps.append(container)
+ if dps:
+ self.configurator.set_containers(dps)
+
+ def _update_selection(self, selected_conduit, selected_dataprovider):
+ changed_conduit = (selected_conduit != self.selectedConduitItem)
+ if changed_conduit:
+ self._update_configuration(selected_conduit)
+
+ self.selectedDataproviderItem = selected_dataprovider
+ self.selectedConduitItem = selected_conduit
+
+ def get_selected_conduit(self):
+ if self.selectedConduitItem:
+ return self.selectedConduitItem.model
+ else:
+ return None
+
+ def get_selected_dataprovider(self):
+ if self.selectedDataproviderItem:
+ return self.selectedDataproviderItem.model
+ else:
+ return None
def _on_conduit_button_press(self, view, target, event):
"""
Handle button clicks on conduits
"""
- self.selectedConduitItem = view
+ self._update_selection(view, None)
#right click
if event.type == gtk.gdk.BUTTON_PRESS:
@@ -394,8 +435,7 @@
@param user_data_dataprovider_wrapper: The dpw that was clicked
@type user_data_dataprovider_wrapper: L{conduit.Module.ModuleWrapper}
"""
- self.selectedDataproviderItem = view
- self.selectedConduitItem = view.get_parent()
+ self._update_selection(view.get_parent(), view)
#single right click
if event.type == gtk.gdk.BUTTON_PRESS:
@@ -553,6 +593,11 @@
conduitCanvasItem.add_dataprovider_canvas_item(item)
self._remove_overlap()
+ #The embed configurator needs notification when a new dataprovider
+ #is added and the currently selected Conduit is being configured
+ if self.selectedConduitItem == conduitCanvasItem:
+ self._update_configuration(self.selectedConduitItem)
+
self._show_hint(conduitCanvasItem, item, item)
self._check_if_dataprovider_needs_configuration(
@@ -612,7 +657,12 @@
dp = dpw.module
conduitCanvasItem = self.selectedDataproviderItem.get_parent()
- dp.configure(self.parentWindow)
+ if hasattr(dp, "configure"):
+ log.critical("%s using old configuration system" % dpw.get_name())
+ dp.configure(self.parentWindow)
+ else:
+ config_container = dp.get_config_container(self.configurator)
+ self.configurator.run(config_container)
self._check_if_dataprovider_needs_configuration(
conduitCanvasItem.model,
dpw)
Added: trunk/conduit/gtkui/ConfigContainer.py
==============================================================================
--- (empty file)
+++ trunk/conduit/gtkui/ConfigContainer.py Tue Feb 10 04:03:44 2009
@@ -0,0 +1,264 @@
+'''Manages configuration items for a dataprovider.
+
+The ConfigContainer should handle all widgets in a configurator, including
+adding, removing and handling such as applying and cancelling the configuration.
+
+Copyright: Alexandre Rosenfeld, 2009
+License: GPLv2
+'''
+
+import sys
+import sets
+import gobject
+import gtk, gtk.glade
+import logging
+log = logging.getLogger("gtkui.ConfigContainer")
+
+from gettext import gettext as _
+import conduit
+import conduit.gtkui.ConfigWidgets as ConfigWidgets
+import conduit.Configurator as Configurator
+
+class Error(Exception):
+ """Base exception for all exceptions raised in this module."""
+ pass
+
+class ConfigContainer(Configurator.BaseConfigContainer):
+ """
+ Gtk implementation to the ConfigController.
+
+ A dataprovider usually does not need to instantiate this class.
+ """
+
+ __gsignals__ = {
+ 'item-changed' : (gobject.SIGNAL_RUN_FIRST, None, (gobject.TYPE_OBJECT,)),
+ }
+
+ # List of registered items. Call register_item to change it.
+ __config_classes = {}
+
+ def __init__(self, dataprovider, configurator):
+ super(ConfigContainer, self).__init__(dataprovider, configurator)
+
+ # Current section
+ self.section = None
+ self.sections = []
+ self.items = []
+ self.built_items = False
+ self.config_values = None
+
+ self._reset_modified_items()
+
+ #the child widget to contain the custom settings
+ self.widgetTable = gtk.Table(rows=1, columns=2)
+ self.widgetTable.set_row_spacings(6)
+ self.widgetTable.set_col_spacings(12)
+
+ self.config_widget = self.widgetTable
+
+ self.firstRow = True
+
+ @classmethod
+ def register_item(cls, name, item_class):
+ cls.__config_classes[name] = item_class
+
+ def _reset_modified_items(self, empty = True):
+ '''
+ Reset the list of modified items. If empty is true, just create a new
+ empty list of modified items (so that no item is modified).
+ If empty is False, set the list to None, so that it will be recreated
+ next time get_modified_items is called.
+ '''
+ if empty:
+ self.modified_items = sets.Set()
+ else:
+ self.modified_items = None
+
+ def _item_changed(self, item, initial_state, value):
+ self.emit("item-changed", item)
+ if self.modified_items is None:
+ self.get_modified_items()
+ if not initial_state:
+ self.modified_items.add(item)
+ elif item in self.modified_items:
+ self.modified_items.remove(item)
+ self.emit('changed', self.is_modified())
+
+ def _rebuild_widgets(self):
+ '''
+ Rebuild widgets if needed
+ '''
+ self.modified_items = None
+ if self.showing:
+ self._build_widgets()
+
+ def _build_widgets(self):
+ '''
+ Creates all necessary widgets
+ '''
+ table = self.widgetTable
+ if self.showing:
+ table.foreach(lambda widget: table.remove(widget))
+ table.resize(1, 2)
+ rows = 0
+ elif self.firstRow:
+ self.firstRow = False
+ rows = 0
+ else:
+ rows = table.get_property('n-rows')
+ for section in sorted(self.sections, key = lambda section: section.order):
+ rows = section.attach(table, rows)
+ if rows != 1:
+ table.set_row_spacing(rows - 1, 16)
+ self.widgetTable.show_all()
+
+ def _reset(self):
+ '''
+ Set all items to their initial state
+ '''
+ for item in self.items:
+ item.reset()
+
+ def add_section(self, title = None, order = 0, use_existing = True, **kwargs):
+ '''
+ Add a section. Returns the Section object.
+ '''
+ if (not title and self.section and not self.section.title) or \
+ (use_existing and self.section and title is self.section.title):
+ return self.section
+ found = False
+ if use_existing and title:
+ for section in self.sections:
+ if section.title == title:
+ self.section = section
+ found = True
+ break
+ if not use_existing or not found:
+ self.section = ConfigWidgets.Section(self, title, order, **kwargs)
+ self.sections.append(self.section)
+ self._rebuild_widgets()
+ return self.section
+
+ def add_item(self, title, kind, order = 0, **kwargs):
+ '''
+ Add a configuration widget. Returns the Item object.
+
+ You can pass properties to the configuration item in kwargs.
+ '''
+ if kind not in self.__config_classes:
+ raise Error("Config widget %s not found" % kind)
+ if not self.section:
+ self.add_section()
+ # If we have a saved configuration in the config dict from the dp,
+ # use it as initial value.
+ if self.config_values is None:
+ if self.dataprovider:
+ self.config_values = self.dataprovider.get_configuration()
+ else:
+ self.config_values = {}
+ if 'config_name' in kwargs:
+ if kwargs['config_name'] in self.config_values:
+ kwargs['initial_value'] = self.config_values[kwargs['config_name']]
+ if 'enabled' not in kwargs:
+ kwargs['enabled'] = self.section.enabled
+ item = self.__config_classes[kind](self, title, order, **kwargs)
+ item.connect("value-changed", self._item_changed)
+ self.items.append(item)
+ self.section.add_item(item)
+ self._rebuild_widgets()
+ return item
+
+ def get_modified_items(self):
+ '''
+ Return a list of items that has been modified
+ '''
+ if self.modified_items is None:
+ self.modified_items = sets.Set([item for item in self.items if not item.is_initial_value()])
+ return self.modified_items
+
+ def is_modified(self):
+ '''
+ Returns true if any item has been modified
+ '''
+ return len(self.get_modified_items()) != 0
+
+ def get_config_values(self, items):
+ '''
+ Returns a dict suitable to set_configuration in a data-provider.
+ '''
+ values = {}
+ for item in items:
+ config_value = item.get_config_value()
+ if config_value:
+ values.update(config_value)
+ return values
+
+ def get_config_widget(self):
+ '''
+ Returns the root configuration widget and
+ builds the configuration widgets if needed
+ '''
+ if not self.built_items:
+ self._build_widgets()
+ self.built_items = True
+ return self.config_widget
+
+ def show(self):
+ '''
+ Show the configuration widget
+ '''
+ super(ConfigContainer, self).show()
+ self.config_widget.show_all()
+
+ #def set_busy(self, busy):
+ # if busy:
+ # self.old_cursor = self.widgetTable.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+ # else:
+ # self.widgetTable.set_cursor(self.old_cursor)
+ # gtk.gdk.flush()
+
+ def apply_config(self, items = None, sections = None):
+ '''
+ Save the current configuration state to the dataprovider and to each
+ item, saving any changes the user might have done.
+ If items is None, all items will be applied. If sections or items are
+ supplied, only these items will be applied.
+ '''
+ super(ConfigContainer, self).apply_config()
+ if not items and not sections:
+ items = self.items
+ elif not items and sections:
+ items = []
+ if sections:
+ for section in sections:
+ items.extend([item for item in section.items if item not in items])
+ config_values = self.get_config_values(items)
+ if config_values:
+ #FIXME: Remove this messsage on production
+ log.debug("Applying configuration: %s" % (config_values))
+ self.dataprovider.set_configuration(config_values)
+ for item in items:
+ item.save_state()
+ if not items and not sections:
+ self._reset_modified_items()
+ else:
+ self._reset_modified_items(False)
+
+ def cancel_config(self):
+ '''
+ Cancel the configuration, reverting any changes the user might have done
+ '''
+ super(ConfigContainer, self).cancel_config()
+ self._reset()
+ self._reset_modified_items()
+
+
+log.debug(dir(ConfigWidgets))
+# Register all item classes in this module
+for klass_name in dir(ConfigWidgets):
+ klass = getattr(ConfigWidgets, klass_name)
+ #log.debug("Class: %s" % getattr(ConfigWidgets, klass_name))
+ if isinstance(klass, type) and issubclass(klass, ConfigWidgets.ConfigItem) and klass != ConfigWidgets.ConfigItem:
+ #log.debug("-- ConfigItem: %s", klass)
+ ConfigContainer.register_item(klass.__item_name__, klass)
+
Added: trunk/conduit/gtkui/ConfigWidgets.py
==============================================================================
--- (empty file)
+++ trunk/conduit/gtkui/ConfigWidgets.py Tue Feb 10 04:03:44 2009
@@ -0,0 +1,615 @@
+"""
+Gtk implementation to the configuration controller
+
+Copyright: Alexandre Rosenfeld, 2009
+License: GPLv2
+"""
+import sys
+import os.path
+import gobject
+import pango
+import gtk, gtk.glade
+import logging
+log = logging.getLogger("gtkui.Config")
+
+from gettext import gettext as _
+import conduit
+
+class Section(gobject.GObject):
+ def __init__(self, container, title, order, **kwargs):
+ '''
+ A section containing items and a title
+ '''
+ gobject.GObject.__init__(self)
+ self.container = container
+ self.title = title
+ self.order = order
+ self.items = []
+ self.__enabled = kwargs.get('enabled', True)
+
+ 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):
+ if self.title:
+ row += 1
+ label = gtk.Label("<b>%s</b>" % (self.title))
+ label.set_alignment(0.0, 0.5)
+ label.set_use_markup(True)
+ table.resize(row, 2)
+ table.attach(label, 0, 2, row - 1, row, xoptions = gtk.FILL | gtk.SHRINK, yoptions = 0 )
+ for item in sorted(self.items, key = lambda item: item.order):
+ row = item.attach(table, row, bool(self.title))
+ return row
+
+ enabled = property(lambda self: self.__enabled,
+ lambda self, v: self.set_enabled(v))
+
+ def set_enabled(self, enabled):
+ '''
+ When enabled, widgets inside this section can be clicked or modified
+ '''
+ for item in self.items:
+ item.set_enabled(enabled)
+ self.__enabled = enabled
+
+ def apply(self):
+ self.container.apply_config(sections = [self])
+
+class ConfigItem(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 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.
+
+ Signals emitted:
+ :value-changed: Emitted everytime the value changes.
+ '''
+
+ __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,
+ needs_space=False, initial_value=None, initial_value_callback=None,
+ save_callback=None, fill=False, enabled=True, **kwargs):
+ '''
+ Create a config item.
+ '''
+ gobject.GObject.__init__(self)
+ self.container = container
+ self.__widget = None
+ self.__label = None
+ 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)
+
+ def _value_changed(self, *args):
+ '''
+ Called everytime the value changes.
+ '''
+ #if self.is_initial_value():
+ # self.emit('initial-state')
+ self.emit('value-changed', self.is_initial_value(), self.value)
+
+ 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
+
+ def _clear_choices(self):
+ '''
+ Implement this to clear the choices on the widget.
+ '''
+ pass
+
+ choices = property(lambda self: self.__choices,
+ lambda self, v: self.set_choices(v))
+
+ def set_choices(self, choices):
+ '''
+ Set choices and automatically rebuild a widget containing a set of choices.
+ '''
+ 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 button sensibility.
+ '''
+ self.enabled = enabled
+ if self.widget:
+ self.widget.set_sensitive(enabled)
+
+ def reset(self):
+ '''
+ Resets the widget to it's initial state
+ '''
+ 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
+
+ def save_state(self):
+ '''
+ Set 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):
+ '''
+ Set the current value as the initial value and calls the dataprovider
+ to save the current value
+ '''
+ self.save_state()
+ self.container.apply_config([self])
+
+ 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)
+ '''
+ if self.needs_label and not self.__label:
+ label_text = self.title
+ if label_text and not label_text.rstrip().endswith(':'):
+ label_text += ':'
+ self.__label = gtk.Label(label_text)
+ self.__label.set_alignment(0.0, 0.5)
+ return self.__label
+
+ def set_label(self, label):
+ '''
+ Sets the label widget
+ '''
+ self.__label = label
+
+ 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.reset()
+ return self.__widget
+
+ def set_widget(self, widget):
+ '''
+ Sets the widget
+ '''
+ self.__widget = widget
+
+ def build_widget(self):
+ '''
+ Implement this to build the underlying widget as needed. Has to assign
+ the widget to self.widget (no need to return it)
+ '''
+ pass
+
+ def get_config_value(self):
+ '''
+ Return a value suitable to a dataprovider
+ '''
+ if not self.config_name:
+ return None
+ value = self.get_value()
+ try:
+ if self.config_type:
+ self.config_type(value)
+ except:
+ log.warning("Value %s could not be translated with %s" % (value, self.config_type))
+ #raise TypeError()
+ else:
+ return {self.config_name: value}
+
+ def _get_value(self):
+ self.get_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))
+
+ def get_value(self):
+ pass
+
+ def set_value(self, value):
+ pass
+
+ def attach(self, table, row, in_section):
+ 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)
+
+class ConfigLabel(ConfigItem):
+ __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 build_widget(self):
+ self.widget = gtk.Label()
+ self.widget.set_alignment(self.xalignment, self.yalignment)
+ self.widget.set_use_markup(self.use_markup)
+
+ def get_value(self):
+ return self.widget.get_text()
+
+ def set_value(self, value):
+ if self.use_markup:
+ self.widget.set_markup(str(value))
+ else:
+ self.widget.set_text(str(value))
+
+class ConfigButton(ConfigItem):
+ __item_name__ = 'button'
+
+ def __init__(self, *args, **kwargs):
+ ConfigItem.__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']
+
+ def build_widget(self):
+ self.widget = gtk.Button(self.title)
+
+ def set_value(self, value):
+ if self.callback_id:
+ self.widget.disconnect(self.callback_id)
+ self.callback_id = None
+ self.callback = None
+ if not callable(value):
+ return None
+ self.callback = value
+ if self.callback:
+ self.callback_id = self.widget.connect("clicked", value)
+
+ def get_value(self):
+ return self.callback
+
+class ConfigFileButton(ConfigItem):
+ __item_name__ = 'filebutton'
+
+ def __init__(self, *args, **kwargs):
+ ConfigItem.__init__(self, *args, **kwargs)
+ self.directory = kwargs.get('directory', False)
+ self._current_filename = None
+
+ def _selection_changed(self, filechooser):
+ if self._current_filename != filechooser.get_filename():
+ self._current_filename = filechooser.get_filename()
+ self._value_changed()
+
+ def build_widget(self):
+ self.widget = gtk.FileChooserButton(self.title)
+ self.widget.connect("selection-changed", self._selection_changed)
+ if self.directory:
+ self.widget.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ else:
+ self.widget.set_action(gtk.FILE_CHOOSER_ACTION_OPEN)
+
+ def set_value(self, value):
+ self.widget.set_filename(str(value))
+
+ def get_value(self):
+ return self._current_filename
+
+class ConfigRadio(ConfigItem):
+ __item_name__ = 'radio'
+
+ def __init__(self, container, title, order, **kwargs):
+ ConfigItem.__init__(self, container, title, order, **kwargs)
+ self.needs_label = title is not None
+ self.buttons = {}
+ self._active_button = None
+
+ def _button_changed(self, button):
+ if button.get_active():
+ self._active_button = button
+ self._value_changed()
+
+ def _clear_choices(self):
+ for widget in self.widget.get_children():
+ self.widget.remove(widget)
+ self.buttons = {}
+
+ def _build_choices(self):
+ last_button = None
+ for value, text in self.choices:
+ last_button = gtk.RadioButton(last_button, text)
+ last_button.connect("toggled", self._button_changed)
+ last_button.show()
+ self.buttons[value] = last_button
+ self.widget.pack_start(last_button)
+
+ def build_widget(self):
+ self.widget = gtk.VBox()
+ self._build_choices()
+
+ def get_value(self):
+ for value, button in self.buttons.iteritems():
+ if button == self._active_button:
+ return value
+ return None
+
+ def set_value(self, new_value):
+ if new_value in self.buttons:
+ self.buttons[new_value].set_active(True)
+ else:
+ log.warn("Value %s could not be applied to config %s" % (repr(self.title), new_value))
+
+class ConfigSpin(ConfigItem):
+ __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)
+
+ 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)
+
+ def get_value(self):
+ return float(self.widget.get_value())
+
+ def set_value(self, value):
+ try:
+ value = float(value)
+ self.widget.set_value(value)
+ except:
+ log.warn("Value %s could not be applied to config %s" % (repr(self.title), value))
+
+class ConfigCombo(ConfigItem):
+ '''
+ A box where the user can select one value from several
+
+ The combo box takes as choices a list of tuples, with a value and a
+ description.
+ The value is what is returned by get_value and what should be set with
+ set_value. The value can have any type.
+ The description is the text shown to the user.
+ '''
+ __item_name__ = 'combo'
+
+ def _build_choices(self):
+ if self.choices:
+ for value, text in self.choices:
+ self.widget.append_text(text)
+
+ def _clear_choices(self):
+ self.widget.get_model().clear()
+
+ def build_widget(self):
+ self.widget = gtk.combo_box_new_text()
+ self._build_choices()
+ self.widget.connect("changed", self._value_changed)
+
+ def get_value(self):
+ active = self.widget.get_active()
+ if len(self.choices) > active and active >= 0:
+ return self.choices[active][0]
+ else:
+ log.warning("No value selected in combo")
+ return None
+
+ def set_value(self, new_value):
+ for idx, (value, text) in enumerate(self.choices):
+ if value == new_value:
+ self.widget.set_active(idx)
+ return
+ log.warn("%s not found in %s" % (new_value, self.title))
+
+class ConfigComboText(ConfigCombo):
+ __item_name__ = 'combotext'
+
+ def build_widget(self):
+ self.widget = gtk.combo_box_entry_new_text()
+ self._build_choices()
+ self.widget.connect("changed", self._value_changed)
+
+ def get_value(self):
+ return self.widget.child.get_text()
+
+ def set_value(self, value):
+ self.widget.child.set_text(str(value))
+
+class ConfigText(ConfigItem):
+ __item_name__ = 'text'
+
+ def __init__(self, *args, **kwargs):
+ ConfigItem.__init__(self, *args, **kwargs)
+ self.password = kwargs.get('password', False)
+
+ def build_widget(self):
+ self.widget = gtk.Entry()
+ self.widget.connect("notify::text", self._value_changed)
+ self.widget.set_visibility(not self.password)
+
+ def get_value(self):
+ return self.widget.get_text()
+
+ def set_value(self, value):
+ self.widget.set_text(str(value))
+
+class ConfigList(ConfigItem):
+ __item_name__ = 'list'
+
+ def __init__(self, *args, **kwargs):
+ ConfigItem.__init__(self, *args, **kwargs)
+ self.needs_label = kwargs.get('needs_label', False)
+ self.initial_value = sorted(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):
+ 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, False))
+
+ def _clear_choices(self):
+ self.model.clear()
+
+ def build_widget(self):
+ self.vbox = gtk.VBox()
+ self.vbox.set_spacing(4)
+ self.scrolled_window = gtk.ScrolledWindow()
+ self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ self.vbox.pack_start(self.scrolled_window)
+ self.total_label = gtk.Label()
+ self.total_label.set_alignment(0.0, 0.0)
+ self.vbox.pack_start(self.total_label, False, False)
+ self.list = gtk.TreeView()
+ self.list.set_property('headers-visible', False)
+ self.list.set_property('rules-hint', True)
+ 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))
+ 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))
+
+ def get_value(self):
+ if not self._checked_items:
+ self._checked_items = sorted([row[0] for row in self.model if row[1]])
+ self._update_total()
+ return self._checked_items
+
+ def set_value(self, value):
+ self._checked_items = []
+ try:
+ for row in self.model:
+ row[1] = (row[0] in value)
+ if row[1]:
+ self._checked_items.append(row[0])
+ except:
+ log.warn("Value %s could not be applied to config %s" % (value, repr(self.title)))
+ self._update_total()
+
+class ConfigCheckBox(ConfigItem):
+ __item_name__ = 'check'
+
+ def __init__(self, *args, **kwargs):
+ ConfigItem.__init__(self, *args, **kwargs)
+ self.needs_label = False
+
+ def build_widget(self):
+ self.widget = gtk.CheckButton()
+ self.widget.set_label(self.title)
+ self.widget.connect("toggled", self._value_changed)
+
+ def get_value(self):
+ return self.widget.get_active()
+
+ def set_value(self, value):
+ self.widget.set_active(bool(value))
+
+
Added: trunk/conduit/gtkui/EmbedConfigurator.py
==============================================================================
--- (empty file)
+++ trunk/conduit/gtkui/EmbedConfigurator.py Tue Feb 10 04:03:44 2009
@@ -0,0 +1,111 @@
+import os.path
+import gobject
+import gtk, gtk.glade
+import logging
+log = logging.getLogger("gtkui.Config")
+
+from gettext import gettext as _
+import conduit
+
+class EmbedConfigurator(gobject.GObject):
+ '''
+ A embed configurator.
+ '''
+
+ def __init__(self, window):
+ gobject.GObject.__init__(self)
+
+ self.window = window
+
+ self.showing = False
+ self.configs = []
+
+ self.vbox = gtk.VBox(spacing = 8)
+ self.vbox.set_border_width(6)
+ self.vbox.hide()
+
+ self.widget = self.vbox
+
+ #self.widget = gtk.ScrolledWindow()
+ #self.widget.add_with_viewport(self.vbox)
+ #self.widget.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+
+ self.buttons = gtk.HButtonBox()
+ self.buttons.set_layout(gtk.BUTTONBOX_END)
+ #self.widget.pack_end(self.buttons)
+ #self.widget.pack_end(gtk.HSeparator())
+
+ self.button_apply = gtk.Button(stock = gtk.STOCK_APPLY)
+ self.button_apply.connect("clicked", self._on_apply_clicked)
+ self.button_cancel = gtk.Button(stock = gtk.STOCK_CANCEL)
+ self.button_cancel.connect("clicked", self._on_cancel_clicked)
+
+ self.buttons.pack_start(self.button_cancel)
+ self.buttons.pack_start(self.button_apply)
+
+ def _config_changed_cb(self, config, item):
+ self._update_modified()
+
+ def _update_modified(self):
+ modified = True
+ for config in self.configs:
+ modified = config.is_modified()
+ if modified:
+ break
+ self.button_cancel.set_sensitive(modified)
+ self.button_apply.set_sensitive(modified)
+
+ def _on_apply_clicked(self, button):
+ for config in self.configs:
+ if config:
+ config.apply_config()
+ self._update_modified()
+
+ def _on_cancel_clicked(self, button):
+ for config in self.configs:
+ if config:
+ config.cancel_config()
+ self._update_modified()
+
+ def has_configure_menu(self):
+ return False
+
+ def run(self):
+ return None
+
+ def set_controllers(self, config_controllers):
+ self.vbox.hide()
+ self.vbox.foreach(lambda widget: self.vbox.remove(widget))
+ for config in self.configs:
+ if config:
+ config.cancel_config()
+ #self.widget.remove(config.get_config_widget())
+ config.hide()
+ self.vbox.pack_end(self.buttons, False, False)
+ #self.widget.show()
+ self.configs = config_controllers
+ for config in self.configs:
+ if config:
+ vbox = gtk.HBox(spacing = 8)
+ name, icon = config.get_name(), config.get_icon()
+ if icon:
+ vbox.pack_start(gtk.image_new_from_pixbuf(icon), False, False)
+ if name:
+ lbl = gtk.Label(name)
+ vbox.pack_start(lbl, False, False)
+ if name or icon:
+ self.vbox.pack_start(vbox, False, False)
+ self.vbox.pack_start(config.get_config_widget(), False, False)
+ self.vbox.pack_start(gtk.HSeparator(), False, False)
+ config.show()
+ config.connect("changed", self._config_changed_cb)
+ self.vbox.show_all()
+ self._update_modified()
+
+ def get_window(self):
+ return self.window
+
+ def get_widget(self):
+ #for config in self.configs:
+ # config.show()
+ return self.widget
Added: trunk/conduit/gtkui/WindowConfigurator.py
==============================================================================
--- (empty file)
+++ trunk/conduit/gtkui/WindowConfigurator.py Tue Feb 10 04:03:44 2009
@@ -0,0 +1,207 @@
+import os.path
+import gobject
+import gtk, gtk.glade
+import logging
+log = logging.getLogger("gtkui.WindowConfigurator")
+
+from gettext import gettext as _
+import conduit
+import conduit.gtkui.ConfigContainer as ConfigContainer
+
+class WindowConfigurator(gobject.GObject):
+ """
+ A window configurator to embed a configuration widget.
+ """
+
+ CONFIG_WINDOW_TITLE_TEXT = _("Configure")
+
+ #Show multiple containers or only shows the currently selected dataprovider
+ #This should be set to False until all dataproviders use the new system
+ MULTIPLE_VIEW = False
+ #Use a notebook instead of stacking the containers. Notebooks are very good
+ #for large configuration windows, that end up being larger then the screen
+ #height (happens frequently).
+ #Creates a border when not used with MULTIPLE_VIEW, so it's disabled while
+ #that is disabled (and should be enabled otherwise)
+ NOTEBOOK = MULTIPLE_VIEW
+
+ def __init__(self, window):
+ """
+ @param window: Parent window (this dialog is modal)
+ @type window: C{gtk.Window}
+ """
+ gobject.GObject.__init__(self)
+
+ self.showing = False
+ self.built_configs = False
+
+ #self.dialogParent = window
+ #The dialog is loaded from a glade file
+ #gladeFile = os.path.join(conduit.SHARED_DATA_DIR, "conduit.glade")
+ #widgets = gtk.glade.XML(gladeFile, "DataProviderConfigDialog")
+
+ self.dialog = gtk.Dialog(self.CONFIG_WINDOW_TITLE_TEXT,
+ window,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OK, gtk.RESPONSE_OK,
+ gtk.STOCK_HELP, gtk.RESPONSE_HELP))
+ #TODO: Unless we actually have a help to show, make the help button
+ #disabled.
+ #BTW, modules should be able to define their own help
+ self.dialog.set_has_separator(False)
+ self.dialog.set_response_sensitive(gtk.RESPONSE_HELP, False)
+ self.dialog.set_default_size(300, -1)
+ #self.dialog = widgets.get_widget("DataProviderConfigDialog")
+ #self.dialog.set_transient_for(self.dialogParent)
+ #self.dialog.set_title(WindowConfigurator.CONFIG_WINDOW_TITLE_TEXT)
+ #self.dialog.set_border_width(6)
+
+ #self.configVBox = widgets.get_widget("configVBox")
+ self.dialog_box = self.dialog.vbox
+
+ self.dialog_box.pack_start(self._make_config_widget())
+ #self.configVBox.pack_start(self.conduit_config.get_config_widget())
+ self.dialog_box.show_all()
+
+ self.container_widgets = {}
+
+ def _make_config_widget(self):
+ if self.NOTEBOOK:
+ self.tabs = {}
+ self.notebook = gtk.Notebook()
+ self.notebook.set_border_width(8)
+ self.notebook.set_show_tabs(self.MULTIPLE_VIEW)
+ return self.notebook
+ else:
+ self.containers_box = gtk.VBox(spacing = 8)
+ self.containers_box.set_border_width(6)
+ return self.containers_box
+
+ def set_containers(self, containers):
+ self._clear_containers()
+ self.built_containers = False
+
+ self.containers = containers
+ #self.configVBox.hide()
+ #import threading
+ #log.debug("THREADS: %s" % threading.activeCount())
+ #if self.notebook:
+ # self.configVBox.remove(self.notebook)
+ #self.notebook = gtk.Notebook()
+ #self.configVBox.pack_start(self.notebook)
+
+ #self.notebook.show_all()
+ #self.configVBox.pack_start(self.config.get_config_widget())
+
+ def _clear_containers(self):
+ if self.NOTEBOOK:
+ #self.notebook.foreach(lambda widget: self.notebook.remove(widget))
+ while self.notebook.get_n_pages() > 0:
+ self.notebook.remove_page(0)
+ self.tabs = {}
+ else:
+ #self.containers_box.foreach(lambda widget: self.containers_box.remove(widget))
+ for container, widget in self.container_widgets.iteritems():
+ self.containers_box.remove(widget)
+ container_widget = container.get_config_widget()
+ container_widget.get_parent().remove(container_widget)
+ self.container_widgets = {}
+
+ def _add_container(self, container, container_widget):
+ if self.NOTEBOOK:
+ hbox = gtk.HBox(spacing = 8)
+ hbox.pack_start(gtk.image_new_from_pixbuf(container.get_icon()), False, False)
+ lbl = gtk.Label(container.get_name())
+ hbox.pack_start(lbl, False, False, 4)
+ hbox.show_all()
+ container_widget.set_border_width(8)
+ widget = container_widget
+ tab_index = self.notebook.append_page(container_widget, hbox)
+ self.tabs[container] = tab_index
+ self.notebook.show_all()
+ else:
+ container_box = gtk.VBox(spacing = 8)
+ widget = container_box
+ if self.MULTIPLE_VIEW:
+ title_box = gtk.HBox(spacing = 8)
+ title_box.pack_start(gtk.image_new_from_pixbuf(container.get_icon()), False, False)
+ lbl = gtk.Label(container.get_name())
+ title_box.pack_start(lbl, False, False, 4)
+ title_box.show_all()
+ container_box.pack_start(title_box, False, False)
+ #config_widget = config.get_config_widget()
+ container_box.pack_start(container_widget, True, True)
+ container_box.pack_start(gtk.HSeparator(), False, False)
+ self.containers_box.pack_start(container_box)
+ self.containers_box.show_all()
+ self.container_widgets[container] = widget
+
+ def build_containers(self):
+ if self.built_containers:
+ return
+ for index, container in enumerate(self.containers):
+ if not container:
+ continue
+ container_widget = container.get_config_widget()
+ #FIXME: The situation below is never reached.
+ #The only way to reach it is if the UI allowed a dataprovider to be
+ #configured even if it shouldnt. Even then, the canvas filters when
+ #there is no configuration container.
+ if not container_widget:
+ container_widget = gtk.Label("No configuration needed for this dataprovider")
+ self._add_container(container, container_widget)
+ self.built_containers = True
+
+ def has_configure_menu(self):
+ return True
+
+ def get_widget(self):
+ return None
+
+ def get_window(self):
+ return self.dialog
+
+ def run(self, config_container):
+ """
+ Runs the dialog, return True if OK is clicked, False otherwise
+
+ @param config_container: container that should be focused, such as the
+ currently selected dataprovider (in a notebook, the currently
+ selected page belongs to this container)
+ """
+ self.build_containers()
+ self.showing = True
+ if config_container and not self.MULTIPLE_VIEW:
+ assert config_container in self.container_widgets
+ for container, container_widget in self.container_widgets.iteritems():
+ if container != config_container:
+ #log.debug("Hiding: %s" % container)
+ container_widget.hide()
+
+ #log.debug("Showing: %s" % config_container)
+ config_container.show()
+ self.container_widgets[config_container].show()
+ containers = [config_container]
+ else:
+ for container, container_widget in self.container_widgets.iteritems():
+ container_widget.show()
+ container.show()
+ containers = self.container_widgets.keys()
+ if self.NOTEBOOK and config_container:
+ self.notebook.set_current_page(self.tabs[config_container])
+ #self.dialog.show()
+ self.dialog.reshow_with_initial_size()
+ resp = self.dialog.run()
+ for container in containers:
+ container.hide()
+ self.dialog.hide()
+ #self.dialog = None
+ self.showing = False
+ if resp == gtk.RESPONSE_OK:
+ for container in containers:
+ container.apply_config()
+ else:
+ for container in containers:
+ container.cancel_config()
+ return (resp == gtk.RESPONSE_OK)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]