class SearchCtrl(wx.Panel): def __init__(self, parent, env): wx.Panel.__init__(self, parent) self.env = env self.max_line_length = 100 self.finder = None self.output = ThreadOutputCtrl(self, env, auto_scroll=False) text_width, text_height = get_text_extent(self.GetFont(), "Copy to Editor") button_size = (text_width + 30, text_height + 10) self.status_label = wx.StaticText(self) button_stop = wx.Button(self, label="&Stop", size=button_size) button_copy = wx.Button(self, label="&Copy to Editor", size=button_size) button_clear = wx.Button(self, label="C&lear", size=button_size) top_sizer = wx.BoxSizer(wx.HORIZONTAL) top_sizer.Add(button_stop, 0, wx.ALIGN_CENTER) top_sizer.AddSpacer(5) top_sizer.Add(button_copy, 0, wx.ALIGN_CENTER) top_sizer.AddSpacer(5) top_sizer.Add(button_clear, 0, wx.ALIGN_CENTER) top_sizer.AddSpacer(5) top_sizer.Add(self.status_label, 0, wx.ALIGN_CENTER) top_sizer.AddSpacer(5) top_sizer.AddStretchSpacer() sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(top_sizer, 0, wx.EXPAND | wx.ALL, 5) sizer.Add(self.output, 1, wx.EXPAND) self.SetSizer(sizer) self.Bind(wx.EVT_BUTTON, self.OnStop, button_stop) self.Bind(wx.EVT_BUTTON, self.OnCopyToEditor, button_copy) self.Bind(wx.EVT_BUTTON, self.OnClear, button_clear) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateStop, button_stop) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateClear, button_copy) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateClear, button_clear) self.output.Bind(wx.EVT_LEFT_DCLICK, self.OnLineDoubleClicked) def Destroy(self): if self.finder: self.finder.stop() wx.Panel.Destroy(self) def OnStop(self, evt): if self.finder: self.finder.stop() def OnUpdateStop(self, evt): evt.Enable(bool(self.finder)) def Clear(self): self.output.ClearAll() self.env.clear_highlight(MARKER_FIND) def OnClear(self, evt): self.Clear() def OnCopyToEditor(self, evt): self.env.open_text(self.output.GetText()) def OnUpdateClear(self, evt): evt.Enable(not self.output.IsEmpty()) def OnLineDoubleClicked(self, evt): cur_line = mark_line = self.output.GetCurrentLine() try: s = self.output.GetLine(cur_line).lstrip() line_num = int(s.split(":", 1)[0]) cur_line -= 1 except ValueError: try: s = self.output.GetLine(cur_line + 1).lstrip() line_num = int(s.split(":", 1)[0]) mark_line = cur_line + 1 except ValueError: evt.Skip() return for cur_line in xrange(cur_line, -1, -1): path = self.output.GetLine(cur_line).rstrip() if r_path_start.match(path): self.env.open_file(path, line_num, MARKER_FIND) self.output.SetHighlightedLine(mark_line, MARKER_FIND) return evt.Skip() def stop(self): if self.finder: self.finder.stop() def find(self, details, filter=None): self.stop() matcher = make_matcher(details.find, case_sensitive=details.case, is_regexp=details.regexp) self.start_time = time.time() self.details = details self.finder = Search(details.path, matcher, output=self, filter=filter) async_call(self.finder.search) self.Clear() self.output.start() def __set_status(self, finder, status): try: if finder is self.finder: self.status_label.SetLabel(status) except wx.PyDeadObjectError: pass def begin_file(self, finder, filepath): wx.CallAfter(self.__set_status, finder, filepath) def add_file(self, finder, filepath): self.output.write(filepath + "\n") def add_line(self, finder, line_num, line): if len(line) > self.max_line_length: line = line[:self.max_line_length] + "..." self.output.write(" %d: %s\n" % (line_num, line)) def end_file(self, finder): self.output.write("\n") def __do_finish(self, finder): try: if finder is self.finder: self.details = None self.finder = None self.status_label.SetLabel("") self.output.stop() except wx.PyDeadObjectError: pass def finish(self, finder, message): completed_time = time.time() - self.start_time if finder is self.finder: self.output.write(message % (self.details.find, self.details.path, completed_time)) wx.CallAfter(self.__do_finish, finder) def end_find(self, finder): self.finish(finder, "Search for '%s' in %s completed in %.2f seconds") def abort_find(self, finder): self.finish(finder, "Search for '%s' in %s aborted after %.2f seconds")
class TerminalCtrl(wx.Panel): def __init__(self, parent, env): wx.Panel.__init__(self, parent) self.env = env self.thread = None self.process = None self.cwd = None self.output = ThreadOutputCtrl(self, env, auto_scroll=True) text_width, text_height = get_text_extent(self.GetFont(), "Copy to Editor") button_size = (text_width + 30, text_height + 10) self.status_label = wx.StaticText(self) button_kill = wx.Button(self, label="&Kill", size=button_size) button_stop = wx.Button(self, label="&Stop", size=button_size) button_copy = wx.Button(self, label="&Copy to Editor", size=button_size) button_clear = wx.Button(self, label="C&lear", size=button_size) top_sizer = wx.BoxSizer(wx.HORIZONTAL) top_sizer.Add(self.status_label, 0, wx.ALIGN_CENTER) top_sizer.AddStretchSpacer() top_sizer.Add(button_kill, 0, wx.ALIGN_CENTER) top_sizer.AddSpacer(5) top_sizer.Add(button_stop, 0, wx.ALIGN_CENTER) top_sizer.AddSpacer(5) top_sizer.Add(button_copy, 0, wx.ALIGN_CENTER) top_sizer.AddSpacer(5) top_sizer.Add(button_clear, 0, wx.ALIGN_CENTER) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(top_sizer, 0, wx.EXPAND | wx.ALL, 5) sizer.Add(self.output, 1, wx.EXPAND) self.SetSizer(sizer) self.Bind(wx.EVT_BUTTON, self.OnKill, button_kill) self.Bind(wx.EVT_BUTTON, self.OnStop, button_stop) self.Bind(wx.EVT_BUTTON, self.OnCopyToEditor, button_copy) self.Bind(wx.EVT_BUTTON, self.OnClear, button_clear) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateStop, button_kill) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateStop, button_stop) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateClear, button_copy) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateClear, button_clear) self.output.Bind(wx.EVT_LEFT_DCLICK, self.OnLineDoubleClicked) def OnCopyToEditor(self, evt): self.env.open_text(self.output.GetText()) def Clear(self): self.output.ClearAll() self.env.clear_highlight(MARKER_ERROR) def OnClear(self, evt): self.Clear() def OnUpdateClear(self, evt): evt.Enable(not self.output.IsEmpty()) def OnStop(self, evt): self.stop() def OnKill(self, evt): self.kill() def OnUpdateStop(self, evt): evt.Enable(self.process is not None and isinstance(self.process, KillablePopen)) def OpenFileOnLine(self, output_line, highlight_line): s = self.output.GetLine(output_line) for pattern in file_line_patterns: m = pattern.match(s) if m: path = os.path.join(self.cwd or self.env.project_root, m.group("file")) line = int(m.group("line")) self.env.open_file(path, line, MARKER_ERROR) self.output.SetHighlightedLine(highlight_line, MARKER_ERROR) return True return False def OnLineDoubleClicked(self, evt): output_line = self.output.GetCurrentLine() if self.OpenFileOnLine(output_line, output_line): return if self.OpenFileOnLine(output_line - 1, output_line): return evt.Skip() def ClearHighlight(self, marker_type): self.output.ClearHighlight(marker_type) def set_status(self, status): self.status_label.SetLabel(status.replace("&", "&&")) self.Layout() @property def is_running(self): return bool(self.process) def run(self, cmdline, env=None, cwd=None, stdin=None, stdout=None, killable=True): if self.process: return self.process = run_shell_command(cmdline, env=env, cwd=cwd, killable=killable) self.thread = threading.Thread(target=self.__thread, args=(self.process, stdin, stdout)) self.thread.start() self.cmdline = cmdline self.cwd = cwd self.set_status("Running (pid %d)" % self.process.pid) self.Clear() self.output.start() def stop(self): if self.process: self.process.terminate() def kill(self): if self.process: self.process.kill() def __thread(self, process, stdin=None, stdout=None): rc = None buffer = StringIO() if stdout else None try: if stdin is not None: process.stdin.write(stdin) process.stdin.close() while True: line = process.stdout.readline() if not line: break self.output.write(line.decode("utf-8", "replace")) if buffer: buffer.write(line) rc = process.wait() finally: if buffer: s = buffer.getvalue().decode("utf-8", "replace") buffer.close() if rc == 0: wx.CallAfter(stdout.write, s) else: wx.CallAfter(stdout.close) try: wx.CallAfter(self.__thread_exit, process, rc) except wx.PyDeadObjectError: pass def __thread_exit(self, process, rc): if rc != 0: self.env.show_terminal() self.set_status("Process terminated%s" % ( " with return code %d" % rc if rc is not None else "")) if self.process is process: self.thread = None self.process = None self.output.stop()