=== modified file 'examples/pykhtmlsite.py' --- examples/pykhtmlsite.py 2007-02-05 22:39:48 +0000 +++ examples/pykhtmlsite.py 2007-02-11 15:51:11 +0000 @@ -9,24 +9,32 @@ PyKHTMLUrl = "http://localhost/PyKHTML/" -browser = pykhtml.Browser() - -def extractBitsFromPage(): +def extractBitsFromPage(browser): # getElementsByTagName returns a generator, so we convert # to a list and access the first element title = list(browser.document.getElementsByTagName("title"))[0] print "Title:", title.text + # Get the navigation navigation = [] for item in browser.document.getElementById("navigation").children: - # if this child item is an element and its tag name - # is 'li' (i.e if it's a list item) + # if this child item is an element (as opposed to + # a text node or whatever) and its tag name is 'li' + # (i.e if it's a list item) if isinstance(item, pykhtml.dom.Element) and item.tagName == "li": - navigation.append(item.children[0].text) + # Add the text contents of the list item's first + # child to our list + anchor = item.children[0] + navigation.append(anchor.text) print "Navigation:", " | ".join(navigation) + # Stop here, we're done pykhtml.stopEventLoop() def main(): + browser = pykhtml.Browser() + # the browser is passed as a parameter to extractBitsFromPage + # when it is called (when the page has loaded) browser.load(PyKHTMLUrl, extractBitsFromPage) + # kick things off pykhtml.startEventLoop() === modified file 'pykhtml/__init__.py' --- pykhtml/__init__.py 2007-02-10 20:22:22 +0000 +++ pykhtml/__init__.py 2007-02-11 15:51:11 +0000 @@ -9,9 +9,26 @@ import dom -### set to true to see what's happening visually +# set to true to see what's happening visually debugWithGUI = False +class curry: + """ Partial application of parameters. This is used internally but is also very useful with [[Browser.load]] as it allows you to pass data to other functions. + EXAMPLE. """ + def __init__(self, fun, *args, **kwargs): + self.fun = fun + self.pending = args[:] + self.kwargs = kwargs.copy() + + def __call__(self, *args, **kwargs): + if kwargs and self.kwargs: + kw = self.kwargs.copy() + kw.update(kwargs) + else: + kw = kwargs or self.kwargs + + return self.fun(*(self.pending + args), **kw) + class _Dummy(object): pass @@ -46,7 +63,6 @@ xvfb.display = data - path = os.environ["PATH"].split(os.pathsep) def pathSearch(name): """ Utility function to search for and get the full path of a file in $PATH """ @@ -105,10 +121,9 @@ #sys.exit(0) def timer(time, func): - """ Call the given function after the alloted time. Requires that the PyKHTML event loop is running """ + """ Call the given function after the alloted time. The PyKHTML event loop needs to be running """ qt.QTimer.singleShot(int(time * 1000), func) - class Browser(object): """ A Browser is the main class you use to navigate around and visit different pages. Have a look at Browser.load and Browser.document to access basic use. """ def __init__(self): @@ -122,18 +137,26 @@ self.part.setJavaEnabled(False) self.part.setPluginsEnabled(False) self.part.setAutoloadImages(False) - self.connect = self.part.connect # sore finger remedy + # sore finger remedy: + self.connect = self.part.connect self.disconnect = self.part.disconnect self.loadFunction = None + self._passReferenceToCallbacks = True + + def _setPassReferenceToCallbacks(self, b): + self._referencelessCallbacks = b + def _getPassReferenceToCallbacks(self): + return self._referencelessCallbacks + passReferenceToCallbacks = property(_getPassReferenceToCallbacks, _setPassReferenceToCallbacks, None, "Set whether callbacks passed to functions such as [[Browser.load]] or [[dom.Document.visit]] will have a reference to this browser object passed as a parameter. Default is True") def _setLocation(self, uri): self.part.openURL(kdecore.KURL(uri)) def _getLocation(self): return str(self.part.url().url()) - location = property(_getLocation, _setLocation, None, "Browse to a new location. You probably don't want to set this directly as you'll receive no notification when the page has loaded. Have a look at Browser.load instead") + location = property(_getLocation, _setLocation, None, "Browse to a new location. You probably don't want to set this directly as you'll receive no notification when the page has loaded. Have a look at [[Browser.load]] instead") def load(self, uri, callback): - """ Load a webpage in the browser. It takes as parameters the URI of the page to load, and a callable object to call when the page has loaded. """ + """ Load a webpage in the browser. It takes as parameters the URI of the page to load, and a callable object to call when the page has loaded. This callback will be given the browser object as a reference unless you set [[Browser.referencelessCallbacks]] to True """ if self.loadFunction: self.disconnect(self.part, qt.SIGNAL("docCreated()"). self._slotDocCreated) self.loadFunction = callback @@ -147,9 +170,12 @@ raise AttributeError("No load function callback present") func = self.loadFunction self.loadFunction = None - # do this so the DOM loads fully. Huzzah - doc = self.document - dom.Element(doc._d).addEvent("load", func) + # If _passReferenceToCallbacks, bind this browser to the function + if self._passReferenceToCallbacks: + func = curry(func, self) + # do this so the DOM loads fully. Cast to an Element -- not strictly correct, but we just want to get to addEvent. + # XX why not just put addEvent in Node and make Document inherit from Node? Document IS meant to be a Node, after all. + docElement = dom.Element(self.document._d).addEvent("load", func) @property def source(self):