예제 #1
0
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")
예제 #2
0
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()