Skip to content
Open
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
27 changes: 27 additions & 0 deletions src/petab_gui/controllers/mother_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
process_file,
)
from ..views import TaskBar
from ..views.other_views import NextStepsPanel
from .logger_controller import LoggerController
from .sbml_controller import SbmlController
from .table_controllers import (
Expand Down Expand Up @@ -150,6 +151,11 @@ def __init__(self, view, model: PEtabModel):
}
self.sbml_checkbox_states = {"sbml": False, "antimony": False}
self.unsaved_changes = False
# Next Steps Panel
self.next_steps_panel = NextStepsPanel(self.view)
self.next_steps_panel.dont_show_again_changed.connect(
self._handle_next_steps_dont_show_again
)
self.filter = QLineEdit()
self.filter_active = {} # Saves which tables the filter applies to
self.actions = self.setup_actions()
Expand Down Expand Up @@ -489,6 +495,12 @@ def setup_actions(self):
)
)

# Show next steps panel action
actions["next_steps"] = QAction(
qta.icon("mdi6.lightbulb-on"), "Possible next steps...", self.view
)
actions["next_steps"].triggered.connect(self._show_next_steps_panel)

# Undo / Redo
actions["undo"] = QAction(qta.icon("mdi6.undo"), "&Undo", self.view)
actions["undo"].setShortcut(QKeySequence.Undo)
Expand Down Expand Up @@ -582,6 +594,13 @@ def save_model(self):
"Save Project",
f"Project saved successfully to {file_name}",
)

# Show next steps panel if not disabled
if not settings_manager.get_value(
"next_steps/dont_show_again", False, bool
):
self.next_steps_panel.show_panel()

return True

def save_single_table(self):
Expand Down Expand Up @@ -1414,6 +1433,14 @@ def about(self):
f"<a href='file://{config_file}'>{config_file}</a></small>",
)

def _show_next_steps_panel(self):
"""Show the next steps panel (ignores 'don't show again' preference)."""
self.next_steps_panel.show_panel()

def _handle_next_steps_dont_show_again(self, dont_show: bool):
"""Handle the 'don't show again' checkbox in the next steps panel."""
settings_manager.set_value("next_steps/dont_show_again", dont_show)

def get_current_problem(self):
"""Get the current PEtab problem from the model."""
return self.model.current_petab_problem
213 changes: 213 additions & 0 deletions src/petab_gui/views/other_views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
"""Collection of other views aside from the main ones."""

from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import (
QCheckBox,
QComboBox,
QDialog,
QFrame,
QHBoxLayout,
QLabel,
QLineEdit,
QPushButton,
QTextBrowser,
QVBoxLayout,
QWidget,
)


Expand Down Expand Up @@ -64,3 +69,211 @@ def get_result(self) -> tuple[str | None, str | None]:
time_text = (self._time.text() or "").strip() or None
preeq = (self._preeq_edit.text() or "").strip()
return dose, time_text, preeq


class NextStepsPanel(QWidget):
"""Non-modal panel showing possible next steps after saving."""

dont_show_again_changed = Signal(bool)

def __init__(self, parent=None):
super().__init__(parent, Qt.Window | Qt.WindowStaysOnTopHint)
self.setWindowTitle("Possible next steps")
self.setMinimumWidth(450)
self.setMaximumWidth(600)
self.setMinimumHeight(360)

# Main layout
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(12, 12, 12, 12)
main_layout.setSpacing(10)

# Description
desc = QLabel(
"This parameter estimation problem can now be used in the following tools:"
)
desc.setWordWrap(True)
main_layout.addWidget(desc)

# Main suggestions
suggestions_layout = QVBoxLayout()
suggestions_layout.setSpacing(8)

# pyPESTO action
pypesto_frame = QFrame()
pypesto_frame.setStyleSheet(
"QFrame { background-color: rgba(100, 149, 237, 0.08); "
"border-radius: 4px; padding: 8px; }"
)
pypesto_layout = QVBoxLayout(pypesto_frame)
pypesto_layout.setContentsMargins(8, 8, 8, 8)
pypesto_layout.setSpacing(4)

pypesto_text = QTextBrowser()
pypesto_text.setOpenExternalLinks(True)
pypesto_text.setFrameStyle(QFrame.NoFrame)
pypesto_text.setStyleSheet("QTextBrowser { background: transparent; }")
pypesto_text.setVerticalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOff
)
pypesto_text.setHtml(
'<p style="margin:0; line-height:1.3;">'
"<b>▶ Parameter estimation</b><br/>"
"Use pyPESTO for parameter estimation and uncertainty "
"analysis<br/>"
'<a href="https://pypesto.readthedocs.io/en/latest/example/'
'petab_import.html">pyPESTO documentation</a></p>'
)
pypesto_layout.addWidget(pypesto_text)
suggestions_layout.addWidget(pypesto_frame)

# COPASI action
copasi_frame = QFrame()
copasi_frame.setStyleSheet(
"QFrame { background-color: rgba(169, 169, 169, 0.08); "
"border-radius: 4px; padding: 8px; }"
)
copasi_layout = QVBoxLayout(copasi_frame)
copasi_layout.setContentsMargins(8, 8, 8, 8)
copasi_layout.setSpacing(4)

