MS Edge backend fails in Notebook (other than first page)

Hi Robin,

I’m still a new user apparently, so I had to create a new topic. I have some example code illustrating my issue described in this topic.

It turns out the issue happens when using Edge in Notebook pages other than the first. In my code below, if you chagne the backend to IE, it works fine. I’ve also tried combinations where there is only one webview, and that fails with Edge if it is not in the first Notebook page.

  • Windows7
  • Edge Version 87.0.664.30 (Official build) beta (64-bit)
  • wxPython4.1.1a1.dev5044+6959fd7f-cp37-cp37m-win32.whl

Unfotunately, I don’t have a Windows 10 machine to try it on now. But this strikes me as unrelated to the OS?

import wx
import wx.html2 as webview


class DVHAMainFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)

        self.notebook_main_view = wx.Notebook(self, wx.ID_ANY)
        self.tab_keys = ['Browser 1', 'Browser 2']

        self.notebook_tab = {key: wx.Panel(self.notebook_main_view, wx.ID_ANY) for key in self.tab_keys}
        self.plot = {key: webview.WebView.New(self.notebook_tab[key], backend=webview.WebViewBackendEdge) for key in self.tab_keys}
        for plot in self.plot.values():
            plot.LoadURL("http://www.google.com")

        notebook_sizers = {key: wx.BoxSizer(wx.VERTICAL) for key in self.tab_keys}
        for key, notebook_tab in self.notebook_tab.items():
            notebook_sizers[key].Add(self.plot[key], 1, wx.EXPAND, 0)
            notebook_tab.SetSizer(notebook_sizers[key])
            self.notebook_main_view.AddPage(self.notebook_tab[key], key)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.notebook_main_view, 1, wx.EXPAND, 0)

        self.SetSizer(sizer)
        self.Layout()
        self.Center()


class MainApp(wx.App):
    def OnInit(self):
        self.SetAppName('DVH Analytics')
        self.frame = DVHAMainFrame(None, wx.ID_ANY, "")
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True


def start():
    app = MainApp()
    app.MainLoop()


if __name__ == "__main__":
    start()

I was able to replicate the issue on Windows 10. The reason I suspected it to be a OS-specific issue is that since it’s a MS component, and since Win7 has moved into the end-of-support stage, there’s a (slim) chance that there is some API that isn’t able to be implemented on Win7, or some issue like that.

However I do see it on Win10 so that rules out that idea. It is definitely weird. For me it varies somewhat which tab is successfully loaded and which is not. That made me think that perhaps there is some sort of timing issue due to creating all the webviews at the same time. I experimented a little and found that delaying the creation of the notebook pages a little bit, and only doing one at a time, solves the problem. One possibility is that at least part of the Edge component’s creation is asynchronous and is done in follow up events, so trying to make a new one immediately after the first WebView.New returns is messing things up internally.

Here is my tweaked version of your sample:

import wx
import wx.html2 as webview
from wx.lib.inspection import InspectionTool
from wx.lib.agw.flatnotebook import FlatNotebook

NotebookClass = wx.Notebook
# NotebookClass = FlatNotebook
# BACKEND = webview.WebViewBackendDefault
BACKEND = webview.WebViewBackendEdge


class DVHAMainFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)

        self.notebook_main_view = NotebookClass(self, wx.ID_ANY)
        self.tab_keys = ['Browser 1', 'Browser 2', 'Browser 3']

        delay = 0
        for key in self.tab_keys:
            wx.CallLater(delay, self.make_page, key)
            delay += 50

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.notebook_main_view, 1, wx.EXPAND, 0)

        self.SetSizer(sizer)
        self.Layout()
        self.Center()

    def make_page(self, title):
        page = wx.Panel(self.notebook_main_view)
        plot = webview.WebView.New(page, backend=BACKEND)
        plot.LoadURL("http://www.google.com")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(plot, 1, wx.EXPAND)
        page.SetSizer(sizer)
        self.notebook_main_view.AddPage(page, title)


class MainApp(wx.App):
    def OnInit(self):
        self.SetAppName('DVH Analytics')
        self.frame = DVHAMainFrame(None, wx.ID_ANY, "", size=(800,600))
        self.SetTopWindow(self.frame)
        # InspectionTool().Show()
        self.frame.Show()
        return True


def start():
    app = MainApp()
    app.MainLoop()


if __name__ == "__main__":
    start()
1 Like

Thanks for being so quick!

Unfortunately, I can make it fail with only one browser.

import wx
import wx.html2 as webview
# from wx.lib.inspection import InspectionTool
# from wx.lib.agw.flatnotebook import FlatNotebook

