Issue
I am trying to plot telemetry data from an RC vehicle (not being specific because I want to apply this code for all kinds of projects, currently a rocket) to a computer using serial port. I want to show the COM port selection, connection status, and all kinds of data in labels and plots.
I am trying to use pyqtgraph but it is only allowing me to place a single plot filling my window.
How do I go about making a window that has multiple plots and labels in the same window?
This is my code so far.
#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_())
Solution
To answer Your question, You need to gather some knowledge about layouts and element positioning.
In Your example, You are not using any layouts, only moving widgets to fixed positions in pixels. This approach might work for very limited cases, but it's strongly recommended to use Layouts.
Here is some introduction to it.
This will help You to make Your GUI look consistent in different windows sizes. As opposite to Your current approach, where You have to maximize Your windows size to see all the elements.
What if someone running Your app at resolution of only 800 x 600 pixels, while Your elements occupies 1024 x 768 screen size?
They simply won't fit to that screen.
This issue can be solved by using layouts.
Also have a look at very nice designer tool, which simplify the whole process of user interface creation.
To help You in Your current situation, You must again position Your plots and resize them, as they are not inside any layout. So just as You did with Your label and Combobox, set position and size of Your plots:
self.plots.move(0, 35)
self.plots.resize(1024, 768)
This will move plots to pixel [ x=0, y=35 ] and resize them to 1024px x 768x pixels.
Answered By - Domarm
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.