python - Qt (PyQt) 事件循环内的 xmlrpc?

标签 python multithreading pyqt pyqt5 xml-rpc

我想将 xmlprc 服务器与 Qt 应用程序一起使用,在这种情况下是 Qt Web 引擎,以便 xmlrpc 客户端可以向其发送命令。我必须与现有客户合作。

这里有两个事件循环。运行 Qt 的 exec_() 是阻塞的,运行 xmlrpc 的 serve_forever() 是阻塞的。

我首先想知道您是否知道 Python 的 xmlrpc 库可以与 Qt 的事件循环很好地集成。所以一切都可以开箱即用。

<小时/>

我没有找到,所以我尝试在线程内启动 rpc 服务器。它基本上可以工作,但无法与 Qt 小部件交互。我们怎样才能做到这一点?

下面是 pyqt5==5.12 和 PyQtWebEngine。

from PyQt5.QtCore import QUrl from PyQt5.QtCore import QThread
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget

from xmlrpc.server import SimpleXMLRPCServer

# Qt
URL_START = "http://ddg.gg"

app = QApplication([])
window = QWidget()
layout = QVBoxLayout()

webview = QWebEngineView()
webview.setUrl(QUrl(URL_START))

minibuffer = QWebEngineView()
mb_prompt = """
<html>
<div> hello minibuffer </div>
</html>
"""
minibuffer.setHtml(mb_prompt)

layout.addWidget(webview)
layout.addWidget(minibuffer)

window.setWindowTitle("My browser")
window.setLayout(layout)
window.show()

# xmlrpc
def hello(name):
    return "hello " + name

def set_minibuffer(name):
    return minibuffer.setHtml(mb_prompt.replace("minibuffer", name))


class RPCThread(QThread):
    def run(self):
        # sleep a little bit to make sure QApplication is running.
        self.sleep(1)
        print("--- starting server…")
        self.rpcserver = SimpleXMLRPCServer(("localhost", 8282))
        self.rpcserver.register_function(hello)
        self.rpcserver.register_function(set_minibuffer)

        self.rpcserver.serve_forever()

class RPCWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.thread = RPCThread(self)
        self.thread.start()

rpcwidget = RPCWidget()
rpcwidget.show()

# Qt main loop.
print("--- Qt loop")
app.exec_()

来自客户端:

from xmlrpc.client import ServerProxy
client = ServerProxy("http://localhost:8282"
print(client.hello("me"))  # OK
# Trying to set the web engine's html content:
print(client.set_minibuffer("me")  # fails

失败了

--- starting server…
127.0.0.1 - - [27/Mar/2019 16:52:01] "POST /RPC2 HTTP/1.1" 200 -
Received signal 11 SEGV_MAPERR 000000000000
#0 0x7f9b5bf3e8bf <unknown>
#1 0x7f9b5bf3ecbb <unknown>
#2 0x7f9b5bf3f33e <unknown>
#3 0x7f9b6667ef20 <unknown>
#4 0x7f9b5b9f3515 <unknown>
#5 0x7f9b5b9f37eb <unknown>
#6 0x7f9b5b9f3b79 <unknown>
#7 0x7f9b5a4731c0 QtWebEngineCore::WebContentsAdapter::setContent()
#8 0x7f9b610c7bf5 QWebEnginePage::setHtml()
#9 0x7f9b612fb824 meth_QWebEngineView_setHtml
#10 0x0000005030d5 <unknown>
#11 0x000000506859 _PyEval_EvalFrameDefault
#12 0x000000504c28 <unknown>
#13 0x00000058644b <unknown>
#14 0x00000059ebbe PyObject_Call
#15 0x000000507c17 _PyEval_EvalFrameDefault
[…]

最佳答案

您有 2 个错误:

  • 您不能也不得直接从另一个线程修改 GUI,在您的情况下,set_minibuffer() 是在 SimpleXMLRPCServer 所在的线程中执行的。相反,您必须通过 pyqtSignalQMetaObject::invokeMethod、自定义 QEventsQTimer.singleShot(0, ... )

  • 我不是 xmlrpc 专家,但 Why can't xmlrpc client append item to list accessable via xmlrpc server procedure? ,它表示您必须将 SimpleXMLRPCServer 对象传递给构造函数 allow_none = True

考虑到上述情况,解决方案是:

from functools import partial

# ...

from PyQt5.QtCore import QTimer

# ...

def set_minibuffer(name):
    html = mb_prompt.replace("minibuffer", name)
    wrapper = partial(minibuffer.setHtml, html)
    QTimer.singleShot(0, wrapper)
    return html


class RPCThread(QThread):
    def run(self):
        # sleep a little bit to make sure QApplication is running.
        self.sleep(1)
        print("--- starting server…")
        self.rpcserver = SimpleXMLRPCServer(("localhost", 8282), allow_none=True)
        self.rpcserver.register_function(hello)
        self.rpcserver.register_function(set_minibuffer)

        self.rpcserver.serve_forever()

# ...

关于python - Qt (PyQt) 事件循环内的 xmlrpc?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55382314/

相关文章:

python - 编写可重用代码

python - Django 2.0 slug urls.py 意外关键字参数 'slug'

c# - 跨线程隐藏方法

python - 动态创建的选项卡-选项卡关闭时销毁对象

python - PySide/PyQt : PointingHandCursor recipes?

python - Django StreamingHttpResponse 没有设置正确的文件名

python - 如何多次运行一个函数

java - 像使用同步关键字时一样,在 CyclicBarrier 或 CountDownLatch 上进行缓存刷新

java - 生产者 - Java 中的消费者多线程

python - 从按键事件设置小部件