You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

260 lines
8.1 KiB

  1. import os
  2. from collections import OrderedDict, namedtuple
  3. from itertools import chain
  4. from gooey.gui.lang.i18n import _
  5. from gooey.gui.util.quoting import quote
  6. ArgumentGroup = namedtuple('ArgumentGroup', 'name command required_args optional_args')
  7. class MyWidget(object):
  8. # TODO: Undumbify damn
  9. # TODO: Undumbify _value/value access
  10. def __init__(self, type, title, help, default, nargs, commands, choices):
  11. self.type = type
  12. self.title = title
  13. self.help = help
  14. self.default = default
  15. self._value = default
  16. self.nargs = nargs
  17. self.commands = commands
  18. self.choices = choices
  19. @property
  20. def value(self):
  21. # TODO: split into stategy or subclass thingie
  22. if self.type == 'CheckBox':
  23. return self.commands[0] if self._value else None
  24. if self.type == 'RadioGroup':
  25. try:
  26. return self.commands[self._value.index(True)][0]
  27. except ValueError:
  28. return None
  29. if self.type == 'MultiFileChooser':
  30. value = ' '.join(quote(x) for x in self._value.split(os.pathsep) if x)
  31. if self.commands and value:
  32. return u'{} {}'.format(self.commands[0], value)
  33. return value or None
  34. if self.type == 'Textarea':
  35. if self.commands and self._value:
  36. return '{} {}'.format(self.commands[0], quote(self._value.encode('unicode_escape')))
  37. else:
  38. return quote(self._value.encode('unicode_escape')) if self._value else ''
  39. if self.type == 'CommandField':
  40. if self.commands and self._value:
  41. return u'{} {}'.format(self.commands[0], self._value)
  42. else:
  43. return self._value or None
  44. if self.type == 'Counter':
  45. '''
  46. Returns
  47. str(option_string * DropDown Value)
  48. e.g.
  49. -vvvvv
  50. '''
  51. if not str(self._value).isdigit():
  52. return None
  53. arg = str(self.commands[0]).replace('-', '')
  54. repeated_args = arg * int(self._value)
  55. return '-' + repeated_args
  56. if self.type == 'Dropdown':
  57. if self._value == 'Select Option':
  58. return None
  59. elif self.commands and self._value:
  60. return u'{} {}'.format(self.commands[0], quote(self._value))
  61. else:
  62. return quote(self._value) if self._value else ''
  63. else:
  64. if self.commands and self._value:
  65. if not self.nargs:
  66. v = quote(self._value)
  67. else:
  68. v = self._value
  69. return u'{0} {1}'.format(self.commands[0], v)
  70. else:
  71. if not self._value:
  72. return None
  73. elif not self.nargs:
  74. return quote(self._value)
  75. else:
  76. return self._value
  77. @value.setter
  78. def value(self, val):
  79. self._value = val
  80. @classmethod
  81. def from_dict(cls, data):
  82. def maybe_unpack(collection, attr):
  83. # TODO: RadioGroups need to support defaults
  84. try:
  85. if isinstance(collection, list):
  86. return [item[attr] for item in collection]
  87. return collection[attr]
  88. except:
  89. return None
  90. details = data['data']
  91. return cls(
  92. data['type'],
  93. maybe_unpack(details, 'display_name'),
  94. maybe_unpack(details, 'help'),
  95. maybe_unpack(details, 'default'),
  96. maybe_unpack(details, 'nargs'),
  97. maybe_unpack(details, 'commands'),
  98. maybe_unpack(details, 'choices')
  99. )
  100. class States(object):
  101. CONFIGURING = 'configuring'
  102. RUNNNING = 'running'
  103. SUCCESS = 'success'
  104. ERROR = 'error'
  105. STOPPED = 'stopped'
  106. class MyModel(object):
  107. '''
  108. '''
  109. def wrap(self, groups):
  110. output = OrderedDict()
  111. for name, group in groups.items():
  112. output[name] = ArgumentGroup(
  113. name,
  114. group['command'],
  115. *self.group_arguments(group['contents'])
  116. )
  117. return output
  118. def __init__(self, build_spec):
  119. self.current_state = States.CONFIGURING
  120. self.build_spec = build_spec
  121. self.layout_type = self.build_spec.get('layout_type')
  122. self.auto_start = self.build_spec.get('auto_start')
  123. self.progress_regex = self.build_spec.get('progress_regex')
  124. self.progress_expr = self.build_spec.get('progress_expr')
  125. self.disable_progress_bar_animation = self.build_spec['disable_progress_bar_animation']
  126. self.program_name = self.build_spec.get('program_name')
  127. self.default_size = self.build_spec.get('default_size')
  128. self.heading_title = _("settings_title")
  129. self.heading_subtitle = self.build_spec['program_description'] or ''
  130. self.use_monospace_font = self.build_spec.get('monospace_display')
  131. self.stop_button_disabled = self.build_spec['disable_stop_button']
  132. self.argument_groups = self.wrap(self.build_spec.get('widgets', {}))
  133. self.active_group = next(iter(self.argument_groups))
  134. self.num_required_cols = self.build_spec['num_required_cols']
  135. self.num_optional_cols = self.build_spec['num_optional_cols']
  136. self.text_states = {
  137. States.CONFIGURING: {
  138. 'title': _("settings_title"),
  139. 'subtitle': self.build_spec['program_description'] or ''
  140. },
  141. States.RUNNNING: {
  142. 'title': _("running_title"),
  143. 'subtitle': _('running_msg')
  144. },
  145. States.SUCCESS: {
  146. 'title': _('finished_title'),
  147. 'subtitle': _('finished_msg')
  148. },
  149. States.ERROR: {
  150. 'title': _('finished_title'),
  151. 'subtitle': _('finished_error')
  152. }
  153. }
  154. @property
  155. def required_args(self):
  156. return self.argument_groups[self.active_group].required_args
  157. @property
  158. def optional_args(self):
  159. return self.argument_groups[self.active_group].optional_args
  160. def update_state(self, state):
  161. self.current_state = state
  162. text = self.text_states[state]
  163. self.heading_title = text['title']
  164. self.heading_subtitle = text['subtitle']
  165. def is_valid(self):
  166. # TODO: fix skipping_config.. whatever that did
  167. # currently breaks when you supply it as a decorator option
  168. # return self.skipping_config() and self.required_section_complete()
  169. return self.is_required_section_complete()
  170. def skipping_config(self):
  171. return self.build_spec['manual_start']
  172. def is_required_section_complete(self):
  173. completed_values = list(filter(None, [arg.value for arg in self.required_args]))
  174. return len(self.required_args) == len(completed_values)
  175. def build_command_line_string(self):
  176. optional_args = [arg.value for arg in self.optional_args]
  177. required_args = [c.value for c in self.required_args if c.commands]
  178. position_args = [c.value for c in self.required_args if not c.commands]
  179. if position_args:
  180. position_args.insert(0, "--")
  181. cmd_string = ' '.join(list(filter(None, chain(required_args, optional_args, position_args))))
  182. if self.layout_type == 'column':
  183. cmd_string = u'{} {}'.format(self.argument_groups[self.active_group].command, cmd_string)
  184. return u'{} --ignore-gooey {}'.format(self.build_spec['target'], cmd_string)
  185. def group_arguments(self, widget_list):
  186. is_required = lambda widget: widget['required']
  187. not_checkbox = lambda widget: widget['type'] != 'CheckBox'
  188. required_args, optional_args = self.partition(widget_list, is_required)
  189. if self.build_spec['group_by_type']:
  190. optional_args = chain(*self.partition(optional_args, not_checkbox))
  191. return list(map(self.to_object, required_args)), list(map(self.to_object, optional_args))
  192. @staticmethod
  193. def partition(collection, condition):
  194. return list(filter(condition, collection)), list(filter(lambda x: not condition(x), collection))
  195. def to_object(self, data):
  196. details = data['data']
  197. return MyWidget(
  198. data['type'],
  199. self.maybe_unpack(details, 'display_name'),
  200. self.maybe_unpack(details, 'help'),
  201. self.maybe_unpack(details, 'default'),
  202. self.maybe_unpack(details, 'nargs'),
  203. self.maybe_unpack(details, 'commands'),
  204. self.maybe_unpack(details, 'choices')
  205. )
  206. @staticmethod
  207. def maybe_unpack(collection, attr):
  208. # TODO: RadioGroups need to support defaults
  209. try:
  210. if isinstance(collection, list):
  211. return [item[attr] for item in collection]
  212. return collection[attr]
  213. except:
  214. return None