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