PaIRS-UniNa 0.2.5__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. PaIRS_UniNa/Calibration_Tab.py +331 -0
  2. PaIRS_UniNa/Changes.txt +110 -0
  3. PaIRS_UniNa/Custom_Top.py +303 -0
  4. PaIRS_UniNa/Explorer.py +3069 -0
  5. PaIRS_UniNa/FolderLoop.py +372 -0
  6. PaIRS_UniNa/Input_Tab.py +719 -0
  7. PaIRS_UniNa/Input_Tab_CalVi.py +788 -0
  8. PaIRS_UniNa/Input_Tab_tools.py +3023 -0
  9. PaIRS_UniNa/Log_Tab.py +110 -0
  10. PaIRS_UniNa/Output_Tab.py +924 -0
  11. PaIRS_UniNa/PaIRS.py +18 -0
  12. PaIRS_UniNa/PaIRS_PIV.py +873 -0
  13. PaIRS_UniNa/PaIRS_pypacks.py +1250 -0
  14. PaIRS_UniNa/Process_Tab.py +1757 -0
  15. PaIRS_UniNa/Process_Tab_CalVi.py +313 -0
  16. PaIRS_UniNa/Process_Tab_Disp.py +163 -0
  17. PaIRS_UniNa/Process_Tab_Min.py +120 -0
  18. PaIRS_UniNa/ResizePopup.py +55 -0
  19. PaIRS_UniNa/Saving_tools.py +296 -0
  20. PaIRS_UniNa/TabTools.py +1251 -0
  21. PaIRS_UniNa/Vis_Tab.py +2100 -0
  22. PaIRS_UniNa/Vis_Tab_CalVi.py +983 -0
  23. PaIRS_UniNa/Whatsnew.py +118 -0
  24. PaIRS_UniNa/_PaIRS_PIV.pyd +0 -0
  25. PaIRS_UniNa/__init__.py +6 -0
  26. PaIRS_UniNa/__main__.py +45 -0
  27. PaIRS_UniNa/addwidgets_ps.py +1125 -0
  28. PaIRS_UniNa/calib.py +1488 -0
  29. PaIRS_UniNa/calibView.py +826 -0
  30. PaIRS_UniNa/gPaIRS.py +3769 -0
  31. PaIRS_UniNa/gPalette.py +189 -0
  32. PaIRS_UniNa/icons/abort.png +0 -0
  33. PaIRS_UniNa/icons/about.png +0 -0
  34. PaIRS_UniNa/icons/align_all.png +0 -0
  35. PaIRS_UniNa/icons/announcement.png +0 -0
  36. PaIRS_UniNa/icons/automatic_levels_off.png +0 -0
  37. PaIRS_UniNa/icons/automatic_levels_on.png +0 -0
  38. PaIRS_UniNa/icons/automatic_off.png +0 -0
  39. PaIRS_UniNa/icons/automatic_on.png +0 -0
  40. PaIRS_UniNa/icons/automatic_size_off.png +0 -0
  41. PaIRS_UniNa/icons/automatic_size_on.png +0 -0
  42. PaIRS_UniNa/icons/axes.png +0 -0
  43. PaIRS_UniNa/icons/background.png +0 -0
  44. PaIRS_UniNa/icons/background_vectors.png +0 -0
  45. PaIRS_UniNa/icons/bin_off.png +0 -0
  46. PaIRS_UniNa/icons/bin_on.png +0 -0
  47. PaIRS_UniNa/icons/browse_file_c.png +0 -0
  48. PaIRS_UniNa/icons/browse_folder_c.png +0 -0
  49. PaIRS_UniNa/icons/brush_cursor.png +0 -0
  50. PaIRS_UniNa/icons/bugfix.png +0 -0
  51. PaIRS_UniNa/icons/cal_proc.png +0 -0
  52. PaIRS_UniNa/icons/cal_proc_off.png +0 -0
  53. PaIRS_UniNa/icons/cal_step.png +0 -0
  54. PaIRS_UniNa/icons/cal_step_off.png +0 -0
  55. PaIRS_UniNa/icons/calibrate.png +0 -0
  56. PaIRS_UniNa/icons/calibration_logo.png +0 -0
  57. PaIRS_UniNa/icons/change_folder.png +0 -0
  58. PaIRS_UniNa/icons/change_folder_off.png +0 -0
  59. PaIRS_UniNa/icons/checklist.png +0 -0
  60. PaIRS_UniNa/icons/clean.png +0 -0
  61. PaIRS_UniNa/icons/clean_run.png +0 -0
  62. PaIRS_UniNa/icons/close.png +0 -0
  63. PaIRS_UniNa/icons/close_all.png +0 -0
  64. PaIRS_UniNa/icons/close_project.png +0 -0
  65. PaIRS_UniNa/icons/close_workspace.png +0 -0
  66. PaIRS_UniNa/icons/colormap.png +0 -0
  67. PaIRS_UniNa/icons/colormaps/Accent.png +0 -0
  68. PaIRS_UniNa/icons/colormaps/BrBG.png +0 -0
  69. PaIRS_UniNa/icons/colormaps/Dark2.png +0 -0
  70. PaIRS_UniNa/icons/colormaps/PRGn.png +0 -0
  71. PaIRS_UniNa/icons/colormaps/Paired.png +0 -0
  72. PaIRS_UniNa/icons/colormaps/Pastel1.png +0 -0
  73. PaIRS_UniNa/icons/colormaps/Pastel2.png +0 -0
  74. PaIRS_UniNa/icons/colormaps/PiYG.png +0 -0
  75. PaIRS_UniNa/icons/colormaps/PuOr.png +0 -0
  76. PaIRS_UniNa/icons/colormaps/RdBu.png +0 -0
  77. PaIRS_UniNa/icons/colormaps/RdGy.png +0 -0
  78. PaIRS_UniNa/icons/colormaps/RdYlBu.png +0 -0
  79. PaIRS_UniNa/icons/colormaps/RdYlGn.png +0 -0
  80. PaIRS_UniNa/icons/colormaps/Set1.png +0 -0
  81. PaIRS_UniNa/icons/colormaps/Set2.png +0 -0
  82. PaIRS_UniNa/icons/colormaps/Set3.png +0 -0
  83. PaIRS_UniNa/icons/colormaps/Spectral.png +0 -0
  84. PaIRS_UniNa/icons/colormaps/Wistia.png +0 -0
  85. PaIRS_UniNa/icons/colormaps/afmhot.png +0 -0
  86. PaIRS_UniNa/icons/colormaps/autumn.png +0 -0
  87. PaIRS_UniNa/icons/colormaps/binary.png +0 -0
  88. PaIRS_UniNa/icons/colormaps/blackVector.png +0 -0
  89. PaIRS_UniNa/icons/colormaps/blueVector.png +0 -0
  90. PaIRS_UniNa/icons/colormaps/bone.png +0 -0
  91. PaIRS_UniNa/icons/colormaps/brg.png +0 -0
  92. PaIRS_UniNa/icons/colormaps/bwr.png +0 -0
  93. PaIRS_UniNa/icons/colormaps/cividis.png +0 -0
  94. PaIRS_UniNa/icons/colormaps/cool.png +0 -0
  95. PaIRS_UniNa/icons/colormaps/coolwarm.png +0 -0
  96. PaIRS_UniNa/icons/colormaps/copper.png +0 -0
  97. PaIRS_UniNa/icons/colormaps/cubehelix.png +0 -0
  98. PaIRS_UniNa/icons/colormaps/cyanVector.png +0 -0
  99. PaIRS_UniNa/icons/colormaps/flag.png +0 -0
  100. PaIRS_UniNa/icons/colormaps/gist_heat.png +0 -0
  101. PaIRS_UniNa/icons/colormaps/gray.png +0 -0
  102. PaIRS_UniNa/icons/colormaps/greenVector.png +0 -0
  103. PaIRS_UniNa/icons/colormaps/hot.png +0 -0
  104. PaIRS_UniNa/icons/colormaps/hsv.png +0 -0
  105. PaIRS_UniNa/icons/colormaps/inferno.png +0 -0
  106. PaIRS_UniNa/icons/colormaps/jet.png +0 -0
  107. PaIRS_UniNa/icons/colormaps/magentaVector.png +0 -0
  108. PaIRS_UniNa/icons/colormaps/magma.png +0 -0
  109. PaIRS_UniNa/icons/colormaps/ocean.png +0 -0
  110. PaIRS_UniNa/icons/colormaps/pink.png +0 -0
  111. PaIRS_UniNa/icons/colormaps/plasma.png +0 -0
  112. PaIRS_UniNa/icons/colormaps/prism.png +0 -0
  113. PaIRS_UniNa/icons/colormaps/rainbow.png +0 -0
  114. PaIRS_UniNa/icons/colormaps/redVector.png +0 -0
  115. PaIRS_UniNa/icons/colormaps/seismic.png +0 -0
  116. PaIRS_UniNa/icons/colormaps/spring.png +0 -0
  117. PaIRS_UniNa/icons/colormaps/summer.png +0 -0
  118. PaIRS_UniNa/icons/colormaps/tab10.png +0 -0
  119. PaIRS_UniNa/icons/colormaps/tab20.png +0 -0
  120. PaIRS_UniNa/icons/colormaps/tab20b.png +0 -0
  121. PaIRS_UniNa/icons/colormaps/tab20c.png +0 -0
  122. PaIRS_UniNa/icons/colormaps/terrain.png +0 -0
  123. PaIRS_UniNa/icons/colormaps/twilight.png +0 -0
  124. PaIRS_UniNa/icons/colormaps/viridis.png +0 -0
  125. PaIRS_UniNa/icons/colormaps/whiteVector.png +0 -0
  126. PaIRS_UniNa/icons/colormaps/winter.png +0 -0
  127. PaIRS_UniNa/icons/colormaps/yellowVector.png +0 -0
  128. PaIRS_UniNa/icons/common_region.png +0 -0
  129. PaIRS_UniNa/icons/common_region_off.png +0 -0
  130. PaIRS_UniNa/icons/completed.png +0 -0
  131. PaIRS_UniNa/icons/contourf_off.png +0 -0
  132. PaIRS_UniNa/icons/contourf_on.png +0 -0
  133. PaIRS_UniNa/icons/copy.png +0 -0
  134. PaIRS_UniNa/icons/copy_process.png +0 -0
  135. PaIRS_UniNa/icons/copy_process_off.png +0 -0
  136. PaIRS_UniNa/icons/copygrid.png +0 -0
  137. PaIRS_UniNa/icons/cursor_lamp.png +0 -0
  138. PaIRS_UniNa/icons/cut.png +0 -0
  139. PaIRS_UniNa/icons/cut_warnings.png +0 -0
  140. PaIRS_UniNa/icons/darkmode.png +0 -0
  141. PaIRS_UniNa/icons/debug_run.png +0 -0
  142. PaIRS_UniNa/icons/delete.png +0 -0
  143. PaIRS_UniNa/icons/deleteErr.png +0 -0
  144. PaIRS_UniNa/icons/disp_step.png +0 -0
  145. PaIRS_UniNa/icons/disp_step_off.png +0 -0
  146. PaIRS_UniNa/icons/down.png +0 -0
  147. PaIRS_UniNa/icons/edit_list.png +0 -0
  148. PaIRS_UniNa/icons/editing.png +0 -0
  149. PaIRS_UniNa/icons/example_list.png +0 -0
  150. PaIRS_UniNa/icons/find_all_planes.png +0 -0
  151. PaIRS_UniNa/icons/find_plane.png +0 -0
  152. PaIRS_UniNa/icons/flaticon_PaIRS.png +0 -0
  153. PaIRS_UniNa/icons/flaticon_PaIRS_beta.png +0 -0
  154. PaIRS_UniNa/icons/flaticon_PaIRS_download.png +0 -0
  155. PaIRS_UniNa/icons/flip_y_off.png +0 -0
  156. PaIRS_UniNa/icons/flip_y_on.png +0 -0
  157. PaIRS_UniNa/icons/focusErrr.png +0 -0
  158. PaIRS_UniNa/icons/gear.gif +0 -0
  159. PaIRS_UniNa/icons/gear.png +0 -0
  160. PaIRS_UniNa/icons/ger.png +0 -0
  161. PaIRS_UniNa/icons/greenv.png +0 -0
  162. PaIRS_UniNa/icons/guide.png +0 -0
  163. PaIRS_UniNa/icons/icon_CalVi.png +0 -0
  164. PaIRS_UniNa/icons/icon_PaIRS.png +0 -0
  165. PaIRS_UniNa/icons/import.png +0 -0
  166. PaIRS_UniNa/icons/import_set.png +0 -0
  167. PaIRS_UniNa/icons/information.png +0 -0
  168. PaIRS_UniNa/icons/input_logo.png +0 -0
  169. PaIRS_UniNa/icons/issue.png +0 -0
  170. PaIRS_UniNa/icons/laser_NTR.png +0 -0
  171. PaIRS_UniNa/icons/laser_TR_double.png +0 -0
  172. PaIRS_UniNa/icons/laser_TR_single.png +0 -0
  173. PaIRS_UniNa/icons/link.png +0 -0
  174. PaIRS_UniNa/icons/linked.png +0 -0
  175. PaIRS_UniNa/icons/loaded.png +0 -0
  176. PaIRS_UniNa/icons/loading_2.gif +0 -0
  177. PaIRS_UniNa/icons/log_logo.png +0 -0
  178. PaIRS_UniNa/icons/logo_CalVi.png +0 -0
  179. PaIRS_UniNa/icons/logo_CalVi_completo.png +0 -0
  180. PaIRS_UniNa/icons/logo_CalVi_party.png +0 -0
  181. PaIRS_UniNa/icons/logo_PaIRS.png +0 -0
  182. PaIRS_UniNa/icons/logo_PaIRS_completo.png +0 -0
  183. PaIRS_UniNa/icons/logo_PaIRS_download.png +0 -0
  184. PaIRS_UniNa/icons/logo_PaIRS_party_rect.png +0 -0
  185. PaIRS_UniNa/icons/logo_PaIRS_rect.png +0 -0
  186. PaIRS_UniNa/icons/logo_opaco.png +0 -0
  187. PaIRS_UniNa/icons/mask.png +0 -0
  188. PaIRS_UniNa/icons/measure.png +0 -0
  189. PaIRS_UniNa/icons/measure_off.png +0 -0
  190. PaIRS_UniNa/icons/min_proc.png +0 -0
  191. PaIRS_UniNa/icons/min_proc_off.png +0 -0
  192. PaIRS_UniNa/icons/min_step.png +0 -0
  193. PaIRS_UniNa/icons/min_step_off.png +0 -0
  194. PaIRS_UniNa/icons/minus.png +0 -0
  195. PaIRS_UniNa/icons/mirror_u.png +0 -0
  196. PaIRS_UniNa/icons/mirror_v.png +0 -0
  197. PaIRS_UniNa/icons/mirror_x.png +0 -0
  198. PaIRS_UniNa/icons/mirror_y.png +0 -0
  199. PaIRS_UniNa/icons/mtplt.png +0 -0
  200. PaIRS_UniNa/icons/new.png +0 -0
  201. PaIRS_UniNa/icons/new_workspace.png +0 -0
  202. PaIRS_UniNa/icons/news.png +0 -0
  203. PaIRS_UniNa/icons/normal_run.png +0 -0
  204. PaIRS_UniNa/icons/open.png +0 -0
  205. PaIRS_UniNa/icons/open_image.png +0 -0
  206. PaIRS_UniNa/icons/open_new_window.png +0 -0
  207. PaIRS_UniNa/icons/open_result.png +0 -0
  208. PaIRS_UniNa/icons/open_workspace.png +0 -0
  209. PaIRS_UniNa/icons/output_logo.png +0 -0
  210. PaIRS_UniNa/icons/paste_above.png +0 -0
  211. PaIRS_UniNa/icons/paste_below.png +0 -0
  212. PaIRS_UniNa/icons/pause.png +0 -0
  213. PaIRS_UniNa/icons/paused.png +0 -0
  214. PaIRS_UniNa/icons/piv_proc.png +0 -0
  215. PaIRS_UniNa/icons/piv_proc_off.png +0 -0
  216. PaIRS_UniNa/icons/piv_step.png +0 -0
  217. PaIRS_UniNa/icons/piv_step_off.png +0 -0
  218. PaIRS_UniNa/icons/plane.png +0 -0
  219. PaIRS_UniNa/icons/play.png +0 -0
  220. PaIRS_UniNa/icons/plus.png +0 -0
  221. PaIRS_UniNa/icons/process_logo.png +0 -0
  222. PaIRS_UniNa/icons/process_loop.png +0 -0
  223. PaIRS_UniNa/icons/project.png +0 -0
  224. PaIRS_UniNa/icons/pylog.png +0 -0
  225. PaIRS_UniNa/icons/python_warning.png +0 -0
  226. PaIRS_UniNa/icons/queue.png +0 -0
  227. PaIRS_UniNa/icons/quit.png +0 -0
  228. PaIRS_UniNa/icons/read.png +0 -0
  229. PaIRS_UniNa/icons/read_list.png +0 -0
  230. PaIRS_UniNa/icons/redo.png +0 -0
  231. PaIRS_UniNa/icons/redx.png +0 -0
  232. PaIRS_UniNa/icons/reset.png +0 -0
  233. PaIRS_UniNa/icons/reset_levels.png +0 -0
  234. PaIRS_UniNa/icons/resize_icon.png +0 -0
  235. PaIRS_UniNa/icons/restore.png +0 -0
  236. PaIRS_UniNa/icons/restore_undo.png +0 -0
  237. PaIRS_UniNa/icons/rotate_clock.png +0 -0
  238. PaIRS_UniNa/icons/rotate_counter.png +0 -0
  239. PaIRS_UniNa/icons/rotate_v_clock.png +0 -0
  240. PaIRS_UniNa/icons/rotate_v_counter.png +0 -0
  241. PaIRS_UniNa/icons/running.gif +0 -0
  242. PaIRS_UniNa/icons/running.png +0 -0
  243. PaIRS_UniNa/icons/running_warn.png +0 -0
  244. PaIRS_UniNa/icons/sandglass.png +0 -0
  245. PaIRS_UniNa/icons/save.png +0 -0
  246. PaIRS_UniNa/icons/save_and_stop.png +0 -0
  247. PaIRS_UniNa/icons/save_cfg.png +0 -0
  248. PaIRS_UniNa/icons/saveas.png +0 -0
  249. PaIRS_UniNa/icons/saveas_workspace.png +0 -0
  250. PaIRS_UniNa/icons/scale_all.png +0 -0
  251. PaIRS_UniNa/icons/scale_down.png +0 -0
  252. PaIRS_UniNa/icons/scale_up.png +0 -0
  253. PaIRS_UniNa/icons/scan_list.png +0 -0
  254. PaIRS_UniNa/icons/scan_path.png +0 -0
  255. PaIRS_UniNa/icons/search.png +0 -0
  256. PaIRS_UniNa/icons/showIW_off.png +0 -0
  257. PaIRS_UniNa/icons/showIW_on.png +0 -0
  258. PaIRS_UniNa/icons/show_all.png +0 -0
  259. PaIRS_UniNa/icons/sort.png +0 -0
  260. PaIRS_UniNa/icons/sort_reversed.png +0 -0
  261. PaIRS_UniNa/icons/spiv_proc.png +0 -0
  262. PaIRS_UniNa/icons/spiv_proc_off.png +0 -0
  263. PaIRS_UniNa/icons/star.png +0 -0
  264. PaIRS_UniNa/icons/step_inheritance.png +0 -0
  265. PaIRS_UniNa/icons/subMIN_off.png +0 -0
  266. PaIRS_UniNa/icons/subMIN_on.png +0 -0
  267. PaIRS_UniNa/icons/tom.png +0 -0
  268. PaIRS_UniNa/icons/trash.png +0 -0
  269. PaIRS_UniNa/icons/undo.png +0 -0
  270. PaIRS_UniNa/icons/unedited.png +0 -0
  271. PaIRS_UniNa/icons/unina_dii.png +0 -0
  272. PaIRS_UniNa/icons/uninitialized.png +0 -0
  273. PaIRS_UniNa/icons/unlink.png +0 -0
  274. PaIRS_UniNa/icons/unwrap_items.png +0 -0
  275. PaIRS_UniNa/icons/up.png +0 -0
  276. PaIRS_UniNa/icons/updating_import.gif +0 -0
  277. PaIRS_UniNa/icons/updating_pairs.gif +0 -0
  278. PaIRS_UniNa/icons/vectorColor.png +0 -0
  279. PaIRS_UniNa/icons/vettore.png +0 -0
  280. PaIRS_UniNa/icons/view.png +0 -0
  281. PaIRS_UniNa/icons/view_off.png +0 -0
  282. PaIRS_UniNa/icons/vis_logo.png +0 -0
  283. PaIRS_UniNa/icons/waiting_circle.png +0 -0
  284. PaIRS_UniNa/icons/warning.png +0 -0
  285. PaIRS_UniNa/icons/warning_circle.png +0 -0
  286. PaIRS_UniNa/icons/window.png +0 -0
  287. PaIRS_UniNa/icons/workspace.png +0 -0
  288. PaIRS_UniNa/icons/wrap_items.png +0 -0
  289. PaIRS_UniNa/icons/write_list.png +0 -0
  290. PaIRS_UniNa/listLib.py +303 -0
  291. PaIRS_UniNa/mtfPIV.py +256 -0
  292. PaIRS_UniNa/parForMulti.py +435 -0
  293. PaIRS_UniNa/parForWorkers.py +593 -0
  294. PaIRS_UniNa/pivParFor.py +235 -0
  295. PaIRS_UniNa/plt_util.py +141 -0
  296. PaIRS_UniNa/preProcParFor.py +155 -0
  297. PaIRS_UniNa/procTools.py +1391 -0
  298. PaIRS_UniNa/readcfg.py +52 -0
  299. PaIRS_UniNa/rqrdpckgs.txt +8 -0
  300. PaIRS_UniNa/stereoPivParFor.py +227 -0
  301. PaIRS_UniNa/tAVarie.py +215 -0
  302. PaIRS_UniNa/tabSplitter.py +612 -0
  303. PaIRS_UniNa/ui_Calibration_Tab.py +545 -0
  304. PaIRS_UniNa/ui_Custom_Top.py +296 -0
  305. PaIRS_UniNa/ui_Input_Tab.py +1101 -0
  306. PaIRS_UniNa/ui_Input_Tab_CalVi.py +1283 -0
  307. PaIRS_UniNa/ui_Log_Tab.py +263 -0
  308. PaIRS_UniNa/ui_Output_Tab.py +2362 -0
  309. PaIRS_UniNa/ui_Process_Tab.py +3810 -0
  310. PaIRS_UniNa/ui_Process_Tab_CalVi.py +1549 -0
  311. PaIRS_UniNa/ui_Process_Tab_Disp.py +1141 -0
  312. PaIRS_UniNa/ui_Process_Tab_Min.py +437 -0
  313. PaIRS_UniNa/ui_ResizePopup.py +204 -0
  314. PaIRS_UniNa/ui_Vis_Tab.py +1628 -0
  315. PaIRS_UniNa/ui_Vis_Tab_CalVi.py +1251 -0
  316. PaIRS_UniNa/ui_Whatsnew.py +132 -0
  317. PaIRS_UniNa/ui_gPairs.py +871 -0
  318. PaIRS_UniNa/ui_infoPaIRS.py +551 -0
  319. PaIRS_UniNa/whatsnew.txt +6 -0
  320. pairs_unina-0.2.5.dist-info/METADATA +151 -0
  321. pairs_unina-0.2.5.dist-info/RECORD +323 -0
  322. pairs_unina-0.2.5.dist-info/WHEEL +5 -0
  323. pairs_unina-0.2.5.dist-info/top_level.txt +2 -0
