我正在构建一个GUI,该GUI通过COM端口连接到Windows上的蓝牙设备,这很容易。在启动过程中,工具栏类列出了可用的COM端口。
class ToolBar(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self)
self.menu = self.menuBar()
#self.menu.installEventFilter(self)
self.coms = QMenu('COM Selection', self)
self.group = QActionGroup(self.coms)
self.checkSerial = serial.tools.list_ports.comports()
self.populate()
for text in self.texts:
action = QAction(text, self.coms, checkable=True, checked=text==self.texts[0])
self.coms.addAction(action)
self.group.addAction(action)
self.group.setExclusive(True)
self.group.triggered.connect(self.onTriggered)
self.menu.addMenu(self.coms)
def populate(self):
self.texts = [p.device for p in self.checkSerial]
self.checkSerial.clear()
return False
def onTriggered(self, action):
print(action.text())
Constants.comPort = action.text()
当按下开始/停止按钮时,MainWindow类中的closeCom函数将读取用户选择以连接到COM。但是,其中一个COM已被serial.tools.list_ports列出,当我尝试连接以读取数据时,它似乎已连接并升高。如果我在不调用工具栏的情况下启动GUI,因此未列出COMS,则在脚本中以文本形式输入COM端口是没有问题的。
def closeCom(self):
if self.log==False:
self.serialPort.close()
print('stopped logging')
self.timer.stop()
self.saver.stop()
self.file.close()
if self.log==True:
self.portName = 'COM4'
self.portName = Constants.comPort
baudRate = 9600
self.serialPort = serialPlot(self.portName, baudRate) # initializes all required variables
self.serialPort.readSerialStart() #data starting to save
我尝试使用蓝牙,因为虽然找不到如何使用pyserial中的设备地址连接到设备的示例,但该模块在查找设备时似乎无法连接。
import bluetooth
devices = bluetooth.discover_devices(lookup_names=True)
print(type(devices))
print("Devices found: %s" % len(devices))
for item in devices:
print(item)
因此,总而言之,我的问题是,我可以在将它们以pyserial列出后清除COM,以便我可以连接吗;如果没有,我可以使用蓝牙库找到的地址进行连接。我愿意接受建议,因为这整个星期我都坚持不懈!
这是完整的代码,以防万一。
非常感谢!
import serial
import serial.tools.list_ports
import time
import threading
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QSizePolicy, QSlider, QSpacerItem, \
QVBoxLayout, QWidget
from PyQt5.QtWidgets import QAction, QActionGroup, QMenu, QApplication
import pyqtgraph as pg
import sys
import queue
import bluetooth
devices = bluetooth.discover_devices(lookup_names=True)
print(type(devices))
print("Devices found: %s" % len(devices))
for item in devices:
print(item)
#import win32com.client
#wmi = win32com.client.GetObject("winmgmts:")
#for serial in wmi.InstancesOf("Win32_SerialPort"):
# print (serial.Name, serial.Description)
app = QtGui.QApplication(sys.argv)
class Constants(object):
#class for storing Gui constants & user input
#all static rather than object elements
#Varible to store com port that is being logged
comPort="select com port"
class ToolBar(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self)
self.menu = self.menuBar()
#self.menu.installEventFilter(self)
self.coms = QMenu('COM Selection', self)
self.group = QActionGroup(self.coms)
self.checkSerial = serial.tools.list_ports.comports()
self.populate()
for text in self.texts:
action = QAction(text, self.coms, checkable=True, checked=text==self.texts[0])
self.coms.addAction(action)
self.group.addAction(action)
self.group.setExclusive(True)
self.group.triggered.connect(self.onTriggered)
self.menu.addMenu(self.coms)
def populate(self):
self.texts = [p.device for p in self.checkSerial]
self.checkSerial.clear()
return False
def onTriggered(self, action):
print(action.text())
Constants.comPort = action.text()
class serialPlot:
def __init__(self, serialPort = 'COM', serialBaud = 9600):
self.port = serialPort
self.baud = serialBaud
self.isRun = True
self.isReceiving = False
self.thread = None
#creating text file for data save
self.file = open("data.txt","w+")
header = "elapsed time milliseconds, rpm, L power watts, R power watts, \n"
self.file.write(str(header))
self.file.close()
print('Trying to connect to: ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
try:
self.serialConnection = serial.Serial(serialPort, serialBaud, timeout=4)
print('Connected to ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
except:
print("Failed to connect with " + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
#timer for logging
self.elaspedTime = QtCore.QTime()
self.elaspedTime.start()
TimeAxisItem.startTime = self.elaspedTime
def readSerialStart(self):
if self.thread == None:
self.thread = threading.Thread(target=self.backgroundThread)
self.thread.start()
# Block till we start receiving values
while self.isReceiving != True:
time.sleep(0.1)
def dataSave(self):
self.file = open("data.txt", "a+")
self.file.write(self.valueRead.replace("\r\n","\n"))
self.file.close()
def backgroundThread(self): # retrieve data
time.sleep(1.0) # give some buffer time for retrieving data
self.serialConnection.reset_input_buffer()
while (self.isRun):
#self.serialConnection.readinto(self.rawData)
self.rawData = self.serialConnection.readline()
self.isReceiving = True
#print(self.rawData)
self.valueRead = self.rawData.decode(encoding='UTF-8',errors='ignore')
self.dataArray = self.valueRead.split('\r\n')
self.data = self.dataArray[0].split(',')
try:
dataCollect.time.append(self.elaspedTime.elapsed())
dataCollect.rpm.append(float(self.data[1]))
dataCollect.L_power.append(float(self.data[2]))
dataCollect.R_power.append(float(self.data[3]))
except ValueError and IndexError:
dataCollect.time.append(0)
dataCollect.rpm.append(0)
dataCollect.L_power.append(0)
dataCollect.R_power.append(0)
self.dataSave()#save the data
def close(self):
self.isRun = False
self.thread.join()
self.serialConnection.close()
print('Disconnected...')
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self, parent=None)
self.Tool_Bar = ToolBar(self)
self.setMenuWidget(self.Tool_Bar)
self.central_widget = QtGui.QStackedWidget()
self.setCentralWidget(self.central_widget)
self.login_widget = LoginWidget(self)
self.login_widget.startStop.clicked.connect(self.changeState)
self.login_widget.startStop.clicked.connect(self.plotter)
self.login_widget.Close.clicked.connect(self.closeGui)
self.login_widget.saveData.clicked.connect(self.saveData)
self.central_widget.addWidget(self.login_widget)
self.log=False
#creating text file for data save
self.file = open("data.txt","w+")
header = "elapsed time milliseconds, rpm, L power watts, R power watts, \n"
self.file.write(str(header))
self.file.close()
#data
for i in range(1000):
dataCollect.time.append(0)
dataCollect.rpm.append(0)
dataCollect.L_power.append(0)
dataCollect.R_power.append(0)
self.login_widget.w1.slider.valueChanged.connect(self.update_plot)
def update_plot(self):
value = dataCollect.value
dataCollect.time = queue.deque(maxlen = value)
dataCollect.rpm = queue.deque(maxlen = value)
dataCollect.L_power = queue.deque(maxlen = value)
dataCollect.R_power = queue.deque(maxlen = value)
def closeGui(self):
QtGui.QApplication(sys.argv).exit()
def closeCom(self):
if self.log==False:
self.serialPort.close()
print('stopped logging')
self.timer.stop()
self.saver.stop()
self.file.close()
if self.log==True:
self.portName = 'COM4'
self.portName = Constants.comPort
baudRate = 9600
self.serialPort = serialPlot(self.portName, baudRate) # initializes all required variables
self.serialPort.readSerialStart() #data starting to save
def changeState(self):
self.log = not self.log
self.closeCom()
def Display(self):
#update labels
rpm = str(sum([dataCollect.rpm[i] for i in range(80)])/80)
self.login_widget.LiveValueRPM.setText(rpm)
L = str(sum([dataCollect.L_power[i] for i in range(80)])/80)
self.login_widget.LiveValueLeft.setText(L)
R = str(sum([dataCollect.R_power[i] for i in range(80)])/80)
self.login_widget.LiveValueRight.setText(R)
QtGui.QApplication.processEvents()
def updater(self):
self.curve0.setData(dataCollect.time, dataCollect.L_power, clear=True)
self.curve1.setData(dataCollect.time, dataCollect.R_power, clear=True)
self.curve2.setData(dataCollect.time, dataCollect.rpm, clear=True)
QtGui.QApplication.processEvents()
def plotter(self):
#self.login_widget.plot.getPlotItem().addLegend()
self.curve0 = self.login_widget.plot.getPlotItem().plot(clear=True,pen='b', name='rpm')
self.curve1 = self.login_widget.plot.getPlotItem().plot(pen='g', name='Left Power')
self.curve2 = self.login_widget.plot.getPlotItem().plot(pen='r', name='Right Power')
self.login_widget.plot.getPlotItem().setLabel('bottom', 'Time [hrs,mins,secs]')
self.login_widget.plot.getPlotItem().setLabel('left', "Power [W] or [rpm]")
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updater)
self.timer.start(10)
self.saver = QtCore.QTimer()
self.saver.timeout.connect(self.Display)
self.saver.start(1000)
def saveData(self):
file_name = QtGui.QFileDialog.getSaveFileName(self,"Save file", "default name here", ".txt;;.csv" )
saveLocation = file_name[0]
fileExtension = file_name[1]
#The data saved during logging as txt file
data=[]
with open("data.txt", "r") as f:
for line in f:
print(len(line))
if len(line) < 64:
data.append(line.rstrip())
#saving the data in the preferred form and file location for user
with open(''.join([saveLocation,fileExtension]), "w", encoding='utf-8') as f:
for s in data:
f.write(s)
f.write("\n")
class TimeAxisItem(pg.AxisItem):
"""Internal timestamp for x-axis"""
startTime = QtCore.QTime().currentTime()
def __init__(self, *args, **kwargs):
super(TimeAxisItem, self).__init__(*args, **kwargs)
def tickStrings(self, values, scale, spacing):
"""Function overloading the weak default version to provide timestamp"""
return [TimeAxisItem.startTime.addMSecs(value).toString('hh:mm:ss') for value in values]
#return [QtCore.QTime().currentTime().toString('hh:mm:ss') for value in values]
class dataCollect(object):
time = queue.deque(maxlen = 1000)
rpm = queue.deque(maxlen = 1000)
L_power = queue.deque(maxlen = 1000)
R_power = queue.deque(maxlen = 1000)
class Slider(QWidget):
def __init__(self, parent=None):
super().__init__()
hbox = QHBoxLayout()
self.slider = QSlider()
self.slider.setOrientation(Qt.Horizontal)
self.slider.setTickPosition(QSlider.TicksBelow)
self.slider.setTickInterval(100)
self.slider.setMinimum(0)
self.slider.setMaximum(1000)
self.slider.setValue(500)
self.slider.valueChanged.connect(self.changedValue)
#self.label = QLabel("50")
#self.label.setFont(QtGui.QFont("Sanserif", 15))
hbox.addWidget(self.slider)
#hbox.addWidget(self.label)
self.setLayout(hbox)
#self.show()
def changedValue(self):
size = self.slider.value()
#self.label.setText(str(size))
dataCollect.value = size#number of data points to set graph
#print(size)
class LoginWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self)
hbox = QtGui.QGridLayout(self)
L0_layout = QtGui.QGridLayout()
L1_layout = QtGui.QGridLayout()
R_layout = QtGui.QGridLayout()
L0_widgets = QtGui.QFrame()
L0_widgets.setFrameShape(QtGui.QFrame.StyledPanel)
L0_widgets.setLayout(L0_layout)
L1_widgets = QtGui.QFrame()
L1_widgets.setFrameShape(QtGui.QFrame.StyledPanel)
L1_widgets.setLayout(L1_layout)
self.startStop = QtGui.QPushButton('Start / Stop Plotting')
L0_layout.addWidget(self.startStop, 1, 0)
self.Close = QtGui.QPushButton('Close')
L0_layout.addWidget(self.Close, 2, 0)
self.saveData = QtGui.QPushButton('Save Data')
L0_layout.addWidget(self.saveData, 3, 0)
#create space
self.Blank = QtGui.QLabel()
L0_layout.addWidget(self.Blank, 4, 0)
#set bold text for vaule display
myFont=QtGui.QFont()
myFont.setBold(True)
#live readout left power
self.LeftPower = QtGui.QLabel('Average Left Power (W)')
self.LeftPower.setFont(myFont)
self.LeftPower.setStyleSheet("font: 15pt")
self.LeftPower.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
L0_layout.addWidget(self.LeftPower, 5, 0)
self.LiveValueLeft = QtGui.QLabel("0")
self.LiveValueLeft.setFont(myFont)
#self.LiveValueLeft.setStyleSheet("font: 15pt")
self.LiveValueLeft.setStyleSheet("QLabel{color: green; font: 15pt}")
self.LiveValueLeft.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
L0_layout.addWidget(self.LiveValueLeft, 6, 0)
#live readout right power
self.RightPower = QtGui.QLabel('Average Right Power (W)')
self.RightPower.setFont(myFont)
self.RightPower.setStyleSheet("font: 15pt")
self.RightPower.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
L0_layout.addWidget(self.RightPower, 7, 0)
self.LiveValueRight = QtGui.QLabel("0")
self.LiveValueRight.setFont(myFont)
#self.LiveValueRight.setStyleSheet("font: 15pt")
self.LiveValueRight.setStyleSheet("QLabel{color: red; font: 15pt}")
self.LiveValueRight.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
L0_layout.addWidget(self.LiveValueRight, 8, 0)
#live readout rpm
self.RPM = QtGui.QLabel('Average rpm')
self.RPM.setFont(myFont)
self.RPM.setStyleSheet("font: 15pt")
self.RPM.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
L0_layout.addWidget(self.RPM, 9, 0)
self.LiveValueRPM = QtGui.QLabel("0")
self.LiveValueRPM.setFont(myFont)
#self.LiveValueRPM.setStyleSheet("font: 15pt")
self.LiveValueRPM.setStyleSheet("QLabel{color: blue; font: 15pt}")
self.LiveValueRPM.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
L0_layout.addWidget(self.LiveValueRPM, 10, 0)
self.AxisAdjust = QtGui.QLabel('Move Silder to Adjust X Axis')
self.AxisAdjust.setFont(myFont)
self.AxisAdjust.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
L0_layout.addWidget(self.AxisAdjust, 11, 0)
#self.w1 = CustomWidget()
self.w1 = Slider()
L0_layout.addWidget(self.w1, 12, 0)
self.plot = pg.PlotWidget()
self.plot = pg.PlotWidget(axisItems={'bottom': TimeAxisItem(orientation='bottom')})
self.plot.plotItem.setMouseEnabled(x=False, y=False)
self.plot.showGrid(x=True,y=True,alpha=1)
self.plot.addLegend()
splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
splitter1.addWidget(L0_widgets)
splitter3 = QtGui.QSplitter(QtCore.Qt.Horizontal)
splitter3.addWidget(splitter1)
splitter3.addWidget(self.plot)
hbox.addWidget(splitter3)
self.setGeometry(300, 300, 300, 200)
self.setLayout(hbox)
if __name__ == '__main__':
window = MainWindow()
window.show()
app.exec_()