PaIRS-UniNa 0.2.7__cp312-cp312-macosx_11_0_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. PaIRS_UniNa/Calibration_Tab.py +331 -0
  2. PaIRS_UniNa/Changes.txt +135 -0
  3. PaIRS_UniNa/Custom_Top.py +303 -0
  4. PaIRS_UniNa/Explorer.py +3086 -0
  5. PaIRS_UniNa/FolderLoop.py +372 -0
  6. PaIRS_UniNa/Input_Tab.py +722 -0
  7. PaIRS_UniNa/Input_Tab_CalVi.py +788 -0
  8. PaIRS_UniNa/Input_Tab_tools.py +3024 -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 +1376 -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 +1254 -0
  21. PaIRS_UniNa/Vis_Tab.py +2148 -0
  22. PaIRS_UniNa/Vis_Tab_CalVi.py +983 -0
  23. PaIRS_UniNa/Whatsnew.py +129 -0
  24. PaIRS_UniNa/_PaIRS_PIV.so +0 -0
  25. PaIRS_UniNa/__init__.py +6 -0
  26. PaIRS_UniNa/__main__.py +45 -0
  27. PaIRS_UniNa/addwidgets_ps.py +1133 -0
  28. PaIRS_UniNa/calib.py +1488 -0
  29. PaIRS_UniNa/calibView.py +833 -0
  30. PaIRS_UniNa/gPaIRS.py +3856 -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/flaticon_PaIRS_download_warning.png +0 -0
  156. PaIRS_UniNa/icons/flip_y_off.png +0 -0
  157. PaIRS_UniNa/icons/flip_y_on.png +0 -0
  158. PaIRS_UniNa/icons/focusErrr.png +0 -0
  159. PaIRS_UniNa/icons/gear.gif +0 -0
  160. PaIRS_UniNa/icons/gear.png +0 -0
  161. PaIRS_UniNa/icons/ger.png +0 -0
  162. PaIRS_UniNa/icons/greenv.png +0 -0
  163. PaIRS_UniNa/icons/guide.png +0 -0
  164. PaIRS_UniNa/icons/icon_CalVi.png +0 -0
  165. PaIRS_UniNa/icons/icon_PaIRS.png +0 -0
  166. PaIRS_UniNa/icons/import.png +0 -0
  167. PaIRS_UniNa/icons/import_set.png +0 -0
  168. PaIRS_UniNa/icons/information.png +0 -0
  169. PaIRS_UniNa/icons/input_logo.png +0 -0
  170. PaIRS_UniNa/icons/issue.png +0 -0
  171. PaIRS_UniNa/icons/laser_NTR.png +0 -0
  172. PaIRS_UniNa/icons/laser_TR_double.png +0 -0
  173. PaIRS_UniNa/icons/laser_TR_single.png +0 -0
  174. PaIRS_UniNa/icons/link.png +0 -0
  175. PaIRS_UniNa/icons/linked.png +0 -0
  176. PaIRS_UniNa/icons/loaded.png +0 -0
  177. PaIRS_UniNa/icons/loading_2.gif +0 -0
  178. PaIRS_UniNa/icons/log_logo.png +0 -0
  179. PaIRS_UniNa/icons/logo_CalVi.png +0 -0
  180. PaIRS_UniNa/icons/logo_CalVi_completo.png +0 -0
  181. PaIRS_UniNa/icons/logo_CalVi_party.png +0 -0
  182. PaIRS_UniNa/icons/logo_PaIRS.png +0 -0
  183. PaIRS_UniNa/icons/logo_PaIRS_completo.png +0 -0
  184. PaIRS_UniNa/icons/logo_PaIRS_download.png +0 -0
  185. PaIRS_UniNa/icons/logo_PaIRS_party_rect.png +0 -0
  186. PaIRS_UniNa/icons/logo_PaIRS_rect.png +0 -0
  187. PaIRS_UniNa/icons/logo_opaco.png +0 -0
  188. PaIRS_UniNa/icons/mask.png +0 -0
  189. PaIRS_UniNa/icons/measure.png +0 -0
  190. PaIRS_UniNa/icons/measure_off.png +0 -0
  191. PaIRS_UniNa/icons/min_proc.png +0 -0
  192. PaIRS_UniNa/icons/min_proc_off.png +0 -0
  193. PaIRS_UniNa/icons/min_step.png +0 -0
  194. PaIRS_UniNa/icons/min_step_off.png +0 -0
  195. PaIRS_UniNa/icons/minus.png +0 -0
  196. PaIRS_UniNa/icons/mirror_u.png +0 -0
  197. PaIRS_UniNa/icons/mirror_v.png +0 -0
  198. PaIRS_UniNa/icons/mirror_x.png +0 -0
  199. PaIRS_UniNa/icons/mirror_y.png +0 -0
  200. PaIRS_UniNa/icons/mtplt.png +0 -0
  201. PaIRS_UniNa/icons/new.png +0 -0
  202. PaIRS_UniNa/icons/new_workspace.png +0 -0
  203. PaIRS_UniNa/icons/news.png +0 -0
  204. PaIRS_UniNa/icons/normal_run.png +0 -0
  205. PaIRS_UniNa/icons/open.png +0 -0
  206. PaIRS_UniNa/icons/open_image.png +0 -0
  207. PaIRS_UniNa/icons/open_new_window.png +0 -0
  208. PaIRS_UniNa/icons/open_result.png +0 -0
  209. PaIRS_UniNa/icons/open_workspace.png +0 -0
  210. PaIRS_UniNa/icons/output_logo.png +0 -0
  211. PaIRS_UniNa/icons/paste_above.png +0 -0
  212. PaIRS_UniNa/icons/paste_below.png +0 -0
  213. PaIRS_UniNa/icons/pause.png +0 -0
  214. PaIRS_UniNa/icons/paused.png +0 -0
  215. PaIRS_UniNa/icons/pencil_bw.png +0 -0
  216. PaIRS_UniNa/icons/piv_proc.png +0 -0
  217. PaIRS_UniNa/icons/piv_proc_off.png +0 -0
  218. PaIRS_UniNa/icons/piv_step.png +0 -0
  219. PaIRS_UniNa/icons/piv_step_off.png +0 -0
  220. PaIRS_UniNa/icons/plane.png +0 -0
  221. PaIRS_UniNa/icons/play.png +0 -0
  222. PaIRS_UniNa/icons/plus.png +0 -0
  223. PaIRS_UniNa/icons/process_logo.png +0 -0
  224. PaIRS_UniNa/icons/process_loop.png +0 -0
  225. PaIRS_UniNa/icons/project.png +0 -0
  226. PaIRS_UniNa/icons/pylog.png +0 -0
  227. PaIRS_UniNa/icons/python_warning.png +0 -0
  228. PaIRS_UniNa/icons/queue.png +0 -0
  229. PaIRS_UniNa/icons/quit.png +0 -0
  230. PaIRS_UniNa/icons/read.png +0 -0
  231. PaIRS_UniNa/icons/read_list.png +0 -0
  232. PaIRS_UniNa/icons/redo.png +0 -0
  233. PaIRS_UniNa/icons/redx.png +0 -0
  234. PaIRS_UniNa/icons/reset.png +0 -0
  235. PaIRS_UniNa/icons/reset_levels.png +0 -0
  236. PaIRS_UniNa/icons/resize_icon.png +0 -0
  237. PaIRS_UniNa/icons/restore.png +0 -0
  238. PaIRS_UniNa/icons/restore_undo.png +0 -0
  239. PaIRS_UniNa/icons/rotate_clock.png +0 -0
  240. PaIRS_UniNa/icons/rotate_counter.png +0 -0
  241. PaIRS_UniNa/icons/rotate_v_clock.png +0 -0
  242. PaIRS_UniNa/icons/rotate_v_counter.png +0 -0
  243. PaIRS_UniNa/icons/running.gif +0 -0
  244. PaIRS_UniNa/icons/running.png +0 -0
  245. PaIRS_UniNa/icons/running_warn.png +0 -0
  246. PaIRS_UniNa/icons/sandglass.png +0 -0
  247. PaIRS_UniNa/icons/save.png +0 -0
  248. PaIRS_UniNa/icons/save_and_stop.png +0 -0
  249. PaIRS_UniNa/icons/save_cfg.png +0 -0
  250. PaIRS_UniNa/icons/saveas.png +0 -0
  251. PaIRS_UniNa/icons/saveas_workspace.png +0 -0
  252. PaIRS_UniNa/icons/scale_all.png +0 -0
  253. PaIRS_UniNa/icons/scale_down.png +0 -0
  254. PaIRS_UniNa/icons/scale_up.png +0 -0
  255. PaIRS_UniNa/icons/scan_list.png +0 -0
  256. PaIRS_UniNa/icons/scan_path.png +0 -0
  257. PaIRS_UniNa/icons/search.png +0 -0
  258. PaIRS_UniNa/icons/showIW_off.png +0 -0
  259. PaIRS_UniNa/icons/showIW_on.png +0 -0
  260. PaIRS_UniNa/icons/show_all.png +0 -0
  261. PaIRS_UniNa/icons/sort.png +0 -0
  262. PaIRS_UniNa/icons/sort_reversed.png +0 -0
  263. PaIRS_UniNa/icons/spiv_proc.png +0 -0
  264. PaIRS_UniNa/icons/spiv_proc_off.png +0 -0
  265. PaIRS_UniNa/icons/star.png +0 -0
  266. PaIRS_UniNa/icons/step_inheritance.png +0 -0
  267. PaIRS_UniNa/icons/subMIN_off.png +0 -0
  268. PaIRS_UniNa/icons/subMIN_on.png +0 -0
  269. PaIRS_UniNa/icons/tom.png +0 -0
  270. PaIRS_UniNa/icons/trash.png +0 -0
  271. PaIRS_UniNa/icons/undo.png +0 -0
  272. PaIRS_UniNa/icons/unedited.png +0 -0
  273. PaIRS_UniNa/icons/unina_dii.png +0 -0
  274. PaIRS_UniNa/icons/uninitialized.png +0 -0
  275. PaIRS_UniNa/icons/unlink.png +0 -0
  276. PaIRS_UniNa/icons/unwrap_items.png +0 -0
  277. PaIRS_UniNa/icons/up.png +0 -0
  278. PaIRS_UniNa/icons/updating_import.gif +0 -0
  279. PaIRS_UniNa/icons/updating_pairs.gif +0 -0
  280. PaIRS_UniNa/icons/vectorColor.png +0 -0
  281. PaIRS_UniNa/icons/vettore.png +0 -0
  282. PaIRS_UniNa/icons/view.png +0 -0
  283. PaIRS_UniNa/icons/view_off.png +0 -0
  284. PaIRS_UniNa/icons/vis_logo.png +0 -0
  285. PaIRS_UniNa/icons/waiting_circle.png +0 -0
  286. PaIRS_UniNa/icons/warning.png +0 -0
  287. PaIRS_UniNa/icons/warning_circle.png +0 -0
  288. PaIRS_UniNa/icons/window.png +0 -0
  289. PaIRS_UniNa/icons/workspace.png +0 -0
  290. PaIRS_UniNa/icons/wrap_items.png +0 -0
  291. PaIRS_UniNa/icons/write_list.png +0 -0
  292. PaIRS_UniNa/listLib.py +303 -0
  293. PaIRS_UniNa/mtfPIV.py +256 -0
  294. PaIRS_UniNa/parForMulti.py +435 -0
  295. PaIRS_UniNa/parForWorkers.py +593 -0
  296. PaIRS_UniNa/pivParFor.py +235 -0
  297. PaIRS_UniNa/plt_util.py +141 -0
  298. PaIRS_UniNa/preProcParFor.py +155 -0
  299. PaIRS_UniNa/procTools.py +1394 -0
  300. PaIRS_UniNa/readcfg.py +52 -0
  301. PaIRS_UniNa/rqrdpckgs.txt +9 -0
  302. PaIRS_UniNa/stereoPivParFor.py +227 -0
  303. PaIRS_UniNa/tAVarie.py +215 -0
  304. PaIRS_UniNa/tabSplitter.py +612 -0
  305. PaIRS_UniNa/ui_Calibration_Tab.py +545 -0
  306. PaIRS_UniNa/ui_Custom_Top.py +296 -0
  307. PaIRS_UniNa/ui_Input_Tab.py +1101 -0
  308. PaIRS_UniNa/ui_Input_Tab_CalVi.py +1283 -0
  309. PaIRS_UniNa/ui_Log_Tab.py +263 -0
  310. PaIRS_UniNa/ui_Output_Tab.py +2362 -0
  311. PaIRS_UniNa/ui_Process_Tab.py +3810 -0
  312. PaIRS_UniNa/ui_Process_Tab_CalVi.py +1549 -0
  313. PaIRS_UniNa/ui_Process_Tab_Disp.py +1141 -0
  314. PaIRS_UniNa/ui_Process_Tab_Min.py +437 -0
  315. PaIRS_UniNa/ui_ResizePopup.py +204 -0
  316. PaIRS_UniNa/ui_Vis_Tab.py +1628 -0
  317. PaIRS_UniNa/ui_Vis_Tab_CalVi.py +1251 -0
  318. PaIRS_UniNa/ui_Whatsnew.py +132 -0
  319. PaIRS_UniNa/ui_gPairs.py +877 -0
  320. PaIRS_UniNa/ui_infoPaIRS.py +551 -0
  321. PaIRS_UniNa/whatsnew.txt +5 -0
  322. pairs_unina-0.2.7.dist-info/METADATA +160 -0
  323. pairs_unina-0.2.7.dist-info/RECORD +325 -0
  324. pairs_unina-0.2.7.dist-info/WHEEL +5 -0
  325. pairs_unina-0.2.7.dist-info/top_level.txt +2 -0
