wxPython WebView: how to reload on error?

2019-07-27 22:40发布

问题:

I'm using wx.html2.WebView to load a Website in a Dialog, which is working fine.

Problem: If the Website can't be reached for any reason, the output will be Could not connect: Connection refused.

Desired behaviour: Try to reload the URL after x seconds on every fail.

import wx 
import wx.html2 
import time

URL = "http://mydomain.tld"

class MyBrowser(wx.Frame): 
  def __init__(self, *args, **kwds): 
    wx.Frame.__init__(self, *args, **kwds) 
    self.browser = wx.html2.WebView.New(self) 
    self.browser.Bind(wx.html2.EVT_WEBVIEW_ERROR, self.on_webview_error)

  def on_webview_error(self, evt):
    # Printing works
    print("Error: can't load page, try again in 3 seconds.")
    # Sleeping works
    time.sleep(3)
    # Reloading doesn't work
    self.browser.LoadURL(URL) # OR self.browser.Reload()
    # Weird: Error is rendered now


if __name__ == '__main__': 
  app = wx.App() 
  dialog = MyBrowser(None, -1) 
  dialog.browser.LoadURL(URL) 
  dialog.Show() 
  app.MainLoop() 

The problem occurs in on_webview_error(self, evt). My guess is that I'm using the function not correctly, especially because the error message is rendered after the reloading.

Any idea?
Thanks in advance!

回答1:

There is something strange going on there!
The only way that I could force it to work was to redefine the WebView each time. I may be being over zealous with the Destroy each time as well.
This works but may not necessarily be exactly what you are after.

import wx
import wx.html2
import time

URL = "http://mydomain.tld"

class MyBrowser(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)
        self.url = URL
        self.browser = wx.html2.WebView.New(self, -1, size=(900,600))
        self.browser.Bind(wx.html2.EVT_WEBVIEW_ERROR, self.on_webview_error)
        self.browser.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.on_webview_load)
        self.retries = 0
        self.max_retries = 10

    def on_webview_error(self, evt):
        self.URL = evt.GetURL()
        print(self.URL)
        self.retries += 1
        if self.retries > self.max_retries: # Give up
            self.Destroy()
        print("Error {} of {} attempts to load {}, trying again in 3 seconds.".format(self.retries,self.max_retries,self.URL))
        if self.retries > 5: # Try alternate
            self.URL = "http://wxPython.org"
            print("Swapping to alternate Url "+self.URL)
        self.browser.Destroy()

        time.sleep(3)

        self.browser = wx.html2.WebView.New(self, -1, size=(900,600))
        self.browser.Bind(wx.html2.EVT_WEBVIEW_ERROR, self.on_webview_error)
        self.browser.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.on_webview_load)
        self.browser.LoadURL(self.URL)

    def on_webview_load(self, evt):
        print(self.URL, "Load complete")

if __name__ == '__main__':
  app = wx.App()
  dialog = MyBrowser(None, -1)
  dialog.browser.LoadURL(URL)
  dialog.Show()
  app.MainLoop()


回答2:

In wxPython you always have a main thread that is the one responsible for drawing and updating the GUI. In your MWE the line time.sleep(3) allows you to wait for 3 seconds before trying to reload the page again. However, this has the side effect of sending the main thread of the programm to sleep before being able to update the GUI and show the error message. If you move the time.sleep(3) line to a different thread the GUI may be updated without any problem. Here is a solution:

import wx 
import wx.html2 
import time
import _thread
from pubsub import pub

URL = "https://mydomain.tld"

class MyBrowser(wx.Frame): 
    def __init__(self, *args, **kwds): 
        wx.Frame.__init__(self, *args, **kwds) 
        self.browser = wx.html2.WebView.New(self) 
        self.browser.Bind(wx.html2.EVT_WEBVIEW_ERROR, self.on_webview_error)
        self.counter = 0
        pub.subscribe(self.loadwww, 'Try Again')
        pub.subscribe(self.loadalt, 'Give UP')

    def loadwww(self):
        self.browser.LoadURL("https://mydomain.tld")

    def loadalt(self):
        self.browser.LoadURL("https://www.google.com")

    def on_webview_error(self, evt):
        self.counter += 1
        _thread.start_new_thread(self.wait, (3,))   

    def wait(self, sec):
        if self.counter <= 5:
            print(self.counter)
            print("Error: can't load page, try again in 3 seconds.")
            time.sleep(sec)
            wx.CallAfter(pub.sendMessage, 'Try Again')
        else:
            wx.CallAfter(pub.sendMessage, 'Give UP')    

if __name__ == '__main__': 
    app = wx.App() 
    dialog = MyBrowser(None, -1) 
    dialog.browser.LoadURL(URL) 
    dialog.Show() 
    app.MainLoop()