NotebookClass = wx.Notebook
# NotebookClass = FlatNotebook
# BACKEND = webview.WebViewBackendDefault
BACKEND = webview.WebViewBackendEdge


class DVHAMainFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)

        self.notebook_main_view = NotebookClass(self, wx.ID_ANY)
        self.tab_keys = ['Browser 1', 'Browser 2', 'Browser 3']

        for key in self.tab_keys:
            func = self.make_web_page if key == self.tab_keys[-1] else self.make_text_page
            func(key)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.notebook_main_view, 1, wx.EXPAND, 0)

        self.SetSizer(sizer)
        self.Layout()
        self.Center()

    def make_web_page(self, title):
        page = wx.Panel(self.notebook_main_view)
        plot = webview.WebView.New(page, backend=BACKEND)
        plot.LoadURL("http://www.google.com")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(plot, 1, wx.EXPAND)
        page.SetSizer(sizer)
        self.notebook_main_view.AddPage(page, title)

    def make_text_page(self, title):
        page = wx.Panel(self.notebook_main_view)
        text = wx.StaticText(page, wx.ID_ANY, "This is just text for %s" % title)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(text, 1, wx.EXPAND)
        page.SetSizer(sizer)
        self.notebook_main_view.AddPage(page, title)


class MainApp(wx.App):
    def OnInit(self):
        self.SetAppName('DVH Analytics')
        self.frame = DVHAMainFrame(None, wx.ID_ANY, "", size=(800,600))
        self.SetTopWindow(self.frame)
        # InspectionTool().Show()
        self.frame.Show()
        return True


def start():
    app = MainApp()
    app.MainLoop()


if __name__ == "__main__":
    start()

Does it work if you restore the delay using wx.CallLater like in my example?

Oh I see… you did say it was a notebook creation issue. Not a webview generation issue.

The site should automatically promote you from new_user to basic_user after you’ve spent a certain amount of time on the site, entered a number of topics, and read a number of posts. From your current stats it looks like you just need to read another 10 posts or so. Let me know if the auto-promotion doesn’t happen.

1 Like

I’m still troubleshooting. I applied the wx.CallLater approach for wx.Notebook.AddPage in my original program, but that did not resolve the issue. Hopefully, I can generate additional, simple code to demonstrate this. Maybe something else in make_page has the root cause?

NOTE: This also included spacing out webview generations by 0.1s as well.

Hello,

I tried this code and I have the same problem.
The webview in the first page works fine, but in the other pages it does not load.
The documentation says, that calling the Create function is highly recommended, maybe this helps?

It actually seems to be a problem with the notebook.
Two webviews in one window do not seem to be a problem when placed side by side.

EDIT: other widgets are also affected. But using IE as a Backend works fine, even in notebooks.

@Robin Is there any way we can help to find the issue?

Filing ticket at trac.wxwidgets.org is probably the next step, but they’re going to want a C++ example to demonstrate the problem.

Someone in he wx-users googlegroup responded with this ticket https://trac.wxwidgets.org/ticket/18851
and the information, that this should have been fixed recently :slight_smile:
How long do commits usually take to get into wxpython?

I found that message thread in wx-users (thanks for searching for that) but I don’t see anybody saying that a fix has been made. The patch in the ticket is just for providing a way to reproduce the issue. I’ve added a comment on the ticket confirming the issue.

Here is the link to my message, which I posted today. Someone said there was a fix, but he could not find the assoziated commit.
https://groups.google.com/g/wx-users/c/5qX2opqSA-w/m/s8rVYzx8BAAJ

Edit: Someone said the fix was part of this commit: https://github.com/wxWidgets/wxWidgets/commit/52138fc1f15070f3cb4895fe4aa447589e88c768

Well, the ticket page states:

will always stay invisible if its parent is hidden when the wxWebView is constructed

This suggests that the workaround is to create the webview only delayed, when the page is visible. Doesn’t this work?

Sort of. The original example worked for me when I delayed the creation of the notebook pages (which contained the WebView) until after the MainLoop started, and separated the creation of multiple pages by a few ms, so any any async work the webview needs to do has a chance to be completed before the next webview. That worked for me, but, IIUC, not for @cutright.

Although my workaround is not working now that I’ve tried it again…

That PR was included in the wxWidgets snapshot used for the wxPython 4.1.1 release. Since the issue is still happening then it looks like that PR didn’t fix it.

Here is another workaround. If the webview itself is the page widget (IOW, no wx.Panel between the webview and the notebook) then it’s able to handle multiple webviews in the notebook with no apparent issues. Here is an updated sample. Notice the flags at the top that let you choose between using delay/no-delay and panel/no-panel.

import wx
import wx.html2 as webview

print(wx.version())

