syd 0.2.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
syd/parameters.py CHANGED
@@ -3,14 +3,13 @@ from dataclasses import dataclass, field
3
3
  from abc import ABC, abstractmethod
4
4
  from enum import Enum
5
5
  from copy import deepcopy
6
- from warnings import warn
7
6
 
8
7
  from .support import (
9
8
  NoInitialValue,
10
9
  ParameterMeta,
11
10
  ParameterUpdateError,
12
- ParameterUpdateWarning,
13
11
  get_parameter_attributes,
12
+ warn_parameter_update,
14
13
  )
15
14
 
16
15
  T = TypeVar("T")
@@ -433,12 +432,10 @@ class SelectionParameter(Parameter[Any]):
433
432
 
434
433
  # If value is not found after all checks, reset to first option
435
434
  if not value_found:
436
- warn(
437
- ParameterUpdateWarning(
438
- self.name,
439
- type(self).__name__,
440
- f"Value {self.value} not in options, setting to first option ({self.options[0]})",
441
- )
435
+ warn_parameter_update(
436
+ self.name,
437
+ type(self).__name__,
438
+ f"Value {self.value} not in options, setting to first option ({self.options[0]})",
442
439
  )
443
440
  self.value = self.options[0]
444
441
 
@@ -558,22 +555,18 @@ class MultipleSelectionParameter(Parameter[List[Any]]):
558
555
  def _validate_update(self) -> None:
559
556
  self.options = self._validate_options(self.options)
560
557
  if not isinstance(self.value, (list, tuple)):
561
- warn(
562
- ParameterUpdateWarning(
563
- self.name,
564
- type(self).__name__,
565
- f"For parameter {self.name}, value {self.value} is not a list or tuple. Setting to empty list.",
566
- )
558
+ warn_parameter_update(
559
+ self.name,
560
+ type(self).__name__,
561
+ f"For parameter {self.name}, value {self.value} is not a list or tuple. Setting to empty list.",
567
562
  )
568
563
  self.value = []
569
564
  if not all(val in self.options for val in self.value):
570
565
  invalid = [val for val in self.value if val not in self.options]
571
- warn(
572
- ParameterUpdateWarning(
573
- self.name,
574
- type(self).__name__,
575
- f"For parameter {self.name}, value {self.value} contains invalid selections: {invalid}. Setting to empty list.",
576
- )
566
+ warn_parameter_update(
567
+ self.name,
568
+ type(self).__name__,
569
+ f"For parameter {self.name}, value {self.value} contains invalid selections: {invalid}. Setting to empty list.",
577
570
  )
578
571
  self.value = []
579
572
  # Keep only unique values while preserving order based on self.options
@@ -652,21 +645,17 @@ class IntegerParameter(Parameter[int]):
652
645
 
653
646
  if compare_to_range:
654
647
  if new_value < self.min:
655
- warn(
656
- ParameterUpdateWarning(
657
- self.name,
658
- type(self).__name__,
659
- f"Value {new_value} below minimum {self.min}, clamping",
660
- )
648
+ warn_parameter_update(
649
+ self.name,
650
+ type(self).__name__,
651
+ f"Value {new_value} below minimum {self.min}, clamping",
661
652
  )
662
653
  new_value = self.min
663
654
  if new_value > self.max:
664
- warn(
665
- ParameterUpdateWarning(
666
- self.name,
667
- type(self).__name__,
668
- f"Value {new_value} above maximum {self.max}, clamping",
669
- )
655
+ warn_parameter_update(
656
+ self.name,
657
+ type(self).__name__,
658
+ f"Value {new_value} above maximum {self.max}, clamping",
670
659
  )
671
660
  new_value = self.max
672
661
  return int(new_value)
@@ -688,12 +677,10 @@ class IntegerParameter(Parameter[int]):
688
677
  "IntegerParameter must have both min and max bounds",
689
678
  )
690
679
  if self.min > self.max:
691
- warn(
692
- ParameterUpdateWarning(
693
- self.name,
694
- type(self).__name__,
695
- f"Min value greater than max value, swapping",
696
- )
680
+ warn_parameter_update(
681
+ self.name,
682
+ type(self).__name__,
683
+ f"Min value greater than max value, swapping",
697
684
  )
698
685
  self.min, self.max = self.max, self.min
699
686
  self.value = self._validate(self.value)
@@ -788,21 +775,17 @@ class FloatParameter(Parameter[float]):
788
775
 
789
776
  if compare_to_range:
790
777
  if new_value < self.min:
791
- warn(
792
- ParameterUpdateWarning(
793
- self.name,
794
- type(self).__name__,
795
- f"Value {new_value} below minimum {self.min}, clamping",
796
- )
778
+ warn_parameter_update(
779
+ self.name,
780
+ type(self).__name__,
781
+ f"Value {new_value} below minimum {self.min}, clamping",
797
782
  )
798
783
  new_value = self.min
799
784
  if new_value > self.max:
800
- warn(
801
- ParameterUpdateWarning(
802
- self.name,
803
- type(self).__name__,
804
- f"Value {new_value} above maximum {self.max}, clamping",
805
- )
785
+ warn_parameter_update(
786
+ self.name,
787
+ type(self).__name__,
788
+ f"Value {new_value} above maximum {self.max}, clamping",
806
789
  )
807
790
  new_value = self.max
808
791
 
@@ -825,12 +808,10 @@ class FloatParameter(Parameter[float]):
825
808
  "FloatParameter must have both min and max bounds",
826
809
  )
827
810
  if self.min > self.max:
828
- warn(
829
- ParameterUpdateWarning(
830
- self.name,
831
- type(self).__name__,
832
- f"Min value greater than max value, swapping",
833
- )
811
+ warn_parameter_update(
812
+ self.name,
813
+ type(self).__name__,
814
+ f"Min value greater than max value, swapping",
834
815
  )
835
816
  self.min, self.max = self.max, self.min
836
817
  self.value = self._validate(self.value)
@@ -932,31 +913,25 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
932
913
  high = self._validate_single(new_value[1])
933
914
 
934
915
  if low > high:
935
- warn(
936
- ParameterUpdateWarning(
937
- self.name,
938
- type(self).__name__,
939
- f"Low value {low} greater than high value {high}, swapping",
940
- )
916
+ warn_parameter_update(
917
+ self.name,
918
+ type(self).__name__,
919
+ f"Low value {low} greater than high value {high}, swapping",
941
920
  )
942
921
  low, high = high, low
943
922
 
944
923
  if low < self.min:
945
- warn(
946
- ParameterUpdateWarning(
947
- self.name,
948
- type(self).__name__,
949
- f"Low value {low} below minimum {self.min}, clamping",
950
- )
924
+ warn_parameter_update(
925
+ self.name,
926
+ type(self).__name__,
927
+ f"Low value {low} below minimum {self.min}, clamping",
951
928
  )
952
929
  low = self.min
953
930
  if high > self.max:
954
- warn(
955
- ParameterUpdateWarning(
956
- self.name,
957
- type(self).__name__,
958
- f"High value {high} above maximum {self.max}, clamping",
959
- )
931
+ warn_parameter_update(
932
+ self.name,
933
+ type(self).__name__,
934
+ f"High value {high} above maximum {self.max}, clamping",
960
935
  )
961
936
  high = self.max
962
937
 
@@ -979,12 +954,10 @@ class IntegerRangeParameter(Parameter[Tuple[int, int]]):
979
954
  "IntegerRangeParameter must have both min and max bounds",
980
955
  )
981
956
  if self.min > self.max:
982
- warn(
983
- ParameterUpdateWarning(
984
- self.name,
985
- type(self).__name__,
986
- f"Min value greater than max value, swapping",
987
- )
957
+ warn_parameter_update(
958
+ self.name,
959
+ type(self).__name__,
960
+ f"Min value greater than max value, swapping",
988
961
  )
989
962
  self.min, self.max = self.max, self.min
990
963
  self.value = self._validate(self.value)
@@ -1102,31 +1075,25 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
1102
1075
  high = self._validate_single(new_value[1])
1103
1076
 
1104
1077
  if low > high:
1105
- warn(
1106
- ParameterUpdateWarning(
1107
- self.name,
1108
- type(self).__name__,
1109
- f"Low value {low} greater than high value {high}, swapping",
1110
- )
1078
+ warn_parameter_update(
1079
+ self.name,
1080
+ type(self).__name__,
1081
+ f"Low value {low} greater than high value {high}, swapping",
1111
1082
  )
1112
1083
  low, high = high, low
1113
1084
 
1114
1085
  if low < self.min:
1115
- warn(
1116
- ParameterUpdateWarning(
1117
- self.name,
1118
- type(self).__name__,
1119
- f"Low value {low} below minimum {self.min}, clamping",
1120
- )
1086
+ warn_parameter_update(
1087
+ self.name,
1088
+ type(self).__name__,
1089
+ f"Low value {low} below minimum {self.min}, clamping",
1121
1090
  )
1122
1091
  low = self.min
1123
1092
  if high > self.max:
1124
- warn(
1125
- ParameterUpdateWarning(
1126
- self.name,
1127
- type(self).__name__,
1128
- f"High value {high} above maximum {self.max}, clamping",
1129
- )
1093
+ warn_parameter_update(
1094
+ self.name,
1095
+ type(self).__name__,
1096
+ f"High value {high} above maximum {self.max}, clamping",
1130
1097
  )
1131
1098
  high = self.max
1132
1099
 
@@ -1149,12 +1116,10 @@ class FloatRangeParameter(Parameter[Tuple[float, float]]):
1149
1116
  "FloatRangeParameter must have both min and max bounds",
1150
1117
  )
1151
1118
  if self.min > self.max:
1152
- warn(
1153
- ParameterUpdateWarning(
1154
- self.name,
1155
- type(self).__name__,
1156
- f"Min value greater than max value, swapping",
1157
- )
1119
+ warn_parameter_update(
1120
+ self.name,
1121
+ type(self).__name__,
1122
+ f"Min value greater than max value, swapping",
1158
1123
  )
1159
1124
  self.min, self.max = self.max, self.min
1160
1125
  self.value = self._validate(self.value)
syd/support.py CHANGED
@@ -1,5 +1,17 @@
1
1
  from abc import ABCMeta
2
2
  from typing import Any, List
3
+ from warnings import warn
4
+ from contextlib import contextmanager
5
+ import matplotlib.pyplot as plt
6
+
7
+
8
+ @contextmanager
9
+ def plot_context():
10
+ plt.ioff()
11
+ try:
12
+ yield
13
+ finally:
14
+ plt.ion()
3
15
 
4
16
 
5
17
  class NoUpdate:
@@ -20,6 +32,9 @@ class NoUpdate:
20
32
  and other._noupdate_identifier == self._noupdate_identifier
21
33
  )
22
34
 
35
+ def __repr__(self):
36
+ return "NotUpdated"
37
+
23
38
 
24
39
  class NoInitialValue:
25
40
  """Singleton class to represent a non-initial value in parameter operations."""
@@ -39,6 +54,9 @@ class NoInitialValue:
39
54
  and other._noinitialvalue_identifier == self._noinitialvalue_identifier
40
55
  )
41
56
 
57
+ def __repr__(self):
58
+ return "NotInitialized"
59
+
42
60
 
43
61
  # Keep original Parameter class and exceptions unchanged
44
62
  class ParameterAddError(Exception):
@@ -110,6 +128,15 @@ class ParameterUpdateWarning(Warning):
110
128
  )
111
129
 
112
130
 
131
+ def warn_parameter_update(
132
+ parameter_name: str, parameter_type: str, message: str = None
133
+ ):
134
+ """
135
+ Warn the user that a parameter has been updated to a value behind the scenes.
136
+ """
137
+ warn(ParameterUpdateWarning(parameter_name, parameter_type, message))
138
+
139
+
113
140
  def get_parameter_attributes(param_class) -> List[str]:
114
141
  """
115
142
  Get all valid attributes for a parameter class.
syd/viewer.py CHANGED
@@ -7,7 +7,6 @@ from matplotlib.figure import Figure
7
7
  from .parameters import ParameterType, ActionType, Parameter
8
8
  from .support import NoUpdate, NoInitialValue, ParameterAddError, ParameterUpdateError
9
9
 
10
-
11
10
  # Create the singleton instances
12
11
  NO_UPDATE = NoUpdate()
13
12
  NO_INITIAL_VALUE = NoInitialValue()
@@ -217,24 +216,29 @@ class Viewer:
217
216
  """Deploy the app in a notebook or standalone environment"""
218
217
  env = env.lower()
219
218
  if env == "notebook":
220
- from .notebook_deployment import NotebookDeployer
219
+ # On demand import because the deployers need to import the viewer
220
+ from .notebook_deployment.deployer import NotebookDeployer
221
221
 
222
222
  deployer = NotebookDeployer(self, **kwargs)
223
223
  deployer.deploy()
224
224
  return self
225
225
 
226
226
  elif env == "browser" or env == "flask":
227
- from .flask_deployment import deploy_flask
227
+ # On demand import because the deployers need to import the viewer
228
+ from .flask_deployment.deployer import FlaskDeployer
228
229
 
229
- # Ensure port is None by default if not specified
230
230
  if "port" not in kwargs:
231
231
  kwargs["port"] = None
232
+ if "continuous" in kwargs:
233
+ kwargs.pop("continuous")
232
234
 
233
- deployer = deploy_flask(self, **kwargs)
235
+ deployer = FlaskDeployer(self, **kwargs)
236
+ deployer.deploy()
234
237
  return self
238
+
235
239
  else:
236
240
  raise ValueError(
237
- f"Unsupported environment: {env}, only 'notebook', 'plotly', 'plotly-inline', and 'flask' are supported right now."
241
+ f"Unsupported environment: {env}, only 'notebook', 'flask'/'browser' are supported right now."
238
242
  )
239
243
 
240
244
  @contextmanager
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: syd
3
+ Version: 1.0.0
4
+ Summary: A Python package for making GUIs for data science easy.
5
+ Project-URL: Homepage, https://github.com/landoskape/syd
6
+ Author-email: Andrew Landau <andrew+tyler+landau+getridofthisanddtheplusses@gmail.com>
7
+ License-Expression: GPL-3.0-or-later
8
+ License-File: LICENSE
9
+ Keywords: data-science,gui,interactive,jupyter,machine-learning,notebook,python
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Requires-Python: >=3.9
21
+ Requires-Dist: flask
22
+ Requires-Dist: ipykernel
23
+ Requires-Dist: ipympl
24
+ Requires-Dist: ipywidgets
25
+ Requires-Dist: matplotlib
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
28
+ Requires-Dist: pytest>=7.0.0; extra == 'test'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Syd
32
+
33
+ [![PyPI version](https://badge.fury.io/py/syd.svg)](https://badge.fury.io/py/syd)
34
+ [![Tests](https://github.com/landoskape/syd/actions/workflows/tests.yml/badge.svg)](https://github.com/landoskape/syd/actions/workflows/tests.yml)
35
+ [![Documentation Status](https://readthedocs.org/projects/shareyourdata/badge/?version=stable)](https://shareyourdata.readthedocs.io/en/stable/?badge=stable)
36
+ [![codecov](https://codecov.io/gh/landoskape/syd/branch/main/graph/badge.svg)](https://codecov.io/gh/landoskape/syd)
37
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
38
+
39
+
40
+ A package to help you share your data!
41
+
42
+ Have you ever wanted to look through all your data really quickly interactively? Of course you have. Mo data mo problems, but only if you don't know what to do with it. And that's why Syd stands for show your data!
43
+
44
+ Syd is a system for creating a data viewing GUI that you can view in a jupyter notebook or in a web browser. And guess what? Since it can open in a web browser, you can even open it on any other computer on your local network! For example, your PI's computer. Gone are the days of single random examples that they make infinitely stubborn conclusions about. Now, you can look at all the examples, quickly and easily, on their computer. And that's why Syd stands for share your data!
45
+
46
+ Okay, so what is it? Syd is an automated system to convert some basic python plotting code into an interactive GUI. This means you only have to think about _**what**_ you want to plot and _**which**_ parameters you want to be interactive. Syd handles all the behind-the-scenes action required to make an interface. And do you know what that means? It means you get to spend your time _thinking_ about your data, rather than writing code to look at it. And that's why Syd stands for Science, Yes! Dayummmm!
47
+
48
+ ## Installation
49
+ It's easy, just use pip install. The dependencies are light so it should work in most environments.
50
+ ```bash
51
+ pip install syd
52
+ ```
53
+
54
+ ## Documentation
55
+ The full documentation is available at [shareyourdata.readthedocs.io](https://shareyourdata.readthedocs.io/). It includes a quick start guide, a comprehensive tutorial, and an API reference for the different elements of Syd. If you have any questions or want to suggest improvements to the docs, please let us know on the [github issues page](https://github.com/landoskape/syd/issues)!
56
+
57
+ ## Quick Start
58
+ This is an example of a sine wave viewer which is about as simple as it gets. You can choose which env to use - if you use ``env="notebook"`` then the GUI will deploy as the output of a jupyter cell (this only works in jupyter!). If you use ``env="browser"`` then the GUI will open a page in your default web browser and you can interact with the data there (works in jupyter notebooks and also from python scripts!).
59
+ ```python
60
+ import matplotlib.pyplot as plt
61
+ import numpy as np
62
+ from syd import make_viewer
63
+ def plot(viewer, state):
64
+ fig, ax = plt.subplots()
65
+ x = np.linspace(0, 10, 1000)
66
+ y = state['amplitude'] * np.sin(state['frequency'] * x)
67
+ ax.plot(x, y)
68
+ return fig
69
+
70
+ viewer = make_viewer()
71
+ viewer.set_plot(plot)
72
+ viewer.add_float('amplitude', value=1.0, min=0, max=2)
73
+ viewer.add_float('frequency', value=1.0, min=0.1, max=5)
74
+
75
+ # env = "browser" # for viewing in a web browser
76
+ env = "notebook" # for viewing within a jupyter notebook
77
+ viewer.deploy(env=env)
78
+ ```
79
+
80
+ ### More Examples
81
+ We have several examples of more complex viewers with detailed explanations in the comments. Here are the links and descriptions to each of them:
82
+
83
+ | Example | Description | Try It! |
84
+ |---------|-------------|---------------|
85
+ | [Basic Tutorial](examples/1-simple_example.ipynb) | A good starting point with detailed explanations of how to use the core elements of Syd. | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/landoskape/syd/blob/main/examples/1-simple_example.ipynb) |
86
+ | [Comprehensive](examples/2a-complex_example.ipynb) | Showcases just about everything you can do with Syd. | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/landoskape/syd/blob/main/examples/2a-complex_example.ipynb) |
87
+ | [Making a Viewer Class](examples/2b-subclass_example.ipynb) | Rewrites the comprehensive example as a class, which is useful when you have complex data processing or callbacks. | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/landoskape/syd/blob/main/examples/2b-subclass_example.ipynb) |
88
+ | [Data Loading](examples/3-data_loading.ipynb) | Showcases different ways to get your data into a Syd viewer. | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/landoskape/syd/blob/main/examples/3-data_loading.ipynb) |
89
+ | [Hierarchical Callbacks](examples/4-hierarchical_callbacks.ipynb) | Demonstrates how to handle complicated callback situations. | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/landoskape/syd/blob/main/examples/4-hierarchical_callbacks.ipynb) |
90
+
91
+
92
+ ### Data loading
93
+ Thinking about how to get data into a Syd viewer can be non-intuitive. For some examples that showcase different ways to get your data into a Syd viewer, check out the [data loading example](examples/3-data_loading.ipynb). Or, if you just want a quick example, check this out:
94
+ ```python
95
+ import numpy as np
96
+ from matplotlib import pyplot as plt
97
+ from syd import make_viewer
98
+
99
+ # Suppose you computed some data somewhere (in a script or in a jupyter notebook)
100
+ data = np.random.randn(100, 1000)
101
+
102
+ # When you write a plot function like this, it'll be able to access the data variable
103
+ def plot(state):
104
+ fig = plt.figure()
105
+ ax = fig.add_subplot(111)
106
+
107
+ # plot indexes to the data that you created outside the plot function
108
+ ax.imshow(data[state["index"]])
109
+ return fig
110
+
111
+ # Since plot "knows" about the data variable, all you need to do is pass the plot
112
+ # function to the syd viewer and it'll be able to access the data once deployed!
113
+ viewer = make_viewer(plot)
114
+ viewer.deploy(env="browser")
115
+ ```
116
+
117
+ ### Handling Hierarchical Callbacks
118
+ Syd dramatically reduces the amount of work you need to do to build a GUI for viewing your data. However, it can still be a bit complicated to think about callbacks. Below is a quick demonstration, to try it yourself, check out the full example [here](examples/4-hierarchical_callbacks.ipynb).
119
+
120
+ For example, suppose your dataset is composed of electrophysiology recordings from 3 mice, where each mouse has a different number of sesssions, and each session has a different number of neurons. You want to build a viewer to choose the mouse, then choose the session, and then view a particular neuron from within that session. But the viewer will break if you try to index to session 5 for mouse 2 but mouse 2 only has 4 sessions!
121
+
122
+ This is where hierarchical callbacks come in. There's a straightforward pattern to handling this kind of situation that you can follow. You can write a callback for each **level** of the hierarchy. Then, each callback can call the next callback in the hierarchy. It looks like this:
123
+ ```python
124
+ import numpy as np
125
+ from syd import Viewer # Much easier to build a Viewer class for hierarchical callbacks
126
+
127
+ class MouseViewer(Viewer):
128
+ def __init__(self, mice_names):
129
+ self.mice_names = mice_names
130
+
131
+ self.add_selection("mouse", options=list(mice_names))
132
+
133
+ # We don't know how many sessions or neurons to pick from yet!
134
+ self.add_integer("session", min=0, max=1)
135
+ self.add_integer("neuron", min=0, max=1)
136
+
137
+ # Any time the mouse changes, update the sessions to pick from
138
+ self.on_change("mouse", self.update_mouse)
139
+
140
+ # Any time the session changes, update the neurons to pick from
141
+ self.on_change("session", self.update_session)
142
+
143
+ # Since we built callbacks for setting the range of the session
144
+ # and neuron parameters, we can use them here!
145
+ # To get the state, we can use self.state, which is the current
146
+ # state of the viewer (in the init function, it'll just be the
147
+ # default value for each parameter you've added already).
148
+ self.update_mouse(self.state)
149
+
150
+ def update_mouse(self, state):
151
+ # Pseudo code for getting the number of sessions for a given mouse
152
+ num_sessions = get_num_sessions(state["mouse"])
153
+
154
+ # Now we update the number of sessions to pick from
155
+ self.update_integer("session", max=num_sessions - 1)
156
+
157
+ # Now we need to update the neurons to choose from ....
158
+ # But! Updating the session parameter might trigger a change to the
159
+ # session value. So, instead of using the state dictionary that was
160
+ # passed into the function, we can get the ~NEW~ state dictionary like this:
161
+ new_state = self.state
162
+
163
+ # Then perform the session update callback!
164
+ self.update_session(new_state)
165
+
166
+ def update_session(self, state):
167
+ # Pseudo code for getting the number of neurons for a given mouse and session
168
+ num_neurons = get_num_neurons(state["mouse"], state["session"])
169
+
170
+ # Now we update the number of neurons to pick from
171
+ self.update_integer("neuron", max=num_neurons - 1)
172
+
173
+ def plot(self, state):
174
+ # Pseudo code for plotting the data
175
+ data = get_data(state["mouse"], state["session"], state["neuron"])
176
+ fig = plot_the_data(data)
177
+ return fig
178
+
179
+ # Now we can create a viewer and deploy it
180
+ viewer = MouseViewer(["Mouse 1", "Mouse 2", "Mouse 3"])
181
+ viewer.deploy(env="browser")
182
+ ```
183
+
184
+ ## License
185
+
186
+ This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
187
+
188
+ ## Contributing
189
+
190
+ Contributions are welcome! Here's how you can help:
191
+
192
+ 1. Fork the repository
193
+ 2. Create a new branch (`git checkout -b feature/amazing-feature`)
194
+ 3. Make your changes
195
+ 4. Run the tests (`pytest`)
196
+ 5. Commit your changes (`git commit -m 'Add amazing feature'`)
197
+ 6. Push to the branch (`git push origin feature/amazing-feature`)
198
+ 7. Open a Pull Request online
199
+
200
+ Please make sure to update tests as appropriate and adhere to the existing coding style (black, line-length=88, other style guidelines not capture by black, generally following pep8 guidelines).
201
+
202
+
203
+ ## To-Do List
204
+ - Layout controls
205
+ - [ ] Improve the display and make it look better
206
+ - [ ] Add a "save" button that saves the current state of the viewer to a json file
207
+ - [ ] Add a "load" button that loads the viewer state from a json file
208
+ - [ ] Add a "freeze" button that allows the user to update state variables without updating the plot until unfreezing
209
+ - [ ] Add a window for capturing any error messages that might be thrown by the plot function. Maybe we could have a little interface for looking at each one (up to a point) and the user could press a button to throw an error for the traceback.
210
+ - [ ] Consider "app_deployed" context for each deployer...
211
+ - [ ] Consider adding a step to the integer parameters...
212
+ - Idea for figure management:
213
+ - [ ] We could make fig=?, ax=? arguments optional for the plot function and add a
214
+ "recycle_figure: bool = False" flag be part of the deploy API. This way, an
215
+ advanced user that wants snappy responsivity or complex figure management can
216
+ do so, but the default is for the user to generate a new figure object each time.
217
+ - Export options:
218
+ - [ ] Export lite: export the viewer as a HTML/Java package that contains an incomplete set of renderings of figures -- using a certain set of parameters.
219
+ - [ ] Export full: export the viewer in a way that contains the data to give full functionality.
@@ -0,0 +1,19 @@
1
+ syd/__init__.py,sha256=LSf7L9CZ7WVB6qb88vX6YgnCffcQLsDdhIggNMEGd10,250
2
+ syd/parameters.py,sha256=dlnYOVsi1CDtC2toVECf0kNBRipVrtUjr6XVX86b5MA,42886
3
+ syd/support.py,sha256=7wztPMaL750opyBDnaYYRVyBR5QUJtVspDzQWpu50rk,6106
4
+ syd/viewer.py,sha256=UYDfH9TNds0CiC3vThINLrMNZE8ikSFqJHgjM8yMD94,49323
5
+ syd/flask_deployment/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
+ syd/flask_deployment/deployer.py,sha256=w-zuXX1PCnWRa_VCevbQ7yqMTNH5x3Dri-sQQXOF1sM,25110
7
+ syd/flask_deployment/testing_principles.md,sha256=GyULM97sDeie8h3tSPoduOckdMNGyWuwm1RdHo5jzK0,10130
8
+ syd/flask_deployment/static/__init__.py,sha256=ieWE8NKR-APw7h4Ge0ooZGk6wZrneSSs_1cMyTPbQSA,65
9
+ syd/flask_deployment/static/css/styles.css,sha256=gA-Urdq9wmd-XpVcxILxPGlkiOtKtujG7Mw7dWftIVM,5210
10
+ syd/flask_deployment/static/js/viewer.js,sha256=kSY24VGjlQLe-jtPOEU1nE7U0ALC_ZaVzGIBuEXZsTs,27441
11
+ syd/flask_deployment/templates/__init__.py,sha256=ieWE8NKR-APw7h4Ge0ooZGk6wZrneSSs_1cMyTPbQSA,65
12
+ syd/flask_deployment/templates/index.html,sha256=fr1g9IOwNttULhQCIcw_fo0sNpmdgznSGfPStQllR_E,1594
13
+ syd/notebook_deployment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ syd/notebook_deployment/deployer.py,sha256=LcKq0Jy_znrVFF7S0uBJrQUWf1hB-NKmDYiq1OG0Jr8,8930
15
+ syd/notebook_deployment/widgets.py,sha256=UbkasRf8wY9beUwpwJYjv9X0Lus3DvgAEIORHwaC-zA,20058
16
+ syd-1.0.0.dist-info/METADATA,sha256=xEpq6sJo2eE6uMZDCwgLwS1yMQd1ZRoz--SWDE4t0F0,13043
17
+ syd-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ syd-1.0.0.dist-info/licenses/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
19
+ syd-1.0.0.dist-info/RECORD,,