deapi 5.3b3__tar.gz → 5.3b4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. {deapi-5.3b3 → deapi-5.3b4}/PKG-INFO +1 -1
  2. {deapi-5.3b3 → deapi-5.3b4}/deapi/client.py +192 -65
  3. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/01_fps.py +0 -3
  4. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/02_hwRoisize.py +0 -3
  5. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/03_hwBinning.py +0 -3
  6. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/04_swBinning.py +0 -3
  7. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/05_swhwBinning.py +0 -3
  8. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/06_patternPixel.py +0 -3
  9. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/07_reference.py +0 -3
  10. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/08_virtmask.py +0 -3
  11. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/09_scanRoi.py +0 -3
  12. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/10_imageStatistics.py +16 -15
  13. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/func.py +2 -0
  14. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_client.py +0 -9
  15. {deapi-5.3b3 → deapi-5.3b4}/deapi.egg-info/PKG-INFO +1 -1
  16. {deapi-5.3b3 → deapi-5.3b4}/pyproject.toml +1 -1
  17. {deapi-5.3b3 → deapi-5.3b4}/.github/workflows/build.yaml +0 -0
  18. {deapi-5.3b3 → deapi-5.3b4}/.github/workflows/documentation.yaml +0 -0
  19. {deapi-5.3b3 → deapi-5.3b4}/.github/workflows/publish.yaml +0 -0
  20. {deapi-5.3b3 → deapi-5.3b4}/.github/workflows/test-publish.yaml +0 -0
  21. {deapi-5.3b3 → deapi-5.3b4}/.pre-commit-config.yaml +0 -0
  22. {deapi-5.3b3 → deapi-5.3b4}/CHANGES.rst +0 -0
  23. {deapi-5.3b3 → deapi-5.3b4}/LICENSE +0 -0
  24. {deapi-5.3b3 → deapi-5.3b4}/README.md +0 -0
  25. {deapi-5.3b3 → deapi-5.3b4}/deapi/Guide to use mamba.pdf +0 -0
  26. {deapi-5.3b3 → deapi-5.3b4}/deapi/__init__.py +0 -0
  27. {deapi-5.3b3 → deapi-5.3b4}/deapi/buffer_protocols/__init__.py +0 -0
  28. {deapi-5.3b3 → deapi-5.3b4}/deapi/buffer_protocols/pb_2_3_0.py +0 -0
  29. {deapi-5.3b3 → deapi-5.3b4}/deapi/buffer_protocols/pb_3_11_4.py +0 -0
  30. {deapi-5.3b3 → deapi-5.3b4}/deapi/buffer_protocols/pb_3_19_3.py +0 -0
  31. {deapi-5.3b3 → deapi-5.3b4}/deapi/buffer_protocols/pb_3_23_3.py +0 -0
  32. {deapi-5.3b3 → deapi-5.3b4}/deapi/buffer_protocols/pb_3_6_1.py +0 -0
  33. {deapi-5.3b3 → deapi-5.3b4}/deapi/conf.py +0 -0
  34. {deapi-5.3b3 → deapi-5.3b4}/deapi/data_types.py +0 -0
  35. {deapi-5.3b3 → deapi-5.3b4}/deapi/fake_data/__init__.py +0 -0
  36. {deapi-5.3b3 → deapi-5.3b4}/deapi/fake_data/base_fake_data.py +0 -0
  37. {deapi-5.3b3 → deapi-5.3b4}/deapi/fake_data/grains.py +0 -0
  38. {deapi-5.3b3 → deapi-5.3b4}/deapi/index.rst +0 -0
  39. {deapi-5.3b3 → deapi-5.3b4}/deapi/prop_dump.json +0 -0
  40. {deapi-5.3b3 → deapi-5.3b4}/deapi/python_3_instruction.txt +0 -0
  41. {deapi-5.3b3 → deapi-5.3b4}/deapi/release_notes.txt +0 -0
  42. {deapi-5.3b3 → deapi-5.3b4}/deapi/service/de_service.py +0 -0
  43. {deapi-5.3b3 → deapi-5.3b4}/deapi/simulated_server/__init__.py +0 -0
  44. {deapi-5.3b3 → deapi-5.3b4}/deapi/simulated_server/fake_server.py +0 -0
  45. {deapi-5.3b3 → deapi-5.3b4}/deapi/simulated_server/initialize_server.py +0 -0
  46. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/__init__.py +0 -0
  47. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/conftest.py +0 -0
  48. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/__init__.py +0 -0
  49. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/propertyName.py +0 -0
  50. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/original_tests/test_legacy.py +0 -0
  51. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/speed_tests/__init__.py +0 -0
  52. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/speed_tests/test_internal_file_saving.py +0 -0
  53. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/speed_tests/test_movie_buffer_transfer.py +0 -0
  54. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_binning_rois/__init__.py +0 -0
  55. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_binning_rois/test_rois.py +0 -0
  56. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_examples.py +0 -0
  57. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_fake_server/__init__.py +0 -0
  58. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_fake_server/test_server.py +0 -0
  59. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_file_saving/__init__.py +0 -0
  60. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_file_saving/test_file_loading_libertem.py +0 -0
  61. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_file_saving/test_file_loading_rsciio.py +0 -0
  62. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_file_saving/test_h5ebsd.py +0 -0
  63. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_file_saving/test_scan_pattern_saving.py +0 -0
  64. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_insitu/__init__.py +0 -0
  65. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_insitu/test_start_stop.py +0 -0
  66. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_utils/__init__.py +0 -0
  67. {deapi-5.3b3 → deapi-5.3b4}/deapi/tests/test_utils/test_utils.py +0 -0
  68. {deapi-5.3b3 → deapi-5.3b4}/deapi/utils.py +0 -0
  69. {deapi-5.3b3 → deapi-5.3b4}/deapi/version.py +0 -0
  70. {deapi-5.3b3 → deapi-5.3b4}/deapi/wrappers.py +0 -0
  71. {deapi-5.3b3 → deapi-5.3b4}/deapi.egg-info/SOURCES.txt +0 -0
  72. {deapi-5.3b3 → deapi-5.3b4}/deapi.egg-info/dependency_links.txt +0 -0
  73. {deapi-5.3b3 → deapi-5.3b4}/deapi.egg-info/entry_points.txt +0 -0
  74. {deapi-5.3b3 → deapi-5.3b4}/deapi.egg-info/requires.txt +0 -0
  75. {deapi-5.3b3 → deapi-5.3b4}/deapi.egg-info/top_level.txt +0 -0
  76. {deapi-5.3b3 → deapi-5.3b4}/doc/Makefile +0 -0
  77. {deapi-5.3b3 → deapi-5.3b4}/doc/_static/de_api_icon.svg +0 -0
  78. {deapi-5.3b3 → deapi-5.3b4}/doc/_templates/autosummary/base.rst +0 -0
  79. {deapi-5.3b3 → deapi-5.3b4}/doc/_templates/custom-attribute-template.rst +0 -0
  80. {deapi-5.3b3 → deapi-5.3b4}/doc/_templates/custom-class-template.rst +0 -0
  81. {deapi-5.3b3 → deapi-5.3b4}/doc/_templates/custom-function-template.rst +0 -0
  82. {deapi-5.3b3 → deapi-5.3b4}/doc/_templates/custom-module-template.rst +0 -0
  83. {deapi-5.3b3 → deapi-5.3b4}/doc/_templates/custrom-method-template.rst +0 -0
  84. {deapi-5.3b3 → deapi-5.3b4}/doc/changes.rst +0 -0
  85. {deapi-5.3b3 → deapi-5.3b4}/doc/conf.py +0 -0
  86. {deapi-5.3b3 → deapi-5.3b4}/doc/help/dev_guide.rst +0 -0
  87. {deapi-5.3b3 → deapi-5.3b4}/doc/help/help.rst +0 -0
  88. {deapi-5.3b3 → deapi-5.3b4}/doc/help/index.rst +0 -0
  89. {deapi-5.3b3 → deapi-5.3b4}/doc/help/pyDEServer.rst +0 -0
  90. {deapi-5.3b3 → deapi-5.3b4}/doc/index.rst +0 -0
  91. {deapi-5.3b3 → deapi-5.3b4}/doc/intro.rst +0 -0
  92. {deapi-5.3b3 → deapi-5.3b4}/doc/make.bat +0 -0
  93. {deapi-5.3b3 → deapi-5.3b4}/doc/reference/index.rst +0 -0
  94. {deapi-5.3b3 → deapi-5.3b4}/examples/README.rst +0 -0
  95. {deapi-5.3b3 → deapi-5.3b4}/examples/live_imaging/README.rst +0 -0
  96. {deapi-5.3b3 → deapi-5.3b4}/examples/live_imaging/bright_spot_intensity.py +0 -0
  97. {deapi-5.3b3 → deapi-5.3b4}/examples/live_imaging/taking_an_image_every_minute.py +0 -0
  98. {deapi-5.3b3 → deapi-5.3b4}/examples/live_imaging/viewing_the_sensor.py +0 -0
  99. {deapi-5.3b3 → deapi-5.3b4}/examples/live_imaging/viewing_the_sensor_tem.py +0 -0
  100. {deapi-5.3b3 → deapi-5.3b4}/examples/references/README.rst +0 -0
  101. {deapi-5.3b3 → deapi-5.3b4}/examples/setting_parameters/README.rst +0 -0
  102. {deapi-5.3b3 → deapi-5.3b4}/examples/setting_parameters/setting_up_stem.py +0 -0
  103. {deapi-5.3b3 → deapi-5.3b4}/examples/virtual_imaging/README.rst +0 -0
  104. {deapi-5.3b3 → deapi-5.3b4}/examples/virtual_imaging/setting_virtual_masks.py +0 -0
  105. {deapi-5.3b3 → deapi-5.3b4}/examples/virtual_imaging/vdf_vbf.py +0 -0
  106. {deapi-5.3b3 → deapi-5.3b4}/examples/visualization/README.rst +0 -0
  107. {deapi-5.3b3 → deapi-5.3b4}/examples/visualization/creating_a_simple_dashboard.py +0 -0
  108. {deapi-5.3b3 → deapi-5.3b4}/examples/visualization/using_get_result.py +0 -0
  109. {deapi-5.3b3 → deapi-5.3b4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deapi
3
- Version: 5.3b3
3
+ Version: 5.3b4
4
4
  Summary: API for DE Server
5
5
  Author: Direct Electron
6
6
  Project-URL: Bug Reports, https://github.com/directelectron/deapi/issues
@@ -15,6 +15,9 @@ import mmap
15
15
  from datetime import datetime
16
16
  from time import sleep
17
17
  import re
18
+ import win32event
19
+ import win32api
20
+ import threading
18
21
  from typing import List, Union, Tuple
19
22
 
20
23
  import numpy as np
@@ -80,7 +83,7 @@ class Client:
80
83
  return
81
84
 
82
85
  def __str__(self):
83
- return f"Client(host={self.host}, port={self.port}, camera={self.get_current_camera()})"
86
+ return f"Client(host={self.host}, port={self.port}, camera={self.camera})"
84
87
 
85
88
  def _ipython_key_completions_(self):
86
89
  return self.list_properties()
@@ -91,12 +94,12 @@ class Client:
91
94
  <tr>
92
95
  <th>Host</th>
93
96
  <th>Port</th>
94
- <th>Current Camera</th>
97
+ <th>Camera</th>
95
98
  </tr>
96
99
  <tr>
97
100
  <td>{self.host}</td>
98
101
  <td>{self.port}</td>
99
- <td>{self.currCamera}</td>
102
+ <td>{self.camera}</td>
100
103
  </tr>
101
104
  </table>
102
105
  <details>
@@ -176,12 +179,13 @@ class Client:
176
179
  self.socket.setblocking(False)
177
180
  self.socket.settimeout(2)
178
181
 
179
- self.cameras = self.__getStrings(self.LIST_CAMERAS)
180
- if logLevel == logging.DEBUG:
181
- log.debug("Available cameras: %s", self.cameras)
182
- self.currCamera = self.cameras[0]
182
+ command = self._addSingleCommand(self.LIST_CAMERAS, None)
183
+ response = self._sendCommand(command)
184
+ if response != False:
185
+ self.camera = self.__getParameters(response.acknowledge[0])[0]
186
+
183
187
  if logLevel == logging.DEBUG:
184
- log.debug("Current camera: %s", self.currCamera)
188
+ log.debug("Camera: %s", self.camera)
185
189
 
186
190
  self.connected = True
187
191
  self.host = host
@@ -259,9 +263,12 @@ class Client:
259
263
 
260
264
  def list_cameras(self) -> List[str]:
261
265
  """
266
+ Deprecated function.
262
267
  List the available cameras on the server.
263
268
  """
264
- return self.cameras
269
+ log.warning("list_cameras is deprecated.")
270
+
271
+ return [self.camera]
265
272
 
266
273
  def get_virtual_mask(self, index):
267
274
  mask_name = f"virtual_mask{index}"
@@ -275,35 +282,29 @@ class Client:
275
282
  _,
276
283
  ) = self.get_result(mask_name, DataType.DE8u, attributes=a)
