Search code examples
wxpythonaccessibility

wx.html2.WebView and screen readers


In wxPython 4.0 Phoenix, I'm trying to use wx.html2.WebView with a screen reader.

Whether with JAWS or NVDA, I have to make a left mouse click on the Widget to be able to see my page in an accessible Web interface.

Here is my code, but know that I'm having the same problem with the LoadURL method.

Should I add something so that the focus is directly in the web interface as soon as the Widget is displayed?

Thank you in advance for your answers.

import wx
import wx.html2

class MyWebView (wx.Dialog):

    def __init__(self, parent):
        wx.Dialog.__init__(self, parent)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.browser = wx.html2.WebView.New(self)
        self.changeBtn = wx.Button (self, -1, label="Change page")

        # The pages that I want to display disposed in a tuple.
        self.pages = (
        "<html><head><title>Hello everyone !</title></head><body><h1>We're testing wx.html2.WebView with a Screen Reader !</h1></body></html>",
        "<html><head><title>Second page !</title></head><body><h1>This is a second page</h1></body></html>",
        "<html><head><title>Third page !</title></head><body><h1>This is a third page</h1></body></html>"
        )

        self.index = 0
        self.display = self.pages[self.index]
        sizer.Add(self.browser, 1, wx.EXPAND, 10)
        sizer.Add(self.changeBtn)
        self.SetSizer(sizer)
        self.SetSize((700, 700))

    # Events.
        self.changeBtn.Bind (wx.EVT_BUTTON, self.onChangePage)

    def onChangePage (self, evt):
        self.index += 1
        if self.index == len (self.pages):
            self.index = 0
        self.display = self.pages[self.index]
        self.browser.SetPage(self.display, "")

if __name__ == '__main__':
    app = wx.App()
    dialog = MyWebView (None)
    dialog.browser.SetPage(dialog.display, "")
    dialog.Show ()
    app.MainLoop()

Kind regards.


Solution

  • Under windows, due to a strange bug with Internet explorer Server, you effectively need to click somewhere in the widget so that the screen readers can interact with it. Once that initial click has been made, screen readers perfectly work with the widget.

    Note that the same problem still occurs with the latest wxWidgets 3.1.3 in C++. In fact, nor wxWidgets, nor wxPython are responsible for this bug. It also occurs when embedding Internet Explorer Server directly using ActiveX and Win32 API.

    You can generate the required initial mouse click so that the widget becomes accessible with something like this:

    robot = wx.UIActionSimulator() 
    self.browser.SetFocus() 
    position = self.browser.GetPosition() 
    position = self.browser.ClientToScreen(position) 
    robot.MouseMove(position) 
    robot.MouseClick() 
    

    wx.UIActionSimulator is a class allowing to generate user events like a robot. Normally it is used for demos, UI automated tests, or replaying recorded macros. Here we use it to make the desired mouse click. A similar solution also works in C++.

    1. We set the focus to the widget
    2. We get its top-left position relative to the window
    3. The UIActionSimulator requires absolute screen coordinates, so we need to convert the position
    4. We move the mouse and click

    By clicking on the very top-left-most point of the widget, it's very unlikely to produce an undesired effect, while not totally impossible.