PaIRS-UniNa 0.1.13__cp310-cp310-win_amd64.whl → 0.2.8__cp310-cp310-win_amd64.whl

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