Changes due to added test for fileConfig contributed by Shane Hathaway.
[python.git] / Tools / webchecker / wsgui.py
blob0c851ca5db3e777278fdebb57fb072ab3db02cb2
1 #! /usr/bin/env python
3 """Tkinter-based GUI for websucker.
5 Easy use: type or paste source URL and destination directory in
6 their respective text boxes, click GO or hit return, and presto.
7 """
9 from Tkinter import *
10 import Tkinter
11 import websucker
12 import sys
13 import os
14 import threading
15 import Queue
16 import time
18 VERBOSE = 2
21 try:
22 class Canceled(Exception):
23 "Exception used to cancel run()."
24 except (NameError, TypeError):
25 Canceled = __name__ + ".Canceled"
28 class SuckerThread(websucker.Sucker):
30 stopit = 0
31 savedir = None
32 rootdir = None
34 def __init__(self, msgq):
35 self.msgq = msgq
36 websucker.Sucker.__init__(self)
37 self.setflags(verbose=VERBOSE)
38 self.urlopener.addheaders = [
39 ('User-agent', 'websucker/%s' % websucker.__version__),
42 def message(self, format, *args):
43 if args:
44 format = format%args
45 ##print format
46 self.msgq.put(format)
48 def run1(self, url):
49 try:
50 try:
51 self.reset()
52 self.addroot(url)
53 self.run()
54 except Canceled:
55 self.message("[canceled]")
56 else:
57 self.message("[done]")
58 finally:
59 self.msgq.put(None)
61 def savefile(self, text, path):
62 if self.stopit:
63 raise Canceled
64 websucker.Sucker.savefile(self, text, path)
66 def getpage(self, url):
67 if self.stopit:
68 raise Canceled
69 return websucker.Sucker.getpage(self, url)
71 def savefilename(self, url):
72 path = websucker.Sucker.savefilename(self, url)
73 if self.savedir:
74 n = len(self.rootdir)
75 if path[:n] == self.rootdir:
76 path = path[n:]
77 while path[:1] == os.sep:
78 path = path[1:]
79 path = os.path.join(self.savedir, path)
80 return path
82 def XXXaddrobot(self, *args):
83 pass
85 def XXXisallowed(self, *args):
86 return 1
89 class App:
91 sucker = None
92 msgq = None
94 def __init__(self, top):
95 self.top = top
96 top.columnconfigure(99, weight=1)
97 self.url_label = Label(top, text="URL:")
98 self.url_label.grid(row=0, column=0, sticky='e')
99 self.url_entry = Entry(top, width=60, exportselection=0)
100 self.url_entry.grid(row=0, column=1, sticky='we',
101 columnspan=99)
102 self.url_entry.focus_set()
103 self.url_entry.bind("<Key-Return>", self.go)
104 self.dir_label = Label(top, text="Directory:")
105 self.dir_label.grid(row=1, column=0, sticky='e')
106 self.dir_entry = Entry(top)
107 self.dir_entry.grid(row=1, column=1, sticky='we',
108 columnspan=99)
109 self.go_button = Button(top, text="Go", command=self.go)
110 self.go_button.grid(row=2, column=1, sticky='w')
111 self.cancel_button = Button(top, text="Cancel",
112 command=self.cancel,
113 state=DISABLED)
114 self.cancel_button.grid(row=2, column=2, sticky='w')
115 self.auto_button = Button(top, text="Paste+Go",
116 command=self.auto)
117 self.auto_button.grid(row=2, column=3, sticky='w')
118 self.status_label = Label(top, text="[idle]")
119 self.status_label.grid(row=2, column=4, sticky='w')
120 self.top.update_idletasks()
121 self.top.grid_propagate(0)
123 def message(self, text, *args):
124 if args:
125 text = text % args
126 self.status_label.config(text=text)
128 def check_msgq(self):
129 while not self.msgq.empty():
130 msg = self.msgq.get()
131 if msg is None:
132 self.go_button.configure(state=NORMAL)
133 self.auto_button.configure(state=NORMAL)
134 self.cancel_button.configure(state=DISABLED)
135 if self.sucker:
136 self.sucker.stopit = 0
137 self.top.bell()
138 else:
139 self.message(msg)
140 self.top.after(100, self.check_msgq)
142 def go(self, event=None):
143 if not self.msgq:
144 self.msgq = Queue.Queue(0)
145 self.check_msgq()
146 if not self.sucker:
147 self.sucker = SuckerThread(self.msgq)
148 if self.sucker.stopit:
149 return
150 self.url_entry.selection_range(0, END)
151 url = self.url_entry.get()
152 url = url.strip()
153 if not url:
154 self.top.bell()
155 self.message("[Error: No URL entered]")
156 return
157 self.rooturl = url
158 dir = self.dir_entry.get().strip()
159 if not dir:
160 self.sucker.savedir = None
161 else:
162 self.sucker.savedir = dir
163 self.sucker.rootdir = os.path.dirname(
164 websucker.Sucker.savefilename(self.sucker, url))
165 self.go_button.configure(state=DISABLED)
166 self.auto_button.configure(state=DISABLED)
167 self.cancel_button.configure(state=NORMAL)
168 self.message( '[running...]')
169 self.sucker.stopit = 0
170 t = threading.Thread(target=self.sucker.run1, args=(url,))
171 t.start()
173 def cancel(self):
174 if self.sucker:
175 self.sucker.stopit = 1
176 self.message("[canceling...]")
178 def auto(self):
179 tries = ['PRIMARY', 'CLIPBOARD']
180 text = ""
181 for t in tries:
182 try:
183 text = self.top.selection_get(selection=t)
184 except TclError:
185 continue
186 text = text.strip()
187 if text:
188 break
189 if not text:
190 self.top.bell()
191 self.message("[Error: clipboard is empty]")
192 return
193 self.url_entry.delete(0, END)
194 self.url_entry.insert(0, text)
195 self.go()
198 class AppArray:
200 def __init__(self, top=None):
201 if not top:
202 top = Tk()
203 top.title("websucker GUI")
204 top.iconname("wsgui")
205 top.wm_protocol('WM_DELETE_WINDOW', self.exit)
206 self.top = top
207 self.appframe = Frame(self.top)
208 self.appframe.pack(fill='both')
209 self.applist = []
210 self.exit_button = Button(top, text="Exit", command=self.exit)
211 self.exit_button.pack(side=RIGHT)
212 self.new_button = Button(top, text="New", command=self.addsucker)
213 self.new_button.pack(side=LEFT)
214 self.addsucker()
215 ##self.applist[0].url_entry.insert(END, "http://www.python.org/doc/essays/")
217 def addsucker(self):
218 self.top.geometry("")
219 frame = Frame(self.appframe, borderwidth=2, relief=GROOVE)
220 frame.pack(fill='x')
221 app = App(frame)
222 self.applist.append(app)
224 done = 0
226 def mainloop(self):
227 while not self.done:
228 time.sleep(0.1)
229 self.top.update()
231 def exit(self):
232 for app in self.applist:
233 app.cancel()
234 app.message("[exiting...]")
235 self.done = 1
238 def main():
239 AppArray().mainloop()
241 if __name__ == '__main__':
242 main()