PyQt5 如何在使用 evaluateJavaScript 时更新 GUI 如果 python 仍然是 运行
PyQt5 How to update GUI when use evaluateJavaScript if python is still running
我想使用 evaluateJavaScript 命令动态修改我的 GUI
我注意到 GUI 仅在 python 完成工作后才会更新。我创建了这个示例代码:
基本 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 更新代码 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:按下 "start" 按钮后,我希望看到数字 0 1 2 3 4 依次出现在我的 GUI 中。
当前结果:按下"start"按钮N秒后没有任何反应,然后出现数字4
一般来说,GUI 框架不应该有阻塞代码,阻塞来自 运行 的事件循环。
如果你真的需要这个,你应该使用线程/Qthread 并发送信号来更新 GUI
正如@gelonida指出的那样,一般的解决方案是使用线程并通过信号通知GUI,因此我的回答提供了一种可能的实现方式。
要在线程中实现,for 循环必须在顺序任务中重新排列,例如使用迭代器,然后在任务完成时通过信号调用下一个。对于本例中的耗时任务,我创建了一个工作线程,该工作线程位于另一个线程中,并且必须调用 "task" 方法。
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_())
我想使用 evaluateJavaScript 命令动态修改我的 GUI
我注意到 GUI 仅在 python 完成工作后才会更新。我创建了这个示例代码:
基本 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 更新代码 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:按下 "start" 按钮后,我希望看到数字 0 1 2 3 4 依次出现在我的 GUI 中。 当前结果:按下"start"按钮N秒后没有任何反应,然后出现数字4
一般来说,GUI 框架不应该有阻塞代码,阻塞来自 运行 的事件循环。
如果你真的需要这个,你应该使用线程/Qthread 并发送信号来更新 GUI
正如@gelonida指出的那样,一般的解决方案是使用线程并通过信号通知GUI,因此我的回答提供了一种可能的实现方式。
要在线程中实现,for 循环必须在顺序任务中重新排列,例如使用迭代器,然后在任务完成时通过信号调用下一个。对于本例中的耗时任务,我创建了一个工作线程,该工作线程位于另一个线程中,并且必须调用 "task" 方法。
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_())