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