diff options
Diffstat (limited to 'Lib/idlelib/OutputWindow.py')
-rw-r--r-- | Lib/idlelib/OutputWindow.py | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/Lib/idlelib/OutputWindow.py b/Lib/idlelib/OutputWindow.py new file mode 100644 index 0000000..12280ad --- /dev/null +++ b/Lib/idlelib/OutputWindow.py @@ -0,0 +1,279 @@ +# changes by dscherer@cmu.edu +# - OutputWindow and OnDemandOutputWindow have been hastily +# extended to provide readline() support, an "iomark" separate +# from the "insert" cursor, and scrolling to clear the window. +# These changes are used by the ExecBinding module to provide +# standard input and output for user programs. Many of the new +# features are very similar to features of PyShell, which is a +# subclass of OutputWindow. Someone should make some sense of +# this. + +from Tkinter import * +from EditorWindow import EditorWindow +import re +import tkMessageBox + +from UndoDelegator import UndoDelegator + +class OutputUndoDelegator(UndoDelegator): + reading = 0 + # Forbid insert/delete before the I/O mark, in the blank lines after + # the output, or *anywhere* if we are not presently doing user input + def insert(self, index, chars, tags=None): + try: + if (self.delegate.compare(index, "<", "iomark") or + self.delegate.compare(index, ">", "endmark") or + (index!="iomark" and not self.reading)): + self.delegate.bell() + return + except TclError: + pass + UndoDelegator.insert(self, index, chars, tags) + def delete(self, index1, index2=None): + try: + if (self.delegate.compare(index1, "<", "iomark") or + self.delegate.compare(index1, ">", "endmark") or + (index2 and self.delegate.compare(index2, ">=", "endmark")) or + not self.reading): + self.delegate.bell() + return + except TclError: + pass + UndoDelegator.delete(self, index1, index2) + +class OutputWindow(EditorWindow): + """An editor window that can serve as an input and output file. + The input support has been rather hastily hacked in, and should + not be trusted. + """ + + UndoDelegator = OutputUndoDelegator + source_window = None + + def __init__(self, *args, **keywords): + if keywords.has_key('source_window'): + self.source_window = keywords['source_window'] + apply(EditorWindow.__init__, (self,) + args) + self.text.bind("<<goto-file-line>>", self.goto_file_line) + self.text.bind("<<newline-and-indent>>", self.enter_callback) + self.text.mark_set("iomark","1.0") + self.text.mark_gravity("iomark", LEFT) + self.text.mark_set("endmark","1.0") + + # Customize EditorWindow + + def ispythonsource(self, filename): + # No colorization needed + return 0 + + def short_title(self): + return "Output" + + def long_title(self): + return "" + + def maybesave(self): + # Override base class method -- don't ask any questions + if self.get_saved(): + return "yes" + else: + return "no" + + # Act as input file - incomplete + + def set_line_and_column(self, event=None): + index = self.text.index(INSERT) + if (self.text.compare(index, ">", "endmark")): + self.text.mark_set("insert", "endmark") + self.text.see("insert") + EditorWindow.set_line_and_column(self) + + reading = 0 + canceled = 0 + endoffile = 0 + + def readline(self): + save = self.reading + try: + self.reading = self.undo.reading = 1 + self.text.mark_set("insert", "iomark") + self.text.see("insert") + self.top.mainloop() + finally: + self.reading = self.undo.reading = save + line = self.text.get("input", "iomark") + if self.canceled: + self.canceled = 0 + raise KeyboardInterrupt + if self.endoffile: + self.endoffile = 0 + return "" + return line or '\n' + + def close(self): + self.interrupt() + return EditorWindow.close(self) + + def interrupt(self): + if self.reading: + self.endoffile = 1 + self.top.quit() + + def enter_callback(self, event): + if self.reading and self.text.compare("insert", ">=", "iomark"): + self.text.mark_set("input", "iomark") + self.text.mark_set("iomark", "insert") + self.write('\n',"iomark") + self.text.tag_add("stdin", "input", "iomark") + self.text.update_idletasks() + self.top.quit() # Break out of recursive mainloop() in raw_input() + + return "break" + + # Act as output file + + def write(self, s, tags=(), mark="iomark"): + self.text.mark_gravity(mark, RIGHT) + self.text.insert(mark, str(s), tags) + self.text.mark_gravity(mark, LEFT) + self.text.see(mark) + self.text.update() + + def writelines(self, l): + map(self.write, l) + + def flush(self): + pass + + # Our own right-button menu + + rmenu_specs = [ + ("Go to file/line", "<<goto-file-line>>"), + ] + + file_line_pats = [ + r'file "([^"]*)", line (\d+)', + r'([^\s]+)\((\d+)\)', + r'([^\s]+):\s*(\d+):', + ] + + file_line_progs = None + + def goto_file_line(self, event=None): + if self.file_line_progs is None: + l = [] + for pat in self.file_line_pats: + l.append(re.compile(pat, re.IGNORECASE)) + self.file_line_progs = l + # x, y = self.event.x, self.event.y + # self.text.mark_set("insert", "@%d,%d" % (x, y)) + line = self.text.get("insert linestart", "insert lineend") + result = self._file_line_helper(line) + if not result: + # Try the previous line. This is handy e.g. in tracebacks, + # where you tend to right-click on the displayed source line + line = self.text.get("insert -1line linestart", + "insert -1line lineend") + result = self._file_line_helper(line) + if not result: + tkMessageBox.showerror( + "No special line", + "The line you point at doesn't look like " + "a valid file name followed by a line number.", + master=self.text) + return + filename, lineno = result + edit = self.untitled(filename) or self.flist.open(filename) + edit.gotoline(lineno) + edit.wakeup() + + def untitled(self, filename): + if filename!='Untitled' or not self.source_window or self.source_window.io.filename: + return None + return self.source_window + + def _file_line_helper(self, line): + for prog in self.file_line_progs: + m = prog.search(line) + if m: + break + else: + return None + filename, lineno = m.group(1, 2) + if not self.untitled(filename): + try: + f = open(filename, "r") + f.close() + except IOError: + return None + try: + return filename, int(lineno) + except TypeError: + return None + +# This classes now used by ExecBinding.py: + +class OnDemandOutputWindow: + source_window = None + + tagdefs = { + # XXX Should use IdlePrefs.ColorPrefs + "stdin": {"foreground": "black"}, + "stdout": {"foreground": "blue"}, + "stderr": {"foreground": "red"}, + } + + def __init__(self, flist): + self.flist = flist + self.owin = None + self.title = "Output" + self.close_hook = None + self.old_close = None + + def owclose(self): + if self.close_hook: + self.close_hook() + if self.old_close: + self.old_close() + + def set_title(self, title): + self.title = title + if self.owin and self.owin.text: + self.owin.saved_change_hook() + + def write(self, s, tags=(), mark="iomark"): + if not self.owin or not self.owin.text: + self.setup() + self.owin.write(s, tags, mark) + + def readline(self): + if not self.owin or not self.owin.text: + self.setup() + return self.owin.readline() + + def scroll_clear(self): + if self.owin and self.owin.text: + lineno = self.owin.getlineno("endmark") + self.owin.text.mark_set("insert","endmark") + self.owin.text.yview(float(lineno)) + self.owin.wakeup() + + def setup(self): + self.owin = owin = OutputWindow(self.flist, source_window = self.source_window) + owin.short_title = lambda self=self: self.title + text = owin.text + + self.old_close = owin.close_hook + owin.close_hook = self.owclose + + # xxx Bad hack: 50 blank lines at the bottom so that + # we can scroll the top of the window to the output + # cursor in scroll_clear(). There must be a better way... + owin.text.mark_gravity('endmark', LEFT) + owin.text.insert('iomark', '\n'*50) + owin.text.mark_gravity('endmark', RIGHT) + + for tag, cnf in self.tagdefs.items(): + if cnf: + apply(text.tag_configure, (tag,), cnf) + text.tag_raise('sel') |