Исправления:
- Добавлен столбец 'Разбавление (x)' для каждого реагента - Переработана логика расчёта с учётом разбавления - Исправлен расчёт количества растворителя - Растворитель отображается в первой строке таблицы - Поддержка дробных значений разбавления
This commit is contained in:
+109
-56
@@ -1,100 +1,120 @@
|
|||||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QTableWidgetItem, QComboBox
|
from PyQt5.QtWidgets import QMessageBox, QFileDialog, QTableWidgetItem, QComboBox, QDoubleSpinBox, QLineEdit
|
||||||
from model import Reagent
|
from PyQt5.QtCore import Qt
|
||||||
|
from model import Model
|
||||||
|
from view import MainWindow
|
||||||
|
import json
|
||||||
|
from reagent import Reagent
|
||||||
|
|
||||||
|
|
||||||
class Controller:
|
class Controller:
|
||||||
def __init__(self, view, model):
|
def __init__(self):
|
||||||
self.view = view
|
self.model = Model()
|
||||||
self.model = model
|
self.view = MainWindow()
|
||||||
self.setup_connections()
|
self._connect_signals()
|
||||||
|
|
||||||
|
def _connect_signals(self):
|
||||||
def setup_connections(self):
|
"""Подключает обработчики событий интерфейса"""
|
||||||
"""Подключает сигналы виджетов к методам контроллера"""
|
|
||||||
self.view.add_row_btn.clicked.connect(self.add_reagent_row)
|
self.view.add_row_btn.clicked.connect(self.add_reagent_row)
|
||||||
self.view.remove_row_btn.clicked.connect(self.remove_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.save_btn.clicked.connect(self.save_composition)
|
||||||
self.view.load_btn.clicked.connect(self.load_composition)
|
self.view.load_btn.clicked.connect(self.load_composition)
|
||||||
|
# Подключаем обновление названия растворителя в таблице при изменении поля
|
||||||
# Авторасчёт при изменении количества среды
|
self.view.solvent_input.textChanged.connect(self.view.update_solvent_name)
|
||||||
self.view.amount_input.valueChanged.connect(self.calculate)
|
|
||||||
# Авторасчёт при смене единицы измерения
|
|
||||||
self.view.amount_unit_combo.currentTextChanged.connect(self.calculate)
|
|
||||||
|
|
||||||
def add_reagent_row(self):
|
def add_reagent_row(self):
|
||||||
"""Добавляет новую строку в таблицу реагентов с предустановленными значениями"""
|
"""Добавляет новую строку для реагента"""
|
||||||
row = self.view.table.rowCount()
|
self.view.add_new_row()
|
||||||
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(""))
|
|
||||||
|
|
||||||
def remove_reagent_row(self):
|
def remove_reagent_row(self):
|
||||||
"""Удаляет выделенную строку из таблицы реагентов и из модели"""
|
"""Удаляет выбранную строку реагента"""
|
||||||
current_row = self.view.table.currentRow()
|
self.view.remove_selected_row()
|
||||||
if current_row >= 0:
|
|
||||||
self.view.table.removeRow(current_row)
|
|
||||||
if 0 <= current_row < len(self.model.reagents):
|
|
||||||
del self.model.reagents[current_row]
|
|
||||||
|
|
||||||
|
def _perform_calculation(self):
|
||||||
def calculate(self):
|
"""Выполняет расчёт и обновляет интерфейс"""
|
||||||
"""Выполняет расчёт количеств реагентов и растворителя"""
|
|
||||||
try:
|
try:
|
||||||
self._update_model_from_view()
|
self._update_model_from_view()
|
||||||
results = self.model.calculate_amounts()
|
results, solvent_amount, solvent_percentage = self.model.calculate_amounts()
|
||||||
self.view.update_results(results)
|
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:
|
except ValueError as e:
|
||||||
self.view.show_error(f"Ошибка в данных: {str(e)}")
|
self.view.show_error(f"Ошибка в данных: {str(e)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.view.show_error(f"Неожиданная ошибка: {str(e)}")
|
self.view.show_error(f"Неожиданная ошибка: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _update_model_from_view(self):
|
def _update_model_from_view(self):
|
||||||
"""Обновляет модель данными из интерфейса"""
|
"""Обновляет модель данными из интерфейса (только реагенты, без растворителя)"""
|
||||||
|
# Очищаем список реагентов в модели
|
||||||
self.model.reagents.clear()
|
self.model.reagents.clear()
|
||||||
|
|
||||||
|
# Обновляем общее количество и единицу измерения
|
||||||
self.model.total_amount = self.view.amount_input.value()
|
self.model.total_amount = self.view.amount_input.value()
|
||||||
self.model.amount_unit = self.view.amount_unit_combo.currentText()
|
self.model.amount_unit = self.view.amount_unit_combo.currentText()
|
||||||
self.model.solvent = self.view.solvent_input.text()
|
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)
|
name_item = self.view.table.item(row, 0)
|
||||||
percentage_item = self.view.table.item(row, 1)
|
percentage_item = self.view.table.item(row, 1)
|
||||||
unit_widget = self.view.table.cellWidget(row, 2)
|
unit_widget = self.view.table.cellWidget(row, 2)
|
||||||
conversion_item = self.view.table.item(row, 3)
|
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]):
|
if not all([name_item, percentage_item, conversion_item]):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
name = name_item.text()
|
name = name_item.text()
|
||||||
percentage = float(percentage_item.text())
|
percentage = float(percentage_item.text())
|
||||||
unit = unit_widget.currentText()
|
unit = unit_widget.currentText() if unit_widget else "мг"
|
||||||
conversion_factor = float(conversion_item.text())
|
conversion_factor = float(conversion_item.text())
|
||||||
|
|
||||||
reagent = Reagent(name, percentage, unit, conversion_factor)
|
# Получаем коэффициент разбавления (поддерживаем QDoubleSpinBox и QLineEdit)
|
||||||
self.model.add_reagent(reagent)
|
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):
|
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)
|
self.view.amount_input.setValue(self.model.total_amount)
|
||||||
index = self.view.amount_unit_combo.findText(self.model.amount_unit)
|
index = self.view.amount_unit_combo.findText(self.model.amount_unit)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
self.view.amount_unit_combo.setCurrentIndex(index)
|
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:
|
for reagent in self.model.reagents:
|
||||||
row = self.view.table.rowCount()
|
row = self.view.table.rowCount()
|
||||||
self.view.table.insertRow(row)
|
self.view.table.insertRow(row)
|
||||||
|
|
||||||
self.view.table.setItem(row, 0, QTableWidgetItem(reagent.name))
|
self.view.table.setItem(row, 0, QTableWidgetItem(reagent.name))
|
||||||
self.view.table.setItem(row, 1, QTableWidgetItem(f"{reagent.percentage:.2f}"))
|
self.view.table.setItem(row, 1, QTableWidgetItem(f"{reagent.percentage:.2f}"))
|
||||||
|
|
||||||
@@ -102,21 +122,34 @@ class Controller:
|
|||||||
unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"])
|
unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"])
|
||||||
unit_combo.setCurrentText(reagent.unit)
|
unit_combo.setCurrentText(reagent.unit)
|
||||||
self.view.table.setCellWidget(row, 2, unit_combo)
|
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):
|
def save_composition(self):
|
||||||
"""Сохраняет состав среды в JSON‑файл"""
|
"""Сохраняет состав среды в JSON-файл"""
|
||||||
filename, _ = QFileDialog.getSaveFileName(
|
filename, _ = QFileDialog.getSaveFileName(
|
||||||
self.view,
|
self.view,
|
||||||
"Сохранить состав среды",
|
"Сохранить состав среды",
|
||||||
"",
|
"",
|
||||||
"JSON Files (*.json);;All Files (*)"
|
"JSON Files (*.json);;All Files (*)"
|
||||||
)
|
)
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
if not filename.lower().endswith('.json'):
|
if not filename.lower().endswith('.json'):
|
||||||
filename += '.json'
|
filename += '.json'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._update_model_from_view()
|
self._update_model_from_view()
|
||||||
self.model.save_to_file(filename)
|
self.model.save_to_file(filename)
|
||||||
@@ -125,18 +158,38 @@ class Controller:
|
|||||||
self.view.show_error(f"Ошибка сохранения: {str(e)}")
|
self.view.show_error(f"Ошибка сохранения: {str(e)}")
|
||||||
|
|
||||||
def load_composition(self):
|
def load_composition(self):
|
||||||
"""Загружает состав среды из JSON‑файла"""
|
"""Загружает состав среды из JSON-файла"""
|
||||||
filename, _ = QFileDialog.getOpenFileName(
|
filename, _ = QFileDialog.getOpenFileName(
|
||||||
self.view,
|
self.view,
|
||||||
"Загрузить состав среды",
|
"Загрузить состав среды",
|
||||||
"",
|
"",
|
||||||
"JSON Files (*.json);;All Files (*)"
|
"JSON Files (*.json);;All Files (*)"
|
||||||
)
|
)
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
try:
|
try:
|
||||||
self.model.load_from_file(filename)
|
self.model.load_from_file(filename)
|
||||||
self._update_view_from_model() # Исправлено: добавлены скобки ()
|
self._update_view_from_model()
|
||||||
self.calculate()
|
QMessageBox.information(
|
||||||
QMessageBox.information(self.view, "Успех", "Состав среды успешно загружен!")
|
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:
|
except Exception as e:
|
||||||
self.view.show_error(f"Ошибка загрузки: {str(e)}")
|
QMessageBox.critical(
|
||||||
|
self.view,
|
||||||
|
"Ошибка",
|
||||||
|
f"Ошибка при загрузке состава: {str(e)}"
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,34 +1,19 @@
|
|||||||
|
# main.py
|
||||||
import sys
|
import sys
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
from model import Model
|
|
||||||
from view import MainWindow
|
|
||||||
from controller import Controller
|
from controller import Controller
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Создаём приложение Qt
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
# Инициализируем компоненты MVC
|
# Создаём контроллер - он сам создаст модель и представление
|
||||||
model = Model() # Модель данных
|
controller = Controller()
|
||||||
view = MainWindow() # Графический интерфейс
|
|
||||||
controller = Controller(view, model) # Контроллер, связывающий модель и представление
|
|
||||||
|
|
||||||
# Связываем модель с представлением — критически важный шаг для устранения ошибки «Модель не установлена»
|
# Показываем главное окно
|
||||||
view.set_model(model)
|
controller.view.show()
|
||||||
|
|
||||||
# Настраиваем контроллер (подключаем обработчики событий к кнопкам)
|
# Запускаем цикл обработки событий
|
||||||
controller.setup_connections()
|
|
||||||
|
|
||||||
|
|
||||||
# Отображаем главное окно приложения
|
|
||||||
view.show()
|
|
||||||
|
|
||||||
# Запускаем главный цикл обработки событий Qt и ожидаем завершения приложения
|
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
|
from reagent import Reagent
|
||||||
|
|
||||||
VOLUME_UNITS = {
|
VOLUME_UNITS = {
|
||||||
'нл': 0.001, # 1 нл = 0.001 мкл
|
'нл': 0.001, # 1 нл = 0.001 мкл
|
||||||
@@ -16,135 +16,175 @@ MASS_UNITS = {
|
|||||||
'кг': 1000000.0 # 1 кг = 1 000 000 000 мкг
|
'кг': 1000000.0 # 1 кг = 1 000 000 000 мкг
|
||||||
}
|
}
|
||||||
|
|
||||||
class Reagent:
|
|
||||||
def __init__(self, name: str, percentage: float, unit: str, conversion_factor: float = 1.0):
|
|
||||||
self.name = name
|
|
||||||
self.percentage = percentage
|
|
||||||
self.unit = unit
|
|
||||||
self.conversion_factor = conversion_factor
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
"""Преобразует реагент в словарь для сохранения в JSON"""
|
|
||||||
return {
|
|
||||||
'name': self.name,
|
|
||||||
'percentage': self.percentage,
|
|
||||||
'unit': self.unit,
|
|
||||||
'conversion_factor': self.conversion_factor
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, data):
|
|
||||||
"""Создаёт реагент из словаря, загруженного из JSON"""
|
|
||||||
return cls(
|
|
||||||
name=data['name'],
|
|
||||||
percentage=data['percentage'],
|
|
||||||
unit=data['unit'],
|
|
||||||
conversion_factor=data['conversion_factor']
|
|
||||||
)
|
|
||||||
|
|
||||||
class Model:
|
class Model:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.reagents = []
|
self.total_amount = 100.0 # общее количество среды (по умолчанию — 1000 мл)
|
||||||
self.total_amount = 1000.0 # по умолчанию 1000 мл
|
self.amount_unit = 'мл' # единица измерения общего количества
|
||||||
self.amount_unit = "мл"
|
self.solvent = 'Вода' # растворитель по умолчанию
|
||||||
self.solvent = "Вода"
|
self.reagents = [] # список реагентов
|
||||||
def convert_amount(self, amount_mkl_or_mkg: float, target_unit: str, is_volume: bool) -> float:
|
|
||||||
|
def convert_amount(self, amount_base: float, target_unit: str, is_volume: bool) -> float:
|
||||||
"""
|
"""
|
||||||
Пересчитывает количество из базовых единиц (мкл/мкг) в целевую единицу.
|
Пересчитывает количество из базовых единиц (мкл/мкг) в целевую единицу.
|
||||||
|
|
||||||
Args:
|
|
||||||
amount_mkl_or_mkg: количество в базовых единицах (мкл или мкг)
|
|
||||||
target_unit: целевая единица измерения
|
|
||||||
is_volume: True — объём, False — масса
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Количество в целевой единице
|
|
||||||
"""
|
"""
|
||||||
if is_volume:
|
if is_volume:
|
||||||
conversion_factor = VOLUME_UNITS.get(target_unit, 1.0)
|
conversion_factor = VOLUME_UNITS.get(target_unit, 1.0)
|
||||||
else:
|
else:
|
||||||
conversion_factor = MASS_UNITS.get(target_unit, 1.0)
|
conversion_factor = MASS_UNITS.get(target_unit, 1.0)
|
||||||
|
|
||||||
return amount_mkl_or_mkg / conversion_factor
|
return amount_base / conversion_factor
|
||||||
|
|
||||||
def add_reagent(self, reagent: Reagent):
|
def calculate_amounts(self) -> tuple:
|
||||||
"""Добавляет реагент в список"""
|
|
||||||
self.reagents.append(reagent)
|
|
||||||
|
|
||||||
def calculate_amounts(self) -> list:
|
|
||||||
"""
|
"""
|
||||||
Рассчитывает абсолютное количество каждого реагента с учётом единиц измерения.
|
Рассчитывает:
|
||||||
Возвращает список количеств в единицах измерения реагента.
|
1. Исходное количество реагента для неразбавленного состава
|
||||||
|
2. Количество разбавленного реагента, которое нужно взять
|
||||||
|
3. Количество растворителя с учётом добавленных разбавленных реагентов
|
||||||
|
|
||||||
|
Возвращает: (список количеств разбавленных реагентов, количество растворителя, процент растворителя)
|
||||||
"""
|
"""
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
# Проверяем, есть ли реагенты
|
||||||
|
if not self.reagents:
|
||||||
|
return results, self.total_amount, 100.0
|
||||||
|
|
||||||
|
# Суммируем проценты всех реагентов
|
||||||
|
total_percentage = sum(reagent.percentage for reagent in self.reagents)
|
||||||
|
|
||||||
|
|
||||||
|
# Проверяем, что сумма процентов не превышает 100
|
||||||
|
if total_percentage > 100:
|
||||||
|
raise ValueError(f"Сумма процентов реагентов ({total_percentage:.2f}%) превышает 100%")
|
||||||
|
|
||||||
|
# Шаг 1: Рассчитываем общий объём среды в базовых единицах (мкл)
|
||||||
|
total_in_base = self.total_amount * VOLUME_UNITS[self.amount_unit]
|
||||||
|
|
||||||
|
# Шаг 2: Рассчитываем количество каждого реагента для неразбавленного состава
|
||||||
|
undiluted_amounts = []
|
||||||
for reagent in self.reagents:
|
for reagent in self.reagents:
|
||||||
# Базовый расчёт в процентах от общего количества
|
# Расчёт количества реагента в базовых единицах
|
||||||
base_amount = (reagent.percentage / 100) * self.total_amount
|
amount_in_base = (reagent.percentage / 100) * total_in_base
|
||||||
|
|
||||||
# Определяем, объём или масса
|
# Определение типа единицы измерения реагента
|
||||||
|
is_volume = reagent.unit in VOLUME_UNITS
|
||||||
|
|
||||||
|
# Применение коэффициента пересчёта
|
||||||
|
adjusted_amount = amount_in_base * reagent.conversion_factor
|
||||||
|
|
||||||
|
# Пересчёт в целевую единицу измерения
|
||||||
|
final_amount = self.convert_amount(adjusted_amount, reagent.unit, is_volume)
|
||||||
|
|
||||||
|
undiluted_amounts.append(final_amount)
|
||||||
|
|
||||||
|
# Шаг 3: Рассчитываем количество разбавленного реагента, которое нужно взять
|
||||||
|
# и общий объём вносимых разбавленных реагентов (в базовых единицах)
|
||||||
|
diluted_amounts = []
|
||||||
|
total_diluted_volume_base = 0
|
||||||
|
|
||||||
|
for i, reagent in enumerate(self.reagents):
|
||||||
|
dilution_factor = getattr(reagent, 'dilution_factor', 1.0)
|
||||||
|
|
||||||
|
if dilution_factor <= 0:
|
||||||
|
dilution_factor = 1.0
|
||||||
|
|
||||||
|
# Количество разбавленного реагента = исходное количество / фактор разбавления
|
||||||
|
diluted_amount = undiluted_amounts[i] * dilution_factor
|
||||||
|
diluted_amounts.append(diluted_amount)
|
||||||
|
|
||||||
|
# Вычисляем объём разбавленного реагента в базовых единицах (мкл)
|
||||||
is_volume = reagent.unit in VOLUME_UNITS
|
is_volume = reagent.unit in VOLUME_UNITS
|
||||||
is_mass = reagent.unit in MASS_UNITS
|
|
||||||
if is_volume:
|
if is_volume:
|
||||||
# Переводим общее количество среды в мкл для расчёта
|
# Если реагент в объёмных единицах, его объём = diluted_amount
|
||||||
total_in_mkl = self.total_amount * VOLUME_UNITS[self.amount_unit]
|
reagent_volume_base = diluted_amount * VOLUME_UNITS[reagent.unit]
|
||||||
# Расчёт в мкл
|
|
||||||
amount_in_mkl = (reagent.percentage / 100) * total_in_mkl
|
|
||||||
# Переводим обратно в единицы реагента
|
|
||||||
final_amount = self.convert_amount(amount_in_mkl, reagent.unit, True)
|
|
||||||
elif is_mass:
|
|
||||||
# Переводим общее количество среды в мкг для расчёта (если среда в единицах объёма,
|
|
||||||
# предполагаем плотность = 1 г/мл)
|
|
||||||
if self.amount_unit in VOLUME_UNITS:
|
|
||||||
# Предполагаем плотность 1 г/мл: 1 мл ≈ 1 г
|
|
||||||
total_in_mkg = self.total_amount * VOLUME_UNITS[self.amount_unit] # в мкл
|
|
||||||
total_in_mkg *= 1000 # переводим в мкг (1 мкл воды ≈ 1 мкг)
|
|
||||||
else:
|
else:
|
||||||
total_in_mkg = self.total_amount * MASS_UNITS[self.amount_unit]
|
# Если реагент в массовых единицах, считаем, что он вносит пренебрежимый объём
|
||||||
|
# или можно добавить коэффициент плотности, пока игнорируем
|
||||||
|
reagent_volume_base = 0
|
||||||
|
|
||||||
# Расчёт в мкг
|
total_diluted_volume_base += reagent_volume_base
|
||||||
amount_in_mkg = (reagent.percentage / 100) * total_in_mkg
|
|
||||||
# Переводим в единицы реагента
|
|
||||||
final_amount = self.convert_amount(amount_in_mkg, reagent.unit, False)
|
|
||||||
else:
|
|
||||||
# Если единица неизвестна, возвращаем базовый расчёт
|
|
||||||
final_amount = base_amount
|
|
||||||
|
|
||||||
results.append(final_amount)
|
# Шаг 4: Рассчитываем количество растворителя
|
||||||
return results
|
# Растворитель = общий объём - объём всех разбавленных реагентов
|
||||||
|
solvent_volume_base = total_in_base - total_diluted_volume_base
|
||||||
|
|
||||||
|
# Переводим количество растворителя в целевую единицу измерения
|
||||||
|
solvent_amount = solvent_volume_base / VOLUME_UNITS[self.amount_unit]
|
||||||
|
|
||||||
def get_solvent_info(self) -> dict:
|
# Если объём реагентов превышает общий объём, корректируем
|
||||||
"""Возвращает информацию о растворителе: процент и количество"""
|
if solvent_amount < 0:
|
||||||
total_percentage = sum(r.percentage for r in self.reagents)
|
solvent_amount = 0
|
||||||
solvent_percentage = max(0, 100 - total_percentage) # не меньше 0%
|
|
||||||
solvent_amount = (solvent_percentage / 100) * self.total_amount
|
# Процент растворителя в неразбавленном составе
|
||||||
return {
|
solvent_percentage = 100 - total_percentage
|
||||||
'percentage': solvent_percentage,
|
|
||||||
'amount': solvent_amount,
|
return diluted_amounts, solvent_amount, solvent_percentage
|
||||||
'unit': self.amount_unit
|
|
||||||
}
|
|
||||||
|
|
||||||
def save_to_file(self, filename: str):
|
def save_to_file(self, filename: str):
|
||||||
"""Сохраняет состав среды в JSON‑файл"""
|
"""Сохраняет модель в JSON-файл"""
|
||||||
data = {
|
data = {
|
||||||
'total_amount': self.total_amount,
|
'total_amount': self.total_amount,
|
||||||
'amount_unit': self.amount_unit,
|
'amount_unit': self.amount_unit,
|
||||||
'solvent': self.solvent,
|
'solvent': self.solvent,
|
||||||
'reagents': [r.to_dict() for r in self.reagents]
|
'reagents': [
|
||||||
|
{
|
||||||
|
'name': r.name,
|
||||||
|
'percentage': r.percentage,
|
||||||
|
'unit': r.unit,
|
||||||
|
'conversion_factor': r.conversion_factor,
|
||||||
|
'dilution_factor': getattr(r, 'dilution_factor', 1.0)
|
||||||
|
} for r in self.reagents
|
||||||
|
]
|
||||||
}
|
}
|
||||||
with open(filename, 'w', encoding='utf-8') as f:
|
with open(filename, 'w', encoding='utf-8') as f:
|
||||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
json.dump(data, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
def load_from_file(self, filename: str):
|
def load_from_file(self, filename: str):
|
||||||
"""Загружает состав среды из JSON‑файла"""
|
"""Загружает модель из JSON-файла"""
|
||||||
with open(filename, 'r', encoding='utf-8') as f:
|
with open(filename, 'r', encoding='utf-8') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
# Восстанавливаем данные модели
|
|
||||||
self.total_amount = data['total_amount']
|
self.total_amount = data['total_amount']
|
||||||
self.amount_unit = data['amount_unit']
|
self.amount_unit = data['amount_unit']
|
||||||
self.solvent = data['solvent']
|
self.solvent = data['solvent']
|
||||||
# Очищаем и заполняем список реагентов
|
|
||||||
self.reagents.clear()
|
self.reagents.clear()
|
||||||
for reagent_data in data['reagents']:
|
|
||||||
reagent = Reagent.from_dict(reagent_data)
|
for r_data in data['reagents']:
|
||||||
|
reagent = Reagent(
|
||||||
|
name=r_data['name'],
|
||||||
|
percentage=r_data['percentage'],
|
||||||
|
unit=r_data['unit'],
|
||||||
|
conversion_factor=r_data.get('conversion_factor', 1.0)
|
||||||
|
)
|
||||||
|
# Добавляем коэффициент разбавления
|
||||||
|
reagent.dilution_factor = r_data.get('dilution_factor', 1.0)
|
||||||
self.reagents.append(reagent)
|
self.reagents.append(reagent)
|
||||||
|
|
||||||
|
def add_reagent(self, name: str, percentage: float, unit: str, conversion_factor: float = 1.0,
|
||||||
|
dilution_factor: float = 1.0):
|
||||||
|
"""Добавляет новый реагент в модель"""
|
||||||
|
reagent = Reagent(name, percentage, unit, conversion_factor)
|
||||||
|
reagent.dilution_factor = dilution_factor
|
||||||
|
self.reagents.append(reagent)
|
||||||
|
|
||||||
|
def remove_reagent(self, index: int):
|
||||||
|
"""Удаляет реагент по индексу"""
|
||||||
|
if 0 <= index < len(self.reagents):
|
||||||
|
del self.reagents[index]
|
||||||
|
|
||||||
|
def clear_reagents(self):
|
||||||
|
"""Очищает список реагентов"""
|
||||||
|
self.reagents.clear()
|
||||||
|
|
||||||
|
def get_reagent_count(self) -> int:
|
||||||
|
"""Возвращает количество реагентов в модели"""
|
||||||
|
return len(self.reagents)
|
||||||
|
|
||||||
|
def set_total_amount(self, amount: float, unit: str):
|
||||||
|
"""Устанавливает общее количество среды и единицу измерения"""
|
||||||
|
self.total_amount = amount
|
||||||
|
self.amount_unit = unit
|
||||||
|
|
||||||
|
def set_solvent(self, solvent_name: str):
|
||||||
|
"""Устанавливает название растворителя"""
|
||||||
|
self.solvent = solvent_name
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class Reagent:
|
||||||
|
def __init__(self, name: str, percentage: float, unit: str, conversion_factor: float = 1.0):
|
||||||
|
self.name = name
|
||||||
|
self.percentage = percentage
|
||||||
|
self.unit = unit
|
||||||
|
self.conversion_factor = conversion_factor # коэффициент пересчёта
|
||||||
|
self.dilution_factor = 1.0 # во сколько раз разбавляем (1 = без разбавления)
|
||||||
@@ -1,134 +1,239 @@
|
|||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (QMainWindow, QVBoxLayout, QHBoxLayout,
|
||||||
QMainWindow, QVBoxLayout, QHBoxLayout, QWidget,
|
QTableWidget, QTableWidgetItem, QPushButton,
|
||||||
QLabel, QDoubleSpinBox, QComboBox, QPushButton,
|
QLabel, QDoubleSpinBox, QComboBox, QLineEdit,
|
||||||
QTableWidget, QTableWidgetItem, QLineEdit, QMessageBox
|
QWidget, QMessageBox)
|
||||||
)
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setWindowTitle("Калькулятор питательных сред")
|
self.setWindowTitle("Калькулятор питательных сред")
|
||||||
self.setGeometry(100, 100, 800, 600)
|
self.setGeometry(100, 100, 1000, 600)
|
||||||
self._init_ui()
|
self._init_ui()
|
||||||
|
|
||||||
def _init_ui(self):
|
def _init_ui(self):
|
||||||
"""Инициализирует все виджеты интерфейса"""
|
|
||||||
central_widget = QWidget()
|
central_widget = QWidget()
|
||||||
self.setCentralWidget(central_widget)
|
self.setCentralWidget(central_widget)
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
layout = QVBoxLayout(central_widget)
|
# Верхняя панель: параметры среды
|
||||||
|
top_layout = QHBoxLayout()
|
||||||
# Секция параметров среды
|
top_layout.addWidget(QLabel("Общее количество:"))
|
||||||
params_layout = QHBoxLayout()
|
|
||||||
|
|
||||||
# Общее количество среды
|
|
||||||
amount_layout = QHBoxLayout()
|
|
||||||
amount_layout.addWidget(QLabel("Общее количество:"))
|
|
||||||
self.amount_input = QDoubleSpinBox()
|
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.setValue(1000.0)
|
||||||
self.amount_input.setSingleStep(10)
|
top_layout.addWidget(self.amount_input)
|
||||||
amount_layout.addWidget(self.amount_input)
|
|
||||||
|
|
||||||
# Единица измерения
|
|
||||||
self.amount_unit_combo = QComboBox()
|
self.amount_unit_combo = QComboBox()
|
||||||
self.amount_unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"])
|
self.amount_unit_combo.addItems(["нл", "мкл", "мл", "л"])
|
||||||
self.amount_unit_combo.setCurrentText("мл")
|
self.amount_unit_combo.setCurrentText("мл")
|
||||||
amount_layout.addWidget(self.amount_unit_combo)
|
top_layout.addWidget(self.amount_unit_combo)
|
||||||
|
|
||||||
# Растворитель
|
top_layout.addWidget(QLabel("Растворитель:"))
|
||||||
solvent_layout = QHBoxLayout()
|
self.solvent_input = QLineEdit("Вода")
|
||||||
solvent_layout.addWidget(QLabel("Растворитель:"))
|
top_layout.addWidget(self.solvent_input)
|
||||||
self.solvent_input = QLineEdit()
|
layout.addLayout(top_layout)
|
||||||
self.solvent_input.setText("Вода")
|
|
||||||
solvent_layout.addWidget(self.solvent_input)
|
|
||||||
|
|
||||||
params_layout.addLayout(amount_layout)
|
# Таблица реагентов (с растворителем в первой строке)
|
||||||
params_layout.addSpacing(20)
|
layout.addWidget(QLabel("Состав среды:"))
|
||||||
params_layout.addLayout(solvent_layout)
|
|
||||||
params_layout.addStretch()
|
|
||||||
|
|
||||||
layout.addLayout(params_layout)
|
|
||||||
|
|
||||||
# Таблица реагентов
|
|
||||||
self.table = QTableWidget()
|
self.table = QTableWidget()
|
||||||
self.table.setColumnCount(5)
|
self.table.setColumnCount(6) # Увеличиваем до 6 колонок
|
||||||
self.table.setHorizontalHeaderLabels([
|
self.table.setHorizontalHeaderLabels(["Название", "%", "Единица", "Коэфф.", "Разбавление (x)", "Результат"])
|
||||||
"Название реагента",
|
|
||||||
"Процент (%)",
|
|
||||||
"Единица измерения",
|
|
||||||
"Коэффициент пересчёта",
|
|
||||||
"Результат"
|
|
||||||
])
|
|
||||||
layout.addWidget(self.table)
|
layout.addWidget(self.table)
|
||||||
|
|
||||||
# Кнопки управления
|
# Кнопки управления
|
||||||
buttons_layout = QHBoxLayout()
|
btn_layout = QHBoxLayout()
|
||||||
|
|
||||||
self.add_row_btn = QPushButton("Добавить реагент")
|
self.add_row_btn = QPushButton("Добавить реагент")
|
||||||
self.remove_row_btn = QPushButton("Удалить реагент")
|
self.remove_row_btn = QPushButton("Удалить реагент")
|
||||||
self.calculate_btn = QPushButton("Рассчитать")
|
self.calculate_btn = QPushButton("Рассчитать")
|
||||||
self.save_btn = QPushButton("Сохранить")
|
self.save_btn = QPushButton("Сохранить")
|
||||||
self.load_btn = QPushButton("Загрузить")
|
self.load_btn = QPushButton("Загрузить")
|
||||||
|
|
||||||
buttons_layout.addWidget(self.add_row_btn)
|
btn_layout.addWidget(self.add_row_btn)
|
||||||
buttons_layout.addWidget(self.remove_row_btn)
|
btn_layout.addWidget(self.remove_row_btn)
|
||||||
buttons_layout.addWidget(self.calculate_btn)
|
btn_layout.addWidget(self.calculate_btn)
|
||||||
buttons_layout.addWidget(self.save_btn)
|
btn_layout.addWidget(self.save_btn)
|
||||||
buttons_layout.addWidget(self.load_btn)
|
btn_layout.addWidget(self.load_btn)
|
||||||
buttons_layout.addStretch()
|
layout.addLayout(btn_layout)
|
||||||
|
|
||||||
layout.addLayout(buttons_layout)
|
central_widget.setLayout(layout)
|
||||||
|
self.add_initial_rows()
|
||||||
|
|
||||||
# Добавляем начальную строку
|
def add_initial_rows(self):
|
||||||
self.add_initial_row()
|
"""Добавляет начальные строки: растворитель и первый реагент"""
|
||||||
|
# Добавляем строку растворителя (первая, нередактируемая)
|
||||||
|
self.add_solvent_row()
|
||||||
|
# Добавляем строку для первого реагента
|
||||||
|
self.add_new_row()
|
||||||
|
|
||||||
def add_initial_row(self):
|
def add_solvent_row(self):
|
||||||
"""Добавляет начальную строку в таблицу"""
|
"""Добавляет строку растворителя (нередактируемая)"""
|
||||||
self.table.insertRow(0)
|
row_count = self.table.rowCount()
|
||||||
self.table.setItem(0, 0, QTableWidgetItem("Реагент"))
|
self.table.insertRow(row_count)
|
||||||
self.table.setItem(0, 1, QTableWidgetItem("1.0"))
|
|
||||||
|
# Название растворителя (берём из поля ввода)
|
||||||
|
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 = QComboBox()
|
||||||
unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"])
|
unit_combo.addItems(["нг", "мкг", "мг", "г", "кг", "нл", "мкл", "мл", "л"])
|
||||||
unit_combo.setCurrentText("мг")
|
unit_combo.setCurrentText("г")
|
||||||
self.table.setCellWidget(0, 2, unit_combo)
|
self.table.setCellWidget(row_count, 2, unit_combo)
|
||||||
|
|
||||||
self.table.setItem(0, 3, QTableWidgetItem("1.0"))
|
self.table.setItem(row_count, 3, QTableWidgetItem("1.0"))
|
||||||
self.table.setItem(0, 4, QTableWidgetItem(""))
|
|
||||||
|
|
||||||
def set_model(self, model):
|
# Разбавление - обычное текстовое поле
|
||||||
"""Устанавливает связь с моделью"""
|
dilution_edit = QLineEdit("1.0")
|
||||||
self.model = model
|
dilution_edit.setAlignment(Qt.AlignRight)
|
||||||
|
dilution_edit.setToolTip("Во сколько раз разбавить (1 = без разбавления)")
|
||||||
|
self.table.setCellWidget(row_count, 4, dilution_edit)
|
||||||
|
|
||||||
def update_results(self, results: list):
|
self.table.setItem(row_count, 5, QTableWidgetItem(""))
|
||||||
"""Обновляет столбец результатов в таблице"""
|
|
||||||
for row, amount in enumerate(results):
|
|
||||||
if row < self.table.rowCount():
|
|
||||||
# Округление до 4 знаков после запятой
|
|
||||||
self.table.setItem(row, 4, QTableWidgetItem(f"{amount:.4f}"))
|
|
||||||
|
|
||||||
|
def remove_selected_row(self):
|
||||||
|
"""Удаляет выделенную строку из таблицы (кроме строки растворителя)"""
|
||||||
|
selected_rows = set()
|
||||||
|
for item in self.table.selectedItems():
|
||||||
|
selected_rows.add(item.row())
|
||||||
|
|
||||||
def show_error(self, message: str):
|
# Удаляем строки в обратном порядке, пропуская строку растворителя (индекс 0)
|
||||||
"""Показывает диалоговое окно с ошибкой"""
|
for row in sorted(selected_rows, reverse=True):
|
||||||
QMessageBox.critical(self, "Ошибка", message)
|
if row > 0: # Не удаляем строку растворителя
|
||||||
|
self.table.removeRow(row)
|
||||||
|
|
||||||
def get_table_data(self) -> list:
|
def get_table_data(self) -> list:
|
||||||
"""Возвращает данные из таблицы для отладки"""
|
"""Возвращает данные таблицы в виде списка списков (только реагенты, без растворителя)"""
|
||||||
data = []
|
data = []
|
||||||
for row in range(self.table.rowCount()):
|
# Начинаем с 1 строки (пропускаем растворитель)
|
||||||
|
for row in range(1, self.table.rowCount()):
|
||||||
row_data = []
|
row_data = []
|
||||||
for col in range(self.table.columnCount() - 1): # не берём столбец результатов
|
|
||||||
item = self.table.item(row, col)
|
# Название (колонка 0)
|
||||||
if item:
|
name_item = self.table.item(row, 0)
|
||||||
row_data.append(item.text())
|
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:
|
else:
|
||||||
widget = self.table.cellWidget(row, col)
|
row_data.append("мг")
|
||||||
if widget and isinstance(widget, QComboBox):
|
|
||||||
row_data.append(widget.currentText())
|
# Коэффициент (колонка 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:
|
else:
|
||||||
row_data.append("")
|
row_data.append(1.0)
|
||||||
|
|
||||||
data.append(row_data)
|
data.append(row_data)
|
||||||
return 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("-")
|
||||||
|
|||||||
Reference in New Issue
Block a user