nch(OpenERP) has proposed merging
lp:~openerp-commiter/openobject-client/asynchronous_rpccall into
lp:openobject-client.
Requested reviews:
OpenERP sa GTK client R&D (openerp-dev-gtk)
Display progressbar for long rpc calls, set window's cursor to watch from arrow
when there is small rpc calls to the server.
--
https://code.launchpad.net/~openerp-commiter/openobject-client/asynchronous_rpccall/+merge/39831
Your team OpenERP sa GTK client R&D is requested to review the proposed merge
of lp:~openerp-commiter/openobject-client/asynchronous_rpccall into
lp:openobject-client.
=== modified file 'bin/common/common.py'
--- bin/common/common.py 2010-10-27 11:58:25 +0000
+++ bin/common/common.py 2010-11-02 09:51:44 +0000
@@ -167,6 +167,19 @@
win.show_all()
return win, pb
+def set_busy_cursor():
+ root = gtk.gdk.screen_get_default()
+ cursor_win = root.get_active_window()
+ if cursor_win:
+ cursor_win.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+ gtk.main_iteration(False)
+ return cursor_win
+
+def reset_busy_cursor(cursor_win=None):
+ if cursor_win:
+ cursor_win.set_cursor(None)
+ gtk.main_iteration()
+
def _search_file(file, dir='path.share'):
tests = [
lambda x: os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]), x),
=== modified file 'bin/modules/gui/window/tree.py'
--- bin/modules/gui/window/tree.py 2010-09-27 05:55:25 +0000
+++ bin/modules/gui/window/tree.py 2010-11-02 09:51:44 +0000
@@ -186,23 +186,27 @@
self.sig_action(keyword='client_print_multi')
def sig_action(self, widget=None, keyword='tree_but_action', id=None, report_type='pdf', warning=True):
- ids = self.ids_get()
-
- if not id and ids and len(ids):
- id = ids[0]
- if id:
- ctx = self.context.copy()
- if 'active_ids' in ctx:
- del ctx['active_ids']
- if 'active_id' in ctx:
- del ctx['active_id']
- obj = service.LocalService('action.main')
- return obj.exec_keyword(keyword, {'model':self.model, 'id':id,
- 'ids':ids, 'report_type':report_type, 'window': self.window}, context=ctx,
- warning=warning)
- else:
- common.message(_('No resource selected!'))
- return False
+ cursor = common.set_busy_cursor()
+ try:
+ res = False
+ ids = self.ids_get()
+ if not id and ids and len(ids):
+ id = ids[0]
+ if id:
+ ctx = self.context.copy()
+ if 'active_ids' in ctx:
+ del ctx['active_ids']
+ if 'active_id' in ctx:
+ del ctx['active_id']
+ obj = service.LocalService('action.main')
+ res = obj.exec_keyword(keyword, {'model':self.model, 'id':id,
+ 'ids':ids, 'report_type':report_type, 'window': self.window}, context=ctx,
+ warning=warning)
+ else:
+ common.message(_('No resource selected!'))
+ finally:
+ common.reset_busy_cursor(cursor)
+ return res
def sig_open(self, widget, iter, path):
if not self.sig_action(widget, 'tree_but_open', warning=False):
=== modified file 'bin/rpc.py'
--- bin/rpc.py 2010-10-13 10:44:15 +0000
+++ bin/rpc.py 2010-11-02 09:51:44 +0000
@@ -29,7 +29,8 @@
import common
import options
import os
-
+import thread
+import time
import re
CONCURRENCY_CHECK_FIELD = '__last_update'
@@ -68,23 +69,52 @@
class gw_inter(object):
- __slots__ = ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
+ __slots__ = ('_url', '_db', '_uid', '_passwd', '_sock', '_obj','thread_loop','res','exception')
def __init__(self, url, db, uid, passwd, obj='/object'):
self._url = url
self._db = db
self._uid = uid
self._obj = obj
self._passwd = passwd
+ self.thread_loop = False
+ self.exception = None
+ self.res = None
+
def exec_auth(method, *args):
pass
+
def execute(method, *args):
pass
+ def raise_exception(self, exception):
+ if isinstance(exception, socket.error):
+ common.message(_('Unable to reach to OpenERP server !\nYou should check your connection to the network and the OpenERP server.'), _('Connection Error'), type=gtk.MESSAGE_ERROR)
+ raise rpc_exception(69, 'Connection refused!')
+ else:
+ if isinstance(exception, xmlrpclib.Fault) \
+ or isinstance(exception, tiny_socket.Myexception):
+ a = rpc_exception(exception.faultCode, exception.faultString)
+ if a.type in ('warning','UserError'):
+ if a.message in ('ConcurrencyException') and len(args) > 4:
+ if common.concurrency(args[0], args[2][0], args[4]):
+ if CONCURRENCY_CHECK_FIELD in args[4]:
+ del args[4][CONCURRENCY_CHECK_FIELD]
+ return self.rpc_exec_auth(obj, method, *args)
+ else:
+ common.warning(a.data, a.message)
+ else:
+ common.error(_('Application Error'), exception.faultCode, exception.faultString)
+ else:
+ common.error(_('Application Error'), _('View details'), str(exception))
+ raise exception
+
+
class xmlrpc_gw(gw_inter):
__slots__ = ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
def __init__(self, url, db, uid, passwd, obj='/object'):
gw_inter.__init__(self, url, db, uid, passwd, obj)
self._sock = xmlrpclib.ServerProxy(url+obj)
+
def exec_auth(self, method, *args):
logging.getLogger('rpc.request').debug_rpc(str((method, self._db, self._uid, '*', args)))
res = self.execute(method, self._uid, self._passwd, *args)
@@ -105,8 +135,38 @@
return result
def execute(self, method, *args):
- result = getattr(self._sock,method)(self._db, *args)
- return self.__convert(result)
+ self.thread_loop = False
+ self.exception = None
+
+ def execute_call(method, args):
+ try:
+ self.res = getattr(self._sock,method)(self._db, *args)
+ self.res = self.__convert(self.res)
+ except Exception, e:
+ self.exception = e
+ self.thread_loop = True
+ return True
+
+ thread.start_new_thread(execute_call, (method, args))
+ counter = 0
+ win = None
+ progressbar = None
+ while not self.thread_loop:
+ time.sleep(0.01)
+ counter += 1
+ if counter == 100:
+ if not win or not progressbar:
+ win, progressbar = common.OpenERP_Progressbar()
+ if progressbar and (counter % 10) == 0:
+ progressbar.pulse()
+ if win:
+ gtk.main_iteration(False)
+ if win:
+ win.destroy()
+ gtk.main_iteration()
+ if self.exception:
+ self.raise_exception(self.exception)
+ return self.res
class tinySocket_gw(gw_inter):
__slots__ = ('_url', '_db', '_uid', '_passwd', '_sock', '_obj')
@@ -114,26 +174,53 @@
gw_inter.__init__(self, url, db, uid, passwd, obj)
self._sock = tiny_socket.mysocket()
self._obj = obj[1:]
+
def exec_auth(self, method, *args):
logging.getLogger('rpc.request').debug_rpc(str((method, self._db, self._uid, '*', args)))
res = self.execute(method, self._uid, self._passwd, *args)
logging.getLogger('rpc.result').debug_rpc_answer(str(res))
return res
+
def execute(self, method, *args):
- self._sock.connect(self._url)
- try:
- self._sock.mysend((self._obj, method, self._db)+args)
- res = self._sock.myreceive()
- self._sock.disconnect()
- return res
- except Exception,e:
+ self.thread_loop = False
+ self.exception = None
+
+ def execute_call(method, args):
+ self._sock.connect(self._url)
try:
+ self._sock.mysend((self._obj, method, self._db)+args)
+ self.res = self._sock.myreceive()
self._sock.disconnect()
- except Exception:
- pass
- # make sure we keep the exception context, even
- # if disconnect() raised above.
- raise e
+ except Exception, e:
+ try:
+ self._sock.disconnect()
+ except Exception:
+ pass
+ self.exception = e
+ self.thread_loop = True
+ return True
+
+ thread.start_new_thread(execute_call, (method, args))
+ counter = 0
+ win = None
+ progressbar = None
+ while not self.thread_loop:
+ time.sleep(0.01)
+ counter += 1
+ if counter == 100:
+ if not win or not progressbar:
+ win, progressbar = common.OpenERP_Progressbar()
+ if progressbar and (counter % 10) == 0:
+ progressbar.pulse()
+ if win:
+ gtk.main_iteration(False)
+ if win:
+ win.destroy()
+ gtk.main_iteration()
+ if self.exception:
+ self.raise_exception(self.exception)
+ return self.res
+
class rpc_session(object):
__slots__ = ('_open', '_url', 'uid', 'uname', '_passwd', '_gw', 'db', 'context', 'timezone')
@@ -149,14 +236,8 @@
self.timezone = 'utc'
def rpc_exec(self, obj, method, *args):
- try:
sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
return sock.execute(method, *args)
- except socket.error, e:
- common.message(str(e), title=_('Connection refused !'), type=gtk.MESSAGE_ERROR)
- raise rpc_exception(69, _('Connection refused!'))
- except xmlrpclib.Fault, err:
- raise rpc_exception(err.faultCode, err.faultString)
def rpc_exec_auth_try(self, obj, method, *args):
if self._open:
@@ -166,44 +247,13 @@
raise rpc_exception(1, 'not logged')
def rpc_exec_auth_wo(self, obj, method, *args):
- try:
- sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
- return sock.exec_auth(method, *args)
- except xmlrpclib.Fault, err:
- a = rpc_exception(err.faultCode, err.faultString)
- except tiny_socket.Myexception, err:
- a = rpc_exception(err.faultCode, err.faultString)
- if a.code in ('warning', 'UserError'):
- common.warning(a.data, a.message)
- return None
- raise a
+ sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
+ return sock.exec_auth(method, *args)
def rpc_exec_auth(self, obj, method, *args):
if self._open:
- try:
- sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
- return sock.exec_auth(method, *args)
- except socket.error, e:
- common.message(_('Unable to reach to OpenERP server !\nYou should check your connection to the network and the OpenERP server.'), _('Connection Error'), type=gtk.MESSAGE_ERROR)
- raise rpc_exception(69, 'Connection refused!')
- except Exception, e:
- if isinstance(e, xmlrpclib.Fault) \
- or isinstance(e, tiny_socket.Myexception):
- a = rpc_exception(e.faultCode, e.faultString)
- if a.type in ('warning','UserError'):
- if a.message in ('ConcurrencyException') and len(args) > 4:
- if common.concurrency(args[0], args[2][0], args[4]):
- if CONCURRENCY_CHECK_FIELD in args[4]:
- del args[4][CONCURRENCY_CHECK_FIELD]
- return self.rpc_exec_auth(obj, method, *args)
- else:
- common.warning(a.data, a.message)
- else:
- common.error(_('Application Error'), e.faultCode, e.faultString)
- else:
- common.error(_('Application Error'), _('View details'), str(e))
- #TODO Must propagate the exception?
- raise
+ sock = self._gw(self._url, self.db, self.uid, self._passwd, obj)
+ return sock.exec_auth(method, *args)
else:
raise rpc_exception(1, 'not logged')
=== modified file 'bin/widget/model/record.py'
--- bin/widget/model/record.py 2010-10-27 10:00:15 +0000
+++ bin/widget/model/record.py 2010-11-02 09:51:44 +0000
@@ -342,55 +342,61 @@
id : Id of the record for which the button is clicked
attrs : Button Attributes
"""
- if not id:
- id = self.id
- if not attrs.get('confirm', False) or common.sur(attrs['confirm']):
- button_type = attrs.get('type', 'workflow')
- obj = service.LocalService('action.main')
-
- if button_type == 'workflow':
- result = rpc.session.rpc_exec_auth('/object', 'exec_workflow',
- self.resource, attrs['name'], self.id)
- if type(result)==type({}):
- if result['type']== 'ir.actions.act_window_close':
- screen.window.destroy()
+ cursor = common.set_busy_cursor()
+ reload = True
+ reset = False
+ try:
+ if not id:
+ id = self.id
+ if not attrs.get('confirm') or common.sur(attrs['confirm']):
+ button_type = attrs.get('type', 'workflow')
+ obj = service.LocalService('action.main')
+ if button_type == 'workflow':
+ result = rpc.session.rpc_exec_auth('/object', 'exec_workflow',
+ self.resource, attrs['name'], self.id)
+ if isinstance(result, dict):
+ if result['type'] == 'ir.actions.act_window_close':
+ screen.window.destroy()
+ else:
+ datas = {'ids':[id], 'id':id}
+ obj._exec_action(result, datas)
+ elif isinstance(result, list):
+ datas = {'ids':[id]}
+ for rs in result:
+ obj._exec_action(rs, datas)
+ elif button_type == 'object':
+ if self.id:
+ context = self.context_get()
+ if 'context' in attrs:
+ context.update(self.expr_eval(attrs['context'], check_load=False))
+ result = rpc.session.rpc_exec_auth('/object', 'execute',
+ self.resource,attrs['name'], [id], context)
+ if isinstance(result, dict):
+ if not result.get('nodestroy', False):
+ screen.window.destroy()
+ if result.get('type') in ('ir.actions.report.xml','ir.actions.report.custom'):
+ common.reset_busy_cursor(cursor)
+ reset = True
+ obj._exec_action(result, {}, context=context)
else:
- datas = {'ids':[id], 'id':id}
- obj._exec_action(result, datas)
- elif type([]) == type(result):
- datas = {'ids':[id]}
- for rs in result:
- obj._exec_action(rs, datas)
-
- elif button_type == 'object':
- if not self.id:
- return
- context = self.context_get()
- if 'context' in attrs:
- context.update(self.expr_eval(attrs['context'], check_load=False))
- result = rpc.session.rpc_exec_auth('/object', 'execute',
- self.resource,attrs['name'], [id], context)
- if isinstance(result, dict):
- if not result.get('nodestroy', False):
- screen.window.destroy()
- obj._exec_action(result, {}, context=context)
-
- elif button_type == 'action':
- action_id = int(attrs['name'])
- context = screen.context.copy()
- if 'context' in attrs:
- context.update(self.expr_eval(attrs['context'], check_load=False))
- datas = {'model':self.resource,
- 'id': id or False,
- 'ids': id and [id] or [],
- 'report_type': 'pdf'
- }
- obj.execute(action_id, datas, context=context)
- else:
- raise Exception, 'Unallowed button type'
- if screen.current_model and screen.current_view.view_type != 'tree':
- screen.reload()
-
-
+ reload = False
+ elif button_type == 'action':
+ action_id = int(attrs['name'])
+ context = screen.context.copy()
+ if 'context' in attrs:
+ context.update(self.expr_eval(attrs['context'], check_load=False))
+ datas = {'model':self.resource,
+ 'id': id or False,
+ 'ids': id and [id] or [],
+ 'report_type': 'pdf'
+ }
+ obj.execute(action_id, datas, context=context)
+ else:
+ raise Exception, 'Unallowed button type'
+ if reload and screen.current_model and screen.current_view.view_type != 'tree':
+ screen.reload()
+ finally:
+ if not reset:
+ common.reset_busy_cursor(cursor)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== modified file 'bin/widget/view/form.py'
--- bin/widget/view/form.py 2010-10-12 10:40:02 +0000
+++ bin/widget/view/form.py 2010-11-02 09:51:44 +0000
@@ -266,7 +266,9 @@
'ids': [id],
}
obj = service.LocalService('action.main')
+ cursor = common.set_busy_cursor()
value = obj._exec_action(act, data, context)
+ common.reset_busy_cursor(cursor)
if type in ('print', 'action'):
self.screen.reload()
return value
_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help : https://help.launchpad.net/ListHelp