New search widget

review103001
This commit is contained in:
Cédric Krier committed 2011-10-03 10:07:48 +02:00
1 parent 8bcc90c2c7
commit 6ab08fec91
19 files changed
+1697 -1281

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* New search widget
* Improve memory management of Binary fields
* Support buffer for Binary fields
* Remove delete on Escape in editable tree
+98 -3
View File
@@ -92,9 +92,9 @@ Figure: Tryton client application::
| |-+ | |-----------------------------------------------||
Tool bar | | |- | | New Save Switch Reload | Prev Next | Attach v ||
| | |- | |-----------------------------------------------||
| + | | ||
| |-+ | | ||
| | |- | | ||
| + | | ____________________ ||
Search widget | |-+ | | Search | | <- -> Find Clear ||
| | |- | |-----------------------------------------------||
| | |- | | ||
| + | | ||
View | |-+ | | ||
@@ -874,6 +874,101 @@ Attachment:
On click it opens the attachments :term:`dialog`. The default dialog
shows a list view of the attached files and links.
Search Widget
*************
The search widget adds the ability to easily search for records on the current
tab. This widget is visible only on :term:`tree view`.
The Syntax
^^^^^^^^^^
A query is composed of search clauses.
A clause is composed of a field name (with `:` at the end), an operator and a value.
The field name is optional and defaults to the record name.
The operator is also optional and defaults to `like` or `equal` depending on
the type of the field. The default operator is `=` except for fields of type
`char`, `text` and `many2one` which is `ilike`.
Field Names
^^^^^^^^^^^
All field names shown in the :term:`tree view` can be searched. Field names
must be followed by a `:`
For example: ``Name:``
If the field name contains spaces, it is possible to
escape it using double quotes.
For example: ``"Receivable Today":``
Operators
^^^^^^^^^
The following operators can be used:
* `=`: equal to
* `<`: less then
* `<=`: less then or equal to
* `>`: greater then
* `>=`: greater then or equal to
* `!=`: not equal
* `!`: not equal or not like (depending of the type of field)
For example: ``Name: != Dwight``
.. note:: The `ilike` operator is never explicit and `%` is appended to the
value to make it behaves like `starts with`
Values
^^^^^^
The format of the value depends on the type of the field.
A list of values can be set using `;` as separator.
For example: ``Name: Michael; Pam``
It will find all records having the `Name` starting with `Michael` or
`Pam`.
A range of number values can be set using `..`.
For example: ``Amount: 100..500``
It will find all records with `Amount` between `100` and `500` (left
included and right excluded)
There are two wildcards:
* `%`: matches any string of zero or more characters.
* `_`: matches any single character.
It is possible to escape special characters in values by using double quotes.
For example: ``Name: "Michael:Scott"``
Here it will search with the value `Michael:Scott`.
Clause composition
^^^^^^^^^^^^^^^^^^
The clauses can be composed using the two boolean operators `and` and `or`.
By default, there is an implicit `and` between each clause if no operator is
specified.
For example: ``Name: Michael Amount: 100``
is the same as ``Name: Michael and Amount: 100``
The `and` operator has a highest precedence than `or` but you can change it by
using parenthesis.
For example: ``(Name: Michael or Name: Pam) and Amount: 100``
is different than ``Name: Michael or Name: Pam and Amount: 100``
which is evaluated as ``Name: Michael or (Name: Pam and Amount: 100)``
Appendix
********
+2
View File
@@ -161,6 +161,8 @@ class DateEntry(gtk.Entry):
return False
match = self.regex.match(text)
if not match:
return False
for i in range(len(match.groups())):
val = match.group(i + 1)
n = len(val)
+1476
View File
File diff suppressed because it is too large. Load diff
-2
View File
@@ -101,8 +101,6 @@ class Main(object):
gtk.gdk.CONTROL_MASK|gtk.gdk.SHIFT_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Delete', gtk.keysyms.D,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Find', gtk.keysyms.F,
gtk.gdk.CONTROL_MASK)
gtk.accel_map_add_entry('<tryton>/Form/Next', gtk.keysyms.Page_Down,
0)
gtk.accel_map_add_entry('<tryton>/Form/Previous', gtk.keysyms.Page_Up,
-12
View File
@@ -11,7 +11,6 @@ from tryton.action import Action
from tryton.config import CONFIG
from tryton.gui import Main
from tryton.gui.window import Window
from tryton.gui.window.win_search import WinSearch
from tryton.gui.window.preference import Preference
from tryton.gui.window.win_export import WinExport
from tryton.gui.window.win_import import WinImport
@@ -59,7 +58,6 @@ class Form(SignalEvent, TabContent):
(_('_Delete...'), 'tryton-delete', 'sig_remove',
'<tryton>/Form/Delete'),
(None,) * 4,
(_('_Find...'), 'tryton-find', 'sig_search', '<tryton>/Form/Find'),
(_('_Next'), 'tryton-go-next', 'sig_next', '<tryton>/Form/Next'),
(_('_Previous'), 'tryton-go-previous', 'sig_previous',
'<tryton>/Form/Previous'),
@@ -442,16 +440,6 @@ class Form(SignalEvent, TabContent):
menu.show_all()
menu.popup(None, None, menu_position, 0, 0)
def sig_search(self, widget=None):
if not self.modified_save():
return
def callback(result):
if result:
self.screen.clear()
self.screen.load(result)
WinSearch(self.model, callback, domain=self.domain,
context=self.context)
def message_info(self, message, color='red'):
if message:
self.info_label.set_label(message)
+71 -55
View File
@@ -9,13 +9,13 @@ import xml.dom.minidom
import tryton.rpc as rpc
from tryton.gui.window.view_form.model.group import Group
from tryton.gui.window.view_form.view.screen_container import ScreenContainer
from tryton.gui.window.view_form.widget_search import Form
from tryton.signal_event import SignalEvent
from tryton.common import node_attributes
from tryton.config import CONFIG
import tryton.common as common
from tryton.exceptions import TrytonServerError
from tryton.jsonrpc import JSONEncoder
from tryton.common.tdp import DomainParser
class Screen(SignalEvent):
@@ -36,6 +36,8 @@ class Screen(SignalEvent):
if domain is None:
domain = []
self.limit = limit or 1000
self.offset = 0
super(Screen, self).__init__()
self.readonly = readonly
@@ -62,13 +64,13 @@ class Screen(SignalEvent):
self.screen_container.alternate_view = alternate_view
self.widget = self.screen_container.widget_get()
self.__current_view = 0
self.limit = limit
self.search_value = search_value
self.fields_view_tree = None
self.sort = sort
self.view_to_load = []
self.expanded_nodes = collections.defaultdict(
lambda: collections.defaultdict(lambda: None))
self.domain_parser = None
if mode:
self.view_to_load = mode[1:]
@@ -84,68 +86,86 @@ class Screen(SignalEvent):
def search_active(self, active=True):
if active and not self.parent:
if not self.filter_widget:
if not self.fields_view_tree:
ctx = {}
ctx.update(rpc.CONTEXT)
ctx.update(self.context)
try:
self.fields_view_tree = rpc.execute('model',
self.model_name, 'fields_view_get', False,
'tree', ctx)
except TrytonServerError:
return
self.filter_widget = Form(self.fields_view_tree,
self.model_name, self.domain, (self, self.search_filter),
self.context)
self.screen_container.add_filter(self.filter_widget.widget,
self.search_filter, self.search_clear,
self.search_prev, self.search_next)
self.filter_widget.set_limit(self.limit)
self.filter_widget.value = self.search_value
if not self.fields_view_tree:
ctx = {}
ctx.update(rpc.CONTEXT)
ctx.update(self.context)
try:
self.fields_view_tree = rpc.execute('model',
self.model_name, 'fields_view_get', False,
'tree', ctx)
except TrytonServerError:
return
fields = self.fields_view_tree['fields']
for name, props in fields.iteritems():
if props['type'] not in ('selection', 'reference'):
continue
if isinstance(props['selection'], (tuple, list)):
continue
props['selection'] = self.get_selection(props)
self.domain_parser = DomainParser(
self.fields_view_tree['fields'])
self.screen_container.set_screen(self)
self.screen_container.show_filter()
else:
self.screen_container.hide_filter()
def search_prev(self, widget=None):
self.filter_widget.prev()
self.search_filter()
def get_selection(self, props):
try:
selection = rpc.execute('model',
self.model_name, props['selection'], rpc.CONTEXT)
except TrytonServerError, exception:
common.process_exception(exception, None)
selection = []
selection.sort(lambda x, y: cmp(x[1], y[1]))
return selection
def search_next(self, widget=None):
self.filter_widget.next()
self.search_filter()
def search_prev(self, search_string):
self.offset -= self.limit
self.search_filter(search_string=search_string)
def search_clear(self, widget=None):
self.filter_widget.clear()
self.clear()
def search_next(self, search_string):
self.offset += self.limit
self.search_filter(search_string=search_string)
def search_filter(self, widget=None, only_ids=False):
limit = None
offset = 0
values = []
if self.filter_widget:
limit = self.filter_widget.get_limit()
offset = self.filter_widget.get_offset()
values = self.filter_widget.value
def search_complete(self, search_string):
parsed_tree = self.domain_parser.parse(search_string)
res = list(r for r in parsed_tree.complete() \
if r.strip() != search_string)
return res
def search_filter(self, search_string=None, only_ids=False):
domain = []
if self.domain_parser:
if search_string is not None:
domain = self.domain_parser.parse(search_string or '').domain()
else:
domain = self.search_value
self.screen_container.set_text(self.domain_parser.string(domain))
else:
values = [('id', 'in', [x.id for x in self.group])]
domain = [('id', 'in', [x.id for x in self.group])]
ctx = {}
ctx.update(rpc.CONTEXT)
ctx.update(self.context)
if values:
if domain:
if self.domain:
values = ['AND', values, self.domain]
domain = ['AND', domain, self.domain]
else:
values = self.domain
rpc_args = ('model', self.model_name, 'search', values, offset, limit,
self.sort, ctx)
domain = self.domain
rpc_args = ('model', self.model_name, 'search', domain, self.offset,
self.limit, self.sort, ctx)
try:
ids = rpc.execute(*rpc_args)
except TrytonServerError, exception:
ids = (common.process_exception(exception, *rpc_args) or [])
if not only_ids:
if len(ids) == limit:
rpc_args = ('model', self.model_name, 'search_count', values,
if len(ids) == self.limit:
rpc_args = ('model', self.model_name, 'search_count', domain,
ctx)
try:
self.search_count = rpc.execute(*rpc_args)
@@ -154,9 +174,9 @@ class Screen(SignalEvent):
*rpc_args) or 0)
else:
self.search_count = len(ids)
self.screen_container.but_prev.set_sensitive(bool(offset))
if (len(ids) == limit
and self.search_count > limit + offset):
self.screen_container.but_prev.set_sensitive(bool(self.offset))
if (len(ids) == self.limit
and self.search_count > self.limit + self.offset):
self.screen_container.but_next.set_sensitive(True)
else:
self.screen_container.but_next.set_sensitive(False)
@@ -220,12 +240,8 @@ class Screen(SignalEvent):
def __set_current_record(self, record):
self.__current_record = record
if self.filter_widget:
offset = int(self.filter_widget.get_offset())
else:
offset = 0
try:
pos = self.group.index(record) + offset + 1
pos = self.group.index(record) + self.offset + 1
except ValueError:
pos = []
i = record
@@ -234,7 +250,7 @@ class Screen(SignalEvent):
i = i.parent
pos.reverse()
pos = tuple(pos)
self.signal('record-message', (pos or 0, len(self.group) + offset,
self.signal('record-message', (pos or 0, len(self.group) + self.offset,
self.search_count, record and record.id))
attachment_count = 0
if record and record.attachment_count > 0:
@@ -16,6 +16,7 @@ class ScreenContainer(object):
self.filter_vbox = None
self.but_prev = None
self.but_next = None
self.current_text = ''
self.alternate_viewport = gtk.Viewport()
self.alternate_viewport.set_shadow_type(gtk.SHADOW_NONE)
self.alternate_view = False
@@ -23,20 +24,35 @@ class ScreenContainer(object):
def widget_get(self):
return self.vbox
def add_filter(self, widget, fnct, clear_fnct, prev_fnct, next_fnct):
tooltips = common.Tooltips()
def set_screen(self, screen):
self.screen = screen
tooltips = common.Tooltips()
if self.filter_vbox is not None:
return
self.filter_vbox = gtk.VBox(spacing=0)
self.filter_vbox.set_border_width(0)
hbox = gtk.HBox(homogeneous=False, spacing=0)
label = gtk.Label(_('Search'))
label.set_alignment(0.0, 0.5)
hbox.pack_start(label, expand=True, fill=True)
hbox.pack_start(label, expand=False, fill=False, padding=5)
self.search_entry = gtk.Entry()
self.search_entry.set_alignment(0.0)
self.completion = gtk.EntryCompletion()
self.completion.set_model(gtk.ListStore(str))
self.completion.set_text_column(0)
self.completion.set_match_func(lambda *a: True, None)
self.search_entry.connect_after('activate', self.do_search)
self.search_entry.set_completion(self.completion)
self.search_entry.connect('changed', self.changed)
hbox.pack_start(self.search_entry, expand=True, fill=True, padding=5)
but_prev = gtk.Button()
self.but_prev = but_prev
tooltips.set_tip(but_prev, _('Previous'))
but_prev.connect('clicked', prev_fnct)
but_prev.connect('clicked', self.search_prev)
img_prev = gtk.Image()
img_prev.set_from_stock('tryton-go-previous',
gtk.ICON_SIZE_SMALL_TOOLBAR)
@@ -48,7 +64,7 @@ class ScreenContainer(object):
but_next = gtk.Button()
self.but_next = but_next
tooltips.set_tip(but_next, _('Next'))
but_next.connect('clicked', next_fnct)
but_next.connect('clicked', self.search_next)
img_next = gtk.Image()
img_next.set_from_stock('tryton-go-next',
gtk.ICON_SIZE_SMALL_TOOLBAR)
@@ -59,7 +75,7 @@ class ScreenContainer(object):
but_find = gtk.Button()
tooltips.set_tip(but_find, _('Find'))
but_find.connect('clicked', fnct)
but_find.connect('clicked', self.do_search)
img_find = gtk.Image()
img_find.set_from_stock('tryton-find', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_find.set_alignment(0.5, 0.5)
@@ -69,7 +85,7 @@ class ScreenContainer(object):
but_clear = gtk.Button()
tooltips.set_tip(but_clear, _('Clear'))
but_clear.connect('clicked', clear_fnct)
but_clear.connect('clicked', self.clear)
img_clear = gtk.Image()
img_clear.set_from_stock('tryton-clear', gtk.ICON_SIZE_SMALL_TOOLBAR)
img_clear.set_alignment(0.5, 0.5)
@@ -83,7 +99,6 @@ class ScreenContainer(object):
hseparator = gtk.HSeparator()
hseparator.show()
self.filter_vbox.pack_start(hseparator, expand=True, fill=False)
self.filter_vbox.pack_start(widget, expand=True, fill=True)
self.vbox.pack_start(self.filter_vbox, expand=False, fill=True)
@@ -114,3 +129,29 @@ class ScreenContainer(object):
self.viewport.remove(self.viewport.get_child())
self.viewport.add(widget)
self.viewport.show_all()
def changed(self, editable):
res = self.screen.search_complete(self.get_text())
self.current_text = self.get_text()
model = self.completion.get_model()
model.clear()
for r in res:
model.append([r.strip()])
def clear(self, widget=None):
self.search_entry.set_text('');
def get_text(self):
return self.search_entry.get_text().strip().decode('utf-8')
def set_text(self, value):
return self.search_entry.set_text(value)
def search_next(self, widget=None):
self.screen.search_next(self.get_text())
def search_prev(self, widget=None):
self.screen.search_prev(self.get_text())
def do_search(self, widget=None):
self.screen.search_filter(self.get_text())
@@ -1,3 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from form import *
@@ -1,203 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import time
import datetime
import gtk
import gettext
import locale
from interface import Interface
from tryton.common import DT_FORMAT, DHM_FORMAT, TRYTON_ICON
from tryton.common import date_widget, Tooltips, datetime_strftime
from tryton.common import get_toplevel_window
from tryton.translate import date_format
import gobject
_ = gettext.gettext
class Calendar(Interface):
def __init__(self, name, attrs=None, context=None,
on_change=None):
super(Calendar, self).__init__(name, attrs=attrs, context=context,
on_change=on_change)
tooltips = Tooltips()
self.widget = gtk.HBox(spacing=3)
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 (['=', _('is')],
['between', _('is between')],
['not between', _('is not between')],
['!=', _('is not')],
):
self.liststore.append(oper)
self.combo.set_active(0)
self.widget.pack_start(self.combo, False, False)
self.combo.connect('changed', self._changed)
self.combo.connect('changed', self.on_change)
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)
self.entry1.connect('key_press_event', self.on_change)
tooltips.set_tip(self.entry1, _('Start date'))
self.widget.pack_start(self.widget1, expand=False, fill=True)
self.eb1 = gtk.EventBox()
tooltips.set_tip(self.eb1, _('Open the calendar'))
self.eb1.set_events(gtk.gdk.BUTTON_PRESS)
self.eb1.connect('button_press_event', self.cal_open, self.entry1)
img = gtk.Image()
img.set_from_stock('tryton-find', gtk.ICON_SIZE_BUTTON)
img.set_alignment(0.5, 0.5)
self.eb1.add(img)
self.widget.pack_start(self.eb1, 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
self.entry2.set_property('width-chars', 10)
self.entry2.set_property('activates_default', True)
self.entry2.connect('key_press_event', self.on_change)
tooltips.set_tip(self.entry2, _('End date'))
self.widget.pack_start(self.widget2, expand=False, fill=True)
self.eb2 = gtk.EventBox()
tooltips.set_tip(self.eb2, _('Open the calendar'))
self.eb2.set_events(gtk.gdk.BUTTON_PRESS)
self.eb2.connect('button_press_event', self.cal_open, self.entry2)
img = gtk.Image()
img.set_from_stock('tryton-find', gtk.ICON_SIZE_BUTTON)
img.set_alignment(0.5, 0.5)
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 = datetime.date(*time.strptime(value, self.format)[:3])
except ValueError:
return False
if self.attrs.get('type', 'date') == 'datetime':
return datetime_strftime(datetime.datetime.combine(date,
datetime.time.min), DHM_FORMAT)
return datetime_strftime(date, DT_FORMAT)
def _value_get(self):
oper = self.liststore.get_value(self.combo.get_active_iter(), 0)
if oper in ('=', '!='):
value = self._date_get(self.entry1.get_text())
if value:
return [(self.name, oper, value)]
else:
return []
else:
res = []
if oper == 'between':
clause = 'AND'
oper1 = '>='
oper2 = '<='
else:
clause = 'OR'
oper1 = '<='
oper2 = '>='
res.append(clause)
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):
if not value:
return ''
try:
return datetime_strftime(value, self.format)
except ValueError:
return ''
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)
def cal_open(self, widget, event, dest):
parent = get_toplevel_window()
win = gtk.Dialog(_('Date selection'), parent,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
win.set_has_separator(True)
win.set_icon(TRYTON_ICON)
cal = gtk.Calendar()
cal.display_options(gtk.CALENDAR_SHOW_HEADING | \
gtk.CALENDAR_SHOW_DAY_NAMES | \
gtk.CALENDAR_SHOW_WEEK_NUMBERS)
cal.connect('day-selected-double-click',
lambda *x: win.response(gtk.RESPONSE_OK))
win.vbox.pack_start(cal, expand=True, fill=True)
win.show_all()
try:
val = self._date_get(dest.get_text())
if val:
cal.select_month(int(val[5:7])-1, int(val[0:4]))
cal.select_day(int(val[8:10]))
except ValueError:
pass
response = win.run()
if response == gtk.RESPONSE_OK:
year, month, day = cal.get_date()
date = datetime.date(year, month + 1, day)
dest.set_text(datetime_strftime(date, self.format))
self.on_change()
win.destroy()
parent.present()
def clear(self):
self.value = ('=', '')
def sig_activate(self, fct):
self.entry1.connect_after('activate', fct)
self.entry2.connect_after('activate', fct)
@@ -1,82 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
import gettext
from interface import Interface
import gobject
_ = gettext.gettext
class Char(Interface):
def __init__(self, name, attrs=None, context=None, on_change=None):
super(Char, self).__init__(name, attrs=attrs, context=context,
on_change=on_change)
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', _('does not contain')],
['=', _('equals')],
['!=', _('does not equal')],
['starts', _('starts with')],
['ends', _('ends with')],
):
self.liststore.append(oper)
self.combo.set_active(0)
self.combo.connect('changed', self.on_change)
self.widget.pack_start(self.combo, False, False)
self.entry = gtk.Entry()
self.entry.set_max_length(int(self.attrs.get('size', 0)))
self.entry.set_width_chars(5)
self.entry.set_property('activates_default', True)
self.entry.connect('key_press_event', self.on_change)
self.widget.pack_start(self.entry, True, True)
self.widget.show_all()
def _value_get(self):
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
if oper in ('ilike', 'not ilike') \
and '%' not in value:
return [(self.name, oper, '%' + value + '%')]
elif oper == 'starts':
return [(self.name, 'ilike', value + '%')]
elif oper == 'ends':
return [(self.name, 'ilike', '%' + value)]
return [(self.name, oper, value)]
else:
return []
def _value_set(self, value):
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 = ['ilike', '']
def _readonly_set(self, 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.entry.connect_after('activate', fct)
@@ -1,77 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
import gettext
from interface import Interface
_ = gettext.gettext
class CheckBox(Interface):
def __init__(self, name, attrs=None, context=None, on_change=None):
super(CheckBox, self).__init__(name, attrs=attrs, context=context,
on_change=on_change)
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.child.connect('key_press_event', self.on_change)
self.widget.set_focus_chain([self.widget.child])
if self.name != 'active' or \
not (self.name == 'active' \
and self.context.get('active_test', True)):
self.widget.append_text('')
self.widget.child.set_text('')
else:
self.widget.child.set_text(_('Yes'))
self.widget.append_text(_('Yes'))
self.widget.append_text(_('No'))
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(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 \
and event.state & gtk.gdk.CONTROL_MASK \
and event.keyval == gtk.keysyms.space:
self.widget.popup()
def _value_get(self):
val = self.widget.child.get_text()
if not val \
and self.name == 'active' \
and self.context.get('active_test', True):
val = _('Yes')
self.widget.child.set_text(val)
if val:
return [(self.name, '=', int(val == _('Yes')))]
self.widget.child.set_text('')
return []
def _value_set(self, value):
if value == '':
if self.name != 'active' or \
(self.name == 'active' \
and self.context.get('active_test', True)):
self.widget.child.set_text('')
else:
self.widget.child.set_text(_('Yes'))
return
if value:
self.widget.child.set_text(_('Yes'))
else:
self.widget.child.set_text(_('No'))
value = property(_value_get, _value_set)
def sig_activate(self, fct):
self.widget.child.connect_after('activate', fct)
@@ -1,88 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import locale
from decimal import Decimal
from integer import Integer
import gettext
_ = gettext.gettext
class Float(Integer):
def __init__(self, name, attrs=None, context=None, on_change=None):
super(Float, self).__init__(name, attrs=attrs, context=context,
on_change=on_change)
if isinstance(self.attrs.get('digits'), basestring):
self.digits = (16, 2)
else:
self.digits = self.attrs.get('digits', (16, 2))
def _value_get(self):
try:
value1 = locale.atof(self.entry1.get_text())
except ValueError:
value1 = False
try:
value2 = locale.atof(self.entry2.get_text())
except ValueError:
value2 = False
return self._get_clause(value1, value2)
def _value_set(self, value):
def conv(value):
if value == False:
return ''
else:
return locale.format('%.' + str(self.digits[1]) + 'f',
value or 0.0, True)
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)
def sig_insert_text(self, widget, new_text, new_text_length, position):
value = widget.get_text()
position = widget.get_position()
new_value = value[:position] + new_text + value[position:]
if new_value == '-':
return
try:
if len(str(int(locale.atof(new_value)))) > self.digits[0]:
widget.stop_emission('insert-text')
except ValueError:
widget.stop_emission('insert-text')
class Numeric(Float):
def _value_get(self):
try:
value1 = Decimal(str(locale.atof(self.entry1.get_text())))
except ValueError:
value1 = False
try:
value2 = Decimal(str(locale.atof(self.entry2.get_text())))
except ValueError:
value2 = False
res = self._get_clause(value1, value2)
return res
def _value_set(self, value):
super(Numeric, self)._value_set(value)
value = property(_value_get, _value_set)
@@ -1,307 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
from xml.parsers import expat
import sys
import gettext
from tryton.config import CONFIG
_ = gettext.gettext
class _container(object):
def __init__(self):
self.cont = []
self.count = 0
self.col = 0
def new(self, col=6):
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)
table.set_border_width(1)
self.cont.append( (table, 1, 0) )
def get(self):
return self.cont[-1][0]
def pop(self):
return self.cont.pop()[0]
def newline(self):
(table, i, j) = self.cont[-1]
if i > 0:
self.cont[-1] = (table, 1, j+1)
table.resize(j + 1, self.col)
def wid_add(self, widget, length=1, name=None, expand=False, ypadding=0):
self.count += 1
(table, i, j) = self.cont[-1]
if length > self.col:
length = self.col
if length + i > self.col:
self.newline()
(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)
wid = vbox
else:
wid = widget
yopt = False
if expand:
yopt = yopt | gtk.EXPAND |gtk.FILL
table.attach(wid, i, i+length, j, j+1,
yoptions=yopt, xoptions=gtk.FILL|gtk.EXPAND,
ypadding=ypadding, xpadding=5)
self.cont[-1] = (table, i+length, j)
return wid
class Parse(object):
def __init__(self, fields, model='', context=None, on_change=None):
self.fields = fields
self.model = model
self.col = CONFIG['client.modepda'] and 1 or 6
self.focusable = None
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 = {}
self.context = context or {}
self.on_change = on_change
def _psr_start(self, name, attrs):
if name in ('form','tree'):
self.title = attrs.get('string', self.title)
self.container.new(self.col)
elif name == 'field':
if attrs['name'] in self.fields:
val = attrs.get('select', None)
if val is None:
val = self.fields[attrs['name']].get('select', 0)
val = int(val)
if val:
if val <= 1:
self.add_widget(attrs, val)
else:
self.add_widget_end.append((attrs, val))
def add_widget(self, attrs, val):
ftype = attrs.get('widget', self.fields[str(attrs['name'])]['type'])
self.fields[str(attrs['name'])].update(attrs)
self.fields[str(attrs['name'])]['model']=self.model
if ftype not in WIDGETS_TYPE:
return False
widget_act = WIDGETS_TYPE[ftype][0](str(attrs['name']),
attrs=self.fields[attrs['name']], context=self.context,
on_change=self.on_change)
if 'string' in self.fields[str(attrs['name'])]:
label = self.fields[str(attrs['name'])]['string'] + _(':')
else:
label = None
size = WIDGETS_TYPE[ftype][1]
if not self.focusable:
self.focusable = widget_act.widget
wid = self.container.wid_add(widget_act.widget, size, label,
int(self.fields[str(attrs['name'])].get('expand',0)))
if int(val) <= 1:
wid.show()
self.dict_widget[str(attrs['name'])] = (widget_act, wid, int(val))
def add_parameters(self):
hb_param = gtk.HBox(spacing=3)
hb_param.pack_start(gtk.Label(_('Limit:')), expand=False, fill=False)
self.spin_limit.set_numeric(False)
self.spin_limit.set_adjustment(gtk.Adjustment(value=CONFIG['client.limit'],
lower=1, upper=sys.maxint, step_incr=10, page_incr=100))
self.spin_limit.set_property('visible', True)
hb_param.pack_start(self.spin_limit, expand=False, fill=False)
hb_param.pack_start(gtk.Label(_('Offset:')), expand=False, fill=False)
self.spin_offset.set_numeric(False)
self.spin_offset.set_adjustment(gtk.Adjustment(value=0, lower=0,
upper=sys.maxint, step_incr=CONFIG['client.limit'], page_incr=100))
hb_param.pack_start(self.spin_offset, expand=False, fill=False)
hb_param.show_all()
return hb_param
def _psr_end(self, name):
pass
def _psr_char(self, name):
pass
def parse(self, xml_data):
psr = expat.ParserCreate()
psr.StartElementHandler = self._psr_start
psr.EndElementHandler = self._psr_end
psr.CharacterDataHandler = self._psr_char
self.container = _container()
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()
self.container.new(self.col)
for i in self.add_widget_end:
self.add_widget(*i)
if ('active' in self.fields) and ('active' not in self.dict_widget):
val = self.fields['active'].get('select', 0)
val = int(val)
self.add_widget(self.fields['active'], val)
self.add_widget_end = []
vbox2.pack_start(self.container.pop())
vbox2.pack_start(self.add_parameters())
expander.add(vbox2)
vbox.pack_start(expander)
return (self.dict_widget, vbox)
class Form(object):
def __init__(self, view, model=None, domain=None, call=None, context=None):
if domain is None:
domain = []
if context is None:
context = {}
parser = Parse(view['fields'], model=model, context=context,
on_change=self.on_change)
self.fields = view['fields']
self.model = model
self.parser = parser
self.call = call
self.context = context
(self.widgets, self.widget) = parser.parse(view['arch'])
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)
self.spin_offset = parser.spin_offset
self.spin_offset.set_activates_default(True)
self.focusable = parser.focusable
self.id = 0
self.name = parser.title
for i in self.widgets.itervalues():
i[0].sig_activate(self.sig_activate)
self.spin_limit.connect_after('activate', self.sig_activate)
self.spin_offset.connect_after('activate', self.sig_activate)
def clear(self):
self.id = 0
for i in self.widgets.itervalues():
i[0].clear()
self.spin_offset.set_value(0)
def next(self):
self.spin_offset.set_value(self.spin_offset.get_value()
+ self.spin_limit.get_value())
def prev(self):
self.spin_offset.set_value(max(self.spin_offset.get_value()
- self.spin_limit.get_value(), 0))
def on_change(self, *args):
self.spin_offset.set_value(0)
def limit_changed(self, widget):
self.spin_offset.set_increments(step=self.spin_limit.get_value(),
page=100)
def set_limit(self, value):
if not value:
value = CONFIG['client.limit']
return self.spin_limit.set_value(value)
def get_limit(self):
return self.spin_limit.get_value()
def get_offset(self):
return self.spin_offset.get_value()
def sig_activate(self, *args):
if self.call:
obj, fct = self.call
fct(obj)
def _value_get(self):
res = []
for i in self.widgets:
res += self.widgets[i][0].value
return res
def _value_set(self, value):
if not value:
return
for i in value:
if i in self.widgets:
self.widgets[i][0].value = value[i]
value = property(_value_get, _value_set)
import calendar
import float
import integer
import selection
import char
import checkbox
import reference
WIDGETS_TYPE = {
'date': (calendar.Calendar, 2),
'datetime': (calendar.Calendar, 2),
'float': (float.Float, 2),
'numeric': (float.Numeric, 2),
'integer': (integer.Integer, 2),
'biginteger': (integer.Integer, 2),
'selection': (selection.Selection, 2),
'many2one_selection': (selection.Selection, 2),
'char': (char.Char, 2),
'boolean': (checkbox.CheckBox, 2),
'reference': (reference.Reference, 2),
'text': (char.Char, 2),
'email': (char.Char, 2),
'url': (char.Char, 2),
'many2one': (char.Char, 2),
'one2many': (char.Char, 2),
'one2many_form': (char.Char, 2),
'one2many_list': (char.Char, 2),
'many2many_edit': (char.Char, 2),
'many2many': (char.Char, 2),
'callto': (char.Char, 2),
'sip': (char.Char, 2),
'one2one': (char.Char, 2),
}
@@ -1,139 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
from interface import Interface
import locale
import gettext
import gobject
_ = gettext.gettext
class Integer(Interface):
def __init__(self, name, attrs=None, context=None, on_change=None):
super(Integer, self).__init__(name, attrs=attrs, context=context,
on_change=on_change)
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 (['=', _('equals')],
['between', _('is between')],
['not between', _('is not between')],
['!=', _('is different')],
):
self.liststore.append(oper)
self.combo.set_active(0)
self.widget.pack_start(self.combo, False, False)
self.combo.connect('changed', self._changed)
self.combo.connect('changed', self.on_change)
self.entry1 = gtk.Entry()
self.entry1.set_max_length(0)
self.entry1.set_width_chars(5)
self.entry1.set_activates_default(True)
self.entry1.set_alignment(1.0)
self.entry1.connect('insert_text', self.sig_insert_text)
self.entry1.connect('key_press_event', self.on_change)
self.widget.pack_start(self.entry1, expand=True, fill=True)
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)
self.entry2.set_activates_default(True)
self.entry2.set_alignment(1.0)
self.entry2.connect('insert_text', self.sig_insert_text)
self.entry2.connect('key_press_event', self.on_change)
self.widget.pack_start(self.entry2, expand=True, 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())
except ValueError:
value1 = False
try:
value2 = locale.atoi(self.entry2.get_text())
except ValueError:
value2 = False
return self._get_clause(value1, value2)
def _get_clause(self, value1, value2):
oper = self.liststore.get_value(self.combo.get_active_iter(), 0)
if oper in ('=', '!='):
if self.entry1.get_text():
return [(self.name, oper, value1 or 0)]
else:
return []
else:
res = []
if oper == 'between':
clause = 'AND'
oper1 = '>='
oper2 = '<='
else:
clause = 'OR'
oper1 = '<='
oper2 = '>='
res.append(clause)
if value1 is not False:
res.append((self.name, oper1, value1))
if value2 is not False:
res.append((self.name, oper2, value2))
return [res]
def _value_set(self, value):
def conv(value):
if value == False:
return ''
else:
return locale.format('%d', value or 0, True)
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)
def sig_activate(self, fct):
self.entry1.connect_after('activate', fct)
self.entry2.connect_after('activate', fct)
def sig_insert_text(self, widget, new_text, new_text_length, position):
value = widget.get_text()
position = widget.get_position()
new_value = value[:position] + new_text + value[position:]
if new_value == '-':
return
try:
locale.atoi(new_value)
except ValueError:
widget.stop_emission('insert-text')
@@ -1,36 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gettext
_ = gettext.gettext
class Interface(object):
"Interface for search widget"
def __init__(self, name, attrs=None, context=None, on_change=None):
if attrs is None:
attrs = {}
self._value = None
self.name = name
self.model = attrs.get('model', None)
self.attrs = attrs or {}
self.context = context or {}
self.on_change = on_change
def clear(self):
self.value = ''
def _value_get(self):
return self._value
def _value_set(self, value):
self._value = value
value = property(_value_get, _value_set)
def _readonly_set(self, value):
pass
def sig_activate(self, fct):
pass
@@ -1,130 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
import gettext
from interface import Interface
import tryton.rpc as rpc
import tryton.common as common
import gobject
from tryton.exceptions import TrytonServerError
_ = gettext.gettext
class Reference(Interface):
def __init__(self, name, attrs=None, context=None, on_change=None):
super(Reference, self).__init__(name, attrs=attrs, context=context,
on_change=on_change)
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', _('equals')],
['not like', _('is different')],
):
self.liststore.append(oper)
self.combo.set_active(0)
self.combo.connect('changed', self.on_change)
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.child.connect('key_press_event', self.on_change)
self.entry.set_focus_chain([self.entry.child])
self._selection = {}
selection = self.attrs.get('selection', [])
if 'relation' in self.attrs:
try:
result = rpc.execute('model',
self.attrs['relation'], 'search_read',
self.attrs.get('domain', []),
0, None, None, ['rec_name'], rpc.CONTEXT)
selection = [(x['id'], x['rec_name']) for x in result]
except TrytonServerError, exception:
common.process_exception(exception)
selection = []
else:
if not isinstance(selection, (list, tuple)):
try:
selection = rpc.execute('model',
self.attrs['model'], selection, rpc.CONTEXT)
except TrytonServerError, exception:
common.process_exception(exception)
selection = []
selection.sort(lambda x, y: cmp(x[1], y[1]))
self.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.entry.get_model()
model.clear()
lst = []
for (i, j) in selection:
name = str(j)
if type(i) == type(1):
name += ' (' + str(i) + ')'
lst.append(name)
self._selection[name] = i
self.entry.append_text('')
for name in lst:
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):
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):
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, None,
'The content of the widget or ValueError if not valid')
def clear(self):
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)
@@ -1,135 +0,0 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
import operator
from interface import Interface
import tryton.rpc as rpc
import tryton.common as common
import gobject
import gettext
from tryton.pyson import PYSONDecoder
from tryton.exceptions import TrytonServerError
_ = gettext.gettext
class Selection(Interface):
def __init__(self, name, attrs=None, context=None, on_change=None):
super(Selection, self).__init__(name, attrs=attrs, context=context,
on_change=on_change)
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 (['=', _('equals')],
['!=', _('is different')],
):
self.liststore.append(oper)
self.combo.set_active(0)
self.combo.connect('changed', self.on_change)
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.child.connect('key_press_event', self.on_change)
self.entry.set_focus_chain([self.entry.child])
self._selection = {}
selection = self.attrs.get('selection', [])
if 'relation' in self.attrs:
if not self.attrs.get('domain'):
domain = []
else:
domain = PYSONDecoder(rpc.CONTEXT).decode(self.attrs['domain'])
try:
result = rpc.execute('model', self.attrs['relation'],
'search_read', domain, 0, None, None,
['rec_name'], rpc.CONTEXT)
selection = [(x['id'], x['rec_name']) for x in result]
except TrytonServerError, exception:
common.process_exception(exception)
selection = []
else:
if not isinstance(selection, (list, tuple)):
try:
selection = rpc.execute('model',
self.attrs['model'], selection, rpc.CONTEXT)
except TrytonServerError, exception:
common.process_exception(exception)
selection = []
selection.sort(key=operator.itemgetter(1))
self.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.entry.get_model()
model.clear()
self._selection = {}
lst = []
for (i, j) in selection:
name = str(j)
if type(i) == type(1):
name += ' (' + str(i) + ')'
lst.append(name)
self._selection[name] = i
self.entry.append_text('')
for name in lst:
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):
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):
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
def clear(self):
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.combo.set_sensitive(not value)
self.entry.set_sensitive(not value)
def sig_activate(self, fct):
self.entry.child.connect_after('activate', fct)
-1
View File
@@ -4,7 +4,6 @@ import gtk
import gettext
import tryton.rpc as rpc
from tryton.gui.window.view_form.screen import Screen
import tryton.gui.window.view_form.widget_search as widget_search
from tryton.config import TRYTON_ICON
from tryton.gui.window.win_form import WinForm
from tryton.gui.window.nomodal import NoModal