PaIRS-UniNa 0.2.9__cp310-cp310-macosx_11_0_universal2.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 (333) hide show
  1. PaIRS_UniNa/Calibration_Tab.py +346 -0
  2. PaIRS_UniNa/Changes.txt +162 -0
  3. PaIRS_UniNa/Custom_Top.py +303 -0
  4. PaIRS_UniNa/Explorer.py +3168 -0
  5. PaIRS_UniNa/FolderLoop.py +562 -0
  6. PaIRS_UniNa/Input_Tab.py +831 -0
  7. PaIRS_UniNa/Input_Tab_CalVi.py +786 -0
  8. PaIRS_UniNa/Input_Tab_tools.py +3022 -0
  9. PaIRS_UniNa/Log_Tab.py +110 -0
  10. PaIRS_UniNa/Output_Tab.py +922 -0
  11. PaIRS_UniNa/PaIRS.py +18 -0
  12. PaIRS_UniNa/PaIRS_PIV.py +873 -0
  13. PaIRS_UniNa/PaIRS_pypacks.py +1421 -0
  14. PaIRS_UniNa/Process_Tab.py +1757 -0
  15. PaIRS_UniNa/Process_Tab_CalVi.py +313 -0
  16. PaIRS_UniNa/Process_Tab_Disp.py +163 -0
  17. PaIRS_UniNa/Process_Tab_Min.py +120 -0
  18. PaIRS_UniNa/ResizePopup.py +55 -0
  19. PaIRS_UniNa/SPIVCalHelp.py +155 -0
  20. PaIRS_UniNa/Saving_tools.py +296 -0
  21. PaIRS_UniNa/TabTools.py +1254 -0
  22. PaIRS_UniNa/Vis_Tab.py +2169 -0
  23. PaIRS_UniNa/Vis_Tab_CalVi.py +983 -0
  24. PaIRS_UniNa/Whatsnew.py +130 -0
  25. PaIRS_UniNa/_PaIRS_PIV.so +0 -0
  26. PaIRS_UniNa/__init__.py +6 -0
  27. PaIRS_UniNa/__main__.py +45 -0
  28. PaIRS_UniNa/addwidgets_ps.py +1133 -0
  29. PaIRS_UniNa/calib.py +1488 -0
  30. PaIRS_UniNa/calibView.py +833 -0
  31. PaIRS_UniNa/gPaIRS.py +3914 -0
  32. PaIRS_UniNa/gPalette.py +189 -0
  33. PaIRS_UniNa/icons/abort.png +0 -0
  34. PaIRS_UniNa/icons/about.png +0 -0
  35. PaIRS_UniNa/icons/align_all.png +0 -0
  36. PaIRS_UniNa/icons/announcement.png +0 -0
  37. PaIRS_UniNa/icons/automatic_levels_off.png +0 -0
  38. PaIRS_UniNa/icons/automatic_levels_on.png +0 -0
  39. PaIRS_UniNa/icons/automatic_off.png +0 -0
  40. PaIRS_UniNa/icons/automatic_on.png +0 -0
  41. PaIRS_UniNa/icons/automatic_size_off.png +0 -0
  42. PaIRS_UniNa/icons/automatic_size_on.png +0 -0
  43. PaIRS_UniNa/icons/axes.png +0 -0
  44. PaIRS_UniNa/icons/background.png +0 -0
  45. PaIRS_UniNa/icons/background_vectors.png +0 -0
  46. PaIRS_UniNa/icons/bin_off.png +0 -0
  47. PaIRS_UniNa/icons/bin_on.png +0 -0
  48. PaIRS_UniNa/icons/browse_file_c.png +0 -0
  49. PaIRS_UniNa/icons/browse_folder_c.png +0 -0
  50. PaIRS_UniNa/icons/brush_cursor.png +0 -0
  51. PaIRS_UniNa/icons/bugfix.png +0 -0
  52. PaIRS_UniNa/icons/cal_proc.png +0 -0
  53. PaIRS_UniNa/icons/cal_proc_off.png +0 -0
  54. PaIRS_UniNa/icons/cal_step.png +0 -0
  55. PaIRS_UniNa/icons/cal_step_off.png +0 -0
  56. PaIRS_UniNa/icons/calibrate.png +0 -0
  57. PaIRS_UniNa/icons/calibration_logo.png +0 -0
  58. PaIRS_UniNa/icons/change_folder.png +0 -0
  59. PaIRS_UniNa/icons/change_folder_off.png +0 -0
  60. PaIRS_UniNa/icons/checklist.png +0 -0
  61. PaIRS_UniNa/icons/clean.png +0 -0
  62. PaIRS_UniNa/icons/clean_run.png +0 -0
  63. PaIRS_UniNa/icons/close.png +0 -0
  64. PaIRS_UniNa/icons/close_all.png +0 -0
  65. PaIRS_UniNa/icons/close_project.png +0 -0
  66. PaIRS_UniNa/icons/close_workspace.png +0 -0
  67. PaIRS_UniNa/icons/colormap.png +0 -0
  68. PaIRS_UniNa/icons/colormaps/Accent.png +0 -0
  69. PaIRS_UniNa/icons/colormaps/BrBG.png +0 -0
  70. PaIRS_UniNa/icons/colormaps/Dark2.png +0 -0
  71. PaIRS_UniNa/icons/colormaps/PRGn.png +0 -0
  72. PaIRS_UniNa/icons/colormaps/Paired.png +0 -0
  73. PaIRS_UniNa/icons/colormaps/Pastel1.png +0 -0
  74. PaIRS_UniNa/icons/colormaps/Pastel2.png +0 -0
  75. PaIRS_UniNa/icons/colormaps/PiYG.png +0 -0
  76. PaIRS_UniNa/icons/colormaps/PuOr.png +0 -0
  77. PaIRS_UniNa/icons/colormaps/RdBu.png +0 -0
  78. PaIRS_UniNa/icons/colormaps/RdGy.png +0 -0
  79. PaIRS_UniNa/icons/colormaps/RdYlBu.png +0 -0
  80. PaIRS_UniNa/icons/colormaps/RdYlGn.png +0 -0
  81. PaIRS_UniNa/icons/colormaps/Set1.png +0 -0
  82. PaIRS_UniNa/icons/colormaps/Set2.png +0 -0
  83. PaIRS_UniNa/icons/colormaps/Set3.png +0 -0
  84. PaIRS_UniNa/icons/colormaps/Spectral.png +0 -0
  85. PaIRS_UniNa/icons/colormaps/Wistia.png +0 -0
  86. PaIRS_UniNa/icons/colormaps/afmhot.png +0 -0
  87. PaIRS_UniNa/icons/colormaps/autumn.png +0 -0
  88. PaIRS_UniNa/icons/colormaps/binary.png +0 -0
  89. PaIRS_UniNa/icons/colormaps/blackVector.png +0 -0
  90. PaIRS_UniNa/icons/colormaps/blueVector.png +0 -0
  91. PaIRS_UniNa/icons/colormaps/bone.png +0 -0
  92. PaIRS_UniNa/icons/colormaps/brg.png +0 -0
  93. PaIRS_UniNa/icons/colormaps/bwr.png +0 -0
  94. PaIRS_UniNa/icons/colormaps/cividis.png +0 -0
  95. PaIRS_UniNa/icons/colormaps/cool.png +0 -0
  96. PaIRS_UniNa/icons/colormaps/coolwarm.png +0 -0
  97. PaIRS_UniNa/icons/colormaps/copper.png +0 -0
  98. PaIRS_UniNa/icons/colormaps/cubehelix.png +0 -0
  99. PaIRS_UniNa/icons/colormaps/cyanVector.png +0 -0
  100. PaIRS_UniNa/icons/colormaps/flag.png +0 -0
  101. PaIRS_UniNa/icons/colormaps/gist_heat.png +0 -0
  102. PaIRS_UniNa/icons/colormaps/gray.png +0 -0
  103. PaIRS_UniNa/icons/colormaps/greenVector.png +0 -0
  104. PaIRS_UniNa/icons/colormaps/hot.png +0 -0
  105. PaIRS_UniNa/icons/colormaps/hsv.png +0 -0
  106. PaIRS_UniNa/icons/colormaps/inferno.png +0 -0
  107. PaIRS_UniNa/icons/colormaps/jet.png +0 -0
  108. PaIRS_UniNa/icons/colormaps/magentaVector.png +0 -0
  109. PaIRS_UniNa/icons/colormaps/magma.png +0 -0
  110. PaIRS_UniNa/icons/colormaps/ocean.png +0 -0
  111. PaIRS_UniNa/icons/colormaps/pink.png +0 -0
  112. PaIRS_UniNa/icons/colormaps/plasma.png +0 -0
  113. PaIRS_UniNa/icons/colormaps/prism.png +0 -0
  114. PaIRS_UniNa/icons/colormaps/rainbow.png +0 -0
  115. PaIRS_UniNa/icons/colormaps/redVector.png +0 -0
  116. PaIRS_UniNa/icons/colormaps/seismic.png +0 -0
  117. PaIRS_UniNa/icons/colormaps/spring.png +0 -0
  118. PaIRS_UniNa/icons/colormaps/summer.png +0 -0
  119. PaIRS_UniNa/icons/colormaps/tab10.png +0 -0
  120. PaIRS_UniNa/icons/colormaps/tab20.png +0 -0
  121. PaIRS_UniNa/icons/colormaps/tab20b.png +0 -0
  122. PaIRS_UniNa/icons/colormaps/tab20c.png +0 -0
  123. PaIRS_UniNa/icons/colormaps/terrain.png +0 -0
  124. PaIRS_UniNa/icons/colormaps/twilight.png +0 -0
  125. PaIRS_UniNa/icons/colormaps/viridis.png +0 -0
  126. PaIRS_UniNa/icons/colormaps/whiteVector.png +0 -0
  127. PaIRS_UniNa/icons/colormaps/winter.png +0 -0
  128. PaIRS_UniNa/icons/colormaps/yellowVector.png +0 -0
  129. PaIRS_UniNa/icons/common_region.png +0 -0
  130. PaIRS_UniNa/icons/common_region_off.png +0 -0
  131. PaIRS_UniNa/icons/completed.png +0 -0
  132. PaIRS_UniNa/icons/contourf_off.png +0 -0
  133. PaIRS_UniNa/icons/contourf_on.png +0 -0
  134. PaIRS_UniNa/icons/copy.png +0 -0
  135. PaIRS_UniNa/icons/copy_process.png +0 -0
  136. PaIRS_UniNa/icons/copy_process_off.png +0 -0
  137. PaIRS_UniNa/icons/copygrid.png +0 -0
  138. PaIRS_UniNa/icons/cursor_lamp.png +0 -0
  139. PaIRS_UniNa/icons/cut.png +0 -0
  140. PaIRS_UniNa/icons/cut_warnings.png +0 -0
  141. PaIRS_UniNa/icons/darkmode.png +0 -0
  142. PaIRS_UniNa/icons/debug_run.png +0 -0
  143. PaIRS_UniNa/icons/delete.png +0 -0
  144. PaIRS_UniNa/icons/deleteErr.png +0 -0
  145. PaIRS_UniNa/icons/disp_step.png +0 -0
  146. PaIRS_UniNa/icons/disp_step_off.png +0 -0
  147. PaIRS_UniNa/icons/down.png +0 -0
  148. PaIRS_UniNa/icons/edit_list.png +0 -0
  149. PaIRS_UniNa/icons/editing.png +0 -0
  150. PaIRS_UniNa/icons/example_list.png +0 -0
  151. PaIRS_UniNa/icons/find_all_planes.png +0 -0
  152. PaIRS_UniNa/icons/find_plane.png +0 -0
  153. PaIRS_UniNa/icons/flaticon_PaIRS.png +0 -0
  154. PaIRS_UniNa/icons/flaticon_PaIRS_beta.png +0 -0
  155. PaIRS_UniNa/icons/flaticon_PaIRS_download.png +0 -0
  156. PaIRS_UniNa/icons/flaticon_PaIRS_download_warning.png +0 -0
  157. PaIRS_UniNa/icons/flip_y_off.png +0 -0
  158. PaIRS_UniNa/icons/flip_y_on.png +0 -0
  159. PaIRS_UniNa/icons/focusErrr.png +0 -0
  160. PaIRS_UniNa/icons/folder_loop_cleanup.png +0 -0
  161. PaIRS_UniNa/icons/folder_loop_cleanup_off.png +0 -0
  162. PaIRS_UniNa/icons/gear.gif +0 -0
  163. PaIRS_UniNa/icons/gear.png +0 -0
  164. PaIRS_UniNa/icons/ger.png +0 -0
  165. PaIRS_UniNa/icons/greenv.png +0 -0
  166. PaIRS_UniNa/icons/guide.png +0 -0
  167. PaIRS_UniNa/icons/icon_CalVi.png +0 -0
  168. PaIRS_UniNa/icons/icon_PaIRS.png +0 -0
  169. PaIRS_UniNa/icons/import.png +0 -0
  170. PaIRS_UniNa/icons/import_set.png +0 -0
  171. PaIRS_UniNa/icons/information.png +0 -0
  172. PaIRS_UniNa/icons/information2.png +0 -0
  173. PaIRS_UniNa/icons/input_logo.png +0 -0
  174. PaIRS_UniNa/icons/issue.png +0 -0
  175. PaIRS_UniNa/icons/laser_NTR.png +0 -0
  176. PaIRS_UniNa/icons/laser_TR_double.png +0 -0
  177. PaIRS_UniNa/icons/laser_TR_single.png +0 -0
  178. PaIRS_UniNa/icons/link.png +0 -0
  179. PaIRS_UniNa/icons/linked.png +0 -0
  180. PaIRS_UniNa/icons/loaded.png +0 -0
  181. PaIRS_UniNa/icons/loading_2.gif +0 -0
  182. PaIRS_UniNa/icons/log_logo.png +0 -0
  183. PaIRS_UniNa/icons/logo_CalVi.png +0 -0
  184. PaIRS_UniNa/icons/logo_CalVi_completo.png +0 -0
  185. PaIRS_UniNa/icons/logo_CalVi_party.png +0 -0
  186. PaIRS_UniNa/icons/logo_PaIRS.png +0 -0
  187. PaIRS_UniNa/icons/logo_PaIRS_completo.png +0 -0
  188. PaIRS_UniNa/icons/logo_PaIRS_download.png +0 -0
  189. PaIRS_UniNa/icons/logo_PaIRS_party_rect.png +0 -0
  190. PaIRS_UniNa/icons/logo_PaIRS_rect.png +0 -0
  191. PaIRS_UniNa/icons/logo_opaco.png +0 -0
  192. PaIRS_UniNa/icons/mask.png +0 -0
  193. PaIRS_UniNa/icons/measure.png +0 -0
  194. PaIRS_UniNa/icons/measure_off.png +0 -0
  195. PaIRS_UniNa/icons/min_proc.png +0 -0
  196. PaIRS_UniNa/icons/min_proc_off.png +0 -0
  197. PaIRS_UniNa/icons/min_step.png +0 -0
  198. PaIRS_UniNa/icons/min_step_off.png +0 -0
  199. PaIRS_UniNa/icons/minus.png +0 -0
  200. PaIRS_UniNa/icons/mirror_u.png +0 -0
  201. PaIRS_UniNa/icons/mirror_v.png +0 -0
  202. PaIRS_UniNa/icons/mirror_x.png +0 -0
  203. PaIRS_UniNa/icons/mirror_y.png +0 -0
  204. PaIRS_UniNa/icons/mtplt.png +0 -0
  205. PaIRS_UniNa/icons/new.png +0 -0
  206. PaIRS_UniNa/icons/new_workspace.png +0 -0
  207. PaIRS_UniNa/icons/news.png +0 -0
  208. PaIRS_UniNa/icons/normal_run.png +0 -0
  209. PaIRS_UniNa/icons/open.png +0 -0
  210. PaIRS_UniNa/icons/open_image.png +0 -0
  211. PaIRS_UniNa/icons/open_new_window.png +0 -0
  212. PaIRS_UniNa/icons/open_result.png +0 -0
  213. PaIRS_UniNa/icons/open_workspace.png +0 -0
  214. PaIRS_UniNa/icons/output_logo.png +0 -0
  215. PaIRS_UniNa/icons/paste_above.png +0 -0
  216. PaIRS_UniNa/icons/paste_below.png +0 -0
  217. PaIRS_UniNa/icons/pause.png +0 -0
  218. PaIRS_UniNa/icons/paused.png +0 -0
  219. PaIRS_UniNa/icons/pencil_bw.png +0 -0
  220. PaIRS_UniNa/icons/piv_proc.png +0 -0
  221. PaIRS_UniNa/icons/piv_proc_off.png +0 -0
  222. PaIRS_UniNa/icons/piv_step.png +0 -0
  223. PaIRS_UniNa/icons/piv_step_off.png +0 -0
  224. PaIRS_UniNa/icons/plane.png +0 -0
  225. PaIRS_UniNa/icons/play.png +0 -0
  226. PaIRS_UniNa/icons/plus.png +0 -0
  227. PaIRS_UniNa/icons/process_logo.png +0 -0
  228. PaIRS_UniNa/icons/process_loop.png +0 -0
  229. PaIRS_UniNa/icons/project.png +0 -0
  230. PaIRS_UniNa/icons/pylog.png +0 -0
  231. PaIRS_UniNa/icons/python_warning.png +0 -0
  232. PaIRS_UniNa/icons/queue.png +0 -0
  233. PaIRS_UniNa/icons/quit.png +0 -0
  234. PaIRS_UniNa/icons/read.png +0 -0
  235. PaIRS_UniNa/icons/read_list.png +0 -0
  236. PaIRS_UniNa/icons/redo.png +0 -0
  237. PaIRS_UniNa/icons/redx.png +0 -0
  238. PaIRS_UniNa/icons/reset.png +0 -0
  239. PaIRS_UniNa/icons/reset_levels.png +0 -0
  240. PaIRS_UniNa/icons/resize_icon.png +0 -0
  241. PaIRS_UniNa/icons/restore.png +0 -0
  242. PaIRS_UniNa/icons/restore_undo.png +0 -0
  243. PaIRS_UniNa/icons/rotate_clock.png +0 -0
  244. PaIRS_UniNa/icons/rotate_counter.png +0 -0
  245. PaIRS_UniNa/icons/rotate_v_clock.png +0 -0
  246. PaIRS_UniNa/icons/rotate_v_counter.png +0 -0
  247. PaIRS_UniNa/icons/running.gif +0 -0
  248. PaIRS_UniNa/icons/running.png +0 -0
  249. PaIRS_UniNa/icons/running_warn.png +0 -0
  250. PaIRS_UniNa/icons/sandglass.png +0 -0
  251. PaIRS_UniNa/icons/save.png +0 -0
  252. PaIRS_UniNa/icons/save_and_stop.png +0 -0
  253. PaIRS_UniNa/icons/save_cfg.png +0 -0
  254. PaIRS_UniNa/icons/saveas.png +0 -0
  255. PaIRS_UniNa/icons/saveas_workspace.png +0 -0
  256. PaIRS_UniNa/icons/scale_all.png +0 -0
  257. PaIRS_UniNa/icons/scale_down.png +0 -0
  258. PaIRS_UniNa/icons/scale_up.png +0 -0
  259. PaIRS_UniNa/icons/scan_list.png +0 -0
  260. PaIRS_UniNa/icons/scan_path.png +0 -0
  261. PaIRS_UniNa/icons/scan_path_loop.png +0 -0
  262. PaIRS_UniNa/icons/scan_path_loop_off.png +0 -0
  263. PaIRS_UniNa/icons/search.png +0 -0
  264. PaIRS_UniNa/icons/showIW_off.png +0 -0
  265. PaIRS_UniNa/icons/showIW_on.png +0 -0
  266. PaIRS_UniNa/icons/show_all.png +0 -0
  267. PaIRS_UniNa/icons/sort.png +0 -0
  268. PaIRS_UniNa/icons/sort_reversed.png +0 -0
  269. PaIRS_UniNa/icons/spiv_proc.png +0 -0
  270. PaIRS_UniNa/icons/spiv_proc_off.png +0 -0
  271. PaIRS_UniNa/icons/spiv_setup_no.png +0 -0
  272. PaIRS_UniNa/icons/spiv_setup_ok.png +0 -0
  273. PaIRS_UniNa/icons/star.png +0 -0
  274. PaIRS_UniNa/icons/step_inheritance.png +0 -0
  275. PaIRS_UniNa/icons/subMIN_off.png +0 -0
  276. PaIRS_UniNa/icons/subMIN_on.png +0 -0
  277. PaIRS_UniNa/icons/tom.png +0 -0
  278. PaIRS_UniNa/icons/trash.png +0 -0
  279. PaIRS_UniNa/icons/undo.png +0 -0
  280. PaIRS_UniNa/icons/unedited.png +0 -0
  281. PaIRS_UniNa/icons/unina_dii.png +0 -0
  282. PaIRS_UniNa/icons/uninitialized.png +0 -0
  283. PaIRS_UniNa/icons/unlink.png +0 -0
  284. PaIRS_UniNa/icons/unwrap_items.png +0 -0
  285. PaIRS_UniNa/icons/up.png +0 -0
  286. PaIRS_UniNa/icons/updating_import.gif +0 -0
  287. PaIRS_UniNa/icons/updating_pairs.gif +0 -0
  288. PaIRS_UniNa/icons/vectorColor.png +0 -0
  289. PaIRS_UniNa/icons/vettore.png +0 -0
  290. PaIRS_UniNa/icons/view.png +0 -0
  291. PaIRS_UniNa/icons/view_off.png +0 -0
  292. PaIRS_UniNa/icons/vis_logo.png +0 -0
  293. PaIRS_UniNa/icons/waiting_circle.png +0 -0
  294. PaIRS_UniNa/icons/warning.png +0 -0
  295. PaIRS_UniNa/icons/warning_circle.png +0 -0
  296. PaIRS_UniNa/icons/window.png +0 -0
  297. PaIRS_UniNa/icons/workspace.png +0 -0
  298. PaIRS_UniNa/icons/wrap_items.png +0 -0
  299. PaIRS_UniNa/icons/write_list.png +0 -0
  300. PaIRS_UniNa/listLib.py +303 -0
  301. PaIRS_UniNa/mtfPIV.py +256 -0
  302. PaIRS_UniNa/parForMulti.py +435 -0
  303. PaIRS_UniNa/parForWorkers.py +593 -0
  304. PaIRS_UniNa/pivParFor.py +235 -0
  305. PaIRS_UniNa/plt_util.py +141 -0
  306. PaIRS_UniNa/preProcParFor.py +155 -0
  307. PaIRS_UniNa/procTools.py +1439 -0
  308. PaIRS_UniNa/readcfg.py +52 -0
  309. PaIRS_UniNa/rqrdpckgs.txt +9 -0
  310. PaIRS_UniNa/stereoPivParFor.py +227 -0
  311. PaIRS_UniNa/tAVarie.py +215 -0
  312. PaIRS_UniNa/tabSplitter.py +612 -0
  313. PaIRS_UniNa/ui_Calibration_Tab.py +578 -0
  314. PaIRS_UniNa/ui_Custom_Top.py +296 -0
  315. PaIRS_UniNa/ui_Input_Tab.py +1101 -0
  316. PaIRS_UniNa/ui_Input_Tab_CalVi.py +1283 -0
  317. PaIRS_UniNa/ui_Log_Tab.py +263 -0
  318. PaIRS_UniNa/ui_Output_Tab.py +2362 -0
  319. PaIRS_UniNa/ui_Process_Tab.py +3810 -0
  320. PaIRS_UniNa/ui_Process_Tab_CalVi.py +1549 -0
  321. PaIRS_UniNa/ui_Process_Tab_Disp.py +1141 -0
  322. PaIRS_UniNa/ui_Process_Tab_Min.py +437 -0
  323. PaIRS_UniNa/ui_ResizePopup.py +204 -0
  324. PaIRS_UniNa/ui_Vis_Tab.py +1628 -0
  325. PaIRS_UniNa/ui_Vis_Tab_CalVi.py +1251 -0
  326. PaIRS_UniNa/ui_Whatsnew.py +132 -0
  327. PaIRS_UniNa/ui_gPairs.py +877 -0
  328. PaIRS_UniNa/ui_infoPaIRS.py +551 -0
  329. PaIRS_UniNa/whatsnew.txt +6 -0
  330. pairs_unina-0.2.9.dist-info/METADATA +166 -0
  331. pairs_unina-0.2.9.dist-info/RECORD +333 -0
  332. pairs_unina-0.2.9.dist-info/WHEEL +5 -0
  333. pairs_unina-0.2.9.dist-info/top_level.txt +2 -0
