microeye 2.3.2__py3-none-any.whl

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 (190) hide show
  1. microEye/__init__.py +47 -0
  2. microEye/_version.py +2 -0
  3. microEye/analysis/__init__.py +1 -0
  4. microEye/analysis/checklist_dialog.py +143 -0
  5. microEye/analysis/cmosMaps.py +228 -0
  6. microEye/analysis/filters/__init__.py +9 -0
  7. microEye/analysis/filters/base.py +21 -0
  8. microEye/analysis/filters/spatial.py +338 -0
  9. microEye/analysis/filters/temporal.py +76 -0
  10. microEye/analysis/fitting/__init__.py +0 -0
  11. microEye/analysis/fitting/fit.py +680 -0
  12. microEye/analysis/fitting/nena.py +375 -0
  13. microEye/analysis/fitting/phasor_fit.py +90 -0
  14. microEye/analysis/fitting/processing.py +317 -0
  15. microEye/analysis/fitting/psf/__init__.py +6 -0
  16. microEye/analysis/fitting/psf/extract.py +1129 -0
  17. microEye/analysis/fitting/psf/rubost_mean.py +150 -0
  18. microEye/analysis/fitting/psf/spline.py +167 -0
  19. microEye/analysis/fitting/psf/stats/__init__.py +12 -0
  20. microEye/analysis/fitting/psf/stats/core.py +295 -0
  21. microEye/analysis/fitting/psf/stats/curve_fit.py +708 -0
  22. microEye/analysis/fitting/psf/stats/io.py +104 -0
  23. microEye/analysis/fitting/psf/stats/slope_fit.py +171 -0
  24. microEye/analysis/fitting/psf/temp.py +147 -0
  25. microEye/analysis/fitting/psf/test.py +47 -0
  26. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +657 -0
  27. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +1336 -0
  28. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +270 -0
  29. microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +1 -0
  30. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +609 -0
  31. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +1396 -0
  32. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +1426 -0
  33. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +231 -0
  34. microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +2 -0
  35. microEye/analysis/fitting/pyfit3Dcspline/__init__.py +10 -0
  36. microEye/analysis/fitting/pyfit3Dcspline/constants.py +27 -0
  37. microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +903 -0
  38. microEye/analysis/fitting/results.py +917 -0
  39. microEye/analysis/fitting/results_stats.py +251 -0
  40. microEye/analysis/fitting/tardis.py +209 -0
  41. microEye/analysis/multi_viewer.py +568 -0
  42. microEye/analysis/processing/__init__.py +1 -0
  43. microEye/analysis/processing/frc.py +120 -0
  44. microEye/analysis/rendering/__init__.py +3 -0
  45. microEye/analysis/rendering/base.py +452 -0
  46. microEye/analysis/rendering/cloud.py +215 -0
  47. microEye/analysis/rendering/core.py +47 -0
  48. microEye/analysis/rendering/volumetric.py +198 -0
  49. microEye/analysis/tools/__init__.py +0 -0
  50. microEye/analysis/tools/kymograms.py +1079 -0
  51. microEye/analysis/tools/roi_selectors.py +556 -0
  52. microEye/analysis/utils/__init__.py +3 -0
  53. microEye/analysis/utils/coordinates.py +18 -0
  54. microEye/analysis/utils/images.py +68 -0
  55. microEye/analysis/utils/windows.py +19 -0
  56. microEye/analysis/viewer/__init__.py +3 -0
  57. microEye/analysis/viewer/image_options_widget.py +615 -0
  58. microEye/analysis/viewer/images.py +1225 -0
  59. microEye/analysis/viewer/layers_widget.py +415 -0
  60. microEye/analysis/viewer/localizations.py +1304 -0
  61. microEye/analysis/viewer/psf.py +1003 -0
  62. microEye/analysis/viewer/volume.py +456 -0
  63. microEye/hardware/__init__.py +3 -0
  64. microEye/hardware/cams/__init__.py +29 -0
  65. microEye/hardware/cams/camera_calibration.py +99 -0
  66. microEye/hardware/cams/camera_list.py +528 -0
  67. microEye/hardware/cams/camera_options.py +694 -0
  68. microEye/hardware/cams/camera_panel.py +941 -0
  69. microEye/hardware/cams/dummy/__init__.py +1 -0
  70. microEye/hardware/cams/dummy/dummy_panel.py +759 -0
  71. microEye/hardware/cams/jobs.py +497 -0
  72. microEye/hardware/cams/line_profiler.py +99 -0
  73. microEye/hardware/cams/linescan/IR_Cam.py +474 -0
  74. microEye/hardware/cams/linescan/__init__.py +1 -0
  75. microEye/hardware/cams/micam.py +524 -0
  76. microEye/hardware/cams/pco/__init__.py +58 -0
  77. microEye/hardware/cams/pco/enums.py +382 -0
  78. microEye/hardware/cams/pco/pco_cam.py +761 -0
  79. microEye/hardware/cams/pco/pco_panel.py +477 -0
  80. microEye/hardware/cams/shortcuts.py +312 -0
  81. microEye/hardware/cams/thorlabs/__init__.py +1 -0
  82. microEye/hardware/cams/thorlabs/thorlabs.py +1508 -0
  83. microEye/hardware/cams/thorlabs/thorlabs_panel.py +850 -0
  84. microEye/hardware/cams/ueye/__init__.py +1 -0
  85. microEye/hardware/cams/ueye/ueye_camera.py +1023 -0
  86. microEye/hardware/cams/ueye/ueye_panel.py +861 -0
  87. microEye/hardware/cams/vimba/__init__.py +1 -0
  88. microEye/hardware/cams/vimba/vimba_cam.py +1000 -0
  89. microEye/hardware/cams/vimba/vimba_panel.py +813 -0
  90. microEye/hardware/device.py +60 -0
  91. microEye/hardware/lasers/__init__.py +13 -0
  92. microEye/hardware/lasers/io_matchbox.py +791 -0
  93. microEye/hardware/lasers/io_params.py +85 -0
  94. microEye/hardware/lasers/io_single_laser.py +742 -0
  95. microEye/hardware/lasers/laser_relay.py +594 -0
  96. microEye/hardware/mieye/__init__.py +1 -0
  97. microEye/hardware/mieye/acquisition_manager.py +467 -0
  98. microEye/hardware/mieye/devices_manager.py +533 -0
  99. microEye/hardware/mieye/miEye.py +659 -0
  100. microEye/hardware/misc/__init__.py +0 -0
  101. microEye/hardware/misc/acquisition_view.py +71 -0
  102. microEye/hardware/misc/reglo.py +761 -0
  103. microEye/hardware/misc/temp.py +188 -0
  104. microEye/hardware/port_config.py +59 -0
  105. microEye/hardware/protocols/__init__.py +2 -0
  106. microEye/hardware/protocols/actions.py +402 -0
  107. microEye/hardware/protocols/actions_items.py +703 -0
  108. microEye/hardware/protocols/designer.py +244 -0
  109. microEye/hardware/protocols/scene_manager.py +191 -0
  110. microEye/hardware/protocols/serialization.py +97 -0
  111. microEye/hardware/pycromanager/__init__.py +16 -0
  112. microEye/hardware/pycromanager/core.py +1433 -0
  113. microEye/hardware/pycromanager/devices.py +461 -0
  114. microEye/hardware/pycromanager/enums.py +107 -0
  115. microEye/hardware/pycromanager/headless.py +153 -0
  116. microEye/hardware/pycromanager/utils.py +34 -0
  117. microEye/hardware/pycromanager/widgets/__init__.py +5 -0
  118. microEye/hardware/pycromanager/widgets/bridges.py +407 -0
  119. microEye/hardware/pycromanager/widgets/headless_manager.py +258 -0
  120. microEye/hardware/pycromanager/widgets/headless_options.py +224 -0
  121. microEye/hardware/pycromanager/widgets/pycro_panel.py +455 -0
  122. microEye/hardware/stages/__init__.py +18 -0
  123. microEye/hardware/stages/elliptec/__init__.py +5 -0
  124. microEye/hardware/stages/elliptec/baseDevice.py +314 -0
  125. microEye/hardware/stages/elliptec/device.py +384 -0
  126. microEye/hardware/stages/elliptec/deviceID.py +212 -0
  127. microEye/hardware/stages/elliptec/devicePort.py +379 -0
  128. microEye/hardware/stages/elliptec/deviceStatus.py +65 -0
  129. microEye/hardware/stages/elliptec/devicesView.py +706 -0
  130. microEye/hardware/stages/elliptec/ellDevices.py +134 -0
  131. microEye/hardware/stages/elliptec/messageUpdater.py +34 -0
  132. microEye/hardware/stages/elliptec/motorInfo.py +153 -0
  133. microEye/hardware/stages/elliptec/stage.py +62 -0
  134. microEye/hardware/stages/elliptec/test.py +139 -0
  135. microEye/hardware/stages/kinesis/__init__.py +1 -0
  136. microEye/hardware/stages/kinesis/kdc101/__init__.py +1 -0
  137. microEye/hardware/stages/kinesis/kdc101/enums.py +1002 -0
  138. microEye/hardware/stages/kinesis/kdc101/factory.py +171 -0
  139. microEye/hardware/stages/kinesis/kdc101/kdc101.py +718 -0
  140. microEye/hardware/stages/kinesis/kinesis.py +776 -0
  141. microEye/hardware/stages/piezo_concept.py +607 -0
  142. microEye/hardware/stages/stabilizer.py +785 -0
  143. microEye/hardware/stages/stage.py +89 -0
  144. microEye/hardware/widgets/__init__.py +10 -0
  145. microEye/hardware/widgets/controller.py +246 -0
  146. microEye/hardware/widgets/devices.py +133 -0
  147. microEye/hardware/widgets/focusWidget.py +264 -0
  148. microEye/hardware/widgets/qlist_slider.py +113 -0
  149. microEye/hardware/widgets/scan_acquisition.py +424 -0
  150. microEye/icons/1024.png +0 -0
  151. microEye/icons/128.png +0 -0
  152. microEye/icons/16.png +0 -0
  153. microEye/icons/24.png +0 -0
  154. microEye/icons/256.png +0 -0
  155. microEye/icons/32.png +0 -0
  156. microEye/icons/48.png +0 -0
  157. microEye/icons/512.png +0 -0
  158. microEye/icons/64.png +0 -0
  159. microEye/icons/__init__.py +0 -0
  160. microEye/icons/close.svg +88 -0
  161. microEye/icons/mieye.png +0 -0
  162. microEye/icons/min.svg +83 -0
  163. microEye/icons/viewer.png +0 -0
  164. microEye/launcher.py +42 -0
  165. microEye/qt.py +181 -0
  166. microEye/utils/__init__.py +2 -0
  167. microEye/utils/enum_encoder.py +10 -0
  168. microEye/utils/expandable_groupbox.py +93 -0
  169. microEye/utils/gui_helper.py +457 -0
  170. microEye/utils/hid/__init__.py +8 -0
  171. microEye/utils/hid/controller.py +153 -0
  172. microEye/utils/hid/device.py +63 -0
  173. microEye/utils/hid/enums.py +140 -0
  174. microEye/utils/hid/utils.py +58 -0
  175. microEye/utils/labelled_slider.py +134 -0
  176. microEye/utils/metadata.py +599 -0
  177. microEye/utils/metadata_tree.py +718 -0
  178. microEye/utils/micro_launcher.py +245 -0
  179. microEye/utils/parameter_tree.py +325 -0
  180. microEye/utils/pyscripting.py +444 -0
  181. microEye/utils/retry_exec.py +35 -0
  182. microEye/utils/start_gui.py +112 -0
  183. microEye/utils/thread_worker.py +115 -0
  184. microEye/utils/uImage.py +1512 -0
  185. microeye-2.3.2.dist-info/METADATA +401 -0
  186. microeye-2.3.2.dist-info/RECORD +190 -0
  187. microeye-2.3.2.dist-info/WHEEL +5 -0
  188. microeye-2.3.2.dist-info/entry_points.txt +2 -0
  189. microeye-2.3.2.dist-info/licenses/LICENSE +674 -0
  190. microeye-2.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,761 @@
