I would like to modify my GUI dynamically with the evaluateJavaScript command
I noticed that the GUI is updated only when python has finished working. I created this example code:
Base GUI:
webView = QWebView()
myObj = ExampleClass(webView)
webView.page().mainFrame().addToJavaScriptWindowObject("pyObj", myObj)
webView.setHtml('''
<html>
<body>
<div id="content"></div>
<button onClick="pyObj.example()">start</button>
</body>
</html>
''')
Python code for update Html GUI:
class ExampleClass(QtCore.QObject):
def __init__(self, webView):
super(ExampleClass, self).__init__(webView)
self.webView = webView
@QtCore.pyqtSlot()
def example(self):
print("start")
for i in range(0,5):
print(i)
self.webView.page().mainFrame().evaluateJavaScript('document.getElementById("content").innerHTML = "'+str(i)+'";')
time.sleep(1)
#simulation of a heavy code
for a in range(0,999):
for b in range(0,9999):
c = a * b;
Objective: after pressing the "start" button I would like to see the numbers 0 1 2 3 4 sequentially appear in my GUI. Current result: after pressing the "start" button for N seconds nothing happens and then the number 4 appears
As @gelonida points out, the solution in general is to use threads and notify the GUI through signals, so my answer provides a possible implementation.
To implement in threads, the for-loop must be rearranged in sequential tasks, for example using iterators, and then call the next one when the task has been completed through a signal. For the time-consuming task in this case I have created a worker who lives in another thread and the "task" method must be invoked.
import sys
from PyQt5 import QtCore, QtWidgets, QtWebKitWidgets
class Worker(QtCore.QObject):
finished = QtCore.pyqtSignal()
@QtCore.pyqtSlot()
def task(self):
# simulation of a heavy code
for a in range(999):
for b in range(9999):
c = a * b
self.finished.emit()
class ExampleClass(QtCore.QObject):
def __init__(self, webView):
super(ExampleClass, self).__init__(webView)
self.webView = webView
thread = QtCore.QThread(self)
thread.start()
self.m_worker = Worker()
self.m_worker.moveToThread(thread)
self.m_worker.finished.connect(self.execute)
self.m_numbers = None
@QtCore.pyqtSlot()
def example(self):
print("example")
self.m_numbers = iter(range(5))
self.execute()
@QtCore.pyqtSlot()
def execute(self):
if self.m_numbers is None:
return
try:
i = next(self.m_numbers)
except StopIteration:
return
else:
self.webView.page().mainFrame().evaluateJavaScript(
'document.getElementById("content").innerHTML = "{}";'.format(i)
)
QtCore.QTimer.singleShot(1000, self.m_worker.task)
if __name__ == "__main__":
html = """<html>
<body>
<div id="content"></div>
<button onClick="pyObj.example()">start</button>
</body>
</html>"""
app = QtWidgets.QApplication(sys.argv)
view = QtWebKitWidgets.QWebView()
obj = ExampleClass(view)
view.page().mainFrame().addToJavaScriptWindowObject("pyObj", obj)
view.setHtml(html)
view.resize(640, 480)
view.show()
sys.exit(app.exec_())