Skip to content
Merged
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
18 changes: 18 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,21 @@ include-package-data = true

[tool.setuptools.package-data]
"petab_gui.assets" = ["PEtab.png"]

[tool.ruff]
line-length = 79
lint.select = [
"F", # Pyflakes
"I", # isort
"D", # pydocstyle (PEP 257)
"S", # flake8-bandit
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"T20", # flake8-print
"W", # pycodestyle Warnings
"E", # pycodestyle Errors
"UP", # pyupgrade
# "ANN", # flakes-annotations TODO: currently produces ~1500 errors to manual fix
]
[tool.ruff.lint.pydocstyle]
convention = "pep257"
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
"""Setup script for petab-gui."""
from setuptools import setup # type: ignore


if __name__ == "__main__":
setup(version="0.0.1")
setup(version="0.0.1")
97 changes: 41 additions & 56 deletions src/petab_gui/C.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Constants for the PEtab edit GUI."""

import numpy as np

COLUMNS = {
Expand Down Expand Up @@ -30,36 +31,39 @@
"nominalValue": {"type": np.float64, "optional": False},
"estimate": {"type": np.object_, "optional": False},
"initializationPriorType": {"type": np.object_, "optional": True},
"initializationPriorParameters": {"type": np.object_, "optional": True},
"initializationPriorParameters": {
"type": np.object_,
"optional": True,
},
"objectivePriorType": {"type": np.object_, "optional": True},
"objectivePriorParameters": {"type": np.object_, "optional": True},
},
"condition": {
"conditionId": {"type": np.object_, "optional": False},
"conditionName": {"type": np.object_, "optional": False},
}
},
}

CONFIG = {
'window_title': 'My Application',
'window_size': (800, 600),
'table_titles': {
'data': 'Data',
'parameters': 'Parameters',
'observables': 'Observables',
'conditions': 'Conditions'
"window_title": "My Application",
"window_size": (800, 600),
"table_titles": {
"data": "Data",
"parameters": "Parameters",
"observables": "Observables",
"conditions": "Conditions",
},
"summary_title": "Summary",
"buttons": {
"test_consistency": "Test Consistency",
"proceed_optimization": "Proceed to Optimization",
},
'summary_title': 'Summary',
'buttons': {
'test_consistency': 'Test Consistency',
'proceed_optimization': 'Proceed to Optimization'
}
}

# String constants
ROW = 'row'
COLUMN = 'column'
INDEX = 'index'
ROW = "row"
COLUMN = "column"
INDEX = "index"

COPY_FROM = "copy from"
USE_DEFAULT = "use default"
Expand Down Expand Up @@ -120,66 +124,47 @@
"observable": ALLOWED_STRATEGIES_OBS,
"parameter": ALLOWED_STRATEGIES_PAR,
"condition": ALLOWED_STRATEGIES_COND,
"measurement": ALLOWED_STRATEGIES_MEAS
"measurement": ALLOWED_STRATEGIES_MEAS,
}
DEFAULT_OBS_CONFIG = {
"observableId": {
"strategy": COPY_FROM, SOURCE_COLUMN: "observableFormula",
DEFAULT_VALUE: "new_observable"
},
"observableName": {
"strategy": COPY_FROM, SOURCE_COLUMN: "observableId"
},
"noiseFormula": {
"strategy": USE_DEFAULT, DEFAULT_VALUE: 1
"strategy": COPY_FROM,
SOURCE_COLUMN: "observableFormula",
DEFAULT_VALUE: "new_observable",
},
"observableName": {"strategy": COPY_FROM, SOURCE_COLUMN: "observableId"},
"noiseFormula": {"strategy": USE_DEFAULT, DEFAULT_VALUE: 1},
"observableTransformation": {
"strategy": USE_DEFAULT,
DEFAULT_VALUE: "lin"
DEFAULT_VALUE: "lin",
},
"noiseDistribution": {
"strategy": USE_DEFAULT,
DEFAULT_VALUE: "normal"
}
"noiseDistribution": {"strategy": USE_DEFAULT, DEFAULT_VALUE: "normal"},
}
DEFAULT_PAR_CONFIG = {
"parameterName": {
"strategy": COPY_FROM, SOURCE_COLUMN: "parameterId",
DEFAULT_VALUE: "new_parameter"
},
"parameterScale": {
"strategy": USE_DEFAULT, DEFAULT_VALUE: "log10"
},
"lowerBound": {
"strategy": MIN_COLUMN
},
"upperBound": {
"strategy": MAX_COLUMN
},
"estimate": {
"strategy": USE_DEFAULT, DEFAULT_VALUE: 1
},
"nominalValue": {
"strategy": SBML_LOOK
"strategy": COPY_FROM,
SOURCE_COLUMN: "parameterId",
DEFAULT_VALUE: "new_parameter",
},
"parameterScale": {"strategy": USE_DEFAULT, DEFAULT_VALUE: "log10"},
"lowerBound": {"strategy": MIN_COLUMN},
"upperBound": {"strategy": MAX_COLUMN},
"estimate": {"strategy": USE_DEFAULT, DEFAULT_VALUE: 1},
"nominalValue": {"strategy": SBML_LOOK},
}
DEFAULT_COND_CONFIG = {
"conditionId": {
"strategy": USE_DEFAULT, DEFAULT_VALUE: "new_condition"
},
"conditionName": {
"strategy": COPY_FROM, SOURCE_COLUMN: "conditionId"
}
"conditionId": {"strategy": USE_DEFAULT, DEFAULT_VALUE: "new_condition"},
"conditionName": {"strategy": COPY_FROM, SOURCE_COLUMN: "conditionId"},
}
DEFAULT_MEAS_CONFIG = {}
DEFAULT_CONFIGS = {
"observable": DEFAULT_OBS_CONFIG,
"parameter": DEFAULT_PAR_CONFIG,
"condition": DEFAULT_COND_CONFIG,
"measurement": DEFAULT_MEAS_CONFIG
"measurement": DEFAULT_MEAS_CONFIG,
}

COMMON_ERRORS = {
r"Error parsing '': Syntax error at \d+:\d+: mismatched input '<EOF>' "
r"expecting \{[^}]+\}" : "Invalid empty cell!"
r"expecting \{[^}]+\}": "Invalid empty cell!"
}
4 changes: 3 additions & 1 deletion src/petab_gui/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
from .app import main
"""Package for the PETAB GUI."""

from .app import main
5 changes: 3 additions & 2 deletions src/petab_gui/__main__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import sys
from petab_gui import app


def main():
app.main()


if __name__ == "__main__":
main()
main()
80 changes: 60 additions & 20 deletions src/petab_gui/app.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,66 @@
from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QFileOpenEvent, QIcon
from PySide6.QtCore import QEvent
from importlib.resources import files
import sys
import os
import petab.v1 as petab
import sys
from importlib.resources import files
from pathlib import Path

from PySide6.QtCore import QEvent
from PySide6.QtGui import QFileOpenEvent, QIcon
from PySide6.QtWidgets import QApplication

from .views import MainWindow
from .controllers import MainController
from .models import PEtabModel

from pathlib import Path
from .views import MainWindow


def find_example(path: Path) -> Path:
"""Find the example directory by traversing up from the given path.

Args:
path: The starting path to search from

Returns:
Path: The path to the example directory

Raises:
FileNotFoundError: If the example directory cannot be found
"""
while path.parent != path:
if (path / "example").is_dir():
return path / "example"
path = path.parent

raise FileNotFoundError("Could not find examples directory")


def get_icon() -> QIcon:
"""Get the Icon for the Window"""
"""Get the Icon for the Window."""
icon_path = files("petab_gui.assets").joinpath("PEtab.png")
if not icon_path.is_file():
raise FileNotFoundError(f"Icon file not found: {icon_path}")
icon = QIcon(str(icon_path))
return icon
return QIcon(str(icon_path))


class PEtabGuiApp(QApplication):
"""Main application class for PEtab GUI.

Inherits from QApplication and sets up the MVC components.
"""

def __init__(self):
"""Initialize the PEtab GUI application.

Sets up the model, view, and controller components.
Handles command line arguments for opening files.
"""
super().__init__(sys.argv)

# Load the stylesheet
# self.apply_stylesheet()
self.setWindowIcon(get_icon())
self.model = PEtabModel()
self.view = MainWindow()
self.view.setWindowIcon(get_icon())
self.controller = MainController(self.view, self.model)

# hack to be discussed
# Connect the view to the controller
self.view.controller = self.controller

if len(sys.argv) > 1 and os.path.isfile(sys.argv[1]):
Expand All @@ -52,27 +69,50 @@ def __init__(self):
self.view.show()

def event(self, event):
"""Handle application events.

Args:
event: The Qt event to handle

Returns:
bool: Result of the event handling from the parent class

Notes:
Currently handles FileOpen events to open files dropped on
the application.
"""
if event.type() == QEvent.FileOpen:
openEvent = QFileOpenEvent(event)
openEvent = QFileOpenEvent(event)
self.controller.open_file(openEvent.file(), mode="overwrite")

return super().event(event)

def apply_stylesheet(self):
"""Load and apply the QSS stylesheet."""
"""Load and apply the QSS stylesheet to the application.

Reads the stylesheet.css file from the same directory as this module
and applies it to the application. If the file doesn't exist,
no stylesheet is applied.
"""
stylesheet_path = os.path.join(
os.path.dirname(__file__), "stylesheet.css"
)
if os.path.exists(stylesheet_path):
with open(stylesheet_path, "r") as f:
with open(stylesheet_path) as f:
self.setStyleSheet(f.read())
else:
print(f"Warning: Stylesheet '{stylesheet_path}' not found!")
pass


def main():
"""Entry point for the PEtab GUI application.

Creates the application instance and starts the event loop.
The function exits with the return code from the application.
"""
app = PEtabGuiApp()
sys.exit(app.exec())


if __name__ == "__main__":
main()
Loading