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