277
284
  return res
278
-
285
+
279
286
  def get_current_camera(self) -> str:
280
287
  """
281
288
  Get the current camera on the server.
282
289
  """
283
- if self.currCamera is None:
290
+ if self.camera == "":
284
291
  return "No current camera"
285
292
  else:
286
- return self.currCamera
287
-
293
+ return self.camera
294
+
288
295
  @write_only
289
296
  def set_current_camera(self, camera_name: str = None):
290
297
  """
298
+ Deprecated function.
291
299
  Set the current camera on the server.
292
300
  """
293
- if camera_name is None:
294
- return False
301
+ log.warning("set_current_camera is deprecated.")
295
302
 
296
- self.currCamera = camera_name
297
-
298
- if logLevel == logging.DEBUG:
299
- log.debug("current camera: %s", camera_name)
300
-
301
- self.refreshProperties = True
302
303
  return True
303
304
 
304
305
  def list_properties(self, options=None, search=None):
305
306
  """
306
- Get a list of property names from the current camera on DE-Server
307
+ Get a list of property names from the camera on DE-Server
307
308
 
308
309
  Parameters
309
310
  ----------
@@ -322,7 +323,7 @@ class Client:
322
323
 
323
324
  def list_registers(self, options=None, search=None):
324
325
  """
