diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b0d304f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,35 @@ +# http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +charset = utf-8 + +# Docstrings and comments use max_line_length = 79 +[*.py] +max_line_length = 119 + +[*.rst] +indent_size = 3 + +# Use 2 spaces for the HTML files +[*.html] +indent_size = 2 + +# The JSON files contain newlines inconsistently +[*.json] +indent_size = 2 +insert_final_newline = false + +# Makefiles always use tabs for indentation +[Makefile] +indent_style = tab + +# Batch files use tabs for indentation +[*.bat] +indent_style = tab diff --git a/AUTHORS b/AUTHORS index 5d4d499..4d270ab 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,5 +1,5 @@ Authors ------- -Aquiles Carattino +Aquiles Carattino Sanli Faez diff --git a/docs/_static/example_config.yml b/docs/_static/example_config.yml new file mode 100644 index 0000000..82c4720 --- /dev/null +++ b/docs/_static/example_config.yml @@ -0,0 +1,72 @@ +%YAML 1.2 +--- +# Default parameters for the Tracking program +# All parameters can be changed to accommodate user needs. +# All parameters can be changed at runtime with the appropriate config window +user: + name: Aquiles + +saving: + auto_save: False + auto_save_waterfall: True + directory: C:\Users\carat002\Data + filename_video: Video # Can be the same filename for video and photo + filename_photo: Snap + filename_tracks: Tracks + filename_waterfall: Waterfall + filename_trajectory: Trajectory + filename_log: Log + max_memory: 200 # In megabytes + +GUI: + length_waterfall: 20 # Total length of the Waterfall (lines) + refresh_time: 50 # Refresh rate of the GUI (in ms) + +camera: + model: dummy_camera # Should be a python file in model/cameras + init: 0 # Initial arguments to pass when creating the camera + #extra_args: [extra, arguments] # Extra arguments that can be passed when constructing the model + model_camera: Orca Flash # To keep a registry of which camera was used in the experiment + exposure_time: 30ms # Initial exposure time (in ms) + fps: 30 # Frames per second, should either be defined by the camera or within the model based on timing + binning_x: 1 # Binning + binning_y: 1 + roi_x1: 0 + roi_x2: 599 + roi_y1: 0 + roi_y2: 399 + background: '' # Full path to background file, or empty for none. + background_method: [Method1, Method2] + +waterfall: # Parameters for calculating the waterfall plot + length: 20 # The total length of the waterfall (lines) + vertical_bin: 10 # Total number of lines of the CCD to integrate + +movie: + buffer_length: 1000 # Frames + +tracking: + locate: + diameter: 11 # Diameter of the particles (in pixels) to track, has to be an odd number + invert: False + minmass: 100 + link: + memory: 3 + search_range: 4 + filter: # Filter spurious trajectories + min_length: 25 + process: + compute_drift: False + um_pixel: 0.01 # Microns per pixel (calibration of the microscope) + min_traj_length: 2 + min_mass: 0.05 + max_size: 50.0 + max_ecc: 1 + fps: 30 + param_1: 0. + param_2: 0 + +debug: + logging_level: Nothing # One of Nothing, Debug, Info, Warning, Error + queue_memory: False + to_screen: True \ No newline at end of file diff --git a/docs/_static/screenshot_01.png b/docs/_static/screenshot_01.png new file mode 100644 index 0000000..6c10b90 Binary files /dev/null and b/docs/_static/screenshot_01.png differ diff --git a/docs/_static/screenshot_free_run.png b/docs/_static/screenshot_free_run.png new file mode 100644 index 0000000..b8d4fe0 Binary files /dev/null and b/docs/_static/screenshot_free_run.png differ diff --git a/docs/_static/screenshot_particles.png b/docs/_static/screenshot_particles.png new file mode 100644 index 0000000..9e71378 Binary files /dev/null and b/docs/_static/screenshot_particles.png differ diff --git a/docs/_static/screenshot_save_image.png b/docs/_static/screenshot_save_image.png new file mode 100644 index 0000000..577c980 Binary files /dev/null and b/docs/_static/screenshot_save_image.png differ diff --git a/docs/_static/screenshot_snap.png b/docs/_static/screenshot_snap.png new file mode 100644 index 0000000..42f4207 Binary files /dev/null and b/docs/_static/screenshot_snap.png differ diff --git a/docs/_static/screenshot_toolbar.png b/docs/_static/screenshot_toolbar.png new file mode 100644 index 0000000..1d1a4dd Binary files /dev/null and b/docs/_static/screenshot_toolbar.png differ diff --git a/docs/_static/screenshot_tracking.png b/docs/_static/screenshot_tracking.png new file mode 100644 index 0000000..1d1a4dd Binary files /dev/null and b/docs/_static/screenshot_tracking.png differ diff --git a/docs/conf.py b/docs/conf.py index b281205..884e2b5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -177,4 +177,6 @@ # -- Options for todo extension ---------------------------------------------- # If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True \ No newline at end of file +todo_include_todos = True + +autodoc_mock_imports = ["pydaqmx", "lantz"] \ No newline at end of file diff --git a/docs/developers/controller/devices/hamamatsu.rst b/docs/developers/controller/devices/hamamatsu.rst new file mode 100644 index 0000000..b1163e9 --- /dev/null +++ b/docs/developers/controller/devices/hamamatsu.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.controller.devices.hamamatsu.hamamatsu_camera + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/controller/devices/index.rst b/docs/developers/controller/devices/index.rst new file mode 100644 index 0000000..3ef7e8e --- /dev/null +++ b/docs/developers/controller/devices/index.rst @@ -0,0 +1,13 @@ +.. automodule:: pynta.controller.devices + :members: + :undoc-members: + :show-inheritance: + + +.. toctree:: + :maxdepth: 2 + :caption: List of available devices + + hamamatsu + keysight + photonicscience diff --git a/docs/developers/controller/devices/keysight.rst b/docs/developers/controller/devices/keysight.rst new file mode 100644 index 0000000..6ec197a --- /dev/null +++ b/docs/developers/controller/devices/keysight.rst @@ -0,0 +1,7 @@ +Keysight +======== + +.. automodule:: pynta.controller.devices.keysight.infiniivision + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/controller/devices/photonicscience.rst b/docs/developers/controller/devices/photonicscience.rst new file mode 100644 index 0000000..6c4c1cc --- /dev/null +++ b/docs/developers/controller/devices/photonicscience.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.controller.devices.photonicscience.scmoscam + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/controller/index.rst b/docs/developers/controller/index.rst new file mode 100644 index 0000000..02d2430 --- /dev/null +++ b/docs/developers/controller/index.rst @@ -0,0 +1,13 @@ +Pynta Controllers +================= + +.. automodule:: pynta.controller + :members: + :undoc-members: + :show-inheritance: + +.. toctree:: + :maxdepth: 1 + :caption: Contents + + devices/index diff --git a/docs/developers/index.rst b/docs/developers/index.rst index c4620c8..28bf2af 100644 --- a/docs/developers/index.rst +++ b/docs/developers/index.rst @@ -16,19 +16,23 @@ Below you will find an introduction to the code architecture. Read through to un Controller ---------- -In our definition of the MVC pattern, :mod:`controllers ` are the drivers of the devices. Controllers for cameras, for example, rely on library files (.dll files on Windows, .so in Linux) that can be more or less documented. For example :mod:`~pynta.controller.devices.hamamatsu` uses the *DCAM-API*, while :mod:`~pynta.controller.devices.photonicscience` uses *scmoscam.dll*. Controllers should follow the specifications of each device as closely as possible. For instance, the units each devices uses for setting exposure times or framerates, etc. Moreover, they shouldn't implement anything that the device is not capable of doing. A clear example would be to set a region of interest. Some cameras support it but some don't. You could be tempted to apply a ROI in the software directly, but you shouldn't do it in the Controller, there is a better place as we will see later. +In our definition of the MVC pattern, :mod:`~pynta.controller` are the drivers of the devices. Controllers for cameras, for example, rely on library files (.dll files on Windows, .so in Linux) that can be more or less documented. For example :mod:`~pynta.controller.devices.hamamatsu` uses the *DCAM-API*, while :mod:`~pynta.controller.devices.photonicscience` uses *scmoscam.dll*. Controllers should follow the specifications of each device as closely as possible. For instance, the units each devices uses for setting exposure times or framerates, etc. Moreover, they shouldn't implement anything that the device is not capable of doing. A clear example would be to set a region of interest. Some cameras support it but some don't. You could be tempted to apply a ROI in the software directly, but you shouldn't do it in the Controller, there is a better place as we will see later. Controllers are not always plain python files, sometimes are entire packages on themselves. For instance, the controller for Basler cameras (PyPylons) or National Instruments cards (pyDAQmx) are packages on their own. Sometimes the controllers where developed by other people, like the case of the Hamamatsu code, which was borrowed from Zhuang's lab. If you explore the controllers folder, you will notice that there are some available, like the :mod:`~pynta.controller.devices.keysight` ,that holds the drivers for an oscilloscope and function generator. Those controllers are remnants of a different incarnation of PyNTA, but that may still be valuable for the future. Model ----- +In the MVC pattern that we have defined earlier, :mod:`~pynta.model` is the place to define all the logic on how to use the devices is located. The core principle of splitting Controllers and Models is to separate what the device can do by design and what the user imposes to the devices. For example, imagine a camera which can acquire frames one by one. The controller would provide a way to snap a single-frame. However, a user may want to acquire a movie by developing a for-loop that acquires a series of frames. The controller should reflect the capacities of the device, while the model will include all the user-imposed logic. + +A clear advantage of having models is that in the case where controllers are not part of the package, for example if the driver is provided by the manufacturer itself, a model will make explicit how the driver is used. For example, NI-cards are very complex devices, and if one wants to monitor a signal, there are several required steps, which can be written down as a method of a class and re-used as much as wanted. Models can make use of explicit units (through Pint), for example. + +Models are not limited to devices, but they also make explicit how an experiment is performed. In PyNTA, there are available at least two different experimental models, one for performing nanoparticle tracking analysis in 2D images (the classical NTA) and one to perform tracking analysis in hollow optical fibers, i.e., 1-D nanoparticle tracking. In these experiment models, you can find all the steps and how they relate to each other. For example, you can't subtract background if you haven't acquired the background first, etc. Models for the Cameras ^^^^^^^^^^^^^^^^^^^^^^ +Imagine you have a camera that doesn't support setting a region of interest (ROI), you can still crop the resulting images in the software, giving the same result as what a camera that does support ROI does. By having an intermediate layer between the controllers and the users, it is possible to detach the specific logic of an experiment and the specifications of the devices. Moreover, it makes very straightforward to add new devices to the experiment, exchanging cameras, etc. -Models define the way the users will interact with the devices. Imagine you have a camera that doesn't support setting a region of interest (ROI), you can still crop the resulting images in the software, giving the same result as what a camera that does support ROI does. By having an intermediate layer between the controllers and the users, it is possible to detach the specific logic of an experiment and the specifications of the devices. Moreover, it makes very straightforward to add new devices to the experiment, exchanging cameras, etc. - -Therefore in :doc:`pynta.model.cameras` is where we will develop classes that have always the same methods and outputs defined, but that behave completely different when communicating with the devices. The starting point is the :mod:`skeleton `, where the ``cameraBase`` class is defined. In this class all the methods and variables needed by the rest of the program are defined. This strategy not only allows to keep track of the functions, it also enables the subclassing, which will be discussed later. +Therefore in :mod:`~pynta.model.cameras` is where we will develop classes that have always the same methods and outputs defined, but that behave completely different when communicating with the devices. The starting point is the :mod:`skeleton `, where the ``cameraBase`` class is defined. In this class all the methods and variables needed by the rest of the program are defined. This strategy not only allows to keep track of the functions, it also enables the subclassing, which will be discussed later. Having models also allow to quickly change from one camera to another. For example, if one desires to switch from a :mod:`Hamamatsu ` to a :mod:`PSI `, the only needed thing to do is to replace:: @@ -52,14 +56,13 @@ The idea behind the experiment model is that it makes it very clear what the log View ---- -The View is, as the name suggests, where the GUI is defined. Since we defined all the logic of the experiment in a separate class, the View takes care of only triggering specific steps of the experiment and displaying the results. In principle, the only logic present in the view modules is only for aesthetic reasons. For instance, disable a specific button while a measurement is going on, etc. However, it should be the experiment model the one that avoids triggering two measurements if it is not possible, etc. That guarantees a lower-level safety. +In the MVC patter, :mod:`~pynta.view` is, as the name suggests, where the GUI is defined. Since we defined all the logic of the experiment in a separate class, the View takes care of only triggering specific steps of the experiment and displaying the results. In principle, the only logic present in the view modules is only for aesthetic reasons. For instance, disable a specific button while a measurement is going on, etc. However, it should be the experiment model the one that avoids triggering two measurements if it is not possible, etc. That guarantees a lower-level safety. -:doc:`pynta.view` is where the GUI lives. Within the module, you will also find :doc:`pynta.view.GUI`, in which the different widgets that make up the window are defined and another folder called designer, where the Qt Designer files are located. Adapting the looks of a program should start by looking into the ``.ui`` files, then checking the associated widgets in the ``GUI`` module, in order to connect the proper signals, etc. and finally modifying the main view class. +:mod:`~pynta.view` is where the GUI lives. Within the module, you will also find :mod:`pynta.view.GUI`, in which the different widgets that make up the window are defined and another folder called designer, where the Qt Designer files are located. Adapting the looks of a program should start by looking into the ``.ui`` files, then checking the associated widgets in the ``GUI`` module, in order to connect the proper signals, etc. and finally modifying the main view class. .. toctree:: - :maxdepth: 4 - :caption: Contents: + :maxdepth: 1 + :caption: PyNTA API pynta - modules diff --git a/docs/developers/model/cameras/base_camera.rst b/docs/developers/model/cameras/base_camera.rst new file mode 100644 index 0000000..5fbd2e3 --- /dev/null +++ b/docs/developers/model/cameras/base_camera.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.model.cameras.base_camera + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/model/cameras/basler.rst b/docs/developers/model/cameras/basler.rst new file mode 100644 index 0000000..4c0dc98 --- /dev/null +++ b/docs/developers/model/cameras/basler.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.model.cameras.basler + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/model/cameras/dummy_camera.rst b/docs/developers/model/cameras/dummy_camera.rst new file mode 100644 index 0000000..ecd1a76 --- /dev/null +++ b/docs/developers/model/cameras/dummy_camera.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.model.cameras.dummy_camera + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/model/cameras/hamamatsu.rst b/docs/developers/model/cameras/hamamatsu.rst new file mode 100644 index 0000000..82516d6 --- /dev/null +++ b/docs/developers/model/cameras/hamamatsu.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.model.cameras.hamamatsu + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/model/cameras/index.rst b/docs/developers/model/cameras/index.rst new file mode 100644 index 0000000..ccb0f3c --- /dev/null +++ b/docs/developers/model/cameras/index.rst @@ -0,0 +1,16 @@ +Camera Models +============= +.. automodule:: pynta.model.cameras + :members: + :undoc-members: + :show-inheritance: + +.. toctree:: + :maxdepth: 1 + + base_camera + simulate_brownian + dummy_camera + basler + hamamatsu + psi diff --git a/docs/developers/model/cameras/psi.rst b/docs/developers/model/cameras/psi.rst new file mode 100644 index 0000000..b5d8f53 --- /dev/null +++ b/docs/developers/model/cameras/psi.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.model.cameras.psi + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/model/cameras/simulate_brownian.rst b/docs/developers/model/cameras/simulate_brownian.rst new file mode 100644 index 0000000..4947afb --- /dev/null +++ b/docs/developers/model/cameras/simulate_brownian.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.model.cameras.simulate_brownian + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/pynta.model.daqs.rst b/docs/developers/model/daqs/index.rst similarity index 58% rename from docs/developers/pynta.model.daqs.rst rename to docs/developers/model/daqs/index.rst index 05f8e30..5910714 100644 --- a/docs/developers/pynta.model.daqs.rst +++ b/docs/developers/model/daqs/index.rst @@ -1,38 +1,25 @@ -pynta.model.daqs package -======================== - -Submodules ----------- - -pynta.model.daqs.NI module --------------------------- - -.. automodule:: pynta.model.daqs.NI +Data Acquisition Cards Models +============================= +.. automodule:: pynta.model.daqs :members: :undoc-members: :show-inheritance: -pynta.model.daqs.daq\_dummy module ----------------------------------- +Available DAQS +--------------- -.. automodule:: pynta.model.daqs.daq_dummy +.. automodule:: pynta.model.daqs.NI :members: :undoc-members: :show-inheritance: -pynta.model.daqs.skeleton module --------------------------------- - -.. automodule:: pynta.model.daqs.skeleton +.. automodule:: pynta.model.daqs.daq_dummy :members: :undoc-members: :show-inheritance: -Module contents ---------------- - -.. automodule:: pynta.model.daqs +.. automodule:: pynta.model.daqs.skeleton :members: :undoc-members: :show-inheritance: diff --git a/docs/developers/model/experiments/fiber_tracking.rst b/docs/developers/model/experiments/fiber_tracking.rst new file mode 100644 index 0000000..21652a3 --- /dev/null +++ b/docs/developers/model/experiments/fiber_tracking.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.model.experiment.dispertech.fiber_tracking + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/pynta.model.experiment.rst b/docs/developers/model/experiments/index.rst similarity index 50% rename from docs/developers/pynta.model.experiment.rst rename to docs/developers/model/experiments/index.rst index d84045a..5240748 100644 --- a/docs/developers/pynta.model.experiment.rst +++ b/docs/developers/model/experiments/index.rst @@ -1,45 +1,38 @@ -pynta.model.experiment package -============================== - -Subpackages ------------ +Model for Experiments +===================== .. toctree:: + :maxdepth: 1 + :caption: Available Experiments - pynta.model.experiment.nano_cet - -Submodules ----------- + fiber_tracking + np_tracking -pynta.model.experiment.base\_experiment module ----------------------------------------------- +.. automodule:: pynta.model.experiment + :members: + :undoc-members: + :show-inheritance: .. automodule:: pynta.model.experiment.base_experiment :members: :undoc-members: :show-inheritance: -pynta.model.experiment.publisher module ---------------------------------------- - .. automodule:: pynta.model.experiment.publisher :members: :undoc-members: :show-inheritance: -pynta.model.experiment.subscriber module ----------------------------------------- - .. automodule:: pynta.model.experiment.subscriber :members: :undoc-members: :show-inheritance: - -Module contents ---------------- - -.. automodule:: pynta.model.experiment +.. automodule:: pynta.model.experiment.config :members: :undoc-members: :show-inheritance: + + + + diff --git a/docs/developers/model/experiments/np_tracking.rst b/docs/developers/model/experiments/np_tracking.rst new file mode 100644 index 0000000..fb381ff --- /dev/null +++ b/docs/developers/model/experiments/np_tracking.rst @@ -0,0 +1,4 @@ +.. automodule:: pynta.model.experiment.nanoparticle_tracking.np_tracking + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/developers/pynta.model.rst b/docs/developers/model/index.rst similarity index 64% rename from docs/developers/pynta.model.rst rename to docs/developers/model/index.rst index 2dfd91e..b895d78 100644 --- a/docs/developers/pynta.model.rst +++ b/docs/developers/model/index.rst @@ -1,23 +1,22 @@ -pynta.model package -=================== +PyNTA Models +============ -Models are the place to develop the logic of how the devices are used and how the experiment is performed. You will find models for cameras and daqs. Cameras are heavily used in PyNTA, while DAQs where inherited from a previous incarnation and were left behind as a reference and to speed up future developments in which not only a camera has to be controlled but an external trigger or a different signal is required. +Models are the place to develop the logic of how the devices are used and how the experiment is performed. You will find models for cameras and daqs. Cameras are heavily used in PyNTA, while DAQs where inherited from a previous incarnation and were left here as a reference and to speed up future developments in which not only a camera has to be controlled. The model for the experiment is the easiest place where the developer can have a sense of what is going on under-the-hood. You can check, for example, :mod:`~pynta.model.experiment.nanoparticle_tracking.np_tracking.NPTracking` in order to see the steps that make a tracking measurement. Remember that the program runs with different threads and processes in parallel, and therefore the behavior may not be trivial to understand. -Subpackages ------------ +.. automodule:: pynta.model + :members: + :undoc-members: + :show-inheritance: + .. toctree:: + :maxdepth: 1 + :caption: Available Models - pynta.model.cameras - pynta.model.daqs - pynta.model.experiment + cameras/index + daqs/index + experiments/index -Module contents ---------------- -.. automodule:: pynta.model - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/modules.rst b/docs/developers/modules.rst deleted file mode 100644 index 2138552..0000000 --- a/docs/developers/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -pynta -===== - -.. toctree:: - :maxdepth: 4 - - pynta diff --git a/docs/developers/pynta.controller.data_sources.rst b/docs/developers/pynta.controller.data_sources.rst deleted file mode 100644 index 66b5f57..0000000 --- a/docs/developers/pynta.controller.data_sources.rst +++ /dev/null @@ -1,10 +0,0 @@ -pynta.controller.data\_sources package -====================================== - -Module contents ---------------- - -.. automodule:: pynta.controller.data_sources - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.controller.devices.hamamatsu.rst b/docs/developers/pynta.controller.devices.hamamatsu.rst deleted file mode 100644 index 5b56010..0000000 --- a/docs/developers/pynta.controller.devices.hamamatsu.rst +++ /dev/null @@ -1,22 +0,0 @@ -pynta.controller.devices.hamamatsu package -========================================== - -Submodules ----------- - -pynta.controller.devices.hamamatsu.hamamatsu\_camera module ------------------------------------------------------------ - -.. automodule:: pynta.controller.devices.hamamatsu.hamamatsu_camera - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.controller.devices.hamamatsu - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.controller.devices.keysight.rst b/docs/developers/pynta.controller.devices.keysight.rst deleted file mode 100644 index e68486f..0000000 --- a/docs/developers/pynta.controller.devices.keysight.rst +++ /dev/null @@ -1,22 +0,0 @@ -pynta.controller.devices.keysight package -========================================= - -Submodules ----------- - -pynta.controller.devices.keysight.infiniivision module ------------------------------------------------------- - -.. automodule:: pynta.controller.devices.keysight.infiniivision - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.controller.devices.keysight - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.controller.devices.photonicscience.rst b/docs/developers/pynta.controller.devices.photonicscience.rst deleted file mode 100644 index 0aca3b7..0000000 --- a/docs/developers/pynta.controller.devices.photonicscience.rst +++ /dev/null @@ -1,22 +0,0 @@ -pynta.controller.devices.photonicscience package -================================================ - -Submodules ----------- - -pynta.controller.devices.photonicscience.scmoscam module --------------------------------------------------------- - -.. automodule:: pynta.controller.devices.photonicscience.scmoscam - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.controller.devices.photonicscience - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.controller.devices.rst b/docs/developers/pynta.controller.devices.rst deleted file mode 100644 index c48c276..0000000 --- a/docs/developers/pynta.controller.devices.rst +++ /dev/null @@ -1,19 +0,0 @@ -pynta.controller.devices package -================================ - -Subpackages ------------ - -.. toctree:: - - pynta.controller.devices.hamamatsu - pynta.controller.devices.keysight - pynta.controller.devices.photonicscience - -Module contents ---------------- - -.. automodule:: pynta.controller.devices - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.controller.rst b/docs/developers/pynta.controller.rst deleted file mode 100644 index f18eb60..0000000 --- a/docs/developers/pynta.controller.rst +++ /dev/null @@ -1,27 +0,0 @@ -Pynta Controllers -================= - -.. automodule:: pynta.controller - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pynta.controller.data_sources - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pynta.controller.devices - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pynta.controller.devices.photonicscience.scmoscam - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: pynta.controller.devices.hamamatsu.hamamatsu_camera - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.exceptions.rst b/docs/developers/pynta.exceptions.rst index b4ab129..bae12e4 100644 --- a/docs/developers/pynta.exceptions.rst +++ b/docs/developers/pynta.exceptions.rst @@ -1,22 +1,7 @@ -pynta.exceptions package -======================== - -Submodules ----------- - -pynta.exceptions.exceptions module ----------------------------------- +Exceptions +========== .. automodule:: pynta.exceptions.exceptions :members: :undoc-members: :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.exceptions - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.model.cameras.rst b/docs/developers/pynta.model.cameras.rst deleted file mode 100644 index c12aaa3..0000000 --- a/docs/developers/pynta.model.cameras.rst +++ /dev/null @@ -1,61 +0,0 @@ -pynta.model.cameras package -=========================== - -Submodules ----------- - -pynta.model.cameras.dummy\_camera module ----------------------------------------- - -.. automodule:: pynta.model.cameras.dummy_camera - :members: - :undoc-members: - :show-inheritance: - -pynta.model.cameras.basler module ---------------------------------- -.. automodule:: pynta.model.cameras.basler - :members: - :undoc-members: - :show-inheritance: - -pynta.model.cameras.hamamatsu module ------------------------------------- - -.. automodule:: pynta.model.cameras.hamamatsu - :members: - :undoc-members: - :show-inheritance: - -pynta.model.cameras.psi module ------------------------------- - -.. automodule:: pynta.model.cameras.psi - :members: - :undoc-members: - :show-inheritance: - -pynta.model.cameras.simulate\_brownian module ---------------------------------------------- - -.. automodule:: pynta.model.cameras.simulate_brownian - :members: - :undoc-members: - :show-inheritance: - -pynta.model.cameras.skeleton module ------------------------------------ - -.. automodule:: pynta.model.cameras.skeleton - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.model.cameras - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.model.experiment.nano_cet.rst b/docs/developers/pynta.model.experiment.nano_cet.rst deleted file mode 100644 index cc29f19..0000000 --- a/docs/developers/pynta.model.experiment.nano_cet.rst +++ /dev/null @@ -1,70 +0,0 @@ -pynta.model.experiment.nano\_cet package -======================================== - -Submodules ----------- - -pynta.model.experiment.nano\_cet.decorators module --------------------------------------------------- - -.. automodule:: pynta.model.experiment.nanoparticle_tracking.decorators - :members: - :undoc-members: - :show-inheritance: - -pynta.model.experiment.nano\_cet.exceptions module --------------------------------------------------- - -.. automodule:: pynta.model.experiment.nanoparticle_tracking.exceptions - :members: - :undoc-members: - :show-inheritance: - -pynta.model.experiment.nano\_cet.localization module ----------------------------------------------------- - -.. automodule:: pynta.model.experiment.nanoparticle_tracking.localization - :members: - :undoc-members: - :show-inheritance: - -pynta.model.experiment.nano\_cet.nano\_cet module -------------------------------------------------- - -.. automodule:: pynta.model.experiment.nanoparticle_tracking.np_tracking - :members: - :undoc-members: - :show-inheritance: - -pynta.model.experiment.nano\_cet.saver module ---------------------------------------------- - -.. automodule:: pynta.model.experiment.nanoparticle_tracking.saver - :members: - :undoc-members: - :show-inheritance: - -pynta.model.experiment.nano\_cet.waterfall\_worker module ---------------------------------------------------------- - -.. automodule:: pynta.model.experiment.nanoparticle_tracking.waterfall_worker - :members: - :undoc-members: - :show-inheritance: - -pynta.model.experiment.nano\_cet.win\_nanocet module ----------------------------------------------------- - -.. automodule:: pynta.model.experiment.nanoparticle_tracking.np_tracking - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.model.experiment.nanoparticle_tracking - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.rst b/docs/developers/pynta.rst index f2ee8cd..efc814a 100644 --- a/docs/developers/pynta.rst +++ b/docs/developers/pynta.rst @@ -1,35 +1,16 @@ -pynta package -============= +.. _PyNTA: -Subpackages ------------ +PyNTA API +========= .. toctree:: + :maxdepth: 2 + :caption: Contents: - pynta.controller - pynta.exceptions - pynta.model + controller/index + model/index + view/index pynta.tests pynta.tools pynta.util - pynta.view - -Submodules ----------- - -pynta.config module -------------------- - -.. automodule:: pynta.config - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta - :members: - :undoc-members: - :show-inheritance: + pynta.exceptions diff --git a/docs/developers/pynta.view.GUI.old.Monitor.rst b/docs/developers/pynta.view.GUI.old.Monitor.rst deleted file mode 100644 index 44efea7..0000000 --- a/docs/developers/pynta.view.GUI.old.Monitor.rst +++ /dev/null @@ -1,70 +0,0 @@ -pynta.view.GUI.old.Monitor package -================================== - -Submodules ----------- - -pynta.view.GUI.old.Monitor.LocateParticle module ------------------------------------------------- - -.. automodule:: pynta.view.GUI.old.Monitor.LocateParticle - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.Monitor.cameraViewer module ----------------------------------------------- - -.. automodule:: pynta.view.GUI.old.Monitor.cameraViewer - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.Monitor.clearQueueThread module --------------------------------------------------- - -.. automodule:: pynta.view.GUI.old.Monitor.clearQueueThread - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.Monitor.crossCut module ------------------------------------------- - -.. automodule:: pynta.view.GUI.old.Monitor.crossCut - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.Monitor.popOut module ----------------------------------------- - -.. automodule:: pynta.view.GUI.old.Monitor.popOut - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.Monitor.resources module -------------------------------------------- - -.. automodule:: pynta.view.GUI.old.Monitor.resources - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.Monitor.specialTaskTrack module --------------------------------------------------- - -.. automodule:: pynta.view.GUI.old.Monitor.specialTaskTrack - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.view.GUI.old.Monitor - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.view.GUI.old.rst b/docs/developers/pynta.view.GUI.old.rst deleted file mode 100644 index 9ef9a84..0000000 --- a/docs/developers/pynta.view.GUI.old.rst +++ /dev/null @@ -1,61 +0,0 @@ -pynta.view.GUI.old package -========================== - -Subpackages ------------ - -.. toctree:: - - pynta.view.GUI.old.Monitor - -Submodules ----------- - -pynta.view.GUI.old.mainwindow module ------------------------------------- - -.. automodule:: pynta.view.GUI.old.mainwindow - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.messageWidget module ---------------------------------------- - -.. automodule:: pynta.view.GUI.old.messageWidget - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.trajectoryWidget module ------------------------------------------- - -.. automodule:: pynta.view.GUI.old.trajectoryWidget - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.waterfallWidget module ------------------------------------------ - -.. automodule:: pynta.view.GUI.old.waterfallWidget - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.old.workerThread module --------------------------------------- - -.. automodule:: pynta.view.GUI.old.workerThread - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.view.GUI.old - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.view.GUI.rst b/docs/developers/view/GUI.rst similarity index 51% rename from docs/developers/pynta.view.GUI.rst rename to docs/developers/view/GUI.rst index 187085a..73a2974 100644 --- a/docs/developers/pynta.view.GUI.rst +++ b/docs/developers/view/GUI.rst @@ -1,85 +1,48 @@ -pynta.view.GUI package -====================== - -Subpackages ------------ - -.. toctree:: - - pynta.view.GUI.old - -Submodules ----------- +GUI modules +=========== +.. automodule:: pynta.view.GUI + :members: + :undoc-members: + :show-inheritance: -pynta.view.GUI.camera\_viewer\_widget module --------------------------------------------- +.. automodule:: pynta.view.GUI.camera_focusing + :members: + :undoc-members: + :show-inheritance: .. automodule:: pynta.view.GUI.camera_viewer_widget :members: :undoc-members: :show-inheritance: -pynta.view.GUI.config\_tracking\_widget module ----------------------------------------------- - .. automodule:: pynta.view.GUI.config_tracking_widget :members: :undoc-members: :show-inheritance: -pynta.view.GUI.config\_widget module ------------------------------------- - .. automodule:: pynta.view.GUI.config_widget :members: :undoc-members: :show-inheritance: -pynta.view.GUI.histogram\_tracks\_widget module ------------------------------------------------ - .. automodule:: pynta.view.GUI.histogram_tracks_widget :members: :undoc-members: :show-inheritance: -pynta.view.GUI.histogram\_widget module ---------------------------------------- - .. automodule:: pynta.view.GUI.histogram_widget :members: :undoc-members: :show-inheritance: -pynta.view.GUI.main\_window module ----------------------------------- - .. automodule:: pynta.view.GUI.main_window :members: :undoc-members: :show-inheritance: -pynta.view.GUI.resources module -------------------------------- - -.. automodule:: pynta.view.GUI.resources - :members: - :undoc-members: - :show-inheritance: - -pynta.view.GUI.tracks\_widget module ------------------------------------- - .. automodule:: pynta.view.GUI.tracks_widget :members: :undoc-members: :show-inheritance: -Module contents ---------------- - -.. automodule:: pynta.view.GUI - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/developers/pynta.view.rst b/docs/developers/view/index.rst similarity index 52% rename from docs/developers/pynta.view.rst rename to docs/developers/view/index.rst index 8044456..5bb2a2e 100644 --- a/docs/developers/pynta.view.rst +++ b/docs/developers/view/index.rst @@ -1,37 +1,22 @@ -pynta.view package -================== - -Subpackages ------------ +View +==== +.. automodule:: pynta.view + :members: + :undoc-members: + :show-inheritance: .. toctree:: + :maxdepth: 1 + :caption: Contents - pynta.view.GUI - -Submodules ----------- - -pynta.view.main module ----------------------- + GUI .. automodule:: pynta.view.main :members: :undoc-members: :show-inheritance: -pynta.view.subscriber\_thread module ------------------------------------- - .. automodule:: pynta.view.subscriber_thread :members: :undoc-members: :show-inheritance: - - -Module contents ---------------- - -.. automodule:: pynta.view - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/index.rst b/docs/index.rst index 83c6647..374456b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -58,7 +58,7 @@ Organization, under VICI grant (PI: Prof. Allard Mosk) and Projectruimte FOM.PR1 .. toctree:: :maxdepth: 2 - :caption: Contents: + :caption: Contents installing example_config diff --git a/docs/requirements_docs.txt b/docs/requirements_docs.txt new file mode 100644 index 0000000..78cae68 --- /dev/null +++ b/docs/requirements_docs.txt @@ -0,0 +1,67 @@ +alabaster==0.7.12 +asn1crypto==0.24.0 +Babel==2.7.0 +certifi==2019.6.16 +cffi==1.12.3 +chardet==3.0.4 +cryptography==2.7 +cycler==0.10.0 +docutils==0.14 +h5py==2.9.0 +idna==2.8 +imagesize==1.1.0 +Jinja2==2.10.1 +kiwisolver==1.1.0 +lantz-core==0.5.3 +lantz-drivers==0.5.2 +lantz-ino==0.5.2 +lantz-qt==0.5.3 +lantz-sims==0.5.2 +lantzdev==0.5.2 +llvmlite==0.29.0 +MarkupSafe==1.1.1 +matplotlib==3.1.1 +mkl-fft==1.0.12 +mkl-random==1.0.2 +numba==0.45.0 +numpy==1.16.4 +packaging==19.0 +pandas==0.24.2 +pimpmyclass==0.4.3 +Pint==0.9 +pycparser==2.19 +PyDAQmx==1.4.2 +Pygments==2.4.2 +pyOpenSSL==19.0.0 +pyparsing==2.4.1 +pypylon==1.4.0 +pyqt5==5.13.0 +pyqt5-sip==4.19.18 +pyqtgraph==0.10.0 +pyreadline==2.1 +PySignal==1.1.1 +PySocks==1.7.0 +python-dateutil==2.8.0 +pytz==2019.1 +PyVISA==1.9.1 +PyYAML==5.1.1 +pyzmq==18.0.0 +requests==2.22.0 +scipy==1.2.1 +Serialize==0.1 +six==1.12.0 +snowballstemmer==1.9.0 +Sphinx==2.1.2 +sphinx-rtd-theme==0.4.3 +sphinxcontrib-applehelp==1.0.1 +sphinxcontrib-devhelp==1.0.1 +sphinxcontrib-htmlhelp==1.0.2 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.2 +sphinxcontrib-serializinghtml==1.1.3 +stringparser==0.5 +tornado==6.0.3 +trackpy==0.4.1 +urllib3==1.24.2 +win-inet-pton==1.1.0 +wincertstore==0.2 diff --git a/examples/config/dispertech.yml b/examples/config/dispertech.yml new file mode 100644 index 0000000..e405e3d --- /dev/null +++ b/examples/config/dispertech.yml @@ -0,0 +1,92 @@ +%YAML 1.2 +--- +# Default parameters for the Tracking program +# All parameters can be changed to accommodate user needs. +# All parameters can be changed at runtime with the appropriate config window +user: + name: Aquiles + +arduino: + port: COM3 + +saving: + auto_save: False + auto_save_waterfall: True + directory: D:\Data + filename_video: Video # Can be the same filename for video and photo + filename_photo: Snap + filename_tracks: Tracks + filename_waterfall: Waterfall + filename_trajectory: Trajectory + filename_log: Log + max_memory: 200 # In megabytes + +GUI: + length_waterfall: 20 # Total length of the Waterfall (lines) + refresh_time: 50 # Refresh rate of the GUI (in ms) + +camera_fiber: + model: basler # Should be a python file in model/cameras + init: daA1280 # Initial arguments to pass when creating the camera + #extra_args: [extra, arguments] # Extra arguments that can be passed when constructing the model + model_camera: Dart # To keep a registry of which camera was used in the experiment + exposure_time: 30ms # Initial exposure time (in ms) + fps: 30 # Frames per second, should either be defined by the camera or within the model based on timing + binning_x: 1 # Binning + binning_y: 1 + roi_x1: 0 + roi_x2: 1280 + roi_y1: 0 + roi_y2: 960 + background: '' # Full path to background file, or empty for none. + background_method: [Method1, Method2] + +camera_microscope: + model: basler # Should be a python file in model/cameras + init: acA1920 # Initial arguments to pass when creating the camera + #extra_args: [extra, arguments] # Extra arguments that can be passed when constructing the model + model_camera: ACE # To keep a registry of which camera was used in the experiment + exposure_time: 30ms # Initial exposure time (in ms) + fps: 30 # Frames per second, should either be defined by the camera or within the model based on timing + binning_x: 1 # Binning + binning_y: 1 + roi_x1: 0 + roi_x2: 1920 + roi_y1: 0 + roi_y2: 1200 + background: '' # Full path to background file, or empty for none. + background_method: [Method1, Method2] + +waterfall: # Parameters for calculating the waterfall plot + length: 20 # The total length of the waterfall (lines) + vertical_bin: 10 # Total number of lines of the CCD to integrate + +movie: + buffer_length: 1000 # Frames + +tracking: + locate: + diameter: 5 # Diameter of the particles (in pixels) to track, has to be an odd number + invert: False + minmass: 100 + link: + memory: 3 + search_range: 5 + filter: # Filter spurious trajectories + min_length: 25 + process: + compute_drift: False + um_pixel: 0.15 # Microns per pixel (calibration of the microscope) + min_traj_length: 2 + min_mass: 0.05 + max_size: 50.0 + max_ecc: 1 + fps: 30 + param_1: 0. + param_2: 0 + +debug: + logging_level: Nothing # One of Nothing, Debug, Info, Warning, Error + queue_memory: False + to_screen: True + diff --git a/pynta/config.py b/pynta/config.py index 7033353..a9817c6 100644 --- a/pynta/config.py +++ b/pynta/config.py @@ -7,7 +7,7 @@ or falling edge of the trigger, etc. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ diff --git a/pynta/controller/devices/__init__.py b/pynta/controller/devices/__init__.py index f7bb992..8793fb5 100644 --- a/pynta/controller/devices/__init__.py +++ b/pynta/controller/devices/__init__.py @@ -5,5 +5,11 @@ external libraries. Thus, be aware that you may need to install some dependencies in order to make the controllers work correctly. A common example is the requirement of pyvisa to communicate with serial devices, or the DLL's from Hamamatsu in order to use their cameras. + + For several devices, drivers in Python are provided by the manufacturers and thus they won't be found in this + module. A clear example is the Python wrapper of Pylon to work with Basler cameras, known as PyPylon. In that case, + PyNTA only provides a model which relies on that package. For DAQ devices such as those from National Instruments, + PyNTA depends on PyDAQmx, although NI has released its own wrapper, + `NIDAQmx-Python `_ which may be worth exploring. """ diff --git a/pynta/controller/devices/hamamatsu/hamamatsu_camera.py b/pynta/controller/devices/hamamatsu/hamamatsu_camera.py index 5ccd4cc..f63b95d 100644 --- a/pynta/controller/devices/hamamatsu/hamamatsu_camera.py +++ b/pynta/controller/devices/hamamatsu/hamamatsu_camera.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- """ - hamamatsu_camera.py - ~~~~~~~~~~~~~~~~~~~~ - File taken from `ZhuangLab `_ + Hamamatsu Driver + ================ + Original file taken from `ZhuangLab `_ - A ctypes based interface to Hamamatsu cameras. - (tested on a sCMOS Flash 4.0). + A ctypes based interface to Hamamatsu cameras. (tested on a sCMOS Flash 4.0). The documentation is a little confusing to me on this subject.. I used c_int32 when this is explicitly specified, otherwise I use c_int. @@ -14,9 +13,10 @@ .. todo:: How to stream 2048 x 2048 at max frame rate to the flash disk? The Hamamatsu software can do this. - Copyright: Hazen Babcock - This file was adapted to Python 3 and documented in the numpy style by Aquiles Carattino + This file was adapted to Python 3 and documented in the numpy style by Aquiles Carattino + :copyright: Hazen Babcock + :license: The MIT License """ import ctypes diff --git a/pynta/controller/devices/keysight/infiniivision.py b/pynta/controller/devices/keysight/infiniivision.py index a5f96a0..fa9bdc1 100644 --- a/pynta/controller/devices/keysight/infiniivision.py +++ b/pynta/controller/devices/keysight/infiniivision.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- - """ - inifiniivision.py - ~~~~~~~~~~~~~~~~~ + Keysight InfiniiVision + ====================== Driver for the Keysight InfiniiVision DSOX2022A oscilloscope with function generation capabilities. Source: Programmers Guide, Version 02.39.0000 by Keysight Technologies @@ -10,7 +9,7 @@ .. note:: Not all functions were implemented in the code as methods in the class. However a fair amount of the most useful ones was formatted according to Lantz standards. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ diff --git a/pynta/controller/devices/photonicscience/scmoscam.py b/pynta/controller/devices/photonicscience/scmoscam.py index 452b814..c4a6cfe 100644 --- a/pynta/controller/devices/photonicscience/scmoscam.py +++ b/pynta/controller/devices/photonicscience/scmoscam.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - scmoscam.py - ~~~~~~~~~~~ + Photonic Science GEVSCMOS + ========================= Driver for Photonic Science Cameras (Pleora GEV). This driver was originally written by Perceval Guillou , in Py2 and has been successfully tested with scmoscontrol.dll SCMOS Pleora (GEV) control dll (x86 )v5.6.0.0 (date modified 10/2/2013) diff --git a/pynta/model/cameras/base_camera.py b/pynta/model/cameras/base_camera.py index 46e04be..62f0a5b 100644 --- a/pynta/model/cameras/base_camera.py +++ b/pynta/model/cameras/base_camera.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - Base Camera - =========== + Base Camera Model + ================= Camera class with the base methods. Having a base class exposes the general API for working with cameras. This file is important to keep track of the methods which are exposed to the View. The class BaseCamera should be subclassed when developing new Models for other cameras. This ensures that all the @@ -22,7 +22,7 @@ .. note:: **IMPORTANT** Whatever new function is implemented in a specific model, it should be first declared in the BaseCamera class. In this way the other models will have access to the method and the program will keep running (perhaps with non intended behavior though). - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ import numpy as np @@ -35,30 +35,32 @@ logger = get_logger(__name__) - class BaseCamera: MODE_CONTINUOUS = 1 MODE_SINGLE_SHOT = 0 + ACQUISITION_MODE = { + MODE_CONTINUOUS: 'Continuous', + MODE_SINGLE_SHOT: 'Single' + } def __init__(self, camera): self.camera = camera self.running = False - self.maxWidth = 0 - self.maxHeight = 0 + self.max_width = 0 + self.max_height = 0 self.exposure = 0 self.config = {} self.data_type = np.uint16 # The data type that the camera generates when acquiring images. It is very important to have it available in order to create the buffer and saving to disk. self.logger = get_logger(name=__name__) - def configure(self, properties): + def configure(self, properties: dict): self.logger.info('Updating config') update_cam = False update_roi = False update_exposure = False - update_binning = True - for k in properties: - new_prop = properties[k] + update_binning = False + for k, new_prop in properties.items(): self.logger.debug('Updating {} to {}'.format(k, new_prop)) update_cam = True @@ -79,22 +81,24 @@ def configure(self, properties): if update_cam: if update_roi: - - X = np.sort([properties['roi_x1'], properties['roi_x2']]) - Y = np.sort([properties['roi_y1'], properties['roi_y2']]) - self.setROI(X, Y) + X = sorted([properties['roi_x1'], properties['roi_x2']]) + Y = sorted([properties['roi_y1'], properties['roi_y2']]) + self.set_ROI(X, Y) self.config.update({'roi_x1': X[0], 'roi_x2': X[1], 'roi_y1': Y[0], 'roi_y2': Y[1]}) if update_exposure: - exposure = Q_(properties['exposure_time']) + exposure = properties['exposure_time'] + if isinstance(exposure, str): + exposure = Q_(exposure) + new_exp = self.set_exposure(exposure) self.config['exposure_time'] = new_exp if update_binning: - self.setBinning(properties['binning_x'], properties['binning_y']) + self.set_binning(properties['binning_x'], properties['binning_y']) self.config.update({'binning_x': properties['binning_x'], 'binning_y': properties['binning_y']}) @@ -103,8 +107,8 @@ def initialize(self): """ Initializes the camera. """ - self.maxWidth = self.GetCCDWidth() - self.maxHeight = self.GetCCDHeight() + self.max_width = self.GetCCDWidth() + self.max_height = self.GetCCDHeight() return True @not_implemented @@ -123,7 +127,6 @@ def set_acquisition_mode(self, mode): """ self.mode = mode - @not_implemented def get_acquisition_mode(self): """ Returns the acquisition mode, either continuous or single shot. @@ -159,7 +162,7 @@ def read_camera(self): pass @not_implemented - def setROI(self, X, Y): + def set_ROI(self, X, Y): """ Sets up the ROI. Not all cameras are 0-indexed, so this is an important place to define the proper ROI. @@ -170,14 +173,14 @@ def setROI(self, X, Y): return X, Y - def clearROI(self): + def clear_ROI(self): """ Clears the ROI from the camera. """ - self.setROI([0, self.maxWidth], [0, self.maxHeight]) + self.set_ROI([0, self.max_width], [0, self.max_height]) @not_implemented - def getSize(self): + def get_size(self): """Returns the size in pixels of the image being acquired. This is useful for checking the ROI settings. """ pass @@ -208,7 +211,7 @@ def stopAcq(self): pass @not_implemented - def setBinning(self, xbin, ybin): + def set_binning(self, xbin, ybin): """ Sets the binning of the camera if supported. Has to check if binning in X/Y can be different or not, etc. @@ -218,10 +221,18 @@ def setBinning(self, xbin, ybin): """ pass + @not_implemented + def clear_binning(self): + """ + Clears the binning of the camera to its default value. + """ + pass + @not_implemented def stop_camera(self): """Stops the acquisition and closes the connection with the camera. """ pass - + def __str__(self): + return f"Base Camera {self.camera}" diff --git a/pynta/model/cameras/basler.py b/pynta/model/cameras/basler.py index 552d0c1..d684233 100644 --- a/pynta/model/cameras/basler.py +++ b/pynta/model/cameras/basler.py @@ -1,15 +1,17 @@ """ Basler Camera Model =================== - Model to adapt PyPylon to the needs of Pynta. PyPylon is only a wrapper for Pylon, thus the documentation + Model to adapt PyPylon to the needs of PyNTA. PyPylon is only a wrapper for Pylon, thus the documentation has to be found in the folder where Pylon was installed. It refers only to the C++ documentation, which is very extensive, but not necessarily clear. Some assumptions ---------------- - The program forces software trigger during :meth:`~nanoparticle_tracking.model.cameras.basler.Camera.initialize`. + The program forces software trigger during :meth:`~pynta.model.cameras.basler.Camera.initialize`. """ import logging +import warnings +from typing import Tuple from pypylon import pylon @@ -27,15 +29,19 @@ def __init__(self, camera): self.cam_num = camera self.max_width = 0 self.max_height = 0 - self.logger = get_logger(name=__name__) + self.width = None + self.height = None self.mode = None + self.X = None + self.Y = None + self.friendly_name = None def initialize(self): """ Initializes the communication with the camera. Get's the maximum and minimum width. It also forces the camera to work on Software Trigger. - .. warning:: It may be useful to integrate with other types of triggers in applications that need to - synchronize with other hardware. + .. warning:: It may be useful to integrate other types of triggers in applications that need to + synchronize with other hardware. """ logger.debug('Initializing Basler Camera') @@ -44,20 +50,35 @@ def initialize(self): if len(devices) == 0: raise CameraNotFound('No camera found') - self.camera = pylon.InstantCamera() - self.camera.Attach(tl_factory.CreateDevice(devices[self.cam_num])) - self.camera.Open() + for device in devices: + if self.cam_num in device.GetFriendlyName(): + self.camera = pylon.InstantCamera() + self.camera.Attach(tl_factory.CreateDevice(device)) + self.camera.Open() + self.friendly_name = device.GetFriendlyName() + + if not self.camera: + msg = f'{self.cam_num} not found. Please check your config file and cameras connected' + logger.error(msg) + raise CameraNotFound(msg) logger.info(f'Loaded camera {self.camera.GetDeviceInfo().GetModelName()}') self.max_width = self.camera.Width.Max self.max_height = self.camera.Height.Max + offsetX = self.camera.OffsetX.Value + offsetY = self.camera.OffsetY.Value + width = self.camera.Width.Value + height = self.camera.Height.Value + self.X = (offsetX, offsetX+width) + self.Y = (offsetY, offsetY+height) + self.camera.RegisterConfiguration(pylon.SoftwareTriggerConfiguration(), pylon.RegistrationMode_ReplaceAll, pylon.Cleanup_Delete) self.set_acquisition_mode(self.MODE_SINGLE_SHOT) def set_acquisition_mode(self, mode): - self.logger.info(f'Setting acquisition mode to {mode}') + logger.info(f'Setting acquisition mode to {mode}') if mode == self.MODE_CONTINUOUS: logger.debug(f'Setting buffer to {self.camera.MaxNumBuffer.Value}') self.camera.OutputQueueSize = self.camera.MaxNumBuffer.Value @@ -69,26 +90,32 @@ def set_acquisition_mode(self, mode): self.camera.AcquisitionStart.Execute() - def setROI(self, X, Y): + def set_ROI(self, X: Tuple[int, int], Y: Tuple[int, int]) -> Tuple[int, int]: """ Set up the region of interest of the camera. Basler calls this the Area of Interest (AOI) in their manuals. Beware that not all cameras allow to set the ROI (especially if they are not area sensors). Both the corner positions and the width/height need to be multiple of 4. Compared to Hamamatsu, Baslers provides a very descriptive error warning. + :param tuple X: Horizontal limits for the pixels, 0-indexed and including the extremes. You can also check + :mod:`Base Camera ` + To select, for example, the first 100 horizontal pixels, you would supply the following: (0, 99) + :param tuple Y: Vertical limits for the pixels. + """ - width = abs(X[1]-X[0]) + width = abs(X[1]-X[0])+1 width = int(width-width%4) x_pos = int(X[0]-X[0]%4) - height = int(abs(Y[1]-Y[0])) + height = int(abs(Y[1]-Y[0])+1) y_pos = int(Y[0]-Y[0]%2) + logger.info(f'Updating ROI: (x, y, width, height) = ({x_pos}, {y_pos}, {width}, {height})') if x_pos+width > self.max_width: raise CameraException('ROI width bigger than camera area') if y_pos+height > self.max_height: raise CameraException('ROI height bigger than camera area') # First set offset to minimum, to avoid problems when going to a bigger size - self.clearROI() + self.clear_ROI() logger.debug(f'Setting width to {width}') self.camera.Width.SetValue(width) logger.debug(f'Setting X offset to {x_pos}') @@ -97,25 +124,50 @@ def setROI(self, X, Y): self.camera.Height.SetValue(height) logger.debug(f'Setting Y offset to {y_pos}') self.camera.OffsetY.SetValue(y_pos) - self.X = [x_pos, x_pos+width] - self.Y = [y_pos, y_pos+width] - self.width = width - self.heigth = height - return width, height - - def clearROI(self): + self.X = (x_pos, x_pos+width) + self.Y = (y_pos, y_pos+width) + self.width = self.camera.Width.Value + self.height = self.camera.Height.Value + return self.width, self.height + + def clear_ROI(self): + """ Resets the ROI to the maximum area of the camera""" self.camera.OffsetX.SetValue(self.camera.OffsetX.Min) self.camera.OffsetY.SetValue(self.camera.OffsetY.Min) self.camera.Width.SetValue(self.camera.Width.Max) self.camera.Height.SetValue(self.camera.Height.Max) - def GetCCDWidth(self): + def GetCCDWidth(self) -> int: + """ Get the full width of the camera sensor. + + :return int: Maximum width + + .. deprecated:: 0.1.3 + Use self.max_width instead + + """ + warnings.warn("This method will be removed in a future release. Use cls.max_width instead", DeprecationWarning) return self.max_width - def GetCCDHeight(self): + def GetCCDHeight(self) -> int: + """ Get the full height (in pixels) of the camera sensor. + + :return int: Maximum height + + .. deprecated:: 0.1.3 + Use self.max_height instead + + """ + warnings.warn("This method will be removed in a future release. Use cls.max_height instead", DeprecationWarning) return self.max_height - def getSize(self): + def get_size(self) -> Tuple[int, int]: + """ Get the size of the current Region of Interest (ROI). Remember that the actual size may be different from + the size that the user requests, given that not all cameras accept any pixel. For example, Basler has some + restrictions regarding corner pixels and possible widths. + + :return tuple: (Width, Height) + """ return self.camera.Width.Value, self.camera.Height.Value def trigger_camera(self): @@ -160,9 +212,13 @@ def read_camera(self): return [i.T for i in img] # Transpose to have the correct size def stop_camera(self): + logger.info('Stopping camera') self.camera.StopGrabbing() self.camera.AcquisitionStop.Execute() + def __str__(self): + return self.friendly_name + def __del__(self): self.camera.Close() diff --git a/pynta/model/cameras/dummy_camera.py b/pynta/model/cameras/dummy_camera.py index 0421cdb..728c532 100644 --- a/pynta/model/cameras/dummy_camera.py +++ b/pynta/model/cameras/dummy_camera.py @@ -1,19 +1,19 @@ # -*- coding: utf-8 -*- """ - dummyCamera.py - ~~~~~~~~~~~~~~ + Dummy Camera Model + ================== Dummy camera class for testing GUI and other functionality. This specific version generates randomly diffusing - particles. However, the settings are controlled in a different class, SimBrownian. + particles. However, the settings are controlled in a different class, :mod:`SimBrownian `. .. TODO:: The camera defines plenty of parameters that are not used or that they are confusing later on. Rasing - exceptions does not happen even if trying to extend beyond the maximum dimensions of the CCD. + exceptions does not happen even if trying to extend beyond the maximum dimensions of the CCD. .. TODO:: The parameters for the simulation of the brownian motion should be made explicitly here, in such a way - that can be used from within the config file as well. + that can be used from within the config file as well. .. TODO:: Some of the methods do not return the same datatype as the real models - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ @@ -98,7 +98,7 @@ def read_camera(self): time.sleep(self.exposure.m_as('s') - elapsed) # to simulate exposure time corrected for data generation delay return [sample] - def setROI(self, X, Y): + def set_ROI(self, X, Y): """ Sets up the ROI. Not all cameras are 0-indexed, so this is an important place to define the proper ROI. @@ -116,9 +116,9 @@ def setROI(self, X, Y): self.Y = Y self.X[1] -= 1 self.Y[1] -= 1 - return self.getSize() + return self.get_size() - def getSize(self): + def get_size(self): """ :return: Returns the size in pixels of the image being acquired. This is useful for checking the ROI settings. """ @@ -142,7 +142,7 @@ def GetCCDHeight(self): """ return self.maxY - def setBinning(self, xbin, ybin): + def set_binning(self, xbin, ybin): """Sets the binning of the camera if supported. Has to check if binning in X/Y can be different or not, etc. :param: xbin: binning in x diff --git a/pynta/model/cameras/hamamatsu.py b/pynta/model/cameras/hamamatsu.py index 2ff97de..9a03bee 100644 --- a/pynta/model/cameras/hamamatsu.py +++ b/pynta/model/cameras/hamamatsu.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - hamamatsu.py - ~~~~~~~~~~~~ + Hamamatsu Model + =============== Model class for controlling Hamamatsu cameras via de DCAM-API. At the time of writing this class, little documentation on the DCAM-API was available. Hamamatsu has a different time schedule regarding support of @@ -11,13 +11,13 @@ DCAM-API relies mostly on setting parameters into the camera. The correct data type of each parameter is not well documented; however it is possible to print all the available properties and work from there. The properties are stored in a filed named params.txt next to the :mod:`Hamamatsu Driver - ` + ` .. note:: When setting the ROI, Hamamatsu only allows to set multiples of 4 for every setting (X,Y and vsize, hsize). This is checked in the function. Changing the ROI cannot be done directly, one first needs to disable it and then re-enable. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ import numpy as np @@ -43,10 +43,10 @@ def initialize(self): :return: """ self.camera.initCamera() - self.maxWidth = self.GetCCDWidth() - self.maxHeight = self.GetCCDHeight() - self.X = [0, self.maxWidth-1] - self.Y = [0, self.maxHeight-1] + self.max_width = self.GetCCDWidth() + self.max_height = self.GetCCDHeight() + self.X = [0, self.max_width - 1] + self.Y = [0, self.max_height - 1] # This is important to not have shufled patches of the CCD. # Have to check documentation!! @@ -121,7 +121,7 @@ def read_camera(self): # img = np.reshape(img,(dims[0],dims[1])) return img - def setROI(self, X, Y): + def set_ROI(self, X, Y): """ Sets up the ROI. Not all cameras are 0-indexed, so this is an important place to define the proper ROI. @@ -148,9 +148,9 @@ def setROI(self, X, Y): self.camera.setPropertyValue("subarray_vsize", vsize) self.camera.setPropertyValue("subarray_hsize", hsize) self.camera.setSubArrayMode() - return self.getSize() + return self.get_size() - def getSize(self): + def get_size(self): """Returns the size in pixels of the image being acquired. This is useful for checking the ROI settings. """ X = self.camera.getPropertyValue("subarray_hsize") diff --git a/pynta/model/cameras/psi.py b/pynta/model/cameras/psi.py index 3432fb7..deeda50 100644 --- a/pynta/model/cameras/psi.py +++ b/pynta/model/cameras/psi.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- """ - psi.py - ~~~~~~ + Photonic Science GEV Model + ========================== Model for Photonic Science GEV Cameras. The model just implements the basic methods defined in the - :meth:`~nanoparticle_tracking.model.cameras.skeleton.BaseCamera` using a Photonic Sicence camera. The controller for this - camera is :mod:`~nanoparticle_tracking.controller.devices.photonicscience` + :meth:`~pynta.model.cameras.base_camera.BaseCamera` using a Photonic Sicence camera. The controller for this + camera is :mod:`~pynta.controller.devices.photonicscience` - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ import numpy as np @@ -28,7 +28,7 @@ def initialize(self): """ Initializes the camera. - .. todo:: :meth:`UUTrack.Controller.devices.PhotonicScience.scmoscam.GEVSCMOS.SetGainMode` behaves unexpectedly. + .. todo:: :meth:`pynta.controller.devices.photonicscience.scmoscam.GEVSCMOS.SetGainMode` behaves unexpectedly. One is forced to set the gain mode twice to have it right. So far, this is the only way to prevent the *weird lines* from appearing. Checking the meaning of the gains is a **must**. @@ -42,9 +42,9 @@ def initialize(self): self.camera.EnableAutoLevel(0) self.camera.SetExposure(10, "Millisec") self.trigger_camera() - size = self.getSize() - self.maxWidth = size[0] - self.maxHeight = size[1] + size = self.get_size() + self.max_width = size[0] + self.max_height = size[1] self.camera.SetGainMode("gain30") # Change the gain here! Check scmoscam.py for information def trigger_camera(self): @@ -73,7 +73,7 @@ def read_camera(self): img = np.array(img) return np.transpose(img) - def setROI(self, X, Y): + def set_ROI(self, X, Y): """Sets up the ROI. """ X -= 1 @@ -86,7 +86,7 @@ def setROI(self, X, Y): self.camera.SetSubArea(l, t, r, b) return self.camera.GetSize() - def getSize(self): + def get_size(self): """Returns the size in pixels of the image being acquired. """ return self.camera.GetSize() @@ -117,11 +117,11 @@ def getParameters(self): def GetCCDWidth(self): """Gets the CCD width.""" - return self.getSize()[0] + return self.get_size()[0] def GetCCDHeight(self): """Gets the CCD height.""" - return self.getSize()[1] + return self.get_size()[1] def stopAcq(self): """Stop the acquisition even if ongoing.""" diff --git a/pynta/model/cameras/simulate_brownian.py b/pynta/model/cameras/simulate_brownian.py index 04568b7..637f951 100644 --- a/pynta/model/cameras/simulate_brownian.py +++ b/pynta/model/cameras/simulate_brownian.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - simulate_brownian.py - ==================== + Simulated Brownian Diffusion + ============================ The SimBrownian class generates synthetic images corresponding to particles performing a thermal Brownian motion. This class was designed in order to make explicit the real parameters of the particles (i.e. the diffusion coefficient in real-space) with the magnification of the microscope. The PSF on the camera is derived from the @@ -16,7 +16,7 @@ .. TODO:: Think how to add noise, background, and intensity fluuctuations to the particles. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ import numpy as np diff --git a/pynta/model/daqs/NI.py b/pynta/model/daqs/NI.py index e5d2d02..9e7710d 100644 --- a/pynta/model/daqs/NI.py +++ b/pynta/model/daqs/NI.py @@ -27,8 +27,10 @@ or renaming implies that you have to check all the downstream code, and being YML the first step, it will imply reviewing everything. + .. TODO:: National Instruments has releasedi its own Python driver (NIDAQmx-Python) which may be worth exploring for + future development - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ try: diff --git a/pynta/model/daqs/daq_dummy.py b/pynta/model/daqs/daq_dummy.py index 5651479..41bce0a 100644 --- a/pynta/model/daqs/daq_dummy.py +++ b/pynta/model/daqs/daq_dummy.py @@ -1,20 +1,24 @@ # -*- coding: utf-8 -*- """ - nanoparticle_tracking.model.daqs.daq_dummy.py - ==================================== - Dummy daq class for testing GUI and other functionalities. Based on the skeleton. + Dummy DAQ + ========= + DAQ model for testing GUI and other functionalities. Based on the skeleton. It does not interact with any real + device, it just generates random data and accepts inputs which have no effect. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ +from pynta.util import get_logger from .skeleton import DaqBase +logger = get_logger(__name__) + class DAQDummy(DaqBase): def __init__(self, dev_number=None): - print('Initialized device with number: %s' % dev_number) + super().__init__(dev_number) + logger.info('Initialized device with number: %s' % dev_number) self.test_value = 0 - pass def triggerAnalog(self, conditions): """Triggers an analog measurement. It does not read the value. diff --git a/pynta/model/daqs/skeleton.py b/pynta/model/daqs/skeleton.py index 3c33b16..0834842 100644 --- a/pynta/model/daqs/skeleton.py +++ b/pynta/model/daqs/skeleton.py @@ -1,18 +1,21 @@ # -*- coding: utf-8 -*- """ - base_camera.py - ~~~~~~~~~~~ + Base Model for DAQs + =================== + Base model that makes explicit the API for working with DAQ cards. Every new DAQ card should inherit this model. + This allows to check the argument type, for example, but they also guarantee forward-compatibility in case new + methods are developed. .. note:: **IMPORTANT** Whatever new function is implemented in a specific model, it should be first declared in the - laserBase class. In this way the other models will have access to the method and the program will keep running - (perhaps with non intended behavior though). + laserBase class. In this way the other models will have access to the method and the program will keep running + (perhaps with non intended behavior though). - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: AGPLv3, see LICENSE for more details """ -class DaqBase(): +class DaqBase: def __init__(self, dev_number): self.dev_number = dev_number @@ -42,4 +45,4 @@ def read_analog(self, task_number, conditions): """ Gets the analog values acquired with the triggerAnalog function. """ - pass \ No newline at end of file + pass diff --git a/pynta/model/experiment/__init__.py b/pynta/model/experiment/__init__.py index 6a335f2..08a0f97 100644 --- a/pynta/model/experiment/__init__.py +++ b/pynta/model/experiment/__init__.py @@ -1,3 +1,18 @@ +# -*- coding: utf-8 -*- +""" + Experiment Models + ================= + Experiment models make explicit the different steps needed to perform a measurement. The Base Experiment class + defines some methods that are transversal to all experiments (such as loading a configuration file) but individual + experiment models can overwrite this methods to develop custom solutions. + + Moreover, PyNTA introduces the PUB/SUB pattern in order to exchange information between different parts of the + program in a flexible and efficient way. You can find more information on :mod:`~pynta.model.experiment.publisher` + and :mod:`~pynta.model.experiment.subscriber`. + + :copyright: Aquiles Carattino + :license: GPLv3, see LICENSE for more details +""" from pynta.model.experiment.config import Config config = Config() \ No newline at end of file diff --git a/pynta/model/experiment/base_experiment.py b/pynta/model/experiment/base_experiment.py index 9f6ddf6..8f0e592 100644 --- a/pynta/model/experiment/base_experiment.py +++ b/pynta/model/experiment/base_experiment.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ base_experiment.py - ~~~~~~~~~~~~~~~~~~ + ================== Base class for the experiments. ``BaseExperiment`` defines the common patterns that every experiment should have. Importantly, it starts an independent process called publisher, that will be responsible for broadcasting messages that are appended to a queue. The messages rely on the pyZMQ library and should be tested further in order to @@ -18,7 +18,7 @@ .. TODO:: Check whether the serialization of objects with cPickle may be a bottleneck for performance. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ from multiprocessing import Process, Event @@ -34,7 +34,7 @@ class BaseExperiment: """ Base class to define experiments. Should keep track of the basic methods needed regardless of the experiment to be performed. For instance, a way to start and a way to finalize a measurement. """ - def __init__(self): + def __init__(self, filename=None): self.config = {} # Dictionary storing the configuration of the experiment self.logger = get_logger(name=__name__) self._threads = [] @@ -43,6 +43,8 @@ def __init__(self): self._connections = [] self.subscriber_events = [] + if filename: + self.load_configuration(filename) def stop_publisher(self): """ Puts the proper data to the queue in order to stop the running publisher process diff --git a/pynta/model/experiment/config.py b/pynta/model/experiment/config.py index 1806890..4071dde 100644 --- a/pynta/model/experiment/config.py +++ b/pynta/model/experiment/config.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- """ Experiment Configuration - =========================== + ======================== Class which holds some parameters that need to be used throughout the lifetime of a program. Keeping them in a separate class gives great flexibility, because it allows to overwrite values at run time. .. TODO:: Changes to config at runtime will have no effect on other processes. Find a way in which config can broadcast itself to all the instances of the class - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ from pynta.util import get_logger diff --git a/pynta/model/experiment/dispertech/__init__.py b/pynta/model/experiment/dispertech/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pynta/model/experiment/dispertech/database.py b/pynta/model/experiment/dispertech/database.py new file mode 100644 index 0000000..3ad7a47 --- /dev/null +++ b/pynta/model/experiment/dispertech/database.py @@ -0,0 +1,80 @@ +import sqlite3 +from pathlib import Path +from typing import Tuple + +import yaml + +from pynta.util import get_logger + +logger = get_logger(__name__) + + +def initialize_database() -> Tuple[sqlite3.Connection, sqlite3.Cursor]: + """ We are using a simple SQLite database to store some information regarding the use of the program. The most + important feature is being able to store the latest set parameters in such a way the they can be recovered when + restarting the program. If the database does not exist, it should be created. + """ + home = Path.home() + pynta_path = Path(home, '.pynta') + if not pynta_path.is_dir(): + try: + pynta_path.mkdir() + except PermissionError: + logger.error(f'This user does not have access to {pynta_path}. Consider another directory') + raise + + database_path = Path(pynta_path, 'config.sqlite') + if not database_path.is_file(): + logger.info('Going to create a new database') + conn = sqlite3.connect(str(database_path)) + cur = conn.cursor() + sql_command = """CREATE TABLE configs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + name VARCHAR, + description VARCHAR) + """ + cur.execute(sql_command) + sql_command = """CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP) + """ + cur.execute(sql_command) + sql_command = """CREATE TABLE samples ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR, + description TEXT + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP) + """ + cur.execute(sql_command) + sql_command = """CREATE TABLE experiments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id)) + """ + cur.execute(sql_command) + sql_command = """INSERT INTO configs (name, description) + VALUES + ('Initial Database', 'Starting Fiber Tracking for the first time') + """ + cur.execute(sql_command) + conn.commit() + else: + logger.info('Database found') + conn = sqlite3.connect(str(database_path)) + cur = conn.cursor() + return conn, cur + + +def store_config(db: Tuple[sqlite3.Connection, sqlite3.Cursor], config: dict) -> None: + conn = db[0] + cur = db[1] + data = yaml.dump(config) + sql_command = f"""INSERT INTO configs (name, description) VALUES + ('Saved Config', '{data}') + """ + cur.execute(sql_command) + conn.commit() + diff --git a/pynta/model/experiment/dispertech/fiber_tracking.py b/pynta/model/experiment/dispertech/fiber_tracking.py new file mode 100644 index 0000000..6de1b8f --- /dev/null +++ b/pynta/model/experiment/dispertech/fiber_tracking.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" + Tracking in Hollow Optical Fibers + ================================= + + This experiment is very similar to the nanoparticle tracking experiment, but everything happens inside a hollow + optical fiber. Some steps are different; for example, the user first has to focus the image of the camera on the + top part of the setup in order to couple the laser to the optical fiber. Then, the user needs to maximise the + background signal on the microscope's camera in order to fine-tune the coupling. + + The measurement is essentially a 1-D measurement of diffusion, in which equations need to be adapted for including + diffusion in a cylinder. + + :copyright: Aquiles Carattino + :license: GPLv3, see LICENSE for more details +""" +import importlib +import sqlite3 +from pathlib import Path + +from pynta.model.experiment.base_experiment import BaseExperiment +from pynta.model.experiment.dispertech.util import load_camera_module, instantiate_camera +from pynta.util import get_logger + + +logger = get_logger(name=__name__) + + +class FiberTracking(BaseExperiment): + """ Experiment class for performing nanoparticle tracking analysis inside a hollow optical fiber. + """ + SINGLE_SNAP_BKG = 0 + """Uses only one image to correct the background""" + + ROLLING_AVERAGE = 1 + """Uses a window of averages to correct the background""" + + BACKGROUND_CORRECTION = ( + ('single_snap', SINGLE_SNAP_BKG), + ('rolling_avg', ROLLING_AVERAGE) + ) + + def __init__(self, filename=None): + super().__init__(filename) + self.cameras = { + 'fiber': None, + 'microscope': None + } + self.initialize_threads = [] + + def initialize_cameras(self): + """ The experiment requires two cameras, and they need to be initialized before we can proceed with the + measurement. This requires two entries in the config file with names ``camera_fiber``, which refers to the + camera which monitors the end of the fiber and ``camera_microscope``, which is the one that is used to do the + real measurement. + + """ + + self.cameras['fiber'] = instantiate_camera(config=self.config['camera_fiber']) + self.cameras['microscope'] = instantiate_camera(config=self.config['camera_microscope']) + + logger.info('Initializing the cameras...') + for k, camera in self.cameras.items(): + logger.info(f'Initializing {k} camera: {camera}') + camera.initialize() + + def initialize_mirror(self): + """ Routine to initialize the movable mirror. The steps in this method should be those needed for having the + mirror in a known position (i.e. the homing procedure). + """ + logger.info('Homing mirror') + + def initialize_electronics(self): + """ Routine to initialize the rest of the electronics. For example, the LED's can be set to a default on/off + state. This is also used to measure the temperature. + """ + logger.info('Initializing electronics') + + def initialize(self): + """ Initializes all the devices at the same time using threads. + """ + self.initialize_threads \ No newline at end of file diff --git a/pynta/model/experiment/dispertech/util.py b/pynta/model/experiment/dispertech/util.py new file mode 100644 index 0000000..c5d24bc --- /dev/null +++ b/pynta/model/experiment/dispertech/util.py @@ -0,0 +1,32 @@ +import importlib + +from pynta.util import get_logger + +logger = get_logger(name=__name__) + + +def load_camera_module(name): + try: + logger.info('Importing camera model {}'.format(name)) + logger.debug('pynta.model.cameras.' + name) + camera_model_to_import = 'pynta.model.cameras.' + name + cam_module = importlib.import_module(camera_model_to_import) + except ModuleNotFoundError: + logger.error('The model {} for the camera was not found'.format(name)) + raise + return cam_module + + +def instantiate_camera(config: dict): + cam_module = load_camera_module(config['model']) + cam_init_arguments = config['init'] + if 'extra_args' in config: + logger.info('Initializing camera with extra arguments') + logger.debug('cam_module.camera({}, {})'.format(cam_init_arguments, config['extra_args'])) + camera = cam_module.Camera(cam_init_arguments, *config['extra_args']) + else: + logger.info('Initializing camera without extra arguments') + logger.debug('cam_module.camera({})'.format(cam_init_arguments)) + camera = cam_module.Camera(cam_init_arguments) + + return camera diff --git a/pynta/model/experiment/nanoparticle_tracking/localization.py b/pynta/model/experiment/nanoparticle_tracking/localization.py index 97ffe4d..72d8293 100644 --- a/pynta/model/experiment/nanoparticle_tracking/localization.py +++ b/pynta/model/experiment/nanoparticle_tracking/localization.py @@ -15,7 +15,7 @@ :class:`~nanoparticle_tracking.model.experiment.subscriber.Subscriber` in order to listen for the new data, and in turn publishes it with the Publisher. -:copyright: Aquiles Carattino +:copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ from copy import copy diff --git a/pynta/model/experiment/nanoparticle_tracking/np_tracking.py b/pynta/model/experiment/nanoparticle_tracking/np_tracking.py index 9cee06f..7d37fed 100644 --- a/pynta/model/experiment/nanoparticle_tracking/np_tracking.py +++ b/pynta/model/experiment/nanoparticle_tracking/np_tracking.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - nanoparticle_tracking.py - =========== + Nanoparticle Tracking + ===================== Nanoparticle tracking is a technique that allows to measure the size of very small objects. The core idea is that by locating objects subject to brownian motion, it is possible to reconstruct their movement, which in turn can be fitted to a model which depends on properties of the medium (i.e. viscosity) and on the diameter of the particles. @@ -11,7 +11,7 @@ at providing a superior approach, allowing researchers to have real-time information on the sample studied and a completely transparent approach regarding algorithms used. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ import sys @@ -48,13 +48,11 @@ class NPTracking(BaseExperiment): BACKGROUND_SINGLE_SNAP = 1 def __init__(self, filename=None): - super().__init__() + super().__init__(filename) self.free_run_running = False self.saving_location = False self.logger = get_logger(name=__name__) - self.load_configuration(filename) - self.dropped_frames = 0 self.keep_acquiring = True self.acquiring = False # Status of the acquisition @@ -125,7 +123,7 @@ def initialize_camera(self): self.camera = cam_module.Camera(cam_init_arguments) self.camera.initialize() - self.current_width, self.current_height = self.camera.getSize() + self.current_width, self.current_height = self.camera.get_size() self.logger.info('Camera sensor ROI: {}px X {}px'.format(self.current_width, self.current_height)) self.max_width = self.camera.GetCCDWidth() self.max_height = self.camera.GetCCDHeight() @@ -156,7 +154,7 @@ def set_roi(self, X, Y): """ self.logger.debug('Setting new camera ROI to x={},y={}'.format(X, Y)) - self.current_width, self.current_height = self.camera.setROI(X, Y) + self.current_width, self.current_height = self.camera.set_ROI(X, Y) self.logger.debug('New camera width: {}px, height: {}px'.format(self.current_width, self.current_height)) self.temp_image = None @@ -168,7 +166,7 @@ def clear_roi(self): self.logger.info('Clearing ROI settings') X = [0, self.max_width-1] Y = [0, self.max_height-1] - self.camera.setROI(X, Y) + self.camera.set_ROI(X, Y) @check_camera @check_not_acquiring diff --git a/pynta/model/experiment/nanoparticle_tracking/saver.py b/pynta/model/experiment/nanoparticle_tracking/saver.py index b9b7a6f..b07bf64 100644 --- a/pynta/model/experiment/nanoparticle_tracking/saver.py +++ b/pynta/model/experiment/nanoparticle_tracking/saver.py @@ -1,5 +1,5 @@ """ - nanoparticle_tracking.model.experiment.workerSaver + pynta.model.experiment.workerSaver ================================== When working with multi threading in Python it is important to define the function that will be run in a separate thread. workerSaver is just a function that will be moved to a separate, parallel thread to save data to disk @@ -25,7 +25,7 @@ :copyright: 2017 - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ import zmq import h5py diff --git a/pynta/model/experiment/nanoparticle_tracking/waterfall_worker.py b/pynta/model/experiment/nanoparticle_tracking/waterfall_worker.py index 32e43fa..78b9dff 100644 --- a/pynta/model/experiment/nanoparticle_tracking/waterfall_worker.py +++ b/pynta/model/experiment/nanoparticle_tracking/waterfall_worker.py @@ -10,4 +10,5 @@ def calculate_waterfall(in_queue, out_queue): :type out_queue: :return: :rtype: - """ \ No newline at end of file + """ + pass \ No newline at end of file diff --git a/pynta/model/experiment/publisher.py b/pynta/model/experiment/publisher.py index 12a412d..109c9ce 100644 --- a/pynta/model/experiment/publisher.py +++ b/pynta/model/experiment/publisher.py @@ -11,7 +11,7 @@ publisher and serialized again to be sent. These three steps could be simplify into one if, for example, one assumes that objects where pickled. There is also a possibility of assuming numpy arrays and using a zero-copy strategy. -:copyright: Aquiles Carattino +:copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ diff --git a/pynta/model/experiment/subscriber.py b/pynta/model/experiment/subscriber.py index 2c2ebfd..b7d4e9f 100644 --- a/pynta/model/experiment/subscriber.py +++ b/pynta/model/experiment/subscriber.py @@ -1,6 +1,6 @@ """ - subscriber.py - ============= + Subscriber + ========== Example script on how to run separate processes to process the data coming from a publisher like the one on ``publisher.py``. The first process just grabs the frame and puts it in a Queue. The Queue is then used by another process in order to analyse, process, save, etc. It has to be noted that on UNIX systems, getting diff --git a/pynta/tests/test_controllers.py b/pynta/tests/test_controllers.py index f5155cc..2dc7954 100644 --- a/pynta/tests/test_controllers.py +++ b/pynta/tests/test_controllers.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -""" Test the available controllers for the proper structure. +""" +Test the available controllers for the proper structure. Since controllers are inherently impossible to test on all systems, the only reasonable thing to do is to check for consistency in methods. .. TODO:: Define the minimum structure that makes a controller .. TODO:: Implement tests to assert whether the controllers have the needed methods - :copyright: Aquiles Carattino - :license: AGPLv3, see LICENSE for more details +:copyright: Aquiles Carattino +:license: AGPLv3, see LICENSE for more details """ diff --git a/pynta/tests/test_examples.py b/pynta/tests/test_examples.py index 73b868f..0a65ae3 100644 --- a/pynta/tests/test_examples.py +++ b/pynta/tests/test_examples.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -""" Check whether the examples are able to perform an experiment with dummy devices. +""" +Check whether the examples are able to perform an experiment with dummy devices. .. TODO:: Find a way of testing the UI, is it possible within Travis? may be useful to check what `PyQtGraph `_ is doing for the tests. - :copyright: Aquiles Carattino - :license: AGPLv3, see LICENSE for more details +:copyright: Aquiles Carattino +:license: AGPLv3, see LICENSE for more details """ \ No newline at end of file diff --git a/pynta/tests/test_models.py b/pynta/tests/test_models.py index 7756694..c106bc3 100644 --- a/pynta/tests/test_models.py +++ b/pynta/tests/test_models.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -""" Models can be checked for structure, but also models which depend on dummy devices can be tested +""" +Models can be checked for structure, but also models which depend on dummy devices can be tested thoroughly. .. TODO:: Define minimum structure for each kind of model .. TODO:: Define tests for dummy-based models. - :copyright: Aquiles Carattino - :license: AGPLv3, see LICENSE for more details. +:copyright: Aquiles Carattino +:license: AGPLv3, see LICENSE for more details. """ \ No newline at end of file diff --git a/pynta/tools/worker_thread.py b/pynta/tools/worker_thread.py index 4902600..d7258e5 100644 --- a/pynta/tools/worker_thread.py +++ b/pynta/tools/worker_thread.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ work_thread.py - =========== + ============== Running the acquisition on a separate thread gives a lot of flexibility when designing the program. It comes, however with some potential risks. First, threads are still running on the same Python interpreter. Therefore they do not overcome the GIL limitations. They are able to share memory, which makes them transparent to less experienced @@ -10,7 +10,7 @@ without user intervention for long periods of time. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: AGPLv3, see LICENSE for more details """ from threading import Thread diff --git a/pynta/util/log.py b/pynta/util/log.py index c7da5ff..b06a413 100644 --- a/pynta/util/log.py +++ b/pynta/util/log.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- """ nanoparticle_tracking.util.log.py - ================= + ================================= Adding log capacities to PyNTA - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: AGPLv3, see LICENSE for more details """ import logging diff --git a/pynta/view/GUI/__init__.py b/pynta/view/GUI/__init__.py index e69de29..5d16354 100644 --- a/pynta/view/GUI/__init__.py +++ b/pynta/view/GUI/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +""" + GUI + === + Developing a GUI for an application is a delicated task. On the one hand, developers want to deliver quick solutions. + On the other, users are exposed to programs developed over decades, by large teams, including designers. Therefore, + it is very hard for a single developer to obtain user interfaces as beautiful as the ones you can get from commercial + suppliers. + + However, it is possible to build a collection of tools that can be reused and that can generate a much more + consistent result throughout different applications. The approach PyNTA follows is to develop GUI's using Qt5. There + are different ports of Qt for Python, such as PySide and PyQt. For the time being, PyNTA is based on PyQt5, but this + can change without previous notice. + + To develop a Graphical User Interface (GUI), we have opted to use Qt Designer, and the files are loaded directly to + the class through ``uic.loadUi`` instead of compiling the files into a Python class. On the one hand, this + simplifies the cycle for updating the interface, on the other it does not make explicit which methods are available. + Compiling the files is up to the developer, but the *official* approach is to use only the UI files generated by + Qt Designer. + + .. TODO:: Different wrappers of Qt for Python expose the same API in different ways. We should explore using an + intermediate package to unify the use of Qt. + + :copyright: Aquiles Carattino + :license: GPLv3, see LICENSE for more details +""" \ No newline at end of file diff --git a/pynta/view/GUI/config_widget.py b/pynta/view/GUI/config_widget.py index d7ace15..abfb7f7 100644 --- a/pynta/view/GUI/config_widget.py +++ b/pynta/view/GUI/config_widget.py @@ -6,7 +6,7 @@ .. todo:: Remove the printing to screen of the parameters once the debugging is done. - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ import os diff --git a/pynta/view/GUI/old/Monitor/cameraViewer.py b/pynta/view/GUI/old/Monitor/cameraViewer.py index 9f9b185..d3ae89c 100644 --- a/pynta/view/GUI/old/Monitor/cameraViewer.py +++ b/pynta/view/GUI/old/Monitor/cameraViewer.py @@ -8,7 +8,7 @@ .. todo:: Unify the main viewer with this one. - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ import pyqtgraph as pg diff --git a/pynta/view/GUI/old/Monitor/clearQueueThread.py b/pynta/view/GUI/old/Monitor/clearQueueThread.py index 8d69be4..2a7369f 100644 --- a/pynta/view/GUI/old/Monitor/clearQueueThread.py +++ b/pynta/view/GUI/old/Monitor/clearQueueThread.py @@ -5,7 +5,7 @@ .. warning:: If this Thread is run while the save thread is running there will be data loss without warning. Moreover, of the clear queue destroys the last element of the Queue before the saver arrives to it, te saver will not stop. - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ from pyqtgraph.Qt import QtCore diff --git a/pynta/view/GUI/old/Monitor/crossCut.py b/pynta/view/GUI/old/Monitor/crossCut.py index 3c87312..9bec38c 100644 --- a/pynta/view/GUI/old/Monitor/crossCut.py +++ b/pynta/view/GUI/old/Monitor/crossCut.py @@ -3,7 +3,7 @@ =================================== Window that displays a 1D plot of a cross cut on the main window. - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ import pyqtgraph as pg diff --git a/pynta/view/GUI/old/mainwindow.py b/pynta/view/GUI/old/mainwindow.py index 30d7325..28da0f5 100644 --- a/pynta/view/GUI/old/mainwindow.py +++ b/pynta/view/GUI/old/mainwindow.py @@ -2,7 +2,7 @@ Main Window =========== - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ import os @@ -514,7 +514,7 @@ def startWaterfall(self): self.area.addDock(self.dwaterfall, 'bottom', self.dmainImage) self.dwaterfall.addWidget(self.watWidget) self.show_waterfall = True - Sx, Sy = self.camera.getSize() + Sx, Sy = self.camera.get_size() self.waterfall_data = np.zeros((self._session.GUI['length_waterfall'], Sx)) self.watWidget.img.setImage(np.transpose(self.waterfall_data), autoLevels=False, autoRange=False, autoHistogramRange=False) self.messageWidget.appendLog('i', 'Waterfall opened') @@ -557,8 +557,8 @@ def setROI(self, X, Y): self.messageWidget.appendLog('i', 'Updated roi_y1: %s' % int(Y[0])) self.messageWidget.appendLog('i', 'Updated roi_y2: %s' % int(Y[1])) - Nx, Ny = self.camera.setROI(X, Y) - Sx, Sy = self.camera.getSize() + Nx, Ny = self.camera.set_ROI(X, Y) + Sx, Sy = self.camera.get_size() self.current_width = Sx self.current_height = Sy @@ -796,7 +796,7 @@ def updateSession(self, session): print(self._session) if update_binning: - self.camera.setBinning(session.Camera['binning_x'],session.Camera['binning_y']) + self.camera.set_binning(session.Camera['binning_x'], session.Camera['binning_y']) self.refreshTimer.stop() self.refreshTimer.start(session.GUI['refresh_time']) diff --git a/pynta/view/GUI/old/messageWidget.py b/pynta/view/GUI/old/messageWidget.py index 3de650b..a56dcb0 100644 --- a/pynta/view/GUI/old/messageWidget.py +++ b/pynta/view/GUI/old/messageWidget.py @@ -15,7 +15,7 @@ .. todo:: Change colors for error, info and debug messages. - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ from datetime import datetime from pyqtgraph.Qt import QtGui diff --git a/pynta/view/GUI/old/trajectoryWidget.py b/pynta/view/GUI/old/trajectoryWidget.py index 7be1d61..d689067 100644 --- a/pynta/view/GUI/old/trajectoryWidget.py +++ b/pynta/view/GUI/old/trajectoryWidget.py @@ -5,7 +5,7 @@ .. todo:: adapt this widget for a useful case. - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ import pyqtgraph as pg diff --git a/pynta/view/GUI/old/waterfallWidget.py b/pynta/view/GUI/old/waterfallWidget.py index 107490d..5511ae6 100644 --- a/pynta/view/GUI/old/waterfallWidget.py +++ b/pynta/view/GUI/old/waterfallWidget.py @@ -7,7 +7,7 @@ .. todo:: Unify the 2D displaying of :mod:`camera Main ` and :mod:`camera Viewer `. - .. sectionauthor:: Aquiles Carattino + .. sectionauthor:: Aquiles Carattino """ import pyqtgraph as pg diff --git a/pynta/view/__init__.py b/pynta/view/__init__.py index e69de29..c9a4829 100644 --- a/pynta/view/__init__.py +++ b/pynta/view/__init__.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +""" + View + ==== + In the broad definition, the View should provide all the tools for the interaction between the user and the + computer. These could be both through the command line or through a Graphical User Interface. In practice, however, + command-line interfaces appear naturally once the models and controllers are properly developed. It is also possible + to use Jupyter notebooks to interface with devices and perform experiments. Therefore, the View will be fundamentally + responsible for generating graphical applications. + + :copyright: Aquiles Carattino + :license: GPLv3, see LICENSE for more details +""" \ No newline at end of file diff --git a/pynta/view/subscriber_thread.py b/pynta/view/subscriber_thread.py index 1a50866..bf9947c 100644 --- a/pynta/view/subscriber_thread.py +++ b/pynta/view/subscriber_thread.py @@ -5,7 +5,7 @@ Allows to transform topics coming from a socket (ZMQ) to Qt signals that can be connected to any method, etc. - :copyright: Aquiles Carattino + :copyright: Aquiles Carattino :license: GPLv3, see LICENSE for more details """ import numpy as np diff --git a/setup.py b/setup.py index 6b796a5..165cac6 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ url='https://github.com/nanoepics/pynta', license='GPLv3', author='Aquiles Carattino', - author_email='aquiles@aquicarattino.com', + author_email='aquiles@uetke.com', classifiers=[ 'Intended Audience :: End Users/Desktop', 'Operating System :: Microsoft :: Windows',