Исправления:

- Добавлен столбец 'Разбавление (x)' для каждого реагента
- Переработана логика расчёта с учётом разбавления
- Исправлен расчёт количества растворителя
- Растворитель отображается в первой строке таблицы
- Поддержка дробных значений разбавления
This commit is contained in:
2026-05-05 22:31:08 +05:00
parent cde52d1123
commit 6798fd5f63
5 changed files with 467 additions and 277 deletions
+117 -64
View File
@@ -1,100 +1,120 @@
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QTableWidgetItem, QComboBox
from model import Reagent
from PyQt5.QtWidgets import QMessageBox, QFileDialog, QTableWidgetItem, QComboBox, QDoubleSpinBox, QLineEdit
from PyQt5.QtCore import Qt
from model import Model
from view import MainWindow
import json
from reagent import Reagent
class Controller:
def __init__(self, view, model):
self.view = view
self.model = model
self.setup_connections()
def __init__(self):
self.model = Model()
self.view = MainWindow()
self._connect_signals()
def setup_connections(self):
"""Подключает сигналы виджетов к методам контроллера"""
def _connect_signals(self):
"""Подключает обработчики событий интерфейса"""
self.view.add_row_btn.clicked.connect(self.add_reagent_row)
self.view.remove_row_btn.clicked.connect(self.remove_reagent_row)
self.view.calculate_btn.clicked.connect(self.calculate)
self.view.calculate_btn.clicked.connect(self._perform_calculation)
self.view.save_btn.clicked.connect(self.save_composition)
self.view.load_btn.clicked.connect(self.load_composition)
# Авторасчёт при изменении количества среды
self.view.amount_input.valueChanged.connect(self.calculate)
# Авторасчёт при смене единицы измерения
self.view.amount_unit_combo.currentTextChanged.connect(self.calculate)
# Подключаем обновление названия растворителя в таблице при изменении поля
self.view.solvent_input.textChanged.connect(self.view.update_solvent_name)
def add_reagent_row(self):
"""Добавляет новую строку в таблицу реагентов с предустановленными значениями"""
row = self.view.table.rowCount()
self.view.table.insertRow(row)
self.view.table.setItem(row, 0, QTableWidgetItem("Реагент"))
self.view.table.setItem(row, 1, QTableWidgetItem("1.0"))
unit_combo = QComboBox()
unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"])
unit_combo.setCurrentText("мг")
self.view.table.setCellWidget(row, 2, unit_combo)
self.view.table.setItem(row, 3, QTableWidgetItem("1.0"))
self.view.table.setItem(row, 4, QTableWidgetItem(""))
"""Добавляет новую строку для реагента"""
self.view.add_new_row()
def remove_reagent_row(self):
"""Удаляет выделенную строку из таблицы реагентов и из модели"""
current_row = self.view.table.currentRow()
if current_row >= 0:
self.view.table.removeRow(current_row)
if 0 <= current_row < len(self.model.reagents):
del self.model.reagents[current_row]
"""Удаляет выбранную строку реагента"""
self.view.remove_selected_row()
def _perform_calculation(self):
"""Выполняет расчёт и обновляет интерфейс"""
try:
self._update_model_from_view()
results, solvent_amount, solvent_percentage = self.model.calculate_amounts()
self.view.update_results(results)
def calculate(self):
"""Выполняет расчёт количеств реагентов и растворителя"""
try:
self._update_model_from_view()
results = self.model.calculate_amounts()
self.view.update_results(results)
except ValueError as e:
self.view.show_error(f"Ошибка в данных: {str(e)}")
except Exception as e:
self.view.show_error(f"Неожиданная ошибка: {str(e)}")
# Обновляем информацию о растворителе в первой строке таблицы
self.view.update_solvent_result(solvent_amount, self.model.amount_unit)
self.view.update_solvent_percent(solvent_percentage)
except ValueError as e:
self.view.show_error(f"Ошибка в данных: {str(e)}")
except Exception as e:
self.view.show_error(f"Неожиданная ошибка: {str(e)}")
def _update_model_from_view(self):
"""Обновляет модель данными из интерфейса"""
"""Обновляет модель данными из интерфейса (только реагенты, без растворителя)"""
# Очищаем список реагентов в модели
self.model.reagents.clear()
# Обновляем общее количество и единицу измерения
self.model.total_amount = self.view.amount_input.value()
self.model.amount_unit = self.view.amount_unit_combo.currentText()
self.model.solvent = self.view.solvent_input.text()
for row in range(self.view.table.rowCount()):
# Заполняем реагенты из таблицы (начиная с 1 строки, пропуская растворитель)
for row in range(1, self.view.table.rowCount()):
name_item = self.view.table.item(row, 0)
percentage_item = self.view.table.item(row, 1)
unit_widget = self.view.table.cellWidget(row, 2)
conversion_item = self.view.table.item(row, 3)
dilution_widget = self.view.table.cellWidget(row, 4)
# Пропускаем строку, если какие-то обязательные поля отсутствуют
if not all([name_item, percentage_item, conversion_item]):
continue
name = name_item.text()
percentage = float(percentage_item.text())
unit = unit_widget.currentText()
conversion_factor = float(conversion_item.text())
try:
name = name_item.text()
percentage = float(percentage_item.text())
unit = unit_widget.currentText() if unit_widget else "мг"
conversion_factor = float(conversion_item.text())
reagent = Reagent(name, percentage, unit, conversion_factor)
self.model.add_reagent(reagent)
# Получаем коэффициент разбавления (поддерживаем QDoubleSpinBox и QLineEdit)
dilution_factor = 1.0
if dilution_widget:
if isinstance(dilution_widget, QDoubleSpinBox):
dilution_factor = dilution_widget.value()
elif isinstance(dilution_widget, QLineEdit):
try:
dilution_factor = float(dilution_widget.text())
except ValueError:
dilution_factor = 1.0
# Добавляем реагент в модель
self.model.add_reagent(name, percentage, unit, conversion_factor, dilution_factor)
except ValueError as e:
raise ValueError(f"Ошибка в строке {row + 1}: {str(e)}")
def _update_view_from_model(self):
"""Обновляет интерфейс данными из модели"""
self.view.table.setRowCount(0)
# Очищаем таблицу, но сохраняем строку растворителя
while self.view.table.rowCount() > 1:
self.view.table.removeRow(1)
# Если нет строки растворителя, добавляем её
if self.view.table.rowCount() == 0:
self.view.add_solvent_row()
# Устанавливаем общее количество и единицу измерения
self.view.amount_input.setValue(self.model.total_amount)
index = self.view.amount_unit_combo.findText(self.model.amount_unit)
if index >= 0:
self.view.amount_unit_combo.setCurrentIndex(index)
self.view.solvent_input.setText(self.model.solvent)
# Устанавливаем название растворителя и обновляем его в таблице
self.view.solvent_input.setText(self.model.solvent)
self.view.update_solvent_name()
# Заполняем таблицу реагентами из модели
for reagent in self.model.reagents:
row = self.view.table.rowCount()
self.view.table.insertRow(row)
self.view.table.setItem(row, 0, QTableWidgetItem(reagent.name))
self.view.table.setItem(row, 1, QTableWidgetItem(f"{reagent.percentage:.2f}"))
@@ -102,21 +122,34 @@ class Controller:
unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"])
unit_combo.setCurrentText(reagent.unit)
self.view.table.setCellWidget(row, 2, unit_combo)
self.view.table.setItem(row, 3, QTableWidgetItem(f"{reagent.conversion_factor:.2f}"))
self.view.table.setItem(row, 4, QTableWidgetItem(""))
self.view.table.setItem(row, 3, QTableWidgetItem(f"{reagent.conversion_factor:.2f}"))
# Создаём поле для разбавления (QLineEdit для ручного ввода)
dilution_edit = QLineEdit()
dilution_edit.setText(f"{getattr(reagent, 'dilution_factor', 1.0):.3f}")
dilution_edit.setAlignment(Qt.AlignRight)
dilution_edit.setToolTip("Во сколько раз разбавить (1 = без разбавления)")
self.view.table.setCellWidget(row, 4, dilution_edit)
self.view.table.setItem(row, 5, QTableWidgetItem(""))
# Очищаем результаты
self.view.clear_results()
def save_composition(self):
"""Сохраняет состав среды в JSONфайл"""
"""Сохраняет состав среды в JSON-файл"""
filename, _ = QFileDialog.getSaveFileName(
self.view,
"Сохранить состав среды",
"",
"JSON Files (*.json);;All Files (*)"
)
if filename:
if not filename.lower().endswith('.json'):
filename += '.json'
try:
self._update_model_from_view()
self.model.save_to_file(filename)
@@ -125,18 +158,38 @@ class Controller:
self.view.show_error(f"Ошибка сохранения: {str(e)}")
def load_composition(self):
"""Загружает состав среды из JSONфайла"""
"""Загружает состав среды из JSON-файла"""
filename, _ = QFileDialog.getOpenFileName(
self.view,
"Загрузить состав среды",
"",
"JSON Files (*.json);;All Files (*)"
)
if filename:
try:
self.model.load_from_file(filename)
self._update_view_from_model() # Исправлено: добавлены скобки ()
self.calculate()
QMessageBox.information(self.view, "Успех", "Состав среды успешно загружен!")
self._update_view_from_model()
QMessageBox.information(
self.view,
"Успех",
"Состав среды успешно загружен"
)
except FileNotFoundError:
QMessageBox.critical(
self.view,
"Ошибка",
f"Файл не найден: {filename}"
)
except json.JSONDecodeError as e:
QMessageBox.critical(
self.view,
"Ошибка",
f"Неверный формат JSON-файла: {str(e)}"
)
except Exception as e:
self.view.show_error(f"Ошибка загрузки: {str(e)}")
QMessageBox.critical(
self.view,
"Ошибка",
f"Ошибка при загрузке состава: {str(e)}"
)