325
- Get a list of register names from the current camera on DE-Server
326
+ Get a list of register names from the camera on DE-Server
326
327
  for each register, it contains attributes: name of the register, address, Read only, value of the register
327
328
 
328
329
  Parameters
@@ -345,7 +346,7 @@ class Client:
345
346
  )
346
347
  def get_property_spec(self, property_name: str):
347
348
  """
348
- Get a list of allowed values for a property of the current camera on DE-Server
349
+ Get a list of allowed values for a property of the camera on DE-Server
349
350
  Deprecated since DE-MC 2.7.4
350
351
 
351
352
  Parameters
@@ -410,7 +411,7 @@ class Client:
410
411
  )
411
412
  def get_property_specifications(self, property_name):
412
413
  """
413
- Get a list of allowed values for a property of the current camera on DE-Server
414
+ Get a list of allowed values for a property of the camera on DE-Server
414
415
  Only works for DE-MC version greater or equal to 2.7.4
415
416
 
416
417
  Parameters
@@ -420,10 +421,10 @@ class Client:
420
421
  """
421
422
  t0 = self.GetTime()
422
423
  values = False
423
- command = self.__addSingleCommand(
424
+ command = self._addSingleCommand(
424
425
  self.GET_PROPERTY_SPECIFICATIONS, property_name
425
426
  )
426
- response = self.__sendCommand(command)
427
+ response = self._sendCommand(command)
427
428
  if response == False:
428
429
  return None
429
430
 
@@ -477,7 +478,7 @@ class Client:
477
478
  )
478
479
  def get_property(self, property_name: str):
479
480
  """
480
- Get the value of a property of the current camera on DE-Server
481
+ Get the value of a property of the camera on DE-Server
481
482
 
482
483
  Parameters
483
484
  ----------
@@ -510,7 +511,7 @@ class Client:
510
511
 
511
512
  def get_register(self, register_name: str):
512
513
  """
513
- Get the value of a register of the current camera on DE-Server
514
+ Get the value of a register of the camera on DE-Server
514
515
 
515
516
  Parameters
516
517
  ----------
@@ -565,7 +566,7 @@ class Client:
565
566
  @write_only
566
567
  def set_property(self, name: str, value):
567
568
  """
568
- Set the value of a property of the current camera on DE-Server
569
+ Set the value of a property of the camera on DE-Server
569
570
 
570
571
  Parameters
571
572
  ----------
@@ -601,7 +602,7 @@ class Client:
601
602
  )
602
603
  def set_property_and_get_changed_properties(self, name, value, changed_properties):
603
604
  """
604
- Set the value of a property of the current camera on DE-Server and get all
605
+ Set the value of a property of the camera on DE-Server and get all
605
606
  the changed properties. This is useful for testing and determining how certain
606
607
  properties affect others.
607
608
 
@@ -643,7 +644,7 @@ class Client:
643
644
  @write_only
644
645
  def set_register(self, name: str, value):
645
646
  """
