def display(self): super(DictWidget, self).display() if not self.field: return record_id = self.record.id if self.record else None if record_id != self._record_id: for key in list(self.fields.keys()): self._sig_remove(None, key, modified=False) self._record_id = record_id value = self.field.get_client(self.record) if self.field else {} new_key_names = set(value.keys()) - set(self.field.keys) if new_key_names: self.field.add_keys(list(new_key_names), self.record) decoder = PYSONDecoder() for key, val in sorted(value.items()): if key not in self.field.keys: continue if key not in self.fields: self.add_line(key) widget = self.fields[key] widget.set_value(val) widget.set_readonly(self._readonly) key_domain = decoder.decode(self.field.keys[key].get('domain') or '[]') widget_class(widget.widget, 'invalid', not eval_domain(key_domain, value)) for key in set(self.fields.keys()) - set(value.keys()): self._sig_remove(None, key, modified=False) self._set_button_sensitive()
def domain(self): group_domain = self.__domain if group_domain and isinstance(group_domain, str): decoder = PYSONDecoder(self.context) group_domain = decoder.decode(group_domain) if self.parent and self.child_name: field = self.parent.group.fields[self.child_name] return [group_domain, field.domain_get(self.parent)] return group_domain
def get_tab_domain(self): if not self.notebook: return [] idx = self.notebook.get_current_page() if idx < 0: return [] ctx, domain = self.tab_domain[idx][1] decoder = PYSONDecoder(ctx) return decoder.decode(domain)
def search_filter(self, search_string=None, only_ids=False): if self.context_screen: context_record = self.context_screen.current_record if not context_record.validate(): self.clear() self.context_screen.display(set_cursor=True) return False context = self.context context.update(self.context_screen.get_on_change_value()) self.new_group(context) domain = self.search_domain(search_string, True) if self.context_domain: decoder = PYSONDecoder(self.context) domain = ['AND', domain, decoder.decode(self.context_domain)] tab_domain = self.screen_container.get_tab_domain() if tab_domain: domain = ['AND', domain, tab_domain] context = self.context if self.screen_container.but_active.get_active(): context['active_test'] = False try: ids = RPCExecute('model', self.model_name, 'search', domain, self.offset, self.limit, self.order, context=context) except RPCException: ids = [] if not only_ids: if self.limit is not None and len(ids) == self.limit: try: self.search_count = RPCExecute('model', self.model_name, 'search_count', domain, context=context) except RPCException: self.search_count = 0 else: self.search_count = len(ids) self.screen_container.but_prev.set_sensitive(bool(self.offset)) if (self.limit is not None and 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) if only_ids: return ids self.clear() self.load(ids) self.count_tab_domain() return bool(ids)
def display(self): super(DictWidget, self).display() if not self.field: return record_id = self.record.id if self.record else None if record_id != self._record_id: for key in list(self.fields.keys()): self._sig_remove(None, key, modified=False) self._record_id = record_id value = self.field.get_client(self.record) if self.field else {} new_key_names = set(value.keys()) - set(self.field.keys) if new_key_names: self.field.add_keys(list(new_key_names), self.record) decoder = PYSONDecoder() # ABDC: Allow dictschema to be ordered by a sequence value_ordered = OrderedDict() use_sequence = any( x[1].get('sequence_order', None) for x in self.field.keys.items()) if use_sequence: for skey, svalues in sorted(self.field.keys.items(), key=lambda x: x[1]['sequence_order']): if skey not in value: continue value_ordered[skey] = value[skey] def _loop_order_hook(): if use_sequence: return value_ordered.items() else: return ((key, val) for key, val in sorted(value.items())) for key, val in _loop_order_hook(): if key not in self.field.keys: continue if key not in self.fields: self.add_line(key) widget = self.fields[key] widget.set_value(val) widget.set_readonly(self._readonly) key_domain = decoder.decode( self.field.keys[key].get('domain') or '[]') widget_class( widget.widget, 'invalid', not eval_domain(key_domain, value)) for key in set(self.fields.keys()) - set(value.keys()): self._sig_remove(None, key, modified=False) self._set_button_sensitive()
def display(self, force=False): self.treeview.display_counter += 1 current_record = self.record if (force or not self.treeview.get_model() or self.group != self.treeview.get_model().group): model = AdaptModelGroup(self.group, self.children_field, self.children_definitions) self.treeview.set_model(model) # __select_changed resets current_record to None self.record = current_record if current_record: selection = self.treeview.get_selection() path = current_record.get_index_path(model.group) # JCA : Check selection is not empty before updateing path if selection: selection.select_path(path) if not current_record: selection = self.treeview.get_selection() selection.unselect_all() self.treeview.queue_draw() if self.editable: self.set_state() self.update_arrow() self.update_sum() # Set column visibility depending on attributes and domain domain = [] if self.screen.domain: domain.append(self.screen.get_domain()) tab_domain = self.screen.screen_container.get_tab_domain() if tab_domain: domain.append(tab_domain) domain = simplify(domain) decoder = PYSONDecoder(self.screen.context) for column in self.treeview.get_columns(): name = column.name if not name: continue widget = self.get_column_widget(column) if current_record and widget.editable: widget.set_editable(current_record) if decoder.decode(widget.attrs.get('tree_invisible', '0')): column.set_visible(False) elif name == self.screen.exclude_field: column.set_visible(False) else: inv_domain = domain_inversion(domain, name) if not isinstance(inv_domain, bool): inv_domain = simplify(inv_domain) unique, _, _ = unique_value(inv_domain) column.set_visible(not unique or bool(self.children_field))
class PYSON(Char): def __init__(self, view, attrs): super(PYSON, self).__init__(view, attrs) self.encoder = PYSONEncoder() self.decoder = PYSONDecoder(noeval=True) self.entry.connect('key-release-event', self.validate_pyson) def get_encoded_value(self): value = self.get_value() if not value: return value try: return self.encoder.encode(eval(value, CONTEXT)) except (ValueError, TypeError, NameError, SyntaxError): return None def set_value(self, record, field): field.set_client(record, self.get_encoded_value()) def get_client_value(self, record, field): value = super(PYSON, self).get_client_value(record, field) if value: value = repr(self.decoder.decode(value)) return value def validate_pyson(self, *args): icon = gtk.STOCK_OK if self.get_encoded_value() is None: icon = gtk.STOCK_CANCEL self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, icon) def _focus_out(self): self.validate_pyson() super(PYSON, self)._focus_out()
def updateDatas(self, group): self.datas = {} self.labels = {} self.ids = {} self.group = group minx = None maxx = None for model in group: x = model[self.xfield['name']].get(model) if not minx: minx = x if not maxx: maxx = x minx = min(minx, x) maxx = max(maxx, x) self.labels[x] = model[self.xfield['name']].get_client(model) self.ids.setdefault(x, []) self.ids[x].append(model.id) self.datas.setdefault(x, {}) for yfield in self.yfields: key = yfield.get('key', yfield['name']) self.datas[x].setdefault(key, 0.0) if yfield.get('domain'): context = rpc.CONTEXT.copy() context['context'] = context.copy() context['_user'] = rpc._USER for field in model.group.fields: context[field] = model[field].get(model) if not PYSONDecoder(context).decode(yfield['domain']): continue if yfield['name'] == '#': self.datas[x][key] += 1 else: value = model[yfield['name']].get(model) if isinstance(value, datetime.timedelta): value = value.total_seconds() self.datas[x][key] += float(value or 0) date_format = self.view.screen.context.get('date_format', '%x') datetime_format = date_format + ' %X' if isinstance(minx, datetime.datetime): date = minx while date <= maxx: self.labels[date] = datetime_strftime(date, datetime_format) self.datas.setdefault(date, {}) for yfield in self.yfields: self.datas[date].setdefault( yfield.get('key', yfield['name']), 0.0) date += relativedelta(days=1) elif isinstance(minx, datetime.date): date = minx while date <= maxx: self.labels[date] = datetime_strftime(date, date_format) self.datas.setdefault(date, {}) for yfield in self.yfields: self.datas[date].setdefault( yfield.get('key', yfield['name']), 0.0) date += relativedelta(days=1)
def count_tab_domain(self): def set_tab_counter(count, idx): try: count = count() except RPCException: count = None self.screen_container.set_tab_counter(count, idx) screen_domain = self.search_domain(self.screen_container.get_text()) for idx, (name, (ctx, domain), count) in enumerate( self.screen_container.tab_domain): if not count: continue decoder = PYSONDecoder(ctx) domain = ['AND', decoder.decode(domain), screen_domain] set_tab_counter(lambda: None, idx) RPCExecute('model', self.model_name, 'search_count', domain, context=self.context, callback=functools.partial(set_tab_counter, idx=idx))
def _parse_button(self, node, attributes): button = Button(self.view, attributes) self.view.state_widgets.append(button) column = Gtk.TreeViewColumn( attributes.get('string', ''), button.renderer) column._type = 'button' column.name = None column.set_cell_data_func(button.renderer, button.setter) self._set_column_widget(column, attributes, arrow=False) self._set_column_width(column, attributes) decoder = PYSONDecoder(self.view.screen.context) column.set_visible( not decoder.decode(attributes.get('tree_invisible', '0'))) self.view.treeview.append_column(column)
class PYSON(Char): def __init__(self, view, attrs): super(PYSON, self).__init__(view, attrs) self.encoder = PYSONEncoder() self.decoder = PYSONDecoder(noeval=True) self.entry.connect('key-release-event', self.validate_pyson) def get_encoded_value(self): value = self.get_value() if not value: return value try: return self.encoder.encode(eval(value, CONTEXT)) except Exception: return None def set_value(self): # avoid modification because different encoding value = self.get_encoded_value() previous = self.field.get_client(self.record) if (previous and value == self.encoder.encode( self.decoder.decode(previous))): value = previous self.field.set_client(self.record, value) def get_client_value(self): value = super(PYSON, self).get_client_value() if value: value = repr(self.decoder.decode(value)) return value def validate_pyson(self, *args): icon = 'tryton-ok' if self.get_encoded_value() is None: icon = 'tryton-error' pixbuf = IconFactory.get_pixbuf(icon, Gtk.IconSize.MENU) self.entry.set_icon_from_pixbuf( Gtk.EntryIconPosition.SECONDARY, pixbuf) def _focus_out(self): self.validate_pyson() super(PYSON, self)._focus_out()
def _parse_button(self, node): node_attrs = node_attributes(node) widget = Button(self, node_attrs) self.state_widgets.append(widget) column = gtk.TreeViewColumn(node_attrs.get('string', ''), widget.renderer) column._type = 'button' column.name = None column.set_cell_data_func(widget.renderer, widget.setter) self.set_column_widget(column, None, node_attrs, arrow=False) self.set_column_width(column, None, node_attrs) decoder = PYSONDecoder(self.screen.context) column.set_visible( not decoder.decode(node_attrs.get('tree_invisible', '0'))) self.treeview.append_column(column)
def expr_eval(self, expr): if not isinstance(expr, str): return expr ctx = self.get_eval() ctx['context'] = self.get_context() ctx['active_model'] = self.model_name ctx['active_id'] = self.id if self.parent and self.parent_name: ctx['_parent_' + self.parent_name] = \ common.EvalEnvironment(self.parent) val = PYSONDecoder(ctx).decode(expr) return val
class PYSON(Char): def __init__(self, view, attrs): super(PYSON, self).__init__(view, attrs) self.encoder = PYSONEncoder() self.decoder = PYSONDecoder(noeval=True) self.entry.connect('key-release-event', self.validate_pyson) def get_encoded_value(self): value = self.get_value() if not value: return value try: return self.encoder.encode(eval(value, CONTEXT)) except Exception: return None def set_value(self, record, field): # avoid modification because different encoding value = self.get_encoded_value() previous = field.get_client(record) if (previous and value == self.encoder.encode( self.decoder.decode(previous))): value = previous field.set_client(record, value) def get_client_value(self, record, field): value = super(PYSON, self).get_client_value(record, field) if value: value = repr(self.decoder.decode(value)) return value def validate_pyson(self, *args): icon = gtk.STOCK_OK if self.get_encoded_value() is None: icon = gtk.STOCK_CANCEL self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, icon) def _focus_out(self): self.validate_pyson() super(PYSON, self)._focus_out()
def validate(self, record, softvalidation=False, pre_validate=None): valid = super(DictField, self).validate(record, softvalidation, pre_validate) if self.attrs.get('readonly'): return valid decoder = PYSONDecoder() field_value = self.get_eval(record) domain = [] for key in field_value: if key not in self.keys: continue key_domain = self.keys[key].get('domain') if key_domain: domain.append(decoder.decode(key_domain)) valid_value = eval_domain(domain, field_value) if not valid_value: self.get_state_attrs(record)['invalid'] = 'domain' return valid and valid_value
def update_domain(self, actions): domain_ctx = self.context.copy() domain_ctx['context'] = domain_ctx domain_ctx['_user'] = rpc._USER for action in actions: if action.active: domain_ctx[action.name] = action.active new_domain = PYSONDecoder(domain_ctx).decode( self.action['pyson_domain']) if self.domain == new_domain: return del self.domain[:] self.domain.extend(new_domain) if hasattr(self, 'screen'): # Catch early update # Using idle_add to prevent corruption of the event who triggered # the update. gtk.idle_add(self.display)
def update_domain(self, actions): domain_ctx = self.context.copy() domain_ctx['_actions'] = {} for action in actions: domain_ctx['_actions'][action.name] = action.active new_domain = PYSONDecoder(domain_ctx).decode( self.action['pyson_domain']) if self.domain == new_domain: return del self.domain[:] self.domain.extend(new_domain) if hasattr(self, 'screen'): # Catch early update # Using idle_add to prevent corruption of the event who triggered # the update. def display(): if self.screen.widget.props.window: self.display() GLib.idle_add(display)
def __init__(self, view, attrs): super(PYSON, self).__init__(view, attrs) self.encoder = PYSONEncoder() self.decoder = PYSONDecoder(noeval=True) self.entry.connect('key-release-event', self.validate_pyson)
def __init__(self, attrs=None, context=None): super(Action, self).__init__() self.name = attrs['name'] self.context = context or {} try: self.action = RPCExecute('model', 'ir.action.act_window', 'get', self.name) except RPCException: raise view_ids = None self.action['view_mode'] = None if self.action.get('views', []): view_ids = [x[0] for x in self.action['views']] self.action['view_mode'] = [x[1] for x in self.action['views']] elif self.action.get('view_id', False): view_ids = [self.action['view_id'][0]] if 'view_mode' in attrs: self.action['view_mode'] = attrs['view_mode'] self.action.setdefault('pyson_domain', '[]') self.context.update(rpc.CONTEXT) self.context['_user'] = rpc._USER self.context.update( PYSONDecoder(self.context).decode( self.action.get('pyson_context', '{}'))) eval_ctx = self.context.copy() self.context.update( PYSONDecoder(eval_ctx).decode( self.action.get('pyson_context', '{}'))) self.domain = [] self.update_domain([]) search_context = self.context.copy() search_context['context'] = self.context search_context['_user'] = rpc._USER search_value = PYSONDecoder(search_context).decode( self.action['pyson_search_value'] or '{}') self.widget = gtk.Frame() self.widget.set_border_width(0) vbox = gtk.VBox(homogeneous=False, spacing=3) self.widget.add(vbox) self.title = gtk.Label() self.widget.set_label_widget(self.title) self.widget.set_label_align(0.0, 0.5) self.widget.show_all() self.screen = Screen(self.action['res_model'], mode=self.action['view_mode'], context=self.context, view_ids=view_ids, domain=self.domain, search_value=search_value, row_activate=self.row_activate) vbox.pack_start(self.screen.widget, expand=True, fill=True) self.screen.signal_connect(self, 'record-message', self._active_changed) if attrs.get('string'): self.title.set_text(attrs['string']) else: self.title.set_text(self.action['name']) self.widget.set_size_request(int(attrs.get('width', -1)), int(attrs.get('height', -1))) self.screen.search_filter()
def state_set(self, record): super().state_set(record) if not self.get_visible(): return if CONFIG['client.modepda']: self.hide() return if record: data = { 'model': record.model_name, 'id': record.id, 'ids': [record.id], } context = record.get_context() pyson_ctx = { 'active_model': record.model_name, 'active_id': record.id, 'active_ids': [record.id], } self._current = record.id else: data = {} context = {} pyson_ctx = {} self._current = None pyson_ctx['context'] = context try: self.disconnect_by_func(self.__class__.clicked) except TypeError: pass self.connect('clicked', self.__class__.clicked, [data, context]) action = common.RPCExecute('model', 'ir.action', 'get_action_value', self.action_id, context=context) self.set_label(action['rec_name']) decoder = PYSONDecoder(pyson_ctx) domain = decoder.decode(action['pyson_domain']) if action.get('pyson_search_value'): domain = [domain, decoder.decode(action['pyson_search_value'])] tab_domains = [(n, decoder.decode(d)) for n, d, c in action['domains'] if c] if tab_domains: label = ('%s\n' % action['rec_name']) + '\n'.join( '%s (%%d)' % n for n, _ in tab_domains) else: label = '%s (%%d)' % action['rec_name'] if record and self.action_id in record.links_counts: counter = record.links_counts[self.action_id] self._set_label_counter(label, counter) else: counter = [0] * (len(tab_domains) or 1) if record: record.links_counts[self.action_id] = counter if tab_domains: for i, (_, tab_domain) in enumerate(tab_domains): common.RPCExecute( 'model', action['res_model'], 'search_count', ['AND', domain, tab_domain], context=context, callback=functools.partial(self._set_count, idx=i, current=self._current, counter=counter, label=label)) else: common.RPCExecute('model', action['res_model'], 'search_count', domain, context=context, callback=functools.partial( self._set_count, current=self._current, counter=counter, label=label))
def _exec_action(action, data=None, context=None): if context is None: context = {} if data is None: data = {} else: data = data.copy() if 'type' not in (action or {}): return if action['type'] == 'ir.action.act_window': view_ids = False view_mode = None if action.get('views', []): view_ids = [x[0] for x in action['views']] view_mode = [x[1] for x in action['views']] elif action.get('view_id', False): view_ids = [action['view_id'][0]] action.setdefault('pyson_domain', '[]') ctx = { 'active_model': data.get('res_model'), 'active_id': data.get('id', False), 'active_ids': data.get('ids', []), } ctx.update(rpc.CONTEXT) eval_ctx = ctx.copy() eval_ctx['_user'] = rpc._USER action_ctx = PYSONDecoder(eval_ctx).decode( action.get('pyson_context') or '{}') ctx.update(action_ctx) ctx.update(context) domain_context = ctx.copy() domain_context['context'] = ctx domain_context['_user'] = rpc._USER domain = PYSONDecoder(domain_context).decode( action['pyson_domain']) search_context = ctx.copy() search_context['context'] = ctx search_context['_user'] = rpc._USER search_value = PYSONDecoder(search_context).decode( action['pyson_search_value'] or '[]') tab_domain_context = ctx.copy() tab_domain_context['context'] = ctx tab_domain_context['_user'] = rpc._USER decoder = PYSONDecoder(tab_domain_context) tab_domain = [(n, decoder.decode(d)) for n, d in action['domains']] name = False if action.get('window_name', True): name = action.get('name', False) res_model = action.get('res_model', data.get('res_model')) res_id = action.get('res_id', data.get('res_id')) Window.create(view_ids, res_model, res_id, domain, action_ctx, view_mode, name=name, limit=action.get('limit'), auto_refresh=action.get('auto_refresh'), search_value=search_value, icon=(action.get('icon.rec_name') or ''), tab_domain=tab_domain) elif action['type'] == 'ir.action.wizard': Window.create_wizard(action['wiz_name'], data, direct_print=action.get('direct_print', False), email_print=action.get('email_print', False), email=action.get('email'), name=action.get('name', False), context=context, icon=(action.get('icon.rec_name') or ''), window=action.get('window', False)) elif action['type'] == 'ir.action.report': Action.exec_report(action['report_name'], data, direct_print=action.get('direct_print', False), email_print=action.get('email_print', False), email=action.get('email'), context=context) elif action['type'] == 'ir.action.url': if action['url']: webbrowser.open(action['url'], new=2)
def _exec_action(action, data=None, context=None): if context is None: context = {} else: context = context.copy() if 'date_format' not in context: context['date_format'] = rpc.CONTEXT.get( 'locale', {}).get('date', '%x') if data is None: data = {} else: data = data.copy() if 'type' not in (action or {}): return data['action_id'] = action['id'] if action['type'] == 'ir.action.act_window': view_ids = False view_mode = None if action.get('views', []): view_ids = [x[0] for x in action['views']] view_mode = [x[1] for x in action['views']] elif action.get('view_id', False): view_ids = [action['view_id'][0]] action.setdefault('pyson_domain', '[]') ctx = { 'active_model': data.get('model'), 'active_id': data.get('id'), 'active_ids': data.get('ids', []), } ctx.update(rpc.CONTEXT) ctx['_user'] = rpc._USER decoder = PYSONDecoder(ctx) action_ctx = decoder.decode(action.get('pyson_context') or '{}') action_ctx.update(context) action_ctx.update(ctx) action_ctx.update(data.get('extra_context', {})) action_ctx['context'] = ctx decoder = PYSONDecoder(action_ctx) domain = action['pyson_domain'] order = decoder.decode(action['pyson_order']) search_value = decoder.decode(action['pyson_search_value'] or '[]') tab_domain = [(n, (action_ctx, d)) for n, d in action['domains']] name = False if action.get('window_name', True): name = action.get('name', False) res_model = action.get('res_model', data.get('res_model')) res_id = action.get('res_id', data.get('res_id')) Window.create(view_ids, res_model, res_id, domain, action_ctx, order, view_mode, name=name, limit=action.get('limit'), search_value=search_value, icon=(action.get('icon.rec_name') or ''), tab_domain=tab_domain, context_model=action['context_model']) elif action['type'] == 'ir.action.wizard': context = copy.deepcopy(context) context.update(data.get('extra_context', {})) Window.create_wizard(action['wiz_name'], data, direct_print=action.get('direct_print', False), email_print=action.get('email_print', False), email=action.get('email'), name=action.get('name', False), context=context, icon=(action.get('icon.rec_name') or ''), window=action.get('window', False)) elif action['type'] == 'ir.action.report': Action.exec_report(action['report_name'], data, direct_print=action.get('direct_print', False), email_print=action.get('email_print', False), email=action.get('email'), context=context) elif action['type'] == 'ir.action.url': if action['url']: webbrowser.open(action['url'], new=2)
def _exec_action(action, data=None, context=None): from tryton.gui.window import Window if context is None: context = {} else: context = context.copy() if data is None: data = {} else: data = data.copy() if 'type' not in (action or {}): return context.pop('active_id', None) context.pop('active_ids', None) context.pop('active_model', None) def add_name_suffix(name, context=None): if not data.get('ids') or not data.get('model'): return name max_records = 5 ids = list(filter(lambda id: id >= 0, data['ids']))[:max_records] if not ids: return name rec_names = RPCExecute('model', data['model'], 'read', ids, ['rec_name'], context=context) name_suffix = _(', ').join([x['rec_name'] for x in rec_names]) if len(data['ids']) > len(ids): name_suffix += _(',...') if name_suffix: return _('%s (%s)') % (name, name_suffix) else: return name data['action_id'] = action['id'] if action['type'] == 'ir.action.act_window': view_ids = [] view_mode = None if action.get('views', []): view_ids = [x[0] for x in action['views']] view_mode = [x[1] for x in action['views']] elif action.get('view_id', False): view_ids = [action['view_id'][0]] action.setdefault('pyson_domain', '[]') ctx = { 'active_model': data.get('model'), 'active_id': data.get('id'), 'active_ids': data.get('ids') or [], } ctx.update(rpc.CONTEXT) ctx['_user'] = rpc._USER decoder = PYSONDecoder(ctx) action_ctx = context.copy() action_ctx.update( decoder.decode(action.get('pyson_context') or '{}')) ctx.update(action_ctx) ctx['context'] = ctx decoder = PYSONDecoder(ctx) domain = decoder.decode(action['pyson_domain']) order = decoder.decode(action['pyson_order']) search_value = decoder.decode(action['pyson_search_value'] or '[]') tab_domain = [(n, decoder.decode(d), c) for n, d, c in action['domains']] name = action.get('name', '') if action.get('keyword', ''): name = add_name_suffix(name, action_ctx) res_model = action.get('res_model', data.get('res_model')) res_id = action.get('res_id', data.get('res_id')) limit = action.get('limit') if limit is None: limit = CONFIG['client.limit'] Window.create(res_model, view_ids=view_ids, res_id=res_id, domain=domain, context=action_ctx, order=order, mode=view_mode, name=name, limit=limit, search_value=search_value, icon=(action.get('icon.rec_name') or ''), tab_domain=tab_domain, context_model=action['context_model'], context_domain=action['context_domain']) elif action['type'] == 'ir.action.wizard': name = action.get('name', '') if action.get('keyword', 'form_action') == 'form_action': name = add_name_suffix(name, context) Window.create_wizard(action['wiz_name'], data, direct_print=action.get( 'direct_print', False), name=name, context=context, icon=(action.get('icon.rec_name') or ''), window=action.get('window', False)) elif action['type'] == 'ir.action.report': Action.exec_report(action['report_name'], data, direct_print=action.get('direct_print', False), context=context) elif action['type'] == 'ir.action.url': if action['url']: webbrowser.open(action['url'], new=2)
def get_domain(self): if not self.domain or not isinstance(self.domain, basestring): return self.domain decoder = PYSONDecoder(self.context) return decoder.decode(self.domain)
def __init__(self, attrs=None, context=None): if context is None: context = {} super(Action, self).__init__() self.name = attrs['name'] try: self.action = RPCExecute('model', 'ir.action.act_window', 'get', self.name) except RPCException: raise view_ids = [] view_mode = None if self.action.get('views', []): view_ids = [x[0] for x in self.action['views']] view_mode = [x[1] for x in self.action['views']] elif self.action.get('view_id', False): view_ids = [self.action['view_id'][0]] self.action.setdefault('pyson_domain', '[]') ctx = {} ctx.update(rpc.CONTEXT) ctx['_user'] = rpc._USER decoder = PYSONDecoder(ctx) action_ctx = context.copy() action_ctx.update( decoder.decode(self.action.get('pyson_context') or '{}')) ctx.update(action_ctx) ctx['context'] = ctx decoder = PYSONDecoder(ctx) self.domain = decoder.decode(self.action['pyson_domain']) order = decoder.decode(self.action['pyson_order']) search_value = decoder.decode(self.action['pyson_search_value'] or '[]') tab_domain = [(n, decoder.decode(d), c) for n, d, c in self.action['domains']] limit = self.action.get('limit') if limit is None: limit = CONFIG['client.limit'] self.context = ctx self.domain = [] self.update_domain([]) self.widget = Gtk.Frame() self.widget.set_border_width(0) vbox = Gtk.VBox(homogeneous=False, spacing=3) self.widget.add(vbox) self.title = Gtk.Label() self.widget.set_label_widget(self.title) self.widget.set_label_align(0.0, 0.5) self.widget.show_all() self.screen = Screen(self.action['res_model'], view_ids=view_ids, domain=self.domain, context=action_ctx, order=order, mode=view_mode, limit=limit, search_value=search_value, tab_domain=tab_domain, context_model=self.action['context_model'], context_domain=self.action['context_domain'], row_activate=self.row_activate) 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']) else: self.title.set_text(self.action['name']) self.widget.set_size_request(int(attrs.get('width', -1)), int(attrs.get('height', -1))) self.screen.search_filter()
def _exec_action(action, data=None, context=None): if context is None: context = {} else: context = context.copy() if 'date_format' not in context: context['date_format'] = rpc.CONTEXT.get('locale', {}).get('date', '%x') if data is None: data = {} else: data = data.copy() if 'type' not in (action or {}): return data['action_id'] = action['id'] if action['type'] == 'ir.action.act_window': view_ids = False view_mode = None if action.get('views', []): view_ids = [x[0] for x in action['views']] view_mode = [x[1] for x in action['views']] elif action.get('view_id', False): view_ids = [action['view_id'][0]] action.setdefault('pyson_domain', '[]') ctx = { 'active_model': data.get('model'), 'active_id': data.get('id'), 'active_ids': data.get('ids', []), } ctx.update(rpc.CONTEXT) ctx['_user'] = rpc._USER decoder = PYSONDecoder(ctx) action_ctx = decoder.decode(action.get('pyson_context') or '{}') action_ctx.update(context) action_ctx.update(ctx) action_ctx.update(data.get('extra_context', {})) action_ctx['context'] = ctx decoder = PYSONDecoder(action_ctx) domain = action['pyson_domain'] order = decoder.decode(action['pyson_order']) search_value = decoder.decode(action['pyson_search_value'] or '[]') tab_domain = [(n, (action_ctx, d)) for n, d in action['domains']] name = False if action.get('window_name', True): name = action.get('name', False) res_model = action.get('res_model', data.get('res_model')) res_id = action.get('res_id', data.get('res_id')) Window.create(view_ids, res_model, res_id, domain, action_ctx, order, view_mode, name=name, limit=action.get('limit'), search_value=search_value, icon=(action.get('icon.rec_name') or ''), tab_domain=tab_domain, context_model=action['context_model']) elif action['type'] == 'ir.action.wizard': context = copy.deepcopy(context) context.update(data.get('extra_context', {})) Window.create_wizard(action['wiz_name'], data, direct_print=action.get( 'direct_print', False), email_print=action.get('email_print', False), email=action.get('email'), name=action.get('name', False), context=context, icon=(action.get('icon.rec_name') or ''), window=action.get('window', False)) elif action['type'] == 'ir.action.report': Action.exec_report(action['report_name'], data, direct_print=action.get('direct_print', False), email_print=action.get('email_print', False), email=action.get('email'), context=context) elif action['type'] == 'ir.action.url': if action['url']: webbrowser.open(action['url'], new=2)
def display(self, force=False): self.treeview.display_counter += 1 current_record = self.record if (force or not self.treeview.get_model() or self.group != self.treeview.get_model().group): model = AdaptModelGroup(self.group, self.children_field) self.treeview.set_model(model) # __select_changed resets current_record to None self.record = current_record if current_record: selection = self.treeview.get_selection() path = current_record.get_index_path(model.group) selection.select_path(path) # The search column must be set each time the model is changed self.treeview.set_search_column(0) if not current_record: selection = self.treeview.get_selection() selection.unselect_all() self.treeview.queue_draw() if self.editable: self.set_state() self.update_arrow() self.update_sum() # Set column visibility depending on attributes and domain domain = [] if self.screen.domain: domain.append(self.screen.domain) tab_domain = self.screen.screen_container.get_tab_domain() if tab_domain: domain.append(tab_domain) domain = simplify(domain) decoder = PYSONDecoder(self.screen.context) tree_column_optional = self.screen.tree_column_optional.get( self.view_id, {}) for column in self.treeview.get_columns(): name = column.name if not name: continue widget = self.get_column_widget(column) widget.set_editable() if column.name in tree_column_optional: optional = tree_column_optional[column.name] else: optional = bool(int(widget.attrs.get('optional', '0'))) invisible = decoder.decode(widget.attrs.get('tree_invisible', '0')) if invisible or optional: column.set_visible(False) elif name == self.screen.exclude_field: column.set_visible(False) else: inv_domain = domain_inversion(domain, name) if not isinstance(inv_domain, bool): inv_domain = simplify(inv_domain) unique, _, _ = unique_value(inv_domain) column.set_visible(not unique or bool(self.children_field)) if self.children_field: for i, column in enumerate(self.treeview.get_columns()): if self.draggable and not i: continue if column.get_visible(): self.treeview.set_expander_column(column) break
def sig_win_menu(self, prefs=None): from tryton.gui.window.view_form.screen import Screen if not prefs: try: prefs = RPCExecute('model', 'res.user', 'get_preferences', False) except RPCException: return False if self.menu_screen: self.menu_screen.save_tree_state() for child in self.menu.get_children(): self.menu.remove(child) action = PYSONDecoder().decode(prefs['pyson_menu']) view_ids = [] if action.get('views', []): view_ids = [x[0] for x in action['views']] elif action.get('view_id', False): view_ids = [action['view_id'][0]] ctx = rpc.CONTEXT.copy() decoder = PYSONDecoder(ctx) action_ctx = decoder.decode(action.get('pyson_context') or '{}') domain = decoder.decode(action['pyson_domain']) screen = Screen(action['res_model'], mode=['tree'], view_ids=view_ids, domain=domain, context=action_ctx, readonly=True, limit=None, row_activate=self.menu_row_activate) # Use alternate view to not show search box screen.screen_container.alternate_view = True screen.switch_view(view_type=screen.current_view.view_type) self.menu.pack_start(screen.screen_container.alternate_viewport, True, True) treeview = screen.current_view.treeview treeview.set_headers_visible(False) # Favorite column column = gtk.TreeViewColumn() column.name = None column._type = None favorite_renderer = CellRendererClickablePixbuf() column.pack_start(favorite_renderer, expand=False) def favorite_setter(column, cell, store, iter_): menu = store.get_value(iter_, 0) favorite = menu.value.get('favorite') if favorite: stock_id = 'tryton-star' elif favorite is False: stock_id = 'tryton-unstar' else: stock_id = '' pixbuf = treeview.render_icon(stock_id=stock_id, size=gtk.ICON_SIZE_MENU, detail=None) cell.set_property('pixbuf', pixbuf) column.set_cell_data_func(favorite_renderer, favorite_setter) def toggle_favorite(renderer, path, treeview): if treeview.props.window: self.toggle_favorite(renderer, path, treeview) favorite_renderer.connect( 'clicked', lambda *a: gobject.idle_add(toggle_favorite, *a), treeview) # Unset fixed height mode to add column treeview.set_fixed_height_mode(False) treeview.set_property('enable-grid-lines', gtk.TREE_VIEW_GRID_LINES_NONE) treeview.append_column(column) screen.search_filter() screen.display(set_cursor=True) self.menu_screen = screen
def time_format(field): return PYSONDecoder({}).decode(field['format'])
def search_box(self, widget): def window_hide(window, *args): window.hide() self.search_entry.grab_focus() def key_press(widget, event): if event.keyval == gtk.keysyms.Escape: window_hide(widget) return True return False def search(): self.search_window.hide() text = '' for label, entry in self.search_table.fields: if isinstance(entry, gtk.ComboBox): value = quote(entry.get_active_text()) or None elif isinstance(entry, (Dates, Selection)): value = entry.get_value() else: value = quote(entry.get_text()) or None if value is not None: text += quote(label) + ': ' + value + ' ' self.set_text(text) self.do_search() # Store text after doing the search # because domain parser could simplify the text self.last_search_text = self.get_text() if not self.search_window: self.search_window = gtk.Window() self.search_window.set_transient_for(widget.get_toplevel()) self.search_window.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_POPUP_MENU) self.search_window.set_destroy_with_parent(True) self.search_window.set_decorated(False) self.search_window.set_deletable(False) self.search_window.connect('delete-event', window_hide) self.search_window.connect('key-press-event', key_press) self.search_window.connect('focus-out-event', window_hide) def toggle_window_hide(combobox, shown): if combobox.props.popup_shown: self.search_window.handler_block_by_func(window_hide) else: self.search_window.handler_unblock_by_func(window_hide) vbox = gtk.VBox() fields = [ f for f in self.screen.domain_parser.fields.itervalues() if f.get('searchable', True) ] self.search_table = gtk.Table(rows=len(fields), columns=2) self.search_table.set_homogeneous(False) self.search_table.set_border_width(5) self.search_table.set_row_spacings(2) self.search_table.set_col_spacings(2) # Fill table with fields self.search_table.fields = [] for i, field in enumerate(fields): label = gtk.Label(field['string']) label.set_alignment(0.0, 0.0) self.search_table.attach(label, 0, 1, i, i + 1, yoptions=gtk.FILL) yoptions = False if field['type'] == 'boolean': if hasattr(gtk, 'ComboBoxText'): entry = gtk.ComboBoxText() else: entry = gtk.combo_box_new_text() entry.connect('notify::popup-shown', toggle_window_hide) entry.append_text('') selections = (_('True'), _('False')) for selection in selections: entry.append_text(selection) elif field['type'] == 'selection': selections = tuple(x[1] for x in field['selection']) entry = Selection(selections) yoptions = gtk.FILL | gtk.EXPAND elif field['type'] in ('date', 'datetime', 'time'): date_format = self.screen.context.get('date_format', '%x') if field['type'] == 'date': entry = Dates(date_format) elif field['type'] in ('datetime', 'time'): time_format = PYSONDecoder({}).decode(field['format']) if field['type'] == 'time': entry = Times(time_format) elif field['type'] == 'datetime': entry = DateTimes(date_format, time_format) entry.connect_activate(lambda *a: search()) else: entry = gtk.Entry() entry.connect('activate', lambda *a: search()) label.set_mnemonic_widget(entry) self.search_table.attach(entry, 1, 2, i, i + 1, yoptions=yoptions) self.search_table.fields.append((field['string'], entry)) scrolled = gtk.ScrolledWindow() scrolled.add_with_viewport(self.search_table) scrolled.set_shadow_type(gtk.SHADOW_NONE) scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) vbox.pack_start(scrolled, expand=True, fill=True) find_button = gtk.Button(_('Find')) find_button.connect('clicked', lambda *a: search()) find_img = gtk.Image() find_img.set_from_stock('tryton-find', gtk.ICON_SIZE_SMALL_TOOLBAR) find_button.set_image(find_img) hbuttonbox = gtk.HButtonBox() hbuttonbox.set_spacing(5) hbuttonbox.pack_start(find_button) hbuttonbox.set_layout(gtk.BUTTONBOX_END) vbox.pack_start(hbuttonbox, expand=False, fill=True) self.search_window.add(vbox) vbox.show_all() new_size = map( sum, zip(self.search_table.size_request(), scrolled.size_request())) self.search_window.set_default_size(*new_size) parent = widget.get_toplevel() widget_x, widget_y = widget.translate_coordinates(parent, 0, 0) widget_allocation = widget.get_allocation() # Resize the window to not be out of the parent width, height = self.search_window.get_default_size() allocation = parent.get_allocation() delta_width = allocation.width - (widget_x + width) delta_height = allocation.height - (widget_y + widget_allocation.height + height) if delta_width < 0: width += delta_width if delta_height < 0: height += delta_height self.search_window.resize(width, height) # Move the window under the button if hasattr(widget.window, 'get_root_coords'): x, y = widget.window.get_root_coords(widget_allocation.x, widget_allocation.y) else: x, y = widget.window.get_origin() self.search_window.move(x, y + widget_allocation.height) self.search_window.show() self.search_window.grab_focus() if self.last_search_text.strip() != self.get_text().strip(): for label, entry in self.search_table.fields: if isinstance(entry, gtk.ComboBox): entry.set_active(-1) elif isinstance(entry, Dates): entry.set_values(None, None) elif isinstance(entry, Selection): entry.treeview.get_selection().unselect_all() else: entry.set_text('') if self.search_table.fields: self.search_table.fields[0][1].grab_focus()
def _exec_action(action, data=None, context=None): if context is None: context = {} if data is None: data = {} else: data = data.copy() if 'type' not in (action or {}): return def add_name_suffix(name): if not data.get('ids') or not data.get('model'): return name max_records = 5 rec_names = RPCExecute('model', data['model'], 'read', data['ids'][:max_records], ['rec_name']) name_suffix = _(', ').join([x['rec_name'] for x in rec_names]) if len(data['ids']) > max_records: name_suffix += _(u',\u2026') return _('%s (%s)') % (name, name_suffix) data['action_id'] = action['id'] if action['type'] == 'ir.action.act_window': view_ids = [] view_mode = None if action.get('views', []): view_ids = [x[0] for x in action['views']] view_mode = [x[1] for x in action['views']] elif action.get('view_id', False): view_ids = [action['view_id'][0]] action.setdefault('pyson_domain', '[]') ctx = { 'active_model': data.get('model'), 'active_id': data.get('id'), 'active_ids': data.get('ids', []), } ctx.update(rpc.CONTEXT) ctx['_user'] = rpc._USER decoder = PYSONDecoder(ctx) # TODO: comment changes action_ctx = context.copy() action_ctx.update(decoder.decode( action.get('pyson_context') or '{}')) action_ctx.update(ctx) action_ctx.update(data.get('extra_context', {})) action_ctx['context'] = ctx decoder = PYSONDecoder(action_ctx) domain = action['pyson_domain'] order = decoder.decode(action['pyson_order']) search_value = decoder.decode(action['pyson_search_value'] or '[]') tab_domain = [(n, (action_ctx, d), c) for n, d, c in action['domains']] name = action.get('name', '') if action.get('keyword', '') == 'form_relate': name = add_name_suffix(name) res_model = action.get('res_model', data.get('res_model')) res_id = action.get('res_id', data.get('res_id')) limit = action.get('limit') if limit is None: limit = CONFIG['client.limit'] Window.create(res_model, view_ids=view_ids, res_id=res_id, domain=domain, context=action_ctx, order=order, mode=view_mode, name=name, limit=limit, search_value=search_value, icon=(action.get('icon.rec_name') or ''), tab_domain=tab_domain, context_model=action['context_model'], context_domain=action.get('context_domain', None)) elif action['type'] == 'ir.action.wizard': name = action.get('name', '') if action.get('keyword', 'form_action') == 'form_action': name = add_name_suffix(name) context = copy.deepcopy(context) context.update(data.get('extra_context', {})) Window.create_wizard(action['wiz_name'], data, direct_print=action.get('direct_print', False), email_print=action.get('email_print', False), email=action.get('email'), name=name, context=context, icon=(action.get('icon.rec_name') or ''), window=action.get('window', False)) elif action['type'] == 'ir.action.report': Action.exec_report(action['report_name'], data, direct_print=action.get('direct_print', False), email_print=action.get('email_print', False), email=action.get('email'), context=context) elif action['type'] == 'ir.action.url': if action['url']: webbrowser.open(action['url'], new=2)
def search_box(self, widget): def window_hide(window, *args): window.hide() self.search_entry.grab_focus() def search(): self.search_popover.popdown() text = '' for label, entry in self.search_grid.fields: if isinstance(entry, Gtk.ComboBoxText): value = quote(entry.get_active_text()) or None elif isinstance(entry, (Between, Selection)): value = entry.get_value() else: value = quote(entry.get_text()) or None if value is not None: text += quote(label) + ': ' + value + ' ' self.set_text(text) self.do_search() # Store text after doing the search # because domain parser could simplify the text self.last_search_text = self.get_text() if not self.search_popover: self.search_popover = Gtk.Popover() self.search_popover.set_relative_to(widget) vbox = Gtk.VBox() fields = [ f for f in self.screen.domain_parser.fields.values() if f.get('searchable', True) and '.' not in f['name'] ] self.search_grid = Gtk.Grid(column_spacing=3, row_spacing=3) # Fill table with fields self.search_grid.fields = [] for i, field in enumerate(fields): label = Gtk.Label(label=field['string'], halign=Gtk.Align.START, valign=Gtk.Align.START) self.search_grid.attach(label, 0, i, 1, 1) if field['type'] == 'boolean': entry = Gtk.ComboBoxText() entry.append_text('') selections = (_('True'), _('False')) for selection in selections: entry.append_text(selection) elif field['type'] in ['selection', 'multiselection']: selections = tuple(x[1] for x in field['selection']) entry = Selection(selections) entry.set_vexpand(True) elif field['type'] in ('date', 'datetime', 'time'): date_format = common.date_format( self.screen.context.get('date_format')) if field['type'] == 'date': entry = Dates(date_format) elif field['type'] in ('datetime', 'time'): time_format = PYSONDecoder({}).decode(field['format']) if field['type'] == 'time': entry = Times(time_format) elif field['type'] == 'datetime': entry = DateTimes(date_format, time_format) entry.connect('activate', lambda *a: search()) elif field['type'] in ['integer', 'float', 'numeric']: entry = Numbers() entry.connect('activate', lambda *a: search()) else: entry = Gtk.Entry() entry.connect('activate', lambda *a: search()) label.set_mnemonic_widget(entry) self.search_grid.attach(entry, 1, i, 1, 1) self.search_grid.fields.append((field['string'], entry)) scrolled = Gtk.ScrolledWindow() scrolled.add(self.search_grid) scrolled.set_shadow_type(Gtk.ShadowType.NONE) scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) vbox.pack_start(scrolled, expand=True, fill=True, padding=0) find_button = Gtk.Button(label=_('Find')) find_button.connect('clicked', lambda *a: search()) find_button.set_image( common.IconFactory.get_image('tryton-search', Gtk.IconSize.SMALL_TOOLBAR)) find_button.set_can_default(True) self.search_popover.set_default_widget(find_button) hbuttonbox = Gtk.HButtonBox() hbuttonbox.pack_start(find_button, expand=False, fill=False, padding=0) hbuttonbox.set_layout(Gtk.ButtonBoxStyle.END) vbox.pack_start(hbuttonbox, expand=False, fill=True, padding=0) self.search_popover.add(vbox) vbox.show_all() scrolled.set_size_request( -1, min(self.search_grid.get_preferred_height()[1], 400)) self.search_popover.set_pointing_to( widget.get_icon_area(Gtk.EntryIconPosition.PRIMARY)) self.search_popover.popup() if self.search_grid.fields: self.search_grid.fields[0][1].grab_focus() if self.last_search_text.strip() != self.get_text().strip(): for label, entry in self.search_grid.fields: if isinstance(entry, Gtk.ComboBoxText): entry.set_active(-1) elif isinstance(entry, Between): entry.set_value(None, None) elif isinstance(entry, Selection): entry.treeview.get_selection().unselect_all() else: entry.set_text('')