From 76c8f8f7d4d28cc7d8917a3bddec68e572708c82 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Tue, 5 Apr 2022 15:13:29 +0100 Subject: [PATCH] Add more docstrings and typing (#93) * Add docstrings/typing to utils.py * Add docstring to sample data * Add docstrings/typing to thread_worker.py * Add docstrings/typing to detect.py * Add missing None return typing --- cellfinder_napari/detect/detect.py | 9 ++- cellfinder_napari/detect/thread_worker.py | 12 ++-- cellfinder_napari/sample_data.py | 3 + cellfinder_napari/utils.py | 80 ++++++++++++++++------- 4 files changed, 74 insertions(+), 30 deletions(-) diff --git a/cellfinder_napari/detect/detect.py b/cellfinder_napari/detect/detect.py index 314609c7..d558fa45 100644 --- a/cellfinder_napari/detect/detect.py +++ b/cellfinder_napari/detect/detect.py @@ -33,6 +33,9 @@ def detect() -> FunctionGui: + """ + Create a detection plugin GUI. + """ progress_bar = ProgressBar() @magicgui( @@ -79,6 +82,7 @@ def widget( reset_button, ) -> None: """ + Run detection and classification. Parameters ---------- @@ -206,7 +210,10 @@ def update_progress_bar(label: str, max: int, value: int): widget.header.native.setOpenExternalLinks(True) @widget.reset_button.changed.connect - def restore_defaults() -> None: + def restore_defaults(): + """ + Restore default widget values. + """ defaults = { **DataInputs.defaults(), **DetectionInputs.defaults(), diff --git a/cellfinder_napari/detect/thread_worker.py b/cellfinder_napari/detect/thread_worker.py index ee2db319..3a7a15b3 100644 --- a/cellfinder_napari/detect/thread_worker.py +++ b/cellfinder_napari/detect/thread_worker.py @@ -1,3 +1,5 @@ +from typing import Iterable + from cellfinder_core.main import main as cellfinder_run from napari.qt.threading import WorkerBase, WorkerBaseSignals, thread_worker from qtpy.QtCore import QObject, Signal @@ -11,6 +13,10 @@ class MyWorkerSignals(WorkerBaseSignals): + """ + Signals used by the Worker class below. + """ + # Emits (label, max, value) for the progress bar update_progress_bar = Signal(str, int, int) @@ -36,8 +42,6 @@ def __init__( self.classification_inputs = classification_inputs self.misc_inputs = misc_inputs - self.npoints_detected = None - def work(self) -> list: def detect_callback(plane): self.update_progress_bar.emit( @@ -46,10 +50,10 @@ def detect_callback(plane): plane + 1, ) - def detect_finished_callback(points): + def detect_finished_callback(points: list) -> None: self.npoints_detected = len(points) - def classify_callback(batch): + def classify_callback(batch: int) -> None: self.update_progress_bar.emit( "Classifying cells", # Default cellfinder-core batch size is 32. This seems to give diff --git a/cellfinder_napari/sample_data.py b/cellfinder_napari/sample_data.py index 543c5074..054afa6d 100644 --- a/cellfinder_napari/sample_data.py +++ b/cellfinder_napari/sample_data.py @@ -9,6 +9,9 @@ def load_sample() -> List[LayerData]: + """ + Load some sample data. + """ layers = [] for ch, name in zip([0, 1], ["Signal", "Background"]): data = [] diff --git a/cellfinder_napari/utils.py b/cellfinder_napari/utils.py index 1d190ef7..77622f23 100644 --- a/cellfinder_napari/utils.py +++ b/cellfinder_napari/utils.py @@ -1,7 +1,18 @@ +from typing import Callable, List, Optional, Tuple + +import napari +import numpy as np import pandas as pd from imlib.cells.cells import Cell from pkg_resources import resource_filename -from qtpy.QtWidgets import QComboBox, QLabel, QMessageBox, QPushButton +from qtpy.QtWidgets import ( + QComboBox, + QLabel, + QLayout, + QMessageBox, + QPushButton, + QWidget, +) brainglobe_logo = resource_filename( "cellfinder_napari", "images/brainglobe.png" @@ -9,13 +20,16 @@ def html_label_widget(label: str, tag: str = "b") -> dict: + """ + Create a HMTL label for use with magicgui. + """ return dict( widget_type="Label", label=f"<{tag}>{label}", ) -def add_layers(points, viewer) -> None: +def add_layers(points, viewer: napari.Viewer) -> None: """ Adds classified cell candidates as two separate point layers to the napari viewer. """ @@ -44,14 +58,22 @@ def add_layers(points, viewer) -> None: ) -def cells_df_as_np(cells_df, new_order=[2, 1, 0], type_column="type"): +def cells_df_as_np( + cells_df: pd.DataFrame, + new_order: List[int] = [2, 1, 0], + type_column: str = "type", +) -> np.ndarray: + """ + Convert a dataframe to an array, dropping *type_column* and re-ordering + the columns with *new_order*. + """ cells_df = cells_df.drop(columns=[type_column]) cells = cells_df[cells_df.columns[new_order]] cells = cells.to_numpy() return cells -def cells_to_array(cells): +def cells_to_array(cells: List[Cell]) -> Tuple[np.ndarray, np.ndarray]: df = pd.DataFrame([c.to_dict() for c in cells]) points = cells_df_as_np(df[df["type"] == Cell.CELL]) rejected = cells_df_as_np(df[df["type"] == Cell.UNKNOWN]) @@ -59,15 +81,18 @@ def cells_to_array(cells): def add_combobox( - layout, - label, - items, - row, - column=0, - label_stack=False, + layout: QLayout, + label: str, + items: List[str], + row: int, + column: int = 0, + label_stack: bool = False, callback=None, - width=150, -): + width: int = 150, +) -> Tuple[QComboBox, Optional[QLabel]]: + """ + Add a selection box to *layout*. + """ if label_stack: combobox_row = row + 1 combobox_column = column @@ -92,15 +117,18 @@ def add_combobox( def add_button( - label, - layout, - connected_function, - row, - column=0, - visibility=True, - minimum_width=0, - alignment="center", + label: str, + layout: QLayout, + connected_function: Callable, + row: int, + column: int = 0, + visibility: bool = True, + minimum_width: int = 0, + alignment: str = "center", ) -> QPushButton: + """ + Add a button to *layout*. + """ button = QPushButton(label) if alignment == "center": pass @@ -116,7 +144,7 @@ def add_button( return button -def display_info(widget, title, message): +def display_info(widget: QWidget, title: str, message: str) -> None: """ Display a warning in a pop up that informs about overwriting files @@ -124,17 +152,19 @@ def display_info(widget, title, message): QMessageBox.information(widget, title, message, QMessageBox.Ok) -def display_error_box(message): +def display_error_box(message: str) -> None: + """ + Display a pop up window with an error. + """ msg = QMessageBox() msg.setWindowTitle("Error") msg.setText(message) msg.exec() -def display_question(widget, title, message): +def display_question(widget: QWidget, title: str, message: str) -> bool: """ - Display a warning in a pop up that informs - about overwriting files + Display a warning in a pop up that informs about overwriting files. """ message_reply = QMessageBox.question( widget,