copasi_text = QTextBrowser()
copasi_text.setOpenExternalLinks(True)
copasi_text.setFrameStyle(QFrame.NoFrame)
copasi_text.setStyleSheet("QTextBrowser { background: transparent; }")
copasi_text.setVerticalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOff
)
copasi_text.setHtml(
'<p style="margin:0; line-height:1.3;">'
"<b>⚙ Model adjustment and simulation</b><br/>"
"Use COPASI for further model adjustment and advanced "
"simulation<br/>"
'<a href="https://copasi.org">COPASI website</a></p>'
)
copasi_layout.addWidget(copasi_text)
suggestions_layout.addWidget(copasi_frame)

main_layout.addLayout(suggestions_layout)

# Collapsible section for other tools
self._other_tools_btn = QPushButton(
"📊 ▶ Other tools supporting PEtab"
)
self._other_tools_btn.setCheckable(True)
self._other_tools_btn.setFlat(True)
self._other_tools_btn.setStyleSheet(
"QPushButton { text-align: left; padding: 6px; "
"font-weight: normal; }"
"QPushButton:checked { font-weight: bold; }"
)
self._other_tools_btn.clicked.connect(self._toggle_other_tools)
main_layout.addWidget(self._other_tools_btn)

# Other tools frame (initially hidden)
self._other_tools_frame = QFrame()
self._other_tools_frame.setStyleSheet(
"QFrame { background-color: rgba(144, 238, 144, 0.08); "
"border-radius: 4px; padding: 8px; }"
)
self._other_tools_frame.setVisible(False)
other_tools_layout = QVBoxLayout(self._other_tools_frame)
other_tools_layout.setContentsMargins(8, 8, 8, 8)
other_tools_layout.setSpacing(4)

# Framing text
framing_text = QLabel("Additional tools in the PEtab ecosystem:")
framing_text.setWordWrap(True)
other_tools_layout.addWidget(framing_text)

other_tools_text = QTextBrowser()
other_tools_text.setOpenExternalLinks(True)
other_tools_text.setMaximumHeight(120)
other_tools_text.setFrameStyle(QFrame.NoFrame)
other_tools_text.setStyleSheet(
"QTextBrowser { background: transparent; }"
)
other_tools_text.setVerticalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAsNeeded
)
other_tools_text.setHtml(
'<ul style="margin:4px 0; padding-left: 20px; '
'line-height: 1.6;">'
'<li style="margin-bottom: 4px;">'
'<a href="https://amici.readthedocs.io/en/latest/examples/'
'example_petab/petab.html">AMICI</a> - '
"Advanced simulation and sensitivity analysis</li>"
'<li style="margin-bottom: 4px;">'
'<a href="https://github.com/Data2Dynamics/d2d/wiki">'
"Data2Dynamics</a> - "
"Comprehensive modeling environment</li>"
'<li style="margin-bottom: 4px;">'
'<a href="https://parpe.readthedocs.io">parPE</a> - '
"Parallel parameter estimation</li>"
'<li style="margin-bottom: 4px;">'
'<a href="https://petab.readthedocs.io/en/latest/v1/'
'software_support.html">PEtab documentation</a> - '
"Full list of supporting tools</li>"
"</ul>"
)
other_tools_layout.addWidget(other_tools_text)

main_layout.addWidget(self._other_tools_frame)

# Spacer
main_layout.addStretch()

# Reassurance text
reassurance = QLabel(
"<small><i>You can always access this dialog from the "
"Help menu.</i></small>"
)
reassurance.setWordWrap(True)
reassurance.setStyleSheet("QLabel { color: gray; padding: 0; }")
main_layout.addWidget(reassurance)

# Bottom section with checkbox and close button
bottom_layout = QHBoxLayout()
bottom_layout.setSpacing(8)

self._dont_show_checkbox = QCheckBox("Don't show after saving")
self._dont_show_checkbox.stateChanged.connect(
lambda state: self.dont_show_again_changed.emit(
state == Qt.CheckState.Checked
)
)
bottom_layout.addWidget(self._dont_show_checkbox)

bottom_layout.addStretch()

close_btn = QPushButton("Close")
close_btn.clicked.connect(self.close)
close_btn.setDefault(True)
bottom_layout.addWidget(close_btn)

main_layout.addLayout(bottom_layout)

def _toggle_other_tools(self, checked):
"""Toggle visibility of other tools section."""
self._other_tools_frame.setVisible(checked)
# Update button text to show expand/collapse state
arrow = "▼" if checked else "▶"
icon = "📊"
self._other_tools_btn.setText(
f"{icon} {arrow} Other tools supporting PEtab"
)
# Adjust window size
self.adjustSize()

def show_panel(self):
"""Show the panel and center it on the parent."""
if self.parent():
# Center on parent window
parent_geo = self.parent().geometry()
self.move(
parent_geo.center().x() - self.width() // 2,
parent_geo.center().y() - self.height() // 2,
)
self.show()
self.raise_()
self.activateWindow()
2 changes: 2 additions & 0 deletions src/petab_gui/views/task_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ def __init__(self, parent, actions):

# Add actions to the menu for re-adding tables
self.menu.addAction(actions["open_documentation"])
self.menu.addAction(actions["next_steps"])
self.menu.addSeparator()
self.menu.addAction(actions["whats_this"])
self.menu.addAction(actions["about"])

Expand Down