blob: 0c851ca5db3e777278fdebb57fb072ab3db02cb2 [file] [log] [blame]
Guido van Rossume3bd8211998-06-15 12:35:19 +00001#! /usr/bin/env python
2
3"""Tkinter-based GUI for websucker.
4
5Easy use: type or paste source URL and destination directory in
6their respective text boxes, click GO or hit return, and presto.
7"""
8
9from Tkinter import *
10import Tkinter
Guido van Rossume3bd8211998-06-15 12:35:19 +000011import websucker
12import sys
13import os
Guido van Rossumb77a68e1998-07-08 03:05:22 +000014import threading
15import Queue
Guido van Rossum61b95db1999-11-17 15:13:21 +000016import time
Guido van Rossume3bd8211998-06-15 12:35:19 +000017
Guido van Rossumb77a68e1998-07-08 03:05:22 +000018VERBOSE = 2
Guido van Rossume3bd8211998-06-15 12:35:19 +000019
20
21try:
Guido van Rossum61b95db1999-11-17 15:13:21 +000022 class Canceled(Exception):
23 "Exception used to cancel run()."
Fred Drakea2133332001-05-11 19:40:10 +000024except (NameError, TypeError):
Guido van Rossum61b95db1999-11-17 15:13:21 +000025 Canceled = __name__ + ".Canceled"
Guido van Rossume3bd8211998-06-15 12:35:19 +000026
27
Guido van Rossumb77a68e1998-07-08 03:05:22 +000028class SuckerThread(websucker.Sucker):
Guido van Rossume3bd8211998-06-15 12:35:19 +000029
Guido van Rossum61b95db1999-11-17 15:13:21 +000030 stopit = 0
31 savedir = None
32 rootdir = None
Guido van Rossumb77a68e1998-07-08 03:05:22 +000033
Guido van Rossum61b95db1999-11-17 15:13:21 +000034 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__),
40 ]
Guido van Rossume3bd8211998-06-15 12:35:19 +000041
Guido van Rossum61b95db1999-11-17 15:13:21 +000042 def message(self, format, *args):
43 if args:
44 format = format%args
45 ##print format
46 self.msgq.put(format)
Guido van Rossumb77a68e1998-07-08 03:05:22 +000047
Guido van Rossum61b95db1999-11-17 15:13:21 +000048 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)
Guido van Rossumb77a68e1998-07-08 03:05:22 +000060
Guido van Rossum61b95db1999-11-17 15:13:21 +000061 def savefile(self, text, path):
62 if self.stopit:
63 raise Canceled
64 websucker.Sucker.savefile(self, text, path)
Guido van Rossumb77a68e1998-07-08 03:05:22 +000065
Guido van Rossum61b95db1999-11-17 15:13:21 +000066 def getpage(self, url):
67 if self.stopit:
68 raise Canceled
69 return websucker.Sucker.getpage(self, url)
Guido van Rossumb77a68e1998-07-08 03:05:22 +000070
Guido van Rossum61b95db1999-11-17 15:13:21 +000071 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
81
82 def XXXaddrobot(self, *args):
83 pass
84
85 def XXXisallowed(self, *args):
86 return 1
Guido van Rossumb77a68e1998-07-08 03:05:22 +000087
88
89class App:
90
Guido van Rossum61b95db1999-11-17 15:13:21 +000091 sucker = None
92 msgq = None
Guido van Rossumb77a68e1998-07-08 03:05:22 +000093
Guido van Rossum61b95db1999-11-17 15:13:21 +000094 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)
Guido van Rossum0a13f7f1998-06-15 14:49:16 +0000122
Guido van Rossum61b95db1999-11-17 15:13:21 +0000123 def message(self, text, *args):
124 if args:
125 text = text % args
126 self.status_label.config(text=text)
Guido van Rossume3bd8211998-06-15 12:35:19 +0000127
Guido van Rossum61b95db1999-11-17 15:13:21 +0000128 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)
Guido van Rossumb77a68e1998-07-08 03:05:22 +0000141
Guido van Rossum61b95db1999-11-17 15:13:21 +0000142 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()
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000152 url = url.strip()
Guido van Rossum61b95db1999-11-17 15:13:21 +0000153 if not url:
154 self.top.bell()
155 self.message("[Error: No URL entered]")
156 return
157 self.rooturl = url
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000158 dir = self.dir_entry.get().strip()
Guido van Rossum61b95db1999-11-17 15:13:21 +0000159 if not dir:
160 self.sucker.savedir = None
161 else:
162 self.sucker.savedir = dir
163 self.sucker.rootdir = os.path.dirname(
Neal Norwitz592c4cc2003-07-01 04:14:28 +0000164 websucker.Sucker.savefilename(self.sucker, url))
Guido van Rossum61b95db1999-11-17 15:13:21 +0000165 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()
172
173 def cancel(self):
174 if self.sucker:
175 self.sucker.stopit = 1
176 self.message("[canceling...]")
177
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
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000186 text = text.strip()
Guido van Rossum61b95db1999-11-17 15:13:21 +0000187 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()
Guido van Rossume3bd8211998-06-15 12:35:19 +0000196
197
Guido van Rossumb77a68e1998-07-08 03:05:22 +0000198class AppArray:
199
Guido van Rossum61b95db1999-11-17 15:13:21 +0000200 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/")
Guido van Rossumb77a68e1998-07-08 03:05:22 +0000216
Guido van Rossum61b95db1999-11-17 15:13:21 +0000217 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)
Guido van Rossumb77a68e1998-07-08 03:05:22 +0000223
Guido van Rossum61b95db1999-11-17 15:13:21 +0000224 done = 0
Guido van Rossumb77a68e1998-07-08 03:05:22 +0000225
Guido van Rossum61b95db1999-11-17 15:13:21 +0000226 def mainloop(self):
227 while not self.done:
228 time.sleep(0.1)
229 self.top.update()
230
231 def exit(self):
232 for app in self.applist:
233 app.cancel()
234 app.message("[exiting...]")
235 self.done = 1
Guido van Rossumb77a68e1998-07-08 03:05:22 +0000236
237
238def main():
Guido van Rossum61b95db1999-11-17 15:13:21 +0000239 AppArray().mainloop()
Guido van Rossumb77a68e1998-07-08 03:05:22 +0000240
Guido van Rossume3bd8211998-06-15 12:35:19 +0000241if __name__ == '__main__':
Guido van Rossumb77a68e1998-07-08 03:05:22 +0000242 main()