New wizard design

review172002
This commit is contained in:
Cédric Krier committed 2012-01-02 17:49:06 +01:00
1 parent 4dfd2a76b0
commit a1476fa4cd
8 files changed
+192 -215

No files matched your search

+1 -15
View File
@@ -25,26 +25,12 @@ class Action(object):
if email is None:
email = {}
data = data.copy()
ids = data['ids']
del data['ids']
ctx = rpc.CONTEXT.copy()
ctx.update(context)
ctx['direct_print'] = direct_print
ctx['email_print'] = email_print
ctx['email'] = email
if not ids:
args = ('model', data['model'], 'search', [], 0, None, None, ctx)
try:
ids = rpc.execute(*args)
except TrytonServerError, exception:
ids = common.process_exception(exception, *args)
if not ids:
return False
if ids == []:
message(_('Nothing to print!'))
return False
data['id'] = ids[0]
args = ('report', name, 'execute', ids, data, ctx)
args = ('report', name, 'execute', data.get('ids', []), data, ctx)
rpcprogress = common.RPCProgress('execute', args)
try:
res = rpcprogress.run()
+38
View File
@@ -0,0 +1,38 @@
#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 gtk
from tryton.common import ICONFACTORY
class Button(gtk.Button):
def __init__(self, attrs=None):
self.attrs = attrs or {}
label = attrs.get('string', '')
if '_' not in label:
label = '_' + label
super(Button, self).__init__(label=label, stock=None,
use_underline=True)
self._set_icon(attrs.get('icon'))
def _set_icon(self, stock):
if not stock:
self.set_image(gtk.Image())
return
ICONFACTORY.register_icon(stock)
icon = gtk.Image()
icon.set_from_stock(stock, gtk.ICON_SIZE_SMALL_TOOLBAR)
self.set_image(icon)
def state_set(self, record):
if record:
states = record.expr_eval(self.attrs.get('states', {}), check_load=False)
else:
states = {}
if states.get('invisible', False):
self.hide()
else:
self.show()
self.set_sensitive(not states.get('readonly', False))
self._set_icon(states.get('icon', self.attrs.get('icon')))
@@ -227,6 +227,16 @@ class Record(SignalEvent):
value['id'] = self.id
return value
def get_on_change_value(self, check_load=True):
if check_load:
self._check_load()
value = {}
for name, field in self.group.fields.iteritems():
value[name] = field.get_on_change_value(self,
check_load=check_load)
value['id'] = self.id
return value
def cancel(self):
self._loaded.clear()
self.modified_fields.clear()
@@ -508,6 +508,12 @@ class Screen(SignalEvent):
includeid=includeid, check_load=check_load,
get_modifiedonly=get_modifiedonly)
def get_on_change_value(self, check_load=True):
if not self.current_record:
return None
self.current_view.set_value()
return self.current_record.get_on_change_value(check_load=check_load)
def modified(self):
res = False
if self.current_view.view_type != 'tree':
+50 -1
View File
@@ -9,6 +9,8 @@ import tryton.rpc as rpc
import tryton.common as common
from interface import ParserView
from tryton.config import CONFIG
from tryton.exceptions import TrytonServerError
from tryton.action import Action
_ = gettext.gettext
@@ -22,7 +24,8 @@ class ViewForm(ParserView):
self.view_type = 'form'
for button in self.buttons:
button.form = self
if isinstance(button, gtk.Button):
button.connect('clicked', self.button_clicked)
self.widgets = children
for widgets in self.widgets.itervalues():
@@ -148,3 +151,49 @@ class ViewForm(ParserView):
if focus_widget.widget.is_ancestor(child):
notebook.set_current_page(i)
focus_widget.grab_focus()
def button_clicked(self, widget):
record = self.screen.current_record
record_id = self.screen.save_current()
attrs = widget.attrs
if record_id:
if not attrs.get('confirm', False) or \
common.sur(attrs['confirm']):
button_type = attrs.get('type', 'workflow')
ctx = rpc.CONTEXT.copy()
ctx.update(record.context_get())
if button_type == 'workflow':
args = ('model', self.screen.model_name,
'workflow_trigger_validate', record_id,
attrs['name'], ctx)
try:
rpc.execute(*args)
except TrytonServerError, exception:
common.process_exception(exception, *args)
elif button_type == 'object':
args = ('model', self.screen.model_name, attrs['name'],
[record_id], ctx)
try:
rpc.execute(*args)
except TrytonServerError, exception:
common.process_exception(exception, *args)
elif button_type == 'action':
action_id = None
args = ('model', 'ir.action', 'get_action_id',
int(attrs['name']), ctx)
try:
action_id = rpc.execute(*args)
except TrytonServerError, exception:
action_id = common.process_exception(exception, *args)
if action_id:
Action.execute(action_id, {
'model': self.screen.model_name,
'id': record_id,
'ids': [record_id],
}, context=ctx)
else:
raise Exception('Unallowed button type')
self.screen.reload(written=True)
else:
self.screen.display()
@@ -3,110 +3,17 @@
import gtk
import gettext
import copy
import logging
import tryton.rpc as rpc
from tryton.action import Action
from tryton.gui.window.view_form.view.interface import ParserInterface
import tryton.common as common
from tryton.config import CONFIG, TRYTON_ICON
from tryton.gui.main import Main
import logging
from tryton.exceptions import TrytonServerError
from tryton.common.button import Button
_ = gettext.gettext
class Button(object):
def __init__(self, attrs=None):
super(Button, self).__init__()
self.attrs = attrs or {}
self.widget = gtk.Button()
text = attrs.get('string', _('Unknown'))
if '_' not in text:
text = '_' + text
self.widget.set_use_underline(True)
self.widget.set_label(text)
if attrs.get('icon', False):
try:
stock = attrs['icon']
common.ICONFACTORY.register_icon(stock)
icon = gtk.Image()
icon.set_from_stock(stock, gtk.ICON_SIZE_SMALL_TOOLBAR)
self.widget.set_image(icon)
except KeyError:
log = logging.getLogger('common')
log.warning(_('Wrong icon for the button!'))
self.widget.connect('clicked', self.button_clicked)
self.form = None #fill later by ViewForm
def button_clicked(self, widget):
if not self.form:
return
record = self.form.screen.current_record
obj_id = self.form.screen.save_current()
if obj_id:
if not self.attrs.get('confirm', False) or \
common.sur(self.attrs['confirm']):
button_type = self.attrs.get('type', 'workflow')
ctx = rpc.CONTEXT.copy()
ctx.update(record.context_get())
if button_type == 'workflow':
args = ('model', self.form.screen.model_name,
'workflow_trigger_validate', obj_id,
self.attrs['name'], ctx)
try:
rpc.execute(*args)
except TrytonServerError, exception:
common.process_exception(exception, *args)
elif button_type == 'object':
args = ('model', self.form.screen.model_name, self.attrs['name'],
[obj_id], ctx)
try:
rpc.execute(*args)
except TrytonServerError, exception:
common.process_exception(exception, *args)
elif button_type == 'action':
action_id = None
args = ('model', 'ir.action', 'get_action_id',
int(self.attrs['name']), ctx)
try:
action_id = rpc.execute(*args)
except TrytonServerError, exception:
action_id = common.process_exception(exception, *args)
if action_id:
Action.execute(action_id, {
'model': self.form.screen.model_name,
'id': obj_id,
'ids': [obj_id],
}, context=ctx)
else:
raise Exception('Unallowed button type')
self.form.screen.reload(written=True)
else:
self.form.screen.display()
def state_set(self, record):
if record:
state_changes = record.expr_eval(self.attrs.get('states', {}),
check_load=False)
else:
state_changes = {}
if state_changes.get('invisible', False):
self.widget.hide()
else:
self.widget.show()
self.widget.set_sensitive(not state_changes.get('readonly', False))
if 'icon' in state_changes:
stock = state_changes['icon']
if stock:
common.ICONFACTORY.register_icon(stock)
icon = gtk.Image()
icon.set_from_stock(stock, gtk.ICON_SIZE_SMALL_TOOLBAR)
self.widget.set_image(icon)
else:
self.widget.set_image(gtk.Image())
class Label(gtk.Label):
def __init__(self, str=None, attrs=None):
@@ -421,7 +328,7 @@ class ParserForm(ParserInterface):
elif node.localName == 'button':
button = Button(attrs)
button_list.append(button)
container.wid_add(button.widget,
container.wid_add(button,
help_tip=attrs.get('help', False),
colspan=colspan,
yexpand=yexpand, yfill=yfill,
+4 -5
View File
@@ -43,9 +43,8 @@ class Window(object):
allow_similar=Window.allow_similar)
@staticmethod
def create_wizard(action, data, state='init', direct_print=False,
email_print=False, email=None, name=False, context=None, icon=None,
window=False):
def create_wizard(action, data, direct_print=False, email_print=False,
email=None, name=False, context=None, icon=None, window=False):
from tryton.gui import Main
from wizard import WizardForm, WizardDialog
if window:
@@ -54,5 +53,5 @@ class Window(object):
Main.get_main().win_add(win, Window.hide_current)
else:
win = WizardDialog(name=name)
win.run(action, data, state=state, direct_print=direct_print,
email_print=email_print, email=email, context=context)
win.run(action, data, direct_print=direct_print,
email_print=email_print, email=email, context=context)
+80 -98
View File
@@ -11,6 +11,7 @@ from tryton.gui import Main
from tryton.exceptions import TrytonServerError
from tryton.config import CONFIG
from tryton.gui.window.nomodal import NoModal
from tryton.common.button import Button
_ = gettext.gettext
@@ -22,10 +23,9 @@ class Wizard(object):
self.toolbar_box = None
self.widget.show()
self.name = name or ''
self.model = ''
self.id = None
self.ids = None
self.action = None
self.datas = None
self.state = 'init'
self.direct_print = False
self.email_print = False
self.email = False
@@ -34,98 +34,89 @@ class Wizard(object):
self.response2state = {}
self.__processing = False
self.__waiting_response = False
self.session_id = None
self.start_state = None
self.end_state = None
self.screen = None
self.screen_state = None
def run(self, action, datas, state='init', direct_print=False,
email_print=False, email=None, context=None):
def run(self, action, data, direct_print=False, email_print=False,
email=None, context=None):
self.action = action
self.datas = datas
self.state = state
self.id = data.get('id')
self.ids = data.get('ids')
self.model = data.get('model')
self.direct_print = direct_print
self.email_print = email_print
self.email = email
self.context = context
if not 'form' in datas:
datas['form'] = {}
args = ('wizard', action, 'create', rpc.CONTEXT)
try:
self.wiz_id = rpc.execute(*args)
result = rpc.execute(*args)
except TrytonServerError, exception:
self.wiz_id = common.process_exception(exception, *args)
if not self.wiz_id:
result = common.process_exception(exception, *args)
if not result:
return
self.session_id, self.start_state, self.end_state = result
self.state = self.start_state
self.process()
def process(self):
from tryton.action import Action
res = {}
if self.__processing or self.__waiting_response:
return
try:
self.__processing = True
while self.state != 'end':
while self.state != self.end_state:
ctx = self.context.copy()
ctx.update(rpc.CONTEXT)
ctx['active_id'] = self.datas.get('id')
ctx['active_ids'] = self.datas.get('ids')
ctx['active_id'] = self.id
ctx['active_ids'] = self.ids
ctx['active_model'] = self.model
if self.screen:
data = {self.screen_state: self.screen.get_on_change_value()}
else:
data = {}
rpcprogress = common.RPCProgress('execute', ('wizard',
self.action, 'execute', self.wiz_id, self.datas,
self.action, 'execute', self.session_id, data,
self.state, ctx))
try:
res = rpcprogress.run()
result = rpcprogress.run()
except TrytonServerError, exception:
common.process_exception(exception)
self.end()
self.state = self.end_state
break
if not res:
self.end()
return
if 'datas' in res:
self.datas['form'] = res['datas']
elif res['type'] == 'form':
self.datas['form'] = {}
if res['type'] == 'form':
if 'view' in result:
self.clean()
self.update(res, res['state'], res['object'], context=ctx)
self.screen.current_record.set_default(self.datas['form'])
view = result['view']
self.update(view['fields_view'], view['defaults'],
view['buttons'])
self.screen_state = view['state']
self.__waiting_response = True
break
elif res['type'] == 'action':
self.state = res['state']
sensitive_widget = self.widget.get_toplevel()
if self.state == 'end':
self.end()
Action._exec_action(res['action'], self.datas, context=ctx)
if self.state == 'end' or (
res['action']['type'] == 'ir.action.wizard'
and not sensitive_widget.props.sensitive):
return
elif res['type'] == 'print':
self.datas['report_id'] = res.get('report_id', False)
if res.get('get_id_from_action', False):
backup_ids = self.datas['ids']
self.datas['ids'] = self.datas['form']['ids']
Action.exec_report(res['report'], self.datas,
direct_print=self.direct_print,
email_print=self.email_print, email=self.email,
context=ctx)
self.datas['ids'] = backup_ids
else:
Action.exec_report(res['report'], self.datas,
direct_print=self.direct_print,
email_print=self.email_print, email=self.email,
context=ctx)
self.state = res['state']
elif res['type'] == 'state':
self.state = res['state']
else:
self.state = self.end_state
if self.state == 'end':
if 'actions' in result:
sensitive_widget = self.widget.get_toplevel()
if not 'view' in result:
self.end()
for action in result['actions']:
Action._exec_action(*action, context=ctx)
if (not 'view' in result
or (action[0]['type'] == 'ir.action.wizard'
and not sensitive_widget.props.sensitive)):
return
if self.__waiting_response:
break
if self.state == self.end_state:
self.end()
finally:
self.__processing = False
def destroy(self):
if hasattr(self, 'screen'):
if self.screen:
self.screen.signal_unconnect(self)
self.screen.destroy()
del self.screen
@@ -133,7 +124,7 @@ class Wizard(object):
def end(self):
try:
rpc.execute('wizard', self.action, 'delete', self.wiz_id,
rpc.execute('wizard', self.action, 'delete', self.session_id,
rpc.CONTEXT)
#XXX to remove when company displayed in status bar
rpc.context_reload()
@@ -147,46 +138,37 @@ class Wizard(object):
def response(self, widget, response):
self.__waiting_response = False
state = self.response2state.get(response, 'end')
state = self.response2state.get(response, self.end_state)
self.screen.current_view.set_value()
if not self.screen.current_record.validate() \
and state != 'end':
if (not self.screen.current_record.validate()
and state != self.end_state):
self.screen.display()
return
self.datas['form'].update(self.screen.get())
self.state = state
self.process()
def _get_button(self, state):
button = gtk.Button()
button.set_use_underline(True)
button.set_label('_' + state[1])
if len(state) >= 3:
common.ICONFACTORY.register_icon(state[2])
icon = gtk.Image()
icon.set_from_stock(state[2], gtk.ICON_SIZE_BUTTON)
button.set_image(icon)
self.states[state[0]] = button
def _get_button(self, definition):
button = Button(definition)
self.states[definition['state']] = button
response = len(self.states)
self.response2state[response] = state[0]
self.response2state[response] = definition['state']
button.show()
return button
def update(self, view, states, obj_name, context=None):
self.model = obj_name
def _record_modified(self, screen, signal):
record = screen.current_record
for button in self.states.itervalues():
button.state_set(record)
for state in states:
self._get_button(state)
def update(self, view, defaults, buttons):
for button in buttons:
self._get_button(button)
val = {}
fields = view['fields']
for i in fields:
if 'value' in fields[i]:
val[i] = fields[i]['value']
self.screen = Screen(obj_name, mode=[], context=context)
self.screen = Screen(view['model'], mode=[], context=self.context)
self.screen.add_view(view, display=True)
self.screen.widget.show()
self.screen.signal_connect(self, 'record-modified',
self._record_modified)
title = gtk.Label()
title.set_use_markup(True)
@@ -244,7 +226,7 @@ class Wizard(object):
self.widget.pack_start(self.scrolledwindow)
self.screen.new(default=False)
self.screen.current_record.set_default(val)
self.screen.current_record.set_default(defaults, modified=True)
self.screen.current_view.set_cursor()
@@ -277,14 +259,14 @@ class WizardForm(Wizard,SignalEvent):
self.hbuttonbox.pack_start(button)
return button
def update(self, view, states, obj_name, context=None):
super(WizardForm, self).update(view, states, obj_name, context=context)
def update(self, view, defaults, buttons):
super(WizardForm, self).update(view, defaults, buttons)
self.widget.pack_start(self.hbuttonbox, expand=False, fill=True)
def sig_close(self):
if 'end' in self.states:
self.states['end'].clicked()
return self.state == 'end'
if self.end_state in self.states:
self.states[self.end_state].clicked()
return self.state == self.end_state
def destroy(self):
if self.toolbar_box.get_children():
@@ -326,11 +308,11 @@ class WizardDialog(Wizard, NoModal):
for button in hbuttonbox.get_children():
hbuttonbox.remove(button)
def _get_button(self, state):
button = super(WizardDialog, self)._get_button(state)
def _get_button(self, definition):
button = super(WizardDialog, self)._get_button(definition)
response = len(self.states)
self.dia.add_action_widget(button, response)
if len(state) >= 4 and state[3]:
if definition['default']:
button.set_flags(gtk.CAN_DEFAULT)
button.add_accelerator('clicked', self.accel_group,
gtk.keysyms.Return, gtk.gdk.CONTROL_MASK,
@@ -338,8 +320,8 @@ class WizardDialog(Wizard, NoModal):
self.dia.set_default_response(response)
return button
def update(self, view, states, obj_name, context=None):
super(WizardDialog, self).update(view, states, obj_name, context=context)
def update(self, view, defaults, buttons):
super(WizardDialog, self).update(view, defaults, buttons)
sensible_allocation = self.sensible_widget.get_allocation()
self.dia.set_default_size(int(sensible_allocation.width * 0.9),
int(sensible_allocation.height * 0.9))