diff --git a/src/petab_gui/controllers/mother_controller.py b/src/petab_gui/controllers/mother_controller.py
index fc2ea85..73300a6 100644
--- a/src/petab_gui/controllers/mother_controller.py
+++ b/src/petab_gui/controllers/mother_controller.py
@@ -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 (
@@ -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()
@@ -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)
@@ -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):
@@ -1414,6 +1433,14 @@ def about(self):
f"{config_file}",
)
+ 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
diff --git a/src/petab_gui/views/other_views.py b/src/petab_gui/views/other_views.py
index cb5480b..801f598 100644
--- a/src/petab_gui/views/other_views.py
+++ b/src/petab_gui/views/other_views.py
@@ -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,
)
@@ -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(
+ '
'
+ "▶ Parameter estimation
"
+ "Use pyPESTO for parameter estimation and uncertainty "
+ "analysis
"
+ 'pyPESTO documentation
'
+ )
+ 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(
+ ''
+ "⚙ Model adjustment and simulation
"
+ "Use COPASI for further model adjustment and advanced "
+ "simulation
"
+ 'COPASI website
'
+ )
+ 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(
+ ''
+ '- '
+ 'AMICI - '
+ "Advanced simulation and sensitivity analysis
"
+ '- '
+ ''
+ "Data2Dynamics - "
+ "Comprehensive modeling environment
"
+ '- '
+ 'parPE - '
+ "Parallel parameter estimation
"
+ '- '
+ 'PEtab documentation - '
+ "Full list of supporting tools
"
+ "
"
+ )
+ other_tools_layout.addWidget(other_tools_text)
+
+ main_layout.addWidget(self._other_tools_frame)
+
+ # Spacer
+ main_layout.addStretch()
+
+ # Reassurance text
+ reassurance = QLabel(
+ "You can always access this dialog from the "
+ "Help menu."
+ )
+ 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()
diff --git a/src/petab_gui/views/task_bar.py b/src/petab_gui/views/task_bar.py
index 408221c..2e787c3 100644
--- a/src/petab_gui/views/task_bar.py
+++ b/src/petab_gui/views/task_bar.py
@@ -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"])