Coverage for src/robotide/run/ui.py: 42%

140 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-06 10:40 +0100

1# Copyright 2008-2015 Nokia Networks 

2# Copyright 2016- Robot Framework Foundation 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); 

5# you may not use this file except in compliance with the License. 

6# You may obtain a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, 

12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

13# See the License for the specific language governing permissions and 

14# limitations under the License. 

15 

16import builtins 

17import wx 

18 

19from .process import Process 

20from ..widgets import Font, VerticalSizer, HorizontalSizer, RIDEDialog 

21from ..log import LogOutput 

22from ..publish import RideRunnerStopped 

23 

24_ = wx.GetTranslation # To keep linter/code analyser happy 

25builtins.__dict__['_'] = wx.GetTranslation 

26 

27FINISHED = _('finished') 

28RUN_AGAIN = _('Run Again') 

29RUNNING = _('running') 

30STOP = _('Stop') 

31 

32 

33def get_label(label: str) -> str: 

34 if label == RUN_AGAIN: 

35 return 'on_run_again' 

36 if label == STOP: 

37 return 'on_stop' 

38 raise ValueError 

39 

40 

41class Runner(wx.EvtHandler): 

42 

43 def __init__(self, config, notebook): 

44 wx.EvtHandler.__init__(self) 1cdeb

45 self.Bind(wx.EVT_TIMER, self.on_timer) 1cdeb

46 self.name = config.name 1cdeb

47 self._process = None 1cdeb

48 self._timer = wx.Timer(self) 1cdeb

49 self._config = config 1cdeb

50 self._window = self._get_output_window(notebook) 1cdeb

51 self.output_panel = self._window.output_panel 1cdeb

52 self._pid = None 1cdeb

53 

54 @property 

55 def pid(self): 

56 return self._pid 

57 

58 def _get_output_window(self, notebook): 

59 return _OutputWindow(notebook, self) 

60 

61 def run(self): 

62 self._process = Process(self._config.command) 1cdeb

63 # print(f"DEBUG: runanything.py Runner run process object={self._process}" 

64 # f"\nCommand: {self._config.command}") 

65 if self._process is None: 65 ↛ 66line 65 didn't jump to line 66 because the condition on line 65 was never true1cdeb

66 message_box = RIDEDialog(message=f"FAILED TO RUN {self._config.command}", style=wx.ICON_ERROR) 

67 message_box.ShowModal() 

68 return 

69 try: 1cdeb

70 self._process.start() 1cdeb

71 self._timer.Start(500) 1cdeb

72 self._pid = self._process.pid 1cdeb

73 return self._pid 1cdeb

74 except Exception as err: 

75 message_box = RIDEDialog(message=str(err), style=wx.ICON_ERROR) 

76 message_box.ShowModal() 

77 return -1 

78 

79 def on_timer(self, event=None): 

80 __ = event 1cdeb

81 finished = self._process.is_finished() 1cdeb

82 self._window.update_output(self._process.get_output(), finished) 1cdeb

83 if finished: 83 ↛ exitline 83 didn't return from function 'on_timer' because the condition on line 83 was always true1cdeb

84 self._timer.Stop() 1cdeb

85 

86 def stop(self): 

87 try: 1b

88 self._process.stop() 1b

89 except Exception as err: 

90 message_box = RIDEDialog(message=str(err), style=wx.ICON_ERROR) 

91 message_box.ShowModal() 

92 

93 

94class _OutputWindow(wx.Panel): # wx.ScrolledWindow): 

95 

96 def __init__(self, notebook, runner): 

97 wx.Panel.__init__(self, notebook) 

98 self.notebook = notebook 

99 self.output_panel = self._create_ui() 

100 self._add_to_notebook(notebook, runner.name) 

101 self._runner = runner 

102 self._font_size = Font().fixed.GetPointSize() # DEBUG: This should be the font from General 

103 

104 def _create_ui(self): 

105 self.SetSizer(VerticalSizer()) 

106 self.toolbar = HorizontalSizer() 

107 self.toolbar.Add(self._create_state_button()) 

108 self.Sizer.Add(self.toolbar) 

109 wsize = self.GetParent().GetSize()[0] 

110 self.Sizer.Add(wx.StaticLine(self, size=(wsize, 5))) 

111 output_panel = _OutputDisplay(self) 

112 self.Sizer.add_expanding(output_panel) 

113 self.Sizer.Layout() 

114 return output_panel 

115 

116 def _create_state_button(self): 

117 self._state_button = _StopAndRunAgainButton(self) 

118 return self._state_button 

119 

120 def _add_to_notebook(self, notebook, name): 

121 notebook.add_tab(self, f"{name} ({RUNNING})", allow_closing=False) 

122 notebook.show_tab(self) 

123 

124 def update_output(self, output, finished=False): 

125 if output: 

126 self.output_panel.update(output) 

127 self.SetVirtualSize(self.output_panel.Size) 

128 if finished: 

129 RideRunnerStopped(process=self._runner.pid).publish() 

130 self._rename_tab(f"{self._runner.name} ({FINISHED})") 

131 self.Parent.allow_closing(self) 

132 self._state_button.enable_run_again() 

133 size = (max(85, self._font_size * len(' ' + RUN_AGAIN + ' ')), max(28, self._font_size * 3)) 

134 self._state_button.SetSize(size) 

135 

136 def on_stop(self): 

137 self.Parent.allow_closing(self) 

138 self._runner.stop() 

139 

140 def on_run_again(self): 

141 self.output_panel.clear() 

142 self._rename_tab(f"{self._runner.name} ({RUNNING})") 

143 self.Parent.disallow_closing(self) 

144 self._state_button.reset() 

145 size = (max(85, self._font_size * len(' ' + STOP + ' ')), max(28, self._font_size * 3)) 

146 self._state_button.SetSize(size) 

147 self._runner.run() 

148 

149 def _rename_tab(self, name): 

150 self.Parent.rename_tab(self, name) 

151 

152 

153class _OutputDisplay(LogOutput): 

154 

155 def __init__(self, parent): 

156 self._log = [] 

157 LogOutput.__init__(self, parent) 

158 

159 def update(self, addition): 

160 try: 

161 self._log.append(addition.decode('UTF-8', 'ignore')) 

162 self.update_log(self._log) 

163 except AttributeError: 

164 self._log.append("ERROR") 

165 self.update_log(self._log) 

166 getattr(self.Parent, 'on_stop')() 

167 

168 def clear(self): 

169 self._log = [''] 

170 self.update_log(self._log) 

171 

172 

173class _StopAndRunAgainButton(wx.Button): 

174 

175 def __init__(self, parent): 

176 wx.Button.__init__(self, parent, label=' '+STOP+' ') 

177 self.Bind(wx.EVT_BUTTON, self.on_click, self) 

178 

179 def on_click(self, event): 

180 __ = event 

181 self.Enable(False) 

182 name = get_label(self.LabelText) 

183 getattr(self.Parent, name)() 

184 

185 def enable_run_again(self): 

186 self.Enable() 

187 self.SetLabel(RUN_AGAIN) 

188 

189 def reset(self): 

190 self.Enable() 

191 self.SetLabel(STOP)