Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wx.html2.WebView fails with longer load times in 4.2.1 (regression from 4.1.1) #2510

Open
mmmmichael opened this issue Jan 10, 2024 · 3 comments

Comments

@mmmmichael
Copy link

mmmmichael commented Jan 10, 2024

Operating system: Windows 11.
wxPython version & source: 4.2.1 via pip.
Python version & source: Official Python 3.9.1 installer for Windows.

Description of the problem:

  1. Modify https://github.com/wxWidgets/Phoenix/blob/wxPython-4.2.1/samples/html2/webview_sample.py by adding an import time at the beginning and a time.sleep(120) directly before the app.MainLoop(), see code below.
  2. Run the modified version. At first the buttons are not rendered yet, because the application is hanging in the sleep as expected, see fist screenshot
  3. Wait for 120 seconds until the buttons a shown.
  4. The web view does not show the sample text "html2.WebView Sample ...", see second screenshot.
  5. For comparison: It is shown correctly with 4.1.1, see third screenshot.
Code Example (click to expand)
import sys
import os
from six import BytesIO

import wx
import wx.html2 as webview

import time

HERE = os.path.abspath(os.path.dirname(__file__))
BASE = 'file://{}/'.format(HERE.replace('\\', '/'))
URL = BASE + 'webview_sample.html'
LOGO = os.path.join(HERE, 'logo.png')

# We need to be in the HERE folder for the archive link to work since the sample
# page does not use a full path
os.chdir(HERE)

#--------------------------------------------------------------------------

class SampleFrame(wx.Frame):
    def __init__(self, parent, url=URL):
        wx.Frame.__init__(self, parent, title="html2.WebView Sample", size=(800,900))
        # add a statusbar
        self.CreateStatusBar()

        # Add a menubar with just a quit item
        mb = wx.MenuBar()
        menu = wx.Menu()
        menu.Append(wx.ID_EXIT, '&Quit')
        mb.Append(menu, "File")
        self.SetMenuBar(mb)
        self.Bind(wx.EVT_MENU, self.OnQuit, id=wx.ID_EXIT)

        # Create the main panel
        pnl = WebViewPanel(self, url)

    # Menu event handler to close the frame
    def OnQuit(self, evt):
        self.Close(force=True)

#--------------------------------------------------------------------------

class WebViewPanel(wx.Panel):
    def __init__(self, parent, url):
        wx.Panel.__init__(self, parent)

        self.current = url
        self.frame = self.GetTopLevelParent()
        self.titleBase = self.frame.GetTitle()

        sizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)

        # Ensure that a newer version of the IE/Edge control will be used
        #  on Windows
        webview.WebView.MSWSetEmulationLevel()

        # Create the webView control
        self.wv = webview.WebView.New(self)

        # Register a handler for the memory: file system
        self.wv.RegisterHandler(webview.WebViewFSHandler("memory"))

        # This handler takes care of links that are in zip files
        self.wv.RegisterHandler(webview.WebViewArchiveHandler("wxzip"))

        # And this one is for the custom: file system implemented by the
        # CustomWebViewHandler class below
        self.wv.RegisterHandler(CustomWebViewHandler())

        self.Bind(webview.EVT_WEBVIEW_NAVIGATING, self.OnWebViewNavigating, self.wv)
        self.Bind(webview.EVT_WEBVIEW_NAVIGATED, self.OnWebViewNavigated, self.wv)
        self.Bind(webview.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.wv)
        self.Bind(webview.EVT_WEBVIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv)

        btn = wx.Button(self, -1, "Open", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnOpenButton, btn)
        btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2)

        btn = wx.Button(self, -1, "◀︎", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnPrevPageButton, btn)
        btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2)
        self.Bind(wx.EVT_UPDATE_UI, self.OnCheckCanGoBack, btn)

        btn = wx.Button(self, -1, "▶︎", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnNextPageButton, btn)
        btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2)
        self.Bind(wx.EVT_UPDATE_UI, self.OnCheckCanGoForward, btn)

        btn = wx.Button(self, -1, "Stop", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnStopButton, btn)
        btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2)

        btn = wx.Button(self, -1, "Refresh", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.OnRefreshPageButton, btn)
        btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2)

        txt = wx.StaticText(self, -1, "Location:")
        btnSizer.Add(txt, 0, wx.CENTER|wx.ALL, 2)

        self.location = wx.ComboBox(
            self, -1, "", style=wx.CB_DROPDOWN|wx.TE_PROCESS_ENTER)
        self.location.AppendItems(['http://wxPython.org',
                                   'http://wxwidgets.org',
                                   'http://google.com'])


        self.Bind(wx.EVT_COMBOBOX, self.OnLocationSelect, self.location)
        self.location.Bind(wx.EVT_TEXT_ENTER, self.OnLocationEnter)
        btnSizer.Add(self.location, 1, wx.EXPAND|wx.ALL, 2)


        sizer.Add(btnSizer, 0, wx.EXPAND)
        sizer.Add(self.wv, 1, wx.EXPAND)
        self.SetSizer(sizer)

        self.wv.LoadURL(self.current)


    # WebView events
    def OnWebViewNavigating(self, evt):
        # this event happens prior to trying to get a resource
        if evt.GetURL() == 'https://www.microsoft.com/':
            if wx.MessageBox("Are you sure you want to visit Microsoft?",
                             style=wx.YES_NO|wx.ICON_QUESTION) == wx.NO:
                # This is how you can cancel loading a page.
                evt.Veto()


    def OnWebViewNavigated(self, evt):
        self.frame.SetStatusText("Loading %s..." % evt.GetURL())


    def OnWebViewLoaded(self, evt):
        # The full document has loaded
        self.current = evt.GetURL()
        self.location.SetValue(self.current)
        self.frame.SetStatusText("Loaded")


    def OnWebViewTitleChanged(self, evt):
        # Set the frame's title to include the document's title
        self.frame.SetTitle("%s -- %s" % (self.titleBase, evt.GetString()))


    # Control bar events
    def OnLocationSelect(self, evt):
        url = self.location.GetStringSelection()
        self.wv.LoadURL(url)

    def OnLocationEnter(self, evt):
        url = self.location.GetValue()
        self.location.Append(url)
        self.wv.LoadURL(url)


    def OnOpenButton(self, event):
        dlg = wx.TextEntryDialog(self, "Open Location",
                                "Enter a full URL or local path",
                                self.current, wx.OK|wx.CANCEL)
        dlg.CentreOnParent()

        if dlg.ShowModal() == wx.ID_OK:
            self.current = dlg.GetValue()
            self.wv.LoadURL(self.current)

        dlg.Destroy()

    def OnPrevPageButton(self, event):
        #for i in self.wv.GetBackwardHistory():
        #    print("%s %s" % (i.Url, i.Title))
        self.wv.GoBack()

    def OnNextPageButton(self, event):
        #for i in self.wv.GetForwardHistory():
        #    print("%s %s" % (i.Url, i.Title))
        self.wv.GoForward()

    def OnCheckCanGoBack(self, event):
        event.Enable(self.wv.CanGoBack())

    def OnCheckCanGoForward(self, event):
        event.Enable(self.wv.CanGoForward())

    def OnStopButton(self, evt):
        self.wv.Stop()

    def OnRefreshPageButton(self, evt):
        self.wv.Reload()