1
+ import traceback
2
+
3
+ from microEye.qt import (
4
+ QApplication,
5
+ QDateTime,
6
+ QMainWindow,
7
+ Qt,
8
+ QtCore,
9
+ QtGui,
10
+ QtSerialPort,
11
+ QtWidgets,
12
+ )
13
+ from microEye.utils import StartGUI
14
+
15
+
16
+ class RegloDigital(QtSerialPort.QSerialPort):
17
+ _ACK_ = '*'
18
+ _ERR_ = '#'
19
+ _YES_ = '+'
20
+ _NO_ = '-'
21
+
22
+ # general
23
+ _address_alloc_ = '@'
24
+ _CR_ = '\r'
25
+ _reset_ = '-'
26
+
27
+ # control
28
+ _start_ = 'H'
29
+ _stop_ = 'I'
30
+ _clk_wise_ = 'J'
31
+ _counter_clk_wise_ = 'K'
32
+ _manual_panel_ = 'A'
33
+ _inactive_panel_ = 'B'
34
+ _disp_numbers_ = 'D'
35
+ _disp_letters_ = 'DA'
36
+
37
+ # modes
38
+ _pump_rpm_mode_ = 'L'
39
+ _pump_flowrate_mode_ = 'M'
40
+ _disp_time_mode_ = 'N'
41
+ _disp_vol_mode_ = 'O'
42
+ _pause_time_mode_ = ']'
43
+ _disp_time_pause_mode_ = 'P'
44
+ _disp_vol_pause_mode_ = 'Q'
45
+ _vol_period_mode_ = 'G'
46
+ _total_mode_ = 'R'
47
+
48
+ # inquiry & input
49
+ _status_ = 'E'
50
+ _type_ = '#'
51
+ _version_ = '('
52
+ _id_number_ = ')'
53
+ _speed_ = 'S'
54
+ _def_flowrate_ = '?'
55
+ _cal_flowrate_ = '!'
56
+ _digits_after_decimal_ = '['
57
+ _tubing_inner_diameter_ = '+' # in 1/100 mm
58
+ _disp_time_ = 'V' # in 1/10 sec (0-9999)
59
+ _disp_time_min_ = 'VM' # (0-899)
60
+ _disp_time_hour_ = 'VH' # (0-999)
61
+ _roller_steps_ = 'U' # (00001-65535)
62
+ _roller_steps_plus_ = 'u' # u*65535 + U
63
+ _roller_step_nanoliter_ = 'r' # mmmmee
64
+ _roller_step_default_ = 'r000000'
65
+ _flowrate_mlmin_ = 'f' # mmmmee
66
+ _disp_volume_ml_ = 'v' # mmmmee
67
+ _roller_back_steps_ = '%' # 0-100
68
+ _pause_time_ = 'T' # in 1/10 sec (0-9999)
69
+ _pause_time_min_ = 'TM' # (0-899)
70
+ _pause_time_hour_ = 'TH' # (0-999)
71
+ _disp_cycles_ = '"' # (0-9999)
72
+ _total_vol_ = ':'
73
+ _reset_total_vol_ = 'W'
74
+ _store_app_params_ = '*'
75
+ _set_default_vals_ = '0'
76
+ _foot_switch_ = 'C'
77
+
78
+ def __init__(self, Port: str, Address: int, *args, **kwargs):
79
+ super().__init__(Port)
80
+ self.Address = str(Address)
81
+ # self.setPortName(Port)
82
+ self.setBaudRate(9600)
83
+ self.response = ''
84
+ self.active = False
85
+ self.clockwise = False
86
+
87
+ def _send_command(self, command: str, waitCount: int = 1) -> str:
88
+ if self.isOpen():
89
+ self.write((command + self._CR_).encode())
90
+ self.waitForBytesWritten(500)
91
+ time = QDateTime.currentDateTime()
92
+ while self.bytesAvailable() < waitCount:
93
+ self.waitForReadyRead(500)
94
+ msec = time.msecsTo(QDateTime.currentDateTime())
95
+ if msec > 3000:
96
+ break
97
+ self.response = str(self.readAll(), encoding='utf8').strip('\r\n').strip()
98
+ return self.response
99
+ else:
100
+ return None
101
+
102
+ def start(self) -> str:
103
+ return self._send_command(self.Address + self._start_)
104
+
105
+ def stop(self) -> str:
106
+ return self._send_command(self.Address + self._stop_)
107
+
108
+ def pause(self) -> str:
109
+ return self._send_command(self.Address + self._p)
110
+
111
+ def set_clockwise(self) -> str:
112
+ res = self._send_command(self.Address + self._clk_wise_)
113
+ if res == self._ACK_:
114
+ self.clockwise = True
115
+ return res
116
+
117
+ def set_counter_clockwise(self) -> str:
118
+ res = self._send_command(self.Address + self._counter_clk_wise_)
119
+ if res == self._ACK_:
120
+ self.clockwise = False
121
+ return res
122
+
123
+ def set_pump_rpm_mode(self) -> str:
124
+ return self._send_command(self.Address + self._pump_rpm_mode_)
125
+
126
+ def set_pump_flowrate_mode(self) -> str:
127
+ return self._send_command(self.Address + self._pump_flowrate_mode_)
128
+
129
+ def set_disp_time_mode(self) -> str:
130
+ return self._send_command(self.Address + self._disp_time_mode_)
131
+
132
+ def set_disp_vol_mode(self) -> str:
133
+ return self._send_command(self.Address + self._disp_vol_mode_)
134
+
135
+ def set_pause_time_mode(self) -> str:
136
+ return self._send_command(self.Address + self._pause_time_mode_)
137
+
138
+ def set_disp_time_pause_mode(self) -> str:
139
+ return self._send_command(self.Address + self._disp_time_pause_mode_)
140
+
141
+ def set_disp_vol_pause_mode(self) -> str:
142
+ return self._send_command(self.Address + self._disp_vol_pause_mode_)
143
+
144
+ def set_vol_period_mode(self) -> str:
145
+ return self._send_command(self.Address + self._vol_period_mode_)
146
+
147
+ def set_total_mode(self) -> str:
148
+ return self._send_command(self.Address + self._total_mode_)
149
+
150
+ def get_status(self) -> str:
151
+ res = self._send_command(self.Address + self._status_)
152
+ self.active = res == RegloDigital._YES_
153
+ return res
154
+
155
+ def get_pump_type(self) -> str:
156
+ return self._send_command(self.Address + self._type_)
157
+
158
+ def get_soft_version(self) -> str:
159
+ return self._send_command(self.Address + self._version_)
160
+
161
+ def get_pump_head_id(self) -> str:
162
+ return self._send_command(self.Address + self._id_number_)
163
+
164
+ def set_pump_head_id(self, new_id: int) -> str:
165
+ return self._send_command(self.Address + self._id_number_ + f'{new_id:04d}')
166
+
167
+ def get_speed(self) -> str:
168
+ return self._send_command(self.Address + self._speed_, 8)
169
+
170
+ def set_speed(self, speed: float) -> str:
171
+ return self._send_command(
172
+ self.Address + self._speed_ + (f'{speed:07.2f}').replace('.', '')
173
+ )
174
+
175
+ def get_default_flowrate(self) -> str:
176
+ return self._send_command(self.Address + self._def_flowrate_)
177
+
178
+ def get_calibrated_flowrate(self) -> str:
179
+ return self._send_command(self.Address + self._cal_flowrate_)
180
+
181
+ def get_digits_after_decimal(self) -> str:
182
+ return self._send_command(self.Address + self._digits_after_decimal_)
183
+
184
+ def set_calibrated_flowrate(self, flowrate: float) -> str:
185
+ digits = self.get_digits_after_decimal()
186
+ if digits == '#':
187
+ return '#'
188
+ else:
189
+ # Format with right number of decimal places, then remove decimal point
190
+ formatted = f'{flowrate:.{int(digits)}f}'.replace('.', '')
191
+ # Ensure total length is 5 by padding with zeros if needed
192
+ formatted = formatted.zfill(5)
193
+ return self._send_command(self.Address + self._cal_flowrate_ + formatted)
194
+
195
+ def get_tubing_inner_diameter(self) -> str:
196
+ return self._send_command(self.Address + self._tubing_inner_diameter_, 6)
197
+
198
+ def set_tubing_inner_diameter(self, diameter: int) -> str:
199
+ res = self._send_command(
200
+ self.Address
201
+ + self._tubing_inner_diameter_
202
+ + f'{diameter:04d}'.replace('.', '')
203
+ )
204
+ return res
205
+
206
+ def get_disp_time(self) -> str:
207
+ return self._send_command(self.Address + self._disp_time_, 2)
208
+
209
+ def set_disp_time(self, time: int) -> str:
210
+ value = min(9999, max(0, time))
211
+ return self._send_command(
212
+ self.Address + self._disp_time_ + f'{value:04d}'.replace('.', '')
213
+ )
214
+
215
+ def set_disp_time_min(self, time: int) -> str:
216
+ value = min(899, max(0, time))
217
+ return self._send_command(
218
+ self.Address + self._disp_time_min_ + f'{value:03d}'.replace('.', '')
219
+ )
220
+
221
+ def set_disp_time_hour(self, time: int) -> str:
222
+ value = min(999, max(0, time))
223
+ return self._send_command(
224
+ self.Address + self._disp_time_hour_ + f'{value:03d}'.replace('.', '')
225
+ )
226
+
227
+ def get_roller_steps(self) -> str:
228
+ return self._send_command(self.Address + self._roller_steps_)
229
+
230
+ def set_roller_steps(self, steps: int) -> str:
231
+ value = min(65535, max(1, steps))
232
+ return self._send_command(
233
+ self.Address + self._roller_steps_ + f'{value:05d}'.replace('.', '')
234
+ )
235
+
236
+ def set_roller_steps_plus(self, steps: int) -> str:
237
+ value = min(9999, max(0, steps))
238
+ return self._send_command(
239
+ self.Address + self._roller_steps_plus_ + f'{value:04d}'.replace('.', '')
240
+ )
241
+
242
+ def get_roller_step_nanoliter(self) -> str:
243
+ return self._send_command(self.Address + self._roller_step_nanoliter_)
244
+
245
+ def set_roller_step_nanoliter(self, volume: float) -> str:
246
+ return self._send_command(
247
+ self.Address + self._roller_step_nanoliter_ + self.float_to_mmmmee(volume)
248
+ )
249
+
250
+ def get_flowrate_mlmin(self) -> str:
251
+ res = self._send_command(self.Address + self._flowrate_mlmin_, 9)
252
+ if res != RegloDigital._ERR_:
253
+ return self.mmmmee_to_float(res)
254
+ else:
255
+ return res
256
+
257
+ def set_flowrate_mlmin(self, flowrate: float) -> str:
258
+ return self._send_command(
259
+ self.Address + self._flowrate_mlmin_ + self.float_to_mmmmee(flowrate)
260
+ )
261
+
262
+ def get_disp_volume_ml(self) -> str:
263
+ res = self._send_command(self.Address + self._disp_volume_ml_, 9)
264
+ if res != RegloDigital._ERR_:
265
+ return self.mmmmee_to_float(res)
266
+ else:
267
+ return res
268
+
269
+ def set_disp_volume_ml(self, volume: float) -> str:
270
+ return self._send_command(
271
+ self.Address + self._disp_volume_ml_ + self.float_to_mmmmee(volume)
272
+ )
273
+
274
+ def get_roller_back_steps(self) -> str:
275
+ return self._send_command(self.Address + self._roller_back_steps_)
276
+
277
+ def set_roller_back_steps(self, steps: int) -> str:
278
+ value = min(100, max(0, steps))
279
+ return self._send_command(
280
+ self.Address + self._roller_back_steps_ + f'{value:04d}'.replace('.', '')
281
+ )
282
+
283
+ def get_pause_time(self) -> str:
284
+ return self._send_command(self.Address + self._pause_time_, 2)
285
+
286
+ def set_pause_time(self, time: int) -> str:
287
+ value = min(9999, max(0, time))
288
+ return self._send_command(
289
+ self.Address + self._pause_time_ + f'{value:04d}'.replace('.', '')
290
+ )
291
+
292
+ def set_pause_time_min(self, time: int) -> str:
293
+ value = min(899, max(0, time))
294
+ return self._send_command(
295
+ self.Address + self._pause_time_min_ + f'{value:04d}'.replace('.', '')
296
+ )
297
+
298
+ def set_pause_time_hour(self, time: int) -> str:
299
+ value = min(999, max(0, time))
300
+ return self._send_command(
301
+ self.Address + self._pause_time_hour_ + f'{value:04d}'.replace('.', '')
302
+ )
303
+
304
+ def get_disp_cycles(self) -> str:
305
+ return self._send_command(self.Address + self._disp_cycles_)
306
+
307
+ def set_disp_cycles(self, cycles: int) -> str:
308
+ value = min(9999, max(0, cycles))
309
+ return self._send_command(
310
+ self.Address + self._pause_time_ + f'{value:04d}'.replace('.', '')
311
+ )
312
+
313
+ def get_total_vol(self) -> str:
314
+ return self._send_command(self.Address + self._total_vol_)
315
+
316
+ def get_reset_total_vol(self) -> str:
317
+ return self._send_command(self.Address + self._reset_total_vol_)
318
+
319
+ def store_app_params(self) -> str:
320
+ return self._send_command(self.Address + self._store_app_params_)
321
+
322
+ def set_default_vals(self) -> str:
323
+ return self._send_command(self.Address + self._set_default_vals_)
324
+
325
+ def get_foot_switch(self) -> str:
326
+ return self._send_command(self.Address + self._foot_switch_)
327
+
328
+ def set_foot_switch_toggle(self) -> str:
329
+ return self._send_command(self.Address + self._foot_switch_ + '0000')
330
+
331
+ def set_foot_switch_direct(self) -> str:
332
+ return self._send_command(self.Address + self._foot_switch_ + '0001')
333
+
334
+ def float_to_mmmmee(self, value: float) -> str:
335
+ s_val = f'{value:0.3E}'
336
+ s_val = s_val[0:-2] + s_val[-1:]
337
+
338
+ s_val = s_val.replace('.', '').replace('E', '')
339
+ return s_val
340
+
341
+ def mmmmee_to_float(self, value: str) -> str:
342
+ if len(value) > 1:
343
+ return value[0] + '.' + value[1:]
344
+ else:
345
+ return value
346
+
347
+
348
+ class reglo_digital_module(QMainWindow):
349
+ '''A GUI for RegloDigital control | Inherits QMainWindow'''
350
+
351
+ def __init__(self, *args, **kwargs):
352
+ super().__init__(*args, **kwargs)
353
+
354
+ # setting title
355
+ self.setWindowTitle('microEye RegloDigital module')
356
+
357
+ # setting geometry
358
+ self.setGeometry(0, 0, 800, 600)
359
+
360
+ # Statusbar time
361
+ self.statusBar().showMessage(
362
+ 'Time: ' + QDateTime.currentDateTime().toString('hh:mm:ss,zzz')
363
+ )
364
+
365
+ # RegloDigital
366
+ self.regloDigital = RegloDigital(Port='COM8', Address=1)
367
+ self.regloMessage = ''
368
+ self.cycle = None
369
+
370
+ # Layout
371
+ self.LayoutInit()
372
+
373
+ # We need to store a reference to the plotted line
374
+ # somewhere, so we can apply the new data to it.
375
+ self._plot_ref = None
376
+
377
+ # Statues Bar Timer
378
+ self.timer = QtCore.QTimer()
379
+ self.timer.setInterval(10)
380
+ self.timer.timeout.connect(self.update_gui)
381
+ self.timer.start()
382
+
383
+ # Threading
384
+ self._threadpool = QtCore.QThreadPool.globalInstance()
385
+ print(
386
+ f'Multithreading with maximum {self._threadpool.maxThreadCount()} threads'
387
+ )
388
+
389
+ self.show()
390
+
391
+ # centered
392
+ self.center()
393
+
394
+ def center(self):
395
+ '''Centers the window within the screen.'''
396
+ qtRectangle = self.frameGeometry()
397
+ centerPoint = QApplication.primaryScreen().availableGeometry().center()
398
+ qtRectangle.moveCenter(centerPoint)
399
+ self.move(qtRectangle.topLeft())
400
+
401
+ def LayoutInit(self):
402
+ # the main tab widget
403
+ self.main_widget = QtWidgets.QWidget()
404
+ self.main_view = QtWidgets.QHBoxLayout()
405
+ self.main_widget.setLayout(self.main_view)
406
+ self.setCentralWidget(self.main_widget)
407
+
408
+ # columns
409
+ self.first_col = QtWidgets.QVBoxLayout()
410
+ self.second_col = QtWidgets.QVBoxLayout()
411
+
412
+ self.main_view.addLayout(self.first_col)
413
+ self.main_view.addLayout(self.second_col)
414
+
415
+ # general
416
+ self.general_group = QtWidgets.QGroupBox('General')
417
+ self.general_Layout = QtWidgets.QVBoxLayout()
418
+ self.general_group.setLayout(self.general_Layout)
419
+
420
+ # modes
421
+ self.modes_group = QtWidgets.QGroupBox('Modes')
422
+ self.modes_Layout = QtWidgets.QVBoxLayout()
423
+ self.modes_group.setLayout(self.modes_Layout)
424
+
425
+ # configurations
426
+ self.config_group = QtWidgets.QGroupBox('Config.')
427
+ self.config_Layout = QtWidgets.QVBoxLayout()
428
+ self.config_group.setLayout(self.config_Layout)
429
+
430
+ # incremental cycles
431
+ self.cycle_group = QtWidgets.QGroupBox('Cycles')
432
+ self.cycle_Layout = QtWidgets.QVBoxLayout()
433
+ self.cycle_group.setLayout(self.cycle_Layout)
434
+
435
+ self.first_col.addWidget(self.general_group)
436
+ self.first_col.addWidget(self.modes_group)
437
+ self.second_col.addWidget(self.config_group)
438
+ self.second_col.addWidget(self.cycle_group)
439
+
440
+ self.portname_comboBox = QtWidgets.QComboBox()
441
+
442
+ # adding available serial ports
443
+ for info in QtSerialPort.QSerialPortInfo.availablePorts():
444
+ self.portname_comboBox.addItem(info.portName())
445
+
446
+ self.connect_btn = QtWidgets.QPushButton(
447
+ 'connect', clicked=lambda: self.connect()
448
+ )
449
+ self.toggle_btn = QtWidgets.QPushButton(
450
+ 'start (Idle)', clicked=lambda: self.toggle_pump()
451
+ )
452
+ self.clockwise_btn = QtWidgets.QPushButton(
453
+ 'clockwise (Flow Back)', clicked=lambda: self.regloDigital.set_clockwise()
454
+ )
455
+ self.c_clockwise_btn = QtWidgets.QPushButton(
456
+ 'counter-clockwise (Empty)',
457
+ clicked=lambda: self.regloDigital.set_counter_clockwise(),
458
+ )
459
+
460
+ # mode buttons
461
+ self.rpm_mode_btn = QtWidgets.QPushButton(
462
+ 'RPM Mode', clicked=lambda: self.regloDigital.set_pump_rpm_mode()
463
+ )
464
+ self.flowrate_mode_btn = QtWidgets.QPushButton(
465
+ 'Flowrate Mode', clicked=lambda: self.regloDigital.set_pump_flowrate_mode()
466
+ )
467
+ self.disp_time_mode_btn = QtWidgets.QPushButton(
468
+ 'Dispense Time Mode', clicked=lambda: self.regloDigital.set_disp_time_mode()
469
+ )
470
+ self.disp_vol_mode_btn = QtWidgets.QPushButton(
471
+ 'Dispense Volume Mode',
472
+ clicked=lambda: self.regloDigital.set_disp_vol_mode(),
473
+ )
474
+ self.pause_mode_btn = QtWidgets.QPushButton(
475
+ 'Pause Time Mode', clicked=lambda: self.regloDigital.set_pause_time_mode()
476
+ )
477
+ self.disp_pause_mode_btn = QtWidgets.QPushButton(
478
+ 'Dispense Time / Pause Time Mode',
479
+ clicked=lambda: self.regloDigital.set_disp_time_pause_mode(),
480
+ )
481
+ self.vol_period_mode_btn = QtWidgets.QPushButton(
482
+ 'Volume dependent dispensing within a period mode',
483
+ clicked=lambda: self.regloDigital.set_vol_period_mode(),
484
+ )
485
+ self.total_mode_btn = QtWidgets.QPushButton(
486
+ 'Total Mode', clicked=lambda: self.regloDigital.set_total_mode()
487
+ )
488
+
489
+ self.get_tubing_diameter_btn = QtWidgets.QPushButton(
490
+ 'Set flowrate', clicked=lambda: self.regloDigital.set_flowrate_mlmin(80)
491
+ )
492
+
493
+ self.general_Layout.addWidget(QtWidgets.QLabel('Pump Serial Port:'))
494
+ self.general_Layout.addWidget(self.portname_comboBox)
495
+ self.general_Layout.addWidget(self.connect_btn)
496
+ self.general_Layout.addWidget(self.toggle_btn)
497
+ self.general_Layout.addWidget(self.clockwise_btn)
498
+ self.general_Layout.addWidget(self.c_clockwise_btn)
499
+
500
+ self.modes_Layout.addWidget(self.rpm_mode_btn)
501
+ self.modes_Layout.addWidget(self.flowrate_mode_btn)
502
+ self.modes_Layout.addWidget(self.disp_time_mode_btn)
503
+ self.modes_Layout.addWidget(self.disp_vol_mode_btn)
504
+ self.modes_Layout.addWidget(self.pause_mode_btn)
505
+ self.modes_Layout.addWidget(self.disp_pause_mode_btn)
506
+ self.modes_Layout.addWidget(self.vol_period_mode_btn)
507
+ self.modes_Layout.addWidget(self.total_mode_btn)
508
+ self.modes_Layout.addWidget(self.get_tubing_diameter_btn)
509
+ self.modes_Layout.addStretch()
510
+
511
+ speed_layout = QtWidgets.QHBoxLayout()
512
+ self.speed_box = QtWidgets.QLineEdit()
513
+ self.speed_box.setValidator(QtGui.QDoubleValidator(0.0, 160.0, 2))
514
+ self.set_speed_btn = QtWidgets.QPushButton(
515
+ 'Set',
516
+ clicked=lambda: self.regloDigital.set_speed(float(self.speed_box.text())),
517
+ )
518
+ self.get_speed_btn = QtWidgets.QPushButton(
519
+ 'Get', clicked=lambda: self.speed_box.setText(self.regloDigital.get_speed())
520
+ )
521
+
522
+ speed_layout.addWidget(QtWidgets.QLabel('Speed (rpm): '))
523
+ speed_layout.addWidget(self.speed_box)
524
+ speed_layout.addWidget(self.set_speed_btn)
525
+ speed_layout.addWidget(self.get_speed_btn)
526
+
527
+ flowrate_layout = QtWidgets.QHBoxLayout()
528
+ self.flowrate_box = QtWidgets.QLineEdit()
529
+ self.flowrate_box.setValidator(QtGui.QDoubleValidator(0.0, 2.683e-3, 3))
530
+ self.set_flowrate_btn = QtWidgets.QPushButton(
531
+ 'Set',
532
+ clicked=lambda: self.regloDigital.set_flowrate_mlmin(
533
+ float(self.flowrate_box.text())
534
+ ),
535
+ )
536
+ self.get_flowrate_btn = QtWidgets.QPushButton(
537
+ 'Get',
538
+ clicked=lambda: self.flowrate_box.setText(
539
+ self.regloDigital.get_flowrate_mlmin()
540
+ ),
541
+ )
542
+
543
+ flowrate_layout.addWidget(QtWidgets.QLabel('Flow Rate (l/min): '))
544
+ flowrate_layout.addWidget(self.flowrate_box)
545
+ flowrate_layout.addWidget(self.set_flowrate_btn)
546
+ flowrate_layout.addWidget(self.get_flowrate_btn)
547
+
548
+ disp_vol_layout = QtWidgets.QHBoxLayout()
549
+ self.disp_vol_box = QtWidgets.QLineEdit()
550
+ self.disp_vol_box.setValidator(QtGui.QDoubleValidator(0.0, 1000, 3))
551
+ self.set_disp_vol_btn = QtWidgets.QPushButton(
552
+ 'Set',
553
+ clicked=lambda: self.regloDigital.set_disp_volume_ml(
554
+ float(self.disp_vol_box.text())
555
+ ),
556
+ )
557
+
558
+ disp_vol_layout.addWidget(QtWidgets.QLabel('Dispensed Vol (l): '))
559
+ disp_vol_layout.addWidget(self.disp_vol_box)
560
+ disp_vol_layout.addWidget(self.set_disp_vol_btn)
561
+
562
+ disp_time_layout = QtWidgets.QHBoxLayout()
563
+ self.disp_time_box = QtWidgets.QLineEdit()
564
+ self.disp_time_box.setValidator(QtGui.QIntValidator(0, 9999))
565
+ self.set_disp_time_btn = QtWidgets.QPushButton(
566
+ 'Set',
567
+ clicked=lambda: self.regloDigital.set_disp_time(
568
+ float(self.disp_time_box.text())
569
+ ),
570
+ )
571
+
572
+ disp_time_layout.addWidget(QtWidgets.QLabel('Dispensed Time (1/10s): '))
573
+ disp_time_layout.addWidget(self.disp_time_box)
574
+ disp_time_layout.addWidget(self.set_disp_time_btn)
575
+
576
+ pause_time_layout = QtWidgets.QHBoxLayout()
577
+ self.pause_time_box = QtWidgets.QLineEdit()
578
+ self.pause_time_box.setValidator(QtGui.QIntValidator(0, 9999))
579
+ self.set_pause_time_btn = QtWidgets.QPushButton(
580
+ 'Set',
581
+ clicked=lambda: self.regloDigital.set_pause_time(
582
+ float(self.pause_time_box.text())
583
+ ),
584
+ )
585
+
586
+ pause_time_layout.addWidget(QtWidgets.QLabel('Pause Time (1/10s): '))
587
+ pause_time_layout.addWidget(self.pause_time_box)
588
+ pause_time_layout.addWidget(self.set_pause_time_btn)
589
+
590
+ self.config_Layout.addLayout(speed_layout)
591
+ self.config_Layout.addLayout(flowrate_layout)
592
+ self.config_Layout.addLayout(disp_vol_layout)
593
+ self.config_Layout.addLayout(disp_time_layout)
594
+ self.config_Layout.addLayout(pause_time_layout)
595
+
596
+ self.cycle_label = QtWidgets.QLabel('Number of Cycles: 1')
597
+ self.cycle_slider = QtWidgets.QSlider(Qt.Orientation.Horizontal)
598
+ self.cycle_slider.setMinimum(2)
599
+ self.cycle_slider.setMaximum(50)
600
+ self.cycle_slider.valueChanged.connect(self.cycles_changed)
601
+
602
+ self.inc_label = QtWidgets.QLabel('Speed Increment: 1')
603
+ self.inc_slider = QtWidgets.QSlider(Qt.Orientation.Horizontal)
604
+ self.inc_slider.setMinimum(1)
605
+ self.inc_slider.setMaximum(50)
606
+ self.inc_slider.valueChanged.connect(self.inc_changed)
607
+
608
+ self.cycles_table = QtWidgets.QTableWidget()
609
+ self.cycles_table.setRowCount(1)
610
+ self.cycles_table.setColumnCount(1)
611
+ self.cycles_table.setHorizontalHeaderLabels(['Speed Increment'])
612
+ self.cycles_table.resizeColumnsToContents()
613
+ for r in range(1):
614
+ self.cycles_table.setItem(
615
+ r, 0, QtWidgets.QTableWidgetItem(str(self.inc_slider.value()))
616
+ )
617
+
618
+ self.start_cycle_btn = QtWidgets.QPushButton(
619
+ 'Start Cycles', clicked=lambda: self.cycles_start()
620
+ )
621
+
622
+ self.stop_cycle_btn = QtWidgets.QPushButton(
623
+ 'Stop Cycles', clicked=lambda: self.cycles_stop()
624
+ )
625
+
626
+ self.cycle_Layout.addWidget(self.cycle_label)
627
+ self.cycle_Layout.addWidget(self.cycle_slider)
628
+ self.cycle_Layout.addWidget(self.inc_label)
629
+ self.cycle_Layout.addWidget(self.inc_slider)
630
+ self.cycle_Layout.addWidget(self.cycles_table)
631
+ self.cycle_Layout.addWidget(self.start_cycle_btn)
632
+ self.cycle_Layout.addWidget(self.stop_cycle_btn)
633
+ self.cycle_Layout.addStretch()
634
+
635
+ def toggle_pump(self):
636
+ if self.regloDigital.isOpen():
637
+ if self.regloDigital.active:
638
+ self.regloDigital.stop()
639
+ else:
640
+ self.regloDigital.start()
641
+
642
+ def cycles_changed(self, value):
643
+ self.cycle_label.setText(f'Number of Cycles: {value:d}')
644
+ self.cycles_table.setRowCount(value)
645
+ for r in range(value):
646
+ self.cycles_table.setItem(
647
+ r, 0, QtWidgets.QTableWidgetItem(str(self.inc_slider.value()))
648
+ )
649
+
650
+ def inc_changed(self, value):
651
+ self.inc_label.setText(f'Speed Increment: {value:d}')
652
+ self.cycles_table.setRowCount(self.cycle_slider.value())
653
+ for r in range(self.cycle_slider.value()):
654
+ self.cycles_table.setItem(
655
+ r, 0, QtWidgets.QTableWidgetItem(str(self.inc_slider.value()))
656
+ )
657
+
658
+ def cycles_start(self):
659
+ if self.regloDigital.isOpen():
660
+ self.cycle = {
661
+ 'cycles': self.cycle_slider.value(),
662
+ 'incr': [
663
+ float(self.cycles_table.item(j, 0).text())
664
+ for j in range(self.cycle_slider.value())
665
+ ],
666
+ 'start': QDateTime.currentDateTime(),
667
+ 'duration': int(self.disp_time_box.text()) * 100,
668
+ 'index': 0,
669
+ }
670
+ self.cycle['cycle_dur'] = self.cycle['duration'] / self.cycle['cycles']
671
+
672
+ self.regloDigital.stop()
673
+ self.regloDigital.set_disp_time_mode()
674
+ self.regloDigital.start()
675
+
676
+ def cycles_stop(self):
677
+ self.cycle = None
678
+ self.regloDigital.stop()
679
+
680
+ def update_gui(self):
681
+ self.regloDigital.get_status()
682
+
683
+ msec = None
684
+ if self.cycle is not None:
685
+ msec = self.cycle['start'].msecsTo(QDateTime.currentDateTime())
686
+
687
+ if msec > self.cycle['duration']:
688
+ self.cycle = None
689
+ elif msec > self.cycle['index'] * self.cycle['cycle_dur']:
690
+ speed = (
691
+ float(self.speed_box.text())
692
+ + self.cycle['incr'][self.cycle['index']]
693
+ )
694
+ speed = min(max(speed, 0), 160.0)
695
+ self.regloDigital.set_speed(speed)
696
+ self.speed_box.setText(f'{speed:.2f}')
697
+ self.flowrate_box.setText(self.regloDigital.get_flowrate_mlmin())
698
+ self.cycle['index'] = self.cycle['index'] + 1
699
+
700
+ self.statusBar().showMessage(
701
+ 'Time: {} | Rx: {} | Status {} | Direction {} | Cycle {} sec'.format(
702
+ QDateTime.currentDateTime().toString('hh:mm:ss,zzz'),
703
+ self.regloDigital.response,
704
+ ('Active' if self.regloDigital.active else 'Idle'),
705
+ ('CW' if self.regloDigital.clockwise else 'CCW'),
706
+ 0.00 if msec is None else msec / 1000,
707
+ )
708
+ )
709
+
710
+ # update indicators
711
+ if self.regloDigital.isOpen():
712
+ self.connect_btn.setStyleSheet('background-color: green')
713
+ self.connect_btn.setText('disconnect')
714
+ else:
715
+ self.connect_btn.setStyleSheet('background-color: red')
716
+ self.connect_btn.setText('connect')
717
+
718
+ if self.regloDigital.active:
719
+ self.toggle_btn.setStyleSheet('background-color: green')
720
+ self.toggle_btn.setText('stop (Active)')
721
+ else:
722
+ self.toggle_btn.setStyleSheet('background-color: red')
723
+ self.toggle_btn.setText('start (Idle)')
724
+
725
+ def connect(self):
726
+ try:
727
+ if not self.regloDigital.isOpen():
728
+ if 'COM' in self.portname_comboBox.currentText():
729
+ self.regloDigital.setPortName(self.portname_comboBox.currentText())
730
+ self.regloDigital.open(QtCore.QIODevice.OpenModeFlag.ReadWrite)
731
+ self.regloDigital.get_status()
732
+ self.speed_box.setText(self.regloDigital.get_speed())
733
+ self.flowrate_box.setText(self.regloDigital.get_flowrate_mlmin())
734
+ self.disp_vol_box.setText(self.regloDigital.get_disp_volume_ml())
735
+ self.disp_time_box.setText(self.regloDigital.get_disp_time())
736
+ self.pause_time_box.setText(self.regloDigital.get_pause_time())
737
+ else:
738
+ self.regloDigital.close()
739
+ except Exception:
740
+ traceback.print_exc()
741
+
742
+ def StartGUI():
743
+ '''Initializes a new QApplication and reglo_digital_module.
744
+
745
+ Use
746
+ -------
747
+ app, window = reglo_digital_module.StartGUI()
748
+
749
+ app.exec()
750
+
751
+ Returns
752
+ -------
753
+ tuple (QApplication, reglo_digital_module)
754
+ Returns a tuple with QApp and reglo_digital_module main window.
755
+ '''
756
+ return StartGUI(reglo_digital_module)
757
+
758
+
759
+ if __name__ == '__main__':
760
+ app, window = reglo_digital_module.StartGUI()
761
+ app.exec()