Home | History | Annotate | Download | only in webchecker
      1 #! /usr/bin/env python

      2 
      3 """Tkinter-based GUI for websucker.
      4 
      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 """
      8 
      9 from Tkinter import *
     10 import websucker
     11 import os
     12 import threading
     13 import Queue
     14 import time
     15 
     16 VERBOSE = 2
     17 
     18 
     19 try:
     20     class Canceled(Exception):
     21         "Exception used to cancel run()."
     22 except (NameError, TypeError):
     23     Canceled = __name__ + ".Canceled"
     24 
     25 
     26 class 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 
     87 class 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 
    196 class 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 
    236 def main():
    237     AppArray().mainloop()
    238 
    239 if __name__ == '__main__':
    240     main()
    241