1"""Basic regular expression demostration facility (Perl style syntax)."""
2
3from Tkinter import *
4import re
5
6class ReDemo:
7
8    def __init__(self, master):
9        self.master = master
10
11        self.promptdisplay = Label(self.master, anchor=W,
12                text="Enter a Perl-style regular expression:")
13        self.promptdisplay.pack(side=TOP, fill=X)
14
15        self.regexdisplay = Entry(self.master)
16        self.regexdisplay.pack(fill=X)
17        self.regexdisplay.focus_set()
18
19        self.addoptions()
20
21        self.statusdisplay = Label(self.master, text="", anchor=W)
22        self.statusdisplay.pack(side=TOP, fill=X)
23
24        self.labeldisplay = Label(self.master, anchor=W,
25                text="Enter a string to search:")
26        self.labeldisplay.pack(fill=X)
27        self.labeldisplay.pack(fill=X)
28
29        self.showframe = Frame(master)
30        self.showframe.pack(fill=X, anchor=W)
31
32        self.showvar = StringVar(master)
33        self.showvar.set("first")
34
35        self.showfirstradio = Radiobutton(self.showframe,
36                                         text="Highlight first match",
37                                          variable=self.showvar,
38                                          value="first",
39                                          command=self.recompile)
40        self.showfirstradio.pack(side=LEFT)
41
42        self.showallradio = Radiobutton(self.showframe,
43                                        text="Highlight all matches",
44                                        variable=self.showvar,
45                                        value="all",
46                                        command=self.recompile)
47        self.showallradio.pack(side=LEFT)
48
49        self.stringdisplay = Text(self.master, width=60, height=4)
50        self.stringdisplay.pack(fill=BOTH, expand=1)
51        self.stringdisplay.tag_configure("hit", background="yellow")
52
53        self.grouplabel = Label(self.master, text="Groups:", anchor=W)
54        self.grouplabel.pack(fill=X)
55
56        self.grouplist = Listbox(self.master)
57        self.grouplist.pack(expand=1, fill=BOTH)
58
59        self.regexdisplay.bind('<Key>', self.recompile)
60        self.stringdisplay.bind('<Key>', self.reevaluate)
61
62        self.compiled = None
63        self.recompile()
64
65        btags = self.regexdisplay.bindtags()
66        self.regexdisplay.bindtags(btags[1:] + btags[:1])
67
68        btags = self.stringdisplay.bindtags()
69        self.stringdisplay.bindtags(btags[1:] + btags[:1])
70
71    def addoptions(self):
72        self.frames = []
73        self.boxes = []
74        self.vars = []
75        for name in ('IGNORECASE',
76                     'LOCALE',
77                     'MULTILINE',
78                     'DOTALL',
79                     'VERBOSE'):
80            if len(self.boxes) % 3 == 0:
81                frame = Frame(self.master)
82                frame.pack(fill=X)
83                self.frames.append(frame)
84            val = getattr(re, name)
85            var = IntVar()
86            box = Checkbutton(frame,
87                    variable=var, text=name,
88                    offvalue=0, onvalue=val,
89                    command=self.recompile)
90            box.pack(side=LEFT)
91            self.boxes.append(box)
92            self.vars.append(var)
93
94    def getflags(self):
95        flags = 0
96        for var in self.vars:
97            flags = flags | var.get()
98        flags = flags
99        return flags
100
101    def recompile(self, event=None):
102        try:
103            self.compiled = re.compile(self.regexdisplay.get(),
104                                       self.getflags())
105            bg = self.promptdisplay['background']
106            self.statusdisplay.config(text="", background=bg)
107        except re.error, msg:
108            self.compiled = None
109            self.statusdisplay.config(
110                    text="re.error: %s" % str(msg),
111                    background="red")
112        self.reevaluate()
113
114    def reevaluate(self, event=None):
115        try:
116            self.stringdisplay.tag_remove("hit", "1.0", END)
117        except TclError:
118            pass
119        try:
120            self.stringdisplay.tag_remove("hit0", "1.0", END)
121        except TclError:
122            pass
123        self.grouplist.delete(0, END)
124        if not self.compiled:
125            return
126        self.stringdisplay.tag_configure("hit", background="yellow")
127        self.stringdisplay.tag_configure("hit0", background="orange")
128        text = self.stringdisplay.get("1.0", END)
129        last = 0
130        nmatches = 0
131        while last <= len(text):
132            m = self.compiled.search(text, last)
133            if m is None:
134                break
135            first, last = m.span()
136            if last == first:
137                last = first+1
138                tag = "hit0"
139            else:
140                tag = "hit"
141            pfirst = "1.0 + %d chars" % first
142            plast = "1.0 + %d chars" % last
143            self.stringdisplay.tag_add(tag, pfirst, plast)
144            if nmatches == 0:
145                self.stringdisplay.yview_pickplace(pfirst)
146                groups = list(m.groups())
147                groups.insert(0, m.group())
148                for i in range(len(groups)):
149                    g = "%2d: %r" % (i, groups[i])
150                    self.grouplist.insert(END, g)
151            nmatches = nmatches + 1
152            if self.showvar.get() == "first":
153                break
154
155        if nmatches == 0:
156            self.statusdisplay.config(text="(no match)",
157                                      background="yellow")
158        else:
159            self.statusdisplay.config(text="")
160
161
162# Main function, run when invoked as a stand-alone Python program.
163
164def main():
165    root = Tk()
166    demo = ReDemo(root)
167    root.protocol('WM_DELETE_WINDOW', root.quit)
168    root.mainloop()
169
170if __name__ == '__main__':
171    main()
172