|
1 | 1 | """Classes for the controllers of the tables in the GUI.""" |
2 | 2 | from PySide6.QtWidgets import QInputDialog, QMessageBox, QFileDialog, \ |
3 | 3 | QCompleter |
| 4 | +import numpy as np |
4 | 5 | import pandas as pd |
5 | 6 | import petab.v1 as petab |
6 | 7 | from PySide6.QtCore import Signal, QObject, QModelIndex, QPoint |
@@ -45,6 +46,7 @@ def __init__( |
45 | 46 | self.model.view = self.view.table_view |
46 | 47 | self.proxy_model = PandasTableFilterProxy(model) |
47 | 48 | self.logger = logger |
| 49 | + self.check_petab_lint_mode = True |
48 | 50 | self.mother_controller = mother_controller |
49 | 51 | self.view.table_view.setModel(self.model) |
50 | 52 | self.setup_connections() |
@@ -85,6 +87,8 @@ def setup_connections(self): |
85 | 87 |
|
86 | 88 | def validate_changed_cell(self, row, column): |
87 | 89 | """Validate the changed cell and whether its linting is correct.""" |
| 90 | + if not self.check_petab_lint_mode: |
| 91 | + return |
88 | 92 | row_data = self.model.get_df().iloc[row] |
89 | 93 | index_name = self.model.get_df().index.name |
90 | 94 | row_data = row_data.to_frame().T |
@@ -137,6 +141,12 @@ def open_table(self, file_path=None, separator=None, mode="overwrite"): |
137 | 141 | color="red" |
138 | 142 | ) |
139 | 143 | return |
| 144 | + dtypes = { |
| 145 | + col: self.model._allowed_columns.get( |
| 146 | + col, {"type": np.object_} |
| 147 | + )["type"] for col in new_df.columns |
| 148 | + } |
| 149 | + new_df = new_df.astype(dtypes) |
140 | 150 | if mode is None: |
141 | 151 | mode = prompt_overwrite_or_append(self) |
142 | 152 | # Overwrite or append the table with the new DataFrame |
@@ -259,12 +269,36 @@ def set_index_on_new_row(self, index: QModelIndex): |
259 | 269 | """Set the index of the model when a new row is added.""" |
260 | 270 | self.view.table_view.setCurrentIndex(index) |
261 | 271 |
|
| 272 | + def copy_to_clipboard(self): |
| 273 | + """Copy the currently selected cells to the clipboard.""" |
| 274 | + self.view.copy_to_clipboard() |
| 275 | + |
| 276 | + def paste_from_clipboard(self): |
| 277 | + """Paste the clipboard content to the currently selected cells.""" |
| 278 | + self.check_petab_lint_mode = False |
| 279 | + self.view.paste_from_clipboard() |
| 280 | + self.check_petab_lint_mode = True |
| 281 | + try: |
| 282 | + self.check_petab_lint() |
| 283 | + except Exception as e: |
| 284 | + self.logger.log_message( |
| 285 | + f"PEtab linter failed after copying: {str(e)}", |
| 286 | + color="red" |
| 287 | + ) |
| 288 | + def check_petab_lint(self, row_data): |
| 289 | + """Check a single row of the model with petablint.""" |
| 290 | + raise NotImplementedError( |
| 291 | + "This method must be implemented in child classes." |
| 292 | + ) |
| 293 | + |
262 | 294 |
|
263 | 295 | class MeasurementController(TableController): |
264 | 296 | """Controller of the Measurement table.""" |
265 | 297 |
|
266 | | - def check_petab_lint(self, row_data): |
267 | | - """Check a single row of the model with petablint.""" |
| 298 | + def check_petab_lint(self, row_data: pd.DataFrame = None): |
| 299 | + """Check a number of rows of the model with petablint.""" |
| 300 | + if row_data is None: |
| 301 | + row_data = self.model.get_df() |
268 | 302 | # Can this be done more elegantly? |
269 | 303 | observable_df = self.mother_controller.model.observable.get_df() |
270 | 304 | return petab.check_measurement_df( |
@@ -496,8 +530,10 @@ def setup_connections_specific(self): |
496 | 530 | self.maybe_rename_condition |
497 | 531 | ) |
498 | 532 |
|
499 | | - def check_petab_lint(self, row_data): |
500 | | - """Check a single row of the model with petablint.""" |
| 533 | + def check_petab_lint(self, row_data: pd.DataFrame = None): |
| 534 | + """Check a number of rows of the model with petablint.""" |
| 535 | + if row_data is None: |
| 536 | + row_data = self.model.get_df() |
501 | 537 | observable_df = self.mother_controller.model.observable.get_df() |
502 | 538 | sbml_model = self.mother_controller.model.sbml.get_current_sbml_model() |
503 | 539 | return petab.check_condition_df( |
@@ -532,7 +568,7 @@ def maybe_rename_condition(self, new_id, old_id): |
532 | 568 |
|
533 | 569 | def maybe_add_condition(self, condition_id, old_id=None): |
534 | 570 | """Add a condition to the condition table if it does not exist yet.""" |
535 | | - if condition_id in self.model.get_df().index: |
| 571 | + if condition_id in self.model.get_df().index or not condition_id: |
536 | 572 | return |
537 | 573 | # add a row |
538 | 574 | self.model.insertRows(position=None, rows=1) |
@@ -635,8 +671,10 @@ def setup_connections_specific(self): |
635 | 671 | self.maybe_rename_observable |
636 | 672 | ) |
637 | 673 |
|
638 | | - def check_petab_lint(self, row_data): |
639 | | - """Check a single row of the model with petablint.""" |
| 674 | + def check_petab_lint(self, row_data: pd.DataFrame = None): |
| 675 | + """Check a number of rows of the model with petablint.""" |
| 676 | + if row_data is None: |
| 677 | + row_data = self.model.get_df() |
640 | 678 | return petab.check_observable_df(row_data) |
641 | 679 |
|
642 | 680 | def maybe_rename_observable(self, new_id, old_id): |
@@ -668,7 +706,7 @@ def maybe_add_observable(self, observable_id, old_id=None): |
668 | 706 |
|
669 | 707 | Currently, `old_id` is not used. |
670 | 708 | """ |
671 | | - if observable_id in self.model.get_df().index: |
| 709 | + if observable_id in self.model.get_df().index or not observable_id: |
672 | 710 | return |
673 | 711 | # add a row |
674 | 712 | self.model.insertRows(position=None, rows=1) |
@@ -754,8 +792,10 @@ def setup_completers(self): |
754 | 792 | self.completers["parameterId"] |
755 | 793 | ) |
756 | 794 |
|
757 | | - def check_petab_lint(self, row_data): |
758 | | - """Check a single row of the model with petablint.""" |
| 795 | + def check_petab_lint(self, row_data: pd.DataFrame = None): |
| 796 | + """Check a number of rows of the model with petablint.""" |
| 797 | + if row_data is None: |
| 798 | + row_data = self.model.get_df() |
759 | 799 | observable_df = self.mother_controller.model.observable.get_df() |
760 | 800 | measurement_df = self.mother_controller.model.measurement.get_df() |
761 | 801 | condition_df = self.mother_controller.model.condition.get_df() |
|
0 commit comments