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