问题: 我使用 Python 和 QtCreator (PyQt5 qtpy) 开发了一个 GUI,它从热电偶和压力计获取实时数据,并在 GUI 中持续显示最新的测量结果。 这些测量结果还用于计算后台的体积流量。 我的目标是最终生成两个输出。一方面,当我终止整个测量过程(使用“结束测量”按钮)时,应创建一个包含上述实时测量结果的 csv 文件 --> 效果很好。
第二个输出应该是一个(或两个)图,在 x 轴上显示运行时间(以秒为单位),在主 y 轴上显示温度(该参数在第 70 行中称为 t_input),在辅助轴上显示体积流量y 轴(该参数在第 132 行中称为 qv)。 其实这在我眼里是一个很简单的问题。因为我知道如何创建绘图,但它只是不想在这里工作。我之前的尝试总是导致每次迭代都会创建多个图形。 我还会给你代码和 GUI 的截图,希望你能理解。
如果您有任何疑问等,请告诉我。
我希望你能帮助我,我已经很感激你的帮助了。
致以诚挚的问候
############################################# Imports für QtPy ##################################################
from qtpy import QtWidgets
from frm_ProjectC3.mainwindow import Ui_MainWindow
from PyQt5.QtCore import QTimer
############################################# Import für Messelemente (ADAM), Plotting und CSV-Erstellung ##################################################
import serial, math, time, sys, csv
from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
############################################# Globale Parameter ##################################################
pi = math.pi
date = datetime.now() # Getting the current date and time - Soll dazu genutzt werden, um der txt-Datei automatisert einen Namen geben zu können.
datetime_for_csv = date.strftime("%m%d%Y_%H_%M_%S") # Soll genutzt werden um in die CSV Datei das richtige Datum zu printen
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super().__init__(parent)
self.setWindowTitle("Projekt: C3")
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.timer = QTimer()
self.ser = serial.Serial()
self.ser.baudrate = 9600
self.ser.port = self.ui.cbBox_serialPort.currentText()
#Es benötigt die changed Funktion "currentTextChanged" um den Wert des Com-Ports neu zu setzen.
self.ui.cbBox_serialPort.currentTextChanged.connect(self.on_combobox_changed)
self.ser.stopbits = 1
self.ser.bytesize = 8
self.ser.parity = 'N'
self.ui.btn_MessungStarten.clicked.connect(self.btn_messungStarten_click) #Führt den Button "Messung Starten" aus.
#self.ui.btn_MessungStarten.clicked.connect(self.update_plot)
self.ui.btn_MessungBeenden.clicked.connect(self.btn_messungBeenden_click) #Führt den Button "Messung Beenden" aus.
# currentTextChanged Abfrage des aktuellen Wertes
def on_combobox_changed(self):
self.ser.port = self.ui.cbBox_serialPort.currentText()
def showTime(self):
#Hier werden die Temperaturen durch die entsprechenden ADAM-Controller ausgelesen.
self.ser.write('#00\r'.encode('utf-8'))
s1 = self.ser.read_until('\r'.encode('utf-8'), size=None) # all channels
t = []
for i in range(1, len(s1) - 2, 7):
t.append(float(s1[i:i + 7]))
#Hier werden die Drücke durch die entsprechenden ADAM-Controller ausgelesen (ABER noch in VOLT).
self.ser.write('#02\r'.encode('utf-8'))
s2 = self.ser.read_until('\r'.encode('utf-8'), size=None) # specific channel
p = []
for i in range(1, len(s2) - 2, 7):
p.append(float(s2[i:i + 7]))
############################################################ Hier werden die Temp_Labels mit dem aktuellsten Wert beschrieben. ###########################################################
self.ui.temp_0_output.setText(str(round(t[0],2)))
self.ui.ambientTemp_lbl.setText(str(round(t[0],2)))
self.ui.temp_1_output.setText(str(round(t[1],2)))
self.ui.temp_2_output.setText(str(round(t[2],2)))
self.ui.temp_3_output.setText(str(round(t[3],2)))
self.ui.temp_4_output.setText(str(round(t[4],2)))
self.ui.temp_5_output.setText(str(round(t[5],2)))
self.ui.temp_6_output.setText(str(round(t[6],2)))
self.ui.temp_7_output.setText(str(round(t[7],2)))
############################################################ Arithmetisches Mittel der Temp_1-3 ergeben T_input (Temperatur über Schüttgut) ###########################################################
t_input = round(((t[1] + t[2] + t[3]) / 3), 2)
self.ui.temp_aboveBulk_lbl.setText(str(t_input))
########################################################### Hier werden die Press_Labels mit dem aktuellsten Wert beschrieben --> Werden aufgrund der Linearität von Volt in [Pa] umgerechnet werden. ###########################################################
press_1 = round(1.013 * 10 ** 5 + (p[0] * 20 * 100), 2)
self.ui.press_1_output.setText(str(press_1))
d_press_1 = round(1.013 * 10 ** 5 + (p[1] * 5 * 100), 2)
self.ui.press_dp1_output.setText(str(d_press_1))
d_press_2 = round(1.013 * 10 ** 5 + (p[2] * 5 * 100), 2)
self.ui.press_dp2_output.setText(str(d_press_2))
d_press_3 = round(1.013 * 10 ** 5 + (p[3] * 5 * 100), 2)
self.ui.press_dp3_output.setText(str(d_press_3))
########################################################### Beschriftung der Tabelle in der GUI ###########################################################
# Anzahl der Tabellen-Zeilen:
position = self.ui.dataOutput_tbl.rowCount()
self.ui.dataOutput_tbl.insertRow(position)
# self.ui.dataOutput_tbl.setItem(row, column,item) --> row = position, column = 0 - 13 [0 = Datum; 1-8 = Temps.; 9-13 = Drücke], und item sind die für die jeweilige Spalte zu übergebenden Daten
self.ui.dataOutput_tbl.setItem(position, 0, QtWidgets.QTableWidgetItem(datetime_for_csv))
self.ui.dataOutput_tbl.setItem(position, 1, QtWidgets.QTableWidgetItem(str(round(t[0],2))))
self.ui.dataOutput_tbl.setItem(position, 2, QtWidgets.QTableWidgetItem(str(round(t[1],2))))
self.ui.dataOutput_tbl.setItem(position, 3, QtWidgets.QTableWidgetItem(str(round(t[2],2))))
self.ui.dataOutput_tbl.setItem(position, 4, QtWidgets.QTableWidgetItem(str(round(t[3],2))))
self.ui.dataOutput_tbl.setItem(position, 5, QtWidgets.QTableWidgetItem(str(round(t[4],2))))
self.ui.dataOutput_tbl.setItem(position, 6, QtWidgets.QTableWidgetItem(str(round(t[5],2))))
self.ui.dataOutput_tbl.setItem(position, 7, QtWidgets.QTableWidgetItem(str(round(t[6],2))))
self.ui.dataOutput_tbl.setItem(position, 8, QtWidgets.QTableWidgetItem(str(round(t[7],2))))
self.ui.dataOutput_tbl.setItem(position, 9, QtWidgets.QTableWidgetItem(str(press_1)))
self.ui.dataOutput_tbl.setItem(position, 10, QtWidgets.QTableWidgetItem(str(d_press_1)))
self.ui.dataOutput_tbl.setItem(position, 11, QtWidgets.QTableWidgetItem(str(d_press_2)))
self.ui.dataOutput_tbl.setItem(position, 12, QtWidgets.QTableWidgetItem(str(d_press_3)))
########################################################### Implementierung der Volumenstromberechnung / Implementation of flow rate ###########################################################
if self.ui.feuchtigkeit_umg_txtfield.text() == "":
phi_umg = 60
else:
phi_umg = float(self.ui.feuchtigkeit_umg_txtfield.text())
p_umg = 1.013 * 10 ** 5 # Umgebungsdruck [Pa]
dBlende = 0.040 # Blendendurchmesser [m]
dRohr = 0.054 # Rohrdurchmesser [m]
Kappa = 1.4 # Isentropenkoeffizient
beta = dBlende / dRohr # Durchmesserverhältnis
p2 = press_1 - d_press_3 # Druck nach der Blende (Achtung Wirkdruck) TODO: Welche Drücke sind hier genau gemeint - press_1 und press_3 richtig????
dynVis = (0.0000000119249 * t[0] ** 3 - 0.0000263646 * t[0] ** 2 + 0.0487178 * t[0] + 17.2638) * 1e-6 # dyn. Viskosität am Blendeneintritt (in diesem Druckbereich im wesentlichen nur Temperaturabhängig)
self.ui.dynVis_output.setText(str(dynVis))
psu = 6.11213 ** (17.5043 * t[0] / (241.2 + t[0])) # Sättigungsdampfdruck bei Umgebungstemperatur[°C]
self.ui.saettdampfdruck_umgebtemp_output.setText(str(round(psu,4)))
xu = 0.622 * psu / ((p_umg / (phi_umg/100)) - psu) # Wassergehalt
self.ui.wassergehalt_output.setText(str(round(xu,8)))
pse = 6.11213 ** (17.5043 * t[0] / (241.2 + t[0])) # Sättigungsdampfdruck bei Eintrittstemperatur [°C] #TODO: hier muss glaube eine andere Temp. anstelle von t[0] hin!!!
self.ui.saettdampfdruck_eintrittstemp_output.setText(str(round(pse,4)))
phie = (p_umg + press_1) * xu / (pse * (0.622 + xu)) # Luftfeuchtigkeit Blendeneintritt
self.ui.luftfeuchtigkeit_blendeintritt_output.setText(str(round(phie,5)))
R = 287.058 / (1.0 - phie * pse * 0.377 / press_1) # spez. Gaskonstante
rho = press_1 / ((t[0] + 273.15) * R) # Dichte der Luft
epsilon = 1.0 - (0.351 + 0.256 * pow(beta, 4.0) + 0.93 * pow(beta, 8.0)) * (1.0 - pow((p2 / press_1), (1.0 / Kappa))) # Expansionszahl (Korrekturfaktor)
c = 0.6
A1 = epsilon * pow(dBlende, 2.0) * pow((2.0 * d_press_3 * rho), 0.5) / (dynVis * dRohr * pow((1.0 - pow(beta, 4.0)), 0.5)) #TODO: press_3 hier richtig???
x1 = c * A1
qm = pi / 4.0 * dynVis * dRohr * x1 # Massenstrom [kg/s]
qv = qm / rho # Volumenstrom [m³/s]
self.ui.volumenstrom_output.setText(str(qv))
########################################################### Implementation of PLOT ###########################################################
""" - How do I create the x-axis for the runtime in seconds
- How do I create the datasets for my both y-axis:
- my primary y-axis (t_input in line 71 is a value which updates permanently)
- my secon. y-axis (qv in line 133 is a value which updates permanently)"""
plt.ion()
fig, axis = plt.subplots(2, figsize=(14,9))
fig.suptitle("Temperatur- und Volumenstrommessung")
def btn_messungStarten_click(self):
try:
self.ser.open()
self.timer.start(1000)
self.timer.timeout.connect(self.showTime)
self.ui.com_errormsg_lbl.setText("Sie nutzen " + self.ser.port)
self.ui.com_errormsg_lbl.setStyleSheet("color: green")
self.ui.com_finally_lbl.setText("Die Messung wird gestartet!")
self.ui.com_finally_lbl.setStyleSheet("color: green")
self.ui.btn_MessungStarten.setDisabled(True)
except:
if self.ser.port == "--Auswählen--":
self.ui.com_errormsg_lbl.setText("Bitte wählen Sie einen geeigneten COM-Port.")
self.ui.com_errormsg_lbl.setStyleSheet("color: red")
self.ui.com_finally_lbl.setText("")
else:
self.ui.com_errormsg_lbl.setText("Sie adressieren einen unbelegten COM-Port: " + self.ser.port)
self.ui.com_errormsg_lbl.setStyleSheet("color: red")
self.ui.com_finally_lbl.setText("")
def btn_messungBeenden_click(self):
if self.ser.port == "--Auswählen--":
self.ui.com_errormsg_lbl.setText("Sie haben auf 'Messung stoppen' gedrückt")
self.ui.com_errormsg_lbl.setStyleSheet("color: blue")
self.ui.com_finally_lbl.setText("Das Programm wurde erfolgreich beendet!")
self.ui.com_finally_lbl.setStyleSheet("color: green")
self.timer.stop()
self.ser.close()
else:
self.ui.com_errormsg_lbl.setText("Beendet: " + self.ser.port)
self.ui.com_errormsg_lbl.setStyleSheet("color: blue")
self.ui.com_finally_lbl.setText("Das Programm wurde erfolgreich beendet!")
self.ui.com_finally_lbl.setStyleSheet("color: green")
self.timer.stop()
self.ser.close()
path, ok = QtWidgets.QFileDialog.getSaveFileName(self, "'Data_Output' speichern",datetime_for_csv, '(*.txt)')
if ok:
columns = range(self.ui.dataOutput_tbl.columnCount())
header = [self.ui.dataOutput_tbl.horizontalHeaderItem(column).text() for column in columns]
with open(path, 'w', newline="") as csvfile:
writer = csv.writer(csvfile, delimiter=';', escapechar=' ', quoting=csv.QUOTE_NONE)
writer.writerow(header)
for row in range(self.ui.dataOutput_tbl.rowCount()):
writer.writerow(self.ui.dataOutput_tbl.item(row, column).text() for column in columns)
self.ui.btn_MessungStarten.setDisabled(False)
#Erstellt die GUI bzw. lädt die generierten Elemente aus der mainwindow.py
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
我尝试捕获 t_input 和 qv 值并将它们放入列表中,但它不起作用。 (仅错误消息:进程已完成,退出代码 -1073740791 (0xC0000409))