diff --git a/hatch_build.py b/hatch_build.py new file mode 100644 index 0000000..a0208ee --- /dev/null +++ b/hatch_build.py @@ -0,0 +1,10 @@ +from pathlib import Path +from hatchling.metadata.plugin.interface import MetadataHookInterface +from pymodaq_utils.resources.hatch_build_plugins import update_metadata_from_toml + +here = Path(__file__).absolute().parent + + +class PluginInfoTomlHook(MetadataHookInterface): + def update(self, metadata: dict) -> None: + update_metadata_from_toml(metadata, here) diff --git a/plugin_info.toml b/plugin_info.toml deleted file mode 100644 index bf4cbf0..0000000 --- a/plugin_info.toml +++ /dev/null @@ -1,24 +0,0 @@ -## To modify by developper(s) of the plugin - -[plugin-info] -SHORT_PLUGIN_NAME = 'raspberry' #to be modified, for instance daqmx then rename the module name: -# (pymodaq_plugins_raspberry become pymodaq_plugins_daqmx for instance) - -package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_raspberry' #to modify -description = 'Set of instrument plugins to use with a raspberry' - -author = 'Sebastien J. Weber' -author-email = 'sebastien.weber@cnrs.fr' -license = 'MIT' - -[plugin-install] -#packages required for your plugin: -packages-required = ['pymodaq>=4.0.0', 'picamera2'] - -[features] # defines the plugin features contained into this plugin -instruments = true # true if plugin contains instrument classes (else false, notice the lowercase for toml files) -extensions = false # true if plugins contains dashboard extensions -pid_models = false # true if plugins contains pid models -h5exporters = false # true if plugin contains custom h5 file exporters -scanners = false # true if plugin contains custom scan layout (daq_scan extensions) - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2144b51 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,61 @@ +[features] # defines the plugin features contained into this plugin +instruments = true # true if plugin contains instrument classes (else false, notice the lowercase for toml files) +extensions = false # true if plugins contains dashboard extensions +models = false # true if plugins contains pid models +h5exporters = false # true if plugin contains custom h5 file exporters +scanners = false # true if plugin contains custom scan layout (daq_scan extensions) + +[urls] +package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_raspberry' + +[project] +name = "pymodaq_plugins_raspberry" +description = 'Set of instrument plugins to use with a raspberry' +dependencies = [ + "pymodaq>=5.0.0", + 'picamera2', +] + +authors = [ + {name = "Weber Sébastien", email = "sebastien.weber@cnrs.fr"}, +] +maintainers = [ + {name = "Weber Sébastien", email = "sebastien.weber@cnrs.fr"}, +] + +# nottodo: leave everything below as is! + +dynamic = ["version", "urls", "entry-points"] +readme = "README.rst" +license = { file="LICENSE" } +requires-python = ">=3.8" + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering :: Human Machine Interfaces", + "Topic :: Scientific/Engineering :: Visualization", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: User Interfaces", +] + +[build-system] +requires = [ + "hatchling>=1.9.0", + "hatch-vcs", "toml", + "pymodaq_utils>=0.0.6", +] +build-backend = "hatchling.build" + +[tool.hatch.metadata.hooks.custom] + +[tool.hatch.version] +source = "vcs" + diff --git a/setup.py b/setup.py deleted file mode 100644 index c51046c..0000000 --- a/setup.py +++ /dev/null @@ -1,75 +0,0 @@ -from setuptools import setup, find_packages -import toml - -config = toml.load('./plugin_info.toml') -SHORT_PLUGIN_NAME = config['plugin-info']['SHORT_PLUGIN_NAME'] -PLUGIN_NAME = f"pymodaq_plugins_{SHORT_PLUGIN_NAME}" - - -from pathlib import Path - -if not SHORT_PLUGIN_NAME.isidentifier(): - raise ValueError("'SHORT_PLUGIN_NAME = %s' is not a valid python identifier." % SHORT_PLUGIN_NAME) - -version_file = Path(__file__).parent.joinpath(f'src/{PLUGIN_NAME}/resources/VERSION') # new location of the version file -if not version_file.is_file(): - version_file = Path(__file__).parent.joinpath(f'src/{PLUGIN_NAME}/VERSION') - -with open(str(version_file), 'r') as fvers: - version = fvers.read().strip() - - -with open('README.rst') as fd: - long_description = fd.read() - -setupOpts = dict( - name=PLUGIN_NAME, - description=config['plugin-info']['description'], - long_description=long_description, - license=config['plugin-info']['license'], - url=config['plugin-info']['package-url'], - author=config['plugin-info']['author'], - author_email=config['plugin-info']['author-email'], - classifiers=[ - "Programming Language :: Python :: 3", - "Development Status :: 5 - Production/Stable", - "Environment :: Other Environment", - "Intended Audience :: Science/Research", - "Topic :: Scientific/Engineering :: Human Machine Interfaces", - "Topic :: Scientific/Engineering :: Visualization", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development :: User Interfaces", - ], ) - -#instrument -#extension = false # true if plugins contains dashboard extensions -#pid_models = false # true if plugins contains pid models -#h5exporters = false # true if plugin contains custom h5 file exporters -#scans - -entrypoints = {} -if 'features' in config: - if config['features'].get('instruments', False): - entrypoints['pymodaq.instruments'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}' - if config['features'].get('extensions', False): - entrypoints['pymodaq.extensions'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}' - if config['features'].get('pid_models', False): - entrypoints['pymodaq.pid_models'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}' - if config['features'].get('h5exporters', False): - entrypoints['pymodaq.h5exporters'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}' - if config['features'].get('scanners', False): - entrypoints['pymodaq.scanners'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}' -else: - entrypoints['pymodaq.instruments'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}' -entrypoints['pymodaq.plugins'] = f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}' # generic plugin, usefull for the plugin manager -setup( - version=version, - packages=find_packages(where='./src'), - package_dir={'': 'src'}, - include_package_data=True, - entry_points=entrypoints, - install_requires=['toml', ]+config['plugin-install']['packages-required'], - **setupOpts -) diff --git a/src/pymodaq_plugins_raspberry/__init__.py b/src/pymodaq_plugins_raspberry/__init__.py index ca051e5..caf12c7 100644 --- a/src/pymodaq_plugins_raspberry/__init__.py +++ b/src/pymodaq_plugins_raspberry/__init__.py @@ -1,8 +1,11 @@ from pathlib import Path -from pymodaq.utils.logger import set_logger # to be imported by other modules. - from .utils import Config +from pymodaq_utils.utils import get_version, PackageNotFoundError +from pymodaq_utils.logger import set_logger, get_module_name + config = Config() +try: + __version__ = get_version(__package__) +except PackageNotFoundError: + __version__ = '0.0.0dev' -with open(str(Path(__file__).parent.joinpath('resources/VERSION')), 'r') as fvers: - __version__ = fvers.read().strip() diff --git a/src/pymodaq_plugins_raspberry/daq_viewer_plugins/plugins_2D/daq_2Dviewer_PiCamera.py b/src/pymodaq_plugins_raspberry/daq_viewer_plugins/plugins_2D/daq_2Dviewer_PiCamera.py index dc5bcd4..b0d2a4c 100644 --- a/src/pymodaq_plugins_raspberry/daq_viewer_plugins/plugins_2D/daq_2Dviewer_PiCamera.py +++ b/src/pymodaq_plugins_raspberry/daq_viewer_plugins/plugins_2D/daq_2Dviewer_PiCamera.py @@ -1,9 +1,13 @@ import numpy as np +from qtpy import QtWidgets from pymodaq.utils.daq_utils import ThreadCommand from pymodaq.utils.data import DataFromPlugins, Axis, DataToExport from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main from pymodaq.utils.parameter import Parameter +from pymodaq_utils.logger import set_logger, get_module_name + +logger = set_logger(get_module_name(__file__)) from picamera2 import Picamera2 @@ -23,14 +27,15 @@ class DAQ_2DViewer_PiCamera(DAQ_Viewer_base): """ hardware_averaging = True - live_mode_available = True + live_mode_available = False params = comon_parameters + [ - {'title': 'Zoom:', 'name': 'zoom', 'type': 'slide', 'value': 1.0, 'min': 0., 'max': 1., 'subtype': 'linear'}, - {'title': 'Brightness:', 'name': 'brightness', 'type': 'slide', 'value': 50, 'min': 0, 'max': 100, - 'subtype': 'linear', 'int': True}, - {'title': 'Contrast:', 'name': 'contrast', 'type': 'slide', 'value': 0, 'min': -100, 'max': 100, - 'subtype': 'linear', 'int': True}, + {'title': 'Resolution:', 'name': 'resolution', 'type': 'list', 'value': 'low', 'limits': ['low', 'high']}, + #{'title': 'Zoom:', 'name': 'zoom', 'type': 'slide', 'value': 1.0, 'min': 0., 'max': 1., 'subtype': 'linear'}, + #{'title': 'Brightness:', 'name': 'brightness', 'type': 'slide', 'value': 50, 'min': 0, 'max': 100, + # 'subtype': 'linear', 'int': True}, + #{'title': 'Contrast:', 'name': 'contrast', 'type': 'slide', 'value': 0, 'min': -100, 'max': 100, + # 'subtype': 'linear', 'int': True}, ] def ini_attributes(self): @@ -55,8 +60,11 @@ def commit_settings(self, param: Parameter): A given parameter (within detector_settings) whose value has been changed by the user """ # TODO for your custom plugin - if param.name() == "a_parameter_you've_added_in_self.params": - self.controller.your_method_to_apply_this_param_change() + if param.name() == "resolution": + if param.value() == 'low': + self.controller.switch_mode(self.low_res_config) + else: + self.controller.switch_mode(self.high_res_config) #elif ... def ini_detector(self, controller=None): @@ -74,29 +82,21 @@ def ini_detector(self, controller=None): initialized: bool False if initialization failed otherwise True """ - self.ini_detector_init(old_controller=controller, - new_controller=PiCamera2()) - - # config = self.controller.create_preview_configuration() - # self.controller.configure(config) - self.capture_config = self.controller.create_still_configuration() - - print(self.controller.camera_controls) - print(self.controller.camera_properties) - print(self.controller.capture_metadata()) - self.controller.start(show_preview=True) - - # self.controller.resolution = (self.width, self.height) - # # self.camera.resolution = (1648, 928) - # self.controller.framerate = 32 # nombres d'images par secondes - # - # self.controller.rotation = 180 - # - # self.camera.zoom = (0, 0, 1, 1) - # - # # Produit un tableau tridimensionnel RGB à partir d'une capture RGB - # # self.rawCapture = PiRGBArray(self.camera, size=(self.width, self.height)) - # self.rawCapture = PiRGBArray(self.camera, size=(self.width, self.height)) + if self.is_master: + self.controller = Picamera2() + else: + self.controller = controller + + self.low_res_config = self.controller.create_preview_configuration() + self.high_res_config = self.controller.create_still_configuration() + + self.controller.configure(self.low_res_config) + + + logger.info(self.controller.camera_controls) + logger.info(self.controller.camera_properties) + #print(self.controller.capture_metadata()) + self.controller.start() info = "Whatever info you want to log" initialized = True @@ -104,7 +104,7 @@ def ini_detector(self, controller=None): def close(self): """Terminate the communication protocol""" - if self.controller is not None: + if self.controller is not None and self.is_master: self.controller.close() def grab_data(self, Naverage=1, **kwargs): @@ -125,33 +125,34 @@ def grab_data(self, Naverage=1, **kwargs): self.live = kwargs['live'] else: self.live = False + logger.debug(f'live: {self.live}') if 'wait_time' in kwargs: self.wait_time = kwargs['wait_time'] - - + logger.debug(self.wait_time) + + self.grab(Naverage) + + def grab(self, Naverage): for ind in range(Naverage): - if self.live: - array = self.controller.capture_array("main") - else: - array = self.controller.switch_mode_and_capture_array(self.capture_config, "main") + array = self.controller.capture_array("main") + logger.debug(f'array shape: {array.shape}') if len(array.shape) == 2: arrays = [array] elif len(array.shape) == 3: - arrays = [array[..., ind] for ind in range(array.shape[2])] + arrays = [array[..., ind] for ind in range(3)] #alpha is with the last index equal to 4, we may skip it! else: - arrays = [array[..., ind, 0] for ind in range(array.shape[2])] # TODO: check this with alpha + raise ValueError(f'The array shape is {array.shape} and is not handled') if ind == 0: dwa_camera = DataFromPlugins('PiCamera', data=arrays) else: dwa_camera = dwa_camera.average(DataFromPlugins('PiCamera', data=arrays), weight= ind+1) - if self.live: - self.dte_signal_temp.emit(DataToExport('myplugin', data=[dwa_camera])) - else: - self.dte_signal.emit(DataToExport('myplugin', data=[dwa_camera])) + self.dte_signal.emit(DataToExport('myplugin', data=[dwa_camera])) + def stop(self): """Stop the current grab hardware wise if necessary""" + self.live = False return '' diff --git a/src/pymodaq_plugins_raspberry/resources/VERSION b/src/pymodaq_plugins_raspberry/resources/VERSION deleted file mode 100644 index 4e379d2..0000000 --- a/src/pymodaq_plugins_raspberry/resources/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.0.2 diff --git a/src/pymodaq_plugins_raspberry/utils.py b/src/pymodaq_plugins_raspberry/utils.py index 1f225ab..4981b95 100644 --- a/src/pymodaq_plugins_raspberry/utils.py +++ b/src/pymodaq_plugins_raspberry/utils.py @@ -6,7 +6,7 @@ """ from pathlib import Path -from pymodaq.utils.config import BaseConfig, USER +from pymodaq_utils.config import BaseConfig, USER class Config(BaseConfig):