Provide a way for Javascript code in a Webview to invoke Python functions
What is the problem or limitation you are having?
It is possible for Python to invoke Javascript code in a web view; however, it is not possible for a Javascript function to call back to Python code.
Describe the solution you'd like
PyQt5 WebView not work in iOS or Android so I hope toga can realize this function
this is pyqt5 solution
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Webview Demo</title>
<script type="text/javascript">
function callPython() {
alert(api.print('print from JavaScript!'));
}
</script>
</head>
<body>
<button onclick="callPython()">Call Python Code print</button>
</body>
</html>
webview.py:
import os
from PyQt5.QtCore import QUrl
from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWebKitWidgets import QWebView, QWebPage
from PyQt5.QtWidgets import QWidget
from posmesh.platform import API
class WebPage(QWebPage):
def __init__(self, parent=None):
super(WebPage, self).__init__(parent)
self.api = API()
self.mainFrame().javaScriptWindowObjectCleared.connect(self.populateJavaScriptWindowObject)
def populateJavaScriptWindowObject(self):
self.mainFrame().addToJavaScriptWindowObject('api', self.api)
class Webview(QWidget):
def __init__(self, url="", parent=None):
super(Webview, self).__init__(parent)
self.view = QWebView(self)
self.view.setPage(WebPage())
self.view.settings().setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, True)
self.view.load(QUrl.fromLocalFile(os.getcwd() + url))
self.view.show()
api.py
from PyQt5.QtCore import pyqtSlot as Slot, QObject
class API(QObject):
@Slot(str, result=str)
def print(self, msg):
print(msg)
return 'printed'
Describe alternatives you've considered
Haven't found a WebView jsbridge solution across iOS, Android, windows, and Linux yet using python.
Additional context
No response
Agreed this would be useful mechanism to have in Toga's WebView API. If nothing else, it would be necessary for a full "Electron"-style app shell replacement, so that web code can invoke native system functions.
The implementation is going to be very platform specific. From a quick poke around, here are some pointers on the APIs that will be required:
- iOS/Cocoa (both using WKWebview) This is an example of usage
- Android: This is an example of usage
- GTK: this will be the API to use; the usage will be very similar to iOS, as they're both WebKit based
- Windows: This is a tutorial on how to use the API; interacting with Python.net could be complicated as it's a COM interface.
Ultimately, we need to be able to have a webview.register_handler("name", handler_method) API of some kind; however, in the meantime, you can use the platform specific APIs by dropping to the native interface. On any user-space toga.WebView instance, webview._impl.native will be the platform native WKWebKit/GTK.WebView2/etc instance; it should be possible to use the example code provided above on this native object.
FWIW, this can be currently done with: https://github.com/python-eel/Eel. It can be combined with toga's webview for calling python from js.
@proneon267 Are you suggesting that as something someone could drop in to a Toga app? Or an example of someone else implementing the equivalent functionality (using the APIs that I highlighted in my previous post)?
Yes, someone can just use it in toga as is. I had tested the (toga+python-eel combo) and it had worked properly on desktop and mobile platform like Android.
That... seems very unusual to me. Looking at the code, it only supports Chrome and Edge - so it can't support Toga's macOS interface - and it doesn't include any code differentiating Android from Linux; and it doesn't include any hooks into the native platform webview APIs. Can you provide an example of how it is used?
Sure. I'll find the old project I was working on and get back to you.
@freakboy3742 I have gutted out the complex logic in my old project and replaced it with a simpler logic. Here is the example briefcase project: https://github.com/proneon267/toga-python-eel.
Particularly, take a look at:
app.py: https://github.com/proneon267/toga-python-eel/blob/main/togapythoneel/src/togapythoneel/app.py and script.js: https://github.com/proneon267/toga-python-eel/blob/main/togapythoneel/src/togapythoneel/web/script.js
I have tested the example on Windows, Android and MacOS. Everything seems to be working properly.
I also tried to run it on iOS but got an error because, gevent is not in the Anaconda iOS repository:https://anaconda.org/beeware/repo. So, it seems it is just a matter of building the binary wheels for gevent.
Looks like we are closer to cross-platform Electron-Style apps in Python :)
Ah - you're not using Eel's core functionality.
What you'e done here is use Eel to start a web server; when the web page hits a URL, it hits the web server, which is in Python space, which allows you to run Python code. Eel might make it marginally easier to start a web server... but you could do that with any web server (Django, Flask, or even a http.server subclass).
That's fundamentally different to what this ticket is proposing - the approach suggested by this ticket wouldn't require a standalone webserver. It would integrate directly with the webview, so that invoking Javascript calls back into the same process that is hosting the web view.
Oh - in that case, it will need to be implemented in toga 😅
An implementation note: If this is resolved for GTK or Winforms, the MapView widget introduced by #2379 will be able to implement the on_select handler.