titration start
This commit is contained in:
parent
918e102a0c
commit
16b0273ac8
336
gui/main.py
336
gui/main.py
@ -11,6 +11,7 @@ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
import datetime
|
import datetime
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
class PHControllerGUI(QMainWindow):
|
class PHControllerGUI(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -165,6 +166,47 @@ class PHControllerGUI(QMainWindow):
|
|||||||
|
|
||||||
main_layout.addWidget(plot_group)
|
main_layout.addWidget(plot_group)
|
||||||
|
|
||||||
|
# Titration Plot Group
|
||||||
|
titration_group = QGroupBox("Titration")
|
||||||
|
titration_layout = QVBoxLayout()
|
||||||
|
|
||||||
|
# Titration controls
|
||||||
|
titration_controls_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
# Titration parameters
|
||||||
|
self.titration_step_spin = QDoubleSpinBox()
|
||||||
|
self.titration_step_spin.setRange(0.1, 10.0)
|
||||||
|
self.titration_step_spin.setValue(0.5)
|
||||||
|
self.titration_step_spin.setSuffix(" ml")
|
||||||
|
self.titration_step_spin.setDecimals(1)
|
||||||
|
|
||||||
|
self.titration_wait_spin = QDoubleSpinBox()
|
||||||
|
self.titration_wait_spin.setRange(5.0, 60.0)
|
||||||
|
self.titration_wait_spin.setValue(10.0)
|
||||||
|
self.titration_wait_spin.setSuffix(" s")
|
||||||
|
self.titration_wait_spin.setDecimals(1)
|
||||||
|
|
||||||
|
self.start_titration_btn = QPushButton("Automatische Titration starten")
|
||||||
|
self.start_titration_btn.clicked.connect(self.toggle_auto_titration)
|
||||||
|
self.clear_titration_btn = QPushButton("Titrationsdaten löschen")
|
||||||
|
self.clear_titration_btn.clicked.connect(self.clear_titration_plot)
|
||||||
|
|
||||||
|
titration_controls_layout.addWidget(QLabel("Schritt:"))
|
||||||
|
titration_controls_layout.addWidget(self.titration_step_spin)
|
||||||
|
titration_controls_layout.addWidget(QLabel("Warten:"))
|
||||||
|
titration_controls_layout.addWidget(self.titration_wait_spin)
|
||||||
|
titration_controls_layout.addWidget(self.start_titration_btn)
|
||||||
|
titration_controls_layout.addWidget(self.clear_titration_btn)
|
||||||
|
|
||||||
|
# Titration Plot Widget
|
||||||
|
self.titration_plot = TitrationPlotWidget()
|
||||||
|
|
||||||
|
titration_layout.addLayout(titration_controls_layout)
|
||||||
|
titration_layout.addWidget(self.titration_plot)
|
||||||
|
titration_group.setLayout(titration_layout)
|
||||||
|
|
||||||
|
main_layout.addWidget(titration_group)
|
||||||
|
|
||||||
# Status Bar
|
# Status Bar
|
||||||
self.status_bar = QStatusBar()
|
self.status_bar = QStatusBar()
|
||||||
self.setStatusBar(self.status_bar)
|
self.setStatusBar(self.status_bar)
|
||||||
@ -200,6 +242,17 @@ class PHControllerGUI(QMainWindow):
|
|||||||
self.auto_dose_timer = QTimer()
|
self.auto_dose_timer = QTimer()
|
||||||
self.auto_dose_timer.timeout.connect(self.check_auto_dose_progress)
|
self.auto_dose_timer.timeout.connect(self.check_auto_dose_progress)
|
||||||
|
|
||||||
|
# Titration tracking
|
||||||
|
self.titration_active = False
|
||||||
|
self.titration_start_volume = 0.0
|
||||||
|
self.auto_titration_active = False
|
||||||
|
self.titration_step_size = 0.5 # ml per step
|
||||||
|
self.titration_wait_time = 10.0 # seconds to wait between steps
|
||||||
|
self.titration_timer = QTimer()
|
||||||
|
self.titration_timer.timeout.connect(self.titration_step)
|
||||||
|
self.titration_current_step = 0
|
||||||
|
self.titration_target_volume = 0.0
|
||||||
|
|
||||||
# Enable/disable controls based on connection
|
# Enable/disable controls based on connection
|
||||||
self.set_controls_enabled(False)
|
self.set_controls_enabled(False)
|
||||||
|
|
||||||
@ -266,6 +319,10 @@ class PHControllerGUI(QMainWindow):
|
|||||||
self.auto_dose_timer.stop()
|
self.auto_dose_timer.stop()
|
||||||
self.auto_dosing = False
|
self.auto_dosing = False
|
||||||
|
|
||||||
|
# Stop titration timer
|
||||||
|
self.titration_timer.stop()
|
||||||
|
self.auto_titration_active = False
|
||||||
|
|
||||||
self.connect_btn.setText("Verbinden")
|
self.connect_btn.setText("Verbinden")
|
||||||
self.status_bar.showMessage("Getrennt")
|
self.status_bar.showMessage("Getrennt")
|
||||||
self.set_controls_enabled(False)
|
self.set_controls_enabled(False)
|
||||||
@ -278,6 +335,8 @@ class PHControllerGUI(QMainWindow):
|
|||||||
self.last_flow_rate = 0.0
|
self.last_flow_rate = 0.0
|
||||||
self.auto_dosing = False
|
self.auto_dosing = False
|
||||||
self.auto_dose_timer.stop()
|
self.auto_dose_timer.stop()
|
||||||
|
self.auto_titration_active = False
|
||||||
|
self.titration_timer.stop()
|
||||||
|
|
||||||
def set_controls_enabled(self, enabled):
|
def set_controls_enabled(self, enabled):
|
||||||
self.pump_on_btn.setEnabled(enabled)
|
self.pump_on_btn.setEnabled(enabled)
|
||||||
@ -291,6 +350,10 @@ class PHControllerGUI(QMainWindow):
|
|||||||
self.reset_volume_checkbox.setEnabled(enabled)
|
self.reset_volume_checkbox.setEnabled(enabled)
|
||||||
self.clear_plot_btn.setEnabled(enabled)
|
self.clear_plot_btn.setEnabled(enabled)
|
||||||
self.plot_enabled_checkbox.setEnabled(enabled)
|
self.plot_enabled_checkbox.setEnabled(enabled)
|
||||||
|
self.start_titration_btn.setEnabled(enabled)
|
||||||
|
self.clear_titration_btn.setEnabled(enabled)
|
||||||
|
self.titration_step_spin.setEnabled(enabled)
|
||||||
|
self.titration_wait_spin.setEnabled(enabled)
|
||||||
|
|
||||||
def send_command(self, command):
|
def send_command(self, command):
|
||||||
print(command)
|
print(command)
|
||||||
@ -339,8 +402,8 @@ class PHControllerGUI(QMainWindow):
|
|||||||
self.total_volume_label.setVisible(True)
|
self.total_volume_label.setVisible(True)
|
||||||
|
|
||||||
# Check if volume should be reset or continue accumulating
|
# Check if volume should be reset or continue accumulating
|
||||||
if self.reset_volume_checkbox.isChecked() or self.auto_dosing:
|
if (self.reset_volume_checkbox.isChecked() or self.auto_dosing) and not self.auto_titration_active:
|
||||||
# Reset volume counter (always reset for auto dosing)
|
# Reset volume counter (always reset for auto dosing, but NOT for titration)
|
||||||
self.total_volume = 0.0
|
self.total_volume = 0.0
|
||||||
self.total_volume_label.setText("Gesamt: 0.0 ml")
|
self.total_volume_label.setText("Gesamt: 0.0 ml")
|
||||||
else:
|
else:
|
||||||
@ -399,6 +462,11 @@ class PHControllerGUI(QMainWindow):
|
|||||||
self.pump_off_btn.setEnabled(True)
|
self.pump_off_btn.setEnabled(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Don't start if auto titration is running
|
||||||
|
if self.auto_titration_active:
|
||||||
|
QMessageBox.warning(self, "Warnung", "Automatische Titration läuft bereits!")
|
||||||
|
return
|
||||||
|
|
||||||
volume = self.volume_spin.value()
|
volume = self.volume_spin.value()
|
||||||
self.target_volume = volume
|
self.target_volume = volume
|
||||||
self.auto_dosing = True
|
self.auto_dosing = True
|
||||||
@ -419,39 +487,6 @@ class PHControllerGUI(QMainWindow):
|
|||||||
|
|
||||||
self.status_bar.showMessage(f"Automatische Dosierung läuft... Ziel: {volume} ml")
|
self.status_bar.showMessage(f"Automatische Dosierung läuft... Ziel: {volume} ml")
|
||||||
|
|
||||||
def check_auto_dose_progress(self):
|
|
||||||
"""Check if target volume has been reached during auto dosing"""
|
|
||||||
if not self.auto_dosing:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check if we've reached or exceeded the target volume
|
|
||||||
if self.total_volume >= self.target_volume:
|
|
||||||
# Stop dosing
|
|
||||||
self.auto_dosing = False
|
|
||||||
self.auto_dose_timer.stop()
|
|
||||||
self.send_pump_command(0) # Turn off pump
|
|
||||||
|
|
||||||
# Re-enable manual controls
|
|
||||||
self.pump_on_btn.setEnabled(True)
|
|
||||||
self.pump_off_btn.setEnabled(True)
|
|
||||||
self.auto_dose_btn.setText("Automatisch dosieren")
|
|
||||||
|
|
||||||
# Show completion message
|
|
||||||
actual_volume = self.total_volume
|
|
||||||
difference = actual_volume - self.target_volume
|
|
||||||
|
|
||||||
message = f"Dosierung abgeschlossen!\n"
|
|
||||||
message += f"Zielvolumen: {self.target_volume:.2f} ml\n"
|
|
||||||
message += f"Tatsächliches Volumen: {actual_volume:.2f} ml\n"
|
|
||||||
message += f"Abweichung: {difference:+.2f} ml"
|
|
||||||
|
|
||||||
QMessageBox.information(self, "Automatische Dosierung", message)
|
|
||||||
self.status_bar.showMessage(f"Dosierung abgeschlossen: {actual_volume:.2f} ml von {self.target_volume:.2f} ml")
|
|
||||||
else:
|
|
||||||
# Update progress in status bar
|
|
||||||
progress = (self.total_volume / self.target_volume) * 100
|
|
||||||
self.status_bar.showMessage(f"Dosierung: {self.total_volume:.2f}/{self.target_volume:.2f} ml ({progress:.1f}%)")
|
|
||||||
|
|
||||||
def read_serial_data(self):
|
def read_serial_data(self):
|
||||||
if not self.serial_connection or not self.serial_connection.is_open:
|
if not self.serial_connection or not self.serial_connection.is_open:
|
||||||
return
|
return
|
||||||
@ -469,6 +504,10 @@ class PHControllerGUI(QMainWindow):
|
|||||||
# Add to plot if recording is enabled
|
# Add to plot if recording is enabled
|
||||||
if self.plot_enabled_checkbox.isChecked():
|
if self.plot_enabled_checkbox.isChecked():
|
||||||
self.ph_plot.add_ph_value(ph_value)
|
self.ph_plot.add_ph_value(ph_value)
|
||||||
|
# Add to titration plot if titration is active
|
||||||
|
if self.titration_active or self.auto_titration_active:
|
||||||
|
titrated_volume = self.total_volume - self.titration_start_volume
|
||||||
|
self.titration_plot.add_titration_point(titrated_volume, ph_value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
elif data.startswith('P'): # Pump status
|
elif data.startswith('P'): # Pump status
|
||||||
@ -522,6 +561,7 @@ class PHControllerGUI(QMainWindow):
|
|||||||
self.tube_request_timer.stop()
|
self.tube_request_timer.stop()
|
||||||
self.flow_rate_timer.stop()
|
self.flow_rate_timer.stop()
|
||||||
self.auto_dose_timer.stop()
|
self.auto_dose_timer.stop()
|
||||||
|
self.titration_timer.stop()
|
||||||
self.close_connection()
|
self.close_connection()
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
@ -530,6 +570,148 @@ class PHControllerGUI(QMainWindow):
|
|||||||
self.ph_plot.clear_data()
|
self.ph_plot.clear_data()
|
||||||
self.status_bar.showMessage("pH-Diagramm gelöscht")
|
self.status_bar.showMessage("pH-Diagramm gelöscht")
|
||||||
|
|
||||||
|
def toggle_titration(self):
|
||||||
|
"""Start or stop titration recording"""
|
||||||
|
if self.titration_active:
|
||||||
|
# Stop titration
|
||||||
|
self.titration_active = False
|
||||||
|
self.start_titration_btn.setText("Titration starten")
|
||||||
|
self.status_bar.showMessage("Titration gestoppt")
|
||||||
|
else:
|
||||||
|
# Start titration
|
||||||
|
self.titration_active = True
|
||||||
|
self.titration_start_volume = self.total_volume
|
||||||
|
self.start_titration_btn.setText("Titration stoppen")
|
||||||
|
self.status_bar.showMessage("Titration gestartet")
|
||||||
|
|
||||||
|
def toggle_auto_titration(self):
|
||||||
|
"""Start or stop automatic titration"""
|
||||||
|
if self.auto_titration_active:
|
||||||
|
# Stop auto titration
|
||||||
|
self.auto_titration_active = False
|
||||||
|
self.titration_timer.stop()
|
||||||
|
self.send_pump_command(0) # Turn off pump
|
||||||
|
self.start_titration_btn.setText("Automatische Titration starten")
|
||||||
|
# Re-enable controls
|
||||||
|
self.pump_on_btn.setEnabled(True)
|
||||||
|
self.pump_off_btn.setEnabled(True)
|
||||||
|
self.auto_dose_btn.setEnabled(True)
|
||||||
|
self.status_bar.showMessage("Automatische Titration gestoppt")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Don't start if auto dosing is running
|
||||||
|
if self.auto_dosing:
|
||||||
|
QMessageBox.warning(self, "Warnung", "Automatische Dosierung läuft bereits!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get parameters
|
||||||
|
self.titration_step_size = self.titration_step_spin.value()
|
||||||
|
self.titration_wait_time = self.titration_wait_spin.value()
|
||||||
|
|
||||||
|
# Start auto titration
|
||||||
|
self.auto_titration_active = True
|
||||||
|
self.titration_start_volume = self.total_volume # Remember starting volume
|
||||||
|
self.titration_current_step = 0
|
||||||
|
|
||||||
|
# Update button and disable controls
|
||||||
|
self.start_titration_btn.setText("Automatische Titration stoppen")
|
||||||
|
self.pump_on_btn.setEnabled(False)
|
||||||
|
self.pump_off_btn.setEnabled(False)
|
||||||
|
self.auto_dose_btn.setEnabled(False)
|
||||||
|
|
||||||
|
# Start first titration step
|
||||||
|
self.titration_step()
|
||||||
|
|
||||||
|
self.status_bar.showMessage(f"Automatische Titration gestartet - Schritt: {self.titration_step_size} ml, Warten: {self.titration_wait_time} s")
|
||||||
|
|
||||||
|
def titration_step(self):
|
||||||
|
"""Perform one titration step"""
|
||||||
|
if not self.auto_titration_active:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.titration_current_step += 1
|
||||||
|
|
||||||
|
# Calculate step target volume (only the additional volume for this step)
|
||||||
|
step_target_volume = self.titration_step_size
|
||||||
|
|
||||||
|
# Calculate absolute target volume (total from start of titration)
|
||||||
|
absolute_target_volume = self.titration_start_volume + (self.titration_current_step * self.titration_step_size)
|
||||||
|
|
||||||
|
# Start pumping for this step
|
||||||
|
self.auto_dosing = True # Use auto dosing mechanism
|
||||||
|
self.target_volume = absolute_target_volume # Set absolute target
|
||||||
|
self.send_pump_command(1)
|
||||||
|
self.auto_dose_timer.start(500)
|
||||||
|
|
||||||
|
current_titrated_volume = self.total_volume - self.titration_start_volume
|
||||||
|
total_target_titrated = self.titration_current_step * self.titration_step_size
|
||||||
|
|
||||||
|
self.status_bar.showMessage(f"Titration Schritt {self.titration_current_step}: Dosiere {step_target_volume} ml (Titration gesamt: {total_target_titrated} ml)")
|
||||||
|
|
||||||
|
def check_auto_dose_progress(self):
|
||||||
|
"""Check if target volume has been reached during auto dosing"""
|
||||||
|
if not self.auto_dosing:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if we've reached or exceeded the target volume
|
||||||
|
if self.total_volume >= self.target_volume:
|
||||||
|
# Stop dosing
|
||||||
|
self.auto_dosing = False
|
||||||
|
self.auto_dose_timer.stop()
|
||||||
|
self.send_pump_command(0) # Turn off pump
|
||||||
|
|
||||||
|
# If this was part of auto titration, schedule next step
|
||||||
|
if self.auto_titration_active:
|
||||||
|
# Wait for pH to stabilize, then continue with next step
|
||||||
|
wait_time_ms = int(self.titration_wait_time * 1000)
|
||||||
|
QTimer.singleShot(wait_time_ms, self.titration_step)
|
||||||
|
self.status_bar.showMessage(f"Warte {self.titration_wait_time} s auf pH-Stabilisierung...")
|
||||||
|
else:
|
||||||
|
# Re-enable manual controls for regular auto dosing
|
||||||
|
self.pump_on_btn.setEnabled(True)
|
||||||
|
self.pump_off_btn.setEnabled(True)
|
||||||
|
self.auto_dose_btn.setText("Automatisch dosieren")
|
||||||
|
|
||||||
|
# Show completion message
|
||||||
|
actual_volume = self.total_volume
|
||||||
|
difference = actual_volume - self.target_volume
|
||||||
|
|
||||||
|
message = f"Dosierung abgeschlossen!\n"
|
||||||
|
message += f"Zielvolumen: {self.target_volume:.2f} ml\n"
|
||||||
|
message += f"Tatsächliches Volumen: {actual_volume:.2f} ml\n"
|
||||||
|
message += f"Abweichung: {difference:+.2f} ml"
|
||||||
|
|
||||||
|
QMessageBox.information(self, "Automatische Dosierung", message)
|
||||||
|
self.status_bar.showMessage(f"Dosierung abgeschlossen: {actual_volume:.2f} ml von {self.target_volume:.2f} ml")
|
||||||
|
else:
|
||||||
|
# Update progress in status bar
|
||||||
|
progress = (self.total_volume / self.target_volume) * 100
|
||||||
|
if self.auto_titration_active:
|
||||||
|
current_titrated_volume = self.total_volume - self.titration_start_volume
|
||||||
|
target_titrated_volume = self.titration_current_step * self.titration_step_size
|
||||||
|
self.status_bar.showMessage(f"Titration Schritt {self.titration_current_step}: Gesamt {current_titrated_volume:.2f}/{target_titrated_volume:.2f} ml titriert ({progress:.1f}%)")
|
||||||
|
else:
|
||||||
|
self.status_bar.showMessage(f"Dosierung: {self.total_volume:.2f}/{self.target_volume:.2f} ml ({progress:.1f}%)")
|
||||||
|
|
||||||
|
def clear_titration_plot(self):
|
||||||
|
"""Clear the titration plot data"""
|
||||||
|
try:
|
||||||
|
# Clear the plot data
|
||||||
|
self.titration_plot.clear_data()
|
||||||
|
|
||||||
|
# Reset titration state variables
|
||||||
|
self.titration_active = False
|
||||||
|
self.titration_start_volume = 0.0
|
||||||
|
|
||||||
|
# Update button text if not in auto titration mode
|
||||||
|
if not self.auto_titration_active:
|
||||||
|
self.start_titration_btn.setText("Automatische Titration starten")
|
||||||
|
|
||||||
|
self.status_bar.showMessage("Titrationsdaten gelöscht")
|
||||||
|
except Exception as e:
|
||||||
|
self.status_bar.showMessage(f"Fehler beim Löschen der Titrationsdaten: {str(e)}")
|
||||||
|
QMessageBox.critical(self, "Fehler", f"Fehler beim Löschen der Titrationsdaten:\n{str(e)}")
|
||||||
|
|
||||||
class PHPlotWidget(QWidget):
|
class PHPlotWidget(QWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -638,6 +820,90 @@ class PHPlotWidget(QWidget):
|
|||||||
self.ax.xaxis.set_major_formatter(ticker.ScalarFormatter())
|
self.ax.xaxis.set_major_formatter(ticker.ScalarFormatter())
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
|
|
||||||
|
class TitrationPlotWidget(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
|
||||||
|
self.volumes = []
|
||||||
|
self.ph_values = []
|
||||||
|
|
||||||
|
# Setup plot
|
||||||
|
self.ax.set_xlabel('Dosierte Menge (ml)')
|
||||||
|
self.ax.set_ylabel('pH-Wert')
|
||||||
|
self.ax.set_title('Titrationskurve')
|
||||||
|
self.ax.grid(True, alpha=0.3)
|
||||||
|
self.ax.set_ylim(0, 14) # pH range 0-14
|
||||||
|
self.ax.set_xlim(0, 1) # Initial volume range
|
||||||
|
|
||||||
|
# Layout
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(self.canvas)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
# Plot line and points
|
||||||
|
self.line, = self.ax.plot([], [], 'r-', linewidth=2, label='pH-Verlauf')
|
||||||
|
self.points = self.ax.scatter([], [], c='red', s=30, zorder=5, label='Messpunkte')
|
||||||
|
self.ax.legend()
|
||||||
|
|
||||||
|
# Initial draw
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
|
def add_titration_point(self, volume, ph_value):
|
||||||
|
"""Add new titration point (volume vs pH)"""
|
||||||
|
self.volumes.append(volume)
|
||||||
|
self.ph_values.append(ph_value)
|
||||||
|
self.update_plot()
|
||||||
|
|
||||||
|
def update_plot(self):
|
||||||
|
"""Update the plot with current data"""
|
||||||
|
if len(self.volumes) > 0 and len(self.ph_values) > 0:
|
||||||
|
# Update line data
|
||||||
|
self.line.set_data(self.volumes, self.ph_values)
|
||||||
|
|
||||||
|
# Update scatter points
|
||||||
|
self.points.set_offsets(list(zip(self.volumes, self.ph_values)))
|
||||||
|
|
||||||
|
# Auto-scale x-axis
|
||||||
|
if len(self.volumes) > 1:
|
||||||
|
max_vol = max(self.volumes)
|
||||||
|
self.ax.set_xlim(0, max_vol * 1.1) # Add 10% margin
|
||||||
|
|
||||||
|
# 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))
|
||||||
|
|
||||||
|
# Redraw
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
|
def clear_data(self):
|
||||||
|
"""Clear all titration data points"""
|
||||||
|
try:
|
||||||
|
# Clear data lists
|
||||||
|
self.volumes.clear()
|
||||||
|
self.ph_values.clear()
|
||||||
|
|
||||||
|
# Reset plot elements safely
|
||||||
|
self.line.set_data([], [])
|
||||||
|
|
||||||
|
# Clear scatter points properly - use empty 2D array
|
||||||
|
self.points.set_offsets(np.empty((0, 2)))
|
||||||
|
|
||||||
|
# Reset axis limits
|
||||||
|
self.ax.set_xlim(0, 1)
|
||||||
|
self.ax.set_ylim(0, 14)
|
||||||
|
|
||||||
|
# Redraw canvas
|
||||||
|
self.canvas.draw()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler beim Löschen der Titrationsdaten: {str(e)}")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
window = PHControllerGUI()
|
window = PHControllerGUI()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user