14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm#! /usr/bin/env python
24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""GUI interface to webchecker.
44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmThis works as a Grail applet too!  E.g.
64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm  <APPLET CODE=wcgui.py NAME=CheckerWindow></APPLET>
84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCheckpoints are not (yet???  ever???) supported.
104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmUser interface:
124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmEnter a root to check in the text entry box.  To enter more than one root,
144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmenter them one at a time and press <Return> for each one.
154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCommand buttons Start, Stop and "Check one" govern the checking process in
174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmthe obvious way.  Start and "Check one" also enter the root from the text
184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmentry box if one is present.  There's also a check box (enabled by default)
194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmto decide whether actually to follow external links (since this can slow
204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmthe checking down considerably).  Finally there's a Quit button.
214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmA series of checkbuttons determines whether the corresponding output panel
234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmis shown.  List panels are also automatically shown or hidden when their
244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmstatus changes between empty to non-empty.  There are six panels:
254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmLog        -- raw output from the checker (-v, -q affect this)
274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmTo check   -- links discovered but not yet checked
284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmChecked    -- links that have been checked
294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmBad links  -- links that failed upon checking
304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmErrors     -- pages containing at least one bad link
314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmDetails    -- details about one URL; double click on a URL in any of
324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm              the above list panels (not in Log) will show details
334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm              for that URL
344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmUse your window manager's Close command to quit.
364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCommand line options:
384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm-m bytes  -- skip HTML pages larger than this size (default %(MAXPAGE)d)
404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm-q        -- quiet operation (also suppresses external links report)
414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm-v        -- verbose operation; repeating -v will increase verbosity
424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm-t root   -- specify root dir which should be treated as internal (can repeat)
434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm-a        -- don't check name anchors
444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCommand line arguments:
464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmrooturl   -- URL to start checking
484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             (default %(DEFROOT)s)
494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmXXX The command line options (-m, -q, -v) should be GUI accessible.
514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmXXX The roots should be visible as a list (?).
534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmXXX The multipanel user interface is clumsy.
554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""
574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# ' Emacs bait
594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport sys
624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport getopt
634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmfrom Tkinter import *
644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport tktools
654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport webchecker
664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef main():
684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    try:
694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        opts, args = getopt.getopt(sys.argv[1:], 't:m:qva')
704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    except getopt.error, msg:
714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        sys.stdout = sys.stderr
724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        print msg
734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        print __doc__%vars(webchecker)
744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        sys.exit(2)
754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    webchecker.verbose = webchecker.VERBOSE
764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    webchecker.nonames = webchecker.NONAMES
774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    webchecker.maxpage = webchecker.MAXPAGE
784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    extra_roots = []
794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    for o, a in opts:
804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if o == '-m':
814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            webchecker.maxpage = int(a)
824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if o == '-q':
834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            webchecker.verbose = 0
844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if o == '-v':
854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            webchecker.verbose = webchecker.verbose + 1
864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if o == '-t':
874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            extra_roots.append(a)
884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if o == '-a':
894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            webchecker.nonames = not webchecker.nonames
904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    root = Tk(className='Webchecker')
914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    root.protocol("WM_DELETE_WINDOW", root.quit)
924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    c = CheckerWindow(root)
934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    c.setflags(verbose=webchecker.verbose, maxpage=webchecker.maxpage,
944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm               nonames=webchecker.nonames)
954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if args:
964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for arg in args[:-1]:
974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            c.addroot(arg)
984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        c.suggestroot(args[-1])
994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Usually conditioned on whether external links
1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # will be checked, but since that's not a command
1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # line option, just toss them in.
1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    for url_root in extra_roots:
1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # Make sure it's terminated by a slash,
1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # so that addroot doesn't discard the last
1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # directory component.
1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if url_root[-1] != "/":
1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            url_root = url_root + "/"
1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        c.addroot(url_root, add_to_do = 0)
1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    root.mainloop()
1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass CheckerWindow(webchecker.Checker):
1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, parent, root=webchecker.DEFROOT):
1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__parent = parent
1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__topcontrols = Frame(parent)
1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__topcontrols.pack(side=TOP, fill=X)
1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__label = Label(self.__topcontrols, text="Root URL:")
1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__label.pack(side=LEFT)
1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__rootentry = Entry(self.__topcontrols, width=60)
1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__rootentry.pack(side=LEFT)
1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__rootentry.bind('<Return>', self.enterroot)
1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__rootentry.focus_set()
1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__controls = Frame(parent)
1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__controls.pack(side=TOP, fill=X)
1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__running = 0
1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__start = Button(self.__controls, text="Run", command=self.start)
1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__start.pack(side=LEFT)
1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__stop = Button(self.__controls, text="Stop", command=self.stop,
1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                             state=DISABLED)
1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__stop.pack(side=LEFT)
1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__step = Button(self.__controls, text="Check one",
1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                             command=self.step)
1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__step.pack(side=LEFT)
1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__cv = BooleanVar(parent)
1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__cv.set(self.checkext)
1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__checkext = Checkbutton(self.__controls, variable=self.__cv,
1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                                      command=self.update_checkext,
1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                                      text="Check nonlocal links",)
1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__checkext.pack(side=LEFT)
1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__reset = Button(self.__controls, text="Start over", command=self.reset)
1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__reset.pack(side=LEFT)
1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if __name__ == '__main__': # No Quit button under Grail!
1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__quit = Button(self.__controls, text="Quit",
1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                                 command=self.__parent.quit)
1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__quit.pack(side=RIGHT)
1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__status = Label(parent, text="Status: initial", anchor=W)
1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__status.pack(side=TOP, fill=X)
1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__checking = Label(parent, text="Idle", anchor=W)
1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__checking.pack(side=TOP, fill=X)
1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__mp = mp = MultiPanel(parent)
1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        sys.stdout = self.__log = LogPanel(mp, "Log")
1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__todo = ListPanel(mp, "To check", self, self.showinfo)
1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__done = ListPanel(mp, "Checked", self, self.showinfo)
1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__bad = ListPanel(mp, "Bad links", self, self.showinfo)
1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__errors = ListPanel(mp, "Pages w/ bad links", self, self.showinfo)
1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__details = LogPanel(mp, "Details")
1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.root_seed = None
1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        webchecker.Checker.__init__(self)
1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if root:
1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            root = str(root).strip()
1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if root:
1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.suggestroot(root)
1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.newstatus()
1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def reset(self):
1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        webchecker.Checker.reset(self)
1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for p in self.__todo, self.__done, self.__bad, self.__errors:
1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            p.clear()
1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.root_seed:
1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.suggestroot(self.root_seed)
1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def suggestroot(self, root):
1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__rootentry.delete(0, END)
1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__rootentry.insert(END, root)
1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__rootentry.select_range(0, END)
1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.root_seed = root
1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def enterroot(self, event=None):
1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        root = self.__rootentry.get()
1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        root = root.strip()
1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if root:
1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__checking.config(text="Adding root "+root)
1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__checking.update_idletasks()
1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.addroot(root)
1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__checking.config(text="Idle")
1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                i = self.__todo.items.index(root)
1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            except (ValueError, IndexError):
1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                pass
1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            else:
1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__todo.list.select_clear(0, END)
1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__todo.list.select_set(i)
1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__todo.list.yview(i)
1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__rootentry.delete(0, END)
1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def start(self):
2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__start.config(state=DISABLED, relief=SUNKEN)
2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__stop.config(state=NORMAL)
2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__step.config(state=DISABLED)
2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.enterroot()
2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__running = 1
2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.go()
2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def stop(self):
2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__stop.config(state=DISABLED, relief=SUNKEN)
2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__running = 0
2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def step(self):
2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__start.config(state=DISABLED)
2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__step.config(state=DISABLED, relief=SUNKEN)
2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.enterroot()
2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__running = 0
2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.dosomething()
2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def go(self):
2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.__running:
2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__parent.after_idle(self.dosomething)
2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__checking.config(text="Idle")
2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__start.config(state=NORMAL, relief=RAISED)
2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__stop.config(state=DISABLED, relief=RAISED)
2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__step.config(state=NORMAL, relief=RAISED)
2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    __busy = 0
2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def dosomething(self):
2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.__busy: return
2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__busy = 1
2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.todo:
2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            l = self.__todo.selectedindices()
2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if l:
2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                i = l[0]
2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            else:
2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                i = 0
2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.__todo.list.select_set(i)
2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__todo.list.yview(i)
2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            url = self.__todo.items[i]
2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__checking.config(text="Checking "+self.format_url(url))
2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__parent.update()
2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.dopage(url)
2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.stop()
2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__busy = 0
2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.go()
2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def showinfo(self, url):
2514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        d = self.__details
2524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        d.clear()
2534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        d.put("URL:    %s\n" % self.format_url(url))
2544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.bad.has_key(url):
2554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            d.put("Error:  %s\n" % str(self.bad[url]))
2564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if url in self.roots:
2574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            d.put("Note:   This is a root URL\n")
2584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.done.has_key(url):
2594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            d.put("Status: checked\n")
2604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            o = self.done[url]
2614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif self.todo.has_key(url):
2624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            d.put("Status: to check\n")
2634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            o = self.todo[url]
2644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
2654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            d.put("Status: unknown (!)\n")
2664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            o = []
2674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if (not url[1]) and self.errors.has_key(url[0]):
2684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            d.put("Bad links from this page:\n")
2694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            for triple in self.errors[url[0]]:
2704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                link, rawlink, msg = triple
2714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                d.put("  HREF  %s" % self.format_url(link))
2724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if self.format_url(link) != rawlink: d.put(" (%s)" %rawlink)
2734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                d.put("\n")
2744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                d.put("  error %s\n" % str(msg))
2754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__mp.showpanel("Details")
2764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for source, rawlink in o:
2774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            d.put("Origin: %s" % source)
2784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if rawlink != self.format_url(url):
2794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                d.put(" (%s)" % rawlink)
2804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            d.put("\n")
2814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        d.text.yview("1.0")
2824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def setbad(self, url, msg):
2844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        webchecker.Checker.setbad(self, url, msg)
2854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__bad.insert(url)
2864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.newstatus()
2874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def setgood(self, url):
2894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        webchecker.Checker.setgood(self, url)
2904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__bad.remove(url)
2914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.newstatus()
2924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def newlink(self, url, origin):
2944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        webchecker.Checker.newlink(self, url, origin)
2954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.done.has_key(url):
2964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__done.insert(url)
2974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif self.todo.has_key(url):
2984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.__todo.insert(url)
2994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.newstatus()
3004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def markdone(self, url):
3024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        webchecker.Checker.markdone(self, url)
3034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__done.insert(url)
3044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__todo.remove(url)
3054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.newstatus()
3064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def seterror(self, url, triple):
3084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        webchecker.Checker.seterror(self, url, triple)
3094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__errors.insert((url, ''))
3104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.newstatus()
3114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def newstatus(self):
3134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__status.config(text="Status: "+self.status())
3144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.__parent.update()
3154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def update_checkext(self):
3174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.checkext = self.__cv.get()
3184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass ListPanel:
3214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, mp, name, checker, showinfo=None):
3234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.mp = mp
3244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.name = name
3254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.showinfo = showinfo
3264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.checker = checker
3274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.panel = mp.addpanel(name)
3284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.list, self.frame = tktools.make_list_box(
3294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.panel, width=60, height=5)
3304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.list.config(exportselection=0)
3314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if showinfo:
3324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.list.bind('<Double-Button-1>', self.doubleclick)
3334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.items = []
3344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def clear(self):
3364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.items = []
3374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.list.delete(0, END)
3384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.mp.hidepanel(self.name)
3394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def doubleclick(self, event):
3414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        l = self.selectedindices()
3424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if l:
3434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.showinfo(self.items[l[0]])
3444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def selectedindices(self):
3464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        l = self.list.curselection()
3474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not l: return []
3484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return map(int, l)
3494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def insert(self, url):
3514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if url not in self.items:
3524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not self.items:
3534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.mp.showpanel(self.name)
3544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # (I tried sorting alphabetically, but the display is too jumpy)
3554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            i = len(self.items)
3564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.list.insert(i, self.checker.format_url(url))
3574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.list.yview(i)
3584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.items.insert(i, url)
3594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def remove(self, url):
3614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
3624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            i = self.items.index(url)
3634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except (ValueError, IndexError):
3644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            pass
3654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
3664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            was_selected = i in self.selectedindices()
3674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.list.delete(i)
3684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            del self.items[i]
3694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not self.items:
3704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.mp.hidepanel(self.name)
3714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            elif was_selected:
3724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                if i >= len(self.items):
3734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    i = len(self.items) - 1
3744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.list.select_set(i)
3754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass LogPanel:
3784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, mp, name):
3804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.mp = mp
3814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.name = name
3824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.panel = mp.addpanel(name)
3834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.text, self.frame = tktools.make_text_box(self.panel, height=10)
3844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.text.config(wrap=NONE)
3854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def clear(self):
3874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.text.delete("1.0", END)
3884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.text.yview("1.0")
3894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def put(self, s):
3914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.text.insert(END, s)
3924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if '\n' in s:
3934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.text.yview(END)
3944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def write(self, s):
3964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.text.insert(END, s)
3974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if '\n' in s:
3984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.text.yview(END)
3994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.panel.update()
4004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass MultiPanel:
4034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, parent):
4054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.parent = parent
4064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.frame = Frame(self.parent)
4074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.frame.pack(expand=1, fill=BOTH)
4084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.topframe = Frame(self.frame, borderwidth=2, relief=RAISED)
4094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.topframe.pack(fill=X)
4104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.botframe = Frame(self.frame)
4114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.botframe.pack(expand=1, fill=BOTH)
4124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.panelnames = []
4134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.panels = {}
4144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def addpanel(self, name, on=0):
4164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        v = StringVar(self.parent)
4174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if on:
4184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            v.set(name)
4194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
4204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            v.set("")
4214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        check = Checkbutton(self.topframe, text=name,
4224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                            offvalue="", onvalue=name, variable=v,
4234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                            command=self.checkpanel)
4244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        check.pack(side=LEFT)
4254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        panel = Frame(self.botframe)
4264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        label = Label(panel, text=name, borderwidth=2, relief=RAISED, anchor=W)
4274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        label.pack(side=TOP, fill=X)
4284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        t = v, check, panel
4294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.panelnames.append(name)
4304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.panels[name] = t
4314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if on:
4324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            panel.pack(expand=1, fill=BOTH)
4334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return panel
4344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def showpanel(self, name):
4364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        v, check, panel = self.panels[name]
4374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        v.set(name)
4384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        panel.pack(expand=1, fill=BOTH)
4394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def hidepanel(self, name):
4414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        v, check, panel = self.panels[name]
4424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        v.set("")
4434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        panel.pack_forget()
4444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def checkpanel(self):
4464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for name in self.panelnames:
4474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            v, check, panel = self.panels[name]
4484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            panel.pack_forget()
4494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for name in self.panelnames:
4504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            v, check, panel = self.panels[name]
4514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if v.get():
4524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                panel.pack(expand=1, fill=BOTH)
4534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
4554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmif __name__ == '__main__':
4564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    main()
457