# This file is a part of pzp-hardware, a library of laboratory hardware support Pieces
# for the puzzlepiece GUI & automation framework. Check out https://pzp-hardware.readthedocs.io
# Licensed under the Apache License 2.0 - https://github.com/jdranczewski/pzp-hardware/blob/main/LICENSE
r"""
:module_title:`Holoeye SLM`
Pieces for interacting with `Holoeye SLMs <https://holoeye.com/products/spatial-light-modulators/>`__
using the `puzzlepiece <https://puzzlepiece.readthedocs.io>`__ framework.
Example usage (see :ref:`getting-started` for more details on using Pieces in general)::
import puzzlepiece as pzp
from pzp_hardware.holoeye import slm
app = pzp.QApp()
puzzle = pzp.Puzzle(debug=False)
puzzle.add_piece("slm", slm.Piece, row=0, column=0)
puzzle.show()
app.exec()
Installation
------------
* Install the SLM SDK. You may have to register your SLM at https://customers.holoeye.com/
to get a download link.
* When you first initialise the Piece, you will be prompted for two directories: the Python
examples directory (``C:\Program Files\HOLOEYE Photonics\SLM Display SDK (Python) v4.1.0\examples``)
and the Python API directory
(``C:\Program Files\HOLOEYE Photonics\SLM Display SDK (Python) v4.1.0\api\python``).
Accepting the defaults should be ok in most cases, and you will be prompted if the directories are
not found. If your SDK is installed in a non-standard location, provide the correct directories
when prompted.
Requirements
------------
.. pzp_requirements:: pzp_hardware.holoeye.slm
Available Pieces
----------------
"""
import puzzlepiece as pzp
from puzzlepiece.extras import hardware_tools as pht
from pzp_hardware.generic.mixins import image_preview
import numpy as np
import os
[docs]
class Piece(image_preview.ImagePreview, pzp.Piece):
"""
Piece for controlling a HOLOEYE SLM. Allows setting any image array to the SLM.
Have a look at :class:`pzp_hardware.generic.patterning.patterns` for a quick way
to display test patterns.
.. image:: ../images/pzp_hardware.holoeye.slm.Piece.png
"""
custom_horizontal = True
action_wrap = 1
def define_params(self):
@pzp.param.connect(self)
def connect():
if self.puzzle.debug:
self["image"].set_value(np.zeros((1200, 1920)))
return 1
self._check_call(self.HEDS.SDK.Init(4,1))
self.slm = self.HEDS.SLM.Init("", True, 0.0)
self._check_err_code(self.slm.errorCode())
self["image"].set_value(np.zeros((1200, 1920)))
return 1
@pzp.param.disconnect(self)
def disconnect():
if self.puzzle.debug:
return 0
self.HEDS.SDK.Close()
del self.slm
return 0
image = pzp.param.array(self, 'image')(None)
prev_handle = None
@image.set_setter(self)
@self._ensure
def image(value):
nonlocal prev_handle
if self.puzzle.debug:
return
converted = value.astype(np.uint8, copy=False)
dataHandle = self._check_call(self.slm.loadPhaseData(converted))
scale = self["scale"].value
if scale != 1.:
self._check_call(dataHandle.setTransformScale(scale))
self._check_call(dataHandle.show())
if prev_handle is not None:
self._check_call(prev_handle.release())
prev_handle = dataHandle
return converted
pzp.param.spinbox(self, "scale", 1., 0)(None)
@pzp.param.spinbox(self, "wavelength", 0.)
@self._ensure
def wavelength(value):
if self.puzzle.debug:
return value
self._check_call(self.slm.setWavelength(value))
@wavelength.set_getter(self)
@self._ensure
def wavelength():
if self.puzzle.debug:
return wavelength.value or 0
# the stock "getWavelength" method doesn't work
return self._check_call(self.HEDS.SDK.libapi.heds_slm_get_wavelength(self.slm._id, self.htypes.HEDSCC_Mono))
@pzp.param.text(self, "correction_file", "")
@self._ensure
def correction_file(value):
window = self.slm.window()
if os.path.exists(value):
self._check_call(window.loadWavefrontCompensationFile(value))
return value
else:
self._check_call(window.clearWavefrontCompensation())
return ""
def setup(self):
pht.add_path_directory(
pht.config(
"holoeye_examples_directory",
default=r"C:\Program Files\HOLOEYE Photonics\SLM Display SDK (Python) v4.1.0\examples",
validator=pht.validator_path_exists
)
)
pht.add_path_directory(
pht.config(
"holoeye_python_directory",
default=r"C:\Program Files\HOLOEYE Photonics\SLM Display SDK (Python) v4.1.0\api\python",
validator=pht.validator_path_exists
)
)
pht.requirements(
{
"HEDS": {
"url": "https://pzp-hardware.readthedocs.io/en/latest/auto/pzp_hardware.holoeye.slm.html#installation"
},
"hedslib": {
"url": "https://pzp-hardware.readthedocs.io/en/latest/auto/pzp_hardware.holoeye.slm.html#installation"
}
}
)
import HEDS
import hedslib.heds_types
self.HEDS = HEDS
self.htypes = hedslib.heds_types
def _check_call(self, returned):
try:
code, *others = returned
self._check_err_code(code)
if len(others) > 1:
return others
else:
return others[0]
except TypeError:
# only one value returned
self._check_err_code(returned)
def _check_err_code(self, code):
if code != self.htypes.HEDSERR_NoError:
raise Exception(f"SLM error: {self.HEDS.SDK.ErrorString(code)}")
@pzp.piece.ensurer
def _ensure(self):
if not self.puzzle.debug and not hasattr(self, "slm"):
raise Exception("SLM not connected")
if __name__ == "__main__":
app = pzp.QApp()
puzzle = pzp.Puzzle(name="Holoeye SLM", debug=pht.debug_prompt())
puzzle.add_piece("slm", Piece, 0, 0)
puzzle.show()
app.exec()