/Tools/webchecker/wsgui.py

http://unladen-swallow.googlecode.com/ · Python · 240 lines · 197 code · 35 blank · 8 comment · 34 complexity · 47f7d28994f8d3d673152743dfabf1cf MD5 · raw file

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