diff --git a/displayarray/__main__.py b/displayarray/__main__.py index 4292906..9938d50 100644 --- a/displayarray/__main__.py +++ b/displayarray/__main__.py @@ -1,5 +1,6 @@ """ DisplayArray. + Display NumPy arrays. Usage: @@ -25,6 +26,7 @@ import asyncio def main(argv=None): + """Process command line arguments.""" arguments = docopt(__doc__, argv=argv) if arguments["--version"]: from displayarray import __version__ diff --git a/displayarray/uid.py b/displayarray/_uid.py similarity index 93% rename from displayarray/uid.py rename to displayarray/_uid.py index f64b6df..742503c 100644 --- a/displayarray/uid.py +++ b/displayarray/_uid.py @@ -1,3 +1,5 @@ +"""Generate unique IDs for videos.""" + from collections.abc import Hashable diff --git a/displayarray/util.py b/displayarray/_util.py similarity index 90% rename from displayarray/util.py rename to displayarray/_util.py index cb4eff6..27e37ed 100644 --- a/displayarray/util.py +++ b/displayarray/_util.py @@ -1,3 +1,5 @@ +"""Functions needed to deal with OpenCV.""" + import weakref diff --git a/displayarray/callbacks.py b/displayarray/callbacks.py index de728ca..0e2586c 100644 --- a/displayarray/callbacks.py +++ b/displayarray/callbacks.py @@ -1,3 +1,5 @@ +"""Standard callbacks to use on incoming frames.""" + from displayarray.window import window_commands import numpy as np @@ -35,7 +37,7 @@ class function_display_callback(object): # NOSONAR """ def __init__(self, display_function, finish_function=None): - + """Run display_function on frames.""" self.looping = True self.first_call = True diff --git a/displayarray/effects/__init__.py b/displayarray/effects/__init__.py index e69de29..2885141 100644 --- a/displayarray/effects/__init__.py +++ b/displayarray/effects/__init__.py @@ -0,0 +1 @@ +"""Effects to run on numpy arrays to make data clearer.""" diff --git a/displayarray/effects/crop.py b/displayarray/effects/crop.py index d861075..ae18e32 100644 --- a/displayarray/effects/crop.py +++ b/displayarray/effects/crop.py @@ -1,11 +1,26 @@ +"""Crop any n-dimensional array.""" + import numpy as np -from ..input import mouse_loop +from displayarray.input import mouse_loop class Crop(object): - """A callback class that will return the input array cropped to the output size. N-dimensional.""" + """ + A callback class that will return the input array cropped to the output size. N-dimensional. + + >>> crop_it = Crop((2,2,2)) + >>> arr = np.ones((4,4,4)) + >>> crop_it(arr) + array([[[1., 1.], + [1., 1.]], + + [[1., 1.], + [1., 1.]]]) + + """ def __init__(self, output_size=(64, 64, 3), center=None): + """Create the cropper.""" self._output_size = None self._center = None self.odd_center = None @@ -17,20 +32,24 @@ class Crop(object): @property def output_size(self): + """Get the output size after cropping.""" return self._output_size @output_size.setter def output_size(self, set): + """Set what the output size will be after cropping.""" self._output_size = set if self._output_size is not None: self._output_size = np.asarray(set) @property def center(self): + """Get center crop position on the input.""" return self._center @center.setter def center(self, set): + """Set center crop position on the input.""" self._center = set if self._center is not None: self._center = np.asarray(set) @@ -87,7 +106,7 @@ class Crop(object): return out_array.astype(arr.dtype) def enable_mouse_control(self): - """Move the mouse to move where the crop is from on the original image""" + """Move the mouse to move where the crop is from on the original image.""" @mouse_loop def m_loop(me): diff --git a/displayarray/effects/lens.py b/displayarray/effects/lens.py index 1038c81..5dc8278 100644 --- a/displayarray/effects/lens.py +++ b/displayarray/effects/lens.py @@ -1,9 +1,11 @@ +"""Create lens effects. Currently only 2D+color arrays are supported.""" + import numpy as np -from ..input import mouse_loop +from displayarray.input import mouse_loop import cv2 -class ControllableLens(object): +class _ControllableLens(object): def __init__(self, use_bleed=False, zoom=1, center=None): self.center = center self.zoom = zoom @@ -22,10 +24,66 @@ class ControllableLens(object): ) / 2 -class Barrel(ControllableLens): - def __init__( - self, use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None - ): +class Barrel(_ControllableLens): + """ + Create a barrel distortion. + + >>> distort_it = Barrel(zoom=1, barrel_power=1.5) + >>> x = np.linspace(0, 1, 4) + >>> y = np.linspace(0, 1, 4) + >>> c = np.linspace(0, 1, 2) + >>> arrx, arry, arrc = np.meshgrid(x,y,c) + >>> arrx + array([[[0. , 0. ], + [0.33333333, 0.33333333], + [0.66666667, 0.66666667], + [1. , 1. ]], + + [[0. , 0. ], + [0.33333333, 0.33333333], + [0.66666667, 0.66666667], + [1. , 1. ]], + + [[0. , 0. ], + [0.33333333, 0.33333333], + [0.66666667, 0.66666667], + [1. , 1. ]], + + [[0. , 0. ], + [0.33333333, 0.33333333], + [0.66666667, 0.66666667], + [1. , 1. ]]]) + + >>> distort_it(arrx) + array([[[0.33333333, 0.33333333], + [0.33333333, 0.33333333], + [0.66666667, 0.66666667], + [0.66666667, 0.66666667]], + + [[0.33333333, 0.33333333], + [0.33333333, 0.33333333], + [0.66666667, 0.66666667], + [0.66666667, 0.66666667]], + + [[0.33333333, 0.33333333], + [0.33333333, 0.33333333], + [0.66666667, 0.66666667], + [0.66666667, 0.66666667]], + + [[0.33333333, 0.33333333], + [0.33333333, 0.33333333], + [0.66666667, 0.66666667], + [0.66666667, 0.66666667]]]) + + :param zoom: How far to zoom into the array + :param barrel_power: How much to distort. + 1 = no distortion. >1 increases size of center. 0>> from examples.videos import test_video @@ -179,6 +183,7 @@ async def read_updates_zero_mq( blocking: bool = False, end_callback: Callable[[Any], bool] = lambda x: False, ): + """Read updated frames from ZeroMQ.""" import zmq ctx = zmq.Context() @@ -216,6 +221,7 @@ async def read_updates_ros( poll_rate_hz=None, end_callback: Callable[[Any], bool] = lambda x: False, ): + """Read updated frames from ROS.""" import rospy from rospy.numpy_msg import numpy_msg from rospy.client import _WFM diff --git a/displayarray/frame/get_frame_ids.py b/displayarray/frame/get_frame_ids.py index ac95b18..284aa7f 100644 --- a/displayarray/frame/get_frame_ids.py +++ b/displayarray/frame/get_frame_ids.py @@ -1,3 +1,5 @@ +"""Get camera IDs.""" + import cv2 from typing import List diff --git a/displayarray/frame/np_to_opencv.py b/displayarray/frame/np_to_opencv.py index 3366aed..a9a482c 100644 --- a/displayarray/frame/np_to_opencv.py +++ b/displayarray/frame/np_to_opencv.py @@ -1,3 +1,5 @@ +"""Allow OpenCV to handle numpy arrays as input.""" + import numpy as np import cv2 @@ -6,6 +8,7 @@ class NpCam(object): """Add OpenCV camera controls to a numpy array.""" def __init__(self, img): + """Create a fake camera for OpenCV based on the initial array.""" assert isinstance(img, np.ndarray) self.__img = img self.__is_opened = True diff --git a/displayarray/frame/subscriber_dictionary.py b/displayarray/frame/subscriber_dictionary.py index 174bb3a..1955579 100644 --- a/displayarray/frame/subscriber_dictionary.py +++ b/displayarray/frame/subscriber_dictionary.py @@ -9,6 +9,7 @@ class CamHandler(object): """A camera handler instance that will send commands to and receive data from a camera.""" def __init__(self, name, sub): + """Create the cam handler.""" self.name = name self.cmd = None self.sub: VariableSub = sub @@ -20,6 +21,7 @@ class Cam(object): """A camera publisher instance that will send frames, status, and commands out.""" def __init__(self, name): + """Create the cam.""" self.name = name self.cmd = None self.frame_pub = VariablePub() diff --git a/displayarray/input.py b/displayarray/input.py index c287bec..ea814cb 100644 --- a/displayarray/input.py +++ b/displayarray/input.py @@ -1,3 +1,5 @@ +"""Decorators for creating input loops that OpenCV handles.""" + from displayarray.window import window_commands import threading import time @@ -9,6 +11,7 @@ class MouseEvent(object): """Holds all the OpenCV mouse event information.""" def __init__(self, event, x, y, flags, param): + """Create an OpenCV mouse event.""" self.event = event self.x = x self.y = y @@ -62,10 +65,19 @@ class _mouse_loop_thread(object): # NOSONAR class mouse_loop(object): # NOSONAR - """Run a function on mouse information that is received by the window, continuously in a new thread.""" + """ + Run a function on mouse information that is received by the window, continuously in a new thread. - def __init__(self, f, run_when_no_events=False): - self.t = threading.Thread(target=_mouse_loop_thread(f, run_when_no_events)) + >>> @mouse_loop + ... def fun(mouse_event): + ... print("x:{}, y:{}".format(mouse_event.x, mouse_event.y)) + """ + + def __init__(self, f): + """Start a new mouse thread for the decorated function.""" + self.t = threading.Thread( + target=_mouse_loop_thread(f, run_when_no_events=False) + ) self.t.start() def __call__(self, *args, **kwargs): @@ -111,10 +123,17 @@ class _key_loop_thread(object): # NOSONAR class key_loop(object): # NOSONAR - """Run a function on mouse information that is received by the window, continuously in a new thread.""" + """ + Run a function on mouse information that is received by the window, continuously in a new thread. - def __init__(self, f: Callable[[str], None], run_when_no_events=False): - self.t = threading.Thread(target=_key_loop_thread(f, run_when_no_events)) + >>> @key_loop + ... def fun(key): + ... print("key pressed:{}".format(key)) + """ + + def __init__(self, f: Callable[[str], None]): + """Start a new key thread for the decorated function.""" + self.t = threading.Thread(target=_key_loop_thread(f, run_when_no_events=False)) self.t.start() def __call__(self, *args, **kwargs): diff --git a/displayarray/window/subscriber_windows.py b/displayarray/window/subscriber_windows.py index 97c9b4c..d7b6f7a 100644 --- a/displayarray/window/subscriber_windows.py +++ b/displayarray/window/subscriber_windows.py @@ -1,3 +1,5 @@ +"""OpenCV windows that will display the arrays.""" + import warnings from threading import Thread from typing import List, Union, Callable, Any, Dict, Iterable, Optional @@ -7,13 +9,13 @@ import numpy as np from localpubsub import NoData from displayarray.callbacks import global_cv_display_callback -from displayarray.uid import uid_for_source +from displayarray._uid import uid_for_source from displayarray.frame import subscriber_dictionary from displayarray.frame.frame_updater import FrameCallable from displayarray.frame.frame_updater import FrameUpdater from displayarray.input import MouseEvent from displayarray.window import window_commands -from displayarray.util import WeakMethod +from displayarray._util import WeakMethod from displayarray.effects.select_channels import SelectChannels @@ -29,6 +31,7 @@ class SubscriberWindows(object): video_sources: Iterable[Union[str, int]] = (0,), callbacks: Optional[List[Callable[[np.ndarray], Any]]] = None, ): + """Create the array displaying window.""" self.source_names: List[Union[str, int]] = [] self.close_threads: Optional[List[Thread]] = [] self.frames: List[np.ndarray] = [] @@ -52,6 +55,7 @@ class SubscriberWindows(object): return not self.exited def block(self): + """Update the window continuously while blocking the outer program.""" self.loop() for ct in self.close_threads: ct.join() diff --git a/displayarray/window/window_commands.py b/displayarray/window/window_commands.py index f2015d1..f30a59c 100644 --- a/displayarray/window/window_commands.py +++ b/displayarray/window/window_commands.py @@ -1,3 +1,5 @@ +"""Commands to control the array displaying windows.""" + from localpubsub import VariablePub, VariableSub key_pub = VariablePub() diff --git a/docs/.doctrees/environment.pickle b/docs/.doctrees/environment.pickle index 98a1747..69f0ee3 100644 Binary files a/docs/.doctrees/environment.pickle and b/docs/.doctrees/environment.pickle differ diff --git a/tox.ini b/tox.ini index 2eb68c2..bf25cbd 100644 --- a/tox.ini +++ b/tox.ini @@ -39,6 +39,9 @@ whitelist_externals = poetry description = enforce code style commands = poetry run black displayarray --check +[pydocstyle] +ignore = D105, D212, D203, D202 + [testenv:pydocstyle] whitelist_externals = poetry description = enforce documentation style