Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/Testbase.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
export QT_DEBUG_PLUGINS=1
pip install flake8 pytest pytest-cov pytest-qt pytest-xdist pytest-xvfb setuptools wheel numpy h5py ${{ inputs.qt5 }} toml
pip install pymodaq
pip install -e .
pip install -e .
- name: create local pymodaq folder and setting permissions
run: |
sudo mkdir /etc/.pymodaq
Expand Down
15 changes: 11 additions & 4 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,26 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.x'
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine toml
pip install setuptools wheel twine toml "pymodaq>=4.1.0" pyqt5

- name: create local pymodaq folder and setting permissions
run: |
sudo mkdir /etc/.pymodaq
sudo chmod uo+rw /etc/.pymodaq

- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine check dist/*
twine upload dist/*
10 changes: 7 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ pymodaq_plugins_basler
.. image:: https://github.com/BenediktBurger/pymodaq_plugins_basler/actions/workflows/Test.yml/badge.svg
:target: https://github.com/BenediktBurger/pymodaq_plugins_basler/actions/workflows/Test.yml

Set of PyMoDAQ plugins for cameras by Basler.
Set of PyMoDAQ plugins for cameras by Basler, using the pypylon library. It handles basic camera functionalities (gain, exposure, ROI).
The data is emitted together with spatial axes corresponding either to pixels or to real-world units (um). The pixel size of different camera model is hardcoded in the hardware/basler.py file.
If the camera model is not specified, the pixel size is set to 1 um and can be changed manually by the user in the interface.

The plugin was tested using an acA640-120gm camera. It is compatible with PyMoDAQ 4.4.7.

Authors
=======

* Benedikt Burger
* Romain Geneaux


Instruments
Expand Down Expand Up @@ -54,5 +59,4 @@ Installation instructions
=========================

* You need the manufacturer's driver `Pylon <https://www.baslerweb.com/pylon>`_ for the cameras.
* This package uses the work of a `genergic pylablib camera driver <https://github.com/rgeneaux/pymodaq_plugins_test_pylablib>`_, which is not yet available.
Relevant code is included in this package, so no additional installation needed.

2 changes: 1 addition & 1 deletion plugin_info.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ license = 'MIT'
[plugin-install]
#packages required for your plugin:
packages-required = [
'pymodaq>=4.0.0',
'pymodaq>=4.4.7',
'numpy', # for Basler camera
'pypylon',
]
Expand Down
71 changes: 2 additions & 69 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,4 @@

from pymodaq.resources.setup_plugin import setup
from pathlib import Path

try:
from pymodaq.resources.setup_plugin import setup as _setup
except ModuleNotFoundError:
fallback = True
from setuptools import setup as _setup, find_packages
import toml
else:
fallback = False


def setup():
if not fallback:
return _setup()

config = toml.load('./plugin_info.toml')
SHORT_PLUGIN_NAME = config['plugin-info']['SHORT_PLUGIN_NAME']
PLUGIN_NAME = f"pymodaq_plugins_{SHORT_PLUGIN_NAME}"

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",
], )


return _setup(
version=version,
packages=find_packages(where='./src'),
package_dir={'': 'src'},
include_package_data=True,
entry_points={'pymodaq.plugins': f'{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}',
'pymodaq.pid_models': f"{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}",
'pymodaq.extensions': f"{SHORT_PLUGIN_NAME} = {PLUGIN_NAME}"},
install_requires=['toml', ]+config['plugin-install']['packages-required'],
**setupOpts
)


setup()
setup(Path(__file__).parent)
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@
from pymodaq.utils.daq_utils import ThreadCommand
from pymodaq.control_modules.viewer_utility_classes import main

try:
from pymodaq_plugins_pylablib_camera.daq_viewer_plugins.plugins_2D.daq_2Dviewer_GenericPylablibCamera import DAQ_2DViewer_GenericPylablibCamera
# available here: https://github.com/rgeneaux/pymodaq_plugins_test_pylablib
except ModuleNotFoundError:
# Fall back to the internal version
from pymodaq_plugins_basler.daq_viewer_plugins.plugins_2D.daq_2Dviewer_GenericPylablibCamera import DAQ_2DViewer_GenericPylablibCamera

from pymodaq_plugins_basler.hardware.daq_2Dviewer_GenericPylablibCamera import DAQ_2DViewer_GenericPylablibCamera
from pymodaq_plugins_basler.hardware.basler import DartCamera


Expand All @@ -20,19 +14,14 @@ class DAQ_2DViewer_Basler(DAQ_2DViewer_GenericPylablibCamera):
controller: DartCamera
live_mode_available = True

# Generate a **list** of available cameras.
# Two cases:
# 1) Some pylablib classes have a .list_cameras method, which returns a list of available cameras, so we can just use that
# 2) Other classes have a .get_cameras_number(), which returns the number of connected cameras
# in this case we can define the list as self.camera_list = [*range(number_of_cameras)]

# For Basler, this returns a list of friendly names
camera_list = [cam.GetFriendlyName() for cam in DartCamera.list_cameras()]

# Update the params (nothing to change here)
# Update the params
params = DAQ_2DViewer_GenericPylablibCamera.params + [
{'title': 'Automatic exposure:', 'name': 'auto_exposure', 'type': 'bool', 'value': False},
{'title': 'Gain (dB)', 'name': 'gain', 'type': 'float', 'value': 0, 'limits': [0, 18]},
{'title': 'Gain (dB)', 'name': 'gain', 'type': 'float', 'value': 0},#, 'limits': [0, 18]},
{'title': 'Pixel size (um)', 'name': 'pixel_length', 'type': 'float', 'value': 1, 'default' : 1, 'visible': False},
]
params[next((i for i, item in enumerate(params) if item["name"] == "camera_list"), None)]['limits'] = camera_list # type: ignore

Expand Down Expand Up @@ -70,11 +59,23 @@ def ini_detector(self, controller=None):
self.ini_detector_init(old_controller=controller,
new_controller=self.init_controller())

# Check if pixel length is known
if self.controller.pixel_length is None:
model = self.controller.camera.GetDeviceInfo().GetModelName()
self.emit_status(ThreadCommand('Update_Status', [(f"No pixel length known for camera model '{model}', defaulting to user-chosen one"), 'log']))
self.settings.child('pixel_length').show()

# Check gain mode
if self.controller.raw_gain:
self.settings.child('gain').setOpts(type="int")
self.settings.child('gain').setOpts(title="Gain (raw)")
self.settings.child('gain').setValue(self.controller.gain)

# Get camera name
self.settings.child('camera_info').setValue(self.controller.get_device_info()[1])

# Set exposure time
self.controller.set_exposure(self.settings.child('timing_opts', 'exposure_time').value() / 1000)
self.controller.exposure = self.settings.child('timing_opts', 'exposure_time').value() / 1000

# FPS visibility
self.settings.child('timing_opts', 'fps').setOpts(visible=self.settings.child('timing_opts', 'fps_on').value())
Expand Down Expand Up @@ -106,8 +107,12 @@ def commit_settings(self, param: Parameter) -> None:
if param.name() == "auto_exposure":
self.controller.camera.ExposureAuto.SetValue(
"Continuous" if self.settings['auto_exposure'] else "Off")
elif param.name() == "exposure_time":
self.controller.exposure = param.value()/1000
elif param.name() == "gain":
self.controller.camera.Gain.SetValue(param.value())
self.controller.gain = param.value()
elif param.name() == "pixel_length":
self.controller.pixel_length = param.value()
else:
super().commit_settings(param=param)

Expand All @@ -134,4 +139,4 @@ def callback(self, array) -> None:


if __name__ == '__main__':
main(__file__)
main(__file__, init=False)
Loading
Loading