NotebookClass = wx.Notebook
BACKEND = webview.WebViewBackendEdge
USE_DELAY = False
NO_PANEL = True

class DVHAMainFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)

        self.notebook_main_view = NotebookClass(self, wx.ID_ANY)
        self.tab_keys = ['Browser 1', 'Browser 2', 'Browser 3']

        global BACKEND
        if not webview.WebView.IsBackendAvailable(BACKEND):
            BACKEND = webview.WebViewBackendDefault
        print(f'Using backend: {BACKEND}')

        if USE_DELAY:
            delay = 10
            for key in self.tab_keys:
                wx.CallLater(delay, self.make_page, key)
                delay += 50
        else:
            for key in self.tab_keys:
                self.make_page(key)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.notebook_main_view, 1, wx.EXPAND, 0)

        self.SetSizer(sizer)
        self.Layout()
        self.Center()

    def make_page(self, title):
        if NO_PANEL:
            plot = page = webview.WebView.New(self.notebook_main_view, backend=BACKEND)
        else:
            page = wx.Panel(self.notebook_main_view)
            plot = webview.WebView.New(page, backend=BACKEND)
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(plot, 1, wx.EXPAND)
            page.SetSizer(sizer)

        self.notebook_main_view.AddPage(page, title)
        plot.LoadURL("http://www.google.com")

class MainApp(wx.App):
    def OnInit(self):
        self.SetAppName('DVH Analytics')
        self.frame = DVHAMainFrame(None, wx.ID_ANY, "", size=(800,600))
        self.SetTopWindow(self.frame)
        # InspectionTool().Show()
        self.frame.Show()
        return True

def start():
    app = MainApp()
    app.MainLoop()

if __name__ == "__main__":
    start()

I have just tried and the “single browser fail” works for me if the page is visible:

  • create panel
  • add to notebook
  • make visible (SetSelection)
  • create webview

I.e. just re-ordered and added SetSelection:

    def make_web_page(self, title):
        page = wx.Panel(self.notebook_main_view)
        self.notebook_main_view.AddPage(page, title)
        self.notebook_main_view.SetSelection(self.notebook_main_view.PageCount-1)
        plot = webview.WebView.New(page, backend=BACKEND)
        plot.LoadURL("http://www.google.com")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(plot, 1, wx.EXPAND)
        page.SetSizer(sizer)

This does not help when you want multiple browser tabs.
I could make this work with on-demand creation, though.
I would consider this a better style anyway when working with resource intensive widgets.
When creating the browsers in an EVT_NOTEBOOK_PAGE_CHANGING handler, it fails, as expected as then the parent is not visible.

Here’s the full code:

import wx
import wx.html2 as webview
# from wx.lib.inspection import InspectionTool
# from wx.lib.agw.flatnotebook import FlatNotebook

NotebookClass = wx.Notebook
# NotebookClass = FlatNotebook
#BACKEND = webview.WebViewBackendDefault
BACKEND = webview.WebViewBackendEdge


class DVHAMainFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)

        self.notebook_main_view = NotebookClass(self, wx.ID_ANY)
        self.tab_keys = ['Browser 1', 'Browser 2', 'Browser 3']
        self._created = set()

        for key in self.tab_keys:
            self.make_web_page(key)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.notebook_main_view, 1, wx.EXPAND, 0)

        self.SetSizer(sizer)
        self.Layout()
        self.Center()

        self.make_browser(self.tab_keys[0])
        self.notebook_main_view.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.on_page_selection)

    def make_web_page(self, title):
        # just add a panel, but not yet a browser
        page = wx.Panel(self.notebook_main_view)
        self.notebook_main_view.AddPage(page, title)

    def on_page_selection(self, event):
        # when page is selected, check whether the browser has been created already
        index = event.GetSelection()
        key = self.tab_keys[index]
        if not key in self._created:
            self.make_browser(key)
        event.Skip()

    def make_browser(self, key):
        # on-demand creation of the browser
        index = self.tab_keys.index(key)
        page = self.notebook_main_view.GetPage(index)
        plot = webview.WebView.New(page, backend=BACKEND)
        plot.LoadURL("http://www.google.com")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(plot, 1, wx.EXPAND)
        page.SetSizer(sizer)
        page.Layout()
        self._created.add(key)


class MainApp(wx.App):
    def OnInit(self):
        self.SetAppName('DVH Analytics')
        self.frame = DVHAMainFrame(None, wx.ID_ANY, "", size=(800,600))
        self.SetTopWindow(self.frame)
        # InspectionTool().Show()
        self.frame.Show()
        return True


def start():
    app = MainApp()
    app.MainLoop()


if __name__ == "__main__":
    start()