From 12e9d16eb5ba006adc882007272bde358ada837e Mon Sep 17 00:00:00 2001 From: Stephen Nneji Date: Mon, 19 Oct 2026 12:16:43 +0100 Subject: [PATCH] Add missing docstrings and update readmes --- CONTRIBUTING.md | 11 ++++-- README.md | 2 +- rascal2/core/worker.py | 1 + rascal2/dialogs/settings_dialog.py | 8 ++++ rascal2/dialogs/startup_dialog.py | 20 +++++++++- rascal2/settings.py | 1 + rascal2/ui/model.py | 7 ++++ rascal2/ui/presenter.py | 1 + rascal2/widgets/inputs.py | 13 ++++++- rascal2/widgets/plot.py | 62 ++++++++++++++++++++---------- rascal2/widgets/project/lists.py | 13 +++++++ rascal2/widgets/utils.py | 14 +++++++ 12 files changed, 125 insertions(+), 28 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c824f819..1cb715f0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,8 +43,13 @@ For information on other coverage report formats, see https://pytest-cov.readthe Documentation ------------- -* The documentation will be hosted on GitHub pages. -* It will be written in reStructuredText format and built via Sphinx. +The documentation is hosted using GitHub pages at [https://rascalsoftware.github.io/RasCAL-2/](https://rascalsoftware.github.io/RasCAL-2/). +The documentation should be built using the provided Sphinx make file. The reStructuredText source is in the **doc** +folder while the build will be placed in the **doc/build** folder. + + cd doc + make clean + make html Style guidelines ---------------- @@ -55,7 +60,7 @@ Style guidelines How to build the executable --------------------------- -This script will create the executable for the software in the **packaging/bundle** folder. +The script below will create the executable for the software in the **packaging/bundle** folder. cd packaging python build_exe.py diff --git a/README.md b/README.md index 8d452168..98a201f9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.23183417.svg)](https://doi.org/10.5281/zenodo.23183417) [![license](https://img.shields.io/github/license/RascalSoftware/RasCAL-2.svg)](https://github.com/RascalSoftware/RasCAL-2/blob/master/LICENSE) [![release](https://img.shields.io/github/release/RascalSoftware/RasCAL-2.svg)](https://github.com/RascalSoftware/RasCAL-2/releases) -[![Unit Tests](https://github.com/RascalSoftware/RasCAL-2/actions/workflows/unit_tests.yml/badge.svg)](https://github.com/RascalSoftware/RasCAL-2/actions/workflows/unit_tests.yml) +[![Unit Tests](https://github.com/RascalSoftware/RasCAL-2/actions/workflows/unit_tests.yaml/badge.svg)](https://github.com/RascalSoftware/RasCAL-2/actions/workflows/unit_tests.yaml) diff --git a/rascal2/core/worker.py b/rascal2/core/worker.py index 23dd61a4..8ba1c761 100644 --- a/rascal2/core/worker.py +++ b/rascal2/core/worker.py @@ -33,6 +33,7 @@ def run(self): self.job_failed.emit(e, self._args) def stop(self): + """Stop the worker thread.""" self.stopped = True self.quit() self.wait() diff --git a/rascal2/dialogs/settings_dialog.py b/rascal2/dialogs/settings_dialog.py index d0be3733..8f98017a 100644 --- a/rascal2/dialogs/settings_dialog.py +++ b/rascal2/dialogs/settings_dialog.py @@ -72,6 +72,7 @@ def reset_default_settings(self) -> None: self.accept() def cancel_settings(self): + """Revert changes and close dialog.""" if SETTINGS.style != self.settings.style: change_ui_style(SETTINGS.style) self.reject() @@ -200,6 +201,13 @@ def open_folder_selector(self) -> None: self.changed = True def set_matlab_rat_dir(self, clear=False): + """Set the path of the MATLAB RAT directory. + + Parameters + ---------- + clear : bool, default: False + Indicates current path of the RAT directory should be cleared. + """ if clear: self.rat_path.setText("") get_global_settings().remove("matlab_rat_path") diff --git a/rascal2/dialogs/startup_dialog.py b/rascal2/dialogs/startup_dialog.py index f7b8338e..e8c322ee 100644 --- a/rascal2/dialogs/startup_dialog.py +++ b/rascal2/dialogs/startup_dialog.py @@ -176,6 +176,13 @@ def reject(self): self.parent().startup_dlg.setVisible(True) def project_start_success(self, warning): + """Update UI after successful project creation succeeds. + + Parameters + ---------- + warning : Warning | None + A warning to display to user. + """ self.parent().presenter.initialise_ui() if not self.parent().toolbar.isEnabled(): @@ -197,6 +204,16 @@ def project_start_success(self, warning): message_box.exec() def project_start_failed(self, exception, args): + """Log and report exception if project creation fails. + + Parameters + ---------- + exception : Exception + The raised exception. + args : Tuple + The arguments passed into function that threw exception. + + """ folder_name = args[0] error = str(exception).strip().replace("\n", "") message = f"The Project ({folder_name}) could not be opened because:\n\n{error}" @@ -251,6 +268,7 @@ def verify_folder(folder_path: str) -> None: raise ValueError("Folder already contains a project.") def verify_name(self) -> None: + """Verify a valid project name is given and show error to user.""" if self.project_name.text() == "": self.project_name_error.show() self.project_name.setProperty("error", True) @@ -399,7 +417,7 @@ def load_project(self, item=None): Parameters ---------- - item : Optional[QtWidgets.QListWidgetItem] + item : QtWidgets.QListWidgetItem | None item if load project was called from list widget. """ if isinstance(item, QtWidgets.QListWidgetItem): diff --git a/rascal2/settings.py b/rascal2/settings.py index 5c9cdb7d..7336804f 100644 --- a/rascal2/settings.py +++ b/rascal2/settings.py @@ -141,6 +141,7 @@ class Settings(BaseModel, validate_assignment=True, arbitrary_types_allowed=True ) def model_post_init(self, __context: Any): + """Update setting properties which are unset with the global setting values.""" global_settings = get_global_settings() unset_settings = [s for s in self.model_fields if s not in self.model_fields_set] for setting in unset_settings: diff --git a/rascal2/ui/model.py b/rascal2/ui/model.py index 2d6a5a78..76338ab5 100644 --- a/rascal2/ui/model.py +++ b/rascal2/ui/model.py @@ -265,6 +265,13 @@ def save_project_as_script(self, save_path): self.project.write_script(script=save_path) def is_project_example(self): + """Check if the project is an example. + + Returns + ------- + bool + Indicates the project is an example. + """ return Path(self.save_path).is_relative_to(EXAMPLES_TEMP_PATH) def load_project(self, load_path: str): diff --git a/rascal2/ui/presenter.py b/rascal2/ui/presenter.py index 3badf669..59c6b339 100644 --- a/rascal2/ui/presenter.py +++ b/rascal2/ui/presenter.py @@ -96,6 +96,7 @@ def initialise_ui(self): self.view.enable_elements() def update_title(self): + """Update the window tile with the project save path.""" suffix = " [Example]" if self.model.is_project_example() else f"[{self.model.save_path}]" self.view.setWindowTitle( self.view.windowTitle().split(" - ")[0] + " - " + self.model.project.name + suffix, diff --git a/rascal2/widgets/inputs.py b/rascal2/widgets/inputs.py index a04c3f2f..1b8b22fb 100644 --- a/rascal2/widgets/inputs.py +++ b/rascal2/widgets/inputs.py @@ -646,8 +646,17 @@ def show_progress(self): self.setEnabled(False) self.setText(f"{self.progress_text} ...") - def update_progress(self, current, total): - self.setText(f"{self.progress_text} - {current} of {total}") + def update_progress(self, completed, total): + """Update the progress text. + + Parameters + ---------- + completed : int + The number of completed steps. + total : int + The total number of steps. + """ + self.setText(f"{self.progress_text} - {completed} of {total}") def hide_progress(self): """Hides busy indicator.""" diff --git a/rascal2/widgets/plot.py b/rascal2/widgets/plot.py index d4cce5a2..85d0433e 100644 --- a/rascal2/widgets/plot.py +++ b/rascal2/widgets/plot.py @@ -63,7 +63,17 @@ def plot_with_blit(self, event: ratapi.events.PlotEventData): self.reflectivity_plot.plot_with_blit(event) def show_bayes_plots(self): - bayes_plots = BayesPlotsDialog(self.parent, self.reflectivity_plot.get_current_plot_settings()) + """Show modal dialog with Bayesian plots.""" + plot_settings = { + "x_axis": self.reflectivity_plot.x_axis.currentText(), + "y_axis": self.reflectivity_plot.y_axis.currentText(), + "show_error_bar": self.reflectivity_plot.show_error_bar.checkState(), + "show_grid": self.reflectivity_plot.show_grid.checkState(), + "show_legend": self.reflectivity_plot.show_legend.checkState(), + "slider": self.reflectivity_plot.slider.value(), + } + + bayes_plots = BayesPlotsDialog(self.parent, plot_settings) bayes_plots.exec() def clear(self): @@ -74,7 +84,7 @@ def clear(self): class BayesPlotsDialog(QtWidgets.QDialog): """The modal dialog for the Bayes plots.""" - def __init__(self, parent, initial_plot_settings: list): + def __init__(self, parent, initial_plot_settings: dict): super().__init__(parent) self.parent = parent self.resize_timer = 0 @@ -497,19 +507,8 @@ def make_figure(self) -> matplotlib.figure.Figure: return figure - def get_current_plot_settings(self): - plot_settings = [ - self.x_axis.currentText(), - self.y_axis.currentText(), - self.show_error_bar.checkState(), - self.show_grid.checkState(), - self.show_legend.checkState(), - self.slider.value(), - ] - - return plot_settings - def handle_control_changed(self): + """Replot when the controls are updated.""" if self.blit_plot is None: self.plot_event() else: @@ -658,13 +657,20 @@ def make_toolbar_widget(self): return self.slider - def set_plot_settings(self, plot_settings: list): - self.x_axis.setCurrentText(plot_settings[0]) - self.y_axis.setCurrentText(plot_settings[1]) - self.show_error_bar.setCheckState(plot_settings[2]) - self.show_grid.setCheckState(plot_settings[3]) - self.show_legend.setCheckState(plot_settings[4]) - self.slider.setValue(plot_settings[5]) + def set_plot_settings(self, plot_settings: dict): + """Change plot settings to values in the given dictionary. + + Parameters + ---------- + plot_settings : dict + A dict of plot settings. + """ + self.x_axis.setCurrentText(plot_settings["x_axis"]) + self.y_axis.setCurrentText(plot_settings["y_axis"]) + self.show_error_bar.setCheckState(plot_settings["show_error_bar"]) + self.show_grid.setCheckState(plot_settings["show_grid"]) + self.show_legend.setCheckState(plot_settings["show_legend"]) + self.slider.setValue(plot_settings["slider"]) def plot(self, project, results: ratapi.outputs.BayesResults): """Plot the shaded plot.""" @@ -761,6 +767,15 @@ def plot(self, _, results): self.draw_plot() def update_ui(self, current, total): + """Update the progress of plotting. + + Parameters + ---------- + current : int + The index of plot being drawn. + total: int + the total number of plots. + """ if current + 1 == total: self.update_label.setText("") else: @@ -771,6 +786,10 @@ def draw_plot(self): raise NotImplementedError def resize_canvas(self): + """Resize the canvas and update figure size. + + This was used as a hack to ensure plots are not squashed. + """ self.canvas.setMinimumSize(900, 600) self.update_figure_size() @@ -808,6 +827,7 @@ def make_control_layout(self): return layout def toggle_plot_button(self): + """Show plot button when parameters are selected.""" self.plot_button.setEnabled(len(self.param_combobox.selected_items()) != 0) def update_ui(self, current, total): diff --git a/rascal2/widgets/project/lists.py b/rascal2/widgets/project/lists.py index 5a3bb3f4..698176a8 100644 --- a/rascal2/widgets/project/lists.py +++ b/rascal2/widgets/project/lists.py @@ -853,6 +853,19 @@ def compose_widget(self, i: int, data_widget: Callable[[str], QtWidgets.QWidget] return widget def create_data_table(self, current_data): + """Create data table widget for an item. + + Parameters + ---------- + current_data : np.array + Data Array + + Returns + ------- + QtWidgets.QTableView + The table view widget for the item. + + """ model = ArrayTableModel(current_data) widget = QtWidgets.QTableView() widget.setMinimumSize(450, 150) diff --git a/rascal2/widgets/utils.py b/rascal2/widgets/utils.py index 1b5c73be..db280d08 100644 --- a/rascal2/widgets/utils.py +++ b/rascal2/widgets/utils.py @@ -68,6 +68,20 @@ def minimumSize(self): return size def do_layout(self, rect, test_only): + """Compute the height of layout and set geometry if needed. + + Parameters + ---------- + rect : QRect + Rect with position and width of layout used to compute height. + test_only : bool + Indicates if geometry should not be set. + + Returns + ------- + int + height for the layout. + """ x = rect.x() y = rect.y() space_x = space_y = self.spacing()