646
- Set the value of a register of the current camera on DE-Server
647
+ Set the value of a register of the camera on DE-Server
647
648
 
648
649
  Parameters
649
650
  ----------
@@ -677,7 +678,7 @@ class Client:
677
678
  @write_only
678
679
  def set_engineering_mode(self, enable, password):
679
680
  """
680
- Set the engineering mode of the current camera on DE-Server. Mostly for internal testing.
681
+ Set the engineering mode of the camera on DE-Server. Mostly for internal testing.
681
682
 
682
683
  Parameters
683
684
  ----------
@@ -700,10 +701,10 @@ class Client:
700
701
 
701
702
  ret = False
702
703
 
703
- command = self.__addSingleCommand(
704
+ command = self._addSingleCommand(
704
705
  self.SET_ENG_MODE_GET_CHANGED_PROPERTIES, None, [enable, password]
705
706
  )
706
- response = self.__sendCommand(command)
707
+ response = self._sendCommand(command)
707
708
  if response != False:
708
709
  ret = response.acknowledge[0].error != True
709
710
  self.refreshProperties = True
@@ -720,7 +721,7 @@ class Client:
720
721
  @deprecated_argument(name="sizeY", since="5.2.0", alternative="size_y")
721
722
  def set_hw_roi(self, offset_x: int, offset_y: int, size_x: int, size_y: int):
722
723
  """
723
- Set the hardware region of interest (ROI) of the current camera on DE-Server.
724
+ Set the hardware region of interest (ROI) of the camera on DE-Server.
724
725
 
725
726
  Parameters
726
727
  ----------
@@ -762,14 +763,14 @@ class Client:
762
763
  @deprecated_argument(name="sizeY", since="5.2.0", alternative="size_y")
763
764
  def SetScanSize(self, size_x, size_y):
764
765
  """
765
- Set the scan size of the current camera on DE-Server.
766
+ Set the scan size of the camera on DE-Server.
766
767
  """
767
768
 
768
769
  t0 = self.GetTime()
769
770
  ret = False
770
771
 
771
- command = self.__addSingleCommand(self.SET_SCAN_SIZE, None, [size_x, size_y])
772
- response = self.__sendCommand(command)
772
+ command = self._addSingleCommand(self.SET_SCAN_SIZE, None, [size_x, size_y])
773
+ response = self._sendCommand(command)
773
774
  if response != False:
774
775
  ret = response.acknowledge[0].error != True
775
776
  self.refreshProperties = True
@@ -794,10 +795,10 @@ class Client:
794
795
  t0 = self.GetTime()
795
796
  ret = False
796
797
 
