Remove SignalEvent usage
We register on the screen the windows and on the group the screens to which events must be sent. The window of Screen can be a tab, a dialog or even a widget like for One2Many. They must just have a method with the proper name to receive the corresponding events. The group_list_changed event is dedicated to synchronisation of the Gtk TreeView and ListBox models. issue11059 review395151002
This commit is contained in:
1 parent
5798a57eab
commit
642870837e
17 files changed
+191
-288
No files matched your search
@@ -884,7 +884,6 @@ class Main(Gtk.Application):
|
||||
for i in range(len(self.pages)):
|
||||
if self.pages[i].widget == page_widget:
|
||||
page = self.pages.pop(i)
|
||||
page.signal_unconnect(self)
|
||||
break
|
||||
self.notebook.remove_page(page_id)
|
||||
|
||||
|
||||
@@ -7,14 +7,13 @@ import xml.dom.minidom
|
||||
from tryton.common import MODELNAME, RPCException, RPCExecute
|
||||
from tryton.gui import Main
|
||||
from tryton.gui.window.view_board import ViewBoard
|
||||
from tryton.signal_event import SignalEvent
|
||||
|
||||
from .tabcontent import TabContent
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class Board(SignalEvent, TabContent):
|
||||
class Board(TabContent):
|
||||
'Board'
|
||||
|
||||
def __init__(self, model, name='', **attributes):
|
||||
|
||||
+24
-37
@@ -27,14 +27,13 @@ from tryton.gui.window.revision import Revision
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.gui.window.win_export import WinExport
|
||||
from tryton.gui.window.win_import import WinImport
|
||||
from tryton.signal_event import SignalEvent
|
||||
|
||||
from .tabcontent import TabContent
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class Form(SignalEvent, TabContent):
|
||||
class Form(TabContent):
|
||||
"Form"
|
||||
|
||||
def __init__(self, model, res_id=None, name='', **attributes):
|
||||
@@ -52,21 +51,12 @@ class Form(SignalEvent, TabContent):
|
||||
|
||||
self.screen = Screen(self.model, breadcrumb=[self.name], **attributes)
|
||||
self.screen.widget.show()
|
||||
self.screen.windows.append(self)
|
||||
|
||||
self.create_tabcontent()
|
||||
|
||||
self.set_buttons_sensitive()
|
||||
|
||||
self.screen.signal_connect(self, 'record-message',
|
||||
self._record_message)
|
||||
|
||||
self.screen.signal_connect(self, 'record-modified',
|
||||
lambda *a: GLib.idle_add(self._record_modified, *a))
|
||||
self.screen.signal_connect(self, 'record-saved', self._record_saved)
|
||||
self.screen.signal_connect(
|
||||
self, 'resources',
|
||||
lambda screen, resources: self.update_resources(resources))
|
||||
|
||||
self.attachment_screen = None
|
||||
|
||||
if res_id not in (None, False):
|
||||
@@ -127,8 +117,6 @@ class Form(SignalEvent, TabContent):
|
||||
return id(self)
|
||||
|
||||
def destroy(self):
|
||||
super(Form, self).destroy()
|
||||
self.screen.signal_unconnect(self)
|
||||
self.screen.destroy()
|
||||
|
||||
def sig_attach(self, widget=None):
|
||||
@@ -223,13 +211,13 @@ class Form(SignalEvent, TabContent):
|
||||
but_prev.connect('clicked', lambda *a: screen.display_prev())
|
||||
but_next.connect('clicked', lambda *a: screen.display_next())
|
||||
|
||||
def update_label(screen, data):
|
||||
position, length = data[:2]
|
||||
label.set_text('(%s/%s)' % (position or '_', length))
|
||||
but_prev.set_sensitive(position and position > 1)
|
||||
but_next.set_sensitive(position and position < length)
|
||||
class Preview():
|
||||
def record_message(self, position, length, *args):
|
||||
label.set_text('(%s/%s)' % (position or '_', length))
|
||||
but_prev.set_sensitive(position and position > 1)
|
||||
but_next.set_sensitive(position and position < length)
|
||||
screen.windows.append(Preview())
|
||||
|
||||
screen.signal_connect(self, 'record-message', update_label)
|
||||
vbox.pack_start(screen.widget, expand=True, fill=True, padding=0)
|
||||
return vbox
|
||||
|
||||
@@ -612,10 +600,8 @@ class Form(SignalEvent, TabContent):
|
||||
None, None, menu_position, None, 0,
|
||||
Gtk.get_current_event_time())
|
||||
|
||||
def _record_message(self, screen, signal_data):
|
||||
name = '_'
|
||||
if signal_data[0]:
|
||||
name = str(signal_data[0])
|
||||
def record_message(self, position, size, max_size, record_id):
|
||||
name = str(position) if position else '_'
|
||||
for button_id in ('print', 'relate', 'email', 'open', 'save',
|
||||
'attach'):
|
||||
button = self.buttons[button_id]
|
||||
@@ -626,11 +612,10 @@ class Form(SignalEvent, TabContent):
|
||||
action_type = 'print'
|
||||
can_be_sensitive |= any(
|
||||
b.attrs.get('keyword', 'action') == action_type
|
||||
for b in screen.get_buttons())
|
||||
for b in self.screen.get_buttons())
|
||||
elif button_id == 'save':
|
||||
can_be_sensitive &= not self.screen.readonly
|
||||
button.props.sensitive = (bool(signal_data[0])
|
||||
and can_be_sensitive)
|
||||
button.props.sensitive = bool(position) and can_be_sensitive
|
||||
button_switch = self.buttons['switch']
|
||||
button_switch.props.sensitive = self.screen.number_of_views > 1
|
||||
|
||||
@@ -639,23 +624,25 @@ class Form(SignalEvent, TabContent):
|
||||
menu_save = self.menu_buttons['save']
|
||||
menu_save.props.sensitive = not self.screen.readonly
|
||||
|
||||
msg = name + ' / ' + str(signal_data[1])
|
||||
if (signal_data[1] < signal_data[2]
|
||||
msg = name + ' / ' + str(size)
|
||||
if (size < max_size
|
||||
and self.screen.limit is not None
|
||||
and signal_data[2] > self.screen.limit):
|
||||
msg += _(' of ') + str(signal_data[2])
|
||||
and max_size > self.screen.limit):
|
||||
msg += _(' of ') + str(max_size)
|
||||
self.status_label.set_text(msg)
|
||||
self.message_info()
|
||||
self.activate_save()
|
||||
self.refresh_attachment_preview()
|
||||
|
||||
def _record_modified(self, screen, signal_data):
|
||||
# As it is called via idle_add, the form could have been destroyed in
|
||||
# the meantime.
|
||||
if self.widget_get().props.window:
|
||||
self.activate_save()
|
||||
def record_modified(self):
|
||||
def _record_modified():
|
||||
# As it is called via idle_add, the form could have been destroyed
|
||||
# in the meantime.
|
||||
if self.widget_get().props.window:
|
||||
self.activate_save()
|
||||
GLib.idle_add(_record_modified)
|
||||
|
||||
def _record_saved(self, screen, signal_data):
|
||||
def record_saved(self):
|
||||
self.activate_save()
|
||||
self.refresh_resources()
|
||||
|
||||
|
||||
@@ -13,18 +13,16 @@ from tryton.config import CONFIG
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.gui.window.win_form import WinForm
|
||||
from tryton.pyson import PYSONDecoder
|
||||
from tryton.signal_event import SignalEvent
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class Action(SignalEvent):
|
||||
class Action:
|
||||
|
||||
def __init__(self, attrs=None, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
def __init__(self, view, attrs=None):
|
||||
super(Action, self).__init__()
|
||||
self.name = attrs['name']
|
||||
self.view = view
|
||||
|
||||
try:
|
||||
self.action = RPCExecute('model', 'ir.action.act_window', 'get',
|
||||
@@ -45,7 +43,7 @@ class Action(SignalEvent):
|
||||
ctx.update(rpc.CONTEXT)
|
||||
ctx['_user'] = rpc._USER
|
||||
decoder = PYSONDecoder(ctx)
|
||||
action_ctx = context.copy()
|
||||
action_ctx = self.view.context.copy()
|
||||
action_ctx.update(
|
||||
decoder.decode(self.action.get('pyson_context') or '{}'))
|
||||
ctx.update(action_ctx)
|
||||
@@ -90,10 +88,9 @@ class Action(SignalEvent):
|
||||
context_model=self.action['context_model'],
|
||||
context_domain=self.action['context_domain'],
|
||||
row_activate=self.row_activate)
|
||||
self.screen.windows.append(self)
|
||||
vbox.pack_start(
|
||||
self.screen.widget, expand=True, fill=True, padding=0)
|
||||
self.screen.signal_connect(self, 'record-message',
|
||||
self._active_changed)
|
||||
|
||||
if attrs.get('string'):
|
||||
self.title.set_text(attrs['string'])
|
||||
@@ -134,8 +131,8 @@ class Action(SignalEvent):
|
||||
def display(self):
|
||||
self.screen.search_filter(self.screen.screen_container.get_text())
|
||||
|
||||
def _active_changed(self, *args):
|
||||
self.signal('active-changed')
|
||||
def record_message(self, *args):
|
||||
self.view.active_changed(self)
|
||||
|
||||
def _get_active(self):
|
||||
if self.screen and self.screen.current_record:
|
||||
|
||||
@@ -24,9 +24,7 @@ class BoardXMLViewParser(FormXMLViewParser):
|
||||
def _parse_action(self, node, attributes):
|
||||
attributes.setdefault('yexpand', True)
|
||||
attributes.setdefault('yfill', True)
|
||||
action = Action(attributes, self.view.context)
|
||||
action.signal_connect(
|
||||
self.view, 'active-changed', self.view._active_changed)
|
||||
action = Action(self.view, attributes)
|
||||
self.view.actions.append(action)
|
||||
self.container.add(action.widget, attributes)
|
||||
|
||||
@@ -42,7 +40,7 @@ class ViewBoard(object):
|
||||
self.state_widgets = []
|
||||
self.xml_parser(self, None, {}).parse(xml)
|
||||
self.widget.show_all()
|
||||
self._active_changed(None)
|
||||
self.active_changed(None)
|
||||
|
||||
def widget_get(self):
|
||||
return self.widget
|
||||
@@ -53,7 +51,7 @@ class ViewBoard(object):
|
||||
for state_widget in self.state_widgets:
|
||||
state_widget.state_set(None)
|
||||
|
||||
def _active_changed(self, event_action, *args):
|
||||
def active_changed(self, event_action):
|
||||
for action in self.actions:
|
||||
if action == event_action:
|
||||
continue
|
||||
|
||||
@@ -155,15 +155,13 @@ class Field(object):
|
||||
previous_value = self.get(record)
|
||||
self.set(record, value)
|
||||
if previous_value != self.get(record):
|
||||
record.modified_fields.setdefault(self.name)
|
||||
record.signal('record-modified')
|
||||
self.sig_changed(record)
|
||||
record.validate(softvalidation=True)
|
||||
record.signal('record-changed')
|
||||
record.set_modified(self.name)
|
||||
elif force_change:
|
||||
self.sig_changed(record)
|
||||
record.validate(softvalidation=True)
|
||||
record.signal('record-changed')
|
||||
record.set_modified()
|
||||
|
||||
def get_client(self, record):
|
||||
return self.get(record)
|
||||
@@ -564,29 +562,6 @@ class O2MField(Field):
|
||||
def __init__(self, attrs):
|
||||
super(O2MField, self).__init__(attrs)
|
||||
|
||||
def _group_changed(self, group, record):
|
||||
if not record.parent:
|
||||
return
|
||||
# Store parent as it could be removed by validation
|
||||
parent = record.parent
|
||||
parent.modified_fields.setdefault(self.name)
|
||||
self.sig_changed(parent)
|
||||
parent.validate(softvalidation=True)
|
||||
parent.signal('record-changed')
|
||||
|
||||
def _group_list_changed(self, group, signal):
|
||||
if group.model_name == group.parent.model_name:
|
||||
group.parent.group.signal('group-list-changed', signal)
|
||||
|
||||
def _group_cleared(self, group, signal):
|
||||
if group.model_name == group.parent.model_name:
|
||||
group.parent.signal('group-cleared')
|
||||
|
||||
def _record_modified(self, group, record):
|
||||
if not record.parent:
|
||||
return
|
||||
record.parent.signal('record-modified')
|
||||
|
||||
def _set_default_value(self, record, fields=None):
|
||||
if record.value.get(self.name) is not None:
|
||||
return
|
||||
@@ -603,14 +578,6 @@ class O2MField(Field):
|
||||
if not fields and record.model_name == self.attrs['relation']:
|
||||
group.fields = record.group.fields
|
||||
record.value[self.name] = group
|
||||
self._connect_value(group)
|
||||
|
||||
def _connect_value(self, group):
|
||||
group.signal_connect(group, 'group-changed', self._group_changed)
|
||||
group.signal_connect(group, 'group-list-changed',
|
||||
self._group_list_changed)
|
||||
group.signal_connect(group, 'group-cleared', self._group_cleared)
|
||||
group.signal_connect(group, 'record-modified', self._record_modified)
|
||||
|
||||
def get_client(self, record):
|
||||
self._set_default_value(record)
|
||||
@@ -720,7 +687,7 @@ class O2MField(Field):
|
||||
new_record.set(vals, signal=False)
|
||||
group.append(new_record)
|
||||
# Trigger signal only once with the last record
|
||||
new_record.signal('record-changed')
|
||||
group.record_modified()
|
||||
|
||||
def set(self, record, value, _default=False):
|
||||
group = record.value.get(self.name)
|
||||
@@ -728,7 +695,7 @@ class O2MField(Field):
|
||||
if group is not None:
|
||||
fields = group.fields.copy()
|
||||
# Unconnect to prevent infinite loop
|
||||
group.signal_unconnect(group)
|
||||
group.parent = None
|
||||
group.destroy()
|
||||
elif record.model_name == self.attrs['relation']:
|
||||
fields = record.group.fields
|
||||
@@ -740,9 +707,10 @@ class O2MField(Field):
|
||||
self._set_default_value(record, fields=fields)
|
||||
group = record.value[self.name]
|
||||
|
||||
group.signal_unconnect(group)
|
||||
# Prevent to trigger group-cleared
|
||||
group.parent = None
|
||||
self._set_value(record, value, default=_default)
|
||||
self._connect_value(group)
|
||||
group.parent = record
|
||||
|
||||
def set_client(self, record, value, force_change=False):
|
||||
# domain inversion could try to set None as value
|
||||
@@ -757,15 +725,13 @@ class O2MField(Field):
|
||||
modified = set(previous_ids) != set(value)
|
||||
self._set_value(record, value, modified=modified)
|
||||
if modified:
|
||||
record.modified_fields.setdefault(self.name)
|
||||
record.signal('record-modified')
|
||||
self.sig_changed(record)
|
||||
record.validate(softvalidation=True)
|
||||
record.signal('record-changed')
|
||||
record.set_modified(self.name)
|
||||
elif force_change:
|
||||
self.sig_changed(record)
|
||||
record.validate(softvalidation=True)
|
||||
record.signal('record-changed')
|
||||
record.set_modified()
|
||||
|
||||
def set_default(self, record, value):
|
||||
self.set(record, value, _default=True)
|
||||
@@ -1040,11 +1006,9 @@ class BinaryField(Field):
|
||||
with open(filename, 'wb') as fp:
|
||||
fp.write(data)
|
||||
self.set(record, _FileCache(filename))
|
||||
record.modified_fields.setdefault(self.name)
|
||||
record.signal('record-modified')
|
||||
self.sig_changed(record)
|
||||
record.validate(softvalidation=True)
|
||||
record.signal('record-changed')
|
||||
record.set_modified(self.name)
|
||||
|
||||
def get_size(self, record):
|
||||
result = record.value.get(self.name) or 0
|
||||
|
||||
@@ -5,13 +5,12 @@ import operator
|
||||
from tryton import rpc
|
||||
from tryton.common import MODELACCESS, RPCException, RPCExecute
|
||||
from tryton.common.domain_inversion import is_leaf
|
||||
from tryton.signal_event import SignalEvent
|
||||
|
||||
from .field import Field, M2OField, ReferenceField
|
||||
from .record import Record
|
||||
|
||||
|
||||
class Group(SignalEvent, list):
|
||||
class Group(list):
|
||||
|
||||
def __init__(self, model_name, fields, ids=None, parent=None,
|
||||
parent_name='', child_name='', context=None, domain=None,
|
||||
@@ -22,6 +21,7 @@ class Group(SignalEvent, list):
|
||||
self.__domain = domain
|
||||
self.__domain4inversion = None
|
||||
self.lock_signal = False
|
||||
self.screens = []
|
||||
self.parent = parent
|
||||
self.parent_name = parent_name or ''
|
||||
self.children = []
|
||||
@@ -104,7 +104,7 @@ class Group(SignalEvent, list):
|
||||
super(Group, self).insert(pos, record)
|
||||
self.__id2record[record.id] = record
|
||||
if not self.lock_signal:
|
||||
self.signal('group-list-changed', ('record-added', record, pos))
|
||||
self._group_list_changed('record-added', record, pos)
|
||||
|
||||
def append(self, record):
|
||||
assert record.group is self
|
||||
@@ -114,8 +114,8 @@ class Group(SignalEvent, list):
|
||||
super(Group, self).append(record)
|
||||
self.__id2record[record.id] = record
|
||||
if not self.lock_signal:
|
||||
self.signal('group-list-changed', (
|
||||
'record-added', record, self.__len__() - 1))
|
||||
self._group_list_changed(
|
||||
'record-added', record, self.__len__() - 1)
|
||||
|
||||
def _remove(self, record):
|
||||
idx = self.index(record)
|
||||
@@ -125,7 +125,7 @@ class Group(SignalEvent, list):
|
||||
self.__getitem__(idx + 1)
|
||||
else:
|
||||
self.__getitem__(idx - 1).next[id(self)] = None
|
||||
self.signal('group-list-changed', ('record-removed', record, idx))
|
||||
self._group_list_changed('record-removed', record, idx)
|
||||
super(Group, self).remove(record)
|
||||
del self.__id2record[record.id]
|
||||
|
||||
@@ -137,8 +137,7 @@ class Group(SignalEvent, list):
|
||||
# Destroy record before propagating the signal to recursively
|
||||
# destroy also the underlying records
|
||||
record.destroy()
|
||||
self.signal(
|
||||
'group-list-changed', ('record-removed', record, length - 1))
|
||||
self._group_list_changed('record-removed', record, length - 1)
|
||||
self.pop()
|
||||
length -= 1
|
||||
self.__id2record = {}
|
||||
@@ -155,11 +154,6 @@ class Group(SignalEvent, list):
|
||||
self._remove(record)
|
||||
self.append(record)
|
||||
|
||||
def __setitem__(self, i, value):
|
||||
super(Group, self).__setitem__(i, value)
|
||||
if not self.lock_signal:
|
||||
self.signal('group-list-changed', ('record-changed', i))
|
||||
|
||||
def __repr__(self):
|
||||
return '<Group %s at %s>' % (self.model_name, id(self))
|
||||
|
||||
@@ -262,10 +256,6 @@ class Group(SignalEvent, list):
|
||||
else:
|
||||
self.insert(position, new_record)
|
||||
position += 1
|
||||
new_record.signal_connect(self, 'record-changed',
|
||||
self._record_changed)
|
||||
new_record.signal_connect(self, 'record-modified',
|
||||
self._record_modified)
|
||||
new_records.append(new_record)
|
||||
|
||||
# Remove previously removed or deleted records
|
||||
@@ -278,13 +268,12 @@ class Group(SignalEvent, list):
|
||||
|
||||
if self.lock_signal:
|
||||
self.lock_signal = False
|
||||
self.signal('group-cleared')
|
||||
self._group_list_changed('group-cleared')
|
||||
|
||||
if new_records and modified:
|
||||
for record in new_records:
|
||||
record.modified_fields.setdefault('id')
|
||||
new_records[0].signal('record-modified')
|
||||
new_records[0].signal('record-changed')
|
||||
self.record_modified()
|
||||
|
||||
self.current_idx = 0
|
||||
return True
|
||||
@@ -321,11 +310,7 @@ class Group(SignalEvent, list):
|
||||
|
||||
def add(self, record, position=-1, signal=True):
|
||||
if record.group is not self:
|
||||
record.signal_unconnect(record.group)
|
||||
record.group = self
|
||||
record.signal_connect(self, 'record-changed', self._record_changed)
|
||||
record.signal_connect(self, 'record-modified',
|
||||
self._record_modified)
|
||||
if record not in self:
|
||||
if position == -1:
|
||||
self.append(record)
|
||||
@@ -338,10 +323,8 @@ class Group(SignalEvent, list):
|
||||
if record_del.id == record.id:
|
||||
self.record_deleted.remove(record_del)
|
||||
self.current_idx = position
|
||||
record.modified_fields.setdefault('id')
|
||||
record.signal('record-modified')
|
||||
record.set_modified('id')
|
||||
if signal:
|
||||
self.signal('group-changed', record)
|
||||
# Set parent field to trigger on_change
|
||||
if self.parent and self.parent_name in self.fields:
|
||||
field = self.fields[self.parent_name]
|
||||
@@ -389,23 +372,16 @@ class Group(SignalEvent, list):
|
||||
if index is None:
|
||||
index = 0
|
||||
index += 1
|
||||
record.signal_unconnect(self, 'record-changed')
|
||||
try:
|
||||
record[field].set_client(record, index)
|
||||
finally:
|
||||
record.signal_connect(self, 'record-changed',
|
||||
self._record_changed)
|
||||
record[field].set_client(record, index)
|
||||
changed = record
|
||||
prev = record
|
||||
if changed:
|
||||
self.signal('group-changed', changed)
|
||||
self.record_modified()
|
||||
|
||||
def new(self, default=True, obj_id=None, rec_name=None):
|
||||
record = Record(self.model_name, obj_id, group=self)
|
||||
if default:
|
||||
record.default_get(rec_name=rec_name)
|
||||
record.signal_connect(self, 'record-changed', self._record_changed)
|
||||
record.signal_connect(self, 'record-modified', self._record_modified)
|
||||
return record
|
||||
|
||||
def unremove(self, record, signal=True):
|
||||
@@ -414,7 +390,7 @@ class Group(SignalEvent, list):
|
||||
if record in self.record_deleted:
|
||||
self.record_deleted.remove(record)
|
||||
if signal:
|
||||
record.signal('record-changed', record.parent)
|
||||
self.record_modified()
|
||||
|
||||
def remove(self, record, remove=False, modified=True, signal=True,
|
||||
force_remove=False):
|
||||
@@ -431,11 +407,9 @@ class Group(SignalEvent, list):
|
||||
if record not in self.record_deleted:
|
||||
self.record_deleted.append(record)
|
||||
if record.parent:
|
||||
record.parent.modified_fields.setdefault('id')
|
||||
record.parent.signal('record-modified')
|
||||
record.parent.set_modified('id')
|
||||
if modified:
|
||||
record.modified_fields.setdefault('id')
|
||||
record.signal('record-modified')
|
||||
record.set_modified('id')
|
||||
if record.id < 0 or force_remove:
|
||||
self._remove(record)
|
||||
|
||||
@@ -445,13 +419,7 @@ class Group(SignalEvent, list):
|
||||
self.current_idx = None
|
||||
|
||||
if signal:
|
||||
record.signal('record-changed', record.parent)
|
||||
|
||||
def _record_changed(self, record, signal_data):
|
||||
self.signal('group-changed', record)
|
||||
|
||||
def _record_modified(self, record, signal_data):
|
||||
self.signal('record-modified', record)
|
||||
self.record_modified()
|
||||
|
||||
def prev(self):
|
||||
if len(self) and self.current_idx is not None:
|
||||
@@ -497,7 +465,7 @@ class Group(SignalEvent, list):
|
||||
for record in new:
|
||||
record.set_default(values, signal=False)
|
||||
# Trigger signal only once with the last record
|
||||
record.signal('record-changed')
|
||||
self.record_modified()
|
||||
|
||||
def get(self, id):
|
||||
'Return record with the id'
|
||||
@@ -518,9 +486,8 @@ class Group(SignalEvent, list):
|
||||
# One2Many connect the group to itself to send signals to the parent
|
||||
# but as we are destroying the group, we do not need to notify the
|
||||
# parent otherwise it will trigger unnecessary display.
|
||||
self.signal_unconnect(self)
|
||||
self.clear()
|
||||
super(Group, self).destroy()
|
||||
self.screens.clear()
|
||||
self.parent = None
|
||||
|
||||
def get_by_path(self, path):
|
||||
'return record by path'
|
||||
@@ -537,3 +504,23 @@ class Group(SignalEvent, list):
|
||||
if not isinstance(group, Group):
|
||||
return None
|
||||
return record
|
||||
|
||||
def _group_list_changed(self, action, *args):
|
||||
group = self
|
||||
while group.parent:
|
||||
if group.model_name != group.parent.model_name:
|
||||
break
|
||||
else:
|
||||
group = group.parent.group
|
||||
for screen in group.screens:
|
||||
screen.group_list_changed(group, action, *args)
|
||||
|
||||
def record_modified(self):
|
||||
if not self.parent:
|
||||
for screen in self.screens:
|
||||
screen.record_modified()
|
||||
else:
|
||||
self.parent.modified_fields.setdefault(self.child_name)
|
||||
self.parent.group.fields[self.child_name].sig_changed(self.parent)
|
||||
self.parent.validate(softvalidation=True)
|
||||
self.parent.group.record_modified()
|
||||
@@ -6,14 +6,13 @@ import tryton.common as common
|
||||
from tryton.common import RPCException, RPCExecute
|
||||
from tryton.config import CONFIG
|
||||
from tryton.pyson import PYSONDecoder
|
||||
from tryton.signal_event import SignalEvent
|
||||
|
||||
from . import field as fields
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Record(SignalEvent):
|
||||
class Record:
|
||||
|
||||
id = -1
|
||||
|
||||
@@ -183,10 +182,6 @@ class Record(SignalEvent):
|
||||
parent = parent.parent
|
||||
return i
|
||||
|
||||
def set_modified(self, value):
|
||||
if value:
|
||||
self.signal('record-modified')
|
||||
|
||||
def children_group(self, field_name):
|
||||
if not field_name:
|
||||
return []
|
||||
@@ -448,7 +443,7 @@ class Record(SignalEvent):
|
||||
if validate:
|
||||
self.validate(softvalidation=True)
|
||||
if signal:
|
||||
self.signal('record-changed')
|
||||
self.set_modified()
|
||||
|
||||
def set(self, val, signal=True, validate=True):
|
||||
later = {}
|
||||
@@ -482,7 +477,7 @@ class Record(SignalEvent):
|
||||
if validate:
|
||||
self.validate(fieldnames, softvalidation=True)
|
||||
if signal:
|
||||
self.signal('record-changed')
|
||||
self.set_modified()
|
||||
|
||||
def set_on_change(self, values):
|
||||
for fieldname, value in list(values.items()):
|
||||
@@ -513,7 +508,7 @@ class Record(SignalEvent):
|
||||
self.parent.on_change([self.group.child_name])
|
||||
self.parent.on_change_with([self.group.child_name])
|
||||
|
||||
self.signal('record-changed')
|
||||
self.set_modified()
|
||||
|
||||
def expr_eval(self, expr):
|
||||
if not isinstance(expr, str):
|
||||
@@ -675,9 +670,13 @@ class Record(SignalEvent):
|
||||
return
|
||||
return clicks
|
||||
|
||||
def set_modified(self, field=None):
|
||||
if field:
|
||||
self.modified_fields.setdefault(field)
|
||||
self.group.record_modified()
|
||||
|
||||
def destroy(self):
|
||||
for v in self.value.values():
|
||||
if hasattr(v, 'destroy'):
|
||||
v.destroy()
|
||||
super(Record, self).destroy()
|
||||
self.destroyed = True
|
||||
@@ -27,13 +27,12 @@ from tryton.gui.window.view_form.view.screen_container import ScreenContainer
|
||||
from tryton.jsonrpc import JSONEncoder
|
||||
from tryton.pyson import PYSONDecoder
|
||||
from tryton.rpc import clear_cache
|
||||
from tryton.signal_event import SignalEvent
|
||||
|
||||
_ = gettext.gettext
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Screen(SignalEvent):
|
||||
class Screen:
|
||||
"Screen"
|
||||
|
||||
# Width of tree columns per model
|
||||
@@ -44,7 +43,7 @@ class Screen(SignalEvent):
|
||||
context = attributes.get('context', {})
|
||||
self.limit = attributes.get('limit', CONFIG['client.limit'])
|
||||
self.offset = 0
|
||||
super(Screen, self).__init__()
|
||||
self.windows = []
|
||||
|
||||
self.readonly = attributes.get('readonly', False)
|
||||
if not (MODELACCESS[model_name]['write']
|
||||
@@ -401,12 +400,15 @@ class Screen(SignalEvent):
|
||||
fields = {}
|
||||
fields_views = {}
|
||||
if self.group is not None:
|
||||
self.group.signal_unconnect(self)
|
||||
for name, field in self.group.fields.items():
|
||||
fields[name] = field.attrs
|
||||
fields_views[name] = field.views
|
||||
if self in self.group.screens:
|
||||
self.group.screens.remove(self)
|
||||
group.on_write.update(self.group.on_write)
|
||||
self.tree_states_done.clear()
|
||||
self.__group = group
|
||||
self.group.screens.append(self)
|
||||
self.parent = group.parent
|
||||
self.parent_name = group.parent_name
|
||||
if self.parent:
|
||||
@@ -416,12 +418,6 @@ class Screen(SignalEvent):
|
||||
self.current_record = group[0]
|
||||
else:
|
||||
self.current_record = None
|
||||
self.__group.signal_connect(self, 'group-cleared', self._group_cleared)
|
||||
self.__group.signal_connect(self, 'group-list-changed',
|
||||
self._group_list_changed)
|
||||
self.__group.signal_connect(self, 'record-modified',
|
||||
self._record_modified)
|
||||
self.__group.signal_connect(self, 'group-changed', self._group_changed)
|
||||
self.__group.add_fields(fields)
|
||||
for name, views in fields_views.items():
|
||||
self.__group.fields[name].views.update(views)
|
||||
@@ -434,23 +430,35 @@ class Screen(SignalEvent):
|
||||
self.group = Group(self.model_name, {}, domain=self.domain,
|
||||
context=context, readonly=self.__readonly)
|
||||
|
||||
def _group_cleared(self, group, signal):
|
||||
for view in self.views:
|
||||
if view.view_type == 'tree':
|
||||
view.display(force=True)
|
||||
|
||||
def _group_list_changed(self, group, signal):
|
||||
def group_list_changed(self, group, action, *args):
|
||||
for view in self.views:
|
||||
if hasattr(view, 'group_list_changed'):
|
||||
view.group_list_changed(group, signal)
|
||||
view.group_list_changed(group, action, *args)
|
||||
|
||||
def _record_modified(self, group, signal):
|
||||
self.signal('record-modified', signal)
|
||||
|
||||
def _group_changed(self, group, record):
|
||||
if not self.parent:
|
||||
def record_modified(self, display=True):
|
||||
for window in self.windows:
|
||||
if hasattr(window, 'record_modified'):
|
||||
window.record_modified()
|
||||
if display:
|
||||
self.display()
|
||||
self.signal('group-changed', record)
|
||||
|
||||
def record_message(self, position, size, max_size, record_id):
|
||||
for window in self.windows:
|
||||
if hasattr(window, 'record_message'):
|
||||
window.record_message(position, size, max_size, record_id)
|
||||
|
||||
def record_saved(self):
|
||||
for window in self.windows:
|
||||
if hasattr(window, 'record_saved'):
|
||||
window.record_saved()
|
||||
|
||||
def update_resources(self, resources):
|
||||
for window in self.windows:
|
||||
if hasattr(window, 'update_resources'):
|
||||
window.update_resources(resources)
|
||||
|
||||
def has_update_resources(self):
|
||||
return any(hasattr(w, 'update_resources') for w in self.windows)
|
||||
|
||||
def __get_current_record(self):
|
||||
if (self.__current_record is not None
|
||||
@@ -468,9 +476,10 @@ class Screen(SignalEvent):
|
||||
pos = record.get_index_path()
|
||||
else:
|
||||
pos = 0
|
||||
self.signal('record-message', (pos, len(self.group) + self.offset,
|
||||
self.search_count, record and record.id))
|
||||
self.signal('resources', record.resources if record else None)
|
||||
self.record_message(
|
||||
pos, len(self.group) + self.offset,
|
||||
self.search_count, record and record.id)
|
||||
self.update_resources(record.resources if record else None)
|
||||
# update resources after 1 second
|
||||
GLib.timeout_add(1000, self._update_resources, record)
|
||||
return True
|
||||
@@ -480,16 +489,15 @@ class Screen(SignalEvent):
|
||||
def _update_resources(self, record):
|
||||
if (record
|
||||
and record == self.current_record
|
||||
and self.signal_connected('resources')):
|
||||
self.signal('resources', record.get_resources())
|
||||
and self.has_update_resources()):
|
||||
self.update_resources(record.get_resources())
|
||||
return False
|
||||
|
||||
def destroy(self):
|
||||
self.windows.clear()
|
||||
for view in self.views:
|
||||
view.destroy()
|
||||
del self.views[:]
|
||||
super(Screen, self).destroy()
|
||||
self.group.signal_unconnect(self)
|
||||
self.group.destroy()
|
||||
|
||||
def default_row_activate(self):
|
||||
@@ -695,7 +703,7 @@ class Screen(SignalEvent):
|
||||
path = path[:-1] + ((path[-1][0], record_id),)
|
||||
self.current_record = self.group.get_by_path(path)
|
||||
self.display()
|
||||
self.signal('record-saved')
|
||||
self.record_saved()
|
||||
return saved
|
||||
|
||||
def __get_current_view(self):
|
||||
@@ -772,8 +780,8 @@ class Screen(SignalEvent):
|
||||
self.current_record = None
|
||||
record.group.remove(record, remove=remove, signal=False,
|
||||
force_remove=force_remove)
|
||||
# send record-changed only once
|
||||
record.signal('record-changed')
|
||||
# call only once
|
||||
record.set_modified()
|
||||
|
||||
if delete:
|
||||
for record in records:
|
||||
@@ -881,6 +889,7 @@ class Screen(SignalEvent):
|
||||
self.tree_states[parent][view.children_field] = (
|
||||
[], [[path]])
|
||||
elif view.view_type == 'tree':
|
||||
view.save_width()
|
||||
paths = view.get_expanded_paths()
|
||||
selected_paths = view.get_selected_paths()
|
||||
self.tree_states[parent][view.children_field] = (
|
||||
@@ -951,7 +960,7 @@ class Screen(SignalEvent):
|
||||
if set_cursor:
|
||||
self.set_cursor(reset_view=False)
|
||||
self.set_tree_state()
|
||||
# Force record-message signal
|
||||
# Force record_message signal
|
||||
self.current_record = self.current_record
|
||||
|
||||
def display_next(self):
|
||||
@@ -1211,7 +1220,7 @@ class Screen(SignalEvent):
|
||||
except RPCException:
|
||||
return
|
||||
record.set_on_change(changes)
|
||||
record.signal('record-changed')
|
||||
record.set_modified()
|
||||
|
||||
def _button_class(self, button):
|
||||
ids = [r.id for r in self.selected_records]
|
||||
|
||||
@@ -94,7 +94,7 @@ class Many2Many(Widget):
|
||||
row_activate=self._on_activate,
|
||||
readonly=True,
|
||||
limit=None)
|
||||
self.screen.signal_connect(self, 'record-message', self._sig_label)
|
||||
self.screen.windows.append(self)
|
||||
|
||||
vbox.pack_start(self.screen.widget, expand=True, fill=True, padding=0)
|
||||
|
||||
@@ -270,8 +270,8 @@ class Many2Many(Widget):
|
||||
not self._readonly
|
||||
and self._position))
|
||||
|
||||
def _sig_label(self, screen, signal_data):
|
||||
self._position = signal_data[0]
|
||||
def record_message(self, position, *args):
|
||||
self._position = position
|
||||
self._set_button_sensitive()
|
||||
|
||||
def display(self):
|
||||
|
||||
@@ -170,7 +170,7 @@ class One2Many(Widget):
|
||||
limit=None,
|
||||
breadcrumb=breadcrumb)
|
||||
self.screen.pre_validate = bool(int(attrs.get('pre_validate', 0)))
|
||||
self.screen.signal_connect(self, 'record-message', self._sig_label)
|
||||
self.screen.windows.append(self)
|
||||
|
||||
vbox.pack_start(self.screen.widget, expand=True, fill=True, padding=0)
|
||||
|
||||
@@ -507,9 +507,9 @@ class One2Many(Widget):
|
||||
win.screen.search_filter(quote(text))
|
||||
win.show()
|
||||
|
||||
def _sig_label(self, screen, signal_data):
|
||||
self._position = signal_data[0]
|
||||
self._length = signal_data[1]
|
||||
def record_message(self, position, size, *args):
|
||||
self._position = position
|
||||
self._length = size
|
||||
if self._position:
|
||||
name = str(self._position)
|
||||
else:
|
||||
@@ -555,8 +555,7 @@ class One2Many(Widget):
|
||||
def set_value(self):
|
||||
self.screen.current_view.set_value()
|
||||
if self.screen.modified(): # TODO check if required
|
||||
self.record.modified_fields.setdefault(self.field.name)
|
||||
self.record.signal('record-modified')
|
||||
self.view.screen.record_modified(display=False)
|
||||
return True
|
||||
|
||||
def _completion_match_selected(self, completion, model, iter_):
|
||||
|
||||
@@ -78,7 +78,7 @@ class Widget(object):
|
||||
if not self.widget.props.window:
|
||||
return
|
||||
if self.record and self.get_value() == value:
|
||||
self.record.signal('record-modified')
|
||||
self.view.screen.record_modified(display=False)
|
||||
|
||||
def get_value():
|
||||
if not self.widget.props.window:
|
||||
@@ -125,6 +125,9 @@ class Widget(object):
|
||||
self.invisible_set(self.attrs.get(
|
||||
'invisible', states.get('invisible', False)))
|
||||
|
||||
def get_value(self):
|
||||
pass
|
||||
|
||||
def set_value(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -917,14 +917,16 @@ class ViewTree(View):
|
||||
return True
|
||||
return False
|
||||
|
||||
def group_list_changed(self, group, signal):
|
||||
def group_list_changed(self, group, action, *args):
|
||||
model = self.treeview.get_model()
|
||||
if model is not None:
|
||||
if signal[0] == 'record-added':
|
||||
model.added(group, signal[1])
|
||||
elif signal[0] == 'record-removed':
|
||||
model.removed(group, signal[1])
|
||||
self.display()
|
||||
if action == 'record-added':
|
||||
record, pos = args
|
||||
model.added(group, record)
|
||||
elif action == 'record-removed':
|
||||
record, pos = args
|
||||
model.removed(group, record)
|
||||
self.display(force=action == 'group-cleared')
|
||||
|
||||
def __str__(self):
|
||||
return 'ViewList (%d)' % id(self)
|
||||
|
||||
@@ -114,14 +114,13 @@ class ViewListForm(View):
|
||||
selected_rows = self.listbox.get_selected_rows()
|
||||
return [self.group[r.get_index()] for r in selected_rows]
|
||||
|
||||
def group_list_changed(self, group, signal):
|
||||
action = signal[0]
|
||||
def group_list_changed(self, group, action, *args):
|
||||
if action == 'record-added':
|
||||
position = signal[2]
|
||||
record, position = args
|
||||
self._model.emit('items-changed', position, 0, 1)
|
||||
self._view_forms.insert(position, self._view_forms.pop())
|
||||
elif action == 'record-removed':
|
||||
position = signal[2]
|
||||
record, position = args
|
||||
self._model.emit('items-changed', position, 1, 0)
|
||||
self._view_forms.pop(position)
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ class WinForm(NoModal, InfoBar):
|
||||
self.but_new = None
|
||||
|
||||
self._initial_value = None
|
||||
self.view_type = view_type
|
||||
if view_type == 'form':
|
||||
if new:
|
||||
label, icon = _("Delete"), 'tryton-delete'
|
||||
@@ -277,17 +278,11 @@ class WinForm(NoModal, InfoBar):
|
||||
self.win.vbox.pack_start(
|
||||
self.info_bar, expand=False, fill=True, padding=0)
|
||||
|
||||
self.screen.windows.append(self)
|
||||
if view_type == 'tree':
|
||||
self.screen.signal_connect(self, 'record-message', self._sig_label)
|
||||
self.screen.screen_container.alternate_viewport.connect(
|
||||
'key-press-event', self.on_keypress)
|
||||
|
||||
if self.save_current and not new:
|
||||
self.screen.signal_connect(self, 'record-message',
|
||||
self.activate_save)
|
||||
self.screen.signal_connect(self, 'record-modified',
|
||||
self.activate_save)
|
||||
|
||||
self.register()
|
||||
self.show()
|
||||
|
||||
@@ -350,22 +345,25 @@ class WinForm(NoModal, InfoBar):
|
||||
win.screen.search_filter(quote(value))
|
||||
win.show()
|
||||
|
||||
def _sig_label(self, screen, signal_data):
|
||||
def record_message(self, position, size, *args):
|
||||
self.activate_save()
|
||||
if self.view_type != 'tree':
|
||||
return
|
||||
name = '_'
|
||||
access = common.MODELACCESS[screen.model_name]
|
||||
access = common.MODELACCESS[self.screen.model_name]
|
||||
deletable = True
|
||||
if screen.current_record:
|
||||
deletable = screen.current_record.deletable
|
||||
readonly = screen.group.readonly
|
||||
if signal_data[0] >= 1:
|
||||
name = str(signal_data[0])
|
||||
if self.screen.current_record:
|
||||
deletable = self.screen.current_record.deletable
|
||||
readonly = self.screen.group.readonly
|
||||
if position >= 1:
|
||||
name = str(position)
|
||||
if self.domain is not None:
|
||||
self.but_remove.set_sensitive(True)
|
||||
if signal_data[0] < signal_data[1]:
|
||||
if position < size:
|
||||
self.but_next.set_sensitive(True)
|
||||
else:
|
||||
self.but_next.set_sensitive(False)
|
||||
if signal_data[0] > 1:
|
||||
if position > 1:
|
||||
self.but_pre.set_sensitive(True)
|
||||
else:
|
||||
self.but_pre.set_sensitive(False)
|
||||
@@ -379,10 +377,13 @@ class WinForm(NoModal, InfoBar):
|
||||
self.but_pre.set_sensitive(False)
|
||||
if self.domain is not None:
|
||||
self.but_remove.set_sensitive(False)
|
||||
line = '(%s/%s)' % (name, signal_data[1])
|
||||
line = '(%s/%s)' % (name, position)
|
||||
self.label.set_text(line)
|
||||
|
||||
def activate_save(self, *args):
|
||||
def record_modified(self, *args):
|
||||
self.activate_save()
|
||||
|
||||
def activate_save(self):
|
||||
modified = self.screen.modified()
|
||||
# Keep sensible as change could have been trigger by a Many2One edition
|
||||
sensitive = modified or self.but_ok.props.sensitive
|
||||
@@ -441,7 +442,7 @@ class WinForm(NoModal, InfoBar):
|
||||
elif record.modified:
|
||||
record.cancel()
|
||||
record.reload()
|
||||
record.signal('record-changed')
|
||||
record.set_modified()
|
||||
if added:
|
||||
record.modified_fields.setdefault('id')
|
||||
result = False
|
||||
@@ -461,12 +462,12 @@ class WinForm(NoModal, InfoBar):
|
||||
self.win.set_default_response(Gtk.ResponseType.OK)
|
||||
|
||||
def destroy(self):
|
||||
self.screen.windows.remove(self)
|
||||
self.screen.screen_container.alternate_view = False
|
||||
viewport = self.screen.screen_container.alternate_viewport
|
||||
if viewport and viewport.get_parent():
|
||||
viewport.get_parent().remove(viewport)
|
||||
self.screen.switch_view(view_type=self.prev_view.view_type)
|
||||
self.screen.signal_unconnect(self)
|
||||
if getattr(self, 'win', None):
|
||||
self.win.destroy()
|
||||
NoModal.destroy(self)
|
||||
|
||||
@@ -14,7 +14,6 @@ from tryton.exceptions import TrytonServerError
|
||||
from tryton.gui import Main
|
||||
from tryton.gui.window.nomodal import NoModal
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.signal_event import SignalEvent
|
||||
|
||||
from .infobar import InfoBar
|
||||
from .tabcontent import TabContent
|
||||
@@ -109,7 +108,7 @@ class Wizard(InfoBar):
|
||||
|
||||
self.screen.new(default=False)
|
||||
self.screen.current_record.set_default(view['defaults'])
|
||||
self.update_buttons(self.screen.current_record)
|
||||
self.update_buttons()
|
||||
self.screen.set_cursor()
|
||||
|
||||
self.screen_state = view['state']
|
||||
@@ -187,10 +186,11 @@ class Wizard(InfoBar):
|
||||
button.show()
|
||||
return button
|
||||
|
||||
def _record_changed(self, screen, record):
|
||||
self.update_buttons(record)
|
||||
def record_modified(self):
|
||||
self.update_buttons()
|
||||
|
||||
def update_buttons(self, record):
|
||||
def update_buttons(self):
|
||||
record = self.screen.current_record
|
||||
for button in self.states.values():
|
||||
button.state_set(record)
|
||||
|
||||
@@ -199,12 +199,14 @@ class Wizard(InfoBar):
|
||||
for button in buttons:
|
||||
self._get_button(button)
|
||||
|
||||
if self.screen:
|
||||
self.screen.windows.remove(self)
|
||||
|
||||
self.screen = Screen(view['model'], mode=[], context=self.context)
|
||||
self.screen.add_view(view)
|
||||
self.screen.switch_view()
|
||||
self.screen.widget.show()
|
||||
self.screen.signal_connect(self, 'group-changed',
|
||||
self._record_changed)
|
||||
self.screen.windows.append(self)
|
||||
|
||||
title = Gtk.Label(
|
||||
label=common.ellipsize(self.name, 80),
|
||||
@@ -245,7 +247,7 @@ class Wizard(InfoBar):
|
||||
self.info_bar, expand=False, fill=True, padding=0)
|
||||
|
||||
|
||||
class WizardForm(Wizard, TabContent, SignalEvent):
|
||||
class WizardForm(Wizard, TabContent):
|
||||
"Wizard"
|
||||
|
||||
def __init__(self, name=''):
|
||||
|
||||
@@ -1,42 +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.
|
||||
"Signal event"
|
||||
|
||||
|
||||
class SignalEvent(object):
|
||||
"Signal event"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SignalEvent, self).__init__(*args, **kwargs)
|
||||
self.__connects = {}
|
||||
|
||||
def signal(self, signal, signal_data=None):
|
||||
for fnct, data, key in self.__connects.get(signal, []):
|
||||
fnct(self, signal_data, *data)
|
||||
return True
|
||||
|
||||
def signal_connected(self, signal):
|
||||
return bool(self.__connects.get(signal))
|
||||
|
||||
def signal_connect(self, key, signal, fnct, *data):
|
||||
self.__connects.setdefault(signal, [])
|
||||
if (fnct, data, key) not in self.__connects[signal]:
|
||||
self.__connects[signal].append((fnct, data, key))
|
||||
return True
|
||||
|
||||
def signal_unconnect(self, key, signal=None):
|
||||
if signal is None:
|
||||
signal = list(self.__connects.keys())
|
||||
else:
|
||||
signal = [signal]
|
||||
for sig in signal:
|
||||
i = 0
|
||||
while i < len(self.__connects[sig]):
|
||||
if self.__connects[sig][i][2] is key:
|
||||
del self.__connects[sig][i]
|
||||
else:
|
||||
i += 1
|
||||
return True
|
||||
|
||||
def destroy(self):
|
||||
self.__connects = {}
|
||||
Reference in new issue
Block a user