diagram eigefügt
This commit is contained in:
parent
74e4b5f199
commit
7ba87e9d36
149
gui/main.py
149
gui/main.py
@ -6,6 +6,11 @@ from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout
|
||||
QGroupBox, QStatusBar, QMessageBox, QCheckBox)
|
||||
from PyQt6.QtCore import QTimer, Qt
|
||||
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):
|
||||
def __init__(self):
|
||||
@ -13,7 +18,7 @@ class PHControllerGUI(QMainWindow):
|
||||
self.serial_connection = None
|
||||
self.updating_tube_combo = False # Flag to prevent recursive tube commands
|
||||
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 = QWidget()
|
||||
@ -136,6 +141,30 @@ class PHControllerGUI(QMainWindow):
|
||||
main_layout.addWidget(status_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
|
||||
self.status_bar = QStatusBar()
|
||||
self.setStatusBar(self.status_bar)
|
||||
@ -260,6 +289,8 @@ class PHControllerGUI(QMainWindow):
|
||||
self.auto_dose_btn.setEnabled(enabled)
|
||||
self.volume_spin.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):
|
||||
print(command)
|
||||
@ -432,6 +463,9 @@ class PHControllerGUI(QMainWindow):
|
||||
try:
|
||||
ph_value = float(data[1:])
|
||||
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:
|
||||
pass
|
||||
elif data.startswith('P'): # Pump status
|
||||
@ -488,6 +522,119 @@ class PHControllerGUI(QMainWindow):
|
||||
self.close_connection()
|
||||
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():
|
||||
app = QApplication(sys.argv)
|
||||
window = PHControllerGUI()
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
PyQt6==6.9.0
|
||||
pyserial==3.5
|
||||
PyQt6==6.7.0
|
||||
pyserial==3.5
|
||||
matplotlib==3.7.2
|
||||
numpy<2.0.0
|
||||
Loading…
Reference in New Issue
Block a user