Add notification message

issue11065
review367951004
This commit is contained in:
Cédric Krier committed 2022-04-11 23:41:21 +02:00
1 parent 5ade55babe
commit 985e2f1928
12 files changed
+121 -68

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Support notification message
* Add option to define logging output location
* Remove TreeViewControl
* Manage optional column
+23 -23
View File
@@ -2,16 +2,16 @@
# this repository contains the full copyright notices and license terms.
from . import timedelta
from .common import (
COLOR_SCHEMES, MODELACCESS, MODELHISTORY, MODELNAME, TRYTON_ICON,
VIEW_SEARCH, IconFactory, Login, Logout, RPCContextReload, RPCException,
RPCExecute, RPCProgress, Tooltips, apply_label_attributes, ask,
check_version, concurrency, data2pixbuf, date_format, ellipsize, error,
file_open, file_selection, file_write, filter_domain, generateColorscheme,
get_align, get_hostname, get_port, get_sensible_widget,
get_toplevel_window, hex2rgb, highlight_rgb, humanize, idle_add, mailto,
message, node_attributes, process_exception, resize_pixbuf, selection,
setup_window, slugify, sur, sur_3b, timezoned_date, to_xml,
untimezoned_date, userwarning, warning)
COLOR_SCHEMES, MODELACCESS, MODELHISTORY, MODELNAME, MODELNOTIFICATION,
TRYTON_ICON, VIEW_SEARCH, IconFactory, Login, Logout, RPCContextReload,
RPCException, RPCExecute, RPCProgress, Tooltips, apply_label_attributes,
ask, check_version, concurrency, data2pixbuf, date_format, ellipsize,
error, file_open, file_selection, file_write, filter_domain,
generateColorscheme, get_align, get_hostname, get_port,
get_sensible_widget, get_toplevel_window, hex2rgb, highlight_rgb, humanize,
idle_add, mailto, message, node_attributes, process_exception,
resize_pixbuf, selection, setup_window, slugify, sur, sur_3b,
timezoned_date, to_xml, untimezoned_date, userwarning, warning)
from .domain_inversion import (
concat, domain_inversion, eval_domain, extract_reference_models,
filter_leaf, inverse_leaf, localize_domain, merge,
@@ -19,16 +19,16 @@ from .domain_inversion import (
from .environment import EvalEnvironment
__all__ = [
IconFactory, MODELACCESS, MODELHISTORY, MODELNAME, VIEW_SEARCH,
get_toplevel_window, get_sensible_widget, selection, file_selection,
slugify, file_write, file_open, mailto, message, warning, userwarning, sur,
sur_3b, ask, concurrency, error, to_xml, process_exception, Login, Logout,
node_attributes, hex2rgb, highlight_rgb, generateColorscheme, RPCException,
RPCProgress, RPCExecute, RPCContextReload, Tooltips, COLOR_SCHEMES,
filter_domain, timezoned_date, untimezoned_date, humanize, get_hostname,
get_port, resize_pixbuf, data2pixbuf, apply_label_attributes, ellipsize,
get_align, date_format, idle_add, domain_inversion, eval_domain,
localize_domain, merge, inverse_leaf, filter_leaf,
prepare_reference_domain, extract_reference_models, concat, simplify,
unique_value, EvalEnvironment, timedelta, check_version, TRYTON_ICON,
setup_window]
IconFactory, MODELACCESS, MODELHISTORY, MODELNAME, MODELNOTIFICATION,
VIEW_SEARCH, get_toplevel_window, get_sensible_widget, selection,
file_selection, slugify, file_write, file_open, mailto, message, warning,
userwarning, sur, sur_3b, ask, concurrency, error, to_xml,
process_exception, Login, Logout, node_attributes, hex2rgb, highlight_rgb,
generateColorscheme, RPCException, RPCProgress, RPCExecute,
RPCContextReload, Tooltips, COLOR_SCHEMES, filter_domain, timezoned_date,
untimezoned_date, humanize, get_hostname, get_port, resize_pixbuf,
data2pixbuf, apply_label_attributes, ellipsize, get_align, date_format,
idle_add, domain_inversion, eval_domain, localize_domain, merge,
inverse_leaf, filter_leaf, prepare_reference_domain,
extract_reference_models, concat, simplify, unique_value, EvalEnvironment,
timedelta, check_version, TRYTON_ICON, setup_window]
+19
View File
@@ -286,6 +286,25 @@ class ModelName:
MODELNAME = ModelName()
class ModelNotification:
_depends = {}
def load_names(self):
try:
self._depends = rpc.execute(
'model', 'ir.model', 'get_notification', rpc.CONTEXT)
except TrytonServerError:
pass
def get(self, model):
if not self._depends:
self.load_names()
return self._depends.get(model, [])
MODELNOTIFICATION = ModelNotification()
class ViewSearch(object):
searches = {}
+1
View File
@@ -518,6 +518,7 @@ class Main(Gtk.Application):
common.IconFactory.load_icons,
common.MODELACCESS.load_models,
common.MODELHISTORY.load_history,
common.MODELNOTIFICATION.load_names,
common.VIEW_SEARCH.load_searches,
):
t = threading.Thread(target=target)
+12 -11
View File
@@ -293,7 +293,7 @@ class Form(TabContent):
def sig_logs(self, widget=None):
current_record = self.screen.current_record
if not current_record or current_record.id < 0:
self.message_info(
self.info_bar_add(
_('You have to select one record.'), Gtk.MessageType.INFO)
return False
@@ -407,10 +407,10 @@ class Form(TabContent):
msg = _('Are you sure to remove those records?')
if sur(msg):
if not self.screen.remove(delete=True, force_remove=True):
self.message_info(
self.info_bar_add(
_('Records not removed.'), Gtk.MessageType.ERROR)
else:
self.message_info(_('Records removed.'), Gtk.MessageType.INFO)
self.info_bar_add(_('Records removed.'), Gtk.MessageType.INFO)
self.screen.count_tab_domain(True)
def sig_import(self, widget=None):
@@ -467,7 +467,7 @@ class Form(TabContent):
if not self.modified_save():
return
self.screen.new()
self.message_info()
self.info_bar_clear()
self.activate_save()
def sig_copy(self, widget=None):
@@ -476,7 +476,8 @@ class Form(TabContent):
if not self.modified_save():
return
if self.screen.copy():
self.message_info(_('Working now on the duplicated record(s).'),
self.info_bar_add(
_('Working now on the duplicated record(s).'),
Gtk.MessageType.INFO)
self.screen.count_tab_domain(True)
@@ -489,11 +490,11 @@ class Form(TabContent):
or self.screen.writable):
return
if self.screen.save_current():
self.message_info(_('Record saved.'), Gtk.MessageType.INFO)
self.info_bar_add(_('Record saved.'), Gtk.MessageType.INFO)
self.screen.count_tab_domain(True)
return True
else:
self.message_info(
self.info_bar_add(
self.screen.invalid_message(), Gtk.MessageType.ERROR)
return False
@@ -501,14 +502,14 @@ class Form(TabContent):
if not self.modified_save():
return
self.screen.display_prev()
self.message_info()
self.info_bar_clear()
self.activate_save()
def sig_next(self, widget=None):
if not self.modified_save():
return
self.screen.display_next()
self.message_info()
self.info_bar_clear()
self.activate_save()
def sig_reload(self, test_modified=True):
@@ -529,7 +530,7 @@ class Form(TabContent):
set_cursor = True
break
self.screen.display(set_cursor=set_cursor)
self.message_info()
self.info_bar_clear()
self.set_buttons_sensitive()
self.activate_save()
self.screen.count_tab_domain()
@@ -628,7 +629,7 @@ class Form(TabContent):
else:
msg = "%s/%s" % (name, common.humanize(size))
self.status_label.set_text(msg)
self.message_info()
self.info_bar_clear()
self.activate_save()
self.refresh_attachment_preview()
+24 -20
View File
@@ -9,27 +9,31 @@ class InfoBar(object):
toolbar = None
def create_info_bar(self):
self.info_label = Gtk.Label()
self.__box = Gtk.VBox()
self.__box.show()
self.__messages = set()
return self.__box
self.info_bar = Gtk.InfoBar()
self.info_bar.get_content_area().pack_start(
self.info_label, False, False, 0)
self.info_bar.set_show_close_button(True)
self.info_bar.connect('response', lambda i, r: i.hide())
def info_bar_add(self, message, type_=Gtk.MessageType.ERROR):
key = (message, type_)
if key not in self.__messages:
info_bar = Gtk.InfoBar()
self.__box.pack_start(info_bar, False, False, 0)
area = info_bar.get_content_area()
area.add(Gtk.Label(label=message))
info_bar.set_show_close_button(True)
info_bar.connect('response', self.__response, key)
info_bar.set_message_type(type_)
info_bar.show_all()
def response(self, bar, response):
if response == Gtk.ResponseType.CLOSE:
bar.hide()
def __response(self, widget, response, key):
self.__messages.add(key)
self.__box.remove(widget)
def message_info(self, message=None, type_=Gtk.MessageType.ERROR):
if message:
# Work around https://bugzilla.gnome.org/show_bug.cgi?id=710888
parent = self.info_bar.get_parent()
self.info_bar.unparent()
self.info_bar.set_parent(parent)
def info_bar_refresh(self):
for child in self.__box.get_children():
self.__box.remove(child)
self.info_label.set_label(message)
self.info_bar.set_message_type(type_)
self.info_bar.show_all()
else:
self.info_bar.hide()
def info_bar_clear(self):
self.info_bar_refresh()
self.__messages.clear()
+1 -2
View File
@@ -203,9 +203,8 @@ class TabContent(InfoBar):
self.scrolledwindow.show()
self.main.pack1(self.scrolledwindow, resize=True, shrink=False)
self.create_info_bar()
self.widget.pack_start(
self.info_bar, expand=False, fill=True, padding=0)
self.create_info_bar(), expand=False, fill=True, padding=0)
def make_title_bar(self):
tooltips = common.Tooltips()
@@ -524,3 +524,7 @@ class Group(list):
self.parent.group.fields[self.child_name].sig_changed(self.parent)
self.parent.validate(softvalidation=True)
self.parent.group.record_modified()
def record_notify(self, notifications):
for screen in self.screens:
screen.record_notify(notifications)
+16 -3
View File
@@ -566,9 +566,22 @@ class Record:
'model', self.model_name, 'on_change',
values, fieldnames, context=self.get_context())
except RPCException:
return
for change in changes:
self.set_on_change(change)
pass
else:
for change in changes:
self.set_on_change(change)
notification_fields = common.MODELNOTIFICATION.get(self.model_name)
if set(fieldnames) & set(notification_fields):
values = self._get_on_change_args(notification_fields)
try:
notifications = RPCExecute(
'model', self.model_name, 'on_change_notify', values,
context=self.get_context())
except RPCException:
pass
else:
self.group.record_notify(notifications)
def on_change_with(self, field_names):
field_names = set(field_names)
@@ -20,6 +20,7 @@ from tryton.common import (
sur, warning)
from tryton.common.domain_parser import DomainParser
from tryton.config import CONFIG
from tryton.gui.window.infobar import InfoBar
from tryton.gui.window.view_form.model.group import Group
from tryton.gui.window.view_form.view import View
from tryton.gui.window.view_form.view.screen_container import ScreenContainer
@@ -447,6 +448,18 @@ class Screen:
if display:
self.display()
def record_notify(self, notifications):
for window in self.windows:
if isinstance(window, InfoBar):
window.info_bar_refresh()
for type_, message in notifications:
type_ = {
'info': Gtk.MessageType.INFO,
'warning': Gtk.MessageType.WARNING,
'error': Gtk.MessageType.ERROR,
}.get(type_, Gtk.MessageType.WARNING)
window.info_bar_add(message, type_)
def record_message(self, position, size, max_size, record_id):
for window in self.windows:
if hasattr(window, 'record_message'):
+4 -5
View File
@@ -274,9 +274,8 @@ class WinForm(NoModal, InfoBar):
scroll.add(self.screen.screen_container.alternate_viewport)
self.create_info_bar()
self.win.vbox.pack_start(
self.info_bar, expand=False, fill=True, padding=0)
self.create_info_bar(), expand=False, fill=True, padding=0)
self.screen.windows.append(self)
if view_type == 'tree':
@@ -422,12 +421,12 @@ class WinForm(NoModal, InfoBar):
if record:
validate = record.pre_validate()
if not validate:
self.message_info(self.screen.invalid_message(),
Gtk.MessageType.ERROR)
self.info_bar_add(
self.screen.invalid_message(), Gtk.MessageType.ERROR)
self.screen.set_cursor()
self.screen.display()
return
self.message_info()
self.info_bar_clear()
if response_id == Gtk.ResponseType.ACCEPT:
self.new()
return
+3 -4
View File
@@ -171,10 +171,10 @@ class Wizard(InfoBar):
if (button_attrs.get('validate', True)
and not self.screen.current_record.validate()):
self.screen.display(set_cursor=True)
self.message_info(
self.info_bar_add(
self.screen.invalid_message(), Gtk.MessageType.ERROR)
return
self.message_info()
self.info_bar_clear()
self.state = state
self.process()
@@ -242,9 +242,8 @@ class Wizard(InfoBar):
self.widget.pack_start(
self.scrolledwindow, expand=True, fill=True, padding=0)
self.create_info_bar()
self.widget.pack_start(
self.info_bar, expand=False, fill=True, padding=0)
self.create_info_bar(), expand=False, fill=True, padding=0)
class WizardForm(Wizard, TabContent):