1import fnmatch 2import os 3import sys 4 5from tkinter import StringVar, BooleanVar 6from tkinter.ttk import Checkbutton 7 8from idlelib.searchbase import SearchDialogBase 9from idlelib import searchengine 10 11# Importing OutputWindow here fails due to import loop 12# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow 13 14def grep(text, io=None, flist=None): 15 root = text._root() 16 engine = searchengine.get(root) 17 if not hasattr(engine, "_grepdialog"): 18 engine._grepdialog = GrepDialog(root, engine, flist) 19 dialog = engine._grepdialog 20 searchphrase = text.get("sel.first", "sel.last") 21 dialog.open(text, searchphrase, io) 22 23class GrepDialog(SearchDialogBase): 24 25 title = "Find in Files Dialog" 26 icon = "Grep" 27 needwrapbutton = 0 28 29 def __init__(self, root, engine, flist): 30 SearchDialogBase.__init__(self, root, engine) 31 self.flist = flist 32 self.globvar = StringVar(root) 33 self.recvar = BooleanVar(root) 34 35 def open(self, text, searchphrase, io=None): 36 SearchDialogBase.open(self, text, searchphrase) 37 if io: 38 path = io.filename or "" 39 else: 40 path = "" 41 dir, base = os.path.split(path) 42 head, tail = os.path.splitext(base) 43 if not tail: 44 tail = ".py" 45 self.globvar.set(os.path.join(dir, "*" + tail)) 46 47 def create_entries(self): 48 SearchDialogBase.create_entries(self) 49 self.globent = self.make_entry("In files:", self.globvar)[0] 50 51 def create_other_buttons(self): 52 btn = Checkbutton( 53 self.make_frame()[0], variable=self.recvar, 54 text="Recurse down subdirectories") 55 btn.pack(side="top", fill="both") 56 57 def create_command_buttons(self): 58 SearchDialogBase.create_command_buttons(self) 59 self.make_button("Search Files", self.default_command, 1) 60 61 def default_command(self, event=None): 62 prog = self.engine.getprog() 63 if not prog: 64 return 65 path = self.globvar.get() 66 if not path: 67 self.top.bell() 68 return 69 from idlelib.outwin import OutputWindow # leave here! 70 save = sys.stdout 71 try: 72 sys.stdout = OutputWindow(self.flist) 73 self.grep_it(prog, path) 74 finally: 75 sys.stdout = save 76 77 def grep_it(self, prog, path): 78 dir, base = os.path.split(path) 79 list = self.findfiles(dir, base, self.recvar.get()) 80 list.sort() 81 self.close() 82 pat = self.engine.getpat() 83 print("Searching %r in %s ..." % (pat, path)) 84 hits = 0 85 try: 86 for fn in list: 87 try: 88 with open(fn, errors='replace') as f: 89 for lineno, line in enumerate(f, 1): 90 if line[-1:] == '\n': 91 line = line[:-1] 92 if prog.search(line): 93 sys.stdout.write("%s: %s: %s\n" % 94 (fn, lineno, line)) 95 hits += 1 96 except OSError as msg: 97 print(msg) 98 print(("Hits found: %s\n" 99 "(Hint: right-click to open locations.)" 100 % hits) if hits else "No hits.") 101 except AttributeError: 102 # Tk window has been closed, OutputWindow.text = None, 103 # so in OW.write, OW.text.insert fails. 104 pass 105 106 def findfiles(self, dir, base, rec): 107 try: 108 names = os.listdir(dir or os.curdir) 109 except OSError as msg: 110 print(msg) 111 return [] 112 list = [] 113 subdirs = [] 114 for name in names: 115 fn = os.path.join(dir, name) 116 if os.path.isdir(fn): 117 subdirs.append(fn) 118 else: 119 if fnmatch.fnmatch(name, base): 120 list.append(fn) 121 if rec: 122 for subdir in subdirs: 123 list.extend(self.findfiles(subdir, base, rec)) 124 return list 125 126 def close(self, event=None): 127 if self.top: 128 self.top.grab_release() 129 self.top.withdraw() 130 131 132def _grep_dialog(parent): # htest # 133 from tkinter import Toplevel, Text, SEL, END 134 from tkinter.ttk import Button 135 from idlelib.pyshell import PyShellFileList 136 top = Toplevel(parent) 137 top.title("Test GrepDialog") 138 x, y = map(int, parent.geometry().split('+')[1:]) 139 top.geometry("+%d+%d" % (x, y + 175)) 140 141 flist = PyShellFileList(top) 142 text = Text(top, height=5) 143 text.pack() 144 145 def show_grep_dialog(): 146 text.tag_add(SEL, "1.0", END) 147 grep(text, flist=flist) 148 text.tag_remove(SEL, "1.0", END) 149 150 button = Button(top, text="Show GrepDialog", command=show_grep_dialog) 151 button.pack() 152 153if __name__ == "__main__": 154 import unittest 155 unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False) 156 157 from idlelib.idle_test.htest import run 158 run(_grep_dialog) 159