Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion cnapy/gui_elements/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@
from cnapy.gui_elements.configuration_cplex import CplexConfigurationDialog
from cnapy.gui_elements.configuration_cplex_new import CplexNewConfigurationDialog
from cnapy.gui_elements.configuration_gurobi import GurobiConfigurationDialog
from cnapy.gui_elements.thermodynamics_dialog import ThermodynamicAnalysisTypes, ThermodynamicDialog
from cnapy.gui_elements.thermodynamics_dialog import (
ThermodynamicAnalysisTypes,
ThermodynamicDialog,
COBRAK_AVAILABLE,
COBRAK_IMPORT_ERROR,
)
import cnapy.utils as utils

SBML_suffixes = "*.xml *.sbml *.xml.gz *.sbml.gz *.xml.zip *.sbml.zip"
Expand Down Expand Up @@ -424,6 +429,18 @@ def __init__(self, appdata: AppData, project_path: str | None, scenario_path: st
bottleneck_action.triggered.connect(self.perform_bottleneck_analysis)
self.thermodynamic_menu.addAction(bottleneck_action)

# These three analyses require the optional COBRA-k package. When it is not
# installed, grey the entries out and explain via tooltip instead of letting
# the user open a dialog that cannot run. (perform_* still guard defensively.)
if not COBRAK_AVAILABLE:
for _cobrak_action in (optmdf_action, tfba_action, bottleneck_action):
_cobrak_action.setEnabled(False)
_cobrak_action.setToolTip(
"Requires the optional COBRA-k package "
"(install with: pip install cnapy[thermodynamics])"
)
self.thermodynamic_menu.setToolTipsVisible(True)

self.thermodynamic_menu.addSeparator()

dG0_menu = self.thermodynamic_menu.addMenu("Load dG'° values [in kJ/mol] (replacing all current values)...")
Expand Down Expand Up @@ -2163,8 +2180,29 @@ def set_status_unknown(self):
self.solver_status_symbol.setStyleSheet("color: black")
self.solver_status_symbol.setText("?")

def _require_cobrak(self) -> bool:
"""Warn (and return False) if the optional COBRA-k package is missing.

The thermodynamic methods depend on cobrak, which is an optional
dependency. Guarding here lets CNApy run even when it is not installed.
"""
if COBRAK_AVAILABLE:
return True
QMessageBox.warning(
self,
"COBRA-k not installed",
"The thermodynamic analyses require the optional 'cobrak' (COBRA-k) "
"package, which is not installed.\n\n"
"Install it with 'pip install cnapy[thermodynamics]' or from "
"https://github.com/klamt-lab/COBRA-k\n\n"
f"Import error: {COBRAK_IMPORT_ERROR}",
)
return False

@Slot()
def perform_optmdfpathway(self):
if not self._require_cobrak():
return
# Has to be in self to keep computation thread
self.optmdfpathway_dialog = ThermodynamicDialog(
self.appdata,
Expand All @@ -2175,6 +2213,8 @@ def perform_optmdfpathway(self):

@Slot()
def perform_thermodynamic_fba(self):
if not self._require_cobrak():
return
# Has to be in self to keep computation thread
self.thermodynamic_fba_dialog = ThermodynamicDialog(
self.appdata,
Expand All @@ -2185,6 +2225,8 @@ def perform_thermodynamic_fba(self):

@Slot()
def perform_bottleneck_analysis(self):
if not self._require_cobrak():
return
# Has to be in self to keep computation thread
self.bottleneck_dialog = ThermodynamicDialog(
self.appdata,
Expand Down
21 changes: 16 additions & 5 deletions cnapy/gui_elements/thermodynamics_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,22 @@
from cnapy.gui_elements.central_widget import CentralWidget
from cnapy.gui_elements.solver_buttons import get_solver_buttons
from enum import Enum
from cobrak.constants import LNCONC_VAR_PREFIX, DF_VAR_PREFIX, MDF_VAR_ID, ALL_OK_KEY, OBJECTIVE_VAR_NAME, TERMINATION_CONDITION_KEY
from cobrak.dataclasses import ExtraLinearConstraint, Solver
from cobrak.lps import perform_lp_optimization, perform_lp_thermodynamic_bottleneck_analysis
from cobrak.io import load_annotated_cobrapy_model_as_cobrak_model
from cobrak.cobrapy_model_functionality import get_fullsplit_cobra_model
# cobrak (COBRA-k) powers the thermodynamic methods but is treated as an optional
# dependency so CNApy can be installed and started even when cobrak is unavailable
# in the environment. Import it lazily and gate the "Thermodynamic analyses" menu
# actions on COBRAK_AVAILABLE (see main_window.py). Enable the feature with
# `pip install cnapy[thermodynamics]`.
try:
from cobrak.constants import LNCONC_VAR_PREFIX, DF_VAR_PREFIX, MDF_VAR_ID, ALL_OK_KEY, OBJECTIVE_VAR_NAME, TERMINATION_CONDITION_KEY
from cobrak.dataclasses import ExtraLinearConstraint, Solver
from cobrak.lps import perform_lp_optimization, perform_lp_thermodynamic_bottleneck_analysis
from cobrak.io import load_annotated_cobrapy_model_as_cobrak_model
from cobrak.cobrapy_model_functionality import get_fullsplit_cobra_model
COBRAK_AVAILABLE = True
COBRAK_IMPORT_ERROR = ""
except ModuleNotFoundError as _cobrak_err:
COBRAK_AVAILABLE = False
COBRAK_IMPORT_ERROR = str(_cobrak_err)



Expand Down
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,16 @@ dependencies = [
"openpyxl",
"jpype1==1.5.0",
"setuptools",
"cobrak==0.0.10",
"nest_asyncio",
]

[project.optional-dependencies]
# COBRA-k powers the thermodynamic analyses. It is kept as an optional extra rather
# than a hard dependency so that a plain `pip install cnapy` always succeeds and the
# GUI starts, even if cobrak cannot be installed in a given environment. Enable the
# feature with `pip install cnapy[thermodynamics]`.
thermodynamics = ["cobrak>=0.0.11"]

[project.scripts]
cnapy = "cnapy.__main__:main_cnapy"
[project.urls]
Expand Down
Loading