@@ -0,0 +1,3024 @@
1
+ import os.path
2
+
3
+ from PySide6.QtWidgets import QWidget
4
+ from .PaIRS_pypacks import*
5
+ from .addwidgets_ps import*
6
+ from .TabTools import TABpar
7
+ from .listLib import *
8
+ from concurrent.futures import ThreadPoolExecutor
9
+
10
+ FlagSpinButtons_Debug=True
11
+ batch_size_image_creation=200
12
+ sleep_time_item_worker=0.05
13
+
14
+ class ImageSet(TABpar):
15
+
16
+ def __init__(self,path='',exts=supported_exts):
17
+ self.setup()
18
+ self.lists=[f for f,v in self.__dict__.items() if type(v)==list]
19
+ self.supported_exts = exts
20
+ super().__init__('ImageSet','Input')
21
+ self.unchecked_fields+=['signals','fname','fnumb','outFiles']
22
+ if path: self.scanPath(path)
23
+
24
+ def setup(self):
25
+ self.path = './'
26
+ self.count = 0
27
+ self.pattern = []
28
+ self.pa = []
29
+ self.fname = []
30
+ self.fnumb = []
31
+ self.ndig = []
32
+ self.nimg = []
33
+ self.ind_in = []
34
+ self.ind_fin = []
35
+ self.ext = []
36
+ self.link = []
37
+ self.outFiles = {}
38
+
39
+ def nameF(self,fname,i):
40
+ a=fname[0]
41
+ ndigits=fname[1]
42
+ b=fname[2]
43
+ return a+f"{i:0{ndigits}d}"+b if type(i)==int else a+str(i)+b
44
+
45
+ def numbF(self,fnumb,name):
46
+ n=fnumb[0]
47
+ ndigits=fnumb[1]
48
+ return int(name[n:n+ndigits])
49
+
50
+ def scanPath(self,path):
51
+ pri.Time.blue(f'ImageSet: start scanning path {path}')
52
+ self.clearLists()
53
+ path=myStandardPath(path) #maybe useless
54
+ self.path=path
55
+ files=findFiles_sorted(path+"*.*") # needed to use the recompiled reg expressions
56
+ for k,file in enumerate(files):
57
+ if not any(file.endswith(ex) for ex in self.supported_exts):
58
+ continue
59
+ basename=os.path.basename(file)
60
+ FlagMatch=False
61
+ if self.count:
62
+ FlagMatch=self.checkMatch(basename)
63
+ if not FlagMatch:
64
+ self.addPattern(basename)
65
+ for j in range(k):
66
+ basename=os.path.basename(files[j])
67
+ FlagMatch=self.checkMatch(basename,FlagOnlyLast=True)
68
+ self.sortLists()
69
+ self.createLink()
70
+ self.scanOutFile()
71
+ pri.Time.blue(f'ImageSet: end scanning path {path}')
72
+ return self
73
+
74
+ def scanOutFile(self):
75
+ self.outFiles={}
76
+ if os.path.exists(self.path):
77
+ _,dirnames,_=next(os.walk(self.path))
78
+ foldernames=[self.path]+[self.path+d+'/' for d in dirnames]
79
+ values=[v for _,v in outExt.__dict__.items() if type(v)==str]
80
+ for ext in values:
81
+ self.outFiles[ext]=[]
82
+ for fold in foldernames:
83
+ self.outFiles[ext]+=findFiles_sorted(fold+'*'+ext)
84
+
85
+ def clearLists(self):
86
+ self.count=0
87
+ for f in self.lists: setattr(self,f,[])
88
+ return
89
+
90
+ def checkMatch(self,basename,FlagOnlyLast=False):
91
+ FlagMatch=False
92
+ if FlagOnlyLast: kra=[self.count-1]
93
+ else: kra=range(self.count)
94
+ if self.count:
95
+ for k in kra:
96
+ pa:re.Pattern=re.compile(self.pa[k])
97
+ if pa.match(basename):
98
+ FlagMatch=True
99
+ self.nimg[k]+=1
100
+ ind=self.numbF(self.fnumb[k],basename)
101
+ self.ind_in[k]=min([self.ind_in[k],ind])
102
+ self.ind_fin[k]=max([self.ind_fin[k],ind])
103
+ return FlagMatch
104
+
105
+ def addPattern(self,basename):
106
+ _, ext = os.path.splitext(basename)
107
+ split_basename=re.split(r'(\d+)', basename)
108
+ c=0
109
+ for k,s in enumerate(split_basename):
110
+ if not len(s): continue
111
+ if s[0].isdigit():
112
+ self.count+=1
113
+ ndig=len(s)
114
+ self.ndig.append(ndig)
115
+ self.nimg.append(1)
116
+ self.ind_in.append(int(s))
117
+ self.ind_fin.append(int(s))
118
+
119
+ pattern_list=split_basename.copy()
120
+ pattern_list[k]="*{"+str(ndig)+"}"
121
+ pattern="".join(pattern_list)
122
+ self.pattern.append(pattern)
123
+
124
+ pattern_list[k]='\\d{'+str(ndig)+'}'
125
+ pattern="".join(pattern_list)
126
+ #pa=re.compile(pattern)
127
+ self.pa.append(pattern)
128
+
129
+ pre ="".join(pattern_list[:k])
130
+ post="".join(pattern_list[k+1:])
131
+ fname=[pre,ndig,post] #lambda i, a=pre, b=post, ndigits=ndig: a+f"{i:0{ndigits}d}"+b if type(i)==int else a+str(i)+b
132
+ self.fname.append(fname)
133
+
134
+ fnumb=[c,ndig] #lambda name, n=c, ndigits=ndig: int(name[n:n+ndigits])
135
+ self.fnumb.append(fnumb)
136
+ self.ext.append(ext)
137
+ self.link.append([])
138
+
139
+ c+=len(s)
140
+ return
141
+
142
+ def sortLists(self):
143
+ if not self.count: return
144
+ lind=range(len(self.nimg))
145
+ self.nimg.reverse()
146
+ _,lind=zip(*sorted(zip(self.nimg, lind),reverse=True))
147
+ for f in self.lists:
148
+ v=getattr(self,f)
149
+ v.reverse() if f!='nimg' else None
150
+ v2=[v[i] for i in lind]
151
+ setattr(self,f,v2)
152
+ return
153
+
154
+ def createLink(self):
155
+ for k,p in enumerate(self.pattern):
156
+ alpha=[]
157
+ jalpha=[]
158
+ number=[]
159
+ jnumber=[]
160
+ for j,p2 in enumerate(self.pattern):
161
+ p2:str
162
+ if len(p)!=len(p2): continue
163
+ diff=[i for i in range(len(p)) if p[i]!=p2[i]]
164
+ if len(diff)==1:
165
+ if p2[diff[0]].isalpha():
166
+ jl=jalpha
167
+ l=alpha
168
+ else:
169
+ jl=jnumber
170
+ l=number
171
+ i=0
172
+ while i<len(jl) and p2[diff[0]]<l[i]: i+=1
173
+ jl.insert(i-1,j)
174
+ l.insert(i-1,p2[diff[0]])
175
+ self.link[k]=jalpha+sorted(jnumber)+[k]
176
+ return
177
+
178
+ def print(self):
179
+ pri.Coding.white('\n'+f'Image sets found in path "{self.path}"')
180
+ for k in range(self.count):
181
+ pri.Coding.white(f'{k:2d}: '+f'{self.pattern[k]}'+'\t'+f'n img = {self.nimg[k]} '+'\t'+f'{self.ind_in[k]}-{self.ind_fin[k]}')
182
+ for j in self.link[k]:
183
+ pri.Coding.white(' '+f'{self.pattern[j]}')
184
+ pri.Coding.white(' ')
185
+
186
+ def genList(self,k,i,npairs,step):
187
+ if k>=self.count:
188
+ pri.Error.red(f'Trying to access a non-existing index position ({k}) in the image set structure ({self.count} sets identified)')
189
+ return []
190
+ f=i+npairs*step
191
+ if k>-1: 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
+ self.FlagInGui=False
1608
+
1609
+ font=self.font()
1610
+ font.setItalic(True)
1611
+ self.pixmap_edit=QPixmap(icons_path+'editing.png')
1612
+
1613
+ self.main_layout=QVBoxLayout(self)
1614
+ self.main_layout.setContentsMargins(0,self.main_layout_spacing,0,self.main_layout_spacing)
1615
+ self.main_layout.setSpacing(self.main_layout_spacing)
1616
+
1617
+ self.setLayout(self.main_layout)
1618
+
1619
+ self.w_spin=QWidget()
1620
+ self.spin_layout = QHBoxLayout()
1621
+ self.spin_layout.setContentsMargins(0,0,0,0)
1622
+ self.spin_layout.setSpacing(self.spin_spacing)
1623
+ self.w_spin.setLayout(self.spin_layout)
1624
+
1625
+ self.spin_layout.addItem(QSpacerItem(0, self.spin_height, QSizePolicy.Expanding, QSizePolicy.Minimum))
1626
+
1627
+ self.label_img = QLabel('#:',font=font)
1628
+ self.spin_layout.addWidget(self.label_img)
1629
+ self.spin_img = MyQSpin(self)
1630
+ self.spin_img.setObjectName('spin_img')
1631
+ self.spin_img.setMinimumSize(self.spin_min_width,self.spin_height)
1632
+ self.spin_img.setMaximumHeight(self.spin_height)
1633
+ self.spin_img.setMinimum(0)
1634
+ self.spin_layout.addWidget(self.spin_img)
1635
+ self.label_max_img = QLabel('/0',font=font)
1636
+ self.label_max_img.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
1637
+ self.spin_layout.addWidget(self.label_max_img)
1638
+ self.spin_layout.addItem(QSpacerItem(self.spin_spacer_width, self.spin_height, QSizePolicy.Minimum, QSizePolicy.Minimum))
1639
+
1640
+ self.label_frame = QLabel('frame:',font=font)
1641
+ self.spin_layout.addWidget(self.label_frame)
1642
+ self.spin_frame = MyQSpin(self)
1643
+ self.spin_frame.setObjectName('spin_frame')
1644
+ self.spin_frame.setMinimumSize(self.spin_min_width,self.spin_height)
1645
+ self.spin_frame.setMaximumHeight(self.spin_height)
1646
+ self.spin_frame.setMinimum(1)
1647
+ self.spin_frame.setMaximum(2)
1648
+ self.spin_layout.addWidget(self.spin_frame)
1649
+ self.spin_layout.addItem(QSpacerItem(self.spin_spacer_width, self.spin_height, QSizePolicy.Minimum, QSizePolicy.Minimum))
1650
+
1651
+ self.label_cam = QLabel('cam:',font=font)
1652
+ self.spin_layout.addWidget(self.label_cam)
1653
+ self.spin_cam = MyQSpin(self)
1654
+ self.spin_cam.setObjectName('spin_cam')
1655
+ self.spin_cam.setMinimumSize(self.spin_min_width,self.spin_height)
1656
+ self.spin_cam.setMaximumHeight(self.spin_height)
1657
+ self.spin_cam.setMinimum(1)
1658
+ self.spin_layout.addWidget(self.spin_cam)
1659
+ self.label_max_cam = QLabel('/',font=font)
1660
+ self.label_max_cam.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
1661
+ self.spin_layout.addWidget(self.label_max_cam)
1662
+ self.spin_ncam = MyQSpin(self)
1663
+ self.spin_ncam.setObjectName('spin_ncam')
1664
+ self.spin_ncam.setMinimumSize(self.spin_height,self.spin_height)
1665
+ self.spin_ncam.setMaximumHeight(self.spin_height)
1666
+ self.spin_ncam.setMinimum(1)
1667
+ self.spin_ncam.setMaximum(99)
1668
+ self.spin_ncam.setButtonSymbols(QSpinBox.ButtonSymbols.NoButtons)
1669
+ self.spin_ncam.setToolTip('Number of cameras')
1670
+ self.spin_ncam.setStatusTip('Number of cameras')
1671
+ self.spin_layout.addWidget(self.spin_ncam)
1672
+
1673
+ self.spinSetup()
1674
+
1675
+ self.w_button=QWidget(self)
1676
+ self.button_layout = QHBoxLayout()
1677
+ self.button_layout.setContentsMargins(0,0,0,0)
1678
+ self.button_layout.setSpacing(self.button_spacing)
1679
+ self.w_button.setLayout(self.button_layout)
1680
+
1681
+ self.label_updating_import=QLabel('')
1682
+ self.label_updating_import.setFixedSize(self.button_size,self.button_size)
1683
+ self.updating_import_gif = QMovie(u""+ icons_path +"updating_import.gif")
1684
+ self.updating_import_gif.setScaledSize(self.label_updating_import.size())
1685
+ #self.ui.label_updating_import.setScaledContents(True)
1686
+ self.updating_import_gif.start()
1687
+ self.label_updating_import.setMovie(self.updating_import_gif)
1688
+ self.label_updating_import.setVisible(False)
1689
+ self.button_layout.addWidget(self.label_updating_import)
1690
+
1691
+ self.icon_label=QLabel('')
1692
+ self.icon_label.setFixedSize(self.button_size,self.button_size)
1693
+ self.icon_label.setScaledContents(True)
1694
+ self.icon_label.setPixmap(self.pixmap_edit)
1695
+
1696
+ self.label = QLabel(self.name,font=font)
1697
+ self.label.setMinimumHeight(self.button_size)
1698
+ self.button_layout.addWidget(self.icon_label,alignment=Qt.AlignmentFlag.AlignLeft)
1699
+ self.button_layout.addWidget(self.label, alignment=Qt.AlignmentFlag.AlignLeft)
1700
+
1701
+ self.button_layout.addItem(QSpacerItem(self.label_spacing, self.button_size, QSizePolicy.Minimum, QSizePolicy.Minimum))
1702
+
1703
+ bs=self.button_size
1704
+ self.bShortCuts={}
1705
+ for icon_name in self.icons_names:
1706
+ if '-' in icon_name:
1707
+ self.button_layout.addItem(QSpacerItem(bs, bs, QSizePolicy.Expanding, QSizePolicy.Minimum))
1708
+ elif '|' in icon_name:
1709
+ separator = QFrame()
1710
+ separator.setFrameShape(QFrame.VLine)
1711
+ separator.setFrameShadow(QFrame.Sunken)
1712
+ setattr(self,'sep_'+icon_name[1:],separator)
1713
+ self.button_layout.addWidget(separator)
1714
+ else:
1715
+ b = QToolButton(self)
1716
+ b.setObjectName('button_'+icon_name)
1717
+ if len(self.buttons[icon_name])>2:
1718
+ icon_file=icons_path+self.buttons[icon_name][2]+'.png'
1719
+ else:
1720
+ icon_file=icons_path+icon_name+'.png'
1721
+ b.setIcon(QIcon(icon_file))
1722
+ b.setFixedSize(bs, bs) # Impostare la dimensione quadrata
1723
+ b.setIconSize(QSize(bs-4,bs-4))
1724
+ tip=self.buttons[icon_name][0]
1725
+ if len(self.buttons[icon_name])>1:
1726
+ if self.buttons[icon_name][1]:
1727
+ bshortcut=QShortcut(QCoreApplication.translate("Image Tree",self.buttons[icon_name][1], None),self)
1728
+ def buttonClick(b:QToolButton):
1729
+ if b.isVisible() and b.isEnabled() and self.imTree.hasFocus(): b.click()
1730
+ #bshortcut.activated.connect(lambda but=b:buttonClick(but))
1731
+ tip+=' ('+bshortcut.key().toString(QKeySequence.NativeText)+')'
1732
+ self.bShortCuts[bshortcut.key()[0]]=lambda but=b:buttonClick(but)
1733
+ b.setToolTip(tip)
1734
+ b.setStatusTip(tip)
1735
+ setattr(self,'button_'+icon_name,b)
1736
+ if hasattr(self,'button_'+icon_name+'_action'):
1737
+ b.clicked.connect(getattr(self,'button_'+icon_name+'_action'))
1738
+ if icon_name=='discard_changes':
1739
+ self.spin_layout.insertWidget(0,b)
1740
+ elif icon_name=='confirm_changes':
1741
+ self.spin_layout.insertWidget(1,b)
1742
+ elif icon_name=='up':
1743
+ self.spin_layout.insertWidget(3,b)
1744
+ elif icon_name=='down':
1745
+ self.spin_layout.insertWidget(4,b)
1746
+ else:
1747
+ self.button_layout.addWidget(b)
1748
+
1749
+ self.infoLabel = QLabel('',self)
1750
+ self.infoLabel.setFixedWidth(self.spin_height-8)
1751
+ self.infoLabel.setFixedHeight(self.spin_height-8)
1752
+ self.infoLabel.setScaledContents(True)
1753
+ self.progressBar = QProgressBar(self)
1754
+ self.progressBar.setFixedWidth(self.spin_height*3)
1755
+ self.progressBar.setFixedHeight(self.spin_height-8)
1756
+ self.spin_layout.insertWidget(2,self.progressBar)
1757
+ self.spin_layout.insertWidget(2,self.infoLabel)
1758
+
1759
+ self.tree_layout=QHBoxLayout()
1760
+ self.tree_layout.setContentsMargins(0,0,0,0)
1761
+ self.tree_layout.setSpacing(0)
1762
+
1763
+ self.imageNumbTree=ImageNumberTree()
1764
+ self.imageNumbTree.setVisible(True)
1765
+ self.imageNumbTree.setFrameShape(QFrame.NoFrame)
1766
+
1767
+ self.imTrees=[GlobalImageTree()]
1768
+ self.setAsynImTree(self.imTrees[0])
1769
+ self.imTrees[0].setObjectName('GlobalImageTree')
1770
+ self.imTrees[0].setFrameShape(QFrame.NoFrame)
1771
+ self.imTree=self.imTrees[0]
1772
+ self.imList_old=[[[]]]
1773
+ self.imEx_old=[[[]]]
1774
+ self.tree_layout.addWidget(self.imageNumbTree)
1775
+ self.tree_layout.addWidget(self.imTree)
1776
+ #self.enableParent=lambda:None
1777
+ #self.disableParent=lambda:None
1778
+
1779
+ self.button_edit_list:QPushButton
1780
+ self.button_edit_list.setCheckable(True)
1781
+ self.button_edit_list.setChecked(False)
1782
+ self.indTree=0
1783
+ self.treeSetup()
1784
+
1785
+ self.main_layout.addWidget(self.w_spin)
1786
+ self.main_layout.addWidget(self.w_button)
1787
+ self.main_layout.addLayout(self.tree_layout)
1788
+ self.main_layout.addItem(QSpacerItem(0,0,QSizePolicy.Policy.Minimum,QSizePolicy.Policy.Expanding))
1789
+
1790
+ if __name__ == "__main__":
1791
+ self.button_setList=QPushButton('Reset list (debug)')
1792
+ def resetList():
1793
+ imp,iml,ime=imSet.genListsFromIndex(kc,i,npairs,step,ncam)
1794
+ self.setLists(imp,iml,ime)
1795
+ self.button_setList.clicked.connect(resetList)
1796
+ self.button_setList.setMaximumHeight(30)
1797
+ self.setList_layout = QHBoxLayout()
1798
+ self.setList_layout.addWidget(self.button_setList)
1799
+ self.setList_layout.addItem(QSpacerItem(0, self.spin_height, QSizePolicy.Expanding, QSizePolicy.Minimum))
1800
+ self.main_layout.insertLayout(0,self.setList_layout)
1801
+ stretches=[0,0,0,1,0]
1802
+ else:
1803
+ stretches=[0,0,1,0]
1804
+ for k,s in enumerate(stretches):
1805
+ self.main_layout.setStretch(k,s)
1806
+
1807
+ #self.nullList()
1808
+ self.w_spin.setVisible(self.FlagSpinButtons)
1809
+ self.w_button.setVisible(self.FlagSpinButtons)
1810
+
1811
+ self.FlagAlreadyDisabled=False
1812
+ self.FlagEnabled=[]
1813
+ self.disabledChildren=[]
1814
+
1815
+ self.setWidgetTabOrder()
1816
+ self.imTree.setVisible(True)
1817
+ self.imTree.keyPressEvent=lambda e: self.treeKeyPressEvent(e)
1818
+ self.imTree.installEventFilter(self)
1819
+
1820
+ self.FlagSettingPar=None
1821
+
1822
+ def eventFilter(self, obj, event):
1823
+ if event.type() == QEvent.Type.ShortcutOverride:
1824
+ event.accept()
1825
+ return True
1826
+ return super().eventFilter(obj, event)
1827
+
1828
+ def treeKeyPressEvent(self,event):
1829
+ #pri.Callback.green(f"Pressed key sequence: {QKeySequence(Qt.KeyboardModifier(event.modifiers()) | Qt.Key(event.key())).toString()}")
1830
+ for ksc,f in self.bShortCuts.items():
1831
+ if ksc is None: continue
1832
+ if type(ksc)!=list: ksc=[ksc]
1833
+ for k in ksc:
1834
+ if type(k)==QKeyCombination:
1835
+ if event.key()==k.key() and event.modifiers()==k.keyboardModifiers():
1836
+ f()
1837
+ return True # Impedisce al widget di ricevere l'evento
1838
+ else:
1839
+ if event.key()==k and not event.modifiers():
1840
+ f()
1841
+ return True # Impedisce al widget di ricevere l'evento
1842
+
1843
+ def setAsynImTree(self,imTree: AsynPaIRSTree):
1844
+ imTree.setNumTree(self.imageNumbTree)
1845
+ imTree.disableButtons=self.disableButtons
1846
+ imTree.progressBar=self.progressBar
1847
+ imTree.infoLabel=self.infoLabel
1848
+ imTree.signals.copyCutItems.connect(self.copy_cut_action_future)
1849
+ imTree.signals.pastedItems.connect(self.button_paste_above_below_action_future)
1850
+
1851
+ def nullList(self):
1852
+ nimg=0
1853
+ imList0=[[['' for _ in range(nimg)] for _ in range(2)] for _ in range(self.imTree.ncam)]
1854
+ imEx0=[[[False for _ in range(nimg)] for _ in range(2)] for _ in range(self.imTree.ncam)]
1855
+ self.setLists(self.imTree.path,imList0,imEx0,FlagAsync=False)
1856
+ self.imTree.signals.updateLists.emit()
1857
+
1858
+ def disableButtons(self,Flag=True):
1859
+ self.progressBar.setVisible(Flag)
1860
+ #self.progressBar.setEnabled(Flag) #commenta se self.disableTab(Flag) è commentato
1861
+ self.infoLabel.setPixmap(QPixmap())
1862
+ self.infoLabel.setVisible(Flag)
1863
+ #self.infoLabel.setEnabled(Flag) #commenta se self.disableTab(Flag) è commentato
1864
+ #self.disableTab(Flag)
1865
+ #return
1866
+ self.w_button.setEnabled(not Flag)
1867
+ for c in self.w_spin.findChildren(QWidget):
1868
+ c:QWidget
1869
+ c.setEnabled(not Flag)
1870
+ self.updateSpins()
1871
+ self.setButtonLayout(True)
1872
+ self.progressBar.setEnabled(Flag) #commenta se self.disableTab(Flag) è decommentato
1873
+ self.infoLabel.setEnabled(Flag) #commenta se self.disableTab(Flag) è decommentato
1874
+
1875
+ def disableTab(self,Flag=True):
1876
+ #evita TABpar.FlagSettingPar=Flag così che sai dove FlagSettingPar è settato True o False
1877
+ if Flag:
1878
+ self.FlagSettingPar=TABpar.FlagSettingPar
1879
+ TABpar.FlagSettingPar=True
1880
+ else:
1881
+ if self.FlagSettingPar is not None:
1882
+ TABpar.FlagSettingPar=self.FlagSettingPar
1883
+ for imTree in self.imTrees:
1884
+ imTree.FlagSettingPar=Flag
1885
+ self.label_updating_import.setVisible(Flag)
1886
+ return
1887
+
1888
+ def treeSetup(self):
1889
+ self.imTree.itemSelectionChanged.connect(self.setButtonLayout)
1890
+ #self.imTree.signals.updateLists.connect(self.setButtonLayout)
1891
+ self.imTree.contextMenuEvent=lambda e: self.treeContextMenuEvent(self.imTree,e)
1892
+ self.imTree.keyPressEvent=lambda e: self.treeKeyPressEvent(e)
1893
+ self.imTree.installEventFilter(self)
1894
+ self.imTree.signals.updateTree.connect(self.updateSpins)
1895
+ self.imTree.signals.updateTree.connect(self.resizeHeader)
1896
+ self.imTree.signals.updateTree.connect(self.setButtonLayout)
1897
+ return_action=lambda: self.imTree.setFocus()
1898
+ self.spin_img.addfuncreturn['move2Tree']=return_action
1899
+ self.spin_cam.addfuncreturn['move2Tree']=return_action
1900
+ self.spin_frame.addfuncreturn['move2Tree']=return_action
1901
+
1902
+ def spinSetup(self):
1903
+ self.spin_img.setup()
1904
+ self.spin_cam.setup()
1905
+ self.spin_ncam.setup()
1906
+ self.spin_frame.setup()
1907
+
1908
+ self.spin_img.valueChanged.connect(lambda: self.spins_action(self.spin_img))
1909
+ self.spin_cam.valueChanged.connect(lambda: self.spins_action(self.spin_cam))
1910
+ self.spin_frame.valueChanged.connect(lambda: self.spins_action(self.spin_frame))
1911
+ self.spin_ncam.addfuncreturn['ncam']=self.spin_ncam_action
1912
+ self.spin_ncam.addfuncout['ncam']=self.spin_ncam_action
1913
+
1914
+ def setButtonLayout(self,FlagPostponedSignal=False):
1915
+ self.setButtonLayout_List()
1916
+ self.setButtonLayout_Item(FlagPostponedSignal)
1917
+
1918
+ def setButtonLayout_List(self):
1919
+ if not self.FlagSpinButtons: return
1920
+ #buttons changes when list changes occur
1921
+ FlagWarn=len(self.imTree.warns)>0
1922
+ FlagSingleTree=type(self.imTree)==SingleImageTree
1923
+ FlagItems=self.imTree.topLevelItemCount()>0
1924
+ FlagWrapUnWrap=not FlagSingleTree and self.imTree.ncam>1
1925
+
1926
+ self.button_up.setVisible(FlagItems)
1927
+ self.button_down.setVisible(FlagItems)
1928
+
1929
+ self.button_scan_list.setVisible(not FlagSingleTree)
1930
+ self.sep_scan.setVisible(not FlagSingleTree)
1931
+ self.button_warning.setVisible(FlagWarn)
1932
+ self.button_cut_warnings.setVisible(FlagWarn)
1933
+ self.sep_warning.setVisible(FlagWarn and not FlagSingleTree)
1934
+
1935
+ self.button_confirm_changes.setVisible(FlagSingleTree)
1936
+ self.button_discard_changes.setVisible(FlagSingleTree)
1937
+
1938
+ self.button_edit_list.setVisible(not FlagSingleTree)
1939
+ self.button_write_list.setVisible(not FlagSingleTree)
1940
+ self.button_write_list.setEnabled(FlagItems)
1941
+ self.button_read_list.setVisible(not FlagSingleTree)
1942
+ self.sep_read_list.setVisible(not FlagSingleTree)
1943
+
1944
+ self.button_read.setVisible(FlagSingleTree)
1945
+ self.sep_read.setVisible(FlagSingleTree)
1946
+
1947
+ self.button_wrap_items.setVisible(FlagWrapUnWrap)
1948
+ self.button_unwrap_items.setVisible(FlagWrapUnWrap)
1949
+ self.sep_wrap.setVisible(FlagWrapUnWrap)
1950
+ self.button_sort.setEnabled(FlagItems)
1951
+ self.button_sort_reversed.setEnabled(FlagItems)
1952
+
1953
+ self.button_sort.setVisible(FlagSingleTree)
1954
+ self.button_sort_reversed.setVisible(FlagSingleTree)
1955
+ self.sep_sort.setVisible(FlagSingleTree)
1956
+
1957
+ self.sep_copy.setVisible(not FlagSingleTree)
1958
+ self.button_clean.setVisible(not FlagSingleTree)
1959
+ self.button_clean.setEnabled(FlagItems)
1960
+
1961
+ def setButtonLayout_Item(self,FlagPostponedSignal=False):
1962
+ if not self.FlagSpinButtons: return
1963
+ #buttons changes when item selection changes occur
1964
+ FlagSelected=len(self.imTree.selectedItems())>0
1965
+ FlagCuttedItems=len(self.imTree.cutted_itemList)>0
1966
+ self.button_wrap_items.setEnabled(FlagSelected)
1967
+ self.button_unwrap_items.setEnabled(FlagSelected)
1968
+
1969
+ self.button_copy.setEnabled(FlagSelected)
1970
+ self.button_cut.setEnabled(FlagSelected)
1971
+ self.button_paste_above.setEnabled(FlagCuttedItems)
1972
+ self.button_paste_below.setEnabled(FlagCuttedItems)
1973
+
1974
+ self.imTree.FlagSetting=True
1975
+ img,cam,frame=self.imTree.indexSelection()
1976
+
1977
+ if FlagPostponedSignal:
1978
+ FlagSettingPar=TABpar.FlagSettingPar
1979
+ TABpar.FlagSettingPar=True
1980
+ FlagSignal=self.spin_img.value()!=img or self.spin_cam.value()!=cam or self.spin_frame.value()!=frame
1981
+ self.spin_img.setValue(img)
1982
+ self.spin_cam.setValue(cam)
1983
+ self.spin_frame.setValue(frame)
1984
+ self.spin_ncam.setValue(self.imTree.ncam)
1985
+ if FlagPostponedSignal: TABpar.FlagSettingPar=FlagSettingPar
1986
+
1987
+ self.imTree.FlagSetting=False
1988
+ if FlagSignal and not TABpar.FlagSettingPar: self.signals.selection.emit()
1989
+
1990
+ def setLists(self,path,imList:list,imEx:list,selection=[],FlagAsync=True,FlagOnlyPrepare=False):
1991
+ self.imTree.clearSelection()
1992
+ #self.imTree.setVisible(False)
1993
+ if self.button_edit_list.isChecked():
1994
+ indTree=self.indTree
1995
+ self.button_edit_list.setChecked(False)
1996
+ self.button_edit_list_action(FlagScan=False)
1997
+ else: indTree=0
1998
+ self.imTrees[0].path=path
1999
+ self.imTrees[0].ncam=ncam=len(imList)
2000
+ nimg=len(imList[0][0])
2001
+ self.imTrees[0].imList=copy.deepcopy(imList)
2002
+ self.imTrees[0].imEx=copy.deepcopy(imEx)
2003
+ self.imTrees[0].setImListEx()
2004
+ self.imTrees[0].disableTab=lambda flag: self.imTrees[0].setEnabled(not flag)
2005
+ countTree=1
2006
+ for c in range(ncam):
2007
+ for f in range(2):
2008
+ countTree+=1
2009
+ if len(self.imTrees)<countTree:
2010
+ imTree=SingleImageTree(self)
2011
+ imTree.setFrameShape(QFrame.NoFrame)
2012
+ self.imTrees.append(imTree)
2013
+ self.tree_layout.addWidget(imTree)
2014
+ else:
2015
+ imTree=self.imTrees[countTree-1]
2016
+ imTree.c=c
2017
+ imTree.f=f
2018
+ imTree.path=path
2019
+ imTree.ncam=ncam
2020
+ imTree.nimg=nimg
2021
+ imTree.parentImTree=self.imTrees[0]
2022
+ imTree.disableTab=lambda flag: imTree.setEnabled(not flag)
2023
+ [self.tree_layout.removeWidget(i) for i in self.imTrees[countTree:]]
2024
+
2025
+ if not FlagOnlyPrepare:
2026
+ self.imTrees[0].setLists(selection,FlagAsync)
2027
+ if indTree:
2028
+ self.button_edit_list.setChecked(True)
2029
+ self.button_edit_list_action(FlagScan=False)
2030
+
2031
+ def updateImList(self):
2032
+ #for imTree in self.imTrees:
2033
+ for k,imTree in enumerate(self.imTrees[1:]):
2034
+ imTree:SingleImageTree
2035
+ c=int(k/2)
2036
+ f=k-c*2
2037
+ if c>=self.imTrees[0].ncam: break
2038
+ imTree.imList=self.imTrees[0].imList[c][f]
2039
+ imTree.imEx=self.imTrees[0].imEx[c][f]
2040
+ imTree.itemList[0]=imTree.imList
2041
+ imTree.itemList[1]=imTree.imEx
2042
+ imTree.ncam=self.imTrees[0].ncam
2043
+ if self.initTree[k+1]: imTree.nimg=self.imTrees[0].nimg
2044
+ if self.initTree[self.indTree] and self.indTree:
2045
+ self.initTree[self.indTree]=False
2046
+ imTree=self.imTrees[self.indTree]
2047
+ imTree.setLists([self.spin_img.value(),1,1])
2048
+
2049
+ def button_up_action(self):
2050
+ self.imTree.clearSelection()
2051
+ item=self.imTree.topLevelItem(0)
2052
+ self.imTree.setCurrentItem(item)
2053
+ self.imTree.setSelectedQuickly([item],True)
2054
+ self.imTree.scrollToTop()
2055
+
2056
+ def button_down_action(self):
2057
+ self.imTree.clearSelection()
2058
+ item=self.imTree.topLevelItem(self.imTree.topLevelItemCount()-1)
2059
+ self.imTree.setCurrentItem(item)
2060
+ self.imTree.setSelectedQuickly([item],True)
2061
+ self.imTree.scrollToBottom()
2062
+
2063
+ def button_scan_list_action(self):
2064
+ #self.imTree.setVisible(False)
2065
+ self.imTree.scanLists()
2066
+ self.imTree.resetImNumber()
2067
+ #self.imTree.setVisible(True)
2068
+ self.imTree.signals.updateLists.emit()
2069
+
2070
+ def button_cut_warnings_action(self):
2071
+ items=[self.imTree.topLevelItem(k) for k in self.imTree.warns]
2072
+ indexes=[self.imTree.indexOfTopLevelItem(i) for i in items]
2073
+ self.copy_cut_action(items,indexes,True)
2074
+
2075
+ def button_warning_action(self):
2076
+ i=self.spin_img.value()-1
2077
+ if i in self.imTree.warns:
2078
+ k=self.imTree.warns.index(i)
2079
+ k=k+1 if k<len(self.imTree.warns)-1 else 0
2080
+ else: k=0
2081
+ item=self.imTree.topLevelItem(self.imTree.warns[k])
2082
+ self.imTree.setCurrentItem(item)
2083
+ self.imTree.setSelectedQuickly([item],True)
2084
+
2085
+ def button_edit_list_action(self,FlagScan=True,FlagSignal=False):
2086
+ self.setUpdatesEnabled(False)
2087
+ indTree_old=self.indTree
2088
+ self.initTree=[True for _ in self.imTrees]
2089
+ if not self.button_edit_list.isChecked():
2090
+ self.indTree=0
2091
+ self.disableTab(False)
2092
+ else:
2093
+ if FlagScan: self.imTrees[0].scanLists(FlagChange=False)
2094
+ self.disableTab(True)
2095
+ self.imList_old=copy.deepcopy(self.imTree.imList)
2096
+ self.imEx_old=copy.deepcopy(self.imTree.imEx)
2097
+ c=self.spin_cam.value()-1
2098
+ f=self.spin_frame.value()-1
2099
+ self.indTree=1+c*2+f
2100
+
2101
+ self.imTree=self.imTrees[self.indTree]
2102
+ self.setAsynImTree(self.imTree)
2103
+ self.updateImList()
2104
+ self.treeSetup()
2105
+ self.setButtonLayout(FlagPostponedSignal=FlagSignal)
2106
+
2107
+ self.spins_action()
2108
+ if FlagSignal and not self.signalsBlocked():
2109
+ self.imTree.signals.updateLists.emit()
2110
+ if indTree_old!=self.indTree:
2111
+ self.imTrees[indTree_old].setVisible(False)
2112
+ self.imTree.setVisible(True)
2113
+ self.setUpdatesEnabled(True)
2114
+
2115
+ def resizeHeader(self):
2116
+ self.imTree.resizeHeader()
2117
+ return
2118
+
2119
+ def button_confirm_changes_action(self):
2120
+ nimg=max([imTree.nimg for imTree in self.imTrees[1:]])
2121
+
2122
+ for k,imTree in enumerate(self.imTrees[1:]):
2123
+ imTree:SingleImageTree
2124
+ if imTree.nimg<nimg:
2125
+ imTree.imList.extend(['']*(nimg-imTree.nimg))
2126
+ imTree.imEx.extend([False]*(nimg-imTree.nimg))
2127
+ imTree.nimg=nimg
2128
+ self.imTrees[0].nimg=nimg
2129
+
2130
+ self.imTrees[0].scanLists(FlagChange=False)
2131
+ self.imTrees[0].setLists()
2132
+ self.button_edit_list.setChecked(False)
2133
+ self.button_edit_list_action(FlagScan=False,FlagSignal=True)
2134
+ return
2135
+
2136
+ def button_discard_changes_action(self):
2137
+ self.imTrees[0].imList=self.imList_old
2138
+ self.imTrees[0].imEx=self.imEx_old
2139
+ self.imTrees[0].setImListEx()
2140
+ self.imTrees[0].resetImNumber()
2141
+ self.imTrees[0].scanLists(FlagChange=True)
2142
+ self.button_edit_list.setChecked(False)
2143
+ self.button_edit_list_action(FlagScan=False,FlagSignal=True)
2144
+ return
2145
+
2146
+ def spins_action(self,spin:QSpinBox=None):
2147
+ if self.imTree.FlagSetting: return
2148
+ self.updateSpins()
2149
+ r=self.spin_img.value()-1
2150
+ c=self.spin_cam.value()-1
2151
+ f=self.spin_frame.value()-1
2152
+ if not self.button_edit_list.isChecked(): #global tree
2153
+ self.label.setText(self.name)
2154
+ self.label.setStyleSheet('')
2155
+ self.icon_label.setVisible(False)
2156
+ self.imTree.spinSelection([r+1,c+1,f+1])
2157
+ else: #single tree
2158
+ self.label.setText(self.name + f' (cam: {c+1}, frame: {f+1})')
2159
+ self.label.setStyleSheet('QLabel{color:rgba(0, 116, 255, 1)}')
2160
+ self.icon_label.setVisible(True)
2161
+ indTree=1+c*2+f
2162
+ if indTree!=self.indTree:
2163
+ self.imTree.setVisible(False)
2164
+ self.indTree=indTree
2165
+ self.imTree=self.imTrees[self.indTree]
2166
+ self.setAsynImTree(self.imTree)
2167
+ self.updateImList()
2168
+ self.imTree.setVisible(True)
2169
+ self.treeSetup()
2170
+ self.updateSpins()
2171
+ r=self.spin_img.value()-1
2172
+ item = self.imTree.topLevelItem(r)
2173
+ self.imTree.indexFromItem(item)
2174
+ #self.imTree.clearSelection()
2175
+ self.imTree.setCurrentItem(item, 1)
2176
+ #if spin: spin.setFocus()
2177
+
2178
+ def spin_ncam_action(self):
2179
+ ncam=self.spin_ncam.value()
2180
+ if self.imTree.ncam>ncam:
2181
+ del self.imTree.imList[ncam:]
2182
+ del self.imTree.imEx[ncam:]
2183
+ elif self.imTree.ncam<ncam:
2184
+ for c in range(self.imTree.ncam,ncam):
2185
+ self.imTree.imList[c:c]=[[['' for _ in range(self.imTree.nimg)] for _ in range(2)]]
2186
+ self.imTree.imEx[c:c]=[[[False for _ in range(self.imTree.nimg)] for _ in range(2)]]
2187
+ else: return
2188
+ if self.imTree.ncam!=ncam:
2189
+ self.imTree.ncam=ncam
2190
+ self.imTree.setImListEx()
2191
+ self.imTree.setLists()
2192
+
2193
+ def updateSpins(self):
2194
+ FlagSettingPar=TABpar.FlagSettingPar
2195
+ TABpar.FlagSettingPar=True
2196
+ self.spin_img.setMinimum(min([self.imTree.nimg,0]))
2197
+ self.spin_img.setMaximum(max([self.imTree.nimg,0]))
2198
+ self.spin_img.setToolTip(f'Current image. Number of images: {formatNumber(self.spin_img,self.spin_img.maximum())}')
2199
+ self.spin_img.setStatusTip(self.spin_img.toolTip())
2200
+ if self.spin_img.maximum(): self.label_max_img.setText('/'+self.spin_img.textFromValue(self.spin_img.maximum()))
2201
+ else: self.label_max_img.setText('')
2202
+ self.label_max_img.adjustSize()
2203
+ self.spin_img.setMinimumWidth(self.label_max_img.width()+self.spin_height)
2204
+ self.spin_img.setEnabled(self.imTree.nimg>1)
2205
+
2206
+ self.spin_cam.setMinimum(min([self.imTree.ncam,1]))
2207
+ self.spin_cam.setMaximum(self.imTree.ncam)
2208
+ self.spin_cam.setToolTip(f'Current camera. Number of cameras: {formatNumber(self.spin_cam,self.spin_cam.maximum())}')
2209
+ self.spin_cam.setStatusTip(self.spin_cam.toolTip())
2210
+ FlagSingleTree=type(self.imTree)==SingleImageTree
2211
+ self.spin_cam.setEnabled(self.imTree.ncam>1 and (self.imTree.nimg>1 or FlagSingleTree))
2212
+ self.spin_ncam.setEnabled(not FlagSingleTree and self.FlagCam and not self.FlagInGui)
2213
+
2214
+ self.spin_frame.setToolTip(f'Current frame. Number of frames: {self.spin_frame.maximum()}')
2215
+ self.spin_frame.setStatusTip(self.spin_frame.toolTip())
2216
+ self.spin_frame.setEnabled(self.imTree.nimg>1 or FlagSingleTree)
2217
+ TABpar.FlagSettingPar=FlagSettingPar
2218
+
2219
+ def buttonActionWrapper(self,fun=lambda:None):
2220
+ FlagSettingPar=TABpar.FlagSettingPar
2221
+ TABpar.FlagSettingPar=True
2222
+ fun()
2223
+ TABpar.FlagSettingPar=FlagSettingPar
2224
+ self.imTree.signals.updateLists.emit()
2225
+
2226
+ def button_wrap_unwrap_action(self,FlagWrap=True):
2227
+ #self.imTree.setVisible(False)
2228
+ selectedItems,indexes=self.imTree.selectedTopLevel()
2229
+ self.imTree.wrapUnWrapItems(selectedItems,indexes,FlagWrap)
2230
+
2231
+ def button_wrap_items_action(self):
2232
+ #self.buttonActionWrapper(lambda:
2233
+ self.button_wrap_unwrap_action(FlagWrap=True)
2234
+ #)
2235
+
2236
+ def button_unwrap_items_action(self):
2237
+ #self.buttonActionWrapper(lambda:
2238
+ self.button_wrap_unwrap_action(FlagWrap=False)
2239
+ #)
2240
+
2241
+ def button_copy_cut_action(self, FlagCut=False):
2242
+ #self.imTree.setVisible(False)
2243
+ selectedItems,indexes=self.imTree.selectedTopLevel()
2244
+ self.copy_cut_action(selectedItems,indexes,FlagCut)
2245
+
2246
+ def copy_cut_action(self,items,indexes,FlagCut):
2247
+ self.imTree.copyCutItems(items,indexes,FlagCut)
2248
+
2249
+ def copy_cut_action_future(self):
2250
+ FlagSignal=True
2251
+ self.updateSpins()
2252
+ self.setButtonLayout(FlagPostponedSignal=FlagSignal)
2253
+
2254
+ #self.imTree.setVisible(True)
2255
+ self.imTree.setFocus()
2256
+ if not self.signalsBlocked(): self.imTree.signals.updateLists.emit()
2257
+
2258
+ def button_copy_action(self):
2259
+ self.button_copy_cut_action(FlagCut=False)
2260
+
2261
+ def button_cut_action(self):
2262
+ #self.buttonActionWrapper(lambda:
2263
+ self.button_copy_cut_action(FlagCut=True)
2264
+ #)
2265
+
2266
+ def button_paste_above_below_action(self,FlagAbove=True):
2267
+ if not self.imTree.cutted_items: return
2268
+ #self.imTree.setVisible(False)
2269
+
2270
+ self.FlagResizeHeader=self.imTree.topLevelItemCount()==0
2271
+ self.FlagAbove=FlagAbove
2272
+ selectedItems,indexes=self.imTree.selectedTopLevel()
2273
+ #self.imTree.clearSelection()
2274
+ if FlagAbove:
2275
+ if selectedItems: row=indexes[0]
2276
+ else: row=0
2277
+ else:
2278
+ if selectedItems: row=indexes[-1]+1
2279
+ else: row=self.imTree.topLevelItemCount()
2280
+ self.imTree.pasteItems(self.imTree.cutted_items,row,FlagAbove)
2281
+
2282
+ def button_paste_above_below_action_future(self):
2283
+ self.updateSpins()
2284
+ self.setButtonLayout()
2285
+
2286
+ selectedItems,_=self.imTree.selectedTopLevel()
2287
+ #self.imTree.clearSelection()
2288
+ if self.FlagAbove:
2289
+ firstItemToScroll=selectedItems[0]
2290
+ lastItemToScroll=selectedItems[-1]
2291
+ else:
2292
+ firstItemToScroll=selectedItems[-1]
2293
+ lastItemToScroll=selectedItems[0]
2294
+
2295
+ self.imTree.scrollToItem(firstItemToScroll)
2296
+ self.imTree.scrollToItem(lastItemToScroll)
2297
+
2298
+ kin=self.imTree.indexOfTopLevelItem(firstItemToScroll)
2299
+ firstItemToScroll=self.imageNumbTree.topLevelItem(kin)
2300
+ kfin=self.imTree.indexOfTopLevelItem(lastItemToScroll)
2301
+ lastItemToScroll=self.imageNumbTree.topLevelItem(kfin)
2302
+ self.imageNumbTree.scrollToItem(firstItemToScroll)
2303
+ self.imageNumbTree.scrollToItem(lastItemToScroll)
2304
+
2305
+ #self.imageNumbTree.verticalScrollBar().setValue(self.imTree.verticalScrollBar().value())
2306
+ if self.FlagResizeHeader: self.resizeHeader()
2307
+ #self.imTree.setVisible(True)
2308
+ self.imTree.setFocus()
2309
+ self.imTree.signals.updateLists.emit()
2310
+
2311
+ def button_paste_above_action(self):
2312
+ #self.buttonActionWrapper(lambda:
2313
+ self.button_paste_above_below_action(FlagAbove=True)
2314
+ #)
2315
+
2316
+ def button_paste_below_action(self):
2317
+ #self.buttonActionWrapper(lambda:
2318
+ self.button_paste_above_below_action(FlagAbove=False)
2319
+ #)
2320
+
2321
+ def clean_action(self):
2322
+ #self.imTree.setVisible(False)
2323
+ self.setUpdatesEnabled(False)
2324
+ for imTree in self.imTrees:
2325
+ imTree.clear() #clean_tree(imTree)
2326
+ imTree.cleanLists()
2327
+ imTree.itemList.append(deep_duplicate(imTree.itemList[0]))
2328
+ self.nullList()
2329
+ self.setUpdatesEnabled(True)
2330
+ #self.imTree.setVisible(True)
2331
+
2332
+ def button_clean_action(self):
2333
+ #self.buttonActionWrapper(lambda:
2334
+ self.clean_action()
2335
+ #)
2336
+
2337
+ def button_read_action(self):
2338
+ filenames, _ = QFileDialog.getOpenFileNames(self,\
2339
+ "Select image files from the current directory", filter=text_filter, dir=self.imTree.path,\
2340
+ options=optionNativeDialog)
2341
+ self.imTree:SingleImageTree
2342
+ if filenames:
2343
+ self.FlagResizeHeader=self.imTree.topLevelItemCount()==0
2344
+ self.FlagAbove=False
2345
+ self.imTree.importLists(filenames)
2346
+
2347
+ def button_sort_action(self):
2348
+ self.imTree:SingleImageTree
2349
+ self.imTree.sortLists(reverse=False)
2350
+
2351
+ def button_sort_reversed_action(self):
2352
+ self.imTree:SingleImageTree
2353
+ self.imTree.sortLists(reverse=True)
2354
+
2355
+ def button_read_list_action(self):
2356
+ filename, _ = QFileDialog.getOpenFileName(self,\
2357
+ "Select image list file", dir=self.imTree.path,\
2358
+ options=optionNativeDialog)
2359
+ if filename:
2360
+ imList,imEx=self.read_imFile(filename)
2361
+ if imList:
2362
+ #self.buttonActionWrapper(lambda:
2363
+ TABpar.FlagSettingPar=True
2364
+ self.setLists(self.imTree.path,imList,imEx)
2365
+ TABpar.FlagSettingPar=False
2366
+ #)
2367
+
2368
+ def read_imFile(self,filename):
2369
+ path=self.imTree.path
2370
+ try:
2371
+ with open( filename, 'r') as file:
2372
+ lines = file.readlines()
2373
+ for i, line in enumerate(lines):
2374
+ pairs = line.strip().split(';')
2375
+ if len(pairs) < self.imTree.ncam:
2376
+ 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.")
2377
+ if not i:
2378
+ imList=[[['' for _ in range(len(lines))] for _ in range(2)] for _ in range(self.imTree.ncam)]
2379
+ imEx=[[[False for _ in range(len(lines))] for _ in range(2)] for _ in range(self.imTree.ncam)]
2380
+ for c in range(self.imTree.ncam):
2381
+ pair=pairs[c]
2382
+ items = pair.strip().split(',')
2383
+ if len(items) != 2:
2384
+ 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.")
2385
+ for f in range(2):
2386
+ imList[c][f][i]=items[f].strip()
2387
+ imEx[c][f][i]=os.path.exists(path+imList[c][f][i]) if imList[c][f][i] else False
2388
+ except FileNotFoundError:
2389
+ warningDialog(self,f"File '{filename}' not found at path '{path}'.")
2390
+ imList=imEx=[]
2391
+ except ValueError as e:
2392
+ warningDialog(self,f"Error: {e}")
2393
+ imList=imEx=[]
2394
+ return imList, imEx
2395
+
2396
+ def button_write_list_action(self):
2397
+ filename, _ = QFileDialog.getSaveFileName(self,"Select location and name of the image list file to save",
2398
+ dir=self.imTree.path, filter='*.txt',\
2399
+ options=optionNativeDialog)
2400
+ if filename:
2401
+ if filename[-4:]!='.txt': filename+='.txt' #per adattarlo al mac
2402
+ filename=myStandardRoot('{}'.format(str(filename)))
2403
+ self.write_imFile(filename,self.imTree.imList)
2404
+
2405
+ def write_imFile(self,filename,imList):
2406
+ try:
2407
+ with open(filename, 'w') as file:
2408
+ for i in range(len(imList[0][0])):
2409
+ row = '; '.join([f"{imList[j][0][i]}, {imList[j][1][i]}" for j in range(len(imList))]) + '\n'
2410
+ file.write(row)
2411
+ except Exception as e:
2412
+ pri.Error.red(f"Error writing to file: {e}\n{traceback.format_exc()}\n")
2413
+ warningDialog(self,f"Error writing to file: {e}")
2414
+
2415
+ def treeContextMenuEvent(self, tree:PaIRSTree, event):
2416
+ item=tree.currentItem()
2417
+ if not item: return
2418
+ menu=QMenu(tree)
2419
+ name=[]
2420
+ act=[]
2421
+ fun=[]
2422
+ for nb in self.icons_names:
2423
+ if '-' not in nb and '|' not in nb and nb not in self.excludedFromContextMenu:
2424
+ b:QPushButton=getattr(self,'button_'+nb)
2425
+ if b.isVisible() and b.isEnabled():
2426
+ if hasattr(self,'button_'+nb+'_action'):
2427
+ name.append(nb)
2428
+ act.append(QAction(b.icon(),toPlainText(b.toolTip().split('.')[0]),tree))
2429
+ menu.addAction(act[-1])
2430
+ callback=getattr(self,'button_'+nb+'_action')
2431
+ fun.append(callback)
2432
+ elif '|' in nb:
2433
+ if len(act): menu.addSeparator()
2434
+
2435
+ if len(act):
2436
+ action = menu.exec(tree.mapToGlobal(event.pos()))
2437
+ for nb,a,f in zip(name,act,fun):
2438
+ if a==action:
2439
+ f()
2440
+
2441
+ def setWidgetTabOrder(self):
2442
+ buttons=[getattr(self,'button_'+icon_name) if hasattr(self,'button_'+icon_name) else None for icon_name in self.icons_names]
2443
+ 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:]
2444
+ for i in range(len(widgets)-2):
2445
+ self.setTabOrder(widgets[i],widgets[i+1])
2446
+
2447
+ class CalibrationTree(PaIRSTree):
2448
+
2449
+ def mousePressEvent(self, event: QMouseEvent):
2450
+ TABpar.FlagSettingPar=True
2451
+ self.cursor_pos=event.globalPosition().toPoint()
2452
+ super().mousePressEvent(event)
2453
+ return
2454
+
2455
+ def mouseReleaseEvent(self, event: QMouseEvent):
2456
+ TABpar.FlagSettingPar=False
2457
+ self.itemSelectionChanged.emit()
2458
+ super().mouseReleaseEvent(event)
2459
+ return
2460
+
2461
+ def __init__(self, parent: QWidget=None,listDim=2,listDepth=1):
2462
+ super().__init__(parent,listDim,listDepth)
2463
+
2464
+ columns=["#","filename"]
2465
+ self.setColumnCount(len(columns))
2466
+ self.setHeaderLabels(columns)
2467
+ header=self.header()
2468
+ self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
2469
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
2470
+ header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
2471
+ self.setStyleSheet(f"QTreeView::item {{ height: {self.default_row_height} px; }}")
2472
+
2473
+ self.ncam=1
2474
+ self.calList=create_empty_list_of_dimension(1)
2475
+ self.calEx=create_empty_list_of_dimension(1)
2476
+
2477
+ expand_level(self.itemList,level=0,target_length=2)
2478
+ self.itemList[0]=self.calList
2479
+ self.itemList[1]=self.calEx
2480
+
2481
+ self.setVisible(True)
2482
+
2483
+ def importLists(self,filenames):
2484
+ def createItems():
2485
+ items=[None]*len(filenames)
2486
+ for k,filename in enumerate(filenames):
2487
+ FlagWarn=False
2488
+
2489
+ ex=os.path.exists(filename)
2490
+ self.nimg+=1
2491
+ item_data=[str(self.nimg),os.path.basename(filename) + f' ({os.path.dirname(filename)})']
2492
+ if not ex:
2493
+ FlagWarn=True
2494
+ item=QTreeWidgetItem(None,item_data)
2495
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
2496
+
2497
+ if FlagWarn:
2498
+ item.setIcon(0,self.icon_warning)
2499
+ item.setToolTip(0,'File missing')
2500
+ item.setStatusTip(0,'File missing')
2501
+ data=[FlagWarn,filename,ex]
2502
+ item.setData(0,Qt.ItemDataRole.UserRole,data)
2503
+
2504
+ self.calList.append(filename)
2505
+ self.calEx.append(ex)
2506
+ items[k]=item
2507
+ return (items)
2508
+
2509
+ items=createItems()
2510
+ self.insertItems2List(-1,items,False,NotImplementedError)
2511
+
2512
+ def setLists(self,selection=[]):
2513
+ calList=self.calList
2514
+ calEx=self.calEx
2515
+ self.warns=[]
2516
+
2517
+ pri.Time.blue(f'CalibrationTree: start setting list')
2518
+ self.nimg=nimg=len(calList)
2519
+ if self.nimg:
2520
+ while not calList[self.nimg-1]:
2521
+ calList.pop(self.nimg-1)
2522
+ calEx.pop(self.nimg-1)
2523
+ self.nimg-=1
2524
+ if self.nimg==0: break
2525
+ nimg=self.nimg
2526
+ self.FlagReset=True
2527
+
2528
+ def createItems():
2529
+ items=[None]*nimg
2530
+ for k in range(nimg):
2531
+ FlagWarn=False
2532
+
2533
+ filename=calList[k]
2534
+ ex=calEx[k]=os.path.exists(filename)
2535
+ item_data=[str(k+1),os.path.basename(filename) + f' ({os.path.dirname(filename)})']
2536
+ if not ex:
2537
+ FlagWarn=True
2538
+ item=QTreeWidgetItem(None,item_data)
2539
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
2540
+ items[k]=item
2541
+ item.setToolTip(1,item_data[1])
2542
+ item.setStatusTip(1,item_data[1])
2543
+
2544
+ if FlagWarn:
2545
+ item.setIcon(0,self.icon_warning)
2546
+ item.setToolTip(0,'File missing')
2547
+ item.setStatusTip(0,'File missing')
2548
+ self.warns.append(k)
2549
+ data=[not FlagWarn,filename,ex]
2550
+ item.setData(0,Qt.ItemDataRole.UserRole,data)
2551
+ return (items)
2552
+
2553
+ items=createItems()
2554
+ self.insertItems2List(-1,items,False,selection)
2555
+ pri.Time.blue(f'CalibrationTree: end setting list')
2556
+ return
2557
+
2558
+ class PaIRSTable(QTableWidget):
2559
+ cutted_itemList=[]
2560
+ cutted_items=[]
2561
+ deleted_itemList=[]
2562
+ deleted_items=[]
2563
+ margin_rect=10
2564
+
2565
+ class ImageTable_signals(QObject):
2566
+ updateTree=Signal()
2567
+ updateLists=Signal()
2568
+ createdItems=Signal(int,list,bool,list)
2569
+
2570
+ def __init__(self, parent: QWidget=None, listDim=1, listDepth=0):
2571
+ super().__init__(parent=parent)
2572
+ self.listDim=listDim
2573
+ self.listDepth=listDepth
2574
+ self.signals=self.ImageTable_signals()
2575
+
2576
+ self.setupRowBehaviour()
2577
+
2578
+ self.icon_warning = QIcon()
2579
+ self.icon_warning.addFile(u""+ icons_path +"warn.png", QSize(), QIcon.Normal, QIcon.Off)
2580
+ self.dragged_items=self.dragged_indexes=None
2581
+ self.hovered_item=None
2582
+ self.setAutoScroll(True)
2583
+ self.verticalScrollBarVal=self.verticalScrollBar().value()
2584
+ self.verticalScrollBar().setStyleSheet("""
2585
+ QTreeWidget {
2586
+ margin-bottom: 0px;
2587
+ }
2588
+ QTreeWidget::item {
2589
+ margin-bottom: 0px;
2590
+ }
2591
+ QTreeView {
2592
+ margin-bottom: 0px;
2593
+ }
2594
+ QScrollBar:horizontal{
2595
+ height: 15px;
2596
+ margin: 3px 0px 3px 0px;
2597
+ border: 1px transparent #2A2929;
2598
+ border-radius: 4px;
2599
+ background-color: transparent; /* #2A2929; */
2600
+ }
2601
+ QScrollBar::handle:horizontal{
2602
+ background-color: rgba(180,180,180,180); /* #605F5F; */
2603
+ min-width: 30px;
2604
+ border-radius: 4px;
2605
+ }
2606
+ QScrollBar:vertical{
2607
+ width: 15px;
2608
+ margin: 0px 3px 0px 3px;
2609
+ border: 1px transparent #2A2929;
2610
+ border-radius: 4px;
2611
+ background-color: transparent; /* #2A2929; */
2612
+ }
2613
+ QScrollBar::handle:vertical{
2614
+ background-color: rgba(180,180,180,180); /* #605F5F; */
2615
+ min-height: 30px;
2616
+ border-radius: 4px;
2617
+ }
2618
+ QScrollBar::add-line{
2619
+ border: none;
2620
+ background: none;
2621
+ }
2622
+
2623
+ QScrollBar::sub-line{
2624
+ border: none;
2625
+ background: none;
2626
+ }""")
2627
+ self.cursor_pos=self.cursor().pos()
2628
+
2629
+ self.pen = QPen(qRgba(127,227,255,0.9))
2630
+ self.pen.setWidth(3)
2631
+ #style="background-color: rgba(173,216,230,0.1); color: rgba(128,128,128,0.25);"
2632
+ #self.setStyleSheet(f"QTreeWidget::item:selected {{{style}}}")
2633
+ #self.setStyleSheet(f"QTreeWidget::item:selected:active {{{style}}}")
2634
+
2635
+ style = """
2636
+ QTreeWidget::item:selected:!active {
2637
+ background-color: rgba(0, 116, 255, 0.4);
2638
+ }
2639
+ QTreeWidget::item:selected:active {
2640
+ background-color: rgba(0, 116, 255, 0.8);
2641
+ }
2642
+ """
2643
+ self.setStyleSheet(style)
2644
+
2645
+ self.addfuncreturn={}
2646
+ #self.addfuncreturn={'expand': self.expandRow}
2647
+ self.FlagSetting=False
2648
+ self.FlagReset=False
2649
+ self.FlagCutted=False
2650
+
2651
+ self.nimg=0
2652
+ self.itemList=create_empty_list_of_dimension(self.listDim)
2653
+ self.warns=[]
2654
+
2655
+ self.disableTab=lambda flag: None
2656
+
2657
+ def setupRowBehaviour(self):
2658
+ self.setDragDropMode(QTableWidget.DragDropMode.InternalMove)
2659
+ self.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectItems)
2660
+ self.setDragEnabled(True)
2661
+ self.setAcceptDrops(True)
2662
+ self.setDropIndicatorShown(True)
2663
+ self.setDragDropOverwriteMode(False)
2664
+ self.setDefaultDropAction(Qt.MoveAction)
2665
+ self.setSortingEnabled(False)
2666
+
2667
+ def mousePressEvent(self, event: QMouseEvent):
2668
+ TABpar.FlagSettingPar=True
2669
+ self.itemUponPressing=self.currentItem()
2670
+ self.cursor_pos=event.globalPosition().toPoint()
2671
+ super().mousePressEvent(event)
2672
+ return
2673
+
2674
+ def mouseReleaseEvent(self, event: QMouseEvent):
2675
+ TABpar.FlagSettingPar=False
2676
+ itemUponReleasing=self.currentItem()
2677
+ if itemUponReleasing!=self.itemUponPressing:
2678
+ self.currentItemChanged.emit(itemUponReleasing,itemUponReleasing)
2679
+ super().mouseReleaseEvent(event)
2680
+ return
2681
+
2682
+ def dragEnterEvent(self, event):
2683
+ TABpar.FlagSettingPar=True
2684
+ self.dragged_items = self.selectedItems()
2685
+ rows=[i.row() for i in self.dragged_items]
2686
+ self.setSelectedQuickly(rows,True)
2687
+ self.dragged_items = self.selectedItems()
2688
+ self.dragged_indexes = self.selectedIndexes()
2689
+ self.verticalScrollBarVal=self.verticalScrollBar().value()
2690
+ super().dragEnterEvent(event)
2691
+
2692
+ def dragMoveEvent(self, event):
2693
+ pos = event.position().toPoint()
2694
+ self.hovered_item = item = self.itemAt(pos)
2695
+ super().dragMoveEvent(event) # Allow the event to proceed for row moves
2696
+
2697
+ #@log_qpainter_usage
2698
+ def paintEvent(self, event):
2699
+ super().paintEvent(event)
2700
+ self.paintLines()
2701
+
2702
+ def paintLines(self):
2703
+ item=self.hovered_item
2704
+ if item and self.dragged_items:
2705
+ if self.dragged_items!='externalItem':
2706
+ self.drop_indicator_pos = self.dropIndicatorPosition()
2707
+ if self.drop_indicator_pos == QTableWidget.DropIndicatorPosition.AboveItem:
2708
+ item_rect = self.visualRowRect(item)
2709
+ self.drawDropIndicatorLine(item_rect.top(),item_rect.x(),item_rect.height(),item_rect.width(),-1)
2710
+ elif self.drop_indicator_pos == QTableWidget.DropIndicatorPosition.BelowItem:
2711
+ item_rect = self.visualRowRect(item)
2712
+ self.drawDropIndicatorLine(item_rect.bottom(),item_rect.x(),item_rect.height(),item_rect.width(),+1)
2713
+ else: #below
2714
+ item_rect = self.visualRowRect(item)
2715
+ self.drawDropIndicatorLine(item_rect.bottom(),item_rect.x(),item_rect.height(),item_rect.width(),+1)
2716
+
2717
+ def visualRowRect(self, row: int):
2718
+ if type(row)==QTableWidgetItem:
2719
+ row=row.row()
2720
+ rect = QRect()
2721
+ for column in range(self.columnCount()):
2722
+ item = self.item(row, column)
2723
+ if item:
2724
+ item_rect = self.visualItemRect(item)
2725
+ if column == 0: # Se è la prima colonna, aggiungi il margine
2726
+ item_rect.adjust(self.margin_rect, 0, 0, 0)
2727
+ rect = rect.united(item_rect)
2728
+ return rect
2729
+
2730
+ def drawDropIndicatorLine(self, y_pos,x_pos,dy,dx,sign=1):
2731
+ painter = QPainter(self.viewport())
2732
+ painter.setPen(self.pen)
2733
+ painter.drawLine(0, y_pos, self.viewport().width(), y_pos)
2734
+
2735
+ # Calcola la posizione della freccia
2736
+ s=5*sign
2737
+ for x_pos_2 in (x_pos,x_pos+dx-2*abs(s)):
2738
+ y_pos_2=y_pos-5*sign
2739
+ arrow_top = QPoint(x_pos_2, y_pos_2 - 3*s)
2740
+ arrow_bottom = QPoint(x_pos_2, y_pos_2)
2741
+ arrow_left = QPoint(x_pos_2 - s, y_pos_2-s)
2742
+ arrow_right = QPoint(x_pos_2 + s, y_pos_2-s)
2743
+
2744
+ # Disegna la freccia
2745
+ painter.drawLine(arrow_top, arrow_bottom)
2746
+ #painter.drawLine(arrow_left, arrow_right)
2747
+ painter.drawLine(arrow_bottom, arrow_right)
2748
+ painter.drawLine(arrow_bottom, arrow_left)
2749
+ painter.end()
2750
+
2751
+ def keyPressEvent(self, event):
2752
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter):
2753
+ for f in self.addfuncreturn:
2754
+ self.addfuncreturn[f]()
2755
+ super().keyPressEvent(event)
2756
+ current_item = self.currentItem()
2757
+ if not current_item or not self.isPersistentEditorOpen(current_item):
2758
+ self.setFocus()
2759
+
2760
+ '''
2761
+ def mimeData(self, items):
2762
+ mimeData = QMimeData()
2763
+ encodedData = QByteArray()
2764
+ stream = QDataStream(encodedData, QIODevice.WriteOnly)
2765
+
2766
+ for item in items:
2767
+ row = self.row(item)
2768
+ column = self.column(item)
2769
+ text = item.text()
2770
+ stream.writeInt32(row)
2771
+ stream.writeInt32(column)
2772
+ stream.writeQString(text)
2773
+
2774
+ mimeData.setData("application/x-qabstractitemmodeldatalist", encodedData)
2775
+ return mimeData
2776
+
2777
+ def dropMimeData(self, row, column, data, action):
2778
+ if action == Qt.MoveAction:
2779
+ encodedData = data.data("application/x-qabstractitemmodeldatalist")
2780
+ stream = QDataStream(encodedData, QIODevice.ReadOnly)
2781
+ newItems = []
2782
+
2783
+ while not stream.atEnd():
2784
+ row = stream.readInt32()
2785
+ column = stream.readInt32()
2786
+ text = stream.readQString()
2787
+ newItems.append((row, column, text))
2788
+
2789
+ for row, column, text in newItems:
2790
+ self.setItem(row, column, QTableWidgetItem(text))
2791
+
2792
+ return True
2793
+ return False
2794
+ '''
2795
+
2796
+ def dropEvent(self, event):
2797
+ drop_indicator_position = self.dropIndicatorPosition()
2798
+
2799
+ if drop_indicator_position == QTableWidget.DropIndicatorPosition.OnItem or self.hovered_item is None:
2800
+ target_row_index=None
2801
+ self.verticalScrollBar().setValue(self.verticalScrollBarVal)
2802
+ QCursor.setPos(self.cursor_pos)
2803
+ event.ignore() # Ignore the event if it's not a row move or a drop on an item
2804
+ FlagUpdateList=False
2805
+ else:
2806
+ target_row = self.hovered_item.row()
2807
+ #pri.Coding.yellow(f'Hovered item: {self.item(target_row,0).text()}')
2808
+ source_rows=[]
2809
+ for i in self.dragged_items:
2810
+ if i.row() not in source_rows:
2811
+ source_rows.append(i.row())
2812
+
2813
+ if self.drop_indicator_pos == QTableWidget.DropIndicatorPosition.BelowItem:
2814
+ target_row+=1
2815
+ selectedItems,target_row_new=self.move_rows_to(source_rows,target_row)
2816
+ self.dropLists(selectedItems,source_rows)
2817
+ for r in range(len(source_rows)):
2818
+ for c in range(self.columnCount()-1,-1,-1):
2819
+ i=self.item(r+target_row_new,c)
2820
+ i:QTableWidgetItem
2821
+ """
2822
+ if not c:
2823
+ pri.Coding.yellow(f'row={i.row()} {i.text()}')
2824
+ pri.Coding.red(f'{self.itemList[0][r+target_row_new]}')
2825
+ """
2826
+ i.setSelected(True)
2827
+
2828
+ event.accept()
2829
+ #for i in selectedItems: i.setSelected(True)
2830
+
2831
+
2832
+ FlagUpdateList=True
2833
+
2834
+ self.dragged_items=self.dragged_indexes=None
2835
+ self.repaint()
2836
+ TABpar.FlagSettingPar=False
2837
+ self.itemSelectionChanged.emit()
2838
+ if FlagUpdateList: self.signals.updateLists.emit()
2839
+
2840
+ def move_rows_to(self, rows, target_row):
2841
+ sorted_rows = sorted(rows)
2842
+ # Salvataggio dei dati delle righe da spostare
2843
+ rows_data = []
2844
+ for row in sorted_rows:
2845
+ row_data = [self.item(row, column).text() for column in range(self.columnCount())]
2846
+ rows_data.append(row_data)
2847
+
2848
+ # Rimozione delle righe dalla loro posizione originale
2849
+ diff=0
2850
+ for row in reversed(sorted_rows):
2851
+ self.removeRow(row)
2852
+ if row<target_row: diff+=1
2853
+ target_row-=diff
2854
+
2855
+ selectedItems=[]
2856
+ # Inserimento delle righe nella posizione target
2857
+ for row, data in enumerate(rows_data):
2858
+ target_row_index = target_row + row
2859
+ self.insertRow(target_row_index)
2860
+ for column, text in enumerate(data):
2861
+ item=QTableWidgetItem(text)
2862
+ self.setItem(target_row_index, column,item)
2863
+ selectedItems.append(item)
2864
+ return selectedItems, target_row
2865
+
2866
+ def setSelectedQuickly(self, rows, Flag):
2867
+ self.clearSelection()
2868
+ selectionFlag=QItemSelectionModel.SelectionFlag.Select if Flag else QItemSelectionModel.SelectionFlag.Deselect
2869
+ selection_model = self.selectionModel()
2870
+ selection = QItemSelection()
2871
+ for row in rows:
2872
+ selection.merge(QItemSelection(self.model().index(row, 0), self.model().index(row, self.columnCount() - 1)), selectionFlag)
2873
+ selection_model.select(selection, QItemSelectionModel.SelectionFlag.ClearAndSelect )
2874
+ return
2875
+
2876
+ def dropLists(self, items, indexes):
2877
+ if self.itemList:
2878
+ cutted_items=pop_at_depth(self.itemList,self.listDepth,indexes)
2879
+ insert_at_depth(self.itemList,self.listDepth,items[0].row(),cutted_items)
2880
+ #ind_fin=self.indexOfTopLevelItem(items[-1])
2881
+ return
2882
+
2883
+ def cutLists(self, indexes, FlagDeleted=False):
2884
+ if FlagDeleted: type(self).deleted_itemList=pop_at_depth(self.itemList,self.listDepth,indexes)
2885
+ else: type(self).cutted_itemList=pop_at_depth(self.itemList,self.listDepth,indexes)
2886
+ if not FlagDeleted: self.FlagCutted=True
2887
+ self.nimg-=len(indexes)
2888
+ return
2889
+
2890
+ def deleteLists(self, indexes):
2891
+ self.cutLists(indexes,FlagDeleted=True)
2892
+ return
2893
+
2894
+ def copyLists(self, indexes):
2895
+ type(self).cutted_itemList=copy_at_depth(self.itemList,self.listDepth,indexes)
2896
+ self.FlagCutted=False
2897
+ return
2898
+
2899
+ def pasteLists(self, ind, FlagDeleted=False):
2900
+ pri.Time.magenta('pasteLists: start')
2901
+ if FlagDeleted: iList=type(self).deleted_itemList
2902
+ else: iList=type(self).cutted_itemList
2903
+ self.nimg+=measure_depth_length(iList,self.listDepth)
2904
+ insert_at_depth(self.itemList,self.listDepth,ind,iList)
2905
+ if self.FlagCutted:
2906
+ type(self).cutted_itemList=[]
2907
+ type(self).cutted_items=[]
2908
+ self.FlagCutted=False
2909
+ else:
2910
+ if FlagDeleted: type(self).deleted_itemList=deep_duplicate(iList)
2911
+ else: type(self).cutted_itemList=deep_duplicate(iList)
2912
+ pri.Time.magenta('pasteLists: end')
2913
+ return
2914
+
2915
+ def cleanLists(self):
2916
+ self.itemList=create_empty_list_of_dimension(self.listDim)
2917
+
2918
+ class ImageTable(PaIRSTable):
2919
+
2920
+ def __init__(self, parent: QWidget=None, listDim=2, listDepth=1):
2921
+ super().__init__(parent,listDim,listDepth)
2922
+
2923
+ columns=["Image filename","Plane parameters","Info"]
2924
+ self.setColumnCount(len(columns))
2925
+ header = self.horizontalHeader()
2926
+ self.setHorizontalHeaderLabels(columns)
2927
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.Interactive)
2928
+ header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
2929
+ header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
2930
+
2931
+ self.RowInfo=[]
2932
+ self.InfoLabel:QLabel=None
2933
+ self.DeleteButton:QPushButton=None
2934
+ self.addwid=[]
2935
+ self.addfuncreturn={}
2936
+ self.addfuncout={}
2937
+
2938
+ self.setVisible(True)
2939
+
2940
+ def keyPressEvent(self, event):
2941
+ super().keyPressEvent(event)
2942
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter): #return
2943
+ for f in self.addfuncreturn:
2944
+ self.addfuncreturn[f]()
2945
+
2946
+ def focusOutEvent(self, event):
2947
+ super().focusOutEvent(event)
2948
+ for f in self.addfuncout:
2949
+ self.addfuncout[f]()
2950
+
2951
+ def resizeEvent(self, event):
2952
+ super().resizeEvent(event)
2953
+ self.resizeInfoLabel()
2954
+
2955
+ def resizeInfoLabel(self):
2956
+ if self.InfoLabel and (True if not self.addwid else not self.addwid[0].hasFocus()):
2957
+ item=self.currentItem()
2958
+ if item:
2959
+ self.InfoLabel.show()
2960
+ if self.RowInfo: rowInfo=self.RowInfo[self.currentRow()]
2961
+ else: rowInfo=''
2962
+ tip=item.toolTip()
2963
+ if not "<br>" in tip:
2964
+ fw=lambda t: QtGui.QFontMetrics(self.InfoLabel.font()).size(QtCore.Qt.TextSingleLine,t).width()
2965
+ if fw(tip)>self.InfoLabel.width():
2966
+ k=0
2967
+ while fw(tip[:k])<self.InfoLabel.width():
2968
+ k+=1
2969
+ tip="<br>".join([tip[:k-1], tip[k-1:2*k]])
2970
+ if rowInfo: tip="<br>".join([tip,rowInfo])
2971
+ self.InfoLabel.setText(tip)
2972
+ else:
2973
+ self.InfoLabel.hide()
2974
+ self.InfoLabel.setText('')
2975
+
2976
+ #*************************************************** TESTING
2977
+ if __name__ == "__main__":
2978
+ if currentID==developerIDs['GP_Win_Office']:
2979
+ working_fold='C:/desk/PIV_Img/swirler_png/'
2980
+ elif currentID==developerIDs['GP_Mac_Laptop']:
2981
+ working_fold='/Users/gerardo/Desktop/PIV_Img/swirler_png/'
2982
+ else:
2983
+ working_fold=basefold
2984
+
2985
+ pri.Coding.white('\n'+'*'*50+'\n')
2986
+ imSet=ImageSet()
2987
+ imSet.scanPath(working_fold)
2988
+ imSet.print()
2989
+ pri.Coding.white('\n'+'*'*50+'\n')
2990
+
2991
+ kc=0
2992
+ i=198
2993
+ npairs=6000
2994
+ step=1
2995
+ ncam=2
2996
+
2997
+ app = PaIRSApp(sys.argv)
2998
+ w=ImageTreeWidget(FlagSpinButtons=FlagSpinButtons_Debug)
2999
+ try:
3000
+ imagePath,imageList,imageEx=imSet.genListsFromIndex(kc,i,npairs,step,ncam)
3001
+ w.setLists(imagePath,imageList,imageEx,[10,ncam,1])
3002
+ w.write_imFile(working_fold+'example.txt',imageList)
3003
+ if npairs<100:
3004
+ pri.Coding.white(f'List "{imSet.pattern[kc]}", i={i}, npairs={npairs}, step={step}')
3005
+ for n in range(len(imageList[0][0])):
3006
+ pri.Coding.white(f'{n:6d}:'+'\t'+imageList[0][0][n]+', '+imageList[0][1][n])
3007
+ for c in range(1,len(imageList)):
3008
+ pri.Coding.white(f'{" "*7}'+'\t'+imageList[c][0][n]+', '+imageList[c][1][n])
3009
+ except:
3010
+ traceback.print_exc()
3011
+ w.nullList()
3012
+ pass
3013
+
3014
+ w.resize(750,750)
3015
+ w.setVisible(True)
3016
+
3017
+ '''
3018
+ w2=ImageTable()
3019
+ w2.resize(750,750)
3020
+ w2.setVisible(True)
3021
+ '''
3022
+ sys.exit(app.exec())
3023
+
3024
+ quit()