batoolset 0.1.0__tar.gz
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.
- batoolset-0.1.0/LICENSE +24 -0
- batoolset-0.1.0/PKG-INFO +45 -0
- batoolset-0.1.0/README.md +2 -0
- batoolset-0.1.0/batoolset/GUI/__init__.py +0 -0
- batoolset-0.1.0/batoolset/GUI/list_gui.py +513 -0
- batoolset-0.1.0/batoolset/GUI/multi_image_display.py +989 -0
- batoolset-0.1.0/batoolset/GUI/open.py +325 -0
- batoolset-0.1.0/batoolset/GUI/overlay_hints.py +45 -0
- batoolset-0.1.0/batoolset/GUI/paint2.py +1345 -0
- batoolset-0.1.0/batoolset/GUI/scrollablepaint.py +1152 -0
- batoolset-0.1.0/batoolset/IJ_ROIs/__init__.py +0 -0
- batoolset-0.1.0/batoolset/IJ_ROIs/tools.py +40 -0
- batoolset-0.1.0/batoolset/PATH/__init__.py +0 -0
- batoolset-0.1.0/batoolset/PATH/tools.py +79 -0
- batoolset-0.1.0/batoolset/SQLite_tools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/SQLite_tools/tools.py +840 -0
- batoolset-0.1.0/batoolset/__init__.py +0 -0
- batoolset-0.1.0/batoolset/binary/__init__.py +0 -0
- batoolset-0.1.0/batoolset/binary/tools.py +123 -0
- batoolset-0.1.0/batoolset/binarytools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/binarytools/cell_center_detector.py +290 -0
- batoolset-0.1.0/batoolset/clipboard_tools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/clipboard_tools/tools.py +7 -0
- batoolset-0.1.0/batoolset/colors/__init__.py +0 -0
- batoolset-0.1.0/batoolset/colors/colorgen.py +193 -0
- batoolset-0.1.0/batoolset/colors/tools.py +22 -0
- batoolset-0.1.0/batoolset/command_line/__init__.py +0 -0
- batoolset-0.1.0/batoolset/command_line/tools.py +18 -0
- batoolset-0.1.0/batoolset/dialogs/__init__.py +0 -0
- batoolset-0.1.0/batoolset/dialogs/opensave.py +228 -0
- batoolset-0.1.0/batoolset/dicts/__init__.py +0 -0
- batoolset-0.1.0/batoolset/dicts/tools.py +299 -0
- batoolset-0.1.0/batoolset/dims/__init__.py +0 -0
- batoolset-0.1.0/batoolset/dims/tools.py +83 -0
- batoolset-0.1.0/batoolset/draw/__init__.py +0 -0
- batoolset-0.1.0/batoolset/draw/shapes/Position.py +183 -0
- batoolset-0.1.0/batoolset/draw/shapes/__init__.py +0 -0
- batoolset-0.1.0/batoolset/draw/shapes/automated_alignment_drawing_refactored.py +346 -0
- batoolset-0.1.0/batoolset/draw/shapes/circle2d.py +99 -0
- batoolset-0.1.0/batoolset/draw/shapes/compute_optimal_size_all_merged.py +317 -0
- batoolset-0.1.0/batoolset/draw/shapes/ellipse2d.py +336 -0
- batoolset-0.1.0/batoolset/draw/shapes/freehand2d.py +235 -0
- batoolset-0.1.0/batoolset/draw/shapes/group.py +1830 -0
- batoolset-0.1.0/batoolset/draw/shapes/image2d.py +2469 -0
- batoolset-0.1.0/batoolset/draw/shapes/line2d.py +575 -0
- batoolset-0.1.0/batoolset/draw/shapes/point2d.py +257 -0
- batoolset-0.1.0/batoolset/draw/shapes/polygon2d.py +357 -0
- batoolset-0.1.0/batoolset/draw/shapes/polyline2d.py +235 -0
- batoolset-0.1.0/batoolset/draw/shapes/rect2d.py +388 -0
- batoolset-0.1.0/batoolset/draw/shapes/rectangle2d.py +601 -0
- batoolset-0.1.0/batoolset/draw/shapes/scalebar.py +304 -0
- batoolset-0.1.0/batoolset/draw/shapes/serializablefont.py +126 -0
- batoolset-0.1.0/batoolset/draw/shapes/serializableguiparams.py +10 -0
- batoolset-0.1.0/batoolset/draw/shapes/square2d.py +103 -0
- batoolset-0.1.0/batoolset/draw/shapes/textorientation.py +45 -0
- batoolset-0.1.0/batoolset/draw/shapes/tools.py +467 -0
- batoolset-0.1.0/batoolset/draw/shapes/transform2D.py +350 -0
- batoolset-0.1.0/batoolset/draw/shapes/txt2d.py +550 -0
- batoolset-0.1.0/batoolset/draw/widgets/__init__.py +0 -0
- batoolset-0.1.0/batoolset/draw/widgets/col.py +343 -0
- batoolset-0.1.0/batoolset/draw/widgets/image_group.py +248 -0
- batoolset-0.1.0/batoolset/draw/widgets/image_organizer.py +96 -0
- batoolset-0.1.0/batoolset/draw/widgets/paint.py +184 -0
- batoolset-0.1.0/batoolset/draw/widgets/vectorial.py +257 -0
- batoolset-0.1.0/batoolset/figure/__init__.py +0 -0
- batoolset-0.1.0/batoolset/figure/alignment.py +814 -0
- batoolset-0.1.0/batoolset/figure/column.py +1117 -0
- batoolset-0.1.0/batoolset/figure/fig_tools.py +285 -0
- batoolset-0.1.0/batoolset/figure/minimal_debugger_scale_etc.py +139 -0
- batoolset-0.1.0/batoolset/figure/panel.py +697 -0
- batoolset-0.1.0/batoolset/figure/row.py +852 -0
- batoolset-0.1.0/batoolset/figure/user_selection.py +287 -0
- batoolset-0.1.0/batoolset/files/__init__.py +0 -0
- batoolset-0.1.0/batoolset/files/tools.py +1321 -0
- batoolset-0.1.0/batoolset/images/__init__.py +0 -0
- batoolset-0.1.0/batoolset/images/tools.py +81 -0
- batoolset-0.1.0/batoolset/img.py +5216 -0
- batoolset-0.1.0/batoolset/linux/__init__.py +0 -0
- batoolset-0.1.0/batoolset/linux/tools.py +72 -0
- batoolset-0.1.0/batoolset/lists/__init__.py +0 -0
- batoolset-0.1.0/batoolset/lists/tools.py +436 -0
- batoolset-0.1.0/batoolset/luts/__init__.py +0 -0
- batoolset-0.1.0/batoolset/luts/lut_minimal_test.py +1207 -0
- batoolset-0.1.0/batoolset/matplolib/__init__.py +0 -0
- batoolset-0.1.0/batoolset/matplolib/tools.py +44 -0
- batoolset-0.1.0/batoolset/np_tools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/np_tools/tools.py +252 -0
- batoolset-0.1.0/batoolset/np_tools/trash.py +15 -0
- batoolset-0.1.0/batoolset/pandas_tools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/pandas_tools/tools.py +880 -0
- batoolset-0.1.0/batoolset/partials/__init__.py +0 -0
- batoolset-0.1.0/batoolset/partials/tools.py +83 -0
- batoolset-0.1.0/batoolset/plot_tools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/plot_tools/tools.py +70 -0
- batoolset-0.1.0/batoolset/pyqt/__init__.py +0 -0
- batoolset-0.1.0/batoolset/pyqt/tools.py +691 -0
- batoolset-0.1.0/batoolset/re_tools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/re_tools/tools.py +35 -0
- batoolset-0.1.0/batoolset/serialization/__init__.py +0 -0
- batoolset-0.1.0/batoolset/serialization/tools.py +1192 -0
- batoolset-0.1.0/batoolset/sets/__init__.py +0 -0
- batoolset-0.1.0/batoolset/sets/tools.py +109 -0
- batoolset-0.1.0/batoolset/settings/__init__.py +0 -0
- batoolset-0.1.0/batoolset/settings/global_settings.py +134 -0
- batoolset-0.1.0/batoolset/strings/__init__.py +0 -0
- batoolset-0.1.0/batoolset/strings/tools.py +225 -0
- batoolset-0.1.0/batoolset/ta/__init__.py +0 -0
- batoolset-0.1.0/batoolset/ta/database/__init__.py +0 -0
- batoolset-0.1.0/batoolset/ta/database/advanced_sql_plotter.py +1170 -0
- batoolset-0.1.0/batoolset/ta/database/my_qt_table.py +79 -0
- batoolset-0.1.0/batoolset/ta/database/preview_and_edit_sqlite_db.py +405 -0
- batoolset-0.1.0/batoolset/ta/database/sql.py +2141 -0
- batoolset-0.1.0/batoolset/ta/database/sql_column_name_dialog.py +122 -0
- batoolset-0.1.0/batoolset/ta/measurements/TAmeasures.py +2226 -0
- batoolset-0.1.0/batoolset/ta/measurements/__init__.py +0 -0
- batoolset-0.1.0/batoolset/ta/measurements/measurements3D/__init__.py +0 -0
- batoolset-0.1.0/batoolset/ta/measurements/measurements3D/get_point_on_surface_if_centroid_is_bad.py +175 -0
- batoolset-0.1.0/batoolset/ta/measurements/measurements3D/height_map_to_projection.py +82 -0
- batoolset-0.1.0/batoolset/ta/measurements/measurements3D/measures3D.py +208 -0
- batoolset-0.1.0/batoolset/ta/measurements/measurements3D/triangulate.py +331 -0
- batoolset-0.1.0/batoolset/ta/measurements/nematic.py +562 -0
- batoolset-0.1.0/batoolset/ta/measurements/nematic_measurements.py +107 -0
- batoolset-0.1.0/batoolset/ta/segmentation/__init__.py +0 -0
- batoolset-0.1.0/batoolset/ta/segmentation/neo_wshed.py +314 -0
- batoolset-0.1.0/batoolset/ta/selections/__init__.py +0 -0
- batoolset-0.1.0/batoolset/ta/selections/selection.py +329 -0
- batoolset-0.1.0/batoolset/ta/tracking/__init__.py +0 -0
- batoolset-0.1.0/batoolset/ta/tracking/last_tracking_based_on_matching_or_on_translation_from_mermaid_warp.py +653 -0
- batoolset-0.1.0/batoolset/ta/tracking/local_to_track_correspondance.py +138 -0
- batoolset-0.1.0/batoolset/ta/tracking/rapid_detection_of_vertices.py +517 -0
- batoolset-0.1.0/batoolset/ta/tracking/registration.py +108 -0
- batoolset-0.1.0/batoolset/ta/tracking/swap_detector_and_fixer.py +296 -0
- batoolset-0.1.0/batoolset/ta/tracking/track_correction.py +238 -0
- batoolset-0.1.0/batoolset/ta/tracking/tracking_error_detector_and_fixer.py +3099 -0
- batoolset-0.1.0/batoolset/ta/tracking/tracking_yet_another_approach_pyramidal_registration_n_neo_swapping_correction.py +725 -0
- batoolset-0.1.0/batoolset/ta/utils/TA_tools.py +43 -0
- batoolset-0.1.0/batoolset/ta/utils/__init__.py +0 -0
- batoolset-0.1.0/batoolset/ta/utils/rps_tools.py +1783 -0
- batoolset-0.1.0/batoolset/tests/__init__.py +0 -0
- batoolset-0.1.0/batoolset/tests/rps_tools.py +51 -0
- batoolset-0.1.0/batoolset/tests/tools.py +10 -0
- batoolset-0.1.0/batoolset/tools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/tools/early_stopper_class.py +9 -0
- batoolset-0.1.0/batoolset/tools/guided_data_recoverer.py +110 -0
- batoolset-0.1.0/batoolset/tools/logger.py +100 -0
- batoolset-0.1.0/batoolset/tools/mytimer.py +31 -0
- batoolset-0.1.0/batoolset/tools/prompt_color.py +160 -0
- batoolset-0.1.0/batoolset/tools/qthandler.py +118 -0
- batoolset-0.1.0/batoolset/uitools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/uitools/blinker.py +56 -0
- batoolset-0.1.0/batoolset/unsafe/__init__.py +0 -0
- batoolset-0.1.0/batoolset/unsafe/tools.py +115 -0
- batoolset-0.1.0/batoolset/utils/__init__.py +0 -0
- batoolset-0.1.0/batoolset/utils/commontools.py +158 -0
- batoolset-0.1.0/batoolset/utils/loadlist.py +728 -0
- batoolset-0.1.0/batoolset/version.py +10 -0
- batoolset-0.1.0/batoolset/xml/__init__.py +0 -0
- batoolset-0.1.0/batoolset/xml/tools.py +65 -0
- batoolset-0.1.0/batoolset/zip_tools/__init__.py +0 -0
- batoolset-0.1.0/batoolset/zip_tools/tools.py +76 -0
- batoolset-0.1.0/batoolset.egg-info/PKG-INFO +45 -0
- batoolset-0.1.0/batoolset.egg-info/SOURCES.txt +165 -0
- batoolset-0.1.0/batoolset.egg-info/dependency_links.txt +1 -0
- batoolset-0.1.0/batoolset.egg-info/requires.txt +28 -0
- batoolset-0.1.0/batoolset.egg-info/top_level.txt +1 -0
- batoolset-0.1.0/setup.cfg +4 -0
- batoolset-0.1.0/setup.py +56 -0
batoolset-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, baigouy
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
batoolset-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: batoolset
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An aggregate of functions I find useful
|
|
5
|
+
Home-page: https://github.com/baigouy/batoolset
|
|
6
|
+
Author: Benoit Aigouy
|
|
7
|
+
Author-email: benoit.aigouy@gmail.com
|
|
8
|
+
License: BSD
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.7, <3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: czifile
|
|
16
|
+
Requires-Dist: Markdown
|
|
17
|
+
Requires-Dist: matplotlib>=3.5.2
|
|
18
|
+
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: Pillow>=8.1.2
|
|
20
|
+
Requires-Dist: PyQt6
|
|
21
|
+
Requires-Dist: read-lif
|
|
22
|
+
Requires-Dist: scikit-image>=0.19.3
|
|
23
|
+
Requires-Dist: scipy>=1.7.3
|
|
24
|
+
Requires-Dist: scikit-learn>=1.0.2
|
|
25
|
+
Requires-Dist: tifffile>=2021.11.2
|
|
26
|
+
Requires-Dist: tqdm
|
|
27
|
+
Requires-Dist: natsort
|
|
28
|
+
Requires-Dist: numexpr
|
|
29
|
+
Requires-Dist: urllib3
|
|
30
|
+
Requires-Dist: qtawesome
|
|
31
|
+
Requires-Dist: pandas
|
|
32
|
+
Requires-Dist: numba
|
|
33
|
+
Requires-Dist: elasticdeform
|
|
34
|
+
Requires-Dist: roifile
|
|
35
|
+
Requires-Dist: prettytable
|
|
36
|
+
Requires-Dist: pyperclip
|
|
37
|
+
Requires-Dist: QtPy>=2.1.0
|
|
38
|
+
Requires-Dist: Deprecated
|
|
39
|
+
Requires-Dist: Requests
|
|
40
|
+
Requires-Dist: python-bioformats
|
|
41
|
+
Requires-Dist: python-javabridge
|
|
42
|
+
Requires-Dist: pyautogui
|
|
43
|
+
|
|
44
|
+
# batoolset
|
|
45
|
+
An aggregate of functions I find useful
|
|
File without changes
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
# support list for writing or not stuff
|
|
2
|
+
import os
|
|
3
|
+
from batoolset.settings.global_settings import set_UI # set the UI to qtpy
|
|
4
|
+
set_UI()
|
|
5
|
+
from qtpy import QtCore, QtWidgets
|
|
6
|
+
from qtpy.QtCore import Qt,Signal
|
|
7
|
+
from qtpy.QtWidgets import QWidget, QAbstractItemView, QListWidget, QVBoxLayout, QListWidgetItem, QToolBar, \
|
|
8
|
+
QToolButton, \
|
|
9
|
+
QLabel, QAction
|
|
10
|
+
import pyperclip as clip
|
|
11
|
+
from batoolset.dialogs.opensave import saveFileDialog
|
|
12
|
+
from batoolset.utils.loadlist import loadlist, save_list_to_file
|
|
13
|
+
import os
|
|
14
|
+
import platform
|
|
15
|
+
import subprocess
|
|
16
|
+
from batoolset.files.tools import smart_name_parser
|
|
17
|
+
import qtawesome as qta
|
|
18
|
+
from natsort import natsorted
|
|
19
|
+
from batoolset.tools.logger import TA_logger
|
|
20
|
+
logger = TA_logger()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__DEBUG__ = True
|
|
24
|
+
|
|
25
|
+
IMAGES_2D = ['.tif', '.tiff', '.jpg', '.jpeg', '.png', '.tga', '.bmp']
|
|
26
|
+
STACKS = ['.tif', '.tiff', '.lif', '.czi', '.lif', '.lsm']
|
|
27
|
+
LISTS = ['.lst', '.txt']
|
|
28
|
+
FASTA = ['.fa','.fna','.fasta']
|
|
29
|
+
PYTA_DEFAULT = IMAGES_2D + LISTS
|
|
30
|
+
|
|
31
|
+
class CustomListWidget(QListWidget):
|
|
32
|
+
orderChanged = Signal()
|
|
33
|
+
|
|
34
|
+
def dropEvent(self, event):
|
|
35
|
+
super().dropEvent(event)
|
|
36
|
+
self.orderChanged.emit()
|
|
37
|
+
|
|
38
|
+
class ListGUI(QWidget):
|
|
39
|
+
orderChanged = Signal()
|
|
40
|
+
deletePressed = Signal() # can be used in parent to detect that this was called allowing some action to be taken (more elegant than the callback or the method overlaod in several aspects)!
|
|
41
|
+
# TODO define supported formats --> TODO
|
|
42
|
+
def __init__(self, parent=None, file_to_load=None, supported_files=None, custom_label='Please Drag and Drop files below:', default_double_click_behaviour='full_no_ext', enable_folder_DND=True, detect_list_order_change=False):
|
|
43
|
+
super().__init__(parent)
|
|
44
|
+
if detect_list_order_change:
|
|
45
|
+
self.list = CustomListWidget(self) # a list that contains files to read or play with
|
|
46
|
+
# self.list.orderChanged.connect(self.check_order_changed)
|
|
47
|
+
else:
|
|
48
|
+
self.list = QListWidget(self)
|
|
49
|
+
self.list.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
|
50
|
+
# self.list.selectionModel().selectionChanged.connect(self.selectionChanged) # connect it to sel change
|
|
51
|
+
|
|
52
|
+
# self.list.selectionModel().selectionChanged.connect(self.listSelectionChanged)
|
|
53
|
+
|
|
54
|
+
self.list.itemDoubleClicked.connect(self.open_corresponding_folder)
|
|
55
|
+
|
|
56
|
+
self.enable_folder_DND = enable_folder_DND
|
|
57
|
+
self.default_double_click_behaviour = default_double_click_behaviour
|
|
58
|
+
|
|
59
|
+
layout = QVBoxLayout()
|
|
60
|
+
lab = QLabel(custom_label)
|
|
61
|
+
layout.addWidget(lab)
|
|
62
|
+
layout.addWidget(self.list)
|
|
63
|
+
|
|
64
|
+
self.setLayout(layout)
|
|
65
|
+
# allow support of DND
|
|
66
|
+
self.setAcceptDrops(True)
|
|
67
|
+
self.supported_files = supported_files
|
|
68
|
+
|
|
69
|
+
# status_bar = QStatusBar()
|
|
70
|
+
# layout.addWidget(status_bar)
|
|
71
|
+
# self.list_commands(**kwargs)
|
|
72
|
+
self.list_commands()
|
|
73
|
+
|
|
74
|
+
if file_to_load is not None:
|
|
75
|
+
self.load_list(file_to_load)
|
|
76
|
+
# if __DEBUG__:
|
|
77
|
+
# fake sel list
|
|
78
|
+
# pass
|
|
79
|
+
|
|
80
|
+
# allow move objects within the list
|
|
81
|
+
self.list.setDragDropMode(QAbstractItemView.InternalMove) # j'adore --> maybe put this as an option
|
|
82
|
+
|
|
83
|
+
# allows sorting of lists
|
|
84
|
+
# self.list.setSortingEnabled(True) # we don't want list to be sorted by default
|
|
85
|
+
# self.list.sortItems() # ascending by default
|
|
86
|
+
self.list.sortItems(QtCore.Qt.DescendingOrder) # todo MAKE it a sort button
|
|
87
|
+
|
|
88
|
+
# both lines below support objects handling directly from the list --> a gain of time
|
|
89
|
+
self.list.itemChanged.connect(self.item_changed)
|
|
90
|
+
self._object_dict = {} # dirty hack to store objects and allow for DND of objects
|
|
91
|
+
|
|
92
|
+
def item_changed(self, item):
|
|
93
|
+
obj = self._object_dict.get(str(item)) # Get the object from the dictionary
|
|
94
|
+
if obj is not None:
|
|
95
|
+
# Do something with the object
|
|
96
|
+
print("Object changed:", obj)
|
|
97
|
+
def check_order_changed(self):
|
|
98
|
+
# Implement your order changed logic here
|
|
99
|
+
print("Order changed!")
|
|
100
|
+
# self.setWindowTitle('test')
|
|
101
|
+
|
|
102
|
+
def open_corresponding_folder(self, double_clicked_item):
|
|
103
|
+
try:
|
|
104
|
+
self.open_folder(smart_name_parser(double_clicked_item.toolTip(), ordered_output=[self.default_double_click_behaviour])[0]) #'full_no_ext'
|
|
105
|
+
except:
|
|
106
|
+
# no big deal if it fails
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
def open_folder(self, path):
|
|
110
|
+
if platform.system() == 'Darwin':
|
|
111
|
+
subprocess.Popen(['open', path])
|
|
112
|
+
elif platform.system() == 'Windows':
|
|
113
|
+
os.startfile(path)
|
|
114
|
+
elif platform.system() == 'Linux':
|
|
115
|
+
subprocess.Popen(["xdg-open", path])
|
|
116
|
+
|
|
117
|
+
def list_commands(self): #,**kwargs):
|
|
118
|
+
|
|
119
|
+
# buttons_to_hide = []
|
|
120
|
+
# if 'buttons_to_remove' in kwargs:
|
|
121
|
+
# buttons_to_hide = kwargs['buttons_to_remove']
|
|
122
|
+
# if isinstance(buttons_to_hide, str):
|
|
123
|
+
# buttons_to_hide = [buttons_to_hide]
|
|
124
|
+
|
|
125
|
+
# self.penSize = QSpinBox(objectName='penSize')
|
|
126
|
+
# self.penSize.setSingleStep(1)
|
|
127
|
+
# self.penSize.setRange(1, 256)
|
|
128
|
+
# self.penSize.setValue(3)
|
|
129
|
+
# self.penSize.valueChanged.connect(self.penSizechange)
|
|
130
|
+
|
|
131
|
+
# self.channels = QComboBox(objectName='channels')
|
|
132
|
+
# self.channels.addItem("merge")
|
|
133
|
+
# self.channels.addItem("0")
|
|
134
|
+
# self.channels.addItems(["1", "2", "3"])
|
|
135
|
+
# self.channels.currentIndexChanged.connect(self.channelChange)
|
|
136
|
+
|
|
137
|
+
self.tb = QToolBar()
|
|
138
|
+
#
|
|
139
|
+
toolButton = QToolButton()
|
|
140
|
+
# toolButton.setText('Draw')
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
save_action = QAction(qta.icon('fa5.save'), 'Save image list', self)
|
|
144
|
+
save_action.triggered.connect(self.save_list)
|
|
145
|
+
# END KEEP
|
|
146
|
+
toolButton.setDefaultAction(save_action)
|
|
147
|
+
self.tb.addWidget(toolButton)
|
|
148
|
+
#
|
|
149
|
+
# toolButton2 = QToolButton()
|
|
150
|
+
# toolButton2.setText('Draw')
|
|
151
|
+
# toolButton2.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton))
|
|
152
|
+
# tb.addWidget(toolButton2)
|
|
153
|
+
#
|
|
154
|
+
# toolButton3 = QToolButton()
|
|
155
|
+
# toolButton3.setText('Draw')
|
|
156
|
+
# toolButton3.setIcon(self.style().standardIcon(QStyle.SP_DialogCancelButton))
|
|
157
|
+
# tb.addWidget(toolButton3)
|
|
158
|
+
#
|
|
159
|
+
# toolButton4 = QToolButton()
|
|
160
|
+
# toolButton4.setText('Draw')
|
|
161
|
+
# toolButton4.setIcon(self.style().standardIcon(QStyle.SP_DialogCloseButton))
|
|
162
|
+
# tb.addWidget(toolButton4)
|
|
163
|
+
|
|
164
|
+
toolButton5 = QToolButton()
|
|
165
|
+
# toolButton5.setText("Delete")
|
|
166
|
+
# toolButton5.setIcon(self.style().standardIcon(QStyle.SP_TrashIcon))
|
|
167
|
+
# toolButton5.addAction(dqsdqsdq)
|
|
168
|
+
# pkoi marche pas
|
|
169
|
+
# KEEP
|
|
170
|
+
|
|
171
|
+
delete_sel_action = QAction(qta.icon('mdi.delete-outline'), 'Delete selection', self)
|
|
172
|
+
# toolButton5.addAction(delete_sel_action) # this stuff adds a dropwdon that can be opened by pressing a long time on the button --> keep in mind cause can be useful
|
|
173
|
+
delete_sel_action.triggered.connect(self.removeSel)
|
|
174
|
+
# END KEEP
|
|
175
|
+
toolButton5.setDefaultAction(delete_sel_action)
|
|
176
|
+
self.tb.addWidget(toolButton5)
|
|
177
|
+
|
|
178
|
+
toolButton6 = QToolButton()
|
|
179
|
+
# toolButton6.setText("Copy")
|
|
180
|
+
# toolButton6.setIcon(qta.icon('fa5.copy'))
|
|
181
|
+
copy_action = QAction(qta.icon('fa5.copy'), 'Copy selection to the system clipboard', self)
|
|
182
|
+
copy_action.triggered.connect(self.sel_to_clipboard)
|
|
183
|
+
toolButton6.setDefaultAction(copy_action)
|
|
184
|
+
# toolButton6.ad#dAction(print('toto'))
|
|
185
|
+
self.tb.addWidget(toolButton6)
|
|
186
|
+
|
|
187
|
+
toolButton7 = QToolButton()
|
|
188
|
+
# toolButton7.setText("Paste")
|
|
189
|
+
# toolButton7.setIcon(qta.icon('fa.paste'))
|
|
190
|
+
paste_action = QAction(qta.icon('fa.paste'), 'Paste the system clipboard to the list', self)
|
|
191
|
+
paste_action.triggered.connect(self.paste_from_clipboard)
|
|
192
|
+
toolButton7.setDefaultAction(paste_action)
|
|
193
|
+
self.tb.addWidget(toolButton7)
|
|
194
|
+
|
|
195
|
+
toolButton8 = QToolButton()
|
|
196
|
+
sort_action = QAction(qta.icon('mdi.sort-alphabetical-ascending'), 'Sort list (using the Natsort algorithm)', self)
|
|
197
|
+
sort_action.triggered.connect(self.natsort_list)
|
|
198
|
+
toolButton8.setDefaultAction(sort_action)
|
|
199
|
+
self.tb.addWidget(toolButton8)
|
|
200
|
+
|
|
201
|
+
# tb.addWidget(self.channels)
|
|
202
|
+
|
|
203
|
+
# tb.addAction("Save")
|
|
204
|
+
# tb.addAction("sq...")
|
|
205
|
+
# tb.addWidget(self.penSize)
|
|
206
|
+
|
|
207
|
+
# could have show and hide mask here --> see how I can do that --> but do not delete it in fact
|
|
208
|
+
|
|
209
|
+
self.layout().addWidget(self.tb)
|
|
210
|
+
|
|
211
|
+
# disable item internal move and list addition, just selection is kept
|
|
212
|
+
def freeze(self, bool):
|
|
213
|
+
self.tb.setEnabled(not bool)
|
|
214
|
+
# TODO disable DND of list component
|
|
215
|
+
# if bool:
|
|
216
|
+
# self.list.setDragDropMode(QAbstractItemView.InternalMove)
|
|
217
|
+
# self.setAcceptDrops(True)
|
|
218
|
+
# else:
|
|
219
|
+
# self.list.setDragDropMode(QAbstractItemView.InternalMove)
|
|
220
|
+
self.setAcceptDrops(not bool)
|
|
221
|
+
# how to disable item reorder
|
|
222
|
+
# QGraphicsItem.ItemIsMovable
|
|
223
|
+
# we also prevent internal drag --> it's a way to keep the list functional
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# ça ne marche pas
|
|
228
|
+
# print(bool)
|
|
229
|
+
if bool:
|
|
230
|
+
|
|
231
|
+
# print('entering o,ne')
|
|
232
|
+
|
|
233
|
+
# disable item internal move
|
|
234
|
+
for iii in range(self.list.count()):
|
|
235
|
+
item = self.list.item(iii)
|
|
236
|
+
|
|
237
|
+
# is there a bug here
|
|
238
|
+
if item.flags() & Qt.ItemIsDragEnabled:
|
|
239
|
+
item.setFlags(item.flags() ^ Qt.ItemIsDragEnabled)
|
|
240
|
+
# item.setFlags(item.flags() ^ Qt.ItemIsDropEnabled)
|
|
241
|
+
|
|
242
|
+
# if iii == 0:
|
|
243
|
+
# if item.flags() & Qt.ItemIsDragEnabled:
|
|
244
|
+
# print( "ItemIsDragEnabled", item)
|
|
245
|
+
# else:
|
|
246
|
+
# print("not ItemIsDragEnabled", item)
|
|
247
|
+
|
|
248
|
+
else:
|
|
249
|
+
# allow item internal move
|
|
250
|
+
# print('entering two')
|
|
251
|
+
for iii in range(self.list.count()):
|
|
252
|
+
item = self.list.item(iii)
|
|
253
|
+
item.setFlags(item.flags() | Qt.ItemIsDragEnabled)
|
|
254
|
+
# item.setFlags(item.flags() | Qt.ItemIsDropEnabled)
|
|
255
|
+
# if iii == 0:
|
|
256
|
+
# if item.flags() & Qt.ItemIsDragEnabled:
|
|
257
|
+
# print("ItemIsDragEnabled", item)
|
|
258
|
+
# else:
|
|
259
|
+
# print("not ItemIsDragEnabled", item)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def load_list(self, file_to_load):
|
|
263
|
+
files = loadlist(file_to_load)
|
|
264
|
+
if files is not None:
|
|
265
|
+
# populate the list
|
|
266
|
+
for file in files:
|
|
267
|
+
self.add_file_to_list_if_supported(file)
|
|
268
|
+
else:
|
|
269
|
+
print('list could not be loaded/invalid list --> sorry')
|
|
270
|
+
|
|
271
|
+
# def selectionChanged(self):
|
|
272
|
+
# print('selection changed to override')
|
|
273
|
+
|
|
274
|
+
# allow DND
|
|
275
|
+
def dragEnterEvent(self, event):
|
|
276
|
+
if not self.acceptDrops():
|
|
277
|
+
logger.warning('Drag and drop not supported in "Preview" mode, please select another tab.')
|
|
278
|
+
event.ignore()
|
|
279
|
+
return
|
|
280
|
+
if event.mimeData().hasUrls():
|
|
281
|
+
event.accept()
|
|
282
|
+
else:
|
|
283
|
+
event.ignore()
|
|
284
|
+
|
|
285
|
+
def dragMoveEvent(self, event):
|
|
286
|
+
if not self.acceptDrops():
|
|
287
|
+
logger.warning('Drag and drop not supported in "Preview" mode, please select another tab.')
|
|
288
|
+
event.ignore()
|
|
289
|
+
return
|
|
290
|
+
if event.mimeData().hasUrls():
|
|
291
|
+
event.setDropAction(QtCore.Qt.CopyAction)
|
|
292
|
+
event.accept()
|
|
293
|
+
else:
|
|
294
|
+
event.ignore()
|
|
295
|
+
|
|
296
|
+
# handle DND on drop
|
|
297
|
+
def dropEvent(self, event):
|
|
298
|
+
super().dropEvent(event)
|
|
299
|
+
self.orderChanged.emit()
|
|
300
|
+
|
|
301
|
+
if not self.acceptDrops():
|
|
302
|
+
logger.warning('Drag and drop not supported in "Preview" mode, please select another tab.')
|
|
303
|
+
event.ignore()
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
if event.mimeData().hasUrls():
|
|
307
|
+
event.setDropAction(QtCore.Qt.CopyAction)
|
|
308
|
+
event.accept()
|
|
309
|
+
urls = []
|
|
310
|
+
for url in event.mimeData().urls():
|
|
311
|
+
urls.append(url.toLocalFile())
|
|
312
|
+
# add all dropped items to the list
|
|
313
|
+
for url in urls:
|
|
314
|
+
# item = QListWidgetItem(os.path.basename(url), self.list)
|
|
315
|
+
# item.setToolTip(url)
|
|
316
|
+
# self.list.addItem(item)
|
|
317
|
+
self.add_file_to_list_if_supported(url)
|
|
318
|
+
else:
|
|
319
|
+
event.ignore()
|
|
320
|
+
|
|
321
|
+
def is_file_supported(self, file):
|
|
322
|
+
if self.supported_files is None:
|
|
323
|
+
# accept all by default
|
|
324
|
+
return True
|
|
325
|
+
if smart_name_parser(file, ordered_output=['ext'])[0].lower() in self.supported_files:
|
|
326
|
+
return True
|
|
327
|
+
return False
|
|
328
|
+
|
|
329
|
+
def add_file_to_list_no_check(self, file):
|
|
330
|
+
item = QListWidgetItem(os.path.basename(file), self.list)
|
|
331
|
+
item.setToolTip(file)
|
|
332
|
+
self.list.addItem(item)
|
|
333
|
+
|
|
334
|
+
def add_object_to_list_no_check(self, obj): #added this to add non file objects to the list
|
|
335
|
+
item = QListWidgetItem(str(obj), self.list)
|
|
336
|
+
item.setToolTip(str(obj))
|
|
337
|
+
# item.setData(Qt.UserRole, obj)
|
|
338
|
+
# item.setData(Qt.UserRole, str(obj))
|
|
339
|
+
self._object_dict[str(item)] = obj # Store a reference to the object in the dictionary
|
|
340
|
+
self.list.addItem(item)
|
|
341
|
+
|
|
342
|
+
def add_to_list(self, files, check_if_supported=True):
|
|
343
|
+
if isinstance(files, str):
|
|
344
|
+
if check_if_supported:
|
|
345
|
+
self.add_file_to_list_if_supported(files)
|
|
346
|
+
else:
|
|
347
|
+
self.add_file_to_list_no_check(files)
|
|
348
|
+
elif isinstance(files, list):
|
|
349
|
+
for file in files:
|
|
350
|
+
if check_if_supported:
|
|
351
|
+
self.add_file_to_list_if_supported(file)
|
|
352
|
+
else:
|
|
353
|
+
self.add_file_to_list_no_check(file)
|
|
354
|
+
else:
|
|
355
|
+
print('Non supported, cannot be added to list', files)
|
|
356
|
+
|
|
357
|
+
def list_files_in_directory(self, directory_path):
|
|
358
|
+
files = [os.path.join(directory_path,f) for f in os.listdir(directory_path) if os.path.isfile(os.path.join(directory_path, f))]
|
|
359
|
+
# print(files)
|
|
360
|
+
return files
|
|
361
|
+
|
|
362
|
+
def add_file_to_list_if_supported(self, file):
|
|
363
|
+
# print('checking if supported', file)
|
|
364
|
+
if self.is_file_supported(file) and os.path.isfile(file):
|
|
365
|
+
ext = smart_name_parser(file, ordered_output=['ext'])[0].lower()
|
|
366
|
+
if ext in LISTS:
|
|
367
|
+
TA_list_of_files = loadlist(file)
|
|
368
|
+
return self.add_to_list(TA_list_of_files, check_if_supported=True)
|
|
369
|
+
else:
|
|
370
|
+
self.add_file_to_list_no_check(file)
|
|
371
|
+
elif self.enable_folder_DND and os.path.isdir(file):
|
|
372
|
+
files = self.list_files_in_directory(file)
|
|
373
|
+
for file in files:
|
|
374
|
+
self.add_file_to_list_if_supported(file)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def get_full_list(self):
|
|
378
|
+
# return the complete list of files
|
|
379
|
+
selected_items = []
|
|
380
|
+
for i in range(self.list.count()):
|
|
381
|
+
if str(self.list.item(i)) in self._object_dict:
|
|
382
|
+
selected_items.append(self._object_dict[str(self.list.item(i))])
|
|
383
|
+
else:
|
|
384
|
+
selected_items.append(self.list.item(i).toolTip())
|
|
385
|
+
return selected_items
|
|
386
|
+
|
|
387
|
+
def get_selection(self, mode='single'):
|
|
388
|
+
selected_items = self.list.selectedItems()
|
|
389
|
+
if selected_items:
|
|
390
|
+
if mode == 'single':
|
|
391
|
+
if str(selected_items[0]) in self._object_dict:
|
|
392
|
+
return self._object_dict[str(selected_items[0])]
|
|
393
|
+
# if selected_items[0].data(Qt.UserRole):
|
|
394
|
+
# return selected_items[0].data(Qt.UserRole)
|
|
395
|
+
return selected_items[0].toolTip()
|
|
396
|
+
else:
|
|
397
|
+
sel = []
|
|
398
|
+
for selec in selected_items:
|
|
399
|
+
if str(selec) in self._object_dict:
|
|
400
|
+
sel.append(self._object_dict[str(selec)])
|
|
401
|
+
# elif selec.data(Qt.UserRole):
|
|
402
|
+
# sel.append(selec.data(Qt.UserRole))
|
|
403
|
+
else:
|
|
404
|
+
sel.append(selec.toolTip())
|
|
405
|
+
return sel
|
|
406
|
+
else:
|
|
407
|
+
return None
|
|
408
|
+
|
|
409
|
+
def get_selection_index(self, mode='single'):
|
|
410
|
+
try:
|
|
411
|
+
# selected_items = self.list.selectedIndexes() # this is a weird model index object that is largely useless
|
|
412
|
+
selected_items = [s.row() for s in self.list.selectedIndexes()]
|
|
413
|
+
# print('selected_items',selected_items)
|
|
414
|
+
if selected_items:
|
|
415
|
+
if mode == 'single':
|
|
416
|
+
# print('sel', selected_items[0])
|
|
417
|
+
return selected_items[0]
|
|
418
|
+
else:
|
|
419
|
+
# sel = []
|
|
420
|
+
# for selec in selected_items:
|
|
421
|
+
# sel.append(selec.toolTip())
|
|
422
|
+
return selected_items
|
|
423
|
+
# else:
|
|
424
|
+
except:
|
|
425
|
+
pass
|
|
426
|
+
return None
|
|
427
|
+
|
|
428
|
+
def save_list(self):
|
|
429
|
+
# on va sauver la liste ici
|
|
430
|
+
lst = self.get_full_list()
|
|
431
|
+
if lst:
|
|
432
|
+
path = smart_name_parser(lst[0], ordered_output='parent')
|
|
433
|
+
path = os.path.join(path, 'list.lst')
|
|
434
|
+
|
|
435
|
+
output_file = saveFileDialog(parent_window=self, path=path,
|
|
436
|
+
extensions="Lists (*.lst);;Supported Files (*.lst *.txt);;All Files (*)",
|
|
437
|
+
default_ext='.lst')
|
|
438
|
+
if output_file is not None:
|
|
439
|
+
# now do save the list in the folder of the parent of the first file
|
|
440
|
+
save_list_to_file(lst, output_file)
|
|
441
|
+
# print(lst, output_file)
|
|
442
|
+
|
|
443
|
+
def removeSel(self):
|
|
444
|
+
selected_items = self.list.selectedItems()
|
|
445
|
+
self.list.clearSelection() # Clear selection # required to prevent selection from changing and the software to want to update display or anything alike
|
|
446
|
+
if not selected_items:
|
|
447
|
+
self.clearList()
|
|
448
|
+
self.deletePressed.emit() # send the delete action to the parent
|
|
449
|
+
return
|
|
450
|
+
|
|
451
|
+
# Remove selected items from the list
|
|
452
|
+
for item in selected_items:
|
|
453
|
+
# Remove the item from the dictionary
|
|
454
|
+
if str(item) in self._object_dict:
|
|
455
|
+
del self._object_dict[str(item)]
|
|
456
|
+
for item in selected_items:
|
|
457
|
+
# print('looping')
|
|
458
|
+
self.list.takeItem(self.list.row(item))
|
|
459
|
+
self.deletePressed.emit() # send the delete action to the parent
|
|
460
|
+
|
|
461
|
+
# faire une copie dans le clip de la selection
|
|
462
|
+
def sel_to_clipboard(self):
|
|
463
|
+
sel = self.get_selection(mode='all')
|
|
464
|
+
if sel is not None:
|
|
465
|
+
clip.copy('\n'.join(sel))
|
|
466
|
+
|
|
467
|
+
def paste_from_clipboard(self):
|
|
468
|
+
files = clip.paste()
|
|
469
|
+
# try add this to the list
|
|
470
|
+
try:
|
|
471
|
+
# print(files)
|
|
472
|
+
splitted_files = files.split('\n')
|
|
473
|
+
# print(splitted_files)
|
|
474
|
+
self.add_to_list(splitted_files, check_if_supported=True)
|
|
475
|
+
except:
|
|
476
|
+
# no big deal if fails
|
|
477
|
+
pass
|
|
478
|
+
|
|
479
|
+
# TODO maybe add sort by time as there is even an icon for that and can be useful for my leica files !!!
|
|
480
|
+
# TODO copy icons if they exist on natsort ok for now it's really a detail...
|
|
481
|
+
def natsort_list(self):
|
|
482
|
+
full_list = self.get_full_list()
|
|
483
|
+
if full_list is not None:
|
|
484
|
+
self.clearList()
|
|
485
|
+
self.add_to_list(natsorted(full_list), check_if_supported=False)
|
|
486
|
+
|
|
487
|
+
def clearList(self):
|
|
488
|
+
self.list.clear()
|
|
489
|
+
del self._object_dict
|
|
490
|
+
self._object_dict = {}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
if __name__ == '__main__':
|
|
494
|
+
# TODO add a main method so it can be called directly
|
|
495
|
+
# maybe just show a canvas and give it interesting props --> TODO --> really need fix that too!!!
|
|
496
|
+
import sys
|
|
497
|
+
from qtpy.QtWidgets import QApplication
|
|
498
|
+
|
|
499
|
+
# should probably have his own scroll bar embedded somewhere
|
|
500
|
+
|
|
501
|
+
app = QApplication(sys.argv)
|
|
502
|
+
|
|
503
|
+
w = ListGUI(file_to_load='/E/Sample_images/sample_images_PA/mini/list.lst')
|
|
504
|
+
|
|
505
|
+
# w.freeze(False)
|
|
506
|
+
# w.freeze(True)
|
|
507
|
+
|
|
508
|
+
w.show()
|
|
509
|
+
|
|
510
|
+
print('get_full_list', w.get_full_list()) # very good and useful
|
|
511
|
+
print('sel', w.get_selection(mode='all'))
|
|
512
|
+
|
|
513
|
+
sys.exit(app.exec_())
|