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

- Добавлен столбец 'Разбавление (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
+196 -91
View File
@@ -1,134 +1,239 @@
from PyQt5.QtWidgets import (
QMainWindow, QVBoxLayout, QHBoxLayout, QWidget,
QLabel, QDoubleSpinBox, QComboBox, QPushButton,
QTableWidget, QTableWidgetItem, QLineEdit, QMessageBox
)
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, 800, 600)
self.setGeometry(100, 100, 1000, 600)
self._init_ui()
def _init_ui(self):
"""Инициализирует все виджеты интерфейса"""
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout()
layout = QVBoxLayout(central_widget)
# Секция параметров среды
params_layout = QHBoxLayout()
# Общее количество среды
amount_layout = QHBoxLayout()
amount_layout.addWidget(QLabel("Общее количество:"))
# Верхняя панель: параметры среды
top_layout = QHBoxLayout()
top_layout.addWidget(QLabel("Общее количество:"))
self.amount_input = QDoubleSpinBox()
self.amount_input.setRange(0.001, 1000000)
self.amount_input.setRange(0.001, 1000000.0)
self.amount_input.setValue(1000.0)
self.amount_input.setSingleStep(10)
amount_layout.addWidget(self.amount_input)
top_layout.addWidget(self.amount_input)
# Единица измерения
self.amount_unit_combo = QComboBox()
self.amount_unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"])
self.amount_unit_combo.addItems(["нл", "мкл", "мл", "л"])
self.amount_unit_combo.setCurrentText("мл")
amount_layout.addWidget(self.amount_unit_combo)
top_layout.addWidget(self.amount_unit_combo)
# Растворитель
solvent_layout = QHBoxLayout()
solvent_layout.addWidget(QLabel("Растворитель:"))
self.solvent_input = QLineEdit()
self.solvent_input.setText("Вода")
solvent_layout.addWidget(self.solvent_input)
top_layout.addWidget(QLabel("Растворитель:"))
self.solvent_input = QLineEdit("Вода")
top_layout.addWidget(self.solvent_input)
layout.addLayout(top_layout)
params_layout.addLayout(amount_layout)
params_layout.addSpacing(20)
params_layout.addLayout(solvent_layout)
params_layout.addStretch()
layout.addLayout(params_layout)
# Таблица реагентов
# Таблица реагентов (с растворителем в первой строке)
layout.addWidget(QLabel("Состав среды:"))
self.table = QTableWidget()
self.table.setColumnCount(5)
self.table.setHorizontalHeaderLabels([
"Название реагента",
"Процент (%)",
"Единица измерения",
"Коэффициент пересчёта",
"Результат"
])
self.table.setColumnCount(6) # Увеличиваем до 6 колонок
self.table.setHorizontalHeaderLabels(["Название", "%", "Единица", "Коэфф.", "Разбавление (x)", "Результат"])
layout.addWidget(self.table)
# Кнопки управления
buttons_layout = QHBoxLayout()
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("Загрузить")
buttons_layout.addWidget(self.add_row_btn)
buttons_layout.addWidget(self.remove_row_btn)
buttons_layout.addWidget(self.calculate_btn)
buttons_layout.addWidget(self.save_btn)
buttons_layout.addWidget(self.load_btn)
buttons_layout.addStretch()
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)
layout.addLayout(buttons_layout)
central_widget.setLayout(layout)
self.add_initial_rows()
# Добавляем начальную строку
self.add_initial_row()
def add_initial_rows(self):
"""Добавляет начальные строки: растворитель и первый реагент"""
# Добавляем строку растворителя (первая, нередактируемая)
self.add_solvent_row()
# Добавляем строку для первого реагента
self.add_new_row()
def add_initial_row(self):
"""Добавляет начальную строку в таблицу"""
self.table.insertRow(0)
self.table.setItem(0, 0, QTableWidgetItem("Реагент"))
self.table.setItem(0, 1, QTableWidgetItem("1.0"))
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(0, 2, unit_combo)
unit_combo.setCurrentText("г")
self.table.setCellWidget(row_count, 2, unit_combo)
self.table.setItem(0, 3, QTableWidgetItem("1.0"))
self.table.setItem(0, 4, QTableWidgetItem(""))
self.table.setItem(row_count, 3, QTableWidgetItem("1.0"))
def set_model(self, model):
"""Устанавливает связь с моделью"""
self.model = model
# Разбавление - обычное текстовое поле
dilution_edit = QLineEdit("1.0")
dilution_edit.setAlignment(Qt.AlignRight)
dilution_edit.setToolTip("Во сколько раз разбавить (1 = без разбавления)")
self.table.setCellWidget(row_count, 4, dilution_edit)
def update_results(self, results: list):
"""Обновляет столбец результатов в таблице"""
for row, amount in enumerate(results):
if row < self.table.rowCount():
# Округление до 4 знаков после запятой
self.table.setItem(row, 4, QTableWidgetItem(f"{amount:.4f}"))
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 get_table_data(self) -> list:
"""Возвращает данные из таблицы для отладки"""
data = []
for row in range(self.table.rowCount()):
row_data = []
for col in range(self.table.columnCount() - 1): # не берём столбец результатов
item = self.table.item(row, col)
if item:
row_data.append(item.text())
else:
widget = self.table.cellWidget(row, col)
if widget and isinstance(widget, QComboBox):
row_data.append(widget.currentText())
else:
row_data.append("")
data.append(row_data)
return data
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("-")