From 440f094e8a7e1c1c956719f4d93a9b3343d8d8e9 Mon Sep 17 00:00:00 2001 From: PaulJonasJost Date: Tue, 24 Mar 2026 14:39:56 +0100 Subject: [PATCH 1/3] Just removed from lint.ignore. Testing new workflows. Refs #242 --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5914c00..c2b224d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,6 @@ lint.ignore = [ "D101", # Ignore missing docstring in public classes "F401", # FIXME: those are ignored for now, should be fixed eventually - "E501", # Ignore line too long "ERA001", # Found commented-out code "T201", # `print` found" "SIM105", # Use `contextlib.suppress` From 51d0083ac7a8d865500c3f592c7165a701520efb Mon Sep 17 00:00:00 2001 From: PaulJonasJost Date: Tue, 24 Mar 2026 16:40:10 +0100 Subject: [PATCH 2/3] adopted line length --- src/petab_gui/commands.py | 4 +- .../controllers/file_io_controller.py | 43 ++- .../controllers/mother_controller.py | 16 +- src/petab_gui/controllers/plot_coordinator.py | 12 +- .../controllers/simulation_controller.py | 8 +- .../controllers/table_controllers.py | 51 ++-- src/petab_gui/controllers/utils.py | 12 +- .../controllers/validation_controller.py | 3 +- src/petab_gui/models/tooltips.py | 180 ++++++++---- src/petab_gui/resources/whats_this.py | 258 ++++++++++++------ src/petab_gui/settings/settings_model.py | 3 +- src/petab_gui/utils.py | 3 +- src/petab_gui/views/dialogs.py | 7 +- src/petab_gui/views/main_view.py | 5 +- src/petab_gui/views/simple_plot_view.py | 19 +- 15 files changed, 417 insertions(+), 207 deletions(-) diff --git a/src/petab_gui/commands.py b/src/petab_gui/commands.py index 9ba472a..6fb97d9 100644 --- a/src/petab_gui/commands.py +++ b/src/petab_gui/commands.py @@ -12,8 +12,8 @@ def _convert_dtype_with_nullable_int(series, dtype): """Convert a series to the specified dtype, handling nullable integers. When converting to integer types and the series contains NaN values, - this function automatically uses pandas nullable integer types (Int64, Int32, etc.) - instead of numpy integer types which don't support NaN. + this function automatically uses pandas nullable integer types (Int64, + Int32, etc.) instead of numpy integer types which don't support NaN. Args: series: The pandas Series to convert diff --git a/src/petab_gui/controllers/file_io_controller.py b/src/petab_gui/controllers/file_io_controller.py index 49032dd..b82fd63 100644 --- a/src/petab_gui/controllers/file_io_controller.py +++ b/src/petab_gui/controllers/file_io_controller.py @@ -1,7 +1,7 @@ """File I/O Controller for PEtab GUI. -This module contains the FileIOController class, which handles all file input/output -operations for PEtab models, including: +This module contains the FileIOController class, which handles all +file input/output operations for PEtab models, including: - Opening and saving PEtab YAML files - Opening and saving COMBINE archives (OMEX) - Opening and saving individual tables @@ -35,7 +35,8 @@ class FileIOController: Attributes ---------- main : MainController - Reference to the main controller for access to models, views, and other controllers. + Reference to the main controller for access to models, views, and + other controllers. model : PEtabModel The PEtab model being managed. view : MainWindow @@ -60,8 +61,9 @@ def __init__(self, main_controller): def save_model(self): """Save the entire PEtab model. - Opens a dialog to select the save format and location, then saves the model - as either a COMBINE archive (OMEX), ZIP file, or folder structure. + Opens a dialog to select the save format and location, then saves + the model as either a COMBINE archive (OMEX), ZIP file, or folder + structure. Returns ------- @@ -129,7 +131,8 @@ def save_single_table(self): Returns ------- bool or None - True if saved successfully, False if cancelled, None if no active table. + True if saved successfully, False if cancelled, None if no + active table. """ active_controller = self.main.active_controller() if not active_controller: @@ -253,8 +256,9 @@ def _open_file(self, actionable, file_path, sep, mode): Parameters ---------- actionable : str - Type of file: "yaml", "omex", "sbml", "measurement", "observable", - "parameter", "condition", "visualization", "simulation", "data_matrix". + Type of file: "yaml", "omex", "sbml", "measurement", + "observable", "parameter", "condition", "visualization", + "simulation", "data_matrix". file_path : str Path to the file. sep : str @@ -412,7 +416,8 @@ def _load_file_list(self, controller, file_list, file_type, yaml_dir): file_mode = "overwrite" if i == 0 else "append" controller.open_table(yaml_dir / file_name, mode=file_mode) self.logger.log_message( - f"Loaded {file_type} file ({i + 1}/{len(file_list)}): {file_name}", + f"Loaded {file_type} file ({i + 1}/{len(file_list)}): " + f"{file_name}", color="blue", ) @@ -478,8 +483,8 @@ def open_yaml_and_load_files(self, yaml_path=None, mode="overwrite"): ) if not all_exist: error_msg = ( - "The following files referenced in the YAML are missing:\n - " - + "\n - ".join(missing_files) + "The following files referenced in the YAML are " + "missing:\n - " + "\n - ".join(missing_files) ) self.logger.log_message(error_msg, color="red") QMessageBox.critical(self.view, "Missing Files", error_msg) @@ -565,7 +570,10 @@ def open_yaml_and_load_files(self, yaml_path=None, mode="overwrite"): self.main.unsaved_changes_change(False) except FileNotFoundError as e: - error_msg = f"File not found: {e.filename if hasattr(e, 'filename') else str(e)}" + error_msg = ( + f"File not found: " + f"{e.filename if hasattr(e, 'filename') else str(e)}" + ) self.logger.log_message(error_msg, color="red") QMessageBox.warning(self.view, "File Not Found", error_msg) except KeyError as e: @@ -659,12 +667,14 @@ def load_example(self, example_name): Parameters ---------- example_name : str - Name of the example subdirectory (e.g., "Boehm", "Simple_Conversion"). + Name of the example subdirectory (e.g., "Boehm", + "Simple_Conversion"). Notes ----- Finds and loads the example dataset from the package directory. - No internet connection required - the example is bundled with the package. + No internet connection required - the example is bundled with the + package. """ try: # Use importlib.resources to access packaged example files @@ -687,7 +697,10 @@ def load_example(self, example_name): with as_file(yaml_file) as yaml_path: if not yaml_path.exists(): - error_msg = f"Example '{example_name}' not found or problem.yaml file is missing." + error_msg = ( + f"Example '{example_name}' not found or " + f"problem.yaml file is missing." + ) self.logger.log_message(error_msg, color="red") QMessageBox.warning( self.view, "Example Invalid", error_msg diff --git a/src/petab_gui/controllers/mother_controller.py b/src/petab_gui/controllers/mother_controller.py index 31a6da9..ce2eb69 100644 --- a/src/petab_gui/controllers/mother_controller.py +++ b/src/petab_gui/controllers/mother_controller.py @@ -728,7 +728,8 @@ def replace(self): self.view.toggle_replace() def _toggle_whats_this_mode(self, on: bool): - """Enable/disable click-to-help mode by installing/removing the global filter. + """ + Enable/disable click-to-help mode. On enter: show a short instruction bubble. """ @@ -754,7 +755,7 @@ def _toggle_whats_this_mode(self, on: bool): self._show_help_welcome() def _show_help_welcome(self): - """Centered welcome with a 'Don't show again' option persisted in QSettings.""" + """Welcome with a 'Don't show again' option persisted in QSettings.""" settings = settings_manager.settings if settings.value("help_mode/welcome_disabled", False, type=bool): return @@ -765,9 +766,12 @@ def _show_help_welcome(self): msg.setText( "Welcome to help mode
" "" ) dont = QCheckBox("Don't show again") @@ -795,7 +799,7 @@ def about(self): ) def _show_next_steps_panel(self): - """Show the next steps panel (ignores 'don't show again' preference).""" + """Show the next steps panel.""" # Sync checkbox state with current settings dont_show = settings_manager.get_value( "next_steps/dont_show_again", False, bool diff --git a/src/petab_gui/controllers/plot_coordinator.py b/src/petab_gui/controllers/plot_coordinator.py index 9bf01d5..073fdbf 100644 --- a/src/petab_gui/controllers/plot_coordinator.py +++ b/src/petab_gui/controllers/plot_coordinator.py @@ -23,7 +23,8 @@ class PlotCoordinator: Attributes ---------- main : MainController - Reference to the main controller for access to models, views, and other controllers. + Reference to the main controller for access to models, views, and + other controllers. model : PEtabModel The PEtab model being visualized. view : MainWindow @@ -188,7 +189,8 @@ def _on_plot_point_clicked(self, x, y, label, data_type): x : float X-coordinate of the clicked point (time). y : float - Y-coordinate of the clicked point (measurement or simulation value). + Y-coordinate of the clicked point (measurement or simulation + value). label : str Label of the clicked point (observable ID). data_type : str @@ -250,7 +252,8 @@ def column_index(name): and self._floats_match(row_y, y) ): # Manually update highlight BEFORE selecting row - # This ensures the circle appears even though we skip the signal handler + # This ensures the circle appears even though we skip + # the signal handler if data_type == "measurement": self.plotter.highlight_from_selection([row]) else: @@ -272,7 +275,8 @@ def column_index(name): # Provide feedback if no match found if not matched: self.logger.log_message( - f"No matching row found for plot point (obs={obs}, x={x:.4g}, y={y:.4g})", + f"No matching row found for plot point " + f"(obs={obs}, x={x:.4g}, y={y:.4g})", color="orange", ) diff --git a/src/petab_gui/controllers/simulation_controller.py b/src/petab_gui/controllers/simulation_controller.py index 24b6652..3ef9a4d 100644 --- a/src/petab_gui/controllers/simulation_controller.py +++ b/src/petab_gui/controllers/simulation_controller.py @@ -22,7 +22,8 @@ class SimulationController: Attributes ---------- main : MainController - Reference to the main controller for access to models, views, and other controllers. + Reference to the main controller for access to models, views, and + other controllers. model : PEtabModel The PEtab model being simulated. logger : LoggerController @@ -80,7 +81,7 @@ def simulate(self): value = sbml_model.get_parameter_value(param_id) nominal_values.append(value) except Exception: - # If parameter not found in SBML, use default value of 1 + # If parameter not found in SBML, use default value nominal_values.append(1.0) # Add nominalValue column to parameter_df @@ -108,7 +109,8 @@ def simulate(self): # report current basico / COPASI version self.logger.log_message( - f"Simulate with basico: {basico.__version__}, COPASI: {basico.COPASI.__version__}", + f"Simulate with basico: {basico.__version__}, " + f"COPASI: {basico.COPASI.__version__}", color="green", ) diff --git a/src/petab_gui/controllers/table_controllers.py b/src/petab_gui/controllers/table_controllers.py index b51dc32..52bf16b 100644 --- a/src/petab_gui/controllers/table_controllers.py +++ b/src/petab_gui/controllers/table_controllers.py @@ -227,8 +227,9 @@ def append_df(self, new_df: pd.DataFrame): self.model.beginResetModel() current_df = self.model.get_df() - # For tables without a named index (measurement, visualization, simulation), - # ignore the index to avoid removing appended data due to index conflicts + # For tables without a named index (measurement, visualization, + # simulation), ignore the index to avoid removing appended data due + # to index conflicts if self.model.table_type in [ "measurement", "visualization", @@ -238,7 +239,8 @@ def append_df(self, new_df: pd.DataFrame): [current_df, new_df], axis=0, ignore_index=True ) else: - # For tables with named indices, concatenate and remove duplicate indices + # For tables with named indices, concatenate and remove + # duplicate indices combined_df = pd.concat([current_df, new_df], axis=0) combined_df = combined_df[ ~combined_df.index.duplicated(keep="first") @@ -784,12 +786,14 @@ def _detect_time_column(self, df) -> str | None: return None def _rank_dose_candidates(self, df: pd.DataFrame) -> list[str]: - """Rank DataFrame columns by likelihood of containing dose/concentration data. + """Rank columns by likelihood of containing dose/concentration data. - This method implements a lightweight scoring system to identify and rank - columns that are most likely to contain dose, concentration, or drug-related - data. The ranking is based on multiple heuristics including column naming - patterns, data types, value ranges, and statistical properties. + This method implements a lightweight scoring system to identify + and rank columns that are most likely to contain dose, + concentration, or drug-related + data. The ranking is based on multiple heuristics including column + naming patterns, data types, value ranges, and statistical + properties. Parameters ---------- @@ -800,29 +804,32 @@ def _rank_dose_candidates(self, df: pd.DataFrame) -> list[str]: Returns ------- list[str] - Column names sorted by descending likelihood of containing dose data. - Columns with higher scores appear first. In case of tied scores, - columns with fewer unique values are ranked higher. + Column names sorted by descending likelihood of containing + dose data. Columns with higher scores appear first. In case of + tied scores, columns with fewer unique values are ranked higher. Notes ----- The scoring algorithm considers the following criteria: - - **Name matching** (+2.0 points): Column names containing keywords like - 'dose', 'conc', 'concentration', 'drug', 'compound', 'stim', 'input', - or patterns like 'u' (case-insensitive). + - **Name matching** (+2.0 points): Column names containing keywords + like 'dose', 'conc', 'concentration', 'drug', 'compound', 'stim', + 'input', or patterns like 'u' (case-insensitive). - - **Numeric data type** (+1.0 points): Columns with integer or float dtype. + - **Numeric data type** (+1.0 points): Columns with integer or float + dtype. - - **Reasonable cardinality** (+0.8 points): Columns with 2-30 unique - non-null values, which is typical for dose series. + - **Reasonable cardinality** (+0.8 points): Columns with 2-30 + unique non-null values, which is typical for dose series. - - **Non-negative values** (+0.3 points): All values are >= 0 when converted - to numeric (dose/concentration values are typically non-negative). + - **Non-negative values** (+0.3 points): All values are >= 0 when + converted to numeric (dose/concentration values are typically + non-negative). - - **Monotonic tendency** (+0.2 points): At least 70% of consecutive numeric - differences are non-decreasing, indicating potential dose escalation - patterns. Requires at least 5 non-null numeric values. + - **Monotonic tendency** (+0.2 points): At least 70% of + consecutive numeric differences are non-decreasing, indicating + potential dose escalation patterns. Requires at least 5 non-null + numeric values. Raises ------ diff --git a/src/petab_gui/controllers/utils.py b/src/petab_gui/controllers/utils.py index 3af5af8..f8ef706 100644 --- a/src/petab_gui/controllers/utils.py +++ b/src/petab_gui/controllers/utils.py @@ -29,9 +29,12 @@ class _WhatsThisClickHelp(QObject): While the action is checked: • Left-click shows a What's This bubble for the target under cursor. - • Clicking the SAME target again closes the bubble (stays in help mode). - • ESC closes the bubble if one is open; if none is open, exits help mode. - • Special cases: QTabBar tabs and QHeaderView sections handled explicitly. + • Clicking the SAME target again closes the bubble (stays in help + mode). + • ESC closes the bubble if one is open; if none is open, exits help + mode. + • Special cases: QTabBar tabs and QHeaderView sections handled + explicitly. """ def __init__(self, action): @@ -81,7 +84,8 @@ def eventFilter(self, _obj, ev): if isinstance(w, QScrollBar): return False - # If click landed outside app widgets (e.g., on the bubble), just close it + # If click landed outside app widgets (e.g., on the bubble), + # just close it if not w: self._has_bubble = False return True diff --git a/src/petab_gui/controllers/validation_controller.py b/src/petab_gui/controllers/validation_controller.py index 550cd13..712d4af 100644 --- a/src/petab_gui/controllers/validation_controller.py +++ b/src/petab_gui/controllers/validation_controller.py @@ -23,7 +23,8 @@ class ValidationController: Attributes ---------- main : MainController - Reference to the main controller for access to models, views, and other controllers. + Reference to the main controller for access to models, views, and + other controllers. model : PEtabModel The PEtab model being validated. logger : LoggerController diff --git a/src/petab_gui/models/tooltips.py b/src/petab_gui/models/tooltips.py index 3cb2ff6..00dac90 100644 --- a/src/petab_gui/models/tooltips.py +++ b/src/petab_gui/models/tooltips.py @@ -24,23 +24,32 @@ def register_tips( "measurement", header={ "observableId": "ID from Observables; the output being measured.", - "preequilibrationConditionId": "Condition for preequilibration; empty = none.", + "preequilibrationConditionId": ( + "Condition for preequilibration; empty = none." + ), "simulationConditionId": "Condition ID used for simulation.", "time": "Time in SBML units; number or 'inf' (steady state).", - "measurement": "Measured value; same units/scale as model output.", - "observableParameters": "Override placeholder params; ';'-separated values/names; empty if none.", - "noiseParameters": "Std dev σ as value or name; NaN if σ is modeled.", + "measurement": ("Measured value; same units/scale as model output."), + "observableParameters": ( + "Override placeholder params; ';'-separated values/names; " + "empty if none." + ), + "noiseParameters": ( + "Std dev σ as value or name; NaN if σ is modeled." + ), "datasetId": "Dataset grouping ID (for plotting).", "replicateId": "Replicate label within datasetId.", }, cell={ "observableId": "Must exist in Observables.", - "preequilibrationConditionId": "Condition ID or empty for no preeq.", + "preequilibrationConditionId": ("Condition ID or empty for no preeq."), "simulationConditionId": "Condition ID from Conditions.", "time": "e.g. 0, 5, 10 or 'inf' for steady state.", "measurement": "Numeric observed value.", - "observableParameters": "Match placeholders; provide n ';'-separated values/names.", - "noiseParameters": "Sigma value or parameter name; NaN if modeled.", + "observableParameters": ( + "Match placeholders; provide n ';'-separated values/names." + ), + "noiseParameters": ("Sigma value or parameter name; NaN if modeled."), "datasetId": "Optional dataset ID to group points.", "replicateId": "Optional replicate tag within dataset.", }, @@ -50,20 +59,47 @@ def register_tips( register_tips( "observable", header={ - "observableId": "Unique ID; letters, digits, underscores; not starting with digit. Referenced in Measurements.", - "observableName": "Optional display name; not used for identification.", - "observableFormula": "Observation function as text formula. May use SBML symbols or parameters from parameter table. Can introduce placeholder parameters observableParameter{n}_{observableId}.", - "observableTransformation": "Transformation applied in objective; one of lin, log, log10. Default: lin.", - "noiseFormula": "Noise model formula or numeric σ. May include noiseParameter{n}_{observableId}. Must be overridden in Measurement table if present.", - "noiseDistribution": "Noise distribution type: normal (σ) or laplace (scale). Default: normal.", + "observableId": ( + "Unique ID; letters, digits, underscores; not starting with " + "digit. Referenced in Measurements." + ), + "observableName": ( + "Optional display name; not used for identification." + ), + "observableFormula": ( + "Observation function as text formula. May use SBML symbols " + "or parameters from parameter table. Can introduce placeholder " + "parameters observableParameter{n}_{observableId}." + ), + "observableTransformation": ( + "Transformation applied in objective; one of lin, log, log10. " + "Default: lin." + ), + "noiseFormula": ( + "Noise model formula or numeric σ. May include " + "noiseParameter{n}_{observableId}. Must be overridden in " + "Measurement table if present." + ), + "noiseDistribution": ( + "Noise distribution type: normal (σ) or laplace (scale). " + "Default: normal." + ), }, cell={ - "observableId": "Identifier; must be valid and unique. Used in Measurement table.", + "observableId": ( + "Identifier; must be valid and unique. Used in Measurement table." + ), "observableName": "Optional label for output/plots.", - "observableFormula": "E.g. a species ID, assignment rule, or formula with parameters.", + "observableFormula": ( + "E.g. a species ID, assignment rule, or formula with parameters." + ), "observableTransformation": "Choose: lin, log, or log10.", - "noiseFormula": "Numeric σ or formula with noiseParameter{n}_{observableId}.", - "noiseDistribution": "normal (σ as std dev) or laplace (σ as scale).", + "noiseFormula": ( + "Numeric σ or formula with noiseParameter{n}_{observableId}." + ), + "noiseDistribution": ( + "normal (σ as std dev) or laplace (σ as scale)." + ), }, ) @@ -71,30 +107,52 @@ def register_tips( register_tips( "parameter", header={ - "parameterId": "Unique ID; must match SBML parameter, condition override, or observable/noise parameter.", - "parameterName": "Optional label for plotting; may differ from SBML name.", + "parameterId": ( + "Unique ID; must match SBML parameter, condition override, " + "or observable/noise parameter." + ), + "parameterName": ( + "Optional label for plotting; may differ from SBML name." + ), "parameterScale": "Scale for estimation: lin, log, or log10.", - "lowerBound": "Lower bound (linear space). Optional if estimate==0.", - "upperBound": "Upper bound (linear space). Optional if estimate==0.", - "nominalValue": "Value if not estimated (estimate==0). Linear space.", + "lowerBound": ("Lower bound (linear space). Optional if estimate==0."), + "upperBound": ("Upper bound (linear space). Optional if estimate==0."), + "nominalValue": ( + "Value if not estimated (estimate==0). Linear space." + ), "estimate": "1 = estimated, 0 = fixed.", - "initializationPriorType": "Prior for sampling initial points. Default: parameterScaleUniform.", - "initializationPriorParameters": "Parameters for initialization prior; ';'-separated; numeric.", - "objectivePriorType": "Prior type used in objective function.", - "objectivePriorParameters": "Parameters for objective prior; ';'-separated; numeric.", + "initializationPriorType": ( + "Prior for sampling initial points. Default: " + "parameterScaleUniform." + ), + "initializationPriorParameters": ( + "Parameters for initialization prior; ';'-separated; numeric." + ), + "objectivePriorType": ("Prior type used in objective function."), + "objectivePriorParameters": ( + "Parameters for objective prior; ';'-separated; numeric." + ), }, cell={ - "parameterId": "Must match SBML/condition/observable/noise parameter ID.", + "parameterId": ( + "Must match SBML/condition/observable/noise parameter ID." + ), "parameterName": "Optional descriptive name.", "parameterScale": "Choose lin, log, or log10.", "lowerBound": "Numeric lower bound (linear space).", "upperBound": "Numeric upper bound (linear space).", "nominalValue": "Numeric value if not estimated.", "estimate": "0 = fixed, 1 = estimated.", - "initializationPriorType": "E.g., uniform, normal, laplace, logNormal, parameterScaleUniform…", - "initializationPriorParameters": "Numeric parameters for init prior; e.g., mean;stddev.", - "objectivePriorType": "Prior type for optimization objective.", - "objectivePriorParameters": "Numeric parameters for objective prior.", + "initializationPriorType": ( + "E.g., uniform, normal, laplace, logNormal, parameterScaleUniform…" + ), + "initializationPriorParameters": ( + "Numeric parameters for init prior; e.g., mean;stddev." + ), + "objectivePriorType": ("Prior type for optimization objective."), + "objectivePriorParameters": ( + "Numeric parameters for objective prior." + ), }, ) @@ -102,16 +160,24 @@ def register_tips( register_tips( "condition", header={ - "conditionId": "Unique ID; letters/digits/underscores; not starting with digit. Referenced by Measurements.", - "conditionName": "Optional human-readable name for reports/plots.", - "*": "User-defined column. Needs to be SBML ID column: parameter, species, or compartment.", + "conditionId": ( + "Unique ID; letters/digits/underscores; not starting with " + "digit. Referenced by Measurements." + ), + "conditionName": ("Optional human-readable name for reports/plots."), + "*": ( + "User-defined column. Needs to be SBML ID column: parameter, " + "species, or compartment." + ), }, cell={ "conditionId": "Enter a valid, unique identifier.", "conditionName": "Optional label.", - "*": "User-defined column. Provide numeric value or SBML/parameter ID. " - "Species IDs = initial amount/concentration (NaN = keep preeq/initial). " - "Compartment IDs = initial size.", + "*": ( + "User-defined column. Provide numeric value or SBML/parameter " + "ID. Species IDs = initial amount/concentration (NaN = keep " + "preeq/initial). Compartment IDs = initial size." + ), }, ) @@ -120,13 +186,22 @@ def register_tips( header={ "plotId": "Plot ID; datasets with same ID share axes.", "plotName": "Optional plot display name.", - "plotTypeSimulation": "LinePlot | BarPlot | ScatterPlot. Default: LinePlot.", - "plotTypeData": "MeanAndSD | MeanAndSEM | replicate | provided. Default: MeanAndSD.", - "datasetId": "Dataset grouping ID from Measurements (optional).", - "xValues": "Independent variable: 'time' (default) or parameter/state ID.", + "plotTypeSimulation": ( + "LinePlot | BarPlot | ScatterPlot. Default: LinePlot." + ), + "plotTypeData": ( + "MeanAndSD | MeanAndSEM | replicate | provided. " + "Default: MeanAndSD." + ), + "datasetId": ("Dataset grouping ID from Measurements (optional)."), + "xValues": ( + "Independent variable: 'time' (default) or parameter/state ID." + ), "xOffset": "Offset added to x values (default 0).", "xLabel": "X-axis label; defaults to xValues.", - "xScale": "lin | log | log10 | order (only with LinePlot). Default: lin.", + "xScale": ( + "lin | log | log10 | order (only with LinePlot). Default: lin." + ), "yValues": "Observable ID to plot on Y.", "yOffset": "Offset added to y values (default 0).", "yLabel": "Y-axis label; defaults to yValues.", @@ -136,8 +211,10 @@ def register_tips( cell={ "plotId": "Required; same ID -> same axes.", "plotName": "Optional human-readable name.", - "plotTypeSimulation": "Choose: LinePlot, BarPlot, or ScatterPlot.", - "plotTypeData": "Choose: MeanAndSD, MeanAndSEM, replicate, or provided.", + "plotTypeSimulation": ("Choose: LinePlot, BarPlot, or ScatterPlot."), + "plotTypeData": ( + "Choose: MeanAndSD, MeanAndSEM, replicate, or provided." + ), "datasetId": "Optional dataset ID to include in this plot.", "xValues": "Use 'time' or a parameter/state ID.", "xOffset": "Numeric x offset (e.g., 0).", @@ -210,8 +287,8 @@ def cell_tip(table: str, column: str) -> str: COND_TABLE_TOOLTIP = ( "Condition table
" "• Define condition IDs and names.
" - "• User-defined SBML ID columns override parameters/species initial " - "states (NaN keeps preeq/initial)." + "• User-defined SBML ID columns override parameters/species " + "initial states (NaN keeps preeq/initial)." ) VIS_TABLE_TOOLTIP = ( @@ -239,14 +316,15 @@ def cell_tip(table: str, column: str) -> str: DATA_TABLES_TAB_TOOLTIP = ( "Data Tables
" - "• Edit PEtab tables: Measurement, Observable, Parameter, Condition," - " Visualization.
" - "• Hover headers for definitions;" - " use toolbar to add/remove rows and import/export." + "• Edit PEtab tables: Measurement, Observable, Parameter, " + "Condition, Visualization.
" + "• Hover headers for definitions; use toolbar to add/remove rows " + "and import/export." ) SBML_MODEL_TAB_TOOLTIP = ( "SBML Model
" "• Edit SBML (XML) and Antimony side-by-side.
" - "• Use Forward Changes buttons to sync; see logger for errors." + "• Use Forward Changes buttons to sync; see logger for " + "errors." ) diff --git a/src/petab_gui/resources/whats_this.py b/src/petab_gui/resources/whats_this.py index 4a5d7ee..7c03f59 100644 --- a/src/petab_gui/resources/whats_this.py +++ b/src/petab_gui/resources/whats_this.py @@ -5,19 +5,25 @@ "data_tables": ( "Data Tables
" "
    " - "
  • Edit PEtab tables: Measurement, Observable, Parameter, Condition, Visualization.
  • " - "
  • Hover headers for definitions; right-click for context actions.
  • " - "
  • Keep IDs consistent across tables (e.g., observableId, condition IDs).
  • " - "
  • Import/export to manage files; validation highlights issues.
  • " + "
  • Edit PEtab tables: Measurement, Observable, Parameter, " + "Condition, Visualization.
  • " + "
  • Hover headers for definitions; right-click for context " + "actions.
  • " + "
  • Keep IDs consistent across tables (e.g., " + "observableId, condition IDs).
  • " + "
  • Import/export to manage files; validation highlights " + "issues.
  • " "
