Phoenix icon indicating copy to clipboard operation
Phoenix copied to clipboard

wx.html2: event EVT_WEBVIEW_SCRIPT_RESULT and RunScriptAsync method

Open dmazzella opened this issue 3 years ago • 7 comments
trafficstars

Operating system: Any wxPython version & source: 4.1.2? Python version & source: Any supported

Description of the problem:

Hi, are there any plans for implementing the RunScriptAsync method and its event to handle it?

Thank you

Code Example (click to expand)

self.Bind(wx.html2.EVT_WEBVIEW_SCRIPT_RESULT, self.OnWebViewScriptResult, self.wv)
# example call js function
self.wv.RunScriptAsync("function f(a){return a;}f('Hello World!');")

def OnWebViewScriptResult(self, evt):
    if evt.IsError():
        log.info("Async script execution failed: %s", evt.GetString())
    else:
        log.error("Async script result received; value = %s", evt.GetString())

dmazzella avatar Feb 02 '22 12:02 dmazzella

At the moment, wxPython is based on wxWidgets 3.1.5(ish). It would be added once it moves forward from that.

swt2c avatar Feb 04 '22 00:02 swt2c

Are there any plans on the timing for next release?

Thank you

dmazzella avatar Feb 05 '22 10:02 dmazzella

I tried the latest version and I have the following error:

Code Example (click to expand)

self.Bind(wx.html2.EVT_WEBVIEW_SCRIPT_RESULT, self.OnWebViewScriptResult, self.wv)
# example call js function
self.wv.RunScriptAsync("function f(a){return a;}f('Hello World!');")

def OnWebViewScriptResult(self, evt):
    if evt.IsError():
        log.info("Async script execution failed: %s", evt.GetString())
    else:
        log.error("Async script result received; value = %s", evt.GetString())

module 'wx.html2' has no attribute 'EVT_WEBVIEW_SCRIPT_RESULT'
4.2.0 msw (phoenix) wxWidgets 3.2.

what is the state of RunScriptAsync ?

Thank you

dmazzella avatar Sep 07 '22 14:09 dmazzella

@dmazzella You can install a snapshot build of wxPython for using RunScriptAsync. They are listed here: https://wxpython.org/Phoenix/snapshot-builds

You can easily install a snapshot build via the pip package installer. For example: pip install https://wxpython.org/Phoenix/snapshot-builds/wxPython-4.2.1a1.dev5496+7c4d21d7-cp39-cp39-win_amd64.whl

I have been using RunScriptAsync for a few months now. It runs very well. It's a pity that you can only set one AddScriptMessageHandler at the moment, but maybe that will change...

Good luck with your project =)

ncotrb avatar Sep 07 '22 17:09 ncotrb

A basic demo:

Code Example (click to expand)
import wx
import wx.html2

class MyBrowser(wx.Dialog):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.browser = wx.html2.WebView.New(self, style=wx.NO_BORDER)
        self.browser.AddScriptMessageHandler("message_handler")

        self.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.browser)
        self.Bind(wx.html2.EVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, self.OnWebViewMessage, self.browser)

        self.Bind(wx.EVT_CLOSE, lambda evt: self.DestroyLater())

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.browser, 1, wx.EXPAND)
        self.SetSizer(sizer)

    def OnWebViewLoaded(self, evt):
        self.browser.RunScriptAsync("document.getElementById('paragraph').innerText='Hello from Python!'")

    def OnWebViewMessage(self, evt):
        print(f"Message received: {evt.GetString()}")

if __name__ == "__main__":
    app = wx.App()
    dialog = MyBrowser(None, id=-1, style=wx.DEFAULT_FRAME_STYLE, size=(600, 400))
    dialog.browser.SetPage(
        """
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Test</title>
        </head>
        <body>
            <h1>Demo</h1>
            <p id="paragraph">Lorem ipsum.</p>
            <script>
                window.message_handler.postMessage("Hello from JavaScript!");
            </script>
        </body>
        </html>
        """,
        "",
    )
    dialog.Show()
    app.MainLoop()

ncotrb avatar Sep 07 '22 18:09 ncotrb

Yes, when we updated wxWidgets versions, we overlooked setting up the EVT_WEBVIEW_SCRIPT_RESULT event. Should be an easy fix.

swt2c avatar Sep 07 '22 18:09 swt2c

@dmazzella I was a little curious... You can easily add the missing lines yourself in the meantime until there is a new release. Only two lines of code need to be added. Search for the following two files in this path:

(The path of the wxPython pip installation (on Windows 10) should look something like this)

C:\Users\<YOUR NAME>\AppData\Local\Programs\Python\Python39\Lib\site-packages\wx\

"html2.py" (line 37):

EVT_WEBVIEW_SCRIPT_RESULT = wx.PyEventBinder( wxEVT_WEBVIEW_SCRIPT_RESULT, 1)

"html2.pyi" (line 814):

EVT_WEBVIEW_SCRIPT_RESULT = wx.PyEventBinder( wxEVT_WEBVIEW_SCRIPT_RESULT, 1)

Now this script runs smoothly:

Code Example (click to expand)
import wx
import wx.html2

class MyBrowser(wx.Dialog):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.browser = wx.html2.WebView.New(self, style=wx.NO_BORDER)
        self.browser.AddScriptMessageHandler("message_handler")

        self.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.browser)
        self.Bind(wx.html2.EVT_WEBVIEW_SCRIPT_RESULT, self.OnWebViewScriptResult, self.browser)
        self.Bind(wx.html2.EVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, self.OnWebViewMessage, self.browser)

        self.Bind(wx.EVT_CLOSE, lambda evt: self.DestroyLater())

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.browser, 1, wx.EXPAND)
        self.SetSizer(sizer)

    def OnWebViewLoaded(self, evt):
        self.browser.RunScriptAsync("document.getElementById('paragraph').innerText='Hello from Python!'")
        self.browser.RunScriptAsync("function f(a){return a;}f('Hello World!');")

    def OnWebViewScriptResult(self, evt):
        if evt.IsError():
            print(f"Async script execution failed: {evt.GetString()}")
        else:
            print(f"Async script result received; value = {evt.GetString()}")

    def OnWebViewMessage(self, evt):
        print(f"Message received: {evt.GetString()}")

if __name__ == "__main__":
    app = wx.App()
    dialog = MyBrowser(None, id=-1, style=wx.DEFAULT_FRAME_STYLE, size=(600, 400))
    dialog.browser.SetPage(
        """
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Test</title>
        </head>
        <body>
            <h1>Demo</h1>
            <p id="paragraph">Lorem ipsum.</p>
            <script>
                window.message_handler.postMessage("Hello from JavaScript!");
            </script>
        </body>
        </html>
        """,
        "",
    )
    dialog.Show()
    app.MainLoop()

ncotrb avatar Sep 14 '22 19:09 ncotrb