797
- command = self.__addSingleCommand(
798
+ command = self._addSingleCommand(
798
799
  self.SET_SCAN_SIZE_AND_GET_CHANGED_PROPERTIES, None, [size_x, size_y]
799
800
  )
800
- response = self.__sendCommand(command)
801
+ response = self._sendCommand(command)
801
802
  if response != False:
802
803
  ret = response.acknowledge[0].error != True
803
804
  self.refreshProperties = True
@@ -820,10 +821,10 @@ class Client:
820
821
  t0 = self.GetTime()
821
822
  ret = False
822
823
 
823
- command = self.__addSingleCommand(
824
+ command = self._addSingleCommand(
824
825
  self.SET_SCAN_ROI, None, [enable, offsetX, offsetY, sizeX, sizeY]
825
826
  )
826
- response = self.__sendCommand(command)
827
+ response = self._sendCommand(command)
827
828
  if response != False:
828
829
  ret = response.acknowledge[0].error != True
829
830
  self.refreshProperties = True
@@ -848,12 +849,12 @@ class Client:
848
849
  t0 = self.GetTime()
849
850
  ret = False
850
851
 
851
- command = self.__addSingleCommand(
852
+ command = self._addSingleCommand(
852
853
  self.SET_SCAN_ROI__AND_GET_CHANGED_PROPERTIES,
853
854
  None,
854
855
  [enable, offsetX, offsetY, sizeX, sizeY],
855
856
  )
856
- response = self.__sendCommand(command)
857
+ response = self._sendCommand(command)
857
858
  if response != False:
858
859
  ret = response.acknowledge[0].error != True
859
860
  self.refreshProperties = True
@@ -878,7 +879,7 @@ class Client:
878
879
  self, offsetX: int, offsetY: int, sizeX: int, sizeY: int, changedProperties
879
880
  ):
880
881
  """
881
- Set the hardware region of interest (ROI) of the current camera on DE-Server and get all
882
+ Set the hardware region of interest (ROI) of the camera on DE-Server and get all
882
883
  the changed properties. This is useful for testing and determining how certain
883
884
  properties affect others.
884
885
 
@@ -927,7 +928,7 @@ class Client:
927
928
  @write_only
928
929
  def set_sw_roi(self, offsetX: int, offsetY: int, sizeX: int, sizeY: int):
929
930
  """
930
- Set the software region of interest (ROI) of the current camera on DE-Server.
931
+ Set the software region of interest (ROI) of the camera on DE-Server.
931
932
 
932
933
  Parameters
933
934
  ----------
@@ -970,7 +971,7 @@ class Client:
970
971
  @deprecated_argument(name="useHW", since="5.2.0", alternative="use_hw")
971
972
  def set_binning(self, bin_x, bin_y, use_hw=True):
972
973
  """
973
- Set the binning of the current camera on DE-Server. If useHW is True, the binning will
974
+ Set the binning of the camera on DE-Server. If useHW is True, the binning will
974
975
  use hardware binning. If useHW is False, the binning will use software binning only.
975
976
 
976
977
  Note
@@ -1012,10 +1013,8 @@ class Client:
1012
1013
  if prop_hw_bin_y is not False:
1013
1014
  hw_bin_y = int(prop_hw_bin_y)
1014
1015
 
1015
- if bin_x > 2:
1016
- retval &= self.SetProperty("Binning X", bin_x / hw_bin_x)
1017
- if bin_y > 2:
1018
- retval &= self.SetProperty("Binning Y", bin_y / hw_bin_y)
1016
+ retval &= self.SetProperty("Binning X", bin_x / hw_bin_x)
1017
+ retval &= self.SetProperty("Binning Y", bin_y / hw_bin_y)
1019
1018
 
1020
1019
  if commandVersion >= 13:
1021
1020
  retval &= self.SetProperty("Server Normalize Properties", "On")
@@ -1027,7 +1026,7 @@ class Client:
1027
1026
  self, offsetX, offsetY, sizeX, sizeY, changedProperties
1028
1027
  ):
1029
1028
  """
1030
- Set the software region of interest (ROI) of the current camera on DE-Server and get all of
1029
+ Set the software region of interest (ROI) of the camera on DE-Server and get all of
1031
1030
  the changed properties. This is useful for testing and determining how certain
1032
1031
  properties affect others.
1033
1032
 
@@ -1078,7 +1077,7 @@ class Client:
1078
1077
  self, size_x: int, size_y: int, offset_x: int = None, offset_y: int = None
1079
1078
  ):
1080
1079
  """
1081
- Automatically choose the proper HW ROI and set SW ROI of the current camera on DE-Server.
1080
+ Automatically choose the proper HW ROI and set SW ROI of the camera on DE-Server.
1082
1081
 
1083
1082
  If offset_x and offset_y are not provided, they will be centered on the camera.
1084
1083
 
@@ -1126,7 +1125,7 @@ class Client:
1126
1125
  self, offsetX, offsetY, sizeX, sizeY, changedProperties, timeoutMsec=5000
1127
1126
  ):
1128
1127
  """
1129
- Automatically choose the proper HW ROI and set SW ROI of the current camera on DE-Server and get all of
1128
+ Automatically choose the proper HW ROI and set SW ROI of the camera on DE-Server and get all of
1130
1129
  the changed properties. This is useful for testing and determining how certain
1131
1130
  properties affect others.
1132
1131
 
@@ -1147,12 +1146,12 @@ class Client:
1147
1146
  t0 = self.GetTime()
1148
1147
  ret = False
1149
1148
 
1150
- command = self.__addSingleCommand(
1149
+ command = self._addSingleCommand(
1151
1150
  self.SET_ADAPTIVE_ROI_AND_GET_CHANGED_PROPERTIES,
1152
1151
  None,
1153
1152
  [offsetX, offsetY, sizeX, sizeY],
1154
1153
  )
1155
- response = self.__sendCommand(command)
1154
+ response = self._sendCommand(command)
1156
1155
  if response != False:
1157
1156
  ret = response.acknowledge[0].error != True
1158
1157
  self.refreshProperties = True
@@ -1735,7 +1734,7 @@ class Client:
1735
1734
  @write_only
1736
1735
  def set_virtual_mask(self, id, w, h, mask):
1737
1736
  """
1738
- Set the virtual mask of the current camera on DE-Server.
1737
+ Set the virtual mask of the camera on DE-Server.
1739
1738
 
1740
1739
  Parameters
1741
1740
  ----------
@@ -1776,7 +1775,7 @@ class Client:
1776
1775
  @write_only
1777
1776
  def setROI(self, offsetX, offsetY, sizeX, sizeY, useHWROI=False):
1778
1777
  """
1779
- Set the region of interest (ROI) of the current camera on DE-Server.
1778
+ Set the region of interest (ROI) of the camera on DE-Server.
1780
1779
 
1781
1780
  Parameters
1782
1781
  ----------
@@ -1831,7 +1830,7 @@ class Client:
1831
1830
 
1832
1831
  def get_movie_buffer_info(self, movieBufferInfo=None, timeoutMsec=5000):
1833
1832
  """
1834
- Get the movie buffer information of the current camera on DE-Server.
1833
+ Get the movie buffer information of the camera on DE-Server.
1835
1834
 
1836
1835
  Parameters
1837
1836
  ----------
@@ -1866,7 +1865,7 @@ class Client:
1866
1865
  self, movieBuffer, movieBufferSize, numFrames, timeoutMsec=5000
1867
1866
  ):
1868
1867
  """
1869
- Get the movie buffer of the current camera on DE-Server. The movie buffer
1868
+ Get the movie buffer of the camera on DE-Server. The movie buffer
1870
1869
  is a series of frames that are stored in memory and can be retrieved as
1871
1870
  a single buffer for faster processing.
1872
1871
 
@@ -2477,6 +2476,125 @@ class Client:
2477
2476
  return time.clock()
2478
2477
  else:
2479
2478
  return time.perf_counter()
2479
+
2480
+ def enable_get_event(self):
2481
+ """
2482
+ Enable event retrieval from the server.
2483
+
2484
+ Creates a Windows semaphore to handle event notifications and enables
2485
+ the getEventEnabled flag. This must be called before get_event() can be used.
2486
+
2487
+ Returns
2488
+ -------
2489
+ bool
2490
+ True if event retrieval was successfully enabled, False otherwise.
2491
+ """
2492
+ with self.eventMutex:
2493
+ command = self._addSingleCommand(self.ENABLE_GET_EVENT, None, None)
2494
+ response = self._sendCommand(command)
2495
+
2496
+ if response != False:
2497
+ semaphoreName = self.__getParameters(response.acknowledge[0])[0]
2498
+ if semaphoreName is not None:
2499
+ self.sdkEventSemaphore = win32event.CreateSemaphore(None, 0, 999, semaphoreName)
2500
+ else:
2501
+ return False
2502
+
2503
+ self.getEventEnabled = True
2504
+ return True
2505
+ else:
2506
+ return False
2507
+
2508
+ def get_event(self):
2509
+ """
2510
+ Retrieve the next event from the server.
2511
+
2512
+ Waits for an event signal on the semaphore and retrieves the event specification
2513
+ from the server. This method blocks until an event is signaled. If the client
2514
+ is not connected or event retrieval was not enabled via `enable_get_event()`,
2515
+ an empty list is returned instead of attempting to fetch an event.
2516
+
2517
+ Returns
2518
+ -------
2519
+ list
2520
+ A list of 5 strings that represent the specification for 1 event with the following order:
2521
+ [event_type, name, value, lower_limit, upper_limit].
2522
+ * event_type can be "Property", "AllowedValues", or "Readonly".
2523
+ - "Property" means the event was triggered by a change in a property's value or its limits.
2524
+ - "AllowedValues" means the event was triggered by a change in the options of a property.
2525
+ Please use get_property_specifications after an "AllowedValues" event to get the updated specifications
2526
+ for the property that triggered the event.
2527
+ - "Readonly" means the event was triggered by a property changing between read-only and read-write.
2528
+ * name is the name of the property that triggered the event.
2529
+ * value is the new value of the property that triggered the event.
2530
+ It can also indicate if a property is now read-only ("1") or read-write ("0") for a "Readonly" event.
2531
+ For "AllowedValues" events, value may be an empty string.
2532
+ * lower_limit and upper_limit are the new limits of the property that triggered a "Property" event.
2533
+ If they are empty strings, then the limits did not change.
2534
+ If event retrieval is disabled or the client is disconnected, an empty list is returned.
2535
+ """
2536
+ if self.sdkEventSemaphore is not None:
2537
+ win32event.WaitForSingleObject(self.sdkEventSemaphore, win32event.INFINITE)
2538
+ else:
2539
+ return []
2540
+
2541
+ with self.eventMutex:
2542
+ if not self.connected or not self.getEventEnabled:
2543
+ return []
2544
+
2545
+ command = self._addSingleCommand(self.GET_EVENT, None, None)
2546
+ response = self._sendCommand(command)
2547
+
2548
+ eventSpec = []
2549
+ if response != False:
2550
+ values = self.__getParameters(response.acknowledge[0])
2551
+ if type(values) is list:
2552
+ eventSpec.append(values[0])
2553
+ eventSpec.append(values[1])
2554
+ eventSpec.append(values[2])
2555
+ eventSpec.append(values[3])
2556
+ eventSpec.append(values[4])
2557
+
2558
+ return eventSpec
2559
+
2560
+ def disable_get_event(self):
2561
+ """
2562
+ Disable event retrieval from the server.
2563
+
2564
+ Releases and closes the Windows semaphore used for event notification,
2565
+ and disables the getEventEnabled flag. After calling this, get_event()
2566
+ will no longer function.
2567
+
2568
+ Returns
2569
+ -------
2570
+ bool
2571
+ True if event retrieval was successfully disabled, False otherwise.
2572
+ """
2573
+ with self.eventMutex:
2574
+ command = self._addSingleCommand(self.DISABLE_GET_EVENT, None, None)
2575
+ response = self._sendCommand(command)
2576
+
2577
+ if response != False:
2578
+ if self.sdkEventSemaphore is not None:
2579
+ win32event.ReleaseSemaphore(self.sdkEventSemaphore, 1)
2580
+ win32api.CloseHandle(self.sdkEventSemaphore)
2581
+ self.sdkEventSemaphore = None
2582
+ self.getEventEnabled = False
2583
+ return True
2584
+ else:
2585
+ return False
2586
+
2587
+ def is_get_event_enabled(self):
2588
+ """
2589
+ Check if event retrieval from the server is currently enabled.
2590
+
2591
+ Returns
2592
+ -------
2593
+ bool
2594
+ True if event retrieval is enabled, False otherwise.
2595
+ """
2596
+ with self.eventMutex:
2597
+ return self.getEventEnabled
2480
2598
 
2481
2599
  # private methods
2482
2600
 
@@ -2557,10 +2675,10 @@ class Client:
2557
2675
 
2558
2676
  if command is None:
2559
2677
  return False
2560
-
2678
+
2561
2679
  if len(command.camera_name) == 0:
2562
2680
  command.camera_name = (
2563
- self.currCamera
2681
+ self.camera
2564
2682
  ) # append the current camera name if necessary
2565
2683
 
2566
2684
  try:
@@ -2787,6 +2905,10 @@ class Client:
2787
2905
  GetImage = get_image
2788
2906
  TakeDarkReference = take_dark_reference
2789
2907
  GetTime = get_time
2908
+ EnableGetEvent = enable_get_event
2909
+ GetEvent = get_event
2910
+ DisableGetEvent = disable_get_event
2911
+ IsGetEventEnabled = is_get_event_enabled
2790
2912
 
2791
2913
  # method setProperty was renamed to SetProperty. please use SetProperty
2792
2914
  setProperty = SetProperty
@@ -2799,13 +2921,15 @@ class Client:
2799
2921
  usingMmf = True
2800
2922
  debugImagesFolder = "D:\\DebugImages\\"
2801
2923
  connected = False
2802
- cameras = None
2803
- currCamera = ""
2924
+ camera = ""
2804
2925
  refreshProperties = True
2805
2926
  exposureTime = 1
2806
2927
  host = 0
2807
2928
  port = 0
2808
2929
  read_only = False
2930
+ sdkEventSemaphore = None
2931
+ getEventEnabled = False
2932
+ eventMutex = threading.Lock()
2809
2933
 
2810
2934
  # command lists
2811
2935
  LIST_CAMERAS = 0
@@ -2816,6 +2940,7 @@ class Client:
2816
2940
  GET_IMAGE_16U = 5
2817
2941
  GET_IMAGE_32F = 10
2818
2942
  STOP_ACQUISITION = 11
2943
+ GET_EVENT = 12
2819
2944
  GET_RESULT = 14
2820
2945
  START_ACQUISITION = 15
2821
2946
  SET_HW_ROI = 16
@@ -2838,6 +2963,8 @@ class Client:
2838
2963
  SET_ADAPTIVE_ROI = 33
2839
2964
  SET_ADAPTIVE_ROI_AND_GET_CHANGED_PROPERTIES = 34
2840
2965
  GET_PROPERTY_SPECIFICATIONS = 35
2966
+ ENABLE_GET_EVENT = 36
2967
+ DISABLE_GET_EVENT = 37
2841
2968
  GET_REGISTER = 38
2842
2969
  SET_REGISTER = 39
2843
2970
  LIST_REGISTERS = 40
@@ -9,9 +9,6 @@ from deapi.tests.original_tests import func, propertyName
9
9
  deClient = DEAPI.Client()
10
10
  deClient.Connect()
11
11
 
12
- cameras = deClient.ListCameras()
13
- camera = cameras[0]
14
- deClient.SetCurrentCamera(camera)
15
12
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
16
13
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
17
14
  print(f"Camera Name: {cameraName}, Server Software Version is: {serverVersion}")
@@ -7,9 +7,6 @@ from deapi.tests.original_tests import func, propertyName
7
7
  # Connect to the server
8
8
  deClient = DEAPI.Client()
9
9
  deClient.Connect()
10
- cameras = deClient.ListCameras()
11
- camera = cameras[0]
12
- deClient.SetCurrentCamera(camera)
13
10
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
14
11
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
15
12
  print(f"Camera Name: {cameraName}, Server Software Version is: {serverVersion}")
@@ -8,9 +8,6 @@ from deapi.tests.original_tests import func, propertyName
8
8
  # Connect to the server
9
9
  deClient = DEAPI.Client()
10
10
  deClient.Connect()
11
- cameras = deClient.ListCameras()
12
- camera = cameras[0]
13
- deClient.SetCurrentCamera(camera)
14
11
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
15
12
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
16
13
  print(f"Camera Name: {cameraName}, Server Software Version is: {serverVersion}")
@@ -9,9 +9,6 @@ from deapi.tests.original_tests import func, propertyName
9
9
  # Connect to the server
10
10
  deClient = DEAPI.Client()
11
11
  deClient.Connect()
12
- cameras = deClient.ListCameras()
13
- camera = cameras[0]
14
- deClient.SetCurrentCamera(camera)
15
12
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
16
13
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
17
14
  print(f"Camera Name: {cameraName}, Server Software Version is: {serverVersion}")
@@ -9,9 +9,6 @@ from deapi.tests.original_tests import func, propertyName
9
9
  # Connect to the server
10
10
  deClient = DEAPI.Client()
11
11
  deClient.Connect()
12
- cameras = deClient.ListCameras()
13
- camera = cameras[0]
14
- deClient.SetCurrentCamera(camera)
15
12
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
16
13
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
17
14
  print(f"Camera Name: {cameraName}, Server Software Version is: {serverVersion}")
@@ -11,9 +11,6 @@ from deapi.tests.original_tests import func, propertyName
11
11
  deClient = DEAPI.Client()
12
12
  # Connect to the Server
13
13
  deClient.Connect()
14
- cameras = deClient.ListCameras()
15
- camera = cameras[0]
16
- deClient.SetCurrentCamera(camera)
17
14
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
18
15
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
19
16
  print(f"Camera Name: {cameraName}, Server Software Version is: {serverVersion}")
@@ -9,9 +9,6 @@ from deapi.tests.original_tests import func, propertyName
9
9
  # Connect to the server
10
10
  deClient = DEAPI.Client()
11
11
  deClient.Connect()
12
- cameras = deClient.ListCameras()
13
- camera = cameras[0]
14
- deClient.SetCurrentCamera(camera)
15
12
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
16
13
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
17
14
  print(f"Camera Name: {cameraName}, Server Software Version is: {serverVersion}")
@@ -10,9 +10,6 @@ from deapi.tests.original_tests import func, propertyName
10
10
  # Connect to the server
11
11
  deClient = DEAPI.Client()
12
12
  deClient.Connect()
13
- cameras = deClient.ListCameras()
14
- camera = cameras[0]
15
- deClient.SetCurrentCamera(camera)
16
13
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
17
14
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
18
15
  print(f"Camera Name: {cameraName}, Server Software Version is: {serverVersion}")
@@ -8,9 +8,6 @@ from deapi.tests.original_tests import func, propertyName
8
8
  # Connect to the server
9
9
  deClient = DEAPI.Client()
10
10
  deClient.Connect()
11
- cameras = deClient.ListCameras()
12
- camera = cameras[0]
13
- deClient.SetCurrentCamera(camera)
14
11
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
15
12
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
16
13
  print(f"Camera Name: {cameraName}, Server Software Version: {serverVersion}")
@@ -1,3 +1,4 @@
1
+ import math
1
2
  import sys
2
3
  import unittest
3
4
  import deapi as DEAPI
@@ -22,8 +23,6 @@ from deapi.tests.original_tests import func, propertyName
22
23
 
23
24
  deClient = DEAPI.Client()
24
25
  deClient.Connect()
25
- cameras = deClient.ListCameras()
26
- camera = cameras[0]
27
26
 
28
27
  serverVersion = deClient.GetProperty(propertyName.PROP_SERVER_SOFTWARE_VERSION)
29
28
  cameraName = deClient.GetProperty(propertyName.PROP_CAMERA_NAME)
@@ -75,8 +74,7 @@ fps = 10
75
74
  numPrecision = 6
76
75
  frameCount = 1
77
76
 
78
- deClient.SetCurrentCamera(camera)
79
- # deClient.SetProperty("Test Pattern", testPattern)
77
+ deClient.SetProperty("Test Pattern", testPattern)
80
78
  deClient.SetProperty(propertyName.PROP_INSTRUMENT_CLIENT_ADDRESS, "Manual")
81
79
  deClient.SetProperty(
82
80
  propertyName.PROP_INSTRUMENT_PROJECT_MAGNIFICATION, instrumentProjectMagnification
@@ -139,6 +137,7 @@ class Stats:
139
137
 
140
138
 
141
139
  def statisticsValueCheck(imageProcessingMode, correctionMode):
140
+ ret = True
142
141
  deClient.SetProperty(propertyName.PROP_IMAGE_PROCESSING_MODE, imageProcessingMode)
143
142
  deClient.SetProperty(
144
143
  propertyName.PROP_IMAGE_PROCESSING_FLATFIELD_CORRECTION, correctionMode
@@ -163,14 +162,17 @@ def statisticsValueCheck(imageProcessingMode, correctionMode):
163
162
  print(f"imagemean: {attributes.imageMean}")
164
163
  print(stats.eppix)
165
164
  print(stats.epa2)
166
- # Sometimes the float number are not equal due to the
167
- func.compare2FloatValue(stats.eppix, attributes.eppix, numPrecision, "e-/pix")
168
- func.compare2FloatValue(stats.eppixps, attributes.eppixps, numPrecision, "e-/pix/s")
169
- func.compare2FloatValue(stats.eps, attributes.eps, numPrecision, "e-/s")
170
- func.compare2FloatValue(stats.epa2, attributes.epa2, numPrecision, "e-/a^2")
165
+ # precision issue, we will compare the float number with a tolerance value. The tolerance value is set to 10**-numPrecision * frameCount for e-/pix, 10**-numPrecision * fps for e-/pix/s and 10**-numPrecision * fps * numPhysicalPixels for e-/s.
166
+ ret &= math.isclose(stats.eppix, attributes.eppix, rel_tol=0, abs_tol=10**-numPrecision * frameCount)
167
+ ret &= math.isclose(stats.eppixps, attributes.eppixps, rel_tol=0, abs_tol=10**-numPrecision * fps)
168
+ ret &= math.isclose(stats.eps, attributes.eps, rel_tol=0, abs_tol=10**-numPrecision * fps * numPhysicalPixels)
169
+
170
+ ret &= func.compare2FloatValue(stats.epa2, attributes.epa2, numPrecision, "e-/a^2")
171
+ return ret
171
172
 
172
173
 
173
174
  def compareBin1Bin2(imageProcessingMode, correctionMode, swBinningFactor):
175
+ ret = True
174
176
  deClient.SetProperty(propertyName.PROP_IMAGE_PROCESSING_MODE, imageProcessingMode)
175
177
  deClient.SetProperty(
176
178
  propertyName.PROP_IMAGE_PROCESSING_FLATFIELD_CORRECTION, correctionMode
@@ -210,12 +212,11 @@ def compareBin1Bin2(imageProcessingMode, correctionMode, swBinningFactor):
210
212
  swBinY,
211
213
  )
212
214
 
213
- func.compare2FloatValue(statsBin1.eppix, statsBin2.eppix, numPrecision, "e-/pix")
214
- func.compare2FloatValue(
215
- statsBin1.eppixps, statsBin2.eppixps, numPrecision, "e-/pix/s"
216
- )
217
- func.compare2FloatValue(statsBin1.eps, statsBin2.eps, numPrecision, "e-/s")
218
- func.compare2FloatValue(statsBin1.epa2, statsBin2.epa2, numPrecision, "e-/a^2")
215
+ ret &= func.compare2FloatValue(statsBin1.eppix, statsBin2.eppix, numPrecision, "e-/pix")
216
+ ret &= func.compare2FloatValue(statsBin1.eppixps, statsBin2.eppixps, numPrecision, "e-/pix/s")
217
+ ret &= func.compare2FloatValue(statsBin1.eps, statsBin2.eps, numPrecision, "e-/s")
218
+ ret &= func.compare2FloatValue(statsBin1.epa2, statsBin2.epa2, numPrecision, "e-/a^2")
219
+ return ret
219
220
 
220
221
 
221
222
  class TestPatternPixelValues(unittest.TestCase):
@@ -18,6 +18,8 @@ def compare2FloatValue(expectVal, actualVal, numPrecision, name):
18
18
  f"{name}: expect {round(expectVal, numPrecision)} actual {round(actualVal, numPrecision)}"
19
19
  )
20
20
  return False
21
+ else:
22
+ return True
21
23
 
22
24
 
23
25
  def writeLogFile(caseName, scriptName):
@@ -34,15 +34,6 @@ class TestClient:
34
34
  def test_client_connection(self, client):
35
35
  assert client.connected
36
36
 
37
- def test_list_cameras(self, client):
38
- cameras = client.list_cameras()
39
- assert isinstance(cameras, list)
40
-
41
- def test_set_current_camera(self, client):
42
- cameras = client.list_cameras()
43
- client.set_current_camera(cameras[0])
44
- assert client.get_current_camera() == cameras[0]
45
-
46
37
  def test_list_properties(self, client):
47
38
  properties = client.list_properties()
48
39
  assert isinstance(properties, list)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deapi
3
- Version: 5.3b3
3
+ Version: 5.3b4
4
4
  Summary: API for DE Server
5
5
  Author: Direct Electron
6
6
  Project-URL: Bug Reports, https://github.com/directelectron/deapi/issues
@@ -25,7 +25,7 @@ dependencies = [
25
25
  "sympy",
26
26
  ]
27
27
  description = "API for DE Server"
28
- version = "5.3b3"
28
+ version = "5.3b4"
29
29
  keywords = [
30
30
  "EELS",
31
31
  "STEM",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes