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
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ petab_gui="petab_gui:main"

[project.scripts]
petab_gui_cli="petab_gui:main"

[tool.setuptools]
include-package-data = true

[tool.setuptools.package-data]
"petab_gui.assets" = ["PEtab.png"]
2 changes: 1 addition & 1 deletion src/petab_gui/C.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"conditionId": {"type": np.object_, "optional": False},
"conditionName": {"type": np.object_, "optional": False},
}
}
}

CONFIG = {
'window_title': 'My Application',
Expand Down
13 changes: 12 additions & 1 deletion src/petab_gui/app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QFileOpenEvent
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
Expand All @@ -21,6 +22,15 @@ def find_example(path: Path) -> Path:
raise FileNotFoundError("Could not find examples directory")


def get_icon() -> QIcon:
"""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


class PEtabGuiApp(QApplication):
def __init__(self):
super().__init__(sys.argv)
Expand All @@ -29,6 +39,7 @@ def __init__(self):
# self.apply_stylesheet()
self.model = PEtabModel()
self.view = MainWindow()
self.view.setWindowIcon(get_icon())
self.controller = MainController(self.view, self.model)

# hack to be discussed
Expand Down
Binary file added src/petab_gui/assets/PEtab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 11 additions & 4 deletions src/petab_gui/models/pandas_table_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,17 @@ def _set_data_single(self, index, value):
column_name = self._data_frame.columns[column - col_setoff]
old_value = self._data_frame.iloc[row, column - col_setoff]
# cast to numeric if necessary
if not self._data_frame[column_name].dtype == "object":
try:
value = float(value)
except ValueError:
expected_type = self._allowed_columns.get(column_name, None)
if is_invalid(value):
if not expected_type["optional"]:
return False
self._data_frame.iloc[row, column - col_setoff] = None
self.dataChanged.emit(index, index, [Qt.DisplayRole])
return True
if expected_type:
expected_type = expected_type["type"]
value, error_message = validate_value(value, expected_type)
if error_message:
self.new_log_message.emit(
f"Column '{column_name}' expects a numeric value",
"red"
Expand Down
13 changes: 9 additions & 4 deletions src/petab_gui/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,12 +488,10 @@ def highlightBlock(self, text):

def validate_value(value, expected_type):
try:
if expected_type == "STRING":
if expected_type == np.object_:
value = str(value)
elif expected_type == "NUMERIC":
elif expected_type == np.float64:
value = float(value)
elif expected_type == "BOOLEAN":
value = bool(value)
except ValueError as e:
return None, str(e)
return value, None
Expand Down Expand Up @@ -599,6 +597,12 @@ def get_selected_rectangles(table_view: QTableView) -> np.array:
selected = get_selected(table_view, mode=INDEX)
if not selected:
return None

model = table_view.model()
if hasattr(model, "mapToSource"):
# map all indices to source
selected = [model.mapToSource(index) for index in selected]

rows = [index.row() for index in selected]
cols = [index.column() for index in selected]
min_row, max_row = min(rows), max(rows)
Expand All @@ -609,6 +613,7 @@ def get_selected_rectangles(table_view: QTableView) -> np.array:
)
for index in selected:
selected_rect[index.row() - min_row, index.column() - min_col] = True

return selected_rect, rect_start


Expand Down
38 changes: 24 additions & 14 deletions src/petab_gui/views/table_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,33 +41,43 @@ def paste_from_clipboard(self):
text = clipboard.text()
if not text:
return

# Get the proxy and source models
proxy_model = self.table_view.model()
source_model = proxy_model.sourceModel()

# Get the start index from the current selection
start_index = self.table_view.selectionModel().currentIndex()
if not start_index.isValid():
return
model = self.table_view.model()
row_start, col_start = start_index.row(), start_index.column()
# identify which invalid cells are being pasted into

# Map the start index to the source model
source_index = proxy_model.mapToSource(start_index)
row_start, col_start = source_index.row(), source_index.column()

# Parse clipboard data
pasted_data = [line.split("\t") for line in text.split("\n") if
line.strip()]
num_rows = len(pasted_data)
num_cols = max([len(line) for line in pasted_data])
num_cols = max(len(line) for line in pasted_data)

# Identify which cells are being overridden
overridden_cells = {
(row_start + r, col_start + c)
for r in range(num_rows)
for c in range(num_cols)
if model.index(row_start + r, col_start + c).isValid()
if source_model.index(row_start + r, col_start + c).isValid()
}
invalid_overridden_cells = overridden_cells.intersection(
model._invalid_cells
)
if invalid_overridden_cells:

# Handle invalid cells
if hasattr(source_model, "_invalid_cells"):
invalid_overridden_cells = overridden_cells.intersection(
source_model._invalid_cells)
for row_invalid, col_invalid in invalid_overridden_cells:
model.discard_invalid_cell(row_invalid, col_invalid)
source_model.discard_invalid_cell(row_invalid, col_invalid)

model.setDataFromText(
text, start_index.row(),
start_index.column()
)
# Paste the data into the source model
source_model.setDataFromText(text, row_start, col_start)


class ComboBoxDelegate(QStyledItemDelegate):
Expand Down