changeset 36c90d8bd0f7 in tryton:default
details: https://hg.tryton.org/tryton?cmd=changeset;node=36c90d8bd0f7
description:
Add link button
issue9051
review280841002
diffstat:
CHANGELOG | 1 +
tryton/gui/window/board.py | 1 +
tryton/gui/window/view_board/view_board.py | 3 +
tryton/gui/window/view_form/model/record.py | 2 +
tryton/gui/window/view_form/view/form.py | 7 +-
tryton/gui/window/view_form/view/form_gtk/state_widget.py | 112 ++++++++++++++
6 files changed, 125 insertions(+), 1 deletions(-)
diffs (210 lines):
diff -r f6f4d5759eaa -r 36c90d8bd0f7 CHANGELOG
--- a/CHANGELOG Sun Apr 12 23:21:08 2020 +0200
+++ b/CHANGELOG Mon Apr 13 12:21:55 2020 +0200
@@ -1,3 +1,4 @@
+* Support link button on form
* Add URL for the current export
* Add option to export listed records
* Retire App Menu
diff -r f6f4d5759eaa -r 36c90d8bd0f7 tryton/gui/window/board.py
--- a/tryton/gui/window/board.py Sun Apr 12 23:21:08 2020 +0200
+++ b/tryton/gui/window/board.py Mon Apr 13 12:21:55 2020 +0200
@@ -40,6 +40,7 @@
self.name = name
self.create_tabcontent()
+ self.board.reload()
def get_toolbars(self):
return {}
diff -r f6f4d5759eaa -r 36c90d8bd0f7 tryton/gui/window/view_board/view_board.py
--- a/tryton/gui/window/view_board/view_board.py Sun Apr 12 23:21:08
2020 +0200
+++ b/tryton/gui/window/view_board/view_board.py Mon Apr 13 12:21:55
2020 +0200
@@ -38,6 +38,7 @@
def __init__(self, xml, context=None):
self.context = context
self.actions = []
+ self.state_widgets = []
self.xml_parser(self, None, {}).parse(xml)
self.widget.show_all()
self._active_changed(None)
@@ -48,6 +49,8 @@
def reload(self):
for action in self.actions:
action.display()
+ for state_widget in self.state_widgets:
+ state_widget.state_set(None)
def _active_changed(self, event_action, *args):
for action in self.actions:
diff -r f6f4d5759eaa -r 36c90d8bd0f7 tryton/gui/window/view_form/model/record.py
--- a/tryton/gui/window/view_form/model/record.py Sun Apr 12 23:21:08
2020 +0200
+++ b/tryton/gui/window/view_form/model/record.py Mon Apr 13 12:21:55
2020 +0200
@@ -30,6 +30,7 @@
self._timestamp = None
self.resources = None
self.button_clicks = {}
+ self.links_counts = {}
self.next = {} # Used in Group list
self.value = {}
self.autocompletion = {}
@@ -292,6 +293,7 @@
self.modified_fields.clear()
self._timestamp = None
self.button_clicks.clear()
+ self.links_counts.clear()
def get_timestamp(self):
result = {self.model_name + ',' + str(self.id): self._timestamp}
diff -r f6f4d5759eaa -r 36c90d8bd0f7 tryton/gui/window/view_form/view/form.py
--- a/tryton/gui/window/view_form/view/form.py Sun Apr 12 23:21:08 2020 +0200
+++ b/tryton/gui/window/view_form/view/form.py Mon Apr 13 12:21:55 2020 +0200
@@ -34,7 +34,7 @@
from .form_gtk.multiselection import MultiSelection
from .form_gtk.pyson import PYSON
from .form_gtk.state_widget import (Label, VBox, Image, Frame, ScrolledWindow,
- Notebook, Expander)
+ Notebook, Expander, Link)
_ = gettext.gettext
@@ -258,6 +258,11 @@
self.view.state_widgets.append(button)
self.container.add(button, attributes)
+ def _parse_link(self, node, attributes):
+ link = Link(attrs=attributes)
+ self.view.state_widgets.append(link)
+ self.container.add(link, attributes)
+
def _parse_image(self, node, attributes):
image = Image(attrs=attributes)
self.view.state_widgets.append(image)
diff -r f6f4d5759eaa -r 36c90d8bd0f7
tryton/gui/window/view_form/view/form_gtk/state_widget.py
--- a/tryton/gui/window/view_form/view/form_gtk/state_widget.py Sun Apr 12
23:21:08 2020 +0200
+++ b/tryton/gui/window/view_form/view/form_gtk/state_widget.py Mon Apr 13
12:21:55 2020 +0200
@@ -1,8 +1,13 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
+import functools
+
from gi.repository import Gtk
import tryton.common as common
+from tryton.action import Action
+from tryton.config import CONFIG
+from tryton.pyson import PYSONDecoder
class StateMixin(object):
@@ -104,3 +109,110 @@
if not label:
label = None
super(Expander, self).__init__(label=label, attrs=attrs)
+
+
+class Link(StateMixin, Gtk.Button):
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.set_relief(Gtk.ReliefStyle.NONE)
+ self.set_can_focus(False)
+ if self.attrs.get('icon'):
+ self.set_always_show_image(True)
+ self.set_image(common.IconFactory.get_image(
+ self.attrs['icon'], Gtk.IconSize.LARGE_TOOLBAR))
+ self.set_image_position(Gtk.PositionType.TOP)
+ self._current = None
+
+ @property
+ def action_id(self):
+ return int(self.attrs['id'])
+
+ 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 _set_count(self, value, idx=0, current=None, counter=None, label=''):
+ if current != self._current:
+ return
+ try:
+ counter[idx] = value()
+ except common.RPCException:
+ pass
+ self._set_label_counter(label, counter)
+
+ def _set_label_counter(self, label, counter):
+ self.set_label(label % tuple(counter))
+ if self.attrs.get('empty') == 'hide':
+ if any(counter):
+ self.show()
+ else:
+ self.hide()
+
+ def clicked(self, data):
+ Action.execute(self.action_id, *data, keyword=True)