Explanation:
As the docs points out:
You need to pay attention to local testing — if you try to load the HTML file locally (i.e. with a file:// URL), you'll run into CORS errors due to JavaScript module security requirements. You need to do your testing through a server.
The modules are not accessible if you use local files, therefore you get the error message:
js: Failed to load module script: The server responded with a non-JavaScript MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.
Solution:
In this case there are 2 options:
1. local server
Implement a server, for example using aiohttp + qasync:
import asyncio
import functools
import os
from pathlib import Path
import sys
from aiohttp import web
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWebEngineWidgets import QWebEngineView
import qasync
from qasync import QApplication
application_path = (
Path(sys.executable).resolve().parent
if getattr(sys, "frozen", False)
else Path(__file__).resolve().parent
)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.browser = QWebEngineView()
config_name = "/data_files/init.html"
url = QUrl("http://localhost:4000")
url.setPath(config_name)
self.browser.load(url)
self.setCentralWidget(self.browser)
self.showMaximized()
async def main():
def close_future(future, loop):
loop.call_later(10, future.cancel)
future.cancel()
loop = asyncio.get_event_loop()
future = asyncio.Future()
qt_app = QApplication.instance()
if hasattr(qt_app, "aboutToQuit"):
getattr(qt_app, "aboutToQuit").connect(
functools.partial(close_future, future, loop)
)
app = web.Application()
app.router.add_static("/", application_path, show_index=True)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "localhost", 4000)
await site.start()
view = MainWindow()
view.show()
await future
await runner.cleanup()
return True
if __name__ == "__main__":
try:
qasync.run(main())
except asyncio.exceptions.CancelledError:
sys.exit(0)
2. Custom QWebEngineUrlSchemeHandler
import sys
import os
from PyQt5.QtCore import QCoreApplication, QUrl, QFile, QFileInfo, QMimeDatabase
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtWebEngineCore import (
QWebEngineUrlScheme,
QWebEngineUrlSchemeHandler,
QWebEngineUrlRequestJob,
)
from PyQt5.QtWebEngineWidgets import QWebEngineView
application_path = (
os.path.dirname(sys.executable)
if getattr(sys, "frozen", False)
else os.path.dirname(__file__)
)
class QtSchemeHandler(QWebEngineUrlSchemeHandler):
def requestStarted(self, job):
request_method = job.requestMethod()
if request_method != b"GET":
job.fail(QWebEngineUrlRequestJob.RequestDenied)
return
request_url = job.requestUrl()
request_path = request_url.path()
file = QFile(application_path + request_path)
file.setParent(job)
job.destroyed.connect(file.deleteLater)
if not file.exists() or file.size() == 0:
print(f"resource '{request_path}' not found or is empty")
job.fail(QWebEngineUrlRequestJob.UrlNotFound)
return
file_info = QFileInfo(file)
mime_database = QMimeDatabase()
mime_type = mime_database.mimeTypeForFile(file_info)
job.reply(mime_type.name().encode(), file)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.browser = QWebEngineView()
self.scheme_handler = QtSchemeHandler()
self.browser.page().profile().installUrlSchemeHandler(
b"qt", self.scheme_handler
)
filename = "/data_files/init.html"
url = QUrl("qt://main")
url.setPath(filename)
self.browser.load(url)
self.setCentralWidget(self.browser)
if __name__ == "__main__":
scheme = QWebEngineUrlScheme(b"qt")
scheme.setFlags(QWebEngineUrlScheme.CorsEnabled)
QWebEngineUrlScheme.registerScheme(scheme)
app = QApplication(sys.argv)
QApplication.setApplicationName("v0.1")
window = MainWindow()
window.show()
app.exec_()
File structure:
├── data_files
│ ├── init.html
│ └── js
│ └── main.js
└── main.py