Hi,
I'm the dev behind the 3DfindIT plugin and discovered the issue above yesterday. Additionally to what Bernd said, the problem only occurs when executing JavaScript from a side-thread:
Code: Select all
self.thread = threading.Thread(target=self.runJs, daemon=True)
self.thread.start()
def runJs(self):
self.web_view.page().runJavaScript("(function(){ alert('goodbye'); })();")
This worked fine during the development cycle, but no longer does. The last version I can verify it worked is this one:
OS: Windows 10 (10.0)
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 0.19.22988 (Git)
Build type: Release
Branch: master
Hash: cdc01cb20cfa74079c45b9dc2dab81e3248b2aa5
Python version: 3.6.8
Qt version: 5.12.1
Coin version: 4.0.0a
OCC version: 7.3.0
Locale: German/Germany (de_DE)
As a quick fix I can change my code to wait for tasks in a side-thread, but actually execute them on the main-thread. In the sample above: instead of executing the the JavaScript immediately, raise an event and execute the JavaScript when handling the event. Here's the code from above changed to work with events:
Code: Select all
# ***************************************************************************
# * Copyright (c) 2020 Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
# forum topic
# https://forum.freecadweb.org/viewtopic.php?f=10&t=25189
# very helpful links
# https://stackoverflow.com/questions/51011244/pyside2-and-supporting-addtojavascriptwindowobject
# https://stackoverflow.com/questions/39544089/how-can-i-access-python-code-from-javascript-in-pyqt-5-7/42740287
import os
import tempfile
import threading
from urllib.request import urlretrieve
from PySide2 import QtCore
from PySide2 import QtWidgets
from PySide2 import QtWebEngineWidgets
from PySide2 import QtWebChannel
import FreeCAD
import FreeCADGui
import Part
the_html_code = """
<html>
<head>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
</head>
<header><title>title</title></header>
<body>
<p id="output"></p>
<h2><a href="#" onclick="jsWebPartImporter.text('https://forum.freecadweb.org/download/file.php?id=48016')">Click to import a Part from Internet into FreeCAD</a></h2>
<h2><a href="#" onclick="alert('Javascript works!')">Click for Java Script Test</a></h2>
<script type="text/javascript">
window.onload = function() {
new QWebChannel(qt.webChannelTransport, function (channel) {
window.jsWebPartImporter = channel.objects.jsWebPartImporter;
console.log(jsWebPartImporter);
});
}
</script>
</body>
</html>
"""
class WebPartImporter(QtCore.QObject):
def __init__(self, parent=None):
super(WebPartImporter, self).__init__(parent)
@QtCore.Slot(str)
def text(self, url_to_step_file):
print(url_to_step_file)
# get a temp file with ending step
tmpstepfile_path = tempfile.NamedTemporaryFile(suffix=".step").name
print(tmpstepfile_path)
# download the step file
urlreturn = urlretrieve(url_to_step_file, tmpstepfile_path)
print(urlreturn[0])
# read the step (the read method needs file ending step)
# and add it to the active document
# create a new doc if there is no active doc
Part.show(Part.read(tmpstepfile_path))
os.remove(tmpstepfile_path)
print(FreeCAD.ActiveDocument.Name)
# set the view
FreeCADGui.ActiveDocument.activeView().viewAxonometric()
FreeCADGui.SendMsgToActiveView("ViewFit")
class BrowserWidget(QtWidgets.QDialog):
hasWork = QtCore.Signal(str)
def __init__(self):
super(BrowserWidget, self).__init__()
self.web_view = QtWebEngineWidgets.QWebEngineView()
channel = QtWebChannel.QWebChannel(self)
channel.registerObject("jsWebPartImporter", WebPartImporter(self.web_view))
self.web_view.page().setWebChannel(channel)
# layout
self.web_view.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addLayout(QtWidgets.QHBoxLayout())
self.layout.addWidget(self.web_view)
self.hasWork.connect(self._hasWork)
self.thread = threading.Thread(target=self.runJs, daemon=True)
self.thread.start()
def runJs(self):
self.hasWork.emit("(function(){ alert('goodbye'); })();")
def _hasWork(self, script):
self.web_view.page().runJavaScript(script)
def showEvent(self, event):
super(BrowserWidget, self).showEvent(event)
self.web_view.setHtml(the_html_code)
mw = FreeCADGui.getMainWindow()
d = QtWidgets.QDockWidget()
d.setWidget(BrowserWidget())
mw.addDockWidget(QtCore.Qt.RightDockWidgetArea, d)
Please give me Feedback if I should fix this on my side or if that's something that will be fixed in the core.