是否可以让一个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队列,但我没有任何真正的收获。
我的理想方案是服务器接收数据并将其传递给小组件,这样它就会随着传入的数据而更新,但现在我只是希望它们都能独立工作。
谢谢你
应该清楚,你的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()