@@ -0,0 +1,3069 @@
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(inst)
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("")
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=3,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=4,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=4,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
+ if name is None:
1456
+ processNames=[item[0].name for item in self.itemList[0]]+[item[0].name for item in self.restoreTree.itemList[0]]
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
+ self.createParPrevs(ITE)
1465
+ self.itemList[0].insert(ind,[ITE])
1466
+ else:
1467
+ ITE=self.itemList[0][ind][0]
1468
+
1469
+ item.setText(0,str(ind))
1470
+ item.setTextAlignment(0,Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
1471
+ item.setIcon(1, TreeIcons.icons[ITE.icon]) # Set the icon of the new item
1472
+ #item.setFlags(item.flags() | Qt.ItemIsEditable)
1473
+
1474
+ ind=max([0,ind])
1475
+ self.insertTopLevelItem(ind,item)
1476
+ self.setProcessItemWidget(item,ITE)
1477
+ self.createStepItems(item,ITE)
1478
+ self.setStepItemWidgets(item,ind)
1479
+ item.setExpanded(True)
1480
+
1481
+ item.setData(0,Qt.ItemDataRole.UserRole,[False])
1482
+ self.resetImNumber(ind)
1483
+
1484
+ self.restoreStyleAfterDrag()
1485
+ self.blockSignals(False)
1486
+ if FlagNewItem: self.signals.updateLists.emit()
1487
+ return item
1488
+
1489
+ def setProcessItemWidget(self,item:QTreeWidgetItem,ITE:ITEpar):
1490
+ title=ITE.name
1491
+ title_label = ResizingLabel(title)
1492
+ title_label.setObjectName('title_process')
1493
+ title_label.setFixedHeight(titleHeight)
1494
+ title_label.setMinimumWidth(titleNameWidth)
1495
+ title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
1496
+ # Set the font of the title to bold and the subtitle to double font
1497
+ self.titleFont(title_label)
1498
+
1499
+ if not ITE.date:
1500
+ ITE.date = f"Created: {currentTimeString()}"
1501
+ sub_title=ITE.date
1502
+ sub_title_label = ResizingLabel(sub_title) # Create a QLabel for the subtitle
1503
+ sub_title_label.setObjectName('subtitle_process')
1504
+ sub_title_label.setFixedHeight(subtitleHeight)
1505
+ sub_title_label.setMinimumWidth(titleNameWidth)
1506
+ sub_title_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
1507
+ self.subTitleFont(sub_title_label)
1508
+
1509
+ process_title_layout = QVBoxLayout() # Create a vertical layout to place the subtitle below the title
1510
+ process_title_layout.addWidget(title_label) # Add the title of the element
1511
+ process_title_layout.addWidget(sub_title_label) # Add the subtitle
1512
+ process_title_layout.setContentsMargins(10, 2, 0, 2) # Remove margins
1513
+ process_title_layout.setSpacing(2) # Remove margins
1514
+
1515
+ switch = ModernSwitch(parent=self,name='Process',par=ITE)
1516
+ switch.setSwitch(ITE.FlagQueue)
1517
+ switch.toggled.connect(lambda: self.toogleProject(ITE))
1518
+ switch.setVisible(ITE.flagRun<=0 and ITE.Process!=ProcessTypes.cal and not self.FlagBin)
1519
+
1520
+ #spacer=QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
1521
+ main_layout=QHBoxLayout()
1522
+ main_layout.addLayout(process_title_layout)
1523
+ #main_layout.addSpacerItem(spacer)
1524
+ main_layout.addWidget(switch)
1525
+ main_layout.setContentsMargins(0,0,10,0)
1526
+ main_layout.setSpacing(6)
1527
+ main_layout.setStretch(0,0)
1528
+ main_layout.setStretch(1,1)
1529
+
1530
+ InfoMessage=ITE.InfoMessage()
1531
+ title_label.setToolTip(InfoMessage)
1532
+ title_label.setStatusTip(InfoMessage)
1533
+ sub_title_label.setToolTip(InfoMessage)
1534
+ sub_title_label.setStatusTip(InfoMessage)
1535
+
1536
+ widget = QWidget(self) # Create a widget container for the layout
1537
+ widget.setLayout(main_layout) # Set the layout for the widget container
1538
+ widget.setMinimumWidth(titleNameWidth+switch.switchWidth+main_layout.spacing())
1539
+ self.setItemWidget(item, 1 ,widget)
1540
+ return widget
1541
+
1542
+ def toogleProject(self,ITE:ITEpar):
1543
+ #self.projectTree.itemList[0][ITE.ind[0]].FlagQueue = ITE.FlagQueue
1544
+ topLevelItem=self.Explorer.projectTree.topLevelItem(ITE.ind[0])
1545
+ itemWidget=self.Explorer.projectTree.itemWidget(topLevelItem,1)
1546
+ switch:ModernSwitch=itemWidget.findChildren(ModernSwitch)[0]
1547
+ FlagToogle=False
1548
+ if ITE.FlagQueue==True and switch._checked==False: FlagToogle=True
1549
+ elif ITE.FlagQueue==False:
1550
+ ITELists=self.Explorer.projectTree.itemList[1][ITE.ind[0]][0]
1551
+ if all([i[0].FlagQueue==False for i in ITELists if i[0].flagRun<=0 and i[0].Process!=ProcessTypes.cal]): FlagToogle=True
1552
+ if FlagToogle:
1553
+ switch.blockSignals(True)
1554
+ switch.toggle()
1555
+ switch.blockSignals(False)
1556
+ if self.Explorer.TREpar.process is not None:
1557
+ self.Explorer.ITEpar.FlagQueue=self.Explorer.ITEfromInd(self.Explorer.ITEpar.ind).FlagQueue
1558
+ self.Explorer.TREpar.FlagQueue=self.Explorer.projectTree.itemList[0][self.Explorer.TREpar.project].FlagQueue
1559
+ self.adjustSwitches()
1560
+ # self.itemSelectionChanged.emit()
1561
+
1562
+ def createStepItems(self,parentItem:QTreeWidgetItem,parentITE:ITEpar):
1563
+ indItem=parentITE.ind[2]
1564
+ #ITEs=self.itemList[0][indItem]
1565
+ indChild=-1
1566
+ for stepType,v in parentITE.children.items():
1567
+ QTreeWidgetItem(parentItem)
1568
+ indChild+=1
1569
+ if indChild>=len(self.itemList[0][indItem])-1:
1570
+ ITE=ITEpar(Process=parentITE.Process,Step=stepType)
1571
+ ITE.ind[0]=parentITE.ind[0]
1572
+ ITE.ind[1]=parentITE.ind[1]
1573
+ ITE.ind[2]=indItem
1574
+ ITE.ind[3]=indChild
1575
+ ITE.active=v
1576
+ self.itemList[0][indItem].append(ITE)
1577
+ self.createPars(ITE)
1578
+ return
1579
+
1580
+ def setStepItemWidgets(self,parentItem:QTreeWidgetItem,indItem):
1581
+ for k,data in enumerate(self.itemList[0][indItem][1:]):
1582
+ data:ITEpar
1583
+ item:QTreeWidgetItem=parentItem.child(k)
1584
+ itemWidget=StepItemWidget(data,parent=self)
1585
+ self.setItemWidget(item,1,itemWidget)
1586
+ if len(data.link)==0:
1587
+ if self.gui and data.flagRun==0 and len(self.gui.ui.tabAreaWidget.TABpar_prev_at(data.ind))<=1:
1588
+ item.setIcon(0,self.uneditedIcon)
1589
+ else:
1590
+ item.setIcon(0,QIcon())
1591
+ else:
1592
+ item.setIcon(0,self.linkedIcon)
1593
+ if not data.active:
1594
+ item.setHidden(True)
1595
+
1596
+ def titleFont(self,label:QLabel,fPixelSize=None):
1597
+ if fPixelSize is None:
1598
+ if hasattr(self.gui,'GPApar'): fPixelSize=self.gui.GPApar.fontPixelSize
1599
+ else: fPixelSize=fontPixelSize
1600
+ font = label.font()
1601
+ font.setFamily(fontName)
1602
+ font.setBold(True) # Bold title
1603
+ fPS=min([fPixelSize+dFontPixelSize_processTitle,fontPixelSize+dFontPixelSize_processTitle+3])
1604
+ font.setPixelSize(fPS) # Double font size for subtitle
1605
+ label.setFont(font)
1606
+
1607
+ def subTitleFont(self,label:QLabel,fPixelSize=None):
1608
+ if fPixelSize is None:
1609
+ if hasattr(self.gui,'GPApar'): fPixelSize=self.gui.GPApar.fontPixelSize
1610
+ else: fPixelSize=fontPixelSize
1611
+ font = label.font()
1612
+ font.setFamily(fontName)
1613
+ font.setItalic(True) # Bold title
1614
+ fPS=min([fPixelSize,fontPixelSize+3])
1615
+ font.setPixelSize(fPS) # Double font size for subtitle
1616
+ label.setFont(font)
1617
+
1618
+ def startEditing(self, item, column):
1619
+ if self.itemWidget(item, column):
1620
+ self.editItem(item, column)
1621
+
1622
+ def editItem(self, item, column):
1623
+ if column == 0 or item.parent() is not None: # Se è la prima colonna, non fare nulla
1624
+ return
1625
+ widget = self.itemWidget(item, column)
1626
+ if isinstance(widget, QLineEdit):
1627
+ return # If already editing, do nothing
1628
+ k=self.indexOfTopLevelItem(item)
1629
+ text = self.itemList[0][k][0].name
1630
+ line_edit = CustomLineEdit(text, self)
1631
+ self.titleFont(line_edit)
1632
+ line_edit.setText(text)
1633
+ self.setItemWidget(item, column, line_edit)
1634
+ line_edit.selectAll()
1635
+ line_edit.setFocus()
1636
+ line_edit.editingFinished.connect(lambda: self.finishEditing(item, column))
1637
+ line_edit.cancelEditing.connect(lambda: self.finishEditing(item, column))
1638
+
1639
+ def finishEditing(self, item, column):
1640
+ line_edit = self.itemWidget(item, column)
1641
+ if not hasattr(line_edit,'text'):
1642
+ return
1643
+ new_text = line_edit.text()
1644
+ k=self.indexOfTopLevelItem(item)
1645
+ data=self.itemList[0][k]
1646
+ data[0].name=new_text
1647
+ self.setProcessItemWidget(item, data[0])
1648
+ line_edit.editingFinished.disconnect()
1649
+ line_edit.cancelEditing.disconnect()
1650
+ self.editingFinished()
1651
+
1652
+ def keyPressEvent(self, event):
1653
+ self.actionBar.treeKeyPressEvent(event)
1654
+
1655
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter) and event.modifiers()==Qt.KeyboardModifier.ShiftModifier:
1656
+ self.currentItem().setExpanded(False)
1657
+ return True # Impedisce al widget di ricevere l'evento
1658
+ elif event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter):
1659
+ self.currentItem().setExpanded(True)
1660
+ return True # Impedisce al widget di ricevere l'evento
1661
+ elif event.key()==Qt.Key.Key_Escape:
1662
+ self.setCurrentItem(None)
1663
+ self.clearSelection()
1664
+ self.Explorer.arrangeCurrentProcess(self)
1665
+ return True
1666
+ else:
1667
+ super().keyPressEvent(event)
1668
+
1669
+ def cutItems(self,items,FlagDelete=False):
1670
+ cutted_items=[None]*len(items)
1671
+ items_expanded=[None]*len(items)
1672
+ for k,item in enumerate(items):
1673
+ item:QTreeWidgetItem
1674
+ cutted_items[k]=self.duplicateItem(item)
1675
+ items_expanded[k]=item.isExpanded()
1676
+ if FlagDelete: type(self).deleted_items=cutted_items
1677
+ else: type(self).cutted_items=cutted_items
1678
+ type(self).items_expanded=items_expanded
1679
+ return
1680
+
1681
+ def button_import_action(self):
1682
+ exts=[f'*{e}' for e in [outExt.min,outExt.piv,outExt.cal,outExt.spiv]]
1683
+ dirname=os.path.dirname(self.TREpar.outName) if self.TREpar.outName else ''
1684
+ filename, _ = QFileDialog.getOpenFileName(self.gui,\
1685
+ "Select a PaIRS process file", filter=' '.join(exts),\
1686
+ dir=dirname,\
1687
+ options=optionNativeDialog)
1688
+ if not filename: return
1689
+ FlagError=False
1690
+ errorString=''
1691
+ try:
1692
+ from .Saving_tools import loadList
1693
+ data, errorMessage=loadList(filename)
1694
+ errorString+=errorMessage
1695
+ except Exception as inst:
1696
+ errorString+=str(inst)
1697
+ if errorString:
1698
+ if '_data' in filename:
1699
+ try:
1700
+ basename,ext=os.path.splitext(filename)
1701
+ filename2=basename[:-5]+ext
1702
+ data, errorMessage=loadList(filename2)
1703
+ errorString=errorString+errorMessage if errorMessage else ''
1704
+ except Exception as inst2:
1705
+ errorString+=str(inst2)
1706
+ FlagError=True
1707
+ else:
1708
+ filename=filename2
1709
+ else: FlagError=True
1710
+ if FlagError or errorString:
1711
+ WarningMessage=f'Error with loading the file: {filename}\n'
1712
+ warningDialog(self,WarningMessage)
1713
+ pri.Error.red(f'{WarningMessage}\n{errorString}\n')
1714
+ return
1715
+ try:
1716
+ ind=self.topLevelItemCount()
1717
+ insert_at_depth(self.itemList,self.listDepth,[ind],data)
1718
+ ITEs:TREpar=self.itemList[0][ind]
1719
+ for ITE in ITEs:
1720
+ ITE:ITEpar
1721
+ ITE.ind[0]=self.TREpar.project
1722
+ ITE.ind[1]=int(self.FlagBin)
1723
+ ITE.ind[2]=ind
1724
+ ITE.link=[]
1725
+ ITE.dependencies=[]
1726
+ self.createProcessItem(ind,FlagNewItem=False)
1727
+ for ITE in ITEs: self.Explorer.setITElayout(ITE)
1728
+ item=self.topLevelItem(ind)
1729
+ self.setCurrentItem(item)
1730
+ item.setSelected(True)
1731
+ except Exception as inst:
1732
+ WarningMessage=f'Error while retrieving the process "{data[0][0][0].name}" from the file: {filename}\n'
1733
+ warningDialog(self,WarningMessage)
1734
+ pri.Error.red(f'{WarningMessage}\n{traceback.format_exc()}\n')
1735
+ return
1736
+
1737
+ def save_current_process(self,filename):
1738
+ Process=[]
1739
+ FlagSaved=False
1740
+ for l in self.itemList:
1741
+ Process.append([l[self.TREpar.process]])
1742
+ try:
1743
+ from .Saving_tools import saveList
1744
+ saveList(Process,filename)
1745
+ except Exception as inst:
1746
+ warningDialog(self,f'Error while saving the configuration file {filename}!\nPlease, retry.')
1747
+ pri.Error.red(f'Error while saving the configuration file {filename}!\n{inst}')
1748
+ return FlagSaved
1749
+
1750
+ def button_saveas_action(self):
1751
+ for attr_name, attr_value in vars(ProcessTypes).items():
1752
+ if attr_value == self.ITEpar.Process:
1753
+ procExt=getattr(outExt,attr_name)
1754
+ break
1755
+ Title="Select location and name of the process file to save"
1756
+ dirname=os.path.dirname(self.TREpar.outName) if self.TREpar.outName else ''
1757
+ filename, _ = QFileDialog.getSaveFileName(self,Title,
1758
+ dir=dirname+self.ITEpar.name.replace(' ','_'),
1759
+ filter=f'*{procExt}',\
1760
+ options=optionNativeDialog)
1761
+ if not filename: return
1762
+ if len(filename)>=len(procExt):
1763
+ if filename[-len(procExt):]==procExt: filename=filename[:-len(procExt)] #per adattarlo al mac
1764
+ filename=myStandardRoot('{}'.format(str(filename)))
1765
+ if not outExt.cfg in filename:
1766
+ filename=filename+procExt
1767
+ self.save_current_process(filename)
1768
+
1769
+ def button_info_action(self):
1770
+ warningDialog(self,self.ITEpar.InfoMessage(),pixmap=TreeIcons.pixmaps[self.ITEpar.icon],title='Process information')
1771
+
1772
+ def button_rename_action(self):
1773
+ selected_items = self.selectedItems()
1774
+ if selected_items:
1775
+ item = selected_items[0]
1776
+ #column = self.currentColumn()
1777
+ #self.editItem(item, column)
1778
+ self.editItem(item, 1)
1779
+
1780
+ def deleteDependencies(self,indexes):
1781
+ if hasattr(self.gui,'setLinks'):
1782
+ for i in indexes:
1783
+ for ITE in self.itemList[0][i]:
1784
+ ITE:ITEpar
1785
+ for dep in ITE.dependencies:
1786
+ self.gui.setLinks(dep,ITE.ind,FlagUnlink=True)
1787
+ if ITE.link: self.gui.setLinks(ITE.ind,ITE.link,FlagUnlink=True)
1788
+
1789
+ def copy_cut_delete_action(self,FlagCut=False,FlagDelete=False,FlagRestore=False):
1790
+ items,indexes=self.selectTopLevel()
1791
+ if FlagRestore: FlagDelete=True
1792
+ if FlagDelete and not FlagRestore:
1793
+ FlagCut=False
1794
+ if self.FlagBin:
1795
+ flagYes=questionDialog(self,f"Are you sure you want to remove the selected process{'es' if len(items)>1 else ''}? This operation is irreversible!")
1796
+ else: flagYes=True
1797
+ if not flagYes: return
1798
+ if FlagDelete and not self.FlagBin: self.deleteDependencies(indexes)
1799
+ self.cutItems(items,FlagDelete)
1800
+
1801
+ FlagSignal=True
1802
+ if FlagCut or FlagDelete:
1803
+ if len(indexes)<1000:
1804
+ for item in items:
1805
+ self.takeTopLevelItem(self.indexOfTopLevelItem(item))
1806
+ if not FlagDelete: self.cutLists(indexes)
1807
+ else: self.deleteLists(indexes)
1808
+ else:
1809
+ if not FlagDelete: self.cutLists(indexes)
1810
+ else: self.deleteLists(indexes)
1811
+ FlagSignal=False
1812
+ else:
1813
+ self.copyLists(indexes)
1814
+
1815
+ if FlagCut or FlagDelete:
1816
+ self.setCurrentItem(None)
1817
+ self.clearSelection()
1818
+ self.setFocus()
1819
+ if FlagSignal and not self.signalsBlocked(): self.signals.updateLists.emit()
1820
+ if FlagRestore or (not self.FlagBin and FlagDelete):
1821
+ self.restore_action()
1822
+
1823
+ def restore_action(self):
1824
+ self.restoreTree:ProcessTree
1825
+ 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)
1826
+
1827
+ def paste_above_below_action(self,FlagAbove=True,row=None,FlagPasteDeleted=False,FlagSelectAfterPasting=False):
1828
+ if len(type(self).cutted_items)==0 and not FlagPasteDeleted: return
1829
+ elif len(type(self).deleted_items)==0 and FlagPasteDeleted: return
1830
+ self.blockSignals(True)
1831
+ selectedItems,indexes=self.selectTopLevel()
1832
+ self.blockSignals(False)
1833
+ #self.clearSelection()
1834
+ if FlagPasteDeleted: new_items=type(self).deleted_items
1835
+ else: new_items=type(self).cutted_items
1836
+ if FlagAbove:
1837
+ if row is None:
1838
+ if selectedItems: row=indexes[0]
1839
+ else: row=0
1840
+ firstItemToScroll=new_items[0]
1841
+ lastItemToScroll=new_items[-1]
1842
+ else:
1843
+ if row is None:
1844
+ if selectedItems: row=indexes[-1]+1
1845
+ else: row=self.topLevelItemCount()
1846
+ else: row=row+1
1847
+ firstItemToScroll=new_items[-1]
1848
+ lastItemToScroll=new_items[0]
1849
+ self.insertItems2List(row,new_items,True,FlagSignal=False)
1850
+ for k,item in enumerate(new_items):
1851
+ item:QTreeWidgetItem
1852
+ item.setExpanded(type(self).items_expanded[k])
1853
+
1854
+ indexes=[row+k for k in range(len(new_items))]
1855
+ if not self.FlagCutted and not FlagPasteDeleted:
1856
+ self.cutItems(new_items)
1857
+ self.pasteLists(row,FlagPasteDeleted)
1858
+ for k in indexes:
1859
+ item=self.topLevelItem(k)
1860
+ ITEs=self.itemList[0][k]
1861
+ self.setProcessItemWidget(item,ITEs[0]) # Set the widget container as the
1862
+ self.setStepItemWidgets(item,ITEs[0].ind[2])
1863
+
1864
+ self.scrollToItem(firstItemToScroll)
1865
+ self.scrollToItem(lastItemToScroll)
1866
+ if FlagPasteDeleted and not FlagSelectAfterPasting:
1867
+ type(self).deleted_items=[]
1868
+ self.clearSelection()
1869
+ else:
1870
+ self.setCurrentItem(new_items[-1])
1871
+ new_items[-1].setSelected(True)
1872
+ if FlagPasteDeleted: self.Explorer.processTree_item_selection(self)
1873
+ self.setFocus()
1874
+ self.signals.updateLists.emit()
1875
+
1876
+ def button_copy_action(self):
1877
+ self.copy_cut_delete_action()
1878
+ self.setProcessActionButtonLayout()
1879
+
1880
+ def button_paste_below_action(self):
1881
+ self.blockSignals(True)
1882
+ self.paste_above_below_action(FlagAbove=False)
1883
+ self.blockSignals(False)
1884
+ self.itemSelectionChanged.emit()
1885
+
1886
+ def button_paste_above_action(self):
1887
+ self.blockSignals(True)
1888
+ self.paste_above_below_action()
1889
+ self.blockSignals(False)
1890
+ self.itemSelectionChanged.emit()
1891
+
1892
+ def button_process_loop_action(self):
1893
+ pri.Info.magenta('Process loop over folders')
1894
+ paths=choose_directories()
1895
+ if paths:
1896
+ ITEs=self.Explorer.ITEsfromTRE(self.TREpar)
1897
+ ind=copy.deepcopy(ITEs[0].ind)
1898
+ processType=ITEs[0].Process
1899
+ pixmap_list=[]
1900
+ name_list=[]
1901
+ flag_list=[]
1902
+ for i in range(1,len(ITEs)):
1903
+ ITE:ITEpar=ITEs[i]
1904
+ pixmap_list.append(icons_path+ITE.icon)
1905
+ name_list.append(ITE.name)
1906
+ flag_list.append(ITE.Step!=StepTypes.cal)
1907
+ func=lambda i, opts: self.process_loop(paths,processType,ind,ITEs[0].name,i,opts)
1908
+ dialog = FolderLoopDialog(pixmap_list, name_list, flag_list, parent=self, paths=paths, func=func, process_name=ITEs[0].name)
1909
+ dialog.exec()
1910
+ return
1911
+
1912
+ def process_loop(self,paths,processType,ind,name0,i,opts):
1913
+ nProcess=self.topLevelItemCount()
1914
+ path=paths[i]
1915
+ name=name0+f" (.../{os.path.basename(path)}/)"
1916
+ item=self.createProcessItem(processType,nProcess,name=name)
1917
+ ind_master=copy.deepcopy(ind)
1918
+ ind_master[-1]=-1
1919
+ ind_slave=copy.deepcopy(ind)
1920
+ ind_slave[2]=nProcess
1921
+ ind_slave[-1]=0
1922
+
1923
+ for j in range(len(opts)):
1924
+ ind_master[3]=j
1925
+ ind_slave[3]=j
1926
+ if opts[j] in (0,2):
1927
+ ind_new=self.gui.copy_pars(ind_slave,ind_master,FlagNew=True)
1928
+ item_child=item.child(j)
1929
+ ITE:ITEpar=self.Explorer.ITEfromInd(ind_new)
1930
+ item_child.setHidden(not ITE.active)
1931
+ if opts[j]==2:
1932
+ INP: INPpar=self.gui.w_Input.TABpar_at(ind_new)
1933
+ INP.path=myStandardPath(path)
1934
+ self.gui.w_Input.scanImList(ind_new)
1935
+ self.gui.w_Input.checkINPpar(ind_new)
1936
+ self.gui.w_Input.setINPwarn(ind_new)
1937
+ self.Explorer.setITElayout(ITE)
1938
+
1939
+ TABname=self.gui.w_Input.TABname
1940
+ self.gui.bridge(TABname,ind_new)
1941
+ for w in self.gui.tabWidgets:
1942
+ w:gPaIRS_Tab
1943
+ if w!=self.gui.w_Input:
1944
+ currpar=w.TABpar.duplicate()
1945
+ TABind_new=w.TABpar_at(ind_new)
1946
+ if TABind_new and not TABind_new.FlagNone:
1947
+ w.TABpar.copyfrom(TABind_new)
1948
+ w.adjustTABpar()
1949
+ w.adjustTABparInd()
1950
+ w.TABpar.copyfrom(currpar)
1951
+ self.gui.bridge(w.TABname,ind_new)
1952
+ #item_child=item.child(j)
1953
+ #item_child.setSelected(True)
1954
+ #self.setCurrentItem(item_child)
1955
+ else:
1956
+ self.gui.link_pars(ind_slave,ind_master,FlagSet=False)
1957
+ return
1958
+
1959
+ def button_delete_action(self):
1960
+ self.blockSignals(True)
1961
+ self.copy_cut_delete_action(FlagDelete=True)
1962
+ self.blockSignals(False)
1963
+ self.itemSelectionChanged.emit()
1964
+
1965
+ def button_restore_action(self):
1966
+ self.copy_cut_delete_action(FlagRestore=True)
1967
+
1968
+ def button_clean_action(self):
1969
+ self.blockSignals(True)
1970
+ self.selectAll()
1971
+ self.copy_cut_delete_action(FlagDelete=True)
1972
+ self.repaint()
1973
+ self.blockSignals(False)
1974
+ #self.currentItemChanged.emit(None,None)
1975
+ self.itemSelectionChanged.emit()
1976
+
1977
+ def setProcessActionButtonLayout(self):
1978
+ item=self.currentItem()
1979
+ FlagEnabled=item is not None and item.parent() is None
1980
+ FlagPaste=len(ProcessTree.cutted_items)>0
1981
+ self.actionBar.setVisible(True) #self.actionBar.setVisible(FlagEnabled)
1982
+ self.actionBar.button_saveas.setEnabled(FlagEnabled)
1983
+ self.actionBar.button_info.setEnabled(FlagEnabled)
1984
+ self.actionBar.button_rename.setEnabled(FlagEnabled)
1985
+ self.actionBar.button_copy.setEnabled(FlagEnabled)
1986
+ self.actionBar.button_paste_below.setEnabled(FlagPaste and not self.gui.FlagRun)
1987
+ self.actionBar.button_paste_above.setEnabled(FlagPaste and not self.gui.FlagRun)
1988
+ self.actionBar.button_process_loop.setEnabled(FlagEnabled and not self.FlagBin)
1989
+ self.actionBar.button_restore.setVisible(self.FlagBin)
1990
+ self.actionBar.sep_restore.setVisible(self.FlagBin)
1991
+ self.actionBar.button_restore.setEnabled(FlagEnabled)
1992
+ self.actionBar.button_delete.setEnabled(FlagEnabled)
1993
+ self.actionBar.button_clean.setEnabled(self.topLevelItemCount()>0)
1994
+
1995
+ class StepItemWidget(QWidget):
1996
+ pixmaps={'error' : 'issue.png',
1997
+ 'done' : 'completed.png',
1998
+ 'running' : 'running.png',
1999
+ 'warning' : 'warning_circle.png',
2000
+ 'running_warn' : 'running_warn.png',
2001
+ 'paused' : 'paused.png',
2002
+ 'queue' : 'queue.png',
2003
+ 'uninitialized' : 'uninitialized.png'
2004
+ }
2005
+
2006
+ colors={'error' : 'rgb(254, 61, 61)', #'#C9302C',
2007
+ 'done' : 'rgb( 46,204,113)', #'#4CAE4C',
2008
+ 'running' : 'rgb( 48,107,255)', #'#3A70C7',
2009
+ 'warning' : 'rgb(255,212, 42)', #'#EC971F',
2010
+ 'running_warn' : 'rgb( 48,107,255)', #'#3A70C7',
2011
+ 'paused' : 'rgb(255,127, 42)', #'#3A70C7',
2012
+ 'queue' : 'rgb(181,170,255)', #'#B5AAFF',
2013
+ 'uninitialized' : 'rgb( 0,0,0)',
2014
+ }
2015
+
2016
+ label_size=secondLevelHeight-4
2017
+ bar_width=label_size*3+4
2018
+
2019
+ def __init__(self, ITE:ITEpar, parent=None):
2020
+ super().__init__(parent)
2021
+ if parent is None:
2022
+ self.gui=self.window()
2023
+ else:
2024
+ from .gPaIRS import gPaIRS
2025
+ if hasattr(parent,'gui'):
2026
+ self.gui:gPaIRS=parent.gui
2027
+ else:
2028
+ self.gui:gPaIRS=parent.window()
2029
+
2030
+ # Create the layout
2031
+ layout = QHBoxLayout(self)
2032
+
2033
+ # Add an icon
2034
+ self.stepIcon = QLabel()
2035
+ self.stepIcon.setFixedHeight(secondLevelHeight-2)
2036
+ self.stepIcon.setFixedWidth(secondLevelHeight-2)
2037
+ self.stepIcon.setScaledContents(True)
2038
+ self.stepIcon.setPixmap(TreeIcons.pixmaps[ITE.icon])
2039
+ layout.addWidget(self.stepIcon)
2040
+
2041
+ # Add a spacer
2042
+ spacer = QSpacerItem(5, secondLevelHeight, QSizePolicy.Minimum, QSizePolicy.Minimum)
2043
+ layout.addItem(spacer)
2044
+
2045
+ # Add a label with text
2046
+ self.stepName = QLabel()
2047
+ self.stepName.setObjectName('title_step')
2048
+ self.stepName.setFixedWidth(stepNameWidth)
2049
+ self.stepName.setFixedHeight(secondLevelHeight)
2050
+ self.stepTitleFont(self.stepName)
2051
+ layout.addWidget(self.stepName)
2052
+
2053
+ self.stepName.setText(ITE.name)
2054
+ self.stepName.setToolTip(ITE.name)
2055
+ self.stepName.setStatusTip(ITE.name)
2056
+
2057
+ # Add another icon
2058
+ self.label = ClickableLabel()
2059
+ self.label.setFixedWidth(self.label_size)
2060
+ self.label.setFixedHeight(self.label_size)
2061
+ self.label.setScaledContents(True)
2062
+ self.label.setToolTip(ITE.warningMessage)
2063
+ self.label.setStatusTip(ITE.warningMessage)
2064
+ layout.addWidget(self.label)
2065
+
2066
+ # Add a progress bar
2067
+ self.progressBar = QProgressBar()
2068
+ self.progressBar.setFixedWidth(self.bar_width)
2069
+ self.progressBar.setFixedHeight(self.label_size)
2070
+ layout.addWidget(self.progressBar)
2071
+
2072
+ # Add a spacer
2073
+ spacer = QSpacerItem(40, secondLevelHeight, QSizePolicy.Expanding, QSizePolicy.Minimum)
2074
+ layout.addItem(spacer)
2075
+
2076
+ # Set layout margin and spacing
2077
+ layout.setContentsMargins(secondLevelIndentation, 0, 0, 0)
2078
+ layout.setSpacing(secondLevelSpacing)
2079
+
2080
+ minimumWidth=self.stepIcon.width()+self.stepName.width()+self.label.width()+self.progressBar.width()+secondLevelSpacing*4+secondLevelIndentation
2081
+ self.setMinimumWidth(minimumWidth)
2082
+ self.setFromITE(ITE)
2083
+
2084
+ def stepTitleFont(self,label:QLabel,fPixelSize=None):
2085
+ if fPixelSize is None:
2086
+ if hasattr(self.gui,'GPApar'): fPixelSize=self.gui.GPApar.fontPixelSize
2087
+ else: fPixelSize=fontPixelSize
2088
+ font = label.font()
2089
+ font.setFamily(fontName)
2090
+ fPS=min([fPixelSize + dFontPixelSize_stepTitle, fontPixelSize + dFontPixelSize_stepTitle +3])
2091
+ font.setPixelSize(fPS) # Double font size for subtitle
2092
+ label.setFont(font)
2093
+
2094
+ def colorProgressBar(self,color='green'):
2095
+ style_sheet = f"""
2096
+ QProgressBar {{
2097
+ background-color: transparent;
2098
+ border: 1px solid gray;
2099
+ border-radius: 5px;
2100
+ text-align: center;
2101
+ }}
2102
+
2103
+ QProgressBar::chunk {{
2104
+ background-color: {color};
2105
+ width: 1px; /* Larghezza del chunk */
2106
+ margin: 0px; /* Spazio tra i chunk */
2107
+ }}
2108
+ """
2109
+ self.progressBar.setStyleSheet(style_sheet)
2110
+
2111
+ def setFromITE(self,ITE:ITEpar):
2112
+ pixmap_path=StepItemWidget.pixmaps[ITE.label]
2113
+ FlagGif=False
2114
+ if ITE.flagRun==-2 and ITE.label=='running' and hasattr(self.gui,'runningMovie'):
2115
+ if hasattr(self.gui,'procdata') and self.gui.procdata is ITE.procdata:
2116
+ FlagGif=True
2117
+ if FlagGif:
2118
+ self.label.moviePixmap=TreeIcons.pixmaps[pixmap_path]
2119
+ self.label.setMovie(self.gui.runningMovie)
2120
+ else:
2121
+ self.label.moviePixmap=None
2122
+ self.label.setPixmap(TreeIcons.pixmaps[pixmap_path])
2123
+ self.label.setToolTip(ITE.warningMessage)
2124
+ self.label.setStatusTip(ITE.warningMessage)
2125
+
2126
+ if ITE.Step==StepTypes.cal:
2127
+ self.progressBar.setVisible(ITE.ncam>0)
2128
+ self.progressBar.setMaximum(ITE.ncam)
2129
+ self.progressBar.setValue(ITE.progress)
2130
+ self.progressBar.setFormat(f'{ITE.progress}/{ITE.ncam}')
2131
+ elif ITE.Step==StepTypes.disp:
2132
+ self.progressBar.setVisible(ITE.procdata.Nit>0)
2133
+ self.progressBar.setMaximum(ITE.procdata.Nit)
2134
+ self.progressBar.setValue(ITE.progress)
2135
+ #self.progressBar.setValue(ITE.procdata.numFinalized)
2136
+ #self.progressBar.setFormat(f'{ITE.progress}/{ITE.procdata.Nit}')
2137
+ else:
2138
+ self.progressBar.setVisible(ITE.procdata.nimg>0)
2139
+ self.progressBar.setMaximum(ITE.procdata.nimg)
2140
+ self.progressBar.setValue(ITE.progress)
2141
+ #self.progressBar.setValue(ITE.procdata.numFinalized) # Set the initial value of the progress bar
2142
+
2143
+
2144
+ bar_color=StepItemWidget.colors[ITE.label]
2145
+ self.colorProgressBar(bar_color)
2146
+
2147
+ class PaIRS_Explorer(gPaIRS_Tab):
2148
+ class ProcessTreeWidget_signals(QObject):
2149
+ pass
2150
+
2151
+ def __init__(self,parent=None,widgets=[]):
2152
+ super().__init__(parent,UiClass=None,ParClass=ITEpar)
2153
+ if __name__ == "__main__":
2154
+ iconW = QIcon()
2155
+ iconW.addFile(u""+ icons_path +"checklist.png", QSize(), QIcon.Normal, QIcon.Off)
2156
+ self.setWindowTitle('Process tree widget')
2157
+ self.setWindowIcon(iconW)
2158
+ self.signals=self.ProcessTreeWidget_signals()
2159
+
2160
+ #------------------------------------- Layout
2161
+ self.treeIcons=TreeIcons()
2162
+
2163
+ self.main_layout = QVBoxLayout()
2164
+ self.setLayout(self.main_layout)
2165
+ self.main_layout.setContentsMargins(5, 5, 5, 5)
2166
+
2167
+ self.projectTree_layout= QVBoxLayout()
2168
+ self.projectTree_layout.setContentsMargins(0, 0, 0, 0)
2169
+ self.projectTree_layout.setSpacing(1)
2170
+
2171
+ self.projectActionBar = ActionButtonBar(buttonData=projectActionButtons,globalButtons=projectGlobalActionButtons,buttonSize=projectActionButtonSize)
2172
+ self.projectTree_layout.addWidget(self.projectActionBar)
2173
+ self.projectTree = ProjectTree(self,projectActionBar=self.projectActionBar,widgets=widgets,Explorer=self)
2174
+
2175
+ self.scroll_area_projectTree = QScrollArea(self)
2176
+ self.scroll_area_projectTree.setObjectName('scroll_area_projectTree')
2177
+ self.scroll_area_projectTree.setWidgetResizable(True)
2178
+ self.scroll_area_projectTree.setWidget(self.projectTree)
2179
+ self.scroll_area_projectTree.setMinimumWidth(0)
2180
+ self.scroll_area_projectTree.setStyleSheet(scrollAreaStyle())
2181
+ self.projectTree_layout.addWidget(self.scroll_area_projectTree)
2182
+
2183
+ self.processTree_layout= QVBoxLayout()
2184
+ spa=int(processButtonSize[0]/4)
2185
+ self.processTree_layout.setContentsMargins(0, spa, 0, 0)
2186
+ self.processTree_layout.setSpacing(1)
2187
+ self.processButtonBar = ProcessButtonBar(processData,buttonSize=processButtonSize)
2188
+ self.processTree_layout.addWidget(self.processButtonBar)
2189
+ self.processTree_layout.addItem(QSpacerItem(0,spa,QSizePolicy.Minimum, QSizePolicy.Minimum))
2190
+ self.processActionBar = ActionButtonBar(buttonData=processActionButtons,globalButtons=processGlobalActionButtons,buttonSize=processActionButtonSize)
2191
+ self.processTree_layout.addWidget(self.processActionBar)
2192
+ self.binActionBar = ActionButtonBar(buttonData=processActionButtons,globalButtons=processGlobalActionButtons,buttonSize=processActionButtonSize)
2193
+ self.processTree_layout.addWidget(self.binActionBar)
2194
+
2195
+ self.stepButton_layout=QHBoxLayout()
2196
+ self.stepButton_layout.setContentsMargins(0, 0, 0, 0)
2197
+ self.stepButton_layout.setSpacing(int(stepButtonSize[0]/4))
2198
+
2199
+ self.stepButtonBar=StepButtonBar(stepData,buttonSize=stepButtonSize)
2200
+ self.stepButton_layout.addWidget(self.stepButtonBar)
2201
+
2202
+ self.processActionBar.additionalButtonBars={'globals': [self.processButtonBar], 'items': [self.stepButtonBar]}
2203
+ self.binActionBar.additionalButtonBars={'items': [self.stepButtonBar]}
2204
+
2205
+ self.processTree = ProcessTree(self,processActionBar=self.processActionBar,widgets=widgets,Explorer=self)
2206
+ self.binTree = ProcessTree(self,restoreTree=self.processTree,processActionBar=self.binActionBar,widgets=widgets,Explorer=self)
2207
+ self.processTree.restoreTree=self.binTree
2208
+ itemWidths=[
2209
+ titleNameWidth+ModernSwitch.switchWidth+6, #first level
2210
+ stepNameWidth+StepItemWidget.label_size+StepItemWidget.bar_width+secondLevelSpacing*3 #second level
2211
+ ]
2212
+ self.projectTree.setMinimumWidth(max(itemWidths)+self.projectTree.columnWidth(0)-30)
2213
+ self.processTree.setMinimumWidth(max(itemWidths)+self.processTree.columnWidth(0)-30)
2214
+ self.binTree.setMinimumWidth(max(itemWidths)+self.binTree.columnWidth(0)+40)
2215
+
2216
+ self.Explorer_tree_splitter=QSplitter(self)
2217
+ self.Explorer_tree_splitter.setObjectName('Explorer_tree_splitter')
2218
+ self.Explorer_tree_splitter.setContentsMargins(0, 0, 0, 0)
2219
+ self.Explorer_tree_splitter.setOrientation(Qt.Horizontal)
2220
+ self.scroll_area_processTree = QScrollArea(self)
2221
+ self.scroll_area_processTree.setObjectName('scroll_area_processTree')
2222
+ self.scroll_area_processTree.setWidgetResizable(True)
2223
+ self.scroll_area_processTree.setWidget(self.processTree)
2224
+ self.scroll_area_processTree.setMinimumWidth(0)
2225
+ self.scroll_area_processTree.setStyleSheet(scrollAreaStyle())
2226
+ self.scroll_area_binTree = QScrollArea(self)
2227
+ self.scroll_area_binTree.setObjectName('scroll_area_binTree')
2228
+ self.scroll_area_binTree.setWidgetResizable(True)
2229
+ self.scroll_area_binTree.setWidget(self.binTree)
2230
+ self.scroll_area_binTree.setMinimumWidth(0)
2231
+ self.scroll_area_binTree.setStyleSheet(scrollAreaStyle())
2232
+ self.Explorer_tree_splitter.addWidget(self.scroll_area_processTree)
2233
+ self.Explorer_tree_splitter.addWidget(self.scroll_area_binTree)
2234
+ self.stepButton_layout.addWidget(self.Explorer_tree_splitter)
2235
+ self.processTree_layout.addLayout(self.stepButton_layout)
2236
+
2237
+ stretches=[0,0,0,0,1] #process bar, spacer, process action bar, bin action bar, trees
2238
+ for k,s in enumerate(stretches):
2239
+ self.processTree_layout.setStretch(k,s)
2240
+
2241
+ self.Explorer_main_splitter=QSplitter(self)
2242
+ self.Explorer_main_splitter.setObjectName('Explorer_main_splitter')
2243
+ self.Explorer_main_splitter.setContentsMargins(0, 0, 0, 0)
2244
+ self.Explorer_main_splitter.setOrientation(Qt.Vertical)
2245
+
2246
+ self.projectWidget=QWidget(self)
2247
+ self.projectWidget.setLayout(self.projectTree_layout)
2248
+ self.projectWidget.setMinimumHeight(150)
2249
+
2250
+ self.processWidget=QWidget(self)
2251
+ self.processWidget.setLayout(self.processTree_layout)
2252
+ self.processWidget.setMinimumHeight(300)
2253
+
2254
+ #self.scroll_area_sup = QScrollArea()
2255
+ #self.scroll_area_sup.setObjectName('scroll_area_sup')
2256
+ #self.scroll_area_sup.setWidgetResizable(True)
2257
+ #self.scroll_area_sup.setWidget(self.projectWidget)
2258
+ #self.scroll_area_sup.setMinimumHeight(150)
2259
+ #self.scroll_area_sup.setStyleSheet(scrollAreaStyle())
2260
+ #self.projectWidget.setMinimumHeight(250)
2261
+
2262
+ #self.scroll_area_inf = QScrollArea()
2263
+ #self.scroll_area_inf.setObjectName('scroll_area_inf')
2264
+ #self.scroll_area_inf.setWidgetResizable(True)
2265
+ #self.scroll_area_inf.setWidget(self.processWidget)
2266
+ #self.scroll_area_inf.setMinimumHeight(300)
2267
+ #self.scroll_area_inf.setStyleSheet(scrollAreaStyle())
2268
+ #self.processWidget.setMinimumHeight(400)
2269
+
2270
+ #self.main_splitter.addWidget(self.scroll_area_sup)
2271
+ #self.main_splitter.addWidget(self.scroll_area_inf)
2272
+
2273
+ self.Explorer_main_splitter.addWidget(self.projectWidget)
2274
+ self.Explorer_main_splitter.addWidget(self.processWidget)
2275
+
2276
+ self.Explorer_main_splitter.setHandleWidth(15)
2277
+ self.Explorer_main_splitter.setCollapsible(0, False)
2278
+ self.Explorer_main_splitter.setCollapsible(1, False)
2279
+ self.main_layout.addWidget(self.Explorer_main_splitter)
2280
+
2281
+ # Creazione del pulsante checkable
2282
+ self.binButton = QToolButton(self.processButtonBar)
2283
+ self.binButton.setIconSize(QSize(self.processButtonBar.buttonSize[0], self.processButtonBar.buttonSize[0]))
2284
+ self.binButton.setFixedSize(self.processButtonBar.buttonSize[1], self.processButtonBar.buttonSize[1])
2285
+ if self.processButtonBar.FlagInvisible:
2286
+ self.binButton.setCursor(Qt.CursorShape.PointingHandCursor)
2287
+ self.binButton.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }")
2288
+ self.binButton.pressed.connect(lambda btn=self.binButton: btn.setStyleSheet("QToolButton { border: none; background: #dcdcdc;} QToolButton::menu-indicator { image: none; }"))
2289
+ self.binButton.released.connect(lambda btn=self.binButton: btn.setStyleSheet("QToolButton { border: none; background: none;} QToolButton::menu-indicator { image: none; }"))
2290
+ self.binButton.setToolTip('Deleted processes')
2291
+ self.binButton.setStatusTip('Deleted processes')
2292
+ self.processButtonBar.buttonLayout.addWidget(self.binButton)
2293
+ self.binIconOff=QIcon(''+ icons_path +'bin_off.png')
2294
+ self.binIconOn=QIcon(''+ icons_path +'bin_on.png')
2295
+ self.binButton.setCheckable(True)
2296
+ self.binButton.setChecked(False) # Mostra l'albero 2 di default
2297
+
2298
+ #------------------------------------- Declaration of parameters
2299
+ self.ITEpar:ITEpar=self.TABpar
2300
+ self.ITEpar.FlagNone=False
2301
+ self.processTree.ITEpar=self.ITEpar
2302
+ self.TREpar=self.projectTree.TREpar
2303
+ self.processTree.TREpar=self.projectTree.TREpar
2304
+ self.binTree.TREpar=self.projectTree.TREpar
2305
+ self.widgets=widgets
2306
+
2307
+ #------------------------------------- Callbacks
2308
+ self.currentTree=None
2309
+ self.binButton.clicked.connect(lambda: self.binButton_action(FlagTreeSelection=True))
2310
+ binShortCut=QShortcut(QKeySequence('Ctrl+B'),self)
2311
+ binShortCut.activated.connect(lambda:self.binButton.click())
2312
+ self.Explorer_tree_splitter.splitterMoved.connect(lambda: self.tree_splitter_action(FlagTreeSelection=True))
2313
+
2314
+ self.projectTree.installEventFilter(self)
2315
+ self.projectTree.actionBar.installEventFilter(self)
2316
+ self.processTree.installEventFilter(self)
2317
+ self.binTree.installEventFilter(self)
2318
+
2319
+ #self.processTree.currentItemChanged.connect(lambda: self.arrangeCurrentProcess(self.processTree))
2320
+ #self.binTree.currentItemChanged.connect(lambda: self.arrangeCurrentProcess(self.binTree))
2321
+ self.processTree.itemSelectionChanged.connect(lambda: self.processTree_item_selection(self.processTree))
2322
+ self.binTree.itemSelectionChanged.connect(lambda: self.processTree_item_selection(self.binTree))
2323
+ self.adjustProcessSelection=lambda: None
2324
+ self.binButton_action()
2325
+ #self.processTree_item_selection(self.processTree)
2326
+
2327
+ self.projectTree.adjustSelection=self.adjustProjectSelection
2328
+ self.adjustProjectSelection()
2329
+
2330
+ self.projectPageActions={}
2331
+ for k in projectPageButtons.keys():
2332
+ b:QPushButton=getattr(self.projectActionBar,'button_'+k)
2333
+ self.projectPageActions[k]=lambda butt=b: butt.click()
2334
+
2335
+ self.processPageActions={}
2336
+ def processButtonAction(tree:ProcessTree,butt:DraggableButton):
2337
+ tree.createProcessItem(butt.buttonData['type'])
2338
+ item=tree.topLevelItem(tree.topLevelItemCount()-1)
2339
+ tree.setCurrentItem(item)
2340
+ self.processTree_item_selection(tree)
2341
+ for t,b in self.processButtonBar.buttons.items():
2342
+ b:DraggableButton
2343
+ b.buttonAction=self.processPageActions[t]=lambda tree=self.processTree, butt=b: processButtonAction(tree,butt)
2344
+
2345
+ self.stepPage:StartingPage=None
2346
+ self.stepPageActions={}
2347
+ def stepButtonAction(butt:QToolButton, type):
2348
+ self.stepButton_action(butt, type)
2349
+ def stepPageAction(type):
2350
+ tree:ProcessTree=self.processTree if self.TREpar.tree==0 else self.binTree
2351
+ ITEs=self.ITEsfromTRE(self.TREpar)
2352
+ c=list(ITEs[0].children).index(type)
2353
+ ITE_chiild=ITEs[c+1]
2354
+ b:QPushButton=self.stepButtonBar.buttons[type]
2355
+ if b.isVisible() and b.isEnabled() and not ITE_chiild.active:
2356
+ b.click()
2357
+ elif ITE_chiild.active:
2358
+ item=tree.topLevelItem(self.TREpar.process)
2359
+ child=item.child(c)
2360
+ tree.setCurrentItem(child)
2361
+ self.processTree_item_selection(tree)
2362
+ else:
2363
+ showTip(self,'Current step is disabled! Please, reset the subsequemt step in the process to access it.')
2364
+ for t,b in self.stepButtonBar.buttons.items():
2365
+ b:QToolButton
2366
+ b.clicked.connect(lambda flag, butt=b, type=t: stepButtonAction(butt, type))
2367
+ self.stepPageActions[t]=lambda type=t: stepPageAction(type)
2368
+
2369
+ self.processTree.editingFinished=lambda: self.arrangeCurrentProcess(self.processTree)
2370
+ self.binTree.editingFinished=lambda: self.arrangeCurrentProcess(self.binTree)
2371
+ self.inheritance=lambda: None
2372
+ self.undoInd=None
2373
+ return
2374
+
2375
+ def binButton_action(self,FlagTreeSelection=True):
2376
+ w=self.Explorer_tree_splitter.width()
2377
+ if self.binButton.isChecked():
2378
+ self.Explorer_tree_splitter.setSizes([0, w])
2379
+ else:
2380
+ self.Explorer_tree_splitter.setSizes([w, 0])
2381
+ self.tree_splitter_action(FlagTreeSelection)
2382
+
2383
+ def tree_splitter_action(self,FlagTreeSelection=True):
2384
+ FlagProcessEnabled=self.Explorer_tree_splitter.sizes()[0]>0
2385
+ self.processTree.setEnabled(FlagProcessEnabled)
2386
+ for b in self.processButtonBar.buttons.values():
2387
+ b.setEnabled(FlagProcessEnabled)
2388
+ if not FlagProcessEnabled and FlagTreeSelection:
2389
+ self.processTree.blockSignals(True)
2390
+ self.processTree.setCurrentItem(None)
2391
+ self.processTree.clearSelection()
2392
+ self.processTree.blockSignals(False)
2393
+ self.processTree_item_selection(self.binTree)
2394
+ FlagBinEnabled=self.Explorer_tree_splitter.sizes()[1]>0
2395
+ self.binTree.setEnabled(FlagBinEnabled)
2396
+ self.binButton.setChecked(FlagBinEnabled)
2397
+ self.binButton.setIcon(self.binIconOn if FlagBinEnabled else self.binIconOff)
2398
+ if not FlagBinEnabled and FlagTreeSelection:
2399
+ self.binTree.blockSignals(True)
2400
+ self.binTree.setCurrentItem(None)
2401
+ self.binTree.clearSelection()
2402
+ self.binTree.blockSignals(False)
2403
+ self.processTree_item_selection(self.processTree)
2404
+
2405
+ def eventFilter(self, obj, event):
2406
+ if event.type() == QEvent.Type.ShortcutOverride:
2407
+ event.accept()
2408
+ return True
2409
+ return super().eventFilter(obj, event)
2410
+
2411
+ def keyPressEvent(self, event: QKeyEvent):
2412
+ if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter):
2413
+ if self.processTree.isEnabled():
2414
+ obj=self.processTree
2415
+ else:
2416
+ obj=self.binTree
2417
+ obj.setFocus()
2418
+ return super().keyPressEvent(event)
2419
+
2420
+ def processTree_item_selection(self, tree:ProcessTree):
2421
+ self.currentTree=tree
2422
+ for indTree,t in enumerate([self.processTree,self.binTree]):
2423
+ if t==tree:
2424
+ index,child=self.arrangeCurrentProcess(tree)
2425
+ item=tree.currentItem()
2426
+ if item: item.setSelected(True)
2427
+ self.TREpar.tree=indTree
2428
+ self.TREpar.process=index
2429
+ self.TREpar.step=child
2430
+ if self.TREpar.project is not None:
2431
+ self.projectTree.itemList[0][self.TREpar.project].copyfromfields(self.TREpar,['tree','process','step'])
2432
+ tree.setProcessActionButtonLayout()
2433
+ self.adjustProcessSelection()
2434
+ if self.TREpar.tree==0 and self.Explorer_tree_splitter.sizes()[0]==0:
2435
+ self.binButton.setChecked(False)
2436
+ self.binButton_action(FlagTreeSelection=False)
2437
+ if index is not None and index>=0:
2438
+ if child:
2439
+ self.selectStep()
2440
+ #ITE:ITEpar=self.ITEfromTRE(self.TREpar)
2441
+ #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}')
2442
+ else:
2443
+ self.ITEpar.copyfrom(tree.itemList[0][index][child])
2444
+ #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}')
2445
+ else:
2446
+ t.actionBar.setVisible(False)
2447
+ t.blockSignals(True)
2448
+ t.clearSelection()
2449
+ t.blockSignals(False)
2450
+
2451
+ def arrangeCurrentProcess(self, tree:ProcessTree):
2452
+ index=child=None
2453
+ item=tree.currentItem()
2454
+ if item:
2455
+ self.stepButtonBar.show()
2456
+ if item.parent():
2457
+ child=item.parent().indexOfChild(item)+1
2458
+ item=item.parent()
2459
+ else:
2460
+ child=0
2461
+ index=tree.indexOfTopLevelItem(item)
2462
+ if index<0 or index>len(tree.itemList[0])-1:
2463
+ index=None
2464
+ return index, child
2465
+ ITEs=tree.itemList[0][index]
2466
+ for c in list(stepData):
2467
+ b:QToolButton=self.stepButtonBar.buttons[c]
2468
+ lab:QLabel=self.stepButtonBar.labels[c]
2469
+ #b.label.setVisible(c in allData[0]['mandatory'])
2470
+ nsteps=len(ITEs[0].children)
2471
+ self.setProcessFlagRun(ITEs[0].ind)
2472
+ if c in ITEs[0].children:
2473
+ ind=list(ITEs[0].children).index(c)
2474
+ if c in ITEs[0].mandatory: ITEs[ind+1].active=True
2475
+ if item.childCount()>ind:
2476
+ item_child=item.child(ind)
2477
+ item_child.setHidden(not ITEs[ind+1].active)
2478
+ b.setVisible(c not in ITEs[0].mandatory) #b.setVisible(True)
2479
+ lab.setVisible(c in ITEs[0].mandatory)
2480
+ flagRunnable=all([ITEs[j].flagRun==0 for j in range(ind+2,nsteps+1)]) if ind<nsteps else True
2481
+ flagRunnable=flagRunnable and ITEs[ind+1].flagRun==0 and ITEs[0].flagRun!=-2 #and len(ITEs[ind+1].link)==0
2482
+ b.setEnabled(flagRunnable)
2483
+ lab.setEnabled(flagRunnable)
2484
+ b.setChecked(ITEs[ind+1].active)
2485
+ else:
2486
+ flagRunnable=False
2487
+ b.setVisible(False)
2488
+ lab.setVisible(False)
2489
+ b.setEnabled(False)
2490
+ lab.setEnabled(False)
2491
+ b.setChecked(False)
2492
+ b.setButtonIcon()
2493
+ if self.stepPage:
2494
+ self.stepPage.items[c].setVisible(c in ITEs[0].children) #and b.isChecked())
2495
+ pageButton:QPushButton=self.stepPage.items[c].findChildren(QPushButton)[0]
2496
+ if flagRunnable:
2497
+ pageButton.setIcon(b.icon())
2498
+ else:
2499
+ pageButton.setIcon(b.iconOn)
2500
+ else:
2501
+ self.ITEpar.FlagNone=True
2502
+ self.hideStepButtons()
2503
+ return index, child
2504
+
2505
+ def hideStepButtons(self):
2506
+ for c in list(stepData):
2507
+ b:QToolButton=self.stepButtonBar.buttons[c]
2508
+ lab:QLabel=self.stepButtonBar.labels[c]
2509
+ b.setVisible(False)
2510
+ lab.setVisible(False)
2511
+
2512
+ def setProcessFlagRun(self,ind):
2513
+ ITEs:ITEpar=self.ITEsfromInd(ind)
2514
+ 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
2515
+
2516
+ def selectStep(self):
2517
+ tree=[self.processTree,self.binTree][self.TREpar.tree]
2518
+ index=self.TREpar.process
2519
+ child=self.TREpar.step
2520
+
2521
+ FlagVisible=[]
2522
+ FlagAdjustPar=None
2523
+ #tabAreaWidget=self.widgets[-1]
2524
+ #tabAreaWidget.scrollArea.splitter.hide()
2525
+ #tabAreaWidget.scrollArea.splitter.setFixedWidth(tabAreaWidget.scrollArea.splitter.splitterMaximumSize)
2526
+ self.ITEpar.copyfrom(tree.itemList[0][index][child])
2527
+ """
2528
+ indpar=[j for j in self.ITEpar.ind]
2529
+ for j in range(indpar[-2]):
2530
+ indpar[-2]=j
2531
+ self.inheritance(indpar)
2532
+ """
2533
+ i=None
2534
+ #FlagPrev=CAL.ind[-1]==len(self.w_Calibration.TABpar_prev_at(CAL.ind))-1
2535
+ FlagCalVi=False
2536
+ for k,w in enumerate(self.widgets):
2537
+ w:gPaIRS_Tab
2538
+ try:
2539
+ par:TABpar=tree.itemList[k+1][index][child-1][-1]
2540
+ except Exception as inst:
2541
+ pri.Error.red(f'[selectStep] Error while accessing the prev queues:\n{inst}')
2542
+ pass
2543
+ if w.TABname=='Calibration':
2544
+ flagVisible=par is not None
2545
+ FlagVisible.append(flagVisible)
2546
+ if w.buttonTab: w.buttonTab.setVisible(flagVisible)
2547
+
2548
+ FlagCalVi=w.TABpar.FlagCalVi if flagVisible else False
2549
+ if hasattr(w,'logo_CalVi'): w.logo_CalVi.setVisible(FlagCalVi)
2550
+ if hasattr(w,'button_Run_CalVi'): w.button_Run_CalVi.setVisible(FlagCalVi)
2551
+ elif w.TABname!='TabArea':
2552
+ flagVisible=FlagCalVi if '_CalVi' in w.TABname else par is not None
2553
+ FlagVisible.append(flagVisible)
2554
+ if w.buttonTab: w.buttonTab.setVisible(flagVisible)
2555
+ else:
2556
+ for j,f in enumerate(FlagVisible):
2557
+ if not f: par.FlagVisible[j]=f
2558
+ if par:
2559
+ w.TABpar.copyfrom(par)
2560
+ if FlagAdjustPar is None:
2561
+ FlagAdjustPar=not par.FlagInit
2562
+ i=k
2563
+ else:
2564
+ w.TABpar.FlagNone=True
2565
+
2566
+ FlagAdjustPar=FlagAdjustPar or not self.ITEpar.FlagInit
2567
+ FlagBridge=True
2568
+ if i is not None:
2569
+ #self.gui.setTABpars_at(self.ITEpar.ind,FlagAdjustPar,FlagBridge,widget=self.widgets[i])
2570
+ self.widgets[i].setTABpar(FlagAdjustPar,FlagBridge)
2571
+ #self.widgets[-1].setTABpar(FlagAdjustPar,FlagBridge)
2572
+ #self.widgets[-1].scrollArea.splitter.show()
2573
+
2574
+ def stepButton_action(self,b,t):
2575
+ tree:ProcessTree=self.currentTree
2576
+ item=tree.currentItem()
2577
+ if item.parent():
2578
+ item=item.parent()
2579
+ r=tree.indexOfTopLevelItem(item)
2580
+ ITEs=tree.itemList[0][r]
2581
+ c=list(ITEs[0].children.keys()).index(b.buttonData['type'])
2582
+ child=item.child(c)
2583
+ if b.buttonData['type'] in ITEs[0].mandatory:
2584
+ tree.setCurrentItem(child)
2585
+ self.processTree_item_selection(tree)
2586
+ return
2587
+ ITE:ITEpar=ITEs[c+1]
2588
+ if len(ITE.link)>0:
2589
+ global FlagReturn
2590
+ FlagReturn=False
2591
+ def goToLinkedStep():
2592
+ global FlagReturn
2593
+ self.TREpar.process=ITE.link[2]
2594
+ if ITE.active:
2595
+ self.TREpar.step=ITE.link[3]+1
2596
+ else:
2597
+ self.TREpar.step=0
2598
+ self.gui.adjustProjectSelection()
2599
+ FlagReturn=True
2600
+ return
2601
+ def unLink():
2602
+ global FlagReturn
2603
+ b.setChecked(ITE.active)
2604
+ self.gui.unlink_pars(ITE.ind)
2605
+ b.click()
2606
+ FlagReturn=True
2607
+ return
2608
+ addButton={
2609
+ 'Unlink step': unLink,
2610
+ 'Go to linked step': goToLinkedStep,
2611
+ }
2612
+ ITE0_master=self.ITEsfromInd(ITE.link)[0]
2613
+ linkInfo=f'linked to that of the process:\n\n{ITE0_master.ind[2]+1}: {ITE0_master.name}'
2614
+ 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)
2615
+ if not FlagReturn: b.setChecked(ITE.active)
2616
+ return
2617
+ child.setHidden(not b.isChecked())
2618
+ ITE.active=b.isChecked()
2619
+ self.inheritance(ITE.ind)
2620
+ for dep in ITE.dependencies:
2621
+ ITE_dep:ITEpar=self.ITEfromInd(dep)
2622
+ ITE_dep.active=b.isChecked()
2623
+ item_dep=tree.topLevelItem(dep[2])
2624
+ child_dep=item_dep.child(c)
2625
+ child_dep.setHidden(not b.isChecked())
2626
+ self.gui.inheritance(dep)
2627
+ if self.stepPage: self.stepPage.items[t].setVisible(b.isChecked())
2628
+ FlagSettingPar=TABpar.FlagSettingPar
2629
+ TABpar.FlagSettingPar=True
2630
+ tree.clearSelection()
2631
+ TABpar.FlagSettingPar=FlagSettingPar
2632
+ if ITEs[c+1].active:
2633
+ tree.setCurrentItem(child)
2634
+ child.setSelected(True)
2635
+ else:
2636
+ tree.setCurrentItem(item)
2637
+ item.setSelected(True)
2638
+ return
2639
+
2640
+ def adjustProjectSelection(self):
2641
+ project=self.TREpar.project
2642
+
2643
+ FlagHideStepButtons=False
2644
+ if project==None:
2645
+ self.processWidget.setEnabled(False)
2646
+ for k,tree in enumerate([self.processTree,self.binTree]):
2647
+ tree.blockSignals(True)
2648
+ tree.clearSelection()
2649
+ clean_tree(tree)
2650
+ tree.blockSignals(False)
2651
+ FlagHideStepButtons=True
2652
+ else:
2653
+ self.processWidget.setEnabled(True)
2654
+
2655
+ #for w in self.widgets: w.TABpar_prev=[[],[]]
2656
+ item=None
2657
+ for k,tree in enumerate([self.processTree,self.binTree]):
2658
+ tree.itemList=[]
2659
+ for itemList in self.projectTree.itemList[1:]:
2660
+ tree.itemList.append(itemList[project][k])
2661
+ """
2662
+ for w,prev in zip(self.widgets,tree.itemList[1:]):
2663
+ w:gPaIRS_Tab
2664
+ w.TABpar_prev[k]=prev
2665
+ pass
2666
+ """
2667
+ tree.blockSignals(True)
2668
+ tree.clearSelection()
2669
+ clean_tree(tree)
2670
+ for i in range(len(tree.itemList[0])):
2671
+ tree.createProcessItem(None,i,FlagNewItem=False)
2672
+
2673
+ if self.TREpar.tree==k:
2674
+ item=self.projectTree.topLevelItem(project)
2675
+ self.projectTree.blockSignals(True)
2676
+ self.projectTree.setCurrentItem(item)
2677
+ item.setSelected(True)
2678
+ self.projectTree.blockSignals(False)
2679
+ self.projectTree.setButtonLayout()
2680
+ self.ui.binButton.setChecked(k==1)
2681
+ self.binButton_action(FlagTreeSelection=False)
2682
+ if self.TREpar.process is not None:
2683
+ item=tree.topLevelItem(self.TREpar.process)
2684
+ if item is None:
2685
+ self.TREpar.process=None
2686
+ self.TREpar.step=None
2687
+ if self.TREpar.step:
2688
+ item=item.child(self.TREpar.step-1)
2689
+ if item:
2690
+ tree.setCurrentItem(item)
2691
+ item.setSelected(True)
2692
+ tree.blockSignals(False)
2693
+ if item:
2694
+ item.setSelected(True)
2695
+ else: FlagHideStepButtons=True
2696
+ if FlagHideStepButtons:
2697
+ for c in list(stepData):
2698
+ b:QToolButton=self.stepButtonBar.buttons[c]
2699
+ b.setVisible(False)
2700
+ b.setChecked(False)
2701
+ return
2702
+
2703
+ def ITEsfromTRE(self,TRE:TREpar):
2704
+ return self.projectTree.itemList[1][TRE.project][TRE.tree][TRE.process]
2705
+
2706
+ def ITEsfromInd(self,ind:list):
2707
+ return self.projectTree.itemList[1][ind[0]][ind[1]][ind[2]]
2708
+
2709
+ def ITEfromTRE(self,TRE:TREpar):
2710
+ return self.projectTree.itemList[1][TRE.project][TRE.tree][TRE.process][TRE.step]
2711
+
2712
+ def ITEfromInd(self,ind:list):
2713
+ return self.projectTree.itemList[1][ind[0]][ind[1]][ind[2]][ind[3]+1]
2714
+
2715
+ def cancelUndo(self,ind:list=None):
2716
+ if ind is None:
2717
+ ind=[self.TREpar.project, self.TREpar.tree, self.TREpar.process, self.TREpar.step-1 if self.TREpar.step else -1, -1]
2718
+ if self.undoInd and self.undoInd[:-1]!=ind[:-1]:
2719
+ self.undoInd[-1]=-1
2720
+ if self.gui.checkProcesses(FlagInit=True,ind=self.undoInd):
2721
+ self.setITElayout(self.ITEfromInd(self.undoInd))
2722
+ self.undoInd=None
2723
+
2724
+ def setITElayout(self,ITE:ITEpar=None):
2725
+ if ITE is None:
2726
+ FlagCurrentITE=True
2727
+ ITE=self.ITEpar
2728
+ else:
2729
+ FlagCurrentITE=False
2730
+ if ITE.FlagNone: return
2731
+ self.cancelUndo(ITE.ind)
2732
+ FlagDone=True
2733
+ warningMessage=''
2734
+ ITEs=self.ITEsfromInd(ITE.ind)
2735
+ #mandatory=ITEs[0].mandatory
2736
+ j=1
2737
+ while ITEs[j].Step!=ITE.Step and j<len(ITEs)-1:
2738
+ if ITEs[j].active and ITEs[j].OptionDone==0 and ITEs[j].Step in ITE.parents:
2739
+ FlagDone=False
2740
+ if warningMessage: warningMessage+='\n\n'
2741
+ warningMessage+='--- '+ITEs[j].name+' ---\n'+f'The completion of the step "{ITEs[j].name}" is needed for "{ITE.name}"'
2742
+ j+=1
2743
+ for w in self.widgets:
2744
+ w:gPaIRS_Tab
2745
+ if FlagCurrentITE:
2746
+ TABpar_ind=w.TABpar
2747
+ else:
2748
+ TABpar_ind:TABpar=w.TABpar_at(ITE.ind)
2749
+ if not TABpar_ind: continue
2750
+ if TABpar_ind.FlagNone: continue
2751
+ if w.TABname=='Calibration':
2752
+ ITE.ncam=TABpar_ind.ncam
2753
+ ITE.progress=len(TABpar_ind.calList)
2754
+ if TABpar_ind.OptionDone==0: pass
2755
+ FlagDone=FlagDone and TABpar_ind.OptionDone!=0
2756
+ if not TABpar_ind.OptionDone==1 and not TABpar_ind.FlagNone:
2757
+ if TABpar_ind.warningMessage=='': continue
2758
+ if warningMessage: warningMessage+='\n\n'
2759
+ warningMessage+='--- '+w.TABname+' ---\n'+TABpar_ind.warningMessage
2760
+ if ITE.flagRun==0:
2761
+ if FlagDone:
2762
+ if warningMessage:
2763
+ ITE.warningMessage=warningMessage
2764
+ ITE.label='running_warn'
2765
+ ITE.OptionDone=-1
2766
+ else:
2767
+ if ITE.Step!=StepTypes.cal:
2768
+ ITE.warningMessage='Process step ready for running!'
2769
+ ITE.OptionDone=1
2770
+ ITE.label='running'
2771
+ else:
2772
+ ITE.warningMessage='Calibration files correctly identified!'
2773
+ ITE.OptionDone=1
2774
+ ITE.label='done'
2775
+ else:
2776
+ if warningMessage:
2777
+ ITE.warningMessage=warningMessage
2778
+ ITE.label='warning'
2779
+ else:
2780
+ ITE.warningMessage='Process step not yet initialized!'
2781
+ ITE.label='uninitialized'
2782
+ ITE.OptionDone=0
2783
+ else:
2784
+ procdata:dataTreePar=self.ITEfromInd(ITE.ind).procdata
2785
+ #if hasattr(self.gui,'reset_step') and not os.path.exists(procdata.procOutName()) and not ITE.FlagInit:
2786
+ # self.gui.reset_step(procdata.ind)
2787
+ # return
2788
+ #ITE.progress=procdata.numFinalized
2789
+ if ITE.flagRun==-2:
2790
+ ITE.warningMessage='Process step currently running.'
2791
+ ITE.label='running'
2792
+ else:
2793
+ if not FlagDone:
2794
+ ITE.warningMessage=f'The following issues are detected with the present step:\n{warningMessage}\n\nPlease, check if it is out-of-date!'
2795
+ ITE.procdata.warnings[0]=ITE.procdata.headerSection('CRITICAL ERROR',ITE.warningMessage,'X')
2796
+ ITE.procdata.warnings[1]=''
2797
+ ITE.procdata.setCompleteLog()
2798
+ ITE.label='error'
2799
+ ITE.OptionDone=0
2800
+ elif procdata.FlagErr:
2801
+ ITE.warningMessage='Some errors occured in this process step! See Log for more information.'
2802
+ ITE.label='error'
2803
+ elif ITE.flagRun==-1:
2804
+ ITE.warningMessage='Process step stopped by user.'
2805
+ ITE.label='paused'
2806
+ elif ITE.flagRun==-10:
2807
+ ITE.warningMessage='Process step in the queue for execution.'
2808
+ ITE.label='queue'
2809
+ else:
2810
+ ITE.warningMessage='Process step correctly completed! 💪🏻'
2811
+ ITE.label='done'
2812
+
2813
+ if FlagCurrentITE:
2814
+ ITE:ITEpar=self.ITEfromInd(ITE.ind)
2815
+ ITE.copyfrom(self.ITEpar,exceptions=['procdata'])
2816
+ self.updateItemWidget(ITE)
2817
+ return
2818
+
2819
+ def updateItemWidget(self,ITE:ITEpar):
2820
+ if self.TREpar.project==ITE.ind[0]:
2821
+ tree=[self.processTree,self.binTree][ITE.ind[1]]
2822
+ item:QTreeWidgetItem=tree.topLevelItem(ITE.ind[2])
2823
+ item=item.child(ITE.ind[3])
2824
+ TREind=[self.TREpar.project, self.TREpar.tree, self.TREpar.process, self.TREpar.step-1 if self.TREpar.step else -1, -1]
2825
+ if len(ITE.link)==0:
2826
+ if self.gui and ITE.flagRun==0 and len(self.gui.ui.tabAreaWidget.TABpar_prev_at(ITE.ind))<=1:
2827
+ item.setIcon(0,self.processTree.uneditedIcon)
2828
+ 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:
2829
+ item.setIcon(0,self.processTree.undoneIcon)
2830
+ self.undoInd=self.gui.ui.tabAreaWidget.TABpar.ind
2831
+ else:
2832
+ item.setIcon(0,QIcon())
2833
+ else:
2834
+ item.setIcon(0,self.processTree.linkedIcon)
2835
+
2836
+ itemWidget:StepItemWidget=tree.itemWidget(item,1)
2837
+
2838
+ ITE_ind=self.ITEfromInd(ITE.ind)
2839
+ itemWidget.setFromITE(ITE_ind)
2840
+
2841
+ def updateSwitchMovies(self,ind,FlagStart=False):
2842
+ self.updateProjectSwitchMovie(ind,FlagStart)
2843
+ self.updateProcessSwitchMovie(ind,FlagStart)
2844
+
2845
+ def updateProjectSwitchMovie(self,ind,FlagStart=False):
2846
+ if ind is None: return
2847
+ topLevelItem=self.projectTree.topLevelItem(ind[0])
2848
+ itemWidget=self.projectTree.itemWidget(topLevelItem,1)
2849
+ if itemWidget:
2850
+ switch:ModernSwitch=itemWidget.findChildren(ModernSwitch)[0]
2851
+ if switch:
2852
+ if FlagStart: switch.startTimer()
2853
+ else: switch.stopTimer()
2854
+
2855
+ def updateProcessSwitchMovie(self,ind,FlagStart=False):
2856
+ if ind is None: return
2857
+ if self.TREpar.project==ind[0]:
2858
+ topLevelItem=self.processTree.topLevelItem(ind[2])
2859
+ itemWidget=self.processTree.itemWidget(topLevelItem,1)
2860
+ if itemWidget:
2861
+ switch:ModernSwitch=itemWidget.findChildren(ModernSwitch)[0]
2862
+ if switch:
2863
+ if FlagStart: switch.startTimer()
2864
+ else: switch.stopTimer()
2865
+
2866
+ class StartingPage(QFrame):
2867
+ ICON_SIZE = 65
2868
+ LAYOUT_SPACING = 20
2869
+ LAYOUT_MARGIN = 20
2870
+ ITEM_HEIGHT = 100
2871
+ NAME_LABEL_HEIGHT = 32
2872
+ TEXT_LAYOUT_SPACING = 1
2873
+ CAPTION_WIDTH = 350
2874
+ BUTTON_LAYOUT_TOP_MARGIN = 0
2875
+
2876
+ dFontPixelSize_title=11
2877
+ TITLE_FONT_SIZE = fontPixelSize+dFontPixelSize_title
2878
+ NAME_FONT_SIZE = TITLE_FONT_SIZE-2
2879
+ CAPTION_FONT_SIZE = NAME_FONT_SIZE-2
2880
+
2881
+ def __init__(self, title:str='', processes:dict={}, buttonBar:dict={}):
2882
+ super().__init__()
2883
+ if __name__ == "__main__":
2884
+ iconW = QIcon()
2885
+ iconW.addFile(u""+ icons_path +"logo_PaIRS.png", QSize(), QIcon.Normal, QIcon.Off)
2886
+ self.setWindowTitle(title)
2887
+ self.setWindowIcon(iconW)
2888
+
2889
+ # ScrollArea
2890
+ self.scroll_area = QScrollArea(self)
2891
+ self.scroll_area.setWidgetResizable(True)
2892
+
2893
+ # Widget per contenere il layout principale
2894
+ container_widget = QFrame()
2895
+ container_widget.setObjectName('container')
2896
+ container_widget.setStyleSheet(f"""
2897
+ QFrame#container {{
2898
+ border: 1px solid rgba(128, 128, 128, 0.5);
2899
+ border-radius: 15px;
2900
+ 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));
2901
+ }}
2902
+ QWidget{{
2903
+ background: transparent;
2904
+ }}
2905
+ """)
2906
+
2907
+ # Layout principale verticale
2908
+ self.main_layout = QVBoxLayout(container_widget)
2909
+ self.main_layout.setSpacing(self.LAYOUT_SPACING)
2910
+ self.main_layout.setContentsMargins(self.LAYOUT_MARGIN, self.LAYOUT_MARGIN, self.LAYOUT_MARGIN, self.LAYOUT_MARGIN)
2911
+
2912
+ # Label iniziale con font più grande
2913
+ self.title = QLabel(title)
2914
+ title_font = self.title.font()
2915
+ title_font.setBold(True)
2916
+ self.title.setFont(title_font)
2917
+ self.title.setAlignment(Qt.AlignLeft)
2918
+ self.title.setFixedHeight(self.NAME_LABEL_HEIGHT)
2919
+ self.main_layout.addWidget(self.title)
2920
+
2921
+ self.scroll_area.setWidget(container_widget)
2922
+ self.scroll_area.setStyleSheet(scrollAreaStyle())
2923
+ layout = QVBoxLayout(self)
2924
+ layout.addWidget(self.scroll_area)
2925
+ self.setLayout(layout)
2926
+
2927
+ self.setupPage(processes,buttonBar)
2928
+
2929
+ def setObjectName(self,name:str):
2930
+ QFrame.setObjectName(self,name)
2931
+ self.scroll_area.setObjectName('scroll_area_'+self.objectName())
2932
+ return
2933
+
2934
+ def setupPage(self, processes:dict={}, buttonBar:dict={}):
2935
+ if not len(processes): return
2936
+ button_margin=self.ITEM_HEIGHT-self.ICON_SIZE-self.BUTTON_LAYOUT_TOP_MARGIN
2937
+ CAPTION_HEIGHT=self.ITEM_HEIGHT-self.NAME_LABEL_HEIGHT-self.TEXT_LAYOUT_SPACING
2938
+ self.items={}
2939
+ # Itera sui dizionari nella lista
2940
+ for n, process in processes.items():
2941
+ # Layout orizzontale per ogni processo
2942
+ widget=QWidget()
2943
+ process_layout = QHBoxLayout(widget)
2944
+ process_layout.setSpacing(self.LAYOUT_SPACING)
2945
+ process_layout.setContentsMargins(0, 0, 0, 0)
2946
+
2947
+ # Pulsante con icona
2948
+ button_layout = QHBoxLayout()
2949
+ button_layout.setSpacing(0)
2950
+ button_layout.setContentsMargins(0, self.BUTTON_LAYOUT_TOP_MARGIN, 0, button_margin)
2951
+
2952
+ icon_button = QPushButton()
2953
+ pixmap = QPixmap(icons_path+process['icon']).scaled(self.ICON_SIZE, self.ICON_SIZE, mode=Qt.TransformationMode.SmoothTransformation)
2954
+ icon_button.setIcon(pixmap)
2955
+ icon_button.setIconSize(pixmap.size())
2956
+ icon_button.setFixedSize(self.ICON_SIZE, self.ICON_SIZE)
2957
+ icon_button.setStyleSheet("border: none; background: none;")
2958
+ icon_button.setCursor(Qt.PointingHandCursor)
2959
+
2960
+ # Effetto dinamico al clic
2961
+ icon_button.pressed.connect(lambda btn=icon_button: btn.setStyleSheet("border: none; background: #dcdcdc;"))
2962
+ icon_button.released.connect(lambda btn=icon_button: btn.setStyleSheet("border: none; background: none;"))
2963
+ if buttonBar:
2964
+ def action(name):
2965
+ return lambda: buttonBar[name]()
2966
+ icon_button.clicked.connect(action(n))
2967
+
2968
+ button_layout.addWidget(icon_button)
2969
+ process_layout.addLayout(button_layout)
2970
+
2971
+ # Layout verticale per nome e descrizione
2972
+ text_layout = QVBoxLayout()
2973
+ text_layout.setSpacing(self.TEXT_LAYOUT_SPACING)
2974
+ text_layout.setContentsMargins(0, 0, 0, 0)
2975
+
2976
+ name_label = QLabel(process['name'])
2977
+ name_label.setObjectName('name_label')
2978
+ name_font = name_label.font()
2979
+ name_font.setPixelSize(self.NAME_FONT_SIZE)
2980
+ name_font.setBold(True)
2981
+ name_label.setFont(name_font)
2982
+ name_label.setAlignment(Qt.AlignLeft)
2983
+ name_label.setFixedHeight(self.NAME_LABEL_HEIGHT)
2984
+ text_layout.addWidget(name_label)
2985
+
2986
+ caption_text_edit = QTextEdit(process['caption'])
2987
+ caption_text_edit.setObjectName('caption_text_edit')
2988
+ caption_font = caption_text_edit.font()
2989
+ caption_font.setPixelSize(self.CAPTION_FONT_SIZE)
2990
+ caption_text_edit.setFont(caption_font)
2991
+ caption_text_edit.setAlignment(Qt.AlignmentFlag.AlignJustify)
2992
+ caption_text_edit.setReadOnly(True)
2993
+ caption_text_edit.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
2994
+ caption_text_edit.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
2995
+ caption_text_edit.setFrameStyle(QFrame.NoFrame)
2996
+ #caption_text_edit.setFixedWidth(self.CAPTION_WIDTH)
2997
+ caption_text_edit.setStyleSheet("background: transparent;")
2998
+ caption_text_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
2999
+
3000
+ # Adjust height to content
3001
+ caption_text_edit.document().setTextWidth(caption_text_edit.viewport().width())
3002
+ caption_text_edit.setFixedHeight(CAPTION_HEIGHT)#caption_text_edit.document().size().height())
3003
+
3004
+ text_layout.addWidget(caption_text_edit)
3005
+
3006
+ process_layout.addLayout(text_layout)
3007
+
3008
+ # Spacer orizzontale che si espande
3009
+ #spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
3010
+ #process_layout.addSpacerItem(spacer)
3011
+
3012
+ # Aggiungi il layout orizzontale al layout principale
3013
+ self.main_layout.addWidget(widget)
3014
+
3015
+ self.items[n]=widget
3016
+
3017
+ self.main_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))
3018
+
3019
+ def setFontPixelSize(self,fPixelSize):
3020
+ TITLE_FONT_SIZE = min([fPixelSize+self.dFontPixelSize_title,30])
3021
+ title_font = self.title.font()
3022
+ title_font.setPixelSize(TITLE_FONT_SIZE)
3023
+ title_font.setBold(True)
3024
+ self.title.setFont(title_font)
3025
+
3026
+ NAME_FONT_SIZE = TITLE_FONT_SIZE-2
3027
+ for c in self.findChildren(QLabel,'name_label'):
3028
+ c:QLabel
3029
+ name_font = c.font()
3030
+ name_font.setPixelSize(NAME_FONT_SIZE)
3031
+ name_font.setBold(True)
3032
+ c.setFont(name_font)
3033
+ CAPTION_FONT_SIZE = NAME_FONT_SIZE-4
3034
+ for c in self.findChildren(QTextEdit,'caption_text_edit'):
3035
+ c:QLabel
3036
+ caption_font = c.font()
3037
+ caption_font.setPixelSize(CAPTION_FONT_SIZE)
3038
+ c.setFont(caption_font)
3039
+
3040
+ if __name__ == "__main__":
3041
+ app = QApplication([])
3042
+ app.setStyle('Fusion')
3043
+
3044
+ Explorer = PaIRS_Explorer()
3045
+ if FlagStartingPages:
3046
+ projectStartingPage=StartingPage("Select a project", projectPageButtons,Explorer.projectPageActions)
3047
+ processStartingPage=StartingPage("Select a process", processData,Explorer.processPageActions)
3048
+ stepStartingPage=StartingPage("Set up each step of the process", stepData,Explorer.stepPageActions)
3049
+ Explorer.stepPage=stepStartingPage
3050
+
3051
+ Explorer.resize(500, 750)
3052
+ Explorer.move(0 if FlagStartingPages else 150, 150)
3053
+ Explorer.show()
3054
+ if FlagStartingPages:
3055
+ projectStartingPage.resize(500, 800)
3056
+ projectStartingPage.move(500, 150)
3057
+ projectStartingPage.show()
3058
+ projectStartingPage.setFontPixelSize(fontPixelSize)
3059
+ processStartingPage.resize(500, 800)
3060
+ processStartingPage.move(1000, 150)
3061
+ processStartingPage.show()
3062
+ processStartingPage.setFontPixelSize(fontPixelSize)
3063
+ stepStartingPage.resize(500, 800)
3064
+ stepStartingPage.move(1500, 150)
3065
+ stepStartingPage.show()
3066
+ stepStartingPage.setFontPixelSize(fontPixelSize)
3067
+
3068
+ app.exec()
3069
+