PyQt5和Asyncio

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

是否可以让一个UDP服务器以异步函数的形式运行,接收数据,然后将其传递给同样以异步函数形式运行的(PyQt5)widget?

我们的想法是,当进入服务器的数据被更新时,它也会更新widget。

我已经有了一个简单的UDP服务器和一个(PyQt5)widget,它们都能独立工作,但我很难把它们结合起来,让它们都异步运行并交换数据(服务器向widget传输数据)

[更新]

以下是我正在尝试的一个小工具。

import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow
import asyncio


class Speedometer(QMainWindow):

    angleChanged = QtCore.pyqtSignal(float)

    def __init__(self, parent = None):

        QtWidgets.QWidget.__init__(self, parent)

        self._angle = 0.0

        self._margins = 20

        self._pointText = {0: "40", 30: "50", 60: "60", 90: "70", 120: "80",
                           150:"" , 180: "", 210: "",
                          240: "0", 270: "10", 300: "20", 330: "30", 360: ""}
    def paintEvent(self, event):

        painter = QtGui.QPainter()
        painter.begin(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        painter.fillRect(event.rect(), self.palette().brush(QtGui.QPalette.Window))
        self.drawMarkings(painter)
        self.drawNeedle(painter)

        painter.end()

    def drawMarkings(self, painter):

        painter.save()
        painter.translate(self.width()/2, self.height()/2)
        scale = min((self.width() - self._margins)/120.0,
                    (self.height() - self._margins)/60.0)
        painter.scale(scale, scale)

        font = QtGui.QFont(self.font())
        font.setPixelSize(10)
        metrics = QtGui.QFontMetricsF(font)

        painter.setFont(font)
        painter.setPen(self.palette().color(QtGui.QPalette.Shadow))

        i = 0

        while i < 360:

                if i % 30 == 0 and (i <150 or i > 210):
                    painter.drawLine(0, -40, 0, -50)
                    painter.drawText(-metrics.width(self._pointText[i])/2.0, -52,
                                     self._pointText[i])
                elif i <135 or i > 225:
                    painter.drawLine(0, -45, 0, -50)

                painter.rotate(15)
                i += 15

        painter.restore()

    def drawNeedle(self, painter):

        painter.save()
        painter.translate(self.width()/2, self.height()/1.5)
        painter.rotate(self._angle)
        scale = min((self.width() - self._margins)/120.0,
                    (self.height() - self._margins)/120.0)
        painter.scale(scale, scale) 

        painter.setPen(QtCore.Qt.NoPen)
        painter.setBrush(self.palette().brush(QtGui.QPalette.Shadow))

        painter.drawPolygon(
            QtGui.QPolygon([QtCore.QPoint(-10, 0), QtCore.QPoint(0, -45), QtCore.QPoint(10, 0),
                      QtCore.QPoint(0, 5), QtCore.QPoint(-10, 0)])
            )

        painter.setBrush(self.palette().brush(QtGui.QPalette.Highlight))

        painter.drawPolygon(
            QtGui.QPolygon([QtCore.QPoint(-5, -25), QtCore.QPoint(0, -45), QtCore.QPoint(5, -25),
                      QtCore.QPoint(0, -30), QtCore.QPoint(-5, -25)])
            )

        painter.restore()

    def sizeHint(self):

        return QtCore.QSize(150, 150)

    def angle(self):
        return self._angle

#    @pyqtSlot(float)
    def setAngle(self, angle):

        if angle != self._angle:
            self._angle = angle
            self.angleChanged.emit(angle)
            self.update()

    angle = QtCore.pyqtProperty(float, angle, setAngle)

    @staticmethod
    def mainLoopSpd():
      while True:
            app = QApplication(sys.argv)

            window = QtWidgets.QWidget()
            spd = Speedometer()
            spinBox = QtWidgets.QSpinBox()
            #spd.setAngle(100)
            spinBox.setRange(0, 359)
            spinBox.valueChanged.connect(spd.setAngle)

            layout = QtWidgets.QVBoxLayout()
            layout.addWidget(spd)
            layout.addWidget(spinBox)
            window.setLayout(layout)

            window.show()
            app.exec_()
            #await asyncio.sleep(1)

            sys.exit(app.exec_())

下面是一个UDP套接字端的实现,它也在打印控制台中的数值。

import socket

class UDPserver:
    def __init__(self, parent= None):

        self.localIP     = "127.0.0.1"
        self.localPort   = 20002
        self.bufferSize  = 1024

        self.UDPServerSocket = socket.socket(family= socket.AF_INET, type=socket.SOCK_DGRAM)  # Create a socket object

        self.UDPServerSocket.bind((self.localIP, self.localPort))                                      

        print("UDP server up and listening")

        self.counter= 1

    @staticmethod
    def  mainLoopUDPserver():

            serv= UDPserver()
        #while(True):

            bytesAddressPair = serv.UDPServerSocket.recvfrom(serv.bufferSize)                     # Receive data from the socket
            message = bytesAddressPair[0]                                               # The output of the recvfrom() function is a 2-element array
                                                                                      # First element is the message
            address = bytesAddressPair[1]                                               # Second element is the address of the sender

            newMsg= "{}".format(message)

            serv.counter=serv.counter+1
            NumMssgReceived = "#Num of Mssg Received:{}".format(serv.counter)


            newMsg= newMsg.replace("'","")
            newMsg= newMsg.replace("b","")
            newMsg= newMsg.split("/")


            eastCoord= float(newMsg[0])
            northCoord= float(newMsg[1])
            vehSpeed= float(newMsg[2])

            agYaw= float(newMsg[3])


            eastCoordStr="East Coordinate:{}".format(newMsg[0])
            northCoordStr="North Coordinate:{}".format(newMsg[1])
            vehSpeedStr= "Vehicle Speed:{}".format(newMsg[2])
            agYawStr="Yaw Angle:{}".format(newMsg[3])


            print(NumMssgReceived)
            print(vehSpeedStr)

下面是调用这两个函数的主函数。

from speedometer import Speedometer
import asyncio
from pyServer import UDPserver

class mainApp:
    #vel = 0
    def __init__(self):
        self.velo = 0
        self.queue= asyncio.Queue(0)

    async def server(self):

        while True:

            self.velo= UDPserver.mainLoopUDPserver()
            print("THIS IS VELO{}",self.velo)
            #await self.queue.put(self.velo)

            #vel= await self.queue.get()
            #return vel
            #print("ASSDASDSADSD{}",vel)

            await asyncio.sleep(0)
            #print("HI, vel Received={}",self.veloc)
        #return velo

    async def widget(self):
        while True:
            #vel =  await self.queue.get()
            #print("Hola xDDDDDDD", vel)
            print(">>>>>>>>>>>>>>>NextIteration>>>>>>>>>>>>>>")
            await Speedometer.mainLoopSpd()
            await asyncio.sleep(0)


loop= asyncio.get_event_loop()
mApp= mainApp()

loop.create_task(mApp.server())
loop.create_task(mApp.widget())
loop.run_forever()

所以,当我运行它的时候,它监听服务器,一旦我开始通过UDP发送数据,它就会收到第一条数据,然后打开小部件,小部件运行得很好,但它让服务器停止,它不再接收任何数据。

正如你在评论中看到的那样,我也一直在玩Asyncio队列,但我没有任何真正的收获。

我的理想方案是服务器接收数据并将其传递给小组件,这样它就会随着传入的数据而更新,但现在我只是希望它们都能独立工作。

谢谢你

python pyqt pyqt5 python-asyncio coroutine
1个回答
0
投票

应该清楚,你的UDP服务器并不是异步运行的。

asyncio的逻辑是一切都使用事件循环作为基础,默认情况下Qt不支持,所以你必须使用库,如 qasync(python -m pip install qasync)和 asyncqt(python -m pip install asyncqt)

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

Speedometer.py

from PyQt5 import QtCore, QtGui, QtWidgets


class Speedometer(QtWidgets.QWidget):
    angleChanged = QtCore.pyqtSignal(float)

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

        self._angle = 0.0

        self._margins = 20

        self._pointText = {
            0: "40",
            30: "50",
            60: "60",
            90: "70",
            120: "80",
            150: "",
            180: "",
            210: "",
            240: "0",
            270: "10",
            300: "20",
            330: "30",
            360: "",
        }

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        painter.fillRect(event.rect(), self.palette().brush(QtGui.QPalette.Window))
        self.drawMarkings(painter)
        self.drawNeedle(painter)

    def drawMarkings(self, painter):

        painter.save()
        painter.translate(self.width() / 2, self.height() / 2)
        scale = min(
            (self.width() - self._margins) / 120.0,
            (self.height() - self._margins) / 60.0,
        )
        painter.scale(scale, scale)

        font = QtGui.QFont(self.font())
        font.setPixelSize(10)
        metrics = QtGui.QFontMetricsF(font)

        painter.setFont(font)
        painter.setPen(self.palette().color(QtGui.QPalette.Shadow))

        i = 0

        while i < 360:

            if i % 30 == 0 and (i < 150 or i > 210):
                painter.drawLine(0, -40, 0, -50)
                painter.drawText(
                    -metrics.width(self._pointText[i]) / 2.0, -52, self._pointText[i]
                )
            elif i < 135 or i > 225:
                painter.drawLine(0, -45, 0, -50)

            painter.rotate(15)
            i += 15

        painter.restore()

    def drawNeedle(self, painter):

        painter.save()
        painter.translate(self.width() / 2, self.height() / 1.5)
        painter.rotate(self._angle)
        scale = min(
            (self.width() - self._margins) / 120.0,
            (self.height() - self._margins) / 120.0,
        )
        painter.scale(scale, scale)

        painter.setPen(QtCore.Qt.NoPen)
        painter.setBrush(self.palette().brush(QtGui.QPalette.Shadow))

        painter.drawPolygon(
            QtGui.QPolygon(
                [
                    QtCore.QPoint(-10, 0),
                    QtCore.QPoint(0, -45),
                    QtCore.QPoint(10, 0),
                    QtCore.QPoint(0, 5),
                    QtCore.QPoint(-10, 0),
                ]
            )
        )

        painter.setBrush(self.palette().brush(QtGui.QPalette.Highlight))

        painter.drawPolygon(
            QtGui.QPolygon(
                [
                    QtCore.QPoint(-5, -25),
                    QtCore.QPoint(0, -45),
                    QtCore.QPoint(5, -25),
                    QtCore.QPoint(0, -30),
                    QtCore.QPoint(-5, -25),
                ]
            )
        )

        painter.restore()

    def sizeHint(self):

        return QtCore.QSize(150, 150)

    def angle(self):
        return self._angle

    @QtCore.pyqtSlot(float)
    def setAngle(self, angle):

        if angle != self._angle:
            self._angle = angle
            self.angleChanged.emit(angle)
            self.update()

    angle = QtCore.pyqtProperty(float, angle, setAngle)


if __name__ == "__main__":
    import sys
    import asyncio
    from asyncqt import QEventLoop

    app = QtWidgets.QApplication(sys.argv)
    loop = QEventLoop(app)
    asyncio.set_event_loop(loop)
    with loop:
        w = Speedometer()
        w.angle = 10
        w.show()
        loop.run_forever()

服务器.py

import asyncio

from PyQt5 import QtCore


class UDPserver(QtCore.QObject):
    dataChanged = QtCore.pyqtSignal(float, float, float, float)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._transport = None
        self._counter_message = 0

    @property
    def transport(self):
        return self._transport

    def connection_made(self, transport):
        self._transport = transport

    def datagram_received(self, data, addr):
        self._counter_message += 1
        print("#Num of Mssg Received: {}".format(self._counter_message))
        message = data.decode()
        east_coord_str, north_coord_str, veh_speed_str, ag_yaw_str, *_ = message.split(
            "/"
        )
        try:
            east_coord = float(east_coord_str)
            north_coord = float(north_coord_str)
            veh_speed = float(veh_speed_str)
            ag_yaw = float(ag_yaw_str)
            self.dataChanged.emit(east_coord, north_coord, veh_speed, ag_yaw)
        except ValueError as e:
            print(e)

main.py

import sys
import asyncio

from PyQt5 import QtCore, QtWidgets
from asyncqt import QEventLoop

from speedometer import Speedometer
from server import UDPserver


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.spd = Speedometer()
        self.spinBox = QtWidgets.QSpinBox()
        self.spinBox.setRange(0, 359)
        self.spinBox.valueChanged.connect(lambda value: self.spd.setAngle(value))

        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.spd)
        layout.addWidget(self.spinBox)

    @QtCore.pyqtSlot(float, float, float, float)
    def set_data(self, east_coord, north_coord, veh_speed, ag_yaw):
        print(east_coord, north_coord, veh_speed, ag_yaw)
        self.spd.setAngle(veh_speed)


async def create_server(loop):
    return await loop.create_datagram_endpoint(
        lambda: UDPserver(), local_addr=("127.0.0.1", 20002)
    )


def main():
    app = QtWidgets.QApplication(sys.argv)
    loop = QEventLoop(app)
    asyncio.set_event_loop(loop)

    w = Widget()
    w.resize(640, 480)
    w.show()

    with loop:
        _, protocol = loop.run_until_complete(create_server(loop))
        protocol.dataChanged.connect(w.set_data)
        loop.run_forever()


if __name__ == "__main__":
    main()
© www.soinside.com 2019 - 2024. All rights reserved.