Python 在使用标签和组合框的同时从串行端口使用 pyqtgraph 进行绘图
Python plotting with pyqtgraph from serial port while using labels and combo boxes
我正在尝试使用串行端口将来自 RC 车辆的遥测数据(不具体,因为我想将此代码应用于所有类型的项目,目前是火箭)绘制到计算机上。我想在标签和绘图中显示 COM 端口选择、连接状态和各种数据。
我正在尝试使用 pyqtgraph,但它只允许我放置一个图来填充我的 window。
如何制作一个 window 在同一个 window 中有多个绘图和标签?
到目前为止,这是我的代码。
#includes
import sys
import serial
import atexit
import numpy as np
import pyqtgraph as pg
from pyqtgraph import PlotWidget, plot
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QTimer
from serial.tools.list_ports import comports
from time import sleep
#Beginning of code
class window(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
#Serial port object
self.serialPort = serial.Serial()
#Main window object
self.setGeometry(100, 300, 500, 500)
self.setWindowTitle("Rocket Telemetry")
#Available COMs label object
self.lbl1 = QtWidgets.QLabel(self)
self.lbl1.move(5,0)
self.lbl1.setText("Available COMS")
#Serial Port status label object
self.lbl2 = QtWidgets.QLabel(self)
self.lbl2.move(200,0)
#COM ports drop down list combo box object
self.cbox_coms = QtWidgets.QComboBox(self)
self.cbox_coms.move(85,0)
#Plot objects
self.plots = pg.GraphicsLayoutWidget(self)
# Some random data for scatter plot
x = np.random.normal(size=1000)
y = np.random.normal(size=1000)
# Add subplots
plot1 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=0, col=0, title="Plot @ row 1, column 1")
plot2 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=0, col=1, title="Plot @ row 1, column 2")
plot3 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=0, col=2, title="Plot @ row 1, column 3")
plot4 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=1, col=0, title="Plot @ row 2, column 1")
plot5 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=2, col=0, title="Plot @ row 3, column 1")
# self.pg_layout.showMaximized()
#Show the main page maximized
self.showMaximized()
#If the user has not chosen an option for COM Port or has chosen "Disconnect", the the COM Port lists will be updated. otherwise, go to opening the serial port
def updateComPorts(self):
if self.cbox_coms.currentIndex() <= 0:
print("Updating COM Ports")
self.lbl2.setText("Disconnected!")
self.cbox_coms.clear()
self.cbox_coms.addItem("Disconnect")
for port in comports():
self.cbox_coms.addItem(port.name)
QTimer.singleShot(1000,self.updateComPorts)
else:
QTimer.singleShot(1,self.checkSerialCom)
#Serial port opening and closing takes place here
def checkSerialCom(self):
#If the serial port is open, check if the user wants to disconnect and close the serial port or if they want to change the serial port COM port
if self.serialPort.is_open:
if self.cbox_coms.currentText() == "Disconnect":
print("Closing Serial Port")
self.serialPort.close()
self.lbl2.setText("Disconnected!")
QTimer.singleShot(1000,self.updateComPorts)
elif self.serialPort.port != self.cbox_coms.currentText():
print("Changing Serial Port")
self.serialPort.close()
self.serialPort.port = self.cbox_coms.currentText()
self.serialPort.baudrate = 115200
self.serialPort.open()
print("Opening Serial Port at", self.serialPort.port)
while not self.serialPort.is_open:
print("Serial port is not open yet")
sleep(1)
self.lbl2.setText("Connected!")
QTimer.singleShot(1,self.readSerialData)
else:
QTimer.singleShot(1,self.readSerialData)
else:
if self.cbox_coms.currentText() == "Disconnect":
self.lbl2.setText("Disconnected!")
QTimer.singleShot(1000,self.updateComPorts)
else:
self.serialPort.port = self.cbox_coms.currentText()
self.serialPort.baudrate = 115200
self.serialPort.open()
print("Opening Serial Port at", self.serialPort.port)
while not self.serialPort.is_open:
print("Serial port is not open yet")
sleep(1)
self.lbl2.setText("Connected!")
QTimer.singleShot(1,self.readSerialData)
#Read serial data
def readSerialData(self):
if self.serialPort.is_open and self.serialPort.in_waiting:
print(self.serialPort.read())
QTimer.singleShot(1,ex.updateUIObjects)
#Update UI objects with new data
def updateUIObjects(self):
QTimer.singleShot(1,ex.updateComPorts)
print("\n\n\n\nProgram Started\n")
#main
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = window()
QTimer.singleShot(1,ex.updateComPorts)
sys.exit(app.exec_())
要回答您的问题,您需要了解一些有关布局和元素定位的知识。
在您的示例中,您没有使用任何布局,只是将小部件移动到以像素为单位的固定位置。这种方法可能适用于非常有限的情况,但强烈建议使用布局。
这里有一些 introduction。
这将帮助您使您的 GUI 在不同 windows 尺寸下看起来一致。与您当前的方法相反,您必须最大化 windows 大小才能看到所有元素。
如果有人 运行 您的应用分辨率仅为 800 x 600 像素,而您的元素占用 1024 x 768 屏幕尺寸怎么办?
它们根本不适合那个屏幕。
这个问题可以通过布局来解决。
也可以看看非常好的 designer tool,它简化了用户界面创建的整个过程。
为了在您当前的情况下帮助您,您必须再次定位您的绘图并调整它们的大小,因为它们不在任何布局内。
所以就像你对你的标签和组合框所做的那样,设置你的地块的位置和大小:
self.plots.move(0, 35)
self.plots.resize(1024, 768)
这会将图移动到像素 [x=0, y=35] 并将它们的大小调整为 1024px x 768x 像素。
我正在尝试使用串行端口将来自 RC 车辆的遥测数据(不具体,因为我想将此代码应用于所有类型的项目,目前是火箭)绘制到计算机上。我想在标签和绘图中显示 COM 端口选择、连接状态和各种数据。
我正在尝试使用 pyqtgraph,但它只允许我放置一个图来填充我的 window。
如何制作一个 window 在同一个 window 中有多个绘图和标签?
到目前为止,这是我的代码。
#includes
import sys
import serial
import atexit
import numpy as np
import pyqtgraph as pg
from pyqtgraph import PlotWidget, plot
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QTimer
from serial.tools.list_ports import comports
from time import sleep
#Beginning of code
class window(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
#Serial port object
self.serialPort = serial.Serial()
#Main window object
self.setGeometry(100, 300, 500, 500)
self.setWindowTitle("Rocket Telemetry")
#Available COMs label object
self.lbl1 = QtWidgets.QLabel(self)
self.lbl1.move(5,0)
self.lbl1.setText("Available COMS")
#Serial Port status label object
self.lbl2 = QtWidgets.QLabel(self)
self.lbl2.move(200,0)
#COM ports drop down list combo box object
self.cbox_coms = QtWidgets.QComboBox(self)
self.cbox_coms.move(85,0)
#Plot objects
self.plots = pg.GraphicsLayoutWidget(self)
# Some random data for scatter plot
x = np.random.normal(size=1000)
y = np.random.normal(size=1000)
# Add subplots
plot1 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=0, col=0, title="Plot @ row 1, column 1")
plot2 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=0, col=1, title="Plot @ row 1, column 2")
plot3 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=0, col=2, title="Plot @ row 1, column 3")
plot4 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=1, col=0, title="Plot @ row 2, column 1")
plot5 = self.plots.addPlot(x=x, y=y, pen=None, symbol='x', row=2, col=0, title="Plot @ row 3, column 1")
# self.pg_layout.showMaximized()
#Show the main page maximized
self.showMaximized()
#If the user has not chosen an option for COM Port or has chosen "Disconnect", the the COM Port lists will be updated. otherwise, go to opening the serial port
def updateComPorts(self):
if self.cbox_coms.currentIndex() <= 0:
print("Updating COM Ports")
self.lbl2.setText("Disconnected!")
self.cbox_coms.clear()
self.cbox_coms.addItem("Disconnect")
for port in comports():
self.cbox_coms.addItem(port.name)
QTimer.singleShot(1000,self.updateComPorts)
else:
QTimer.singleShot(1,self.checkSerialCom)
#Serial port opening and closing takes place here
def checkSerialCom(self):
#If the serial port is open, check if the user wants to disconnect and close the serial port or if they want to change the serial port COM port
if self.serialPort.is_open:
if self.cbox_coms.currentText() == "Disconnect":
print("Closing Serial Port")
self.serialPort.close()
self.lbl2.setText("Disconnected!")
QTimer.singleShot(1000,self.updateComPorts)
elif self.serialPort.port != self.cbox_coms.currentText():
print("Changing Serial Port")
self.serialPort.close()
self.serialPort.port = self.cbox_coms.currentText()
self.serialPort.baudrate = 115200
self.serialPort.open()
print("Opening Serial Port at", self.serialPort.port)
while not self.serialPort.is_open:
print("Serial port is not open yet")
sleep(1)
self.lbl2.setText("Connected!")
QTimer.singleShot(1,self.readSerialData)
else:
QTimer.singleShot(1,self.readSerialData)
else:
if self.cbox_coms.currentText() == "Disconnect":
self.lbl2.setText("Disconnected!")
QTimer.singleShot(1000,self.updateComPorts)
else:
self.serialPort.port = self.cbox_coms.currentText()
self.serialPort.baudrate = 115200
self.serialPort.open()
print("Opening Serial Port at", self.serialPort.port)
while not self.serialPort.is_open:
print("Serial port is not open yet")
sleep(1)
self.lbl2.setText("Connected!")
QTimer.singleShot(1,self.readSerialData)
#Read serial data
def readSerialData(self):
if self.serialPort.is_open and self.serialPort.in_waiting:
print(self.serialPort.read())
QTimer.singleShot(1,ex.updateUIObjects)
#Update UI objects with new data
def updateUIObjects(self):
QTimer.singleShot(1,ex.updateComPorts)
print("\n\n\n\nProgram Started\n")
#main
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = window()
QTimer.singleShot(1,ex.updateComPorts)
sys.exit(app.exec_())
要回答您的问题,您需要了解一些有关布局和元素定位的知识。
在您的示例中,您没有使用任何布局,只是将小部件移动到以像素为单位的固定位置。这种方法可能适用于非常有限的情况,但强烈建议使用布局。
这里有一些 introduction。
这将帮助您使您的 GUI 在不同 windows 尺寸下看起来一致。与您当前的方法相反,您必须最大化 windows 大小才能看到所有元素。
如果有人 运行 您的应用分辨率仅为 800 x 600 像素,而您的元素占用 1024 x 768 屏幕尺寸怎么办?
它们根本不适合那个屏幕。
这个问题可以通过布局来解决。
也可以看看非常好的 designer tool,它简化了用户界面创建的整个过程。
为了在您当前的情况下帮助您,您必须再次定位您的绘图并调整它们的大小,因为它们不在任何布局内。 所以就像你对你的标签和组合框所做的那样,设置你的地块的位置和大小:
self.plots.move(0, 35)
self.plots.resize(1024, 768)
这会将图移动到像素 [x=0, y=35] 并将它们的大小调整为 1024px x 768x 像素。