Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 1 | #! /usr/bin/env python |
| 2 | |
| 3 | """GUI interface to webchecker. |
| 4 | |
| 5 | This works as a Grail applet too! E.g. |
| 6 | |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 7 | <APPLET CODE=wcgui.py NAME=CheckerWindow></APPLET> |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 8 | |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 9 | Checkpoints are not (yet??? ever???) supported. |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 10 | |
| 11 | User interface: |
| 12 | |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 13 | Enter a root to check in the text entry box. To enter more than one root, |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 14 | enter them one at a time and press <Return> for each one. |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 15 | |
| 16 | Command buttons Start, Stop and "Check one" govern the checking process in |
| 17 | the obvious way. Start and "Check one" also enter the root from the text |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 18 | entry box if one is present. There's also a check box (enabled by default) |
| 19 | to decide whether actually to follow external links (since this can slow |
| 20 | the checking down considerably). Finally there's a Quit button. |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 21 | |
| 22 | A series of checkbuttons determines whether the corresponding output panel |
| 23 | is shown. List panels are also automatically shown or hidden when their |
| 24 | status changes between empty to non-empty. There are six panels: |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 25 | |
| 26 | Log -- raw output from the checker (-v, -q affect this) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 27 | To check -- links discovered but not yet checked |
| 28 | Checked -- links that have been checked |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 29 | Bad links -- links that failed upon checking |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 30 | Errors -- pages containing at least one bad link |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 31 | Details -- details about one URL; double click on a URL in any of |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 32 | the above list panels (not in Log) will show details |
| 33 | for that URL |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 34 | |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 35 | Use your window manager's Close command to quit. |
| 36 | |
| 37 | Command line options: |
| 38 | |
| 39 | -m bytes -- skip HTML pages larger than this size (default %(MAXPAGE)d) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 40 | -q -- quiet operation (also suppresses external links report) |
| 41 | -v -- verbose operation; repeating -v will increase verbosity |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 42 | |
| 43 | Command line arguments: |
| 44 | |
| 45 | rooturl -- URL to start checking |
| 46 | (default %(DEFROOT)s) |
| 47 | |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 48 | XXX The command line options (-m, -q, -v) should be GUI accessible. |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 49 | |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 50 | XXX The roots should be visible as a list (?). |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 51 | |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 52 | XXX The multipanel user interface is clumsy. |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 53 | |
| 54 | """ |
| 55 | |
| 56 | # ' Emacs bait |
| 57 | |
| 58 | |
| 59 | import sys |
| 60 | import getopt |
| 61 | import string |
| 62 | from Tkinter import * |
| 63 | import tktools |
| 64 | import webchecker |
| 65 | import random |
| 66 | |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 67 | # Override some for a weaker platform |
| 68 | if sys.platform == 'mac': |
| 69 | webchecker.DEFROOT = "http://grail.cnri.reston.va.us/" |
| 70 | webchecker.MAXPAGE = 50000 |
| 71 | webchecker.verbose = 4 |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 72 | |
| 73 | def main(): |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 74 | try: |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 75 | opts, args = getopt.getopt(sys.argv[1:], 'm:qv') |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 76 | except getopt.error, msg: |
| 77 | sys.stdout = sys.stderr |
| 78 | print msg |
| 79 | print __doc__%vars(webchecker) |
| 80 | sys.exit(2) |
| 81 | for o, a in opts: |
| 82 | if o == '-m': |
| 83 | webchecker.maxpage = string.atoi(a) |
| 84 | if o == '-q': |
| 85 | webchecker.verbose = 0 |
| 86 | if o == '-v': |
| 87 | webchecker.verbose = webchecker.verbose + 1 |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 88 | root = Tk(className='Webchecker') |
| 89 | root.protocol("WM_DELETE_WINDOW", root.quit) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 90 | c = CheckerWindow(root) |
| 91 | if args: |
| 92 | for arg in args[:-1]: |
| 93 | c.addroot(arg) |
| 94 | c.suggestroot(args[-1]) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 95 | root.mainloop() |
| 96 | |
| 97 | |
| 98 | class CheckerWindow(webchecker.Checker): |
| 99 | |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 100 | def __init__(self, parent, root=webchecker.DEFROOT): |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 101 | self.__parent = parent |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 102 | |
| 103 | self.__topcontrols = Frame(parent) |
| 104 | self.__topcontrols.pack(side=TOP, fill=X) |
| 105 | self.__label = Label(self.__topcontrols, text="Root URL:") |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 106 | self.__label.pack(side=LEFT) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 107 | self.__rootentry = Entry(self.__topcontrols, width=60) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 108 | self.__rootentry.pack(side=LEFT) |
| 109 | self.__rootentry.bind('<Return>', self.enterroot) |
| 110 | self.__rootentry.focus_set() |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 111 | |
| 112 | self.__controls = Frame(parent) |
| 113 | self.__controls.pack(side=TOP, fill=X) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 114 | self.__running = 0 |
| 115 | self.__start = Button(self.__controls, text="Run", command=self.start) |
| 116 | self.__start.pack(side=LEFT) |
| 117 | self.__stop = Button(self.__controls, text="Stop", command=self.stop, |
| 118 | state=DISABLED) |
| 119 | self.__stop.pack(side=LEFT) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 120 | self.__step = Button(self.__controls, text="Check one", |
| 121 | command=self.step) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 122 | self.__step.pack(side=LEFT) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 123 | self.__cv = BooleanVar() |
| 124 | self.__cv.set(1) |
| 125 | self.__checkext = Checkbutton(self.__controls, variable=self.__cv, |
| 126 | text="Check nonlocal links") |
| 127 | self.__checkext.pack(side=LEFT) |
| 128 | self.__reset = Button(self.__controls, text="Start over", command=self.reset) |
| 129 | self.__reset.pack(side=LEFT) |
| 130 | if __name__ == '__main__': # No Quit button under Grail! |
| 131 | self.__quit = Button(self.__controls, text="Quit", |
| 132 | command=self.__parent.quit) |
| 133 | self.__quit.pack(side=RIGHT) |
| 134 | |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 135 | self.__status = Label(parent, text="Status: initial", anchor=W) |
| 136 | self.__status.pack(side=TOP, fill=X) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 137 | self.__checking = Label(parent, text="Idle", anchor=W) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 138 | self.__checking.pack(side=TOP, fill=X) |
| 139 | self.__mp = mp = MultiPanel(parent) |
| 140 | sys.stdout = self.__log = LogPanel(mp, "Log") |
| 141 | self.__todo = ListPanel(mp, "To check", self.showinfo) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 142 | self.__done = ListPanel(mp, "Checked", self.showinfo) |
| 143 | self.__bad = ListPanel(mp, "Bad links", self.showinfo) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 144 | self.__errors = ListPanel(mp, "Pages w/ bad links", self.showinfo) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 145 | self.__details = LogPanel(mp, "Details") |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 146 | webchecker.Checker.__init__(self) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 147 | del self.checkext # See __getattr__ below |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 148 | if root: |
| 149 | root = string.strip(str(root)) |
| 150 | if root: |
| 151 | self.suggestroot(root) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 152 | self.newstatus() |
| 153 | |
| 154 | def __getattr__(self, name): |
| 155 | if name != 'checkext': raise AttributeError, name |
| 156 | return self.__cv.get() |
| 157 | |
| 158 | def reset(self): |
| 159 | webchecker.Checker.reset(self) |
| 160 | for p in self.__todo, self.__done, self.__bad, self.__errors: |
| 161 | p.clear() |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 162 | |
| 163 | def suggestroot(self, root): |
| 164 | self.__rootentry.delete(0, END) |
| 165 | self.__rootentry.insert(END, root) |
| 166 | self.__rootentry.select_range(0, END) |
| 167 | |
| 168 | def enterroot(self, event=None): |
| 169 | root = self.__rootentry.get() |
| 170 | root = string.strip(root) |
| 171 | if root: |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 172 | self.__checking.config(text="Adding root "+root) |
| 173 | self.__checking.update_idletasks() |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 174 | self.addroot(root) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 175 | self.__checking.config(text="Idle") |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 176 | try: |
| 177 | i = self.__todo.items.index(root) |
| 178 | except (ValueError, IndexError): |
| 179 | pass |
| 180 | else: |
| 181 | self.__todo.list.select_clear(0, END) |
| 182 | self.__todo.list.select_set(i) |
| 183 | self.__todo.list.yview(i) |
| 184 | self.__rootentry.delete(0, END) |
| 185 | |
| 186 | def start(self): |
| 187 | self.__start.config(state=DISABLED, relief=SUNKEN) |
| 188 | self.__stop.config(state=NORMAL) |
| 189 | self.__step.config(state=DISABLED) |
| 190 | self.enterroot() |
| 191 | self.__running = 1 |
| 192 | self.go() |
| 193 | |
| 194 | def stop(self): |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 195 | self.__stop.config(state=DISABLED, relief=SUNKEN) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 196 | self.__running = 0 |
| 197 | |
| 198 | def step(self): |
| 199 | self.__start.config(state=DISABLED) |
| 200 | self.__step.config(state=DISABLED, relief=SUNKEN) |
| 201 | self.enterroot() |
| 202 | self.__running = 0 |
| 203 | self.dosomething() |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 204 | |
| 205 | def go(self): |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 206 | if self.__running: |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 207 | self.__parent.after_idle(self.dosomething) |
| 208 | else: |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 209 | self.__checking.config(text="Idle") |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 210 | self.__start.config(state=NORMAL, relief=RAISED) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 211 | self.__stop.config(state=DISABLED, relief=RAISED) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 212 | self.__step.config(state=NORMAL, relief=RAISED) |
| 213 | |
| 214 | __busy = 0 |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 215 | |
| 216 | def dosomething(self): |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 217 | if self.__busy: return |
| 218 | self.__busy = 1 |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 219 | if self.todo: |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 220 | l = self.__todo.selectedindices() |
| 221 | if l: |
| 222 | i = l[0] |
| 223 | else: |
| 224 | i = 0 |
| 225 | self.__todo.list.select_set(i) |
| 226 | self.__todo.list.yview(i) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 227 | url = self.__todo.items[i] |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 228 | self.__checking.config(text="Checking "+url) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 229 | self.__parent.update() |
| 230 | self.dopage(url) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 231 | else: |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 232 | self.stop() |
| 233 | self.__busy = 0 |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 234 | self.go() |
| 235 | |
| 236 | def showinfo(self, url): |
| 237 | d = self.__details |
| 238 | d.clear() |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 239 | d.put("URL: %s\n" % url) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 240 | if self.bad.has_key(url): |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 241 | d.put("Error: %s\n" % str(self.bad[url])) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 242 | if url in self.roots: |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 243 | d.put("Note: This is a root URL\n") |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 244 | if self.done.has_key(url): |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 245 | d.put("Status: checked\n") |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 246 | o = self.done[url] |
| 247 | elif self.todo.has_key(url): |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 248 | d.put("Status: to check\n") |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 249 | o = self.todo[url] |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 250 | else: |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 251 | d.put("Status: unknown (!)\n") |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 252 | o = [] |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 253 | if self.errors.has_key(url): |
| 254 | d.put("Bad links from this page:\n") |
| 255 | for triple in self.errors[url]: |
| 256 | link, rawlink, msg = triple |
| 257 | d.put(" HREF %s" % link) |
| 258 | if link != rawlink: d.put(" (%s)" %rawlink) |
| 259 | d.put("\n") |
| 260 | d.put(" error %s\n" % str(msg)) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 261 | self.__mp.showpanel("Details") |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 262 | for source, rawlink in o: |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 263 | d.put("Origin: %s" % source) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 264 | if rawlink != url: |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 265 | d.put(" (%s)" % rawlink) |
| 266 | d.put("\n") |
Guido van Rossum | 29f6533 | 1997-05-09 03:19:29 +0000 | [diff] [blame^] | 267 | d.text.yview("1.0") |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 268 | |
| 269 | def setbad(self, url, msg): |
| 270 | webchecker.Checker.setbad(self, url, msg) |
| 271 | self.__bad.insert(url) |
| 272 | self.newstatus() |
| 273 | |
| 274 | def setgood(self, url): |
| 275 | webchecker.Checker.setgood(self, url) |
| 276 | self.__bad.remove(url) |
| 277 | self.newstatus() |
| 278 | |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 279 | def newlink(self, url, origin): |
| 280 | webchecker.Checker.newlink(self, url, origin) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 281 | if self.done.has_key(url): |
| 282 | self.__done.insert(url) |
| 283 | elif self.todo.has_key(url): |
| 284 | self.__todo.insert(url) |
| 285 | self.newstatus() |
| 286 | |
| 287 | def markdone(self, url): |
| 288 | webchecker.Checker.markdone(self, url) |
| 289 | self.__done.insert(url) |
| 290 | self.__todo.remove(url) |
| 291 | self.newstatus() |
| 292 | |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 293 | def seterror(self, url, triple): |
| 294 | webchecker.Checker.seterror(self, url, triple) |
| 295 | self.__errors.insert(url) |
| 296 | self.newstatus() |
| 297 | |
| 298 | def newstatus(self): |
| 299 | self.__status.config(text="Status: "+self.status()) |
| 300 | self.__parent.update() |
| 301 | |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 302 | |
| 303 | class ListPanel: |
| 304 | |
| 305 | def __init__(self, mp, name, showinfo=None): |
| 306 | self.mp = mp |
| 307 | self.name = name |
| 308 | self.showinfo = showinfo |
| 309 | self.panel = mp.addpanel(name) |
| 310 | self.list, self.frame = tktools.make_list_box( |
| 311 | self.panel, width=60, height=5) |
| 312 | self.list.config(exportselection=0) |
| 313 | if showinfo: |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 314 | self.list.bind('<Double-Button-1>', self.doubleclick) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 315 | self.items = [] |
| 316 | |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 317 | def clear(self): |
| 318 | self.items = [] |
| 319 | self.list.delete(0, END) |
| 320 | self.mp.hidepanel(self.name) |
| 321 | |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 322 | def doubleclick(self, event): |
| 323 | l = self.selectedindices() |
| 324 | if l: |
| 325 | self.showinfo(self.list.get(l[0])) |
| 326 | |
| 327 | def selectedindices(self): |
| 328 | l = self.list.curselection() |
| 329 | if not l: return [] |
| 330 | return map(string.atoi, l) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 331 | |
| 332 | def insert(self, url): |
| 333 | if url not in self.items: |
| 334 | if not self.items: |
| 335 | self.mp.showpanel(self.name) |
| 336 | # (I tried sorting alphabetically, but the display is too jumpy) |
| 337 | i = len(self.items) |
| 338 | self.list.insert(i, url) |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 339 | self.list.yview(i) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 340 | self.items.insert(i, url) |
| 341 | |
| 342 | def remove(self, url): |
| 343 | try: |
| 344 | i = self.items.index(url) |
| 345 | except (ValueError, IndexError): |
| 346 | pass |
| 347 | else: |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 348 | was_selected = i in self.selectedindices() |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 349 | self.list.delete(i) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 350 | del self.items[i] |
| 351 | if not self.items: |
| 352 | self.mp.hidepanel(self.name) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 353 | elif was_selected: |
| 354 | if i >= len(self.items): |
| 355 | i = len(self.items) - 1 |
| 356 | self.list.select_set(i) |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 357 | |
| 358 | |
| 359 | class LogPanel: |
| 360 | |
| 361 | def __init__(self, mp, name): |
| 362 | self.mp = mp |
| 363 | self.name = name |
| 364 | self.panel = mp.addpanel(name) |
| 365 | self.text, self.frame = tktools.make_text_box(self.panel, height=10) |
| 366 | self.text.config(wrap=NONE) |
| 367 | |
| 368 | def clear(self): |
| 369 | self.text.delete("1.0", END) |
| 370 | self.text.yview("1.0") |
| 371 | |
Guido van Rossum | af310c1 | 1997-02-02 23:30:32 +0000 | [diff] [blame] | 372 | def put(self, s): |
| 373 | self.text.insert(END, s) |
| 374 | if '\n' in s: |
| 375 | self.text.yview(END) |
| 376 | |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 377 | def write(self, s): |
| 378 | self.text.insert(END, s) |
Guido van Rossum | 4f6ecda | 1997-02-01 05:17:29 +0000 | [diff] [blame] | 379 | if '\n' in s: |
| 380 | self.text.yview(END) |
| 381 | self.panel.update() |
Guido van Rossum | 06981c3 | 1997-01-31 18:58:12 +0000 | [diff] [blame] | 382 | |
| 383 | |
| 384 | class MultiPanel: |
| 385 | |
| 386 | def __init__(self, parent): |
| 387 | self.parent = parent |
| 388 | self.frame = Frame(self.parent) |
| 389 | self.frame.pack(expand=1, fill=BOTH) |
| 390 | self.topframe = Frame(self.frame, borderwidth=2, relief=RAISED) |
| 391 | self.topframe.pack(fill=X) |
| 392 | self.botframe = Frame(self.frame) |
| 393 | self.botframe.pack(expand=1, fill=BOTH) |
| 394 | self.panelnames = [] |
| 395 | self.panels = {} |
| 396 | |
| 397 | def addpanel(self, name, on=0): |
| 398 | v = StringVar() |
| 399 | if on: |
| 400 | v.set(name) |
| 401 | else: |
| 402 | v.set("") |
| 403 | check = Checkbutton(self.topframe, text=name, |
| 404 | offvalue="", onvalue=name, variable=v, |
| 405 | command=self.checkpanel) |
| 406 | check.pack(side=LEFT) |
| 407 | panel = Frame(self.botframe) |
| 408 | label = Label(panel, text=name, borderwidth=2, relief=RAISED, anchor=W) |
| 409 | label.pack(side=TOP, fill=X) |
| 410 | t = v, check, panel |
| 411 | self.panelnames.append(name) |
| 412 | self.panels[name] = t |
| 413 | if on: |
| 414 | panel.pack(expand=1, fill=BOTH) |
| 415 | return panel |
| 416 | |
| 417 | def showpanel(self, name): |
| 418 | v, check, panel = self.panels[name] |
| 419 | v.set(name) |
| 420 | panel.pack(expand=1, fill=BOTH) |
| 421 | |
| 422 | def hidepanel(self, name): |
| 423 | v, check, panel = self.panels[name] |
| 424 | v.set("") |
| 425 | panel.pack_forget() |
| 426 | |
| 427 | def checkpanel(self): |
| 428 | for name in self.panelnames: |
| 429 | v, check, panel = self.panels[name] |
| 430 | panel.pack_forget() |
| 431 | for name in self.panelnames: |
| 432 | v, check, panel = self.panels[name] |
| 433 | if v.get(): |
| 434 | panel.pack(expand=1, fill=BOTH) |
| 435 | |
| 436 | |
| 437 | if __name__ == '__main__': |
| 438 | main() |