From 642870837efd542d93b153da21ebc16cce6e8587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 2 Jan 2022 13:53:27 +0100 Subject: [PATCH] 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 --- tryton/gui/main.py | 1 - tryton/gui/window/board.py | 3 +- tryton/gui/window/form.py | 61 +++++-------- tryton/gui/window/view_board/action.py | 17 ++-- tryton/gui/window/view_board/view_board.py | 8 +- tryton/gui/window/view_form/model/field.py | 56 ++---------- tryton/gui/window/view_form/model/group.py | 91 ++++++++----------- tryton/gui/window/view_form/model/record.py | 19 ++-- tryton/gui/window/view_form/screen/screen.py | 79 +++++++++------- .../view_form/view/form_gtk/many2many.py | 6 +- .../view_form/view/form_gtk/one2many.py | 11 +-- .../window/view_form/view/form_gtk/widget.py | 5 +- tryton/gui/window/view_form/view/list.py | 14 +-- tryton/gui/window/view_form/view/list_form.py | 7 +- tryton/gui/window/win_form.py | 41 +++++---- tryton/gui/window/wizard.py | 18 ++-- tryton/signal_event.py | 42 --------- 17 files changed, 191 insertions(+), 288 deletions(-) delete mode 100644 tryton/signal_event.py diff --git a/tryton/gui/main.py b/tryton/gui/main.py index 55b4a4f178..1a1bfb0e78 100644 --- a/tryton/gui/main.py +++ b/tryton/gui/main.py @@ -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) diff --git a/tryton/gui/window/board.py b/tryton/gui/window/board.py index c9cad37b87..ecdf2e6103 100644 --- a/tryton/gui/window/board.py +++ b/tryton/gui/window/board.py @@ -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): diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py index 5ee1f1c6a9..66a1727512 100644 --- a/tryton/gui/window/form.py +++ b/tryton/gui/window/form.py @@ -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() diff --git a/tryton/gui/window/view_board/action.py b/tryton/gui/window/view_board/action.py index 649b86cdfe..256c87f22b 100644 --- a/tryton/gui/window/view_board/action.py +++ b/tryton/gui/window/view_board/action.py @@ -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: diff --git a/tryton/gui/window/view_board/view_board.py b/tryton/gui/window/view_board/view_board.py index 1cc0297f6f..7d71280021 100644 --- a/tryton/gui/window/view_board/view_board.py +++ b/tryton/gui/window/view_board/view_board.py @@ -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 diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index c22df4d189..c8bcd3e032 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -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 diff --git a/tryton/gui/window/view_form/model/group.py b/tryton/gui/window/view_form/model/group.py index b43c128174..22fb22cff5 100644 --- a/tryton/gui/window/view_form/model/group.py +++ b/tryton/gui/window/view_form/model/group.py @@ -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 '' % (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() diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 760f1e2602..1850784a5d 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -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 diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py index e824d4f73a..891747f550 100644 --- a/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/gui/window/view_form/screen/screen.py @@ -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] diff --git a/tryton/gui/window/view_form/view/form_gtk/many2many.py b/tryton/gui/window/view_form/view/form_gtk/many2many.py index e1f1e38b16..8530ba05c3 100644 --- a/tryton/gui/window/view_form/view/form_gtk/many2many.py +++ b/tryton/gui/window/view_form/view/form_gtk/many2many.py @@ -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): diff --git a/tryton/gui/window/view_form/view/form_gtk/one2many.py b/tryton/gui/window/view_form/view/form_gtk/one2many.py index 2ef15add0e..ff030eccb3 100644 --- a/tryton/gui/window/view_form/view/form_gtk/one2many.py +++ b/tryton/gui/window/view_form/view/form_gtk/one2many.py @@ -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_): diff --git a/tryton/gui/window/view_form/view/form_gtk/widget.py b/tryton/gui/window/view_form/view/form_gtk/widget.py index 179e7ecd8e..d3a250e383 100644 --- a/tryton/gui/window/view_form/view/form_gtk/widget.py +++ b/tryton/gui/window/view_form/view/form_gtk/widget.py @@ -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 diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index cf89f7e0b1..4d64c5f384 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -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) diff --git a/tryton/gui/window/view_form/view/list_form.py b/tryton/gui/window/view_form/view/list_form.py index 504b955c00..2ef66df312 100644 --- a/tryton/gui/window/view_form/view/list_form.py +++ b/tryton/gui/window/view_form/view/list_form.py @@ -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) diff --git a/tryton/gui/window/win_form.py b/tryton/gui/window/win_form.py index 1622ca9707..0e14e1320b 100644 --- a/tryton/gui/window/win_form.py +++ b/tryton/gui/window/win_form.py @@ -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) diff --git a/tryton/gui/window/wizard.py b/tryton/gui/window/wizard.py index 9b08d91075..214469ae69 100644 --- a/tryton/gui/window/wizard.py +++ b/tryton/gui/window/wizard.py @@ -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=''): diff --git a/tryton/signal_event.py b/tryton/signal_event.py deleted file mode 100644 index 699d2ef789..0000000000 --- a/tryton/signal_event.py +++ /dev/null @@ -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 = {}