PageRenderTime 377ms CodeModel.GetById 111ms app.highlight 106ms RepoModel.GetById 156ms app.codeStats 0ms

/Tools/webchecker/wsgui.py

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