具有异步函数执行的 PySide 应用程序
PySide Application with asynchronous function execution
我有一个示例 pyside 演示,我创建它是为了查看 webkit 浏览器与 python 的通信...
我在 webkit
中有两个按钮
按钮 1 - 单击时它会休眠 10 秒,然后打印一条消息
button2 - 单击它会立即打印一条消息。
当我单击按钮 1 时,整个应用程序冻结并等待 python 完成休眠,这意味着我无法单击按钮 2 来执行其他操作。如何在函数调用之间实现异步方法?
我的python代码如下
import sys,json
from time import sleep
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import QWebView, QWebSettings
from PySide.QtNetwork import QNetworkRequest
from PySide.QtCore import QObject, Slot, Signal
html_str="""<!doctype>
<html>
<body>hello world
<button id="button" >button1</button>
<button id="button2" >button2</button>
</body>
</html>
<script type="text/javascript">
document.getElementById("button").onclick=function(){
object.reply(" hello ");
}
document.getElementById("button2").onclick=function(){
object.reply2(" hello ");
}
function data_from_js(msg){
var tag=document.createElement('div');
tag.innerHTML="message from python";
document.body.appendChild(tag);
alert(msg['name']);
}
</script>
<style>
body{
border:solid black 1px;
}
</style>
</doctype>"""
class Qbutton(QObject):
from time import sleep
def __init__(self):
super(Qbutton,self).__init__()
@Slot(str)
def reply(self,recd):
#r=QMessageBox.information(self,"Info",msg)
msgBox = QMessageBox()
sleep(10)
msgBox.setText("python just said"+recd)
msgBox.exec_()
return "I am recieving pythonic data"
#r=QMessageBox.question(self,title,recd,QMessageBox.Yes | QMessageBox.No)
@Slot(str)
def reply2(self,recd):
msgBox = QMessageBox()
msgBox.setText("python just said"+recd+ " another time")
msgBox.exec_()
return "I am recieving pythonic data"
@Slot(str)
def send_tojs(self):
pass
class adstar_gui(QWidget):
def __init__(self):
super(adstar_gui,self).__init__()
self.setWindowTitle("Adstar Wordlist Generator")
self.setMaximumWidth(5000)
self.setMaximumHeight(5000)
self.setMinimumWidth(500)
self.setMinimumHeight(500)
self.show()
print "Sample window"
def closeEvent(self,event):
self.closeEvent()
if __name__=="__main__":
Qapp=QApplication(sys.argv)
t=QWebView()
t.setHtml(html_str)
button=Qbutton()
t.page().mainFrame().addToJavaScriptWindowObject("object",button)
t.show()
#t.page().mainFrame().evaluateJavaScript("data_from_js(%s);" % (json.dumps({'name':"My name is Junior"}) ))
QCoreApplication.processEvents()
#sys.exit(Qapp.exec_())
Qapp.exec_()
问题
如何在 webkit 中点击 button 1
并让 python 在点击按钮 1 时在后台执行某些操作? (这样 button 2
功能不需要等待按钮 1 功能完成)
请使用此演示并对其进行改进...非常感谢
使用QTimer
在特定时间段后执行信号。像这样:
import sys,json
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import QWebView, QWebSettings
from PySide.QtNetwork import QNetworkRequest
from PySide.QtCore import QObject, Slot, Signal, QTimer
html_str="""<!doctype>
<html>
<body>hello world
<button id="button" >button1</button>
<button id="button2" >button2</button>
</body>
</html>
<script type="text/javascript">
document.getElementById("button").onclick=function(){
object.replyAfter10Seconds(" hello ");
}
document.getElementById("button2").onclick=function(){
object.reply2(" hello ");
}
function data_from_js(msg){
var tag=document.createElement('div');
tag.innerHTML="message from python";
document.body.appendChild(tag);
alert(msg['name']);
}
</script>
<style>
body{
border:solid black 1px;
}
</style>
</doctype>"""
class Qbutton(QObject):
def __init__(self):
super(Qbutton,self).__init__()
self.timer = QTimer()
self.timer.setSingleShot(True)
self.timer.setInterval(10 * 1000)
self.timer.timeout.connect(self.reply)
@Slot(str)
def replyAfter10Seconds(self,recd):
self._replyText = recd
print "Started timer"
self.timer.start()
@Slot()
def reply(self):
#r=QMessageBox.information(self,"Info",msg)
msgBox = QMessageBox()
msgBox.setText("python just said"+self._replyText)
msgBox.exec_()
return "I am recieving pythonic data"
#r=QMessageBox.question(self,title,recd,QMessageBox.Yes | QMessageBox.No)
@Slot(str)
def reply2(self,recd):
msgBox = QMessageBox()
msgBox.setText("python just said"+recd+ " another time")
msgBox.exec_()
return "I am recieving pythonic data"
@Slot(str)
def send_tojs(self):
pass
class adstar_gui(QWidget):
def __init__(self):
super(adstar_gui,self).__init__()
self.setWindowTitle("Adstar Wordlist Generator")
self.setMaximumWidth(5000)
self.setMaximumHeight(5000)
self.setMinimumWidth(500)
self.setMinimumHeight(500)
self.show()
print "Sample window"
def closeEvent(self,event):
self.closeEvent()
if __name__=="__main__":
Qapp=QApplication(sys.argv)
t=QWebView()
t.setHtml(html_str)
button=Qbutton()
t.page().mainFrame().addToJavaScriptWindowObject("object",button)
t.show()
t.raise_()
#t.page().mainFrame().evaluateJavaScript("data_from_js(%s);" % (json.dumps({'name':"My name is Junior"}) ))
QCoreApplication.processEvents() # does nothing as long as App.exec_() hasn't statred.
#sys.exit(Qapp.exec_())
Qapp.exec_()
这里有几个问题。首先,值得指出的是当您单击 button1 时应用程序冻结的原因:单击导致 Qt 调用事件处理程序 reply
,并且 Qt 无法处理另一个事件,直到此处理程序 returns(在根据我的经验,所有窗口系统都是这样工作的)。因此,如果您将任何长 运行 例程放入事件处理程序中,您的应用程序将冻结,直到该例程完成。任何时候事件处理程序花费的时间超过 0.05 秒,用户都会注意到。
正如 titusjan 在他的回答中指出的那样,让 Qt 在一定时间间隔后执行函数非常容易。但我认为你的问题不是关于如何处理简单的时间延迟,而是关于如何处理一个 long-运行 的过程。在我的示例代码中,我用一个计算十个一秒延迟的循环替换了你的十秒延迟,我认为这是一个更好的模型来实现你想要实现的目标。
解决方案是在另一个线程中执行长进程。您有两个选择:QThreads,它是 Qt 环境的一部分,以及 Python 线程。它们都可以工作,但我总是尽可能使用 Python 线程。它们有更好的文档记录并且更轻量级。将线程指定为守护进程的能力有时会使应用程序关闭更简单一些。此外,将多线程程序转换为使用多进程的程序也更容易。我在下面的示例代码中使用了 Python 线程。
那么问题来了,应用程序怎么知道副线程什么时候结束呢?为此,您必须创建自定义 Qt Signal。当它完成工作时,您的辅助线程会发出此信号,并且主应用程序会连接一个 Slot 以在发生这种情况时执行某些操作。如果您要制作自定义 Qt Signal,您必须在 QObject 的子类中声明它,就像我在示例中所做的那样。
不用说,所有标准的多线程问题都得处理。
import sys
import json
import threading
from time import sleep
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import QWebView, QWebSettings
from PySide.QtNetwork import QNetworkRequest
from PySide.QtCore import QObject, Slot, Signal
html_str="""<!doctype>
<html>
<body>hello world
<button id="button" >button1</button>
<button id="button2" >button2</button>
</body>
</html>
<script type="text/javascript">
document.getElementById("button").onclick=function(){
object.reply(" hello ");
}
document.getElementById("button2").onclick=function(){
object.reply2(" hello ");
}
function data_from_js(msg){
var tag=document.createElement('div');
tag.innerHTML="message from python";
document.body.appendChild(tag);
alert(msg['name']);
}
</script>
<style>
body{
border:solid black 1px;
}
</style>
</doctype>"""
class Qbutton(QObject):
def __init__(self):
super(Qbutton,self).__init__()
self.long_thread = LongPythonThread()
self.long_thread.thread_finished.connect(self.reply2_finished)
@Slot(str)
def reply(self,recd):
print("reply")
t = threading.Thread(target=self.long_thread.long_thread, args=(recd,))
t.daemon = True
t.start()
@Slot(str)
def reply2(self,recd):
print("reply2")
msgBox = QMessageBox()
msgBox.setText("python just said"+recd)
msgBox.exec_()
return "I am receiving pythonic data"
@Slot(str)
def reply2_finished(self, recd):
print("reply2 finished")
msgBox = QMessageBox()
msgBox.setText("python just said"+recd+ " another time")
msgBox.exec_()
@Slot(str)
def send_tojs(self):
pass
class LongPythonThread(QObject):
thread_finished = Signal(str)
def __init__(self):
super(LongPythonThread,self).__init__()
def long_thread(self, recd):
for n in range(10):
sleep(1.0)
print("Delayed for {:d}s".format(n+1))
self.thread_finished.emit(recd)
if __name__=="__main__":
Qapp=QApplication(sys.argv)
t=QWebView()
t.setHtml(html_str)
button=Qbutton()
t.page().mainFrame().addToJavaScriptWindowObject("object",button)
t.show()
#t.page().mainFrame().evaluateJavaScript("data_from_js(%s);" % (json.dumps({'name':"My name is Junior"}) ))
QCoreApplication.processEvents()
#sys.exit(Qapp.exec_())
Qapp.exec_()
我有一个示例 pyside 演示,我创建它是为了查看 webkit 浏览器与 python 的通信... 我在 webkit
中有两个按钮按钮 1 - 单击时它会休眠 10 秒,然后打印一条消息
button2 - 单击它会立即打印一条消息。
当我单击按钮 1 时,整个应用程序冻结并等待 python 完成休眠,这意味着我无法单击按钮 2 来执行其他操作。如何在函数调用之间实现异步方法?
我的python代码如下
import sys,json
from time import sleep
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import QWebView, QWebSettings
from PySide.QtNetwork import QNetworkRequest
from PySide.QtCore import QObject, Slot, Signal
html_str="""<!doctype>
<html>
<body>hello world
<button id="button" >button1</button>
<button id="button2" >button2</button>
</body>
</html>
<script type="text/javascript">
document.getElementById("button").onclick=function(){
object.reply(" hello ");
}
document.getElementById("button2").onclick=function(){
object.reply2(" hello ");
}
function data_from_js(msg){
var tag=document.createElement('div');
tag.innerHTML="message from python";
document.body.appendChild(tag);
alert(msg['name']);
}
</script>
<style>
body{
border:solid black 1px;
}
</style>
</doctype>"""
class Qbutton(QObject):
from time import sleep
def __init__(self):
super(Qbutton,self).__init__()
@Slot(str)
def reply(self,recd):
#r=QMessageBox.information(self,"Info",msg)
msgBox = QMessageBox()
sleep(10)
msgBox.setText("python just said"+recd)
msgBox.exec_()
return "I am recieving pythonic data"
#r=QMessageBox.question(self,title,recd,QMessageBox.Yes | QMessageBox.No)
@Slot(str)
def reply2(self,recd):
msgBox = QMessageBox()
msgBox.setText("python just said"+recd+ " another time")
msgBox.exec_()
return "I am recieving pythonic data"
@Slot(str)
def send_tojs(self):
pass
class adstar_gui(QWidget):
def __init__(self):
super(adstar_gui,self).__init__()
self.setWindowTitle("Adstar Wordlist Generator")
self.setMaximumWidth(5000)
self.setMaximumHeight(5000)
self.setMinimumWidth(500)
self.setMinimumHeight(500)
self.show()
print "Sample window"
def closeEvent(self,event):
self.closeEvent()
if __name__=="__main__":
Qapp=QApplication(sys.argv)
t=QWebView()
t.setHtml(html_str)
button=Qbutton()
t.page().mainFrame().addToJavaScriptWindowObject("object",button)
t.show()
#t.page().mainFrame().evaluateJavaScript("data_from_js(%s);" % (json.dumps({'name':"My name is Junior"}) ))
QCoreApplication.processEvents()
#sys.exit(Qapp.exec_())
Qapp.exec_()
问题
如何在 webkit 中点击 button 1
并让 python 在点击按钮 1 时在后台执行某些操作? (这样 button 2
功能不需要等待按钮 1 功能完成)
请使用此演示并对其进行改进...非常感谢
使用QTimer
在特定时间段后执行信号。像这样:
import sys,json
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import QWebView, QWebSettings
from PySide.QtNetwork import QNetworkRequest
from PySide.QtCore import QObject, Slot, Signal, QTimer
html_str="""<!doctype>
<html>
<body>hello world
<button id="button" >button1</button>
<button id="button2" >button2</button>
</body>
</html>
<script type="text/javascript">
document.getElementById("button").onclick=function(){
object.replyAfter10Seconds(" hello ");
}
document.getElementById("button2").onclick=function(){
object.reply2(" hello ");
}
function data_from_js(msg){
var tag=document.createElement('div');
tag.innerHTML="message from python";
document.body.appendChild(tag);
alert(msg['name']);
}
</script>
<style>
body{
border:solid black 1px;
}
</style>
</doctype>"""
class Qbutton(QObject):
def __init__(self):
super(Qbutton,self).__init__()
self.timer = QTimer()
self.timer.setSingleShot(True)
self.timer.setInterval(10 * 1000)
self.timer.timeout.connect(self.reply)
@Slot(str)
def replyAfter10Seconds(self,recd):
self._replyText = recd
print "Started timer"
self.timer.start()
@Slot()
def reply(self):
#r=QMessageBox.information(self,"Info",msg)
msgBox = QMessageBox()
msgBox.setText("python just said"+self._replyText)
msgBox.exec_()
return "I am recieving pythonic data"
#r=QMessageBox.question(self,title,recd,QMessageBox.Yes | QMessageBox.No)
@Slot(str)
def reply2(self,recd):
msgBox = QMessageBox()
msgBox.setText("python just said"+recd+ " another time")
msgBox.exec_()
return "I am recieving pythonic data"
@Slot(str)
def send_tojs(self):
pass
class adstar_gui(QWidget):
def __init__(self):
super(adstar_gui,self).__init__()
self.setWindowTitle("Adstar Wordlist Generator")
self.setMaximumWidth(5000)
self.setMaximumHeight(5000)
self.setMinimumWidth(500)
self.setMinimumHeight(500)
self.show()
print "Sample window"
def closeEvent(self,event):
self.closeEvent()
if __name__=="__main__":
Qapp=QApplication(sys.argv)
t=QWebView()
t.setHtml(html_str)
button=Qbutton()
t.page().mainFrame().addToJavaScriptWindowObject("object",button)
t.show()
t.raise_()
#t.page().mainFrame().evaluateJavaScript("data_from_js(%s);" % (json.dumps({'name':"My name is Junior"}) ))
QCoreApplication.processEvents() # does nothing as long as App.exec_() hasn't statred.
#sys.exit(Qapp.exec_())
Qapp.exec_()
这里有几个问题。首先,值得指出的是当您单击 button1 时应用程序冻结的原因:单击导致 Qt 调用事件处理程序 reply
,并且 Qt 无法处理另一个事件,直到此处理程序 returns(在根据我的经验,所有窗口系统都是这样工作的)。因此,如果您将任何长 运行 例程放入事件处理程序中,您的应用程序将冻结,直到该例程完成。任何时候事件处理程序花费的时间超过 0.05 秒,用户都会注意到。
正如 titusjan 在他的回答中指出的那样,让 Qt 在一定时间间隔后执行函数非常容易。但我认为你的问题不是关于如何处理简单的时间延迟,而是关于如何处理一个 long-运行 的过程。在我的示例代码中,我用一个计算十个一秒延迟的循环替换了你的十秒延迟,我认为这是一个更好的模型来实现你想要实现的目标。
解决方案是在另一个线程中执行长进程。您有两个选择:QThreads,它是 Qt 环境的一部分,以及 Python 线程。它们都可以工作,但我总是尽可能使用 Python 线程。它们有更好的文档记录并且更轻量级。将线程指定为守护进程的能力有时会使应用程序关闭更简单一些。此外,将多线程程序转换为使用多进程的程序也更容易。我在下面的示例代码中使用了 Python 线程。
那么问题来了,应用程序怎么知道副线程什么时候结束呢?为此,您必须创建自定义 Qt Signal。当它完成工作时,您的辅助线程会发出此信号,并且主应用程序会连接一个 Slot 以在发生这种情况时执行某些操作。如果您要制作自定义 Qt Signal,您必须在 QObject 的子类中声明它,就像我在示例中所做的那样。
不用说,所有标准的多线程问题都得处理。
import sys
import json
import threading
from time import sleep
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import QWebView, QWebSettings
from PySide.QtNetwork import QNetworkRequest
from PySide.QtCore import QObject, Slot, Signal
html_str="""<!doctype>
<html>
<body>hello world
<button id="button" >button1</button>
<button id="button2" >button2</button>
</body>
</html>
<script type="text/javascript">
document.getElementById("button").onclick=function(){
object.reply(" hello ");
}
document.getElementById("button2").onclick=function(){
object.reply2(" hello ");
}
function data_from_js(msg){
var tag=document.createElement('div');
tag.innerHTML="message from python";
document.body.appendChild(tag);
alert(msg['name']);
}
</script>
<style>
body{
border:solid black 1px;
}
</style>
</doctype>"""
class Qbutton(QObject):
def __init__(self):
super(Qbutton,self).__init__()
self.long_thread = LongPythonThread()
self.long_thread.thread_finished.connect(self.reply2_finished)
@Slot(str)
def reply(self,recd):
print("reply")
t = threading.Thread(target=self.long_thread.long_thread, args=(recd,))
t.daemon = True
t.start()
@Slot(str)
def reply2(self,recd):
print("reply2")
msgBox = QMessageBox()
msgBox.setText("python just said"+recd)
msgBox.exec_()
return "I am receiving pythonic data"
@Slot(str)
def reply2_finished(self, recd):
print("reply2 finished")
msgBox = QMessageBox()
msgBox.setText("python just said"+recd+ " another time")
msgBox.exec_()
@Slot(str)
def send_tojs(self):
pass
class LongPythonThread(QObject):
thread_finished = Signal(str)
def __init__(self):
super(LongPythonThread,self).__init__()
def long_thread(self, recd):
for n in range(10):
sleep(1.0)
print("Delayed for {:d}s".format(n+1))
self.thread_finished.emit(recd)
if __name__=="__main__":
Qapp=QApplication(sys.argv)
t=QWebView()
t.setHtml(html_str)
button=Qbutton()
t.page().mainFrame().addToJavaScriptWindowObject("object",button)
t.show()
#t.page().mainFrame().evaluateJavaScript("data_from_js(%s);" % (json.dumps({'name':"My name is Junior"}) ))
QCoreApplication.processEvents()
#sys.exit(Qapp.exec_())
Qapp.exec_()