@@ -0,0 +1,1421 @@
1
+ from math import ceil, floor
2
+ #PrintTA.flagPriority=PrintTAPriority.always
3
+ Flag_DEBUG=False
4
+ Flag_DEBUG_PARPOOL=False
5
+ FlagPrintTime=False
6
+ FlagPrintCoding=False
7
+
8
+ pwddbg='Buss4Co1Pied1'
9
+ time_warnings_debug=-1 #10000 #milliseconds #5000
10
+
11
+ import uuid
12
+ basefold='./'
13
+ basefold_DEBUGOptions=[]
14
+ basefold_DEBUG='./'
15
+ basefold_DEBUG_VIS=''
16
+ #basefold='B:/dl/apairs/jetcross'
17
+
18
+
19
+ developerIDs={
20
+ 'GP_Win_Office': '231128824800632', #'0x7824af430781',
21
+ 'GP_Win_Office_New': '140626882900161', #'0x7824af430781',
22
+ 'GP_Mac_Laptop': 'V94LRP93FV', #'0xa275dd445ab0',
23
+ 'GP_WSL' : 'b44ec1c0e5a74ffd97bb050c39ef6cb1',
24
+ 'TA_Win_Office': '160983906000941', #'0xccb0da8c896e'
25
+ 'TA_Win_Office_New': '231128824801036', #??
26
+ }
27
+
28
+ import psutil,subprocess
29
+ def getCurrentID():
30
+ #return hex(uuid.getnode())
31
+ serial_number=None
32
+ try:
33
+ if psutil.LINUX:
34
+ def get_linux_serial():
35
+ from pathlib import Path
36
+ candidates = [
37
+ Path("/sys/class/dmi/id/board_serial"),
38
+ Path("/sys/class/dmi/id/product_uuid"),
39
+ Path("/etc/machine-id"), Path("/var/lib/dbus/machine-id") #WSL
40
+ ]
41
+ for p in candidates:
42
+ try:
43
+ if p.is_file():
44
+ val = p.read_text(errors="ignore").strip()
45
+ if val and val.lower() not in {
46
+ "none", "unknown", "not specified", "to be filled by o.e.m."
47
+ }:
48
+ return val
49
+ except Exception as e:
50
+ if Flag_DEBUG:
51
+ print(f"Error while retrieving motherboard serial number: {e}")
52
+ continue
53
+ return None
54
+ serial_number = get_linux_serial()
55
+ """"
56
+ # On Linux, the motherboard serial number can be obtained from the /sys/class/dmi/id/board_serial file
57
+ with open('/sys/class/dmi/id/board_serial', 'r') as f:
58
+ serial_number = f.read().strip()
59
+ """
60
+ elif psutil.WINDOWS:
61
+ # On Windows, the motherboard serial number can be obtained using WMI
62
+ output = subprocess.check_output(["wmic", "baseboard", "get", "SerialNumber"]).decode('utf-8')
63
+ serial_number = output.strip().split('\n')[1].strip()
64
+ elif psutil.MACOS:
65
+ # On macOS, the motherboard serial number can be obtained using the system_profiler command
66
+ output = subprocess.check_output(["system_profiler", "SPHardwareDataType"])
67
+ for line in output.splitlines():
68
+ if b'Serial Number (system)' in line:
69
+ serial_number = line.split(b':')[1].strip().decode('utf-8')
70
+ except Exception as e:
71
+ if Flag_DEBUG:
72
+ print(f"Error while retrieving motherboard serial number: {e}")
73
+ return serial_number
74
+
75
+ currentID=getCurrentID()
76
+ FlagAddMotherBoard=False
77
+ if currentID in (developerIDs['GP_Win_Office'],developerIDs['GP_Win_Office_New']): #gerardo windows
78
+ basefold_DEBUG='C:/desk/PIV_Img/_data/PIV_data/virtual_case/'
79
+ basefold_DEBUGOptions=[
80
+ 'C:/desk/PIV_Img/img1/',
81
+ 'C:/desk/PIV_Img/_data/PIV_data/virtual_case/',
82
+ 'C:/desk/PIV_Img/_data/PIV_data/real_case/',
83
+ 'C:/desk/PIV_Img/_data/Calibration_data/pinhole/',
84
+ 'C:/desk/PIV_Img/_data/Calibration_data/cylinder/',
85
+ ]
86
+ basefold_DEBUG_VIS='C:/desk/PIV_Img/_data/PIV_data/real_case/'
87
+ elif currentID==developerIDs['GP_WSL']:
88
+ basefold_DEBUG='/mnt/c/desk/PIV_Img/_data/PIV_data/virtual_case/'
89
+ basefold_DEBUGOptions=[
90
+ '/mnt/c/desk/PIV_Img/img1/',
91
+ '/mnt/c/desk/PIV_Img/_data/PIV_data/virtual_case/',
92
+ '/mnt/c/desk/PIV_Img/_data/PIV_data/real_case/',
93
+ '/mnt/c/desk/PIV_Img/_data/Calibration_data/pinhole/',
94
+ '/mnt/c/desk/PIV_Img/_data/Calibration_data/cylinder/',
95
+ ]
96
+ basefold_DEBUG_VIS='/mnt/c/desk/PIV_Img/_data/PIV_data/real_case/'
97
+ elif currentID==developerIDs['GP_Mac_Laptop']: #gerardo mac
98
+ basefold_DEBUG='/Users/gerardo/Desktop/PIV_Img/swirler_png/' #'/Users/gerardo/Desktop/PIV_Img/img1/'
99
+ basefold_DEBUGOptions=[
100
+ '/Users/gerardo/Desktop/PIV_Img/img1/',
101
+ '/Users/gerardo/Desktop/PaIRS_examples/PIV_data/virtual_case/',
102
+ #'/Users/gerardo/Desktop/PaIRS_examples/PIV_data/virtual_case_2/',
103
+ '/Users/gerardo/Desktop/PaIRS_examples/PIV_data/real_case/',
104
+ '/Users/gerardo/Desktop/PaIRS_examples/SPIV_data/real_case/img/',
105
+ '/Users/gerardo/Desktop/PaIRS_examples/Calibration_data/pinhole/',
106
+ '/Users/gerardo/Desktop/PaIRS_examples/Calibration_data/cylinder/'
107
+ ]
108
+ basefold_DEBUG_VIS='/Users/gerardo/Desktop/PaIRS_examples/PIV_data/real_case/'
109
+ basefold_DEBUG_VIS='/Users/gerardo/Desktop/PIV_Img/img1/'
110
+ elif currentID in (developerIDs['TA_Win_Office'],developerIDs['TA_Win_Office_New']): #TA windows
111
+ basefold_DEBUG=r'C:\desk\Attuali\PythonLibC\PIV\img'
112
+ basefold_DEBUGOptions=[
113
+ 'C:/desk/PIV_Img/img1/',
114
+ 'C:/desk/PIV_Img/swirler_png/',
115
+ '../../img/calib/',
116
+ r'C:\desk\Attuali\PythonLibC\PIV\img',
117
+ ]
118
+ basefold_DEBUG_VIS=''
119
+ else:
120
+ FlagAddMotherBoard=True
121
+
122
+ #fontName='Inter'
123
+ #fontName='Cambria'
124
+ fontName='Arial'
125
+ fontPixelSize=14
126
+ dfontLog=2
127
+ fontPixelSize_lim=[8,20]
128
+ import platform
129
+ if (platform.system() == "Linux"):
130
+ fontName='sans-serif'
131
+
132
+ Flag_SHOWSPLASH=False
133
+ Flag_GRAPHICS=True #if True PaIRS plots while processing
134
+ Flag_NATIVEDIALOGS=True
135
+ Flag_DISABLE_onUpdate=False
136
+ Flag_RESIZEONRUN=False
137
+ Flag_GROUPSEPARATOR=True
138
+
139
+ imin_im_pair=1 #minimum index value for image pair
140
+
141
+ f_empty_width=250 #blank space in scrollable area within the main window
142
+ time_ScrollBar=250 #time of animation of scroll area
143
+ time_callback2_async=0 #time to test async callbacks
144
+ time_showSplashOnTop=250
145
+ pathCompleterLength=10
146
+
147
+ fileChanges='Changes.txt'
148
+ fileWhatsNew=['whatsnew.txt','whatwasnew.txt']
149
+ icons_path="icons/"
150
+
151
+ from psutil import cpu_count
152
+ NUMTHREADS_MAX=cpu_count(logical=True)#-1
153
+ if NUMTHREADS_MAX<1: NUMTHREADS_MAX=1
154
+ ParFor_sleepTime=0.1
155
+ #multithreading
156
+ FlagStopWorkers=[0]#messo qui ma utilizzato solo da min e PIV
157
+ NUMTHREADS_gPaIRS=0
158
+ SleepTime_Workers=0.5 #for multithreading and other stuff
159
+ timeOutWorker=0 # used in parfor when the proces is stuck
160
+
161
+ from .__init__ import __version__,__subversion__,__year__,__mail__
162
+ from PySide6 import QtCore, QtGui, QtWidgets
163
+ from PySide6.QtCore import*
164
+ from PySide6.QtGui import *
165
+ from PySide6.QtWidgets import*
166
+ from typing import cast
167
+ if Flag_DEBUG_PARPOOL: import debugpy
168
+
169
+ import numpy as np
170
+ import scipy.io, pickle
171
+ from PIL import Image
172
+ from PIL.ImageQt import ImageQt
173
+ import sys, os, glob, copy, re, traceback, datetime
174
+ from time import sleep as timesleep
175
+ from collections import namedtuple
176
+ from .plt_util import writePlt, readPlt
177
+ #from multiprocessing import cpu_count
178
+
179
+ from .tAVarie import *
180
+ deltaTimePlot=0.75
181
+ import concurrent.futures
182
+ import gc#garbage collection si può eliminare
183
+ from .mtfPIV import *
184
+
185
+ import sys
186
+ import concurrent.futures
187
+ import asyncio
188
+
189
+ _old_init_QAction = QAction.__init__
190
+ def _new_init_QAction(self, *args, **kwargs):
191
+ _old_init_QAction(self, *args, **kwargs)
192
+ try:
193
+ self.setIconVisibleInMenu(True)
194
+ except Exception:
195
+ pass
196
+ QAction.__init__ = _new_init_QAction
197
+ _old_init_QMenu = QMenu.__init__
198
+ def _new_init_QMenu(self, *args, **kwargs):
199
+ _old_init_QMenu(self, *args, **kwargs)
200
+ try:
201
+ self.menuAction().setIconVisibleInMenu(True)
202
+ except Exception:
203
+ pass
204
+ QMenu.__init__ = _new_init_QMenu
205
+
206
+ # --- Patch dei metodi QMenu che CREANO/AGGIUNGONO azioni (copre gli overload C++) ---
207
+ _old_addAction = QMenu.addAction
208
+ def _new_addAction(self, *args, **kwargs):
209
+ act:QAction = _old_addAction(self, *args, **kwargs) # può essere creato lato C++
210
+ try:
211
+ if isinstance(act, QAction):
212
+ act.setIconVisibleInMenu(True)
213
+ except Exception:
214
+ pass
215
+ return act
216
+ QMenu.addAction = _new_addAction
217
+
218
+ Flag_ISEXE=getattr(sys, 'frozen', False) #made by pyInstaller
219
+ EXEurl='https://www.pairs.unina.it/#download'
220
+
221
+ class ColorPrint:
222
+ def __init__(self,flagTime=False,prio=PrintTAPriority.medium,faceStd=PrintTA.faceStd,flagFullDebug=False):
223
+ self.flagTime=flagTime
224
+ self.prio=prio
225
+ self.faceStd=faceStd
226
+ self.flagFullDebug=flagFullDebug
227
+ self.setPrints()
228
+
229
+ def setPrints(self):
230
+ if self.flagTime:
231
+ self.white = lambda flagReset=0, *args, **kwargs: PrintTA(PrintTA.white, self.faceStd, self.prio).prTime(flagReset,*args,**kwargs)
232
+ self.red = lambda flagReset=0, *args, **kwargs: PrintTA(PrintTA.red, self.faceStd, self.prio).prTime(flagReset,*args,**kwargs)
233
+ self.green = lambda flagReset=0, *args, **kwargs: PrintTA(PrintTA.green, self.faceStd, self.prio).prTime(flagReset,*args,**kwargs)
234
+ self.blue = lambda flagReset=0, *args, **kwargs: PrintTA(PrintTA.blue, self.faceStd, self.prio).prTime(flagReset,*args,**kwargs)
235
+ self.cyan = lambda flagReset=0, *args, **kwargs: PrintTA(PrintTA.cyan, self.faceStd, self.prio).prTime(flagReset,*args,**kwargs)
236
+ self.magenta = lambda flagReset=0, *args, **kwargs: PrintTA(PrintTA.magenta, self.faceStd, self.prio).prTime(flagReset,*args,**kwargs)
237
+ self.yellow = lambda flagReset=0, *args, **kwargs: PrintTA(PrintTA.yellow, self.faceStd, self.prio).prTime(flagReset,*args,**kwargs)
238
+ else:
239
+ self.white = PrintTA(PrintTA.white, self.faceStd, self.prio).pr
240
+ self.red = PrintTA(PrintTA.red, self.faceStd, self.prio).pr
241
+ self.green = PrintTA(PrintTA.green, self.faceStd, self.prio).pr
242
+ self.blue = PrintTA(PrintTA.blue, self.faceStd, self.prio).pr
243
+ self.cyan = PrintTA(PrintTA.cyan, self.faceStd, self.prio).pr
244
+ self.magenta = PrintTA(PrintTA.magenta, self.faceStd, self.prio).pr
245
+ self.yellow = PrintTA(PrintTA.yellow, self.faceStd, self.prio).pr
246
+
247
+ #if prio is assigned to never, in the gPaIRS initializiation the printing is deactivated, otherwise activated
248
+ #if prio is > veryLow, then by default the printing is activated after gPaIRS initialization
249
+ #flagFullDebug=True means that the printing is available only if fullDebug mode is active
250
+ class GPaIRSPrint:
251
+ def __init__(self):
252
+ self.Info=ColorPrint(prio=PrintTAPriority.medium)
253
+ self.Time=ColorPrint(prio=PrintTAPriority.medium if FlagPrintTime else PrintTAPriority.veryLow,flagTime=True,faceStd=PrintTA.faceUnderline)
254
+ self.Error=ColorPrint(prio=PrintTAPriority.medium,faceStd=PrintTA.faceBold)
255
+ self.IOError=ColorPrint(prio=PrintTAPriority.veryLow,faceStd=PrintTA.faceBold)
256
+ self.Process=ColorPrint(prio=PrintTAPriority.veryLow)
257
+ self.Callback=ColorPrint(prio=PrintTAPriority.veryLow)
258
+ self.TABparDiff=ColorPrint(prio=PrintTAPriority.veryLow)
259
+ self.PlotTime=ColorPrint(prio=PrintTAPriority.veryLow,flagTime=True,faceStd=PrintTA.faceUnderline,flagFullDebug=True)
260
+ self.Coding=ColorPrint(prio=PrintTAPriority.medium if FlagPrintCoding else PrintTAPriority.never,flagFullDebug=True)
261
+
262
+ pri=GPaIRSPrint()
263
+ printTypes={}
264
+ for npt,pt in pri.__dict__.items():
265
+ printTypes[npt]=pt.prio in (PrintTAPriority.medium,PrintTAPriority.mediumHigh,PrintTAPriority.high,PrintTAPriority.always)
266
+
267
+ def activateFlagDebug(Flag=True):
268
+ ''' used to activate the debug mode; when called with false disables'''
269
+ Flag_DEBUG=Flag
270
+ PrintTA.flagPriority=PrintTAPriority.veryLow if Flag_DEBUG else PrintTAPriority.always
271
+ global basefold
272
+ from .gPaIRS import Flag_fullDEBUG
273
+ if not Flag_fullDEBUG:
274
+ basefold='./'
275
+ else:
276
+ basefold=basefold_DEBUG
277
+
278
+ PaIRS_Header=f'PaIRS - version {__version__}\n'+\
279
+ 'Particle Image Reconstruction Software\n'+\
280
+ f'(C) {__year__} Gerardo Paolillo & Tommaso Astarita.\nAll rights reserved.\n'+\
281
+ f'email: {__mail__}\n'+\
282
+ '****************************************\n'
283
+
284
+ from .parForMulti import *
285
+ #from pkg_resources import resource_filename
286
+ from .parForMulti import ParForMul
287
+
288
+ import faulthandler # per capire da dove vengono gli errori c
289
+ faulthandler.enable()
290
+
291
+ if __package__ or "." in __name__:
292
+ import PaIRS_UniNa.PaIRS_PIV as PaIRS_lib
293
+ else:
294
+ import sys
295
+ if (platform.system() == "Darwin"):
296
+ sys.path.append('../lib/mac')
297
+ else:
298
+ #sys.path.append('PaIRS_PIV')
299
+ sys.path.append('../lib')
300
+ import PaIRS_PIV as PaIRS_lib
301
+
302
+ if __package__ or "." in __name__:
303
+ import importlib.resources as resources
304
+ resources_path = resources.files(__package__)
305
+ foldPaIRS = str(resources_path)+"\\"
306
+ foldPaIRS = foldPaIRS.replace('\\', '/')
307
+ else:
308
+ foldPaIRS='./'
309
+ class ProcessTypes:
310
+ null=None
311
+ min=0
312
+ piv=1
313
+ spiv=2
314
+ tpiv=3
315
+ cal=10
316
+
317
+ singleCamera=[piv]
318
+ threeCameras=[min,tpiv]
319
+
320
+ class StepTypes:
321
+ null=None
322
+ min=0
323
+ piv=1
324
+ spiv=2
325
+ cal=10
326
+ disp=11
327
+
328
+ process={
329
+ ProcessTypes.null: '-',
330
+ ProcessTypes.min: 'minimum',
331
+ ProcessTypes.piv: 'PIV',
332
+ ProcessTypes.spiv: 'SPIV',
333
+ ProcessTypes.tpiv: 'TPIV',
334
+ ProcessTypes.cal: 'calibration',
335
+ }
336
+ process_items=[v for v in process.values()]
337
+ process_ord=range(len(process_items))
338
+ class outExt:
339
+ #legacy
340
+ cfg='.pairs_cfg'
341
+ dum='.pairs_dum'
342
+
343
+ #Workspaces and projects
344
+ wksp='.pairs_wksp'
345
+ proj='.pairs_proj'
346
+
347
+ #StepTypes
348
+ min='.pairs_min'
349
+ piv='.pairs_piv'
350
+ spiv='.pairs_spiv'
351
+ cal='.pairs_cal'
352
+ calvi='.calvi'
353
+ disp='.pairs_disp'
354
+
355
+ #Further types of variable
356
+ #PIV process
357
+ pro='.pairs_pro'
358
+ #CalVi
359
+ cfg_calvi='.calvi_cfg'
360
+ pla='.pairs_pla'
361
+
362
+
363
+ lastcfgname='lastWorkSpace'+outExt.wksp
364
+ fileChanges=foldPaIRS+'Changes.txt'
365
+ icons_path=foldPaIRS+icons_path
366
+
367
+ if not Flag_ISEXE:
368
+ fileWhatsNew=[foldPaIRS+f for f in fileWhatsNew]
369
+
370
+ lastcfgname=foldPaIRS+lastcfgname
371
+ pro_path=foldPaIRS+"pro/"
372
+ else:
373
+ from pathlib import Path
374
+ exe_dir = str(Path(sys.argv[0]).resolve().parent)+'/'
375
+ fileWhatsNew=[foldPaIRS+fileWhatsNew[0],exe_dir+fileWhatsNew[1]]
376
+ lastcfgname = exe_dir + lastcfgname
377
+ pro_path = exe_dir + "pro/"
378
+
379
+
380
+ if not os.path.exists(pro_path):
381
+ try:
382
+ os.mkdir(pro_path)
383
+ except Exception as inst:
384
+ pri.Error.red(f'It was not possible to make the directory {pro_path}:\n{traceback.format_exc()}\n\n{inst}')
385
+ custom_list_file="pro_list.txt"
386
+
387
+ exts = Image.registered_extensions()
388
+ supported_exts = sorted({ex for ex, f in exts.items() if f in Image.OPEN})
389
+ text_filter = "Common image files (*.bmp *.gif *.ico *.jpeg *.jpg *.png *.tif *.tiff *.webp"\
390
+ + ");;"+" ;;".join(["{} ".format(fo[1:]) +"(*{})".format(fo) for fo in supported_exts])
391
+ #text_filter = "All files (*"\
392
+ # + ");;"+" ;;".join(["{} ".format(fo[1:]) +"(*{})".format(fo) for fo in supported_exts])
393
+ #text_filter = "All files ("+ " ".join(["*{}".format(fo) for fo in supported_exts])\
394
+ # + ");;"+" ;;".join(["{} ".format(fo[1:]) +"(*{})".format(fo) for fo in supported_exts])
395
+
396
+ if Flag_NATIVEDIALOGS:
397
+ optionNativeDialog=QFileDialog.Options()
398
+ else:
399
+ optionNativeDialog=QFileDialog.Option.DontUseNativeDialog
400
+
401
+ def warningDialog(self:QWidget,Message,time_milliseconds=0,flagScreenCenter=False,icon:QIcon=QIcon(),palette=None,pixmap=None,title='Warning!',flagRichText=False,flagNoButtons=False,addButton:dict=None,FlagStayOnTop=False,pixmapSize=64): #addButton=['Print Message',lambda: print(Message)]
402
+ dlg=None
403
+ if Message:
404
+ if isinstance(self,QMainWindow) and hasattr(self,'w_Input'):
405
+ dlg = QMessageBox(self.w_Input)
406
+ else:
407
+ dlg = QMessageBox(self)
408
+ dlg.setWindowTitle(title)
409
+ dlg.setText(str(Message))
410
+
411
+ if flagRichText: dlg.setTextFormat(Qt.TextFormat.RichText)
412
+ if flagNoButtons:
413
+ dlg.setStandardButtons(QMessageBox.StandardButton.NoButton)
414
+ else:
415
+ dlg.setStandardButtons(QMessageBox.StandardButton.Ok)
416
+ if addButton:
417
+ for addB, addAction in addButton.items():
418
+ abutt = dlg.addButton(addB, QtWidgets.QMessageBox.YesRole)
419
+ abutt.clicked.disconnect()
420
+ def aFun(fun):
421
+ fun()
422
+ dlg.done(0)
423
+ abutt.clicked.connect(lambda flag=None,fun=addAction: aFun(fun))
424
+ dlg.setIcon(QMessageBox.Warning)
425
+ if icon:
426
+ if type(icon)==QIcon: dlg.setWindowIcon(icon)
427
+ else:
428
+ try:
429
+ iconW=QIcon()
430
+ iconW.addFile(icon)
431
+ dlg.setWindowIcon(iconW)
432
+ except Exception as e:
433
+ pri.Error.red(f'Error while reading the window icon from the file {icon}:\n{e}')
434
+ else:
435
+ if not hasattr(self,'windowIcon') or not self.windowIcon():
436
+ iconW=QIcon()
437
+ iconW.addFile(icons_path+'icon_PaIRS.png')
438
+ dlg.setWindowIcon(iconW)
439
+ else:
440
+ dlg.setWindowIcon(self.windowIcon())
441
+ if palette:
442
+ dlg.setPalette(palette)
443
+ if pixmap:
444
+ dlg.setIconPixmap(QPixmap(pixmap).scaled(pixmapSize, pixmapSize, Qt.AspectRatioMode.KeepAspectRatio,Qt.SmoothTransformation))
445
+ if self:
446
+ dlg.setFont(self.font())
447
+ c=dlg.findChildren(QObject)
448
+ for w in c:
449
+ if hasattr(w,'setFont'):
450
+ font=w.font()
451
+ font.setFamily(fontName)
452
+ w.setFont(font)
453
+ #dlg.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
454
+ dlg.show()
455
+ if flagScreenCenter and hasattr(self,'maximumGeometry'):
456
+ geom=dlg.geometry()
457
+ geom.moveCenter(self.maximumGeometry.center())
458
+ dlg.setGeometry(geom)
459
+ if time_milliseconds:
460
+ QTimer.singleShot(time_milliseconds, lambda : dlg.done(0))
461
+ else:
462
+ if Flag_DEBUG and time_warnings_debug>=0:
463
+ QTimer.singleShot(time_warnings_debug, lambda : dlg.done(0))
464
+ if FlagStayOnTop: dlg.setWindowFlag(Qt.WindowStaysOnTopHint, True)
465
+ if not flagNoButtons: dlg.exec()
466
+ return dlg
467
+
468
+ def questionDialog(self,Message,icon=QMessageBox.Warning):
469
+ if isinstance(self,QMainWindow) and hasattr(self,'w_Input'):
470
+ dlg = QMessageBox(self.w_Input)
471
+ else:
472
+ dlg = QMessageBox(self)
473
+ dlg.setWindowTitle("Warning!")
474
+ dlg.setText(str(Message))
475
+ if not self.windowIcon():
476
+ icons_path+'icon_PaIRS.png'
477
+ iconW=QIcon()
478
+ iconW.addFile(icon)
479
+ dlg.setWindowIcon(iconW)
480
+ else:
481
+ dlg.setWindowIcon(self.windowIcon())
482
+
483
+ dlg.setStandardButtons(QMessageBox.Yes|QMessageBox.No)
484
+ dlg.setDefaultButton(QMessageBox.Yes)
485
+ dlg.setIcon(icon)
486
+ if self:
487
+ dlg.setFont(self.font())
488
+ c=dlg.findChildren(QObject)
489
+ for w in c:
490
+ if hasattr(w,'setFont'):
491
+ font=w.font()
492
+ font.setFamily(fontName)
493
+ w.setFont(font)
494
+ button = dlg.exec()
495
+ return button==QMessageBox.Yes
496
+
497
+ def inputDialog(self,title,label,icon=None,palette=None,completer_list=[],width=0,flagMouseCenter=False,flagScreenCenter=False):
498
+ dlg = QtWidgets.QInputDialog(self)
499
+ dlg.setWindowTitle(title)
500
+ dlg.setLabelText(label)
501
+ dlg.setTextValue("")
502
+ if icon:
503
+ dlg.setWindowIcon(icon)
504
+ if palette:
505
+ dlg.setPalette(palette)
506
+ le = dlg.findChild(QtWidgets.QLineEdit)
507
+ if self:
508
+ dlg.setFont(self.font())
509
+ c=dlg.findChildren(QObject)
510
+ for w in c:
511
+ if hasattr(w,'setFont'):
512
+ font=w.font()
513
+ font.setFamily(fontName)
514
+ w.setFont(font)
515
+
516
+ if len(completer_list):
517
+ completer = QtWidgets.QCompleter(completer_list, le)
518
+ completer.setCompletionMode(QCompleter.CompletionMode(1))
519
+ le.setCompleter(completer)
520
+
521
+ if not width: width=int(0.5*self.width())
522
+ dlg.resize(width,dlg.height())
523
+ dlg.updateGeometry()
524
+
525
+ if flagMouseCenter:
526
+ dlg.show()
527
+ geom = dlg.geometry()
528
+ geom.moveCenter(QtGui.QCursor.pos())
529
+ dlg.setGeometry(geom)
530
+
531
+ if flagScreenCenter and hasattr(self,'maximumGeometry'):
532
+ dlg.show()
533
+ geom=dlg.geometry()
534
+ geom.moveCenter(self.maximumGeometry.center())
535
+ dlg.setGeometry(geom)
536
+
537
+ c=dlg.findChildren(QObject)
538
+ for w in c:
539
+ if hasattr(w,'setFont'):
540
+ font=w.font()
541
+ font.setFamily(fontName)
542
+ w.setFont(font)
543
+
544
+ ok, text = (
545
+ dlg.exec() == QtWidgets.QDialog.Accepted,
546
+ dlg.textValue(),
547
+ )
548
+ return ok, text
549
+
550
+ def errorDialog(self,Message,*args):
551
+ if len(args): time_milliseconds = args[0]
552
+ else: time_milliseconds=0
553
+ if Message:
554
+ dlg = QMessageBox(self)
555
+ dlg.setWindowTitle("Warning!")
556
+ dlg.setText(str(Message))
557
+ copy_butt = dlg.addButton('Copy error to clipboard', QtWidgets.QMessageBox.YesRole)
558
+ copy_butt.clicked.disconnect()
559
+ def copy_fun():
560
+ QApplication.clipboard().setText(Message)
561
+ dlg.done(0)
562
+ copy_butt.clicked.connect(copy_fun)
563
+ ok_butt = dlg.addButton('Ok', QtWidgets.QMessageBox.YesRole)
564
+ dlg.setIcon(QMessageBox.Critical)
565
+ if self:
566
+ dlg.setFont(self.font())
567
+ c=dlg.findChildren(QObject)
568
+ for w in c:
569
+ if hasattr(w,'setFont'):
570
+ font=w.font()
571
+ font.setFamily(fontName)
572
+ w.setFont(font)
573
+ #dlg.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
574
+ if time_milliseconds>=0:
575
+ QTimer.singleShot(time_milliseconds, lambda : dlg.done(0))
576
+ else:
577
+ if Flag_DEBUG and time_warnings_debug>=0:
578
+ QTimer.singleShot(time_warnings_debug, lambda : dlg.done(0))
579
+ dlg.exec()
580
+
581
+ def printException(stringa='',flagMessage=Flag_DEBUG,flagDispDialog=False,exception=None): #timemilliseconds=-1 ***
582
+ ''' used to print when an exception is raised TA has decided that the printing function is a simple
583
+ print in this way we cannot have any problems when printing in non-compatible terminals
584
+ use with something like
585
+
586
+ try:
587
+ a=1/0
588
+ except :#non need to put a variable al the info are in traceback
589
+ printException()
590
+ * stringa is an additional string (to specify the point where the error comes from)
591
+ * flagMessage is a flag, if true the error message is generated; default value is Flag_DEBUG
592
+ * flagDispDialog is a flag, if true a critical dialog appears after the exception
593
+ * exception is the exception, normally you don't need it but for parForMul is required
594
+ '''
595
+ #print(f'***** ParForMul Exception ***** Deltat={time()-PrintTA.startTime}\n{traceback.format_exc()}',*args,**kwargs)
596
+ #print(sys.exc_info()[2])
597
+ Message=""
598
+ if flagMessage or flagDispDialog:
599
+ Message+=f'Please, mail to: {__mail__}\n\n'
600
+ Message+=f'***** PaIRS Exception ***** time={time()-PrintTA.startTime}\n'+stringa
601
+ Message+=f'***** traceback.print_exc() ***** \n'
602
+ if exception is None:
603
+ Message+=traceback.format_exc()
604
+ else:
605
+ Message+=''.join(traceback.format_exception(exception))
606
+ Message+=f'***** traceback.extract_stack() ***** \n'
607
+ # to print all the queue comment if not needed
608
+ for st in traceback.format_list( traceback.extract_stack()):
609
+ if 'PAIRS_GUI' in st and 'printException'not in st:# limits to files that have PAIRS_GUI in the path
610
+ Message+=st
611
+ Message+=f'***** PaIRS Exception -> End *****\n'
612
+ if Flag_DEBUG: print(Message)
613
+ #errorDialog(None,Message,timemilliseconds) ***
614
+ if flagDispDialog:
615
+ WarningMessage=f'PaIRS Exception!\n\n'+f'Do you want to copy the error message to the clipboard so that you can send it to: {__mail__}?'
616
+ flagYes=questionDialog(None,WarningMessage,QMessageBox.Critical)
617
+ if flagYes:
618
+ QApplication.clipboard().setText(Message)
619
+ return Message
620
+
621
+ def noPrint(*args,**kwargs):
622
+ pass
623
+
624
+ #import unidecode
625
+ def myStandardPath(path):
626
+ currpath = path.rstrip() # Remove trailing white spaces from the path
627
+ currpath = currpath.replace('\\', '/') # Replace all backslashes with forward slashes
628
+ currpath = currpath.rstrip('/') + '/' if currpath else './' # Add a trailing slash to the path if not present
629
+ currpath = re.sub('/+', '/', currpath) # Reduce consecutive slashes to a single slash
630
+ return currpath
631
+
632
+ def myStandardRoot(root):
633
+ currroot = root.rstrip() # Remove trailing white spaces from the root
634
+ currroot = currroot.replace('\\', '/') # Replace all backslashes with forward slashes
635
+ currroot = re.sub('/+', '/', currroot) # Reduce consecutive slashes to a single slash
636
+ return currroot
637
+
638
+ def relativizePath(currpath:str):
639
+ return currpath
640
+ directory_path = myStandardPath(os.getcwd())
641
+ if directory_path in currpath:
642
+ currpath=currpath.replace(directory_path,'./')
643
+ return currpath
644
+
645
+ def findFiles_sorted(pattern):
646
+ list_files=glob.glob(pattern)
647
+ files=sorted([re.sub(r'\\+',r'/',f) for f in list_files],key=str.lower)
648
+ return files
649
+
650
+ def transfIm(OUT,flagTransf:int=2,Images:list=[],flagRot=1):
651
+ ''' the output is a copy (not deep) of the input list)
652
+ flagTransf==0 solo img
653
+ flagTransf==1 solo piv
654
+ flagTransf==2 solo entrambi (default)
655
+ '''
656
+ if len(Images)==0: return
657
+ if OUT.FlagNone: return Images
658
+
659
+ if flagTransf==1: #solo PIV
660
+ ops=OUT.aimop
661
+ else:
662
+ if OUT.h>0 and OUT.w>0:
663
+ for i,_ in enumerate(Images):
664
+ Images[i]=Images[i][OUT.y:OUT.y+OUT.h,OUT.x:OUT.x+OUT.w]#limita l'img
665
+ ops=OUT.bimop if flagTransf==0 else OUT.vecop
666
+
667
+
668
+ if len(ops):
669
+ for i,_ in enumerate(Images):# non funziona se si fa il normale ciclo for img in Images
670
+ for op in ops:
671
+ if op==1: #rot 90 counter
672
+ Images[i]=np.rot90(Images[i],-1*flagRot)
673
+ elif op==-1: #rot 90 clock
674
+ Images[i]=np.rot90(Images[i],1*flagRot)
675
+ elif op==3: #flip
676
+ Images[i]=np.flipud(Images[i])
677
+ elif op==2:
678
+ Images[i]=np.fliplr(Images[i])
679
+ Images[i]=np.ascontiguousarray(Images[i])
680
+ return Images # the input list is also changed accordingly but it may come in handy in some situation in order to avoid explicitly make a copy
681
+
682
+ def transfVect(OUT,PIV):
683
+ x,y,u,v=transfIm(OUT,flagTransf=1,Images=[PIV.x,PIV.y,PIV.u,PIV.v],flagRot=1)# l'output non sarebbe necessario ma così mi fa anche la copia (per ora virtuale)
684
+ for op in OUT.aimop:
685
+ if op==-1: #rot 90 counter
686
+ # PIV.u,PIV.v=PIV.v,-PIV.u #questa da errore penso perchè non riesce a fare la copia
687
+ u,v=v,-u
688
+ x,y=y,OUT.w-x
689
+ elif op==1: #rot 90 clock
690
+ u,v=-v,u
691
+ x,y=OUT.h-y,x
692
+ elif op==2:#flip
693
+ u=-u
694
+ x=OUT.w-x
695
+ elif op==3: #flip
696
+ v=-v
697
+ y=OUT.h-y
698
+ return x,y,u,v
699
+
700
+ def readCustomListFile():
701
+ custom_list=[]
702
+ filename=pro_path+custom_list_file
703
+ if os.path.exists(filename):
704
+ try:
705
+ with open(filename,'r') as file:
706
+ while True:
707
+ line = file.readline()
708
+ if not line:
709
+ break
710
+ else:
711
+ l=line.strip()
712
+ if l: custom_list.append(l)
713
+ file.close()
714
+ except:
715
+ pri.Error.red(f'Error while opening the custom process list file: {filename}.\n{traceback.format_exc()}\n')
716
+ return custom_list
717
+
718
+ def setCustomList(task):
719
+ custom_list=readCustomListFile()
720
+ for k,name in enumerate(custom_list):
721
+ filename=pro_path+name+outExt.pro
722
+ try:
723
+ with open(filename,'rb') as file:
724
+ var=pickle.load(file)
725
+ task(var,name)
726
+ except Exception as inst:
727
+ pri.Error.red(f'Error while loading custom process file {filename}\t[from list]:\n{traceback.format_exc()}\n\n{inst}')
728
+ custom_list.pop(k)
729
+ if os.path.exists(filename):
730
+ os.remove(filename)
731
+ profiles=glob.glob(pro_path+f"*{outExt.pro}")
732
+ for f in profiles:
733
+ name=os.path.basename(f)[:-10]
734
+ if not name in custom_list:
735
+ filename=myStandardRoot(f)
736
+ try:
737
+ with open(filename,'rb') as file:
738
+ var=pickle.load(file)
739
+ task(var,name)
740
+ custom_list.append(name)
741
+ except Exception as inst:
742
+ pri.Error.red(f'Error while loading the custom process file {filename}\t[from disk]:\n{traceback.format_exc()}\n\n{inst}')
743
+ if os.path.exists(filename):
744
+ os.remove(filename)
745
+ rewriteCustomList(custom_list)
746
+ return custom_list
747
+
748
+ def rewriteCustomList(custom_list):
749
+ filename=pro_path+custom_list_file
750
+ try:
751
+ with open(filename,'w') as file:
752
+ for c in custom_list:
753
+ file.write(c+'\n')
754
+ file.close()
755
+ except:
756
+ pri.Error.red(f'Error while rewriting the custom process file {filename}\t[from disk]:\n{traceback.format_exc()}\n')
757
+
758
+ def identifierName(typeObject:str='proc'):
759
+ username=platform.system()+'-'+os.environ.get('USER', os.environ.get('USERNAME'))
760
+ date_time=QDate.currentDate().toString('yyyy/MM/dd')+'-'+\
761
+ QTime().currentTime().toString()
762
+ ppid=str(os.getppid())+'-'+str(os.getpid())
763
+ version='PaIRS-v'+__version__
764
+ version_user_info=version+'_'+username+'_'+date_time
765
+ id=ppid+'_'+str(uuid.uuid4())
766
+ name=version_user_info+'_'+typeObject+'_'+id
767
+ return name, username, __version__
768
+
769
+ def fileIdenitifierCheck(id: str, filename: str) -> bool:
770
+ """
771
+ Extract the date/time from the identifier 'name' and check whether 'filename'
772
+ has been modified after that timestamp.
773
+ Returns True if file is newer, False otherwise.
774
+ """
775
+
776
+ # --- Extract timestamp from name ---
777
+ # Expected pattern: ..._<date>-<time>_...
778
+ # Example: 'PaIRS-v0.2.8_Linux-user_2025/11/07-12:45:31_proc_...'
779
+ try:
780
+ parts = id.split('_')
781
+ date_str, time_str = parts[2].split('-')[0], parts[2].split('-')[1]
782
+ except Exception:
783
+ pri.Error.red("Identifier format not recognized: cannot extract date and time.")
784
+ return False
785
+
786
+ # Convert date/time strings into QDateTime
787
+ qdate = QDate.fromString(date_str, 'yyyy/MM/dd')
788
+ qtime = QTime.fromString(time_str, 'HH:mm:ss')
789
+ qdt_identifier = QDateTime(qdate, qtime)
790
+ qdt_identifier = qdt_identifier.addSecs(-1) #to be safe
791
+
792
+ if not qdt_identifier.isValid():
793
+ pri.Error.red("Parsed QDateTime is not valid. Check identifier format.")
794
+ return False
795
+
796
+ # --- File timestamp ---
797
+ if not os.path.exists(filename):
798
+ return False
799
+
800
+ file_mtime = os.path.getmtime(filename)
801
+ qdt_file = QDateTime.fromSecsSinceEpoch(int(file_mtime))
802
+
803
+ # True if file was modified after the timestamp stored in name
804
+ return qdt_file > qdt_identifier
805
+
806
+ PlainTextConverter=QtGui.QTextDocument()
807
+ def toPlainText(text):
808
+ PlainTextConverter.setHtml(text) #for safety
809
+ return PlainTextConverter.toPlainText()
810
+
811
+ def showTip(obj,message):
812
+ toolTipDuration=obj.toolTipDuration()
813
+ obj.setToolTipDuration(3000)
814
+ QToolTip.showText(QCursor.pos(),message)
815
+ obj.setToolTipDuration(toolTipDuration)
816
+
817
+ def clean_tree(tree:QTreeWidget):
818
+ def remove_children(item:QTreeWidgetItem):
819
+ while item.childCount() > 0:
820
+ child = item.takeChild(0)
821
+ remove_children(child)
822
+ del item
823
+
824
+ while tree.topLevelItemCount() > 0:
825
+ item = tree.takeTopLevelItem(0)
826
+ # Elimina ricorsivamente tutti i figli dell'elemento
827
+ remove_children(item)
828
+
829
+ def html_image(icon_path,size=16):
830
+ text=f"""
831
+ <img src="{icon_path}" width="{size}" height="{size}" style="margin-right: 0px;">
832
+ """
833
+ return text
834
+
835
+ def procOutName(self):
836
+ for attr_name, attr_value in vars(ProcessTypes).items():
837
+ if attr_value == self.Process:
838
+ procExt=getattr(outExt,attr_name)
839
+ break
840
+ return f'{self.outPathRoot}{procExt}'
841
+
842
+ def stepOutName(self):
843
+ for attr_name, attr_value in vars(StepTypes).items():
844
+ if attr_value == self.Step:
845
+ procExt=getattr(outExt,attr_name)
846
+ break
847
+ return f'{self.outPathRoot}{procExt}'
848
+
849
+ def findIDFromLog(file_path):
850
+ nMaximumLines=50
851
+ try:
852
+ with open(file_path, 'r') as file:
853
+ # Legge fino a 50 righe (o meno, se il file ha meno righe)
854
+ for _ in range(nMaximumLines):
855
+ line = file.readline()
856
+ if not line: # Fine del file
857
+ break
858
+ if line.startswith("PaIRS-v"):
859
+ return line.strip() # Ritorna la riga trovata senza spazi extra
860
+ return None # Nessuna riga trovata
861
+ except FileNotFoundError:
862
+ pri.Error.red(f"File not found: {file_path}")
863
+ return None
864
+
865
+ def resultCheck(tab,par,ind=None):
866
+ if ind is None: ind=par.ind
867
+ ITE=tab.gui.ui.Explorer.ITEfromInd(ind)
868
+ filename=stepOutName(ITE.procdata)+'.log'
869
+ if os.path.exists(filename):
870
+ id=findIDFromLog(filename)
871
+ FlagResult=id==ITE.procdata.name_proc
872
+ else:
873
+ FlagResult=False
874
+ return FlagResult
875
+
876
+ def runPaIRS(self,command='',flagQuestion=True):
877
+ Flag=__package__ or "." in __name__
878
+ pyCommands={
879
+ '' : 'import PaIRS; PaIRS.run()',
880
+ '-c': 'import PaIRS; PaIRS.cleanRun()',
881
+ '-d': 'import PaIRS; PaIRS.debugRun()',
882
+ '-calvi' : 'import CalVi; CalVi.run()',
883
+ '-calvi -c': 'import CalVi; CalVi.cleanRun()',
884
+ '-calvi -d': 'import CalVi; CalVi.debugRun()',
885
+ }
886
+ nameIstance={
887
+ '' : 'PaIRS',
888
+ '-c': 'PaIRS (clean mode)',
889
+ '-d': 'PaIRS (debug mode)',
890
+ '-calvi' : 'CalVi',
891
+ '-calvi -c': 'CalVi (clean mode)',
892
+ '-calvi -d': 'CalVi (debug mode)',
893
+ }
894
+
895
+ class SignalsinstPaIRS(QObject):
896
+ errorSignal=Signal()
897
+ class instPaIRS(QRunnable):
898
+ def __init__(self):
899
+ super(instPaIRS,self).__init__()
900
+ self.isRunning=True
901
+ self.signals=SignalsinstPaIRS()
902
+
903
+ def run(self):
904
+ try:
905
+ import subprocess
906
+ if Flag_ISEXE:
907
+ pri.Info.white(sys.executable+' '+command)
908
+ subprocess.call(sys.executable+' '+command,shell=True)
909
+ else:
910
+ if Flag: #launched from package
911
+ pri.Info.white(sys.executable+' -m PaIRS_UniNa '+command)
912
+ subprocess.call(sys.executable+' -m PaIRS_UniNa '+command,shell=True)
913
+ else:
914
+ pri.Info.white(sys.executable+' -c '+'"'+f"import os; os.chdir('{os.getcwd()}'); {pyCommands[command]}"+'"')
915
+ subprocess.call(sys.executable+' -c '+'"'+f"import os; os.chdir('{os.getcwd()}'); {pyCommands[command]}"+'"',shell=True)
916
+ self.isRunning=False
917
+ except Exception as inst:
918
+ pri.Error.red(inst)
919
+ self.signals.errorSignal.emit()
920
+
921
+ if flagQuestion:
922
+ Message='Are you sure to launch a new istance of '+nameIstance[command]+'?'
923
+ yes=questionDialog(self,Message)
924
+ if not yes: return
925
+ runnable = instPaIRS()
926
+ runnable.signals.errorSignal.connect(lambda: self.warningDialog('It was not possible to launch the module from the present application!\nPlease, retry in another Python environment.'))
927
+ QThreadPool.globalInstance().start(runnable)
928
+ if not hasattr(self,'SecondaryThreads'):
929
+ self.SecondaryThreads=[]
930
+ self.SecondaryThreads.append(runnable)
931
+
932
+ def showSplash(filename=''+ icons_path +'logo_PaIRS_completo.png'):
933
+ splash=QLabel()
934
+ splash_pix = QPixmap(filename)
935
+ splash.setPixmap(splash_pix)
936
+ splash.setScaledContents(True)
937
+ splash.setMaximumSize(360,360)
938
+ splash.setWindowFlags(Qt.Window|Qt.FramelessWindowHint)
939
+ splash.setAttribute(Qt.WA_NoSystemBackground)
940
+ splash.setAttribute(Qt.WA_TranslucentBackground)
941
+ splash.show()
942
+ return splash
943
+
944
+ def checkLatestVersion(self,version,app:QApplication=None,splash:QLabel=None,flagWarning=1):
945
+ flagStopAndDownload=False
946
+ var=self.TABpar
947
+ #var.FlagOutDated=0 if currentVersion==var.latestVersion else var.FlagOutDated
948
+ if abs(var.FlagOutDated)==1:
949
+ warningLatestVersion(self,app,flagExit=0,flagWarning=flagWarning,FlagStayOnTop=True)
950
+ var.FlagOutDated=2 if var.FlagOutDated==1 else -2
951
+ """
952
+ flagStopAndDownload=questionDialog(self,f'A new version of the PaIRS_UniNa package is available. Do you want to download it before starting the current istance of {self.name}?')
953
+ if flagStopAndDownload:
954
+ if splash: splash.hide()
955
+ downloadLatestVersion(self,app)
956
+ return flagStopAndDownload
957
+ else:
958
+ var.FlagOutDated=2
959
+ """
960
+
961
+ packageName='PaIRS_UniNa'
962
+ def printOutDated(flagOutDated,currentVersion,latestVersion):
963
+ """"
964
+ if not flagOutDated:
965
+ flagOutDated2=any([c<l for (c,l) in zip(version.split('.'),latestVersion.split('.'))])
966
+ if flagOutDated2:
967
+ currentVersion=version
968
+ flagOutDated=1
969
+ """
970
+ var.currentVersion=currentVersion
971
+ var.latestVersion=latestVersion
972
+ if flagOutDated==1:
973
+ sOut=f'{packageName} the current version ({currentVersion}) of {packageName} is obsolete! Please, install the latest version: {latestVersion} by using:\npython -m pip install --upgrade {packageName}'
974
+ var.FlagOutDated=2 if var.FlagOutDated==2 else 1
975
+ elif flagOutDated==-1:
976
+ sOut=f'The version of the current instance of {packageName} ({currentVersion}) is newer than the latest official releas ({latestVersion})!\nYou should contact Tommaso and Gerardo if you are a developer and some relevant change is made by yourself!\nIf you are a user, enjoy this beta version and please report any issue!'
977
+ var.FlagOutDated=-2 if var.FlagOutDated==-2 else -1
978
+ elif flagOutDated==-1000:
979
+ sOut=f'Error from pip: it was not possible to check for a new version of the {packageName} package!'
980
+ var.FlagOutDated=-1000
981
+ else:
982
+ sOut=f'{packageName} The current version ({currentVersion}) of {packageName} is up-to-date! Enjoy it!'
983
+ var.FlagOutDated=0
984
+ pri.Info.cyan(f'[{var.FlagOutDated}] '+sOut)
985
+ self.signals.printOutDated.emit()
986
+ #self.ui.button_PaIRS_download.setVisible(flagOutDated>0)
987
+ pass
988
+
989
+ checkOutDated(packageName,printOutDated)
990
+ return flagStopAndDownload
991
+
992
+ def warningLatestVersion(self,app,flagExit=0,flagWarning=0,time_milliseconds=0,FlagStayOnTop=False):
993
+ if not flagExit:
994
+ exitSuggestion=f'exit the current instance of {self.name} and '
995
+ else:
996
+ exitSuggestion=''
997
+ py=myStandardRoot(sys.executable).split('/')[-1].split('.')[0]
998
+ command=f'{py} -m pip install --upgrade PaIRS_UniNa'
999
+ if self.TABpar.FlagOutDated>0:
1000
+ if Flag_ISEXE:
1001
+ Message=f'A new version of the PaIRS_UniNa package is available (current: {self.TABpar.currentVersion}, latest: {self.TABpar.latestVersion}).\nPlease, download it from the following link:\n{EXEurl}'
1002
+ else:
1003
+ Message=f'A new version of the PaIRS_UniNa package is available (current: {self.TABpar.currentVersion}, latest: {self.TABpar.latestVersion}).\nPlease, {exitSuggestion}install it with the following command:\n{command}'
1004
+ elif self.TABpar.FlagOutDated==-1000:
1005
+ Message = ("Unable to check for the latest official release of PaIRS_UniNa. Please check the PyPI page manually for updates:\n""https://pypi.org/project/PaIRS-UniNa/")
1006
+ else:
1007
+ Message=f'The version of the current instance of PaIRS_UniNa ({self.TABpar.currentVersion}) is newer than the latest official releas ({self.TABpar.latestVersion})!\nYou should contact Tommaso and Gerardo if you are a developer and some relevant change is made by yourself!\nIf you are a user, enjoy this beta version and please report any issue!'
1008
+ if flagExit:
1009
+ print(f"\n{'*'*100}\n"+Message+f"\n{'*'*100}\n")
1010
+ if flagWarning:
1011
+ warningDialog(self,Message,time_milliseconds=time_milliseconds,flagScreenCenter=True,pixmap=''+ icons_path +'flaticon_PaIRS_download.png' if self.TABpar.FlagOutDated>0 else ''+ icons_path +'flaticon_PaIRS_download_warning.png' if self.TABpar.FlagOutDated==-1000 else ''+ icons_path +'flaticon_PaIRS_beta.png',FlagStayOnTop=FlagStayOnTop,addButton={"Go to the download page!": lambda: QDesktopServices.openUrl(QUrl(EXEurl))} if Flag_ISEXE else {"See what's new!": lambda: QDesktopServices.openUrl(QUrl("https://pypi.org/project/PaIRS-UniNa/"))} if self.TABpar.FlagOutDated>0 else {})
1012
+
1013
+ def downloadLatestVersion(self,app):
1014
+ try:
1015
+ print(f'{"*"*20} Upgrading PaIRS_UniNa {"*"*20}')
1016
+ splash=showSplash(filename=''+ icons_path +'logo_PaIRS_download.png')
1017
+ app.processEvents()
1018
+ reqs=subprocess.run([sys.executable, '-m', 'pip', 'install','--upgrade','PaIRS_UniNa'],capture_output=True)
1019
+ print(reqs.stderr.decode("utf-8"))
1020
+ print(reqs.stdout.decode("utf-8"))
1021
+ print(f'{"*"*20} PaIRS_UniNa upgraded {"*"*20}')
1022
+ #reqs=subprocess.run([sys.executable, '-m', 'pip', 'install','PaIRS_UniNa'],capture_output=True)
1023
+ #print(reqs.stderr.decode("utf-8"))
1024
+ #print(reqs.stdout.decode("utf-8"))
1025
+ splash.hide()
1026
+ except Exception as inst:
1027
+ print(inst)
1028
+ try:
1029
+ warningDialog(self,f'The following error occured while downloading the latest version of the PaIRS_UniNa package from https://pypi.org:\n{inst}.\n\nPlease, try by yourself with the following command:\nnpython -m pip install --upgrade PaIRS_UniNa')
1030
+ except Exception as inst:
1031
+ print(inst)
1032
+
1033
+ def button_download_PaIRS_action(self,app):
1034
+ warningLatestVersion(self,app,flagExit=0,flagWarning=1)
1035
+ checkLatestVersion(self,__version__,self.app,splash=None,flagWarning=0)
1036
+ return
1037
+ flagStopAndDownload=questionDialog(self,f'A new version of the PaIRS_UniNa package is available. Do you want to close the current instance of {self.name} and download it?')
1038
+ if not flagStopAndDownload: return
1039
+ self.TABpar.FlagOutDated=0
1040
+ self.close()
1041
+ downloadLatestVersion(self,app)
1042
+ print(f'{"*"*20} Relaunching PaIRS {"*"*20}')
1043
+ if self.name=='CalVi': command='-calvi'
1044
+ else: command=''
1045
+ subprocess.call(sys.executable+' -m PaIRS_UniNa '+command,shell=True)
1046
+ #runPaIRS(self,flagQuestion=False)
1047
+
1048
+ import urllib.request, json, ssl
1049
+
1050
+ def get_package_version_urllib(package_name):
1051
+ """Get package version using only standard library"""
1052
+ try:
1053
+ import certifi
1054
+ url = f"https://pypi.org/pypi/{package_name}/json"
1055
+ context = ssl.create_default_context(cafile=certifi.where())
1056
+ with urllib.request.urlopen(url, context=context, timeout=10) as response:
1057
+ data = json.loads(response.read().decode())
1058
+ return True, data['info']['version']
1059
+ except Exception as e:
1060
+ return False, f"Error: {e}"
1061
+
1062
+ def checkOutDated(packageName:str,printOutDated):
1063
+ '''
1064
+ Check if a package is out dated works asynchronously.
1065
+ call with
1066
+ checkOutDated('PaIRS_UniNa',printOutDated)
1067
+ Input
1068
+ packageName the name of the package
1069
+ fun a function that is called when ready and in input will receive a bool (True if outdated and a string that explain to the user what to do)
1070
+ def printOutDated(flagOutDated,sOut):
1071
+ if flagOutDated==1:
1072
+ print (sOut)
1073
+ elseif flagOutDated=-1:
1074
+ print ('Error from pip ')
1075
+ else:
1076
+ pass #in this case the last version of the package is installed
1077
+ '''
1078
+ async def checkOutDatedInternal(packageName):
1079
+ #reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'list','--outdated'])
1080
+ #reqs = subprocess.run([sys.executable, '-m', 'pip', 'list','--outdated'],capture_output=True)
1081
+ #reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'index','versions','PaIRS_UniNa'])
1082
+ #reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'install','PaIRS_UniNa=='])
1083
+
1084
+ """"
1085
+ reqs = subprocess.run([sys.executable, '-m', 'pip', 'list','--outdated'],capture_output=True)
1086
+ outDated = [r.decode().split('==')[0] for r in reqs.stdout.split()]
1087
+ """
1088
+ flagOutDated=-1000
1089
+ currentVersion='none'
1090
+ latestVersion=''
1091
+ try:
1092
+ if Flag_ISEXE:
1093
+ currentVersion=__version__+'.'+__subversion__ if int(__subversion__) else __version__
1094
+ else:
1095
+ if Flag_DEBUG:
1096
+ currentVersion=__version__+'.'+__subversion__ if int(__subversion__) else __version__
1097
+ else:
1098
+ command=[sys.executable, '-m', 'pip', 'show', packageName]
1099
+ reqs = subprocess.run(command,capture_output=True)
1100
+ if reqs.returncode:
1101
+ pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
1102
+ return flagOutDated,currentVersion,latestVersion
1103
+ printing=reqs.stdout.decode("utf-8")
1104
+ pri.Info.cyan( printing )
1105
+ r=reqs.stdout.decode("utf-8").replace('\r','').split('\n')
1106
+ currentVersion='none'
1107
+ for s in r:
1108
+ if 'Version: ' in s:
1109
+ currentVersion=s.replace('Version: ','')
1110
+ break
1111
+ if currentVersion!=__version__:
1112
+ message=f'Greetings, developer!\nThe version of the current instance of PaIRS_UniNa ({__version__}) is different from that installed in the present Python environment ({currentVersion})!\nYou should contact Tommaso and Gerardo if some relevant change is made by yourself!'
1113
+ pri.Info.yellow(f'{"-"*50}\n{message}\n{"-"*50}\n')
1114
+ if Flag_ISEXE:
1115
+ _, latestVersion = get_package_version_urllib("PaIRS_UniNa")
1116
+ else:
1117
+ command=[sys.executable, '-m', 'pip', 'index', 'versions', packageName]
1118
+ reqs = subprocess.run(command,capture_output=True)
1119
+ if not reqs.returncode:
1120
+ printing=reqs.stdout.decode("utf-8")
1121
+ pri.Info.cyan( printing )
1122
+ r=reqs.stdout.decode("utf-8").replace('\r','').split('\n')
1123
+ #currentVersion=r[0].replace(packageName,'').replace('(','').replace(')','').replace(' ','')
1124
+ latestVersion=r[1].replace('Available versions: ','').split(',')[0]
1125
+ else:
1126
+ flagOk,latestVersion=get_package_version_urllib(packageName)
1127
+ if not flagOk:
1128
+ pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
1129
+ pri.Error.red(latestVersion)
1130
+ latestVersion='none'
1131
+
1132
+ """
1133
+ command=[sys.executable, '-m', 'pip', 'list','--outdated']
1134
+ reqs = subprocess.run(command,capture_output=True)
1135
+ if reqs.returncode:
1136
+ pri.Error.red('Error in command:\n'+' '.join(command)+'\n'+reqs.stderr.decode("utf-8") )
1137
+ return flagOutDated,currentVersion,latestVersion
1138
+ outDated = [r.decode().split('==')[0] for r in reqs.stdout.split()]
1139
+ if packageName in outDated:
1140
+ i=outDated.index(packageName)
1141
+ latestVersion=outDated[i+2]
1142
+ else:
1143
+ latestVersion=currentVersion
1144
+ pri.Info.cyan(f'{packageName} ({currentVersion}). Latest version available: {latestVersion}')
1145
+ """
1146
+ #flagOutDated=1 if currentVersion!=latestVersion else 0
1147
+ cV_parts=[int(c) for c in currentVersion.split('.')]
1148
+ lV_parts=[int(c) for c in latestVersion.split('.')]
1149
+ flagOutDated=1 if (cV_parts[0] < lV_parts[0] or
1150
+ cV_parts[1] < lV_parts[1] or
1151
+ cV_parts[2] < lV_parts[2]) \
1152
+ else -1 if (cV_parts[0] > lV_parts[0] or
1153
+ cV_parts[1] > lV_parts[1] or
1154
+ cV_parts[2] > lV_parts[2] ) \
1155
+ or (cV_parts[0] == lV_parts[0] and
1156
+ cV_parts[1] == lV_parts[1] and
1157
+ cV_parts[2] == lV_parts[2] and len(cV_parts)>len(lV_parts)) \
1158
+ else 0
1159
+ except Exception as inst:
1160
+ pri.Error.red(inst)
1161
+ return flagOutDated,currentVersion,latestVersion
1162
+ def checkOutDatedComplete(_f3):
1163
+ flagOutDated,currentVersion,latestVersion=_f3.result()
1164
+ printOutDated (flagOutDated,currentVersion,latestVersion)
1165
+ executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)
1166
+ f3=executor.submit(asyncio.run,checkOutDatedInternal(packageName))
1167
+ f3.add_done_callback(checkOutDatedComplete)
1168
+
1169
+ def changes(self,TabType,filename,title=" Changes"):
1170
+ FlagShow=False
1171
+ if self.logChanges:
1172
+ if self.logChanges.isVisible():
1173
+ FlagShow=True
1174
+ if FlagShow:
1175
+ self.logChanges.hide()
1176
+ self.logChanges.show()
1177
+ else:
1178
+ self.logChanges=TabType(self,True)
1179
+ self.logChanges.resize(720,720)
1180
+ self.logChanges.show()
1181
+ self.logChanges.ui.progress_Proc.hide()
1182
+ self.logChanges.ui.button_close_tab.hide()
1183
+ icon=QPixmap(''+ icons_path +'news.png')
1184
+ self.logChanges.ui.icon.setPixmap(icon)
1185
+ self.logChanges.setWindowIcon(self.windowIcon())
1186
+ self.logChanges.setWindowTitle(title)
1187
+ self.logChanges.ui.name_tab.setText(title)
1188
+
1189
+ self.logChanges.ui.log.setLineWrapColumnOrWidth(self.logChanges.ui.log.width()-20)
1190
+ self.logChanges.ui.log.setStyleSheet("")
1191
+
1192
+ def setFontPixelSize(logChanges:type(self.logChanges),fPixSize):
1193
+ logfont=self.font()
1194
+ logfont.setFamily(fontName)
1195
+ logfont.setPixelSize(fPixSize+2)
1196
+ logChanges.ui.log.setFont(logfont)
1197
+ fPixSize_TabNames=min([fPixSize*2,30])
1198
+ lab=logChanges.ui.name_tab
1199
+ font=lab.font()
1200
+ font.setPixelSize(fPixSize_TabNames)
1201
+ lab.setFont(font)
1202
+ self.logChanges.setFontPixelSize=lambda fS: setFontPixelSize(self.logChanges,fS)
1203
+ self.logChanges.setFontPixelSize(self.TABpar.fontPixelSize)
1204
+ def logResizeEvent(logChanges:type(self.logChanges),e):
1205
+ super(type(logChanges),logChanges).resizeEvent(e)
1206
+ logChanges.ui.log.setLineWrapColumnOrWidth(logChanges.ui.log.width()-20)
1207
+ self.logChanges.ui.log.resizeEvent=lambda e: logResizeEvent(self.logChanges,e)
1208
+
1209
+ self.logChanges.ui.icon.addfuncclick['whatsnew']=self.whatsNew
1210
+ self.logChanges.ui.icon.setCustomCursor()
1211
+
1212
+ try:
1213
+ file = open(filename, "rb")
1214
+ content = file.read().decode("utf-8")
1215
+ self.logChanges.ui.log.setText(content)
1216
+ file.close()
1217
+ except Exception as inst:
1218
+ pri.Error.red(f'There was a problem while reading the file {filename}:\n{inst}')
1219
+ self.logChanges.ui.log.setText(f'No information about PaIRS-UniNa updates available!\n\nSorry for this, try to reinstall PaIRS-UniNa or alternatively contact the authors at {__mail__}.')
1220
+ return
1221
+
1222
+ import webbrowser
1223
+ def downloadExampleData(self,url):
1224
+ Message=f'Test data are available at the following link:\n{url}'
1225
+ warningDialog(self,Message,pixmap=''+ icons_path +'flaticon_PaIRS_download.png',title='Download test data',addButton={'Download data!':lambda:webbrowser.open(url)})
1226
+
1227
+ def optimalPivCores(totCore,nImgs,penCore=1):
1228
+ ''' Used to determine the optimal number of pivCores as a function of the total number of cores and the number of imag to be processed
1229
+ totCore is the total number of cores that can be used
1230
+ nImgs is the number of images
1231
+ penCore is a penalization for the internal parallelization of the PIV process if = to 1 the parallelization it is assumed to be perfect
1232
+ Most probably a value of 0.95-1 should work correctly
1233
+ with 0.95 adding the xth pivCore is counted as: x=10->0.63 20->0.38 40->0.14 80->0.017
1234
+ with 0.98 adding the xth pivCore is counted as: x=10->0.83 20->0.68 40->0.45 80->0.20
1235
+ Output
1236
+ nPivMax the number of pivCores to be used
1237
+ nCoreMax the number of multiProces to be used
1238
+ '''
1239
+ pen=1 #initially the penalization is zero
1240
+ procPower=1 # the processing power of piv is not directly proportional to the numbers of cores
1241
+ nCorePerImgMax=0
1242
+ nPivMax=0
1243
+ for nPiv in range(1,totCore+1):
1244
+ nProc=floor(totCore/nPiv)
1245
+ nCicli=ceil(nImgs/nProc)
1246
+ nCorePerImg=procPower/nCicli
1247
+ #♥print(nPiv,nProc,pen,procPower,nCorePerImg,nCicli)
1248
+ if nCorePerImg>nCorePerImgMax:
1249
+ nCorePerImgMax=nCorePerImg
1250
+ nPivMax=nPiv
1251
+ pen*=penCore
1252
+ procPower+=pen
1253
+ nCoreMax=floor(totCore/nPivMax)
1254
+ #nPivMax=floor(totCore/nCoreMax)
1255
+ return nPivMax,nCoreMax
1256
+
1257
+ from PySide6.QtCore import qInstallMessageHandler, QtMsgType
1258
+ def custom_qt_message_handler(mode, context, message):
1259
+ if ("QPainter" in message or "paintEngine" in message):
1260
+ return #Silenzia questi messaggi
1261
+ print(message) #Altrimenti stampali normalmente (oppure loggali)
1262
+ qInstallMessageHandler(custom_qt_message_handler)
1263
+
1264
+ """
1265
+ def custom_qt_message_handler(mode, context, message):
1266
+ if "QPainter" in message or "paintEngine" in message:
1267
+ print("\n!!! Intercepted Qt message:")
1268
+ print(message)
1269
+ print("\n*** Current Python stacktrace:")
1270
+ traceback.print_stack() # Questo stampa lo stack in cui è stato generato il messaggio
1271
+ else:
1272
+ print(message)
1273
+ qInstallMessageHandler(custom_qt_message_handler)
1274
+ import functools
1275
+ import traceback
1276
+ def log_qpainter_usage(func):
1277
+ @functools.wraps(func)
1278
+ def wrapper(*args, **kwargs):
1279
+ print(f"\n°°° Execution of {func.__name__} in {func.__module__}")
1280
+ traceback.print_stack(limit=4) # Mostra solo lo stack alto
1281
+ return func(*args, **kwargs)
1282
+ return wrapper
1283
+ """
1284
+
1285
+ class PaIRSApp(QApplication):
1286
+ def __init__(self,*args):
1287
+ super().__init__(*args)
1288
+ self.installMessageHandler()
1289
+ self.setStyle('Fusion')
1290
+
1291
+ def applicationSupportsSecureRestorableState(self):
1292
+ return True
1293
+
1294
+ def message_handler(self, mode, context, message):
1295
+ if "QBasicTimer::start" not in message and "QObject::startTimer" not in message:
1296
+ print(message)
1297
+
1298
+ def installMessageHandler(self):
1299
+ qInstallMessageHandler(self.message_handler)
1300
+
1301
+ rqrdpckgs_filename=foldPaIRS+"rqrdpckgs.txt"
1302
+ from packaging.version import Version
1303
+ import importlib.metadata
1304
+
1305
+ def resetRequiredPackagesFile(filename=rqrdpckgs_filename):
1306
+ # Leggi il contenuto esistente
1307
+ try:
1308
+ with open(filename, "r") as f:
1309
+ lines = f.readlines()
1310
+ except FileNotFoundError:
1311
+ pri.Error.red(f"resetRequiredPackagesFile: File {filename} not found.")
1312
+ return
1313
+
1314
+ with open(filename, "w") as f:
1315
+ for line in lines:
1316
+ parts = line.strip().split()
1317
+ if len(parts) >= 3:
1318
+ pkg = parts[0]
1319
+ vmin = parts[1]
1320
+ vmax = parts[2]
1321
+ f.write(f"{pkg}\t{vmin}\t{vmax}\t0\n")
1322
+ else:
1323
+ pri.Error.red(f"resetRequiredPackagesFile: Skipping malformed line: {line}")
1324
+
1325
+ def to_triplet(v: Version) -> tuple[int,int,int]:
1326
+ r = v.release or (0,)
1327
+ return (r[0], r[1] if len(r) > 1 else 0, r[2] if len(r) > 2 else 0)
1328
+
1329
+ def le_ver(a: Version, b: Version) -> bool:
1330
+ a1,a2,a3 = to_triplet(a)
1331
+ b1,b2,b3 = to_triplet(b)
1332
+ if a1 > b1: return False
1333
+ if a1 < b1: return True
1334
+ if a2 > b2: return False
1335
+ if a2 < b2: return True
1336
+ return a3 <= b3
1337
+
1338
+ def checkRequiredPackages(self, filename=rqrdpckgs_filename, FlagDisplay=False, FlagForcePrint=False):
1339
+ required_packages = []
1340
+ vmin_list = []
1341
+ vmax_list = []
1342
+ vcurr_list = []
1343
+
1344
+ # Read file
1345
+ with open(filename, "r") as f:
1346
+ for line in f:
1347
+ #pri.Info.white(line)
1348
+ parts = line.strip().split()
1349
+ if len(parts) >= 4:
1350
+ required_packages.append(parts[0])
1351
+ vmin_list.append(Version(parts[1]))
1352
+ vmax_list.append(Version(parts[2]))
1353
+ vcurr_list.append(Version(parts[3]) if parts[3] != "0" else None)
1354
+ else:
1355
+ pri.Error.red(f"Malformed line: {line}")
1356
+
1357
+ Flag = False
1358
+ warnings = []
1359
+
1360
+ for i, pkg in enumerate(required_packages):
1361
+ try:
1362
+ installed_version = Version(importlib.metadata.version(pkg))
1363
+ except importlib.metadata.PackageNotFoundError:
1364
+ installed_version = None
1365
+
1366
+ # Update current installed version
1367
+ if installed_version is not None and (installed_version != vcurr_list[i] or FlagDisplay):
1368
+ vcurr_list[i] = installed_version
1369
+ Flag = True
1370
+
1371
+ # Check if within [vmin, vmax]
1372
+ if not (le_ver(vmin_list[i],installed_version) and le_ver(installed_version,vmax_list[i])):
1373
+ """
1374
+ warnings.append(
1375
+ f"- {pkg}: installed = {installed_version}, target range = [{vmin_list[i]}, {vmax_list[i]}]"
1376
+ )
1377
+ """
1378
+ warnings.append(
1379
+ f"- {pkg} {installed_version} not in [{vmin_list[i]}, {vmax_list[i]}]"
1380
+ )
1381
+
1382
+ # Show warning
1383
+ if len(warnings)>0: self.FlagPackIssue=True
1384
+ if len(warnings)>0 or FlagForcePrint:
1385
+ message = (
1386
+ "Some installed packages have a version outside the target range used to develop "
1387
+ "the current release of the PaIRS_UniNa package.\n\n"
1388
+ "This may lead to compatibility issues. If you experience unexpected behavior, "
1389
+ "it is recommended to either reinstall the last tested compatible versions or "
1390
+ f"download the executable at {EXEurl}."
1391
+ f" If any issue occurs, please contact the authors at {__mail__}.\n\n"
1392
+ #"or use the standalone executable available at:\n"
1393
+ #"https://pairs.unina.it/#download\n\n"
1394
+ "Incompatible packages:\n"
1395
+ + "\n".join(warnings) +
1396
+ "\n\nYou may reinstall the last compatible versions using the following commands:\n\n"
1397
+ )
1398
+ for i, pkg in enumerate(required_packages):
1399
+ if vcurr_list[i] is not None and (not (vmin_list[i] <= vcurr_list[i] <= vmax_list[i]) or FlagForcePrint):
1400
+ message += (
1401
+ f"python -m pip uninstall {pkg}\n"
1402
+ f"python -m pip install {pkg}=={vmax_list[i]}\n"
1403
+ )
1404
+
1405
+ warningDialog(
1406
+ self,
1407
+ Message=message,
1408
+ flagScreenCenter=True,
1409
+ pixmap=icons_path + 'python_warning.png',
1410
+ pixmapSize=96
1411
+ )
1412
+ elif FlagDisplay:
1413
+ warningDialog(self, Message="All installed packages are within the expected version range.", flagScreenCenter=True,pixmap=icons_path+'greenv.png')
1414
+
1415
+ # Update file if needed
1416
+ if Flag:
1417
+ with open(filename, "w") as f:
1418
+ for pkg, vmin, vmax, vcurr in zip(required_packages, vmin_list, vmax_list, vcurr_list):
1419
+ f.write(f"{pkg}\t{vmin}\t{vmax}\t{vcurr if vcurr else 0}\n")
1420
+
1421
+ return required_packages, vmin_list, vmax_list, vcurr_list