" ), "sbml_model": ( "SBML Model
" "
    " "
  • Edit SBML (XML) and Antimony side-by-side.
  • " - "
  • Use the Forward Changes buttons to convert/sync between views.
  • " + "
  • Use the Forward Changes buttons to convert/sync " + "between views.
  • " "
  • Errors and warnings appear in the logger panel below.
  • " - "
  • Some constructs may not round-trip perfectly; keep the canonical SBML copy.
  • " + "
  • Some constructs may not round-trip perfectly; keep the " + "canonical SBML copy.
  • " "
" ), }, @@ -25,7 +31,8 @@ "sbml_editor": ( "SBML editor (XML)
" "
    " - "
  • Paste or edit valid SBML (L2/L3); keep namespaces intact.
  • " + "
  • Paste or edit valid SBML (L2/L3); keep namespaces " + "intact.
  • " "
  • Click Forward → Antimony to generate Antimony.
  • " "
  • Prefer this pane for full SBML feature coverage.
  • " "
  • Conversion issues are reported in the logger.
  • " @@ -46,67 +53,87 @@ "table": ( "Measurement table
    " "
      " - "
    • Each row is a data point with time, value, and linked IDs.
    • " - "
    • observableId must exist in Observable; " + "
    • Each row is a data point with time, value, and linked " + "IDs.
    • " + "
    • observableId must exist in " + "Observable; " "condition IDs must exist in Condition.
    • " "
    • Use 'inf' for steady-state times.
    • " - "
    • Override placeholders observableParameter{n}_{observableId} " - "and noise parameters noiseParameter{n}_{observableId} when defined.
    • " + "
    • Override placeholders " + "observableParameter{n}_{observableId} " + "and noise parameters " + "noiseParameter{n}_{observableId} when " + "defined.
    • " "
    " ), "columns": { "observableId": ( "
      " - "
    • Reference to an observable defined in the Observable table.
    • " - "
    • Must match an existing observableId.
    • " + "
    • Reference to an observable defined in the Observable " + "table.
    • " + "
    • Must match an existing " + "observableId.
    • " "
    " ), "preequilibrationConditionId": ( "
      " - "
    • Condition used for pre-equilibration; empty = none.
    • " + "
    • Condition used for pre-equilibration; empty = " + "none.
    • " "
    • Must be a valid condition ID if provided.
    • " "
    " ), "simulationConditionId": ( "
      " - "
    • Condition used for simulation parameters (required).
    • " + "
    • Condition used for simulation parameters " + "(required).
    • " "
    • Must be a valid condition ID.
    • " "
    " ), "time": ( "
      " - "
    • Numeric time in SBML units, or 'inf' for steady state.
    • " - "
    • Use a consistent unit system across data and model.
    • " + "
    • Numeric time in SBML units, or 'inf' " + "for steady state.
    • " + "
    • Use a consistent unit system across data and " + "model.
    • " "
    " ), "measurement": ( "
      " - "
    • Observed numeric value in the same scale/units as the model output.
    • " - "
    • Leave blank for missing values if supported by your workflow.
    • " + "
    • Observed numeric value in the same scale/units as " + "the model output.
    • " + "
    • Leave blank for missing values if supported by your " + "workflow.
    • " "
    " ), "observableParameters": ( "
      " - "
    • Overrides for placeholders defined in the observable formula.
    • " - "
    • Provide n semicolon-separated values/names for " - "observableParameter{n}_{observableId}; empty if none.
    • " + "
    • Overrides for placeholders defined in the observable " + "formula.
    • " + "
    • Provide n semicolon-separated " + "values/names for " + "observableParameter{n}_{observableId}; " + "empty if none.
    • " "
    " ), "noiseParameters": ( "
      " - "
    • Noise std-dev (or parameter names); NaN if σ is a model parameter.
    • " - "
    • Same rules as observableParameters for lists and naming.
    • " + "
    • Noise std-dev (or parameter names); " + "NaN if σ is a model parameter.
    • " + "
    • Same rules as observableParameters for " + "lists and naming.
    • " "
    " ), "datasetId": ( "
      " - "
    • Grouping key for plotting (datasets share style/axes).
    • " + "
    • Grouping key for plotting (datasets share " + "style/axes).
    • " "
    • Optional; defaults to per-row if omitted.
    • " "
    " ), "replicateId": ( "
      " - "
    • Label to distinguish replicates within a dataset.
    • " + "
    • Label to distinguish replicates within a " + "dataset.
    • " "
    • Enables error bars/replicate plotting modes.
    • " "
    " ), @@ -116,8 +143,10 @@ "table": ( "Simulation table
    " "
      " - "
    • Holds simulated outputs aligned to measurement definitions.
    • " - "
    • Same IDs as Measurement (observable/conditions/time) for comparison.
    • " + "
    • Holds simulated outputs aligned to measurement " + "definitions.
    • " + "
    • Same IDs as Measurement (observable/conditions/time) for " + "comparison.
    • " "
    • Populated by simulator/export; typically read-only.
    • " "
    " ), @@ -125,55 +154,66 @@ "observableId": ( "
      " "
    • Observable whose simulation is reported.
    • " - "
    • Must match an observableId in Observable.
    • " + "
    • Must match an observableId in " + "Observable.
    • " "
    " ), "preequilibrationConditionId": ( "
      " - "
    • Preequilibration condition used during simulation; empty = none.
    • " + "
    • Preequilibration condition used during simulation; " + "empty = none.
    • " "
    • Must be a valid condition ID if set.
    • " "
    " ), "simulationConditionId": ( "
      " - "
    • Condition used to set simulation parameters (required).
    • " + "
    • Condition used to set simulation parameters " + "(required).
    • " "
    • Must be a valid condition ID.
    • " "
    " ), "time": ( "
      " - "
    • Time point for the simulated value (numeric or 'inf').
    • " + "
    • Time point for the simulated value (numeric or " + "'inf').
    • " "
    • Use same units as the model.
    • " "
    " ), "simulation": ( "
      " - "
    • Simulated numeric value (same scale/units as measurement).
    • " + "
    • Simulated numeric value (same scale/units as " + "measurement).
    • " "
    • Used for plotting and residuals.
    • " "
    " ), "observableParameters": ( "
      " - "
    • Parameters used to evaluate observable placeholders, if applicable.
    • " - "
    • Semicolon-separated values/names; mirrors Measurement rules.
    • " + "
    • Parameters used to evaluate observable placeholders, " + "if applicable.
    • " + "
    • Semicolon-separated values/names; mirrors " + "Measurement rules.
    • " "
    " ), "noiseParameters": ( "
      " - "
    • Noise parameters applied for simulation/plotting modes.
    • " + "
    • Noise parameters applied for simulation/plotting " + "modes.
    • " "
    • Numeric values or names; may be empty.
    • " "
    " ), "datasetId": ( "
      " "
    • Dataset grouping to match plotted series.
    • " - "
    • Optional; align with Measurement for overlays.
    • " + "
    • Optional; align with Measurement for " + "overlays.
    • " "
    " ), "replicateId": ( "
      " - "
    • Replicate label, if simulations are per-replicate.
    • " - "
    • Usually empty unless replicates are simulated explicitly.
    • " + "
    • Replicate label, if simulations are " + "per-replicate.
    • " + "
    • Usually empty unless replicates are simulated " + "explicitly.
    • " "
    " ), }, @@ -182,17 +222,22 @@ "table": ( "Observable table
    " "
      " - "
    • Defines how model states/expressions map to measured outputs.
    • " - "
    • May introduce placeholders observableParameter{n}_{observableId} " + "
    • Defines how model states/expressions map to measured " + "outputs.
    • " + "
    • May introduce placeholders " + "observableParameter{n}_{observableId} " "that are overridden per-measurement.
    • " - "
    • Noise model can be numeric σ or a formula; distribution optional.
    • " + "
    • Noise model can be numeric σ or a formula; distribution " + "optional.
    • " "
    " ), "columns": { "observableId": ( "
      " - "
    • Unique identifier (letters/digits/underscores; not starting with a digit).
    • " - "
    • Referenced by measurement.observableId.
    • " + "
    • Unique identifier (letters/digits/underscores; not " + "starting with a digit).
    • " + "
    • Referenced by " + "measurement.observableId.
    • " "
    " ), "observableName": ( @@ -203,27 +248,36 @@ ), "observableFormula": ( "
      " - "
    • Expression using SBML symbols/parameters (e.g., species ID).
    • " - "
    • May define observableParameter{n}_{observableId} placeholders.
    • " + "
    • Expression using SBML symbols/parameters (e.g., " + "species ID).
    • " + "
    • May define " + "observableParameter{n}_{observableId} " + "placeholders.
    • " "
    " ), "observableTransformation": ( "
      " - "
    • Transformation for objective: lin, log, or log10.
    • " - "
    • Defaults to lin; data and outputs assumed linear if not set.
    • " + "
    • Transformation for objective: lin, " + "log, or log10.
    • " + "
    • Defaults to lin; data and outputs " + "assumed linear if not set.
    • " "
    " ), "noiseFormula": ( "
      " - "
    • Numeric σ (implies normal) or formula for complex noise.
    • " - "
    • May include noiseParameter{n}_{observableId}; " + "
    • Numeric σ (implies normal) or formula for complex " + "noise.
    • " + "
    • May include " + "noiseParameter{n}_{observableId}; " "values provided in Measurement.
    • " "
    " ), "noiseDistribution": ( "
      " - "
    • normal (σ = std dev) or laplace (σ = scale).
    • " - "
    • Log-variants via observableTransformation = log/log10.
    • " + "
    • normal (σ = std dev) or " + "laplace (σ = scale).
    • " + "
    • Log-variants via " + "observableTransformation = log/log10.
    • " "
    " ), }, @@ -232,15 +286,18 @@ "table": ( "Parameter table
    " "
      " - "
    • Declares parameters, estimation flag, and bounds (linear space).
    • " - "
    • parameterId must match SBML or overrides used elsewhere.
    • " + "
    • Declares parameters, estimation flag, and bounds (linear " + "space).
    • " + "
    • parameterId must match SBML or overrides " + "used elsewhere.
    • " "
    • Optional priors for initialization and/or objective.
    • " "
    " ), "columns": { "parameterId": ( "
      " - "
    • Must match an SBML parameter, a condition override, or names used in measurements.
    • " + "
    • Must match an SBML parameter, a condition override, " + "or names used in measurements.
    • " "
    • Unique within this table.
    • " "
    " ), @@ -252,8 +309,10 @@ ), "parameterScale": ( "
      " - "
    • Estimation scale: lin, log, or log10.
    • " - "
    • Affects optimization scaling, not storage format.
    • " + "
    • Estimation scale: lin, " + "log, or log10.
    • " + "
    • Affects optimization scaling, not storage " + "format.
    • " "
    " ), "lowerBound": ( @@ -270,38 +329,46 @@ ), "nominalValue": ( "
      " - "
    • Value used when fixed (estimate==0), in linear space.
    • " + "
    • Value used when fixed (estimate==0), in " + "linear space.
    • " "
    • Optional otherwise.
    • " "
    " ), "estimate": ( "
      " - "
    • 1 = estimated; 0 = fixed to nominal value.
    • " + "
    • 1 = estimated; 0 = fixed " + "to nominal value.
    • " "
    • Controls inclusion in the optimization vector.
    • " "
    " ), "initializationPriorType": ( "
      " - "
    • Prior for initial point sampling (e.g., uniform, normal, " + "
    • Prior for initial point sampling (e.g., " + "uniform, normal, " "parameterScaleUniform).
    • " - "
    • Defaults to parameterScaleUniform.
    • " + "
    • Defaults to " + "parameterScaleUniform.
    • " "
    " ), "initializationPriorParameters": ( "
      " - "
    • Semicolon-separated numeric parameters; default lowerBound;upperBound.
    • " - "
    • Linear scale unless using parameter-scale priors.
    • " + "
    • Semicolon-separated numeric parameters; default " + "lowerBound;upperBound.
    • " + "
    • Linear scale unless using parameter-scale " + "priors.
    • " "
    " ), "objectivePriorType": ( "
      " - "
    • Prior contributing to the objective; same options as initialization prior.
    • " + "
    • Prior contributing to the objective; same options as " + "initialization prior.
    • " "
    • Optional; omit for unregularized fits.
    • " "
    " ), "objectivePriorParameters": ( "
      " - "
    • Semicolon-separated numeric parameters; see initialization prior for formats.
    • " + "
    • Semicolon-separated numeric parameters; see " + "initialization prior for formats.
    • " "
    • Scale rules mirror the chosen prior type.
    • " "
    " ), @@ -311,15 +378,18 @@ "table": ( "Condition table
    " "
      " - "
    • Defines simulation/experimental conditions referenced by other tables.
    • " - "
    • User-defined columns must be SBML IDs (parameter/species/compartment).
    • " + "
    • Defines simulation/experimental conditions referenced by " + "other tables.
    • " + "
    • User-defined columns must be SBML IDs " + "(parameter/species/compartment).
    • " "
    • Species values act as initial conditions;
    • " "
    " ), "columns": { "conditionId": ( "
      " - "
    • Unique identifier (letters/digits/underscores; not starting with a digit).
    • " + "
    • Unique identifier (letters/digits/underscores; not " + "starting with a digit).
    • " "
    • Referenced by Measurement and Simulation.
    • " "
    " ), @@ -331,9 +401,12 @@ ), "*": ( "
      " - "
    • User-defined column. Must be an SBML ID: parameter, species, or compartment.
    • " - "
    • Numbers or IDs allowed; species = initial amount/concentration " - "(NaN keeps preeq/initial), compartments = initial size.
    • " + "
    • User-defined column. Must be an SBML ID: parameter, " + "species, or compartment.
    • " + "
    • Numbers or IDs allowed; species = initial " + "amount/concentration " + "(NaN keeps preeq/initial), compartments = " + "initial size.
    • " "
    " ), }, @@ -342,15 +415,19 @@ "table": ( "Visualization table
    " "
      " - "
    • Groups datasets into plots and configures axes/scales.
    • " - "
    • plotId collects series into the same axes.
    • " - "
    • Choose simulation/data types; set labels and offsets.
    • " + "
    • Groups datasets into plots and configures " + "axes/scales.
    • " + "
    • plotId collects series into the same " + "axes.
    • " + "
    • Choose simulation/data types; set labels and " + "offsets.
    • " "
    " ), "columns": { "plotId": ( "
      " - "
    • Plot grouping key; identical IDs share the same axes.
    • " + "
    • Plot grouping key; identical IDs share the same " + "axes.
    • " "
    • Required for multi-series plots.
    • " "
    " ), @@ -362,25 +439,29 @@ ), "plotTypeSimulation": ( "
      " - "
    • LinePlot | BarPlot | ScatterPlot.
    • " + "
    • LinePlot | BarPlot | " + "ScatterPlot.
    • " "
    • Default is LinePlot.
    • " "
    " ), "plotTypeData": ( "
      " - "
    • MeanAndSD | MeanAndSEM | replicate | provided.
    • " + "
    • MeanAndSD | MeanAndSEM | " + "replicate | provided.
    • " "
    • Default is MeanAndSD.
    • " "
    " ), "datasetId": ( "
      " - "
    • Includes datasets (from Measurement) in this plot.
    • " + "
    • Includes datasets (from Measurement) in this " + "plot.
    • " "
    • Optional; multiple IDs → multiple series.
    • " "
    " ), "xValues": ( "
      " - "
    • Independent variable: time (default) or parameter/state ID.
    • " + "
    • Independent variable: time (default) or " + "parameter/state ID.
    • " "
    • Values appear as x-axis ticks.
    • " "
    " ), @@ -392,20 +473,25 @@ ), "xLabel": ( "
      " - "
    • Custom x-axis label; defaults to xValues.
    • " + "
    • Custom x-axis label; defaults to " + "xValues.
    • " "
    • Use units where helpful.
    • " "
    " ), "xScale": ( "
      " - "
    • lin | log | log10 | order (LinePlot only).
    • " - "
    • Default is lin; order places points equidistantly.
    • " + "
    • lin | log | " + "log10 | order (LinePlot " + "only).
    • " + "
    • Default is lin; order " + "places points equidistantly.
    • " "
    " ), "yValues": ( "
      " "
    • Observable ID to plot on the y-axis.
    • " - "
    • Must match measurement.observableId for overlays.
    • " + "
    • Must match measurement.observableId for " + "overlays.
    • " "
    " ), "yOffset": ( @@ -416,13 +502,15 @@ ), "yLabel": ( "
      " - "
    • Custom y-axis label; defaults to yValues.
    • " + "
    • Custom y-axis label; defaults to " + "yValues.
    • " "
    • Include units where applicable.
    • " "
    " ), "yScale": ( "
      " - "
    • lin | log | log10.
    • " + "
    • lin | log | " + "log10.
    • " "
    • Default is lin.
    • " "
    " ), diff --git a/src/petab_gui/settings/settings_model.py b/src/petab_gui/settings/settings_model.py index 4bbca05..f91400c 100644 --- a/src/petab_gui/settings/settings_model.py +++ b/src/petab_gui/settings/settings_model.py @@ -23,7 +23,8 @@ class SettingsModel(QObject): settings_changed : Signal Emitted when a setting is updated (passes the key as string). new_log_message : Signal - Emitted when a log message should be displayed (passes message and color). + Emitted when a log message should be displayed (passes message + and color). """ settings_changed = Signal(str) # Signal emitted when a setting is updated diff --git a/src/petab_gui/utils.py b/src/petab_gui/utils.py index aae0b08..77d2ccb 100644 --- a/src/petab_gui/utils.py +++ b/src/petab_gui/utils.py @@ -258,7 +258,8 @@ def process_file(filepath, logger): petab.C.CONDITION_ID in header or f"\ufeff{petab.C.CONDITION_ID}" in header ): - # For condition files with single column, use tab as default separator + # For condition files with single column, use tab as default + # separator return "condition", separator if separator is not None else "\t" if petab.C.PLOT_ID in header: return "visualization", separator diff --git a/src/petab_gui/views/dialogs.py b/src/petab_gui/views/dialogs.py index 1db3e6e..566b773 100644 --- a/src/petab_gui/views/dialogs.py +++ b/src/petab_gui/views/dialogs.py @@ -97,8 +97,8 @@ def get_inputs(self): Returns: dict: A dictionary containing: - 'simulationConditionId': The simulation condition ID - - 'preequilibrationConditionId': The preequilibration condition ID - (only included if provided) + - 'preequilibrationConditionId': The preequilibration + condition ID (only included if provided) """ inputs = {} inputs["simulationConditionId"] = self.sim_input.text() @@ -203,7 +203,8 @@ def __init__(self, parent=None): # Description desc = QLabel( - "This parameter estimation problem can now be used in the following tools:" + "This parameter estimation problem can now be used in the " + "following tools:" ) desc.setWordWrap(True) main_layout.addWidget(desc) diff --git a/src/petab_gui/views/main_view.py b/src/petab_gui/views/main_view.py index d672ccf..50dd289 100644 --- a/src/petab_gui/views/main_view.py +++ b/src/petab_gui/views/main_view.py @@ -126,7 +126,8 @@ def __init__(self): self.tab_widget.currentChanged.connect(self.set_docks_visible) - # Track if we're in a minimize/restore cycle (must be set before load_ui_settings) + # Track if we're in a minimize/restore cycle (must be set before + # load_ui_settings) self._was_minimized = False settings_manager.load_ui_settings(self) @@ -233,7 +234,7 @@ def add_menu_action(self, dock_widget, name): def save_dock_visibility(self, visible): """Save the visibility status of a QDockWidget when it changes.""" - # Don't save visibility when window is minimized - Qt hides docks automatically + # Don't save visibility when window is minimized - Qt hides docks if self.isMinimized(): return # if current tab is not the data tab return diff --git a/src/petab_gui/views/simple_plot_view.py b/src/petab_gui/views/simple_plot_view.py index bac7e89..0255600 100644 --- a/src/petab_gui/views/simple_plot_view.py +++ b/src/petab_gui/views/simple_plot_view.py @@ -35,7 +35,7 @@ def __init__(self, vis_df, cond_df, meas_df, sim_df, group_by): self.signals = PlotWorkerSignals() def run(self): - # Move all Matplotlib plotting to the GUI thread. Only prepare payload here. + # Move all Matplotlib plotting to the GUI thread. Only prepare payload. sim_df = self.sim_df if not self.sim_df.empty else None payload = { "vis_df": self.vis_df, @@ -300,8 +300,9 @@ def _update_tabs(self, fig: plt.Figure): ) # Map subplot to observable IDs - # When grouped by condition/dataset, one subplot can have multiple observables - # Extract all observable IDs from legend labels + # When grouped by condition/dataset, one subplot can have + # multiple observables. Extract all observable IDs from legend + # labels subplot_title = ( ax.get_title() if ax.get_title() else f"subplot_{idx}" ) @@ -313,7 +314,8 @@ def _update_tabs(self, fig: plt.Figure): label_parts = legend_label.split() if len(label_parts) == 0: continue - # Extract observable ID (last part before "simulation" if present) + # Extract observable ID (last part before "simulation" + # if present) if label_parts[-1] == "simulation": obs_id = ( label_parts[-2] @@ -434,7 +436,8 @@ def __init__(self): self.highlight_scatters = defaultdict( list ) # (subplot index) → scatter artist - self.point_index_map = {} # (subplot index, observableId, x, y) → row index + # (subplot index, observableId, x, y) → row index + self.point_index_map = {} self.click_callback = None def clear_highlight(self): @@ -480,7 +483,8 @@ def _on_pick(self, event): for handle, lbl in zip(handles, labels, strict=False): if handle is artist: # Extract observable ID and data type from legend label - # Format can be: "observableId", "datasetId observableId", or "datasetId observableId simulation" + # Format can be: "observableId", "datasetId observableId", + # or "datasetId observableId simulation" label_parts = lbl.split() if len(label_parts) == 0: continue @@ -495,7 +499,8 @@ def _on_pick(self, event): ) else: data_type = "measurement" - # Label is last: "dataset obs" -> "obs" or just "obs" -> "obs" + # Label is last: "dataset obs" -> "obs" or just + # "obs" -> "obs" label = label_parts[-1] break From 514908f54cea07a9d7f183643bce02effdfe2ebd Mon Sep 17 00:00:00 2001 From: PaulJonasJost Date: Tue, 24 Mar 2026 16:45:40 +0100 Subject: [PATCH 3/3] adopted line length in tests as well --- tests/test_upload.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/test_upload.py b/tests/test_upload.py index 8d9422e..688ff61 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -448,7 +448,7 @@ def tearDown(self): self.view.deleteLater() def test_single_file_yaml_backward_compatibility(self): - """Test that single-file YAML loading still works (backward compatibility).""" + """Test that single-file YAML loading still works.""" with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) @@ -501,6 +501,7 @@ def test_single_file_yaml_backward_compatibility(self): def test_multi_file_yaml_loading(self): """Test loading YAML with multiple files per category.""" + contr = self.controller.measurement_controller with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) @@ -558,21 +559,15 @@ def test_multi_file_yaml_loading(self): # Call the method self.controller.open_yaml_and_load_files(str(yaml_file)) - # Verify measurement files were loaded (once in overwrite, once in append) - self.assertEqual( - self.controller.measurement_controller.open_table.call_count, 2 - ) + # Verify measurement files loading (once in overwrite/append) + self.assertEqual(contr.open_table.call_count, 2) # Check that first call was with mode='overwrite' - first_call = self.controller.measurement_controller.open_table.call_args_list[ - 0 - ] + first_call = contr.open_table.call_args_list[0] self.assertEqual(first_call[1].get("mode"), "overwrite") # Check that second call was with mode='append' - second_call = self.controller.measurement_controller.open_table.call_args_list[ - 1 - ] + second_call = contr.open_table.call_args_list[1] self.assertEqual(second_call[1].get("mode"), "append") # Verify observable files were loaded