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.

189 lines
5.4 KiB

9 years ago
  1. '''
  2. Created on Dec 22, 2013
  3. @author: Chris
  4. '''
  5. import wx
  6. import os
  7. import re
  8. import sys
  9. import subprocess
  10. from gooey.gui.pubsub import pub
  11. from multiprocessing.dummy import Pool
  12. from gooey.gui import events
  13. from gooey.gui.lang import i18n
  14. from gooey.gui.windows import views
  15. from gooey.gui.util.taskkill import taskkill
  16. YES = 5103
  17. NO = 5104
  18. class Controller(object):
  19. '''
  20. Main controller for the gui.
  21. All controlls are delegated to this central control point.
  22. '''
  23. def __init__(self, base_frame, build_spec):
  24. '''
  25. :type base_frame: BaseWindow
  26. :type build_spec: dict
  27. '''
  28. self.core_gui = base_frame
  29. self.build_spec = build_spec
  30. self._process = None
  31. # wire up all the observers
  32. pub.subscribe(self.on_cancel, events.WINDOW_CANCEL)
  33. pub.subscribe(self.on_stop, events.WINDOW_STOP)
  34. pub.subscribe(self.on_start, events.WINDOW_START)
  35. pub.subscribe(self.on_restart, events.WINDOW_RESTART)
  36. pub.subscribe(self.on_close, events.WINDOW_CLOSE)
  37. pub.subscribe(self.on_edit, events.WINDOW_EDIT)
  38. def on_edit(self):
  39. pub.send_message(events.WINDOW_CHANGE, view_name=views.CONFIG_SCREEN)
  40. def on_close(self):
  41. if self.ask_stop():
  42. self.core_gui.Destroy()
  43. sys.exit()
  44. def on_restart(self):
  45. self.on_start()
  46. def manual_restart(self):
  47. self.on_start()
  48. def on_cancel(self):
  49. msg = i18n._('sure_you_want_to_exit')
  50. dlg = wx.MessageDialog(None, msg, i18n._('close_program'), wx.YES_NO)
  51. result = dlg.ShowModal()
  52. if result == YES:
  53. dlg.Destroy()
  54. self.core_gui.Destroy()
  55. sys.exit()
  56. dlg.Destroy()
  57. def on_start(self):
  58. if not self.skipping_config() and not self.required_section_complete():
  59. return self.show_dialog(i18n._('error_title'), i18n._('error_required_fields'), wx.ICON_ERROR)
  60. cmd_line_args = self.core_gui.GetOptions()
  61. command = '{} --ignore-gooey {}'.format(self.build_spec['target'], cmd_line_args)
  62. pub.send_message(events.WINDOW_CHANGE, view_name=views.RUNNING_SCREEN)
  63. self.run_client_code(command)
  64. def on_stop(self):
  65. self.ask_stop()
  66. def ask_stop(self):
  67. if not self.running():
  68. return True
  69. if self.build_spec['disable_stop_button']:
  70. return False
  71. msg = i18n._('sure_you_want_to_stop')
  72. dlg = wx.MessageDialog(None, msg, i18n._('stop_task'), wx.YES_NO)
  73. result = dlg.ShowModal()
  74. dlg.Destroy()
  75. if result == YES:
  76. self.stop()
  77. return True
  78. return False
  79. def stop(self):
  80. if self.running():
  81. taskkill(self._process.pid)
  82. def running(self):
  83. return self._process and self._process.poll() is None
  84. def run_client_code(self, command):
  85. env = os.environ.copy()
  86. env["GOOEY"] = "1"
  87. print "run command:", command
  88. p = subprocess.Popen(command, bufsize=1, stdout=subprocess.PIPE,
  89. stderr=subprocess.STDOUT, shell=True, env=env)
  90. self._process = p
  91. pool = Pool(1)
  92. pool.apply_async(self.read_stdout, (p, self.process_result))
  93. def read_stdout(self, process, callback):
  94. while True:
  95. line = process.stdout.readline()
  96. if not line:
  97. break
  98. wx.CallAfter(self.core_gui.PublishConsoleMsg, line)
  99. progress = self.progress_from_line(line)
  100. if progress is not None:
  101. wx.CallAfter(self.core_gui.UpdateProgressBar, progress)
  102. wx.CallAfter(callback, process)
  103. def progress_from_line(self, text):
  104. progress_regex = self.build_spec['progress_regex']
  105. if not progress_regex:
  106. return None
  107. match = re.search(progress_regex, text.strip())
  108. if not match:
  109. return None
  110. progress_expr = self.build_spec['progress_expr']
  111. if progress_expr:
  112. return self._eval_progress(match, progress_expr)
  113. else:
  114. return self._search_progress(match)
  115. def _search_progress(self, match):
  116. try:
  117. return int(float(match.group(1)))
  118. except:
  119. return None
  120. def _eval_progress(self, match, eval_expr):
  121. def safe_float(x):
  122. try:
  123. return float(x)
  124. except ValueError:
  125. return x
  126. _locals = {k: safe_float(v) for k, v in match.groupdict().items()}
  127. if "x" not in _locals:
  128. _locals["x"] = [safe_float(x) for x in match.groups()]
  129. try:
  130. return int(float(eval(eval_expr, {}, _locals)))
  131. except:
  132. return None
  133. def process_result(self, process):
  134. _stdout, _ = process.communicate()
  135. if process.returncode == 0:
  136. pub.send_message(events.WINDOW_CHANGE, view_name=views.SUCCESS_SCREEN)
  137. self.success_dialog()
  138. else:
  139. pub.send_message(events.WINDOW_CHANGE, view_name=views.ERROR_SCREEN)
  140. self.error_dialog()
  141. def skipping_config(self):
  142. return self.build_spec['manual_start']
  143. def required_section_complete(self):
  144. required_section = self.core_gui.GetRequiredArgs()
  145. if len(required_section) == 0:
  146. return True # no requirements!
  147. return not any(req == '' for req in required_section)
  148. def success_dialog(self):
  149. self.show_dialog(i18n._("execution_finished"), i18n._('success_message'), wx.ICON_INFORMATION)
  150. def error_dialog(self):
  151. self.show_dialog(i18n._('error_title'), i18n._('uh_oh'), wx.ICON_ERROR)
  152. def show_dialog(self, title, content, style):
  153. a = wx.MessageDialog(None, content, title, style)
  154. a.ShowModal()
  155. a.Destroy()