PaIRS-UniNa 0.2.9__cp310-cp310-macosx_11_0_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. PaIRS_UniNa/Calibration_Tab.py +346 -0
  2. PaIRS_UniNa/Changes.txt +162 -0
  3. PaIRS_UniNa/Custom_Top.py +303 -0
  4. PaIRS_UniNa/Explorer.py +3168 -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 +3022 -0
  9. PaIRS_UniNa/Log_Tab.py +110 -0
  10. PaIRS_UniNa/Output_Tab.py +922 -0
  11. PaIRS_UniNa/PaIRS.py +18 -0
  12. PaIRS_UniNa/PaIRS_PIV.py +873 -0
  13. PaIRS_UniNa/PaIRS_pypacks.py +1421 -0
  14. PaIRS_UniNa/Process_Tab.py +1757 -0
  15. PaIRS_UniNa/Process_Tab_CalVi.py +313 -0
  16. PaIRS_UniNa/Process_Tab_Disp.py +163 -0
  17. PaIRS_UniNa/Process_Tab_Min.py +120 -0
  18. PaIRS_UniNa/ResizePopup.py +55 -0
  19. PaIRS_UniNa/SPIVCalHelp.py +155 -0
  20. PaIRS_UniNa/Saving_tools.py +296 -0
  21. PaIRS_UniNa/TabTools.py +1254 -0
  22. PaIRS_UniNa/Vis_Tab.py +2169 -0
  23. PaIRS_UniNa/Vis_Tab_CalVi.py +983 -0
  24. PaIRS_UniNa/Whatsnew.py +130 -0
  25. PaIRS_UniNa/_PaIRS_PIV.so +0 -0
  26. PaIRS_UniNa/__init__.py +6 -0
  27. PaIRS_UniNa/__main__.py +45 -0
  28. PaIRS_UniNa/addwidgets_ps.py +1133 -0
  29. PaIRS_UniNa/calib.py +1488 -0
  30. PaIRS_UniNa/calibView.py +833 -0
  31. PaIRS_UniNa/gPaIRS.py +3914 -0
  32. PaIRS_UniNa/gPalette.py +189 -0
  33. PaIRS_UniNa/icons/abort.png +0 -0
  34. PaIRS_UniNa/icons/about.png +0 -0
  35. PaIRS_UniNa/icons/align_all.png +0 -0
  36. PaIRS_UniNa/icons/announcement.png +0 -0
  37. PaIRS_UniNa/icons/automatic_levels_off.png +0 -0
  38. PaIRS_UniNa/icons/automatic_levels_on.png +0 -0
  39. PaIRS_UniNa/icons/automatic_off.png +0 -0
  40. PaIRS_UniNa/icons/automatic_on.png +0 -0
  41. PaIRS_UniNa/icons/automatic_size_off.png +0 -0
  42. PaIRS_UniNa/icons/automatic_size_on.png +0 -0
  43. PaIRS_UniNa/icons/axes.png +0 -0
  44. PaIRS_UniNa/icons/background.png +0 -0
  45. PaIRS_UniNa/icons/background_vectors.png +0 -0
  46. PaIRS_UniNa/icons/bin_off.png +0 -0
  47. PaIRS_UniNa/icons/bin_on.png +0 -0
  48. PaIRS_UniNa/icons/browse_file_c.png +0 -0
  49. PaIRS_UniNa/icons/browse_folder_c.png +0 -0
  50. PaIRS_UniNa/icons/brush_cursor.png +0 -0
  51. PaIRS_UniNa/icons/bugfix.png +0 -0
  52. PaIRS_UniNa/icons/cal_proc.png +0 -0
  53. PaIRS_UniNa/icons/cal_proc_off.png +0 -0
  54. PaIRS_UniNa/icons/cal_step.png +0 -0
  55. PaIRS_UniNa/icons/cal_step_off.png +0 -0
  56. PaIRS_UniNa/icons/calibrate.png +0 -0
  57. PaIRS_UniNa/icons/calibration_logo.png +0 -0
  58. PaIRS_UniNa/icons/change_folder.png +0 -0
  59. PaIRS_UniNa/icons/change_folder_off.png +0 -0
  60. PaIRS_UniNa/icons/checklist.png +0 -0
  61. PaIRS_UniNa/icons/clean.png +0 -0
  62. PaIRS_UniNa/icons/clean_run.png +0 -0
  63. PaIRS_UniNa/icons/close.png +0 -0
  64. PaIRS_UniNa/icons/close_all.png +0 -0
  65. PaIRS_UniNa/icons/close_project.png +0 -0
  66. PaIRS_UniNa/icons/close_workspace.png +0 -0
  67. PaIRS_UniNa/icons/colormap.png +0 -0
  68. PaIRS_UniNa/icons/colormaps/Accent.png +0 -0
  69. PaIRS_UniNa/icons/colormaps/BrBG.png +0 -0
  70. PaIRS_UniNa/icons/colormaps/Dark2.png +0 -0
  71. PaIRS_UniNa/icons/colormaps/PRGn.png +0 -0
  72. PaIRS_UniNa/icons/colormaps/Paired.png +0 -0
  73. PaIRS_UniNa/icons/colormaps/Pastel1.png +0 -0
  74. PaIRS_UniNa/icons/colormaps/Pastel2.png +0 -0
  75. PaIRS_UniNa/icons/colormaps/PiYG.png +0 -0
  76. PaIRS_UniNa/icons/colormaps/PuOr.png +0 -0
  77. PaIRS_UniNa/icons/colormaps/RdBu.png +0 -0
  78. PaIRS_UniNa/icons/colormaps/RdGy.png +0 -0
  79. PaIRS_UniNa/icons/colormaps/RdYlBu.png +0 -0
  80. PaIRS_UniNa/icons/colormaps/RdYlGn.png +0 -0
  81. PaIRS_UniNa/icons/colormaps/Set1.png +0 -0
  82. PaIRS_UniNa/icons/colormaps/Set2.png +0 -0
  83. PaIRS_UniNa/icons/colormaps/Set3.png +0 -0
  84. PaIRS_UniNa/icons/colormaps/Spectral.png +0 -0
  85. PaIRS_UniNa/icons/colormaps/Wistia.png +0 -0
  86. PaIRS_UniNa/icons/colormaps/afmhot.png +0 -0
  87. PaIRS_UniNa/icons/colormaps/autumn.png +0 -0
  88. PaIRS_UniNa/icons/colormaps/binary.png +0 -0
  89. PaIRS_UniNa/icons/colormaps/blackVector.png +0 -0
  90. PaIRS_UniNa/icons/colormaps/blueVector.png +0 -0
  91. PaIRS_UniNa/icons/colormaps/bone.png +0 -0
  92. PaIRS_UniNa/icons/colormaps/brg.png +0 -0
  93. PaIRS_UniNa/icons/colormaps/bwr.png +0 -0
  94. PaIRS_UniNa/icons/colormaps/cividis.png +0 -0
  95. PaIRS_UniNa/icons/colormaps/cool.png +0 -0
  96. PaIRS_UniNa/icons/colormaps/coolwarm.png +0 -0
  97. PaIRS_UniNa/icons/colormaps/copper.png +0 -0
  98. PaIRS_UniNa/icons/colormaps/cubehelix.png +0 -0
  99. PaIRS_UniNa/icons/colormaps/cyanVector.png +0 -0
  100. PaIRS_UniNa/icons/colormaps/flag.png +0 -0
  101. PaIRS_UniNa/icons/colormaps/gist_heat.png +0 -0
  102. PaIRS_UniNa/icons/colormaps/gray.png +0 -0
  103. PaIRS_UniNa/icons/colormaps/greenVector.png +0 -0
  104. PaIRS_UniNa/icons/colormaps/hot.png +0 -0
  105. PaIRS_UniNa/icons/colormaps/hsv.png +0 -0
  106. PaIRS_UniNa/icons/colormaps/inferno.png +0 -0
  107. PaIRS_UniNa/icons/colormaps/jet.png +0 -0
  108. PaIRS_UniNa/icons/colormaps/magentaVector.png +0 -0
  109. PaIRS_UniNa/icons/colormaps/magma.png +0 -0
  110. PaIRS_UniNa/icons/colormaps/ocean.png +0 -0
  111. PaIRS_UniNa/icons/colormaps/pink.png +0 -0
  112. PaIRS_UniNa/icons/colormaps/plasma.png +0 -0
  113. PaIRS_UniNa/icons/colormaps/prism.png +0 -0
  114. PaIRS_UniNa/icons/colormaps/rainbow.png +0 -0
  115. PaIRS_UniNa/icons/colormaps/redVector.png +0 -0
  116. PaIRS_UniNa/icons/colormaps/seismic.png +0 -0
  117. PaIRS_UniNa/icons/colormaps/spring.png +0 -0
  118. PaIRS_UniNa/icons/colormaps/summer.png +0 -0
  119. PaIRS_UniNa/icons/colormaps/tab10.png +0 -0
  120. PaIRS_UniNa/icons/colormaps/tab20.png +0 -0
  121. PaIRS_UniNa/icons/colormaps/tab20b.png +0 -0
  122. PaIRS_UniNa/icons/colormaps/tab20c.png +0 -0
  123. PaIRS_UniNa/icons/colormaps/terrain.png +0 -0
  124. PaIRS_UniNa/icons/colormaps/twilight.png +0 -0
  125. PaIRS_UniNa/icons/colormaps/viridis.png +0 -0
  126. PaIRS_UniNa/icons/colormaps/whiteVector.png +0 -0
  127. PaIRS_UniNa/icons/colormaps/winter.png +0 -0
  128. PaIRS_UniNa/icons/colormaps/yellowVector.png +0 -0
  129. PaIRS_UniNa/icons/common_region.png +0 -0
  130. PaIRS_UniNa/icons/common_region_off.png +0 -0
  131. PaIRS_UniNa/icons/completed.png +0 -0
  132. PaIRS_UniNa/icons/contourf_off.png +0 -0
  133. PaIRS_UniNa/icons/contourf_on.png +0 -0
  134. PaIRS_UniNa/icons/copy.png +0 -0
  135. PaIRS_UniNa/icons/copy_process.png +0 -0
  136. PaIRS_UniNa/icons/copy_process_off.png +0 -0
  137. PaIRS_UniNa/icons/copygrid.png +0 -0
  138. PaIRS_UniNa/icons/cursor_lamp.png +0 -0
  139. PaIRS_UniNa/icons/cut.png +0 -0
  140. PaIRS_UniNa/icons/cut_warnings.png +0 -0
  141. PaIRS_UniNa/icons/darkmode.png +0 -0
  142. PaIRS_UniNa/icons/debug_run.png +0 -0
  143. PaIRS_UniNa/icons/delete.png +0 -0
  144. PaIRS_UniNa/icons/deleteErr.png +0 -0
  145. PaIRS_UniNa/icons/disp_step.png +0 -0
  146. PaIRS_UniNa/icons/disp_step_off.png +0 -0
  147. PaIRS_UniNa/icons/down.png +0 -0
  148. PaIRS_UniNa/icons/edit_list.png +0 -0
  149. PaIRS_UniNa/icons/editing.png +0 -0
  150. PaIRS_UniNa/icons/example_list.png +0 -0
  151. PaIRS_UniNa/icons/find_all_planes.png +0 -0
  152. PaIRS_UniNa/icons/find_plane.png +0 -0
  153. PaIRS_UniNa/icons/flaticon_PaIRS.png +0 -0
  154. PaIRS_UniNa/icons/flaticon_PaIRS_beta.png +0 -0
  155. PaIRS_UniNa/icons/flaticon_PaIRS_download.png +0 -0
  156. PaIRS_UniNa/icons/flaticon_PaIRS_download_warning.png +0 -0
  157. PaIRS_UniNa/icons/flip_y_off.png +0 -0
  158. PaIRS_UniNa/icons/flip_y_on.png +0 -0
  159. PaIRS_UniNa/icons/focusErrr.png +0 -0
  160. PaIRS_UniNa/icons/folder_loop_cleanup.png +0 -0
  161. PaIRS_UniNa/icons/folder_loop_cleanup_off.png +0 -0
  162. PaIRS_UniNa/icons/gear.gif +0 -0
  163. PaIRS_UniNa/icons/gear.png +0 -0
  164. PaIRS_UniNa/icons/ger.png +0 -0
  165. PaIRS_UniNa/icons/greenv.png +0 -0
  166. PaIRS_UniNa/icons/guide.png +0 -0
  167. PaIRS_UniNa/icons/icon_CalVi.png +0 -0
  168. PaIRS_UniNa/icons/icon_PaIRS.png +0 -0
  169. PaIRS_UniNa/icons/import.png +0 -0
  170. PaIRS_UniNa/icons/import_set.png +0 -0
  171. PaIRS_UniNa/icons/information.png +0 -0
  172. PaIRS_UniNa/icons/information2.png +0 -0
  173. PaIRS_UniNa/icons/input_logo.png +0 -0
  174. PaIRS_UniNa/icons/issue.png +0 -0
  175. PaIRS_UniNa/icons/laser_NTR.png +0 -0
  176. PaIRS_UniNa/icons/laser_TR_double.png +0 -0
  177. PaIRS_UniNa/icons/laser_TR_single.png +0 -0
  178. PaIRS_UniNa/icons/link.png +0 -0
  179. PaIRS_UniNa/icons/linked.png +0 -0
  180. PaIRS_UniNa/icons/loaded.png +0 -0
  181. PaIRS_UniNa/icons/loading_2.gif +0 -0
  182. PaIRS_UniNa/icons/log_logo.png +0 -0
  183. PaIRS_UniNa/icons/logo_CalVi.png +0 -0
  184. PaIRS_UniNa/icons/logo_CalVi_completo.png +0 -0
  185. PaIRS_UniNa/icons/logo_CalVi_party.png +0 -0
  186. PaIRS_UniNa/icons/logo_PaIRS.png +0 -0
  187. PaIRS_UniNa/icons/logo_PaIRS_completo.png +0 -0
  188. PaIRS_UniNa/icons/logo_PaIRS_download.png +0 -0
  189. PaIRS_UniNa/icons/logo_PaIRS_party_rect.png +0 -0
  190. PaIRS_UniNa/icons/logo_PaIRS_rect.png +0 -0
  191. PaIRS_UniNa/icons/logo_opaco.png +0 -0
  192. PaIRS_UniNa/icons/mask.png +0 -0
  193. PaIRS_UniNa/icons/measure.png +0 -0
  194. PaIRS_UniNa/icons/measure_off.png +0 -0
  195. PaIRS_UniNa/icons/min_proc.png +0 -0
  196. PaIRS_UniNa/icons/min_proc_off.png +0 -0
  197. PaIRS_UniNa/icons/min_step.png +0 -0
  198. PaIRS_UniNa/icons/min_step_off.png +0 -0
  199. PaIRS_UniNa/icons/minus.png +0 -0
  200. PaIRS_UniNa/icons/mirror_u.png +0 -0
  201. PaIRS_UniNa/icons/mirror_v.png +0 -0
  202. PaIRS_UniNa/icons/mirror_x.png +0 -0
  203. PaIRS_UniNa/icons/mirror_y.png +0 -0
  204. PaIRS_UniNa/icons/mtplt.png +0 -0
  205. PaIRS_UniNa/icons/new.png +0 -0
  206. PaIRS_UniNa/icons/new_workspace.png +0 -0
  207. PaIRS_UniNa/icons/news.png +0 -0
  208. PaIRS_UniNa/icons/normal_run.png +0 -0
  209. PaIRS_UniNa/icons/open.png +0 -0
  210. PaIRS_UniNa/icons/open_image.png +0 -0
  211. PaIRS_UniNa/icons/open_new_window.png +0 -0
  212. PaIRS_UniNa/icons/open_result.png +0 -0
  213. PaIRS_UniNa/icons/open_workspace.png +0 -0
  214. PaIRS_UniNa/icons/output_logo.png +0 -0
  215. PaIRS_UniNa/icons/paste_above.png +0 -0
  216. PaIRS_UniNa/icons/paste_below.png +0 -0
  217. PaIRS_UniNa/icons/pause.png +0 -0
  218. PaIRS_UniNa/icons/paused.png +0 -0
  219. PaIRS_UniNa/icons/pencil_bw.png +0 -0
  220. PaIRS_UniNa/icons/piv_proc.png +0 -0
  221. PaIRS_UniNa/icons/piv_proc_off.png +0 -0
  222. PaIRS_UniNa/icons/piv_step.png +0 -0
  223. PaIRS_UniNa/icons/piv_step_off.png +0 -0
  224. PaIRS_UniNa/icons/plane.png +0 -0
  225. PaIRS_UniNa/icons/play.png +0 -0
  226. PaIRS_UniNa/icons/plus.png +0 -0
  227. PaIRS_UniNa/icons/process_logo.png +0 -0
  228. PaIRS_UniNa/icons/process_loop.png +0 -0
  229. PaIRS_UniNa/icons/project.png +0 -0
  230. PaIRS_UniNa/icons/pylog.png +0 -0
  231. PaIRS_UniNa/icons/python_warning.png +0 -0
  232. PaIRS_UniNa/icons/queue.png +0 -0
  233. PaIRS_UniNa/icons/quit.png +0 -0
  234. PaIRS_UniNa/icons/read.png +0 -0
  235. PaIRS_UniNa/icons/read_list.png +0 -0
  236. PaIRS_UniNa/icons/redo.png +0 -0
  237. PaIRS_UniNa/icons/redx.png +0 -0
  238. PaIRS_UniNa/icons/reset.png +0 -0
  239. PaIRS_UniNa/icons/reset_levels.png +0 -0
  240. PaIRS_UniNa/icons/resize_icon.png +0 -0
  241. PaIRS_UniNa/icons/restore.png +0 -0
  242. PaIRS_UniNa/icons/restore_undo.png +0 -0
  243. PaIRS_UniNa/icons/rotate_clock.png +0 -0
  244. PaIRS_UniNa/icons/rotate_counter.png +0 -0
  245. PaIRS_UniNa/icons/rotate_v_clock.png +0 -0
  246. PaIRS_UniNa/icons/rotate_v_counter.png +0 -0
  247. PaIRS_UniNa/icons/running.gif +0 -0
  248. PaIRS_UniNa/icons/running.png +0 -0
  249. PaIRS_UniNa/icons/running_warn.png +0 -0
  250. PaIRS_UniNa/icons/sandglass.png +0 -0
  251. PaIRS_UniNa/icons/save.png +0 -0
  252. PaIRS_UniNa/icons/save_and_stop.png +0 -0
  253. PaIRS_UniNa/icons/save_cfg.png +0 -0
  254. PaIRS_UniNa/icons/saveas.png +0 -0
  255. PaIRS_UniNa/icons/saveas_workspace.png +0 -0
  256. PaIRS_UniNa/icons/scale_all.png +0 -0
  257. PaIRS_UniNa/icons/scale_down.png +0 -0
  258. PaIRS_UniNa/icons/scale_up.png +0 -0
  259. PaIRS_UniNa/icons/scan_list.png +0 -0
  260. PaIRS_UniNa/icons/scan_path.png +0 -0
  261. PaIRS_UniNa/icons/scan_path_loop.png +0 -0
  262. PaIRS_UniNa/icons/scan_path_loop_off.png +0 -0
  263. PaIRS_UniNa/icons/search.png +0 -0
  264. PaIRS_UniNa/icons/showIW_off.png +0 -0
  265. PaIRS_UniNa/icons/showIW_on.png +0 -0
  266. PaIRS_UniNa/icons/show_all.png +0 -0
  267. PaIRS_UniNa/icons/sort.png +0 -0
  268. PaIRS_UniNa/icons/sort_reversed.png +0 -0
  269. PaIRS_UniNa/icons/spiv_proc.png +0 -0
  270. PaIRS_UniNa/icons/spiv_proc_off.png +0 -0
  271. PaIRS_UniNa/icons/spiv_setup_no.png +0 -0
  272. PaIRS_UniNa/icons/spiv_setup_ok.png +0 -0
  273. PaIRS_UniNa/icons/star.png +0 -0
  274. PaIRS_UniNa/icons/step_inheritance.png +0 -0
  275. PaIRS_UniNa/icons/subMIN_off.png +0 -0
  276. PaIRS_UniNa/icons/subMIN_on.png +0 -0
  277. PaIRS_UniNa/icons/tom.png +0 -0
  278. PaIRS_UniNa/icons/trash.png +0 -0
  279. PaIRS_UniNa/icons/undo.png +0 -0
  280. PaIRS_UniNa/icons/unedited.png +0 -0
  281. PaIRS_UniNa/icons/unina_dii.png +0 -0
  282. PaIRS_UniNa/icons/uninitialized.png +0 -0
  283. PaIRS_UniNa/icons/unlink.png +0 -0
  284. PaIRS_UniNa/icons/unwrap_items.png +0 -0
  285. PaIRS_UniNa/icons/up.png +0 -0
  286. PaIRS_UniNa/icons/updating_import.gif +0 -0
  287. PaIRS_UniNa/icons/updating_pairs.gif +0 -0
  288. PaIRS_UniNa/icons/vectorColor.png +0 -0
  289. PaIRS_UniNa/icons/vettore.png +0 -0
  290. PaIRS_UniNa/icons/view.png +0 -0
  291. PaIRS_UniNa/icons/view_off.png +0 -0
  292. PaIRS_UniNa/icons/vis_logo.png +0 -0
  293. PaIRS_UniNa/icons/waiting_circle.png +0 -0
  294. PaIRS_UniNa/icons/warning.png +0 -0
  295. PaIRS_UniNa/icons/warning_circle.png +0 -0
  296. PaIRS_UniNa/icons/window.png +0 -0
  297. PaIRS_UniNa/icons/workspace.png +0 -0
  298. PaIRS_UniNa/icons/wrap_items.png +0 -0
  299. PaIRS_UniNa/icons/write_list.png +0 -0
  300. PaIRS_UniNa/listLib.py +303 -0
  301. PaIRS_UniNa/mtfPIV.py +256 -0
  302. PaIRS_UniNa/parForMulti.py +435 -0
  303. PaIRS_UniNa/parForWorkers.py +593 -0
  304. PaIRS_UniNa/pivParFor.py +235 -0
  305. PaIRS_UniNa/plt_util.py +141 -0
  306. PaIRS_UniNa/preProcParFor.py +155 -0
  307. PaIRS_UniNa/procTools.py +1439 -0
  308. PaIRS_UniNa/readcfg.py +52 -0
  309. PaIRS_UniNa/rqrdpckgs.txt +9 -0
  310. PaIRS_UniNa/stereoPivParFor.py +227 -0
  311. PaIRS_UniNa/tAVarie.py +215 -0
  312. PaIRS_UniNa/tabSplitter.py +612 -0
  313. PaIRS_UniNa/ui_Calibration_Tab.py +578 -0
  314. PaIRS_UniNa/ui_Custom_Top.py +296 -0
  315. PaIRS_UniNa/ui_Input_Tab.py +1101 -0
  316. PaIRS_UniNa/ui_Input_Tab_CalVi.py +1283 -0
  317. PaIRS_UniNa/ui_Log_Tab.py +263 -0
  318. PaIRS_UniNa/ui_Output_Tab.py +2362 -0
  319. PaIRS_UniNa/ui_Process_Tab.py +3810 -0
  320. PaIRS_UniNa/ui_Process_Tab_CalVi.py +1549 -0
  321. PaIRS_UniNa/ui_Process_Tab_Disp.py +1141 -0
  322. PaIRS_UniNa/ui_Process_Tab_Min.py +437 -0
  323. PaIRS_UniNa/ui_ResizePopup.py +204 -0
  324. PaIRS_UniNa/ui_Vis_Tab.py +1628 -0
  325. PaIRS_UniNa/ui_Vis_Tab_CalVi.py +1251 -0
  326. PaIRS_UniNa/ui_Whatsnew.py +132 -0
  327. PaIRS_UniNa/ui_gPairs.py +877 -0
  328. PaIRS_UniNa/ui_infoPaIRS.py +551 -0
  329. PaIRS_UniNa/whatsnew.txt +6 -0
  330. pairs_unina-0.2.9.dist-info/METADATA +166 -0
  331. pairs_unina-0.2.9.dist-info/RECORD +333 -0
  332. pairs_unina-0.2.9.dist-info/WHEEL +5 -0
  333. pairs_unina-0.2.9.dist-info/top_level.txt +2 -0
