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.

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