From 491ee2313ea545f9620670ca9e391e79d4d7cf7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 26 Sep 2008 11:44:19 +0200 Subject: [PATCH] Add operator on search widgets --- .../view_form/widget_search/calendar.py | 82 +++++++++++++---- .../window/view_form/widget_search/char.py | 53 ++++++++--- .../view_form/widget_search/checkbox.py | 1 + .../window/view_form/widget_search/float.py | 17 +++- .../window/view_form/widget_search/form.py | 12 ++- .../window/view_form/widget_search/integer.py | 79 +++++++++++++---- .../view_form/widget_search/reference.py | 88 ++++++++++++++----- .../view_form/widget_search/selection.py | 70 ++++++++++----- 8 files changed, 308 insertions(+), 94 deletions(-) diff --git a/tryton/gui/window/view_form/widget_search/calendar.py b/tryton/gui/window/view_form/widget_search/calendar.py index 3eca50e875..0291ce4419 100644 --- a/tryton/gui/window/view_form/widget_search/calendar.py +++ b/tryton/gui/window/view_form/widget_search/calendar.py @@ -9,6 +9,7 @@ from tryton.common import DT_FORMAT from tryton.common import date_widget from tryton.translate import date_format import mx.DateTime +import gobject _ = gettext.gettext @@ -23,7 +24,23 @@ class Calendar(Interface): self.format = date_format() + self.liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + self.combo = gtk.ComboBox(self.liststore) + cell = gtk.CellRendererText() + self.combo.pack_start(cell, True) + self.combo.add_attribute(cell, 'text', 1) + for oper in (['=', _('at')], + ['between', _('between')], + ['not between', _('not between')], + ['!=', _('not at')], + ): + self.liststore.append(oper) + self.combo.set_active(0) + self.widget.pack_start(self.combo, False, False) + self.combo.connect('changed', self._changed) + self.widget1 = date_widget.ComplexEntry(self.format, spacing=3) + self.widget1.show() self.entry1 = self.widget1.widget self.entry1.set_property('width-chars', 10) self.entry1.set_property('activates_default', True) @@ -41,7 +58,8 @@ class Calendar(Interface): self.eb1.add(img) self.widget.pack_start(self.eb1, expand=False, fill=False) - self.widget.pack_start(gtk.Label('-'), expand=False, fill=False) + self.separator = gtk.Label('-') + self.widget.pack_start(self.separator, expand=False, fill=False) self.widget2 = date_widget.ComplexEntry(self.format, spacing=3) self.entry2 = self.widget2.widget @@ -61,8 +79,22 @@ class Calendar(Interface): self.eb2.add(img) self.widget.pack_start(self.eb2, expand=False, fill=False) + self.widget.show_all() + self._changed(self.combo) + tooltips.enable() + def _changed(self, widget): + oper = self.liststore.get_value(self.combo.get_active_iter(), 0) + if oper in ('=', '!='): + self.entry2.hide() + self.separator.hide() + self.eb2.hide() + else: + self.entry2.show() + self.separator.show() + self.eb2.show() + def _date_get(self, value): try: date = mx.DateTime.strptime(value, self.format) @@ -71,14 +103,24 @@ class Calendar(Interface): return date.strftime(DT_FORMAT) def _value_get(self): - res = [] - val = self._date_get(self.entry1.get_text()) - if val: - res.append((self.name, '>=', val)) - val = self._date_get(self.entry2.get_text()) - if val: - res.append((self.name, '<=', val)) - return res + oper = self.liststore.get_value(self.combo.get_active_iter(), 0) + if oper in ('=', '!='): + return [(self.name, oper, self._date_get(self.entry1.get_text()))] + else: + res = [] + if oper == 'between': + oper1 = '>=' + oper2 = '<=' + else: + oper1 = '<=' + oper2 = '>=' + val = self._date_get(self.entry1.get_text()) + if val: + res.append((self.name, oper1, val)) + val = self._date_get(self.entry2.get_text()) + if val: + res.append((self.name, oper2, val)) + return res def _value_set(self, value): def conv(value): @@ -90,16 +132,18 @@ class Calendar(Interface): except: return '' - val = conv(value[0]) - if val: - self.entry1.set_text() - else: - self.entry1.clear() - val = conv(value[1]) - if val: - self.entry2.set_text() - else: + i = self.liststore.get_iter_root() + while i: + if self.liststore.get_value(i, 0) == value[0]: + self.combo.set_active_iter(i) + break + i = self.liststore.iter_next(i) + + self.entry1.set_text(conv(value[1])) + if len(value) == 2: self.entry2.clear() + else: + self.entry2.set_text(conv(value[2])) value = property(_value_get, _value_set) @@ -134,7 +178,7 @@ class Calendar(Interface): win.destroy() def clear(self): - self.value = ('', '') + self.value = ('=', '') def sig_activate(self, fct): self.entry1.connect_after('activate', fct) diff --git a/tryton/gui/window/view_form/widget_search/char.py b/tryton/gui/window/view_form/widget_search/char.py index 043bb6ae70..97654f17e2 100644 --- a/tryton/gui/window/view_form/widget_search/char.py +++ b/tryton/gui/window/view_form/widget_search/char.py @@ -2,6 +2,7 @@ import gtk import gettext from interface import Interface +import gobject _ = gettext.gettext @@ -13,29 +14,57 @@ class Char(Interface): attrs = {} super(Char, self).__init__(name, parent, attrs) - self.widget = gtk.Entry() - self.widget.set_max_length(int(attrs.get('size', 0))) - self.widget.set_width_chars(5) - self.widget.set_property('activates_default', True) + self.widget = gtk.HBox() + + self.liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + self.combo = gtk.ComboBox(self.liststore) + cell = gtk.CellRendererText() + self.combo.pack_start(cell, True) + self.combo.add_attribute(cell, 'text', 1) + for oper in (['ilike', _('contains')], + ['not ilike', _('not contains')], + ['=', _('equal')], + ['!=', _('different')], + ): + self.liststore.append(oper) + self.combo.set_active(0) + self.widget.pack_start(self.combo, False, False) + + self.entry = gtk.Entry() + self.entry.set_max_length(int(attrs.get('size', 0))) + self.entry.set_width_chars(5) + self.entry.set_property('activates_default', True) + self.widget.pack_start(self.entry, True, True) + self.widget.show_all() def _value_get(self): - value = self.widget.get_text() - if value: - return [(self.name, self.attrs.get('comparator', 'ilike'), value)] + value = self.entry.get_text() + oper = self.liststore.get_value(self.combo.get_active_iter(), 0) + if value or oper != 'ilike': + if oper == '=' and not value: + value = False + return [(self.name, oper, value)] else: return [] def _value_set(self, value): - self.widget.set_text(value or '') + i = self.liststore.get_iter_root() + while i: + if self.liststore.get_value(i, 0) == value[0]: + self.combo.set_active_iter(i) + break + i = self.liststore.iter_next(i) + self.entry.set_text(value[1] or '') value = property(_value_get, _value_set) def clear(self): - self.value = '' + self.value = ['ilike', ''] def _readonly_set(self, value): - self.widget.set_editable(not value) - self.widget.set_sensitive(not value) + self.combo.set_sensitive(not value) + self.entry.set_editable(not value) + self.entry.set_sensitive(not value) def sig_activate(self, fct): - self.widget.connect_after('activate', fct) + self.entry.connect_after('activate', fct) diff --git a/tryton/gui/window/view_form/widget_search/checkbox.py b/tryton/gui/window/view_form/widget_search/checkbox.py index 16577c8a16..2da3e4dc86 100644 --- a/tryton/gui/window/view_form/widget_search/checkbox.py +++ b/tryton/gui/window/view_form/widget_search/checkbox.py @@ -28,6 +28,7 @@ class CheckBox(Interface): completion.set_model(self.widget.get_model()) self.widget.child.set_completion(completion) completion.set_text_column(0) + self.widget.show() def sig_key_press(self, widget, event): if event.type == gtk.gdk.KEY_PRESS \ diff --git a/tryton/gui/window/view_form/widget_search/float.py b/tryton/gui/window/view_form/widget_search/float.py index 3fe4b8ce95..32ebb45521 100644 --- a/tryton/gui/window/view_form/widget_search/float.py +++ b/tryton/gui/window/view_form/widget_search/float.py @@ -33,13 +33,24 @@ class Float(Integer): else: return locale.format('%.' + str(self.digits[1]) + 'f', value or 0.0, True) - self.entry1.set_text(conv(value[0])) - self.entry2.set_text(conv(value[0])) + + i = self.liststore.get_iter_root() + while i: + if self.liststore.get_value(i, 0) == value[0]: + self.combo.set_active_iter(i) + break + i = self.liststore.iter_next(i) + + self.entry1.set_text(conv(value[1])) + if len(value) == 2: + self.entry2.set_text('') + else: + self.entry2.set_text(conv(value[2])) value = property(_value_get, _value_set) def clear(self): - self.value = (False, False) + self.value = ('=', False, False) def sig_insert_text(self, widget, new_text, new_text_length, position): value = widget.get_text() diff --git a/tryton/gui/window/view_form/widget_search/form.py b/tryton/gui/window/view_form/widget_search/form.py index 7154635dda..14d1f525f8 100644 --- a/tryton/gui/window/view_form/widget_search/form.py +++ b/tryton/gui/window/view_form/widget_search/form.py @@ -21,6 +21,7 @@ class _container(object): def new(self, col=8): self.col = col+1 table = gtk.Table(1, col) + table.show() table.set_homogeneous(False) table.set_col_spacings(3) table.set_row_spacings(0) @@ -49,7 +50,9 @@ class _container(object): (table, i, j) = self.cont[-1] if name: vbox = gtk.VBox(homogeneous=False, spacing=1) + vbox.show() label = gtk.Label(name) + label.show() label.set_alignment(0.0, 0.5) vbox.pack_start(label, expand=False) vbox.pack_start(widget, expand=expand, fill=True) @@ -81,7 +84,9 @@ class Parse(object): self.add_widget_end = [] self.container = None self.spin_limit = gtk.SpinButton(climb_rate=1, digits=0) + self.spin_limit.show() self.spin_offset = gtk.SpinButton(climb_rate=1, digits=0) + self.spin_offset.show() self.title = 'Form' self.notebooks = [] self.dict_widget = {} @@ -143,6 +148,7 @@ class Parse(object): hb_param.pack_start(self.spin_offset, expand=False, fill=False) + hb_param.show_all() return hb_param def _psr_end(self, name): @@ -161,10 +167,13 @@ class Parse(object): psr.Parse(xml_data) vbox = gtk.VBox() + vbox.show() vbox.pack_start(self.container.pop()) expander = gtk.Expander(_('Advanced Search')) + expander.show() vbox2 = gtk.VBox() + vbox2.show() self.container = _container(max_width) self.container.new(self.col) @@ -175,7 +184,6 @@ class Parse(object): vbox2.pack_start(self.container.pop()) vbox2.pack_start(self.add_parameters()) - vbox2.show_all() expander.add(vbox2) vbox.pack_start(expander) @@ -199,7 +207,7 @@ class Form(object): if self.parent: width = self.parent.size_request()[0] (self.widgets, self.widget) = parser.parse(xml, width) - self.widget.show_all() + self.widget.show() self.spin_limit = parser.spin_limit self.spin_limit.connect('value-changed', self.limit_changed) self.spin_limit.set_activates_default(True) diff --git a/tryton/gui/window/view_form/widget_search/integer.py b/tryton/gui/window/view_form/widget_search/integer.py index 57a6cf638b..cd7181d3ea 100644 --- a/tryton/gui/window/view_form/widget_search/integer.py +++ b/tryton/gui/window/view_form/widget_search/integer.py @@ -3,6 +3,7 @@ import gtk from interface import Interface import locale import gettext +import gobject _ = gettext.gettext @@ -12,6 +13,22 @@ class Integer(Interface): def __init__(self, name, parent, attrs=None): super(Integer, self).__init__(name, parent, attrs=attrs) self.widget = gtk.HBox(spacing=3) + + self.liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + self.combo = gtk.ComboBox(self.liststore) + cell = gtk.CellRendererText() + self.combo.pack_start(cell, True) + self.combo.add_attribute(cell, 'text', 1) + for oper in (['=', _('equal')], + ['between', _('between')], + ['not between', _('not between')], + ['!=', _('not equal')], + ): + self.liststore.append(oper) + self.combo.set_active(0) + self.widget.pack_start(self.combo, False, False) + self.combo.connect('changed', self._changed) + self.entry1 = gtk.Entry() self.entry1.set_max_length(0) self.entry1.set_width_chars(5) @@ -19,7 +36,8 @@ class Integer(Interface): self.entry1.set_alignment(1.0) self.entry1.connect('insert_text', self.sig_insert_text) self.widget.pack_start(self.entry1, expand=False, fill=True) - self.widget.pack_start(gtk.Label('-'), expand=False, fill=False) + self.separator = gtk.Label('-') + self.widget.pack_start(self.separator, expand=False, fill=False) self.entry2 = gtk.Entry() self.entry2.set_max_length(0) self.entry2.set_width_chars(5) @@ -28,6 +46,18 @@ class Integer(Interface): self.entry2.connect('insert_text', self.sig_insert_text) self.widget.pack_start(self.entry2, expand=False, fill=True) + self.widget.show_all() + self._changed(self.combo) + + def _changed(self, widget): + oper = self.liststore.get_value(self.combo.get_active_iter(), 0) + if oper in ('=', '!='): + self.entry2.hide() + self.separator.hide() + else: + self.entry2.show() + self.separator.show() + def _value_get(self): try: value1 = locale.atoi(self.entry1.get_text()) @@ -40,19 +70,25 @@ class Integer(Interface): return self._get_clause(value1, value2) def _get_clause(self, value1, value2): - res = [] - if value1 > value2: - if value2 != 0: - res.append((self.name, '>=', value2)) - res.append((self.name, '<=', value1)) + oper = self.liststore.get_value(self.combo.get_active_iter(), 0) + if oper in ('=', '!='): + if self.entry1.get_text(): + return [(self.name, oper, value1)] else: - res.append((self.name, '>=', value1)) - elif value1 < value2: - res.append((self.name, '>=', value1)) - res.append((self.name, '<=', value2)) - elif value1 == value2 and value1 != 0: - res.append((self.name, '=', value1)) - return res + return [] + else: + res = [] + if oper == 'between': + oper1 = '>=' + oper2 = '<=' + else: + oper1 = '<=' + oper2 = '>=' + if value1: + res.append((self.name, oper1, value1)) + if value2: + res.append((self.name, oper2, value2)) + return res def _value_set(self, value): def conv(value): @@ -60,13 +96,24 @@ class Integer(Interface): return '' else: return locale.format('%d', value or 0, True) - self.entry1.set_text(conv(value[0])) - self.entry2.set_text(conv(value[1])) + + i = self.liststore.get_iter_root() + while i: + if self.liststore.get_value(i, 0) == value[0]: + self.combo.set_active_iter(i) + break + i = self.liststore.iter_next(i) + + self.entry1.set_text(conv(value[1])) + if len(value) == 2: + self.entry2.set_text('') + else: + self.entry2.set_text(conv(value[2])) value = property(_value_get, _value_set) def clear(self): - self.value = (False, False) + self.value = ('=', False, False) def sig_activate(self, fct): self.entry1.connect_after('activate', fct) diff --git a/tryton/gui/window/view_form/widget_search/reference.py b/tryton/gui/window/view_form/widget_search/reference.py index b2018e0255..250c9ca357 100644 --- a/tryton/gui/window/view_form/widget_search/reference.py +++ b/tryton/gui/window/view_form/widget_search/reference.py @@ -4,6 +4,7 @@ import gettext from interface import Interface import tryton.rpc as rpc import tryton.common as common +import gobject _ = gettext.gettext @@ -14,8 +15,26 @@ class Reference(Interface): if attrs is None: attrs = {} super(Reference, self).__init__(name, parent, attrs) - self.widget = gtk.combo_box_entry_new_text() - self.widget.child.set_editable(False) + + self.widget = gtk.HBox() + + self.liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + self.combo = gtk.ComboBox(self.liststore) + cell = gtk.CellRendererText() + self.combo.pack_start(cell, True) + self.combo.add_attribute(cell, 'text', 1) + for oper in (['like', _('equal')], + ['not like', _('different')], + ): + self.liststore.append(oper) + self.combo.set_active(0) + self.widget.pack_start(self.combo, False, False) + + self.entry = gtk.combo_box_entry_new_text() + self.entry.child.set_editable(True) + self.entry.child.set_property('activates_default', True) + self.entry.child.connect('key_press_event', self.sig_key_press) + self.entry.set_focus_chain([self.entry.child]) self._selection = {} selection = attrs.get('selection', []) if 'relation' in attrs: @@ -37,41 +56,70 @@ class Reference(Interface): selection.sort(lambda x, y: cmp(x[1], y[1])) attrs['selection'] = selection self.set_popdown(selection) - - def get_model(self): - res = self.widget.child.get_text() - return self._selection.get(res, False) + self.widget.pack_start(self.entry, True, True) + self.widget.show_all() def set_popdown(self, selection): - model = self.widget.get_model() + model = self.entry.get_model() model.clear() lst = [] for (i, j) in selection: name = str(j) if type(i) == type(1): - name += ' ('+str(i)+')' + name += ' (' + str(i) + ')' lst.append(name) self._selection[name] = i - self.widget.append_text('') + self.entry.append_text('') for name in lst: - self.widget.append_text(name) + self.entry.append_text(name) + completion = gtk.EntryCompletion() + #Only available in PyGTK 2.6 and above. + if hasattr(completion, 'set_inline_selection'): + completion.set_inline_selection(True) + completion.set_model(model) + self.entry.child.set_completion(completion) + completion.set_text_column(0) return lst + def sig_key_press(self, widget, event): + if event.type == gtk.gdk.KEY_PRESS \ + and event.state & gtk.gdk.CONTROL_MASK \ + and event.keyval == gtk.keysyms.space: + self.entry.popup() + def _value_get(self): - if self.get_model(): - return [(self.name, 'like', self.get_model() + ',')] + value = self._selection.get(self.entry.child.get_text(), False) + oper = self.liststore.get_value(self.combo.get_active_iter(), 0) + if value or oper != 'like': + return [(self.name, oper, value + ',')] else: + self.entry.child.set_text('') return [] def _value_set(self, value): - if value == False: - value = '' - for sel in self._selection: - if self._selection[sel] == value: - self.widget.child.set_text(sel) + i = self.liststore.get_iter_root() + while i: + if self.liststore.get_value(i, 0) == value[0]: + self.combo.set_active_iter(i) + break + i = self.liststore.iter_next(i) + if value[1] == False: + self.entry.child.set_text('') + return + for long_text, sel_value in self._selection.items(): + if sel_value == value[1]: + self.entry.child.set_text(long_text) + break - - value = property(_value_get, _value_set) + value = property(_value_get, _value_set, None, + 'The content of the widget or ValueError if not valid') def clear(self): - self.value = '' + self.value = ['like', False] + + def _readonly_set(self, value): + self.combo.set_sensitive(not value) + self.entry.set_sensitive(not value) + + def sig_activate(self, fct): + self.entry.child.connect_after('activate', fct) diff --git a/tryton/gui/window/view_form/widget_search/selection.py b/tryton/gui/window/view_form/widget_search/selection.py index cef4f2d088..49b1160ffb 100644 --- a/tryton/gui/window/view_form/widget_search/selection.py +++ b/tryton/gui/window/view_form/widget_search/selection.py @@ -3,6 +3,7 @@ import gtk from interface import Interface import tryton.rpc as rpc import tryton.common as common +import gobject class Selection(Interface): @@ -11,11 +12,25 @@ class Selection(Interface): attrs = {} super(Selection, self).__init__(name, parent, attrs) - self.widget = gtk.combo_box_entry_new_text() - self.widget.child.set_editable(True) - self.widget.child.set_property('activates_default', True) - self.widget.child.connect('key_press_event', self.sig_key_press) - self.widget.set_focus_chain([self.widget.child]) + self.widget = gtk.HBox() + + self.liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + self.combo = gtk.ComboBox(self.liststore) + cell = gtk.CellRendererText() + self.combo.pack_start(cell, True) + self.combo.add_attribute(cell, 'text', 1) + for oper in (['=', _('equal')], + ['!=', _('different')], + ): + self.liststore.append(oper) + self.combo.set_active(0) + self.widget.pack_start(self.combo, False, False) + + self.entry = gtk.combo_box_entry_new_text() + self.entry.child.set_editable(True) + self.entry.child.set_property('activates_default', True) + self.entry.child.connect('key_press_event', self.sig_key_press) + self.entry.set_focus_chain([self.entry.child]) self._selection = {} selection = attrs.get('selection', []) if 'relation' in attrs: @@ -37,9 +52,11 @@ class Selection(Interface): selection.sort(lambda x, y: cmp(x[1], y[1])) attrs['selection'] = selection self.set_popdown(selection) + self.widget.pack_start(self.entry, True, True) + self.widget.show_all() def set_popdown(self, selection): - model = self.widget.get_model() + model = self.entry.get_model() model.clear() self._selection = {} lst = [] @@ -49,15 +66,15 @@ class Selection(Interface): name += ' (' + str(i) + ')' lst.append(name) self._selection[name] = i - self.widget.append_text('') + self.entry.append_text('') for name in lst: - self.widget.append_text(name) + self.entry.append_text(name) completion = gtk.EntryCompletion() #Only available in PyGTK 2.6 and above. if hasattr(completion, 'set_inline_selection'): completion.set_inline_selection(True) completion.set_model(model) - self.widget.child.set_completion(completion) + self.entry.child.set_completion(completion) completion.set_text_column(0) return lst @@ -65,32 +82,41 @@ class Selection(Interface): if event.type == gtk.gdk.KEY_PRESS \ and event.state & gtk.gdk.CONTROL_MASK \ and event.keyval == gtk.keysyms.space: - self.widget.popup() + self.entry.popup() def _value_get(self): - res = self._selection.get(self.widget.child.get_text(), False) - if res: - return [(self.name, '=', res)] - self.widget.child.set_text('') - return [] + value = self._selection.get(self.entry.child.get_text(), False) + oper = self.liststore.get_value(self.combo.get_active_iter(), 0) + if value or oper != '=': + return [(self.name, oper, value)] + else: + self.entry.child.set_text('') + return [] def _value_set(self, value): - if value == False: - self.widget.child.set_text('') + i = self.liststore.get_iter_root() + while i: + if self.liststore.get_value(i, 0) == value[0]: + self.combo.set_active_iter(i) + break + i = self.liststore.iter_next(i) + if value[1] == False: + self.entry.child.set_text('') return for long_text, sel_value in self._selection.items(): - if sel_value == value: - self.widget.child.set_text(long_text) + if sel_value == value[1]: + self.entry.child.set_text(long_text) break def clear(self): - self.value = False + self.value = ['=', False] value = property(_value_get, _value_set, None, 'The content of the widget or ValueError if not valid') def _readonly_set(self, value): - self.widget.set_sensitive(not value) + self.combo.set_sensitive(not value) + self.entry.set_sensitive(not value) def sig_activate(self, fct): - self.widget.child.connect_after('activate', fct) + self.entry.child.connect_after('activate', fct)