Introduction

The facility called MakeActiveXClass, for creating windows that contain ActiveX objects such as Internet Explorer (IE) and Acrobat Reader makes many apps a joy to build.

In the case of IE, though, the structure and contents of a page cannot be examined until IE signals that it has completely finished displaying the page.

This recipe shows one way in which execution of code that depends on what is in the document being loaded can be suspended until IE indicates that the document is fully available.

What Objects are Involved

Principal wxPython items used: wxFrame and MakeActiveXClass. Principal Python classes: Event and Thread

Process Overview

IE can be made to signal its events simply by adding functions to the class in which MakeActiveXClass is used to instantiate IE, with the appropriate names. For example, in the sample code below, one sees a function called 'OnDocumentComplete' which is called by IE when it has entirely finished loading a document.

It might seem, at this point, that one could arrange to access the loaded document from within OnDocumentComplete. However, there are two problems with this approach:

1. For some reason, exceptions generated within OnDocumentComplete are not processed. Furthermore, this behaviour is 'inherited' by any routines that OnDocumentComplete calls. This loss of exception handling makes debugging much, much more time-consuming.

2. One can arrange to make a routine that examines IE's document wait for a signal from OnDocumentComplete. However, if this routine runs at the end of the wxFrame derivative's init a deadlock occurs because IE cannot finish loading the document without wxPython's having displayed the ActiveX window containing IE.

This recipe displays a way of dealing with these problems. The routine which is to process the IE document is run in a separate thread which waits for a signal from OnDocumentComplete before proceeding, if the document is not available when the process begins.

Here is output from the script.

>pythonw -u interviews.py
... inside doSomethingWithIE
    creating event inside doSomethingWithIE
    waiting inside doSomethingWithIE
... inside OnDocumentComplete
... obtaining an item from IE document
    event set inside OnDocumentComplete
    item obtained: Name
Exception in thread Thread-1:
Traceback (most recent call last):
  File "J:\Python22\lib\threading.py", line 408, in __bootstrap
    self.run()
  File "J:\Python22\lib\threading.py", line 396, in run
    apply(self.__target, self.__args, self.__kwargs)
  File "interviews.py", line 48, in doSomethingWithIE
    raise Exception, "... inside doSomethingWithIE"
Exception: ... inside doSomethingWithIE

>Exit code: 0

Notice particularly that the exception that is deliberately raised within OnDocumentComplete does "not" appear in the output but that the one raised within doSomethingWithIE does. (Big help in debugging, not that I don't thrill to opportunities to find obscure bugs.)

Finally, notice that doSomethingWithIE is indeed able to access IE's document successfully, as evidenced by the line in the output, " item obtained: Name".

When doSomethingWithIE runs it tests whether IE has finished its loading and, if it has not, doSomethingWithIE waits on an event that will be set by OnDocumentComplete when it is called.

Special Concerns

Special care should be used in writing OnDocumentComplete given that exceptions may not be handled properly. It might be best just to keep this routine as small and uncomplicated as possible.

The one aspect of this code that worries me a little is that there is no protection against the situation in which 'self . whenDocComplete', which refers to an Event, might be recreated whilst the other thread is using it.

Code Sample

   1 from wxPython.wx import *
   2 from win32com.client import Dispatch
   3 from win32com.client.gencache import EnsureModule 
   4 from wxPython.lib.activexwrapper import MakeActiveXClass
   5 from threading import Event, Thread
   6 from os import getcwd
   7 
   8 pageFilename="%s\\%s" %(getcwd(), "justTable.htm",)
   9 
  10 class InterviewFrame(wxFrame):
  11     def __init__(self, parent, id, title):
  12         wxFrame.__init__(self, parent, id, title)
  13 
  14         browserModule=EnsureModule("{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 0, 1, 1)
  15         theClass=MakeActiveXClass(browserModule.WebBrowser, eventObj=self)
  16         self.ie=theClass(self, -1)
  17 
  18         self.SetAutoLayout(true)
  19 
  20         lc=wxLayoutConstraints()
  21         lc.right.SameAs(self , wxRight)
  22         lc.left.SameAs(self, wxLeft)
  23         lc.top.SameAs(self, wxTop)
  24         lc.bottom.SameAs(self, wxBottom)
  25         self.ie.SetConstraints(lc)
  26         
  27         self.whenDocComplete=None        
  28         
  29         self.ie.Navigate("file://%s" % pageFilename)
  30         
  31         Thread(target=self.doSomethingWithIE).start()
  32         
  33     def OnDocumentComplete(self, *others):
  34         print "... inside OnDocumentComplete"
  35         self.whenDocComplete.set()
  36         print "    event set inside OnDocumentComplete"
  37         raise Exception, "... inside OnDocumentComplete"
  38 
  39     def doSomethingWithIE(self):
  40         print "... inside doSomethingWithIE"
  41         if self.ie.ReadyState!=4:
  42             print "    creating event inside doSomethingWithIE"
  43             self.whenDocComplete=Event()
  44             print "    waiting inside doSomethingWithIE"
  45             self.whenDocComplete.wait()
  46         print "... obtaining an item from IE document"
  47         print "    item obtained: %s" % self.ie.Document.getElementsByTagName("input") [ 0 ].name 
  48         raise Exception, "... inside doSomethingWithIE"
  49 
  50 class InterviewApp(wxApp):
  51     def OnInit(self):
  52         frame=InterviewFrame(NULL, -1, 'Handling IE Events')
  53         frame.Show(true)
  54         self.SetTopWindow(frame)
  55         return true
  56 
  57 if __name__=="__main__":
  58     app=InterviewApp(0)
  59     app.MainLoop()

Comments

SynchronisingToInternetExplorer (last edited 2012-05-25 19:01:36 by 216)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.