@@ -0,0 +1,3168 @@
1
+ from PySide6.QtGui import QMouseEvent, QResizeEvent
2
+ from .PaIRS_pypacks import *
3
+ from .TabTools import *
4
+ from .procTools import *
5
+ from .listLib import *
6
+ from datetime import datetime
7
+ from .Input_Tab_tools import PaIRSTree
8
+ import json
9
+ from .FolderLoop import *
10
+
11
+ FlagStartingPages=True
12
+
13
+ MONTH_ABBREVIATIONS = {
14
+ 1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "May", 6: "Jun",
15
+ 7: "Jul", 8: "Aug", 9: "Sep", StepTypes.cal: "Oct", 11: "Nov", 12: "Dec"
16
+ }
17
+ projectActionButtonSize=[20,24]
18
+ processActionButtonSize=[20,24]
19
+ actionButtonSpacing=3
20
+ processButtonSize=[40,40] #icon, button
21
+ stepButtonSize=[32,32]
22
+ headerHeight=24
23
+ titleHeight=22
24
+ subtitleHeight=14
25
+ firstLevelHeight=titleHeight+subtitleHeight
26
+ titleNameWidth=150
27
+ secondLevelHeight=24
28
+ secondLevelIconSize=16
29
+ secondLevelIndentation=9
30
+ secondLevelSpacing=10
31
+ stepNameWidth=230
32
+ processTreeIndentation=10
33
+ column0Width=30
34
+
35
+ dFontPixelSize_processTitle=6
36
+ dFontPixelSize_stepTitle=3
37
+
38
+ projectActionButtons = {
39
+ '-':[],
40
+ 'info': ['Info','Information',Qt.Key.Key_F1,'information'],
41
+ 'rename': ['Rename','Rename the current project',Qt.Key.Key_F2,'editing'],
42
+ '|rename':[],
43
+ 'new': ['New','Create a new project',Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_N],
44
+ 'open': ['Open','Open a previous project',Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_B],
45
+ '|open':[],
46
+ 'save': ['Save','Save the current project',Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_S],
47
+ 'saveas': ['Save as...','Save the current project to a specific path',Qt.KeyboardModifier.ControlModifier|Qt.KeyboardModifier.ShiftModifier|Qt.Key.Key_S],
48
+ '|save':[],
49
+ 'close': ['Close','Close the current project',Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_X,'close_project'],
50
+ '|close':[],
51
+ 'clean': ['Clean','Clean the whole project list',Qt.KeyboardModifier.ControlModifier|Qt.KeyboardModifier.ShiftModifier|Qt.Key.Key_X],
52
+ }
53
+ projectGlobalActionButtons=['new','open','clean']
54
+ projectPageButtons={}
55
+ for n in ('new','open'):
56
+ d={}
57
+ d['name']=projectActionButtons[n][0]
58
+ d['caption']=projectActionButtons[n][1]
59
+ if len(projectActionButtons[n])>3:
60
+ d['icon']=projectActionButtons[n][0][3]
61
+ else:
62
+ d['icon']=n+'.png'
63
+ projectPageButtons[n]=d
64
+
65
+ processActionButtons = {
66
+ '-': [],
67
+ 'import': ['Import','Import processes from disk',Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_B,'open',],
68
+ '|import':[],
69
+ 'saveas': ['Save as...','Save the current project to a specific path',Qt.KeyboardModifier.ControlModifier|Qt.KeyboardModifier.ShiftModifier|Qt.Key.Key_S],
70
+ '|saveas':[],
71
+ 'info': ['Info','Information',Qt.Key.Key_F1,'information'],
72
+ 'rename': ['Rename','Rename the current process',Qt.Key.Key_F2,'editing',],
73
+ '|rename': [],
74
+ 'copy': ['Copy', 'Copy process',Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_C,'copy',],
75
+ '|paste': [],
76
+ 'paste_below': ['Paste below','Paste below the current process', Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_V,'paste_below',],
77
+ 'paste_above': ['Paste above','Paste above the current process',Qt.KeyboardModifier.ControlModifier|Qt.KeyboardModifier.ShiftModifier|Qt.Key.Key_V,'paste_above',],
78
+ '|process_loop': [],
79
+ 'process_loop': ['Copy for process loop', 'Copy process to loop over folders',Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_L,],
80
+ '|restore': [],
81
+ 'restore': ['Restore','Restore the current process',Qt.KeyboardModifier.ControlModifier|Qt.Key.Key_R,'restore',],
82
+ '|delete': [],
83
+ 'delete': ['Delete','Delete the current process',[Qt.Key.Key_Delete,Qt.Key.Key_Backspace],'delete',],
84
+ '|clean': [],
85
+ 'clean': ['Clean','Clean the whole list',[Qt.KeyboardModifier.ShiftModifier|Qt.Key.Key_Delete,Qt.KeyboardModifier.ShiftModifier|Qt.Key.Key_Backspace],'clean'],
86
+ }
87
+ processGlobalActionButtons=['import','paste_below','paste_above','clean']
88
+
89
+ class TreeIcons():
90
+ icons={
91
+ None: None,
92
+ 'project.png': None,
93
+ 'min_proc.png': None,
94
+ 'piv_proc.png': None,
95
+ 'cal_proc.png': None,
96
+ 'spiv_proc.png': None,
97
+ 'cal_step.png': None,
98
+ 'min_step.png': None,
99
+ 'piv_step.png': None,
100
+ 'disp_step.png': None,
101
+ 'cal_step_off.png': None,
102
+ 'min_step_off.png': None,
103
+ 'piv_step_off.png': None,
104
+ 'disp_step_off.png': None,
105
+ 'redx.png': None,
106
+ 'greenv.png': None,
107
+ 'completed.png': None,
108
+ 'issue.png': None,
109
+ 'running.png': None,
110
+ 'warning_circle.png': None,
111
+ 'running_warn.png': None,
112
+ 'paused.png': None,
113
+ 'queue.png': None,
114
+ 'warning.png': None,
115
+ 'workspace.png': None,
116
+ 'uninitialized.png':None,
117
+ 'linked.png':None
118
+ }
119
+ pixmaps={}
120
+ FlagInit=False
121
+
122
+ def __init__(self):
123
+ if not TreeIcons.FlagInit:
124
+ for t in TreeIcons.icons:
125
+ if t:
126
+ TreeIcons.icons[t]=QIcon(icons_path+t)
127
+ TreeIcons.pixmaps[t]=QPixmap(icons_path+t)
128
+ else:
129
+ TreeIcons.icons[t]=QIcon()
130
+ TreeIcons.pixmaps[t]=QPixmap()
131
+ TreeIcons.FlagInit=True
132
+
133
+ def currentTimeString():
134
+ current_time = datetime.now().strftime("%b %d, %Y, %I:%M:%S %p")
135
+ month_number = datetime.now().month # Get the current month number
136
+ month_abbreviation = MONTH_ABBREVIATIONS.get(month_number, "Mmm") # Get the month abbreviation based on the number
137
+ current_time = current_time.replace(current_time[:3], month_abbreviation) # Replace the month abbreviation
138
+ current_time = current_time.replace("am", "AM").replace("pm", "PM") # Convert AM/PM to uppercase
139
+ return current_time
140
+
141
+ def scrollAreaStyle():
142
+ style="""
143
+ QScrollArea
144
+ {
145
+ border: 1pix solid gray;
146
+ background: transparent;
147
+ }
148
+
149
+ QScrollBar:horizontal
150
+ {
151
+ height: 15px;
152
+ margin: 3px 10px 3px 10px;
153
+ border: 1px transparent #2A2929;
154
+ border-radius: 4px;
155
+ background-color: rgba(200,200,200,50); /* #2A2929; */
156
+ }
157
+
158
+ QScrollBar::handle:horizontal
159
+ {
160
+ background-color: rgba(180,180,180,180); /* #605F5F; */
161
+ min-width: 5px;
162
+ border-radius: 4px;
163
+ }
164
+
165
+ QScrollBar:vertical
166
+ {
167
+ background-color: rgba(200,200,200,50); ;
168
+ width: 15px;
169
+ margin: 10px 3px 10px 3px;
170
+ border: 1px transparent #2A2929;
171
+ border-radius: 4px;
172
+ }
173
+
174
+ QScrollBar::handle:vertical
175
+ {
176
+ background-color: rgba(180,180,180,180); /* #605F5F; */
177
+ min-height: 5px;
178
+ border-radius: 4px;
179
+ }
180
+
181
+ QScrollBar::add-line {
182
+ border: none;
183
+ background: none;
184
+ }
185
+
186
+ QScrollBar::sub-line {
187
+ border: none;
188
+ background: none;
189
+ }
190
+ """
191
+ return style
192
+
193
+ class TREpar(TABpar):
194
+ def __init__(self):
195
+ self.setup()
196
+ super().__init__(self.name,'PaIRS_Explorer')
197
+ self.unchecked_fields+=[]
198
+
199
+ def setup(self):
200
+ self.name_proj, self.username, self.version = identifierName(typeObject='proj')
201
+ self.outName = ''
202
+ self.createdDate = currentTimeString()
203
+ self.modifiedDate = self.createdDate
204
+ self.savedDate = ''
205
+ self.FlagSaved = False
206
+ self.FlagQueue = True
207
+ self.FlagRunnable = True
208
+
209
+ self.project = None
210
+ self.tree = 0
211
+ self.process = None
212
+ self.step = None
213
+ self.basename = 'Project'
214
+ self.name = 'Project 1'
215
+ self.date = f'Created: {self.createdDate}'
216
+ self.icon = 'project.png'
217
+
218
+ def saveBullet(self):
219
+ return '' if self.FlagSaved else '<span style="color: #7A8B8B;"><sup>&#9679;</sup></span>'
220
+
221
+ def InfoMessage(self):
222
+ InfoMessage=f'{self.name}'
223
+ if self.FlagSaved:
224
+ InfoMessage+=f'\nFile location: {self.outName}'
225
+ else:
226
+ if self.savedDate:
227
+ InfoMessage+=' (unsaved)'
228
+ else:
229
+ InfoMessage+=' (never saved)'
230
+ InfoMessage+=f'\n\nCreated : {self.createdDate}'
231
+ InfoMessage+=f'\nModified: {self.modifiedDate}'
232
+ if self.savedDate: InfoMessage+=f'\nSaved : {self.savedDate}'
233
+ InfoMessage+=f'\n\nUser: {self.username}'
234
+ InfoMessage+=f'\nPaIRS version: {self.version}'
235
+ return InfoMessage
236
+
237
+ class ITEpar(TABpar):
238
+ def __init__(self,Process=None,Step=None):
239
+ self.setup(Process,Step)
240
+ super().__init__(self.name,'PaIRS_Explorer')
241
+ self.OptionDone=0
242
+ self.warningMessage='Process step not yet initialized!'
243
+ self.procdata.ind=self.ind
244
+ self.unchecked_fields+=[]
245
+
246
+ def setup(self,Process,Step):
247
+ self.Process = Process
248
+ self.Step = Step
249
+
250
+ self.name_proc, self.username, self.version = identifierName(typeObject='proc')
251
+ self.createdDate = currentTimeString()
252
+ self.modifiedDate = self.createdDate
253
+ self.savedDate = ''
254
+ self.FlagSaved = False
255
+ self.FlagQueue = True
256
+ self.dependencies = []
257
+
258
+ self.name=''
259
+ self.icon=''
260
+ self.date=f'Created: {self.createdDate}'
261
+ self.children={}
262
+ self.parents=[]
263
+ self.mandatory=[]
264
+
265
+ self.tabs=[]
266
+ self.active=False
267
+ self.label='uninitialized'
268
+ self.progress=0
269
+ self.ncam=0
270
+
271
+ self.data_fields=[f for f,_ in self.__dict__.items()]
272
+
273
+ buttons=processData if Step is None else stepData
274
+ type=Process if Step is None else Step
275
+ if type in list(buttons):
276
+ for k,v in buttons[type].items():
277
+ if k in self.data_fields:
278
+ setattr(self,k,v)
279
+ self.basename=self.name
280
+ self.name=self.basename
281
+
282
+ self.procdata=dataTreePar(Process,Step)
283
+
284
+ def copyfrom(self, newist, exceptions=None):
285
+ super().copyfrom(newist, exceptions)
286
+ self.procdata.ind=self.ind
287
+ return
288
+
289
+ def InfoMessage(self):
290
+ InfoMessage=f'{self.name}'
291
+ InfoMessage+=f'\n\nCreated : {self.createdDate}'
292
+ InfoMessage+=f'\nModified: {self.modifiedDate}'
293
+ InfoMessage+=f'\n\nUser: {self.username}'
294
+ InfoMessage+=f'\nPaIRS version: {self.version}'
295
+ return InfoMessage
296
+
297
+ class ModernSwitch(QWidget):
298
+ toggled = Signal(bool)
299
+ handleMargin = 3
300
+ switchHeight=25
301
+ switchWidth=2*(switchHeight+handleMargin)
302
+ timerTime=50 #ms
303
+
304
+ def __init__(self, parent=None, name='', par:TREpar=None):
305
+ super().__init__(parent)
306
+ if parent is None:
307
+ self.gui=self.window()
308
+ else:
309
+ from .gPaIRS import gPaIRS
310
+ if hasattr(parent,'gui'):
311
+ self.gui:gPaIRS=parent.gui
312
+ else:
313
+ self.gui:gPaIRS=parent.window()
314
+ self.name=name
315
+ self.par=par
316
+
317
+
318
+ FlagWindowRun=self.gui.FlagRun if hasattr(self.gui,'FlagRun') else False
319
+ self.setFixedSize(self.switchWidth, self.switchHeight)
320
+ if par:
321
+ self._checked = par.FlagQueue #and not FlagWindowRun
322
+ else:
323
+ self._checked = True
324
+ par.FlagQueue=self._checked
325
+
326
+ # Load image for the handle and scale it to fit the ellipse
327
+ self.handle_image = QPixmap(icons_path+"gear.png") # Replace with your image path
328
+ self.handle_image = self.handle_image.scaledToHeight(self.height() - self.handleMargin*2, Qt.SmoothTransformation)
329
+
330
+ self.setCursor(QCursor(QtCore.Qt.PointingHandCursor))
331
+ self.FlagAnimation=False
332
+ self.setSwitchLayout()
333
+
334
+ self.animation = QPropertyAnimation(self, b"handle_position", self)
335
+ self.animation.setDuration(200)
336
+ self.animation.finished.connect(self.setSwitchLayout)
337
+
338
+ self.text_label = QLabel("run ", self)
339
+ #self.text_label.setStyleSheet("color: white;") # Customize text color if needed
340
+ font=self.text_label.font()
341
+ #font.setItalic(True)
342
+ font.setWeight(QFont.Weight.Light)
343
+ self.text_label.setFont(font)
344
+ self.text_label.adjustSize()
345
+
346
+ self.setFixedSize(self.switchWidth, self.switchHeight)
347
+ self.setContentsMargins(self.handleMargin,0,0,0)
348
+
349
+ # Timer per aggiornare l'animazione
350
+ self.FlagRunning=False
351
+ self.gearMovie:QMovie=None
352
+ self.timer = QTimer(self)
353
+ self.timer.timeout.connect(self.update)
354
+
355
+ self.setEnabled(not FlagWindowRun)
356
+ self.setSwitch(self._checked)
357
+ if par and self.gui.procdata:
358
+ if self.gui.procdata.ind[:-2]==par.ind[:-2]:
359
+ self.startTimer()
360
+ #self.timer.start(50) # Aggiorna ogni 50 ms
361
+
362
+ def startTimer(self):
363
+ if not self.FlagRunning and hasattr(self.gui,'gearMovie'):
364
+ self.FlagRunning=True
365
+ self.gearMovie=self.gui.gearMovie
366
+ self.timer.start(self.timerTime)
367
+ pri.Coding.yellow('Start switch timer')
368
+
369
+ def stopTimer(self):
370
+ if self.FlagRunning:
371
+ self.FlagRunning=False
372
+ self.timer.stop()
373
+
374
+ def set_handle_position(self, pos):
375
+ self._handle_position = pos
376
+ self.update()
377
+
378
+ def get_handle_position(self):
379
+ return self._handle_position
380
+
381
+ handle_position = Property(int, fget=get_handle_position, fset=set_handle_position)
382
+
383
+ def paintEvent(self, event):
384
+ painter = QPainter(self)
385
+ painter.setRenderHint(QPainter.Antialiasing)
386
+
387
+ # Draw background ellipse with border
388
+ bg_rect = QRect(0, 0, self.width(), self.height())
389
+
390
+ # Draw filled background ellipse
391
+ painter.setBrush(self._bg_color)
392
+ painter.drawRoundedRect(bg_rect.adjusted(1, 1, -1, -1), self.height() / 2, self.height() / 2)
393
+
394
+ # Calculate position and scale for image
395
+ handle_x = self._handle_position
396
+ handle_y = self.handleMargin
397
+ handle_width = self.handle_image.width()
398
+ handle_height = self.handle_image.height()
399
+
400
+ """
401
+ if self._checked:
402
+ # Draw handle image
403
+ handle_rect = QRect(handle_x, handle_y, handle_width, handle_height)
404
+ painter.drawPixmap(handle_rect, self.handle_image)
405
+ else:
406
+ # Draw handle circle
407
+ handle_radius = self.height() - self.handleMargin*2
408
+ handle_rect = QRect(self._handle_position, self.handleMargin, handle_radius, handle_radius)
409
+ painter.setBrush(Qt.white)
410
+ painter.drawEllipse(handle_rect)
411
+ """
412
+ handle_rect = QRect(handle_x, handle_y, handle_width, handle_height)
413
+ #painter.drawPixmap(handle_rect, self.handle_image)
414
+ if self.FlagRunning:
415
+ painter.drawImage(handle_rect,self.gearMovie.currentImage())
416
+ else:
417
+ painter.drawImage(handle_rect,self.handle_image.toImage())
418
+
419
+ if self.FlagAnimation:
420
+ self.text_label.setText("")
421
+ else:
422
+ if self._checked:
423
+ self.text_label.setText("run ")
424
+ self.text_label.move(self.handleMargin*2, (self.height() - self.text_label.height()) // 2)
425
+ else:
426
+ self.text_label.setText("skip")
427
+ self.text_label.move(self.width() - self.height() - self.handleMargin*2, (self.height() - self.text_label.height()) // 2)
428
+
429
+ def mousePressEvent(self, event):
430
+ if event.button() == Qt.LeftButton and not self.FlagAnimation:
431
+ self.toggle()
432
+
433
+ def toggle(self):
434
+ self._checked = not self._checked
435
+ if self.par: self.par.FlagQueue = self._checked
436
+ self.toggled.emit(self._checked)
437
+ self.animation.setEndValue(self.switchHandlePosition())
438
+ self.FlagAnimation=True
439
+ self.animation.start()
440
+
441
+ def switchHandlePosition(self):
442
+ if self._checked:
443
+ handle_position = self.width() - self.height() + self.handleMargin
444
+ else:
445
+ handle_position = self.handleMargin
446
+ return handle_position
447
+
448
+ def setSwitchLayout(self):
449
+ self.FlagAnimation=False
450
+ if self._checked:
451
+ self._bg_color = QColor(46, 204, 113) #QColor(0, 122, 204) ù
452
+ tip=f'{self.name if self.name else "Item"} added to run queue.'
453
+ else:
454
+ tip=f'{self.name if self.name else "Item"} excluded from run queue.'
455
+ #text_color = self.palette().color(QPalette.WindowText)
456
+ self._bg_color = QColor(175, 175, 175)
457
+ self._bg_color.setAlpha(0.5)
458
+ self.setToolTip(tip)
459
+ self.setStatusTip(tip)
460
+ self.set_handle_position(self.switchHandlePosition())
461
+
462
+ def setSwitch(self,FlagChecked):
463
+ if FlagChecked!=self._checked:
464
+ self._checked=FlagChecked
465
+ self.setSwitchLayout()
466
+
467
+ class ActionButtonBar(QWidget):
468
+ FlagShortCuts = True
469
+
470
+ def __init__(self, buttonData:dict={}, additionalButtonBars={}, globalButtons=[], buttonSize:list=projectActionButtonSize, FlagInvisible=True, tree:PaIRSTree=None):
471
+ super().__init__()
472
+ self.buttonLayout = QHBoxLayout()
473
+ self.buttonLayout.setContentsMargins(0, 0, 0, 0)
474
+ self.buttonLayout.setSpacing(actionButtonSpacing)
475
+ self.setLayout(self.buttonLayout)
476
+
477
+ self.buttonData=buttonData
478
+ self.additionalButtonBars=additionalButtonBars
479
+ self.globalButtons=globalButtons
480
+ self.buttonSize = bs = buttonSize
481
+ self.FlagInvisible=FlagInvisible
482
+ self.tree:QWidget=None
483
+ self.tree=tree
484
+ if self.tree:
485
+ self.tree.actionBar=self
486
+
487
+ self.bShortCuts={}
488
+ for icon_name, data in buttonData.items():
489
+ if '-' in icon_name:
490
+ self.buttonLayout.addItem(QSpacerItem(bs[1], bs[1], QSizePolicy.Expanding, QSizePolicy.Minimum))
491
+ elif '|' in icon_name:
492
+ separator = QFrame()
493
+ separator.setFrameShape(QFrame.VLine)
494
+ separator.setFrameShadow(QFrame.Sunken)
495
+ setattr(self,'sep_'+icon_name[1:],separator)
496
+ self.buttonLayout.addWidget(separator)
497
+ else:
498
+ b = QToolButton(self)
499
+ b.setObjectName('button_'+icon_name)
500
+ b.setCursor(Qt.CursorShape.PointingHandCursor)
501
+ if FlagInvisible:
502
+ b.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }")
503
+ b.pressed.connect(lambda btn=b: btn.setStyleSheet("QToolButton { border: none; background: #dcdcdc;} QToolButton::menu-indicator { image: none; }"))
504
+ b.released.connect(lambda btn=b: btn.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }"))
505
+ setattr(b,'initialStyle',b.styleSheet())
506
+ b.setToolTip(data[1])
507
+ b.setStatusTip(data[1])
508
+
509
+ if len(data)>3:
510
+ icon_file=icons_path+data[3]+'.png'
511
+ else:
512
+ icon_file=icons_path+icon_name+'.png'
513
+ b.setIcon(QIcon(icon_file))
514
+ b.setIconSize(QSize(bs[0],bs[0]))
515
+ b.setFixedSize(bs[1],bs[1]) # Impostare la dimensione quadrata
516
+ tip=data[1]
517
+ if self.FlagShortCuts:
518
+ if len(data)>2:
519
+ if data[2]:
520
+ data2list=data[2] if isinstance(data[2],list) else [data[2]]
521
+ addtip=[]
522
+ for data2 in data2list:
523
+ addtip.append(QKeySequence(data2).toString(QKeySequence.NativeText) )
524
+ def buttonClick(b:QToolButton):
525
+ if b.isVisible() and b.isEnabled(): b.click()
526
+ self.bShortCuts[data2]=lambda but=b:buttonClick(but)
527
+ tip+=' ('+" or ".join(addtip)+')'
528
+ b.setToolTip(tip)
529
+ b.setStatusTip(tip)
530
+ setattr(self,'button_'+icon_name,b)
531
+ self.buttonLayout.addWidget(b)
532
+ if self.tree: self.setButtonActions()
533
+
534
+ def setButtonActions(self):
535
+ for icon_name, data in self.buttonData.items():
536
+ if '-' not in icon_name and '|' not in icon_name:
537
+ if hasattr(self.tree,'button_'+icon_name+'_action'):
538
+ b:QPushButton=getattr(self,'button_'+icon_name)
539
+ b.clicked.connect(getattr(self.tree,'button_'+icon_name+'_action'))
540
+ self.tree.contextMenuEvent=lambda e: ActionButtonBar.treeContextMenuEvent(self,e)
541
+
542
+ def treeContextMenuEvent(self, event):
543
+ tree:PaIRSTree=self.tree
544
+ item=tree.itemAt(event.pos())
545
+ if 'globals' in self.additionalButtonBars:
546
+ buttonBars=[bar for bar in self.additionalButtonBars['globals']]
547
+ else:
548
+ buttonBars=[]
549
+ if item is None:
550
+ buttons=self.globalButtons
551
+ else:
552
+ if item.parent() is None:
553
+ buttons=list(self.buttonData)
554
+ else:
555
+ buttons=self.globalButtons
556
+ if 'items' in self.additionalButtonBars:
557
+ buttonBars=[bar for bar in self.additionalButtonBars['items']]+buttonBars
558
+ if len(buttons)==0 and len(buttonBars)==0:
559
+ return
560
+ tree.FlagContexMenu=True
561
+
562
+ menu=QMenu(tree)
563
+ name=[]
564
+ act=[]
565
+ fun=[]
566
+
567
+ for bar in buttonBars:
568
+ bar:ProcessButtonBar
569
+ for type, b in bar.buttons.items():
570
+ b:QPushButton
571
+ if b.isVisible() and b.isEnabled():
572
+ FlagNamedBar='name' in bar.buttonData[type]
573
+ if FlagNamedBar:
574
+ nameAction=bar.buttonData[type]['name']
575
+ else:
576
+ nameAction=b.toolTip()
577
+ name.append(nameAction)
578
+ if FlagNamedBar and 'class' in bar.buttonData[type]:
579
+ if 'PIV'!=nameAction[:3]: nameAction=nameAction[:1].lower()+nameAction[1:]
580
+ if b.isCheckable():
581
+ nameAction=f"{'De-activate' if b.isChecked() else 'Activate'} {nameAction}"
582
+ else:
583
+ nameAction='Add '+nameAction
584
+ act.append(QAction(b.icon(),nameAction,self))
585
+ menu.addAction(act[-1])
586
+ if isinstance(b,DraggableButton):
587
+ callback=lambda butt=b: butt.buttonAction()
588
+ else:
589
+ callback=lambda butt=b: butt.click()
590
+ fun.append(callback)
591
+ if len(act): menu.addSeparator()
592
+ for nb in buttons:
593
+ if '-' not in nb and '|' not in nb:
594
+ b:QPushButton=getattr(self,'button_'+nb)
595
+ if b.isVisible() and b.isEnabled():
596
+ if hasattr(tree,'button_'+nb+'_action'):
597
+ name.append(nb)
598
+ act.append(QAction(b.icon(),toPlainText(b.toolTip().split('.')[0]),self))
599
+ menu.addAction(act[-1])
600
+ callback=getattr(tree,'button_'+nb+'_action')
601
+ fun.append(callback)
602
+ elif '|' in nb:
603
+ if len(act): menu.addSeparator()
604
+
605
+ if len(act):
606
+ action = menu.exec(tree.mapToGlobal(event.pos()))
607
+ for nb,a,f in zip(name,act,fun):
608
+ if a==action:
609
+ f()
610
+ return
611
+
612
+ def treeKeyPressEvent(self,event):
613
+ for ksc,f in self.bShortCuts.items():
614
+ if ksc is None: continue
615
+ if type(ksc)!=list: ksc=[ksc]
616
+ for k in ksc:
617
+ if type(k)==QKeyCombination:
618
+ if event.key()==k.key() and event.modifiers()==k.keyboardModifiers():
619
+ f()
620
+ return True # Impedisce al widget di ricevere l'evento
621
+ else:
622
+ if event.key()==k and not event.modifiers():
623
+ f()
624
+ return True # Impedisce al widget di ricevere l'evento
625
+
626
+ class ProjectTree(PaIRSTree):
627
+ def __init__(self, parent: QWidget=None, listDim=2, listDepth=1, projectActionBar:ActionButtonBar=None, widgets:list=[], Explorer=None):
628
+ super().__init__(parent, listDim, listDepth)
629
+ self.setSelectionMode(QTreeWidget.SelectionMode.SingleSelection)
630
+ self.setSelectionBehavior(QTreeWidget.SelectionBehavior.SelectRows)
631
+ self.setIconSize(QSize(firstLevelHeight-4, firstLevelHeight-4))
632
+
633
+ columns=["#","Projects"]
634
+
635
+ self.setColumnCount(len(columns))
636
+ self.setHeaderLabels(columns)
637
+ header=self.header()
638
+ header.setFixedHeight(headerHeight)
639
+ self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
640
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
641
+ self.setColumnWidth(0,column0Width)
642
+ self.setIndentation(0)
643
+ header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
644
+ font=header.font()
645
+ font.setItalic(True)
646
+ font.setPixelSize(fontPixelSize+dFontPixelSize_stepTitle)
647
+ header.setFont(font)
648
+
649
+ self.initialStyleSheet=self.styleSheet()
650
+ self.FlagExternalDrag=False
651
+
652
+ self.itemDoubleClicked.connect(self.startEditing)
653
+ self.FlagReset=False
654
+ self.editingFinished=lambda: None
655
+
656
+ self.actionBar=projectActionBar
657
+ if self.actionBar:
658
+ self.actionBar.tree=self
659
+ self.actionBar.setButtonActions()
660
+ self.setupWidgets(widgets)
661
+
662
+ self.TREpar=TREpar()
663
+ self.TREpar_None=TREpar()
664
+ self.adjustSelection=lambda: None
665
+ #self.currentItemChanged.connect(self.tree_item_selection)
666
+ self.itemSelectionChanged.connect(self.tree_item_selection)
667
+ self.tree_item_selection()
668
+
669
+ self.Explorer:PaIRS_Explorer=Explorer
670
+ self.adjustSwitches = lambda: None
671
+ self.modifyWorkspace = lambda: None
672
+ self.setVisible(True)
673
+
674
+ def setupWidgets(self,widgets):
675
+ self.widgets=widgets
676
+ expand_level(self.itemList,0,len(widgets)+2)
677
+ for w,itemList in zip(self.widgets,self.itemList[2:]):
678
+ w:gPaIRS_Tab
679
+ w.TABpar_prev=itemList
680
+
681
+ def createProject(self,TRE:TREpar):
682
+ Project=[ [TRE], [ [[],[]] ] ]
683
+ if not self.widgets: return Project
684
+ for w in self.widgets:
685
+ w:gPaIRS_Tab
686
+ Project.append([[]])
687
+ Project[-1][-1].append([]) #processTree
688
+ Project[-1][-1].append([]) #binTree
689
+ return Project
690
+
691
+ def createProjectItem(self,ind=None,FlagNewItem=True):
692
+ self.blockSignals(True)
693
+ if ind==None: ind=self.topLevelItemCount()
694
+ item = QTreeWidgetItem()
695
+
696
+ if FlagNewItem:
697
+ TRE=TREpar()
698
+ TRE.project=ind
699
+ projectNames=[item.name for item in self.itemList[0]]
700
+ nameInd=1
701
+ TRE.name=f'{TRE.basename} {nameInd}'
702
+ while TRE.name in projectNames:
703
+ nameInd+=1
704
+ TRE.name=f'{TRE.basename} {nameInd}'
705
+ Project=self.createProject(TRE)
706
+ insert_at_depth(self.itemList,self.listDepth,ind,Project)
707
+ else:
708
+ TRE=self.itemList[0][ind]
709
+
710
+ item.setText(0,str(ind))
711
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
712
+ item.setIcon(1, TreeIcons.icons[TRE.icon]) # Set the icon of the new item
713
+ #item.setFlags(item.flags() | Qt.ItemIsEditable)
714
+
715
+ ind=max([0,ind])
716
+ self.insertTopLevelItem(ind,item)
717
+ self.setProjectItemWidget(item,TRE)
718
+
719
+ item.setData(0,Qt.ItemDataRole.UserRole,[False])
720
+ self.resetImNumber(ind)
721
+ self.blockSignals(False)
722
+ if FlagNewItem: self.signals.updateLists.emit()
723
+ return item
724
+
725
+ def setProjectItemWidget(self,item:QTreeWidgetItem,TRE:TREpar):
726
+ title_label = ResizingLabel(TRE.name+TRE.saveBullet())
727
+ title_label.setObjectName('title_project')
728
+ title_label.setFixedHeight(titleHeight)
729
+ title_label.setMinimumWidth(titleNameWidth)
730
+ title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
731
+ # Set the font of the title to bold and the subtitle to double font
732
+ self.titleFont(title_label)
733
+
734
+ sub_title=TRE.date
735
+ sub_title_label = ResizingLabel(sub_title) # Create a QLabel for the subtitle
736
+ sub_title_label.setObjectName('subtitle_project')
737
+ sub_title_label.setFixedHeight(subtitleHeight)
738
+ sub_title_label.setMinimumWidth(titleNameWidth)
739
+ sub_title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
740
+ self.subTitleFont(sub_title_label)
741
+
742
+ project_title_layout = QVBoxLayout() # Create a vertical layout to place the subtitle below the title
743
+ project_title_layout.addWidget(title_label) # Add the title of the element
744
+ project_title_layout.addWidget(sub_title_label) # Add the subtitle
745
+
746
+ project_title_layout.setContentsMargins(10, 2, 0, 2) # Remove margins
747
+ project_title_layout.setSpacing(2) # Remove margins
748
+
749
+ switch = ModernSwitch(parent=self,name='Project',par=TRE)
750
+ switch.setSwitch(TRE.FlagQueue)
751
+ switch.toggled.connect(lambda: self.toogleProcesses(TRE))
752
+ switch.setVisible(len(self.itemList[1][TRE.project][0])>0)
753
+
754
+ #spacer=QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
755
+ main_layout=QHBoxLayout()
756
+ main_layout.addLayout(project_title_layout)
757
+ #main_layout.addSpacerItem(spacer)
758
+ main_layout.addWidget(switch)
759
+ main_layout.setContentsMargins(0,0,10,0)
760
+ main_layout.setSpacing(6)
761
+ main_layout.setStretch(0,0)
762
+ main_layout.setStretch(1,1)
763
+
764
+ InfoMessage=TRE.InfoMessage()
765
+ title_label.setToolTip(InfoMessage)
766
+ title_label.setStatusTip(TRE.name)
767
+ sub_title_label.setToolTip(sub_title)
768
+ sub_title_label.setStatusTip(sub_title)
769
+
770
+ widget = QWidget(self) # Create a widget container for the layout
771
+ widget.setLayout(main_layout) # Set the layout for the widget container
772
+ widget.setMinimumWidth(titleNameWidth+switch.switchWidth+main_layout.spacing())
773
+ self.setItemWidget(item, 1 ,widget)
774
+ return widget
775
+
776
+ def toogleProcesses(self,TRE:TREpar):
777
+ ITELists=self.itemList[1][TRE.project][0]
778
+ for k,ITEList in enumerate(ITELists):
779
+ if self.TREpar.project==TRE.project:
780
+ topLevelItem=self.Explorer.processTree.topLevelItem(k)
781
+ itemWidget=self.Explorer.processTree.itemWidget(topLevelItem,1)
782
+ switch:ModernSwitch=itemWidget.findChildren(ModernSwitch)[0]
783
+ if TRE.FlagQueue!=switch._checked:
784
+ switch.blockSignals(True)
785
+ switch.toggle()
786
+ switch.blockSignals(False)
787
+ else:
788
+ ITEList[0].FlagQueue=TRE.FlagQueue
789
+ if self.Explorer.TREpar.process is not None:
790
+ self.Explorer.ITEpar.FlagQueue=self.Explorer.ITEfromInd(self.Explorer.ITEpar.ind).FlagQueue
791
+ self.Explorer.TREpar.FlagQueue=self.Explorer.projectTree.itemList[0][self.Explorer.TREpar.project].FlagQueue
792
+ self.adjustSwitches()
793
+ #self.itemSelectionChanged.emit()
794
+
795
+ def titleFont(self,label:QLabel,fPixelSize=None):
796
+ if fPixelSize is None:
797
+ if hasattr(self.gui,'GPApar'): fPixelSize=self.gui.GPApar.fontPixelSize
798
+ else: fPixelSize=fontPixelSize
799
+ font = label.font()
800
+ font.setFamily(fontName)
801
+ font.setBold(True) # Bold title
802
+ fPS=min([fPixelSize+dFontPixelSize_processTitle,fontPixelSize+dFontPixelSize_processTitle+3])
803
+ font.setPixelSize(fPS) # Double font size for subtitle
804
+ label.setFont(font)
805
+
806
+ def subTitleFont(self,label:QLabel,fPixelSize=None):
807
+ if fPixelSize is None:
808
+ if hasattr(self.gui,'GPApar'): fPixelSize=self.gui.GPApar.fontPixelSize
809
+ else: fPixelSize=fontPixelSize
810
+ font = label.font()
811
+ font.setFamily(fontName)
812
+ font.setItalic(True) # Bold title
813
+ fPS=min([fPixelSize,fontPixelSize+3])
814
+ font.setPixelSize(fPS) # Double font size for subtitle
815
+ label.setFont(font)
816
+
817
+ def dropEvent(self,event):
818
+ self.blockSignals(True)
819
+ items=self.selectedItems()
820
+ indexes=[self.indexOfTopLevelItem(i) for i in items]
821
+ drop_indicator_position = self.dropIndicatorPosition()
822
+ if drop_indicator_position == QTreeWidget.DropIndicatorPosition.OnItem or self.hovered_item is None:
823
+ self.verticalScrollBar().setValue(self.verticalScrollBarVal)
824
+ QCursor.setPos(self.cursor_pos)
825
+ event.ignore() # Ignore the event if it's not a row move or a drop on an item
826
+ return
827
+ else:
828
+ QTreeWidget.dropEvent(self,event)
829
+ self.dropLists(items,indexes)
830
+ for n,item in enumerate(items):
831
+ k=self.indexOfTopLevelItem(item)
832
+ TRE:TREpar=self.itemList[0][k]
833
+ pri.Coding.blue(f'Dropping {TRE.name} {indexes[n]} --> {k}')
834
+ self.setProjectItemWidget(item,TRE)
835
+ self.dragged_items=self.dragged_indexes=None
836
+ self.repaint()
837
+ self.TREpar.copyfrom(TRE)
838
+ self.setCurrentItem(item)
839
+ self.blockSignals(False)
840
+ self.tree_item_selection()
841
+
842
+ def resetImNumber(self,kin=None,kfin=None):
843
+ super().resetImNumber(kin,kfin)
844
+ if not kin: kin=0
845
+ if not kfin: kfin=self.topLevelItemCount()-1
846
+ TREList=self.itemList[0]
847
+ for i in range(kin, kfin + 1):
848
+ TRE:TREpar=TREList[i]
849
+ TRE.project=i
850
+ return
851
+
852
+ def printProjects(self):
853
+ pri.Coding.red(f'{"*"*50}\nProject list (process tree):')
854
+ for k in range(len(self.itemList[0])):
855
+ pri.Coding.green(f'{k+1}) {self.itemList[0][k].name}')
856
+ for i in self.itemList[1][k][0]:
857
+ for n,j in enumerate(i):
858
+ if n==0: pri.Coding.yellow(f' {j.name}')
859
+ else: pri.Coding.cyan (f' {j.name}')
860
+ pri.Coding.red(f'{"*"*50}\n')
861
+
862
+ def startEditing(self, item, column):
863
+ if self.itemWidget(item, column):
864
+ self.editItem(item, column)
865
+
866
+ def editItem(self, item, column):
867
+ if column == 0 or item.parent() is not None: # Se è la prima colonna, non fare nulla
868
+ return
869
+ widget = self.itemWidget(item, column)
870
+ if isinstance(widget, QLineEdit):
871
+ return # If already editing, do nothing
872
+ k=self.indexOfTopLevelItem(item)
873
+ text = self.itemList[0][k].name
874
+ line_edit = CustomLineEdit(text, self)
875
+ self.titleFont(line_edit)
876
+ line_edit.setText(text)
877
+ self.setItemWidget(item, column, line_edit)
878
+ line_edit.selectAll()
879
+ line_edit.setFocus()
880
+ line_edit.editingFinished.connect(lambda: self.finishEditing(item, column))
881
+ line_edit.cancelEditing.connect(lambda: self.finishEditing(item, column))
882
+
883
+ def finishEditing(self, item, column):
884
+ line_edit = self.itemWidget(item, column)
885
+ if not hasattr(line_edit,'text'):
886
+ return
887
+ new_text = line_edit.text()
888
+ k=self.indexOfTopLevelItem(item)
889
+ TRE:TREpar=self.itemList[0][k]
890
+ TRE.name=new_text
891
+ self.TREpar.name=new_text
892
+ self.setProjectItemWidget(item, TRE)
893
+ self.editingFinished()
894
+
895
+ def keyPressEvent(self, event):
896
+ self.actionBar.treeKeyPressEvent(event)
897
+ if event.key() == Qt.Key.Key_Escape:
898
+ self.setCurrentItem(None)
899
+ self.clearSelection()
900
+ else:
901
+ super().keyPressEvent(event)
902
+
903
+ def cutItems(self,items,FlagDelete=False):
904
+ cutted_items=[None]*len(items)
905
+ for k,item in enumerate(items):
906
+ cutted_items[k]=self.duplicateItem(item)
907
+ if FlagDelete: type(self).deleted_items=cutted_items
908
+ else: type(self).cutted_items=cutted_items
909
+ return
910
+
911
+ def copy_cut_delete_action(self,FlagCut=False,FlagDelete=False):
912
+ items,indexes=self.selectTopLevel()
913
+ if not items: return
914
+ if FlagDelete: FlagCut=False
915
+ self.cutItems(items,FlagDelete)
916
+
917
+ FlagSignal=True
918
+ if FlagCut or FlagDelete:
919
+ if len(indexes)<1000:
920
+ for item in items:
921
+ self.takeTopLevelItem(self.indexOfTopLevelItem(item))
922
+ if not FlagDelete: self.cutLists(indexes)
923
+ else: self.deleteLists(indexes)
924
+ else:
925
+ if not FlagDelete: self.cutLists(indexes)
926
+ else: self.deleteLists(indexes)
927
+ FlagSignal=False
928
+ else:
929
+ self.copyLists(indexes)
930
+
931
+ if FlagCut or FlagDelete:
932
+ self.setCurrentItem(None)
933
+ self.clearSelection()
934
+ self.setFocus()
935
+ if FlagSignal and not self.signalsBlocked(): self.signals.updateLists.emit()
936
+
937
+ def clean_action(self):
938
+ self.setSelectionMode(QTreeWidget.SelectionMode.MultiSelection)
939
+ self.selectAll()
940
+ self.copy_cut_delete_action(FlagDelete=True)
941
+ self.setSelectionMode(QTreeWidget.SelectionMode.SingleSelection)
942
+ return
943
+
944
+ def button_rename_action(self):
945
+ selected_items = self.selectedItems()
946
+ if selected_items:
947
+ item = selected_items[0]
948
+ #column = self.currentColumn()
949
+ #self.editItem(item, column)
950
+ self.editItem(item, 1)
951
+
952
+ def button_info_action(self):
953
+ warningDialog(self,self.TREpar.InfoMessage(),pixmap=TreeIcons.pixmaps[self.TREpar.icon],title='Project information')
954
+
955
+ def button_new_action(self):
956
+ item=self.createProjectItem()
957
+ self.setCurrentItem(item)
958
+ self.modifyWorkspace()
959
+ self.tree_item_selection()
960
+ return
961
+
962
+ def button_open_action(self):
963
+ return
964
+
965
+ def button_save_action(self):
966
+ return False
967
+
968
+ def button_saveas_action(self):
969
+ return False
970
+
971
+ def button_close_action(self):
972
+ FlagQuestion=True
973
+ if not self.TREpar.FlagSaved:
974
+ if questionDialog(self,'The current project is unsaved. Do you want to save it before closing?'):
975
+ FlagQuestion=not self.button_save_action()
976
+ if FlagQuestion:
977
+ flagYes=questionDialog(self,f"Are you sure you want to remove the selected project{'' if self.TREpar.FlagSaved else ' without saving'}?")
978
+ if not flagYes: return
979
+ self.blockSignals(True)
980
+ self.copy_cut_delete_action(FlagDelete=True)
981
+ self.blockSignals(False)
982
+ item=self.currentItem()
983
+ if item: self.TREpar.project=self.indexOfTopLevelItem(item)
984
+ else: self.TREpar.project=None
985
+ self.modifyWorkspace()
986
+ self.tree_item_selection()
987
+ return
988
+
989
+ def button_clean_action(self):
990
+ if self.topLevelItemCount()==1:
991
+ self.button_close_action()
992
+ return
993
+ FlagUnsaved=False
994
+ for t in self.itemList[0]:
995
+ t:TREpar
996
+ if not t.FlagSaved:
997
+ FlagUnsaved=True
998
+ break
999
+ additionalSentence='' if not FlagUnsaved else 'There are some unsaved projects in the current workspace. '
1000
+ flagYes=questionDialog(self,f"{additionalSentence}Are you sure you want to remove the selected projects?")
1001
+ if not flagYes: return
1002
+ self.clean_workspace()
1003
+
1004
+ def clean_workspace(self):
1005
+ self.blockSignals(True)
1006
+ self.clean_action()
1007
+ self.blockSignals(False)
1008
+ item=self.currentItem()
1009
+ if item: self.TREpar.project=self.indexOfTopLevelItem(item)
1010
+ else: self.TREpar.project=None
1011
+ self.modifyWorkspace()
1012
+ self.tree_item_selection()
1013
+ return
1014
+
1015
+ def tree_item_selection(self):
1016
+ if self.TREpar.project is not None:
1017
+ TRE:TREpar=self.itemList[0][self.TREpar.project]
1018
+ if TRE.isEqualTo(self.TREpar,fields=['name','project']):
1019
+ TRE.copyfrom(self.TREpar)
1020
+ item=self.currentItem()
1021
+ FlagEnabled=item!=None
1022
+ if FlagEnabled:
1023
+ self.TREpar.copyfrom(self.itemList[0][self.indexOfTopLevelItem(item)])
1024
+ else:
1025
+ self.TREpar.copyfrom(self.TREpar_None)
1026
+ self.setButtonLayout()
1027
+
1028
+ self.adjustSelection()
1029
+ self.printProjects()
1030
+ return
1031
+
1032
+ def setButtonLayout(self):
1033
+ item=self.currentItem()
1034
+ FlagEnabled=item!=None
1035
+ self.actionBar.button_info.setEnabled(FlagEnabled)
1036
+ self.actionBar.button_rename.setEnabled(FlagEnabled)
1037
+ self.actionBar.button_save.setEnabled(not self.TREpar.FlagSaved and FlagEnabled)
1038
+ self.actionBar.button_saveas.setEnabled(FlagEnabled)
1039
+ self.actionBar.button_close.setEnabled(FlagEnabled)
1040
+ self.actionBar.button_clean.setEnabled(self.topLevelItemCount()>0)
1041
+
1042
+ class DraggableButton(QToolButton):
1043
+ def __init__(self, data: dict = {}, buttonSize: list = processButtonSize, FlagInvisible=True, parent=None):
1044
+ super().__init__(parent)
1045
+ self.buttonData = data
1046
+ self.button_text = data['name']
1047
+ if len(buttonSize) == 0: buttonSize = processButtonSize
1048
+ elif len(buttonSize) < 2: buttonSize.append(buttonSize[0])
1049
+ self.buttonSize = buttonSize
1050
+
1051
+ self.buttonIcon = TreeIcons.icons[data['icon']]
1052
+ self.setIconSize(QSize(self.buttonSize[0], self.buttonSize[0]))
1053
+ self.setIcon(self.buttonIcon)
1054
+ self.setFixedSize(self.buttonSize[1], self.buttonSize[1])
1055
+ if FlagInvisible:
1056
+ self.setCursor(Qt.CursorShape.OpenHandCursor)
1057
+ self.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }")
1058
+ self.setToolTip(data['name']+' (drag to process tree or double click to add)')
1059
+ self.setStatusTip(data['name']+' (drag to process tree or double click to add)')
1060
+
1061
+ self.buttonAction=lambda: None
1062
+
1063
+ def mousePressEvent(self, event):
1064
+ if event.button() == Qt.LeftButton:
1065
+ self.drag_start_position = event.position() # Memorizza la posizione del click del mouse
1066
+ self.setCursor(Qt.CursorShape.ClosedHandCursor)
1067
+ super().mousePressEvent(event)
1068
+
1069
+ def mouseReleaseEvent(self, event):
1070
+ self.setCursor(Qt.CursorShape.OpenHandCursor)
1071
+ super().mouseReleaseEvent(event)
1072
+
1073
+ def mouseMoveEvent(self, event):
1074
+ # Calcola la distanza dal punto di inizio del drag
1075
+ distance = (event.position() - self.drag_start_position).manhattanLength()
1076
+ # Se la distanza è superiore a una soglia, avvia il drag
1077
+ if distance > QApplication.startDragDistance():
1078
+ self.dragIcon()
1079
+ super().mouseMoveEvent(event)
1080
+
1081
+ def mouseDoubleClickEvent(self, event):
1082
+ self.buttonAction()
1083
+ pass # Ignora il doppio click del mouse
1084
+
1085
+ def dragIcon(self):
1086
+ mime_data = QMimeData()
1087
+ mime_data.setText(self.button_text) # Store the button text as Mime data
1088
+ mime_data.setData('application/x-button', json.dumps(self.buttonData['type']).encode())
1089
+ drag = QDrag(self)
1090
+ drag.setMimeData(mime_data)
1091
+ drag.setPixmap(self.buttonIcon.pixmap(QSize(self.buttonSize[1], self.buttonSize[1]))) # Show only the image during dragging
1092
+ sh = int(self.buttonSize[1] * 0.5)
1093
+ drag.setHotSpot(QPoint(sh, sh))
1094
+ self.setIconSize(QSize(0, 0))
1095
+ QApplication.setOverrideCursor(Qt.ClosedHandCursor)
1096
+ drag.exec(Qt.MoveAction)
1097
+ QApplication.restoreOverrideCursor()
1098
+ self.setCursor(Qt.CursorShape.OpenHandCursor)
1099
+ self.setIconSize(QSize(self.buttonSize[0], self.buttonSize[0]))
1100
+
1101
+ class ProcessButtonBar(QWidget):
1102
+ def __init__(self, buttonData=processData, buttonSize=processButtonSize, FlagInvisible=True):
1103
+ super().__init__()
1104
+ self.buttonLayout = QHBoxLayout()
1105
+ self.buttonLayout.setContentsMargins(0, 0, 0, 0)
1106
+ self.buttonLayout.setSpacing(int(buttonSize[0] / 2))
1107
+ self.setLayout(self.buttonLayout)
1108
+ self.buttonData=buttonData
1109
+ self.buttonSize = buttonSize
1110
+ self.FlagInvisible=FlagInvisible
1111
+
1112
+ self.buttons={}
1113
+ for type, data in buttonData.items():
1114
+ if all([f in list(data) for f in ('icon', 'name', 'type')]):
1115
+ if type==ProcessTypes.null: continue
1116
+ button = DraggableButton(data, buttonSize, FlagInvisible)
1117
+ self.buttonLayout.addWidget(button)
1118
+ self.buttons[type]=button
1119
+ self.buttonLayout.addSpacerItem(QSpacerItem(0, self.buttonSize[0], QSizePolicy.Expanding, QSizePolicy.Minimum))
1120
+
1121
+ class StepButtonBar(QWidget):
1122
+ def __init__(self, buttonData=stepData, buttonSize=processButtonSize, FlagInvisible=True):
1123
+ super().__init__()
1124
+ self.buttonLayout = QVBoxLayout()
1125
+ self.buttonLayout.setContentsMargins(10, headerHeight+5, 0, 10)
1126
+ self.buttonLayout.setSpacing(int(buttonSize[0]/2))
1127
+ self.setLayout(self.buttonLayout)
1128
+ self.buttonData=buttonData
1129
+ self.buttonSize = buttonSize
1130
+
1131
+ self.buttons={}
1132
+ self.labels={}
1133
+ for type in buttonData:
1134
+ if type==StepTypes.null: continue
1135
+ data=buttonData[type]
1136
+
1137
+ if all([f in list(data) for f in ('icon', 'name', 'type')]):
1138
+ button = QToolButton(self)
1139
+ button.setIconSize(QSize(self.buttonSize[0], self.buttonSize[0]))
1140
+ button.setFixedSize(self.buttonSize[1], self.buttonSize[1])
1141
+
1142
+ if FlagInvisible:
1143
+ button.setCursor(Qt.CursorShape.PointingHandCursor)
1144
+ button.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }")
1145
+ button.pressed.connect(lambda btn=button: btn.setStyleSheet("QToolButton { border: none; background: #dcdcdc;} QToolButton::menu-indicator { image: none; }"))
1146
+ button.released.connect(lambda btn=button: btn.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }"))
1147
+ setattr(button,'initialStyle',button.styleSheet())
1148
+ button.setToolTip(data['name'])
1149
+ button.setStatusTip(button.toolTip())
1150
+
1151
+ button.setCheckable(True)
1152
+ def buttonIcon(b,d):
1153
+ b.iconOn = TreeIcons.icons[d['icon']]
1154
+ b.iconOff = TreeIcons.icons[d['icon'].replace('.png','_off.png')]
1155
+ b.clicked.connect(lambda:self.setButtonIcon(b))
1156
+ setattr(b,'setButtonIcon',lambda:self.setButtonIcon(b))
1157
+
1158
+ """
1159
+ label=QLabel(self)
1160
+ label.setPixmap( iconOn.pixmap(self.buttonSize[0], self.buttonSize[0]))
1161
+ label.setMargin(int(0.5*(self.buttonSize[1]-self.buttonSize[0])))
1162
+ label.setToolTip(data['name'])
1163
+ label.setStatusTip(data['name'])
1164
+ setattr(b,'label',label)
1165
+ """
1166
+ buttonIcon(button,data)
1167
+
1168
+ self.buttonLayout.addWidget(button)
1169
+ #Dself.buttonLayout.addWidget(button.label)
1170
+ button.setButtonIcon()
1171
+ setattr(button,'buttonData',data)
1172
+
1173
+ self.buttons[type]=button
1174
+
1175
+ label = QLabel(self)
1176
+ label.setPixmap(TreeIcons.pixmaps[data['icon']])
1177
+ label.setFixedSize(self.buttonSize[1], self.buttonSize[1])
1178
+ label.setScaledContents(True)
1179
+ label.setToolTip(data['name']+' (mandatory step)')
1180
+ label.setStatusTip(label.toolTip())
1181
+ self.buttonLayout.addWidget(label)
1182
+ self.labels[type]=label
1183
+
1184
+ self.buttonLayout.addSpacerItem(QSpacerItem(self.buttonSize[0], 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
1185
+
1186
+ def setButtonIcon(self,button:QToolButton):
1187
+ if button.isChecked(): #or not button.isVisible():
1188
+ button.setIcon(button.iconOn)
1189
+ else:
1190
+ button.setIcon(button.iconOff)
1191
+
1192
+ class ProcessTree(PaIRSTree):
1193
+ eventSender=None
1194
+
1195
+ class CustomItemDelegate(QStyledItemDelegate):
1196
+ def __init__(self, parent=None):
1197
+ super().__init__(parent)
1198
+ self.h1 = firstLevelHeight
1199
+ self.h2 = secondLevelHeight
1200
+
1201
+ def sizeHint(self, option, index):
1202
+ if index.parent().isValid(): # Verifica se l'elemento ha un genitore
1203
+ return QSize(option.rect.width(), self.h2) # Altezza per il secondo livello
1204
+ else:
1205
+ return QSize(option.rect.width(), self.h1) # Altezza per il primo livello
1206
+
1207
+ def __init__(self, parent: QWidget=None, listDim=2, listDepth=1, restoreTree=None, processActionBar:ActionButtonBar=None,widgets=[], Explorer=None):
1208
+ super().__init__(parent, listDim, listDepth)
1209
+ if type(restoreTree)==ProcessTree:
1210
+ self.FlagBin=True
1211
+ self.restoreTree=restoreTree
1212
+ self.name='Bin'
1213
+ else:
1214
+ self.FlagBin=False
1215
+ self.restoreTree=None
1216
+ self.name='Process'
1217
+ self.setAcceptDrops(True)
1218
+ #self.setSelectionMode(QTreeWidget.SingleSelection)
1219
+ self.setSelectionBehavior(QTreeWidget.SelectionBehavior.SelectRows)
1220
+ self.delegate=self.CustomItemDelegate()
1221
+ self.setItemDelegate(self.delegate) # Set the default height of all rows to 40 pixels
1222
+ self.setIconSize(QSize(firstLevelHeight-4, firstLevelHeight-4))
1223
+
1224
+ if self.FlagBin:
1225
+ columns=["#","Deleted processes"]
1226
+ self.setStyleSheet("""
1227
+ QHeaderView::section {
1228
+ color: red;
1229
+ }
1230
+ """)
1231
+ else:
1232
+ columns=["#","Processes"]
1233
+ self.setColumnCount(len(columns))
1234
+ self.setHeaderLabels(columns)
1235
+ header=self.header()
1236
+ header.setFixedHeight(headerHeight)
1237
+ self.headerItem().setTextAlignment(0,Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
1238
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
1239
+ self.setColumnWidth(0,column0Width+processTreeIndentation)
1240
+ self.setIndentation(processTreeIndentation)
1241
+ header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
1242
+ font=header.font()
1243
+ font.setItalic(True)
1244
+ font.setPixelSize(fontPixelSize+dFontPixelSize_stepTitle)
1245
+ header.setFont(font)
1246
+
1247
+ self.linkedIcon=QIcon(QPixmap(icons_path+"linked.png").scaled(QSize(secondLevelIconSize,secondLevelIconSize),mode=Qt.TransformationMode.SmoothTransformation))
1248
+ self.uneditedIcon=QIcon(QPixmap(icons_path+"unedited.png").scaled(QSize(secondLevelIconSize,secondLevelIconSize),mode=Qt.TransformationMode.SmoothTransformation))
1249
+ self.undoneIcon=QIcon(QPixmap(icons_path+"undo.png").scaled(QSize(secondLevelIconSize,secondLevelIconSize),mode=Qt.TransformationMode.SmoothTransformation))
1250
+
1251
+ self.initialStyleSheet=self.styleSheet()
1252
+ self.FlagExternalDrag=False
1253
+
1254
+ self.itemDoubleClicked.connect(self.startEditing)
1255
+ self.FlagReset=False
1256
+ self.FlagContexMenu=False
1257
+
1258
+ self.actionBar=processActionBar
1259
+ if self.actionBar:
1260
+ self.actionBar.tree=self
1261
+ self.actionBar.setButtonActions()
1262
+ self.setupWidgets(widgets)
1263
+
1264
+ self.TREpar=TREpar()
1265
+ self.TREpar.tree=int(self.FlagBin)
1266
+ self.ITEpar=ITEpar()
1267
+
1268
+ self.editingFinished=lambda: None
1269
+
1270
+ self.Explorer:PaIRS_Explorer=Explorer
1271
+ self.adjustSwitches = lambda: None
1272
+ self.setVisible(True)
1273
+
1274
+ def setupWidgets(self,widgets):
1275
+ self.widgets=widgets
1276
+ self.widgetNames=[]
1277
+ for w in self.widgets:
1278
+ w:gPaIRS_Tab
1279
+ self.widgetNames.append(w.TABname)
1280
+
1281
+ def resetImNumber(self,kin=None,kfin=None):
1282
+ super().resetImNumber(kin,kfin)
1283
+ if not kin: kin=0
1284
+ if not kfin: kfin=self.topLevelItemCount()-1
1285
+ ITEList=self.itemList[0]
1286
+ inds_old={}
1287
+ deps_old={}
1288
+ links_old={}
1289
+ inds_new={}
1290
+ for i in range(self.topLevelItemCount()):
1291
+ ITEs=ITEList[i]
1292
+ inds_old_i =[]
1293
+ deps_old_i =[]
1294
+ links_old_i=[]
1295
+ for k,ITE in enumerate(ITEs):
1296
+ if k==0:
1297
+ inds_new[ITE.ind[2]]=i
1298
+ inds_old_i.append(copy.deepcopy(ITE.ind))
1299
+ deps_old_i.append(copy.deepcopy(ITE.dependencies))
1300
+ links_old_i.append(copy.deepcopy(ITE.link))
1301
+ ITE:ITEpar
1302
+ ITE.ind[0]=self.TREpar.project
1303
+ ITE.ind[1]=int(self.FlagBin)
1304
+ ITE.ind[2]=i
1305
+ pass
1306
+ inds_old[i]=inds_old_i
1307
+ deps_old[i]=deps_old_i
1308
+ links_old[i]=links_old_i
1309
+ if i<kin or i>kfin: continue
1310
+ for processPrev in self.itemList[1:]:
1311
+ try:
1312
+ for stepPrev in processPrev[i]:
1313
+ for par in stepPrev:
1314
+ if par and not par.FlagNone:
1315
+ par:TABpar
1316
+ pri.Coding.blue(f'{ITEs[0].name} {par.surname} {par.ind} ---> [{self.TREpar.project}, {int(self.FlagBin)}, {i}, {par.ind[3]}, {par.ind[4]}]')
1317
+ par.ind[0]=self.TREpar.project
1318
+ par.ind[1]=int(self.FlagBin)
1319
+ par.ind[2]=i
1320
+ except Exception as inst:
1321
+ pri.Error.red(f"{inst}\n{traceback.format_exc()}")
1322
+ pass
1323
+ if hasattr(self.gui,'setLinks'):
1324
+ inds_new_list=list(inds_new)
1325
+ FlagUnlink=True
1326
+ for i in range(self.topLevelItemCount()):
1327
+ ITEs=ITEList[i]
1328
+ inds_old_i=inds_old[i]
1329
+ deps_old_i=deps_old[i]
1330
+ links_old_i=links_old[i]
1331
+ for ITE,ind_old,dep_old,link_old in zip(ITEs,inds_old_i,deps_old_i,links_old_i):
1332
+ for k,ind_slave_old in enumerate(dep_old):
1333
+ if ind_slave_old[2] not in inds_new_list: #stiamo rigenerando l'albero
1334
+ ITE.dependencies[k]=copy.deepcopy(dep_old[k])
1335
+ else:
1336
+ ind_slave_new=copy.deepcopy(ind_slave_old)
1337
+ ind_slave_new[2]=inds_new[ind_slave_new[2]]
1338
+ self.gui.setLinks(ind_slave_old,ind_old,FlagUnlink,ind_slave_new,ITE.ind)
1339
+ if ind_slave_new[1]==0 and ITE.ind[1]==0:
1340
+ self.gui.setLinks(ind_slave_new,ITE.ind)
1341
+ pass
1342
+ if link_old in inds_new_list:
1343
+ link_new=copy.deepcopy(link_old)
1344
+ link_new[2]=inds_new[link_new[2]]
1345
+ self.gui.setLinks(ind_old,link_old,FlagUnlink,ITE.ind,link_new)
1346
+ if link_new[1]==0 and ITE.ind[1]==0:
1347
+ self.gui.setLinks(ITE.ind,link_new)
1348
+ pass
1349
+ else: #stiamo rigenerando l'albero
1350
+ ITE.link=copy.deepcopy(link_old)
1351
+
1352
+ def dragEnterEvent(self, event):
1353
+ if type(self).eventSender is None: type(self).eventSender=self
1354
+ if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist') and type(self).eventSender!=self:
1355
+ self.FlagExternalDrag=True
1356
+ self.setStyleSheet(self.initialStyleSheet+"QTreeWidget { background-color: rgba(0, 116, 255, 0.2); }")
1357
+ event.accept()
1358
+ elif event.mimeData().hasFormat('application/x-button') and not self.FlagBin:
1359
+ self.FlagExternalDrag=True
1360
+ self.dragged_items='externalItem'
1361
+ self.setStyleSheet(self.initialStyleSheet+"QTreeWidget { background-color: rgba(0, 116, 255, 0.2); }")
1362
+ event.accept()
1363
+ else:
1364
+ super().dragEnterEvent(event)
1365
+
1366
+ def dragMoveEvent(self, event):
1367
+ if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist') and type(self).eventSender!=self:
1368
+ event.setDropAction(Qt.MoveAction)
1369
+ event.accept()
1370
+ elif event.mimeData().hasFormat('application/x-button') and not self.FlagBin:
1371
+ super().dragMoveEvent(event)
1372
+ self.repaint()
1373
+ event.setDropAction(Qt.MoveAction)
1374
+ event.accept()
1375
+ else:
1376
+ super().dragMoveEvent(event)
1377
+
1378
+ def dragLeaveEvent(self,event):
1379
+ self.restoreStyleAfterDrag()
1380
+ super().dragLeaveEvent(event)
1381
+
1382
+ def restoreStyleAfterDrag(self):
1383
+ if self.FlagExternalDrag:
1384
+ self.FlagExternalDrag=False
1385
+ self.dragged_items=None
1386
+ self.setStyleSheet(self.initialStyleSheet)
1387
+ if self.parent(): self.setPalette(self.parent().palette())
1388
+ self.hovered_item=None
1389
+
1390
+ def dropEvent(self, event):
1391
+ if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist') and type(self).eventSender!=self:
1392
+ if self.FlagBin:
1393
+ self.restoreTree.copy_cut_delete_action(FlagDelete=True)
1394
+ else:
1395
+ self.restoreTree.copy_cut_delete_action(FlagRestore=True)
1396
+ self.restoreStyleAfterDrag()
1397
+ event.setDropAction(Qt.MoveAction)
1398
+ event.accept()
1399
+ elif event.mimeData().hasFormat('application/x-button') and not self.FlagBin:
1400
+ if not self.hovered_item or self.gui.FlagRun:
1401
+ ind=self.topLevelItemCount()
1402
+ else:
1403
+ ind=self.indexOfTopLevelItem(self.hovered_item)+1
1404
+
1405
+ t = json.loads(event.mimeData().data('application/x-button').data().decode())
1406
+ item=self.createProcessItem(t,ind)
1407
+ self.setCurrentItem(item)
1408
+ item.setSelected(True)
1409
+
1410
+ self.restoreStyleAfterDrag()
1411
+ event.setDropAction(Qt.MoveAction)
1412
+ event.accept()
1413
+ else:
1414
+ super().dropEvent(event)
1415
+ for item in self.selectedItems():
1416
+ k=self.indexOfTopLevelItem(item)
1417
+ ITEs=self.itemList[0][k]
1418
+ self.setProcessItemWidget(item,ITEs[0])
1419
+ self.setStepItemWidgets(item,ITEs[0].ind[2])
1420
+ type(self).eventSender=None
1421
+ return
1422
+
1423
+ def createParPrevs(self,ITE:ITEpar):
1424
+ if not self.widgets: return
1425
+ for w in self.widgets:
1426
+ w:gPaIRS_Tab
1427
+ w.gen_TABpar(ITE.ind,FlagEmptyPrev=True,FlagInsert=2,Process=ITE.Process,Step=ITE.Step)
1428
+ pass
1429
+ return
1430
+
1431
+ def createPars(self,ITE:ITEpar):
1432
+ if not self.widgets: return
1433
+ for t in ITE.tabs+['TabArea']:
1434
+ if t in self.widgetNames:
1435
+ k=self.widgetNames.index(t)
1436
+ w:gPaIRS_Tab=self.widgets[k]
1437
+ w.gen_TABpar(ITE.ind,FlagInsert=3,Process=ITE.Process,Step=ITE.Step)
1438
+ pass
1439
+ for w, wn in zip(self.widgets,self.widgetNames):
1440
+ if wn not in ITE.tabs+['TabArea']:
1441
+ w.gen_TABpar(ITE.ind,FlagNone=True,FlagInsert=3,Process=ITE.Process,Step=ITE.Step)
1442
+ pass
1443
+ return
1444
+
1445
+ def createProcessItem(self,type,ind=None,FlagNewItem=True,name=None):
1446
+ self.blockSignals(True)
1447
+ if ind==None: ind=self.topLevelItemCount()
1448
+ item = QTreeWidgetItem()
1449
+
1450
+ if FlagNewItem:
1451
+ ITE=ITEpar(Process=type,Step=None)
1452
+ ITE.FlagQueue=self.TREpar.FlagQueue
1453
+ ITE.ind=[self.TREpar.project,int(self.FlagBin),ind,0,0]
1454
+
1455
+ processNames=[item[0].name for item in self.itemList[0]]+[item[0].name for item in self.restoreTree.itemList[0]]
1456
+ if name is None:
1457
+ nameInd=1
1458
+ ITE.name=f'{ITE.basename} {nameInd}'
1459
+ while ITE.name in processNames:
1460
+ nameInd+=1
1461
+ ITE.name=f'{ITE.basename} {nameInd}'
1462
+ else:
1463
+ ITE.name=name
1464
+ nameInd=1
1465
+ while ITE.name in processNames:
1466
+ nameInd+=1
1467
+ ITE.name=f'{name} ({nameInd})'
1468
+ self.createParPrevs(ITE)
1469
+ self.itemList[0].insert(ind,[ITE])
1470
+ else:
1471
+ ITE=self.itemList[0][ind][0]
1472
+
1473
+ item.setText(0,str(ind))
1474
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
1475
+ item.setIcon(1, TreeIcons.icons[ITE.icon]) # Set the icon of the new item
1476
+ #item.setFlags(item.flags() | Qt.ItemIsEditable)
1477
+
1478
+ ind=max([0,ind])
1479
+ self.insertTopLevelItem(ind,item)
1480
+ self.setProcessItemWidget(item,ITE)
1481
+ self.createStepItems(item,ITE)
1482
+ self.setStepItemWidgets(item,ind)
1483
+ item.setExpanded(True)
1484
+
1485
+ item.setData(0,Qt.ItemDataRole.UserRole,[False])
1486
+ self.resetImNumber(ind)
1487
+
1488
+ self.restoreStyleAfterDrag()
1489
+ self.blockSignals(False)
1490
+ if FlagNewItem: self.signals.updateLists.emit()
1491
+ return item
1492
+
1493
+ def setProcessItemWidget(self,item:QTreeWidgetItem,ITE:ITEpar):
1494
+ title=ITE.name
1495
+ title_label = ResizingLabel(title)
1496
+ title_label.setObjectName('title_process')
1497
+ title_label.setFixedHeight(titleHeight)
1498
+ title_label.setMinimumWidth(titleNameWidth)
1499
+ title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
1500
+ # Set the font of the title to bold and the subtitle to double font
1501
+ self.titleFont(title_label)
1502
+
1503
+ if not ITE.date:
1504
+ ITE.date = f"Created: {currentTimeString()}"
1505
+ sub_title=ITE.date
1506
+ sub_title_label = ResizingLabel(sub_title) # Create a QLabel for the subtitle
1507
+ sub_title_label.setObjectName('subtitle_process')
1508
+ sub_title_label.setFixedHeight(subtitleHeight)
1509
+ sub_title_label.setMinimumWidth(titleNameWidth)
1510
+ sub_title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
1511
+ self.subTitleFont(sub_title_label)
1512
+
1513
+ process_title_layout = QVBoxLayout() # Create a vertical layout to place the subtitle below the title
1514
+ process_title_layout.addWidget(title_label) # Add the title of the element
1515
+ process_title_layout.addWidget(sub_title_label) # Add the subtitle
1516
+ process_title_layout.setContentsMargins(10, 2, 0, 2) # Remove margins
1517
+ process_title_layout.setSpacing(2) # Remove margins
1518
+
1519
+ switch = ModernSwitch(parent=self,name='Process',par=ITE)
1520
+ switch.setSwitch(ITE.FlagQueue)
1521
+ switch.toggled.connect(lambda: self.toogleProject(ITE))
1522
+ switch.setVisible(ITE.flagRun<=0 and ITE.Process!=ProcessTypes.cal and not self.FlagBin)
1523
+
1524
+ #spacer=QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
1525
+ main_layout=QHBoxLayout()
1526
+ main_layout.addLayout(process_title_layout)
1527
+ #main_layout.addSpacerItem(spacer)
1528
+ main_layout.addWidget(switch)
1529
+ main_layout.setContentsMargins(0,0,10,0)
1530
+ main_layout.setSpacing(6)
1531
+ main_layout.setStretch(0,0)
1532
+ main_layout.setStretch(1,1)
1533
+
1534
+ InfoMessage=ITE.InfoMessage()
1535
+ title_label.setToolTip(InfoMessage)
1536
+ title_label.setStatusTip(InfoMessage)
1537
+ sub_title_label.setToolTip(InfoMessage)
1538
+ sub_title_label.setStatusTip(InfoMessage)
1539
+
1540
+ widget = QWidget(self) # Create a widget container for the layout
1541
+ widget.setLayout(main_layout) # Set the layout for the widget container
1542
+ widget.setMinimumWidth(titleNameWidth+switch.switchWidth+main_layout.spacing())
1543
+ self.setItemWidget(item, 1 ,widget)
1544
+ return widget
1545
+
1546
+ def toogleProject(self,ITE:ITEpar):
1547
+ #self.projectTree.itemList[0][ITE.ind[0]].FlagQueue = ITE.FlagQueue
1548
+ topLevelItem=self.Explorer.projectTree.topLevelItem(ITE.ind[0])
1549
+ itemWidget=self.Explorer.projectTree.itemWidget(topLevelItem,1)
1550
+ switch:ModernSwitch=itemWidget.findChildren(ModernSwitch)[0]
1551
+ FlagToogle=False
1552
+ if ITE.FlagQueue==True and switch._checked==False: FlagToogle=True
1553
+ elif ITE.FlagQueue==False:
1554
+ ITELists=self.Explorer.projectTree.itemList[1][ITE.ind[0]][0]
1555
+ if all([i[0].FlagQueue==False for i in ITELists if i[0].flagRun<=0 and i[0].Process!=ProcessTypes.cal]): FlagToogle=True
1556
+ if FlagToogle:
1557
+ switch.blockSignals(True)
1558
+ switch.toggle()
1559
+ switch.blockSignals(False)
1560
+ if self.Explorer.TREpar.process is not None:
1561
+ self.Explorer.ITEpar.FlagQueue=self.Explorer.ITEfromInd(self.Explorer.ITEpar.ind).FlagQueue
1562
+ self.Explorer.TREpar.FlagQueue=self.Explorer.projectTree.itemList[0][self.Explorer.TREpar.project].FlagQueue
1563
+ self.adjustSwitches()
1564
+ # self.itemSelectionChanged.emit()
1565
+
1566
+ def createStepItems(self,parentItem:QTreeWidgetItem,parentITE:ITEpar):
1567
+ indItem=parentITE.ind[2]
1568
+ #ITEs=self.itemList[0][indItem]
1569
+ indChild=-1
1570
+ for stepType,v in parentITE.children.items():
1571
+ QTreeWidgetItem(parentItem)
1572
+ indChild+=1
1573
+ if indChild>=len(self.itemList[0][indItem])-1:
1574
+ ITE=ITEpar(Process=parentITE.Process,Step=stepType)
1575
+ ITE.ind[0]=parentITE.ind[0]
1576
+ ITE.ind[1]=parentITE.ind[1]
1577
+ ITE.ind[2]=indItem
1578
+ ITE.ind[3]=indChild
1579
+ ITE.active=v
1580
+ self.itemList[0][indItem].append(ITE)
1581
+ self.createPars(ITE)
1582
+ return
1583
+
1584
+ def setStepItemWidgets(self,parentItem:QTreeWidgetItem,indItem):
1585
+ for k,data in enumerate(self.itemList[0][indItem][1:]):
1586
+ data:ITEpar
1587
+ item:QTreeWidgetItem=parentItem.child(k)
1588
+ itemWidget=StepItemWidget(data,parent=self)
1589
+ self.setItemWidget(item,1,itemWidget)
1590
+ if len(data.link)==0:
1591
+ if self.gui and data.flagRun==0 and len(self.gui.ui.tabAreaWidget.TABpar_prev_at(data.ind))<=1:
1592
+ item.setIcon(0,self.uneditedIcon)
1593
+ else:
1594
+ item.setIcon(0,QIcon())
1595
+ else:
1596
+ item.setIcon(0,self.linkedIcon)
1597
+ if not data.active:
1598
+ item.setHidden(True)
1599
+
1600
+ def titleFont(self,label:QLabel,fPixelSize=None):
1601
+ if fPixelSize is None:
1602
+ if hasattr(self.gui,'GPApar'): fPixelSize=self.gui.GPApar.fontPixelSize
1603
+ else: fPixelSize=fontPixelSize
1604
+ font = label.font()
1605
+ font.setFamily(fontName)
1606
+ font.setBold(True) # Bold title
1607
+ fPS=min([fPixelSize+dFontPixelSize_processTitle,fontPixelSize+dFontPixelSize_processTitle+3])
1608
+ font.setPixelSize(fPS) # Double font size for subtitle
1609
+ label.setFont(font)
1610
+
1611
+ def subTitleFont(self,label:QLabel,fPixelSize=None):
1612
+ if fPixelSize is None:
1613
+ if hasattr(self.gui,'GPApar'): fPixelSize=self.gui.GPApar.fontPixelSize
1614
+ else: fPixelSize=fontPixelSize
1615
+ font = label.font()
1616
+ font.setFamily(fontName)
1617
+ font.setItalic(True) # Bold title
1618
+ fPS=min([fPixelSize,fontPixelSize+3])
1619
+ font.setPixelSize(fPS) # Double font size for subtitle
1620
+ label.setFont(font)
1621
+
1622
+ def startEditing(self, item, column):
1623
+ if self.itemWidget(item, column):
1624
+ self.editItem(item, column)
1625
+
1626
+ def editItem(self, item, column):
1627
+ if column == 0 or item.parent() is not None: # Se è la prima colonna, non fare nulla
1628
+ return
1629
+ widget = self.itemWidget(item, column)
1630
+ if isinstance(widget, QLineEdit):
1631
+ return # If already editing, do nothing
1632
+ k=self.indexOfTopLevelItem(item)
1633
+ text = self.itemList[0][k][0].name
1634
+ line_edit = CustomLineEdit(text, self)
1635
+ self.titleFont(line_edit)
1636
+ line_edit.setText(text)
1637
+ self.setItemWidget(item, column, line_edit)
1638
+ line_edit.selectAll()
1639
+ line_edit.setFocus()
1640
+ line_edit.editingFinished.connect(lambda: self.finishEditing(item, column))
1641
+ line_edit.cancelEditing.connect(lambda: self.finishEditing(item, column))
1642
+
1643
+ def finishEditing(self, item, column):
1644
+ line_edit = self.itemWidget(item, column)
1645
+ if not hasattr(line_edit,'text'):
1646
+ return
1647
+ new_text = line_edit.text()
1648
+ k=self.indexOfTopLevelItem(item)
1649
+ data=self.itemList[0][k]
1650
+ data[0].name=new_text
1651
+ self.setProcessItemWidget(item, data[0])
1652
+ line_edit.editingFinished.disconnect()
1653
+ line_edit.cancelEditing.disconnect()
1654
+ self.editingFinished()
1655
+
1656
+ def keyPressEvent(self, event):
1657
+ self.actionBar.treeKeyPressEvent(event)
1658
+
1659
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter) and event.modifiers()==Qt.KeyboardModifier.ShiftModifier:
1660
+ self.currentItem().setExpanded(False)
1661
+ return True # Impedisce al widget di ricevere l'evento
1662
+ elif event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter):
1663
+ self.currentItem().setExpanded(True)
1664
+ return True # Impedisce al widget di ricevere l'evento
1665
+ elif event.key()==Qt.Key.Key_Escape:
1666
+ self.setCurrentItem(None)
1667
+ self.clearSelection()
1668
+ self.Explorer.arrangeCurrentProcess(self)
1669
+ return True
1670
+ else:
1671
+ super().keyPressEvent(event)
1672
+
1673
+ def cutItems(self,items,FlagDelete=False):
1674
+ cutted_items=[None]*len(items)
1675
+ items_expanded=[None]*len(items)
1676
+ for k,item in enumerate(items):
1677
+ item:QTreeWidgetItem
1678
+ cutted_items[k]=self.duplicateItem(item)
1679
+ items_expanded[k]=item.isExpanded()
1680
+ if FlagDelete: type(self).deleted_items=cutted_items
1681
+ else: type(self).cutted_items=cutted_items
1682
+ type(self).items_expanded=items_expanded
1683
+ return
1684
+
1685
+ def button_import_action(self):
1686
+ exts=[f'*{e}' for e in [outExt.min,outExt.piv,outExt.cal,outExt.spiv]]
1687
+ dirname=os.path.dirname(self.TREpar.outName) if self.TREpar.outName else ''
1688
+ filename, _ = QFileDialog.getOpenFileName(self.gui,\
1689
+ "Select a PaIRS process file", filter=' '.join(exts),\
1690
+ dir=dirname,\
1691
+ options=optionNativeDialog)
1692
+ if not filename: return
1693
+ FlagError=False
1694
+ errorString=''
1695
+ try:
1696
+ from .Saving_tools import loadList
1697
+ data, errorMessage=loadList(filename)
1698
+ errorString+=errorMessage
1699
+ except Exception as inst:
1700
+ errorString+=str(inst)
1701
+ if errorString:
1702
+ if '_data' in filename:
1703
+ try:
1704
+ errorString=''
1705
+ basename,ext=os.path.splitext(filename)
1706
+ filename2=basename[:-5]+ext
1707
+ data, errorMessage=loadList(filename2)
1708
+ #errorString=errorString+errorMessage if errorMessage else ''
1709
+ if errorMessage:
1710
+ WarningMessage="It was not possible to determine which process the selected data file belongs to.\nPlease, try again by loading the process output file directly."
1711
+ warningDialog(self,WarningMessage)
1712
+ except Exception as inst2:
1713
+ errorString+=str(inst2)
1714
+ FlagError=True
1715
+ else:
1716
+ filename=filename2
1717
+ else: FlagError=True
1718
+ if FlagError or errorString:
1719
+ WarningMessage=f'Error with loading the file: {filename}\n'
1720
+ warningDialog(self,WarningMessage)
1721
+ pri.Error.red(f'{WarningMessage}\n{errorString}\n')
1722
+ return
1723
+ try:
1724
+ ind=self.topLevelItemCount()
1725
+ insert_at_depth(self.itemList,self.listDepth,[ind],data)
1726
+ ITEs:TREpar=self.itemList[0][ind]
1727
+ for ITE in ITEs:
1728
+ ITE:ITEpar
1729
+ ITE.ind[0]=self.TREpar.project
1730
+ ITE.ind[1]=int(self.FlagBin)
1731
+ ITE.ind[2]=ind
1732
+ ITE.link=[]
1733
+ ITE.dependencies=[]
1734
+ self.createProcessItem(ind,FlagNewItem=False)
1735
+ for ITE in ITEs: self.Explorer.setITElayout(ITE)
1736
+ item=self.topLevelItem(ind)
1737
+ self.setCurrentItem(item)
1738
+ item.setSelected(True)
1739
+ except Exception as inst:
1740
+ WarningMessage=f'Error while retrieving the process "{data[0][0][0].name}" from the file: {filename}\n'
1741
+ warningDialog(self,WarningMessage)
1742
+ pri.Error.red(f'{WarningMessage}\n{traceback.format_exc()}\n')
1743
+ return
1744
+
1745
+ def save_current_process(self,filename):
1746
+ Process=[]
1747
+ FlagSaved=False
1748
+ for l in self.itemList:
1749
+ Process.append([l[self.TREpar.process]])
1750
+ try:
1751
+ from .Saving_tools import saveList
1752
+ saveList(Process,filename)
1753
+ except Exception as inst:
1754
+ warningDialog(self,f'Error while saving the configuration file {filename}!\nPlease, retry.')
1755
+ pri.Error.red(f'Error while saving the configuration file {filename}!\n{inst}')
1756
+ return FlagSaved
1757
+
1758
+ def button_saveas_action(self):
1759
+ for attr_name, attr_value in vars(ProcessTypes).items():
1760
+ if attr_value == self.ITEpar.Process:
1761
+ procExt=getattr(outExt,attr_name)
1762
+ break
1763
+ Title="Select location and name of the process file to save"
1764
+ dirname=os.path.dirname(self.TREpar.outName) if self.TREpar.outName else ''
1765
+ filename, _ = QFileDialog.getSaveFileName(self,Title,
1766
+ dir=dirname+self.ITEpar.name.replace(' ','_'),
1767
+ filter=f'*{procExt}',\
1768
+ options=optionNativeDialog)
1769
+ if not filename: return
1770
+ if len(filename)>=len(procExt):
1771
+ if filename[-len(procExt):]==procExt: filename=filename[:-len(procExt)] #per adattarlo al mac
1772
+ filename=myStandardRoot('{}'.format(str(filename)))
1773
+ if not outExt.cfg in filename:
1774
+ filename=filename+procExt
1775
+ self.save_current_process(filename)
1776
+
1777
+ def button_info_action(self):
1778
+ warningDialog(self,self.ITEpar.InfoMessage(),pixmap=TreeIcons.pixmaps[self.ITEpar.icon],title='Process information')
1779
+
1780
+ def button_rename_action(self):
1781
+ selected_items = self.selectedItems()
1782
+ if selected_items:
1783
+ item = selected_items[0]
1784
+ #column = self.currentColumn()
1785
+ #self.editItem(item, column)
1786
+ self.editItem(item, 1)
1787
+
1788
+ def deleteDependencies(self,indexes):
1789
+ if hasattr(self.gui,'setLinks'):
1790
+ for i in indexes:
1791
+ for ITE in self.itemList[0][i]:
1792
+ ITE:ITEpar
1793
+ for dep in ITE.dependencies:
1794
+ self.gui.setLinks(dep,ITE.ind,FlagUnlink=True)
1795
+ if ITE.link: self.gui.setLinks(ITE.ind,ITE.link,FlagUnlink=True)
1796
+
1797
+ def copy_cut_delete_action(self,FlagCut=False,FlagDelete=False,FlagRestore=False):
1798
+ items,indexes=self.selectTopLevel()
1799
+ if FlagRestore: FlagDelete=True
1800
+ if FlagDelete and not FlagRestore:
1801
+ FlagCut=False
1802
+ if self.FlagBin:
1803
+ flagYes=questionDialog(self,f"Are you sure you want to remove the selected process{'es' if len(items)>1 else ''}? This operation is irreversible!")
1804
+ else: flagYes=True
1805
+ if not flagYes: return
1806
+ if FlagDelete and not self.FlagBin: self.deleteDependencies(indexes)
1807
+ self.cutItems(items,FlagDelete)
1808
+
1809
+ FlagSignal=True
1810
+ if FlagCut or FlagDelete:
1811
+ if len(indexes)<1000:
1812
+ for item in items:
1813
+ self.takeTopLevelItem(self.indexOfTopLevelItem(item))
1814
+ if not FlagDelete: self.cutLists(indexes)
1815
+ else: self.deleteLists(indexes)
1816
+ else:
1817
+ if not FlagDelete: self.cutLists(indexes)
1818
+ else: self.deleteLists(indexes)
1819
+ FlagSignal=False
1820
+ else:
1821
+ self.copyLists(indexes)
1822
+
1823
+ if FlagCut or FlagDelete:
1824
+ self.setCurrentItem(None)
1825
+ self.clearSelection()
1826
+ self.setFocus()
1827
+ if FlagSignal and not self.signalsBlocked(): self.signals.updateLists.emit()
1828
+ if FlagRestore or (not self.FlagBin and FlagDelete):
1829
+ self.restore_action()
1830
+
1831
+ def restore_action(self):
1832
+ self.restoreTree:ProcessTree
1833
+ self.restoreTree.paste_above_below_action(FlagAbove=not self.FlagBin,row=0 if not self.FlagBin else self.restoreTree.topLevelItemCount()-1,FlagPasteDeleted=True,FlagSelectAfterPasting=True)
1834
+
1835
+ def paste_above_below_action(self,FlagAbove=True,row=None,FlagPasteDeleted=False,FlagSelectAfterPasting=False):
1836
+ if len(type(self).cutted_items)==0 and not FlagPasteDeleted: return
1837
+ elif len(type(self).deleted_items)==0 and FlagPasteDeleted: return
1838
+ self.blockSignals(True)
1839
+ selectedItems,indexes=self.selectTopLevel()
1840
+ self.blockSignals(False)
1841
+ #self.clearSelection()
1842
+ if FlagPasteDeleted: new_items=type(self).deleted_items
1843
+ else: new_items=type(self).cutted_items
1844
+ if FlagAbove:
1845
+ if row is None:
1846
+ if selectedItems: row=indexes[0]
1847
+ else: row=0
1848
+ firstItemToScroll=new_items[0]
1849
+ lastItemToScroll=new_items[-1]
1850
+ else:
1851
+ if row is None:
1852
+ if selectedItems: row=indexes[-1]+1
1853
+ else: row=self.topLevelItemCount()
1854
+ else: row=row+1
1855
+ firstItemToScroll=new_items[-1]
1856
+ lastItemToScroll=new_items[0]
1857
+ self.insertItems2List(row,new_items,True,FlagSignal=False)
1858
+ for k,item in enumerate(new_items):
1859
+ item:QTreeWidgetItem
1860
+ item.setExpanded(type(self).items_expanded[k])
1861
+
1862
+ indexes=[row+k for k in range(len(new_items))]
1863
+ if not self.FlagCutted and not FlagPasteDeleted:
1864
+ self.cutItems(new_items)
1865
+ self.pasteLists(row,FlagPasteDeleted)
1866
+ name_list=[i[0].name for i in self.itemList[0]]
1867
+ for pos,k in enumerate(indexes):
1868
+ item=self.topLevelItem(k)
1869
+ ITE=self.itemList[0][k][0]
1870
+ tail=set(indexes[pos:]);
1871
+ forbidden={name_list[j] for j in range(len(name_list)) if j not in tail}
1872
+ if ITE.name in forbidden:
1873
+ base=ITE.name
1874
+ n=1
1875
+ while f"{base} ({n})" in forbidden: n+=1
1876
+ ITE.name=f"{base} ({n})"
1877
+ name_list[k]=ITE.name
1878
+ self.setProcessItemWidget(item,ITE)
1879
+ self.setStepItemWidgets(item,ITE.ind[2])
1880
+
1881
+ self.scrollToItem(firstItemToScroll)
1882
+ self.scrollToItem(lastItemToScroll)
1883
+ if FlagPasteDeleted and not FlagSelectAfterPasting:
1884
+ type(self).deleted_items=[]
1885
+ self.clearSelection()
1886
+ else:
1887
+ self.setCurrentItem(new_items[-1])
1888
+ new_items[-1].setSelected(True)
1889
+ if FlagPasteDeleted: self.Explorer.processTree_item_selection(self)
1890
+ self.setFocus()
1891
+ self.signals.updateLists.emit()
1892
+
1893
+ def button_copy_action(self):
1894
+ self.copy_cut_delete_action()
1895
+ self.setProcessActionButtonLayout()
1896
+
1897
+ def button_paste_below_action(self):
1898
+ self.blockSignals(True)
1899
+ self.paste_above_below_action(FlagAbove=False)
1900
+ self.blockSignals(False)
1901
+ self.itemSelectionChanged.emit()
1902
+
1903
+ def button_paste_above_action(self):
1904
+ self.blockSignals(True)
1905
+ self.paste_above_below_action()
1906
+ self.blockSignals(False)
1907
+ self.itemSelectionChanged.emit()
1908
+
1909
+ def button_process_loop_action(self):
1910
+ pri.Info.magenta('Process loop over folders')
1911
+ paths=choose_directories()
1912
+ if paths:
1913
+ ITEs=self.Explorer.ITEsfromTRE(self.TREpar)
1914
+ ind=copy.deepcopy(ITEs[0].ind)
1915
+ processType=ITEs[0].Process
1916
+ pixmap_list=[]
1917
+ name_list=[]
1918
+ flag_list=[]
1919
+ for i in range(1,len(ITEs)):
1920
+ ITE:ITEpar=ITEs[i]
1921
+ pixmap_list.append(icons_path+ITE.icon)
1922
+ name_list.append(ITE.name)
1923
+ flag_list.append(ITE.Step!=StepTypes.cal)
1924
+ func=lambda i, opts, cleanup_flag, rescan_flag: self.process_loop(paths,processType,ind,ITEs[0].name,i,opts,cleanup_flag,rescan_flag)
1925
+ dialog = FolderLoopDialog(pixmap_list, name_list, flag_list, parent=self, paths=paths, func=func, process_name=ITEs[0].name)
1926
+ dialog.exec()
1927
+ return
1928
+
1929
+ def process_loop(self,paths,processType,ind,name0,i,opts,cleanup_flag,rescan_flag):
1930
+ nProcess=self.topLevelItemCount()
1931
+ path=paths[i]
1932
+ name=name0+f" (.../{os.path.basename(path)}/)"
1933
+ item=self.createProcessItem(processType,nProcess,name=name)
1934
+ ind_master=copy.deepcopy(ind)
1935
+ ind_master[-1]=-1
1936
+ ind_slave=copy.deepcopy(ind)
1937
+ ind_slave[2]=nProcess
1938
+ ind_slave[-1]=0
1939
+
1940
+ FlagWarning=False
1941
+ for j in range(len(opts)):
1942
+ ind_master[3]=j
1943
+ ind_slave[3]=j
1944
+ if opts[j] in (0,2):
1945
+ ind_new=self.gui.copy_pars(ind_slave,ind_master,FlagNew=True)
1946
+ item_child=item.child(j)
1947
+ ITE:ITEpar=self.Explorer.ITEfromInd(ind_new)
1948
+ item_child.setHidden(not ITE.active)
1949
+ if opts[j]==2:
1950
+ INP: INPpar=self.gui.w_Input.TABpar_at(ind_new)
1951
+ INP.path=myStandardPath(path)
1952
+ if rescan_flag:
1953
+ flagWarning=self.rescanInputPath(ind_master,ind_new,cleanup_flag)
1954
+ else:
1955
+ flagWarning=self.gui.w_Input.scanImList(ind_new)
1956
+ if cleanup_flag: self.gui.w_Input.purgeImList(ind_new)
1957
+ INP.nimg=0
1958
+ if len(INP.imList[0]):
1959
+ if len(INP.imList[0][0]):
1960
+ INP.nimg=len(INP.imList[0][0])
1961
+ self.gui.w_Input.checkINPpar(ind_new)
1962
+ self.gui.w_Input.setINPwarn(ind_new)
1963
+
1964
+ TABname=self.gui.w_Input.TABname
1965
+ self.gui.bridge(TABname,ind_new)
1966
+
1967
+ FlagSettingPar=TABpar.FlagSettingPar
1968
+ TABpar.FlagSettingPar=True
1969
+ for w in self.gui.tabWidgets:
1970
+ w:gPaIRS_Tab
1971
+ if w!=self.gui.w_Input:
1972
+ currpar=w.TABpar.duplicate()
1973
+ TABind_new=w.TABpar_at(ind_new)
1974
+ if TABind_new and not TABind_new.FlagNone:
1975
+ w.TABpar.copyfrom(TABind_new)
1976
+ w.adjustTABpar()
1977
+ w.adjustTABparInd()
1978
+ w.TABpar.copyfrom(currpar)
1979
+ self.gui.bridge(w.TABname,ind_new)
1980
+ TABpar.FlagSettingPar=FlagSettingPar
1981
+ self.Explorer.setITElayout(ITE)
1982
+ FlagWarning=FlagWarning or flagWarning or ITE.OptionDone==0
1983
+
1984
+ #item_child=item.child(j)
1985
+ #item_child.setSelected(True)
1986
+ #self.setCurrentItem(item_child)
1987
+ else:
1988
+ self.gui.link_pars(ind_slave,ind_master,FlagSet=False)
1989
+ return FlagWarning
1990
+
1991
+ def rescanInputPath(self, ind_master, ind_new, FlagNoWarning):
1992
+ """Rescan input on slave using master patterns, then rebuild imList/imEx."""
1993
+ INP: INPpar = self.gui.w_Input.TABpar_at(ind_new) # slave
1994
+ INPm: INPpar = self.gui.w_Input.TABpar_at(ind_master) # master
1995
+
1996
+ # Build A (for frame_1) and B (for frame_2) from master's pattern at master's frames
1997
+ patm = getattr(INPm.imSet, "pattern", [])
1998
+ ncam = INP.inp_ncam
1999
+ def _pat_list(frames,ind0=0):
2000
+ out=[];
2001
+ for k in range(ncam):
2002
+ f = frames[k]-ind0 if k < len(frames) else -1
2003
+ out.append(patm[f] if isinstance(f,int) and 0 <= f < len(patm) else None)
2004
+ return out
2005
+ A = _pat_list(INPm.frame_1)
2006
+ B = _pat_list(INPm.frame_2,1)
2007
+
2008
+ # Rescan slave input path with patterns=A,B (maps patterns -> frame indices on slave)
2009
+ self.gui.w_Input.scanInputPath(ind_new, patterns=[A, B], FlagNoWarning=FlagNoWarning)
2010
+
2011
+ # Rebuild imList/imEx from frames computed on slave
2012
+ INP.imList, INP.imEx = INP.imSet.genListsFromFrame(
2013
+ INP.frame_1, INP.frame_2, INP.ind_in, INP.npairs, INP.step, INP.FlagTR_Import
2014
+ )
2015
+ # Compare slave vs master lists
2016
+ def _lists_differ(a, b):
2017
+ if len(a) != len(b): return True
2018
+ for x, y in zip(a, b):
2019
+ if x != y: return True
2020
+ return False
2021
+
2022
+ FlagWarning = (
2023
+ _lists_differ(INP.imList, INPm.imList) or
2024
+ _lists_differ(INP.imEx, INPm.imEx)
2025
+ )
2026
+
2027
+ return FlagWarning
2028
+
2029
+
2030
+ def button_delete_action(self):
2031
+ self.blockSignals(True)
2032
+ self.copy_cut_delete_action(FlagDelete=True)
2033
+ self.blockSignals(False)
2034
+ self.itemSelectionChanged.emit()
2035
+
2036
+ def button_restore_action(self):
2037
+ self.copy_cut_delete_action(FlagRestore=True)
2038
+
2039
+ def button_clean_action(self):
2040
+ self.blockSignals(True)
2041
+ self.selectAll()
2042
+ self.copy_cut_delete_action(FlagDelete=True)
2043
+ self.repaint()
2044
+ self.blockSignals(False)
2045
+ #self.currentItemChanged.emit(None,None)
2046
+ self.itemSelectionChanged.emit()
2047
+
2048
+ def setProcessActionButtonLayout(self):
2049
+ item=self.currentItem()
2050
+ FlagEnabled=item is not None and item.parent() is None
2051
+ FlagPaste=len(ProcessTree.cutted_items)>0
2052
+ self.actionBar.setVisible(True) #self.actionBar.setVisible(FlagEnabled)
2053
+ self.actionBar.button_saveas.setEnabled(FlagEnabled)
2054
+ self.actionBar.button_info.setEnabled(FlagEnabled)
2055
+ self.actionBar.button_rename.setEnabled(FlagEnabled)
2056
+ self.actionBar.button_copy.setEnabled(FlagEnabled)
2057
+ self.actionBar.button_paste_below.setEnabled(FlagPaste and not self.gui.FlagRun)
2058
+ self.actionBar.button_paste_above.setEnabled(FlagPaste and not self.gui.FlagRun)
2059
+ self.actionBar.button_process_loop.setEnabled(FlagEnabled and not self.FlagBin)
2060
+ self.actionBar.button_restore.setVisible(self.FlagBin)
2061
+ self.actionBar.sep_restore.setVisible(self.FlagBin)
2062
+ self.actionBar.button_restore.setEnabled(FlagEnabled)
2063
+ self.actionBar.button_delete.setEnabled(FlagEnabled)
2064
+ self.actionBar.button_clean.setEnabled(self.topLevelItemCount()>0)
2065
+
2066
+ class StepItemWidget(QWidget):
2067
+ pixmaps={'error' : 'issue.png',
2068
+ 'done' : 'completed.png',
2069
+ 'running' : 'running.png',
2070
+ 'warning' : 'warning_circle.png',
2071
+ 'running_warn' : 'running_warn.png',
2072
+ 'paused' : 'paused.png',
2073
+ 'queue' : 'queue.png',
2074
+ 'uninitialized' : 'uninitialized.png'
2075
+ }
2076
+
2077
+ colors={'error' : 'rgb(254, 61, 61)', #'#C9302C',
2078
+ 'done' : 'rgb( 46,204,113)', #'#4CAE4C',
2079
+ 'running' : 'rgb( 48,107,255)', #'#3A70C7',
2080
+ 'warning' : 'rgb(255,212, 42)', #'#EC971F',
2081
+ 'running_warn' : 'rgb( 48,107,255)', #'#3A70C7',
2082
+ 'paused' : 'rgb(255,127, 42)', #'#3A70C7',
2083
+ 'queue' : 'rgb(181,170,255)', #'#B5AAFF',
2084
+ 'uninitialized' : 'rgb( 0,0,0)',
2085
+ }
2086
+
2087
+ label_size=secondLevelHeight-4
2088
+ bar_width=label_size*3+4
2089
+
2090
+ def __init__(self, ITE:ITEpar, parent=None):
2091
+ super().__init__(parent)
2092
+ if parent is None:
2093
+ self.gui=self.window()
2094
+ else:
2095
+ from .gPaIRS import gPaIRS
2096
+ if hasattr(parent,'gui'):
2097
+ self.gui:gPaIRS=parent.gui
2098
+ else:
2099
+ self.gui:gPaIRS=parent.window()
2100
+
2101
+ # Create the layout
2102
+ layout = QHBoxLayout(self)
2103
+
2104
+ # Add an icon
2105
+ self.stepIcon = QLabel()
2106
+ self.stepIcon.setFixedHeight(secondLevelHeight-2)
2107
+ self.stepIcon.setFixedWidth(secondLevelHeight-2)
2108
+ self.stepIcon.setScaledContents(True)
2109
+ self.stepIcon.setPixmap(TreeIcons.pixmaps[ITE.icon])
2110
+ layout.addWidget(self.stepIcon)
2111
+
2112
+ # Add a spacer
2113
+ spacer = QSpacerItem(5, secondLevelHeight, QSizePolicy.Minimum, QSizePolicy.Minimum)
2114
+ layout.addItem(spacer)
2115
+
2116
+ # Add a label with text
2117
+ self.stepName = QLabel()
2118
+ self.stepName.setObjectName('title_step')
2119
+ self.stepName.setFixedWidth(stepNameWidth)
2120
+ self.stepName.setFixedHeight(secondLevelHeight)
2121
+ self.stepTitleFont(self.stepName)
2122
+ layout.addWidget(self.stepName)
2123
+
2124
+ self.stepName.setText(ITE.name)
2125
+ self.stepName.setToolTip(ITE.name)
2126
+ self.stepName.setStatusTip(ITE.name)
2127
+
2128
+ # Add another icon
2129
+ self.label = ClickableLabel()
2130
+ self.label.setFixedWidth(self.label_size)
2131
+ self.label.setFixedHeight(self.label_size)
2132
+ self.label.setScaledContents(True)
2133
+ self.label.setToolTip(ITE.warningMessage)
2134
+ self.label.setStatusTip(ITE.warningMessage)
2135
+ layout.addWidget(self.label)
2136
+
2137
+ # Add a progress bar
2138
+ self.progressBar = QProgressBar()
2139
+ self.progressBar.setFixedWidth(self.bar_width)
2140
+ self.progressBar.setFixedHeight(self.label_size)
2141
+ layout.addWidget(self.progressBar)
2142
+
2143
+ # Add a spacer
2144
+ spacer = QSpacerItem(40, secondLevelHeight, QSizePolicy.Expanding, QSizePolicy.Minimum)
2145
+ layout.addItem(spacer)
2146
+
2147
+ # Set layout margin and spacing
2148
+ layout.setContentsMargins(secondLevelIndentation, 0, 0, 0)
2149
+ layout.setSpacing(secondLevelSpacing)
2150
+
2151
+ minimumWidth=self.stepIcon.width()+self.stepName.width()+self.label.width()+self.progressBar.width()+secondLevelSpacing*4+secondLevelIndentation
2152
+ self.setMinimumWidth(minimumWidth)
2153
+ self.setFromITE(ITE)
2154
+
2155
+ def stepTitleFont(self,label:QLabel,fPixelSize=None):
2156
+ if fPixelSize is None:
2157
+ if hasattr(self.gui,'GPApar'): fPixelSize=self.gui.GPApar.fontPixelSize
2158
+ else: fPixelSize=fontPixelSize
2159
+ font = label.font()
2160
+ font.setFamily(fontName)
2161
+ fPS=min([fPixelSize + dFontPixelSize_stepTitle, fontPixelSize + dFontPixelSize_stepTitle +3])
2162
+ font.setPixelSize(fPS) # Double font size for subtitle
2163
+ label.setFont(font)
2164
+
2165
+ def colorProgressBar(self,color='green'):
2166
+ style_sheet = f"""
2167
+ QProgressBar {{
2168
+ background-color: transparent;
2169
+ border: 1px solid gray;
2170
+ border-radius: 5px;
2171
+ text-align: center;
2172
+ }}
2173
+
2174
+ QProgressBar::chunk {{
2175
+ background-color: {color};
2176
+ width: 1px; /* Larghezza del chunk */
2177
+ margin: 0px; /* Spazio tra i chunk */
2178
+ }}
2179
+ """
2180
+ self.progressBar.setStyleSheet(style_sheet)
2181
+
2182
+ def setFromITE(self,ITE:ITEpar):
2183
+ pixmap_path=StepItemWidget.pixmaps[ITE.label]
2184
+ FlagGif=False
2185
+ if ITE.flagRun==-2 and ITE.label=='running' and hasattr(self.gui,'runningMovie'):
2186
+ if hasattr(self.gui,'procdata') and self.gui.procdata is ITE.procdata:
2187
+ FlagGif=True
2188
+ if FlagGif:
2189
+ self.label.moviePixmap=TreeIcons.pixmaps[pixmap_path]
2190
+ self.label.setMovie(self.gui.runningMovie)
2191
+ else:
2192
+ self.label.moviePixmap=None
2193
+ self.label.setPixmap(TreeIcons.pixmaps[pixmap_path])
2194
+ self.label.setToolTip(ITE.warningMessage)
2195
+ self.label.setStatusTip(ITE.warningMessage)
2196
+
2197
+ if ITE.Step==StepTypes.cal:
2198
+ self.progressBar.setVisible(ITE.ncam>0)
2199
+ self.progressBar.setMaximum(ITE.ncam)
2200
+ self.progressBar.setValue(ITE.progress)
2201
+ self.progressBar.setFormat(f'{ITE.progress}/{ITE.ncam}')
2202
+ elif ITE.Step==StepTypes.disp:
2203
+ self.progressBar.setVisible(ITE.procdata.Nit>0)
2204
+ self.progressBar.setMaximum(ITE.procdata.Nit)
2205
+ self.progressBar.setValue(ITE.progress)
2206
+ #self.progressBar.setValue(ITE.procdata.numFinalized)
2207
+ #self.progressBar.setFormat(f'{ITE.progress}/{ITE.procdata.Nit}')
2208
+ else:
2209
+ self.progressBar.setVisible(ITE.procdata.nimg>0)
2210
+ self.progressBar.setMaximum(ITE.procdata.nimg)
2211
+ self.progressBar.setValue(ITE.progress)
2212
+ #self.progressBar.setValue(ITE.procdata.numFinalized) # Set the initial value of the progress bar
2213
+
2214
+
2215
+ bar_color=StepItemWidget.colors[ITE.label]
2216
+ self.colorProgressBar(bar_color)
2217
+
2218
+ class PaIRS_Explorer(gPaIRS_Tab):
2219
+ class ProcessTreeWidget_signals(QObject):
2220
+ pass
2221
+
2222
+ def __init__(self,parent=None,widgets=[]):
2223
+ super().__init__(parent,UiClass=None,ParClass=ITEpar)
2224
+ if __name__ == "__main__":
2225
+ iconW = QIcon()
2226
+ iconW.addFile(u""+ icons_path +"checklist.png", QSize(), QIcon.Normal, QIcon.Off)
2227
+ self.setWindowTitle('Process tree widget')
2228
+ self.setWindowIcon(iconW)
2229
+ self.signals=self.ProcessTreeWidget_signals()
2230
+
2231
+ #------------------------------------- Layout
2232
+ self.treeIcons=TreeIcons()
2233
+
2234
+ self.main_layout = QVBoxLayout()
2235
+ self.setLayout(self.main_layout)
2236
+ self.main_layout.setContentsMargins(5, 5, 5, 5)
2237
+
2238
+ self.projectTree_layout= QVBoxLayout()
2239
+ self.projectTree_layout.setContentsMargins(0, 0, 0, 0)
2240
+ self.projectTree_layout.setSpacing(1)
2241
+
2242
+ self.projectActionBar = ActionButtonBar(buttonData=projectActionButtons,globalButtons=projectGlobalActionButtons,buttonSize=projectActionButtonSize)
2243
+ self.projectTree_layout.addWidget(self.projectActionBar)
2244
+ self.projectTree = ProjectTree(self,projectActionBar=self.projectActionBar,widgets=widgets,Explorer=self)
2245
+
2246
+ self.scroll_area_projectTree = QScrollArea(self)
2247
+ self.scroll_area_projectTree.setObjectName('scroll_area_projectTree')
2248
+ self.scroll_area_projectTree.setWidgetResizable(True)
2249
+ self.scroll_area_projectTree.setWidget(self.projectTree)
2250
+ self.scroll_area_projectTree.setMinimumWidth(0)
2251
+ self.scroll_area_projectTree.setStyleSheet(scrollAreaStyle())
2252
+ self.projectTree_layout.addWidget(self.scroll_area_projectTree)
2253
+
2254
+ self.processTree_layout= QVBoxLayout()
2255
+ spa=int(processButtonSize[0]/4)
2256
+ self.processTree_layout.setContentsMargins(0, spa, 0, 0)
2257
+ self.processTree_layout.setSpacing(1)
2258
+ self.processButtonBar = ProcessButtonBar(processData,buttonSize=processButtonSize)
2259
+ self.processTree_layout.addWidget(self.processButtonBar)
2260
+ self.processTree_layout.addItem(QSpacerItem(0,spa,QSizePolicy.Minimum, QSizePolicy.Minimum))
2261
+ self.processActionBar = ActionButtonBar(buttonData=processActionButtons,globalButtons=processGlobalActionButtons,buttonSize=processActionButtonSize)
2262
+ self.processTree_layout.addWidget(self.processActionBar)
2263
+ self.binActionBar = ActionButtonBar(buttonData=processActionButtons,globalButtons=processGlobalActionButtons,buttonSize=processActionButtonSize)
2264
+ self.processTree_layout.addWidget(self.binActionBar)
2265
+
2266
+ self.stepButton_layout=QHBoxLayout()
2267
+ self.stepButton_layout.setContentsMargins(0, 0, 0, 0)
2268
+ self.stepButton_layout.setSpacing(int(stepButtonSize[0]/4))
2269
+
2270
+ self.stepButtonBar=StepButtonBar(stepData,buttonSize=stepButtonSize)
2271
+ self.stepButton_layout.addWidget(self.stepButtonBar)
2272
+
2273
+ self.processActionBar.additionalButtonBars={'globals': [self.processButtonBar], 'items': [self.stepButtonBar]}
2274
+ self.binActionBar.additionalButtonBars={'items': [self.stepButtonBar]}
2275
+
2276
+ self.processTree = ProcessTree(self,processActionBar=self.processActionBar,widgets=widgets,Explorer=self)
2277
+ self.binTree = ProcessTree(self,restoreTree=self.processTree,processActionBar=self.binActionBar,widgets=widgets,Explorer=self)
2278
+ self.processTree.restoreTree=self.binTree
2279
+ itemWidths=[
2280
+ titleNameWidth+ModernSwitch.switchWidth+6, #first level
2281
+ stepNameWidth+StepItemWidget.label_size+StepItemWidget.bar_width+secondLevelSpacing*3 #second level
2282
+ ]
2283
+ self.projectTree.setMinimumWidth(max(itemWidths)+self.projectTree.columnWidth(0)-30)
2284
+ self.processTree.setMinimumWidth(max(itemWidths)+self.processTree.columnWidth(0)-30)
2285
+ self.binTree.setMinimumWidth(max(itemWidths)+self.binTree.columnWidth(0)+40)
2286
+
2287
+ self.Explorer_tree_splitter=QSplitter(self)
2288
+ self.Explorer_tree_splitter.setObjectName('Explorer_tree_splitter')
2289
+ self.Explorer_tree_splitter.setContentsMargins(0, 0, 0, 0)
2290
+ self.Explorer_tree_splitter.setOrientation(Qt.Horizontal)
2291
+ self.scroll_area_processTree = QScrollArea(self)
2292
+ self.scroll_area_processTree.setObjectName('scroll_area_processTree')
2293
+ self.scroll_area_processTree.setWidgetResizable(True)
2294
+ self.scroll_area_processTree.setWidget(self.processTree)
2295
+ self.scroll_area_processTree.setMinimumWidth(0)
2296
+ self.scroll_area_processTree.setStyleSheet(scrollAreaStyle())
2297
+ self.scroll_area_binTree = QScrollArea(self)
2298
+ self.scroll_area_binTree.setObjectName('scroll_area_binTree')
2299
+ self.scroll_area_binTree.setWidgetResizable(True)
2300
+ self.scroll_area_binTree.setWidget(self.binTree)
2301
+ self.scroll_area_binTree.setMinimumWidth(0)
2302
+ self.scroll_area_binTree.setStyleSheet(scrollAreaStyle())
2303
+ self.Explorer_tree_splitter.addWidget(self.scroll_area_processTree)
2304
+ self.Explorer_tree_splitter.addWidget(self.scroll_area_binTree)
2305
+ self.stepButton_layout.addWidget(self.Explorer_tree_splitter)
2306
+ self.processTree_layout.addLayout(self.stepButton_layout)
2307
+
2308
+ stretches=[0,0,0,0,1] #process bar, spacer, process action bar, bin action bar, trees
2309
+ for k,s in enumerate(stretches):
2310
+ self.processTree_layout.setStretch(k,s)
2311
+
2312
+ self.Explorer_main_splitter=QSplitter(self)
2313
+ self.Explorer_main_splitter.setObjectName('Explorer_main_splitter')
2314
+ self.Explorer_main_splitter.setContentsMargins(0, 0, 0, 0)
2315
+ self.Explorer_main_splitter.setOrientation(Qt.Vertical)
2316
+
2317
+ self.projectWidget=QWidget(self)
2318
+ self.projectWidget.setLayout(self.projectTree_layout)
2319
+ self.projectWidget.setMinimumHeight(150)
2320
+
2321
+ self.processWidget=QWidget(self)
2322
+ self.processWidget.setLayout(self.processTree_layout)
2323
+ self.processWidget.setMinimumHeight(300)
2324
+
2325
+ #self.scroll_area_sup = QScrollArea()
2326
+ #self.scroll_area_sup.setObjectName('scroll_area_sup')
2327
+ #self.scroll_area_sup.setWidgetResizable(True)
2328
+ #self.scroll_area_sup.setWidget(self.projectWidget)
2329
+ #self.scroll_area_sup.setMinimumHeight(150)
2330
+ #self.scroll_area_sup.setStyleSheet(scrollAreaStyle())
2331
+ #self.projectWidget.setMinimumHeight(250)
2332
+
2333
+ #self.scroll_area_inf = QScrollArea()
2334
+ #self.scroll_area_inf.setObjectName('scroll_area_inf')
2335
+ #self.scroll_area_inf.setWidgetResizable(True)
2336
+ #self.scroll_area_inf.setWidget(self.processWidget)
2337
+ #self.scroll_area_inf.setMinimumHeight(300)
2338
+ #self.scroll_area_inf.setStyleSheet(scrollAreaStyle())
2339
+ #self.processWidget.setMinimumHeight(400)
2340
+
2341
+ #self.main_splitter.addWidget(self.scroll_area_sup)
2342
+ #self.main_splitter.addWidget(self.scroll_area_inf)
2343
+
2344
+ self.Explorer_main_splitter.addWidget(self.projectWidget)
2345
+ self.Explorer_main_splitter.addWidget(self.processWidget)
2346
+
2347
+ self.Explorer_main_splitter.setHandleWidth(15)
2348
+ self.Explorer_main_splitter.setCollapsible(0, False)
2349
+ self.Explorer_main_splitter.setCollapsible(1, False)
2350
+ self.main_layout.addWidget(self.Explorer_main_splitter)
2351
+
2352
+ # Creazione del pulsante checkable
2353
+ self.binButton = QToolButton(self.processButtonBar)
2354
+ self.binButton.setIconSize(QSize(self.processButtonBar.buttonSize[0], self.processButtonBar.buttonSize[0]))
2355
+ self.binButton.setFixedSize(self.processButtonBar.buttonSize[1], self.processButtonBar.buttonSize[1])
2356
+ if self.processButtonBar.FlagInvisible:
2357
+ self.binButton.setCursor(Qt.CursorShape.PointingHandCursor)
2358
+ self.binButton.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }")
2359
+ self.binButton.pressed.connect(lambda btn=self.binButton: btn.setStyleSheet("QToolButton { border: none; background: #dcdcdc;} QToolButton::menu-indicator { image: none; }"))
2360
+ self.binButton.released.connect(lambda btn=self.binButton: btn.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }"))
2361
+ self.binButton.setToolTip('Deleted processes')
2362
+ self.binButton.setStatusTip('Deleted processes')
2363
+ self.processButtonBar.buttonLayout.addWidget(self.binButton)
2364
+ self.binIconOff=QIcon(''+ icons_path +'bin_off.png')
2365
+ self.binIconOn=QIcon(''+ icons_path +'bin_on.png')
2366
+ self.binButton.setCheckable(True)
2367
+ self.binButton.setChecked(False) # Mostra l'albero 2 di default
2368
+
2369
+ #------------------------------------- Declaration of parameters
2370
+ self.ITEpar:ITEpar=self.TABpar
2371
+ self.ITEpar.FlagNone=False
2372
+ self.processTree.ITEpar=self.ITEpar
2373
+ self.TREpar=self.projectTree.TREpar
2374
+ self.processTree.TREpar=self.projectTree.TREpar
2375
+ self.binTree.TREpar=self.projectTree.TREpar
2376
+ self.widgets=widgets
2377
+
2378
+ #------------------------------------- Callbacks
2379
+ self.currentTree=None
2380
+ self.binButton.clicked.connect(lambda: self.binButton_action(FlagTreeSelection=True))
2381
+ binShortCut=QShortcut(QKeySequence('Ctrl+B'),self)
2382
+ binShortCut.activated.connect(lambda:self.binButton.click())
2383
+ self.Explorer_tree_splitter.splitterMoved.connect(lambda: self.tree_splitter_action(FlagTreeSelection=True))
2384
+
2385
+ self.projectTree.installEventFilter(self)
2386
+ self.projectTree.actionBar.installEventFilter(self)
2387
+ self.processTree.installEventFilter(self)
2388
+ self.binTree.installEventFilter(self)
2389
+
2390
+ #self.processTree.currentItemChanged.connect(lambda: self.arrangeCurrentProcess(self.processTree))
2391
+ #self.binTree.currentItemChanged.connect(lambda: self.arrangeCurrentProcess(self.binTree))
2392
+ self.processTree.itemSelectionChanged.connect(lambda: self.processTree_item_selection(self.processTree))
2393
+ self.binTree.itemSelectionChanged.connect(lambda: self.processTree_item_selection(self.binTree))
2394
+ self.adjustProcessSelection=lambda: None
2395
+ self.binButton_action()
2396
+ #self.processTree_item_selection(self.processTree)
2397
+
2398
+ self.projectTree.adjustSelection=self.adjustProjectSelection
2399
+ self.adjustProjectSelection()
2400
+
2401
+ self.projectPageActions={}
2402
+ for k in projectPageButtons.keys():
2403
+ b:QPushButton=getattr(self.projectActionBar,'button_'+k)
2404
+ self.projectPageActions[k]=lambda butt=b: butt.click()
2405
+
2406
+ self.processPageActions={}
2407
+ def processButtonAction(tree:ProcessTree,butt:DraggableButton):
2408
+ tree.createProcessItem(butt.buttonData['type'])
2409
+ item=tree.topLevelItem(tree.topLevelItemCount()-1)
2410
+ tree.setCurrentItem(item)
2411
+ self.processTree_item_selection(tree)
2412
+ for t,b in self.processButtonBar.buttons.items():
2413
+ b:DraggableButton
2414
+ b.buttonAction=self.processPageActions[t]=lambda tree=self.processTree, butt=b: processButtonAction(tree,butt)
2415
+
2416
+ self.stepPage:StartingPage=None
2417
+ self.stepPageActions={}
2418
+ def stepButtonAction(butt:QToolButton, type):
2419
+ self.stepButton_action(butt, type)
2420
+ def stepPageAction(type):
2421
+ tree:ProcessTree=self.processTree if self.TREpar.tree==0 else self.binTree
2422
+ ITEs=self.ITEsfromTRE(self.TREpar)
2423
+ c=list(ITEs[0].children).index(type)
2424
+ ITE_chiild=ITEs[c+1]
2425
+ b:QPushButton=self.stepButtonBar.buttons[type]
2426
+ if b.isVisible() and b.isEnabled() and not ITE_chiild.active:
2427
+ b.click()
2428
+ elif ITE_chiild.active:
2429
+ item=tree.topLevelItem(self.TREpar.process)
2430
+ child=item.child(c)
2431
+ tree.setCurrentItem(child)
2432
+ self.processTree_item_selection(tree)
2433
+ else:
2434
+ showTip(self,'Current step is disabled! Please, reset the subsequemt step in the process to access it.')
2435
+ for t,b in self.stepButtonBar.buttons.items():
2436
+ b:QToolButton
2437
+ b.clicked.connect(lambda flag, butt=b, type=t: stepButtonAction(butt, type))
2438
+ self.stepPageActions[t]=lambda type=t: stepPageAction(type)
2439
+
2440
+ self.processTree.editingFinished=lambda: self.arrangeCurrentProcess(self.processTree)
2441
+ self.binTree.editingFinished=lambda: self.arrangeCurrentProcess(self.binTree)
2442
+ self.inheritance=lambda: None
2443
+ self.undoInd=None
2444
+ return
2445
+
2446
+ def binButton_action(self,FlagTreeSelection=True):
2447
+ w=self.Explorer_tree_splitter.width()
2448
+ if self.binButton.isChecked():
2449
+ self.Explorer_tree_splitter.setSizes([0, w])
2450
+ else:
2451
+ self.Explorer_tree_splitter.setSizes([w, 0])
2452
+ self.tree_splitter_action(FlagTreeSelection)
2453
+
2454
+ def tree_splitter_action(self,FlagTreeSelection=True):
2455
+ FlagProcessEnabled=self.Explorer_tree_splitter.sizes()[0]>0
2456
+ self.processTree.setEnabled(FlagProcessEnabled)
2457
+ for b in self.processButtonBar.buttons.values():
2458
+ b.setEnabled(FlagProcessEnabled)
2459
+ if not FlagProcessEnabled and FlagTreeSelection:
2460
+ self.processTree.blockSignals(True)
2461
+ self.processTree.setCurrentItem(None)
2462
+ self.processTree.clearSelection()
2463
+ self.processTree.blockSignals(False)
2464
+ self.processTree_item_selection(self.binTree)
2465
+ FlagBinEnabled=self.Explorer_tree_splitter.sizes()[1]>0
2466
+ self.binTree.setEnabled(FlagBinEnabled)
2467
+ self.binButton.setChecked(FlagBinEnabled)
2468
+ self.binButton.setIcon(self.binIconOn if FlagBinEnabled else self.binIconOff)
2469
+ if not FlagBinEnabled and FlagTreeSelection:
2470
+ self.binTree.blockSignals(True)
2471
+ self.binTree.setCurrentItem(None)
2472
+ self.binTree.clearSelection()
2473
+ self.binTree.blockSignals(False)
2474
+ self.processTree_item_selection(self.processTree)
2475
+
2476
+ def eventFilter(self, obj, event):
2477
+ if event.type() == QEvent.Type.ShortcutOverride:
2478
+ event.accept()
2479
+ return True
2480
+ return super().eventFilter(obj, event)
2481
+
2482
+ def keyPressEvent(self, event: QKeyEvent):
2483
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter):
2484
+ if self.processTree.isEnabled():
2485
+ obj=self.processTree
2486
+ else:
2487
+ obj=self.binTree
2488
+ obj.setFocus()
2489
+ return super().keyPressEvent(event)
2490
+
2491
+ def processTree_item_selection(self, tree:ProcessTree):
2492
+ self.currentTree=tree
2493
+ for indTree,t in enumerate([self.processTree,self.binTree]):
2494
+ if t==tree:
2495
+ index,child=self.arrangeCurrentProcess(tree)
2496
+ item=tree.currentItem()
2497
+ if item: item.setSelected(True)
2498
+ self.TREpar.tree=indTree
2499
+ self.TREpar.process=index
2500
+ self.TREpar.step=child
2501
+ if self.TREpar.project is not None:
2502
+ self.projectTree.itemList[0][self.TREpar.project].copyfromfields(self.TREpar,['tree','process','step'])
2503
+ tree.setProcessActionButtonLayout()
2504
+ self.adjustProcessSelection()
2505
+ if self.TREpar.tree==0 and self.Explorer_tree_splitter.sizes()[0]==0:
2506
+ self.binButton.setChecked(False)
2507
+ self.binButton_action(FlagTreeSelection=False)
2508
+ if index is not None and index>=0:
2509
+ if child:
2510
+ self.selectStep()
2511
+ #ITE:ITEpar=self.ITEfromTRE(self.TREpar)
2512
+ #pri.Coding.green(f'Process tree selection --->\t {self.TREpar.project} {self.TREpar.tree} {self.TREpar.process} {self.TREpar.step-1 if self.TREpar.step else self.TREpar.step} \t {ITE.ind}')
2513
+ else:
2514
+ self.ITEpar.copyfrom(tree.itemList[0][index][child])
2515
+ #pri.Coding.green(f'Process tree selection --->\t {self.TREpar.tree} {self.TREpar.process} {self.TREpar.step-1 if self.TREpar.step else self.TREpar.step}')
2516
+ else:
2517
+ t.actionBar.setVisible(False)
2518
+ t.blockSignals(True)
2519
+ t.clearSelection()
2520
+ t.blockSignals(False)
2521
+
2522
+ def arrangeCurrentProcess(self, tree:ProcessTree):
2523
+ index=child=None
2524
+ item=tree.currentItem()
2525
+ if item:
2526
+ self.stepButtonBar.show()
2527
+ if item.parent():
2528
+ child=item.parent().indexOfChild(item)+1
2529
+ item=item.parent()
2530
+ else:
2531
+ child=0
2532
+ index=tree.indexOfTopLevelItem(item)
2533
+ if index<0 or index>len(tree.itemList[0])-1:
2534
+ index=None
2535
+ return index, child
2536
+ ITEs=tree.itemList[0][index]
2537
+ for c in list(stepData):
2538
+ b:QToolButton=self.stepButtonBar.buttons[c]
2539
+ lab:QLabel=self.stepButtonBar.labels[c]
2540
+ #b.label.setVisible(c in allData[0]['mandatory'])
2541
+ nsteps=len(ITEs[0].children)
2542
+ self.setProcessFlagRun(ITEs[0].ind)
2543
+ if c in ITEs[0].children:
2544
+ ind=list(ITEs[0].children).index(c)
2545
+ if c in ITEs[0].mandatory: ITEs[ind+1].active=True
2546
+ if item.childCount()>ind:
2547
+ item_child=item.child(ind)
2548
+ item_child.setHidden(not ITEs[ind+1].active)
2549
+ b.setVisible(c not in ITEs[0].mandatory) #b.setVisible(True)
2550
+ lab.setVisible(c in ITEs[0].mandatory)
2551
+ flagRunnable=all([ITEs[j].flagRun==0 for j in range(ind+2,nsteps+1)]) if ind<nsteps else True
2552
+ flagRunnable=flagRunnable and ITEs[ind+1].flagRun==0 and ITEs[0].flagRun!=-2 #and len(ITEs[ind+1].link)==0
2553
+ b.setEnabled(flagRunnable)
2554
+ lab.setEnabled(flagRunnable)
2555
+ b.setChecked(ITEs[ind+1].active)
2556
+ else:
2557
+ flagRunnable=False
2558
+ b.setVisible(False)
2559
+ lab.setVisible(False)
2560
+ b.setEnabled(False)
2561
+ lab.setEnabled(False)
2562
+ b.setChecked(False)
2563
+ b.setButtonIcon()
2564
+ if self.stepPage:
2565
+ self.stepPage.items[c].setVisible(c in ITEs[0].children) #and b.isChecked())
2566
+ pageButton:QPushButton=self.stepPage.items[c].findChildren(QPushButton)[0]
2567
+ if flagRunnable:
2568
+ pageButton.setIcon(b.icon())
2569
+ else:
2570
+ pageButton.setIcon(b.iconOn)
2571
+ else:
2572
+ self.ITEpar.FlagNone=True
2573
+ self.hideStepButtons()
2574
+ return index, child
2575
+
2576
+ def hideStepButtons(self):
2577
+ for c in list(stepData):
2578
+ b:QToolButton=self.stepButtonBar.buttons[c]
2579
+ lab:QLabel=self.stepButtonBar.labels[c]
2580
+ b.setVisible(False)
2581
+ lab.setVisible(False)
2582
+
2583
+ def setProcessFlagRun(self,ind):
2584
+ ITEs:ITEpar=self.ITEsfromInd(ind)
2585
+ ITEs[0].flagRun=-2 if any([i.flagRun==-2 for i in ITEs[1:] if i.active]) else -1 if any([i.flagRun==-1 for i in ITEs[1:] if i.active]) else 2 if all([i.flagRun==2 for i in ITEs[1:] if i.active]) else 1 if all([i.flagRun>0 for i in ITEs[1:] if i.active]) else 0
2586
+
2587
+ def selectStep(self):
2588
+ tree=[self.processTree,self.binTree][self.TREpar.tree]
2589
+ index=self.TREpar.process
2590
+ child=self.TREpar.step
2591
+
2592
+ FlagVisible=[]
2593
+ FlagAdjustPar=None
2594
+ #tabAreaWidget=self.widgets[-1]
2595
+ #tabAreaWidget.scrollArea.splitter.hide()
2596
+ #tabAreaWidget.scrollArea.splitter.setFixedWidth(tabAreaWidget.scrollArea.splitter.splitterMaximumSize)
2597
+ self.ITEpar.copyfrom(tree.itemList[0][index][child])
2598
+ """
2599
+ indpar=[j for j in self.ITEpar.ind]
2600
+ for j in range(indpar[-2]):
2601
+ indpar[-2]=j
2602
+ self.inheritance(indpar)
2603
+ """
2604
+ i=None
2605
+ #FlagPrev=CAL.ind[-1]==len(self.w_Calibration.TABpar_prev_at(CAL.ind))-1
2606
+ FlagCalVi=False
2607
+ for k,w in enumerate(self.widgets):
2608
+ w:gPaIRS_Tab
2609
+ try:
2610
+ par:TABpar=tree.itemList[k+1][index][child-1][-1]
2611
+ except Exception as inst:
2612
+ pri.Error.red(f'[selectStep] Error while accessing the prev queues:\n{inst}')
2613
+ pass
2614
+ if w.TABname=='Calibration':
2615
+ flagVisible=par is not None
2616
+ FlagVisible.append(flagVisible)
2617
+ if w.buttonTab: w.buttonTab.setVisible(flagVisible)
2618
+
2619
+ FlagCalVi=w.TABpar.FlagCalVi if flagVisible else False
2620
+ if hasattr(w,'logo_CalVi'): w.logo_CalVi.setVisible(FlagCalVi)
2621
+ if hasattr(w,'button_Run_CalVi'): w.button_Run_CalVi.setVisible(FlagCalVi)
2622
+ elif w.TABname!='TabArea':
2623
+ flagVisible=FlagCalVi if '_CalVi' in w.TABname else par is not None
2624
+ FlagVisible.append(flagVisible)
2625
+ if w.buttonTab: w.buttonTab.setVisible(flagVisible)
2626
+ else:
2627
+ for j,f in enumerate(FlagVisible):
2628
+ if not f: par.FlagVisible[j]=f
2629
+ if par:
2630
+ w.TABpar.copyfrom(par)
2631
+ if FlagAdjustPar is None:
2632
+ FlagAdjustPar=not par.FlagInit
2633
+ i=k
2634
+ else:
2635
+ w.TABpar.FlagNone=True
2636
+
2637
+ FlagAdjustPar=FlagAdjustPar or not self.ITEpar.FlagInit
2638
+ FlagBridge=True
2639
+ if i is not None:
2640
+ #self.gui.setTABpars_at(self.ITEpar.ind,FlagAdjustPar,FlagBridge,widget=self.widgets[i])
2641
+ self.widgets[i].setTABpar(FlagAdjustPar,FlagBridge)
2642
+ #self.widgets[-1].setTABpar(FlagAdjustPar,FlagBridge)
2643
+ #self.widgets[-1].scrollArea.splitter.show()
2644
+
2645
+ def stepButton_action(self,b,t):
2646
+ tree:ProcessTree=self.currentTree
2647
+ item=tree.currentItem()
2648
+ if item.parent():
2649
+ item=item.parent()
2650
+ r=tree.indexOfTopLevelItem(item)
2651
+ ITEs=tree.itemList[0][r]
2652
+ c=list(ITEs[0].children.keys()).index(b.buttonData['type'])
2653
+ child=item.child(c)
2654
+ if b.buttonData['type'] in ITEs[0].mandatory:
2655
+ tree.setCurrentItem(child)
2656
+ self.processTree_item_selection(tree)
2657
+ return
2658
+ ITE:ITEpar=ITEs[c+1]
2659
+ if len(ITE.link)>0:
2660
+ global FlagReturn
2661
+ FlagReturn=False
2662
+ def goToLinkedStep():
2663
+ global FlagReturn
2664
+ self.TREpar.process=ITE.link[2]
2665
+ if ITE.active:
2666
+ self.TREpar.step=ITE.link[3]+1
2667
+ else:
2668
+ self.TREpar.step=0
2669
+ self.gui.adjustProjectSelection()
2670
+ FlagReturn=True
2671
+ return
2672
+ def unLink():
2673
+ global FlagReturn
2674
+ b.setChecked(ITE.active)
2675
+ self.gui.unlink_pars(ITE.ind)
2676
+ b.click()
2677
+ FlagReturn=True
2678
+ return
2679
+ addButton={
2680
+ 'Unlink step': unLink,
2681
+ 'Go to linked step': goToLinkedStep,
2682
+ }
2683
+ ITE0_master=self.ITEsfromInd(ITE.link)[0]
2684
+ linkInfo=f'linked to that of the process:\n\n{ITE0_master.ind[2]+1}: {ITE0_master.name}'
2685
+ warningDialog(self,f'The current {ITE.name} step is {linkInfo}.\n\nYou should either modify the step of the above process or unlink the current step from it.',addButton=addButton)
2686
+ if not FlagReturn: b.setChecked(ITE.active)
2687
+ return
2688
+ child.setHidden(not b.isChecked())
2689
+ ITE.active=b.isChecked()
2690
+ self.inheritance(ITE.ind)
2691
+ for dep in ITE.dependencies:
2692
+ ITE_dep:ITEpar=self.ITEfromInd(dep)
2693
+ ITE_dep.active=b.isChecked()
2694
+ item_dep=tree.topLevelItem(dep[2])
2695
+ child_dep=item_dep.child(c)
2696
+ child_dep.setHidden(not b.isChecked())
2697
+ self.gui.inheritance(dep)
2698
+ if self.stepPage: self.stepPage.items[t].setVisible(b.isChecked())
2699
+ FlagSettingPar=TABpar.FlagSettingPar
2700
+ TABpar.FlagSettingPar=True
2701
+ tree.clearSelection()
2702
+ TABpar.FlagSettingPar=FlagSettingPar
2703
+ if ITEs[c+1].active:
2704
+ tree.setCurrentItem(child)
2705
+ child.setSelected(True)
2706
+ else:
2707
+ tree.setCurrentItem(item)
2708
+ item.setSelected(True)
2709
+ return
2710
+
2711
+ def adjustProjectSelection(self):
2712
+ project=self.TREpar.project
2713
+
2714
+ FlagHideStepButtons=False
2715
+ if project==None:
2716
+ self.processWidget.setEnabled(False)
2717
+ for k,tree in enumerate([self.processTree,self.binTree]):
2718
+ tree.blockSignals(True)
2719
+ tree.clearSelection()
2720
+ clean_tree(tree)
2721
+ tree.blockSignals(False)
2722
+ FlagHideStepButtons=True
2723
+ else:
2724
+ self.processWidget.setEnabled(True)
2725
+
2726
+ #for w in self.widgets: w.TABpar_prev=[[],[]]
2727
+ item=None
2728
+ for k,tree in enumerate([self.processTree,self.binTree]):
2729
+ tree.itemList=[]
2730
+ for itemList in self.projectTree.itemList[1:]:
2731
+ tree.itemList.append(itemList[project][k])
2732
+ """
2733
+ for w,prev in zip(self.widgets,tree.itemList[1:]):
2734
+ w:gPaIRS_Tab
2735
+ w.TABpar_prev[k]=prev
2736
+ pass
2737
+ """
2738
+ tree.blockSignals(True)
2739
+ tree.clearSelection()
2740
+ clean_tree(tree)
2741
+ for i in range(len(tree.itemList[0])):
2742
+ tree.createProcessItem(None,i,FlagNewItem=False)
2743
+
2744
+ if self.TREpar.tree==k:
2745
+ item=self.projectTree.topLevelItem(project)
2746
+ self.projectTree.blockSignals(True)
2747
+ self.projectTree.setCurrentItem(item)
2748
+ item.setSelected(True)
2749
+ self.projectTree.blockSignals(False)
2750
+ self.projectTree.setButtonLayout()
2751
+ self.ui.binButton.setChecked(k==1)
2752
+ self.binButton_action(FlagTreeSelection=False)
2753
+ if self.TREpar.process is not None:
2754
+ item=tree.topLevelItem(self.TREpar.process)
2755
+ if item is None:
2756
+ self.TREpar.process=None
2757
+ self.TREpar.step=None
2758
+ if self.TREpar.step:
2759
+ item=item.child(self.TREpar.step-1)
2760
+ if item:
2761
+ tree.setCurrentItem(item)
2762
+ item.setSelected(True)
2763
+ tree.blockSignals(False)
2764
+ if item:
2765
+ item.setSelected(True)
2766
+ else: FlagHideStepButtons=True
2767
+ if FlagHideStepButtons:
2768
+ for c in list(stepData):
2769
+ b:QToolButton=self.stepButtonBar.buttons[c]
2770
+ b.setVisible(False)
2771
+ b.setChecked(False)
2772
+ return
2773
+
2774
+ def ITEsfromTRE(self,TRE:TREpar):
2775
+ return self.projectTree.itemList[1][TRE.project][TRE.tree][TRE.process]
2776
+
2777
+ def ITEsfromInd(self,ind:list):
2778
+ return self.projectTree.itemList[1][ind[0]][ind[1]][ind[2]]
2779
+
2780
+ def ITEfromTRE(self,TRE:TREpar):
2781
+ return self.projectTree.itemList[1][TRE.project][TRE.tree][TRE.process][TRE.step]
2782
+
2783
+ def ITEfromInd(self,ind:list):
2784
+ return self.projectTree.itemList[1][ind[0]][ind[1]][ind[2]][ind[3]+1]
2785
+
2786
+ def cancelUndo(self,ind:list=None):
2787
+ if ind is None:
2788
+ ind=[self.TREpar.project, self.TREpar.tree, self.TREpar.process, self.TREpar.step-1 if self.TREpar.step else -1, -1]
2789
+ if self.undoInd and self.undoInd[:-1]!=ind[:-1]:
2790
+ self.undoInd[-1]=-1
2791
+ if self.gui.checkProcesses(FlagInit=True,ind=self.undoInd):
2792
+ self.setITElayout(self.ITEfromInd(self.undoInd))
2793
+ self.undoInd=None
2794
+
2795
+ def setITElayout(self,ITE:ITEpar=None):
2796
+ if ITE is None:
2797
+ FlagCurrentITE=True
2798
+ ITE=self.ITEpar
2799
+ else:
2800
+ FlagCurrentITE=False
2801
+ if ITE.FlagNone: return
2802
+ self.cancelUndo(ITE.ind)
2803
+ FlagDone=True
2804
+ warningMessage=''
2805
+ ITEs=self.ITEsfromInd(ITE.ind)
2806
+ #mandatory=ITEs[0].mandatory
2807
+ j=1
2808
+ while ITEs[j].Step!=ITE.Step and j<len(ITEs)-1:
2809
+ if ITEs[j].active and ITEs[j].OptionDone==0 and ITEs[j].Step in ITE.parents:
2810
+ FlagDone=False
2811
+ if warningMessage: warningMessage+='\n\n'
2812
+ warningMessage+='--- '+ITEs[j].name+' ---\n'+f'The completion of the step "{ITEs[j].name}" is needed for "{ITE.name}"'
2813
+ j+=1
2814
+ for w in self.widgets:
2815
+ w:gPaIRS_Tab
2816
+ if FlagCurrentITE:
2817
+ TABpar_ind=w.TABpar
2818
+ else:
2819
+ TABpar_ind:TABpar=w.TABpar_at(ITE.ind)
2820
+ if not TABpar_ind: continue
2821
+ if TABpar_ind.FlagNone: continue
2822
+ if w.TABname=='Calibration':
2823
+ ITE.ncam=TABpar_ind.ncam
2824
+ ITE.progress=len(TABpar_ind.calList)
2825
+ if TABpar_ind.OptionDone==0: pass
2826
+ FlagDone=FlagDone and TABpar_ind.OptionDone!=0
2827
+ if not TABpar_ind.OptionDone==1 and not TABpar_ind.FlagNone:
2828
+ if TABpar_ind.warningMessage=='': continue
2829
+ if warningMessage: warningMessage+='\n\n'
2830
+ warningMessage+='--- '+w.TABname+' ---\n'+TABpar_ind.warningMessage
2831
+ if ITE.flagRun==0:
2832
+ if FlagDone:
2833
+ if warningMessage:
2834
+ ITE.warningMessage=warningMessage
2835
+ ITE.label='running_warn'
2836
+ ITE.OptionDone=-1
2837
+ else:
2838
+ if ITE.Step!=StepTypes.cal:
2839
+ ITE.warningMessage='Process step ready for running!'
2840
+ ITE.OptionDone=1
2841
+ ITE.label='running'
2842
+ else:
2843
+ ITE.warningMessage='Calibration files correctly identified!'
2844
+ ITE.OptionDone=1
2845
+ ITE.label='done'
2846
+ else:
2847
+ if warningMessage:
2848
+ ITE.warningMessage=warningMessage
2849
+ ITE.label='warning'
2850
+ else:
2851
+ ITE.warningMessage='Process step not yet initialized!'
2852
+ ITE.label='uninitialized'
2853
+ ITE.OptionDone=0
2854
+ else:
2855
+ procdata:dataTreePar=self.ITEfromInd(ITE.ind).procdata
2856
+ #if hasattr(self.gui,'reset_step') and not os.path.exists(procdata.procOutName()) and not ITE.FlagInit:
2857
+ # self.gui.reset_step(procdata.ind)
2858
+ # return
2859
+ #ITE.progress=procdata.numFinalized
2860
+ if ITE.flagRun==-2:
2861
+ ITE.warningMessage='Process step currently running.'
2862
+ ITE.label='running'
2863
+ else:
2864
+ if not FlagDone:
2865
+ ITE.warningMessage=f'The following issues are detected with the present step:\n{warningMessage}\n\nPlease, check if it is out-of-date!'
2866
+ ITE.procdata.warnings[0]=ITE.procdata.headerSection('CRITICAL ERROR',ITE.warningMessage,'X')
2867
+ ITE.procdata.warnings[1]=''
2868
+ ITE.procdata.setCompleteLog()
2869
+ ITE.label='error'
2870
+ ITE.OptionDone=0
2871
+ elif procdata.FlagErr:
2872
+ ITE.warningMessage='Some errors occured in this process step! See Log for more information.'
2873
+ ITE.label='error'
2874
+ elif ITE.flagRun==-1:
2875
+ ITE.warningMessage='Process step stopped by user.'
2876
+ ITE.label='paused'
2877
+ elif ITE.flagRun==-10:
2878
+ ITE.warningMessage='Process step in the queue for execution.'
2879
+ ITE.label='queue'
2880
+ else:
2881
+ ITE.warningMessage='Process step correctly completed! 💪🏻'
2882
+ ITE.label='done'
2883
+
2884
+ if FlagCurrentITE:
2885
+ ITE:ITEpar=self.ITEfromInd(ITE.ind)
2886
+ ITE.copyfrom(self.ITEpar,exceptions=['procdata'])
2887
+ self.updateItemWidget(ITE)
2888
+ return
2889
+
2890
+ def updateItemWidget(self,ITE:ITEpar):
2891
+ if self.TREpar.project==ITE.ind[0]:
2892
+ tree=[self.processTree,self.binTree][ITE.ind[1]]
2893
+ item:QTreeWidgetItem=tree.topLevelItem(ITE.ind[2])
2894
+ item=item.child(ITE.ind[3])
2895
+ TREind=[self.TREpar.project, self.TREpar.tree, self.TREpar.process, self.TREpar.step-1 if self.TREpar.step else -1, -1]
2896
+ if len(ITE.link)==0:
2897
+ if self.gui and ITE.flagRun==0 and len(self.gui.ui.tabAreaWidget.TABpar_prev_at(ITE.ind))<=1:
2898
+ item.setIcon(0,self.processTree.uneditedIcon)
2899
+ elif self.gui and TREind[:-1]==ITE.ind[:-1] and self.gui.ui.tabAreaWidget.TABpar.ind[-1]<len(self.gui.w_Input.TABpar_prev_at(ITE.ind))-1:
2900
+ item.setIcon(0,self.processTree.undoneIcon)
2901
+ self.undoInd=self.gui.ui.tabAreaWidget.TABpar.ind
2902
+ else:
2903
+ item.setIcon(0,QIcon())
2904
+ else:
2905
+ item.setIcon(0,self.processTree.linkedIcon)
2906
+
2907
+ itemWidget:StepItemWidget=tree.itemWidget(item,1)
2908
+
2909
+ ITE_ind=self.ITEfromInd(ITE.ind)
2910
+ itemWidget.setFromITE(ITE_ind)
2911
+
2912
+ def updateSwitchMovies(self,ind,FlagStart=False):
2913
+ self.updateProjectSwitchMovie(ind,FlagStart)
2914
+ self.updateProcessSwitchMovie(ind,FlagStart)
2915
+
2916
+ def updateProjectSwitchMovie(self,ind,FlagStart=False):
2917
+ if ind is None: return
2918
+ topLevelItem=self.projectTree.topLevelItem(ind[0])
2919
+ itemWidget=self.projectTree.itemWidget(topLevelItem,1)
2920
+ if itemWidget:
2921
+ switch:ModernSwitch=itemWidget.findChildren(ModernSwitch)[0]
2922
+ if switch:
2923
+ if FlagStart: switch.startTimer()
2924
+ else: switch.stopTimer()
2925
+
2926
+ def updateProcessSwitchMovie(self,ind,FlagStart=False):
2927
+ if ind is None: return
2928
+ if self.TREpar.project==ind[0]:
2929
+ topLevelItem=self.processTree.topLevelItem(ind[2])
2930
+ itemWidget=self.processTree.itemWidget(topLevelItem,1)
2931
+ if itemWidget:
2932
+ switch:ModernSwitch=itemWidget.findChildren(ModernSwitch)[0]
2933
+ if switch:
2934
+ if FlagStart: switch.startTimer()
2935
+ else: switch.stopTimer()
2936
+
2937
+ class StartingPage(QFrame):
2938
+ ICON_SIZE = 65
2939
+ LAYOUT_SPACING = 20
2940
+ LAYOUT_MARGIN = 20
2941
+ ITEM_HEIGHT = 100
2942
+ NAME_LABEL_HEIGHT = 32
2943
+ TEXT_LAYOUT_SPACING = 1
2944
+ CAPTION_WIDTH = 350
2945
+ BUTTON_LAYOUT_TOP_MARGIN = 0
2946
+
2947
+ dFontPixelSize_title=11
2948
+ TITLE_FONT_SIZE = fontPixelSize+dFontPixelSize_title
2949
+ NAME_FONT_SIZE = TITLE_FONT_SIZE-2
2950
+ CAPTION_FONT_SIZE = NAME_FONT_SIZE-2
2951
+
2952
+ def __init__(self, title:str='', processes:dict={}, buttonBar:dict={}):
2953
+ super().__init__()
2954
+ if __name__ == "__main__":
2955
+ iconW = QIcon()
2956
+ iconW.addFile(u""+ icons_path +"logo_PaIRS.png", QSize(), QIcon.Normal, QIcon.Off)
2957
+ self.setWindowTitle(title)
2958
+ self.setWindowIcon(iconW)
2959
+
2960
+ # ScrollArea
2961
+ self.scroll_area = QScrollArea(self)
2962
+ self.scroll_area.setWidgetResizable(True)
2963
+
2964
+ # Widget per contenere il layout principale
2965
+ container_widget = QFrame()
2966
+ container_widget.setObjectName('container')
2967
+ container_widget.setStyleSheet(f"""
2968
+ QFrame#container {{
2969
+ border: 1px solid rgba(128, 128, 128, 0.5);
2970
+ border-radius: 15px;
2971
+ background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 rgba(128, 128, 128, 0), stop:1 rgba(224, 224, 224, 0.25));
2972
+ }}
2973
+ QWidget{{
2974
+ background: transparent;
2975
+ }}
2976
+ """)
2977
+
2978
+ # Layout principale verticale
2979
+ self.main_layout = QVBoxLayout(container_widget)
2980
+ self.main_layout.setSpacing(self.LAYOUT_SPACING)
2981
+ self.main_layout.setContentsMargins(self.LAYOUT_MARGIN, self.LAYOUT_MARGIN, self.LAYOUT_MARGIN, self.LAYOUT_MARGIN)
2982
+
2983
+ # Label iniziale con font più grande
2984
+ self.title = QLabel(title)
2985
+ title_font = self.title.font()
2986
+ title_font.setBold(True)
2987
+ self.title.setFont(title_font)
2988
+ self.title.setAlignment(Qt.AlignLeft)
2989
+ self.title.setFixedHeight(self.NAME_LABEL_HEIGHT)
2990
+ self.main_layout.addWidget(self.title)
2991
+
2992
+ self.scroll_area.setWidget(container_widget)
2993
+ self.scroll_area.setStyleSheet(scrollAreaStyle())
2994
+ layout = QVBoxLayout(self)
2995
+ layout.addWidget(self.scroll_area)
2996
+ self.setLayout(layout)
2997
+
2998
+ self.setupPage(processes,buttonBar)
2999
+
3000
+ def setObjectName(self,name:str):
3001
+ QFrame.setObjectName(self,name)
3002
+ self.scroll_area.setObjectName('scroll_area_'+self.objectName())
3003
+ return
3004
+
3005
+ def setupPage(self, processes:dict={}, buttonBar:dict={}):
3006
+ if not len(processes): return
3007
+ button_margin=self.ITEM_HEIGHT-self.ICON_SIZE-self.BUTTON_LAYOUT_TOP_MARGIN
3008
+ CAPTION_HEIGHT=self.ITEM_HEIGHT-self.NAME_LABEL_HEIGHT-self.TEXT_LAYOUT_SPACING
3009
+ self.items={}
3010
+ # Itera sui dizionari nella lista
3011
+ for n, process in processes.items():
3012
+ # Layout orizzontale per ogni processo
3013
+ widget=QWidget()
3014
+ widget.setObjectName("process_item")
3015
+ widget.setStyleSheet("""
3016
+ QWidget#process_item:hover {
3017
+ background-color: rgba(64, 64, 255, 33);
3018
+ border-radius: 10px;
3019
+ }
3020
+ """)
3021
+ process_layout = QHBoxLayout(widget)
3022
+ process_layout.setSpacing(self.LAYOUT_SPACING)
3023
+ process_layout.setContentsMargins(10, 10, 10, 0)
3024
+
3025
+ # Pulsante con icona
3026
+ button_layout = QHBoxLayout()
3027
+ button_layout.setSpacing(0)
3028
+ button_layout.setContentsMargins(0, self.BUTTON_LAYOUT_TOP_MARGIN, 0, button_margin)
3029
+
3030
+ icon_button = QPushButton()
3031
+ pixmap = QPixmap(icons_path+process['icon']).scaled(self.ICON_SIZE, self.ICON_SIZE, mode=Qt.TransformationMode.SmoothTransformation)
3032
+ icon_button.setIcon(pixmap)
3033
+ icon_button.setIconSize(pixmap.size())
3034
+ icon_button.setFixedSize(self.ICON_SIZE, self.ICON_SIZE)
3035
+ icon_button.setStyleSheet("border: none; background: none;")
3036
+ icon_button.setCursor(Qt.PointingHandCursor)
3037
+
3038
+ # Effetto dinamico al clic
3039
+ icon_button.pressed.connect(lambda btn=icon_button: btn.setStyleSheet("border: none; background: #dcdcdc;"))
3040
+ icon_button.released.connect(lambda btn=icon_button: btn.setStyleSheet("border: none; background: none;"))
3041
+ if buttonBar:
3042
+ def action(name):
3043
+ return lambda: buttonBar[name]()
3044
+ icon_button.clicked.connect(action(n))
3045
+
3046
+ button_layout.addWidget(icon_button)
3047
+ process_layout.addLayout(button_layout)
3048
+
3049
+ # Layout verticale per nome e descrizione
3050
+ text_layout = QVBoxLayout()
3051
+ text_layout.setSpacing(self.TEXT_LAYOUT_SPACING)
3052
+ text_layout.setContentsMargins(0, 0, 0, 0)
3053
+
3054
+ name_label = QLabel(process['name'])
3055
+ name_label.setObjectName('name_label')
3056
+ name_font = name_label.font()
3057
+ name_font.setPixelSize(self.NAME_FONT_SIZE)
3058
+ name_font.setBold(True)
3059
+ name_label.setFont(name_font)
3060
+ name_label.setAlignment(Qt.AlignLeft)
3061
+ name_label.setFixedHeight(self.NAME_LABEL_HEIGHT)
3062
+ text_layout.addWidget(name_label)
3063
+
3064
+ caption_text_edit = QTextEdit(process['caption'])
3065
+ caption_text_edit.setObjectName('caption_text_edit')
3066
+ caption_font = caption_text_edit.font()
3067
+ caption_font.setPixelSize(self.CAPTION_FONT_SIZE)
3068
+ caption_text_edit.setFont(caption_font)
3069
+ caption_text_edit.setAlignment(Qt.AlignmentFlag.AlignJustify)
3070
+ caption_text_edit.setReadOnly(True)
3071
+ caption_text_edit.setTextInteractionFlags(Qt.NoTextInteraction)
3072
+ caption_text_edit.viewport().setCursor(Qt.PointingHandCursor)
3073
+ caption_text_edit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
3074
+ caption_text_edit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
3075
+ caption_text_edit.setFrameStyle(QFrame.NoFrame)
3076
+ #caption_text_edit.setFixedWidth(self.CAPTION_WIDTH)
3077
+ caption_text_edit.setStyleSheet("background: transparent;")
3078
+ caption_text_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
3079
+
3080
+ # Adjust height to content
3081
+ caption_text_edit.document().setTextWidth(caption_text_edit.viewport().width())
3082
+ caption_text_edit.setFixedHeight(CAPTION_HEIGHT)#caption_text_edit.document().size().height())
3083
+
3084
+ text_layout.addWidget(caption_text_edit)
3085
+
3086
+ process_layout.addLayout(text_layout)
3087
+
3088
+ # Spacer orizzontale che si espande
3089
+ #spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
3090
+ #process_layout.addSpacerItem(spacer)
3091
+
3092
+ # Aggiungi il layout orizzontale al layout principale
3093
+ self.main_layout.addWidget(widget)
3094
+
3095
+ # --- Make the whole row clickable (icon + labels + background) ---
3096
+ if buttonBar:
3097
+
3098
+ def make_clickable(w, callback):
3099
+ w.setCursor(Qt.PointingHandCursor)
3100
+
3101
+ def mouseReleaseEvent(event, cb=callback, ww=w):
3102
+ if event.button() == Qt.LeftButton:
3103
+ cb()
3104
+ # call base implementation to keep default behaviour
3105
+ QWidget.mouseReleaseEvent(ww, event)
3106
+
3107
+ w.mouseReleaseEvent = mouseReleaseEvent
3108
+
3109
+ # entire row + title + caption all trigger the same action
3110
+ make_clickable(widget, action(n))
3111
+ #make_clickable(name_label, click_callback)
3112
+ #make_clickable(caption_text_edit, click_callback)
3113
+
3114
+ self.items[n]=widget
3115
+
3116
+ self.main_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))
3117
+
3118
+ def setFontPixelSize(self,fPixelSize):
3119
+ TITLE_FONT_SIZE = min([fPixelSize+self.dFontPixelSize_title,30])
3120
+ title_font = self.title.font()
3121
+ title_font.setPixelSize(TITLE_FONT_SIZE)
3122
+ title_font.setBold(True)
3123
+ self.title.setFont(title_font)
3124
+
3125
+ NAME_FONT_SIZE = TITLE_FONT_SIZE-2
3126
+ for c in self.findChildren(QLabel,'name_label'):
3127
+ c:QLabel
3128
+ name_font = c.font()
3129
+ name_font.setPixelSize(NAME_FONT_SIZE)
3130
+ name_font.setBold(True)
3131
+ c.setFont(name_font)
3132
+ CAPTION_FONT_SIZE = NAME_FONT_SIZE-4
3133
+ for c in self.findChildren(QTextEdit,'caption_text_edit'):
3134
+ c:QLabel
3135
+ caption_font = c.font()
3136
+ caption_font.setPixelSize(CAPTION_FONT_SIZE)
3137
+ c.setFont(caption_font)
3138
+
3139
+ if __name__ == "__main__":
3140
+ app = QApplication([])
3141
+ app.setStyle('Fusion')
3142
+
3143
+ Explorer = PaIRS_Explorer()
3144
+ if FlagStartingPages:
3145
+ projectStartingPage=StartingPage("Select a project", projectPageButtons,Explorer.projectPageActions)
3146
+ processStartingPage=StartingPage("Select a process", processData,Explorer.processPageActions)
3147
+ stepStartingPage=StartingPage("Set up each step of the process", stepData,Explorer.stepPageActions)
3148
+ Explorer.stepPage=stepStartingPage
3149
+
3150
+ Explorer.resize(500, 750)
3151
+ Explorer.move(0 if FlagStartingPages else 150, 150)
3152
+ Explorer.show()
3153
+ if FlagStartingPages:
3154
+ projectStartingPage.resize(500, 800)
3155
+ projectStartingPage.move(500, 150)
3156
+ projectStartingPage.show()
3157
+ projectStartingPage.setFontPixelSize(fontPixelSize)
3158
+ processStartingPage.resize(500, 800)
3159
+ processStartingPage.move(1000, 150)
3160
+ processStartingPage.show()
3161
+ processStartingPage.setFontPixelSize(fontPixelSize)
3162
+ stepStartingPage.resize(500, 800)
3163
+ stepStartingPage.move(1500, 150)
3164
+ stepStartingPage.show()
3165
+ stepStartingPage.setFontPixelSize(fontPixelSize)
3166
+
3167
+ app.exec()
3168
+