def _update_account_type(self, account_type): if not self.account_type.get_sensitive(): return if account_type != Account.TYPE_BANK: self._remove_bank_widgets() self._remove_bank_option_widgets() self.code.set_sensitive(True) return self.code.set_sensitive(False) self.bank_type = ProxyComboBox() self._add_widget(api.escape(_("Bank:")), self.bank_type) self.bank_type.connect('content-changed', self._on_bank_type__content_changed) self.bank_type.show() banks = [(_('Generic'), None)] banks.extend([(b.description, b.bank_number) for b in get_all_banks()]) self.bank_type.prefill(banks) if self.model.bank: try: self.bank_type.select(self.model.bank.bank_number) except KeyError: self.bank_type.select(None) self._update_bank_type()
def __init__(self, label='', values=None): """ Create a new ComboSearchFilter object. :param label: label of the search filter :param values: items to put in the combo, see :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill` """ self._block_updates = False SearchFilter.__init__(self, label=label) label = gtk.Label(label) self.pack_start(label, False, False) label.show() self.title_label = label # We create the mode, but it will only be added to this box when # enable_advanced is called self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) for option in (ComboEquals, ComboDifferent): self.add_option(option) self.mode.select_item_by_position(0) self.combo = ProxyComboBox() if values: self.update_values(values) self.combo.connect('content-changed', self._on_combo__content_changed) self.pack_start(self.combo, False, False, 6) self.combo.show()
def __init__(self, label='', values=None): """ Create a new ComboSearchFilter object. :param label: label of the search filter :param values: items to put in the combo, see :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill` """ self._block_updates = False SearchFilter.__init__(self, label=label) label = Gtk.Label(label=label) self.pack_start(label, False, False, 0) label.show() self.title_label = label # We create the mode, but it will only be added to this box when # enable_advanced is called self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) for option in (ComboEquals, ComboDifferent): self.add_option(option) self.mode.select_item_by_position(0) self.combo = ProxyComboBox() if values: self.update_values(values) self.combo.connect('content-changed', self._on_combo__content_changed) self.pack_start(self.combo, False, False, 6) self.combo.show()
def add_combo(self, data=None): """Add a combo for selecting an option""" combo = ProxyComboBox() self.pack_start(combo, False, False, 0) if data: combo.prefill(data) return combo
def __init__(self, label, chars=0): """ Create a new StringSearchFilter object. @param label: label of the search filter @param chars: maximum number of chars used by the search entry """ SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.pack_start(self.title_label, False, False) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = gtk.Entry() self.entry.connect('activate', self._on_entry__activate) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() for option in (Contains, DoesNotContain): self._add_option(option) self.mode.select_item_by_position(0)
def _setup_work_orders_widgets(self): self._work_orders_hbox = Gtk.HBox(spacing=6) self.item_vbox.pack_start(self._work_orders_hbox, False, True, 6) self.item_vbox.reorder_child(self._work_orders_hbox, 0) self._work_orders_hbox.show() label = Gtk.Label(label=_("Work order:")) self._work_orders_hbox.pack_start(label, False, True, 0) data = [] for wo in self.wizard.workorders: # The work order might be already approved if we are editing a sale if wo.can_approve(): wo.approve() self.setup_work_order(wo) data.append([wo.description, wo]) if len(data) <= _MAX_WORK_ORDERS_FOR_RADIO: self.work_orders_combo = None for desc, wo in data: self._add_work_order_radio(desc, wo) else: self.work_orders_combo = ProxyComboBox() self.work_orders_combo.prefill(data) self._selected_workorder = self.work_orders_combo.get_selected() self._work_orders_hbox.pack_start(self.work_orders_combo, False, False, 0) self._work_orders_hbox.show_all()
def _add_options(self, attr, pos): combo = ProxyComboBox() label = gtk.Label(attr.attribute.description) # This dictionary is populated with the purpose of tests self._widgets[attr.attribute.description] = combo self.attr_table.attach(label, 0, 1, pos, pos + 1, 0, 0, 0, 0) self.attr_table.attach(combo, 1, 2, pos, pos + 1, 0, gtk.EXPAND | gtk.FILL, 0, 0) self.attr_table.show_all() self._fill_options(combo, attr) combo.connect('changed', self._on_combo_selection__changed)
class ComboSearchFilter(SearchFilter): """ - a label - a combo with a set of predefined item to select from """ __gtype_name__ = 'ComboSearchFilter' def __init__(self, label='', values=None): """ Create a new ComboSearchFilter object. @param name: name of the search filter @param values: items to put in the combo, see L{kiwi.ui.widgets.combo.ProxyComboBox.prefill} """ SearchFilter.__init__(self, label=label) label = gtk.Label(label) self.pack_start(label, False, False) label.show() self.title_label = label self.combo = ProxyComboBox() if values: self.combo.prefill(values) self.combo.connect('content-changed', self._on_combo__content_changed) self.pack_start(self.combo, False, False, 6) self.combo.show() # # SearchFilter # def get_state(self): return NumberQueryState(filter=self, value=self.combo.get_selected_data()) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.combo # # Public API # def select(self, data): """ selects an item in the combo @param data: what to select """ self.combo.select(data) # # Callbacks # def _on_combo__content_changed(self, mode): self.emit('changed')
def test_prefill_attr_none(self): model = Settable(attr=None) proxy = Proxy(FakeView(), model) combo = ProxyComboBox() combo.data_type = int combo.model_attribute = 'attr' combo.prefill([('foo', 10), ('bar', 20)]) proxy.add_widget('attr', combo) # Even though attr is None, the combo doesn't allow something # not prefilled in it to be selected. In this case, it will select # the first item (prefill actually does that) instead. self.assertEqual(model.attr, 10)
def build_widget(self): if self.use_entry: combo = ProxyComboEntry() else: combo = ProxyComboBox() self.fill(combo) return combo
def _setup_work_orders_widgets(self): self._work_orders_hbox = gtk.HBox(spacing=6) self.item_vbox.pack_start(self._work_orders_hbox, False, True, 6) self.item_vbox.reorder_child(self._work_orders_hbox, 0) self._work_orders_hbox.show() label = gtk.Label(_("Work order:")) self._work_orders_hbox.pack_start(label, False, True) data = [] for wo in self.wizard.workorders: # The work order might be already approved if we are editing a sale if wo.can_approve(): wo.approve() self.setup_work_order(wo) data.append([wo.description, wo]) if len(data) <= _MAX_WORK_ORDERS_FOR_RADIO: self.work_orders_combo = None for desc, wo in data: self._add_work_order_radio(desc, wo) else: self.work_orders_combo = ProxyComboBox() self.work_orders_combo.prefill(data) self._selected_workorder = self.work_orders_combo.get_selected() self._work_orders_hbox.pack_start(self.work_orders_combo, False, False) self._work_orders_hbox.show_all()
def __init__(self): self._js_data = None self._js_options = None self._current = None gtk.Window.__init__(self) self.set_size_request(800, 480) self.vbox = gtk.VBox() self.add(self.vbox) self.vbox.show() hbox = gtk.HBox() self.vbox.pack_start(hbox, False, False, 6) hbox.show() label = gtk.Label('Period:') hbox.pack_start(label, False, False, 6) label.show() self.chart_type = ProxyComboBox() self.chart_type.connect( 'content-changed', self._on_chart_type__content_changed) hbox.pack_start(self.chart_type, False, False, 6) self.chart_type.show() self.period_values = ProxyComboBox() self.period_values.connect( 'content-changed', self._on_period_values__content_changed) hbox.pack_start(self.period_values, False, False, 6) self.period_values.show() self._view = WebView() self._view.get_view().connect( 'load-finished', self._on_view__document_load_finished) self.vbox.pack_start(self._view, True, True) self.results = ObjectList() self.results.connect( 'row-activated', self._on_results__row_activated) self.vbox.pack_start(self.results, True, True) self._setup_daemon()
def _setup_widgets(self): self.pack_start(gtk.Label(_('Update')), False, False) self.field_combo = ProxyComboBox() self.field_combo.connect('changed', self._on_field_combo__changed) self.pack_start(self.field_combo, False, False) self.editor_placeholder = gtk.EventBox() self.pack_start(self.editor_placeholder, False, False) self.apply_button = gtk.Button(stock=gtk.STOCK_APPLY) self.apply_button.connect('clicked', self._on_apply_button__clicked) self.pack_start(self.apply_button, False, False) for field in self._fields: # Don't let the user edit unique fields for now if field.unique: continue self.field_combo.append_item(field.label, field) self.field_combo.select_item_by_position(0)
def _add_options(self, attr, pos): combo = ProxyComboBox() label = Gtk.Label(label=attr.attribute.description + u':') label.set_alignment(xalign=1, yalign=0.5) # This dictionary is populated with the purpose of tests self._widgets[attr.attribute.description] = combo # Use 3 labels per row row_pos = pos / 3 col_pos = 2 * (pos % 3) self.attr_table.attach(label, col_pos, col_pos + 1, row_pos, row_pos + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 0, 0, 0) self.attr_table.attach(combo, col_pos + 1, col_pos + 2, row_pos, row_pos + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 0, 0, 0) self.attr_table.show_all() self._fill_options(combo, attr) combo.connect('changed', self._on_combo_selection__changed)
def __init__(self, label=''): """ Create a new DateSearchFilter object. @param label: name of the search filter """ self._options = {} SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.pack_start(self.title_label, False, False) self.title_label.show() self.mode = ProxyComboBox() self.mode.connect( 'content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.mode.show() self.from_label = gtk.Label(_("From:")) self.pack_start(self.from_label, False, False) self.from_label.show() self.start_date = ProxyDateEntry() self._start_changed_id = self.start_date.connect( 'content-changed', self._on_start_date__changed) self.pack_start(self.start_date, False, False, 6) self.start_date.show() self.to_label = gtk.Label(_("To:")) self.pack_start(self.to_label, False, False) self.to_label.show() self.end_date = ProxyDateEntry() self._end_changed_id = self.end_date.connect( 'content-changed', self._on_end_date__changed) self.pack_start(self.end_date, False, False, 6) self.end_date.show() self.add_custom_options() for option in (Any, Today, Yesterday, LastWeek, LastMonth): self.add_option(option) self.mode.select_item_by_position(0)
def __init__(self, label=''): """ Create a new NumberSearchFilter object. :param label: name of the search filter """ self._options = {} SearchFilter.__init__(self, label=label) self.title_label = Gtk.Label(label=label) self.title_label.set_alignment(1.0, 0.5) self.pack_start(self.title_label, False, False, 0) self.title_label.show() self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.mode.show() self.start = Gtk.SpinButton(climb_rate=1.0) self.start.set_adjustment( Gtk.Adjustment(lower=-MAX_INT - 1, upper=MAX_INT, step_increment=1)) self.pack_start(self.start, False, False, 6) self.start.show() self.start.connect_after('activate', self._on_entry__activate) self.and_label = Gtk.Label(label=_("And")) self.pack_start(self.and_label, False, False, 0) self.and_label.show() self.end = Gtk.SpinButton(climb_rate=1.0) self.start.set_adjustment( Gtk.Adjustment(lower=-MAX_INT - 1, upper=MAX_INT, step_increment=1)) self.pack_start(self.end, False, False, 6) self.end.show() self.end.connect_after('activate', self._on_entry__activate) for option in (LowerThan, EqualsTo, GreaterThan, Between): self.add_option(option) self.mode.select_item_by_position(0)
def _setup_options_combo_slave(self): widget = ProxyComboBox() widget.props.sensitive = self.sensitive widget.model_attribute = "field_value" widget.data_type = unicode data = [(value, unicode(key)) for key, value in self.detail.options.items()] widget.prefill(data) self.proxy.add_widget("field_value", widget) self.container.add(widget) widget.show()
def __init__(self, label='', values=None): """ Create a new ComboSearchFilter object. @param name: name of the search filter @param values: items to put in the combo, see L{kiwi.ui.widgets.combo.ProxyComboBox.prefill} """ SearchFilter.__init__(self, label=label) label = gtk.Label(label) self.pack_start(label, False, False) label.show() self.title_label = label self.combo = ProxyComboBox() if values: self.combo.prefill(values) self.combo.connect('content-changed', self._on_combo__content_changed) self.pack_start(self.combo, False, False, 6) self.combo.show()
def __init__(self, label, chars=0, container=None): """ Create a new StringSearchFilter object. :param label: label of the search filter :param chars: maximum number of chars used by the search entry """ self._container = container SearchFilter.__init__(self, label=label) self.title_label = Gtk.Label(label=label) self.pack_start(self.title_label, False, False, 0) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = Gtk.Entry() self.entry.set_placeholder_text(_("Search")) self.entry.props.secondary_icon_sensitive = False data = environ.get_resource_string('stoq', 'pixmaps', 'stoq-funnel-16x16.png') image = pixbuf_from_string(data) self.entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, image) self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY, _("Add a filter")) self.entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_CLEAR) self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Clear the search")) self.entry.connect("icon-release", self._on_entry__icon_release) self.entry.connect('activate', self._on_entry__activate) self.entry.connect('changed', self._on_entry__changed) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() for option in [ ContainsAll, ContainsExactly, IdenticalTo, DoesNotContain ]: self._add_option(option) self.mode.select_item_by_position(0)
def __init__(self): self._js_data = None self._js_options = None self._current = None gtk.Window.__init__(self) self.set_size_request(800, 480) self.vbox = gtk.VBox() self.add(self.vbox) self.vbox.show() hbox = gtk.HBox() self.vbox.pack_start(hbox, False, False, 6) hbox.show() label = gtk.Label('Period:') hbox.pack_start(label, False, False, 6) label.show() self.chart_type = ProxyComboBox() self.chart_type.connect('content-changed', self._on_chart_type__content_changed) hbox.pack_start(self.chart_type, False, False, 6) self.chart_type.show() self.period_values = ProxyComboBox() self.period_values.connect('content-changed', self._on_period_values__content_changed) hbox.pack_start(self.period_values, False, False, 6) self.period_values.show() self._view = WebView() self._view.get_view().connect('load-finished', self._on_view__document_load_finished) self.vbox.pack_start(self._view, True, True) self.results = ObjectList() self.results.connect('row-activated', self._on_results__row_activated) self.vbox.pack_start(self.results, True, True) self._setup_daemon()
def __init__(self, label='', values=None): """ Create a new ComboSearchFilter object. :param label: label of the search filter :param values: items to put in the combo, see :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill` """ self._block_updates = False SearchFilter.__init__(self, label=label) label = gtk.Label(label) self.pack_start(label, False, False) label.show() self.title_label = label self.combo = ProxyComboBox() if values: self.update_values(values) self.combo.connect('content-changed', self._on_combo__content_changed) self.pack_start(self.combo, False, False, 6) self.combo.show()
def __init__(self, store, field, other_fields): """ :param store: a storm store if its needed :param field: the field that is being edited :param other_fields: other fields available for math operations """ assert len(self.operations) self._store = store self._other_fields = other_fields self._oper = None self._field = field super(Editor, self).__init__(spacing=6) self.operations_combo = ProxyComboBox() self.pack_start(self.operations_combo, True, True, 0) self.operations_combo.connect('changed', self._on_operation_changed) for oper in self.operations: self.operations_combo.append_item(oper.label, oper) self.operations_combo.select(self.operations[0]) self.show_all()
def __init__(self, label, chars=0, container=None): """ Create a new StringSearchFilter object. :param label: label of the search filter :param chars: maximum number of chars used by the search entry """ self._container = container SearchFilter.__init__(self, label=label) self.title_label = Gtk.Label(label=label) self.pack_start(self.title_label, False, False, 0) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = Gtk.Entry() self.entry.set_placeholder_text(_("Search")) self.entry.props.secondary_icon_sensitive = False self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, 'fa-filter-symbolic') self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY, _("Add a filter")) self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, 'edit-clear-symbolic') self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Clear the search")) self.entry.connect("icon-release", self._on_entry__icon_release) self.entry.connect('activate', self._on_entry__activate) self.entry.connect('changed', self._on_entry__changed) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() # Default filter will be only contains all. When advanced filter is enabled it will add # other options self._add_option(ContainsAll) self.mode.select_item_by_position(0)
def __init__(self, label, chars=0, container=None): """ Create a new StringSearchFilter object. :param label: label of the search filter :param chars: maximum number of chars used by the search entry """ self._container = container SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.pack_start(self.title_label, False, False) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = HintedEntry() self.entry.set_hint(_("Search")) self.entry.show_hint() self.entry.props.secondary_icon_sensitive = False self.entry.set_icon_from_stock(gtk.ENTRY_ICON_PRIMARY, gtk.STOCK_FIND) self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR) self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_SECONDARY, _("Clear the search")) self.entry.connect("icon-release", self._on_entry__icon_release) self.entry.connect('activate', self._on_entry__activate) self.entry.connect('changed', self._on_entry__changed) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() for option in (ContainsAll, ContainsExactly, DoesNotContain): self._add_option(option) self.mode.select_item_by_position(0)
def __init__(self, label, chars=0, container=None): """ Create a new StringSearchFilter object. :param label: label of the search filter :param chars: maximum number of chars used by the search entry """ self._container = container SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.pack_start(self.title_label, False, False) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = HintedEntry() self.entry.set_hint(_("Search")) self.entry.show_hint() self.entry.props.secondary_icon_sensitive = False data = environ.get_resource_string('stoq', 'pixmaps', 'stoq-funnel-16x16.png') image = pixbuf_from_string(data) self.entry.set_icon_from_pixbuf(gtk.ENTRY_ICON_PRIMARY, image) self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_PRIMARY, _("Add a filter")) self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR) self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_SECONDARY, _("Clear the search")) self.entry.connect("icon-release", self._on_entry__icon_release) self.entry.connect('activate', self._on_entry__activate) self.entry.connect('changed', self._on_entry__changed) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() for option in (ContainsAll, ContainsExactly, DoesNotContain): self._add_option(option) self.mode.select_item_by_position(0)
def __init__(self, label=''): """ Create a new NumberSearchFilter object. @param label: name of the search filter """ self._options = {} SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.title_label.set_alignment(1.0, 0.5) self.pack_start(self.title_label, False, False) self.title_label.show() self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.mode.show() self.start = gtk.SpinButton(climb_rate=1.0) self.start.get_adjustment().step_increment = 1.0 self.start.set_range(-sys.maxint-1, sys.maxint) self.pack_start(self.start, False, False, 6) self.start.show() self.start.connect_after('activate', self._on_entry__activate) self.and_label = gtk.Label(_("And")) self.pack_start(self.and_label, False, False) self.and_label.show() self.end = gtk.SpinButton(climb_rate=1.0) self.end.get_adjustment().step_increment = 1.0 self.end.set_range(-sys.maxint-1, sys.maxint) self.pack_start(self.end, False, False, 6) self.end.show() self.end.connect_after('activate', self._on_entry__activate) for option in (LowerThan, EqualsTo, GreaterThan, Between): self.add_option(option) self.mode.select_item_by_position(0)
def get_widget_for_type(rtype): if rtype is types.boolean: return ProxyCheckButton() elif rtype is types.file: w = ProxyFileChooserButton('Select File') #w.set_action(gtk.FILE_CHOOSER_ACTION_SAVE) return w elif rtype is types.readonlyfile: w = ProxyFileChooserButton('Select File') w.set_sensitive(False) #w.set_action(gtk.FILE_CHOOSER_ACTION_SAVE) return w elif rtype in [types.directory]: w = ProxyFileChooserButton(title='Select Directory') w.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) return w elif rtype is types.font: return ProxyFontButton() elif rtype is types.color: return CleverProxyColorButton() elif rtype is types.integer: w = ProxySpinButton() return w elif rtype.__name__ is 'intrange': adjvals = rtype.lower, rtype.upper, rtype.step adj = gtk.Adjustment(0, *adjvals) w = ProxySpinButton() w.set_adjustment(adj) return w elif rtype is types.readonly: return FormattedLabel(VC_NAME_MU) elif rtype.__name__ is 'stringlist': w = ProxyComboBox() w.set_property('data-type', str) w.prefill(rtype.choices) return w else: w = ProxyEntry(data_type=str) w.set_width_chars(18) return w
class Editor(Gtk.HBox): """Base class for field editors Subclasses must define a list of operations and a datatype """ operations = [] data_type = None def __init__(self, store, field, other_fields): """ :param store: a storm store if its needed :param field: the field that is being edited :param other_fields: other fields available for math operations """ assert len(self.operations) self._store = store self._other_fields = other_fields self._oper = None self._field = field super(Editor, self).__init__(spacing=6) self.operations_combo = ProxyComboBox() self.pack_start(self.operations_combo, True, True, 0) self.operations_combo.connect('changed', self._on_operation_changed) for oper in self.operations: self.operations_combo.append_item(oper.label, oper) self.operations_combo.select(self.operations[0]) self.show_all() def set_field(self, field): assert field.data_type == self.data_type self._field = field self._oper.set_field(field) def _on_operation_changed(self, combo): if self._oper is not None: # Remove previous operation self.remove(self._oper) self._oper = combo.get_selected()(self._store, self._field, self._other_fields) self.pack_start(self._oper, True, True, 0) def apply_operation(self, item): return self._oper.apply_operation(item)
class StringSearchFilter(SearchFilter): """ Contains: - a label - an entry :ivar entry: the entry :ivar label: the label """ __gtype_name__ = 'StringSearchFilter' def __init__(self, label, chars=0, container=None): """ Create a new StringSearchFilter object. :param label: label of the search filter :param chars: maximum number of chars used by the search entry """ self._container = container SearchFilter.__init__(self, label=label) self.title_label = Gtk.Label(label=label) self.pack_start(self.title_label, False, False, 0) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = Gtk.Entry() self.entry.set_placeholder_text(_("Search")) self.entry.props.secondary_icon_sensitive = False data = environ.get_resource_string('stoq', 'pixmaps', 'stoq-funnel-16x16.png') image = pixbuf_from_string(data) self.entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, image) self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY, _("Add a filter")) self.entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_CLEAR) self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Clear the search")) self.entry.connect("icon-release", self._on_entry__icon_release) self.entry.connect('activate', self._on_entry__activate) self.entry.connect('changed', self._on_entry__changed) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() for option in [ ContainsAll, ContainsExactly, IdenticalTo, DoesNotContain ]: self._add_option(option) self.mode.select_item_by_position(0) def _add_option(self, option_type): option = option_type() self.mode.append_item(option.name, option_type) self._options[option_type] = option # # Callbacks # def _on_mode__content_changed(self, combo): self.emit('changed') def _on_entry__activate(self, entry): self.emit('changed') def _on_entry__changed(self, entry): entry.props.secondary_icon_sensitive = bool(entry.get_text()) def _position_filter_menu(self, data): window = self.entry.get_icon_window(Gtk.EntryIconPosition.PRIMARY) x, y = window.get_origin() y += window.get_size()[1] border = self.entry.style_get_property('progress-border') if border is not None: y += border.bottom return (x, y, True) def _on_entry__icon_release(self, entry, icon_pos, event): if icon_pos == Gtk.EntryIconPosition.SECONDARY: entry.set_text("") entry.grab_focus() self.emit('changed') elif icon_pos == Gtk.EntryIconPosition.PRIMARY: # We don't need create popup filters if haven't search columns. if (not self._container or not hasattr(self._container, 'menu') or not self._container.menu): return self._container.menu.popup(None, None, None, self._position_filter_menu, 0, event.time) # # SearchFilter # def get_state(self): option = self.mode.get_selected_data() return StringQueryState(filter=self, text=str(self.entry.get_text()), mode=option and option.mode) def set_state(self, text, mode=None): self.entry.set_text(text) if mode is not None: self.mode.select_item_by_position(mode) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.mode def get_description(self): desc = self.entry.get_text() if desc: mode = self.mode.get_selected_label() return '%s %s "%s"' % ( self.title_label.get_text(), mode, desc, ) # # Public API # def enable_advanced(self): # Do not show the funnel icon if its an advanced filter self.entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, None) self.mode.show() def set_label(self, label): self.title_label.set_text(label)
class DateSearchFilter(SearchFilter): """ A filter which helps you to search by a date interval. Can be customized through add_option. """ __gtype_name__ = 'DateSearchFilter' class Type(enum): (USER_DAY, USER_INTERVAL) = range(100, 102) def __init__(self, label=''): """ Create a new DateSearchFilter object. @param label: name of the search filter """ self._options = {} SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.pack_start(self.title_label, False, False) self.title_label.show() self.mode = ProxyComboBox() self.mode.connect( 'content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.mode.show() self.from_label = gtk.Label(_("From:")) self.pack_start(self.from_label, False, False) self.from_label.show() self.start_date = ProxyDateEntry() self._start_changed_id = self.start_date.connect( 'content-changed', self._on_start_date__changed) self.pack_start(self.start_date, False, False, 6) self.start_date.show() self.to_label = gtk.Label(_("To:")) self.pack_start(self.to_label, False, False) self.to_label.show() self.end_date = ProxyDateEntry() self._end_changed_id = self.end_date.connect( 'content-changed', self._on_end_date__changed) self.pack_start(self.end_date, False, False, 6) self.end_date.show() self.add_custom_options() for option in (Any, Today, Yesterday, LastWeek, LastMonth): self.add_option(option) self.mode.select_item_by_position(0) # # SearchFilter # def get_state(self): start = self.start_date.get_date() end = self.end_date.get_date() if start == end: return DateQueryState(filter=self, date=start) return DateIntervalQueryState(filter=self, start=start, end=end) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.mode def get_description(self): desc = '' start_date = self.start_date.get_date() end_date = self.end_date.get_date() if start_date: if end_date and start_date != end_date: desc += ' %s %s %s %s' % (_(u'from'), start_date.strftime('%x'), _(u'to'), end_date.strftime('%x'),) else: desc += start_date.strftime('%x') if desc: return '%s %s' % (self.get_title_label().get_text(), desc,) # # Public API # def clear_options(self): """ Removes all previously added options """ self._options = {} self.mode.clear() def add_option(self, option_type, position=-2): """ Adds a date option @param option_type: option to add @type option_type: a L{DateSearchOption} subclass """ option = option_type() num = len(self.mode) + position self.mode.insert_item(num, option.name, option_type) self._options[option_type] = option def add_option_fixed(self, name, date, position=-2): """ Adds a fixed option, eg one for which date is not possible to modify. @param name: name of the option @param date: fixed data @param position: position to add the option at """ option_type = type('', (FixedDateSearchOption,), dict(name=name, date=date)) self.add_option(option_type, position=position) def add_option_fixed_interval(self, name, start, end, position=-2): """ Adds a fixed option interval, eg one for which the dates are not possible to modify. @param name: name of the option @param start: start of the fixed interval @param end: end of the fixed interval @param position: position to add the option at """ option_type = type('', (FixedIntervalSearchOption,), dict(name=name, start=start, end=end)) self.add_option(option_type, position=position) def add_custom_options(self): """Adds the custom options 'Custom day' and 'Custom interval' which let the user define its own interval dates. """ pos = len(self.mode) + 1 for name, option_type in [ (_('Custom day'), DateSearchFilter.Type.USER_DAY), (_('Custom interval'), DateSearchFilter.Type.USER_INTERVAL)]: self.mode.insert_item(pos, name, option_type) pos += 1 def get_start_date(self): """ Get the start date. @returns: start date @rtype: datetime.date or None """ return self.start_date.get_date() def get_end_date(self): """ Get the end date. @returns: end date @rtype: datetime.date or None """ return self.end_date.get_date() def set_use_date_entries(self, use_date_entries): """ Toggles the visibility of the user selectable date entries @param use_date_entries: """ self.from_label.props.visible = use_date_entries self.to_label.props.visible = use_date_entries self.start_date.props.visible = use_date_entries self.end_date.props.visible = use_date_entries def select(self, data=None, position=None): """ selects an item in the combo Data or position can be sent in. If nothing is sent in the first item will be selected, if any @param data: data to select @param position: position of data to select """ if data is not None and position is not None: raise TypeError("You can't send in both data and position") if data is None and position is None: position = 0 if position is not None: if len(self.mode): self.mode.select_item_by_position(position) elif data: self.mode.select(data) # # Private # def _update_dates(self): # This is called when we change mode date_type = self.mode.get_selected_data() if date_type is None: return # If we switch to a user selectable day, make sure that # both dates are set to today if date_type == DateSearchFilter.Type.USER_DAY: today = datetime.date.today() self.start_date.set_date(today) self.end_date.set_date(today) # And for user interval, set start to today and to tomorrow elif date_type == DateSearchFilter.Type.USER_INTERVAL: today = datetime.date.today() self.start_date.set_date(today) self.end_date.set_date(today + datetime.timedelta(days=1)) # Finally for pre-defined ones let the DateSearchOption decide what the # values are going to be, these dates are not user editable so # we don't need to do any checking. else: option = self._options.get(date_type) assert option, (date_type, self._options) start_date, end_date = option.get_interval() self.start_date.set_date(start_date) self.end_date.set_date(end_date) def _update_sensitivity(self): date_type = self.mode.get_selected_data() enabled = date_type == DateSearchFilter.Type.USER_INTERVAL self.to_label.set_sensitive(enabled) self.end_date.set_sensitive(enabled) enabled = (date_type == DateSearchFilter.Type.USER_INTERVAL or date_type == DateSearchFilter.Type.USER_DAY) self.from_label.set_sensitive(enabled) self.start_date.set_sensitive(enabled) def _internal_set_start_date(self, date): self.start_date.handler_block(self._start_changed_id) self.start_date.set_date(date) self.start_date.handler_unblock(self._start_changed_id) def _internal_set_end_date(self, date): self.end_date.handler_block(self._end_changed_id) self.end_date.set_date(date) self.end_date.handler_unblock(self._end_changed_id) def _restore_date_validation(self): self.start_date.set_valid() self.end_date.set_valid() # # Callbacks # def _on_mode__content_changed(self, mode): self._update_dates() self._update_sensitivity() self._restore_date_validation() self.emit('changed') def _on_start_date__changed(self, start_date): date_type = self.mode.get_selected_data() start = start_date.get_date() # For user days, just make sure that the date entries # always are in sync if date_type == DateSearchFilter.Type.USER_DAY: if start is None: self.start_date.set_invalid(_(u'Invalid date')) else: self.start_date.set_valid() self._internal_set_end_date(start) # Make sure that we cannot select a start date after # the end date, be nice and increase the end date if # the start date happen to be the same elif date_type == DateSearchFilter.Type.USER_INTERVAL: end = self.end_date.get_date() if start is None: self.start_date.set_invalid(_(u'Invalid date')) return if end and start >= end: self._internal_set_end_date(start + datetime.timedelta(days=1)) self.start_date.set_valid() def _on_end_date__changed(self, end_date): date_type = self.mode.get_selected_data() # We don't need to do anything for user day, since # this the end date widget is disabled if date_type == DateSearchFilter.Type.USER_DAY: pass # Make sure that we cannot select an end date before # the start date, be nice and decrease the start date if # the end date happen to be the same elif date_type == DateSearchFilter.Type.USER_INTERVAL: start = self.start_date.get_date() end = end_date.get_date() if end is None: self.end_date.set_invalid(_(u'Invalid date')) else: self.end_date.set_valid() if start and end and end <= start: self._internal_set_start_date(end - datetime.timedelta(days=1))
class ComboSearchFilter(SearchFilter): """ - a label - a combo with a set of predefined item to select from """ __gtype_name__ = 'ComboSearchFilter' def __init__(self, label='', values=None): """ Create a new ComboSearchFilter object. :param label: label of the search filter :param values: items to put in the combo, see :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill` """ self._block_updates = False SearchFilter.__init__(self, label=label) label = Gtk.Label(label=label) self.pack_start(label, False, False, 0) label.show() self.title_label = label # We create the mode, but it will only be added to this box when # enable_advanced is called self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) for option in (ComboEquals, ComboDifferent): self.add_option(option) self.mode.select_item_by_position(0) self.combo = ProxyComboBox() if values: self.update_values(values) self.combo.connect('content-changed', self._on_combo__content_changed) self.pack_start(self.combo, False, False, 6) self.combo.show() # # SearchFilter # def get_state(self): value = self.combo.get_selected_data() mode = self.mode.get_selected_data() state = NumberQueryState(filter=self, value=value, mode=mode.mode) if hasattr(value, 'id'): state.value_id = value.id return state def set_state(self, value, value_id=None, mode=None): if mode is None: mode = NumberQueryState.EQUALS if value_id is not None: for item in self.combo.get_model_items().values(): if item is None: continue # Filter can come as a string or as a FilterItem object item_id = item if isinstance(item, str) else item.id if item_id == value_id: value = item break self.select(value) def update_values(self, values): self._block_updates = True self.combo.prefill(values) self._block_updates = False def get_title_label(self): return self.title_label def get_mode_combo(self): return self.combo def get_description(self): desc = '' data = self.combo.get_selected_data() if data is not None: desc += self.combo.get_selected_label() return '%s %s' % ( self.title_label.get_text(), desc, ) # # Public API # def add_option(self, option_type, position=0): """ Adds an option :param option_type: option to add :type option_type: a :class:`ComboSearchOption` subclass """ option = option_type() num = len(self.mode) + position self.mode.insert_item(num, option.name, option_type) def select(self, data): """ selects an item in the combo :param data: what to select """ self.combo.select(data) def enable_advanced(self): self.pack_start(self.mode, False, False, 6) self.reorder_child(self.mode, 1) self.mode.show() # # Callbacks # def _on_mode__content_changed(self, combo): if not self._block_updates: self.emit('changed') def _on_combo__content_changed(self, mode): if not self._block_updates: self.emit('changed')
class TestComboBox(unittest.TestCase): def setUp(self): self.combo = ProxyComboBox() def _prefill(self): self.combo.prefill((('Johan', 1981), ('Lorenzo', 1979), ('Christian', 1976))) def testPrefill(self): self.combo.prefill(('foo', 'bar')) model = self.combo.get_model() self.assertEqual(tuple(model[0]), ('foo', None)) self.assertEqual(tuple(model[1]), ('bar', None)) def testPrefillWithData(self): self.combo.prefill((('foo', 42), ('bar', 138))) model = self.combo.get_model() self.assertEqual(tuple(model[0]), ('foo', 42)) self.assertEqual(tuple(model[1]), ('bar', 138)) self.combo.prefill([]) self.assertEqual(len(self.combo.get_model()), 0) self.assertEqual(len(model), 0) self.assertEqual(len(self.combo), 0) def testSelectItemByPosition(self): self._prefill() self.combo.select_item_by_position(1) model = self.combo.get_model() iter = self.combo.get_active_iter() self.assertEqual(model.get_value(iter, 0), 'Lorenzo') self.assertEqual(model.get_value(iter, 1), 1979) self.assertRaises(KeyError, self.combo.select_item_by_label, 4) def testSelectItemByLabel(self): self._prefill() self.combo.select_item_by_label('Christian') model = self.combo.get_model() iter = self.combo.get_active_iter() rowNo = model.get_path(iter)[0] self.assertEqual(rowNo, 2) self.assertRaises(KeyError, self.combo.select_item_by_label, 'Salgado') def testSelectByData(self): self._prefill() self.combo.select_item_by_data(1976) model = self.combo.get_model() iter = self.combo.get_active_iter() rowNo = model.get_path(iter)[0] self.assertEqual(rowNo, 2) self.assertEqual(model.get_value(iter, 0), 'Christian') self.assertEqual(model.get_value(iter, 1), 1976) self.assertRaises(KeyError, self.combo.select_item_by_data, 1980) def testGetSelectedData(self): self._prefill() self.combo.select_item_by_position(0) self.assertEqual(self.combo.get_selected_data(), 1981) self.assertRaises(TypeError, self.combo.select_item_by_position, 'foobar') def testGetSelectedLabel(self): self._prefill() def testClear(self): self._prefill() self.combo.clear() self.assertEqual(map(list, self.combo.get_model()), [])
class StringSearchFilter(SearchFilter): """ Contains: - a label - an entry :ivar entry: the entry :ivar label: the label """ def __init__(self, label, chars=0, container=None): """ Create a new StringSearchFilter object. :param label: label of the search filter :param chars: maximum number of chars used by the search entry """ self._container = container SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.pack_start(self.title_label, False, False) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = HintedEntry() self.entry.set_hint(_("Search")) self.entry.show_hint() self.entry.props.secondary_icon_sensitive = False data = environ.get_resource_string('stoq', 'pixmaps', 'stoq-funnel-16x16.png') image = pixbuf_from_string(data) self.entry.set_icon_from_pixbuf(gtk.ENTRY_ICON_PRIMARY, image) self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_PRIMARY, _("Add a filter")) self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR) self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_SECONDARY, _("Clear the search")) self.entry.connect("icon-release", self._on_entry__icon_release) self.entry.connect('activate', self._on_entry__activate) self.entry.connect('changed', self._on_entry__changed) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() for option in (ContainsAll, ContainsExactly, DoesNotContain): self._add_option(option) self.mode.select_item_by_position(0) def _add_option(self, option_type, position=-2): option = option_type() num = abs(position) self.mode.insert_item(num, option.name, option_type) self._options[option_type] = option # # Callbacks # def _on_mode__content_changed(self, combo): self.emit('changed') def _on_entry__activate(self, entry): self.emit('changed') def _on_entry__changed(self, entry): entry.props.secondary_icon_sensitive = bool(entry.get_text()) def _position_filter_menu(self, data): window = self.entry.get_icon_window(gtk.ENTRY_ICON_PRIMARY) x, y = window.get_origin() y += window.get_size()[1] border = self.entry.style_get_property('progress-border') if border is not None: y += border.bottom return (x, y, True) def _on_entry__icon_release(self, entry, icon_pos, event): if icon_pos == gtk.ENTRY_ICON_SECONDARY: entry.set_text("") entry.grab_focus() self.emit('changed') elif icon_pos == gtk.ENTRY_ICON_PRIMARY: # We don't need create popup filters if haven't search columns. if (not self._container or not hasattr(self._container, 'menu') or not self._container.menu): return self._container.menu.popup(None, None, self._position_filter_menu, 0, event.time) # # SearchFilter # def get_state(self): option = self.mode.get_selected_data() return StringQueryState(filter=self, text=unicode(self.entry.get_text()), mode=option and option.mode) def set_state(self, text, mode=None): self.entry.set_text(text) if mode is not None: self.mode.select_item_by_position(mode) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.mode def get_description(self): desc = self.entry.get_text() if desc: mode = self.mode.get_selected_label() return '%s %s "%s"' % (self.title_label.get_text(), mode, desc,) # # Public API # def enable_advanced(self): # Do not show the funnel icon if its an advanced filter self.entry.set_icon_from_pixbuf(gtk.ENTRY_ICON_PRIMARY, None) self.mode.show() def set_label(self, label): self.title_label.set_text(label)
class DateSearchFilter(SearchFilter): """ A filter which helps you to search by a date interval. Can be customized through add_option. """ __gtype_name__ = 'DateSearchFilter' class Type(enum): (USER_DAY, USER_INTERVAL) = range(100, 102) def __init__(self, label=''): """ Create a new DateSearchFilter object. :param label: name of the search filter """ self._options = {} SearchFilter.__init__(self, label=label) self.title_label = Gtk.Label(label=label) self.pack_start(self.title_label, False, False, 0) self.title_label.show() self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.mode.show() self.from_label = Gtk.Label(label=_("From:")) self.pack_start(self.from_label, False, False, 0) self.from_label.show() self.start_date = ProxyDateEntry() self._start_changed_id = self.start_date.connect( 'content-changed', self._on_start_date__changed) self.pack_start(self.start_date, False, False, 6) self.start_date.show() self.to_label = Gtk.Label(label=_("To:")) self.pack_start(self.to_label, False, False, 0) self.to_label.show() self.end_date = ProxyDateEntry() self._end_changed_id = self.end_date.connect( 'content-changed', self._on_end_date__changed) self.pack_start(self.end_date, False, False, 6) self.end_date.show() self.add_custom_options() for option in (Any, Today, Yesterday, LastWeek, LastMonth): self.add_option(option) self.mode.select_item_by_position(0) # # SearchFilter # def get_state(self): start = self.start_date.get_date() end = self.end_date.get_date() if start == end: return DateQueryState(filter=self, date=start) return DateIntervalQueryState(filter=self, start=start, end=end) def set_state(self, start, end=None): self.start_date.set_date(start) if end is not None: self.end_date.set_date(end) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.mode def get_description(self): desc = '' start_date = self.start_date.get_date() end_date = self.end_date.get_date() if start_date: if end_date and start_date != end_date: desc += ' %s %s %s %s' % ( _(u'from'), start_date.strftime('%x'), _(u'to'), end_date.strftime('%x'), ) else: desc += start_date.strftime('%x') if desc: return '%s %s' % ( self.get_title_label().get_text(), desc, ) # # Public API # def clear_options(self): """ Removes all previously added options """ self._options = {} self.mode.clear() def add_option(self, option_type, position=-2): """ Adds a date option :param option_type: option to add :type option_type: a :class:`DateSearchOption` subclass """ option = option_type() num = len(self.mode) + position self.mode.insert_item(num, option.name, option_type) self._options[option_type] = option def add_option_fixed(self, name, date, position=-2): """ Adds a fixed option, eg one for which date is not possible to modify. :param name: name of the option :param date: fixed data :param position: position to add the option at """ option_type = type('', (FixedDateSearchOption, ), dict(name=name, date=date)) self.add_option(option_type, position=position) def add_option_fixed_interval(self, name, start, end, position=-2): """ Adds a fixed option interval, eg one for which the dates are not possible to modify. :param name: name of the option :param start: start of the fixed interval :param end: end of the fixed interval :param position: position to add the option at """ option_type = type('', (FixedIntervalSearchOption, ), dict(name=name, start=start, end=end)) self.add_option(option_type, position=position) def add_custom_options(self): """Adds the custom options 'Custom day' and 'Custom interval' which let the user define its own interval dates. """ pos = len(self.mode) + 1 for name, option_type in [ (_('Custom day'), DateSearchFilter.Type.USER_DAY), (_('Custom interval'), DateSearchFilter.Type.USER_INTERVAL) ]: self.mode.insert_item(pos, name, option_type) pos += 1 def get_start_date(self): """ Get the start date. :returns: start date :rtype: datetime.date or None """ return self.start_date.get_date() def get_end_date(self): """ Get the end date. :returns: end date :rtype: datetime.date or None """ return self.end_date.get_date() def set_use_date_entries(self, use_date_entries): """ Toggles the visibility of the user selectable date entries :param use_date_entries: """ self.from_label.props.visible = use_date_entries self.to_label.props.visible = use_date_entries self.start_date.props.visible = use_date_entries self.end_date.props.visible = use_date_entries def select(self, data=None, position=None): """ selects an item in the combo Data or position can be sent in. If nothing is sent in the first item will be selected, if any :param data: data to select :param position: position of data to select """ if data is not None and position is not None: raise TypeError("You can't send in both data and position") if data is None and position is None: position = 0 if position is not None: if len(self.mode): self.mode.select_item_by_position(position) elif data: self.mode.select(data) # # Private # def _update_dates(self): # This is called when we change mode date_type = self.mode.get_selected_data() if date_type is None: return # If we switch to a user selectable day, make sure that # both dates are set to today if date_type == DateSearchFilter.Type.USER_DAY: today = datetime.date.today() self.start_date.set_date(today) self.end_date.set_date(today) # And for user interval, set start to today and to tomorrow elif date_type == DateSearchFilter.Type.USER_INTERVAL: today = datetime.date.today() self.start_date.set_date(today) self.end_date.set_date(today + datetime.timedelta(days=1)) # Finally for pre-defined ones let the DateSearchOption decide what the # values are going to be, these dates are not user editable so # we don't need to do any checking. else: option = self._options.get(date_type) assert option, (date_type, self._options) start_date, end_date = option.get_interval() self.start_date.set_date(start_date) self.end_date.set_date(end_date) def _update_sensitivity(self): date_type = self.mode.get_selected_data() enabled = date_type == DateSearchFilter.Type.USER_INTERVAL self.to_label.set_sensitive(enabled) self.end_date.set_sensitive(enabled) enabled = (date_type == DateSearchFilter.Type.USER_INTERVAL or date_type == DateSearchFilter.Type.USER_DAY) self.from_label.set_sensitive(enabled) self.start_date.set_sensitive(enabled) def _internal_set_start_date(self, date): self.start_date.handler_block(self._start_changed_id) self.start_date.set_date(date) self.start_date.handler_unblock(self._start_changed_id) def _internal_set_end_date(self, date): self.end_date.handler_block(self._end_changed_id) self.end_date.set_date(date) self.end_date.handler_unblock(self._end_changed_id) def _restore_date_validation(self): self.start_date.set_valid() self.end_date.set_valid() # # Callbacks # def _on_mode__content_changed(self, mode): self._update_dates() self._update_sensitivity() self._restore_date_validation() self.emit('changed') def _on_start_date__changed(self, start_date): date_type = self.mode.get_selected_data() start = start_date.get_date() # For user days, just make sure that the date entries # always are in sync if date_type == DateSearchFilter.Type.USER_DAY: if start is None: self.start_date.set_invalid(_(u'Invalid date')) else: self.start_date.set_valid() self._internal_set_end_date(start) # Make sure that we cannot select a start date after # the end date, be nice and increase the end date if # the start date happen to be the same elif date_type == DateSearchFilter.Type.USER_INTERVAL: end = self.end_date.get_date() if start is None: self.start_date.set_invalid(_(u'Invalid date')) return if end and start >= end: self._internal_set_end_date(start + datetime.timedelta(days=1)) self.start_date.set_valid() def _on_end_date__changed(self, end_date): date_type = self.mode.get_selected_data() # We don't need to do anything for user day, since # this the end date widget is disabled if date_type == DateSearchFilter.Type.USER_DAY: pass # Make sure that we cannot select an end date before # the start date, be nice and decrease the start date if # the end date happen to be the same elif date_type == DateSearchFilter.Type.USER_INTERVAL: start = self.start_date.get_date() end = end_date.get_date() if end is None: self.end_date.set_invalid(_(u'Invalid date')) else: self.end_date.set_valid() if start and end and end <= start: self._internal_set_start_date(end - datetime.timedelta(days=1))
class ChartDialog(gtk.Window): def __init__(self): self._js_data = None self._js_options = None self._current = None gtk.Window.__init__(self) self.set_size_request(800, 480) self.vbox = gtk.VBox() self.add(self.vbox) self.vbox.show() hbox = gtk.HBox() self.vbox.pack_start(hbox, False, False, 6) hbox.show() label = gtk.Label('Period:') hbox.pack_start(label, False, False, 6) label.show() self.chart_type = ProxyComboBox() self.chart_type.connect( 'content-changed', self._on_chart_type__content_changed) hbox.pack_start(self.chart_type, False, False, 6) self.chart_type.show() self.period_values = ProxyComboBox() self.period_values.connect( 'content-changed', self._on_period_values__content_changed) hbox.pack_start(self.period_values, False, False, 6) self.period_values.show() self._view = WebView() self._view.get_view().connect( 'load-finished', self._on_view__document_load_finished) self.vbox.pack_start(self._view, True, True) self.results = ObjectList() self.results.connect( 'row-activated', self._on_results__row_activated) self.vbox.pack_start(self.results, True, True) self._setup_daemon() @api.async def _setup_daemon(self): daemon = yield start_daemon() self._daemon_uri = daemon.base_uri proxy = daemon.get_client() yield proxy.callRemote('start_webservice') self.chart_type.prefill([ ('Year', 'YearlyPayments'), ('Month', 'MonthlyPayments'), ('Day', 'DailyPayments'), ]) @api.async def _invoke_chart(self, chart_type_name, **report_kwargs): def _get_chart_url(**kwargs): params = [] for key, value in kwargs.items(): params.append(key + '=' + str(value)) return '%s/web/chart.json?%s' % ( self._daemon_uri, '&'.join(params)) url = _get_chart_url(type=chart_type_name, **report_kwargs) page = yield getPage(url) data = json.loads(page) api.asyncReturn(data) def _render_chart(self, chart_class, response): self._render_javascript(chart_class, response) self._render_objectlist(chart_class, response) def _render_javascript(self, chart_class, response): ticks = [item['short_title'] for item in response['items']] self._js_data = response['data'] options = {} options['description'] = response['description'] options['series'] = [dict(label=c['title']) for c in chart_class.columns][1:] options['xaxis_ticks'] = ticks self._js_options = options self._view.load_uri('%s/web/static/chart.html' % ( self._daemon_uri,)) def _render_objectlist(self, chart_class, response): columns = [] for kwargs in chart_class.columns: kwargs = kwargs.copy() name = kwargs.pop('name') columns.append(Column(name, **kwargs)) self.results.set_columns(columns) items = [] for item in response['items']: settable = Settable(**item) settable.chart_class = chart_class items.append(settable) self.results.add_list(items, clear=True) self.results.show() def _load_finished(self): self._view.js_function_call( "plot", self._js_data, self._js_options) @api.async def _show_one(self, chart_type_name, start, end): chart_class = get_chart_class(chart_type_name) report_kwargs = dict(start=start.strftime('%Y-%m-%d'), end=end.strftime('%Y-%m-%d')) # Get chart datab response = yield self._invoke_chart(chart_type_name, **report_kwargs) self._render_chart(chart_class, response) def _update_period_values(self): chart_type_name = self.chart_type.get_selected() chart_class = get_chart_class(chart_type_name) values = chart_class.get_combo_labels() self.period_values.prefill(values) # # Callbacks # def _on_view__document_load_finished(self, view, frame): self._load_finished() def _on_chart_type__content_changed(self, combo): self._update_period_values() def _on_period_values__content_changed(self, combo): kind = self.chart_type.get_selected() value = self.period_values.get_selected() if not value: return start, end = value if self._current == (kind, start, end): return self._show_one(kind, start, end) self._current = kind, start, end def _on_results__row_activated(self, results, item): chart_type_name = item.chart_class.__name__ if chart_type_name == 'YearlyPayments': start = localdate(item.year, 1, 1).date() end = localdate(item.year, 12, 31).date() chart_type_name = 'MonthlyPayments' elif chart_type_name == 'MonthlyPayments': start = localdate(item.year, item.month, 1).date() end = start + relativedelta(days=31) chart_type_name = 'DailyPayments' else: return self._show_one(chart_type_name, start, end)
class NumberSearchFilter(SearchFilter): """ A filter which helps you to search by a number interval. """ __gtype_name__ = 'NumberSearchFilter' def __init__(self, label=''): """ Create a new NumberSearchFilter object. :param label: name of the search filter """ self._options = {} SearchFilter.__init__(self, label=label) self.title_label = Gtk.Label(label=label) self.title_label.set_alignment(1.0, 0.5) self.pack_start(self.title_label, False, False, 0) self.title_label.show() self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.mode.show() self.start = Gtk.SpinButton(climb_rate=1.0) self.start.get_adjustment().step_increment = 1.0 self.start.set_range(-MAX_INT - 1, MAX_INT) self.pack_start(self.start, False, False, 6) self.start.show() self.start.connect_after('activate', self._on_entry__activate) self.and_label = Gtk.Label(label=_("And")) self.pack_start(self.and_label, False, False, 0) self.and_label.show() self.end = Gtk.SpinButton(climb_rate=1.0) self.end.get_adjustment().step_increment = 1.0 self.end.set_range(-MAX_INT - 1, MAX_INT) self.pack_start(self.end, False, False, 6) self.end.show() self.end.connect_after('activate', self._on_entry__activate) for option in (LowerThan, EqualsTo, GreaterThan, Between): self.add_option(option) self.mode.select_item_by_position(0) def set_digits(self, digits): """ Number of decimal place to be displayed :param digits: number of decimal places """ self.start.set_digits(digits) self.end.set_digits(digits) # # Private # def _update_visibility(self): option = self.mode.get_selected_data() numbers = option.numbers if numbers == 0: self.start.hide() self.and_label.hide() self.end.hide() elif numbers == 1: self.start.show() self.and_label.hide() self.end.hide() elif numbers == 2: self.start.show() self.and_label.show() self.end.show() # # Callbacks # def _on_entry__activate(self, entry): self.emit('changed') def _on_mode__content_changed(self, combo): self._update_visibility() self.emit('changed') # # SearchFilter # def get_state(self): # Using Decimals for better precision. start_value = Decimal("%.2f" % self.start.get_value()) end_value = Decimal("%.2f" % self.end.get_value()) option = self.mode.get_selected_data() start, end = option().get_interval(start_value, end_value) return NumberIntervalQueryState(filter=self, start=start, end=end) def set_state(self, start, end): self.start.set_value(start) self.end.set_value(end) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.mode def get_description(self): desc = '' option = self.mode.get_selected_data() if option is not None: desc += option.name if option.numbers > 0: start = self.start.get_value_as_int() if option.numbers == 1: desc += ' %d' % start elif option.numbers == 2: end = self.end.get_value_as_int() desc += ' %d %s %d' % ( start, self.and_label.get_text(), end, ) if desc: return '%s %s' % (self.get_title_label().get_text(), desc) # # Public API # def add_option(self, option_type, position=-2): """ Adds a date option :param option_type: option to add :type option_type: a :class:`NumberSearchOption` subclass """ option = option_type() num = len(self.mode) + position self.mode.insert_item(num, option.name, option_type) self._options[option_type] = option
class ComboSearchFilter(SearchFilter): """ - a label - a combo with a set of predefined item to select from """ __gtype_name__ = 'ComboSearchFilter' def __init__(self, label='', values=None): """ Create a new ComboSearchFilter object. :param label: label of the search filter :param values: items to put in the combo, see :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill` """ self._block_updates = False SearchFilter.__init__(self, label=label) label = gtk.Label(label) self.pack_start(label, False, False) label.show() self.title_label = label # We create the mode, but it will only be added to this box when # enable_advanced is called self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) for option in (ComboEquals, ComboDifferent): self.add_option(option) self.mode.select_item_by_position(0) self.combo = ProxyComboBox() if values: self.update_values(values) self.combo.connect('content-changed', self._on_combo__content_changed) self.pack_start(self.combo, False, False, 6) self.combo.show() # # SearchFilter # def get_state(self): value = self.combo.get_selected_data() mode = self.mode.get_selected_data() state = NumberQueryState(filter=self, value=value, mode=mode.mode) if hasattr(value, 'id'): state.value_id = value.id return state def set_state(self, value, value_id=None, mode=None): if mode is None: mode = NumberQueryState.EQUALS if value_id is not None: for item in self.combo.get_model_items().values(): if item is None: continue if item.id == value_id: value = item break self.select(value) def update_values(self, values): self._block_updates = True self.combo.prefill(values) self._block_updates = False def get_title_label(self): return self.title_label def get_mode_combo(self): return self.combo def get_description(self): desc = '' data = self.combo.get_selected_data() if data is not None: desc += self.combo.get_selected_label() return '%s %s' % (self.title_label.get_text(), desc,) # # Public API # def add_option(self, option_type, position=0): """ Adds an option :param option_type: option to add :type option_type: a :class:`ComboSearchOption` subclass """ option = option_type() num = len(self.mode) + position self.mode.insert_item(num, option.name, option_type) def select(self, data): """ selects an item in the combo :param data: what to select """ self.combo.select(data) def enable_advanced(self): self.pack_start(self.mode, False, False, 6) self.reorder_child(self.mode, 1) self.mode.show() # # Callbacks # def _on_mode__content_changed(self, combo): if not self._block_updates: self.emit('changed') def _on_combo__content_changed(self, mode): if not self._block_updates: self.emit('changed')
class AccountEditor(BaseEditor): """ Account Editor """ gladefile = "AccountEditor" proxy_widgets = ['description', 'code'] model_type = Account model_name = _('Account') def __init__(self, store, model=None, parent_account=None): self._last_account_type = None self._bank_number = -1 self._bank_widgets = [] self._bank_option_widgets = [] self._option_fields = {} self._test_button = None self.existing = model is not None self.parent_account = parent_account self.bank_model = _TemporaryBankAccount() BaseEditor.__init__(self, store, model) action_area = self.main_dialog.action_area action_area.set_layout(Gtk.ButtonBoxStyle.END) action_area.pack_start(self._test_button, False, False, 0) action_area.set_child_secondary(self._test_button, True) self._test_button.show() # # BaseEditor hooks # def create_model(self, store): return Account(description=u"", account_type=Account.TYPE_CASH, store=store) def _setup_widgets(self): self._test_button = Gtk.Button(_("Print a test bill")) self._test_button.connect('clicked', self._on_test_button__clicked) self.parent_accounts = AccountTree(with_code=False, create_mode=True) self.parent_accounts.connect( 'selection-changed', self._on_parent_accounts__selection_changed) self.tree_box.pack_start(self.parent_accounts, True, True, 0) self.tree_box.reorder_child(self.parent_accounts, 0) if sysparam.compare_object('IMBALANCE_ACCOUNT', self.model): self.account_type.set_sensitive(False) self.account_type.prefill(Account.account_type_descriptions) account_type = self.model.account_type self.parent_accounts.insert_initial(self.store, edited_account=self.model) if self.parent_account: account = self.parent_accounts.get_account_by_id( self.parent_account.id) self.parent_accounts.select(account) if not self.existing: account_type = account.account_type self.account_type.select(account_type) self.parent_accounts.show() def setup_proxies(self): self._setup_widgets() self.add_proxy(self.model, AccountEditor.proxy_widgets) def validate_confirm(self): if not self.model.description: return False account = self.parent_accounts.get_selected() if not account: return True return account.selectable def on_confirm(self): new_parent = self.parent_accounts.get_selected() if new_parent: new_parent = new_parent.account if new_parent != self.model: self.model.parent = new_parent self.model.account_type = self.account_type.get_selected() self._save_bank() def refresh_ok(self, value): BaseEditor.refresh_ok(self, value) account_type = self.account_type.get_selected() if account_type != Account.TYPE_BANK: value = False self._test_button.set_sensitive(value) # Private def _save_bank(self): bank_account = self.model.bank if not bank_account: bank_account = BankAccount(account=self.model, store=self.store) # FIXME: Who sets this to a str? bank_account.bank_account = unicode(self.bank_model.bank_account) bank_account.bank_branch = unicode(self.bank_model.bank_branch) if self._bank_number is not None: bank_account.bank_number = self.bank_model.bank_number self._save_bank_bill_options(bank_account) def _save_bank_bill_options(self, bank_account): for option, entry in self._option_fields.items(): value = unicode(entry.get_text()) bill_option = self.store.find(BillOption, bank_account=bank_account, option=option).one() if bill_option is None: bill_option = BillOption(store=self.store, bank_account=bank_account, option=option, value=value) bill_option.value = value def _add_widget(self, label, widget, options=False): n_rows = self.table.props.n_rows l = Gtk.Label() l.set_markup(label) l.props.xalign = 1.0 self.table.resize(n_rows + 1, 2) self.table.attach(l, 0, 1, n_rows, n_rows + 1, Gtk.AttachOptions.FILL, 0, 0, 0) self.table.attach(widget, 1, 2, n_rows, n_rows + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 0, 0, 0) if options: self._bank_option_widgets.extend([l, widget]) else: self._bank_widgets.extend([l, widget]) l.show() return l def _update_bank_type(self): self._remove_bank_option_widgets() bank_number = self.bank_type.get_selected() bank_info = None if bank_number: bank_info = get_bank_info_by_number(bank_number) self.bank_number = ProxyEntry() self.bank_number.props.data_type = int self.bank_number.set_sensitive(False) bank_number_lbl = self._add_widget(api.escape(_("Number:")), self.bank_number, options=True) self.bank_branch = ProxyEntry() self.bank_branch.connect('validate', self._on_bank_branch__validate, bank_info) self.bank_branch.props.data_type = 'str' self.bank_branch.props.mandatory = True self.bank_branch.model_attribute = "bank_branch" bank_branch_lbl = self._add_widget(api.escape(_("Agency:")), self.bank_branch, options=True) if bank_number is not None: bank_branch_lbl.show() self.bank_branch.show() else: bank_branch_lbl.hide() self.bank_account = ProxyEntry() self.bank_account.connect('validate', self._on_bank_account__validate, bank_info) self._add_widget(api.escape(_("Account:")), self.bank_account, options=True) self.bank_account.model_attribute = "bank_account" self.bank_account.props.data_type = 'str' if bank_number is not None: self.bank_account.props.mandatory = True self.bank_account.show() attributes = ['bank_account', 'bank_branch', 'bank_number'] if bank_number is not None: bank_number_lbl.show() self.bank_number.show() self.bank_model.bank_number = bank_number for i, option in enumerate(bank_info.get_extra_options()): name = 'option' + str(i) entry = ProxyEntry() entry.model_attribute = name setattr(self, name, entry) # Set the model attr too so it can be validated setattr(self.bank_model, name, u'') entry.props.data_type = 'str' entry.connect('validate', self._on_bank_option__validate, bank_info, option) name = option.replace('_', ' ').capitalize() self._add_widget("<i>%s</i>:" % api.escape(name), entry, options=True) entry.show() self._option_fields[option] = entry attributes.append(entry.model_attribute) else: bank_number_lbl.hide() self.bank_proxy = self.add_proxy(self.bank_model, attributes) self._fill_bank_account() def _fill_bank_account(self): if not self.model.bank: return self.bank_model.bank_branch = self.model.bank.bank_branch.encode( 'utf-8') self.bank_model.bank_account = self.model.bank.bank_account.encode( 'utf-8') self.bank_proxy.update('bank_branch') self.bank_proxy.update('bank_account') bill_options = list( self.store.find(BillOption, bank_account=self.model.bank)) for bill_option in bill_options: if bill_option.option is None: continue field_entry = self._option_fields.get(bill_option.option) if field_entry: field_entry.set_text(bill_option.value) def _update_account_type(self, account_type): if not self.account_type.get_sensitive(): return if account_type != Account.TYPE_BANK: self._remove_bank_widgets() self._remove_bank_option_widgets() self.code.set_sensitive(True) return self.code.set_sensitive(False) self.bank_type = ProxyComboBox() self._add_widget(api.escape(_("Bank:")), self.bank_type) self.bank_type.connect('content-changed', self._on_bank_type__content_changed) self.bank_type.show() banks = [(_('Generic'), None)] banks.extend([(b.description, b.bank_number) for b in get_all_banks()]) self.bank_type.prefill(banks) if self.model.bank: try: self.bank_type.select(self.model.bank.bank_number) except KeyError: self.bank_type.select(None) self._update_bank_type() def _remove_bank_widgets(self): for widget in self._bank_widgets: widget.get_parent().remove(widget) widget.destroy() self.table.resize(5, 2) self._bank_widgets = [] def _remove_bank_option_widgets(self): for widget in self._bank_option_widgets: widget.get_parent().remove(widget) widget.destroy() self.table.resize(5 + len(self._bank_widgets) / 2, 2) self._bank_option_widgets = [] self._option_fields = {} def _print_test_bill(self): try: bank_info = get_bank_info_by_number(self.bank_model.bank_number) except NotImplementedError: info(_("This bank does not support printing of bills")) return kwargs = dict( valor_documento=12345.67, data_vencimento=datetime.date.today(), data_documento=datetime.date.today(), data_processamento=datetime.date.today(), nosso_numero=u'24533', numero_documento=u'1138', sacado=[_(u"Drawee"), _(u"Address"), _(u"Details")], cedente=[_(u"Supplier"), _(u"Address"), _(u"Details"), ("CNPJ")], demonstrativo=[_(u"Demonstration")], instrucoes=[_(u"Instructions")], agencia=self.bank_model.bank_branch, conta=self.bank_model.bank_account, ) for opt in self.bank_model.options: kwargs[opt.option] = opt.value data = bank_info(**kwargs) print_report(BillTestReport, data) # Callbacks def _on_parent_accounts__selection_changed(self, objectlist, account): self.force_validation() def on_description__activate(self, entry): if self.validate_confirm(): self.confirm() def on_description__validate(self, entry, text): if not text: return ValidationError(_("Account description cannot be empty")) def on_account_type__content_changed(self, account_type): account_type = account_type.get_selected() if self._last_account_type == account_type: return self._update_account_type(account_type) self._last_account_type = account_type def _on_bank_type__content_changed(self, bank_type): bank_number = bank_type.get_selected() if self._bank_number == bank_number: return self._update_bank_type() self._bank_number = bank_number def _on_bank_branch__validate(self, entry, value, bank_info): if bank_info: try: bank_info.validate_field(value) except BoletoException as e: return ValidationError(str(e)) def _on_bank_account__validate(self, entry, value, bank_info): if bank_info: try: bank_info.validate_field(value) except BoletoException as e: return ValidationError(str(e)) def _on_bank_option__validate(self, entry, value, bank_info, option): try: bank_info.validate_option(option, value) except BoletoException as e: return ValidationError(str(e)) self.bank_model.set_option(option, value) def _on_test_button__clicked(self, button): self._print_test_bill()
class WorkOrderQuoteItemStep(SaleQuoteItemStep): """Third step for work order pre-sales Just like :class:`stoqlib.gui.wizards.salequotewizard.SaleQuoteItemStep`, but each item added here will be added to a workorder too (selected on a combo). """ # # Public API # def get_extra_columns(self): """Get some extra columns for the items list Subclasses can override this and add some extra columns. Those columns will be added just after the 'description' and before the 'quantity' columns. """ return [ Column('_equipment', title=_(u'Equipment'), data_type=str, ellipsize=Pango.EllipsizeMode.END) ] def setup_work_order(self, work_order): """Do some extra setup for the work order This is called at the initialization of this step. Subclasses can override this to do any extra setup they need on the work order. :param work_order: the |workorder| we are describing """ # # SaleQuoteItemStep # def setup_proxies(self): self._radio_group = None self._setup_work_orders_widgets() super(WorkOrderQuoteItemStep, self).setup_proxies() def get_order_item(self, sellable, price, quantity, batch=None, parent=None): item = super(WorkOrderQuoteItemStep, self).get_order_item(sellable, price, quantity, batch=batch, parent=parent) work_order = self._selected_workorder wo_item = work_order.add_sellable(sellable, price=price, batch=batch, quantity=quantity) wo_item.sale_item = item item._equipment = work_order.description return item def get_saved_items(self): for item in super(WorkOrderQuoteItemStep, self).get_saved_items(): wo_item = WorkOrderItem.get_from_sale_item(self.store, item) item._equipment = wo_item.order.description yield item def remove_items(self, items): # Remove the workorder items first to avoid reference problems for item in items: wo_item = WorkOrderItem.get_from_sale_item(self.store, item) # If the item's quantity_decreased changed in this step, the # synchronization between the 2 that happens on self.validate_step # would not have happened yet, meaning that order.remove_item # would try to return a wrong quantity to the stock. Force the # synchronization to avoid any problems like that wo_item.quantity_decreased = item.quantity_decreased wo_item.order.remove_item(wo_item) super(WorkOrderQuoteItemStep, self).remove_items(items) def get_columns(self): columns = [ Column('sellable.code', title=_(u'Code'), data_type=str, visible=False), Column('sellable.barcode', title=_(u'Barcode'), data_type=str, visible=False), Column('sellable.description', title=_('Description'), data_type=str, expand=True, format_func=self._format_description, format_func_data=True), ] columns.extend(self.get_extra_columns()) columns.extend([ Column('quantity', title=_(u'Quantity'), data_type=decimal.Decimal, format_func=format_quantity), Column('base_price', title=_('Original Price'), data_type=currency), Column('price', title=_('Sale Price'), data_type=currency), Column('sale_discount', title=_('Discount'), data_type=decimal.Decimal, format_func=get_formatted_percentage), Column('total', title=_(u'Total'), data_type=currency), ]) return columns def validate_step(self): # When finishing the wizard, make sure that all modifications on # sale items on this step are propagated to their work order items for sale_item in self.model.get_items(): wo_item = WorkOrderItem.get_from_sale_item(self.store, sale_item) wo_item.quantity = sale_item.quantity wo_item.quantity_decreased = sale_item.quantity_decreased wo_item.price = sale_item.price return super(WorkOrderQuoteItemStep, self).validate_step() # # Private # def _format_description(self, item, data): return format_sellable_description(item.sellable, item.batch) def _setup_work_orders_widgets(self): self._work_orders_hbox = Gtk.HBox(spacing=6) self.item_vbox.pack_start(self._work_orders_hbox, False, True, 6) self.item_vbox.reorder_child(self._work_orders_hbox, 0) self._work_orders_hbox.show() label = Gtk.Label(label=_("Work order:")) self._work_orders_hbox.pack_start(label, False, True, 0) data = [] for wo in self.wizard.workorders: # The work order might be already approved if we are editing a sale if wo.can_approve(): wo.approve() self.setup_work_order(wo) data.append([wo.description, wo]) if len(data) <= _MAX_WORK_ORDERS_FOR_RADIO: self.work_orders_combo = None for desc, wo in data: self._add_work_order_radio(desc, wo) else: self.work_orders_combo = ProxyComboBox() self.work_orders_combo.prefill(data) self._selected_workorder = self.work_orders_combo.get_selected() self._work_orders_hbox.pack_start(self.work_orders_combo, False, False, 0) self._work_orders_hbox.show_all() def _add_work_order_radio(self, desc, workorder): radio = Gtk.RadioButton(group=self._radio_group, label=desc) radio._workorder = workorder radio.connect('toggled', self._on_work_order_radio__toggled) if self._radio_group is None: self._radio_group = radio self._selected_workorder = workorder self._work_orders_hbox.pack_start(radio, False, False, 6) radio.show_all() # # Callbacks # def on_work_orders_combo__content_changed(self, combo): self._selected_workorder = combo.get_selected() def _on_work_order_radio__toggled(self, radio): if not radio.get_active(): return self._selected_workorder = radio._workorder
class StringSearchFilter(SearchFilter): """ Contains: - a label - an entry :ivar entry: the entry :ivar label: the label """ def __init__(self, label, chars=0, container=None): """ Create a new StringSearchFilter object. :param label: label of the search filter :param chars: maximum number of chars used by the search entry """ self._container = container SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.pack_start(self.title_label, False, False) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = HintedEntry() self.entry.set_hint(_("Search")) self.entry.show_hint() self.entry.props.secondary_icon_sensitive = False self.entry.set_icon_from_stock(gtk.ENTRY_ICON_PRIMARY, gtk.STOCK_FIND) self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR) self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_SECONDARY, _("Clear the search")) self.entry.connect("icon-release", self._on_entry__icon_release) self.entry.connect('activate', self._on_entry__activate) self.entry.connect('changed', self._on_entry__changed) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() for option in (ContainsAll, ContainsExactly, DoesNotContain): self._add_option(option) self.mode.select_item_by_position(0) def _add_option(self, option_type, position=-2): option = option_type() num = abs(position) self.mode.insert_item(num, option.name, option_type) self._options[option_type] = option # # Callbacks # def _on_mode__content_changed(self, combo): self.emit('changed') def _on_entry__activate(self, entry): self.emit('changed') def _on_entry__changed(self, entry): entry.props.secondary_icon_sensitive = bool(entry.get_text()) def _position_filter_menu(self, data): window = self.entry.get_icon_window(gtk.ENTRY_ICON_PRIMARY) x, y = window.get_origin() y += window.get_size()[1] border = self.entry.style_get_property('progress-border') if border is not None: y += border.bottom return (x, y, True) def _on_entry__icon_release(self, entry, icon_pos, event): if icon_pos == gtk.ENTRY_ICON_SECONDARY: entry.set_text("") entry.grab_focus() self.emit('changed') elif icon_pos == gtk.ENTRY_ICON_PRIMARY: # We don't need create popup filters if haven't search columns. if (not self._container or not hasattr(self._container, 'menu') or not self._container.menu): return self._container.menu.popup(None, None, self._position_filter_menu, 0, event.time) # # SearchFilter # def get_state(self): option = self.mode.get_selected_data() return StringQueryState(filter=self, text=self.entry.get_text(), mode=option and option.mode) def set_state(self, text, mode=None): self.entry.set_text(text) if mode is not None: self.mode.select_item_by_position(mode) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.mode def get_description(self): desc = self.entry.get_text() if desc: mode = self.mode.get_selected_label() return '%s %s "%s"' % ( self.title_label.get_text(), mode, desc, ) # # Public API # def enable_advanced(self): self.mode.show() def set_label(self, label): self.title_label.set_text(label)
class StringSearchFilter(SearchFilter): """ - a label - an entry @ivar entry: the entry @ivar label: the label """ def __init__(self, label, chars=0): """ Create a new StringSearchFilter object. @param label: label of the search filter @param chars: maximum number of chars used by the search entry """ SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.pack_start(self.title_label, False, False) self.title_label.show() self._options = {} self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.entry = gtk.Entry() self.entry.connect('activate', self._on_entry__activate) if chars: self.entry.set_width_chars(chars) self.pack_start(self.entry, False, False, 6) self.entry.show() for option in (Contains, DoesNotContain): self._add_option(option) self.mode.select_item_by_position(0) def _add_option(self, option_type, position=-2): option = option_type() num = len(self.mode) + position self.mode.insert_item(num, option.name, option_type) self._options[option_type] = option # # Callbacks # def _on_mode__content_changed(self, combo): self.emit('changed') def _on_entry__activate(self, entry): self.emit('changed') # # SearchFilter # def get_state(self): option = self.mode.get_selected_data() return StringQueryState(filter=self, text=self.entry.get_text(), mode=option.mode) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.mode def get_description(self): desc = self.entry.get_text() if desc: mode = self.mode.get_selected_label() return '%s %s "%s"' % (self.title_label.get_text(), mode, desc,) # # Public API # def enable_advaced(self): self.mode.show() def set_label(self, label): self.title_label.set_text(label)
class ComboSearchFilter(SearchFilter): """ - a label - a combo with a set of predefined item to select from """ __gtype_name__ = 'ComboSearchFilter' def __init__(self, label='', values=None): """ Create a new ComboSearchFilter object. :param label: label of the search filter :param values: items to put in the combo, see :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill` """ self._block_updates = False SearchFilter.__init__(self, label=label) label = gtk.Label(label) self.pack_start(label, False, False) label.show() self.title_label = label self.combo = ProxyComboBox() if values: self.update_values(values) self.combo.connect('content-changed', self._on_combo__content_changed) self.pack_start(self.combo, False, False, 6) self.combo.show() # # SearchFilter # def get_state(self): value = self.combo.get_selected_data() state = NumberQueryState(filter=self, value=value) if hasattr(value, 'id'): state.value_id = value.id return state def set_state(self, value, value_id=None): if value_id is not None: for item in self.combo.get_model_items().values(): if item is None: continue if item.id == value_id: value = item break self.select(value) def update_values(self, values): self._block_updates = True self.combo.prefill(values) self._block_updates = False def get_title_label(self): return self.title_label def get_mode_combo(self): return self.combo def get_description(self): desc = '' data = self.combo.get_selected_data() if data is not None: desc += self.combo.get_selected_label() return '%s %s' % ( self.title_label.get_text(), desc, ) # # Public API # def select(self, data): """ selects an item in the combo :param data: what to select """ self.combo.select(data) # # Callbacks # def _on_combo__content_changed(self, mode): if not self._block_updates: self.emit('changed')
def setUp(self): self.combo = ProxyComboBox()
class WorkOrderQuoteItemStep(SaleQuoteItemStep): """Third step for work order pre-sales Just like :class:`stoqlib.gui.wizards.salequotewizard.SaleQuoteItemStep`, but each item added here will be added to a workorder too (selected on a combo). """ # # Public API # def get_extra_columns(self): """Get some extra columns for the items list Subclasses can override this and add some extra columns. Those columns will be added just after the 'description' and before the 'quantity' columns. """ return [Column('_equipment', title=_(u'Equipment'), data_type=str, ellipsize=pango.ELLIPSIZE_END)] def setup_work_order(self, work_order): """Do some extra setup for the work order This is called at the initialization of this step. Subclasses can override this to do any extra setup they need on the work order. :param work_order: the |workorder| we are describing """ # # SaleQuoteItemStep # def setup_proxies(self): self._radio_group = None self._setup_work_orders_widgets() super(WorkOrderQuoteItemStep, self).setup_proxies() def get_order_item(self, sellable, price, quantity, batch=None): item = super(WorkOrderQuoteItemStep, self).get_order_item( sellable, price, quantity, batch=batch) work_order = self._selected_workorder wo_item = work_order.add_sellable( sellable, price=price, batch=batch, quantity=quantity) wo_item.sale_item = item item._equipment = work_order.description return item def get_saved_items(self): for item in super(WorkOrderQuoteItemStep, self).get_saved_items(): wo_item = WorkOrderItem.get_from_sale_item(self.store, item) item._equipment = wo_item.order.description yield item def remove_items(self, items): # Remove the workorder items first to avoid reference problems for item in items: wo_item = WorkOrderItem.get_from_sale_item(self.store, item) wo_item.order.remove_item(wo_item) super(WorkOrderQuoteItemStep, self).remove_items(items) def get_columns(self): columns = [ Column('sellable.code', title=_(u'Code'), data_type=str, visible=False), Column('sellable.barcode', title=_(u'Barcode'), data_type=str, visible=False), Column('sellable.description', title=_('Description'), data_type=str, expand=True, format_func=self._format_description, format_func_data=True), ] columns.extend(self.get_extra_columns()) columns.extend([ Column('quantity', title=_(u'Quantity'), data_type=decimal.Decimal, format_func=format_quantity), Column('base_price', title=_('Original Price'), data_type=currency), Column('price', title=_('Sale Price'), data_type=currency), Column('sale_discount', title=_('Discount'), data_type=decimal.Decimal, format_func=get_formatted_percentage), Column('total', title=_(u'Total'), data_type=currency), ]) return columns def validate_step(self): # When finishing the wizard, make sure that all modifications on # sale items on this step are propagated to their work order items for sale_item in self.model.get_items(): wo_item = WorkOrderItem.get_from_sale_item(self.store, sale_item) wo_item.quantity = sale_item.quantity wo_item.quantity_decreased = sale_item.quantity_decreased wo_item.price = sale_item.price return super(WorkOrderQuoteItemStep, self).validate_step() # # Private # def _format_description(self, item, data): return format_sellable_description(item.sellable, item.batch) def _setup_work_orders_widgets(self): self._work_orders_hbox = gtk.HBox(spacing=6) self.item_vbox.pack_start(self._work_orders_hbox, False, True, 6) self.item_vbox.reorder_child(self._work_orders_hbox, 0) self._work_orders_hbox.show() label = gtk.Label(_("Work order:")) self._work_orders_hbox.pack_start(label, False, True) data = [] for wo in self.wizard.workorders: # The work order might be already approved if we are editing a sale if wo.can_approve(): wo.approve() self.setup_work_order(wo) data.append([wo.description, wo]) if len(data) <= _MAX_WORK_ORDERS_FOR_RADIO: self.work_orders_combo = None for desc, wo in data: self._add_work_order_radio(desc, wo) else: self.work_orders_combo = ProxyComboBox() self.work_orders_combo.prefill(data) self._selected_workorder = self.work_orders_combo.get_selected() self._work_orders_hbox.pack_start(self.work_orders_combo, False, False) self._work_orders_hbox.show_all() def _add_work_order_radio(self, desc, workorder): radio = gtk.RadioButton(group=self._radio_group, label=desc) radio.set_data('workorder', workorder) radio.connect('toggled', self._on_work_order_radio__toggled) if self._radio_group is None: self._radio_group = radio self._selected_workorder = workorder self._work_orders_hbox.pack_start(radio, False, False, 6) radio.show_all() # # Callbacks # def on_work_orders_combo__content_changed(self, combo): self._selected_workorder = combo.get_selected() def _on_work_order_radio__toggled(self, radio): if not radio.get_active(): return self._selected_workorder = radio.get_data('workorder')
class MassEditorWidget(Gtk.HBox): _editors = { currency: DecimalEditor, Decimal: DecimalEditor, str: UnicodeEditor, datetime.date: DateEditor, object: ObjectEditor, } def __init__(self, store, fields, results): self._store = store self._editor = None self._fields = fields self._results = results super(MassEditorWidget, self).__init__(spacing=6) self._setup_widgets() def _filter_fields(self, data_type): return [f for f in self._fields if f.data_type == data_type] def _setup_editor(self, field): # Reuse editor if its possible (not when data_type is an object, since # that requires changing the reference values) if (self._editor and field.data_type is not object and self._editor.data_type == field.data_type): self._editor.set_field(field) return if self._editor: self.editor_placeholder.remove(self._editor) other_fields = self._filter_fields(field.data_type) klass = self._editors[field.data_type] self._editor = klass(self._store, field, other_fields) self.editor_placeholder.add(self._editor) def _setup_widgets(self): label = Gtk.Label(label=_('Update')) self.pack_start(label, False, False, 0) self.field_combo = ProxyComboBox() self.field_combo.connect('changed', self._on_field_combo__changed) self.pack_start(self.field_combo, False, False, 0) self.editor_placeholder = Gtk.EventBox() self.pack_start(self.editor_placeholder, False, False, 0) self.apply_button = Gtk.Button(stock=Gtk.STOCK_APPLY) self.apply_button.connect('clicked', self._on_apply_button__clicked) self.pack_start(self.apply_button, False, False, 0) for field in self._fields: # Don't let the user edit unique fields for now if field.unique or field.read_only: continue self.field_combo.append_item(field.label, field) self.field_combo.select_item_by_position(0) def _apply(self): marker('Updating values') for i in self._results: self._editor.apply_operation(i) self._results.refresh(i) marker('Done updating values') # # Public API # def get_changed_objects(self): """Returns a set of all the changed objects""" objs = set() for field in self._fields: objs.update(field.new_values.keys()) return objs # # BaseEditorSlave # def confirm(self, dialog): marker('Saving data') objs = self.get_changed_objects() total = len(objs) for i, obj in enumerate(objs): for field in self._fields: field.save_value(obj) yield i, total # Flush soon, so that any errors triggered by database constraints # pop up. self._store.flush() marker('Done saving data') # # Callbacks # def _on_field_combo__changed(self, combo): self._setup_editor(combo.get_selected()) def _on_apply_button__clicked(self, button): self._apply()
class NumberSearchFilter(SearchFilter): """ A filter which helps you to search by a number interval. """ __gtype_name__ = 'NumberSearchFilter' def __init__(self, label=''): """ Create a new NumberSearchFilter object. @param label: name of the search filter """ self._options = {} SearchFilter.__init__(self, label=label) self.title_label = gtk.Label(label) self.title_label.set_alignment(1.0, 0.5) self.pack_start(self.title_label, False, False) self.title_label.show() self.mode = ProxyComboBox() self.mode.connect('content-changed', self._on_mode__content_changed) self.pack_start(self.mode, False, False, 6) self.mode.show() self.start = gtk.SpinButton(climb_rate=1.0) self.start.get_adjustment().step_increment = 1.0 self.start.set_range(-sys.maxint-1, sys.maxint) self.pack_start(self.start, False, False, 6) self.start.show() self.start.connect_after('activate', self._on_entry__activate) self.and_label = gtk.Label(_("And")) self.pack_start(self.and_label, False, False) self.and_label.show() self.end = gtk.SpinButton(climb_rate=1.0) self.end.get_adjustment().step_increment = 1.0 self.end.set_range(-sys.maxint-1, sys.maxint) self.pack_start(self.end, False, False, 6) self.end.show() self.end.connect_after('activate', self._on_entry__activate) for option in (LowerThan, EqualsTo, GreaterThan, Between): self.add_option(option) self.mode.select_item_by_position(0) def set_digits(self, digits): """ Number of decimal place to be displayed @param digits: number of decimal places """ self.start.set_digits(digits) self.end.set_digits(digits) # # Private # def _update_visibility(self): option = self.mode.get_selected_data() numbers = option.numbers if numbers == 0: self.start.hide() self.and_label.hide() self.end.hide() elif numbers == 1: self.start.show() self.and_label.hide() self.end.hide() elif numbers == 2: self.start.show() self.and_label.show() self.end.show() # # Callbacks # def _on_entry__activate(self, entry): self.emit('changed') def _on_mode__content_changed(self, combo): self._update_visibility() self.emit('changed') # # SearchFilter # def get_state(self): # Using Decimals for better precision. start_value = Decimal("%.2f" % self.start.get_value()) end_value = Decimal("%.2f" % self.end.get_value()) option = self.mode.get_selected_data() start, end = option().get_interval(start_value, end_value) return NumberIntervalQueryState(filter=self, start=start, end=end) def get_title_label(self): return self.title_label def get_mode_combo(self): return self.mode def get_description(self): desc = '' option = self.mode.get_selected_data() if option is not None: desc += option.name if option.numbers > 0: start = self.start.get_value_as_int() if option.numbers == 1: desc += ' %d' % start elif option.numbers == 2: end = self.end.get_value_as_int() desc += ' %d %s %d' % (start, self.and_label.get_text(), end,) if desc: return '%s %s' % (self.get_title_label().get_text(), desc) # # Public API # def add_option(self, option_type, position=-2): """ Adds a date option @param option_type: option to add @type option_type: a L{NumberSearchOption} subclass """ option = option_type() num = len(self.mode) + position self.mode.insert_item(num, option.name, option_type) self._options[option_type] = option
class AccountEditor(BaseEditor): """ Account Editor """ gladefile = "AccountEditor" proxy_widgets = ['description', 'code'] model_type = Account model_name = _('Account') def __init__(self, store, model=None, parent_account=None): self._last_account_type = None self._bank_number = -1 self._bank_widgets = [] self._bank_option_widgets = [] self._option_fields = {} self._test_button = None self.existing = model is not None self.parent_account = parent_account self.bank_model = _TemporaryBankAccount() BaseEditor.__init__(self, store, model) action_area = self.main_dialog.action_area action_area.set_layout(gtk.BUTTONBOX_END) action_area.pack_start(self._test_button, expand=False, fill=False) action_area.set_child_secondary(self._test_button, True) self._test_button.show() # # BaseEditor hooks # def create_model(self, store): return Account(description=u"", account_type=Account.TYPE_CASH, store=store) def _setup_widgets(self): self._test_button = gtk.Button(_("Print a test bill")) self._test_button.connect('clicked', self._on_test_button__clicked) self.parent_accounts = AccountTree(with_code=False, create_mode=True) self.parent_accounts.connect('selection-changed', self._on_parent_accounts__selection_changed) self.tree_box.pack_start(self.parent_accounts) self.tree_box.reorder_child(self.parent_accounts, 0) if self.model == sysparam(self.store).IMBALANCE_ACCOUNT: self.account_type.set_sensitive(False) self.account_type.prefill(Account.account_type_descriptions) account_type = self.model.account_type self.parent_accounts.insert_initial(self.store, edited_account=self.model) if self.parent_account: account = self.parent_accounts.get_account_by_id( self.parent_account.id) self.parent_accounts.select(account) if not self.existing: account_type = account.account_type self.account_type.select(account_type) self.parent_accounts.show() def setup_proxies(self): self._setup_widgets() self.add_proxy(self.model, AccountEditor.proxy_widgets) def validate_confirm(self): if not self.model.description: return False account = self.parent_accounts.get_selected() if not account: return True return account.selectable def on_confirm(self): new_parent = self.parent_accounts.get_selected() if new_parent: new_parent = new_parent.account if new_parent != self.model: self.model.parent = new_parent self.model.account_type = self.account_type.get_selected() self._save_bank() def refresh_ok(self, value): BaseEditor.refresh_ok(self, value) account_type = self.account_type.get_selected() if account_type != Account.TYPE_BANK: value = False self._test_button.set_sensitive(value) # Private def _save_bank(self): bank_account = self.model.bank if not bank_account: bank_account = BankAccount(account=self.model, store=self.store) # FIXME: Who sets this to a str? bank_account.bank_account = unicode(self.bank_model.bank_account) bank_account.bank_branch = unicode(self.bank_model.bank_branch) if self._bank_number is not None: bank_account.bank_number = self.bank_model.bank_number self._save_bank_bill_options(bank_account) def _save_bank_bill_options(self, bank_account): for option, entry in self._option_fields.items(): value = unicode(entry.get_text()) bill_option = self.store.find(BillOption, bank_account=bank_account, option=option).one() if bill_option is None: bill_option = BillOption(store=self.store, bank_account=bank_account, option=option, value=value) bill_option.value = value def _add_widget(self, label, widget, options=False): n_rows = self.table.props.n_rows l = gtk.Label() l.set_markup(label) l.props.xalign = 1.0 self.table.resize(n_rows + 1, 2) self.table.attach( l, 0, 1, n_rows, n_rows + 1, gtk.FILL, 0, 0, 0) self.table.attach( widget, 1, 2, n_rows, n_rows + 1, gtk.EXPAND | gtk.FILL, 0, 0, 0) if options: self._bank_option_widgets.extend([l, widget]) else: self._bank_widgets.extend([l, widget]) l.show() return l def _update_bank_type(self): self._remove_bank_option_widgets() bank_number = self.bank_type.get_selected() bank_info = None if bank_number: bank_info = get_bank_info_by_number(bank_number) self.bank_number = ProxyEntry() self.bank_number.props.data_type = int self.bank_number.set_sensitive(False) bank_number_lbl = self._add_widget(api.escape(_("Number:")), self.bank_number, options=True) self.bank_branch = ProxyEntry() self.bank_branch.connect('validate', self._on_bank_branch__validate, bank_info) self.bank_branch.props.data_type = 'str' self.bank_branch.props.mandatory = True self.bank_branch.model_attribute = "bank_branch" bank_branch_lbl = self._add_widget(api.escape(_("Agency:")), self.bank_branch, options=True) if bank_number is not None: bank_branch_lbl.show() self.bank_branch.show() else: bank_branch_lbl.hide() self.bank_account = ProxyEntry() self.bank_account.connect('validate', self._on_bank_account__validate, bank_info) self._add_widget(api.escape(_("Account:")), self.bank_account, options=True) self.bank_account.model_attribute = "bank_account" self.bank_account.props.data_type = 'str' if bank_number is not None: self.bank_account.props.mandatory = True self.bank_account.show() attributes = ['bank_account', 'bank_branch', 'bank_number'] if bank_number is not None: bank_number_lbl.show() self.bank_number.show() self.bank_model.bank_number = bank_number for i, option in enumerate(bank_info.get_extra_options()): name = 'option' + str(i) entry = ProxyEntry() entry.model_attribute = name setattr(self, name, entry) # Set the model attr too so it can be validated setattr(self.bank_model, name, u'') entry.props.data_type = 'str' entry.connect('validate', self._on_bank_option__validate, bank_info, option) self._add_widget("<i>%s</i>:" % (api.escape(option.capitalize()), ), entry, options=True) entry.show() self._option_fields[option] = entry attributes.append(entry.model_attribute) else: bank_number_lbl.hide() self.bank_proxy = self.add_proxy( self.bank_model, attributes) self._fill_bank_account() def _fill_bank_account(self): if not self.model.bank: return self.bank_model.bank_branch = self.model.bank.bank_branch.encode('utf-8') self.bank_model.bank_account = self.model.bank.bank_account.encode('utf-8') self.bank_proxy.update('bank_branch') self.bank_proxy.update('bank_account') bill_options = list(self.store.find(BillOption, bank_account=self.model.bank)) for bill_option in bill_options: if bill_option.option is None: continue field_entry = self._option_fields.get(bill_option.option) if field_entry: field_entry.set_text(bill_option.value) def _update_account_type(self, account_type): if not self.account_type.get_sensitive(): return if account_type != Account.TYPE_BANK: self._remove_bank_widgets() self._remove_bank_option_widgets() self.code.set_sensitive(True) return self.code.set_sensitive(False) self.bank_type = ProxyComboBox() self._add_widget(api.escape(_("Bank:")), self.bank_type) self.bank_type.connect('content-changed', self._on_bank_type__content_changed) self.bank_type.show() banks = [(_('Generic'), None)] banks.extend([(b.description, b.bank_number) for b in get_all_banks()]) self.bank_type.prefill(banks) if self.model.bank: try: self.bank_type.select(self.model.bank.bank_number) except KeyError: self.bank_type.select(None) self._update_bank_type() def _remove_bank_widgets(self): for widget in self._bank_widgets: widget.get_parent().remove(widget) widget.destroy() self.table.resize(5, 2) self._bank_widgets = [] def _remove_bank_option_widgets(self): for widget in self._bank_option_widgets: widget.get_parent().remove(widget) widget.destroy() self.table.resize(5 + len(self._bank_widgets) / 2, 2) self._bank_option_widgets = [] self._option_fields = {} def _print_test_bill(self): try: bank_info = get_bank_info_by_number(self.bank_model.bank_number) except NotImplementedError: info(_("This bank does not support printing of bills")) return kwargs = dict( valor_documento=12345.67, data_vencimento=datetime.date.today(), data_documento=datetime.date.today(), data_processamento=datetime.date.today(), nosso_numero=u'624533', numero_documento=u'1138', sacado=[_(u"Drawee"), _(u"Address"), _(u"Details")], cedente=_(u"Supplier"), demonstrativo=[_(u"Demonstration")], instrucoes=[_(u"Instructions")], agencia=self.bank_model.bank_branch, conta=self.bank_model.bank_account, ) for opt in self.bank_model.options: kwargs[opt.option] = opt.value data = bank_info(**kwargs) print_report(BillTestReport, data) # Callbacks def _on_parent_accounts__selection_changed(self, objectlist, account): self.force_validation() def on_description__activate(self, entry): if self.validate_confirm(): self.confirm() def on_description__validate(self, entry, text): if not text: return ValidationError(_("Account description cannot be empty")) def on_account_type__content_changed(self, account_type): account_type = account_type.get_selected() if self._last_account_type == account_type: return self._update_account_type(account_type) self._last_account_type = account_type def _on_bank_type__content_changed(self, bank_type): bank_number = bank_type.get_selected() if self._bank_number == bank_number: return self._update_bank_type() self._bank_number = bank_number def _on_bank_branch__validate(self, entry, value, bank_info): if bank_info: try: bank_info.validate_field(value) except BoletoException, e: return ValidationError(str(e))