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): self.model = Model() self.view = MainWindow() self._connect_signals() 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._perform_calculation) self.view.save_btn.clicked.connect(self.save_composition) self.view.load_btn.clicked.connect(self.load_composition) # Подключаем обновление названия растворителя в таблице при изменении поля self.view.solvent_input.textChanged.connect(self.view.update_solvent_name) def add_reagent_row(self): """Добавляет новую строку для реагента""" self.view.add_new_row() def remove_reagent_row(self): """Удаляет выбранную строку реагента""" 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) # Обновляем информацию о растворителе в первой строке таблицы 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() # Заполняем реагенты из таблицы (начиная с 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 try: name = name_item.text() percentage = float(percentage_item.text()) unit = unit_widget.currentText() if unit_widget else "мг" conversion_factor = float(conversion_item.text()) # Получаем коэффициент разбавления (поддерживаем 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): """Обновляет интерфейс данными из модели""" # Очищаем таблицу, но сохраняем строку растворителя 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.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}")) unit_combo = QComboBox() 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}")) # Создаём поле для разбавления (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-файл""" 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) QMessageBox.information(self.view, "Успех", "Состав среды успешно сохранён!") except Exception as e: self.view.show_error(f"Ошибка сохранения: {str(e)}") def load_composition(self): """Загружает состав среды из 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() 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: QMessageBox.critical( self.view, "Ошибка", f"Ошибка при загрузке состава: {str(e)}" )