25032024 lightfield pymodaq plugin
parent
fd977cd831
commit
49bd905d9b
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Sebastien Weber <sebastien.weber@cemes.fr>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,3 @@
|
||||
include README.rst
|
||||
include LICENSE
|
||||
graft src
|
@ -0,0 +1,24 @@
|
||||
## To modify by developer(s) of the plugin
|
||||
|
||||
[plugin-info]
|
||||
SHORT_PLUGIN_NAME = 'lightfield' #to be modified, for instance daqmx then rename the module name:
|
||||
# (pymodaq_plugins_template become pymodaq_plugins_daqmx for instance)
|
||||
|
||||
package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_template' #to modify
|
||||
description = 'some word about your plugin'
|
||||
|
||||
author = 'Sébastien Quistrebert'
|
||||
author-email = 'Author email'
|
||||
license = 'MIT'
|
||||
|
||||
[plugin-install]
|
||||
#packages required for your plugin:
|
||||
packages-required = ['pymodaq>=4.1.0']
|
||||
|
||||
[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 or other models (optimisation...)
|
||||
h5exporters = false # true if plugin contains custom h5 file exporters
|
||||
scanners = false # true if plugin contains custom scan layout (daq_scan extensions)
|
||||
|
@ -0,0 +1,4 @@
|
||||
from pymodaq.resources.setup_plugin import setup
|
||||
from pathlib import Path
|
||||
|
||||
setup(Path(__file__).parent)
|
@ -0,0 +1,8 @@
|
||||
from pathlib import Path
|
||||
from pymodaq.utils.logger import set_logger # to be imported by other modules.
|
||||
|
||||
from .utils import Config
|
||||
config = Config()
|
||||
|
||||
with open(str(Path(__file__).parent.joinpath('resources/VERSION')), 'r') as fvers:
|
||||
__version__ = fvers.read().strip()
|
@ -0,0 +1,13 @@
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from .. import set_logger
|
||||
logger = set_logger('move_plugins', add_to_console=False)
|
||||
|
||||
for path in Path(__file__).parent.iterdir():
|
||||
try:
|
||||
if '__init__' not in str(path):
|
||||
importlib.import_module('.' + path.stem, __package__)
|
||||
except Exception as e:
|
||||
logger.warning("{:} plugin couldn't be loaded due to some missing packages or errors: {:}".format(path.stem, str(e)))
|
||||
pass
|
||||
|
@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from ... import set_logger
|
||||
logger = set_logger('viewer0D_plugins', add_to_console=False)
|
||||
|
||||
for path in Path(__file__).parent.iterdir():
|
||||
try:
|
||||
if '__init__' not in str(path):
|
||||
importlib.import_module('.' + path.stem, __package__)
|
||||
except Exception as e:
|
||||
logger.warning("{:} plugin couldn't be loaded due to some missing packages or errors: {:}".format(path.stem, str(e)))
|
||||
pass
|
||||
|
@ -0,0 +1,13 @@
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from ... import set_logger
|
||||
logger = set_logger('viewer1D_plugins', add_to_console=False)
|
||||
|
||||
for path in Path(__file__).parent.iterdir():
|
||||
try:
|
||||
if '__init__' not in str(path):
|
||||
importlib.import_module('.' + path.stem, __package__)
|
||||
except Exception as e:
|
||||
logger.warning("{:} plugin couldn't be loaded due to some missing packages or errors: {:}".format(path.stem, str(e)))
|
||||
pass
|
||||
|
@ -0,0 +1,14 @@
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from ... import set_logger
|
||||
logger = set_logger('viewer2D_plugins', add_to_console=False)
|
||||
|
||||
for path in Path(__file__).parent.iterdir():
|
||||
try:
|
||||
if '__init__' not in str(path):
|
||||
importlib.import_module('.' + path.stem, __package__)
|
||||
except Exception as e:
|
||||
logger.warning("{:} plugin couldn't be loaded due to some missing packages or errors: {:}".format(path.stem, str(e)))
|
||||
pass
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from ... import set_logger
|
||||
logger = set_logger('viewerND_plugins', add_to_console=False)
|
||||
|
||||
for path in Path(__file__).parent.iterdir():
|
||||
try:
|
||||
if '__init__' not in str(path):
|
||||
importlib.import_module('.' + path.stem, __package__)
|
||||
except Exception as e:
|
||||
logger.warning("{:} plugin couldn't be loaded due to some missing packages or errors: {:}".format(path.stem, str(e)))
|
||||
pass
|
||||
|
@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created the 01/06/2023
|
||||
|
||||
@author: Sebastien Weber
|
||||
"""
|
@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created the 01/06/2023
|
||||
|
||||
@author: Sebastien Weber
|
||||
"""
|
@ -0,0 +1,195 @@
|
||||
from pymodaq.utils import gui_utils as gutils
|
||||
from pymodaq.utils import daq_utils as utils
|
||||
from pyqtgraph.parametertree import Parameter, ParameterTree
|
||||
from pymodaq.utils.parameter import pymodaq_ptypes
|
||||
from qtpy import QtWidgets, QtCore
|
||||
|
||||
from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
|
||||
from pymodaq.utils.plotting.data_viewers.viewer2D import Viewer2D
|
||||
|
||||
|
||||
config = utils.load_config()
|
||||
logger = utils.set_logger(utils.get_module_name(__file__))
|
||||
|
||||
EXTENSION_NAME = 'MY_EXTENSION_NAME'
|
||||
CLASS_NAME = 'MyExtension'
|
||||
|
||||
|
||||
class MyExtension(gutils.CustomApp):
|
||||
# list of dicts enabling the settings tree on the user interface
|
||||
params = [
|
||||
{'title': 'Main settings:', 'name': 'main_settings', 'type': 'group', 'children': [
|
||||
{'title': 'Save base path:', 'name': 'base_path', 'type': 'browsepath',
|
||||
'value': config['data_saving']['h5file']['save_path']},
|
||||
{'title': 'File name:', 'name': 'target_filename', 'type': 'str', 'value': "", 'readonly': True},
|
||||
{'title': 'Date:', 'name': 'date', 'type': 'date', 'value': QtCore.QDate.currentDate()},
|
||||
{'title': 'Do something, such as showing data:', 'name': 'do_something', 'type': 'bool', 'value': False},
|
||||
{'title': 'Something done:', 'name': 'something_done', 'type': 'led', 'value': False, 'readonly': True},
|
||||
{'title': 'Infos:', 'name': 'info', 'type': 'text', 'value': ""},
|
||||
{'title': 'push:', 'name': 'push', 'type': 'bool_push', 'value': False}
|
||||
]},
|
||||
{'title': 'Other settings:', 'name': 'other_settings', 'type': 'group', 'children': [
|
||||
{'title': 'List of stuffs:', 'name': 'list_stuff', 'type': 'list', 'value': 'first',
|
||||
'limits': ['first', 'second', 'third'], 'tip': 'choose a stuff from the list'},
|
||||
{'title': 'List of integers:', 'name': 'list_int', 'type': 'list', 'value': 0,
|
||||
'limits': [0, 256, 512], 'tip': 'choose a stuff from this int list'},
|
||||
{'title': 'one integer:', 'name': 'an_integer', 'type': 'int', 'value': 500, },
|
||||
{'title': 'one float:', 'name': 'a_float', 'type': 'float', 'value': 2.7, },
|
||||
]},
|
||||
]
|
||||
|
||||
def __init__(self, dockarea, dashboard):
|
||||
super().__init__(dockarea, dashboard)
|
||||
self.setup_ui()
|
||||
|
||||
def connect_things(self):
|
||||
pass
|
||||
|
||||
def setup_docks(self):
|
||||
"""
|
||||
to be subclassed to setup the docks layout
|
||||
for instance:
|
||||
|
||||
self.docks['ADock'] = gutils.Dock('ADock name)
|
||||
self.dockarea.addDock(self.docks['ADock"])
|
||||
self.docks['AnotherDock'] = gutils.Dock('AnotherDock name)
|
||||
self.dockarea.addDock(self.docks['AnotherDock"], 'bottom', self.docks['ADock"])
|
||||
|
||||
See Also
|
||||
########
|
||||
pyqtgraph.dockarea.Dock
|
||||
"""
|
||||
self.docks['settings'] = gutils.Dock('Settings')
|
||||
self.dockarea.addDock(self.docks['settings'])
|
||||
self.docks['settings'].addWidget(self.settings_tree)
|
||||
|
||||
self.docks['modmanager'] = gutils.Dock('Module Manager')
|
||||
self.dockarea.addDock(self.docks['modmanager'], 'right', self.docks['settings'])
|
||||
self.docks['modmanager'].addWidget(self.modules_manager.settings_tree)
|
||||
|
||||
self.docks['viewer1D'] = gutils.Dock('Viewers')
|
||||
self.dockarea.addDock(self.docks['viewer1D'], 'right', self.docks['modmanager'])
|
||||
|
||||
self.docks['viewer2D'] = gutils.Dock('Viewers')
|
||||
self.dockarea.addDock(self.docks['viewer2D'], 'bottom', self.docks['viewer1D'])
|
||||
|
||||
widg = QtWidgets.QWidget()
|
||||
self.viewer1D = Viewer1D(widg)
|
||||
self.docks['viewer1D'].addWidget(widg)
|
||||
|
||||
widg1 = QtWidgets.QWidget()
|
||||
self.viewer2D = Viewer2D(widg1)
|
||||
self.docks['viewer2D'].addWidget(widg1)
|
||||
|
||||
def setup_menu(self):
|
||||
'''
|
||||
to be subclassed
|
||||
create menu for actions contained into the self.actions_manager, for instance:
|
||||
|
||||
For instance:
|
||||
|
||||
file_menu = self.menubar.addMenu('File')
|
||||
self.actions_manager.affect_to('load', file_menu)
|
||||
self.actions_manager.affect_to('save', file_menu)
|
||||
|
||||
file_menu.addSeparator()
|
||||
self.actions_manager.affect_to('quit', file_menu)
|
||||
'''
|
||||
pass
|
||||
|
||||
def value_changed(self, param):
|
||||
''' to be subclassed for actions to perform when one of the param's value in self.settings is changed
|
||||
|
||||
For instance:
|
||||
if param.name() == 'do_something':
|
||||
if param.value():
|
||||
print('Do something')
|
||||
self.settings.child('main_settings', 'something_done').setValue(False)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
param: (Parameter) the parameter whose value just changed
|
||||
'''
|
||||
if param.name() == 'do_something':
|
||||
if param.value():
|
||||
self.modules_manager.det_done_signal.connect(self.show_data)
|
||||
else:
|
||||
self.modules_manager.det_done_signal.disconnect()
|
||||
|
||||
def param_deleted(self, param):
|
||||
''' to be subclassed for actions to perform when one of the param in self.settings has been deleted
|
||||
|
||||
Parameters
|
||||
----------
|
||||
param: (Parameter) the parameter that has been deleted
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def child_added(self, param):
|
||||
''' to be subclassed for actions to perform when a param has been added in self.settings
|
||||
|
||||
Parameters
|
||||
----------
|
||||
param: (Parameter) the parameter that has been deleted
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def setup_actions(self):
|
||||
pass
|
||||
|
||||
def show_data(self, data_all):
|
||||
data1D = []
|
||||
data2D = []
|
||||
labels1D = []
|
||||
labels2D = []
|
||||
dims = ['data1D', 'data2D']
|
||||
for det in data_all:
|
||||
for dim in dims:
|
||||
if len(data_all[det][dim]) != 0:
|
||||
for channel in data_all[det][dim]:
|
||||
if dim == 'data1D':
|
||||
labels1D.append(channel)
|
||||
data1D.append(data_all[det][dim][channel]['data'])
|
||||
else:
|
||||
labels2D.append(channel)
|
||||
data2D.append(data_all[det][dim][channel]['data'])
|
||||
self.viewer1D.show_data(data1D)
|
||||
self.viewer2D.setImage(*data2D[:min(3, len(data2D))])
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
import sys
|
||||
from pymodaq.dashboard import DashBoard
|
||||
from pathlib import Path
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
mainwindow = QtWidgets.QMainWindow()
|
||||
dockarea = gutils.DockArea()
|
||||
mainwindow.setCentralWidget(dockarea)
|
||||
|
||||
# init the dashboard
|
||||
mainwindow_dash = QtWidgets.QMainWindow()
|
||||
area_dash = gutils.DockArea()
|
||||
mainwindow_dash.setCentralWidget(area_dash)
|
||||
dashboard = DashBoard(area_dash)
|
||||
file = Path(utils.get_set_preset_path()).joinpath(f"{config['presets']['default_preset_for_scan']}.xml")
|
||||
if file.exists():
|
||||
dashboard.set_preset_mode(file)
|
||||
else:
|
||||
msgBox = QtWidgets.QMessageBox()
|
||||
msgBox.setText(f"The default file specified in the configuration file does not exists!\n"
|
||||
f"{file}\n"
|
||||
f"Impossible to load the DAQ_Scan Module")
|
||||
msgBox.setStandardButtons(msgBox.Ok)
|
||||
ret = msgBox.exec()
|
||||
|
||||
prog = MyExtension(dockarea, dashboard)
|
||||
|
||||
mainwindow.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
@ -0,0 +1,115 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Feb 5 17:09:01 2024
|
||||
Sébastien Quistrebert
|
||||
"""
|
||||
|
||||
import pyqtgraph as pg
|
||||
|
||||
# Import the .NET class library
|
||||
import clr, ctypes
|
||||
|
||||
# Import python sys module
|
||||
import sys, os
|
||||
|
||||
# numpy import
|
||||
import numpy as np
|
||||
|
||||
# Import c compatible List and String
|
||||
from System import String
|
||||
from System.Collections.Generic import List
|
||||
from System.Runtime.InteropServices import Marshal
|
||||
from System.Runtime.InteropServices import GCHandle, GCHandleType
|
||||
|
||||
# Add needed dll references
|
||||
sys.path.append(os.environ['LIGHTFIELD_ROOT'])
|
||||
sys.path.append(os.environ['LIGHTFIELD_ROOT']+"\\AddInViews")
|
||||
clr.AddReference('PrincetonInstruments.LightFieldViewV5')
|
||||
clr.AddReference('PrincetonInstruments.LightField.AutomationV5')
|
||||
clr.AddReference('PrincetonInstruments.LightFieldAddInSupportServices')
|
||||
|
||||
# PI imports
|
||||
from PrincetonInstruments.LightField.Automation import *
|
||||
from PrincetonInstruments.LightField.AddIns import *
|
||||
|
||||
|
||||
class PILF():
|
||||
|
||||
def __init__(self):
|
||||
self._auto = Automation(True, List[String]())
|
||||
|
||||
# Get LightField Application object
|
||||
self._application = self._auto.LightFieldApplication
|
||||
|
||||
# Get experiment object
|
||||
self._experiment = self._application.Experiment
|
||||
|
||||
#Close the shutter for security
|
||||
self._experiment.SetValue(CameraSettings.ShutterTimingMode, 'AlwaysClosed')
|
||||
self._auto.LightFieldClosing += self.lightField_closing
|
||||
|
||||
def err_init(self):
|
||||
camera = None
|
||||
err = False
|
||||
# Find connected device
|
||||
for device in self._experiment.ExperimentDevices:
|
||||
if (device.Type == DeviceType.Camera and self._experiment.IsReadyToRun):
|
||||
camera = device
|
||||
|
||||
if (camera == None):
|
||||
err = "No camera detected."
|
||||
|
||||
if (not self._experiment.IsReadyToRun):
|
||||
err = "Experiment not ready"
|
||||
|
||||
return err
|
||||
|
||||
def capture_spectra(self):
|
||||
if self._experiment.get_Name() == 'LABVIEW_20180912_64lignes_3kHz_1zone' and self._experiment.IsReadyToRun:
|
||||
frames = self._experiment.GetValue(ExperimentSettings.AcquisitionFramesToStore)
|
||||
image_array = np.zeros((1024,frames))
|
||||
dataset = self._experiment.Capture(frames)
|
||||
# Stop processing if we do not have all frames
|
||||
if (dataset.Frames != frames):
|
||||
# Clean up the image data set
|
||||
dataset.Dispose()
|
||||
raise Exception("Frames are not equal.")
|
||||
image_frame = dataset.GetFrame(0, frames - 1)
|
||||
image_array = np.frombuffer(dataset.GetDataBuffer(), dtype = 'uint16').reshape((image_frame.Width, frames), order = 'F')
|
||||
return image_array
|
||||
|
||||
def lightField_closing(self, sender, event_args):
|
||||
#Close the shutter for security
|
||||
self._experiment.SetValue(CameraSettings.ShutterTimingMode, 'AlwaysClosed')
|
||||
self.unhook_events()
|
||||
self.close()
|
||||
|
||||
def open_shutter(self, openShutter = True):
|
||||
if openShutter:
|
||||
self._experiment.SetValue(CameraSettings.ShutterTimingMode, 'AlwaysOpen')
|
||||
else:
|
||||
self._experiment.SetValue(CameraSettings.ShutterTimingMode, 'AlwaysClosed')
|
||||
|
||||
def setNframes(self, Nframes = 1000):
|
||||
self._experiment.SetValue(ExperimentSettings.AcquisitionFramesToStore, str(Nframes))
|
||||
|
||||
def unhook_events(self):
|
||||
# Unhook the eventhandler for IsReadyToRunChanged
|
||||
# Will be called upon exiting
|
||||
self._auto.LightFieldClosing -= self.lightField_closing
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
self._auto.Dispose()
|
||||
|
||||
def get_x_axis(self):
|
||||
return np.arange(1024)
|
||||
|
||||
if __name__ == "__main__":
|
||||
pilf = PILF()
|
||||
pilf._experiment.SetValue(CameraSettings.ShutterTimingMode, 'AlwaysOpen')
|
||||
image_array = pilf.capture_spectra()
|
||||
pilf._experiment.SetValue(CameraSettings.ShutterTimingMode, 'AlwaysClosed')
|
||||
pilf.lightField_closing(1,1)
|
@ -0,0 +1,90 @@
|
||||
from pymodaq.extensions.pid.utils import PIDModelGeneric, OutputToActuator, InputFromDetector, main
|
||||
from pymodaq.utils.data import DataToExport
|
||||
from typing import List
|
||||
|
||||
|
||||
def some_function_to_convert_the_pid_outputs(outputs: List[float], dt: float, stab=True):
|
||||
""" Should be replaced here or in the model class to process the outputs """
|
||||
return outputs
|
||||
|
||||
|
||||
def some_function_to_convert_the_data(measurements: DataToExport):
|
||||
""" Should be replaced here or in the model class to process the measurement """
|
||||
a = 0
|
||||
b = 1
|
||||
return [a, b]
|
||||
|
||||
|
||||
class PIDModelTemplate(PIDModelGeneric):
|
||||
limits = dict(max=dict(state=False, value=100),
|
||||
min=dict(state=False, value=-100),)
|
||||
konstants = dict(kp=0.1, ki=0.000, kd=0.0000)
|
||||
|
||||
Nsetpoints = 2 # number of setpoints
|
||||
setpoint_ini = [128, 128] # number and values of initial setpoints
|
||||
setpoints_names = ['Xaxis', 'Yaxis'] # number and names of setpoints
|
||||
|
||||
actuators_name = ["Xpiezo", "Ypiezo"] # names of actuator's control modules involved in the PID
|
||||
detectors_name = ['Camera'] # names of detector's control modules involved in the PID
|
||||
|
||||
params = [] # list of dict to initialize specific Parameters
|
||||
|
||||
def __init__(self, pid_controller):
|
||||
super().__init__(pid_controller)
|
||||
|
||||
def update_settings(self, param):
|
||||
"""
|
||||
Get a parameter instance whose value has been modified by a user on the UI
|
||||
Parameters
|
||||
----------
|
||||
param: (Parameter) instance of Parameter object
|
||||
"""
|
||||
if param.name() == '':
|
||||
pass
|
||||
|
||||
def ini_model(self):
|
||||
super().ini_model()
|
||||
|
||||
# add here other specifics initialization if needed
|
||||
|
||||
def convert_input(self, measurements: DataToExport):
|
||||
"""
|
||||
Convert the measurements in the units to be fed to the PID (same dimensionality as the setpoint)
|
||||
Parameters
|
||||
----------
|
||||
measurements: DataToExport
|
||||
Data from the declared detectors from which the model extract a value of the same units as the setpoint
|
||||
|
||||
Returns
|
||||
-------
|
||||
InputFromDetector: the converted input in the setpoints units
|
||||
|
||||
"""
|
||||
|
||||
x, y = some_function_to_convert_the_data(measurements)
|
||||
return InputFromDetector([y, x])
|
||||
|
||||
def convert_output(self, outputs: List[float], dt: float, stab=True):
|
||||
"""
|
||||
Convert the output of the PID in units to be fed into the actuator
|
||||
Parameters
|
||||
----------
|
||||
outputs: List of float
|
||||
output value from the PID from which the model extract a value of the same units as the actuator
|
||||
dt: float
|
||||
Ellapsed time since the last call to this function
|
||||
stab: bool
|
||||
|
||||
Returns
|
||||
-------
|
||||
OutputToActuator: the converted output
|
||||
|
||||
"""
|
||||
outputs = some_function_to_convert_the_pid_outputs(outputs, dt, stab)
|
||||
return OutputToActuator(mode='rel', values=outputs)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main("BeamSteeringMockNoModel.xml") # some preset configured with the right actuators and detectors
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created the 01/06/2023
|
||||
|
||||
@author: Sebastien Weber
|
||||
"""
|
@ -0,0 +1 @@
|
||||
0.0.1
|
@ -0,0 +1,2 @@
|
||||
#this is the configuration file of the plugin
|
||||
|
@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created the 01/06/2023
|
||||
|
||||
@author: Sebastien Weber
|
||||
"""
|
@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created the 31/08/2023
|
||||
|
||||
@author: Sebastien Weber
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
from pymodaq.utils.config import BaseConfig, USER
|
||||
|
||||
|
||||
class Config(BaseConfig):
|
||||
"""Main class to deal with configuration values for this plugin"""
|
||||
config_template_path = Path(__file__).parent.joinpath('resources/config_template.toml')
|
||||
config_name = f"config_{__package__.split('pymodaq_plugins_')[1]}"
|
@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created the 17/10/2023
|
||||
|
||||
@author: Sebastien Weber
|
||||
"""
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
import importlib
|
||||
import pkgutil
|
||||
|
||||
|
||||
MANDATORY_MOVE_METHODS = ['ini_attributes', 'get_actuator_value', 'close', 'commit_settings',
|
||||
'ini_stage', 'move_abs', 'move_home', 'move_rel', 'stop_motion']
|
||||
MANDATORY_VIEWER_METHODS = ['ini_attributes', 'grab_data', 'close', 'commit_settings',
|
||||
'ini_detector', ]
|
||||
|
||||
|
||||
def get_package_name():
|
||||
here = Path(__file__).parent
|
||||
package_name = here.parent.stem
|
||||
return package_name
|
||||
|
||||
|
||||
def get_move_plugins():
|
||||
pkg_name = get_package_name()
|
||||
move_mod = importlib.import_module(f'{pkg_name}.daq_move_plugins')
|
||||
|
||||
plugin_list = [mod for mod in [mod[1] for mod in
|
||||
pkgutil.iter_modules([str(move_mod.path.parent)])]
|
||||
if 'daq_move_' in mod]
|
||||
return plugin_list, move_mod
|
||||
|
||||
|
||||
def get_viewer_plugins(dim='0D'):
|
||||
pkg_name = get_package_name()
|
||||
viewer_mod = importlib.import_module(f'{pkg_name}.daq_viewer_plugins.plugins_{dim}')
|
||||
|
||||
plugin_list = [mod for mod in [mod[1] for mod in
|
||||
pkgutil.iter_modules([str(viewer_mod.path.parent)])]
|
||||
if f'daq_{dim}viewer_' in mod]
|
||||
return plugin_list, viewer_mod
|
||||
|
||||
|
||||
def test_package_name_ok():
|
||||
assert 'pymodaq_plugins_' in get_package_name()[0:16]
|
||||
|
||||
|
||||
def test_imports():
|
||||
pkg_name = get_package_name()
|
||||
mod = importlib.import_module(pkg_name)
|
||||
assert hasattr(mod, 'config')
|
||||
assert hasattr(mod, '__version__')
|
||||
move_mod = importlib.import_module(f'{pkg_name}', 'daq_move_plugins')
|
||||
importlib.import_module(f'{pkg_name}', 'daq_viewer_plugins')
|
||||
importlib.import_module(f'{pkg_name}', 'extensions')
|
||||
importlib.import_module(f'{pkg_name}', 'models')
|
||||
importlib.import_module(f'{pkg_name}.daq_viewer_plugins', 'plugins_0D')
|
||||
importlib.import_module(f'{pkg_name}.daq_viewer_plugins', 'plugins_1D')
|
||||
importlib.import_module(f'{pkg_name}.daq_viewer_plugins', 'plugins_2D')
|
||||
importlib.import_module(f'{pkg_name}.daq_viewer_plugins', 'plugins_ND')
|
||||
|
||||
|
||||
def test_move_inst_plugins_name():
|
||||
plugin_list, move_mod = get_move_plugins()
|
||||
for plug in plugin_list:
|
||||
name = plug.split('daq_move_')[1]
|
||||
assert hasattr(getattr(move_mod, plug), f'DAQ_Move_{name}')
|
||||
|
||||
|
||||
def test_move_has_mandatory_methods():
|
||||
plugin_list, move_mod = get_move_plugins()
|
||||
for plug in plugin_list:
|
||||
name = plug.split('daq_move_')[1]
|
||||
klass = getattr(getattr(move_mod, plug), f'DAQ_Move_{name}')
|
||||
for meth in MANDATORY_MOVE_METHODS:
|
||||
assert hasattr(klass, meth)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dim', ('0D', '1D', '2D', 'ND'))
|
||||
def test_viewer_has_mandatory_methods(dim):
|
||||
plugin_list, mod = get_viewer_plugins(dim)
|
||||
for plug in plugin_list:
|
||||
name = plug.split(f'daq_{dim}viewer_')[1]
|
||||
try:
|
||||
module = importlib.import_module(f'.{plug}', mod.__package__)
|
||||
except Exception:
|
||||
break
|
||||
klass = getattr(module, f'DAQ_{dim}Viewer_{name}')
|
||||
for meth in MANDATORY_VIEWER_METHODS:
|
||||
assert hasattr(klass, meth)
|
Loading…
Reference in New Issue