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,3026 @@
1
+ import os.path
2
+
3
+ from PySide6.QtWidgets import QWidget
4
+ from .PaIRS_pypacks import*
5
+ from .addwidgets_ps import*
6
+ from .TabTools import TABpar
7
+ from .listLib import *
8
+ from concurrent.futures import ThreadPoolExecutor
9
+
10
+ FlagSpinButtons_Debug=True
11
+ batch_size_image_creation=200
12
+ sleep_time_item_worker=0.05
13
+
14
+ class ImageSet(TABpar):
15
+
16
+ def __init__(self,path='',exts=supported_exts):
17
+ self.setup()
18
+ self.lists=[f for f,v in self.__dict__.items() if type(v)==list]
19
+ self.supported_exts = exts
20
+ super().__init__('ImageSet','Input')
21
+ self.unchecked_fields+=['signals','fname','fnumb','outFiles']
22
+ if path: self.scanPath(path)
23
+
24
+ def setup(self):
25
+ self.path = './'
26
+ self.count = 0
27
+ self.pattern = []
28
+ self.pa = []
29
+ self.fname = []
30
+ self.fnumb = []
31
+ self.ndig = []
32
+ self.nimg = []
33
+ self.ind_in = []
34
+ self.ind_fin = []
35
+ self.ext = []
36
+ self.link = []
37
+ self.outFiles = {}
38
+
39
+ def nameF(self,fname,i):
40
+ a=fname[0]
41
+ ndigits=fname[1]
42
+ b=fname[2]
43
+ return a+f"{i:0{ndigits}d}"+b if type(i)==int else a+str(i)+b
44
+
45
+ def numbF(self,fnumb,name):
46
+ n=fnumb[0]
47
+ ndigits=fnumb[1]
48
+ return int(name[n:n+ndigits])
49
+
50
+ def scanPath(self,path):
51
+ pri.Time.blue(f'ImageSet: start scanning path {path}')
52
+ self.clearLists()
53
+ path=myStandardPath(path) #maybe useless
54
+ self.path=path
55
+ files=findFiles_sorted(path+"*.*") # needed to use the recompiled reg expressions
56
+ for k,file in enumerate(files):
57
+ if not any(file.endswith(ex) for ex in self.supported_exts):
58
+ continue
59
+ basename=os.path.basename(file)
60
+ FlagMatch=False
61
+ if self.count:
62
+ FlagMatch=self.checkMatch(basename)
63
+ if not FlagMatch:
64
+ self.addPattern(basename)
65
+ for j in range(k):
66
+ basename=os.path.basename(files[j])
67
+ FlagMatch=self.checkMatch(basename,FlagOnlyLast=True)
68
+ self.sortLists()
69
+ self.createLink()
70
+ self.scanOutFile()
71
+ pri.Time.blue(f'ImageSet: end scanning path {path}')
72
+ return self
73
+
74
+ def scanOutFile(self):
75
+ self.outFiles={}
76
+ if os.path.exists(self.path):
77
+ _,dirnames,_=next(os.walk(self.path))
78
+ foldernames=[self.path]+[self.path+d+'/' for d in dirnames]
79
+ values=[v for _,v in outExt.__dict__.items() if type(v)==str]
80
+ for ext in values:
81
+ self.outFiles[ext]=[]
82
+ for fold in foldernames:
83
+ self.outFiles[ext]+=findFiles_sorted(fold+'*'+ext)
84
+
85
+ def clearLists(self):
86
+ self.count=0
87
+ for f in self.lists: setattr(self,f,[])
88
+ return
89
+
90
+ def checkMatch(self,basename,FlagOnlyLast=False):
91
+ FlagMatch=False
92
+ if FlagOnlyLast: kra=[self.count-1]
93
+ else: kra=range(self.count)
94
+ if self.count:
95
+ for k in kra:
96
+ pa:re.Pattern=re.compile(self.pa[k])
97
+ if pa.match(basename):
98
+ FlagMatch=True
99
+ self.nimg[k]+=1
100
+ ind=self.numbF(self.fnumb[k],basename)
101
+ self.ind_in[k]=min([self.ind_in[k],ind])
102
+ self.ind_fin[k]=max([self.ind_fin[k],ind])
103
+ return FlagMatch
104
+
105
+ def addPattern(self,basename):
106
+ _, ext = os.path.splitext(basename)
107
+ split_basename=re.split(r'(\d+)', basename)
108
+ c=0
109
+ for k,s in enumerate(split_basename):
110
+ if not len(s): continue
111
+ if s[0].isdigit():
112
+ self.count+=1
113
+ ndig=len(s)
114
+ self.ndig.append(ndig)
115
+ self.nimg.append(1)
116
+ self.ind_in.append(int(s))
117
+ self.ind_fin.append(int(s))
118
+
119
+ pattern_list=split_basename.copy()
120
+ pattern_list[k]="*{"+str(ndig)+"}"
121
+ pattern="".join(pattern_list)
122
+ self.pattern.append(pattern)
123
+
124
+ pattern_list[k]='\\d{'+str(ndig)+'}'
125
+ pattern="".join(pattern_list)
126
+ #pa=re.compile(pattern)
127
+ self.pa.append(pattern)
128
+
129
+ pre ="".join(pattern_list[:k])
130
+ post="".join(pattern_list[k+1:])
131
+ fname=[pre,ndig,post] #lambda i, a=pre, b=post, ndigits=ndig: a+f"{i:0{ndigits}d}"+b if type(i)==int else a+str(i)+b
132
+ self.fname.append(fname)
133
+
134
+ fnumb=[c,ndig] #lambda name, n=c, ndigits=ndig: int(name[n:n+ndigits])
135
+ self.fnumb.append(fnumb)
136
+ self.ext.append(ext)
137
+ self.link.append([])
138
+
139
+ c+=len(s)
140
+ return
141
+
142
+ def sortLists(self):
143
+ if not self.count: return
144
+ lind=range(len(self.nimg))
145
+ self.nimg.reverse()
146
+ _,lind=zip(*sorted(zip(self.nimg, lind),reverse=True))
147
+ for f in self.lists:
148
+ v=getattr(self,f)
149
+ v.reverse() if f!='nimg' else None
150
+ v2=[v[i] for i in lind]
151
+ setattr(self,f,v2)
152
+ return
153
+
154
+ def createLink(self):
155
+ for k,p in enumerate(self.pattern):
156
+ alpha=[]
157
+ jalpha=[]
158
+ number=[]
159
+ jnumber=[]
160
+ for j,p2 in enumerate(self.pattern):
161
+ p2:str
162
+ if len(p)!=len(p2): continue
163
+ diff=[i for i in range(len(p)) if p[i]!=p2[i]]
164
+ if len(diff)==1:
165
+ if p2[diff[0]].isalpha():
166
+ jl=jalpha
167
+ l=alpha
168
+ else:
169
+ jl=jnumber
170
+ l=number
171
+ i=0
172
+ while i<len(jl) and p2[diff[0]]<l[i]: i+=1
173
+ jl.insert(i-1,j)
174
+ l.insert(i-1,p2[diff[0]])
175
+ self.link[k]=jalpha+sorted(jnumber)+[k]
176
+ return
177
+
178
+ def print(self):
179
+ pri.Coding.white('\n'+f'Image sets found in path "{self.path}"')
180
+ for k in range(self.count):
181
+ pri.Coding.white(f'{k:2d}: '+f'{self.pattern[k]}'+'\t'+f'n img = {self.nimg[k]} '+'\t'+f'{self.ind_in[k]}-{self.ind_fin[k]}')
182
+ for j in self.link[k]:
183
+ pri.Coding.white(' '+f'{self.pattern[j]}')
184
+ pri.Coding.white(' ')
185
+
186
+ def genList(self,k,i,npairs,step):
187
+ if k>=self.count:
188
+ pri.Error.red(f'Trying to access a non-existing index position ({k}) in the image set structure ({self.count} sets identified)')
189
+ return []
190
+ f=i+npairs*step
191
+ if k>-1 and step>0:
192
+ return [self.nameF(self.fname[k],j) for j in range(i,f,step)]
193
+ else:
194
+ return ['' for _ in range(i,f,step)] if step else []
195
+
196
+ def genListsFromIndex(self,k,i=None,npairs=None,step=None,ncam=None):
197
+ if k>=self.count:
198
+ pri.Error.red(f'Trying to access a non-existing index position ({k}) in the image set structure ({self.count} sets identified)')
199
+ imList=[[[]]]
200
+ imEx=[[[]]]
201
+ return self.path,imList,imEx
202
+ if not i: i=self.ind_in[k]
203
+ if not npairs: npairs=self.nimg[k]
204
+ if not step: step=1
205
+ if not ncam: ncam=max([len(self.link[k])-1,1])
206
+ l_c1_f1=self.genList(k,i,npairs,step)
207
+ imEx1=[os.path.exists(self.path+f) if f else False for f in l_c1_f1 ]
208
+ l_c1_f2=self.genList(self.link[k][0],i,npairs,step)
209
+ imEx2=[os.path.exists(self.path+f) if f else False for f in l_c1_f2]
210
+ imList=[[l_c1_f1,l_c1_f2]]
211
+ imEx=[[imEx1,imEx2]]
212
+
213
+ for c in range(1,ncam):
214
+ if c<len(self.link[k])-1:
215
+ k_c=self.link[k][c]
216
+ l_c_f1=self.genList(k_c,i,npairs,step)
217
+ imEx1=[os.path.exists(self.path+f) if f else False for f in l_c_f1 ]
218
+ l_c_f2=self.genList(self.link[k_c][0],i,npairs,step)
219
+ imEx2=[os.path.exists(self.path+f) if f else False for f in l_c_f2]
220
+ else:
221
+ l_c_f1=['' for j in range(npairs)]
222
+ l_c_f2=['' for j in range(npairs)]
223
+ imEx1=[False for j in range(npairs)]
224
+ imEx2=[False for j in range(npairs)]
225
+ imList.append([l_c_f1,l_c_f2])
226
+ imEx.append([imEx1,imEx2])
227
+ return self.path,imList,imEx
228
+
229
+ def genListsFromFrame(self,frame_1,frame_2,i,npairs,step,FlagTR):
230
+ ncam=len(frame_1)
231
+ imList=[[['' for _ in range(npairs)] for _ in range(2)] for _ in range(ncam)]
232
+ imEx=[[[False for _ in range(npairs)] for _ in range(2)] for _ in range(ncam)]
233
+ if i>-1:
234
+ for c in range(ncam):
235
+ stepc=step
236
+ f1=frame_1[c]
237
+ f2=frame_2[c]-1
238
+ if f2==-1:
239
+ f2=f1
240
+ i2=i+stepc
241
+ stepc*=2
242
+ else: i2=i
243
+ if not FlagTR:
244
+ imList[c][0]=self.genList(f1,i,npairs,stepc)
245
+ imList[c][1]=self.genList(f2,i2,npairs,stepc)
246
+ else:
247
+ npairs_half=int(npairs/2)+1
248
+ a=self.genList(f1,i,npairs_half,stepc)
249
+ b=self.genList(f2,i2,npairs_half,stepc)
250
+ imListTR=[val for pair in zip(a, b) for val in pair]
251
+ imList[c][0]=imListTR[:npairs]
252
+ imList[c][1]=imListTR[1:npairs+1]
253
+ imEx[c][0]=[os.path.exists(self.path+f) if f else False for f in imList[c][0]]
254
+ imEx[c][1]=[os.path.exists(self.path+f) if f else False for f in imList[c][1]]
255
+ return imList,imEx
256
+
257
+ class PaIRSTree(QTreeWidget):
258
+ cutted_itemList=[]
259
+ cutted_items=[]
260
+ items_expanded=[]
261
+ deleted_itemList=[]
262
+ deleted_items=[]
263
+
264
+ default_row_height=20
265
+ default_col_width=12
266
+ default_indentation=10
267
+
268
+ class ImageTree_signals(QObject):
269
+ updateTree=Signal()
270
+ updateLists=Signal()
271
+ createdItems=Signal(int,list,bool,list)
272
+ copyCutItems=Signal()
273
+ pastedItems=Signal()
274
+
275
+ endOfTask = Signal()
276
+ stopWorker = Signal()
277
+ keep_on = Signal()
278
+
279
+ def on_scroll(self):
280
+ return
281
+
282
+ def mousePressEvent(self, event: QMouseEvent):
283
+ self.cursor_pos=event.globalPosition().toPoint()
284
+ super().mousePressEvent(event)
285
+ return
286
+
287
+ def selectedTopLevel(self):
288
+ selectedItems=[i for i in self.selectedItems() if i.parent() is None]
289
+ indexes=[self.indexOfTopLevelItem(i) for i in selectedItems]
290
+ return selectedItems,indexes
291
+
292
+ def selectTopLevel(self):
293
+ selectedItems=[]
294
+ bottomItems=[]
295
+ for item in self.selectedItems():
296
+ if item.parent():
297
+ if item not in bottomItems: bottomItems.append(item)
298
+ if item.parent() not in selectedItems: selectedItems.append(item.parent())
299
+ elif item not in selectedItems: selectedItems.append(item)
300
+ self.setSelectedQuickly(bottomItems,False)
301
+ indexes=self.setSelectedQuickly(selectedItems,True)
302
+ return selectedItems,indexes
303
+
304
+ def dragEnterEvent(self, event):
305
+ TABpar.FlagSettingPar=True
306
+ self.dragged_items, self.dragged_indexes = self.selectTopLevel()
307
+ self.expandedItems=[i for i in self.dragged_items if i.isExpanded()]
308
+ #self.collapseAll()
309
+ self.verticalScrollBarVal=self.verticalScrollBar().value()
310
+ super().dragEnterEvent(event)
311
+
312
+ def dragMoveEvent(self, event):
313
+ pos = event.position().toPoint()
314
+ self.hovered_item = item = self.itemAt(pos)
315
+ if item is not None:
316
+ if item.parent(): self.hovered_item = None
317
+ super().dragMoveEvent(event) # Allow the event to proceed for row moves
318
+
319
+ #@log_qpainter_usage
320
+ def paintEvent(self, event):
321
+ super().paintEvent(event)
322
+ self.paintLines()
323
+
324
+ def paintLines(self):
325
+ item=self.hovered_item
326
+ if item and self.dragged_items:
327
+ if self.dragged_items!='externalItem':
328
+ self.drop_indicator_pos = self.dropIndicatorPosition()
329
+ if self.drop_indicator_pos == QTreeWidget.DropIndicatorPosition.AboveItem:
330
+ item_rect = self.visualItemRect(item)
331
+ self.drawDropIndicatorLine(item_rect.top(),item_rect.x(),item_rect.height(),item_rect.width(),-1)
332
+ elif self.drop_indicator_pos == QTreeWidget.DropIndicatorPosition.BelowItem:
333
+ item_rect = self.visualItemRect(item)
334
+ self.drawDropIndicatorLine(item_rect.bottom(),item_rect.x(),item_rect.height(),item_rect.width(),+1)
335
+ else: #below
336
+ item_rect = self.visualItemRect(item)
337
+ self.drawDropIndicatorLine(item_rect.bottom(),item_rect.x(),item_rect.height(),item_rect.width(),+1)
338
+
339
+ def drawDropIndicatorLine(self, y_pos,x_pos,dy,dx,sign=1):
340
+ painter = QPainter(self.viewport())
341
+ painter.setPen(self.pen)
342
+ painter.drawLine(0, y_pos, self.viewport().width(), y_pos)
343
+
344
+ # Calcola la posizione della freccia
345
+ s=5*sign
346
+ for x_pos_2 in (x_pos+2*abs(s),x_pos+dx-2*abs(s)):
347
+ y_pos_2=y_pos-5*sign
348
+ arrow_top = QPoint(x_pos_2, y_pos_2 - 3*s)
349
+ arrow_bottom = QPoint(x_pos_2, y_pos_2)
350
+ arrow_left = QPoint(x_pos_2 - s, y_pos_2-s)
351
+ arrow_right = QPoint(x_pos_2 + s, y_pos_2-s)
352
+
353
+ # Disegna la freccia
354
+ painter.drawLine(arrow_top, arrow_bottom)
355
+ #painter.drawLine(arrow_left, arrow_right)
356
+ painter.drawLine(arrow_bottom, arrow_right)
357
+ painter.drawLine(arrow_bottom, arrow_left)
358
+ painter.end()
359
+
360
+ def dropEvent(self, event):
361
+ drop_indicator_position = self.dropIndicatorPosition()
362
+
363
+ if drop_indicator_position == QTreeWidget.DropIndicatorPosition.OnItem or self.hovered_item is None:
364
+ self.verticalScrollBar().setValue(self.verticalScrollBarVal)
365
+ QCursor.setPos(self.cursor_pos)
366
+ event.ignore() # Ignore the event if it's not a row move or a drop on an item
367
+ FlagUpdateList=False
368
+ else:
369
+ #self.setVisible(False)
370
+ super().dropEvent(event) # Allow the event to proceed for row moves
371
+ """
372
+ ind=self.indexOfTopLevelItem(self.hovered_item)
373
+ if drop_indicator_position == QTreeWidget.DropIndicatorPosition.AboveItem: ind=ind-1
374
+ for index in self.dragged_indexes:
375
+ self.takeTopLevelItem(index)
376
+ self.insertTopLevelItems(ind,self.dragged_items)
377
+ """
378
+
379
+ for i in self.expandedItems: i.setExpanded(True)
380
+ self.dropLists(self.dragged_items,self.dragged_indexes)
381
+ FlagUpdateList=True
382
+ self.setCurrentItem(self.dragged_items[-1])
383
+ self.setSelectedQuickly(self.dragged_items,True)
384
+ self.dragged_items=self.dragged_indexes=None
385
+ self.repaint()
386
+ #self.setVisible(True)
387
+ #evita TABpar.FlagSettingPar=self.FlagSettingPar così che sai dove FlagSettingPar è settato True o False
388
+ if self.FlagSettingPar:
389
+ TABpar.FlagSettingPar=True
390
+ else:
391
+ TABpar.FlagSettingPar=False
392
+ if FlagUpdateList: self.signals.updateLists.emit()
393
+
394
+ def keyPressEvent(self, event):
395
+ super().keyPressEvent(event)
396
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter) and self.hasFocus():
397
+ for f in self.addfuncreturn:
398
+ self.addfuncreturn[f]()
399
+
400
+ def __init__(self, parent: QWidget=None, listDim=1, listDepth=0):
401
+ super().__init__(parent)
402
+ if parent is None:
403
+ self.gui=self.window()
404
+ else:
405
+ from .gPaIRS import gPaIRS
406
+ if hasattr(parent,'gui'):
407
+ self.gui:gPaIRS=parent.gui
408
+ else:
409
+ self.gui:gPaIRS=parent.window()
410
+ self.listDim=listDim
411
+ self.listDepth=listDepth
412
+ self.signals=self.ImageTree_signals()
413
+
414
+ self.setSelectionMode(QTreeWidget.SelectionMode.ExtendedSelection) # Enable multi-selection mode
415
+ self.setSelectionBehavior(QTreeWidget.SelectionBehavior.SelectItems)
416
+ #self.setSelectionBehavior(QTreeWidget.SelectionBehavior.SelectRows)
417
+ self.setDragDropMode(QTreeWidget.DragDropMode.InternalMove) # Abilita il trascinamento delle voci dell'albero
418
+ self.header().setSectionsMovable(False)
419
+ self.setDropIndicatorShown(True)
420
+
421
+ self.icon_warning = QIcon()
422
+ self.icon_warning.addFile(u""+ icons_path +"warning.png", QSize(), QIcon.Normal, QIcon.Off)
423
+ self.dragged_items=self.dragged_indexes=None
424
+ self.hovered_item=None
425
+ self.setAutoScroll(True)
426
+ self.verticalScrollBarVal=self.verticalScrollBar().value()
427
+ self.verticalScrollBar().setStyleSheet("""
428
+ QTreeWidget {
429
+ margin-bottom: 0px;
430
+ }
431
+ QTreeWidget::item {
432
+ margin-bottom: 0px;
433
+ }
434
+ QTreeView {
435
+ margin-bottom: 0px;
436
+ }
437
+ QScrollBar:horizontal{
438
+ height: 15px;
439
+ margin: 3px 0px 3px 0px;
440
+ border: 1px transparent #2A2929;
441
+ border-radius: 4px;
442
+ background-color: transparent; /* #2A2929; */
443
+ }
444
+ QScrollBar::handle:horizontal{
445
+ background-color: rgba(180,180,180,180); /* #605F5F; */
446
+ min-width: 30px;
447
+ border-radius: 4px;
448
+ }
449
+ QScrollBar:vertical{
450
+ width: 15px;
451
+ margin: 0px 3px 0px 3px;
452
+ border: 1px transparent #2A2929;
453
+ border-radius: 4px;
454
+ background-color: transparent; /* #2A2929; */
455
+ }
456
+ QScrollBar::handle:vertical{
457
+ background-color: rgba(180,180,180,180); /* #605F5F; */
458
+ min-height: 30px;
459
+ border-radius: 4px;
460
+ }
461
+ QScrollBar::add-line{
462
+ border: none;
463
+ background: none;
464
+ }
465
+
466
+ QScrollBar::sub-line{
467
+ border: none;
468
+ background: none;
469
+ }""")
470
+ #self.verticalScrollBar().valueChanged.connect(self.on_scroll)
471
+ self.cursor_pos=self.cursor().pos()
472
+
473
+ self.pen = QPen(qRgba(127,227,255,0.9))
474
+ self.pen.setWidth(3)
475
+ #style="background-color: rgba(173,216,230,0.1); color: rgba(128,128,128,0.25);"
476
+ #self.setStyleSheet(f"QTreeWidget::item:selected {{{style}}}")
477
+ #self.setStyleSheet(f"QTreeWidget::item:selected:active {{{style}}}")
478
+
479
+ style = """
480
+ QTreeWidget::item:hover {
481
+ background-color: rgba(0, 116, 255, 0.1);
482
+ }
483
+ QTreeWidget::item:selected:!active {
484
+ background-color: rgba(0, 116, 255, 0.4);
485
+ }
486
+ QTreeWidget::item:selected:active {
487
+ background-color: rgba(0, 116, 255, 0.8);
488
+ }
489
+ """
490
+ self.setStyleSheet(style)
491
+
492
+ self.addfuncreturn={}
493
+ #self.addfuncreturn={'expand': self.expandRow}
494
+ self.FlagSetting=False
495
+ self.FlagReset=False
496
+ self.FlagCutted=False
497
+
498
+ self.nimg=0
499
+ self.itemList=create_empty_list_of_dimension(self.listDim)
500
+ self.warns=[]
501
+
502
+ self.signals.createdItems.connect(self.insertItems2List)
503
+ self.disableTab=lambda flag: None
504
+ self.FlagSettingPar=False
505
+
506
+ self.setAlternatingRowColors(True)
507
+
508
+ self.setVisible(False)
509
+
510
+ def duplicateItem(self, item:QTreeWidgetItem, parent=None):
511
+ new_item = QTreeWidgetItem(parent)
512
+ for column in range(item.columnCount()):
513
+ new_item.setText(column, item.text(column))
514
+ new_item.setTextAlignment(column, Qt.AlignmentFlag(item.textAlignment(column)))
515
+ new_item.setIcon(column, item.icon(column))
516
+ new_item.setData(0,Qt.ItemDataRole.UserRole,item.data(0,Qt.ItemDataRole.UserRole))
517
+ for i in range(item.childCount()):
518
+ self.duplicateItem(item.child(i), new_item)
519
+ if parent: new_item.setExpanded(item.isExpanded())
520
+ return new_item
521
+
522
+ def setSelectedQuickly(self, items, Flag):
523
+ selectionFlag=QItemSelectionModel.SelectionFlag.Select if Flag else QItemSelectionModel.SelectionFlag.Deselect
524
+ selection_model = self.selectionModel()
525
+ selection = QItemSelection()
526
+ indexes=[]
527
+ for i in items:
528
+ i:QTreeWidgetItem
529
+ if i is None: continue
530
+ try:
531
+ if i.parent() is None: index=self.indexOfTopLevelItem(i)
532
+ else: index=i.parent().indexOfChild(i)
533
+ except:
534
+ continue
535
+ selection.merge(QItemSelection(self.model().index(index, 0), self.model().index(index, self.columnCount()-1)), selectionFlag)
536
+ indexes.append(index)
537
+ selection_model.select(selection, QItemSelectionModel.SelectionFlag.ClearAndSelect )
538
+ return indexes
539
+
540
+ def resetImNumber(self,kin=None,kfin=None):
541
+ if not kin: kin=0
542
+ if not kfin: kfin=self.topLevelItemCount()-1
543
+ self.setUpdatesEnabled(False)
544
+
545
+ root_item = self.invisibleRootItem()
546
+ self.warns=[]
547
+ for i in range(self.topLevelItemCount()):
548
+ child_item = root_item.child(i)
549
+ if i>=kin and i<=kfin:
550
+ current_text = child_item.text(0)
551
+ new_text = str(i + 1)
552
+ if current_text != new_text:
553
+ child_item.setText(0, new_text)
554
+ if not child_item.data(0,Qt.ItemDataRole.UserRole)[0] and i not in self.warns:
555
+ self.warns.append(i)
556
+ self.warns.sort()
557
+
558
+ self.setUpdatesEnabled(True)
559
+ return
560
+
561
+ @Slot(int,list,bool,list)
562
+ def insertItems2List(self,i=-1,items=[],FlagSelect=False,selection=[],FlagSignal=True):
563
+ if self.FlagReset:
564
+ self.clear() #clean_tree(self)
565
+ self.FlagReset=False
566
+ if i==-1:
567
+ self.addTopLevelItems(items)
568
+ else:
569
+ self.insertTopLevelItems(i,items)
570
+ if not selection:
571
+ if FlagSelect: self.setSelectedQuickly(items,True)
572
+ else: self.setSelectedQuickly(items[0:1],True)
573
+ else:
574
+ self.spinSelection(selection)
575
+ if items:
576
+ self.scrollToItem(items[-1])
577
+ self.scrollToItem(items[0])
578
+ self.signals.updateTree.emit()
579
+ self.disableTab(False)
580
+ if FlagSignal and not self.signalsBlocked():
581
+ self.signals.updateLists.emit()
582
+
583
+ def spinSelection(self,selection):
584
+ return
585
+
586
+ def dropLists(self, items, indexes):
587
+ ind_in=self.indexOfTopLevelItem(items[0])
588
+ cutted_items=pop_at_depth(self.itemList,self.listDepth,indexes)
589
+ insert_at_depth(self.itemList,self.listDepth,ind_in,cutted_items)
590
+ #ind_fin=self.indexOfTopLevelItem(items[-1])
591
+ self.resetImNumber(kin=max([ind_in-1,0]))
592
+ return
593
+
594
+ def cutLists(self, indexes, FlagDeleted=False):
595
+ if FlagDeleted: type(self).deleted_itemList=pop_at_depth(self.itemList,self.listDepth,indexes)
596
+ else: type(self).cutted_itemList=pop_at_depth(self.itemList,self.listDepth,indexes)
597
+ if not FlagDeleted: self.FlagCutted=True
598
+ self.nimg-=len(indexes)
599
+ self.resetImNumber(kin=min(indexes))
600
+ return
601
+
602
+ def deleteLists(self, indexes):
603
+ self.cutLists(indexes,FlagDeleted=True)
604
+ return
605
+
606
+ def copyLists(self, indexes):
607
+ type(self).cutted_itemList=copy_at_depth(self.itemList,self.listDepth,indexes)
608
+ self.FlagCutted=False
609
+ return
610
+
611
+ def pasteLists(self, ind, FlagDeleted=False):
612
+ pri.Time.magenta('pasteLists: start')
613
+ if FlagDeleted: iList=type(self).deleted_itemList
614
+ else: iList=type(self).cutted_itemList
615
+ self.nimg+=measure_depth_length(iList,self.listDepth)
616
+
617
+ insert_at_depth(self.itemList,self.listDepth,ind,iList)
618
+
619
+ if self.FlagCutted:
620
+ if FlagDeleted:
621
+ type(self).deleted_itemList=[]
622
+ type(self).deleted_items=[]
623
+ else:
624
+ type(self).cutted_itemList=[]
625
+ type(self).cutted_items=[]
626
+ type(self).items_expanded=[]
627
+ self.FlagCutted=False
628
+ else:
629
+ if FlagDeleted: type(self).deleted_itemList=deep_duplicate(iList)
630
+ else: type(self).cutted_itemList=deep_duplicate(iList)
631
+ pri.Time.magenta('pasteLists: list end')
632
+ self.resetImNumber(kin=ind)
633
+ pri.Time.magenta('pasteLists: end')
634
+ return
635
+
636
+ def cleanLists(self):
637
+ self.itemList=create_empty_list_of_dimension(self.listDim)
638
+
639
+ class ItemWorker(QRunnable):
640
+ creationType=0
641
+ FlagRunning=False
642
+ class ItemWorkerSignals(QObject):
643
+ batchItems = Signal(int,int,list) # Signal to send created item to main thread
644
+
645
+ def __init__(self, batch_size=batch_size_image_creation, kin=0, nimg=0, selection=[]):
646
+ super().__init__()
647
+ self.batch_size=batch_size
648
+ self.kin=kin
649
+ self.nimg=nimg
650
+ if not selection:
651
+ selection=[kin,None,None]
652
+ self.selection=selection
653
+
654
+ self.signals = self.ItemWorkerSignals()
655
+ self.is_running=True
656
+ self.is_working=True
657
+ self.is_busy=True
658
+
659
+ def run(self):
660
+ ItemWorker.FlagRunning=True
661
+ kin=self.kin
662
+ j=0
663
+ while kin<self.nimg:
664
+ if not self.is_running:
665
+ ItemWorker.FlagRunning=False
666
+ return
667
+ j+=1
668
+ kfin=min([kin+self.batch_size-1,self.nimg-1])
669
+ #print(f'{j} kin={kin} kfin={kfin}')
670
+ self.signals.batchItems.emit(kin,kfin,self.selection) # Send item to the main thread
671
+ kin=kfin+1
672
+ self.is_busy=True
673
+ while self.is_busy:
674
+ timesleep(sleep_time_item_worker) # Simulate delay in item creation
675
+ while self.is_running:
676
+ timesleep(sleep_time_item_worker)
677
+ ItemWorker.FlagRunning=False
678
+
679
+ @Slot()
680
+ def stopWorker(self):
681
+ self.is_running=False
682
+
683
+ @Slot()
684
+ def endOfTask(self):
685
+ self.is_working=False
686
+
687
+ @Slot()
688
+ def keep_on(self):
689
+ self.is_busy=False
690
+
691
+ class ImageNumberTree(PaIRSTree):
692
+ minimum_width=20
693
+ margin=30
694
+
695
+ def __init__(self, parent: QWidget=None, listDim=4, listDepth=3):
696
+ super().__init__(parent,listDim,listDepth)
697
+
698
+ self.setMinimumWidth(self.minimum_width+self.margin)
699
+
700
+ columns=['#']
701
+ self.setColumnCount(len(columns))
702
+ self.setHeaderLabels(columns)
703
+ header=self.header()
704
+ self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
705
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
706
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
707
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
708
+ self.setIndentation(0)
709
+ self.setUniformRowHeights(True)
710
+ self.setStyleSheet(f"QTreeView::item {{ height: {self.default_row_height} px; }}")
711
+
712
+ #Disabilita la selezione
713
+ self.setSelectionMode(QTreeWidget.NoSelection)
714
+ #Disabilita il drag-and-drop
715
+ self.setDragEnabled(False)
716
+ self.setDropIndicatorShown(False)
717
+ self.setAcceptDrops(False)
718
+
719
+ self.resizeTree()
720
+ self.nItem=0
721
+ self.ncam=1
722
+
723
+ #self.itemSelectionChanged.connect(self.selectParent)
724
+ self.mainTree:PaIRSTree=None
725
+
726
+ def resizeTree(self):
727
+ n=self.topLevelItemCount()
728
+ currwidth=self.maximumWidth()
729
+ if not n:
730
+ self.setMaximumWidth(self.minimumWidth())
731
+ else:
732
+ text=self.topLevelItem(n-1).text(0)
733
+ textSize=QtGui.QFontMetrics(self.font()).size(QtCore.Qt.TextSingleLine, text)
734
+ if abs(currwidth-textSize.width()-self.margin)>self.margin:
735
+ self.setMaximumWidth(textSize.width()+self.margin) # Aggiungi un margine se necessario
736
+ self.currwidth=self.maximumWidth()
737
+ if abs(currwidth-self.currwidth)>self.margin:
738
+ self.resizeColumnToContents(0)
739
+
740
+ def insertNumbers(self,nItem,ncam):
741
+ if ncam!=self.ncam:
742
+ self.clear()
743
+ self.ncam=ncam
744
+ self.nItem=nItem
745
+ n=self.topLevelItemCount()
746
+ items=[]
747
+ if n<nItem:
748
+ for i in range(n,nItem):
749
+ item=QTreeWidgetItem(self,[f'{i+1}'])
750
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignVCenter)
751
+ for c in range(ncam-1):
752
+ item.addChild(QTreeWidgetItem(item,''))
753
+ #item.setExpanded(False)
754
+ items.append(item)
755
+ self.addTopLevelItems(items)
756
+ elif n>nItem:
757
+ self.blockSignals(True)
758
+ for i in range(n,nItem-1,-1):
759
+ self.takeTopLevelItem(i)
760
+ self.blockSignals(False)
761
+ self.resizeTree()
762
+
763
+ def selectParent(self):
764
+ selectedItems=self.selectedItems()
765
+ self.blockSignals(True)
766
+ self.clearSelection()
767
+ for i in selectedItems:
768
+ if i.parent() is not None:
769
+ i.parent().setSelected(True)
770
+ else:
771
+ i.setSelected(True)
772
+ self.blockSignals(False)
773
+
774
+ class AsynPaIRSTree(PaIRSTree):
775
+ class PaIRSTreeTypes:
776
+ single=0
777
+ glob=1
778
+
779
+ def on_scroll(self):
780
+ return
781
+
782
+ def __init__(self, parent: QWidget=None, listDim=2, listDepth=1, FlagNum=False):
783
+ super().__init__(parent,listDim,listDepth)
784
+
785
+ self.columnLabels=[]
786
+ self.FlagNum = FlagNum
787
+ self.pixmap_edit = QPixmap(icons_path+"edit_list.png")
788
+ self.pixmap_copy = QPixmap(icons_path+"copy.png")
789
+ self.pixmap_cut = QPixmap(icons_path+"cut.png")
790
+ self.pixmap_wrap = QPixmap(icons_path+"wrap_items.png")
791
+ self.pixmap_unwrap = QPixmap(icons_path+"unwrap_items.png")
792
+ self.pixmap_paste_above = QPixmap(icons_path+"paste_above.png")
793
+ self.pixmap_paste_below = QPixmap(icons_path+"paste_below.png")
794
+ self.pixmap_import_items = QPixmap(icons_path+"read.png")
795
+
796
+ self.ThreadPool = QThreadPool()
797
+ self.batch_size=batch_size_image_creation
798
+ self.numTree:ImageNumberTree=None
799
+ self.disableButtons=lambda flag:None
800
+ self.progressBar:QProgressBar=None
801
+ self.infoLabel:QLabel=None
802
+
803
+ self.initializeWorker()
804
+
805
+ self.imList=create_empty_list_of_dimension(self.listDim-1)
806
+ self.imEx=create_empty_list_of_dimension(self.listDim-1)
807
+
808
+ self.itemWorker=None
809
+
810
+ self.type=self.PaIRSTreeTypes().glob
811
+ self.numTree=None
812
+
813
+ def setVisible(self,visible):
814
+ super().setVisible(visible)
815
+ if hasattr(self,'numTree') and self.numTree is not None:
816
+ self.numTree.setVisible(visible)
817
+
818
+ def setNumTree(self,numTree:ImageNumberTree):
819
+ self.numTree=numTree
820
+ self.numTree.mainTree=self
821
+
822
+ self.verticalScrollBar().valueChanged.connect(self.scrollNumTree)
823
+ self.numTree.verticalScrollBar().valueChanged.connect(self.scrollMainTree)
824
+
825
+ self.itemExpanded.connect(lambda item: self.expandNumTree(item,True))
826
+ self.itemCollapsed.connect(lambda item: self.expandNumTree(item,False))
827
+ self.numTree.itemExpanded.connect(lambda item: self.expandMainTree(item,True))
828
+ self.numTree.itemCollapsed.connect(lambda item: self.expandMainTree(item,False))
829
+
830
+ #self.itemSelectionChanged.connect(self.selectNumTree)
831
+ #self.numTree.itemSelectionChanged.connect(self.selectMainTree)
832
+
833
+ self.Expanding=False
834
+
835
+ def scrollNumTree(self):
836
+ self.numTree.verticalScrollBar().setValue(self.verticalScrollBar().value())
837
+
838
+ def scrollMainTree(self):
839
+ self.verticalScrollBar().setValue(self.numTree.verticalScrollBar().value())
840
+
841
+ def expandNumTree(self,item,flag):
842
+ if self.Expanding: return
843
+ self.Expanding=True
844
+ i=self.indexOfTopLevelItem(item)
845
+ self.numTree.topLevelItem(i).setExpanded(flag)
846
+ self.Expanding=False
847
+
848
+ def expandMainTree(self,item,flag):
849
+ if self.Expanding: return
850
+ self.Expanding=True
851
+ i=self.numTree.indexOfTopLevelItem(item)
852
+ self.topLevelItem(i).setExpanded(flag)
853
+ self.Expanding=False
854
+
855
+ def selectNumTree(self):
856
+ selectedItems=self.selectedItems()
857
+ selectedItems=[i.parent() if i.parent() is not None else i for i in selectedItems]
858
+ items=[self.numTree.topLevelItem(self.indexOfTopLevelItem(i)) for i in selectedItems]
859
+ self.numTree.setSelectedQuickly(items,True)
860
+
861
+ def selectMainTree(self):
862
+ selectedItems=self.numTree.selectedItems()
863
+ items=[self.topLevelItem(self.numTree.indexOfTopLevelItem(i)) for i in selectedItems]
864
+ self.setSelectedQuickly(items,True)
865
+
866
+ def resetImNumber(self,kin=None,kfin=None):
867
+ self.setUpdatesEnabled(False)
868
+
869
+ if self.numTree:
870
+ self.numTree.insertNumbers(self.nimg,self.ncam)
871
+ for k in range(self.nimg):
872
+ item=self.topLevelItem(k)
873
+ numb=self.numTree.topLevelItem(k)
874
+ numb.setExpanded(item.isExpanded())
875
+
876
+ warns=np.array(self.imEx)
877
+ if len(warns.shape)==3:
878
+ self.warns:list=np.where(np.any(warns == False, axis=(0, 1)))[0].tolist()
879
+ else:
880
+ self.warns = np.where(warns == False)[0].tolist()
881
+ self.warns.sort()
882
+ self.setColumnHidden(0,(self.ncam==1 or self.type==self.PaIRSTreeTypes.single) and len(self.warns)==0)
883
+
884
+ self.setUpdatesEnabled(True)
885
+ return
886
+
887
+ def setItemWorker(self,nimg,selection,fun,pixmap):
888
+ self.disableButtons(True)
889
+ #self.disableTab(True)
890
+ worker = ItemWorker(self.batch_size,0,nimg,selection)
891
+ worker.signals.batchItems.connect(fun)
892
+ self.signals.endOfTask.connect(worker.endOfTask)
893
+ self.signals.stopWorker.connect(worker.stopWorker)
894
+ self.signals.keep_on.connect(worker.keep_on)
895
+ if self.progressBar:
896
+ self.progressBar.setMinimum(0)
897
+ self.progressBar.setMaximum(nimg-1)
898
+ self.progressBar.setValue(0)
899
+ self.infoLabel.setPixmap(pixmap)
900
+ return worker
901
+
902
+ def initializeWorker(self):
903
+ self.workerItems=[]
904
+ self.workerIndexes=[]
905
+ self.workerFlagCut=None
906
+ self.workerFlagWrap=None
907
+ self.workerInd=None
908
+ self.workerNimg=0
909
+
910
+ def setStepButtonEnabled(self,FlagEnabled):
911
+ self.stepButton.setEnabled(FlagEnabled)
912
+ return
913
+
914
+ def stopWorker(self):
915
+ if self.itemWorker is not None:
916
+ self.signals.stopWorker.emit()
917
+ self.initializeWorker()
918
+ #QApplication.processEvents()
919
+ timesleep(sleep_time_item_worker*2)
920
+ self.stepButton.setEnabled(True)
921
+ #while ItemWorker.FlagRunning==True:
922
+ # timesleep(sleep_time_item_worker*2)
923
+
924
+ def startWorker(self):
925
+ self.stopWorker()
926
+ self.stepButton:QToolButton=self.gui.ui.Explorer.stepButtonBar.buttons[self.gui.ui.Explorer.ITEpar.Step]
927
+ self.stepButtonEnabled=self.stepButton.isEnabled()
928
+ self.setStepButtonEnabled(False)
929
+
930
+ #worker
931
+ def setLists(self,selection=[],FlagAsync=True):
932
+ self.startWorker()
933
+ if self.type==self.PaIRSTreeTypes().glob:
934
+ self.nimg=nimg=len(self.imList[0][0])
935
+ self.ncam=len(self.imList)
936
+ else:
937
+ self.nimg=nimg=len(self.imList)
938
+ self.ncam=1
939
+ self.setColumnHidden(0,self.ncam==1)
940
+ if self.ncam==1 and len(self.columnLabels)>1:
941
+ columns=[""]+self.columnLabels[1:]
942
+ else:
943
+ columns=self.columnLabels
944
+ self.setHeaderLabels(columns)
945
+
946
+ self.clear() #clean_tree(self)
947
+
948
+ if self.nimg:
949
+ if self.FlagNum:
950
+ if not selection:
951
+ selection=[0,None,None]
952
+ self.createChuncks(0,self.nimg-1,selection)
953
+ else:
954
+ self.itemWorker=self.setItemWorker(nimg,selection,self.createChuncks,self.pixmap_edit)
955
+ self.ThreadPool.start(self.itemWorker)
956
+ else:
957
+ if self.workerFlagCut: self.endOfCut()
958
+ self.endOfCreation()
959
+ return
960
+
961
+ def resizeHeader(self):
962
+ for i in range(self.columnCount()-1):
963
+ self.resizeColumnToContents(i)
964
+
965
+ @Slot(int,int,list)
966
+ def createChuncks(self,kin,kfin,selection):
967
+ FlagSettingPar=TABpar.FlagSettingPar
968
+ TABpar.FlagSettingPar=True
969
+ items=self.createItems(kin,kfin)
970
+ self.addTopLevelItems(items)
971
+ if self.numTree: self.numTree.insertNumbers(kfin,self.ncam)
972
+ self.resizeHeader()
973
+ if self.progressBar: self.progressBar.setValue(kfin)
974
+ if kin<=selection[0]<=kfin:
975
+ if selection[1] is None:
976
+ item_in=self.topLevelItem(selection[0])
977
+ self.setSelectedQuickly([item_in],True)
978
+ else:
979
+ self.spinSelection(selection)
980
+ self.signals.keep_on.emit()
981
+ TABpar.FlagSettingPar=FlagSettingPar
982
+ if kfin==self.nimg-1:
983
+ self.endOfCreation()
984
+
985
+
986
+ def endOfCreation(self):
987
+ if self.workerFlagCut:
988
+ self.endOfCut()
989
+ else:
990
+ self.resetImNumber()
991
+ self.signals.updateTree.emit()
992
+ self.disableTab(False)
993
+ self.signals.stopWorker.emit()
994
+ self.setStepButtonEnabled(self.stepButtonEnabled)
995
+ self.disableButtons(False)
996
+ self.itemWorker=None
997
+ if not self.signalsBlocked():
998
+ self.signals.updateLists.emit()
999
+
1000
+ #worker
1001
+ def copyCutItems(self,items,indexes,FlagCut):
1002
+ self.startWorker()
1003
+ self.workerItems=items
1004
+ self.workerIndexes=indexes
1005
+ self.workerFlagCut=FlagCut
1006
+ self.workerNimg=self.nimg
1007
+ nimg=len(self.workerIndexes)
1008
+
1009
+ type(self).cutted_items=[None]*nimg
1010
+ type(self).cutted_itemList=[]
1011
+
1012
+ pixmap=self.pixmap_cut if FlagCut else self.pixmap_copy
1013
+ self.itemWorker=self.setItemWorker(nimg,[],self.copyCutChunks,pixmap)
1014
+ self.ThreadPool.start(self.itemWorker)
1015
+
1016
+ @Slot(int,int,list)
1017
+ def copyCutChunks(self,kin,kfin,selection):
1018
+ FlagSettingPar=TABpar.FlagSettingPar
1019
+ TABpar.FlagSettingPar=True
1020
+ n=len(self.workerIndexes)-1
1021
+ for k in range(n-kin,n-kfin-1,-1):
1022
+ type(self).cutted_items[k]=self.duplicateItem(self.workerItems[k])
1023
+ cutted_list=copy.deepcopy(type(self).cutted_itemList)
1024
+ self.blockSignals(True)
1025
+ self.setUpdatesEnabled(False)
1026
+ if self.workerFlagCut:
1027
+ if n<0.5*self.workerNimg:
1028
+ for k in range(n-kin,n-kfin-1,-1):
1029
+ self.takeTopLevelItem(self.workerIndexes[k])
1030
+ self.cutLists(self.workerIndexes[n-kfin:n-kin+1])
1031
+ else:
1032
+ self.copyLists(self.workerIndexes[n-kfin:n-kin+1])
1033
+ self.setUpdatesEnabled(True)
1034
+ self.blockSignals(False)
1035
+ insert_at_depth(type(self).cutted_itemList,self.listDepth,-1,cutted_list)
1036
+ if self.numTree: self.numTree.insertNumbers(self.nimg,self.ncam)
1037
+ self.resizeHeader()
1038
+ self.progressBar.setValue(kfin)
1039
+ if kin<=selection[0]<=kfin and self.workerFlagCut:
1040
+ if selection[1] is None:
1041
+ item_in=self.topLevelItem(selection[0])
1042
+ self.setSelectedQuickly([item_in],True)
1043
+ else:
1044
+ self.spinSelection(selection)
1045
+ self.signals.keep_on.emit()
1046
+ TABpar.FlagSettingPar=FlagSettingPar
1047
+ if kfin==len(self.workerIndexes)-1:
1048
+ if n>=0.5*self.workerNimg:
1049
+ if self.workerFlagCut:
1050
+ self.setLists()
1051
+ return
1052
+ self.endOfCut()
1053
+ if not self.signalsBlocked():
1054
+ self.signals.updateLists.emit()
1055
+
1056
+ def endOfCut(self):
1057
+ self.resetImNumber()
1058
+ self.disableButtons(False)
1059
+ self.workerItems=[]
1060
+ self.workerIndexes=[]
1061
+ self.workerFlagCut=None
1062
+ self.workerNimg=0
1063
+ self.signals.copyCutItems.emit()
1064
+ self.itemWorker=None
1065
+ self.setStepButtonEnabled(True)
1066
+
1067
+
1068
+ #worker
1069
+ def wrapUnWrapItems(self,items,indexes,FlagWrap):
1070
+ self.startWorker()
1071
+ self.workerItems=items
1072
+ self.workerIndexes=indexes
1073
+ self.workerFlagWrap=FlagWrap
1074
+ nimg=len(self.workerIndexes)
1075
+
1076
+ pixmap=self.pixmap_wrap if FlagWrap else self.pixmap_unwrap
1077
+ self.itemWorker=self.setItemWorker(nimg,[],self.wrapUnWrapChuncks,pixmap)
1078
+ self.ThreadPool.start(self.itemWorker)
1079
+
1080
+ @Slot(int,int,list)
1081
+ def wrapUnWrapChuncks(self,kin,kfin,selection):
1082
+ FlagSettingPar=TABpar.FlagSettingPar
1083
+ TABpar.FlagSettingPar=True
1084
+ self.blockSignals(True)
1085
+ self.setUpdatesEnabled(False)
1086
+ for k in range(kin,kfin+1):
1087
+ item:QTreeWidgetItem=self.workerItems[k]
1088
+ item.setExpanded(not self.workerFlagWrap)
1089
+ self.setUpdatesEnabled(True)
1090
+ self.blockSignals(False)
1091
+ self.progressBar.setValue(kfin)
1092
+ #QApplication.processEvents()
1093
+ self.signals.keep_on.emit()
1094
+ TABpar.FlagSettingPar=FlagSettingPar
1095
+ if kfin==len(self.workerIndexes)-1:
1096
+ self.endOfWrap()
1097
+
1098
+
1099
+ def endOfWrap(self):
1100
+ self.resetImNumber()
1101
+ self.disableButtons(False)
1102
+ self.workerItems=[]
1103
+ self.workerIndexes=[]
1104
+ self.workerFlagWrap=None
1105
+ self.itemWorker=None
1106
+ self.setStepButtonEnabled(True)
1107
+
1108
+ #worker
1109
+ def pasteItems(self,items,i,FlagAbove):
1110
+ self.startWorker()
1111
+ self.workerItems=items
1112
+ self.workerInd=i
1113
+ self.clearSelection()
1114
+ nimg=len(self.workerItems)
1115
+
1116
+ pixmap=self.pixmap_paste_above if FlagAbove else self.pixmap_paste_below
1117
+ self.itemWorker=self.setItemWorker(nimg,[],self.pasteChuncks,pixmap)
1118
+ self.ThreadPool.start(self.itemWorker)
1119
+
1120
+ @Slot(int,int,list)
1121
+ def pasteChuncks(self,kin,kfin,selection):
1122
+ FlagSettingPar=TABpar.FlagSettingPar
1123
+ TABpar.FlagSettingPar=True
1124
+ n=len(self.workerItems)
1125
+ items=self.workerItems[kin:kfin+1]
1126
+ self.insertTopLevelItems(self.workerInd+kin,items)
1127
+ self.blockSignals(True)
1128
+ for i in items:
1129
+ i:QTreeWidgetItem
1130
+ i.setSelected(True)
1131
+ self.blockSignals(False)
1132
+ if self.numTree: self.numTree.insertNumbers(self.nimg+kfin,self.ncam)
1133
+ self.resizeHeader()
1134
+ self.progressBar.setValue(kfin)
1135
+ self.signals.keep_on.emit()
1136
+ TABpar.FlagSettingPar=FlagSettingPar
1137
+ if kfin==n-1:
1138
+ self.endOfPaste()
1139
+
1140
+
1141
+ def endOfPaste(self):
1142
+ self.FlagCutted=True
1143
+ self.pasteLists(self.workerInd)
1144
+ self.workerItems=[]
1145
+ self.workerInd=None
1146
+
1147
+ self.signals.updateTree.emit()
1148
+ self.disableTab(False)
1149
+ self.signals.stopWorker.emit()
1150
+ self.setStepButtonEnabled(True)
1151
+ self.disableButtons(False)
1152
+ self.signals.pastedItems.emit()
1153
+ self.itemWorker=None
1154
+ if not self.signalsBlocked():
1155
+ self.signals.updateLists.emit()
1156
+
1157
+ #worker
1158
+ def importItems(self,items):
1159
+ self.startWorker()
1160
+ self.workerItems=items
1161
+ self.clearSelection()
1162
+ nimg=len(self.workerItems)
1163
+
1164
+ pixmap=self.pixmap_import_items
1165
+ self.itemWorker=self.setItemWorker(nimg,[],self.importChuncks,pixmap)
1166
+ self.ThreadPool.start(self.itemWorker)
1167
+
1168
+ @Slot(int,int,list)
1169
+ def importChuncks(self,kin,kfin,selection):
1170
+ FlagSettingPar=TABpar.FlagSettingPar
1171
+ TABpar.FlagSettingPar=True
1172
+ n=len(self.workerItems)
1173
+ items=self.workerItems[kin:kfin+1]
1174
+ self.addTopLevelItems(items)
1175
+ for i in items:
1176
+ i:QTreeWidgetItem
1177
+ i.setSelected(True)
1178
+ if self.numTree: self.numTree.insertNumbers(self.nimg+kfin,self.ncam)
1179
+ self.resizeHeader()
1180
+ self.progressBar.setValue(kfin)
1181
+ self.signals.keep_on.emit()
1182
+ TABpar.FlagSettingPar=FlagSettingPar
1183
+ if kfin==n-1:
1184
+ self.endOfImport()
1185
+
1186
+ def endOfImport(self):
1187
+ self.resetImNumber()
1188
+ self.workerItems=[]
1189
+
1190
+ self.signals.updateTree.emit()
1191
+ self.disableTab(False)
1192
+ self.signals.stopWorker.emit()
1193
+ self.setStepButtonEnabled(True)
1194
+ self.disableButtons(False)
1195
+ self.signals.pastedItems.emit()
1196
+ self.itemWorker=None
1197
+ if not self.signalsBlocked():
1198
+ self.signals.updateLists.emit()
1199
+
1200
+ class GlobalImageTree(AsynPaIRSTree):
1201
+
1202
+ def __init__(self, parent: QWidget=None, listDim=4, listDepth=3, FlagNum=False):
1203
+ super().__init__(parent,listDim,listDepth)
1204
+
1205
+ self.FlagNum=FlagNum
1206
+ self.columnLabels=["cam","frame 1","frame 2"]
1207
+ self.setHeaderLabels(self.columnLabels)
1208
+ self.setColumnCount(len(self.columnLabels))
1209
+ header=self.header()
1210
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
1211
+ header.setSectionResizeMode(1, QHeaderView.ResizeMode.Interactive)
1212
+ header.setSectionResizeMode(2, QHeaderView.ResizeMode.Interactive)
1213
+ self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1214
+ self.setUniformRowHeights(True)
1215
+ self.setStyleSheet(self.styleSheet()+f"QTreeView::item {{ height: {self.default_row_height} px; }}")
1216
+
1217
+ self.path=''
1218
+ self.ncam=1
1219
+ self.nframe=2
1220
+ self.imList=create_empty_list_of_dimension(self.listDim-1)
1221
+ self.imEx=create_empty_list_of_dimension(self.listDim-1)
1222
+ self.setImListEx()
1223
+ self.itemDoubleClicked.connect(self.item_double_clicked)
1224
+
1225
+ def item_double_clicked(self, item, column):
1226
+ self.setSelectedQuickly([item]+self.selectedItems(),True)
1227
+
1228
+ def setImListEx(self):
1229
+ self.itemList=create_empty_list_of_dimension(self.listDim)
1230
+ expand_level(self.itemList,level=0,target_length=2)
1231
+ #expand_level(self.itemList,level=1,target_length=self.ncam)
1232
+ #expand_level(self.itemList,level=2,target_length=self.nframe)
1233
+
1234
+ self.itemList[0]=self.imList
1235
+ self.itemList[1]=self.imEx
1236
+
1237
+ def indexSelection(self):
1238
+ item=self.currentItem()
1239
+ if item:
1240
+ FlagBottomLevel=bool(item.parent())
1241
+ parent_item=item.parent() if FlagBottomLevel else item
1242
+ img=self.indexOfTopLevelItem(parent_item)+1
1243
+ cam=1 if not FlagBottomLevel else parent_item.indexOfChild(item)+2
1244
+ frame=1 if self.currentColumn()<=1 else self.currentColumn()
1245
+ else:
1246
+ img=cam=frame=0
1247
+ return img, cam, frame
1248
+
1249
+ def spinSelection(self,selection):
1250
+ if not selection:
1251
+ self.clearSelection()
1252
+ #self.setCurrentItem(None)
1253
+ return
1254
+ r,c,f=[i-1 for i in selection][:]
1255
+ if r<0:
1256
+ self.clearSelection()
1257
+ #self.setCurrentItem(None)
1258
+ self.signals.updateTree.emit()
1259
+ return
1260
+ parent_item = self.topLevelItem(r)
1261
+ if parent_item:
1262
+ if c==0: item=parent_item
1263
+ else: item=parent_item.child(c-1)
1264
+ self.indexFromItem(item)
1265
+ self.setCurrentItem(item, f+1)
1266
+
1267
+ def createNullItem(self,k=None):
1268
+ if not k: k=self.topLevelItemCount()
1269
+ data=[False,[],[]]
1270
+ data[1].append(['(!)','(!)'])
1271
+ data[2].append([False,False])
1272
+ item_data=['1','(!)','(!)']
1273
+ item=QTreeWidgetItem(None,item_data)
1274
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1275
+ item.setToolTip(1,item_data[1])
1276
+ item.setStatusTip(1,item_data[1])
1277
+ item.setToolTip(2,item_data[2])
1278
+ item.setStatusTip(2,item_data[2])
1279
+ for c in range(1,self.ncam):
1280
+ data[1].append(['(!)','(!)'])
1281
+ data[2].append([False,False])
1282
+ item_data=[str(c+1),'(!)','(!)']
1283
+ item2=QTreeWidgetItem(item,item_data)
1284
+ item2.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1285
+ item.setToolTip(2,item_data[2])
1286
+ item.setStatusTip(2,item_data[2])
1287
+ item.setToolTip(3,item_data[3])
1288
+ item.setStatusTip(3,item_data[3])
1289
+ item.setIcon(0,self.icon_warning)
1290
+ item.setToolTip(0,'Files (!) missing')
1291
+ item.setStatusTip(0,'Files (!) missing')
1292
+ item.setData(0,Qt.ItemDataRole.UserRole,data)
1293
+ self.insertTopLevelItem(k,item)
1294
+ self.warns.append(k)
1295
+
1296
+ def scanLists(self,FlagChange=True):
1297
+ def scan_items(k):
1298
+ topLevelItem=None
1299
+ FlagChanged=False
1300
+ FlagWarn=False
1301
+ for c in range(self.ncam):
1302
+ for f in range(2):
1303
+ item=None
1304
+ self.imEx[c][f][k]=os.path.exists(self.path+self.imList[c][f][k]) if self.imList[c][f][k] else False
1305
+ FlagWarn=FlagWarn or not self.imEx[c][f][k]
1306
+ if FlagChange: #and ex!=self.imEx[c][f][k]
1307
+ FlagChanged=True
1308
+ if item==None:
1309
+ if topLevelItem==None: topLevelItem=self.topLevelItem(k)
1310
+ if c: item=topLevelItem.child(c-1)
1311
+ else: item=topLevelItem
1312
+ text=self.imList[c][f][k]
1313
+ if not self.imEx[c][f][k]: text=text+' (!)' if text else '(!)'
1314
+ item.setText(f+1,text)
1315
+ item.setToolTip(f+1,text)
1316
+ item.setStatusTip(f+1,text)
1317
+ #if not FlagChanged: return
1318
+ if topLevelItem==None: topLevelItem=self.topLevelItem(k)
1319
+ #FlagWarn_old=k in self.warns
1320
+ #if FlagWarn!=FlagWarn_old:
1321
+ if FlagWarn:
1322
+ self.warns.append(k)
1323
+ topLevelItem.setIcon(0,self.icon_warning)
1324
+ topLevelItem.setToolTip(0,'Files (!) missing')
1325
+ topLevelItem.setStatusTip(0,'Files (!) missing')
1326
+ elif k in self.warns:
1327
+ self.warns.remove(k)
1328
+ topLevelItem.setIcon(0,QIcon())
1329
+ topLevelItem.setToolTip(0,'')
1330
+ topLevelItem.setStatusTip(0,'')
1331
+ return
1332
+ for k in range(self.topLevelItemCount()): scan_items(k)
1333
+ self.resetImNumber()
1334
+ return
1335
+
1336
+ def printImageList(self):
1337
+ for i in range(self.nimg):
1338
+ s='*' if not self.eim[i] else ' '
1339
+ pri.Coding.white(f'{i:5d}{s}:'+'\t'+f'{self.imList[0][0][i]}, {self.imList[0][1][i]}')
1340
+ for c in range(1,self.ncam):
1341
+ pri.Coding.white(f' '+'\t'+f'{self.imList[c][0][i]}, {self.imList[c][1][i]}')
1342
+
1343
+ def createItems(self,kin=None,kfin=None):
1344
+ imList=self.imList
1345
+ imEx=self.imEx
1346
+
1347
+ if kin is None: kin=0
1348
+ if kfin is None: kfin=min([self.batch_size-1,len(imList[0][0])])
1349
+ nimg=kfin-kin+1
1350
+ ncam=len(imList)
1351
+
1352
+ self.warns=[]
1353
+ items=[None]*nimg
1354
+ j=-1
1355
+ for k in range(kin,kfin+1):
1356
+ j+=1
1357
+ data=[True,[],[]]
1358
+ FlagWarn=False
1359
+
1360
+ if k>=len(imList[0][0]):
1361
+ for i in range(kfin,k-1,-1):
1362
+ items.pop(-1)
1363
+ break
1364
+ images=[imList[0][0][k], imList[0][1][k]]
1365
+ ex=[imEx[0][0][k], imEx[0][1][k]]
1366
+ data[1].append(images)
1367
+ data[2].append(ex)
1368
+ item_data=['1' if self.ncam>1 else '']+images#[str(k+1),'1']+images
1369
+ if not ex[0]:
1370
+ FlagWarn=True
1371
+ item_data[1]=item_data[1]+' (!)' if item_data[1] else '(!)'
1372
+ if not ex[1]:
1373
+ FlagWarn=True
1374
+ item_data[2]=item_data[2]+' (!)' if item_data[2] else '(!)'
1375
+
1376
+ item=QTreeWidgetItem(self,item_data)
1377
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1378
+ items[j]=item
1379
+ item.setToolTip(1,item_data[1])
1380
+ item.setStatusTip(1,item_data[1])
1381
+ item.setToolTip(2,item_data[2])
1382
+ item.setStatusTip(2,item_data[2])
1383
+
1384
+ for c in range(1,ncam):
1385
+ images=[imList[c][0][k], imList[c][1][k]]
1386
+ ex=[imEx[c][0][k], imEx[c][1][k]]
1387
+ data[1].append(images)
1388
+ data[2].append(ex)
1389
+ item_data=[str(c+1)]+images #['',str(c+1)]+images
1390
+ if not ex[0]:
1391
+ FlagWarn=True
1392
+ item_data[1]=item_data[1]+' (!)' if item_data[1] else '(!)'
1393
+ if not ex[1]:
1394
+ FlagWarn=True
1395
+ item_data[2]=item_data[2]+' (!)' if item_data[2] else '(!)'
1396
+ if c>item.childCount()-1:
1397
+ item2=QTreeWidgetItem(item,item_data)
1398
+ item2.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1399
+ else:
1400
+ item2=item.child(c)
1401
+ [item2.setText(k,t) for k,t in enumerate(item_data)]
1402
+ item.setToolTip(1,item_data[1])
1403
+ item.setStatusTip(1,item_data[1])
1404
+ item.setToolTip(2,item_data[2])
1405
+ item.setStatusTip(2,item_data[2])
1406
+ [item.takeChild(k) for k in range(item.childCount()-1,ncam-1,-1)]
1407
+
1408
+ if FlagWarn:
1409
+ data[0]=False
1410
+ item.setIcon(0,self.icon_warning)
1411
+ item.setToolTip(0,'Files (!) missing')
1412
+ item.setStatusTip(0,'Files (!) missing')
1413
+ self.warns.append(k)
1414
+ item.setData(0,Qt.ItemDataRole.UserRole,data)
1415
+ #self.signals.createdItem.emit()
1416
+ return items
1417
+
1418
+ class SingleImageTree(AsynPaIRSTree):
1419
+
1420
+ def __init__(self, parent: QWidget=None,listDim=2,listDepth=1):
1421
+ super().__init__(parent,listDim,listDepth)
1422
+
1423
+ columns=['',"filename"]
1424
+ self.setColumnCount(len(columns))
1425
+ self.setHeaderLabels(columns)
1426
+ header=self.header()
1427
+ self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1428
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
1429
+ header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
1430
+ self.setUniformRowHeights(True)
1431
+ self.setStyleSheet(f"QTreeView::item {{ height: {self.default_row_height} px; }}")
1432
+
1433
+ self.c=None #cam
1434
+ self.f=None #frame
1435
+ self.parentImTree=None
1436
+ self.ncam=1
1437
+
1438
+ self.imList=create_empty_list_of_dimension(1)
1439
+ self.imEx=create_empty_list_of_dimension(1)
1440
+
1441
+ expand_level(self.itemList,level=0,target_length=2)
1442
+ self.itemList[0]=self.imList
1443
+ self.itemList[1]=self.imEx
1444
+
1445
+ self.type=self.PaIRSTreeTypes().single
1446
+
1447
+ def indexSelection(self):
1448
+ item=self.currentItem()
1449
+ if item:
1450
+ FlagBottomLevel=bool(item.parent())
1451
+ parent_item=item.parent() if FlagBottomLevel else item
1452
+ img=self.indexOfTopLevelItem(parent_item)+1
1453
+ else:
1454
+ img=0
1455
+ cam=self.c+1
1456
+ frame=self.f+1
1457
+ return img, cam, frame
1458
+
1459
+ def spinSelection(self,selection):
1460
+ if not selection:
1461
+ self.clearSelection()
1462
+ #self.setCurrentItem(None)
1463
+ return
1464
+ r,c,f=[i-1 for i in selection][:]
1465
+ if r<0:
1466
+ self.clearSelection()
1467
+ #self.setCurrentItem(None)
1468
+ self.signals.updateTree.emit()
1469
+ return
1470
+ parent_item = self.topLevelItem(r)
1471
+ if parent_item:
1472
+ if c==0: item=parent_item
1473
+ else: item=parent_item.child(c-1)
1474
+ self.indexFromItem(item)
1475
+ self.setCurrentItem(item, f+1)
1476
+
1477
+ def createItems(self,kin=None,kfin=None):
1478
+ imList=self.imList
1479
+ imEx=self.imEx
1480
+
1481
+ if kin is None: kin=0
1482
+ if kfin is None: kfin=min([self.batch_size-1,len(imList[0][0])])
1483
+ nimg=kfin-kin+1
1484
+
1485
+ self.warns=[]
1486
+ items=[None]*nimg
1487
+ j=-1
1488
+ for k in range(kin,kfin+1):
1489
+ j+=1
1490
+ FlagWarn=False
1491
+
1492
+ image=imList[k]
1493
+ ex=imEx[k]
1494
+ item_data=['',image]
1495
+ if not ex:
1496
+ FlagWarn=True
1497
+ item=QTreeWidgetItem(None,item_data)
1498
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1499
+ items[j]=item
1500
+ item.setToolTip(1,item_data[1])
1501
+ item.setStatusTip(1,item_data[1])
1502
+
1503
+ if FlagWarn:
1504
+ item.setIcon(0,self.icon_warning)
1505
+ item.setToolTip(0,'File missing')
1506
+ item.setStatusTip(0,'File missing')
1507
+ self.warns.append(k)
1508
+ data=[not FlagWarn,image,ex]
1509
+ item.setData(0,Qt.ItemDataRole.UserRole,data)
1510
+ return items
1511
+
1512
+ def sortLists(self, reverse=False):
1513
+ zipped_lists=sorted(zip(self.imList, self.imEx), key=lambda x: x[0], reverse=reverse)
1514
+ sorted_imList, sorted_imEx=zip(*zipped_lists)
1515
+ for k in range(self.nimg):
1516
+ self.imList[k]=sorted_imList[k]
1517
+ self.imEx[k]=sorted_imEx[k]
1518
+ self.setLists()
1519
+
1520
+ def importLists(self,filenames):
1521
+ def createItems():
1522
+ items=[None]*len(filenames)
1523
+ for k,filename in enumerate(filenames):
1524
+ FlagWarn=False
1525
+
1526
+ image=os.path.basename(filename)
1527
+ ex=os.path.exists(self.path+image)
1528
+ self.nimg+=1
1529
+ item_data=['',image]
1530
+ if not ex:
1531
+ FlagWarn=True
1532
+ item=QTreeWidgetItem(None,item_data)
1533
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1534
+
1535
+ if FlagWarn:
1536
+ item.setIcon(0,self.icon_warning)
1537
+ item.setToolTip(0,'File missing')
1538
+ item.setStatusTip(0,'File missing')
1539
+ data=[FlagWarn,image,ex]
1540
+ item.setData(0,Qt.ItemDataRole.UserRole,data)
1541
+
1542
+ self.imList.append(image)
1543
+ self.imEx.append(ex)
1544
+ items[k]=item
1545
+ return items
1546
+
1547
+ items=createItems()
1548
+ self.importItems(items)
1549
+
1550
+ class ImageTreeWidget(QWidget):
1551
+ class ImageTreeWidget_signals(QObject):
1552
+ selection=Signal()
1553
+ pass
1554
+
1555
+ buttons={
1556
+ 'discard_changes': ['Discard changes','Escape','redx'],
1557
+ 'confirm_changes': ['Confirm changes','Ctrl+Return','greenv'],
1558
+ 'up' : ['Move to the top of the list','Ctrl+Up'],
1559
+ 'down' : ['Move to the bottom of the list','Ctrl+Down'],
1560
+ 'scan_list': ['Re-scan current list to check for missing files','F5'],
1561
+ '|scan':[],
1562
+ 'warning': ['Check missing files','Ctrl+W'],
1563
+ 'cut_warnings': ['Cut all items with missing files','Alt+X'],
1564
+ '|warning': [],
1565
+ 'edit_list': ['Edit the list','F2'],
1566
+ '-1': [],
1567
+ 'read_list': ['Read image list file from the current folder','Ctrl+T'],
1568
+ 'write_list': ['Write current image list to folder','Ctrl+S'],
1569
+ '|read_list': [],
1570
+ 'read': ['Read image files from the current folder','Ctrl+R'],
1571
+ '|read': [],
1572
+ 'sort': ['Sort items in alphabetical order','Ctrl+Q'],
1573
+ 'sort_reversed': ['Sort items in alphabetical reversed order','Ctrl+Alt+Q'],
1574
+ '|sort': [],
1575
+ 'wrap_items': ['Collapse selected items','Shift+Space'],
1576
+ 'unwrap_items': ['Expand selected items','Space'],
1577
+ '|wrap': [],
1578
+ 'copy': ['Copy selected items from the list','Ctrl+C'],
1579
+ 'cut': ['Cut selected items from the list','Ctrl+X'],
1580
+ 'paste_below': ['Paste below the current item','Ctrl+V'],
1581
+ 'paste_above': ['Paste above the current item','Ctrl+Shift+V'],
1582
+ '|copy': [],
1583
+ 'clean': ['Clean the whole list']
1584
+ }
1585
+ icons_names=list(buttons)
1586
+ excludedFromContextMenu=('scan_list','warning','cut_warnings','edit_list','confirm_changes','discard_changes')
1587
+
1588
+ main_layout_spacing=3
1589
+
1590
+ spin_min_width=40
1591
+ spin_height=24
1592
+ spin_spacer_width=15
1593
+ spin_spacing=5
1594
+
1595
+ label_spacing=5
1596
+ button_spacing=5
1597
+ button_size=20
1598
+
1599
+ def __init__(self,parent=None,FlagSpinButtons=True):
1600
+ super().__init__(parent)
1601
+ if __name__ == "__main__":
1602
+ iconW = QIcon()
1603
+ iconW.addFile(u""+ icons_path +"input_logo.png", QSize(), QIcon.Normal, QIcon.Off)
1604
+ self.setWindowTitle('Image tree widget')
1605
+ self.setWindowIcon(iconW)
1606
+
1607
+ self.name='Image set'
1608
+ self.signals=self.ImageTreeWidget_signals()
1609
+ self.FlagSpinButtons=FlagSpinButtons
1610
+ self.FlagCam=True
1611
+ self.FlagInGui=False
1612
+
1613
+ font=self.font()
1614
+ font.setItalic(True)
1615
+ self.pixmap_edit=QPixmap(icons_path+'editing.png')
1616
+
1617
+ self.main_layout=QVBoxLayout(self)
1618
+ self.main_layout.setContentsMargins(0,self.main_layout_spacing,0,self.main_layout_spacing)
1619
+ self.main_layout.setSpacing(self.main_layout_spacing)
1620
+
1621
+ self.setLayout(self.main_layout)
1622
+
1623
+ self.w_spin=QWidget()
1624
+ self.spin_layout = QHBoxLayout()
1625
+ self.spin_layout.setContentsMargins(0,0,0,0)
1626
+ self.spin_layout.setSpacing(self.spin_spacing)
1627
+ self.w_spin.setLayout(self.spin_layout)
1628
+
1629
+ self.spin_layout.addItem(QSpacerItem(0, self.spin_height, QSizePolicy.Expanding, QSizePolicy.Minimum))
1630
+
1631
+ self.label_img = QLabel('#:',font=font)
1632
+ self.spin_layout.addWidget(self.label_img)
1633
+ self.spin_img = MyQSpin(self)
1634
+ self.spin_img.setObjectName('spin_img')
1635
+ self.spin_img.setMinimumSize(self.spin_min_width,self.spin_height)
1636
+ self.spin_img.setMaximumHeight(self.spin_height)
1637
+ self.spin_img.setMinimum(0)
1638
+ self.spin_layout.addWidget(self.spin_img)
1639
+ self.label_max_img = QLabel('/0',font=font)
1640
+ self.label_max_img.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
1641
+ self.spin_layout.addWidget(self.label_max_img)
1642
+ self.spin_layout.addItem(QSpacerItem(self.spin_spacer_width, self.spin_height, QSizePolicy.Minimum, QSizePolicy.Minimum))
1643
+
1644
+ self.label_frame = QLabel('frame:',font=font)
1645
+ self.spin_layout.addWidget(self.label_frame)
1646
+ self.spin_frame = MyQSpin(self)
1647
+ self.spin_frame.setObjectName('spin_frame')
1648
+ self.spin_frame.setMinimumSize(self.spin_min_width,self.spin_height)
1649
+ self.spin_frame.setMaximumHeight(self.spin_height)
1650
+ self.spin_frame.setMinimum(1)
1651
+ self.spin_frame.setMaximum(2)
1652
+ self.spin_layout.addWidget(self.spin_frame)
1653
+ self.spin_layout.addItem(QSpacerItem(self.spin_spacer_width, self.spin_height, QSizePolicy.Minimum, QSizePolicy.Minimum))
1654
+
1655
+ self.label_cam = QLabel('cam:',font=font)
1656
+ self.spin_layout.addWidget(self.label_cam)
1657
+ self.spin_cam = MyQSpin(self)
1658
+ self.spin_cam.setObjectName('spin_cam')
1659
+ self.spin_cam.setMinimumSize(self.spin_min_width,self.spin_height)
1660
+ self.spin_cam.setMaximumHeight(self.spin_height)
1661
+ self.spin_cam.setMinimum(1)
1662
+ self.spin_layout.addWidget(self.spin_cam)
1663
+ self.label_max_cam = QLabel('/',font=font)
1664
+ self.label_max_cam.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
1665
+ self.spin_layout.addWidget(self.label_max_cam)
1666
+ self.spin_ncam = MyQSpin(self)
1667
+ self.spin_ncam.setObjectName('spin_ncam')
1668
+ self.spin_ncam.setMinimumSize(self.spin_height,self.spin_height)
1669
+ self.spin_ncam.setMaximumHeight(self.spin_height)
1670
+ self.spin_ncam.setMinimum(1)
1671
+ self.spin_ncam.setMaximum(99)
1672
+ self.spin_ncam.setButtonSymbols(QSpinBox.ButtonSymbols.NoButtons)
1673
+ self.spin_ncam.setToolTip('Number of cameras')
1674
+ self.spin_ncam.setStatusTip('Number of cameras')
1675
+ self.spin_layout.addWidget(self.spin_ncam)
1676
+
1677
+ self.spinSetup()
1678
+
1679
+ self.w_button=QWidget(self)
1680
+ self.button_layout = QHBoxLayout()
1681
+ self.button_layout.setContentsMargins(0,0,0,0)
1682
+ self.button_layout.setSpacing(self.button_spacing)
1683
+ self.w_button.setLayout(self.button_layout)
1684
+
1685
+ self.label_updating_import=QLabel('')
1686
+ self.label_updating_import.setFixedSize(self.button_size,self.button_size)
1687
+ self.updating_import_gif = QMovie(u""+ icons_path +"updating_import.gif")
1688
+ self.updating_import_gif.setScaledSize(self.label_updating_import.size())
1689
+ #self.ui.label_updating_import.setScaledContents(True)
1690
+ self.updating_import_gif.start()
1691
+ self.label_updating_import.setMovie(self.updating_import_gif)
1692
+ self.label_updating_import.setVisible(False)
1693
+ self.button_layout.addWidget(self.label_updating_import)
1694
+
1695
+ self.icon_label=QLabel('')
1696
+ self.icon_label.setFixedSize(self.button_size,self.button_size)
1697
+ self.icon_label.setScaledContents(True)
1698
+ self.icon_label.setPixmap(self.pixmap_edit)
1699
+
1700
+ self.label = QLabel(self.name,font=font)
1701
+ self.label.setMinimumHeight(self.button_size)
1702
+ self.button_layout.addWidget(self.icon_label,alignment=Qt.AlignmentFlag.AlignLeft)
1703
+ self.button_layout.addWidget(self.label, alignment=Qt.AlignmentFlag.AlignLeft)
1704
+
1705
+ self.button_layout.addItem(QSpacerItem(self.label_spacing, self.button_size, QSizePolicy.Minimum, QSizePolicy.Minimum))
1706
+
1707
+ bs=self.button_size
1708
+ self.bShortCuts={}
1709
+ for icon_name in self.icons_names:
1710
+ if '-' in icon_name:
1711
+ self.button_layout.addItem(QSpacerItem(bs, bs, QSizePolicy.Expanding, QSizePolicy.Minimum))
1712
+ elif '|' in icon_name:
1713
+ separator = QFrame()
1714
+ separator.setFrameShape(QFrame.VLine)
1715
+ separator.setFrameShadow(QFrame.Sunken)
1716
+ setattr(self,'sep_'+icon_name[1:],separator)
1717
+ self.button_layout.addWidget(separator)
1718
+ else:
1719
+ b = QToolButton(self)
1720
+ b.setObjectName('button_'+icon_name)
1721
+ if len(self.buttons[icon_name])>2:
1722
+ icon_file=icons_path+self.buttons[icon_name][2]+'.png'
1723
+ else:
1724
+ icon_file=icons_path+icon_name+'.png'
1725
+ b.setIcon(QIcon(icon_file))
1726
+ b.setFixedSize(bs, bs) # Impostare la dimensione quadrata
1727
+ b.setIconSize(QSize(bs-4,bs-4))
1728
+ tip=self.buttons[icon_name][0]
1729
+ if len(self.buttons[icon_name])>1:
1730
+ if self.buttons[icon_name][1]:
1731
+ bshortcut=QShortcut(QCoreApplication.translate("Image Tree",self.buttons[icon_name][1], None),self)
1732
+ def buttonClick(b:QToolButton):
1733
+ if b.isVisible() and b.isEnabled() and self.imTree.hasFocus(): b.click()
1734
+ #bshortcut.activated.connect(lambda but=b:buttonClick(but))
1735
+ tip+=' ('+bshortcut.key().toString(QKeySequence.NativeText)+')'
1736
+ self.bShortCuts[bshortcut.key()[0]]=lambda but=b:buttonClick(but)
1737
+ b.setToolTip(tip)
1738
+ b.setStatusTip(tip)
1739
+ setattr(self,'button_'+icon_name,b)
1740
+ if hasattr(self,'button_'+icon_name+'_action'):
1741
+ b.clicked.connect(getattr(self,'button_'+icon_name+'_action'))
1742
+ if icon_name=='discard_changes':
1743
+ self.spin_layout.insertWidget(0,b)
1744
+ elif icon_name=='confirm_changes':
1745
+ self.spin_layout.insertWidget(1,b)
1746
+ elif icon_name=='up':
1747
+ self.spin_layout.insertWidget(3,b)
1748
+ elif icon_name=='down':
1749
+ self.spin_layout.insertWidget(4,b)
1750
+ else:
1751
+ self.button_layout.addWidget(b)
1752
+
1753
+ self.infoLabel = QLabel('',self)
1754
+ self.infoLabel.setFixedWidth(self.spin_height-8)
1755
+ self.infoLabel.setFixedHeight(self.spin_height-8)
1756
+ self.infoLabel.setScaledContents(True)
1757
+ self.progressBar = QProgressBar(self)
1758
+ self.progressBar.setFixedWidth(self.spin_height*3)
1759
+ self.progressBar.setFixedHeight(self.spin_height-8)
1760
+ self.spin_layout.insertWidget(2,self.progressBar)
1761
+ self.spin_layout.insertWidget(2,self.infoLabel)
1762
+
1763
+ self.tree_layout=QHBoxLayout()
1764
+ self.tree_layout.setContentsMargins(0,0,0,0)
1765
+ self.tree_layout.setSpacing(0)
1766
+
1767
+ self.imageNumbTree=ImageNumberTree()
1768
+ self.imageNumbTree.setVisible(True)
1769
+ self.imageNumbTree.setFrameShape(QFrame.NoFrame)
1770
+
1771
+ self.imTrees=[GlobalImageTree()]
1772
+ self.setAsynImTree(self.imTrees[0])
1773
+ self.imTrees[0].setObjectName('GlobalImageTree')
1774
+ self.imTrees[0].setFrameShape(QFrame.NoFrame)
1775
+ self.imTree=self.imTrees[0]
1776
+ self.imList_old=[[[]]]
1777
+ self.imEx_old=[[[]]]
1778
+ self.tree_layout.addWidget(self.imageNumbTree)
1779
+ self.tree_layout.addWidget(self.imTree)
1780
+ #self.enableParent=lambda:None
1781
+ #self.disableParent=lambda:None
1782
+
1783
+ self.button_edit_list:QPushButton
1784
+ self.button_edit_list.setCheckable(True)
1785
+ self.button_edit_list.setChecked(False)
1786
+ self.indTree=0
1787
+ self.treeSetup()
1788
+
1789
+ self.main_layout.addWidget(self.w_spin)
1790
+ self.main_layout.addWidget(self.w_button)
1791
+ self.main_layout.addLayout(self.tree_layout)
1792
+ self.main_layout.addItem(QSpacerItem(0,0,QSizePolicy.Policy.Minimum,QSizePolicy.Policy.Expanding))
1793
+
1794
+ if __name__ == "__main__":
1795
+ self.button_setList=QPushButton('Reset list (debug)')
1796
+ def resetList():
1797
+ imp,iml,ime=imSet.genListsFromIndex(kc,i,npairs,step,ncam)
1798
+ self.setLists(imp,iml,ime)
1799
+ self.button_setList.clicked.connect(resetList)
1800
+ self.button_setList.setMaximumHeight(30)
1801
+ self.setList_layout = QHBoxLayout()
1802
+ self.setList_layout.addWidget(self.button_setList)
1803
+ self.setList_layout.addItem(QSpacerItem(0, self.spin_height, QSizePolicy.Expanding, QSizePolicy.Minimum))
1804
+ self.main_layout.insertLayout(0,self.setList_layout)
1805
+ stretches=[0,0,0,1,0]
1806
+ else:
1807
+ stretches=[0,0,1,0]
1808
+ for k,s in enumerate(stretches):
1809
+ self.main_layout.setStretch(k,s)
1810
+
1811
+ #self.nullList()
1812
+ self.w_spin.setVisible(self.FlagSpinButtons)
1813
+ self.w_button.setVisible(self.FlagSpinButtons)
1814
+
1815
+ self.FlagAlreadyDisabled=False
1816
+ self.FlagEnabled=[]
1817
+ self.disabledChildren=[]
1818
+
1819
+ self.setWidgetTabOrder()
1820
+ self.imTree.setVisible(True)
1821
+ self.imTree.keyPressEvent=lambda e: self.treeKeyPressEvent(e)
1822
+ self.imTree.installEventFilter(self)
1823
+
1824
+ self.FlagSettingPar=None
1825
+
1826
+ def eventFilter(self, obj, event):
1827
+ if event.type() == QEvent.Type.ShortcutOverride:
1828
+ event.accept()
1829
+ return True
1830
+ return super().eventFilter(obj, event)
1831
+
1832
+ def treeKeyPressEvent(self,event):
1833
+ #pri.Callback.green(f"Pressed key sequence: {QKeySequence(Qt.KeyboardModifier(event.modifiers()) | Qt.Key(event.key())).toString()}")
1834
+ for ksc,f in self.bShortCuts.items():
1835
+ if ksc is None: continue
1836
+ if type(ksc)!=list: ksc=[ksc]
1837
+ for k in ksc:
1838
+ if type(k)==QKeyCombination:
1839
+ if event.key()==k.key() and event.modifiers()==k.keyboardModifiers():
1840
+ f()
1841
+ return True # Impedisce al widget di ricevere l'evento
1842
+ else:
1843
+ if event.key()==k and not event.modifiers():
1844
+ f()
1845
+ return True # Impedisce al widget di ricevere l'evento
1846
+
1847
+ def setAsynImTree(self,imTree: AsynPaIRSTree):
1848
+ imTree.setNumTree(self.imageNumbTree)
1849
+ imTree.disableButtons=self.disableButtons
1850
+ imTree.progressBar=self.progressBar
1851
+ imTree.infoLabel=self.infoLabel
1852
+ imTree.signals.copyCutItems.connect(self.copy_cut_action_future)
1853
+ imTree.signals.pastedItems.connect(self.button_paste_above_below_action_future)
1854
+
1855
+ def nullList(self):
1856
+ nimg=0
1857
+ imList0=[[['' for _ in range(nimg)] for _ in range(2)] for _ in range(self.imTree.ncam)]
1858
+ imEx0=[[[False for _ in range(nimg)] for _ in range(2)] for _ in range(self.imTree.ncam)]
1859
+ self.setLists(self.imTree.path,imList0,imEx0,FlagAsync=False)
1860
+ self.imTree.signals.updateLists.emit()
1861
+
1862
+ def disableButtons(self,Flag=True):
1863
+ self.progressBar.setVisible(Flag)
1864
+ #self.progressBar.setEnabled(Flag) #commenta se self.disableTab(Flag) è commentato
1865
+ self.infoLabel.setPixmap(QPixmap())
1866
+ self.infoLabel.setVisible(Flag)
1867
+ #self.infoLabel.setEnabled(Flag) #commenta se self.disableTab(Flag) è commentato
1868
+ #self.disableTab(Flag)
1869
+ #return
1870
+ self.w_button.setEnabled(not Flag)
1871
+ for c in self.w_spin.findChildren(QWidget):
1872
+ c:QWidget
1873
+ c.setEnabled(not Flag)
1874
+ self.updateSpins()
1875
+ self.setButtonLayout(True)
1876
+ self.progressBar.setEnabled(Flag) #commenta se self.disableTab(Flag) è decommentato
1877
+ self.infoLabel.setEnabled(Flag) #commenta se self.disableTab(Flag) è decommentato
1878
+
1879
+ def disableTab(self,Flag=True):
1880
+ #evita TABpar.FlagSettingPar=Flag così che sai dove FlagSettingPar è settato True o False
1881
+ if Flag:
1882
+ self.FlagSettingPar=TABpar.FlagSettingPar
1883
+ TABpar.FlagSettingPar=True
1884
+ else:
1885
+ if self.FlagSettingPar is not None:
1886
+ TABpar.FlagSettingPar=self.FlagSettingPar
1887
+ for imTree in self.imTrees:
1888
+ imTree.FlagSettingPar=Flag
1889
+ self.label_updating_import.setVisible(Flag)
1890
+ return
1891
+
1892
+ def treeSetup(self):
1893
+ self.imTree.itemSelectionChanged.connect(self.setButtonLayout)
1894
+ #self.imTree.signals.updateLists.connect(self.setButtonLayout)
1895
+ self.imTree.contextMenuEvent=lambda e: self.treeContextMenuEvent(self.imTree,e)
1896
+ self.imTree.keyPressEvent=lambda e: self.treeKeyPressEvent(e)
1897
+ self.imTree.installEventFilter(self)
1898
+ self.imTree.signals.updateTree.connect(self.updateSpins)
1899
+ self.imTree.signals.updateTree.connect(self.resizeHeader)
1900
+ self.imTree.signals.updateTree.connect(self.setButtonLayout)
1901
+ return_action=lambda: self.imTree.setFocus()
1902
+ self.spin_img.addfuncreturn['move2Tree']=return_action
1903
+ self.spin_cam.addfuncreturn['move2Tree']=return_action
1904
+ self.spin_frame.addfuncreturn['move2Tree']=return_action
1905
+
1906
+ def spinSetup(self):
1907
+ self.spin_img.setup()
1908
+ self.spin_cam.setup()
1909
+ self.spin_ncam.setup()
1910
+ self.spin_frame.setup()
1911
+
1912
+ self.spin_img.valueChanged.connect(lambda: self.spins_action(self.spin_img))
1913
+ self.spin_cam.valueChanged.connect(lambda: self.spins_action(self.spin_cam))
1914
+ self.spin_frame.valueChanged.connect(lambda: self.spins_action(self.spin_frame))
1915
+ self.spin_ncam.addfuncreturn['ncam']=self.spin_ncam_action
1916
+ self.spin_ncam.addfuncout['ncam']=self.spin_ncam_action
1917
+
1918
+ def setButtonLayout(self,FlagPostponedSignal=False):
1919
+ self.setButtonLayout_List()
1920
+ self.setButtonLayout_Item(FlagPostponedSignal)
1921
+
1922
+ def setButtonLayout_List(self):
1923
+ if not self.FlagSpinButtons: return
1924
+ #buttons changes when list changes occur
1925
+ FlagWarn=len(self.imTree.warns)>0
1926
+ FlagSingleTree=type(self.imTree)==SingleImageTree
1927
+ FlagItems=self.imTree.topLevelItemCount()>0
1928
+ FlagWrapUnWrap=not FlagSingleTree and self.imTree.ncam>1
1929
+
1930
+ self.button_up.setVisible(FlagItems)
1931
+ self.button_down.setVisible(FlagItems)
1932
+
1933
+ self.button_scan_list.setVisible(not FlagSingleTree)
1934
+ self.sep_scan.setVisible(not FlagSingleTree)
1935
+ self.button_warning.setVisible(FlagWarn)
1936
+ self.button_cut_warnings.setVisible(FlagWarn)
1937
+ self.sep_warning.setVisible(FlagWarn and not FlagSingleTree)
1938
+
1939
+ self.button_confirm_changes.setVisible(FlagSingleTree)
1940
+ self.button_discard_changes.setVisible(FlagSingleTree)
1941
+
1942
+ self.button_edit_list.setVisible(not FlagSingleTree)
1943
+ self.button_write_list.setVisible(not FlagSingleTree)
1944
+ self.button_write_list.setEnabled(FlagItems)
1945
+ self.button_read_list.setVisible(not FlagSingleTree)
1946
+ self.sep_read_list.setVisible(not FlagSingleTree)
1947
+
1948
+ self.button_read.setVisible(FlagSingleTree)
1949
+ self.sep_read.setVisible(FlagSingleTree)
1950
+
1951
+ self.button_wrap_items.setVisible(FlagWrapUnWrap)
1952
+ self.button_unwrap_items.setVisible(FlagWrapUnWrap)
1953
+ self.sep_wrap.setVisible(FlagWrapUnWrap)
1954
+ self.button_sort.setEnabled(FlagItems)
1955
+ self.button_sort_reversed.setEnabled(FlagItems)
1956
+
1957
+ self.button_sort.setVisible(FlagSingleTree)
1958
+ self.button_sort_reversed.setVisible(FlagSingleTree)
1959
+ self.sep_sort.setVisible(FlagSingleTree)
1960
+
1961
+ self.sep_copy.setVisible(not FlagSingleTree)
1962
+ self.button_clean.setVisible(not FlagSingleTree)
1963
+ self.button_clean.setEnabled(FlagItems)
1964
+
1965
+ def setButtonLayout_Item(self,FlagPostponedSignal=False):
1966
+ if not self.FlagSpinButtons: return
1967
+ #buttons changes when item selection changes occur
1968
+ FlagSelected=len(self.imTree.selectedItems())>0
1969
+ FlagCuttedItems=len(self.imTree.cutted_itemList)>0
1970
+ self.button_wrap_items.setEnabled(FlagSelected)
1971
+ self.button_unwrap_items.setEnabled(FlagSelected)
1972
+
1973
+ self.button_copy.setEnabled(FlagSelected)
1974
+ self.button_cut.setEnabled(FlagSelected)
1975
+ self.button_paste_above.setEnabled(FlagCuttedItems)
1976
+ self.button_paste_below.setEnabled(FlagCuttedItems)
1977
+
1978
+ self.imTree.FlagSetting=True
1979
+ img,cam,frame=self.imTree.indexSelection()
1980
+
1981
+ if FlagPostponedSignal:
1982
+ FlagSettingPar=TABpar.FlagSettingPar
1983
+ TABpar.FlagSettingPar=True
1984
+ FlagSignal=self.spin_img.value()!=img or self.spin_cam.value()!=cam or self.spin_frame.value()!=frame
1985
+ self.spin_img.setValue(img)
1986
+ self.spin_cam.setValue(cam)
1987
+ self.spin_frame.setValue(frame)
1988
+ self.spin_ncam.setValue(self.imTree.ncam)
1989
+ if FlagPostponedSignal: TABpar.FlagSettingPar=FlagSettingPar
1990
+
1991
+ self.imTree.FlagSetting=False
1992
+ if FlagSignal and not TABpar.FlagSettingPar: self.signals.selection.emit()
1993
+
1994
+ def setLists(self,path,imList:list,imEx:list,selection=[],FlagAsync=True,FlagOnlyPrepare=False):
1995
+ self.imTree.clearSelection()
1996
+ #self.imTree.setVisible(False)
1997
+ if self.button_edit_list.isChecked():
1998
+ indTree=self.indTree
1999
+ self.button_edit_list.setChecked(False)
2000
+ self.button_edit_list_action(FlagScan=False)
2001
+ else: indTree=0
2002
+ self.imTrees[0].path=path
2003
+ self.imTrees[0].ncam=ncam=len(imList)
2004
+ nimg=len(imList[0][0])
2005
+ self.imTrees[0].imList=copy.deepcopy(imList)
2006
+ self.imTrees[0].imEx=copy.deepcopy(imEx)
2007
+ self.imTrees[0].setImListEx()
2008
+ self.imTrees[0].disableTab=lambda flag: self.imTrees[0].setEnabled(not flag)
2009
+ countTree=1
2010
+ for c in range(ncam):
2011
+ for f in range(2):
2012
+ countTree+=1
2013
+ if len(self.imTrees)<countTree:
2014
+ imTree=SingleImageTree(self)
2015
+ imTree.setFrameShape(QFrame.NoFrame)
2016
+ self.imTrees.append(imTree)
2017
+ self.tree_layout.addWidget(imTree)
2018
+ else:
2019
+ imTree=self.imTrees[countTree-1]
2020
+ imTree.c=c
2021
+ imTree.f=f
2022
+ imTree.path=path
2023
+ imTree.ncam=ncam
2024
+ imTree.nimg=nimg
2025
+ imTree.parentImTree=self.imTrees[0]
2026
+ imTree.disableTab=lambda flag: imTree.setEnabled(not flag)
2027
+ [self.tree_layout.removeWidget(i) for i in self.imTrees[countTree:]]
2028
+
2029
+ if not FlagOnlyPrepare:
2030
+ self.imTrees[0].setLists(selection,FlagAsync)
2031
+ if indTree:
2032
+ self.button_edit_list.setChecked(True)
2033
+ self.button_edit_list_action(FlagScan=False)
2034
+
2035
+ def updateImList(self):
2036
+ #for imTree in self.imTrees:
2037
+ for k,imTree in enumerate(self.imTrees[1:]):
2038
+ imTree:SingleImageTree
2039
+ c=int(k/2)
2040
+ f=k-c*2
2041
+ if c>=self.imTrees[0].ncam: break
2042
+ imTree.imList=self.imTrees[0].imList[c][f]
2043
+ imTree.imEx=self.imTrees[0].imEx[c][f]
2044
+ imTree.itemList[0]=imTree.imList
2045
+ imTree.itemList[1]=imTree.imEx
2046
+ imTree.ncam=self.imTrees[0].ncam
2047
+ if self.initTree[k+1]: imTree.nimg=self.imTrees[0].nimg
2048
+ if self.initTree[self.indTree] and self.indTree:
2049
+ self.initTree[self.indTree]=False
2050
+ imTree=self.imTrees[self.indTree]
2051
+ imTree.setLists([self.spin_img.value(),1,1])
2052
+
2053
+ def button_up_action(self):
2054
+ self.imTree.clearSelection()
2055
+ item=self.imTree.topLevelItem(0)
2056
+ self.imTree.setCurrentItem(item)
2057
+ self.imTree.setSelectedQuickly([item],True)
2058
+ self.imTree.scrollToTop()
2059
+
2060
+ def button_down_action(self):
2061
+ self.imTree.clearSelection()
2062
+ item=self.imTree.topLevelItem(self.imTree.topLevelItemCount()-1)
2063
+ self.imTree.setCurrentItem(item)
2064
+ self.imTree.setSelectedQuickly([item],True)
2065
+ self.imTree.scrollToBottom()
2066
+
2067
+ def button_scan_list_action(self):
2068
+ #self.imTree.setVisible(False)
2069
+ self.imTree.scanLists()
2070
+ self.imTree.resetImNumber()
2071
+ #self.imTree.setVisible(True)
2072
+ self.imTree.signals.updateLists.emit()
2073
+
2074
+ def button_cut_warnings_action(self):
2075
+ items=[self.imTree.topLevelItem(k) for k in self.imTree.warns]
2076
+ indexes=[self.imTree.indexOfTopLevelItem(i) for i in items]
2077
+ self.copy_cut_action(items,indexes,True)
2078
+
2079
+ def button_warning_action(self):
2080
+ i=self.spin_img.value()-1
2081
+ if i in self.imTree.warns:
2082
+ k=self.imTree.warns.index(i)
2083
+ k=k+1 if k<len(self.imTree.warns)-1 else 0
2084
+ else: k=0
2085
+ item=self.imTree.topLevelItem(self.imTree.warns[k])
2086
+ self.imTree.setCurrentItem(item)
2087
+ self.imTree.setSelectedQuickly([item],True)
2088
+
2089
+ def button_edit_list_action(self,FlagScan=True,FlagSignal=False):
2090
+ self.setUpdatesEnabled(False)
2091
+ indTree_old=self.indTree
2092
+ self.initTree=[True for _ in self.imTrees]
2093
+ if not self.button_edit_list.isChecked():
2094
+ self.indTree=0
2095
+ self.disableTab(False)
2096
+ else:
2097
+ if FlagScan: self.imTrees[0].scanLists(FlagChange=False)
2098
+ self.disableTab(True)
2099
+ self.imList_old=copy.deepcopy(self.imTree.imList)
2100
+ self.imEx_old=copy.deepcopy(self.imTree.imEx)
2101
+ c=self.spin_cam.value()-1
2102
+ f=self.spin_frame.value()-1
2103
+ self.indTree=1+c*2+f
2104
+
2105
+ self.imTree=self.imTrees[self.indTree]
2106
+ self.setAsynImTree(self.imTree)
2107
+ self.updateImList()
2108
+ self.treeSetup()
2109
+ self.setButtonLayout(FlagPostponedSignal=FlagSignal)
2110
+
2111
+ self.spins_action()
2112
+ if FlagSignal and not self.signalsBlocked():
2113
+ self.imTree.signals.updateLists.emit()
2114
+ if indTree_old!=self.indTree:
2115
+ self.imTrees[indTree_old].setVisible(False)
2116
+ self.imTree.setVisible(True)
2117
+ self.setUpdatesEnabled(True)
2118
+
2119
+ def resizeHeader(self):
2120
+ self.imTree.resizeHeader()
2121
+ return
2122
+
2123
+ def button_confirm_changes_action(self):
2124
+ nimg=max([imTree.nimg for imTree in self.imTrees[1:]])
2125
+
2126
+ for k,imTree in enumerate(self.imTrees[1:]):
2127
+ imTree:SingleImageTree
2128
+ if imTree.nimg<nimg:
2129
+ imTree.imList.extend(['']*(nimg-imTree.nimg))
2130
+ imTree.imEx.extend([False]*(nimg-imTree.nimg))
2131
+ imTree.nimg=nimg
2132
+ self.imTrees[0].nimg=nimg
2133
+
2134
+ self.imTrees[0].scanLists(FlagChange=False)
2135
+ self.imTrees[0].setLists()
2136
+ self.button_edit_list.setChecked(False)
2137
+ self.button_edit_list_action(FlagScan=False,FlagSignal=True)
2138
+ return
2139
+
2140
+ def button_discard_changes_action(self):
2141
+ self.imTrees[0].imList=self.imList_old
2142
+ self.imTrees[0].imEx=self.imEx_old
2143
+ self.imTrees[0].setImListEx()
2144
+ self.imTrees[0].resetImNumber()
2145
+ self.imTrees[0].scanLists(FlagChange=True)
2146
+ self.button_edit_list.setChecked(False)
2147
+ self.button_edit_list_action(FlagScan=False,FlagSignal=True)
2148
+ return
2149
+
2150
+ def spins_action(self,spin:QSpinBox=None):
2151
+ if self.imTree.FlagSetting: return
2152
+ self.updateSpins()
2153
+ r=self.spin_img.value()-1
2154
+ c=self.spin_cam.value()-1
2155
+ f=self.spin_frame.value()-1
2156
+ if not self.button_edit_list.isChecked(): #global tree
2157
+ self.label.setText(self.name)
2158
+ self.label.setStyleSheet('')
2159
+ self.icon_label.setVisible(False)
2160
+ self.imTree.spinSelection([r+1,c+1,f+1])
2161
+ else: #single tree
2162
+ self.label.setText(self.name + f' (cam: {c+1}, frame: {f+1})')
2163
+ self.label.setStyleSheet('QLabel{color:rgba(0, 116, 255, 1)}')
2164
+ self.icon_label.setVisible(True)
2165
+ indTree=1+c*2+f
2166
+ if indTree!=self.indTree:
2167
+ self.imTree.setVisible(False)
2168
+ self.indTree=indTree
2169
+ self.imTree=self.imTrees[self.indTree]
2170
+ self.setAsynImTree(self.imTree)
2171
+ self.updateImList()
2172
+ self.imTree.setVisible(True)
2173
+ self.treeSetup()
2174
+ self.updateSpins()
2175
+ r=self.spin_img.value()-1
2176
+ item = self.imTree.topLevelItem(r)
2177
+ self.imTree.indexFromItem(item)
2178
+ #self.imTree.clearSelection()
2179
+ self.imTree.setCurrentItem(item, 1)
2180
+ #if spin: spin.setFocus()
2181
+
2182
+ def spin_ncam_action(self):
2183
+ ncam=self.spin_ncam.value()
2184
+ if self.imTree.ncam>ncam:
2185
+ del self.imTree.imList[ncam:]
2186
+ del self.imTree.imEx[ncam:]
2187
+ elif self.imTree.ncam<ncam:
2188
+ for c in range(self.imTree.ncam,ncam):
2189
+ self.imTree.imList[c:c]=[[['' for _ in range(self.imTree.nimg)] for _ in range(2)]]
2190
+ self.imTree.imEx[c:c]=[[[False for _ in range(self.imTree.nimg)] for _ in range(2)]]
2191
+ else: return
2192
+ if self.imTree.ncam!=ncam:
2193
+ self.imTree.ncam=ncam
2194
+ self.imTree.setImListEx()
2195
+ self.imTree.setLists()
2196
+
2197
+ def updateSpins(self):
2198
+ FlagSettingPar=TABpar.FlagSettingPar
2199
+ TABpar.FlagSettingPar=True
2200
+ self.spin_img.setMinimum(min([self.imTree.nimg,0]))
2201
+ self.spin_img.setMaximum(max([self.imTree.nimg,0]))
2202
+ self.spin_img.setToolTip(f'Current image. Number of images: {formatNumber(self.spin_img,self.spin_img.maximum())}')
2203
+ self.spin_img.setStatusTip(self.spin_img.toolTip())
2204
+ if self.spin_img.maximum(): self.label_max_img.setText('/'+self.spin_img.textFromValue(self.spin_img.maximum()))
2205
+ else: self.label_max_img.setText('')
2206
+ self.label_max_img.adjustSize()
2207
+ self.spin_img.setMinimumWidth(self.label_max_img.width()+self.spin_height)
2208
+ self.spin_img.setEnabled(self.imTree.nimg>1)
2209
+
2210
+ self.spin_cam.setMinimum(min([self.imTree.ncam,1]))
2211
+ self.spin_cam.setMaximum(self.imTree.ncam)
2212
+ self.spin_cam.setToolTip(f'Current camera. Number of cameras: {formatNumber(self.spin_cam,self.spin_cam.maximum())}')
2213
+ self.spin_cam.setStatusTip(self.spin_cam.toolTip())
2214
+ FlagSingleTree=type(self.imTree)==SingleImageTree
2215
+ self.spin_cam.setEnabled(self.imTree.ncam>1 and (self.imTree.nimg>1 or FlagSingleTree))
2216
+ self.spin_ncam.setEnabled(not FlagSingleTree and self.FlagCam and not self.FlagInGui)
2217
+
2218
+ self.spin_frame.setToolTip(f'Current frame. Number of frames: {self.spin_frame.maximum()}')
2219
+ self.spin_frame.setStatusTip(self.spin_frame.toolTip())
2220
+ self.spin_frame.setEnabled(self.imTree.nimg>1 or FlagSingleTree)
2221
+ TABpar.FlagSettingPar=FlagSettingPar
2222
+
2223
+ def buttonActionWrapper(self,fun=lambda:None):
2224
+ FlagSettingPar=TABpar.FlagSettingPar
2225
+ TABpar.FlagSettingPar=True
2226
+ fun()
2227
+ TABpar.FlagSettingPar=FlagSettingPar
2228
+ self.imTree.signals.updateLists.emit()
2229
+
2230
+ def button_wrap_unwrap_action(self,FlagWrap=True):
2231
+ #self.imTree.setVisible(False)
2232
+ selectedItems,indexes=self.imTree.selectedTopLevel()
2233
+ self.imTree.wrapUnWrapItems(selectedItems,indexes,FlagWrap)
2234
+
2235
+ def button_wrap_items_action(self):
2236
+ #self.buttonActionWrapper(lambda:
2237
+ self.button_wrap_unwrap_action(FlagWrap=True)
2238
+ #)
2239
+
2240
+ def button_unwrap_items_action(self):
2241
+ #self.buttonActionWrapper(lambda:
2242
+ self.button_wrap_unwrap_action(FlagWrap=False)
2243
+ #)
2244
+
2245
+ def button_copy_cut_action(self, FlagCut=False):
2246
+ #self.imTree.setVisible(False)
2247
+ selectedItems,indexes=self.imTree.selectedTopLevel()
2248
+ self.copy_cut_action(selectedItems,indexes,FlagCut)
2249
+
2250
+ def copy_cut_action(self,items,indexes,FlagCut):
2251
+ self.imTree.copyCutItems(items,indexes,FlagCut)
2252
+
2253
+ def copy_cut_action_future(self):
2254
+ FlagSignal=True
2255
+ self.updateSpins()
2256
+ self.setButtonLayout(FlagPostponedSignal=FlagSignal)
2257
+
2258
+ #self.imTree.setVisible(True)
2259
+ self.imTree.setFocus()
2260
+ if not self.signalsBlocked(): self.imTree.signals.updateLists.emit()
2261
+
2262
+ def button_copy_action(self):
2263
+ self.button_copy_cut_action(FlagCut=False)
2264
+
2265
+ def button_cut_action(self):
2266
+ #self.buttonActionWrapper(lambda:
2267
+ self.button_copy_cut_action(FlagCut=True)
2268
+ #)
2269
+
2270
+ def button_paste_above_below_action(self,FlagAbove=True):
2271
+ if not self.imTree.cutted_items: return
2272
+ #self.imTree.setVisible(False)
2273
+
2274
+ self.FlagResizeHeader=self.imTree.topLevelItemCount()==0
2275
+ self.FlagAbove=FlagAbove
2276
+ selectedItems,indexes=self.imTree.selectedTopLevel()
2277
+ #self.imTree.clearSelection()
2278
+ if FlagAbove:
2279
+ if selectedItems: row=indexes[0]
2280
+ else: row=0
2281
+ else:
2282
+ if selectedItems: row=indexes[-1]+1
2283
+ else: row=self.imTree.topLevelItemCount()
2284
+ self.imTree.pasteItems(self.imTree.cutted_items,row,FlagAbove)
2285
+
2286
+ def button_paste_above_below_action_future(self):
2287
+ self.updateSpins()
2288
+ self.setButtonLayout()
2289
+
2290
+ selectedItems,_=self.imTree.selectedTopLevel()
2291
+ #self.imTree.clearSelection()
2292
+ if self.FlagAbove:
2293
+ firstItemToScroll=selectedItems[0]
2294
+ lastItemToScroll=selectedItems[-1]
2295
+ else:
2296
+ firstItemToScroll=selectedItems[-1]
2297
+ lastItemToScroll=selectedItems[0]
2298
+
2299
+ self.imTree.scrollToItem(firstItemToScroll)
2300
+ self.imTree.scrollToItem(lastItemToScroll)
2301
+
2302
+ kin=self.imTree.indexOfTopLevelItem(firstItemToScroll)
2303
+ firstItemToScroll=self.imageNumbTree.topLevelItem(kin)
2304
+ kfin=self.imTree.indexOfTopLevelItem(lastItemToScroll)
2305
+ lastItemToScroll=self.imageNumbTree.topLevelItem(kfin)
2306
+ self.imageNumbTree.scrollToItem(firstItemToScroll)
2307
+ self.imageNumbTree.scrollToItem(lastItemToScroll)
2308
+
2309
+ #self.imageNumbTree.verticalScrollBar().setValue(self.imTree.verticalScrollBar().value())
2310
+ if self.FlagResizeHeader: self.resizeHeader()
2311
+ #self.imTree.setVisible(True)
2312
+ self.imTree.setFocus()
2313
+ self.imTree.signals.updateLists.emit()
2314
+
2315
+ def button_paste_above_action(self):
2316
+ #self.buttonActionWrapper(lambda:
2317
+ self.button_paste_above_below_action(FlagAbove=True)
2318
+ #)
2319
+
2320
+ def button_paste_below_action(self):
2321
+ #self.buttonActionWrapper(lambda:
2322
+ self.button_paste_above_below_action(FlagAbove=False)
2323
+ #)
2324
+
2325
+ def clean_action(self):
2326
+ #self.imTree.setVisible(False)
2327
+ self.setUpdatesEnabled(False)
2328
+ for imTree in self.imTrees:
2329
+ imTree.clear() #clean_tree(imTree)
2330
+ imTree.cleanLists()
2331
+ imTree.itemList.append(deep_duplicate(imTree.itemList[0]))
2332
+ self.nullList()
2333
+ self.setUpdatesEnabled(True)
2334
+ #self.imTree.setVisible(True)
2335
+
2336
+ def button_clean_action(self):
2337
+ #self.buttonActionWrapper(lambda:
2338
+ self.clean_action()
2339
+ #)
2340
+
2341
+ def button_read_action(self):
2342
+ filenames, _ = QFileDialog.getOpenFileNames(self,\
2343
+ "Select image files from the current directory", filter=text_filter, dir=self.imTree.path,\
2344
+ options=optionNativeDialog)
2345
+ self.imTree:SingleImageTree
2346
+ if filenames:
2347
+ self.FlagResizeHeader=self.imTree.topLevelItemCount()==0
2348
+ self.FlagAbove=False
2349
+ self.imTree.importLists(filenames)
2350
+
2351
+ def button_sort_action(self):
2352
+ self.imTree:SingleImageTree
2353
+ self.imTree.sortLists(reverse=False)
2354
+
2355
+ def button_sort_reversed_action(self):
2356
+ self.imTree:SingleImageTree
2357
+ self.imTree.sortLists(reverse=True)
2358
+
2359
+ def button_read_list_action(self):
2360
+ filename, _ = QFileDialog.getOpenFileName(self,\
2361
+ "Select image list file", dir=self.imTree.path,\
2362
+ options=optionNativeDialog)
2363
+ if filename:
2364
+ imList,imEx=self.read_imFile(filename)
2365
+ if imList:
2366
+ #self.buttonActionWrapper(lambda:
2367
+ TABpar.FlagSettingPar=True
2368
+ self.setLists(self.imTree.path,imList,imEx)
2369
+ TABpar.FlagSettingPar=False
2370
+ #)
2371
+
2372
+ def read_imFile(self,filename):
2373
+ path=self.imTree.path
2374
+ try:
2375
+ with open( filename, 'r') as file:
2376
+ lines = file.readlines()
2377
+ for i, line in enumerate(lines):
2378
+ pairs = line.strip().split(';')
2379
+ if len(pairs) < self.imTree.ncam:
2380
+ raise ValueError(f"Invalid format in line {i + 1}:\n\n{line.strip()}\n\nEach line should contain at least {self.imTree.ncam} pairs of image filenames separated by a semicolon.")
2381
+ if not i:
2382
+ imList=[[['' for _ in range(len(lines))] for _ in range(2)] for _ in range(self.imTree.ncam)]
2383
+ imEx=[[[False for _ in range(len(lines))] for _ in range(2)] for _ in range(self.imTree.ncam)]
2384
+ for c in range(self.imTree.ncam):
2385
+ pair=pairs[c]
2386
+ items = pair.strip().split(',')
2387
+ if len(items) != 2:
2388
+ raise ValueError(f"Invalid format in line {i + 1}:\n\n{line.strip()}\n\nEach pair should contain exactly two image filenames separated by a comma.")
2389
+ for f in range(2):
2390
+ imList[c][f][i]=items[f].strip()
2391
+ imEx[c][f][i]=os.path.exists(path+imList[c][f][i]) if imList[c][f][i] else False
2392
+ except FileNotFoundError:
2393
+ warningDialog(self,f"File '{filename}' not found at path '{path}'.")
2394
+ imList=imEx=[]
2395
+ except ValueError as e:
2396
+ warningDialog(self,f"Error: {e}")
2397
+ imList=imEx=[]
2398
+ return imList, imEx
2399
+
2400
+ def button_write_list_action(self):
2401
+ filename, _ = QFileDialog.getSaveFileName(self,"Select location and name of the image list file to save",
2402
+ dir=self.imTree.path, filter='*.txt',\
2403
+ options=optionNativeDialog)
2404
+ if filename:
2405
+ if filename[-4:]!='.txt': filename+='.txt' #per adattarlo al mac
2406
+ filename=myStandardRoot('{}'.format(str(filename)))
2407
+ self.write_imFile(filename,self.imTree.imList)
2408
+
2409
+ def write_imFile(self,filename,imList):
2410
+ try:
2411
+ with open(filename, 'w') as file:
2412
+ for i in range(len(imList[0][0])):
2413
+ row = '; '.join([f"{imList[j][0][i]}, {imList[j][1][i]}" for j in range(len(imList))]) + '\n'
2414
+ file.write(row)
2415
+ except Exception as e:
2416
+ pri.Error.red(f"Error writing to file: {e}\n{traceback.format_exc()}\n")
2417
+ warningDialog(self,f"Error writing to file: {e}")
2418
+
2419
+ def treeContextMenuEvent(self, tree:PaIRSTree, event):
2420
+ item=tree.currentItem()
2421
+ if not item: return
2422
+ menu=QMenu(tree)
2423
+ menu.setStyleSheet(self.window().ui.menu.styleSheet())
2424
+ name=[]
2425
+ act=[]
2426
+ fun=[]
2427
+ for nb in self.icons_names:
2428
+ if '-' not in nb and '|' not in nb and nb not in self.excludedFromContextMenu:
2429
+ b:QPushButton=getattr(self,'button_'+nb)
2430
+ if b.isVisible() and b.isEnabled():
2431
+ if hasattr(self,'button_'+nb+'_action'):
2432
+ name.append(nb)
2433
+ act.append(QAction(b.icon(),toPlainText(b.toolTip().split('.')[0]),tree))
2434
+ menu.addAction(act[-1])
2435
+ callback=getattr(self,'button_'+nb+'_action')
2436
+ fun.append(callback)
2437
+ elif '|' in nb:
2438
+ if len(act): menu.addSeparator()
2439
+
2440
+ if len(act):
2441
+ action = menu.exec(tree.mapToGlobal(event.pos()))
2442
+ for nb,a,f in zip(name,act,fun):
2443
+ if a==action:
2444
+ f()
2445
+
2446
+ def setWidgetTabOrder(self):
2447
+ buttons=[getattr(self,'button_'+icon_name) if hasattr(self,'button_'+icon_name) else None for icon_name in self.icons_names]
2448
+ widgets=[self]+[self.button_setList if hasattr(self,'button_setList') else None]+buttons[:4]+[self.spin_img, self.spin_cam, self.spin_ncam, self.spin_frame]+buttons[4:]
2449
+ for i in range(len(widgets)-2):
2450
+ self.setTabOrder(widgets[i],widgets[i+1])
2451
+
2452
+ class CalibrationTree(PaIRSTree):
2453
+
2454
+ def mousePressEvent(self, event: QMouseEvent):
2455
+ TABpar.FlagSettingPar=True
2456
+ self.cursor_pos=event.globalPosition().toPoint()
2457
+ super().mousePressEvent(event)
2458
+ return
2459
+
2460
+ def mouseReleaseEvent(self, event: QMouseEvent):
2461
+ TABpar.FlagSettingPar=False
2462
+ self.itemSelectionChanged.emit()
2463
+ super().mouseReleaseEvent(event)
2464
+ return
2465
+
2466
+ def __init__(self, parent: QWidget=None,listDim=2,listDepth=1):
2467
+ super().__init__(parent,listDim,listDepth)
2468
+
2469
+ header=self.header()
2470
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
2471
+ header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
2472
+ self.setStyleSheet(self.styleSheet()+f"QTreeView::item {{ height: {self.default_row_height} px; }}")
2473
+ self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
2474
+
2475
+ self.ncam=1
2476
+ self.calList=create_empty_list_of_dimension(1)
2477
+ self.calEx=create_empty_list_of_dimension(1)
2478
+
2479
+ expand_level(self.itemList,level=0,target_length=2)
2480
+ self.itemList[0]=self.calList
2481
+ self.itemList[1]=self.calEx
2482
+
2483
+ self.setVisible(True)
2484
+
2485
+ def importLists(self,filenames):
2486
+ def createItems():
2487
+ items=[None]*len(filenames)
2488
+ for k,filename in enumerate(filenames):
2489
+ FlagWarn=False
2490
+
2491
+ ex=os.path.exists(filename)
2492
+ self.nimg+=1
2493
+ item_data=[str(self.nimg),os.path.basename(filename) + f' ({os.path.dirname(filename)})']
2494
+ if not ex:
2495
+ FlagWarn=True
2496
+ item=QTreeWidgetItem(None,item_data)
2497
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
2498
+
2499
+ if FlagWarn:
2500
+ item.setIcon(0,self.icon_warning)
2501
+ item.setToolTip(0,'File missing')
2502
+ item.setStatusTip(0,'File missing')
2503
+ data=[FlagWarn,filename,ex]
2504
+ item.setData(0,Qt.ItemDataRole.UserRole,data)
2505
+
2506
+ self.calList.append(filename)
2507
+ self.calEx.append(ex)
2508
+ items[k]=item
2509
+ return (items)
2510
+
2511
+ items=createItems()
2512
+ self.insertItems2List(-1,items,False,NotImplementedError)
2513
+
2514
+ def setLists(self,selection=[]):
2515
+ calList=self.calList
2516
+ calEx=self.calEx
2517
+ self.warns=[]
2518
+
2519
+ pri.Time.blue(f'CalibrationTree: start setting list')
2520
+ self.nimg=nimg=len(calList)
2521
+ if self.nimg:
2522
+ while not calList[self.nimg-1]:
2523
+ calList.pop(self.nimg-1)
2524
+ calEx.pop(self.nimg-1)
2525
+ self.nimg-=1
2526
+ if self.nimg==0: break
2527
+ nimg=self.nimg
2528
+ self.FlagReset=True
2529
+
2530
+ def createItems():
2531
+ items=[None]*nimg
2532
+ for k in range(nimg):
2533
+ FlagWarn=False
2534
+
2535
+ filename=calList[k]
2536
+ ex=calEx[k]=os.path.exists(filename)
2537
+ item_data=[str(k+1),os.path.basename(filename) + f' ({os.path.dirname(filename)})']
2538
+ if not ex:
2539
+ FlagWarn=True
2540
+ item=QTreeWidgetItem(None,item_data)
2541
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
2542
+ items[k]=item
2543
+ item.setToolTip(1,item_data[1])
2544
+ item.setStatusTip(1,item_data[1])
2545
+
2546
+ if FlagWarn:
2547
+ item.setIcon(0,self.icon_warning)
2548
+ item.setToolTip(0,'File missing')
2549
+ item.setStatusTip(0,'File missing')
2550
+ self.warns.append(k)
2551
+ data=[not FlagWarn,filename,ex]
2552
+ item.setData(0,Qt.ItemDataRole.UserRole,data)
2553
+ return (items)
2554
+
2555
+ items=createItems()
2556
+ self.insertItems2List(-1,items,False,selection)
2557
+ pri.Time.blue(f'CalibrationTree: end setting list')
2558
+ return
2559
+
2560
+ class PaIRSTable(QTableWidget):
2561
+ cutted_itemList=[]
2562
+ cutted_items=[]
2563
+ deleted_itemList=[]
2564
+ deleted_items=[]
2565
+ margin_rect=10
2566
+
2567
+ class ImageTable_signals(QObject):
2568
+ updateTree=Signal()
2569
+ updateLists=Signal()
2570
+ createdItems=Signal(int,list,bool,list)
2571
+
2572
+ def __init__(self, parent: QWidget=None, listDim=1, listDepth=0):
2573
+ super().__init__(parent=parent)
2574
+ self.listDim=listDim
2575
+ self.listDepth=listDepth
2576
+ self.signals=self.ImageTable_signals()
2577
+
2578
+ self.setupRowBehaviour()
2579
+
2580
+ self.icon_warning = QIcon()
2581
+ self.icon_warning.addFile(u""+ icons_path +"warn.png", QSize(), QIcon.Normal, QIcon.Off)
2582
+ self.dragged_items=self.dragged_indexes=None
2583
+ self.hovered_item=None
2584
+ self.setAutoScroll(True)
2585
+ self.verticalScrollBarVal=self.verticalScrollBar().value()
2586
+ self.verticalScrollBar().setStyleSheet("""
2587
+ QTreeWidget {
2588
+ margin-bottom: 0px;
2589
+ }
2590
+ QTreeWidget::item {
2591
+ margin-bottom: 0px;
2592
+ }
2593
+ QTreeView {
2594
+ margin-bottom: 0px;
2595
+ }
2596
+ QScrollBar:horizontal{
2597
+ height: 15px;
2598
+ margin: 3px 0px 3px 0px;
2599
+ border: 1px transparent #2A2929;
2600
+ border-radius: 4px;
2601
+ background-color: transparent; /* #2A2929; */
2602
+ }
2603
+ QScrollBar::handle:horizontal{
2604
+ background-color: rgba(180,180,180,180); /* #605F5F; */
2605
+ min-width: 30px;
2606
+ border-radius: 4px;
2607
+ }
2608
+ QScrollBar:vertical{
2609
+ width: 15px;
2610
+ margin: 0px 3px 0px 3px;
2611
+ border: 1px transparent #2A2929;
2612
+ border-radius: 4px;
2613
+ background-color: transparent; /* #2A2929; */
2614
+ }
2615
+ QScrollBar::handle:vertical{
2616
+ background-color: rgba(180,180,180,180); /* #605F5F; */
2617
+ min-height: 30px;
2618
+ border-radius: 4px;
2619
+ }
2620
+ QScrollBar::add-line{
2621
+ border: none;
2622
+ background: none;
2623
+ }
2624
+
2625
+ QScrollBar::sub-line{
2626
+ border: none;
2627
+ background: none;
2628
+ }""")
2629
+ self.cursor_pos=self.cursor().pos()
2630
+
2631
+ self.pen = QPen(qRgba(127,227,255,0.9))
2632
+ self.pen.setWidth(3)
2633
+
2634
+ style = """
2635
+ QTableWidget::item:hover {
2636
+ background-color: rgba(0, 116, 255, 0.1);
2637
+ }
2638
+ QTableWidget::item:selected:!active {
2639
+ background-color: rgba(0, 116, 255, 0.4);
2640
+ }
2641
+ QTableWidget::item:selected:active {
2642
+ background-color: rgba(0, 116, 255, 0.8);
2643
+ }
2644
+ """
2645
+ self.setStyleSheet(style)
2646
+
2647
+ self.addfuncreturn={}
2648
+ #self.addfuncreturn={'expand': self.expandRow}
2649
+ self.FlagSetting=False
2650
+ self.FlagReset=False
2651
+ self.FlagCutted=False
2652
+
2653
+ self.nimg=0
2654
+ self.itemList=create_empty_list_of_dimension(self.listDim)
2655
+ self.warns=[]
2656
+
2657
+ self.disableTab=lambda flag: None
2658
+
2659
+ def setupRowBehaviour(self):
2660
+ self.setDragDropMode(QTableWidget.DragDropMode.InternalMove)
2661
+ self.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectItems)
2662
+ self.setDragEnabled(True)
2663
+ self.setAcceptDrops(True)
2664
+ self.setDropIndicatorShown(True)
2665
+ self.setDragDropOverwriteMode(False)
2666
+ self.setDefaultDropAction(Qt.MoveAction)
2667
+ self.setSortingEnabled(False)
2668
+
2669
+ def mousePressEvent(self, event: QMouseEvent):
2670
+ TABpar.FlagSettingPar=True
2671
+ self.itemUponPressing=self.currentItem()
2672
+ self.cursor_pos=event.globalPosition().toPoint()
2673
+ super().mousePressEvent(event)
2674
+ return
2675
+
2676
+ def mouseReleaseEvent(self, event: QMouseEvent):
2677
+ TABpar.FlagSettingPar=False
2678
+ itemUponReleasing=self.currentItem()
2679
+ if itemUponReleasing!=self.itemUponPressing:
2680
+ self.currentItemChanged.emit(itemUponReleasing,itemUponReleasing)
2681
+ super().mouseReleaseEvent(event)
2682
+ return
2683
+
2684
+ def dragEnterEvent(self, event):
2685
+ TABpar.FlagSettingPar=True
2686
+ self.dragged_items = self.selectedItems()
2687
+ rows=[i.row() for i in self.dragged_items]
2688
+ self.setSelectedQuickly(rows,True)
2689
+ self.dragged_items = self.selectedItems()
2690
+ self.dragged_indexes = self.selectedIndexes()
2691
+ self.verticalScrollBarVal=self.verticalScrollBar().value()
2692
+ super().dragEnterEvent(event)
2693
+
2694
+ def dragMoveEvent(self, event):
2695
+ pos = event.position().toPoint()
2696
+ self.hovered_item = item = self.itemAt(pos)
2697
+ super().dragMoveEvent(event) # Allow the event to proceed for row moves
2698
+
2699
+ #@log_qpainter_usage
2700
+ def paintEvent(self, event):
2701
+ super().paintEvent(event)
2702
+ self.paintLines()
2703
+
2704
+ def paintLines(self):
2705
+ item=self.hovered_item
2706
+ if item and self.dragged_items:
2707
+ if self.dragged_items!='externalItem':
2708
+ self.drop_indicator_pos = self.dropIndicatorPosition()
2709
+ if self.drop_indicator_pos == QTableWidget.DropIndicatorPosition.AboveItem:
2710
+ item_rect = self.visualRowRect(item)
2711
+ self.drawDropIndicatorLine(item_rect.top(),item_rect.x(),item_rect.height(),item_rect.width(),-1)
2712
+ elif self.drop_indicator_pos == QTableWidget.DropIndicatorPosition.BelowItem:
2713
+ item_rect = self.visualRowRect(item)
2714
+ self.drawDropIndicatorLine(item_rect.bottom(),item_rect.x(),item_rect.height(),item_rect.width(),+1)
2715
+ else: #below
2716
+ item_rect = self.visualRowRect(item)
2717
+ self.drawDropIndicatorLine(item_rect.bottom(),item_rect.x(),item_rect.height(),item_rect.width(),+1)
2718
+
2719
+ def visualRowRect(self, row: int):
2720
+ if type(row)==QTableWidgetItem:
2721
+ row=row.row()
2722
+ rect = QRect()
2723
+ for column in range(self.columnCount()):
2724
+ item = self.item(row, column)
2725
+ if item:
2726
+ item_rect = self.visualItemRect(item)
2727
+ if column == 0: # Se è la prima colonna, aggiungi il margine
2728
+ item_rect.adjust(self.margin_rect, 0, 0, 0)
2729
+ rect = rect.united(item_rect)
2730
+ return rect
2731
+
2732
+ def drawDropIndicatorLine(self, y_pos,x_pos,dy,dx,sign=1):
2733
+ painter = QPainter(self.viewport())
2734
+ painter.setPen(self.pen)
2735
+ painter.drawLine(0, y_pos, self.viewport().width(), y_pos)
2736
+
2737
+ # Calcola la posizione della freccia
2738
+ s=5*sign
2739
+ for x_pos_2 in (x_pos,x_pos+dx-2*abs(s)):
2740
+ y_pos_2=y_pos-5*sign
2741
+ arrow_top = QPoint(x_pos_2, y_pos_2 - 3*s)
2742
+ arrow_bottom = QPoint(x_pos_2, y_pos_2)
2743
+ arrow_left = QPoint(x_pos_2 - s, y_pos_2-s)
2744
+ arrow_right = QPoint(x_pos_2 + s, y_pos_2-s)
2745
+
2746
+ # Disegna la freccia
2747
+ painter.drawLine(arrow_top, arrow_bottom)
2748
+ #painter.drawLine(arrow_left, arrow_right)
2749
+ painter.drawLine(arrow_bottom, arrow_right)
2750
+ painter.drawLine(arrow_bottom, arrow_left)
2751
+ painter.end()
2752
+
2753
+ def keyPressEvent(self, event):
2754
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter):
2755
+ for f in self.addfuncreturn:
2756
+ self.addfuncreturn[f]()
2757
+ super().keyPressEvent(event)
2758
+ current_item = self.currentItem()
2759
+ if not current_item or not self.isPersistentEditorOpen(current_item):
2760
+ self.setFocus()
2761
+
2762
+ '''
2763
+ def mimeData(self, items):
2764
+ mimeData = QMimeData()
2765
+ encodedData = QByteArray()
2766
+ stream = QDataStream(encodedData, QIODevice.WriteOnly)
2767
+
2768
+ for item in items:
2769
+ row = self.row(item)
2770
+ column = self.column(item)
2771
+ text = item.text()
2772
+ stream.writeInt32(row)
2773
+ stream.writeInt32(column)
2774
+ stream.writeQString(text)
2775
+
2776
+ mimeData.setData("application/x-qabstractitemmodeldatalist", encodedData)
2777
+ return mimeData
2778
+
2779
+ def dropMimeData(self, row, column, data, action):
2780
+ if action == Qt.MoveAction:
2781
+ encodedData = data.data("application/x-qabstractitemmodeldatalist")
2782
+ stream = QDataStream(encodedData, QIODevice.ReadOnly)
2783
+ newItems = []
2784
+
2785
+ while not stream.atEnd():
2786
+ row = stream.readInt32()
2787
+ column = stream.readInt32()
2788
+ text = stream.readQString()
2789
+ newItems.append((row, column, text))
2790
+
2791
+ for row, column, text in newItems:
2792
+ self.setItem(row, column, QTableWidgetItem(text))
2793
+
2794
+ return True
2795
+ return False
2796
+ '''
2797
+
2798
+ def dropEvent(self, event):
2799
+ drop_indicator_position = self.dropIndicatorPosition()
2800
+
2801
+ if drop_indicator_position == QTableWidget.DropIndicatorPosition.OnItem or self.hovered_item is None:
2802
+ target_row_index=None
2803
+ self.verticalScrollBar().setValue(self.verticalScrollBarVal)
2804
+ QCursor.setPos(self.cursor_pos)
2805
+ event.ignore() # Ignore the event if it's not a row move or a drop on an item
2806
+ FlagUpdateList=False
2807
+ else:
2808
+ target_row = self.hovered_item.row()
2809
+ #pri.Coding.yellow(f'Hovered item: {self.item(target_row,0).text()}')
2810
+ source_rows=[]
2811
+ for i in self.dragged_items:
2812
+ if i.row() not in source_rows:
2813
+ source_rows.append(i.row())
2814
+
2815
+ if self.drop_indicator_pos == QTableWidget.DropIndicatorPosition.BelowItem:
2816
+ target_row+=1
2817
+ selectedItems,target_row_new=self.move_rows_to(source_rows,target_row)
2818
+ self.dropLists(selectedItems,source_rows)
2819
+ for r in range(len(source_rows)):
2820
+ for c in range(self.columnCount()-1,-1,-1):
2821
+ i=self.item(r+target_row_new,c)
2822
+ i:QTableWidgetItem
2823
+ """
2824
+ if not c:
2825
+ pri.Coding.yellow(f'row={i.row()} {i.text()}')
2826
+ pri.Coding.red(f'{self.itemList[0][r+target_row_new]}')
2827
+ """
2828
+ i.setSelected(True)
2829
+
2830
+ event.accept()
2831
+ #for i in selectedItems: i.setSelected(True)
2832
+
2833
+
2834
+ FlagUpdateList=True
2835
+
2836
+ self.dragged_items=self.dragged_indexes=None
2837
+ self.repaint()
2838
+ TABpar.FlagSettingPar=False
2839
+ self.itemSelectionChanged.emit()
2840
+ if FlagUpdateList: self.signals.updateLists.emit()
2841
+
2842
+ def move_rows_to(self, rows, target_row):
2843
+ sorted_rows = sorted(rows)
2844
+ # Salvataggio dei dati delle righe da spostare
2845
+ rows_data = []
2846
+ for row in sorted_rows:
2847
+ row_data = [self.item(row, column).text() for column in range(self.columnCount())]
2848
+ rows_data.append(row_data)
2849
+
2850
+ # Rimozione delle righe dalla loro posizione originale
2851
+ diff=0
2852
+ for row in reversed(sorted_rows):
2853
+ self.removeRow(row)
2854
+ if row<target_row: diff+=1
2855
+ target_row-=diff
2856
+
2857
+ selectedItems=[]
2858
+ # Inserimento delle righe nella posizione target
2859
+ for row, data in enumerate(rows_data):
2860
+ target_row_index = target_row + row
2861
+ self.insertRow(target_row_index)
2862
+ for column, text in enumerate(data):
2863
+ item=QTableWidgetItem(text)
2864
+ self.setItem(target_row_index, column,item)
2865
+ selectedItems.append(item)
2866
+ return selectedItems, target_row
2867
+
2868
+ def setSelectedQuickly(self, rows, Flag):
2869
+ self.clearSelection()
2870
+ selectionFlag=QItemSelectionModel.SelectionFlag.Select if Flag else QItemSelectionModel.SelectionFlag.Deselect
2871
+ selection_model = self.selectionModel()
2872
+ selection = QItemSelection()
2873
+ for row in rows:
2874
+ selection.merge(QItemSelection(self.model().index(row, 0), self.model().index(row, self.columnCount() - 1)), selectionFlag)
2875
+ selection_model.select(selection, QItemSelectionModel.SelectionFlag.ClearAndSelect )
2876
+ return
2877
+
2878
+ def dropLists(self, items, indexes):
2879
+ if self.itemList:
2880
+ cutted_items=pop_at_depth(self.itemList,self.listDepth,indexes)
2881
+ insert_at_depth(self.itemList,self.listDepth,items[0].row(),cutted_items)
2882
+ #ind_fin=self.indexOfTopLevelItem(items[-1])
2883
+ return
2884
+
2885
+ def cutLists(self, indexes, FlagDeleted=False):
2886
+ if FlagDeleted: type(self).deleted_itemList=pop_at_depth(self.itemList,self.listDepth,indexes)
2887
+ else: type(self).cutted_itemList=pop_at_depth(self.itemList,self.listDepth,indexes)
2888
+ if not FlagDeleted: self.FlagCutted=True
2889
+ self.nimg-=len(indexes)
2890
+ return
2891
+
2892
+ def deleteLists(self, indexes):
2893
+ self.cutLists(indexes,FlagDeleted=True)
2894
+ return
2895
+
2896
+ def copyLists(self, indexes):
2897
+ type(self).cutted_itemList=copy_at_depth(self.itemList,self.listDepth,indexes)
2898
+ self.FlagCutted=False
2899
+ return
2900
+
2901
+ def pasteLists(self, ind, FlagDeleted=False):
2902
+ pri.Time.magenta('pasteLists: start')
2903
+ if FlagDeleted: iList=type(self).deleted_itemList
2904
+ else: iList=type(self).cutted_itemList
2905
+ self.nimg+=measure_depth_length(iList,self.listDepth)
2906
+ insert_at_depth(self.itemList,self.listDepth,ind,iList)
2907
+ if self.FlagCutted:
2908
+ type(self).cutted_itemList=[]
2909
+ type(self).cutted_items=[]
2910
+ self.FlagCutted=False
2911
+ else:
2912
+ if FlagDeleted: type(self).deleted_itemList=deep_duplicate(iList)
2913
+ else: type(self).cutted_itemList=deep_duplicate(iList)
2914
+ pri.Time.magenta('pasteLists: end')
2915
+ return
2916
+
2917
+ def cleanLists(self):
2918
+ self.itemList=create_empty_list_of_dimension(self.listDim)
2919
+
2920
+ class ImageTable(PaIRSTable):
2921
+
2922
+ def __init__(self, parent: QWidget=None, listDim=2, listDepth=1):
2923
+ super().__init__(parent,listDim,listDepth)
2924
+
2925
+ columns=["Image filename","Plane parameters","Info"]
2926
+ self.setColumnCount(len(columns))
2927
+ header = self.horizontalHeader()
2928
+ self.setHorizontalHeaderLabels(columns)
2929
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.Interactive)
2930
+ header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
2931
+ header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
2932
+
2933
+ self.RowInfo=[]
2934
+ self.InfoLabel:QLabel=None
2935
+ self.DeleteButton:QPushButton=None
2936
+ self.addwid=[]
2937
+ self.addfuncreturn={}
2938
+ self.addfuncout={}
2939
+
2940
+ self.setVisible(True)
2941
+
2942
+ def keyPressEvent(self, event):
2943
+ super().keyPressEvent(event)
2944
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter): #return
2945
+ for f in self.addfuncreturn:
2946
+ self.addfuncreturn[f]()
2947
+
2948
+ def focusOutEvent(self, event):
2949
+ super().focusOutEvent(event)
2950
+ for f in self.addfuncout:
2951
+ self.addfuncout[f]()
2952
+
2953
+ def resizeEvent(self, event):
2954
+ super().resizeEvent(event)
2955
+ self.resizeInfoLabel()
2956
+
2957
+ def resizeInfoLabel(self):
2958
+ if self.InfoLabel and (True if not self.addwid else not self.addwid[0].hasFocus()):
2959
+ item=self.currentItem()
2960
+ if item:
2961
+ self.InfoLabel.show()
2962
+ if self.RowInfo: rowInfo=self.RowInfo[self.currentRow()]
2963
+ else: rowInfo=''
2964
+ tip=item.toolTip()
2965
+ if not "<br>" in tip:
2966
+ fw=lambda t: QtGui.QFontMetrics(self.InfoLabel.font()).size(QtCore.Qt.TextSingleLine,t).width()
2967
+ if fw(tip)>self.InfoLabel.width():
2968
+ k=0
2969
+ while fw(tip[:k])<self.InfoLabel.width():
2970
+ k+=1
2971
+ tip="<br>".join([tip[:k-1], tip[k-1:2*k]])
2972
+ if rowInfo: tip="<br>".join([tip,rowInfo])
2973
+ self.InfoLabel.setText(tip)
2974
+ else:
2975
+ self.InfoLabel.hide()
2976
+ self.InfoLabel.setText('')
2977
+
2978
+ #*************************************************** TESTING
2979
+ if __name__ == "__main__":
2980
+ if currentID==developerIDs['GP_Win_Office']:
2981
+ working_fold='C:/desk/PIV_Img/swirler_png/'
2982
+ elif currentID==developerIDs['GP_Mac_Laptop']:
2983
+ working_fold='/Users/gerardo/Desktop/PIV_Img/swirler_png/'
2984
+ else:
2985
+ working_fold=basefold
2986
+
2987
+ pri.Coding.white('\n'+'*'*50+'\n')
2988
+ imSet=ImageSet()
2989
+ imSet.scanPath(working_fold)
2990
+ imSet.print()
2991
+ pri.Coding.white('\n'+'*'*50+'\n')
2992
+
2993
+ kc=0
2994
+ i=198
2995
+ npairs=6000
2996
+ step=1
2997
+ ncam=2
2998
+
2999
+ app = PaIRSApp(sys.argv)
3000
+ w=ImageTreeWidget(FlagSpinButtons=FlagSpinButtons_Debug)
3001
+ try:
3002
+ imagePath,imageList,imageEx=imSet.genListsFromIndex(kc,i,npairs,step,ncam)
3003
+ w.setLists(imagePath,imageList,imageEx,[10,ncam,1])
3004
+ w.write_imFile(working_fold+'example.txt',imageList)
3005
+ if npairs<100:
3006
+ pri.Coding.white(f'List "{imSet.pattern[kc]}", i={i}, npairs={npairs}, step={step}')
3007
+ for n in range(len(imageList[0][0])):
3008
+ pri.Coding.white(f'{n:6d}:'+'\t'+imageList[0][0][n]+', '+imageList[0][1][n])
3009
+ for c in range(1,len(imageList)):
3010
+ pri.Coding.white(f'{" "*7}'+'\t'+imageList[c][0][n]+', '+imageList[c][1][n])
3011
+ except:
3012
+ traceback.print_exc()
3013
+ w.nullList()
3014
+ pass
3015
+
3016
+ w.resize(750,750)
3017
+ w.setVisible(True)
3018
+
3019
+ '''
3020
+ w2=ImageTable()
3021
+ w2.resize(750,750)
3022
+ w2.setVisible(True)
3023
+ '''
3024
+ sys.exit(app.exec())
3025
+
3026
+ quit()