如何将 asyncio 任务添加到 pyqt5 事件循环,以便它运行并避免从未等待的错误?

How do I add asyncio task to pyqt5 event loop so that it runs and avoids the never awaited error?

我是 asyncio 的新手,我想在我的 pyqt 应用程序中利用它来处理通过 tcp 连接进行的通信。

我做了这个简单的演示来学习如何在异步上下文中处理 QT 循环。我已经看到其他 post 与此相关,但它们比我现在尝试做的要复杂得多。因此,我在单独的 window 中启动服务器客户端,以便它进行监听,并尝试通过我的小部件上的简单按钮单击事件发送消息。尽可能准系统....我的问题是它不起作用。

我希望能够进行一次信息交换以及端口为流打开的情况。我认为这些任务在 asyncio 中会很简单,但在这一点上让它与 qt 很好地配合似乎很困难。

现在我得到

RuntimeWarning: coroutine 'PushButton.sendmessage' was never awaited
  rslt = self.__app.exec_()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

我不知道从哪里开始解决这个问题。

test.py

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, pyqtSlot
import sys
import asyncio
from asyncqt import QEventLoop
from async_client import tcp_echo_client


class PushButton(QWidget):

    loop = None

    def __init__(self,app_loop):
        super(PushButton,self).__init__()
        self.initUI()
        self.loop = loop

    def initUI(self):
        self.setWindowTitle("PushButton")
        self.setGeometry(400,400,300,260)
        self.send_bttn = QPushButton(self)
        self.send_bttn.setText("SEnd Message")      
        self.send_bttn.clicked.connect(self.sendmessage)
        self.send_bttn.move(100,100)
        
    @pyqtSlot()    
    async def sendmessage(self):
        run_command = asyncio.create_task(tcp_echo_client("hey",self_loop))
        asyncio.run(run_command)



if __name__ == '__main__':
    app = QApplication(sys.argv)
    loop = QEventLoop(app)
    asyncio.set_event_loop(loop)  # NEW must set the event loop    sys.exit(app.exec_()) 
    
    ex = PushButton(loop)
    ex.show()
    with loop:
        loop.run_forever()

简单的 echo 客户端例程

import asyncio


async def tcp_echo_client(message, loop):
    reader, writer = await asyncio.open_connection('127.0.0.1', 8889,
                                                   loop=loop)

    print('Send: %r' % message)
    writer.write(message.encode())

    data = await reader.read(100)
    print('Received: %r' % data.decode())

    print('Close the socket')
    writer.close()

和响应服务器


import asyncio

async def handle_echo(reader, writer):
    data = await reader.read(100)
    message = data.decode()
    addr = writer.get_extra_info('peername')
    print("Received %r from %r" % (message, addr))

    print("Send: %r" % message)
    writer.write(data)
    await writer.drain()

    print("Close the client socket")
    writer.close()

loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_echo, '127.0.0.1', 8889, loop=loop)
server = loop.run_until_complete(coro)

# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()

问题是,如果你调用一个协程的插槽,那么你必须使用 asyncSlot 装饰器,也不要使用 ayncion.run()await(除了消除其他 错别字).

import sys
import asyncio

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton

from asyncqt import QEventLoop, asyncSlot
from async_client import tcp_echo_client


class PushButton(QWidget):

    loop = None

    def __init__(self, loop=None):
        super(PushButton, self).__init__()
        self.initUI()
        self.loop = loop or asyncio.get_event_loop()

    def initUI(self):
        self.setWindowTitle("PushButton")
        self.setGeometry(400, 400, 300, 260)
        self.send_bttn = QPushButton(self)
        self.send_bttn.setText("SEnd Message")
        self.send_bttn.clicked.connect(self.sendmessage)
        self.send_bttn.move(100, 100)

    @asyncSlot()
    async def sendmessage(self):
        await tcp_echo_client("hey", self.loop)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    loop = QEventLoop(app)
    asyncio.set_event_loop(loop)

    ex = PushButton(loop)
    ex.show()
    with loop:
        loop.run_forever()