#--------------------------------------------------------------------------

def SetupMemoryFiles():
    # Set up an in-memory file system with virtual "files" that can be
    # loaded into the webview just like network or local file sources.
    # These "files" can be access using a protocol specifier of "memory:"
    wx.FileSystem.AddHandler(wx.MemoryFSHandler())
    wx.FileSystem.AddHandler(wx.ArchiveFSHandler())
    wx.MemoryFSHandler.AddFile(
        "page1.html",
        "<html><head><title>File System Example</title>"
        "<link rel='stylesheet' type='text/css' href='memory:test.css'>"
        "</head><body><h1>Page 1</h1>"
        "<p><img src='memory:logo.png'></p>"
        "<p>This file was loaded directly from a virtual in-memory filesystem.</p>"
        "<p>Here's another page: <a href='memory:page2.html'>Page 2</a>.</p></body>")
    wx.MemoryFSHandler.AddFile(
        "page2.html",
        "<html><head><title>File System Example</title>"
        "<link rel='stylesheet' type='text/css' href='memory:test.css'>"
        "</head><body><h1>Page 2</h1>"
        "<p><a href='memory:page1.html'>Page 1</a> was better.</p></body>")
    wx.MemoryFSHandler.AddFile(
        "test.css",
        "h1 {color: red;}")
    wx.MemoryFSHandler.AddFile('logo.png', wx.Bitmap(LOGO), wx.BITMAP_TYPE_PNG)

#--------------------------------------------------------------------------

class CustomWebViewHandler(webview.WebViewHandler):
    # This shows how to make a custom handler for providing content to the
    # WebView for a specific scheme ("custom:" in this case).  This handler
    # class simply needs to implement the GetFile method, which will be called
    # whenever a resource with a matching scheme is to be loaded, and it needs
    # to respond by returning a wx.FSFile object.  This means that, unlike the
    # wx.MemoryFSHandler, content can be generated on the fly rather than
    # needing to be preloaded into a filesystem.
    #

    def __init__(self):
        wx.html2.WebViewHandler.__init__(self, 'custom')
        self.count = 0

    def GetFile(self, uri):
        # We'll just provide the same content for every URI in this example, but
        # normally you would generate or fetch appropriate content that is
        # referenced by the given URI.
        self.count += 1
        content = """\
            <html><head><title>Custom Handler Example</title></head>
            <body><h1>Custom Handler Example</h1>
            <p>This page is provided dynamically from the CustomWebViewHandler class.</p>
            <p>It has been loaded {} times.</p>
            </body></html>""".format(self.count)
        stream = BytesIO(content.encode('utf-8'))
        fsfile = wx.FSFile(stream, uri, 'page1.html', 'text/html', wx.DateTime.Now())
        return fsfile

#--------------------------------------------------------------------------

def main():
    app = wx.App()
    SetupMemoryFiles()
    frm = SampleFrame(None)
    frm.Show()
    time.sleep(120)
    app.MainLoop()


#--------------------------------------------------------------------------

if __name__ == '__main__':
    main()

image
image
image

@swt2c
Copy link
Collaborator

swt2c commented Jan 11, 2024

I presume in point 5 that you mean 4.1.1?

I wonder if this is a drawing issue - does resizing the window cause it to work?

@mmmmichael
Copy link
Author

mmmmichael commented Jan 11, 2024

Yes, in point 5 I mean 4.1.1, not 4.2.1. I fixed it in the description.

No, resizing the window doesn't help, also minimizing and maximizing again doesn't help, see following screenshot.
Also pressing Open and entering a complete different URL like https://google.com, selecting a URL in the dropdown or pressing refresh doesn't work. It always remains the same.

Adding prints shows that the only event called from the webview is EVT_UPDATE_UI. But no EVT_WEBVIEW_NAVIGATING, EVT_WEBVIEW_NAVIGATED, EVT_WEBVIEW_LOADED or EVT_WEBVIEW_TITLE_CHANGED.

image

image

@Metallicow
Copy link
Contributor

If a button has not shown up in 42 seconds then disreguard all the awesomeness, of Al Gore not winning the Internectcy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants