From 8e60f768cf90f5addfda78b876bd87ed2769b811 Mon Sep 17 00:00:00 2001 From: Sascha Silbe Date: Mon, 18 Feb 2013 19:11:09 +0000 Subject: Add support for showing options that were inactive during start-up SANE options can be interdependent, e.g. the JPEG compression ratio (integer) may only be available if JPEG compression (bool) is enabled. Since these values can change at run-time in response to user actions, we need to cope with options becoming active or inactive. Previously we skipped inactive options and copied the current value of the (active) options to the widget right away when adding it. Since we now need to query the value of each option after each user action (since the activeness of the option might have changed and the previous value might not have been known), we do a single "update" run on start-up after all widgets have been added, rather than having two different places (per widget) where the value is set and special-casing the inactive options when adding the widgets. --- diff --git a/scan.py b/scan.py index 1bee370..a887f58 100644 --- a/scan.py +++ b/scan.py @@ -55,6 +55,9 @@ class SettingsToolbar(gtk.ToolPalette): def __init__(self): gtk.ToolPalette.__init__(self) self._scanner = None + self._widget_by_option = {} + self._toolitem_by_option = {} + self._updating = False def modify_bg(self, state, color): gtk.ToolPalette.modify_bg(self, state, color) @@ -68,6 +71,8 @@ class SettingsToolbar(gtk.ToolPalette): self._add_options() def _clear(self): + self._widget_by_option = {} + self._toolitem_by_option = {} for widget in list(self): self.remove(widget) @@ -113,25 +118,17 @@ class SettingsToolbar(gtk.ToolPalette): for option_name in group: py_name = name_map[option_name] option = self._scanner[py_name] - if not option.is_active(): - # FIXME: check SANE docs what inactive is used for - # I have a feeling we might need to cope with this - # changing after changing other settings like the mode - # (color/gray/...) - logging.warning('skipping inactive option %r', option_name) - continue - value = getattr(self._scanner, py_name) if (option.type == sane.TYPE_BOOL): - widget = self._add_bool(option, value) + widget = self._add_bool(option) elif (option.type == sane.TYPE_INT): - widget = self._add_integer(option, value) + widget = self._add_integer(option) elif (option.type == sane.TYPE_FIXED): - widget = self._add_fixed_point(option, value) + widget = self._add_fixed_point(option) elif (option.type == sane.TYPE_STRING): - widget = self._add_string(option, value) + widget = self._add_string(option) elif (option.type == sane.TYPE_BUTTON): - widget = self._add_action(option, value) + widget = self._add_action(option) else: logging.warning('Skipping setting %r of unknown type %r', option_name, option.type) @@ -142,7 +139,7 @@ class SettingsToolbar(gtk.ToolPalette): ' widget', option_name, option.type) continue - widget.set_sensitive(option.is_settable()) + widget.show() # FIXME: take translation from SANE label = gtk.Label(_(option.title)) @@ -155,6 +152,7 @@ class SettingsToolbar(gtk.ToolPalette): tool_item = gtk.ToolItem() tool_item.add(hbox) + hbox.show() # FIXME: take translation from SANE tool_item.set_tooltip_text(_(option.desc)) # TODO: represent unit @@ -162,8 +160,13 @@ class SettingsToolbar(gtk.ToolPalette): tool_group.insert(tool_item, -1) logging.debug('%r size request: %r', tool_group, tool_group.size_request()) - self.show_all() + self._widget_by_option[py_name] = widget + self._toolitem_by_option[py_name] = tool_item + self._update_state() + self.show() + + def _update_size(self): # HACK to work around GTK not calculating the size of # ToolItemGroups (and thus this ToolPalette) correctly. See # http://mail.gnome.org/archives/gtkmm-list/2011-September/msg00112.html @@ -175,23 +178,22 @@ class SettingsToolbar(gtk.ToolPalette): self.set_size_request(-1, tool_groups_height) - def _add_bool(self, option, value): + def _add_bool(self, option): button = gtk.CheckButton() - button.set_active(value) button.connect('toggled', lambda widget, option=option: self._toggle_cb(widget, option)) return button - def _add_integer(self, option, value): + def _add_integer(self, option): if isinstance(option.constraint, tuple): - return self._add_spinbutton(option, value) + return self._add_spinbutton(option) elif isinstance(option.constraint, list): - return self._add_combo(option, value) - return self._add_spinbutton(option, value) + return self._add_combo(option) + return self._add_spinbutton(option) - def _add_fixed_point(self, option, value): + def _add_fixed_point(self, option): if isinstance(option.constraint, tuple): - return self._add_spinbutton(option, value) + return self._add_spinbutton(option) elif isinstance(option.constraint, list): # TODO return None @@ -200,17 +202,17 @@ class SettingsToolbar(gtk.ToolPalette): return None @trace() - def _add_string(self, option, value): + def _add_string(self, option): if option.constraint: - return self._add_combo(option, value) + return self._add_combo(option) - return self._add_entry(option, value) + return self._add_entry(option) - def _add_action(self, option, value): + def _add_action(self, option): # TODO return None - def _add_spinbutton(self, option, value): + def _add_spinbutton(self, option): if isinstance(option.constraint, tuple): lower, upper, step = option.constraint else: @@ -220,17 +222,17 @@ class SettingsToolbar(gtk.ToolPalette): # no quantization set => guess it step = 1 - spin_adj = gtk.Adjustment(value, lower, upper, step, step*10, 0) + spin_adj = gtk.Adjustment(lower, lower, upper, step, step*10, 0) spin = gtk.SpinButton(spin_adj, 0, 0) spin.props.snap_to_ticks = True spin.set_digits(self._calc_digits(step)) - if isinstance(value, float): + if option.type == sane.TYPE_INT: spin.connect('value-changed', lambda widget, option=option: - self._spin_float_changed_cb(widget, option)) + self._spin_int_changed_cb(widget, option)) else: spin.connect('value-changed', lambda widget, option=option: - self._spin_int_changed_cb(widget, option)) + self._spin_float_changed_cb(widget, option)) spin.set_numeric(True) spin.show() @@ -245,10 +247,9 @@ class SettingsToolbar(gtk.ToolPalette): return digits - def _add_entry(self, option, value): + def _add_entry(self, option): # untested entry = gtk.Entry() - entry.set_text(value) entry.connect('focus-out-event', lambda widget, option=option: self._entry_changed_cb(widget, option)) entry.connect('activated', lambda widget, option=option: @@ -256,38 +257,87 @@ class SettingsToolbar(gtk.ToolPalette): return entry @trace() - def _add_combo(self, option, value): + def _add_combo(self, option): box = ComboBox() for combo_value in option.constraint: # FIXME: take translation from SANE box.append_item(combo_value, _(combo_value)) - new_idx = option.constraint.index(value) - box.set_active(new_idx) box.connect('changed', lambda widget, option=option: self._combo_changed_cb(widget, option)) return box @trace() def _toggle_cb(self, button, option): + if self._updating: + return new_value = button.get_active() setattr(self._scanner, option.py_name, new_value) + self._update_state() @trace() def _entry_changed_cb(self, entry, option): + if self._updating: + return setattr(self._scanner, option.py_name, entry.get_text()) + self._update_state() @trace() def _combo_changed_cb(self, combo, option): + if self._updating: + return setattr(self._scanner, option.py_name, combo.get_value()) + self._update_state() @trace() def _spin_float_changed_cb(self, spin, option): + if self._updating: + return setattr(self._scanner, option.py_name, spin.get_value()) + self._update_state() @trace() def _spin_int_changed_cb(self, spin, option): + if self._updating: + return setattr(self._scanner, option.py_name, int(spin.get_value())) + self._update_state() + + def _update_state(self): + """(Re-)check SANE attributes after changes were made (or on start-up) + + Some SANE options are interdependent, so we need to update + attributes after every change. E.g. the JPEG compression ratio + (integer) is only available if JPEG compression (bool) is + enabled. In addition, the current value is only available for + "active" options, so we can't set the values of widgets for + inactive options. + """ + try: + self._updating = True + for py_option_name, widget in self._widget_by_option.items(): + toolitem = self._toolitem_by_option[py_option_name] + option = self._scanner[py_option_name] + widget.set_sensitive(option.is_settable()) + if not option.is_active(): + toolitem.hide() + continue + + value = getattr(self._scanner, py_option_name) + if isinstance(widget, gtk.CheckButton): + widget.set_active(value) + elif isinstance(widget, gtk.SpinButton): + widget.set_value(value) + elif isinstance(widget, gtk.Entry): + widget.set_text(value) + elif isinstance(widget, ComboBox): + new_idx = option.constraint.index(value) + widget.set_active(new_idx) + toolitem.show() + + self._update_size() + finally: + self._updating = False class ScanThread(threading.Thread): -- cgit v0.9.1