用于处理PHP的异步python函数

问题描述 投票:1回答:1

[我正在使用pyqt5开发一个可通过QtWebChannel用作Web应用程序的应用程序,当您单击带有html的“ Python”文本的按钮时,将执行一个js函数(login_py),该函数可恢复输入值(# request)并显示一个gif(loader.gif),表示正在处理请求,然后通过后端将该值发送给python并通过python执行php_function函数,该函数处理来自php文件(login.php)的调用最后,删除gif,并将php响应放置在一个跨度(#response)中。

我的问题是,当按下“ Python”按钮时,应用程序窗口会暂时冻结,并且这种情况并不令人愉快,此外,表示它正在加载的gif从未出现,我想知道是否可以使请求更类似于对ajax的请求,窗口不会冻结。

或者,有一个带有文本“ Javascript”的按钮通过ajax处理请求,就像我希望对“ Python”按钮的请求一样。

我澄清说,原始的login.php文件倾向于花费未知的时间来响应,为了不放置整个原始代码,我保留了这个修改后的login.php以模拟我的情况。

login.php

<?php
error_reporting(0);
sleep(5);
header("Content-type: application/json");
if ($argv[1]) {
    $var = $argv[1];
} else {
    $var = $_GET['one'];
}
echo '{"one":"'.$var.'"}';
?>

test.py

from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets, QtWebChannel
import json
import os

#path = "http://localhost/test.html"
path = "test.html"
dirr = os.path.join(os.path.dirname(os.path.realpath(__file__)), path)

class Backend(QtCore.QObject):
    valueChanged = QtCore.pyqtSignal(str)
    def __init__(self, parent=None):
        super().__init__(parent)
        self._value = ""

    @QtCore.pyqtProperty(str)
    def value(self):
        return self._value

    @value.setter
    def value(self, v):
        self._value = v
        self.valueChanged.emit(v)

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.webEngineView = QtWebEngineWidgets.QWebEngineView()
        lay = QtWidgets.QVBoxLayout(self)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self.webEngineView)
        backend = Backend(self)
        backend.valueChanged.connect(self.php_function)
        self.channel = QtWebChannel.QWebChannel()
        self.channel.registerObject("backend", backend)
        self.webEngineView.page().setWebChannel(self.channel)
        #self.webEngineView.setUrl(QtCore.QUrl(path))
        self.webEngineView.setUrl(QtCore.QUrl.fromLocalFile(dirr))

    @QtCore.pyqtSlot(str)
    def php_function(self, value):
        data = os.popen('php login.php ' + value).read()
        data = json.loads(data)
        self.webEngineView.page().runJavaScript('$("#loader").removeAttr("src"); $("#response").text("'+data["one"]+'");')

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

test.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="qrc:///qtwebchannel/qwebchannel.js"></script>
    <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
</head>
<script type="text/javascript">
var backend = null;
if (typeof QWebChannel !== "undefined") {
    new QWebChannel(qt.webChannelTransport, function (channel) {
        backend = channel.objects.backend;
    });
}
function login_py(request) {
    $("#loader").attr("src", "loader.gif");
    backend.value = request;
}
function login_js(request) {
    $("#loader").attr("src", "loader.gif");
    $.ajax({
        url: 'login.php',
        type: 'get',
        data: {one:request},
        success: function (data) {
            $("#loader").removeAttr("src");
            $("#response").text(data.one);
        }
    })
}
</script>
<body>
<input id="request">
<button onclick="login_js($('#request').val())">Javascript</button>
<button onclick="login_py($('#request').val())">Python</button>
<br>
<br>
<img id="loader">
<span id="response"></span>
</body>
</html>
python asynchronous pyqt pyqt5
1个回答
2
投票

os.popen()是一项阻塞任务,它将冻结Qt的事件循环,从而导致GUI冻结,因此,您必须使用不阻塞的方法(例如,使用QProcess)替换该方法,或者在极端情况下在该方法上运行另一个线程,在这种情况下,使用QProcess并异步发送信息就足够了。

import json
import os

from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets, QtWebChannel


current_dir = os.path.dirname(os.path.realpath(__file__))
# path = "http://localhost/test.html"
path = "test.html"
dirr = os.path.join(current_dir, path)


class Backend(QtCore.QObject):
    valueChanged = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._value = ""

    @QtCore.pyqtProperty(str, notify=valueChanged)
    def value(self):
        return self._value

    @value.setter
    def value(self, v):
        self._value = v
        self.valueChanged.emit(v)


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.webEngineView = QtWebEngineWidgets.QWebEngineView()
        lay = QtWidgets.QVBoxLayout(self)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self.webEngineView)
        backend = Backend(self)
        backend.valueChanged.connect(self.php_function)
        self.channel = QtWebChannel.QWebChannel()
        self.channel.registerObject("backend", backend)
        self.webEngineView.page().setWebChannel(self.channel)
        # self.webEngineView.setUrl(QtCore.QUrl(path))
        self.webEngineView.setUrl(QtCore.QUrl.fromLocalFile(dirr))

        self._php_process = PhpProcess()
        self._php_process.resultChanged.connect(self.process_php_result)

    @QtCore.pyqtSlot(str)
    def php_function(self, value):
        self._php_process.execute(value)

    @QtCore.pyqtSlot(str)
    def process_php_result(self, data):
        data = json.loads(data)
        self.webEngineView.page().runJavaScript(
            '$("#loader").removeAttr("src"); $("#response").text("'
            + data["one"]
            + '");'
        )


class PhpProcess(QtCore.QObject):
    resultChanged = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._process = QtCore.QProcess(self)
        self._process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)

    @QtCore.pyqtSlot(str)
    def execute(self, parameter):
        self._process.start("php", [os.path.join(current_dir, "login.php"), parameter])

    @QtCore.pyqtSlot()
    def on_readyReadStandardOutput(self):
        output = self._process.readAllStandardOutput()
        self.resultChanged.emit(output.data().decode())


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())
© www.soinside.com 2019 - 2024. All rights reserved.