#! /usr/bin/env python3 """Tkinter-based GUI for websucker. Easy use: type or paste source URL and destination directory in their respective text boxes, click GO or hit return, and presto. """ from Tkinter import * import websucker import os import threading import queue import time VERBOSE = 2 try: class Canceled(Exception): "Exception used to cancel run()." except (NameError, TypeError): Canceled = __name__ + ".Canceled" class SuckerThread(websucker.Sucker): stopit = 0 savedir = None rootdir = None def __init__(self, msgq): self.msgq = msgq websucker.Sucker.__init__(self) self.setflags(verbose=VERBOSE) self.urlopener.addheaders = [ ('User-agent', 'websucker/%s' % websucker.__version__), ] def message(self, format, *args): if args: format = format%args ##print format self.msgq.put(format) def run1(self, url): try: try: self.reset() self.addroot(url) self.run() except Canceled: self.message("[canceled]") else: self.message("[done]") finally: self.msgq.put(None) def savefile(self, text, path): if self.stopit: raise Canceled websucker.Sucker.savefile(self, text, path) def getpage(self, url): if self.stopit: raise Canceled return websucker.Sucker.getpage(self, url) def savefilename(self, url): path = websucker.Sucker.savefilename(self, url) if self.savedir: n = len(self.rootdir) if path[:n] == self.rootdir: path = path[n:] while path[:1] == os.sep: path = path[1:] path = os.path.join(self.savedir, path) return path def XXXaddrobot(self, *args): pass def XXXisallowed(self, *args): return 1 class App: sucker = None msgq = None def __init__(self, top): self.top = top top.columnconfigure(99, weight=1) self.url_label = Label(top, text="URL:") self.url_label.grid(row=0, column=0, sticky='e') self.url_entry = Entry(top, width=60, exportselection=0) self.url_entry.grid(row=0, column=1, sticky='we', columnspan=99) self.url_entry.focus_set() self.url_entry.bind("", self.go) self.dir_label = Label(top, text="Directory:") self.dir_label.grid(row=1, column=0, sticky='e') self.dir_entry = Entry(top) self.dir_entry.grid(row=1, column=1, sticky='we', columnspan=99) self.go_button = Button(top, text="Go", command=self.go) self.go_button.grid(row=2, column=1, sticky='w') self.cancel_button = Button(top, text="Cancel", command=self.cancel, state=DISABLED) self.cancel_button.grid(row=2, column=2, sticky='w') self.auto_button = Button(top, text="Paste+Go", command=self.auto) self.auto_button.grid(row=2, column=3, sticky='w') self.status_label = Label(top, text="[idle]") self.status_label.grid(row=2, column=4, sticky='w') self.top.update_idletasks() self.top.grid_propagate(0) def message(self, text, *args): if args: text = text % args self.status_label.config(text=text) def check_msgq(self): while not self.msgq.empty(): msg = self.msgq.get() if msg is None: self.go_button.configure(state=NORMAL) self.auto_button.configure(state=NORMAL) self.cancel_button.configure(state=DISABLED) if self.sucker: self.sucker.stopit = 0 self.top.bell() else: self.message(msg) self.top.after(100, self.check_msgq) def go(self, event=None): if not self.msgq: self.msgq = queue.Queue(0) self.check_msgq() if not self.sucker: self.sucker = SuckerThread(self.msgq) if self.sucker.stopit: return self.url_entry.selection_range(0, END) url = self.url_entry.get() url = url.strip() if not url: self.top.bell() self.message("[Error: No URL entered]") return self.rooturl = url dir = self.dir_entry.get().strip() if not dir: self.sucker.savedir = None else: self.sucker.savedir = dir self.sucker.rootdir = os.path.dirname( websucker.Sucker.savefilename(self.sucker, url)) self.go_button.configure(state=DISABLED) self.auto_button.configure(state=DISABLED) self.cancel_button.configure(state=NORMAL) self.message( '[running...]') self.sucker.stopit = 0 t = threading.Thread(target=self.sucker.run1, args=(url,)) t.start() def cancel(self): if self.sucker: self.sucker.stopit = 1 self.message("[canceling...]") def auto(self): tries = ['PRIMARY', 'CLIPBOARD'] text = "" for t in tries: try: text = self.top.selection_get(selection=t) except TclError: continue text = text.strip() if text: break if not text: self.top.bell() self.message("[Error: clipboard is empty]") return self.url_entry.delete(0, END) self.url_entry.insert(0, text) self.go() class AppArray: def __init__(self, top=None): if not top: top = Tk() top.title("websucker GUI") top.iconname("wsgui") top.wm_protocol('WM_DELETE_WINDOW', self.exit) self.top = top self.appframe = Frame(self.top) self.appframe.pack(fill='both') self.applist = [] self.exit_button = Button(top, text="Exit", command=self.exit) self.exit_button.pack(side=RIGHT) self.new_button = Button(top, text="New", command=self.addsucker) self.new_button.pack(side=LEFT) self.addsucker() ##self.applist[0].url_entry.insert(END, "http://www.python.org/doc/essays/") def addsucker(self): self.top.geometry("") frame = Frame(self.appframe, borderwidth=2, relief=GROOVE) frame.pack(fill='x') app = App(frame) self.applist.append(app) done = 0 def mainloop(self): while not self.done: time.sleep(0.1) self.top.update() def exit(self): for app in self.applist: app.cancel() app.message("[exiting...]") self.done = 1 def main(): AppArray().mainloop() if __name__ == '__main__': main()