from PyQt5.QtWidgets import (QMainWindow, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QPushButton, QLabel, QDoubleSpinBox, QComboBox, QLineEdit, QWidget, QMessageBox) from PyQt5.QtCore import Qt class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Калькулятор питательных сред") self.setGeometry(100, 100, 1000, 600) self._init_ui() def _init_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout() # Верхняя панель: параметры среды top_layout = QHBoxLayout() top_layout.addWidget(QLabel("Общее количество:")) self.amount_input = QDoubleSpinBox() self.amount_input.setRange(0.001, 1000000.0) self.amount_input.setValue(1000.0) top_layout.addWidget(self.amount_input) self.amount_unit_combo = QComboBox() self.amount_unit_combo.addItems(["нл", "мкл", "мл", "л"]) self.amount_unit_combo.setCurrentText("мл") top_layout.addWidget(self.amount_unit_combo) top_layout.addWidget(QLabel("Растворитель:")) self.solvent_input = QLineEdit("Вода") top_layout.addWidget(self.solvent_input) layout.addLayout(top_layout) # Таблица реагентов (с растворителем в первой строке) layout.addWidget(QLabel("Состав среды:")) self.table = QTableWidget() self.table.setColumnCount(6) # Увеличиваем до 6 колонок self.table.setHorizontalHeaderLabels(["Название", "%", "Единица", "Коэфф.", "Разбавление (x)", "Результат"]) layout.addWidget(self.table) # Кнопки управления btn_layout = QHBoxLayout() self.add_row_btn = QPushButton("Добавить реагент") self.remove_row_btn = QPushButton("Удалить реагент") self.calculate_btn = QPushButton("Рассчитать") self.save_btn = QPushButton("Сохранить") self.load_btn = QPushButton("Загрузить") btn_layout.addWidget(self.add_row_btn) btn_layout.addWidget(self.remove_row_btn) btn_layout.addWidget(self.calculate_btn) btn_layout.addWidget(self.save_btn) btn_layout.addWidget(self.load_btn) layout.addLayout(btn_layout) central_widget.setLayout(layout) self.add_initial_rows() def add_initial_rows(self): """Добавляет начальные строки: растворитель и первый реагент""" # Добавляем строку растворителя (первая, нередактируемая) self.add_solvent_row() # Добавляем строку для первого реагента self.add_new_row() def add_solvent_row(self): """Добавляет строку растворителя (нередактируемая)""" row_count = self.table.rowCount() self.table.insertRow(row_count) # Название растворителя (берём из поля ввода) solvent_name = self.solvent_input.text() solvent_item = QTableWidgetItem(solvent_name) solvent_item.setFlags(solvent_item.flags() & ~Qt.ItemIsEditable) solvent_item.setBackground(Qt.lightGray) self.table.setItem(row_count, 0, solvent_item) # Процент (будет рассчитан автоматически) percent_item = QTableWidgetItem("") percent_item.setFlags(percent_item.flags() & ~Qt.ItemIsEditable) percent_item.setBackground(Qt.lightGray) self.table.setItem(row_count, 1, percent_item) # Единица измерения (не используется для растворителя) unit_item = QTableWidgetItem("-") unit_item.setFlags(unit_item.flags() & ~Qt.ItemIsEditable) unit_item.setBackground(Qt.lightGray) self.table.setItem(row_count, 2, unit_item) # Коэффициент (не используется для растворителя) coeff_item = QTableWidgetItem("-") coeff_item.setFlags(coeff_item.flags() & ~Qt.ItemIsEditable) coeff_item.setBackground(Qt.lightGray) self.table.setItem(row_count, 3, coeff_item) # Разбавление (не используется для растворителя) dilution_item = QTableWidgetItem("-") dilution_item.setFlags(dilution_item.flags() & ~Qt.ItemIsEditable) dilution_item.setBackground(Qt.lightGray) self.table.setItem(row_count, 4, dilution_item) # Результат (будет заполнен при расчёте) result_item = QTableWidgetItem("") result_item.setFlags(result_item.flags() & ~Qt.ItemIsEditable) result_item.setBackground(Qt.lightGray) self.table.setItem(row_count, 5, result_item) def update_solvent_name(self): """Обновляет название растворителя в первой строке таблицы""" solvent_name = self.solvent_input.text() name_item = self.table.item(0, 0) if name_item: name_item.setText(solvent_name) def add_new_row(self): """Добавляет новую строку для реагента""" row_count = self.table.rowCount() self.table.insertRow(row_count) self.table.setItem(row_count, 0, QTableWidgetItem(f"Реагент_{row_count}")) self.table.setItem(row_count, 1, QTableWidgetItem("0.0")) unit_combo = QComboBox() unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"]) unit_combo.setCurrentText("г") self.table.setCellWidget(row_count, 2, unit_combo) self.table.setItem(row_count, 3, QTableWidgetItem("1.0")) # Разбавление - обычное текстовое поле dilution_edit = QLineEdit("1.0") dilution_edit.setAlignment(Qt.AlignRight) dilution_edit.setToolTip("Во сколько раз разбавить (1 = без разбавления)") self.table.setCellWidget(row_count, 4, dilution_edit) self.table.setItem(row_count, 5, QTableWidgetItem("")) def remove_selected_row(self): """Удаляет выделенную строку из таблицы (кроме строки растворителя)""" selected_rows = set() for item in self.table.selectedItems(): selected_rows.add(item.row()) # Удаляем строки в обратном порядке, пропуская строку растворителя (индекс 0) for row in sorted(selected_rows, reverse=True): if row > 0: # Не удаляем строку растворителя self.table.removeRow(row) def get_table_data(self) -> list: """Возвращает данные таблицы в виде списка списков (только реагенты, без растворителя)""" data = [] # Начинаем с 1 строки (пропускаем растворитель) for row in range(1, self.table.rowCount()): row_data = [] # Название (колонка 0) name_item = self.table.item(row, 0) row_data.append(name_item.text() if name_item else "") # Процент (колонка 1) percent_item = self.table.item(row, 1) row_data.append(percent_item.text() if percent_item else "0") # Единица измерения (колонка 2 - комбобокс) unit_widget = self.table.cellWidget(row, 2) if unit_widget and isinstance(unit_widget, QComboBox): row_data.append(unit_widget.currentText()) else: row_data.append("мг") # Коэффициент (колонка 3) coeff_item = self.table.item(row, 3) row_data.append(coeff_item.text() if coeff_item else "1.0") # Разбавление (колонка 4 - spinbox) dilution_widget = self.table.cellWidget(row, 4) if dilution_widget and isinstance(dilution_widget, QDoubleSpinBox): dilution_factor = dilution_widget.value() row_data.append(dilution_factor) else: row_data.append(1.0) data.append(row_data) return data def update_solvent_percent(self, solvent_percent: float): """Обновляет процент растворителя в первой строке""" percent_item = self.table.item(0, 1) if percent_item: percent_item.setText(f"{solvent_percent:.2f}") def show_error(self, message: str): """Показывает сообщение об ошибке""" QMessageBox.critical(self, "Ошибка", message) def update_results(self, results: list): """Обновляет столбец результатов (индекс 5) в таблице""" # Начинаем с 1 строки (реагенты), 0 строка - растворитель for row, amount in enumerate(results, start=1): if row < self.table.rowCount(): formatted_amount = f"{amount:.4f}" self.table.setItem(row, 5, QTableWidgetItem(formatted_amount)) def update_solvent_result(self, solvent_amount: float, unit: str): """Обновляет результат для растворителя в первой строке""" formatted_amount = f"{solvent_amount:.4f}" result_item = self.table.item(0, 5) if result_item: result_item.setText(formatted_amount) # Также обновляем единицу измерения в колонке 2 для информации unit_item = self.table.item(0, 2) if unit_item: unit_item.setText(unit) def update_display(self, solvent: str, total_amount: float, amount_unit: str): """Обновляет отображение растворителя и общего количества среды""" self.solvent_input.setText(solvent) self.update_solvent_name() self.amount_input.setValue(total_amount) self.amount_unit_combo.setCurrentText(amount_unit) def clear_results(self): """Очищает столбец результатов для всех строк""" for row in range(self.table.rowCount()): self.table.setItem(row, 5, QTableWidgetItem("")) # Очищаем процент растворителя percent_item = self.table.item(0, 1) if percent_item: percent_item.setText("") # Очищаем единицу измерения растворителя unit_item = self.table.item(0, 2) if unit_item: unit_item.setText("-")