diff --git a/doc/source/beta.rst b/doc/source/beta.rst new file mode 100644 index 00000000..f9ea157a --- /dev/null +++ b/doc/source/beta.rst @@ -0,0 +1,10 @@ +############### +Class reference +############### + + +.. toctree:: + :titlesonly: + :maxdepth: 1 + + modules diff --git a/doc/source/conf.py b/doc/source/conf.py index 04943ba1..b552ac77 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -86,7 +86,6 @@ def setup(app): # The full version, including alpha/beta/rc tags release = '' - # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. @@ -122,7 +121,7 @@ def setup(app): # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. diff --git a/doc/source/workspace.rst b/doc/source/workspace.rst index 19333222..5d3e5d95 100644 --- a/doc/source/workspace.rst +++ b/doc/source/workspace.rst @@ -89,7 +89,7 @@ Common Operations ***************** Load PDB/SDF/MMCIF as a Nanome Complex -================================ +====================================== .. code-block:: python from nanome.api.structure import Complex @@ -103,7 +103,7 @@ Load PDB/SDF/MMCIF as a Nanome Complex Export Nanome Complex as PDB/SDF/MMCIF -================================ +====================================== .. code-block:: python from nanome.api.structure import Complex diff --git a/nanome/api/plugin_instance.py b/nanome/api/plugin_instance.py index 93c68b7e..649a355c 100644 --- a/nanome/api/plugin_instance.py +++ b/nanome/api/plugin_instance.py @@ -132,7 +132,7 @@ def request_workspace(self, callback=None): def request_complex_list(self, callback=None): """ - | Request the list of all complexes in the workspace, in shallow mode + | Request the list of all complexes in the workspace, in shallow mode. :param callback: Callback to receive list of complexes. :type callback: Callable[[List[Complex]], None] diff --git a/nanome/beta/redis_interface.py b/nanome/beta/redis_interface.py index 59271543..af05ef10 100644 --- a/nanome/beta/redis_interface.py +++ b/nanome/beta/redis_interface.py @@ -4,17 +4,27 @@ import redis import time import uuid - +from typing import List, Union from nanome import PluginInstance from nanome._internal.enums import Messages from nanome._internal.network import Packet from nanome.api.serializers import CommandMessageSerializer from nanome.api.streams import Stream -from nanome.util import Logs, enums +from nanome.api import shapes, structure, ui, user +from nanome.util import Logs, enums, Vector3, Quaternion +from nanome.util.file import LoadInfoDone __all__ = ['PluginInstanceRedisInterface'] +workspace_struct = Union[ + structure.Complex, structure.Molecule, structure.Chain, + structure.Residue, structure.Atom, structure.Bond] +ui_content = Union[ + ui.Button, ui.Label, ui.Mesh, ui.Image, + ui.Dropdown, ui.DropdownItem, ui.LoadingBar, + ui.Slider, ui.TextInput +] NTS_RESPONSE_TIMEOUT = os.environ.get('NTS_RESPONSE_TIMEOUT', 30) @@ -41,9 +51,6 @@ def __init__(self, redis_host, redis_port, redis_password, redis_channel=None): self.session_id = None self.version_table = None - def set_channel(self, value): - self.channel = value - def connect(self): """Ping Redis, and then get data from plugin required for serialization.""" self.redis.ping() @@ -52,8 +59,8 @@ def connect(self): self.session_id = plugin_data['session_id'] self.version_table = plugin_data['version_table'] - def create_writing_stream(self, indices_list, stream_type): - """Return a stream wrapped in the RedisStreamInterface""" + def create_writing_stream(self, indices_list: List[int], stream_type: enums.StreamType) -> Stream: + """Create a stream allowing the plugin to continuously update properties of many objects.""" message_type = Messages.stream_create expects_response = True args = (stream_type, indices_list, enums.StreamDirection.writing) @@ -64,34 +71,48 @@ def create_writing_stream(self, indices_list, stream_type): stream = Stream(None, *stream_args) return stream - def request_workspace(self): + def request_workspace(self) -> structure.Workspace: + """Request the entire workspace, in deep mode""" message_type = Messages.workspace_request expects_response = True args = None response = self._send_message(message_type, args, expects_response) return response - def request_complexes(self, id_list): + def request_complexes(self, id_list: List[int]) -> List[structure.Complex]: + """Requests a list of complexes by their indices. + + Complexes returned contains the full structure (atom/bond/residue/chain/molecule) + """ message_type = Messages.complexes_request expects_response = True args = id_list response = self._send_message(message_type, args, expects_response) return response - def update_structures_shallow(self, structures): + def update_structures_shallow(self, structures: List[workspace_struct]): + """Update the specific molecular structures in the scene to match the structures in parameter. + + Only updates the structure's data, will not update children or other descendents. + """ message_type = Messages.structures_shallow_update expects_response = False args = structures self._send_message(message_type, args, expects_response) - def update_structures_deep(self, struct_list): + def update_structures_deep(self, struct_list: List[workspace_struct]): + """Update the specific molecular structures in the scene to match the structures in parameter. + + Will also update descendent structures and can be used to remove descendent structures. + """ message_type = Messages.structures_deep_update expects_response = True args = struct_list response = self._send_message(message_type, args, expects_response) return response - def request_complex_list(self): + def request_complex_list(self) -> List[structure.Complex]: + """Request the list of all complexes in the workspace, in shallow mode.""" message_type = Messages.complex_list_request args = None expects_response = True @@ -107,38 +128,47 @@ def _send_message(self, message_type: Messages, fn_args, expects_response): response = self._deserialize_payload(serialized_response) return response - def zoom_on_structures(self, structures): + def zoom_on_structures(self, structures: workspace_struct) -> None: + """Repositions and resizes the workspace such that the provided structure(s) will be in the + + center of the users view. + """ message_type = Messages.structures_zoom expects_response = False args = structures self._send_message(message_type, args, expects_response) - async def send_notification(self, notification_type: enums.NotificationTypes, message): + async def send_notification(self, notification_type: enums.NotificationTypes, message: str): + """Send a notification to the user""" message_type = Messages.notification_send expects_response = False args = [notification_type, message] self._send_message(message_type, args, expects_response) - def center_on_structures(self, structures): + def center_on_structures(self, structures: workspace_struct): + """Repositions the workspace such that the provided structure(s) will be in the center of the world.""" message_type = Messages.structures_center expects_response = False args = structures self._send_message(message_type, args, expects_response) - def add_to_workspace(self, complex_list): + def add_to_workspace(self, complex_list: List[structure.Complex]): + """Add a list of complexes to the current workspace.""" message_type = Messages.add_to_workspace expects_response = True args = complex_list response = self._send_message(message_type, args, expects_response) return response - def open_url(self, url, desktop_browser=False): + def open_url(self, url: str, desktop_browser: bool = False) -> None: + """Open a URL in the user's browser.""" message_type = Messages.open_url expects_response = False args = (url, desktop_browser) self._send_message(message_type, args, expects_response) - def request_presenter_info(self): + def request_presenter_info(self) -> user.PresenterInfo: + """Get info about the presenter.""" message_type = Messages.presenter_info_request expects_response = True args = None @@ -146,13 +176,20 @@ def request_presenter_info(self): return result def request_controller_transforms(self): + """Requests presenter controller info. + + (head position, head rotation, left controller position, + left controller rotation, right controller position, + right controller rotation) + """ message_type = Messages.controller_transforms_request expects_response = True args = None result = self._send_message(message_type, args, expects_response) return result - def apply_color_scheme(self, color_scheme, target, only_carbons=False): + def apply_color_scheme(self, color_scheme: enums.ColorScheme, target: enums.ColorSchemeTarget, only_carbons: bool = False): + """Applies a color scheme to selected atoms.""" message_type = Messages.apply_color_scheme expects_response = False args = (color_scheme, target, only_carbons) @@ -186,7 +223,7 @@ def _rpc_request(self, message, expects_response=False): message_data = pickle.loads(pickled_message_data) return message_data - def upload_shapes(self, shape_list): + def upload_shapes(self, shape_list: List[shapes.Shape]): """Upload a list of shapes to the server. :arg: shape_list: List of shapes to upload. @@ -200,7 +237,8 @@ def upload_shapes(self, shape_list): shape._index = shape_index return shape_list - def get_plugin_data(self): + def get_plugin_data(self) -> dict: + """Custom function to get data necessary for serialization from the remote plugin.""" function_name = 'get_plugin_data' expects_response = True message = self._build_message(function_name, None, None, expects_response) @@ -216,32 +254,38 @@ def _build_packet(self, message_type, args=None, expects_response=False): packet.write(message) return request_id, packet - def update_content(self, *content): + def update_content(self, *content: ui_content) -> None: + """Update specific UI elements (button, slider, list...) + """ message_type = Messages.content_update expects_response = False args = list(content) self._send_message(message_type, args, expects_response) - def update_node(self, *nodes): + def update_node(self, *nodes: List[ui.LayoutNode]) -> None: + """Updates layout nodes and their children.""" message_type = Messages.node_update expects_response = False args = nodes self._send_message(message_type, args, expects_response) - def set_menu_transform(self, index, position, rotation, scale): + def set_menu_transform(self, index: int, position: Vector3, rotation: Quaternion, scale: Vector3): + """Update the position, scale, and rotation of the menu.""" message_type = Messages.menu_transform_set expects_response = False args = [index, position, rotation, scale] self._send_message(message_type, args, expects_response) - def request_menu_transform(self, index): + def request_menu_transform(self, index: int): + """Requests spatial information of the plugin menu (position, rotation, scale).""" message_type = Messages.menu_transform_request expects_response = True args = [index] response = self._send_message(message_type, args, expects_response) return response - def update_stream(self, stream, data): + def update_stream(self, stream: Stream, data: List): + """Feed new data into an existing stream.""" message_type = Messages.stream_feed expects_response = True @@ -252,13 +296,15 @@ def update_stream(self, stream, data): response = self._send_message(message_type, args, expects_response) return response - def destroy_stream(self, stream): + def destroy_stream(self, stream: Stream): + """Delete an existing stream.""" message_type = Messages.stream_destroy expects_response = False args = stream.id self._send_message(message_type, args, expects_response) - def send_files_to_load(self, files_list): + def send_files_to_load(self, files_list: List[str]) -> LoadInfoDone: + """Send a molecular structure file (PDB, SDF, etc.) to the workspace to render.""" message_type = Messages.load_file expects_response = True files_and_data = [] diff --git a/nanome/util/logs.py b/nanome/util/logs.py index 97ad03c8..21453def 100644 --- a/nanome/util/logs.py +++ b/nanome/util/logs.py @@ -19,12 +19,13 @@ class LogType(IntEnum): @classmethod def error(cls, *args, **kwargs): """ - | Prints an error + | Prints an error. :param args: Variable length argument list :type args: Anything printable :param kwargs: Keyword arguments to pass to python logging module. + For options, see https://github.com/python/cpython/blob/main/Lib/logging/__init__.py#L1604 """ module = cls.caller_name() @@ -36,12 +37,13 @@ def error(cls, *args, **kwargs): @classmethod def warning(cls, *args, **kwargs): """ - | Prints a warning + | Prints a warning. :param args: Variable length argument list :type args: Anything printable :param kwargs: Keyword arguments to pass to python logging module. + For options, see https://github.com/python/cpython/blob/main/Lib/logging/__init__.py#L1604 """ module = cls.caller_name() @@ -52,12 +54,13 @@ def warning(cls, *args, **kwargs): @classmethod def message(cls, *args, **kwargs): """ - | Prints a message + | Prints a message. :param args: Variable length argument list :type args: Anything printable :param kwargs: Keyword arguments to pass to python logging module. + For options, see https://github.com/python/cpython/blob/main/Lib/logging/__init__.py#L1604 """ module = cls.caller_name() @@ -68,13 +71,14 @@ def message(cls, *args, **kwargs): @classmethod def debug(cls, *args, **kwargs): """ - | Prints a debug message + | Prints a debug message. | Prints only if plugin started in verbose mode (with -v argument) :param args: Variable length argument list :type args: Anything printable :param kwargs: Keyword arguments to pass to python logging module. + For options, see https://github.com/python/cpython/blob/main/Lib/logging/__init__.py#L1604 """ module = cls.caller_name()