diagram eigefügt

This commit is contained in:
jens 2025-07-10 23:10:13 +02:00
parent 74e4b5f199
commit 7ba87e9d36
2 changed files with 152 additions and 3 deletions

View File

@ -6,6 +6,11 @@ from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout
QGroupBox, QStatusBar, QMessageBox, QCheckBox) QGroupBox, QStatusBar, QMessageBox, QCheckBox)
from PyQt6.QtCore import QTimer, Qt from PyQt6.QtCore import QTimer, Qt
from PyQt6.QtGui import QFont from PyQt6.QtGui import QFont
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import datetime
from collections import deque
class PHControllerGUI(QMainWindow): class PHControllerGUI(QMainWindow):
def __init__(self): def __init__(self):
@ -13,7 +18,7 @@ class PHControllerGUI(QMainWindow):
self.serial_connection = None self.serial_connection = None
self.updating_tube_combo = False # Flag to prevent recursive tube commands self.updating_tube_combo = False # Flag to prevent recursive tube commands
self.setWindowTitle("Arduino pH Controller") self.setWindowTitle("Arduino pH Controller")
self.setGeometry(100, 100, 600, 400) self.setGeometry(100, 100, 900, 800) # Larger window for plot
# Central Widget # Central Widget
central_widget = QWidget() central_widget = QWidget()
@ -136,6 +141,30 @@ class PHControllerGUI(QMainWindow):
main_layout.addWidget(status_group) main_layout.addWidget(status_group)
main_layout.addWidget(control_group) main_layout.addWidget(control_group)
# pH Plot Group
plot_group = QGroupBox("pH-Verlauf")
plot_layout = QVBoxLayout()
# Plot controls
plot_controls_layout = QHBoxLayout()
self.clear_plot_btn = QPushButton("Diagramm löschen")
self.clear_plot_btn.clicked.connect(self.clear_ph_plot)
self.plot_enabled_checkbox = QCheckBox("pH-Aufzeichnung")
self.plot_enabled_checkbox.setChecked(True)
plot_controls_layout.addWidget(self.plot_enabled_checkbox)
plot_controls_layout.addWidget(self.clear_plot_btn)
plot_controls_layout.addStretch()
# pH Plot Widget
self.ph_plot = PHPlotWidget()
plot_layout.addLayout(plot_controls_layout)
plot_layout.addWidget(self.ph_plot)
plot_group.setLayout(plot_layout)
main_layout.addWidget(plot_group)
# Status Bar # Status Bar
self.status_bar = QStatusBar() self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar) self.setStatusBar(self.status_bar)
@ -260,6 +289,8 @@ class PHControllerGUI(QMainWindow):
self.auto_dose_btn.setEnabled(enabled) self.auto_dose_btn.setEnabled(enabled)
self.volume_spin.setEnabled(enabled) self.volume_spin.setEnabled(enabled)
self.reset_volume_checkbox.setEnabled(enabled) self.reset_volume_checkbox.setEnabled(enabled)
self.clear_plot_btn.setEnabled(enabled)
self.plot_enabled_checkbox.setEnabled(enabled)
def send_command(self, command): def send_command(self, command):
print(command) print(command)
@ -432,6 +463,9 @@ class PHControllerGUI(QMainWindow):
try: try:
ph_value = float(data[1:]) ph_value = float(data[1:])
self.ph_label.setText(f"pH: {ph_value:.2f}") self.ph_label.setText(f"pH: {ph_value:.2f}")
# Add to plot if recording is enabled
if self.plot_enabled_checkbox.isChecked():
self.ph_plot.add_ph_value(ph_value)
except ValueError: except ValueError:
pass pass
elif data.startswith('P'): # Pump status elif data.startswith('P'): # Pump status
@ -488,6 +522,119 @@ class PHControllerGUI(QMainWindow):
self.close_connection() self.close_connection()
event.accept() event.accept()
def clear_ph_plot(self):
"""Clear the pH plot data"""
self.ph_plot.clear_data()
self.status_bar.showMessage("pH-Diagramm gelöscht")
class PHPlotWidget(QWidget):
def __init__(self):
super().__init__()
self.figure = Figure(figsize=(8, 4))
self.canvas = FigureCanvas(self.figure)
self.ax = self.figure.add_subplot(111)
# Data storage (keep last 300 points = 10 minutes at 2-second intervals)
self.times = deque(maxlen=300)
self.ph_values = deque(maxlen=300)
self.start_time = None # Track when recording started
# Setup plot
self.ax.set_xlabel('Zeit')
self.ax.set_ylabel('pH-Wert')
self.ax.set_title('pH-Verlauf')
self.ax.grid(True, alpha=0.3)
self.ax.set_ylim(0, 14) # pH range 0-14
# Layout
layout = QVBoxLayout()
layout.addWidget(self.canvas)
self.setLayout(layout)
# Plot line
self.line, = self.ax.plot([], [], 'b-', linewidth=2, label='pH-Wert')
self.ax.legend()
# Initial draw
self.canvas.draw()
def add_ph_value(self, ph_value):
"""Add new pH value with current timestamp"""
current_time = datetime.datetime.now()
# Set start time on first value
if self.start_time is None:
self.start_time = current_time
# Calculate relative time in seconds
relative_time = (current_time - self.start_time).total_seconds()
self.times.append(relative_time)
self.ph_values.append(ph_value)
self.update_plot()
def update_plot(self):
"""Update the plot with current data"""
if len(self.times) > 0 and len(self.ph_values) > 0:
# Update line data
self.line.set_data(self.times, self.ph_values)
# Auto-scale x-axis to show recent data
if len(self.times) > 1:
self.ax.set_xlim(min(self.times), max(self.times))
# Auto-scale y-axis around data with some margin
if len(self.ph_values) > 0:
min_ph = min(self.ph_values)
max_ph = max(self.ph_values)
margin = 0.5
self.ax.set_ylim(max(0, min_ph - margin), min(14, max_ph + margin))
# Format x-axis for time display (seconds/minutes/hours)
self.format_time_axis()
# Redraw
self.canvas.draw()
def format_time_axis(self):
"""Format the x-axis to show time in appropriate units"""
if len(self.times) == 0:
return
max_time = max(self.times)
if max_time < 120: # Less than 2 minutes - show seconds
self.ax.set_xlabel('Zeit (Sekunden)')
# No need to change tick formatting for seconds
elif max_time < 7200: # Less than 2 hours - show minutes
self.ax.set_xlabel('Zeit (Minuten)')
# Convert tick labels to minutes
import matplotlib.ticker as ticker
def format_minutes(x, pos):
return f'{x/60:.1f}'
self.ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_minutes))
else: # Show hours
self.ax.set_xlabel('Zeit (Stunden)')
# Convert tick labels to hours
import matplotlib.ticker as ticker
def format_hours(x, pos):
return f'{x/3600:.1f}'
self.ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_hours))
def clear_data(self):
"""Clear all data points"""
self.times.clear()
self.ph_values.clear()
self.start_time = None # Reset start time
self.line.set_data([], [])
self.ax.set_xlim(0, 1)
self.ax.set_ylim(0, 14)
self.ax.set_xlabel('Zeit') # Reset to default label
# Reset tick formatter to default
import matplotlib.ticker as ticker
self.ax.xaxis.set_major_formatter(ticker.ScalarFormatter())
self.canvas.draw()
def main(): def main():
app = QApplication(sys.argv) app = QApplication(sys.argv)
window = PHControllerGUI() window = PHControllerGUI()

View File

@ -1,2 +1,4 @@
PyQt6==6.9.0 PyQt6==6.7.0
pyserial==3.5 pyserial==3.5
matplotlib==3.7.2
numpy<2.0.0