From 12410a51e7d386ff1a2db9630aad75adab2aa0a7 Mon Sep 17 00:00:00 2001
From: PaulJonasJost
Date: Tue, 13 Jan 2026 15:52:19 +0100
Subject: [PATCH 1/5] added an offboarding
---
.../controllers/mother_controller.py | 27 +++
src/petab_gui/views/other_views.py | 213 ++++++++++++++++++
src/petab_gui/views/task_bar.py | 2 +
3 files changed, 242 insertions(+)
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"])
From 34ab4835825da4573ca5ab548aee9bfd12b8c1be Mon Sep 17 00:00:00 2001
From: PaulJonasJost
Date: Tue, 20 Jan 2026 17:10:38 +0100
Subject: [PATCH 2/5] Changes to content to match next Steps tutorial
---
.../controllers/mother_controller.py | 11 ++-
src/petab_gui/views/other_views.py | 69 ++++++++++++++-----
2 files changed, 60 insertions(+), 20 deletions(-)
diff --git a/src/petab_gui/controllers/mother_controller.py b/src/petab_gui/controllers/mother_controller.py
index 73300a6..d480d46 100644
--- a/src/petab_gui/controllers/mother_controller.py
+++ b/src/petab_gui/controllers/mother_controller.py
@@ -596,9 +596,11 @@ def save_model(self):
)
# Show next steps panel if not disabled
- if not settings_manager.get_value(
+ dont_show = settings_manager.get_value(
"next_steps/dont_show_again", False, bool
- ):
+ )
+ if not dont_show:
+ self.next_steps_panel.set_dont_show_again(dont_show)
self.next_steps_panel.show_panel()
return True
@@ -1435,6 +1437,11 @@ def about(self):
def _show_next_steps_panel(self):
"""Show the next steps panel (ignores 'don't show again' preference)."""
+ # Sync checkbox state with current settings
+ dont_show = settings_manager.get_value(
+ "next_steps/dont_show_again", False, bool
+ )
+ self.next_steps_panel.set_dont_show_again(dont_show)
self.next_steps_panel.show_panel()
def _handle_next_steps_dont_show_again(self, dont_show: bool):
diff --git a/src/petab_gui/views/other_views.py b/src/petab_gui/views/other_views.py
index 801f598..a33c41d 100644
--- a/src/petab_gui/views/other_views.py
+++ b/src/petab_gui/views/other_views.py
@@ -12,7 +12,6 @@
QPushButton,
QTextBrowser,
QVBoxLayout,
- QWidget,
)
@@ -64,21 +63,22 @@ def __init__(
btns.addWidget(ok)
lay.addLayout(btns)
- def get_result(self) -> tuple[str | None, str | None]:
+ def get_result(self) -> tuple[str | None, str | None, str]:
dose = self._dose.currentText() or 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):
+class NextStepsPanel(QDialog):
"""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)
+ super().__init__(parent)
self.setWindowTitle("Possible next steps")
+ self.setModal(False)
self.setMinimumWidth(450)
self.setMaximumWidth(600)
self.setMinimumHeight(360)
@@ -99,6 +99,36 @@ def __init__(self, parent=None):
suggestions_layout = QVBoxLayout()
suggestions_layout.setSpacing(8)
+ # Benchmark Collection action
+ benchmark_frame = QFrame()
+ benchmark_frame.setStyleSheet(
+ "QFrame { background-color: rgba(255, 193, 7, 0.08); "
+ "border-radius: 4px; padding: 8px; }"
+ )
+ benchmark_layout = QVBoxLayout(benchmark_frame)
+ benchmark_layout.setContentsMargins(8, 8, 8, 8)
+ benchmark_layout.setSpacing(4)
+
+ benchmark_text = QTextBrowser()
+ benchmark_text.setOpenExternalLinks(True)
+ benchmark_text.setFrameStyle(QFrame.NoFrame)
+ benchmark_text.setStyleSheet(
+ "QTextBrowser { background: transparent; }"
+ )
+ benchmark_text.setVerticalScrollBarPolicy(
+ Qt.ScrollBarPolicy.ScrollBarAlwaysOff
+ )
+ benchmark_text.setHtml(
+ ''
+ "📚 Contribute to Benchmark Collection
"
+ "Share your PEtab problem with the community to validate it, "
+ "enable reproducibility, and support benchmarking
"
+ 'Benchmark Collection
'
+ )
+ benchmark_layout.addWidget(benchmark_text)
+ suggestions_layout.addWidget(benchmark_frame)
+
# pyPESTO action
pypesto_frame = QFrame()
pypesto_frame.setStyleSheet(
@@ -118,9 +148,9 @@ def __init__(self, parent=None):
)
pypesto_text.setHtml(
''
- "▶ Parameter estimation
"
- "Use pyPESTO for parameter estimation and uncertainty "
- "analysis
"
+ "▶ Parameter Estimation with pyPESTO
"
+ "Use pyPESTO for parameter estimation, uncertainty analysis, "
+ "and model selection
"
'pyPESTO documentation
'
)
@@ -146,9 +176,9 @@ def __init__(self, parent=None):
)
copasi_text.setHtml(
''
- "⚙ Model adjustment and simulation
"
+ "⚙ Advanced Model Adaptation and Simulation
"
"Use COPASI for further model adjustment and advanced "
- "simulation
"
+ "simulation with a graphical interface
"
'COPASI website
'
)
copasi_layout.addWidget(copasi_text)
@@ -202,14 +232,15 @@ def __init__(self, parent=None):
''
'AMICI - '
- "Advanced simulation and sensitivity analysis"
+ "Efficient simulation and sensitivity analysis"
+ ''
+ ''
+ "PEtab.jl - "
+ "High-performance Julia parameter estimation"
''
''
"Data2Dynamics - "
- "Comprehensive modeling environment"
- ''
- 'parPE - '
- "Parallel parameter estimation"
+ "MATLAB-based comprehensive modeling framework"
''
'PEtab documentation - '
@@ -237,10 +268,8 @@ def __init__(self, parent=None):
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
- )
+ self._dont_show_checkbox.toggled.connect(
+ self.dont_show_again_changed.emit
)
bottom_layout.addWidget(self._dont_show_checkbox)
@@ -265,6 +294,10 @@ def _toggle_other_tools(self, checked):
# Adjust window size
self.adjustSize()
+ def set_dont_show_again(self, dont_show: bool):
+ """Set the 'don't show again' checkbox state."""
+ self._dont_show_checkbox.setChecked(dont_show)
+
def show_panel(self):
"""Show the panel and center it on the parent."""
if self.parent():
From c3c37ae99530011750fab5d7fdcdde7ab083b963 Mon Sep 17 00:00:00 2001
From: PaulJonasJost
Date: Tue, 20 Jan 2026 17:20:44 +0100
Subject: [PATCH 3/5] Unified cards in a function, added some description to
functions
---
.../controllers/mother_controller.py | 10 +-
src/petab_gui/views/other_views.py | 184 ++++++++++--------
2 files changed, 111 insertions(+), 83 deletions(-)
diff --git a/src/petab_gui/controllers/mother_controller.py b/src/petab_gui/controllers/mother_controller.py
index d480d46..98351c9 100644
--- a/src/petab_gui/controllers/mother_controller.py
+++ b/src/petab_gui/controllers/mother_controller.py
@@ -600,7 +600,6 @@ def save_model(self):
"next_steps/dont_show_again", False, bool
)
if not dont_show:
- self.next_steps_panel.set_dont_show_again(dont_show)
self.next_steps_panel.show_panel()
return True
@@ -1445,7 +1444,14 @@ def _show_next_steps_panel(self):
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."""
+ """Handle the 'don't show again' checkbox state change.
+
+ Connected to the next steps panel's dont_show_again_changed signal.
+ Persists the user's preference to settings.
+
+ Args:
+ dont_show: Whether to suppress the panel on future saves
+ """
settings_manager.set_value("next_steps/dont_show_again", dont_show)
def get_current_problem(self):
diff --git a/src/petab_gui/views/other_views.py b/src/petab_gui/views/other_views.py
index a33c41d..712e137 100644
--- a/src/petab_gui/views/other_views.py
+++ b/src/petab_gui/views/other_views.py
@@ -75,18 +75,38 @@ class NextStepsPanel(QDialog):
dont_show_again_changed = Signal(bool)
+ # Styling constants
+ MIN_WIDTH = 450
+ MAX_WIDTH = 600
+ MIN_HEIGHT = 360
+ FRAME_PADDING = 8
+ FRAME_BORDER_RADIUS = 4
+ LAYOUT_MARGIN = 12
+ LAYOUT_SPACING = 10
+
+ # Card background colors
+ COLOR_BENCHMARK = "rgba(255, 193, 7, 0.08)"
+ COLOR_PYPESTO = "rgba(100, 149, 237, 0.08)"
+ COLOR_COPASI = "rgba(169, 169, 169, 0.08)"
+ COLOR_OTHER_TOOLS = "rgba(144, 238, 144, 0.08)"
+
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Possible next steps")
self.setModal(False)
- self.setMinimumWidth(450)
- self.setMaximumWidth(600)
- self.setMinimumHeight(360)
+ self.setMinimumWidth(self.MIN_WIDTH)
+ self.setMaximumWidth(self.MAX_WIDTH)
+ self.setMinimumHeight(self.MIN_HEIGHT)
# Main layout
main_layout = QVBoxLayout(self)
- main_layout.setContentsMargins(12, 12, 12, 12)
- main_layout.setSpacing(10)
+ main_layout.setContentsMargins(
+ self.LAYOUT_MARGIN,
+ self.LAYOUT_MARGIN,
+ self.LAYOUT_MARGIN,
+ self.LAYOUT_MARGIN,
+ )
+ main_layout.setSpacing(self.LAYOUT_SPACING)
# Description
desc = QLabel(
@@ -100,88 +120,44 @@ def __init__(self, parent=None):
suggestions_layout.setSpacing(8)
# Benchmark Collection action
- benchmark_frame = QFrame()
- benchmark_frame.setStyleSheet(
- "QFrame { background-color: rgba(255, 193, 7, 0.08); "
- "border-radius: 4px; padding: 8px; }"
- )
- benchmark_layout = QVBoxLayout(benchmark_frame)
- benchmark_layout.setContentsMargins(8, 8, 8, 8)
- benchmark_layout.setSpacing(4)
-
- benchmark_text = QTextBrowser()
- benchmark_text.setOpenExternalLinks(True)
- benchmark_text.setFrameStyle(QFrame.NoFrame)
- benchmark_text.setStyleSheet(
- "QTextBrowser { background: transparent; }"
+ benchmark_frame = self._create_tool_card(
+ bg_color=self.COLOR_BENCHMARK,
+ html_content=(
+ ''
+ "📚 Contribute to Benchmark Collection
"
+ "Share your PEtab problem with the community to validate it, "
+ "enable reproducibility, and support benchmarking
"
+ 'Benchmark Collection
'
+ ),
)
- benchmark_text.setVerticalScrollBarPolicy(
- Qt.ScrollBarPolicy.ScrollBarAlwaysOff
- )
- benchmark_text.setHtml(
- ''
- "📚 Contribute to Benchmark Collection
"
- "Share your PEtab problem with the community to validate it, "
- "enable reproducibility, and support benchmarking
"
- 'Benchmark Collection
'
- )
- benchmark_layout.addWidget(benchmark_text)
suggestions_layout.addWidget(benchmark_frame)
# 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_frame = self._create_tool_card(
+ bg_color=self.COLOR_PYPESTO,
+ html_content=(
+ ''
+ "▶ Parameter Estimation with pyPESTO
"
+ "Use pyPESTO for parameter estimation, uncertainty analysis, "
+ "and model selection
"
+ 'pyPESTO documentation
'
+ ),
)
- pypesto_text.setHtml(
- ''
- "▶ Parameter Estimation with pyPESTO
"
- "Use pyPESTO for parameter estimation, uncertainty analysis, "
- "and model selection
"
- '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_frame = self._create_tool_card(
+ bg_color=self.COLOR_COPASI,
+ html_content=(
+ ''
+ "⚙ Advanced Model Adaptation and Simulation
"
+ "Use COPASI for further model adjustment and advanced "
+ "simulation with a graphical interface
"
+ 'COPASI website
'
+ ),
)
- copasi_text.setHtml(
- ''
- "⚙ Advanced Model Adaptation and Simulation
"
- "Use COPASI for further model adjustment and advanced "
- "simulation with a graphical interface
"
- 'COPASI website
'
- )
- copasi_layout.addWidget(copasi_text)
suggestions_layout.addWidget(copasi_frame)
main_layout.addLayout(suggestions_layout)
@@ -203,12 +179,18 @@ def __init__(self, parent=None):
# 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; }"
+ f"QFrame {{ background-color: {self.COLOR_OTHER_TOOLS}; "
+ f"border-radius: {self.FRAME_BORDER_RADIUS}px; "
+ f"padding: {self.FRAME_PADDING}px; }}"
)
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.setContentsMargins(
+ self.FRAME_PADDING,
+ self.FRAME_PADDING,
+ self.FRAME_PADDING,
+ self.FRAME_PADDING,
+ )
other_tools_layout.setSpacing(4)
# Framing text
@@ -282,6 +264,46 @@ def __init__(self, parent=None):
main_layout.addLayout(bottom_layout)
+ def _create_tool_card(
+ self, bg_color: str, html_content: str, scrollbar_policy=None
+ ) -> QFrame:
+ """Create a styled card for displaying tool information.
+
+ Args:
+ bg_color: Background color for the frame (rgba string)
+ html_content: HTML content to display in the text browser
+ scrollbar_policy: Optional scrollbar policy (defaults to AlwaysOff)
+
+ Returns:
+ Configured QFrame containing the tool information
+ """
+ frame = QFrame()
+ frame.setStyleSheet(
+ f"QFrame {{ background-color: {bg_color}; "
+ f"border-radius: {self.FRAME_BORDER_RADIUS}px; "
+ f"padding: {self.FRAME_PADDING}px; }}"
+ )
+ layout = QVBoxLayout(frame)
+ layout.setContentsMargins(
+ self.FRAME_PADDING,
+ self.FRAME_PADDING,
+ self.FRAME_PADDING,
+ self.FRAME_PADDING,
+ )
+ layout.setSpacing(4)
+
+ text_browser = QTextBrowser()
+ text_browser.setOpenExternalLinks(True)
+ text_browser.setFrameStyle(QFrame.NoFrame)
+ text_browser.setStyleSheet("QTextBrowser { background: transparent; }")
+ if scrollbar_policy is None:
+ scrollbar_policy = Qt.ScrollBarPolicy.ScrollBarAlwaysOff
+ text_browser.setVerticalScrollBarPolicy(scrollbar_policy)
+ text_browser.setHtml(html_content)
+ layout.addWidget(text_browser)
+
+ return frame
+
def _toggle_other_tools(self, checked):
"""Toggle visibility of other tools section."""
self._other_tools_frame.setVisible(checked)
From 1ce383e5769c3488b0ade8d6e01dee5a2ce136e4 Mon Sep 17 00:00:00 2001
From: PaulJonasJost
Date: Tue, 20 Jan 2026 17:33:49 +0100
Subject: [PATCH 4/5] Slight change to wording of benchmark
---
src/petab_gui/views/other_views.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/petab_gui/views/other_views.py b/src/petab_gui/views/other_views.py
index 712e137..746dfb3 100644
--- a/src/petab_gui/views/other_views.py
+++ b/src/petab_gui/views/other_views.py
@@ -125,8 +125,9 @@ def __init__(self, parent=None):
html_content=(
''
"📚 Contribute to Benchmark Collection
"
- "Share your PEtab problem with the community to validate it, "
- "enable reproducibility, and support benchmarking
"
+ "Share your publsihed PEtab problem with the community to "
+ "validate it, enable reproducibility, and support "
+ "benchmarking
"
'Benchmark Collection
'
),
From 2fd09fb80d8772387d8c8c4510311144501c28a4 Mon Sep 17 00:00:00 2001
From: Paul Jonas Jost <70631928+PaulJonasJost@users.noreply.github.com>
Date: Tue, 20 Jan 2026 22:42:13 +0100
Subject: [PATCH 5/5] Apply suggestions from code review
Co-authored-by: Daniel Weindl
---
src/petab_gui/views/other_views.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/petab_gui/views/other_views.py b/src/petab_gui/views/other_views.py
index 746dfb3..f75561f 100644
--- a/src/petab_gui/views/other_views.py
+++ b/src/petab_gui/views/other_views.py
@@ -127,7 +127,7 @@ def __init__(self, parent=None):
"📚 Contribute to Benchmark Collection
"
"Share your publsihed PEtab problem with the community to "
"validate it, enable reproducibility, and support "
- "benchmarking
"
+ "benchmarking.
"
'Benchmark Collection
'
),
@@ -141,7 +141,7 @@ def __init__(self, parent=None):
''
"▶ Parameter Estimation with pyPESTO
"
"Use pyPESTO for parameter estimation, uncertainty analysis, "
- "and model selection
"
+ "and model selection.
"
'pyPESTO documentation
'
),
@@ -155,7 +155,7 @@ def __init__(self, parent=None):
''
"⚙ Advanced Model Adaptation and Simulation
"
"Use COPASI for further model adjustment and advanced "
- "simulation with a graphical interface
"
+ "simulation with a graphical interface.
"
'COPASI website
'
),
)