PaIRS-UniNa 0.2.7__cp310-cp310-macosx_11_0_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of PaIRS-UniNa might be problematic. Click here for more details.

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