io_ctrl.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. """
  2. 输入输出的底层实现函数
  3. """
  4. import logging
  5. from .session import chose_impl, next_client_event, get_current_task_id, get_current_session
  6. logger = logging.getLogger(__name__)
  7. def send_msg(cmd, spec=None):
  8. msg = dict(command=cmd, spec=spec, task_id=get_current_task_id())
  9. get_current_session().send_task_command(msg)
  10. @chose_impl
  11. def single_input(item_spec, valid_func, preprocess_func):
  12. """
  13. Note: 鲁棒性在上层完成
  14. 将单个input构造成input_group,并获取返回值
  15. :param item_spec: 单个输入项的参数 'name' must in item_spec, 参数一定已经验证通过
  16. :param valid_func: Not None
  17. :param preprocess_func: Not None
  18. """
  19. if item_spec.get('name') is None: # single input
  20. item_spec['name'] = 'data'
  21. else: # as input_group item
  22. return dict(item_spec=item_spec, valid_func=valid_func, preprocess_func=preprocess_func)
  23. label = item_spec['label']
  24. name = item_spec['name']
  25. # todo 是否可以原地修改spec
  26. item_spec['label'] = ''
  27. item_spec.setdefault('auto_focus', True) # 如果没有设置autofocus参数,则开启参数 todo CHECKBOX, RADIO 特殊处理
  28. spec = dict(label=label, inputs=[item_spec])
  29. data = yield input_control(spec, {name: preprocess_func}, {name: valid_func})
  30. return data[name]
  31. @chose_impl
  32. def input_control(spec, preprocess_funcs, item_valid_funcs, form_valid_funcs=None):
  33. """
  34. 发送input命令,监听事件,验证输入项,返回结果
  35. :param spec:
  36. :param preprocess_funcs: keys 严格等于 spec中的name集合
  37. :param item_valid_funcs: keys 严格等于 spec中的name集合
  38. :param form_valid_funcs:
  39. :return:
  40. """
  41. send_msg('input_group', spec)
  42. data = yield input_event_handle(item_valid_funcs, form_valid_funcs, preprocess_funcs)
  43. send_msg('destroy_form')
  44. return data
  45. def check_item(name, data, valid_func, preprocess_func):
  46. try:
  47. data = preprocess_func(data)
  48. error_msg = valid_func(data)
  49. except Exception as e:
  50. logger.warning('Get %r in valid_func for name:"%s"', e, name)
  51. error_msg = '字段内容不合法'
  52. if error_msg is not None:
  53. send_msg('update_input', dict(target_name=name, attributes={
  54. 'valid_status': False,
  55. 'invalid_feedback': error_msg
  56. }))
  57. return False
  58. return True
  59. @chose_impl
  60. def input_event_handle(item_valid_funcs, form_valid_funcs, preprocess_funcs):
  61. """
  62. 根据提供的校验函数处理表单事件
  63. :param item_valid_funcs: map(name -> valid_func) valid_func 为 None 时,不进行验证
  64. valid_func: callback(data) -> error_msg or None
  65. :param form_valid_funcs: callback(data) -> (name, error_msg) or None
  66. :param preprocess_funcs:
  67. :return:
  68. """
  69. while True:
  70. event = yield next_client_event()
  71. event_name, event_data = event['event'], event['data']
  72. if event_name == 'input_event':
  73. input_event = event_data['event_name']
  74. if input_event == 'blur':
  75. onblur_name = event_data['name']
  76. check_item(onblur_name, event_data['value'], item_valid_funcs[onblur_name],
  77. preprocess_funcs[onblur_name])
  78. elif event_name == 'from_submit':
  79. all_valid = True
  80. # 调用输入项验证函数进行校验
  81. for name, valid_func in item_valid_funcs.items():
  82. if not check_item(name, event_data[name], valid_func, preprocess_funcs[name]):
  83. all_valid = False
  84. if all_valid: # todo 减少preprocess_funcs[name]调用次数
  85. data = {name: preprocess_funcs[name](val) for name, val in event_data.items()}
  86. # 调用表单验证函数进行校验
  87. if form_valid_funcs:
  88. v_res = form_valid_funcs(data)
  89. if v_res is not None:
  90. all_valid = False
  91. onblur_name, error_msg = v_res
  92. send_msg('update_input', dict(target_name=onblur_name, attributes={
  93. 'valid_status': False,
  94. 'invalid_feedback': error_msg
  95. }))
  96. if all_valid:
  97. break
  98. elif event_name == 'from_cancel':
  99. data = None
  100. break
  101. else:
  102. logger.warning("Unhandled Event: %s", event)
  103. return data
  104. def output_register_callback(callback, **options):
  105. task_id = get_current_session().register_callback(callback, **options)
  106. return task_id